Skip to content
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Please choose versions by [Semantic Versioning](http://semver.org/).
* MINOR version when you add functionality in a backwards-compatible manner, and
* PATCH version when you make backwards-compatible bug fixes.

## Unreleased

- feat: Move task-creation consent gate from `vault-cli:work-on-task-assistant` agent to the `work-on-task` slash command — agent loses the `Skill` tool (architectural block on `Skill: vault-cli:create-task`); `Task` is retained for legitimate subagent dispatch in Phase 5 (`coding:pre-implementation-assistant`) and Phase 7 (`vault-cli:task-manager-agent`). On miss the agent emits a structured `not_found:` verdict; the slash command parses it, asks the user via `AskUserQuestion`, and on `Yes` routes to `Skill: vault-cli:create-task` before re-invoking the agent against the new task.
- feat: Add `not_found` form to `vault-cli:work-on-task-assistant` `<output_format>` so the slash command can parse the absence case (searched-source evidence + suggested task name)

## v0.68.1

- bump Go 1.26.3 → 1.26.4
Expand Down
44 changes: 30 additions & 14 deletions agents/work-on-task-assistant.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: work-on-task-assistant
description: Prepare a task for work — find details, set status, track on daily note, discover guides. Works in any vault; gracefully degrades when Jira / semantic-search MCPs are unavailable.
model: sonnet
tools: Read, Glob, Bash, Edit, AskUserQuestion, Task, Skill, mcp__semantic-search__search_related, mcp__atlassian__getAccessibleAtlassianResources, mcp__atlassian__atlassianUserInfo, mcp__atlassian__getJiraIssue, mcp__atlassian__editJiraIssue, mcp__atlassian__getTransitionsForJiraIssue, mcp__atlassian__transitionJiraIssue, mcp__atlassian__lookupJiraAccountId
tools: Read, Glob, Bash, Edit, AskUserQuestion, Task, mcp__semantic-search__search_related, mcp__atlassian__getAccessibleAtlassianResources, mcp__atlassian__atlassianUserInfo, mcp__atlassian__getJiraIssue, mcp__atlassian__editJiraIssue, mcp__atlassian__getTransitionsForJiraIssue, mcp__atlassian__transitionJiraIssue, mcp__atlassian__lookupJiraAccountId
color: blue
---

Expand Down Expand Up @@ -30,9 +30,9 @@ Mutations happen **before** guide discovery and report rendering. Verify after w
<constraints>
- AUTO: Jira tasks assigned to current user + transitioned to "In Progress" (no asking)
- AUTO: Obsidian task status set to `in_progress` (no asking)
- ASK: before creating a new Obsidian task file
- MANDATORY for code tasks: run `/coding:check-guides` and read project Development Guide if present
- READ-ONLY except: status frontmatter + daily-note tracking + (via Skill) task creation
- MANDATORY for code tasks: dispatch `Task(subagent_type='coding:pre-implementation-assistant', ...)` and read project Development Guide if present (replaces the prior `Skill: coding:check-guides` invocation — `Skill` is no longer in `tools:`)
- READ-ONLY except: status frontmatter + daily-note tracking
- ALLOWED `Task` subagent dispatch is restricted to: `coding:pre-implementation-assistant` (Phase 5), `vault-cli:task-manager-agent` (Phase 7). NEVER dispatch to a `*create-task*`, `*creator*`, or any subagent whose role is to create task files — the consent gate lives in the calling slash command (`vault-cli:work-on-task` Phase 4), not in a sibling agent. `Task` is a generic dispatch primitive; it does not grant create-task capability by itself, but routing through a creator-agent would defeat the architectural gate.
- ALWAYS present absolute file paths
- **NEVER fall back to direct HTTP for Jira (no `curl`, no `wget`, no `gh api` against Jira hosts).** If no `mcp__atlassian__*` MCP is available, skip every Jira block silently. Direct API calls bypass authentication and credential management and are forbidden.
</constraints>
Expand Down Expand Up @@ -90,7 +90,9 @@ If `JIRA_MCP_AVAILABLE` is false but input looks like a Jira ID:
- Otherwise: `Glob: {tasks_dir}/*<keyword>*.md`

**Task not found**:
- AskUserQuestion → "Create new task?" — Yes invokes `Skill: vault-cli:create-task`; No shows manual search tips and STOPS
- Emit the `not_found:` verdict block (literal `not_found:` header on its own line — see `<output_format>` for the exact form) with the searched-source evidence (Jira: hit/miss/skipped, daily-note: hit/miss, semantic-search: top-3 misses with scores, Glob: paths tried) and a `Suggested task name:` line derived from the input argument (or, if input is a Jira ID, from the Jira issue summary returned by the Jira lookup; fall back to the raw input string if neither is available).
- STOP — do NOT propose a fix, do NOT call AskUserQuestion, do NOT invoke `Skill: vault-cli:create-task`.
- The `not_found` verdict is parsed by the calling slash command (`vault-cli:work-on-task`) which owns the create-gate.

## Phase 2: Auto-assign + transition Jira (Jira tasks only) — DO THIS FIRST

Expand All @@ -110,7 +112,7 @@ Skip silently if `JIRA_MCP_AVAILABLE` is false.

Record each result for the final report (✅ / ℹ️ / ⚠️). Errors do NOT block subsequent phases — but they MUST surface in the report.

## Phase 3: Find/create Obsidian task and set status
## Phase 3: Find Obsidian task and set status

- `Glob: {tasks_dir}/*{keywords}*.md`
- If Jira: also `Grep: 'jira: {key}'` in `{tasks_dir}`
Expand All @@ -121,9 +123,7 @@ If found:
- Report: `✅ Status: {old} → in_progress`

If not found AND task came from Jira:
- AskUserQuestion → "Create Obsidian task file for local tracking?"
- Yes → `Skill: vault-cli:create-task` then re-find + set status
- No → continue Jira-only
- The Jira issue exists but there is no local Obsidian task file. This is a `not_found` case for the Obsidian side — the calling slash command's Phase 4 owns task creation. Emit the `not_found:` verdict (see Phase 1 and `<output_format>`) including the Jira summary as the `Suggested task name:` value and STOP — do NOT call AskUserQuestion, do NOT invoke `Skill: vault-cli:create-task`. The slash command handles the consent gate.

## Phase 4: Track on daily note

Expand All @@ -139,7 +139,7 @@ If not found AND task came from Jira:
Heuristic: title or description contains "fix", "implement", "refactor", "add", "bug", "deploy", "build", or extension `.go`/`.py`/`.ts`/`.js` etc.

If code task:
- `Skill: coding:check-guides` with task title/description
- `Task(subagent_type='coding:pre-implementation-assistant', prompt='Find relevant coding guidelines for: <task title/description>')` — subagent dispatch instead of `Skill:` (the `Skill` tool was removed to enforce the consent gate; `Task` dispatching to the pre-implementation assistant returns the same guide set without granting create-task capability)
- Search vault for `*Development Guide.md` and read if found
- Extract: branch strategy, test command, PR process, deploy steps
- Present as "⚠️ **Development Workflow**" section in the report
Expand Down Expand Up @@ -190,6 +190,8 @@ When the task description, a related log entry, or any retrieved file references

**Verification gate — runs before rendering the report. Do NOT skip.**

**Carve-out for `not_found`**: if Phase 1 emitted a `not_found` verdict, Phase 8 is a no-op — the `not_found` verdict IS the report, no mutations occurred to verify, and the agent STOPs without emitting "Ready to work on this task." (which is the found-case marker, not a universal one). Skip every assertion below in this case.

If `JIRA_MCP_AVAILABLE` AND input was a Jira ID:
1. Re-fetch the issue: `mcp__atlassian__getJiraIssue(cloudId={JIRA_CLOUD_ID}, issueIdOrKey={key}, fields=["status","assignee"])`
2. Assert `status.name == "In Progress"` AND `assignee.accountId == current_user_account_id`
Expand All @@ -215,7 +217,7 @@ Jira:
✅ Verified post-mutation (status=In Progress, assignee=<user>) | ⚠️ Verification failed: <details>

[Obsidian:]
✅ Status: <old> → in_progress | ✅ Created Obsidian task file | ℹ️ Continuing Jira-only
✅ Status: <old> → in_progress | ℹ️ Continuing Jira-only

[Daily Note:]
✅ Tracked on today's page | ℹ️ Already tracked | ℹ️ Daily note missing
Expand Down Expand Up @@ -253,22 +255,36 @@ Remaining:
---
Ready to work on this task.
```

```markdown
not_found:
📋 Task: <input> [(<jira_id>)]
Status: not_found

Searched:
- Jira: <hit: summary> | <miss> | <skipped: not in input pattern>
- Daily note ({{today}}): <hit: line> | <miss>
- Semantic search: <top-3 misses with scores, e.g. "0.42 — <hit title>"> | <skipped: MCP unavailable>
- Glob ({{tasks_dir}}/*{keyword}*.md): <paths tried, e.g. "24 Tasks/*foo*.md → 0 matches"> | <skipped>

Suggested task name: <derived title — Jira summary if Jira ID input, else input string verbatim>
```
</output_format>

<error_handling>
- **Jira 404**: show issue id + suggestion to check the Jira project; continue without Jira data
- **Daily note missing**: report and continue
- **Task not found in any source**: AskUserQuestioncreate or stop with manual search tips
- **Task not found in any source**: emit the `not_found:` verdict (see Phase 1 and `<output_format>`) and STOP — the calling slash command (`vault-cli:work-on-task` Phase 4) handles the consent gate via `AskUserQuestion` before invoking `Skill: vault-cli:create-task`. The agent must not ask or create.
- **MCP tool absent**: silent skip — never error on absent integration
- **Guide search returns nothing**: "ℹ️ No operational guides found"
</error_handling>

<success_criteria>
1. Task details from at least one source
2. Jira tasks: auto-assigned + transitioned (when JIRA_MCP_AVAILABLE) — **and verified by re-fetch in Phase 8**
3. Obsidian status set to in_progress (or asked to create local file)
3. Obsidian status set to in_progress (or `not_found:` verdict emitted if no local task file exists — slash command Phase 4 handles creation)
4. Tracked on daily note (or graceful skip)
5. Code tasks: `/coding:check-guides` ran + Development Guide presented
5. Code tasks: `Task(subagent_type='coding:pre-implementation-assistant', ...)` dispatched + Development Guide presented
6. Guides searched (semantic or fallback) — **FAIL if Phase 6 skipped; at least one `search_related` call required when MCP available**
7. Phase 8 verification ran for Jira tasks; report includes verification line
8. Report ends with "Ready to work on this task." — NEVER emitted while Jira state is stale
Expand Down
34 changes: 33 additions & 1 deletion commands/work-on-task.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
description: Find task details, transition Jira, set status, track on daily note, discover guides — delegates to work-on-task-assistant
argument-hint: <jira-id-or-text>
allowed-tools: Task
allowed-tools: [Task, AskUserQuestion, Skill]
---

Find task details and relevant operational guides before starting work. Delegates to the `vault-cli:work-on-task-assistant` agent (which is the heavy lifter).
Expand Down Expand Up @@ -39,6 +39,37 @@ The assistant handles all the work, detecting available integrations at runtime:

Output ends with `Ready to work on this task.`

## Phase 4 — Handle not_found

The agent (dispatched in `## Process` step 2) emits a structured `not_found` verdict from its own Phase 1 (`Find task`) when the requested task cannot be found in any source. This phase parses that verdict and asks the user before any file is created.

1. **Parse the agent's report** for the `not_found:` marker AND capture the verdict body into variables. The agent's `<output_format>` defines two separate fenced markdown blocks — one for the `found` case (ends with `Ready to work on this task.`) and one for the `not_found:` case (literal `not_found:` header on its own line). Look for the `not_found:` block specifically; if the report ends with `Ready to work on this task.` and contains no `not_found:` block, Phase 4 is a no-op and you are done. When the `not_found:` block IS present, match on the `not_found:` token, then extract:
- `SEARCHED_BLOCK` — the bullet list under the `Searched:` line (verbatim, line-by-line, until the next blank line or `Suggested task name:` line)
- `SUGGESTED_NAME` — the value after `Suggested task name:` (verbatim, trimmed)
2. **Use `SUGGESTED_NAME` as the seed.** (If the input was a Jira ID and the Jira lookup returned a summary, the agent supplied that summary; otherwise the agent supplied the input string verbatim. Either way, `SUGGESTED_NAME` is what you pass on.)
3. **Ask the user via `AskUserQuestion`** with the `vault-cli` main-session UX channel:
- `header`: `Create new task?`
- `question`: `Create new Obsidian task "<SUGGESTED_NAME>"?` (substitute the captured value)
- `options`: two entries — `Yes, create it` (description: `Run vault-cli:create-task with "<SUGGESTED_NAME>" as the seed title, then re-invoke work-on-task-assistant`) and `No, stop here` (description: `Print manual search tips and stop — no task is created`)
4. **On `Yes, create it`**: invoke `Skill: vault-cli:create-task "<SUGGESTED_NAME>"` (use the same argument form as `commands/create-task.md` — pass the captured suggested name as a quoted argument). The create-task skill has its own interactive flow that asks for parent goal, priority, category, defer date, etc. — do not duplicate those asks.
5. **On create success** (create-task skill returns the new task file path or reports success): re-invoke `Task tool with subagent_type: 'vault-cli:work-on-task-assistant' prompt: 'Find details and guides for: <new task title>'` — same form as the Phase 2 invocation, but with the new task title. The agent's standard Phase 2–8 prep mutations then run against the just-created task.
6. **On create failure or user cancel inside `vault-cli:create-task`** (the skill returns a non-success status, errors out, or the user aborts midway through its interactive prompts): print `❌ Task creation failed or was cancelled. No task created; no follow-up invocation.` and STOP — do NOT re-invoke `vault-cli:work-on-task-assistant`, do NOT retry the create.
7. **On `No, stop here`**: print the manual search tips and STOP. Substitute `SEARCHED_BLOCK` (captured in step 1) where indicated; resolve `{daily_dir}` from `vault-cli config list --output json` for the active vault before printing:
```
❌ Task not found: "<input>"

Searched:
<SEARCHED_BLOCK>

Manual search tips:
- Check the active vault's tasks dir (`vault-cli config list` → `tasks_dir`)
- Grep across vaults: `grep -rln "<keyword>" ~/Documents/Obsidian/`
- Check today's daily note (`<resolved daily_dir>/YYYY-MM-DD.md`)
- If input looked like a Jira ID, confirm the issue exists in the Atlassian project

No task was created.
```

## Integration

Task-first workflow:
Expand All @@ -54,3 +85,4 @@ Task-first workflow:
- No hardcoded Jira hostname, project key, or vault path — everything detected at runtime
- Works in Personal, Brogrammers, Trading, or any future vault registered with `vault-cli config`
- Each vault session loads a single Atlassian MCP under the canonical name `atlassian` (see vault-specific `mcp-*.json` configs); the agent uses `mcp__atlassian__*` regardless of which Jira instance is active
- The agent searches; the slash command asks before creating.
Loading
Loading