agile-context-engineering 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +18 -0
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +7 -1
- package/README.md +16 -12
- 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-technical-application-architect.md +315 -315
- package/bin/install.js +587 -173
- package/hooks/ace-check-update.js +15 -14
- package/hooks/ace-statusline.js +30 -12
- package/hooks/hooks.json +14 -0
- package/package.json +3 -2
- package/shared/lib/ace-core.js +53 -0
- package/shared/lib/ace-core.test.js +308 -308
- package/shared/lib/ace-story.test.js +250 -250
- package/skills/execute-story/SKILL.md +116 -110
- package/skills/execute-story/script.js +13 -27
- package/skills/execute-story/script.test.js +261 -261
- package/skills/execute-story/story-template.xml +451 -451
- package/skills/execute-story/workflow.xml +3 -1
- package/skills/help/SKILL.md +71 -69
- package/skills/help/script.js +32 -35
- package/skills/help/script.test.js +183 -183
- package/skills/help/workflow.xml +14 -3
- package/skills/init-coding-standards/SKILL.md +91 -72
- package/skills/init-coding-standards/coding-standards-template.xml +531 -531
- package/skills/init-coding-standards/script.js +50 -59
- package/skills/init-coding-standards/script.test.js +70 -70
- package/skills/init-coding-standards/workflow.xml +1 -1
- package/skills/map-cross-cutting/SKILL.md +126 -89
- package/skills/map-cross-cutting/workflow.xml +1 -1
- package/skills/map-guide/SKILL.md +126 -89
- package/skills/map-guide/workflow.xml +1 -1
- package/skills/map-pattern/SKILL.md +125 -89
- package/skills/map-pattern/workflow.xml +1 -1
- package/skills/map-story/SKILL.md +180 -127
- package/skills/map-story/templates/tech-debt-index.xml +125 -125
- package/skills/map-story/workflow.xml +2 -2
- package/skills/map-subsystem/SKILL.md +155 -111
- package/skills/map-subsystem/script.js +51 -60
- package/skills/map-subsystem/script.test.js +68 -68
- package/skills/map-subsystem/templates/subsystem-architecture.xml +343 -343
- package/skills/map-subsystem/templates/subsystem-structure.xml +234 -234
- package/skills/map-subsystem/workflow.xml +1173 -1173
- package/skills/map-sys-doc/SKILL.md +125 -90
- package/skills/map-sys-doc/workflow.xml +1 -1
- package/skills/map-system/SKILL.md +103 -85
- package/skills/map-system/script.js +75 -84
- package/skills/map-system/script.test.js +73 -73
- package/skills/map-system/templates/system-structure.xml +177 -177
- package/skills/map-system/templates/testing-framework.xml +283 -283
- package/skills/map-system/workflow.xml +667 -667
- package/skills/map-walkthrough/SKILL.md +140 -92
- package/skills/map-walkthrough/workflow.xml +457 -457
- package/skills/plan-backlog/SKILL.md +93 -75
- package/skills/plan-backlog/script.js +121 -136
- package/skills/plan-backlog/script.test.js +83 -83
- package/skills/plan-backlog/workflow.xml +1348 -1348
- package/skills/plan-feature/SKILL.md +99 -76
- package/skills/plan-feature/feature-template.xml +361 -361
- package/skills/plan-feature/script.js +131 -148
- package/skills/plan-feature/script.test.js +80 -80
- package/skills/plan-feature/workflow.xml +1 -1
- package/skills/plan-product-vision/SKILL.md +91 -75
- package/skills/plan-product-vision/product-vision-template.xml +227 -227
- package/skills/plan-product-vision/script.js +51 -60
- package/skills/plan-product-vision/script.test.js +69 -69
- package/skills/plan-product-vision/workflow.xml +337 -337
- package/skills/plan-story/SKILL.md +125 -102
- package/skills/plan-story/script.js +18 -49
- package/skills/plan-story/story-template.xml +8 -1
- package/skills/plan-story/workflow.xml +17 -1
- package/skills/research-external-solution/SKILL.md +120 -107
- package/skills/research-external-solution/external-solution-template.xml +832 -832
- package/skills/research-external-solution/script.js +229 -238
- package/skills/research-external-solution/script.test.js +134 -134
- package/skills/research-external-solution/workflow.xml +657 -657
- package/skills/research-integration-solution/SKILL.md +121 -98
- package/skills/research-integration-solution/integration-solution-template.xml +1015 -1015
- package/skills/research-integration-solution/script.js +223 -231
- package/skills/research-integration-solution/script.test.js +134 -134
- package/skills/research-integration-solution/workflow.xml +711 -711
- package/skills/research-story-wiki/SKILL.md +101 -92
- package/skills/research-story-wiki/script.js +223 -231
- package/skills/research-story-wiki/script.test.js +138 -138
- package/skills/research-story-wiki/story-wiki-template.xml +194 -194
- package/skills/research-story-wiki/workflow.xml +473 -473
- package/skills/research-technical-solution/SKILL.md +131 -103
- package/skills/research-technical-solution/script.js +223 -231
- package/skills/research-technical-solution/script.test.js +134 -134
- package/skills/research-technical-solution/technical-solution-template.xml +1025 -1025
- package/skills/research-technical-solution/workflow.xml +761 -761
- package/skills/review-story/SKILL.md +99 -100
- package/skills/review-story/script.js +8 -16
- package/skills/review-story/script.test.js +169 -169
- package/skills/review-story/story-template.xml +451 -451
- package/skills/review-story/workflow.xml +1 -1
- package/skills/update/SKILL.md +65 -53
- package/skills/update/workflow.xml +21 -5
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</purpose>
|
|
18
18
|
|
|
19
19
|
<mandatory-context>
|
|
20
|
-
|
|
20
|
+
All supporting resource files are auto-loaded in the skill prompt above. Do NOT re-read them.
|
|
21
21
|
Also read any document or text passed as parameter ($ARGUMENTS) in the invoking command.
|
|
22
22
|
</mandatory-context>
|
|
23
23
|
|
|
@@ -556,6 +556,8 @@
|
|
|
556
556
|
- Follow the plan step by step — do NOT skip steps or reorder
|
|
557
557
|
- After each significant step: run build check + test check
|
|
558
558
|
- NO commits — all changes stay as working tree changes
|
|
559
|
+
- NEVER use node -e with Windows backslash paths in bash — backslashes
|
|
560
|
+
get eaten as escape characters. Use the Read tool to read files.
|
|
559
561
|
- Auto-fix bugs, missing critical code, and blocking issues (Rules 1-3)
|
|
560
562
|
- For architectural changes beyond story scope (Rule 4): STOP and include
|
|
561
563
|
in your return report — main will ask the user
|
package/skills/help/SKILL.md
CHANGED
|
@@ -1,69 +1,71 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: help
|
|
3
|
-
description: Check project initialization status and suggest next steps
|
|
4
|
-
argument-hint: ""
|
|
5
|
-
disable-model-invocation: false
|
|
6
|
-
allowed-tools: Read, Bash, Write, AskUserQuestion
|
|
7
|
-
model: sonnet
|
|
8
|
-
effort: medium
|
|
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
|
-
|
|
1
|
+
---
|
|
2
|
+
name: help
|
|
3
|
+
description: Check project initialization status and suggest next steps
|
|
4
|
+
argument-hint: ""
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
allowed-tools: Read, Bash, Write, AskUserQuestion
|
|
7
|
+
model: sonnet
|
|
8
|
+
effort: medium
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Supporting Resources (auto-loaded)
|
|
12
|
+
|
|
13
|
+
!`cat "${CLAUDE_SKILL_DIR}/workflow.xml"`
|
|
14
|
+
|
|
15
|
+
!`cat "${CLAUDE_SKILL_DIR}/../../shared/utils/questioning.xml"`
|
|
16
|
+
|
|
17
|
+
!`cat "${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md"`
|
|
18
|
+
|
|
19
|
+
```xml
|
|
20
|
+
<command>
|
|
21
|
+
|
|
22
|
+
<execution-time>
|
|
23
|
+
<runs-after>
|
|
24
|
+
<trigger>At any time — to check which ACE documents exist and what to do next</trigger>
|
|
25
|
+
<trigger>At the start of a new project — to see the initialization checklist</trigger>
|
|
26
|
+
</runs-after>
|
|
27
|
+
<use-when>
|
|
28
|
+
<condition>Starting a new project and want to see what needs to be set up</condition>
|
|
29
|
+
<condition>Returning to a project and want to check initialization status</condition>
|
|
30
|
+
<condition>Unsure which ACE command to run next</condition>
|
|
31
|
+
</use-when>
|
|
32
|
+
</execution-time>
|
|
33
|
+
|
|
34
|
+
<input>
|
|
35
|
+
<flags>
|
|
36
|
+
</flags>
|
|
37
|
+
|
|
38
|
+
<parameters>
|
|
39
|
+
<required>
|
|
40
|
+
</required>
|
|
41
|
+
|
|
42
|
+
<optional>
|
|
43
|
+
</optional>
|
|
44
|
+
</parameters>
|
|
45
|
+
</input>
|
|
46
|
+
|
|
47
|
+
<execution-context>
|
|
48
|
+
<!-- All supporting files are auto-loaded in the Supporting Resources section above.
|
|
49
|
+
The model does NOT need to Read these files — they are already in context. -->
|
|
50
|
+
</execution-context>
|
|
51
|
+
|
|
52
|
+
<output>
|
|
53
|
+
<objective>
|
|
54
|
+
Detect which ACE documents exist (product vision, system architecture, system structure,
|
|
55
|
+
coding standards, testing framework). Display a status dashboard showing what's done
|
|
56
|
+
and what's missing. Suggest the next command to run based on gaps.
|
|
57
|
+
</objective>
|
|
58
|
+
|
|
59
|
+
<artifacts>
|
|
60
|
+
- .ace/settings.json (created on first run if missing)
|
|
61
|
+
</artifacts>
|
|
62
|
+
</output>
|
|
63
|
+
|
|
64
|
+
<process>
|
|
65
|
+
Execute the help workflow from
|
|
66
|
+
`workflow.xml` end-to-end.
|
|
67
|
+
This is a lightweight state-check and routing workflow.
|
|
68
|
+
|
|
69
|
+
CRITICAL MANDATORY STEP — DO NOT SKIP:
|
|
70
|
+
Before displaying the status dashboard, you MUST run:
|
|
71
|
+
```
|
package/skills/help/script.js
CHANGED
|
@@ -22,7 +22,7 @@ const path = require('path');
|
|
|
22
22
|
const {
|
|
23
23
|
loadConfig, pathExists, resolveModel,
|
|
24
24
|
detectBrownfieldStatus, loadSettings, writeSettings,
|
|
25
|
-
output, error,
|
|
25
|
+
output, error, runSkillScript,
|
|
26
26
|
} = require('../../shared/lib/ace-core');
|
|
27
27
|
|
|
28
28
|
// ─── Runtime Config Dir ─────────────────────────────────────────────────────
|
|
@@ -31,7 +31,7 @@ const {
|
|
|
31
31
|
* Detect the runtime config directory name.
|
|
32
32
|
* In the plugin context, the script lives at:
|
|
33
33
|
* <base>/<config-dir>/skills/help/script.js
|
|
34
|
-
* Default to '.claude'
|
|
34
|
+
* Default to '.claude' for backwards compatibility.
|
|
35
35
|
*/
|
|
36
36
|
function getRuntimeConfigDirName() {
|
|
37
37
|
try {
|
|
@@ -39,7 +39,7 @@ function getRuntimeConfigDirName() {
|
|
|
39
39
|
const skillsDir = path.dirname(skillDir); // <base>/<config-dir>/skills
|
|
40
40
|
const configDir = path.dirname(skillsDir); // <base>/<config-dir>
|
|
41
41
|
const dirName = path.basename(configDir);
|
|
42
|
-
if (dirName === '.opencode' || dirName === '.claude') {
|
|
42
|
+
if (dirName === '.opencode' || dirName === '.codex' || dirName === '.claude') {
|
|
43
43
|
return dirName;
|
|
44
44
|
}
|
|
45
45
|
} catch {}
|
|
@@ -50,40 +50,20 @@ const RUNTIME_CONFIG_DIR = getRuntimeConfigDirName();
|
|
|
50
50
|
|
|
51
51
|
// ─── CLI Dispatch ────────────────────────────────────────────────────────────
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
case 'ensure-settings':
|
|
63
|
-
cmdEnsureSettings(cwd, raw);
|
|
64
|
-
break;
|
|
65
|
-
case 'setup-github':
|
|
66
|
-
cmdSetupGithubProject(cwd, raw);
|
|
67
|
-
break;
|
|
68
|
-
case 'write-github-settings':
|
|
69
|
-
cmdWriteGithubSettings(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
|
|
70
|
-
break;
|
|
71
|
-
case 'sync-agent-teams':
|
|
72
|
-
cmdSyncAgentTeams(cwd, raw);
|
|
73
|
-
break;
|
|
74
|
-
case 'write-agent-teams':
|
|
75
|
-
cmdWriteAgentTeamsSetting(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
|
|
76
|
-
break;
|
|
77
|
-
case 'verify-path-exists': {
|
|
78
|
-
const targetPath = args[1];
|
|
53
|
+
runSkillScript({
|
|
54
|
+
init: cmdInit,
|
|
55
|
+
'ensure-settings': (cwd, raw) => cmdEnsureSettings(cwd, raw),
|
|
56
|
+
'setup-github': (cwd, raw) => cmdSetupGithubProject(cwd, raw),
|
|
57
|
+
'write-github-settings': (cwd, raw, args) => cmdWriteGithubSettings(cwd, raw, args),
|
|
58
|
+
'sync-agent-teams': (cwd, raw) => cmdSyncAgentTeams(cwd, raw),
|
|
59
|
+
'write-agent-teams': (cwd, raw, args) => cmdWriteAgentTeamsSetting(cwd, raw, args),
|
|
60
|
+
'verify-path-exists': (cwd, raw, args, parsed) => {
|
|
61
|
+
const targetPath = parsed._positional || args[0];
|
|
79
62
|
if (!targetPath) error('path required for verify-path-exists');
|
|
80
63
|
const exists = pathExists(cwd, targetPath);
|
|
81
64
|
output({ exists, path: targetPath }, raw, exists ? 'true' : 'false');
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
default:
|
|
85
|
-
error(`Unknown command: ${cmd}\nAvailable: init, ensure-settings, setup-github, write-github-settings, sync-agent-teams, write-agent-teams, verify-path-exists`);
|
|
86
|
-
}
|
|
65
|
+
},
|
|
66
|
+
});
|
|
87
67
|
|
|
88
68
|
// ─── Init: Help ─────────────────────────────────────────────────────────────
|
|
89
69
|
|
|
@@ -103,6 +83,7 @@ function cmdInit(cwd, raw) {
|
|
|
103
83
|
|
|
104
84
|
// Config
|
|
105
85
|
commit_docs: config.commit_docs,
|
|
86
|
+
runtime_config_dir: RUNTIME_CONFIG_DIR,
|
|
106
87
|
|
|
107
88
|
// Existing state
|
|
108
89
|
has_product_vision: pathExists(cwd, '.docs/product/product-vision.md'),
|
|
@@ -252,6 +233,17 @@ function cmdWriteGithubSettings(cwd, raw, extraArgs) {
|
|
|
252
233
|
// ─── Sync Agent Teams ───────────────────────────────────────────────────────
|
|
253
234
|
|
|
254
235
|
function cmdSyncAgentTeams(cwd, raw) {
|
|
236
|
+
if (RUNTIME_CONFIG_DIR === '.codex') {
|
|
237
|
+
const settings = loadSettings(cwd);
|
|
238
|
+
const wasDifferent = settings.agent_teams !== false;
|
|
239
|
+
if (wasDifferent) {
|
|
240
|
+
settings.agent_teams = false;
|
|
241
|
+
writeSettings(cwd, settings);
|
|
242
|
+
}
|
|
243
|
+
output({ agent_teams: false, synced: wasDifferent, runtime: 'codex' }, raw);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
255
247
|
// Source of truth: runtime settings.json env var (e.g. .claude/settings.json)
|
|
256
248
|
const claudeSettingsPath = path.join(cwd, RUNTIME_CONFIG_DIR, 'settings.json');
|
|
257
249
|
let claudeEnabled = false;
|
|
@@ -280,9 +272,14 @@ function cmdSyncAgentTeams(cwd, raw) {
|
|
|
280
272
|
function cmdWriteAgentTeamsSetting(cwd, raw, extraArgs) {
|
|
281
273
|
const enabled = extraArgs[0] === 'true';
|
|
282
274
|
const settings = loadSettings(cwd);
|
|
283
|
-
settings.agent_teams = enabled;
|
|
275
|
+
settings.agent_teams = RUNTIME_CONFIG_DIR === '.codex' ? false : enabled;
|
|
284
276
|
writeSettings(cwd, settings);
|
|
285
277
|
|
|
278
|
+
if (RUNTIME_CONFIG_DIR === '.codex') {
|
|
279
|
+
output({ written: true, agent_teams: false, settings, runtime: 'codex' }, raw);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
286
283
|
// Also update the project's runtime settings.json (e.g. .claude/)
|
|
287
284
|
const claudeDir = path.join(cwd, RUNTIME_CONFIG_DIR);
|
|
288
285
|
const claudeSettingsPath = path.join(claudeDir, 'settings.json');
|
|
@@ -1,183 +1,183 @@
|
|
|
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
|
-
function runScript(subcommand, args, cwd) {
|
|
39
|
-
return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
|
|
40
|
-
cwd,
|
|
41
|
-
encoding: 'utf-8',
|
|
42
|
-
timeout: 10000,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function cleanup(tmpDir) {
|
|
47
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
describe('help script', () => {
|
|
53
|
-
|
|
54
|
-
describe('init', () => {
|
|
55
|
-
let tmpDir;
|
|
56
|
-
|
|
57
|
-
before(() => { tmpDir = createTestProject(); });
|
|
58
|
-
after(() => { cleanup(tmpDir); });
|
|
59
|
-
|
|
60
|
-
it('returns valid JSON with environment detection', () => {
|
|
61
|
-
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
62
|
-
|
|
63
|
-
assert.strictEqual(typeof result.commit_docs, 'boolean');
|
|
64
|
-
assert.strictEqual(typeof result.has_git, 'boolean');
|
|
65
|
-
assert.strictEqual(typeof result.has_product_vision, 'boolean');
|
|
66
|
-
assert.strictEqual(typeof result.has_system_architecture, 'boolean');
|
|
67
|
-
assert.strictEqual(typeof result.has_system_structure, 'boolean');
|
|
68
|
-
assert.strictEqual(typeof result.has_coding_standards, 'boolean');
|
|
69
|
-
assert.strictEqual(typeof result.has_testing_framework, 'boolean');
|
|
70
|
-
assert.strictEqual(typeof result.is_brownfield, 'boolean');
|
|
71
|
-
assert.strictEqual(typeof result.is_greenfield, 'boolean');
|
|
72
|
-
assert.ok(result.product_owner_model, 'should have product_owner_model');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('detects brownfield vs greenfield correctly', () => {
|
|
76
|
-
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
77
|
-
assert.strictEqual(result.is_brownfield, !result.is_greenfield);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('ensure-settings', () => {
|
|
82
|
-
let tmpDir;
|
|
83
|
-
|
|
84
|
-
before(() => { tmpDir = createTestProject(); });
|
|
85
|
-
after(() => { cleanup(tmpDir); });
|
|
86
|
-
|
|
87
|
-
it('reports settings already exist when they do', () => {
|
|
88
|
-
const result = JSON.parse(runScript('ensure-settings', '', tmpDir));
|
|
89
|
-
assert.strictEqual(result.created, false);
|
|
90
|
-
assert.ok(result.settings);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('creates settings when they do not exist', () => {
|
|
94
|
-
const freshDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
|
|
95
|
-
const aceDir = path.join(freshDir, '.ace');
|
|
96
|
-
fs.mkdirSync(aceDir, { recursive: true });
|
|
97
|
-
fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
|
|
98
|
-
version: '0.1.0',
|
|
99
|
-
projectName: 'test',
|
|
100
|
-
model_profile: 'balanced',
|
|
101
|
-
commit_docs: true,
|
|
102
|
-
github: { enabled: false },
|
|
103
|
-
}, null, 2));
|
|
104
|
-
|
|
105
|
-
const result = JSON.parse(runScript('ensure-settings', '', freshDir));
|
|
106
|
-
assert.strictEqual(result.created, true);
|
|
107
|
-
assert.ok(result.settings);
|
|
108
|
-
assert.strictEqual(result.settings.model_profile, 'balanced');
|
|
109
|
-
|
|
110
|
-
cleanup(freshDir);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('sync-agent-teams', () => {
|
|
115
|
-
let tmpDir;
|
|
116
|
-
|
|
117
|
-
before(() => { tmpDir = createTestProject(); });
|
|
118
|
-
after(() => { cleanup(tmpDir); });
|
|
119
|
-
|
|
120
|
-
it('returns agent_teams boolean', () => {
|
|
121
|
-
const result = JSON.parse(runScript('sync-agent-teams', '', tmpDir));
|
|
122
|
-
assert.strictEqual(typeof result.agent_teams, 'boolean');
|
|
123
|
-
assert.strictEqual(typeof result.synced, 'boolean');
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
describe('write-agent-teams', () => {
|
|
128
|
-
let tmpDir;
|
|
129
|
-
|
|
130
|
-
before(() => { tmpDir = createTestProject(); });
|
|
131
|
-
after(() => { cleanup(tmpDir); });
|
|
132
|
-
|
|
133
|
-
it('enables agent teams', () => {
|
|
134
|
-
const result = JSON.parse(runScript('write-agent-teams', 'true', tmpDir));
|
|
135
|
-
assert.strictEqual(result.written, true);
|
|
136
|
-
assert.strictEqual(result.agent_teams, true);
|
|
137
|
-
|
|
138
|
-
// Verify .ace/settings.json updated
|
|
139
|
-
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
140
|
-
assert.strictEqual(settings.agent_teams, true);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('disables agent teams', () => {
|
|
144
|
-
const result = JSON.parse(runScript('write-agent-teams', 'false', tmpDir));
|
|
145
|
-
assert.strictEqual(result.written, true);
|
|
146
|
-
assert.strictEqual(result.agent_teams, false);
|
|
147
|
-
|
|
148
|
-
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
149
|
-
assert.strictEqual(settings.agent_teams, false);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe('verify-path-exists', () => {
|
|
154
|
-
let tmpDir;
|
|
155
|
-
|
|
156
|
-
before(() => { tmpDir = createTestProject(); });
|
|
157
|
-
after(() => { cleanup(tmpDir); });
|
|
158
|
-
|
|
159
|
-
it('returns true for existing path', () => {
|
|
160
|
-
const result = runScript('verify-path-exists', '.ace/config.json --raw', tmpDir).trim();
|
|
161
|
-
assert.strictEqual(result, 'true');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('returns false for non-existing path', () => {
|
|
165
|
-
const result = runScript('verify-path-exists', 'nonexistent/file.md --raw', tmpDir).trim();
|
|
166
|
-
assert.strictEqual(result, 'false');
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('error handling', () => {
|
|
171
|
-
it('errors on unknown command', () => {
|
|
172
|
-
assert.throws(() => {
|
|
173
|
-
execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('errors on verify-path-exists without path', () => {
|
|
178
|
-
assert.throws(() => {
|
|
179
|
-
execSync(`node "${SCRIPT}" verify-path-exists`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
});
|
|
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
|
+
function runScript(subcommand, args, cwd) {
|
|
39
|
+
return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
|
|
40
|
+
cwd,
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
timeout: 10000,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cleanup(tmpDir) {
|
|
47
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
describe('help script', () => {
|
|
53
|
+
|
|
54
|
+
describe('init', () => {
|
|
55
|
+
let tmpDir;
|
|
56
|
+
|
|
57
|
+
before(() => { tmpDir = createTestProject(); });
|
|
58
|
+
after(() => { cleanup(tmpDir); });
|
|
59
|
+
|
|
60
|
+
it('returns valid JSON with environment detection', () => {
|
|
61
|
+
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
62
|
+
|
|
63
|
+
assert.strictEqual(typeof result.commit_docs, 'boolean');
|
|
64
|
+
assert.strictEqual(typeof result.has_git, 'boolean');
|
|
65
|
+
assert.strictEqual(typeof result.has_product_vision, 'boolean');
|
|
66
|
+
assert.strictEqual(typeof result.has_system_architecture, 'boolean');
|
|
67
|
+
assert.strictEqual(typeof result.has_system_structure, 'boolean');
|
|
68
|
+
assert.strictEqual(typeof result.has_coding_standards, 'boolean');
|
|
69
|
+
assert.strictEqual(typeof result.has_testing_framework, 'boolean');
|
|
70
|
+
assert.strictEqual(typeof result.is_brownfield, 'boolean');
|
|
71
|
+
assert.strictEqual(typeof result.is_greenfield, 'boolean');
|
|
72
|
+
assert.ok(result.product_owner_model, 'should have product_owner_model');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('detects brownfield vs greenfield correctly', () => {
|
|
76
|
+
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
77
|
+
assert.strictEqual(result.is_brownfield, !result.is_greenfield);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('ensure-settings', () => {
|
|
82
|
+
let tmpDir;
|
|
83
|
+
|
|
84
|
+
before(() => { tmpDir = createTestProject(); });
|
|
85
|
+
after(() => { cleanup(tmpDir); });
|
|
86
|
+
|
|
87
|
+
it('reports settings already exist when they do', () => {
|
|
88
|
+
const result = JSON.parse(runScript('ensure-settings', '', tmpDir));
|
|
89
|
+
assert.strictEqual(result.created, false);
|
|
90
|
+
assert.ok(result.settings);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('creates settings when they do not exist', () => {
|
|
94
|
+
const freshDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
|
|
95
|
+
const aceDir = path.join(freshDir, '.ace');
|
|
96
|
+
fs.mkdirSync(aceDir, { recursive: true });
|
|
97
|
+
fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
|
|
98
|
+
version: '0.1.0',
|
|
99
|
+
projectName: 'test',
|
|
100
|
+
model_profile: 'balanced',
|
|
101
|
+
commit_docs: true,
|
|
102
|
+
github: { enabled: false },
|
|
103
|
+
}, null, 2));
|
|
104
|
+
|
|
105
|
+
const result = JSON.parse(runScript('ensure-settings', '', freshDir));
|
|
106
|
+
assert.strictEqual(result.created, true);
|
|
107
|
+
assert.ok(result.settings);
|
|
108
|
+
assert.strictEqual(result.settings.model_profile, 'balanced');
|
|
109
|
+
|
|
110
|
+
cleanup(freshDir);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('sync-agent-teams', () => {
|
|
115
|
+
let tmpDir;
|
|
116
|
+
|
|
117
|
+
before(() => { tmpDir = createTestProject(); });
|
|
118
|
+
after(() => { cleanup(tmpDir); });
|
|
119
|
+
|
|
120
|
+
it('returns agent_teams boolean', () => {
|
|
121
|
+
const result = JSON.parse(runScript('sync-agent-teams', '', tmpDir));
|
|
122
|
+
assert.strictEqual(typeof result.agent_teams, 'boolean');
|
|
123
|
+
assert.strictEqual(typeof result.synced, 'boolean');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('write-agent-teams', () => {
|
|
128
|
+
let tmpDir;
|
|
129
|
+
|
|
130
|
+
before(() => { tmpDir = createTestProject(); });
|
|
131
|
+
after(() => { cleanup(tmpDir); });
|
|
132
|
+
|
|
133
|
+
it('enables agent teams', () => {
|
|
134
|
+
const result = JSON.parse(runScript('write-agent-teams', 'true', tmpDir));
|
|
135
|
+
assert.strictEqual(result.written, true);
|
|
136
|
+
assert.strictEqual(result.agent_teams, true);
|
|
137
|
+
|
|
138
|
+
// Verify .ace/settings.json updated
|
|
139
|
+
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
140
|
+
assert.strictEqual(settings.agent_teams, true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('disables agent teams', () => {
|
|
144
|
+
const result = JSON.parse(runScript('write-agent-teams', 'false', tmpDir));
|
|
145
|
+
assert.strictEqual(result.written, true);
|
|
146
|
+
assert.strictEqual(result.agent_teams, false);
|
|
147
|
+
|
|
148
|
+
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
149
|
+
assert.strictEqual(settings.agent_teams, false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('verify-path-exists', () => {
|
|
154
|
+
let tmpDir;
|
|
155
|
+
|
|
156
|
+
before(() => { tmpDir = createTestProject(); });
|
|
157
|
+
after(() => { cleanup(tmpDir); });
|
|
158
|
+
|
|
159
|
+
it('returns true for existing path', () => {
|
|
160
|
+
const result = runScript('verify-path-exists', '.ace/config.json --raw', tmpDir).trim();
|
|
161
|
+
assert.strictEqual(result, 'true');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('returns false for non-existing path', () => {
|
|
165
|
+
const result = runScript('verify-path-exists', 'nonexistent/file.md --raw', tmpDir).trim();
|
|
166
|
+
assert.strictEqual(result, 'false');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('error handling', () => {
|
|
171
|
+
it('errors on unknown command', () => {
|
|
172
|
+
assert.throws(() => {
|
|
173
|
+
execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('errors on verify-path-exists without path', () => {
|
|
178
|
+
assert.throws(() => {
|
|
179
|
+
execSync(`node "${SCRIPT}" verify-path-exists`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|