agile-context-engineering 0.3.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +10 -0
  3. package/CHANGELOG.md +7 -1
  4. package/LICENSE +51 -51
  5. package/README.md +330 -318
  6. package/agents/ace-code-discovery-analyst.md +245 -245
  7. package/agents/ace-code-integration-analyst.md +248 -248
  8. package/agents/ace-code-reviewer.md +375 -375
  9. package/agents/ace-product-owner.md +365 -361
  10. package/agents/ace-project-researcher.md +606 -606
  11. package/agents/ace-research-synthesizer.md +228 -228
  12. package/agents/ace-technical-application-architect.md +315 -315
  13. package/agents/ace-wiki-mapper.md +449 -445
  14. package/bin/install.js +605 -195
  15. package/hooks/ace-check-update.js +71 -62
  16. package/hooks/ace-statusline.js +107 -89
  17. package/hooks/hooks.json +14 -0
  18. package/package.json +7 -5
  19. package/shared/lib/ace-core.js +361 -0
  20. package/shared/lib/ace-core.test.js +308 -0
  21. package/shared/lib/ace-github.js +753 -0
  22. package/shared/lib/ace-story.js +400 -0
  23. package/shared/lib/ace-story.test.js +250 -0
  24. package/{agile-context-engineering → shared}/utils/questioning.xml +110 -110
  25. package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
  26. package/{commands/ace/execute-story.md → skills/execute-story/SKILL.md} +116 -138
  27. package/skills/execute-story/script.js +291 -0
  28. package/skills/execute-story/script.test.js +261 -0
  29. package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +451 -451
  30. package/skills/execute-story/walkthrough-template.xml +255 -0
  31. package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +1221 -1219
  32. package/skills/help/SKILL.md +71 -0
  33. package/skills/help/script.js +315 -0
  34. package/skills/help/script.test.js +183 -0
  35. package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +544 -533
  36. package/{commands/ace/init-coding-standards.md → skills/init-coding-standards/SKILL.md} +91 -83
  37. package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +531 -531
  38. package/skills/init-coding-standards/script.js +50 -0
  39. package/skills/init-coding-standards/script.test.js +70 -0
  40. package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +381 -386
  41. package/skills/map-cross-cutting/SKILL.md +126 -0
  42. package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +197 -197
  43. package/skills/map-cross-cutting/workflow.xml +330 -0
  44. package/skills/map-guide/SKILL.md +126 -0
  45. package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +137 -137
  46. package/skills/map-guide/workflow.xml +320 -0
  47. package/skills/map-pattern/SKILL.md +125 -0
  48. package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +159 -159
  49. package/skills/map-pattern/workflow.xml +331 -0
  50. package/{commands/ace/map-story.md → skills/map-story/SKILL.md} +180 -165
  51. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +115 -115
  52. package/skills/map-story/templates/guide.xml +137 -0
  53. package/skills/map-story/templates/pattern.xml +159 -0
  54. package/skills/map-story/templates/system-cross-cutting.xml +197 -0
  55. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +381 -381
  56. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +125 -125
  57. package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/walkthrough.xml +255 -255
  58. package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +1046 -1046
  59. package/{commands/ace/map-subsystem.md → skills/map-subsystem/SKILL.md} +155 -140
  60. package/skills/map-subsystem/script.js +51 -0
  61. package/skills/map-subsystem/script.test.js +68 -0
  62. package/skills/map-subsystem/templates/decizions.xml +115 -0
  63. package/skills/map-subsystem/templates/guide.xml +137 -0
  64. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +174 -174
  65. package/skills/map-subsystem/templates/pattern.xml +159 -0
  66. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +343 -343
  67. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +234 -234
  68. package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
  69. package/skills/map-subsystem/templates/system.xml +381 -0
  70. package/skills/map-subsystem/templates/walkthrough.xml +255 -0
  71. package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +1173 -1178
  72. package/skills/map-sys-doc/SKILL.md +125 -0
  73. package/skills/map-sys-doc/system.xml +381 -0
  74. package/skills/map-sys-doc/workflow.xml +336 -0
  75. package/{commands/ace/map-system.md → skills/map-system/SKILL.md} +103 -92
  76. package/skills/map-system/script.js +75 -0
  77. package/skills/map-system/script.test.js +73 -0
  78. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +254 -254
  79. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +177 -177
  80. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +283 -283
  81. package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/wiki-readme.xml +296 -296
  82. package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +667 -672
  83. package/{commands/ace/map-walkthrough.md → skills/map-walkthrough/SKILL.md} +140 -127
  84. package/skills/map-walkthrough/walkthrough.xml +255 -0
  85. package/{agile-context-engineering/workflows/map-walkthrough.xml → skills/map-walkthrough/workflow.xml} +457 -457
  86. package/{commands/ace/plan-backlog.md → skills/plan-backlog/SKILL.md} +93 -83
  87. package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +231 -231
  88. package/skills/plan-backlog/script.js +121 -0
  89. package/skills/plan-backlog/script.test.js +83 -0
  90. package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +1348 -1356
  91. package/{commands/ace/plan-feature.md → skills/plan-feature/SKILL.md} +99 -89
  92. package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +361 -361
  93. package/skills/plan-feature/script.js +131 -0
  94. package/skills/plan-feature/script.test.js +80 -0
  95. package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +1487 -1495
  96. package/{commands/ace/plan-product-vision.md → skills/plan-product-vision/SKILL.md} +91 -81
  97. package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +227 -227
  98. package/skills/plan-product-vision/script.js +51 -0
  99. package/skills/plan-product-vision/script.test.js +69 -0
  100. package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +337 -342
  101. package/{commands/ace/plan-story.md → skills/plan-story/SKILL.md} +139 -159
  102. package/skills/plan-story/script.js +295 -0
  103. package/skills/plan-story/script.test.js +240 -0
  104. package/skills/plan-story/story-template.xml +458 -0
  105. package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1301 -944
  106. package/{commands/ace/research-external-solution.md → skills/research-external-solution/SKILL.md} +120 -138
  107. package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +832 -832
  108. package/skills/research-external-solution/script.js +229 -0
  109. package/skills/research-external-solution/script.test.js +134 -0
  110. package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +657 -659
  111. package/{commands/ace/research-integration-solution.md → skills/research-integration-solution/SKILL.md} +121 -135
  112. package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +1015 -1015
  113. package/skills/research-integration-solution/script.js +223 -0
  114. package/skills/research-integration-solution/script.test.js +134 -0
  115. package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +711 -713
  116. package/{commands/ace/research-story-wiki.md → skills/research-story-wiki/SKILL.md} +101 -116
  117. package/skills/research-story-wiki/script.js +223 -0
  118. package/skills/research-story-wiki/script.test.js +138 -0
  119. package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +194 -194
  120. package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +473 -475
  121. package/{commands/ace/research-technical-solution.md → skills/research-technical-solution/SKILL.md} +131 -147
  122. package/skills/research-technical-solution/script.js +223 -0
  123. package/skills/research-technical-solution/script.test.js +134 -0
  124. package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +1025 -1025
  125. package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +761 -763
  126. package/{commands/ace/review-story.md → skills/review-story/SKILL.md} +99 -109
  127. package/skills/review-story/script.js +249 -0
  128. package/skills/review-story/script.test.js +169 -0
  129. package/skills/review-story/story-template.xml +451 -0
  130. package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +279 -281
  131. package/{commands/ace/update.md → skills/update/SKILL.md} +65 -56
  132. package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +33 -18
  133. package/agile-context-engineering/src/ace-tools.js +0 -2881
  134. package/agile-context-engineering/src/ace-tools.test.js +0 -1089
  135. package/agile-context-engineering/templates/_command.md +0 -54
  136. package/agile-context-engineering/templates/_workflow.xml +0 -17
  137. package/agile-context-engineering/templates/config.json +0 -0
  138. package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
  139. package/commands/ace/help.md +0 -93
@@ -0,0 +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
+ ## 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
+ ```
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * help skill script — Entry point for all ace-tools operations
5
+ * needed by the help skill.
6
+ *
7
+ * Subcommands:
8
+ * init Environment detection for help workflow
9
+ * ensure-settings Create .ace/settings.json with defaults if missing
10
+ * setup-github Detect gh CLI, repo, and list GitHub Projects
11
+ * write-github-settings Write GitHub Project settings (key=value args)
12
+ * sync-agent-teams Sync agent_teams from runtime settings to .ace/settings.json
13
+ * write-agent-teams <true|false> Enable/disable agent teams in ACE + runtime settings
14
+ * verify-path-exists <path> Check file/directory existence
15
+ *
16
+ * Usage: node script.js <subcommand> [args] [--raw]
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ const {
23
+ loadConfig, pathExists, resolveModel,
24
+ detectBrownfieldStatus, loadSettings, writeSettings,
25
+ output, error, runSkillScript,
26
+ } = require('../../shared/lib/ace-core');
27
+
28
+ // ─── Runtime Config Dir ─────────────────────────────────────────────────────
29
+
30
+ /**
31
+ * Detect the runtime config directory name.
32
+ * In the plugin context, the script lives at:
33
+ * <base>/<config-dir>/skills/help/script.js
34
+ * Default to '.claude' for backwards compatibility.
35
+ */
36
+ function getRuntimeConfigDirName() {
37
+ try {
38
+ const skillDir = __dirname; // <base>/<config-dir>/skills/help
39
+ const skillsDir = path.dirname(skillDir); // <base>/<config-dir>/skills
40
+ const configDir = path.dirname(skillsDir); // <base>/<config-dir>
41
+ const dirName = path.basename(configDir);
42
+ if (dirName === '.opencode' || dirName === '.codex' || dirName === '.claude') {
43
+ return dirName;
44
+ }
45
+ } catch {}
46
+ return '.claude';
47
+ }
48
+
49
+ const RUNTIME_CONFIG_DIR = getRuntimeConfigDirName();
50
+
51
+ // ─── CLI Dispatch ────────────────────────────────────────────────────────────
52
+
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];
62
+ if (!targetPath) error('path required for verify-path-exists');
63
+ const exists = pathExists(cwd, targetPath);
64
+ output({ exists, path: targetPath }, raw, exists ? 'true' : 'false');
65
+ },
66
+ });
67
+
68
+ // ─── Init: Help ─────────────────────────────────────────────────────────────
69
+
70
+ /**
71
+ * Environment detection for the help workflow (project status dashboard).
72
+ * Detects: git, gh CLI, brownfield status, wiki state, product artifacts.
73
+ */
74
+ function cmdInit(cwd, raw) {
75
+ const config = loadConfig(cwd);
76
+ const brownfield = detectBrownfieldStatus(cwd);
77
+
78
+ const result = {
79
+ // Models (pre-resolved so workflows know which model to spawn each agent with)
80
+ product_owner_model: resolveModel(cwd, 'ace-product-owner'),
81
+ researcher_model: resolveModel(cwd, 'ace-project-researcher'),
82
+ synthesizer_model: resolveModel(cwd, 'ace-research-synthesizer'),
83
+
84
+ // Config
85
+ commit_docs: config.commit_docs,
86
+ runtime_config_dir: RUNTIME_CONFIG_DIR,
87
+
88
+ // Existing state
89
+ has_product_vision: pathExists(cwd, '.docs/product/product-vision.md'),
90
+ has_system_architecture: pathExists(cwd, '.docs/wiki/system-wide/system-architecture.md'),
91
+ has_system_structure: pathExists(cwd, '.docs/wiki/system-wide/system-structure.md'),
92
+ has_coding_standards: pathExists(cwd, '.docs/wiki/system-wide/coding-standards.md'),
93
+ has_testing_framework: pathExists(cwd, '.docs/wiki/system-wide/testing-framework.md'),
94
+ project_exists: pathExists(cwd, '.docs/product/product-vision.md'),
95
+ has_codebase_map: pathExists(cwd, '.ace/codebase'),
96
+ planning_exists: pathExists(cwd, '.ace'),
97
+
98
+ // Brownfield detection
99
+ ...brownfield,
100
+ needs_codebase_map: brownfield.is_brownfield && !pathExists(cwd, '.ace/codebase'),
101
+
102
+ // Git state
103
+ has_git: pathExists(cwd, '.git'),
104
+
105
+ // GitHub CLI
106
+ has_gh_cli: (() => {
107
+ try {
108
+ const { execSync } = require('child_process');
109
+ execSync('gh --version', { stdio: 'pipe' });
110
+ return true;
111
+ } catch { return false; }
112
+ })(),
113
+ };
114
+
115
+ output(result, raw);
116
+ }
117
+
118
+ // ─── Ensure Settings ────────────────────────────────────────────────────────
119
+
120
+ function cmdEnsureSettings(cwd, raw) {
121
+ const settingsPath = path.join(cwd, '.ace', 'settings.json');
122
+ const alreadyExists = pathExists(cwd, '.ace/settings.json');
123
+
124
+ if (!alreadyExists) {
125
+ const SETTINGS_DEFAULTS = {
126
+ model_profile: 'balanced',
127
+ commit_docs: true,
128
+ agent_teams: false,
129
+ github_project: {
130
+ enabled: false,
131
+ gh_installed: false,
132
+ repo: '',
133
+ project_number: null,
134
+ owner: '',
135
+ },
136
+ };
137
+ const defaults = JSON.parse(JSON.stringify(SETTINGS_DEFAULTS));
138
+ writeSettings(cwd, defaults);
139
+ output({ created: true, path: settingsPath, settings: defaults }, raw);
140
+ } else {
141
+ const settings = loadSettings(cwd);
142
+ output({ created: false, path: settingsPath, settings }, raw);
143
+ }
144
+ }
145
+
146
+ // ─── Setup GitHub Project ───────────────────────────────────────────────────
147
+
148
+ function cmdSetupGithubProject(cwd, raw) {
149
+ const { execSync } = require('child_process');
150
+ const settings = loadSettings(cwd);
151
+
152
+ // Detect gh CLI
153
+ let ghInstalled = false;
154
+ try {
155
+ execSync('gh --version', { stdio: 'pipe' });
156
+ ghInstalled = true;
157
+ } catch {}
158
+
159
+ // Detect repo
160
+ let repo = '';
161
+ let owner = '';
162
+ if (ghInstalled) {
163
+ try {
164
+ repo = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {
165
+ cwd,
166
+ stdio: ['pipe', 'pipe', 'pipe'],
167
+ encoding: 'utf-8',
168
+ }).trim();
169
+ owner = repo.split('/')[0] || '';
170
+ } catch {}
171
+ }
172
+
173
+ // List projects
174
+ let projects = [];
175
+ if (ghInstalled && owner) {
176
+ try {
177
+ const projectsJson = execSync(`gh project list --owner ${owner} --limit 10 --format json`, {
178
+ cwd,
179
+ stdio: ['pipe', 'pipe', 'pipe'],
180
+ encoding: 'utf-8',
181
+ }).trim();
182
+ const parsed = JSON.parse(projectsJson);
183
+ projects = (parsed.projects || parsed || []).map(p => ({
184
+ number: p.number,
185
+ title: p.title,
186
+ }));
187
+ } catch {}
188
+ }
189
+
190
+ output({
191
+ gh_installed: ghInstalled,
192
+ repo,
193
+ owner,
194
+ projects,
195
+ current_settings: settings.github_project,
196
+ }, raw);
197
+ }
198
+
199
+ // ─── Write GitHub Settings ──────────────────────────────────────────────────
200
+
201
+ function cmdWriteGithubSettings(cwd, raw, extraArgs) {
202
+ const settings = loadSettings(cwd);
203
+
204
+ for (const arg of extraArgs) {
205
+ const eqIndex = arg.indexOf('=');
206
+ if (eqIndex === -1) continue;
207
+ const key = arg.substring(0, eqIndex);
208
+ const value = arg.substring(eqIndex + 1);
209
+
210
+ switch (key) {
211
+ case 'enabled':
212
+ settings.github_project.enabled = value === 'true';
213
+ break;
214
+ case 'gh_installed':
215
+ settings.github_project.gh_installed = value === 'true';
216
+ break;
217
+ case 'repo':
218
+ settings.github_project.repo = value;
219
+ break;
220
+ case 'project_number':
221
+ settings.github_project.project_number = value === 'null' ? null : parseInt(value, 10);
222
+ break;
223
+ case 'owner':
224
+ settings.github_project.owner = value;
225
+ break;
226
+ }
227
+ }
228
+
229
+ writeSettings(cwd, settings);
230
+ output({ written: true, settings }, raw);
231
+ }
232
+
233
+ // ─── Sync Agent Teams ───────────────────────────────────────────────────────
234
+
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
+
247
+ // Source of truth: runtime settings.json env var (e.g. .claude/settings.json)
248
+ const claudeSettingsPath = path.join(cwd, RUNTIME_CONFIG_DIR, 'settings.json');
249
+ let claudeEnabled = false;
250
+ try {
251
+ const claudeRaw = fs.readFileSync(claudeSettingsPath, 'utf-8');
252
+ const claudeSettings = JSON.parse(claudeRaw);
253
+ const val = claudeSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
254
+ claudeEnabled = val === '1' || val === 'true';
255
+ } catch {
256
+ // File doesn't exist or is invalid — treat as disabled
257
+ }
258
+
259
+ // Sync ACE settings to match Claude's source of truth
260
+ const settings = loadSettings(cwd);
261
+ const wasDifferent = settings.agent_teams !== claudeEnabled;
262
+ if (wasDifferent) {
263
+ settings.agent_teams = claudeEnabled;
264
+ writeSettings(cwd, settings);
265
+ }
266
+
267
+ output({ agent_teams: claudeEnabled, synced: wasDifferent }, raw);
268
+ }
269
+
270
+ // ─── Write Agent Teams Setting ──────────────────────────────────────────────
271
+
272
+ function cmdWriteAgentTeamsSetting(cwd, raw, extraArgs) {
273
+ const enabled = extraArgs[0] === 'true';
274
+ const settings = loadSettings(cwd);
275
+ settings.agent_teams = RUNTIME_CONFIG_DIR === '.codex' ? false : enabled;
276
+ writeSettings(cwd, settings);
277
+
278
+ if (RUNTIME_CONFIG_DIR === '.codex') {
279
+ output({ written: true, agent_teams: false, settings, runtime: 'codex' }, raw);
280
+ return;
281
+ }
282
+
283
+ // Also update the project's runtime settings.json (e.g. .claude/)
284
+ const claudeDir = path.join(cwd, RUNTIME_CONFIG_DIR);
285
+ const claudeSettingsPath = path.join(claudeDir, 'settings.json');
286
+
287
+ let claudeSettings = {};
288
+ try {
289
+ const existing = fs.readFileSync(claudeSettingsPath, 'utf-8');
290
+ claudeSettings = JSON.parse(existing);
291
+ } catch {
292
+ // File doesn't exist or is invalid — start fresh
293
+ }
294
+
295
+ if (!claudeSettings.env) {
296
+ claudeSettings.env = {};
297
+ }
298
+
299
+ if (enabled) {
300
+ claudeSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
301
+ } else {
302
+ delete claudeSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
303
+ // Clean up empty env object
304
+ if (Object.keys(claudeSettings.env).length === 0) {
305
+ delete claudeSettings.env;
306
+ }
307
+ }
308
+
309
+ if (!fs.existsSync(claudeDir)) {
310
+ fs.mkdirSync(claudeDir, { recursive: true });
311
+ }
312
+ fs.writeFileSync(claudeSettingsPath, JSON.stringify(claudeSettings, null, 2) + '\n', 'utf-8');
313
+
314
+ output({ written: true, agent_teams: enabled, settings, claude_settings: claudeSettings }, raw);
315
+ }
@@ -0,0 +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
+ });