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