agile-context-engineering 0.5.0 → 0.5.1

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 (102) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +7 -1
  4. package/README.md +16 -12
  5. package/agents/ace-code-discovery-analyst.md +245 -245
  6. package/agents/ace-code-integration-analyst.md +248 -248
  7. package/agents/ace-code-reviewer.md +375 -375
  8. package/agents/ace-product-owner.md +365 -361
  9. package/agents/ace-project-researcher.md +606 -606
  10. package/agents/ace-technical-application-architect.md +315 -315
  11. package/bin/install.js +587 -173
  12. package/hooks/ace-check-update.js +15 -14
  13. package/hooks/ace-statusline.js +30 -12
  14. package/hooks/hooks.json +14 -0
  15. package/package.json +3 -2
  16. package/shared/lib/ace-core.js +53 -0
  17. package/shared/lib/ace-core.test.js +308 -308
  18. package/shared/lib/ace-story.test.js +250 -250
  19. package/skills/execute-story/SKILL.md +116 -110
  20. package/skills/execute-story/script.js +13 -27
  21. package/skills/execute-story/script.test.js +261 -261
  22. package/skills/execute-story/story-template.xml +451 -451
  23. package/skills/execute-story/workflow.xml +3 -1
  24. package/skills/help/SKILL.md +71 -69
  25. package/skills/help/script.js +32 -35
  26. package/skills/help/script.test.js +183 -183
  27. package/skills/help/workflow.xml +14 -3
  28. package/skills/init-coding-standards/SKILL.md +91 -72
  29. package/skills/init-coding-standards/coding-standards-template.xml +531 -531
  30. package/skills/init-coding-standards/script.js +50 -59
  31. package/skills/init-coding-standards/script.test.js +70 -70
  32. package/skills/init-coding-standards/workflow.xml +1 -1
  33. package/skills/map-cross-cutting/SKILL.md +126 -89
  34. package/skills/map-cross-cutting/workflow.xml +1 -1
  35. package/skills/map-guide/SKILL.md +126 -89
  36. package/skills/map-guide/workflow.xml +1 -1
  37. package/skills/map-pattern/SKILL.md +125 -89
  38. package/skills/map-pattern/workflow.xml +1 -1
  39. package/skills/map-story/SKILL.md +180 -127
  40. package/skills/map-story/templates/tech-debt-index.xml +125 -125
  41. package/skills/map-story/workflow.xml +2 -2
  42. package/skills/map-subsystem/SKILL.md +155 -111
  43. package/skills/map-subsystem/script.js +51 -60
  44. package/skills/map-subsystem/script.test.js +68 -68
  45. package/skills/map-subsystem/templates/subsystem-architecture.xml +343 -343
  46. package/skills/map-subsystem/templates/subsystem-structure.xml +234 -234
  47. package/skills/map-subsystem/workflow.xml +1173 -1173
  48. package/skills/map-sys-doc/SKILL.md +125 -90
  49. package/skills/map-sys-doc/workflow.xml +1 -1
  50. package/skills/map-system/SKILL.md +103 -85
  51. package/skills/map-system/script.js +75 -84
  52. package/skills/map-system/script.test.js +73 -73
  53. package/skills/map-system/templates/system-structure.xml +177 -177
  54. package/skills/map-system/templates/testing-framework.xml +283 -283
  55. package/skills/map-system/workflow.xml +667 -667
  56. package/skills/map-walkthrough/SKILL.md +140 -92
  57. package/skills/map-walkthrough/workflow.xml +457 -457
  58. package/skills/plan-backlog/SKILL.md +93 -75
  59. package/skills/plan-backlog/script.js +121 -136
  60. package/skills/plan-backlog/script.test.js +83 -83
  61. package/skills/plan-backlog/workflow.xml +1348 -1348
  62. package/skills/plan-feature/SKILL.md +99 -76
  63. package/skills/plan-feature/feature-template.xml +361 -361
  64. package/skills/plan-feature/script.js +131 -148
  65. package/skills/plan-feature/script.test.js +80 -80
  66. package/skills/plan-feature/workflow.xml +1 -1
  67. package/skills/plan-product-vision/SKILL.md +91 -75
  68. package/skills/plan-product-vision/product-vision-template.xml +227 -227
  69. package/skills/plan-product-vision/script.js +51 -60
  70. package/skills/plan-product-vision/script.test.js +69 -69
  71. package/skills/plan-product-vision/workflow.xml +337 -337
  72. package/skills/plan-story/SKILL.md +125 -102
  73. package/skills/plan-story/script.js +18 -49
  74. package/skills/plan-story/story-template.xml +8 -1
  75. package/skills/plan-story/workflow.xml +17 -1
  76. package/skills/research-external-solution/SKILL.md +120 -107
  77. package/skills/research-external-solution/external-solution-template.xml +832 -832
  78. package/skills/research-external-solution/script.js +229 -238
  79. package/skills/research-external-solution/script.test.js +134 -134
  80. package/skills/research-external-solution/workflow.xml +657 -657
  81. package/skills/research-integration-solution/SKILL.md +121 -98
  82. package/skills/research-integration-solution/integration-solution-template.xml +1015 -1015
  83. package/skills/research-integration-solution/script.js +223 -231
  84. package/skills/research-integration-solution/script.test.js +134 -134
  85. package/skills/research-integration-solution/workflow.xml +711 -711
  86. package/skills/research-story-wiki/SKILL.md +101 -92
  87. package/skills/research-story-wiki/script.js +223 -231
  88. package/skills/research-story-wiki/script.test.js +138 -138
  89. package/skills/research-story-wiki/story-wiki-template.xml +194 -194
  90. package/skills/research-story-wiki/workflow.xml +473 -473
  91. package/skills/research-technical-solution/SKILL.md +131 -103
  92. package/skills/research-technical-solution/script.js +223 -231
  93. package/skills/research-technical-solution/script.test.js +134 -134
  94. package/skills/research-technical-solution/technical-solution-template.xml +1025 -1025
  95. package/skills/research-technical-solution/workflow.xml +761 -761
  96. package/skills/review-story/SKILL.md +99 -100
  97. package/skills/review-story/script.js +8 -16
  98. package/skills/review-story/script.test.js +169 -169
  99. package/skills/review-story/story-template.xml +451 -451
  100. package/skills/review-story/workflow.xml +1 -1
  101. package/skills/update/SKILL.md +65 -53
  102. package/skills/update/workflow.xml +21 -5
@@ -1,100 +1,99 @@
1
- ---
2
- name: review-story
3
- description: Standalone code review -- performs 3-level artifact verification, anti-pattern detection, coding standards enforcement, and tech debt discovery against a story's implementation
4
- argument-hint: "story=<file-path|github-url>"
5
- disable-model-invocation: true
6
- allowed-tools: Read, Bash, Glob, Grep
7
- model: sonnet
8
- effort: high
9
- context: fork
10
- agent: ace-code-reviewer
11
- ---
12
-
13
- # Review Story
14
-
15
- Review a story's implementation directly -- performing 3-level artifact verification, anti-pattern detection, coding standards enforcement, and tech debt discovery.
16
-
17
- ## When to Use
18
-
19
- - After `/ace:execute-story` -- to re-run code review after manual changes
20
- - Anytime to review a story implementation standalone
21
- - When a story has been implemented and needs code review
22
- - Re-checking after manual changes post-execution
23
- - Verifying stories implemented outside ACE
24
- - Pre-merge quality gate
25
-
26
- ## Input
27
-
28
- ### Required
29
-
30
- - **`story`** -- Story source:
31
- - **File path**: Path to a fully-planned story markdown file (must have AC + Technical Solution sections)
32
- - **GitHub URL or issue number**: GitHub story reference
33
- - Must be a valid, accessible file or GitHub issue.
34
- - The story MUST have Acceptance Criteria and Technical Solution.
35
- - If not valid, stop and prompt the user.
36
-
37
- ## Environment Context (preprocessed)
38
-
39
- !`node "${CLAUDE_SKILL_DIR}/script.js" init "$ARGUMENTS" 2>/dev/null`
40
-
41
- ## Supporting Resources
42
-
43
- Read ALL of these before starting the workflow:
44
-
45
- - **Workflow**: Read [workflow.xml](workflow.xml) -- complete orchestration process with all steps
46
- - **Story template**: Read [story-template.xml](story-template.xml) -- story document structure reference
47
- - **UI formatting**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md` -- ACE output formatting rules
48
-
49
- ## Process
50
-
51
- Use the `ace-code-reviewer` agent for code review.
52
-
53
- 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.
54
-
55
- Read all supporting resources listed above, then execute the workflow defined in [workflow.xml](workflow.xml) end-to-end.
56
-
57
- **CRITICAL REQUIREMENTS:**
58
- - Story MUST have Acceptance Criteria -- STOP if missing
59
- - Story MUST have Technical Solution -- STOP if missing
60
- - This is a READ-ONLY command -- do NOT modify any code
61
- - Coding standards violations are BLOCKERS, not warnings
62
- - Dead code and backwards-compatible shims must be flagged for DELETION
63
-
64
- The review process:
65
- 1. Load story context (AC, Technical Solution, Out of Scope)
66
- 2. Identify changed/created files related to the story
67
- 3. Run 3-level artifact verification (EXISTS -> SUBSTANTIVE -> WIRED)
68
- 4. Detect anti-patterns (dead code, stubs, TODOs, hardcoded values)
69
- 5. Enforce coding standards (mandatory, blocker-level)
70
- 6. Check AC coverage (all Gherkin scenarios implemented)
71
- 7. Discover pre-existing tech debt in touched files
72
- 8. Report results with structured format
73
-
74
- ## Artifacts
75
-
76
- ```
77
- No files modified -- this command is read-only.
78
- Outputs a structured review report to the conversation.
79
- ```
80
-
81
- ## Example Usage
82
-
83
- ```
84
- # Review a story from a file path
85
- /ace:review-story \
86
- story=.ace/artifacts/product/e1-auth/f3-oauth/s1-buttons/s1-buttons.md
87
-
88
- # Review from a GitHub issue
89
- /ace:review-story \
90
- story=https://github.com/owner/repo/issues/95
91
-
92
- # With just an issue number
93
- /ace:review-story story=#95
94
- ```
95
-
96
- ## Next Steps
97
-
98
- - `/ace:execute-story story=...` -- Re-execute to fix reported issues
99
- - `/ace:review-story story=...` -- Re-run review after fixes
100
- - `/ace:help` -- Check project status
1
+ ---
2
+ name: review-story
3
+ description: Standalone code review -- performs 3-level artifact verification, anti-pattern detection, coding standards enforcement, and tech debt discovery against a story's implementation
4
+ argument-hint: "story=<file-path|github-url>"
5
+ disable-model-invocation: true
6
+ allowed-tools: Read, Bash, Glob, Grep
7
+ model: sonnet
8
+ effort: high
9
+ context: fork
10
+ agent: ace-code-reviewer
11
+ ---
12
+
13
+ ## Environment Context (preprocessed)
14
+
15
+ !`node "${CLAUDE_SKILL_DIR}/script.js" init "$ARGUMENTS" 2>/dev/null`
16
+
17
+ ## Supporting Resources (auto-loaded)
18
+
19
+ !`cat "${CLAUDE_SKILL_DIR}/workflow.xml"`
20
+
21
+ !`cat "${CLAUDE_SKILL_DIR}/story-template.xml"`
22
+
23
+ !`cat "${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md"`
24
+
25
+ ```xml
26
+ <command>
27
+
28
+ <execution-time>
29
+ <runs-after>
30
+ <trigger>After /ace:execute-story to re-run code review after manual changes</trigger>
31
+ <trigger>Anytime to review a story implementation standalone</trigger>
32
+ </runs-after>
33
+ <use-when>
34
+ <condition>A story has been implemented and needs code review</condition>
35
+ <condition>Re-checking after manual changes post-execution</condition>
36
+ <condition>Verifying stories implemented outside ACE</condition>
37
+ <condition>Pre-merge quality gate</condition>
38
+ </use-when>
39
+ </execution-time>
40
+
41
+ <input>
42
+ <flags>
43
+ </flags>
44
+
45
+ <parameters>
46
+ <required>
47
+ <param name="story" type="file | github-url">
48
+ Story source — can be either:
49
+ - **File path**: Path to a fully-planned story markdown file
50
+ (must have AC + Technical Solution sections)
51
+ - **GitHub URL or issue number**: GitHub story reference
52
+ Must be a valid, accessible file or GitHub issue.
53
+ The story MUST have Acceptance Criteria and Technical Solution.
54
+ </param>
55
+ </required>
56
+ </parameters>
57
+ </input>
58
+
59
+ <execution-context>
60
+ <!-- All supporting files are auto-loaded in the Supporting Resources section above.
61
+ The model does NOT need to Read these files — they are already in context. -->
62
+ </execution-context>
63
+
64
+ <output>
65
+ <objective>
66
+ Review a story's implementation directly (the session IS the reviewer):
67
+ 1. Load story context (AC, Technical Solution, Out of Scope)
68
+ 2. Identify changed/created files related to the story
69
+ 3. Run 3-level artifact verification (EXISTS → SUBSTANTIVE → WIRED)
70
+ 4. Detect anti-patterns (dead code, stubs, TODOs, hardcoded values)
71
+ 5. Enforce coding standards (mandatory, blocker-level)
72
+ 6. Check AC coverage (all Gherkin scenarios implemented)
73
+ 7. Discover pre-existing tech debt in touched files
74
+ 8. Report results with structured format
75
+ </objective>
76
+
77
+ <artifacts>
78
+ No files modified this command is read-only.
79
+ Outputs a structured review report to the conversation.
80
+ </artifacts>
81
+ </output>
82
+
83
+ <process>
84
+ For this command use the `ace-code-reviewer` agent
85
+ that's specialized in code review.
86
+
87
+ Execute the review-story workflow from
88
+ `workflow.xml` end-to-end.
89
+
90
+ **CRITICAL REQUIREMENTS:**
91
+ - Story MUST have Acceptance Criteria — STOP if missing
92
+ - Story MUST have Technical Solution — STOP if missing
93
+ - This is a READ-ONLY command — do NOT modify any code
94
+ - Coding standards violations are BLOCKERS, not warnings
95
+ - Dead code and backwards-compatible shims must be flagged for DELETION
96
+ </process>
97
+
98
+ <example-usage>
99
+ ```
@@ -15,7 +15,7 @@ const path = require('path');
15
15
 
16
16
  const {
17
17
  loadConfig, pathExists, safeReadFile, loadSettings, resolveModel,
18
- execCommand, output, error,
18
+ execCommand, output, error, runSkillScript,
19
19
  } = require('../../shared/lib/ace-core');
20
20
 
21
21
  const {
@@ -26,18 +26,9 @@ const {
26
26
 
27
27
  // ─── CLI Dispatch ────────────────────────────────────────────────────────────
28
28
 
29
- const cwd = process.cwd();
30
- const args = process.argv.slice(2);
31
- const raw = args.includes('--raw');
32
- const cmd = args[0];
33
-
34
- switch (cmd) {
35
- case 'init':
36
- cmdInit(cwd, raw, args.slice(1).filter(a => a !== '--raw').join(' ') || undefined);
37
- break;
38
- default:
39
- error(`Unknown command: ${cmd}\nAvailable: init`);
40
- }
29
+ runSkillScript({
30
+ init: cmdInit,
31
+ });
41
32
 
42
33
  // ─── Init: Execute/Review Story ─────────────────────────────────────────────
43
34
 
@@ -53,7 +44,8 @@ switch (cmd) {
53
44
  * 6. computeStoryPaths
54
45
  * 7. Output JSON with all data
55
46
  */
56
- function cmdInit(cwd, raw, storyParam) {
47
+ function cmdInit(cwd, raw, args, parsed) {
48
+ const storyParam = parsed.story || parsed._positional || null;
57
49
  const config = loadConfig(cwd);
58
50
 
59
51
  // ── Environment detection ──
@@ -70,9 +62,9 @@ function cmdInit(cwd, raw, storyParam) {
70
62
 
71
63
  // ── Agent teams detection (sync from runtime settings) ──
72
64
  // RUNTIME_CONFIG_DIR is not in shared libs — detect inline
73
- // Try .claude first, then .opencode
65
+ // Try runtime settings files that can carry ACE agent-team state.
74
66
  let agent_teams = settings.agent_teams || false;
75
- for (const configDir of ['.claude', '.opencode']) {
67
+ for (const configDir of ['.claude', '.codex', '.opencode']) {
76
68
  const claudeSettingsPath = path.join(cwd, configDir, 'settings.json');
77
69
  try {
78
70
  const claudeRaw = fs.readFileSync(claudeSettingsPath, 'utf-8');
@@ -1,169 +1,169 @@
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
- /**
11
- * Create a minimal ACE project structure in a temp directory.
12
- */
13
- function createTestProject() {
14
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
15
-
16
- // .ace/config.json
17
- const aceDir = path.join(tmpDir, '.ace');
18
- fs.mkdirSync(aceDir, { recursive: true });
19
- fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
20
- version: '0.1.0',
21
- projectName: 'test-project',
22
- model_profile: 'quality',
23
- commit_docs: true,
24
- github: { enabled: false },
25
- }, null, 2));
26
-
27
- // .ace/settings.json
28
- fs.writeFileSync(path.join(aceDir, 'settings.json'), JSON.stringify({
29
- model_profile: 'quality',
30
- commit_docs: true,
31
- agent_teams: false,
32
- github_project: { enabled: false, gh_installed: false, repo: '', project_number: null, owner: '' },
33
- }, null, 2));
34
-
35
- return tmpDir;
36
- }
37
-
38
- /**
39
- * Create a story file in the test project.
40
- */
41
- function createStoryFile(tmpDir, relPath, content) {
42
- const fullPath = path.join(tmpDir, relPath);
43
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
44
- fs.writeFileSync(fullPath, content, 'utf-8');
45
- return relPath;
46
- }
47
-
48
- function runScript(subcommand, args, cwd) {
49
- return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
50
- cwd,
51
- encoding: 'utf-8',
52
- timeout: 10000,
53
- });
54
- }
55
-
56
- function cleanup(tmpDir) {
57
- fs.rmSync(tmpDir, { recursive: true, force: true });
58
- }
59
-
60
- // ─── Tests ───────────────────────────────────────────────────────────────────
61
-
62
- describe('review-story script', () => {
63
-
64
- describe('init', () => {
65
- let tmpDir;
66
-
67
- before(() => { tmpDir = createTestProject(); });
68
- after(() => { cleanup(tmpDir); });
69
-
70
- it('returns valid JSON with environment detection for a story file', () => {
71
- const storyContent = [
72
- '# S1: Add Login Button',
73
- '**Feature**: F1 User Auth | **Epic**: E1 Platform',
74
- '**Status**: Todo | **Size**: 3 | **Sprint**: — | **Link**: —',
75
- '',
76
- '## User Story',
77
- '',
78
- '> As a user,',
79
- '> I want to click a login button,',
80
- '> so that I can access my account.',
81
- '',
82
- '## Description',
83
- '',
84
- 'Adds a login button to the header.',
85
- '',
86
- '## Acceptance Criteria',
87
- '',
88
- '### Scenario: Click login button',
89
- '',
90
- '**Given** the user is on the homepage',
91
- '**When** they click "Login"',
92
- '**Then** they see the login form',
93
- '',
94
- '## Technical Solution',
95
- '',
96
- 'Add a LoginButton component to the header.',
97
- ].join('\n');
98
-
99
- const storyPath = createStoryFile(
100
- tmpDir,
101
- '.ace/artifacts/product/e1-platform/f1-user-auth/s1-add-login-button/s1-add-login-button.md',
102
- storyContent
103
- );
104
-
105
- const result = JSON.parse(runScript('init', storyPath, tmpDir));
106
-
107
- assert.ok(result.executor_model, 'should have executor_model');
108
- assert.ok(result.reviewer_model, 'should have reviewer_model');
109
- assert.strictEqual(result.story_valid, true, 'story should be valid');
110
- assert.strictEqual(result.story_source, 'file');
111
- assert.strictEqual(result.story.id, 'S1');
112
- assert.strictEqual(result.story.title, 'Add Login Button');
113
- assert.strictEqual(result.story.status, 'Todo');
114
- assert.strictEqual(result.has_acceptance_criteria, true);
115
- assert.strictEqual(result.acceptance_criteria_count, 1);
116
- assert.strictEqual(result.has_technical_solution, true);
117
- assert.ok(result.paths, 'should have computed paths');
118
- assert.ok(result.paths.story_file.includes('s1-add-login-button'));
119
- assert.strictEqual(result.has_story_file, true);
120
- assert.strictEqual(typeof result.commit_docs, 'boolean');
121
- assert.strictEqual(typeof result.has_git, 'boolean');
122
- assert.strictEqual(typeof result.agent_teams, 'boolean');
123
- assert.ok(result.story_content, 'should include story_content');
124
- });
125
-
126
- it('errors on init without story param', () => {
127
- const result = JSON.parse(runScript('init', '', tmpDir));
128
- assert.strictEqual(result.story_valid, false);
129
- });
130
-
131
- it('handles non-existent story file gracefully', () => {
132
- const result = JSON.parse(runScript('init', 'nonexistent/story.md', tmpDir));
133
- assert.strictEqual(result.story_valid, false);
134
- assert.ok(result.story_error.includes('not found'));
135
- });
136
-
137
- it('detects missing technical solution section', () => {
138
- const storyContent = [
139
- '# S2: Simple Story',
140
- '**Feature**: F1 Test | **Epic**: E1 Test',
141
- '**Status**: Todo | **Size**: 1 | **Sprint**: — | **Link**: —',
142
- '',
143
- '## Description',
144
- '',
145
- 'A simple story without technical solution.',
146
- ].join('\n');
147
-
148
- const storyPath = createStoryFile(
149
- tmpDir,
150
- '.ace/artifacts/product/e1-test/f1-test/s2-simple-story/s2-simple-story.md',
151
- storyContent
152
- );
153
-
154
- const result = JSON.parse(runScript('init', storyPath, tmpDir));
155
-
156
- assert.strictEqual(result.story_valid, true);
157
- assert.strictEqual(result.has_technical_solution, false);
158
- assert.strictEqual(result.has_acceptance_criteria, false);
159
- });
160
- });
161
-
162
- describe('error handling', () => {
163
- it('errors on unknown command', () => {
164
- assert.throws(() => {
165
- execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
166
- });
167
- });
168
- });
169
- });
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
+ /**
11
+ * Create a minimal ACE project structure in a temp directory.
12
+ */
13
+ function createTestProject() {
14
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
15
+
16
+ // .ace/config.json
17
+ const aceDir = path.join(tmpDir, '.ace');
18
+ fs.mkdirSync(aceDir, { recursive: true });
19
+ fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
20
+ version: '0.1.0',
21
+ projectName: 'test-project',
22
+ model_profile: 'quality',
23
+ commit_docs: true,
24
+ github: { enabled: false },
25
+ }, null, 2));
26
+
27
+ // .ace/settings.json
28
+ fs.writeFileSync(path.join(aceDir, 'settings.json'), JSON.stringify({
29
+ model_profile: 'quality',
30
+ commit_docs: true,
31
+ agent_teams: false,
32
+ github_project: { enabled: false, gh_installed: false, repo: '', project_number: null, owner: '' },
33
+ }, null, 2));
34
+
35
+ return tmpDir;
36
+ }
37
+
38
+ /**
39
+ * Create a story file in the test project.
40
+ */
41
+ function createStoryFile(tmpDir, relPath, content) {
42
+ const fullPath = path.join(tmpDir, relPath);
43
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
44
+ fs.writeFileSync(fullPath, content, 'utf-8');
45
+ return relPath;
46
+ }
47
+
48
+ function runScript(subcommand, args, cwd) {
49
+ return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
50
+ cwd,
51
+ encoding: 'utf-8',
52
+ timeout: 10000,
53
+ });
54
+ }
55
+
56
+ function cleanup(tmpDir) {
57
+ fs.rmSync(tmpDir, { recursive: true, force: true });
58
+ }
59
+
60
+ // ─── Tests ───────────────────────────────────────────────────────────────────
61
+
62
+ describe('review-story script', () => {
63
+
64
+ describe('init', () => {
65
+ let tmpDir;
66
+
67
+ before(() => { tmpDir = createTestProject(); });
68
+ after(() => { cleanup(tmpDir); });
69
+
70
+ it('returns valid JSON with environment detection for a story file', () => {
71
+ const storyContent = [
72
+ '# S1: Add Login Button',
73
+ '**Feature**: F1 User Auth | **Epic**: E1 Platform',
74
+ '**Status**: Todo | **Size**: 3 | **Sprint**: — | **Link**: —',
75
+ '',
76
+ '## User Story',
77
+ '',
78
+ '> As a user,',
79
+ '> I want to click a login button,',
80
+ '> so that I can access my account.',
81
+ '',
82
+ '## Description',
83
+ '',
84
+ 'Adds a login button to the header.',
85
+ '',
86
+ '## Acceptance Criteria',
87
+ '',
88
+ '### Scenario: Click login button',
89
+ '',
90
+ '**Given** the user is on the homepage',
91
+ '**When** they click "Login"',
92
+ '**Then** they see the login form',
93
+ '',
94
+ '## Technical Solution',
95
+ '',
96
+ 'Add a LoginButton component to the header.',
97
+ ].join('\n');
98
+
99
+ const storyPath = createStoryFile(
100
+ tmpDir,
101
+ '.ace/artifacts/product/e1-platform/f1-user-auth/s1-add-login-button/s1-add-login-button.md',
102
+ storyContent
103
+ );
104
+
105
+ const result = JSON.parse(runScript('init', storyPath, tmpDir));
106
+
107
+ assert.ok(result.executor_model, 'should have executor_model');
108
+ assert.ok(result.reviewer_model, 'should have reviewer_model');
109
+ assert.strictEqual(result.story_valid, true, 'story should be valid');
110
+ assert.strictEqual(result.story_source, 'file');
111
+ assert.strictEqual(result.story.id, 'S1');
112
+ assert.strictEqual(result.story.title, 'Add Login Button');
113
+ assert.strictEqual(result.story.status, 'Todo');
114
+ assert.strictEqual(result.has_acceptance_criteria, true);
115
+ assert.strictEqual(result.acceptance_criteria_count, 1);
116
+ assert.strictEqual(result.has_technical_solution, true);
117
+ assert.ok(result.paths, 'should have computed paths');
118
+ assert.ok(result.paths.story_file.includes('s1-add-login-button'));
119
+ assert.strictEqual(result.has_story_file, true);
120
+ assert.strictEqual(typeof result.commit_docs, 'boolean');
121
+ assert.strictEqual(typeof result.has_git, 'boolean');
122
+ assert.strictEqual(typeof result.agent_teams, 'boolean');
123
+ assert.ok(result.story_content, 'should include story_content');
124
+ });
125
+
126
+ it('errors on init without story param', () => {
127
+ const result = JSON.parse(runScript('init', '', tmpDir));
128
+ assert.strictEqual(result.story_valid, false);
129
+ });
130
+
131
+ it('handles non-existent story file gracefully', () => {
132
+ const result = JSON.parse(runScript('init', 'nonexistent/story.md', tmpDir));
133
+ assert.strictEqual(result.story_valid, false);
134
+ assert.ok(result.story_error.includes('not found'));
135
+ });
136
+
137
+ it('detects missing technical solution section', () => {
138
+ const storyContent = [
139
+ '# S2: Simple Story',
140
+ '**Feature**: F1 Test | **Epic**: E1 Test',
141
+ '**Status**: Todo | **Size**: 1 | **Sprint**: — | **Link**: —',
142
+ '',
143
+ '## Description',
144
+ '',
145
+ 'A simple story without technical solution.',
146
+ ].join('\n');
147
+
148
+ const storyPath = createStoryFile(
149
+ tmpDir,
150
+ '.ace/artifacts/product/e1-test/f1-test/s2-simple-story/s2-simple-story.md',
151
+ storyContent
152
+ );
153
+
154
+ const result = JSON.parse(runScript('init', storyPath, tmpDir));
155
+
156
+ assert.strictEqual(result.story_valid, true);
157
+ assert.strictEqual(result.has_technical_solution, false);
158
+ assert.strictEqual(result.has_acceptance_criteria, false);
159
+ });
160
+ });
161
+
162
+ describe('error handling', () => {
163
+ it('errors on unknown command', () => {
164
+ assert.throws(() => {
165
+ execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
166
+ });
167
+ });
168
+ });
169
+ });