agile-context-engineering 0.3.0 → 0.5.0

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