agile-context-engineering 0.3.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 (139) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +10 -0
  3. package/CHANGELOG.md +7 -1
  4. package/LICENSE +51 -51
  5. package/README.md +330 -318
  6. package/agents/ace-code-discovery-analyst.md +245 -245
  7. package/agents/ace-code-integration-analyst.md +248 -248
  8. package/agents/ace-code-reviewer.md +375 -375
  9. package/agents/ace-product-owner.md +365 -361
  10. package/agents/ace-project-researcher.md +606 -606
  11. package/agents/ace-research-synthesizer.md +228 -228
  12. package/agents/ace-technical-application-architect.md +315 -315
  13. package/agents/ace-wiki-mapper.md +449 -445
  14. package/bin/install.js +605 -195
  15. package/hooks/ace-check-update.js +71 -62
  16. package/hooks/ace-statusline.js +107 -89
  17. package/hooks/hooks.json +14 -0
  18. package/package.json +7 -5
  19. package/shared/lib/ace-core.js +361 -0
  20. package/shared/lib/ace-core.test.js +308 -0
  21. package/shared/lib/ace-github.js +753 -0
  22. package/shared/lib/ace-story.js +400 -0
  23. package/shared/lib/ace-story.test.js +250 -0
  24. package/{agile-context-engineering → shared}/utils/questioning.xml +110 -110
  25. package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
  26. package/{commands/ace/execute-story.md → skills/execute-story/SKILL.md} +116 -138
  27. package/skills/execute-story/script.js +291 -0
  28. package/skills/execute-story/script.test.js +261 -0
  29. package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +451 -451
  30. package/skills/execute-story/walkthrough-template.xml +255 -0
  31. package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +1221 -1219
  32. package/skills/help/SKILL.md +71 -0
  33. package/skills/help/script.js +315 -0
  34. package/skills/help/script.test.js +183 -0
  35. package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +544 -533
  36. package/{commands/ace/init-coding-standards.md → skills/init-coding-standards/SKILL.md} +91 -83
  37. package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +531 -531
  38. package/skills/init-coding-standards/script.js +50 -0
  39. package/skills/init-coding-standards/script.test.js +70 -0
  40. package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +381 -386
  41. package/skills/map-cross-cutting/SKILL.md +126 -0
  42. package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +197 -197
  43. package/skills/map-cross-cutting/workflow.xml +330 -0
  44. package/skills/map-guide/SKILL.md +126 -0
  45. package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +137 -137
  46. package/skills/map-guide/workflow.xml +320 -0
  47. package/skills/map-pattern/SKILL.md +125 -0
  48. package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +159 -159
  49. package/skills/map-pattern/workflow.xml +331 -0
  50. package/{commands/ace/map-story.md → skills/map-story/SKILL.md} +180 -165
  51. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +115 -115
  52. package/skills/map-story/templates/guide.xml +137 -0
  53. package/skills/map-story/templates/pattern.xml +159 -0
  54. package/skills/map-story/templates/system-cross-cutting.xml +197 -0
  55. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +381 -381
  56. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +125 -125
  57. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/walkthrough.xml +255 -255
  58. package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +1046 -1046
  59. package/{commands/ace/map-subsystem.md → skills/map-subsystem/SKILL.md} +155 -140
  60. package/skills/map-subsystem/script.js +51 -0
  61. package/skills/map-subsystem/script.test.js +68 -0
  62. package/skills/map-subsystem/templates/decizions.xml +115 -0
  63. package/skills/map-subsystem/templates/guide.xml +137 -0
  64. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +174 -174
  65. package/skills/map-subsystem/templates/pattern.xml +159 -0
  66. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +343 -343
  67. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +234 -234
  68. package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
  69. package/skills/map-subsystem/templates/system.xml +381 -0
  70. package/skills/map-subsystem/templates/walkthrough.xml +255 -0
  71. package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +1173 -1178
  72. package/skills/map-sys-doc/SKILL.md +125 -0
  73. package/skills/map-sys-doc/system.xml +381 -0
  74. package/skills/map-sys-doc/workflow.xml +336 -0
  75. package/{commands/ace/map-system.md → skills/map-system/SKILL.md} +103 -92
  76. package/skills/map-system/script.js +75 -0
  77. package/skills/map-system/script.test.js +73 -0
  78. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +254 -254
  79. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +177 -177
  80. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +283 -283
  81. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/wiki-readme.xml +296 -296
  82. package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +667 -672
  83. package/{commands/ace/map-walkthrough.md → skills/map-walkthrough/SKILL.md} +140 -127
  84. package/skills/map-walkthrough/walkthrough.xml +255 -0
  85. package/{agile-context-engineering/workflows/map-walkthrough.xml → skills/map-walkthrough/workflow.xml} +457 -457
  86. package/{commands/ace/plan-backlog.md → skills/plan-backlog/SKILL.md} +93 -83
  87. package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +231 -231
  88. package/skills/plan-backlog/script.js +121 -0
  89. package/skills/plan-backlog/script.test.js +83 -0
  90. package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +1348 -1356
  91. package/{commands/ace/plan-feature.md → skills/plan-feature/SKILL.md} +99 -89
  92. package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +361 -361
  93. package/skills/plan-feature/script.js +131 -0
  94. package/skills/plan-feature/script.test.js +80 -0
  95. package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +1487 -1495
  96. package/{commands/ace/plan-product-vision.md → skills/plan-product-vision/SKILL.md} +91 -81
  97. package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +227 -227
  98. package/skills/plan-product-vision/script.js +51 -0
  99. package/skills/plan-product-vision/script.test.js +69 -0
  100. package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +337 -342
  101. package/{commands/ace/plan-story.md → skills/plan-story/SKILL.md} +139 -159
  102. package/skills/plan-story/script.js +295 -0
  103. package/skills/plan-story/script.test.js +240 -0
  104. package/skills/plan-story/story-template.xml +458 -0
  105. package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1301 -944
  106. package/{commands/ace/research-external-solution.md → skills/research-external-solution/SKILL.md} +120 -138
  107. package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +832 -832
  108. package/skills/research-external-solution/script.js +229 -0
  109. package/skills/research-external-solution/script.test.js +134 -0
  110. package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +657 -659
  111. package/{commands/ace/research-integration-solution.md → skills/research-integration-solution/SKILL.md} +121 -135
  112. package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +1015 -1015
  113. package/skills/research-integration-solution/script.js +223 -0
  114. package/skills/research-integration-solution/script.test.js +134 -0
  115. package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +711 -713
  116. package/{commands/ace/research-story-wiki.md → skills/research-story-wiki/SKILL.md} +101 -116
  117. package/skills/research-story-wiki/script.js +223 -0
  118. package/skills/research-story-wiki/script.test.js +138 -0
  119. package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +194 -194
  120. package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +473 -475
  121. package/{commands/ace/research-technical-solution.md → skills/research-technical-solution/SKILL.md} +131 -147
  122. package/skills/research-technical-solution/script.js +223 -0
  123. package/skills/research-technical-solution/script.test.js +134 -0
  124. package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +1025 -1025
  125. package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +761 -763
  126. package/{commands/ace/review-story.md → skills/review-story/SKILL.md} +99 -109
  127. package/skills/review-story/script.js +249 -0
  128. package/skills/review-story/script.test.js +169 -0
  129. package/skills/review-story/story-template.xml +451 -0
  130. package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +279 -281
  131. package/{commands/ace/update.md → skills/update/SKILL.md} +65 -56
  132. package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +33 -18
  133. package/agile-context-engineering/src/ace-tools.js +0 -2881
  134. package/agile-context-engineering/src/ace-tools.test.js +0 -1089
  135. package/agile-context-engineering/templates/_command.md +0 -54
  136. package/agile-context-engineering/templates/_workflow.xml +0 -17
  137. package/agile-context-engineering/templates/config.json +0 -0
  138. package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
  139. package/commands/ace/help.md +0 -93
@@ -1,109 +1,99 @@
1
- ---
2
- name: ace: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
- allowed-tools:
6
- - Read
7
- - Bash
8
- - Glob
9
- - Grep
10
- ---
11
-
12
- ```xml
13
- <command>
14
-
15
- <execution-time>
16
- <runs-after>
17
- <trigger>After /ace:execute-story to re-run code review after manual changes</trigger>
18
- <trigger>Anytime — to review a story implementation standalone</trigger>
19
- </runs-after>
20
- <use-when>
21
- <condition>A story has been implemented and needs code review</condition>
22
- <condition>Re-checking after manual changes post-execution</condition>
23
- <condition>Verifying stories implemented outside ACE</condition>
24
- <condition>Pre-merge quality gate</condition>
25
- </use-when>
26
- </execution-time>
27
-
28
- <input>
29
- <flags>
30
- </flags>
31
-
32
- <parameters>
33
- <required>
34
- <param name="story" type="file | github-url">
35
- Story source can be either:
36
- - **File path**: Path to a fully-planned story markdown file
37
- (must have AC + Technical Solution sections)
38
- - **GitHub URL or issue number**: GitHub story reference
39
- Must be a valid, accessible file or GitHub issue.
40
- The story MUST have Acceptance Criteria and Technical Solution.
41
- </param>
42
- </required>
43
- </parameters>
44
- </input>
45
-
46
- <execution-context>
47
- <review-story-workflow>@~/.claude/agile-context-engineering/workflows/review-story.xml</review-story-workflow>
48
- <story-template>@~/.claude/agile-context-engineering/templates/product/story.xml</story-template>
49
- <ui-formatting>@~/.claude/agile-context-engineering/utils/ui-formatting.md</ui-formatting>
50
- </execution-context>
51
-
52
- <output>
53
- <objective>
54
- Review a story's implementation directly (the session IS the reviewer):
55
- 1. Load story context (AC, Technical Solution, Out of Scope)
56
- 2. Identify changed/created files related to the story
57
- 3. Run 3-level artifact verification (EXISTS → SUBSTANTIVE → WIRED)
58
- 4. Detect anti-patterns (dead code, stubs, TODOs, hardcoded values)
59
- 5. Enforce coding standards (mandatory, blocker-level)
60
- 6. Check AC coverage (all Gherkin scenarios implemented)
61
- 7. Discover pre-existing tech debt in touched files
62
- 8. Report results with structured format
63
- </objective>
64
-
65
- <artifacts>
66
- No files modified this command is read-only.
67
- Outputs a structured review report to the conversation.
68
- </artifacts>
69
- </output>
70
-
71
- <process>
72
- For this command use the `ace-code-reviewer` agent
73
- that's specialized in code review.
74
-
75
- Execute the review-story workflow from
76
- `@~/.claude/agile-context-engineering/workflows/review-story.xml` end-to-end.
77
-
78
- **CRITICAL REQUIREMENTS:**
79
- - Story MUST have Acceptance Criteria STOP if missing
80
- - Story MUST have Technical Solution — STOP if missing
81
- - This is a READ-ONLY command — do NOT modify any code
82
- - Coding standards violations are BLOCKERS, not warnings
83
- - Dead code and backwards-compatible shims must be flagged for DELETION
84
- </process>
85
-
86
- <example-usage>
87
- ```
88
- # Review a story from a file path
89
- /ace:review-story \
90
- story=.ace/artifacts/product/e1-auth/f3-oauth/s1-buttons/s1-buttons.md
91
-
92
- # Review from a GitHub issue
93
- /ace:review-story \
94
- story=https://github.com/owner/repo/issues/95
95
-
96
- # With just an issue number
97
- /ace:review-story story=#95
98
- ```
99
- </example-usage>
100
-
101
- <next-steps>
102
- **After this command:**
103
- - `/ace:execute-story story=...` — Re-execute to fix reported issues
104
- - `/ace:review-story story=...` — Re-run review after fixes
105
- - `/ace:help` — Check project status
106
- </next-steps>
107
-
108
- </command>
109
- ```
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
+ ```
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * review-story skill script — Entry point for ace-tools operations
5
+ * needed by the review-story skill.
6
+ *
7
+ * Subcommands:
8
+ * init [story-param] Environment detection for review-story workflow
9
+ *
10
+ * Usage: node script.js <subcommand> [args] [--raw]
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const {
17
+ loadConfig, pathExists, safeReadFile, loadSettings, resolveModel,
18
+ execCommand, output, error, runSkillScript,
19
+ } = require('../../shared/lib/ace-core');
20
+
21
+ const {
22
+ classifyStoryParam, extractStoryMetadata, extractStoryRequirements,
23
+ extractMarkdownSection, extractIssueNumber, extractIssueNumberFromFile,
24
+ computeStoryPaths,
25
+ } = require('../../shared/lib/ace-story');
26
+
27
+ // ─── CLI Dispatch ────────────────────────────────────────────────────────────
28
+
29
+ runSkillScript({
30
+ init: cmdInit,
31
+ });
32
+
33
+ // ─── Init: Execute/Review Story ─────────────────────────────────────────────
34
+
35
+ /**
36
+ * Environment detection for the review-story workflow.
37
+ *
38
+ * Replicates cmdInitExecuteStory from ace-tools.js:
39
+ * 1. loadConfig, detect git/gh CLI/settings/agent_teams
40
+ * 2. classifyStoryParam
41
+ * 3. Load story content
42
+ * 4. extractStoryMetadata, extractStoryRequirements
43
+ * 5. Detect key sections (AC, technical solution, wiki refs, coding standards)
44
+ * 6. computeStoryPaths
45
+ * 7. Output JSON with all data
46
+ */
47
+ function cmdInit(cwd, raw, args, parsed) {
48
+ const storyParam = parsed.story || parsed._positional || null;
49
+ const config = loadConfig(cwd);
50
+
51
+ // ── Environment detection ──
52
+ const has_git = pathExists(cwd, '.git');
53
+ const has_gh_cli = (() => {
54
+ try {
55
+ const { execSync } = require('child_process');
56
+ execSync('gh --version', { stdio: 'pipe' });
57
+ return true;
58
+ } catch { return false; }
59
+ })();
60
+ const settings = loadSettings(cwd);
61
+ const github_project = settings.github_project;
62
+
63
+ // ── Agent teams detection (sync from runtime settings) ──
64
+ // RUNTIME_CONFIG_DIR is not in shared libs — detect inline
65
+ // Try runtime settings files that can carry ACE agent-team state.
66
+ let agent_teams = settings.agent_teams || false;
67
+ for (const configDir of ['.claude', '.codex', '.opencode']) {
68
+ const claudeSettingsPath = path.join(cwd, configDir, 'settings.json');
69
+ try {
70
+ const claudeRaw = fs.readFileSync(claudeSettingsPath, 'utf-8');
71
+ const claudeSettings = JSON.parse(claudeRaw);
72
+ const val = claudeSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
73
+ if (val === '1' || val === 'true') {
74
+ agent_teams = true;
75
+ }
76
+ break; // found a settings file, stop looking
77
+ } catch {}
78
+ }
79
+
80
+ // ── Classify the story parameter ──
81
+ const classified = classifyStoryParam(storyParam);
82
+
83
+ // Early exit if invalid
84
+ if (classified.type === null || classified.type === 'invalid') {
85
+ output({
86
+ executor_model: resolveModel(cwd, 'ace-executor'),
87
+ reviewer_model: resolveModel(cwd, 'ace-code-reviewer'),
88
+ commit_docs: config.commit_docs,
89
+ has_git, has_gh_cli, github_project, agent_teams,
90
+ story_source: null,
91
+ story_valid: false,
92
+ story_error: classified.reason || 'No story parameter provided',
93
+ story_content: null,
94
+ story: { id: null, title: null, status: null, size: null },
95
+ feature: { id: null, title: null },
96
+ epic: { id: null, title: null },
97
+ has_acceptance_criteria: false,
98
+ acceptance_criteria_count: 0,
99
+ has_technical_solution: false,
100
+ has_wiki_refs: false,
101
+ has_coding_standards: false,
102
+ paths: null,
103
+ }, raw);
104
+ return;
105
+ }
106
+
107
+ // ── Load story content ──
108
+ let storyContent = null;
109
+ let storySource = classified.type === 'file' ? 'file' : 'github';
110
+ let storyError = null;
111
+ let storyFilePath = null;
112
+
113
+ if (classified.type === 'file') {
114
+ const resolvedPath = path.isAbsolute(classified.filePath)
115
+ ? classified.filePath
116
+ : path.join(cwd, classified.filePath);
117
+ if (!pathExists(cwd, classified.filePath)) {
118
+ storyError = `Story file not found: ${classified.filePath}`;
119
+ } else {
120
+ storyContent = safeReadFile(resolvedPath);
121
+ storyFilePath = classified.filePath;
122
+ if (!storyContent) storyError = `Could not read story file: ${classified.filePath}`;
123
+ }
124
+ } else {
125
+ // github-url or issue-number
126
+ if (!has_gh_cli) {
127
+ storyError = 'GitHub CLI (gh) not installed. Cannot fetch GitHub issues.';
128
+ } else {
129
+ const repo = classified.repo || (github_project.repo || null);
130
+ if (!repo) {
131
+ storyError = 'No repository configured. Provide a full GitHub URL or configure github_project.repo in settings.';
132
+ } else {
133
+ const ghResult = execCommand(
134
+ `gh issue view ${classified.issueNumber} --repo ${repo} --json title,body,labels,state`,
135
+ cwd
136
+ );
137
+ if (!ghResult) {
138
+ storyError = `Could not fetch GitHub issue #${classified.issueNumber} from ${repo}.`;
139
+ } else {
140
+ try {
141
+ const issue = JSON.parse(ghResult);
142
+ storyContent = issue.body || '';
143
+ if (storyContent && !storyContent.match(/^#\s+/m)) {
144
+ storyContent = `# ${issue.title}\n\n${storyContent}`;
145
+ }
146
+ } catch {
147
+ storyError = `Failed to parse GitHub issue response for #${classified.issueNumber}.`;
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ // ── Extract metadata & requirements ──
155
+ const metadata = extractStoryMetadata(storyContent);
156
+ const requirements = extractStoryRequirements(storyContent);
157
+
158
+ // ── Detect key sections ──
159
+ const has_acceptance_criteria = requirements.acceptance_criteria_count > 0;
160
+ const has_technical_solution = storyContent
161
+ ? !!extractMarkdownSection(storyContent, 'Technical Solution', 2)
162
+ : false;
163
+ const has_wiki_refs = storyContent
164
+ ? !!extractMarkdownSection(storyContent, 'Relevant Wiki', 2)
165
+ : false;
166
+ const has_coding_standards = pathExists(cwd, '.docs/wiki/system-wide/coding-standards.md');
167
+
168
+ // ── Compute paths ──
169
+ let paths = null;
170
+ let has_story_file = false;
171
+
172
+ if (storyFilePath) {
173
+ const resolvedPath = path.isAbsolute(storyFilePath)
174
+ ? storyFilePath
175
+ : path.join(cwd, storyFilePath);
176
+ const storyDir = path.dirname(resolvedPath);
177
+ const relStoryDir = path.relative(cwd, storyDir).replace(/\\/g, '/');
178
+ const storySlug = path.basename(storyDir);
179
+ const featureDir = path.dirname(storyDir);
180
+ const relFeatureDir = path.relative(cwd, featureDir).replace(/\\/g, '/');
181
+ const featureSlug = path.basename(featureDir);
182
+
183
+ paths = {
184
+ epic_slug: null,
185
+ feature_slug: featureSlug,
186
+ story_slug: storySlug,
187
+ story_dir: relStoryDir,
188
+ story_file: storyFilePath.replace(/\\/g, '/'),
189
+ external_analysis_file: `${relStoryDir}/external-analysis.md`,
190
+ integration_analysis_file: `${relStoryDir}/integration-analysis.md`,
191
+ feature_dir: relFeatureDir,
192
+ feature_file: `${relFeatureDir}/${featureSlug}.md`,
193
+ product_backlog: '.ace/artifacts/product/product-backlog.md',
194
+ coding_standards: '.docs/wiki/system-wide/coding-standards.md',
195
+ };
196
+ has_story_file = true;
197
+ } else if (metadata.epic.id && metadata.feature.id && metadata.id) {
198
+ const computed = computeStoryPaths(
199
+ metadata.epic.id, metadata.epic.title || '',
200
+ metadata.feature.id, metadata.feature.title || '',
201
+ metadata.id, metadata.title || ''
202
+ );
203
+ if (computed) {
204
+ paths = {
205
+ ...computed,
206
+ product_backlog: '.ace/artifacts/product/product-backlog.md',
207
+ coding_standards: '.docs/wiki/system-wide/coding-standards.md',
208
+ };
209
+ has_story_file = pathExists(cwd, paths.story_file);
210
+ }
211
+ }
212
+
213
+ // ── Extract GitHub issue numbers ──
214
+ const storyIssueNumber = extractIssueNumber(metadata.link);
215
+ const featureIssueNumber = paths ? extractIssueNumberFromFile(cwd, paths.feature_file) : null;
216
+
217
+ // ── Build result ──
218
+ const result = {
219
+ executor_model: resolveModel(cwd, 'ace-executor'),
220
+ reviewer_model: resolveModel(cwd, 'ace-code-reviewer'),
221
+ commit_docs: config.commit_docs,
222
+ has_git, has_gh_cli, github_project, agent_teams,
223
+ story_source: storySource,
224
+ story_valid: storyContent !== null && storyError === null,
225
+ story_error: storyError,
226
+ story_content: storyContent,
227
+ story: {
228
+ id: metadata.id,
229
+ title: metadata.title,
230
+ status: metadata.status,
231
+ size: metadata.size,
232
+ issue_number: storyIssueNumber,
233
+ },
234
+ feature: {
235
+ ...metadata.feature,
236
+ issue_number: featureIssueNumber,
237
+ },
238
+ epic: metadata.epic,
239
+ has_acceptance_criteria,
240
+ acceptance_criteria_count: requirements.acceptance_criteria_count,
241
+ has_technical_solution,
242
+ has_wiki_refs,
243
+ has_coding_standards,
244
+ paths,
245
+ has_story_file,
246
+ };
247
+
248
+ output(result, raw);
249
+ }
@@ -0,0 +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
+ });