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.
- package/.claude-plugin/marketplace.json +18 -0
- package/.claude-plugin/plugin.json +10 -0
- package/CHANGELOG.md +7 -1
- package/LICENSE +51 -51
- package/README.md +330 -318
- package/agents/ace-code-discovery-analyst.md +245 -245
- package/agents/ace-code-integration-analyst.md +248 -248
- package/agents/ace-code-reviewer.md +375 -375
- package/agents/ace-product-owner.md +365 -361
- package/agents/ace-project-researcher.md +606 -606
- package/agents/ace-research-synthesizer.md +228 -228
- package/agents/ace-technical-application-architect.md +315 -315
- package/agents/ace-wiki-mapper.md +449 -445
- package/bin/install.js +605 -195
- package/hooks/ace-check-update.js +71 -62
- package/hooks/ace-statusline.js +107 -89
- package/hooks/hooks.json +14 -0
- package/package.json +7 -5
- package/shared/lib/ace-core.js +361 -0
- package/shared/lib/ace-core.test.js +308 -0
- package/shared/lib/ace-github.js +753 -0
- package/shared/lib/ace-story.js +400 -0
- package/shared/lib/ace-story.test.js +250 -0
- package/{agile-context-engineering → shared}/utils/questioning.xml +110 -110
- package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
- package/{commands/ace/execute-story.md → skills/execute-story/SKILL.md} +116 -138
- package/skills/execute-story/script.js +291 -0
- package/skills/execute-story/script.test.js +261 -0
- package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +451 -451
- package/skills/execute-story/walkthrough-template.xml +255 -0
- package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +1221 -1219
- package/skills/help/SKILL.md +71 -0
- package/skills/help/script.js +315 -0
- package/skills/help/script.test.js +183 -0
- package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +544 -533
- package/{commands/ace/init-coding-standards.md → skills/init-coding-standards/SKILL.md} +91 -83
- package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +531 -531
- package/skills/init-coding-standards/script.js +50 -0
- package/skills/init-coding-standards/script.test.js +70 -0
- package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +381 -386
- package/skills/map-cross-cutting/SKILL.md +126 -0
- package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +197 -197
- package/skills/map-cross-cutting/workflow.xml +330 -0
- package/skills/map-guide/SKILL.md +126 -0
- package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +137 -137
- package/skills/map-guide/workflow.xml +320 -0
- package/skills/map-pattern/SKILL.md +125 -0
- package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +159 -159
- package/skills/map-pattern/workflow.xml +331 -0
- package/{commands/ace/map-story.md → skills/map-story/SKILL.md} +180 -165
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +115 -115
- package/skills/map-story/templates/guide.xml +137 -0
- package/skills/map-story/templates/pattern.xml +159 -0
- package/skills/map-story/templates/system-cross-cutting.xml +197 -0
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +381 -381
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +125 -125
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/walkthrough.xml +255 -255
- package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +1046 -1046
- package/{commands/ace/map-subsystem.md → skills/map-subsystem/SKILL.md} +155 -140
- package/skills/map-subsystem/script.js +51 -0
- package/skills/map-subsystem/script.test.js +68 -0
- package/skills/map-subsystem/templates/decizions.xml +115 -0
- package/skills/map-subsystem/templates/guide.xml +137 -0
- package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +174 -174
- package/skills/map-subsystem/templates/pattern.xml +159 -0
- package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +343 -343
- package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +234 -234
- package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
- package/skills/map-subsystem/templates/system.xml +381 -0
- package/skills/map-subsystem/templates/walkthrough.xml +255 -0
- package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +1173 -1178
- package/skills/map-sys-doc/SKILL.md +125 -0
- package/skills/map-sys-doc/system.xml +381 -0
- package/skills/map-sys-doc/workflow.xml +336 -0
- package/{commands/ace/map-system.md → skills/map-system/SKILL.md} +103 -92
- package/skills/map-system/script.js +75 -0
- package/skills/map-system/script.test.js +73 -0
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +254 -254
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +177 -177
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +283 -283
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/wiki-readme.xml +296 -296
- package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +667 -672
- package/{commands/ace/map-walkthrough.md → skills/map-walkthrough/SKILL.md} +140 -127
- package/skills/map-walkthrough/walkthrough.xml +255 -0
- package/{agile-context-engineering/workflows/map-walkthrough.xml → skills/map-walkthrough/workflow.xml} +457 -457
- package/{commands/ace/plan-backlog.md → skills/plan-backlog/SKILL.md} +93 -83
- package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +231 -231
- package/skills/plan-backlog/script.js +121 -0
- package/skills/plan-backlog/script.test.js +83 -0
- package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +1348 -1356
- package/{commands/ace/plan-feature.md → skills/plan-feature/SKILL.md} +99 -89
- package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +361 -361
- package/skills/plan-feature/script.js +131 -0
- package/skills/plan-feature/script.test.js +80 -0
- package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +1487 -1495
- package/{commands/ace/plan-product-vision.md → skills/plan-product-vision/SKILL.md} +91 -81
- package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +227 -227
- package/skills/plan-product-vision/script.js +51 -0
- package/skills/plan-product-vision/script.test.js +69 -0
- package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +337 -342
- package/{commands/ace/plan-story.md → skills/plan-story/SKILL.md} +139 -159
- package/skills/plan-story/script.js +295 -0
- package/skills/plan-story/script.test.js +240 -0
- package/skills/plan-story/story-template.xml +458 -0
- package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1301 -944
- package/{commands/ace/research-external-solution.md → skills/research-external-solution/SKILL.md} +120 -138
- package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +832 -832
- package/skills/research-external-solution/script.js +229 -0
- package/skills/research-external-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +657 -659
- package/{commands/ace/research-integration-solution.md → skills/research-integration-solution/SKILL.md} +121 -135
- package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +1015 -1015
- package/skills/research-integration-solution/script.js +223 -0
- package/skills/research-integration-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +711 -713
- package/{commands/ace/research-story-wiki.md → skills/research-story-wiki/SKILL.md} +101 -116
- package/skills/research-story-wiki/script.js +223 -0
- package/skills/research-story-wiki/script.test.js +138 -0
- package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +194 -194
- package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +473 -475
- package/{commands/ace/research-technical-solution.md → skills/research-technical-solution/SKILL.md} +131 -147
- package/skills/research-technical-solution/script.js +223 -0
- package/skills/research-technical-solution/script.test.js +134 -0
- package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +1025 -1025
- package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +761 -763
- package/{commands/ace/review-story.md → skills/review-story/SKILL.md} +99 -109
- package/skills/review-story/script.js +249 -0
- package/skills/review-story/script.test.js +169 -0
- package/skills/review-story/story-template.xml +451 -0
- package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +279 -281
- package/{commands/ace/update.md → skills/update/SKILL.md} +65 -56
- package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +33 -18
- package/agile-context-engineering/src/ace-tools.js +0 -2881
- package/agile-context-engineering/src/ace-tools.test.js +0 -1089
- package/agile-context-engineering/templates/_command.md +0 -54
- package/agile-context-engineering/templates/_workflow.xml +0 -17
- package/agile-context-engineering/templates/config.json +0 -0
- package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
- package/commands/ace/help.md +0 -93
|
@@ -1,116 +1,101 @@
|
|
|
1
|
-
---
|
|
2
|
-
name:
|
|
3
|
-
description: Research and curate wiki references relevant to a story's technical solution
|
|
4
|
-
argument-hint: "story=<file-path|github-url>"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
/ace:research-story-wiki \
|
|
103
|
-
story=https://github.com/owner/repo/issues/95
|
|
104
|
-
```
|
|
105
|
-
</example-usage>
|
|
106
|
-
|
|
107
|
-
<next-steps>
|
|
108
|
-
**After this command:**
|
|
109
|
-
- Continue with story refinement — external analysis (pass 3) or technical solution (pass 5)
|
|
110
|
-
- `/ace:research-external-solution story=... external-codebase=...` — Analyze external reference systems
|
|
111
|
-
- `/ace:plan-story story=...` — Continue the full story pipeline
|
|
112
|
-
- `/ace:help` — Check project initialization status
|
|
113
|
-
</next-steps>
|
|
114
|
-
|
|
115
|
-
</command>
|
|
116
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: research-story-wiki
|
|
3
|
+
description: Research and curate wiki references relevant to a story's technical solution
|
|
4
|
+
argument-hint: "story=<file-path|github-url>"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: Read, Bash, Write, Edit, AskUserQuestion, Glob, Grep, Agent
|
|
7
|
+
model: opus
|
|
8
|
+
effort: max
|
|
9
|
+
context: fork
|
|
10
|
+
agent: ace-wiki-mapper
|
|
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-wiki-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:plan-story pass 1 — once story business requirements (sections 1-8) are complete</trigger>
|
|
31
|
+
<trigger>Anytime — when a story needs its Relevant Wiki section populated or refreshed</trigger>
|
|
32
|
+
</runs-after>
|
|
33
|
+
<use-when>
|
|
34
|
+
<condition>Story has sections 1-8 complete but no Relevant Wiki section</condition>
|
|
35
|
+
<condition>Story scope changed and wiki references need re-evaluation</condition>
|
|
36
|
+
<condition>Called as part of /ace:plan-story pipeline (pass 2)</condition>
|
|
37
|
+
</use-when>
|
|
38
|
+
</execution-time>
|
|
39
|
+
|
|
40
|
+
<input>
|
|
41
|
+
<flags>
|
|
42
|
+
</flags>
|
|
43
|
+
|
|
44
|
+
<parameters>
|
|
45
|
+
<required>
|
|
46
|
+
<param name="story" type="file | github-url">
|
|
47
|
+
Story source — can be either:
|
|
48
|
+
- **File path**: Path to a markdown file containing the story (from plan-story command)
|
|
49
|
+
- **GitHub URL or issue number**: GitHub story reference
|
|
50
|
+
Must be a valid, accessible file or GitHub issue.
|
|
51
|
+
Contains the user story, description, and acceptance criteria.
|
|
52
|
+
**The story's requirements define which wiki docs are relevant.**
|
|
53
|
+
If not valid, stop and prompt the user.
|
|
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
|
+
Research the project's engineering wiki to identify documents that are
|
|
67
|
+
directly relevant to implementing this story. The output is a curated
|
|
68
|
+
`## Relevant Wiki` section that gets written into the story file
|
|
69
|
+
(and GitHub issue if applicable).
|
|
70
|
+
|
|
71
|
+
This section serves as the implementation context index — when an agent
|
|
72
|
+
starts implementing this story, it loads every document listed here.
|
|
73
|
+
|
|
74
|
+
The research process:
|
|
75
|
+
1. ALWAYS curates the four system-wide docs with story-specific reasons
|
|
76
|
+
2. Identifies affected subsystems from story content and feature context
|
|
77
|
+
3. Spawns parallel ace-wiki-mapper agents to scan subsystem wiki docs
|
|
78
|
+
4. Compiles a categorized, deduplicated list of relevant references
|
|
79
|
+
|
|
80
|
+
All output is written to the story file — populating the Relevant Wiki
|
|
81
|
+
section placeholder left by pass 1.
|
|
82
|
+
</objective>
|
|
83
|
+
|
|
84
|
+
<artifacts>
|
|
85
|
+
Modifies in-place: the story file at
|
|
86
|
+
.ace/artifacts/product/<id-epic_name>/<id-feature_name>/<id-story_name>/<id-story_name>.md
|
|
87
|
+
(adds/replaces the ## Relevant Wiki section)
|
|
88
|
+
</artifacts>
|
|
89
|
+
</output>
|
|
90
|
+
|
|
91
|
+
<process>
|
|
92
|
+
For this command use the `ace-wiki-mapper` agent
|
|
93
|
+
that's specialized in wiki exploration and curation.
|
|
94
|
+
|
|
95
|
+
Execute the research-story-wiki workflow from
|
|
96
|
+
`workflow.xml` end-to-end.
|
|
97
|
+
Preserve all workflow gates (validation, approvals, commits).
|
|
98
|
+
</process>
|
|
99
|
+
|
|
100
|
+
<example-usage>
|
|
101
|
+
```
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* research-story-wiki skill script — Entry point for ace-tools operations
|
|
5
|
+
* needed by the research-story-wiki skill.
|
|
6
|
+
*
|
|
7
|
+
* Subcommands:
|
|
8
|
+
* init [story-param] Environment detection for research-story-wiki workflow
|
|
9
|
+
*
|
|
10
|
+
* Usage: node script.js <subcommand> [args] [--raw]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
loadConfig, pathExists, safeReadFile, loadSettings, resolveModel,
|
|
17
|
+
execCommand, output, error, runSkillScript,
|
|
18
|
+
} = require('../../shared/lib/ace-core');
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
classifyStoryParam, extractStoryMetadata, extractStoryRequirements,
|
|
22
|
+
extractWikiReferences,
|
|
23
|
+
computeStoryPaths,
|
|
24
|
+
} = require('../../shared/lib/ace-story');
|
|
25
|
+
|
|
26
|
+
// ─── CLI Dispatch ────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
runSkillScript({
|
|
29
|
+
init: cmdInit,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ─── Init: Research Story ───────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Environment detection for the research-story-wiki workflow.
|
|
36
|
+
*
|
|
37
|
+
* Replicates cmdInitResearchStory from ace-tools.js:
|
|
38
|
+
* 1. loadConfig, detect git/gh CLI/github_project
|
|
39
|
+
* 2. classifyStoryParam — validate story source
|
|
40
|
+
* 3. Load story content (from file or GitHub)
|
|
41
|
+
* 4. extractStoryMetadata, extractStoryRequirements, extractWikiReferences
|
|
42
|
+
* 5. computeStoryPaths or derive from file location
|
|
43
|
+
* 6. Check artifact existence (external/integration analysis, feature file)
|
|
44
|
+
* 7. Verify wiki doc existence
|
|
45
|
+
* 8. 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 github_project = (() => {
|
|
61
|
+
const settings = loadSettings(cwd);
|
|
62
|
+
return settings.github_project;
|
|
63
|
+
})();
|
|
64
|
+
|
|
65
|
+
// ── Classify the story parameter ──
|
|
66
|
+
const classified = classifyStoryParam(storyParam);
|
|
67
|
+
|
|
68
|
+
// Early exit if invalid
|
|
69
|
+
if (classified.type === null || classified.type === 'invalid') {
|
|
70
|
+
output({
|
|
71
|
+
analyst_model: resolveModel(cwd, 'ace-code-integration-analyst'),
|
|
72
|
+
mapper_model: resolveModel(cwd, 'ace-wiki-mapper'),
|
|
73
|
+
commit_docs: config.commit_docs,
|
|
74
|
+
has_git, has_gh_cli, github_project,
|
|
75
|
+
story_source: null,
|
|
76
|
+
story_valid: false,
|
|
77
|
+
story_error: classified.reason || 'No story parameter provided',
|
|
78
|
+
story: { id: null, title: null, status: null, size: null },
|
|
79
|
+
feature: { id: null, title: null },
|
|
80
|
+
epic: { id: null, title: null },
|
|
81
|
+
user_story: null, description: null, acceptance_criteria_count: 0,
|
|
82
|
+
paths: null,
|
|
83
|
+
has_external_analysis: false, has_integration_analysis: false, has_feature_file: false,
|
|
84
|
+
wiki_references: { system_wide: [], subsystem_docs: [], total_count: 0 },
|
|
85
|
+
wiki_docs_exist: { existing: [], missing: [] },
|
|
86
|
+
}, raw);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Load story content ──
|
|
91
|
+
let storyContent = null;
|
|
92
|
+
let storySource = classified.type === 'file' ? 'file' : 'github';
|
|
93
|
+
let storyError = null;
|
|
94
|
+
let storyFilePath = null;
|
|
95
|
+
|
|
96
|
+
if (classified.type === 'file') {
|
|
97
|
+
const resolvedPath = path.isAbsolute(classified.filePath)
|
|
98
|
+
? classified.filePath
|
|
99
|
+
: path.join(cwd, classified.filePath);
|
|
100
|
+
if (!pathExists(cwd, classified.filePath)) {
|
|
101
|
+
storyError = `Story file not found: ${classified.filePath}`;
|
|
102
|
+
} else {
|
|
103
|
+
storyContent = safeReadFile(resolvedPath);
|
|
104
|
+
storyFilePath = classified.filePath;
|
|
105
|
+
if (!storyContent) storyError = `Could not read story file: ${classified.filePath}`;
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// github-url or issue-number
|
|
109
|
+
if (!has_gh_cli) {
|
|
110
|
+
storyError = 'GitHub CLI (gh) not installed. Cannot fetch GitHub issues.';
|
|
111
|
+
} else {
|
|
112
|
+
const repo = classified.repo || (github_project.repo || null);
|
|
113
|
+
if (!repo) {
|
|
114
|
+
storyError = 'No repository configured. Provide a full GitHub URL or configure github_project.repo in settings.';
|
|
115
|
+
} else {
|
|
116
|
+
const ghResult = execCommand(
|
|
117
|
+
`gh issue view ${classified.issueNumber} --repo ${repo} --json title,body,labels,state`,
|
|
118
|
+
cwd
|
|
119
|
+
);
|
|
120
|
+
if (!ghResult) {
|
|
121
|
+
storyError = `Could not fetch GitHub issue #${classified.issueNumber} from ${repo}.`;
|
|
122
|
+
} else {
|
|
123
|
+
try {
|
|
124
|
+
const issue = JSON.parse(ghResult);
|
|
125
|
+
storyContent = issue.body || '';
|
|
126
|
+
if (storyContent && !storyContent.match(/^#\s+/m)) {
|
|
127
|
+
storyContent = `# ${issue.title}\n\n${storyContent}`;
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
storyError = `Failed to parse GitHub issue response for #${classified.issueNumber}.`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Extract metadata & requirements ──
|
|
138
|
+
const metadata = extractStoryMetadata(storyContent);
|
|
139
|
+
const requirements = extractStoryRequirements(storyContent);
|
|
140
|
+
const wikiRefs = extractWikiReferences(storyContent);
|
|
141
|
+
|
|
142
|
+
// ── Compute paths ──
|
|
143
|
+
let paths = null;
|
|
144
|
+
if (storyFilePath) {
|
|
145
|
+
const resolvedPath = path.isAbsolute(storyFilePath)
|
|
146
|
+
? storyFilePath
|
|
147
|
+
: path.join(cwd, storyFilePath);
|
|
148
|
+
const storyDir = path.dirname(resolvedPath);
|
|
149
|
+
const relStoryDir = path.relative(cwd, storyDir).replace(/\\/g, '/');
|
|
150
|
+
const storySlug = path.basename(storyDir);
|
|
151
|
+
const featureDir = path.dirname(storyDir);
|
|
152
|
+
const relFeatureDir = path.relative(cwd, featureDir).replace(/\\/g, '/');
|
|
153
|
+
const featureSlug = path.basename(featureDir);
|
|
154
|
+
|
|
155
|
+
paths = {
|
|
156
|
+
epic_slug: null,
|
|
157
|
+
feature_slug: featureSlug,
|
|
158
|
+
story_slug: storySlug,
|
|
159
|
+
story_dir: relStoryDir,
|
|
160
|
+
story_file: storyFilePath.replace(/\\/g, '/'),
|
|
161
|
+
external_analysis_file: `${relStoryDir}/external-analysis.md`,
|
|
162
|
+
integration_analysis_file: `${relStoryDir}/integration-analysis.md`,
|
|
163
|
+
feature_dir: relFeatureDir,
|
|
164
|
+
feature_file: `${relFeatureDir}/${featureSlug}.md`,
|
|
165
|
+
};
|
|
166
|
+
} else if (metadata.epic.id && metadata.feature.id && metadata.id) {
|
|
167
|
+
paths = computeStoryPaths(
|
|
168
|
+
metadata.epic.id, metadata.epic.title || '',
|
|
169
|
+
metadata.feature.id, metadata.feature.title || '',
|
|
170
|
+
metadata.id, metadata.title || ''
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Check artifact existence ──
|
|
175
|
+
const has_external_analysis = paths ? pathExists(cwd, paths.external_analysis_file) : false;
|
|
176
|
+
const has_integration_analysis = paths ? pathExists(cwd, paths.integration_analysis_file) : false;
|
|
177
|
+
const has_feature_file = paths ? pathExists(cwd, paths.feature_file) : false;
|
|
178
|
+
|
|
179
|
+
// ── Verify wiki doc existence ──
|
|
180
|
+
const allWikiPaths = [...wikiRefs.system_wide, ...wikiRefs.subsystem_docs.map(d => d.path)];
|
|
181
|
+
const wikiExisting = [];
|
|
182
|
+
const wikiMissing = [];
|
|
183
|
+
for (const wikiPath of allWikiPaths) {
|
|
184
|
+
if (pathExists(cwd, wikiPath)) {
|
|
185
|
+
wikiExisting.push(wikiPath);
|
|
186
|
+
} else {
|
|
187
|
+
wikiMissing.push(wikiPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ── Build result ──
|
|
192
|
+
const result = {
|
|
193
|
+
analyst_model: resolveModel(cwd, 'ace-code-integration-analyst'),
|
|
194
|
+
mapper_model: resolveModel(cwd, 'ace-wiki-mapper'),
|
|
195
|
+
commit_docs: config.commit_docs,
|
|
196
|
+
has_git, has_gh_cli, github_project,
|
|
197
|
+
story_source: storySource,
|
|
198
|
+
story_valid: storyContent !== null && storyError === null,
|
|
199
|
+
story_error: storyError,
|
|
200
|
+
story: {
|
|
201
|
+
id: metadata.id,
|
|
202
|
+
title: metadata.title,
|
|
203
|
+
status: metadata.status,
|
|
204
|
+
size: metadata.size,
|
|
205
|
+
},
|
|
206
|
+
feature: metadata.feature,
|
|
207
|
+
epic: metadata.epic,
|
|
208
|
+
user_story: requirements.user_story,
|
|
209
|
+
description: requirements.description,
|
|
210
|
+
acceptance_criteria_count: requirements.acceptance_criteria_count,
|
|
211
|
+
paths,
|
|
212
|
+
has_external_analysis,
|
|
213
|
+
has_integration_analysis,
|
|
214
|
+
has_feature_file,
|
|
215
|
+
wiki_references: wikiRefs,
|
|
216
|
+
wiki_docs_exist: {
|
|
217
|
+
existing: wikiExisting,
|
|
218
|
+
missing: wikiMissing,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
output(result, raw);
|
|
223
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
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('research-story-wiki 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
|
+
].join('\n');
|
|
94
|
+
|
|
95
|
+
const storyPath = createStoryFile(
|
|
96
|
+
tmpDir,
|
|
97
|
+
'.ace/artifacts/product/e1-platform/f1-user-auth/s1-add-login-button/s1-add-login-button.md',
|
|
98
|
+
storyContent
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const result = JSON.parse(runScript('init', storyPath, tmpDir));
|
|
102
|
+
|
|
103
|
+
assert.ok(result.analyst_model, 'should have analyst_model');
|
|
104
|
+
assert.ok(result.mapper_model, 'should have mapper_model');
|
|
105
|
+
assert.strictEqual(result.story_valid, true, 'story should be valid');
|
|
106
|
+
assert.strictEqual(result.story_source, 'file');
|
|
107
|
+
assert.strictEqual(result.story.id, 'S1');
|
|
108
|
+
assert.strictEqual(result.story.title, 'Add Login Button');
|
|
109
|
+
assert.strictEqual(result.story.status, 'Todo');
|
|
110
|
+
assert.strictEqual(result.acceptance_criteria_count, 1);
|
|
111
|
+
assert.ok(result.paths, 'should have computed paths');
|
|
112
|
+
assert.ok(result.paths.story_file.includes('s1-add-login-button'));
|
|
113
|
+
assert.strictEqual(typeof result.commit_docs, 'boolean');
|
|
114
|
+
assert.strictEqual(typeof result.has_git, 'boolean');
|
|
115
|
+
assert.ok(result.wiki_references, 'should have wiki_references');
|
|
116
|
+
assert.ok(result.wiki_docs_exist, 'should have wiki_docs_exist');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('errors on init without story param', () => {
|
|
120
|
+
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
121
|
+
assert.strictEqual(result.story_valid, false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('handles non-existent story file gracefully', () => {
|
|
125
|
+
const result = JSON.parse(runScript('init', 'nonexistent/story.md', tmpDir));
|
|
126
|
+
assert.strictEqual(result.story_valid, false);
|
|
127
|
+
assert.ok(result.story_error.includes('not found'));
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('error handling', () => {
|
|
132
|
+
it('errors on unknown command', () => {
|
|
133
|
+
assert.throws(() => {
|
|
134
|
+
execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|