agile-context-engineering 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/LICENSE +51 -51
  3. package/README.md +332 -324
  4. package/agents/ace-product-owner.md +1 -1
  5. package/agents/ace-research-synthesizer.md +228 -228
  6. package/agents/ace-wiki-mapper.md +449 -445
  7. package/bin/install.js +60 -64
  8. package/hooks/ace-check-update.js +70 -62
  9. package/hooks/ace-statusline.js +89 -89
  10. package/package.json +5 -4
  11. package/shared/lib/ace-core.js +308 -0
  12. package/shared/lib/ace-core.test.js +308 -0
  13. package/shared/lib/ace-github.js +753 -0
  14. package/shared/lib/ace-story.js +400 -0
  15. package/shared/lib/ace-story.test.js +250 -0
  16. package/{agile-context-engineering → shared}/utils/questioning.xml +110 -110
  17. package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
  18. package/skills/execute-story/SKILL.md +110 -0
  19. package/skills/execute-story/script.js +305 -0
  20. package/skills/execute-story/script.test.js +261 -0
  21. package/skills/execute-story/walkthrough-template.xml +255 -0
  22. package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +1219 -1219
  23. package/skills/help/SKILL.md +69 -0
  24. package/skills/help/script.js +318 -0
  25. package/skills/help/script.test.js +183 -0
  26. package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +540 -540
  27. package/skills/init-coding-standards/SKILL.md +72 -0
  28. package/skills/init-coding-standards/script.js +59 -0
  29. package/skills/init-coding-standards/script.test.js +70 -0
  30. package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +381 -386
  31. package/skills/map-cross-cutting/SKILL.md +89 -0
  32. package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +197 -197
  33. package/skills/map-cross-cutting/workflow.xml +330 -0
  34. package/skills/map-guide/SKILL.md +89 -0
  35. package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +137 -137
  36. package/skills/map-guide/workflow.xml +320 -0
  37. package/skills/map-pattern/SKILL.md +89 -0
  38. package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +159 -159
  39. package/skills/map-pattern/workflow.xml +331 -0
  40. package/skills/map-story/SKILL.md +127 -0
  41. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +115 -115
  42. package/skills/map-story/templates/guide.xml +137 -0
  43. package/skills/map-story/templates/pattern.xml +159 -0
  44. package/skills/map-story/templates/system-cross-cutting.xml +197 -0
  45. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +381 -381
  46. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/walkthrough.xml +255 -255
  47. package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +1046 -1046
  48. package/skills/map-subsystem/SKILL.md +111 -0
  49. package/skills/map-subsystem/script.js +60 -0
  50. package/skills/map-subsystem/script.test.js +68 -0
  51. package/skills/map-subsystem/templates/decizions.xml +115 -0
  52. package/skills/map-subsystem/templates/guide.xml +137 -0
  53. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +174 -174
  54. package/skills/map-subsystem/templates/pattern.xml +159 -0
  55. package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
  56. package/skills/map-subsystem/templates/system.xml +381 -0
  57. package/skills/map-subsystem/templates/walkthrough.xml +255 -0
  58. package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +15 -20
  59. package/skills/map-sys-doc/SKILL.md +90 -0
  60. package/skills/map-sys-doc/system.xml +381 -0
  61. package/skills/map-sys-doc/workflow.xml +336 -0
  62. package/skills/map-system/SKILL.md +85 -0
  63. package/skills/map-system/script.js +84 -0
  64. package/skills/map-system/script.test.js +73 -0
  65. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +254 -254
  66. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/wiki-readme.xml +296 -296
  67. package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +11 -16
  68. package/skills/map-walkthrough/SKILL.md +92 -0
  69. package/skills/map-walkthrough/walkthrough.xml +255 -0
  70. package/skills/plan-backlog/SKILL.md +75 -0
  71. package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +231 -231
  72. package/skills/plan-backlog/script.js +136 -0
  73. package/skills/plan-backlog/script.test.js +83 -0
  74. package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +13 -21
  75. package/skills/plan-feature/SKILL.md +76 -0
  76. package/skills/plan-feature/script.js +148 -0
  77. package/skills/plan-feature/script.test.js +80 -0
  78. package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +1487 -1495
  79. package/skills/plan-product-vision/SKILL.md +75 -0
  80. package/skills/plan-product-vision/script.js +60 -0
  81. package/skills/plan-product-vision/script.test.js +69 -0
  82. package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +4 -9
  83. package/skills/plan-story/SKILL.md +116 -0
  84. package/skills/plan-story/script.js +326 -0
  85. package/skills/plan-story/script.test.js +240 -0
  86. package/skills/plan-story/story-template.xml +451 -0
  87. package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1285 -944
  88. package/skills/research-external-solution/SKILL.md +107 -0
  89. package/skills/research-external-solution/script.js +238 -0
  90. package/skills/research-external-solution/script.test.js +134 -0
  91. package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +4 -6
  92. package/skills/research-integration-solution/SKILL.md +98 -0
  93. package/skills/research-integration-solution/script.js +231 -0
  94. package/skills/research-integration-solution/script.test.js +134 -0
  95. package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +3 -5
  96. package/skills/research-story-wiki/SKILL.md +92 -0
  97. package/skills/research-story-wiki/script.js +231 -0
  98. package/skills/research-story-wiki/script.test.js +138 -0
  99. package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +3 -5
  100. package/skills/research-technical-solution/SKILL.md +103 -0
  101. package/skills/research-technical-solution/script.js +231 -0
  102. package/skills/research-technical-solution/script.test.js +134 -0
  103. package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +3 -5
  104. package/skills/review-story/SKILL.md +100 -0
  105. package/skills/review-story/script.js +257 -0
  106. package/skills/review-story/script.test.js +169 -0
  107. package/skills/review-story/story-template.xml +451 -0
  108. package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +279 -281
  109. package/skills/update/SKILL.md +53 -0
  110. package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +12 -13
  111. package/agile-context-engineering/src/ace-tools.js +0 -2881
  112. package/agile-context-engineering/src/ace-tools.test.js +0 -1089
  113. package/agile-context-engineering/templates/_command.md +0 -54
  114. package/agile-context-engineering/templates/_workflow.xml +0 -17
  115. package/agile-context-engineering/templates/config.json +0 -0
  116. package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
  117. package/commands/ace/execute-story.md +0 -138
  118. package/commands/ace/help.md +0 -93
  119. package/commands/ace/init-coding-standards.md +0 -83
  120. package/commands/ace/map-story.md +0 -165
  121. package/commands/ace/map-subsystem.md +0 -140
  122. package/commands/ace/map-system.md +0 -92
  123. package/commands/ace/map-walkthrough.md +0 -127
  124. package/commands/ace/plan-backlog.md +0 -83
  125. package/commands/ace/plan-feature.md +0 -89
  126. package/commands/ace/plan-product-vision.md +0 -81
  127. package/commands/ace/plan-story.md +0 -159
  128. package/commands/ace/research-external-solution.md +0 -138
  129. package/commands/ace/research-integration-solution.md +0 -135
  130. package/commands/ace/research-story-wiki.md +0 -116
  131. package/commands/ace/research-technical-solution.md +0 -147
  132. package/commands/ace/review-story.md +0 -109
  133. package/commands/ace/update.md +0 -56
  134. /package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +0 -0
  135. /package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +0 -0
  136. /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +0 -0
  137. /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +0 -0
  138. /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +0 -0
  139. /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +0 -0
  140. /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +0 -0
  141. /package/{agile-context-engineering/workflows/map-walkthrough.xml → skills/map-walkthrough/workflow.xml} +0 -0
  142. /package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +0 -0
  143. /package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +0 -0
  144. /package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +0 -0
  145. /package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +0 -0
  146. /package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +0 -0
  147. /package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +0 -0
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: plan-product-vision
3
+ description: Create or update the product vision through architecture-aware questioning and guided writing
4
+ argument-hint: "[optional: context='PRD, specs, or notes to build on']"
5
+ disable-model-invocation: true
6
+ allowed-tools: Read, Bash, Write, Task, AskUserQuestion
7
+ model: opus
8
+ effort: high
9
+ ---
10
+
11
+ # Plan Product Vision
12
+
13
+ Create or update the product vision through architecture-aware questioning and guided writing.
14
+
15
+ ## When to Use
16
+
17
+ - During `/ace:help` — as part of initial project setup
18
+ - After `/ace:map-system` — once codebase is mapped, leverage architecture context
19
+ - Anytime — to create or refresh the product vision for a project
20
+ - Starting a new project and want to define the product vision (greenfield or brownfield)
21
+ - Existing product vision is outdated or missing
22
+ - System architecture has been mapped and you want to align vision with subsystem capabilities
23
+ - Pivoting the product direction and need to rewrite the vision
24
+
25
+ ## Input
26
+
27
+ ### Optional
28
+
29
+ - **`context`** — Existing PRD, specs, or notes to use as a starting point. Will be refined through the interview process, not used as-is.
30
+
31
+ ## Environment Context (preprocessed)
32
+
33
+ !`node "${CLAUDE_SKILL_DIR}/script.js" init "$ARGUMENTS" 2>/dev/null`
34
+
35
+ ## Supporting Resources
36
+
37
+ Read ALL of these before starting the workflow:
38
+
39
+ - **Workflow**: Read [workflow.xml](workflow.xml) — complete orchestration process with all steps
40
+ - **Product vision template**: Read [product-vision-template.xml](product-vision-template.xml) — output format for the product vision
41
+ - **Questioning guide**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/questioning.xml` — deep questioning techniques
42
+ - **UI formatting**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md` — ACE output formatting rules
43
+
44
+ ## Process
45
+
46
+ Use the `ace-product-owner` agent for requirements gathering, deep questioning, and vision specification.
47
+
48
+ The Environment Context above contains the preprocessed INIT JSON — use it directly instead of running the init script manually. The workflow's step 1 setup can skip the init bash call since that data is already available.
49
+
50
+ Read all supporting resources listed above, then execute the workflow defined in [workflow.xml](workflow.xml) end-to-end. Preserve all workflow gates (validation, approvals, commits).
51
+
52
+ ## Artifacts
53
+
54
+ ```
55
+ .docs/product/product-vision.md
56
+ ```
57
+
58
+ ## Example Usage
59
+
60
+ ```
61
+ # Create a new product vision from scratch
62
+ /ace:plan-product-vision
63
+
64
+ # Create vision with existing PRD as seed
65
+ /ace:plan-product-vision context="path/to/existing-prd.md"
66
+
67
+ # Update vision after architecture changes
68
+ /ace:plan-product-vision
69
+ ```
70
+
71
+ ## Next Steps
72
+
73
+ - `/ace:init-coding-standards` — Establish coding standards for the project
74
+ - `/ace:map-system` — Map codebase structure and architecture
75
+ - `/ace:help` — Check project initialization status and next steps
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * plan-product-vision skill script — Entry point for ace-tools operations
5
+ * needed by the plan-product-vision skill.
6
+ *
7
+ * Subcommands:
8
+ * init [args] Environment detection for plan-product-vision workflow
9
+ *
10
+ * Usage: node script.js <subcommand> [args] [--raw]
11
+ */
12
+
13
+ const {
14
+ loadConfig, pathExists, resolveModel,
15
+ detectBrownfieldStatus, output, error,
16
+ } = require('../../shared/lib/ace-core');
17
+
18
+ // ─── CLI Dispatch ────────────────────────────────────────────────────────────
19
+
20
+ const cwd = process.cwd();
21
+ const args = process.argv.slice(2);
22
+ const raw = args.includes('--raw');
23
+ const cmd = args[0];
24
+
25
+ switch (cmd) {
26
+ case 'init':
27
+ cmdInit(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
28
+ break;
29
+ default:
30
+ error(`Unknown command: ${cmd}\nAvailable: init`);
31
+ }
32
+
33
+ // ─── Init: Plan Product Vision ──────────────────────────────────────────────
34
+
35
+ function cmdInit(cwd, raw) {
36
+ const config = loadConfig(cwd);
37
+ const brownfield = detectBrownfieldStatus(cwd);
38
+
39
+ const result = {
40
+ // Models
41
+ product_owner_model: resolveModel(cwd, 'ace-product-owner'),
42
+
43
+ // Config
44
+ commit_docs: config.commit_docs,
45
+
46
+ // Existing state
47
+ has_product_vision: pathExists(cwd, '.docs/product/product-vision.md'),
48
+
49
+ // Brownfield detection
50
+ ...brownfield,
51
+
52
+ // Architecture context
53
+ has_system_architecture: pathExists(cwd, '.docs/wiki/system-wide/system-architecture.md'),
54
+
55
+ // Git state
56
+ has_git: pathExists(cwd, '.git'),
57
+ };
58
+
59
+ output(result, raw);
60
+ }
@@ -0,0 +1,69 @@
1
+ const { describe, it, before, after } = require('node:test');
2
+ const assert = require('node:assert');
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ const SCRIPT = path.join(__dirname, 'script.js');
9
+
10
+ function createTestProject() {
11
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
12
+
13
+ const aceDir = path.join(tmpDir, '.ace');
14
+ fs.mkdirSync(aceDir, { recursive: true });
15
+ fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
16
+ version: '0.1.0',
17
+ projectName: 'test-project',
18
+ model_profile: 'quality',
19
+ commit_docs: true,
20
+ github: { enabled: false },
21
+ }, null, 2));
22
+
23
+ return tmpDir;
24
+ }
25
+
26
+ function runScript(subcommand, args, cwd) {
27
+ return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
28
+ cwd,
29
+ encoding: 'utf-8',
30
+ timeout: 10000,
31
+ });
32
+ }
33
+
34
+ function cleanup(tmpDir) {
35
+ fs.rmSync(tmpDir, { recursive: true, force: true });
36
+ }
37
+
38
+ describe('plan-product-vision script', () => {
39
+ it('errors on unknown command', () => {
40
+ assert.throws(() => {
41
+ execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
42
+ });
43
+ });
44
+
45
+ describe('init', () => {
46
+ let tmpDir;
47
+
48
+ before(() => { tmpDir = createTestProject(); });
49
+ after(() => { cleanup(tmpDir); });
50
+
51
+ it('init returns valid JSON', () => {
52
+ const result = JSON.parse(runScript('init', '', tmpDir));
53
+ assert.ok(typeof result === 'object');
54
+ assert.ok(result.product_owner_model, 'should have product_owner_model');
55
+ assert.strictEqual(typeof result.commit_docs, 'boolean');
56
+ assert.strictEqual(typeof result.has_git, 'boolean');
57
+ assert.strictEqual(typeof result.is_brownfield, 'boolean');
58
+ assert.strictEqual(typeof result.has_product_vision, 'boolean');
59
+ assert.strictEqual(typeof result.has_system_architecture, 'boolean');
60
+ });
61
+
62
+ it('returns brownfield detection fields', () => {
63
+ const result = JSON.parse(runScript('init', '', tmpDir));
64
+ assert.strictEqual(typeof result.is_brownfield, 'boolean');
65
+ assert.strictEqual(typeof result.is_greenfield, 'boolean');
66
+ assert.strictEqual(result.is_brownfield, !result.is_greenfield);
67
+ });
68
+ });
69
+ });
@@ -24,18 +24,13 @@
24
24
  <step name="setup" order="1">
25
25
  **MANDATORY FIRST STEP — Execute environment detection before any user interaction:**
26
26
 
27
- ```bash
28
- INIT=$(node ~/.claude/agile-context-engineering/src/ace-tools.js init product-vision)
29
- ```
27
+ INIT is available from the preprocessed Environment Context above — do NOT re-run init.
30
28
 
31
- Parse JSON for: `product_owner_model`, `commit_docs`, `has_product_vision`,
29
+ Parse INIT JSON for: `product_owner_model`, `commit_docs`, `has_product_vision`,
32
30
  `is_brownfield`, `is_greenfield`, `has_existing_code`, `has_package_file`,
33
31
  `has_system_architecture`, `has_git`.
34
32
 
35
- Also resolve the product owner model for spawning the writing agent:
36
- ```bash
37
- PO_MODEL=$(node ~/.claude/agile-context-engineering/src/ace-tools.js resolve-model ace-product-owner --raw)
38
- ```
33
+ PO_MODEL is available from INIT.product_owner_model do NOT re-run resolve-model.
39
34
 
40
35
  Display stage banner:
41
36
 
@@ -223,7 +218,7 @@
223
218
  {paste the context brief here}
224
219
 
225
220
  **Instructions:**
226
- 1. Read the product vision template: ~/.claude/agile-context-engineering/templates/product/product-vision.xml
221
+ 1. Read the product vision template: ${CLAUDE_SKILL_DIR}/product-vision-template.xml
227
222
  2. Write `.docs/product/product-vision.md` following the template structure and guidelines
228
223
  3. Do not compress — capture everything from the context brief
229
224
  4. Write the file using the Write tool
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: plan-story
3
+ description: Plan a story through deep questioning to create CRYSTAL-CLEAR acceptance criteria with ZERO assumptions, then dispatch wiki research, external analysis, integration analysis, and technical solution design. Use this whenever the user wants to plan a story, specify a story, create acceptance criteria, refine story requirements, or take a story stub and turn it into a full specification — whether from a feature stub, GitHub issue, inline text, or from scratch.
4
+ argument-hint: "[story=<file-path|github-url>] [text=<inline-description>] [external-codebase=<source-path|github-url>] [external-docs=<weblink|filepath>] [lib-docs=<weblinks-and-filepaths>]"
5
+ disable-model-invocation: true
6
+ allowed-tools: Read, Bash, Write, Edit, AskUserQuestion, Glob, Grep, Agent
7
+ model: opus
8
+ effort: high
9
+ ---
10
+
11
+ # Plan Story
12
+
13
+ Orchestrate story specification through deep questioning and multi-pass research.
14
+
15
+ ## When to Use
16
+
17
+ - After `/ace:plan-feature` — when a feature's story breakdown exists with stub story files
18
+ - To create or refine a story specification from a description or GitHub issue
19
+ - When a story stub needs formal INVEST-compliant specification
20
+ - When an existing story needs refinement due to scope changes or AC gaps
21
+ - When you have a new story idea (text or no input) and need it placed in the backlog
22
+
23
+ ## Input
24
+
25
+ ### Optional
26
+
27
+ - **`story`** — Story source:
28
+ - **File path**: Path to a markdown file containing the story seed (typically from plan-feature, or any markdown with a description)
29
+ - **GitHub URL or issue number**: e.g., `story=#95` or `story=https://github.com/owner/repo/issues/95`
30
+ - When omitted, enters "new story" mode and guides the user through backlog placement.
31
+
32
+ - **`text`** — Inline story description to seed the planning session. Used when no story file exists yet — the text becomes the initial description and the workflow asks where in the backlog this story belongs. Ignored if `story` is provided.
33
+
34
+ - **`external-codebase`** — Path or GitHub repo to an external system to analyze. Triggers pass 3 automatically.
35
+
36
+ - **`external-docs`** — Link or filepath to external system documentation. Only used with external-codebase.
37
+
38
+ - **`lib-docs`** — Space-separated weblinks and/or filepaths to library/API documentation. Injected into the Relevant Wiki section after pass 2.
39
+
40
+ ## Environment Context (preprocessed)
41
+
42
+ !`node "${CLAUDE_SKILL_DIR}/script.js" init "$ARGUMENTS" 2>/dev/null`
43
+
44
+ ## Supporting Resources
45
+
46
+ Read ALL of these before starting the workflow:
47
+
48
+ - **Workflow**: Read [workflow.xml](workflow.xml) — complete orchestration process with all steps
49
+ - **Story template**: Read [story-template.xml](story-template.xml) — output format for the story specification
50
+ - **Questioning guide**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/questioning.xml` — deep questioning techniques
51
+ - **UI formatting**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md` — ACE output formatting rules
52
+
53
+ ## Process
54
+
55
+ Use the `ace-product-owner` agent for requirements gathering, deep questioning, and story specification.
56
+
57
+ The Environment Context above contains the preprocessed INIT JSON — use it directly instead of running the init script manually. The workflow's step 1 setup can skip the init bash call since that data is already available.
58
+
59
+ Read all supporting resources listed above, then execute the workflow defined in [workflow.xml](workflow.xml) end-to-end. Preserve all workflow gates (validation, approvals, commits).
60
+
61
+ **Context Window Protection:**
62
+ Passes 2-5 run as background agents. Each agent writes output directly to files.
63
+ The orchestrator MUST NOT call TaskOutput on any background agent.
64
+ Only track when each pass finishes to start the next one.
65
+
66
+ **Pass execution order:**
67
+ Pass 2 + Pass 3 (if applicable) → wait → Pass 4 → wait → Pass 5 → wait → done
68
+
69
+ ## Artifacts
70
+
71
+ ```
72
+ .ace/artifacts/product/<epic>/<feature>/<story>/
73
+ ├── <story>.md # Story specification (passes 1, 2, 5)
74
+ ├── external-analysis.md # OPTIONAL (pass 3)
75
+ └── integration-analysis.md # Integration analysis (pass 4)
76
+ ```
77
+
78
+ ## Example Usage
79
+
80
+ ```
81
+ # New story from inline text — workflow asks for backlog placement
82
+ /ace:plan-story text="User can reset their password via email link"
83
+
84
+ # New story with no input — workflow asks title then placement
85
+ /ace:plan-story
86
+
87
+ # From a plan-feature stub file
88
+ /ace:plan-story story=.ace/artifacts/product/e1-auth/f3-oauth/s1-buttons/s1-buttons.md
89
+
90
+ # From a loose markdown file (no ACE structure yet — triggers placement flow)
91
+ /ace:plan-story story=notes/password-reset-idea.md
92
+
93
+ # From a GitHub issue
94
+ /ace:plan-story story=https://github.com/owner/repo/issues/95
95
+
96
+ # With external system analysis
97
+ /ace:plan-story \
98
+ story=.ace/artifacts/product/e1-charts/f2-rendering/s3-canvas/s3-canvas.md \
99
+ external-codebase=src/external/lightweight-charts/ \
100
+ external-docs=https://tradingview.github.io/lightweight-charts/
101
+
102
+ # With library documentation
103
+ /ace:plan-story \
104
+ story=.ace/artifacts/product/e1-charts/f2-rendering/s3-canvas/s3-canvas.md \
105
+ lib-docs="https://docs.some-lib.io/api src/vendor/some-lib/README.md"
106
+
107
+ # With just an issue number
108
+ /ace:plan-story story=#95
109
+ ```
110
+
111
+ ## Next Steps
112
+
113
+ - `/ace:execute-story story=...` — Execute the story implementation
114
+ - `/ace:plan-story story=...` — Plan the next story in the feature
115
+ - `/ace:verify-story story=...` — Verify a completed story
116
+ - `/ace:help` — Check project initialization status
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * plan-story skill script — Entry point for all ace-tools operations
5
+ * needed by the plan-story skill.
6
+ *
7
+ * Subcommands:
8
+ * init [story-param] Environment detection for plan-story workflow
9
+ * update-state story=X status=Y Update story status across files
10
+ * sync-github repo=X story_file=Y Sync story/feature to GitHub
11
+ * resolve-model <agent-type> Get model for agent based on profile
12
+ *
13
+ * Usage: node script.js <subcommand> [args] [--raw]
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ const {
20
+ loadConfig, pathExists, safeReadFile, generateSlug, resolveModel,
21
+ detectBrownfieldStatus, loadSettings, execCommand,
22
+ output, error, parseKeyValueArgs,
23
+ } = require('../../shared/lib/ace-core');
24
+
25
+ const {
26
+ classifyStoryParam, extractStoryMetadata, extractStoryRequirements,
27
+ extractIssueNumber, extractIssueNumberFromFile, computeStoryPaths,
28
+ updateState,
29
+ } = require('../../shared/lib/ace-story');
30
+
31
+ const { syncStory, resolveFields, createIssue } = require('../../shared/lib/ace-github');
32
+
33
+ // ─── CLI Dispatch ────────────────────────────────────────────────────────────
34
+
35
+ const cwd = process.cwd();
36
+ const args = process.argv.slice(2);
37
+ const raw = args.includes('--raw');
38
+ const cmd = args[0];
39
+
40
+ switch (cmd) {
41
+ case 'init':
42
+ cmdInit(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
43
+ break;
44
+ case 'update-state':
45
+ updateState(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
46
+ break;
47
+ case 'sync-github':
48
+ syncStory(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
49
+ break;
50
+ case 'resolve-model': {
51
+ const agentType = args[1];
52
+ if (!agentType) error('resolve-model requires agent-type argument');
53
+ const model = resolveModel(cwd, agentType);
54
+ output({ model, agent: agentType }, raw, model);
55
+ break;
56
+ }
57
+ case 'generate-slug': {
58
+ const text = args.slice(1).filter(a => a !== '--raw').join(' ');
59
+ if (!text) error('generate-slug requires text argument');
60
+ const slug = generateSlug(text);
61
+ output({ slug }, raw, slug);
62
+ break;
63
+ }
64
+ case 'resolve-fields':
65
+ resolveFields(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
66
+ break;
67
+ case 'create-issue':
68
+ createIssue(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
69
+ break;
70
+ default:
71
+ error(`Unknown command: ${cmd}\nAvailable: init, update-state, sync-github, resolve-model, generate-slug, resolve-fields, create-issue`);
72
+ }
73
+
74
+ // ─── Init: Plan Story ────────────────────────────────────────────────────────
75
+
76
+ /**
77
+ * Environment detection for the plan-story workflow.
78
+ *
79
+ * Detects: git, gh CLI, GitHub project, brownfield status, wiki state,
80
+ * product artifacts, and story source/content/metadata.
81
+ *
82
+ * Supports three input modes:
83
+ * - story param provided → loads existing file or fetches GitHub issue
84
+ * - text param provided (no story) → uses inline text as seed description
85
+ * - neither → new story mode, workflow handles placement
86
+ *
87
+ * initArgs: array of arguments — either a single positional path/URL,
88
+ * or key=value pairs (story=X text=Y)
89
+ */
90
+ function cmdInit(cwd, raw, initArgs) {
91
+ const config = loadConfig(cwd);
92
+ const brownfield = detectBrownfieldStatus(cwd);
93
+
94
+ // ── Environment detection ──
95
+ const has_git = pathExists(cwd, '.git');
96
+ const has_gh_cli = (() => {
97
+ try {
98
+ const { execSync } = require('child_process');
99
+ execSync('gh --version', { stdio: 'pipe' });
100
+ return true;
101
+ } catch { return false; }
102
+ })();
103
+ const github_project = loadSettings(cwd).github_project;
104
+
105
+ // Wiki detection
106
+ const wikiSystemDir = '.docs/wiki/system-wide';
107
+ const has_wiki_system_wide = pathExists(cwd, wikiSystemDir);
108
+ const wikiSubsystemsDir = '.docs/wiki/subsystems';
109
+ const has_wiki_subsystems = pathExists(cwd, wikiSubsystemsDir);
110
+ let wiki_subsystem_names = [];
111
+ if (has_wiki_subsystems) {
112
+ try {
113
+ const entries = fs.readdirSync(path.join(cwd, wikiSubsystemsDir), { withFileTypes: true });
114
+ wiki_subsystem_names = entries.filter(e => e.isDirectory()).map(e => e.name);
115
+ } catch {}
116
+ }
117
+ const has_wiki = has_wiki_system_wide || has_wiki_subsystems;
118
+
119
+ // ── Parse input: support both positional and key=value forms ──
120
+ // Positional: script.js init path/to/story.md
121
+ // Key-value: script.js init story=path/to/story.md text="some description"
122
+ const hasKeyValue = initArgs.some(a => a.includes('='));
123
+ let resolvedStoryParam = null;
124
+ let textParam = null;
125
+
126
+ if (hasKeyValue) {
127
+ const kvArgs = parseKeyValueArgs(initArgs);
128
+ resolvedStoryParam = kvArgs.story || null;
129
+ textParam = kvArgs.text || null;
130
+ } else if (initArgs.length > 0) {
131
+ // Positional: treat entire arg list as the story param
132
+ resolvedStoryParam = initArgs.join(' ');
133
+ }
134
+
135
+ // ── Base result (shared across all modes) ──
136
+ const baseResult = {
137
+ product_owner_model: resolveModel(cwd, 'ace-product-owner'),
138
+ commit_docs: config.commit_docs,
139
+ has_git, has_gh_cli, github_project,
140
+ ...brownfield,
141
+ has_wiki, has_wiki_system_wide, has_wiki_subsystems, wiki_subsystem_names,
142
+ has_product_vision: pathExists(cwd, '.docs/product/product-vision.md'),
143
+ has_product_backlog: pathExists(cwd, '.ace/artifacts/product/product-backlog.md'),
144
+ };
145
+
146
+ // ── Mode 1: No story param and no text → new story mode ──
147
+ if (!resolvedStoryParam && !textParam) {
148
+ output({
149
+ ...baseResult,
150
+ story_source: 'new',
151
+ story_valid: true,
152
+ story_error: null,
153
+ story_content: null,
154
+ story: { id: null, title: null, status: null, size: null, issue_number: null },
155
+ feature: { id: null, title: null, issue_number: null },
156
+ epic: { id: null, title: null },
157
+ user_story: null, description: null, acceptance_criteria_count: 0,
158
+ paths: null,
159
+ has_external_analysis: false, has_integration_analysis: false,
160
+ has_feature_file: false, has_story_file: false,
161
+ }, raw);
162
+ return;
163
+ }
164
+
165
+ // ── Mode 2: Text param only → use text as seed description ──
166
+ if (!resolvedStoryParam && textParam) {
167
+ output({
168
+ ...baseResult,
169
+ story_source: 'text',
170
+ story_valid: true,
171
+ story_error: null,
172
+ story_content: textParam,
173
+ story: { id: null, title: null, status: null, size: null, issue_number: null },
174
+ feature: { id: null, title: null, issue_number: null },
175
+ epic: { id: null, title: null },
176
+ user_story: null, description: textParam, acceptance_criteria_count: 0,
177
+ paths: null,
178
+ has_external_analysis: false, has_integration_analysis: false,
179
+ has_feature_file: false, has_story_file: false,
180
+ }, raw);
181
+ return;
182
+ }
183
+
184
+ // ── Mode 3: Story param provided → classify and load ──
185
+ const classified = classifyStoryParam(resolvedStoryParam);
186
+
187
+ // Invalid param
188
+ if (classified.type === null || classified.type === 'invalid') {
189
+ output({
190
+ ...baseResult,
191
+ story_source: null,
192
+ story_valid: false,
193
+ story_error: classified.reason || 'No story parameter provided',
194
+ story_content: null,
195
+ story: { id: null, title: null, status: null, size: null, issue_number: null },
196
+ feature: { id: null, title: null, issue_number: null },
197
+ epic: { id: null, title: null },
198
+ user_story: null, description: null, acceptance_criteria_count: 0,
199
+ paths: null,
200
+ has_external_analysis: false, has_integration_analysis: false,
201
+ has_feature_file: false, has_story_file: false,
202
+ }, raw);
203
+ return;
204
+ }
205
+
206
+ // ── Load story content ──
207
+ let storyContent = null;
208
+ let storySource = classified.type === 'file' ? 'file' : 'github';
209
+ let storyError = null;
210
+ let storyFilePath = null;
211
+
212
+ if (classified.type === 'file') {
213
+ const resolvedPath = path.isAbsolute(classified.filePath)
214
+ ? classified.filePath
215
+ : path.join(cwd, classified.filePath);
216
+ if (!pathExists(cwd, classified.filePath)) {
217
+ storyError = `Story file not found: ${classified.filePath}`;
218
+ } else {
219
+ storyContent = safeReadFile(resolvedPath);
220
+ storyFilePath = classified.filePath;
221
+ if (!storyContent) storyError = `Could not read story file: ${classified.filePath}`;
222
+ }
223
+ } else {
224
+ // github-url or issue-number
225
+ if (!has_gh_cli) {
226
+ storyError = 'GitHub CLI (gh) not installed. Cannot fetch GitHub issues.';
227
+ } else {
228
+ const repo = classified.repo || (github_project.repo || null);
229
+ if (!repo) {
230
+ storyError = 'No repository configured. Provide a full GitHub URL or configure github_project.repo in settings.';
231
+ } else {
232
+ const ghResult = execCommand(
233
+ `gh issue view ${classified.issueNumber} --repo ${repo} --json title,body,labels,state`,
234
+ cwd
235
+ );
236
+ if (!ghResult) {
237
+ storyError = `Could not fetch GitHub issue #${classified.issueNumber} from ${repo}.`;
238
+ } else {
239
+ try {
240
+ const issue = JSON.parse(ghResult);
241
+ storyContent = issue.body || '';
242
+ if (storyContent && !storyContent.match(/^#\s+/m)) {
243
+ storyContent = `# ${issue.title}\n\n${storyContent}`;
244
+ }
245
+ } catch {
246
+ storyError = `Failed to parse GitHub issue response for #${classified.issueNumber}.`;
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ // ── Extract metadata & requirements ──
254
+ const metadata = extractStoryMetadata(storyContent);
255
+ const requirements = extractStoryRequirements(storyContent);
256
+
257
+ // ── Compute paths ──
258
+ let paths = null;
259
+ let has_story_file = false;
260
+
261
+ if (storyFilePath) {
262
+ const resolvedPath = path.isAbsolute(storyFilePath)
263
+ ? storyFilePath
264
+ : path.join(cwd, storyFilePath);
265
+ const storyDir = path.dirname(resolvedPath);
266
+ const relStoryDir = path.relative(cwd, storyDir).replace(/\\/g, '/');
267
+ const storySlug = path.basename(storyDir);
268
+ const featureDir = path.dirname(storyDir);
269
+ const relFeatureDir = path.relative(cwd, featureDir).replace(/\\/g, '/');
270
+ const featureSlug = path.basename(featureDir);
271
+
272
+ paths = {
273
+ epic_slug: null,
274
+ feature_slug: featureSlug,
275
+ story_slug: storySlug,
276
+ story_dir: relStoryDir,
277
+ story_file: storyFilePath.replace(/\\/g, '/'),
278
+ external_analysis_file: `${relStoryDir}/external-analysis.md`,
279
+ integration_analysis_file: `${relStoryDir}/integration-analysis.md`,
280
+ feature_dir: relFeatureDir,
281
+ feature_file: `${relFeatureDir}/${featureSlug}.md`,
282
+ };
283
+ has_story_file = true;
284
+ } else if (metadata.epic.id && metadata.feature.id && metadata.id) {
285
+ paths = computeStoryPaths(
286
+ metadata.epic.id, metadata.epic.title || '',
287
+ metadata.feature.id, metadata.feature.title || '',
288
+ metadata.id, metadata.title || ''
289
+ );
290
+ has_story_file = paths ? pathExists(cwd, paths.story_file) : false;
291
+ }
292
+
293
+ // ── Check artifact existence ──
294
+ const has_external_analysis = paths ? pathExists(cwd, paths.external_analysis_file) : false;
295
+ const has_integration_analysis = paths ? pathExists(cwd, paths.integration_analysis_file) : false;
296
+ const has_feature_file = paths ? pathExists(cwd, paths.feature_file) : false;
297
+
298
+ // ── Build result ──
299
+ output({
300
+ ...baseResult,
301
+ story_source: storySource,
302
+ story_valid: storyContent !== null && storyError === null,
303
+ story_error: storyError,
304
+ story_content: storyContent,
305
+ story: {
306
+ id: metadata.id,
307
+ title: metadata.title,
308
+ status: metadata.status,
309
+ size: metadata.size,
310
+ issue_number: extractIssueNumber(metadata.link),
311
+ },
312
+ feature: {
313
+ ...metadata.feature,
314
+ issue_number: paths ? extractIssueNumberFromFile(cwd, paths.feature_file) : null,
315
+ },
316
+ epic: metadata.epic,
317
+ user_story: requirements.user_story,
318
+ description: requirements.description,
319
+ acceptance_criteria_count: requirements.acceptance_criteria_count,
320
+ paths,
321
+ has_external_analysis,
322
+ has_integration_analysis,
323
+ has_feature_file,
324
+ has_story_file,
325
+ }, raw);
326
+ }