Skip to content

feat(cli): add openspec agent command group — cut tokens + turns for AI skills#1132

Open
jussirantala wants to merge 4 commits into
Fission-AI:mainfrom
jussirantala:feat/agent-helpers
Open

feat(cli): add openspec agent command group — cut tokens + turns for AI skills#1132
jussirantala wants to merge 4 commits into
Fission-AI:mainfrom
jussirantala:feat/agent-helpers

Conversation

@jussirantala
Copy link
Copy Markdown

@jussirantala jussirantala commented May 27, 2026

Summary

Adds an openspec agent command group whose sole reason for existing is to cut token usage and round-trips when an AI assistant drives the OpenSpec workflow. Today the bundled skill templates (openspec-propose, openspec-apply-change) inline a multi-step CLI dance in their prompt text — status --json, then instructions <artifact> --json, then re-check status, then manually edit tasks.md checkboxes. Every round-trip is its own tool call, its own JSON parse, and its own prompt-text overhead, and every consumer ends up rewriting the same orchestration in their own repo. This PR moves three deterministic operations into the CLI itself and updates the bundled templates so an openspec update slims the skills automatically.

Estimated impact on a typical 30-task propose+apply cycle: ~15–20% fewer tokens, ~30–50 fewer turns, ~10–12 minutes of wall-clock saved.

The new commands sit under a dedicated agent namespace rather than at the top level — they're explicitly skill-facing, not user-facing, and shouldn't pollute openspec --help for human users.

Added

Command Replaces Notes
openspec agent resolve-change [name] [--auto] [--json] list --json + name-validation + ambiguity handling in every skill Distinct exit codes (0 ok, 1 none, 2 not found, 3 ambiguous) so callers branch without parsing stderr.
openspec agent next-artifact --change <name> two-call status --json + instructions <artifact> --json Single JSON payload bundling the next ready artifact + its instructions. { "done": true } when complete.
openspec agent mark-task-done <task-id> --change <name> sed/regex edits to flip - [ ]- [x] Idempotent, CRLF-preserving, anchored matching so 1.1 does not match 1.10. Resolves tracking file via schema's apply.tracks.

Changed

  • openspec instructions apply --change <n> --json payload is enriched:
    • tasks[].numericId — captures the leading N(.N)* token (e.g. "1.1", "2.3.4") when the tracking file uses numbered tasks. Existing positional id field is unchanged.
    • nextPendingId (top-level) — first unchecked task with a numericId, or null. Pairs with agent mark-task-done to drive an apply loop without re-parsing tasks.md.
  • src/core/templates/workflows/{propose,apply-change}.ts now use the three new commands instead of inlining orchestration. After this lands, downstream consumers run openspec update and pick up the slimmer skill bodies automatically. Other workflow templates (continue-change, archive-change, explore, new-change, sync-specs, verify-change, bulk-archive-change, ff-change) are intentionally left untouched in this PR to keep review surface small; they can be migrated incrementally.

Non-goals

  • No behavior change for existing commands. status, instructions, list, archive, new change, set change are unchanged.
  • No new dependencies. All three commands reuse existing internals (loadChangeContext, formatChangeStatus, generateInstructions, generateApplyInstructions, resolveSchema).
  • No interactive prompts. Agent-facing surface; ambiguity always exits non-zero with structured stderr.

Backward compatibility

  • Additive only. TaskItem gains optional numericId; existing consumers that key off id / description / done keep working. Note: when a numeric prefix is detected the description field no longer carries the leading 1.1 — this is the intentional improvement (consumers should display by numericId if present, else by positional id).
  • ApplyInstructions gains nextPendingId; existing consumers ignore it.

Validation

  • pnpm install
  • pnpm run build
  • pnpm test
    • 1644 / 1644 affected tests pass. The two pre-existing zsh-installer Windows-only chmod tests that fail on my machine also fail on clean upstream/main — confirmed by stashing my changes and re-running. Not regressions from this PR.
  • New tests: 23 cases in test/commands/agent-helpers.test.ts covering list mode, named lookup, --auto ambiguity matrix, next-artifact happy path / mid-stream / all-done, mark-task-done flip / idempotent / 1.1 vs 1.10 boundary / CRLF preservation / schema without apply.tracks / no-match, plus three cases on the enriched instructions apply --json payload.
  • Refreshed hash gates in test/core/templates/skill-templates-parity.test.ts for the two refactored templates (function payload + generated skill content).
  • Smoke against a freshly openspec init'd workspace:
    node bin/openspec.js init --tools none --force
    node bin/openspec.js new change demo-feat
    node bin/openspec.js agent resolve-change --auto             # → demo-feat
    node bin/openspec.js agent next-artifact --change demo-feat  # → JSON for proposal
    # (write artifacts...)
    node bin/openspec.js agent mark-task-done --change demo-feat 1.1

Notes

  • Drafted while migrating a consumer repo (Epic Games' epic-design-system) away from local .mjs helper files that wrapped the same openspec invocations. Once this PR is released, that repo and any other consumer can drop their local helper scripts and run openspec update to regenerate the slimmer skill templates.
  • Branch was rebased onto origin/main before pushing so the PR diff is clean.

Summary by CodeRabbit

  • New Features

    • Added three new agent CLI commands for resolving changes, retrieving artifacts, and marking tasks complete
    • Tasks now support hierarchical numeric IDs for better organization
    • Enhanced JSON output includes next pending task information for workflow automation
  • Documentation

    • Updated CLI documentation for new agent commands
    • Revised workflow templates for improved change application and artifact proposal processes

Review Change Stack

Jussi Rantala added 2 commits May 27, 2026 12:20
Three new verb-first agent helper commands that let AI skills drive the
OpenSpec workflow without re-implementing CLI orchestration. Also enriches
the existing 'instructions apply --json' payload with numericId and
nextPendingId fields so downstream callers can target tasks by author-
supplied hierarchical id (1.1, 2.3.4) rather than position.

- openspec resolve-change [name] [--auto] [--json]: list active changes,
  validate a named change, or auto-select when exactly one is active.
- openspec next-artifact --change <name>: bundle status next-ready
  artifact lookup with full instructions in one JSON payload, so skills
  can run a propose loop with one call per artifact instead of two.
- openspec mark-task-done --change <name> <task-id>: flip a tracked
  tasks.md checkbox by hierarchical task id with anchored matching
  (1.1 does not match 1.10), EOL preservation, idempotent on
  already-done lines.

Existing parseTasksFile() now also captures the leading N(.N)* token
into a new optional 'numericId' field on TaskItem; legacy positional
'id' remains unchanged. ApplyInstructions gains a 'nextPendingId'
field (first unchecked task that has a numericId, or null).
…pers

- propose template: collapse status + instructions loop into a single
  next-artifact call. Drops one round-trip per artifact and removes ~30
  lines of LLM-facing orchestration prose from both the skill and the
  command variants.
- apply-change template: replace 'openspec list --json + AskUserQuestion'
  flow with 'openspec resolve-change --auto'; collapse status + apply
  instructions into a single 'instructions apply --change --json' call
  (which now includes schemaName + nextPendingId); swap manual checkbox
  editing for 'openspec mark-task-done'.
- Update completion command registry with the three new commands.
- Refresh hash gates in skill-templates-parity test.
- Add CLI docs (docs/cli.md) for resolve-change / next-artifact /
  mark-task-done plus a note on the new numericId / nextPendingId fields
  on 'instructions apply --json'.
- Add agent-helpers changeset (minor).
@jussirantala jussirantala requested a review from TabishB as a code owner May 27, 2026 09:35
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 762e217c-bce9-430b-9586-8809f3f52a43

📥 Commits

Reviewing files that changed from the base of the PR and between b85d8b4 and 00ef520.

📒 Files selected for processing (8)
  • .changeset/agent-helpers.md
  • docs/cli.md
  • src/cli/index.ts
  • src/core/completions/command-registry.ts
  • src/core/templates/workflows/apply-change.ts
  • src/core/templates/workflows/propose.ts
  • test/commands/agent-helpers.test.ts
  • test/core/templates/skill-templates-parity.test.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/cli.md
  • .changeset/agent-helpers.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/core/completions/command-registry.ts
  • test/core/templates/skill-templates-parity.test.ts
  • src/core/templates/workflows/propose.ts
  • src/core/templates/workflows/apply-change.ts
  • test/commands/agent-helpers.test.ts

📝 Walkthrough

Walkthrough

This PR adds three agent helper CLI commands (resolve-change, next-artifact, mark-task-done), enriches instruction/task payloads with per-task numericId and nextPendingId, integrates commands into the CLI/completions, updates workflow templates to use the new commands, and adds docs and comprehensive tests.

Changes

Agent Helper Commands and Task Metadata Enrichment

Layer / File(s) Summary
Type contracts and task metadata enrichment
src/commands/workflow/shared.ts, src/commands/workflow/instructions.ts
TaskItem gains optional numericId; ApplyInstructions adds nextPendingId. parseTasksFile extracts numbered prefixes and pickNextPendingId selects the next unchecked numeric task.
Resolve change command
src/commands/workflow/resolve-change.ts
New resolve-change CLI command lists, validates, or auto-selects active changes with distinct exit codes and optional JSON output.
Next artifact command
src/commands/workflow/next-artifact.ts
New next-artifact CLI command returns the next runnable artifact bundled with full instructions or { done: true } when complete; reports blocked dependencies and supports JSON/human output.
Mark task done command
src/commands/workflow/mark-task-done.ts
New mark-task-done flips a specific tracking-file checkbox by anchored hierarchical id, is idempotent, preserves CRLF/LF, and reports clear statuses and exit codes.
CLI wiring and command registry
src/cli/index.ts, src/commands/workflow/index.ts, src/core/completions/command-registry.ts
Registers and documents the openspec agent group and subcommands, exports handlers from the workflow index, and updates completions/registry metadata.
Workflow template updates
src/core/templates/workflows/apply-change.ts, src/core/templates/workflows/propose.ts
Refactors skill/command templates to orchestrate apply/propose loops using openspec agent resolve-change --auto, openspec agent next-artifact --change, and openspec agent mark-task-done, and documents the enriched instructions apply --json shape.
Documentation, changelog, and test suite
docs/cli.md, .changeset/agent-helpers.md, test/commands/agent-helpers.test.ts, test/core/templates/skill-templates-parity.test.ts
Adds CLI docs for the new agent commands and instructions apply --json fields; includes a Vitest suite covering resolve-change, next-artifact, mark-task-done, and instruction enrichment; updates template parity hashes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Fission-AI/OpenSpec#603: Overlaps with apply-change template completion guidance adjustments (opsx continuation/archive messaging).
  • Fission-AI/OpenSpec#698: Touches the workflow template factories that this PR updates to use agent commands.

Suggested reviewers

  • TabishB

Poem

🐰 I hop through changes, light and spry,
Resolve the change and fetch what's nigh,
Next artifact, please show the way,
Mark tasks done — then off we play! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding an openspec agent command group to reduce token usage and turns for AI skills.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jussirantala jussirantala marked this pull request as draft May 27, 2026 09:36
These three commands are explicitly skill-facing — keeping them as
top-level verbs polluted `openspec --help` for human users with three
lines they never need. Move them under a dedicated `openspec agent`
subcommand group instead, so the top-level surface stays focused on
human workflows and agents call `openspec agent <subcommand>`
explicitly.

- src/cli/index.ts: nest under `agent` subcommand group.
- src/core/completions/command-registry.ts: register `agent` as a
  subcommand group entry.
- src/core/templates/workflows/{propose,apply-change}.ts: update skill
  + command template invocations to the new namespace.
- docs/cli.md: rename section headers + Agent-Compatible table entries.
- test/commands/agent-helpers.test.ts: prefix every runCLI call with
  `agent`.
- test/core/templates/skill-templates-parity.test.ts: refresh function
  payload + generated content hashes for the two refactored templates.
- .changeset/agent-helpers.md: re-describe surface as a command group.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/commands/agent-helpers.test.ts (1)

138-146: ⚡ Quick win

Use canonical path comparison for path identity assertions.

This test is asserting path identity via suffix matching. Compare canonicalized actual/expected paths instead, and keep any spelling checks separate if needed.

Suggested fix
-import { promises as fs } from 'fs';
+import { promises as fs, realpathSync } from 'fs';
@@
       const json = JSON.parse(result.stdout);
       expect(json.name).toBe('alpha');
       expect(typeof json.path).toBe('string');
-      expect(json.path.endsWith('alpha')).toBe(true);
+      const expectedPath = realpathSync.native(path.join(changesDir, 'alpha'));
+      const actualPath = realpathSync.native(json.path);
+      expect(actualPath).toBe(expectedPath);

As per coding guidelines: "When asserting existing filesystem paths as identities in tests, canonicalize both actual and expected paths using fs.realpathSync.native()".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/commands/agent-helpers.test.ts` around lines 138 - 146, The test
currently checks path identity with a suffix check; update the assertion in the
'--json with a named change emits structured payload' spec to canonicalize both
actual and expected paths using fs.realpathSync.native before comparing.
Specifically, call fs.realpathSync.native(json.path) and
fs.realpathSync.native(path.join(tempDir, 'alpha')) (or the equivalent expected
path used in the test) and assert they are strictly equal; keep the existing
typeof and name assertions unchanged and import/require fs and path if not
already present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/cli.md`:
- Around line 880-882: The fenced code blocks containing command synopses like
"openspec resolve-change [name] [options]", "openspec next-artifact --change
<name> [options]", and "openspec mark-task-done <task-id> --change <name>
[options]" are missing language identifiers; update each triple-backtick fence
that wraps these command lines to include a language tag (e.g., ```bash) so
markdownlint MD040 is satisfied. Locate the three command synopsis blocks (and
the analogous blocks containing the same commands elsewhere) and prepend "bash"
to the opening ``` of each fenced code block.

In `@src/core/templates/workflows/apply-change.ts`:
- Line 39: The template string in apply-change.ts uses an incorrect command
token `openspec-continue-change`; update the blocked-state wording to use the
proper CLI command form (for example replace `openspec-continue-change` with
`openspec continue-change` or the intended alias) so it matches other template
commands and avoids misleading users; locate the string near where `state`:
`"blocked"` is described and update that literal to the corrected command text.

In `@src/core/templates/workflows/propose.ts`:
- Around line 43-67: Update the template text to consistently reference the
payload returned by openspec next-artifact instead of referring to openspec
instructions; specifically, change any guidance that tells users to follow
fields from "openspec instructions" to read and act on the JSON payload from
"openspec next-artifact" (fields: artifactId, resolvedOutputPath, template,
instruction, context, rules, dependencies), ensure steps that list using the
TodoWrite tool, AskUserQuestion tool and the loop logic all explicitly reference
the next-artifact response structure, and apply the same replacement for the
other occurrence noted around lines 138-162 so there is no mention of "openspec
instructions" anywhere in the propose workflow template.

---

Nitpick comments:
In `@test/commands/agent-helpers.test.ts`:
- Around line 138-146: The test currently checks path identity with a suffix
check; update the assertion in the '--json with a named change emits structured
payload' spec to canonicalize both actual and expected paths using
fs.realpathSync.native before comparing. Specifically, call
fs.realpathSync.native(json.path) and fs.realpathSync.native(path.join(tempDir,
'alpha')) (or the equivalent expected path used in the test) and assert they are
strictly equal; keep the existing typeof and name assertions unchanged and
import/require fs and path if not already present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a833d91-893c-404f-bc41-9bc2c0437d0e

📥 Commits

Reviewing files that changed from the base of the PR and between fd92ccc and b85d8b4.

📒 Files selected for processing (14)
  • .changeset/agent-helpers.md
  • docs/cli.md
  • src/cli/index.ts
  • src/commands/workflow/index.ts
  • src/commands/workflow/instructions.ts
  • src/commands/workflow/mark-task-done.ts
  • src/commands/workflow/next-artifact.ts
  • src/commands/workflow/resolve-change.ts
  • src/commands/workflow/shared.ts
  • src/core/completions/command-registry.ts
  • src/core/templates/workflows/apply-change.ts
  • src/core/templates/workflows/propose.ts
  • test/commands/agent-helpers.test.ts
  • test/core/templates/skill-templates-parity.test.ts

Comment thread docs/cli.md Outdated
Comment thread src/core/templates/workflows/apply-change.ts Outdated
Comment thread src/core/templates/workflows/propose.ts
@jussirantala jussirantala changed the title feat(cli): add resolve-change, next-artifact, mark-task-done — cut tokens + turns for agent skills feat(cli): add openspec agent command group — cut tokens + turns for AI skills May 27, 2026
- docs/cli.md: add 'bash' language tag to the three new agent subcommand
  synopsis fences (markdownlint MD040). Lines 880, 929, 992.
- src/core/templates/workflows/propose.ts: replace stale 'openspec
  instructions' guideline reference with 'openspec agent next-artifact'
  to match the refactored loop. Both skill + command variants.
- src/core/templates/workflows/apply-change.ts: disambiguate the
  blocked-state message — 'suggest openspec-continue-change' →
  'suggest the `openspec-continue-change` skill'. Skill name, not CLI
  command form.
- Refresh template parity hashes for the three affected functions.
@jussirantala jussirantala marked this pull request as ready for review May 27, 2026 09:53
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.

1 participant