agile-context-engineering 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +7 -1
  4. package/README.md +16 -12
  5. package/agents/ace-code-discovery-analyst.md +245 -245
  6. package/agents/ace-code-integration-analyst.md +248 -248
  7. package/agents/ace-code-reviewer.md +375 -375
  8. package/agents/ace-product-owner.md +365 -361
  9. package/agents/ace-project-researcher.md +606 -606
  10. package/agents/ace-technical-application-architect.md +315 -315
  11. package/bin/install.js +587 -173
  12. package/hooks/ace-check-update.js +15 -14
  13. package/hooks/ace-statusline.js +30 -12
  14. package/hooks/hooks.json +14 -0
  15. package/package.json +3 -2
  16. package/shared/lib/ace-core.js +53 -0
  17. package/shared/lib/ace-core.test.js +308 -308
  18. package/shared/lib/ace-story.test.js +250 -250
  19. package/skills/execute-story/SKILL.md +116 -110
  20. package/skills/execute-story/script.js +13 -27
  21. package/skills/execute-story/script.test.js +261 -261
  22. package/skills/execute-story/story-template.xml +451 -451
  23. package/skills/execute-story/workflow.xml +3 -1
  24. package/skills/help/SKILL.md +71 -69
  25. package/skills/help/script.js +32 -35
  26. package/skills/help/script.test.js +183 -183
  27. package/skills/help/workflow.xml +14 -3
  28. package/skills/init-coding-standards/SKILL.md +91 -72
  29. package/skills/init-coding-standards/coding-standards-template.xml +531 -531
  30. package/skills/init-coding-standards/script.js +50 -59
  31. package/skills/init-coding-standards/script.test.js +70 -70
  32. package/skills/init-coding-standards/workflow.xml +1 -1
  33. package/skills/map-cross-cutting/SKILL.md +126 -89
  34. package/skills/map-cross-cutting/workflow.xml +1 -1
  35. package/skills/map-guide/SKILL.md +126 -89
  36. package/skills/map-guide/workflow.xml +1 -1
  37. package/skills/map-pattern/SKILL.md +125 -89
  38. package/skills/map-pattern/workflow.xml +1 -1
  39. package/skills/map-story/SKILL.md +180 -127
  40. package/skills/map-story/templates/tech-debt-index.xml +125 -125
  41. package/skills/map-story/workflow.xml +2 -2
  42. package/skills/map-subsystem/SKILL.md +155 -111
  43. package/skills/map-subsystem/script.js +51 -60
  44. package/skills/map-subsystem/script.test.js +68 -68
  45. package/skills/map-subsystem/templates/subsystem-architecture.xml +343 -343
  46. package/skills/map-subsystem/templates/subsystem-structure.xml +234 -234
  47. package/skills/map-subsystem/workflow.xml +1173 -1173
  48. package/skills/map-sys-doc/SKILL.md +125 -90
  49. package/skills/map-sys-doc/workflow.xml +1 -1
  50. package/skills/map-system/SKILL.md +103 -85
  51. package/skills/map-system/script.js +75 -84
  52. package/skills/map-system/script.test.js +73 -73
  53. package/skills/map-system/templates/system-structure.xml +177 -177
  54. package/skills/map-system/templates/testing-framework.xml +283 -283
  55. package/skills/map-system/workflow.xml +667 -667
  56. package/skills/map-walkthrough/SKILL.md +140 -92
  57. package/skills/map-walkthrough/workflow.xml +457 -457
  58. package/skills/plan-backlog/SKILL.md +93 -75
  59. package/skills/plan-backlog/script.js +121 -136
  60. package/skills/plan-backlog/script.test.js +83 -83
  61. package/skills/plan-backlog/workflow.xml +1348 -1348
  62. package/skills/plan-feature/SKILL.md +99 -76
  63. package/skills/plan-feature/feature-template.xml +361 -361
  64. package/skills/plan-feature/script.js +131 -148
  65. package/skills/plan-feature/script.test.js +80 -80
  66. package/skills/plan-feature/workflow.xml +1 -1
  67. package/skills/plan-product-vision/SKILL.md +91 -75
  68. package/skills/plan-product-vision/product-vision-template.xml +227 -227
  69. package/skills/plan-product-vision/script.js +51 -60
  70. package/skills/plan-product-vision/script.test.js +69 -69
  71. package/skills/plan-product-vision/workflow.xml +337 -337
  72. package/skills/plan-story/SKILL.md +125 -102
  73. package/skills/plan-story/script.js +18 -49
  74. package/skills/plan-story/story-template.xml +8 -1
  75. package/skills/plan-story/workflow.xml +17 -1
  76. package/skills/research-external-solution/SKILL.md +120 -107
  77. package/skills/research-external-solution/external-solution-template.xml +832 -832
  78. package/skills/research-external-solution/script.js +229 -238
  79. package/skills/research-external-solution/script.test.js +134 -134
  80. package/skills/research-external-solution/workflow.xml +657 -657
  81. package/skills/research-integration-solution/SKILL.md +121 -98
  82. package/skills/research-integration-solution/integration-solution-template.xml +1015 -1015
  83. package/skills/research-integration-solution/script.js +223 -231
  84. package/skills/research-integration-solution/script.test.js +134 -134
  85. package/skills/research-integration-solution/workflow.xml +711 -711
  86. package/skills/research-story-wiki/SKILL.md +101 -92
  87. package/skills/research-story-wiki/script.js +223 -231
  88. package/skills/research-story-wiki/script.test.js +138 -138
  89. package/skills/research-story-wiki/story-wiki-template.xml +194 -194
  90. package/skills/research-story-wiki/workflow.xml +473 -473
  91. package/skills/research-technical-solution/SKILL.md +131 -103
  92. package/skills/research-technical-solution/script.js +223 -231
  93. package/skills/research-technical-solution/script.test.js +134 -134
  94. package/skills/research-technical-solution/technical-solution-template.xml +1025 -1025
  95. package/skills/research-technical-solution/workflow.xml +761 -761
  96. package/skills/review-story/SKILL.md +99 -100
  97. package/skills/review-story/script.js +8 -16
  98. package/skills/review-story/script.test.js +169 -169
  99. package/skills/review-story/story-template.xml +451 -451
  100. package/skills/review-story/workflow.xml +1 -1
  101. package/skills/update/SKILL.md +65 -53
  102. package/skills/update/workflow.xml +21 -5
@@ -17,7 +17,7 @@
17
17
  </purpose>
18
18
 
19
19
  <mandatory-context>
20
- Read all files referenced by the invoking command's execution-context before starting.
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
@@ -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
- # Help
12
-
13
- Interactive state-checker, settings configurator, and guided router for project initialization. Detects which ACE documents exist, ensures settings are configured (including GitHub Project integration), displays a status dashboard, and offers to run missing setup commands.
14
-
15
- ## When to Use
16
-
17
- - At any time to check which ACE documents exist and what to do next
18
- - At the start of a new project to see the initialization checklist
19
- - Returning to a project and want to check initialization status
20
- - Unsure which ACE command to run next
21
-
22
- ## Input
23
-
24
- No parameters required.
25
-
26
- ## Supporting Resources
27
-
28
- Read ALL of these before starting the workflow:
29
-
30
- - **Workflow**: Read [workflow.xml](workflow.xml) -- complete orchestration process with all steps
31
- - **Questioning guide**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/questioning.xml` -- deep questioning techniques
32
- - **UI formatting**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md` -- ACE output formatting rules
33
-
34
- ## Process
35
-
36
- Execute the help workflow from [workflow.xml](workflow.xml) end-to-end.
37
- This is a lightweight state-check and routing workflow.
38
-
39
- CRITICAL MANDATORY STEP -- DO NOT SKIP:
40
- Before displaying the status dashboard, run:
41
- ```bash
42
- node "${CLAUDE_SKILL_DIR}/script.js" sync-agent-teams --raw
43
- ```
44
- Then you MUST use `AskUserQuestion` to ask the user whether they want to
45
- enable or disable Claude Code Agent Teams (experimental feature).
46
- This step is NOT optional. You MUST present this question every time.
47
-
48
- When the user chooses to enable or disable Agent Teams, you MUST run the
49
- bash command -- NEVER directly edit .ace/settings.json or .claude/settings.json:
50
- ```bash
51
- node "${CLAUDE_SKILL_DIR}/script.js" write-agent-teams true
52
- ```
53
- or
54
- ```bash
55
- node "${CLAUDE_SKILL_DIR}/script.js" write-agent-teams false
56
- ```
57
- The write-agent-teams command updates BOTH .ace/settings.json AND .claude/settings.json.
58
- Direct file edits will cause the two files to go out of sync.
59
-
60
- ## Artifacts
61
-
62
- - `.ace/settings.json` (created on first run if missing)
63
-
64
- ## Next Steps
65
-
66
- **Specialized commands for each document:**
67
- - `/ace:plan-product-vision` -- Create or update the product vision
68
- - `/ace:map-system` -- Map codebase structure, architecture, and testing framework
69
- - `/ace:init-coding-standards` -- Generate tailored coding standards
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
+ ```
@@ -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' since the plugin always runs in Claude Code.
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
- const cwd = process.cwd();
54
- const args = process.argv.slice(2);
55
- const raw = args.includes('--raw');
56
- const cmd = args[0];
57
-
58
- switch (cmd) {
59
- case 'init':
60
- cmdInit(cwd, raw);
61
- break;
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
- break;
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
+ });