agile-context-engineering 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +10 -0
- package/LICENSE +51 -51
- package/README.md +332 -324
- package/agents/ace-product-owner.md +1 -1
- package/agents/ace-research-synthesizer.md +228 -228
- package/agents/ace-wiki-mapper.md +449 -445
- package/bin/install.js +60 -64
- package/hooks/ace-check-update.js +70 -62
- package/hooks/ace-statusline.js +89 -89
- package/package.json +5 -4
- package/shared/lib/ace-core.js +308 -0
- package/shared/lib/ace-core.test.js +308 -0
- package/shared/lib/ace-github.js +753 -0
- package/shared/lib/ace-story.js +400 -0
- package/shared/lib/ace-story.test.js +250 -0
- package/{agile-context-engineering → shared}/utils/questioning.xml +110 -110
- package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
- package/skills/execute-story/SKILL.md +110 -0
- package/skills/execute-story/script.js +305 -0
- package/skills/execute-story/script.test.js +261 -0
- package/skills/execute-story/walkthrough-template.xml +255 -0
- package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +1219 -1219
- package/skills/help/SKILL.md +69 -0
- package/skills/help/script.js +318 -0
- package/skills/help/script.test.js +183 -0
- package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +540 -540
- package/skills/init-coding-standards/SKILL.md +72 -0
- package/skills/init-coding-standards/script.js +59 -0
- package/skills/init-coding-standards/script.test.js +70 -0
- package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +381 -386
- package/skills/map-cross-cutting/SKILL.md +89 -0
- package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +197 -197
- package/skills/map-cross-cutting/workflow.xml +330 -0
- package/skills/map-guide/SKILL.md +89 -0
- package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +137 -137
- package/skills/map-guide/workflow.xml +320 -0
- package/skills/map-pattern/SKILL.md +89 -0
- package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +159 -159
- package/skills/map-pattern/workflow.xml +331 -0
- package/skills/map-story/SKILL.md +127 -0
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +115 -115
- package/skills/map-story/templates/guide.xml +137 -0
- package/skills/map-story/templates/pattern.xml +159 -0
- package/skills/map-story/templates/system-cross-cutting.xml +197 -0
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +381 -381
- package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/walkthrough.xml +255 -255
- package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +1046 -1046
- package/skills/map-subsystem/SKILL.md +111 -0
- package/skills/map-subsystem/script.js +60 -0
- package/skills/map-subsystem/script.test.js +68 -0
- package/skills/map-subsystem/templates/decizions.xml +115 -0
- package/skills/map-subsystem/templates/guide.xml +137 -0
- package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +174 -174
- package/skills/map-subsystem/templates/pattern.xml +159 -0
- package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
- package/skills/map-subsystem/templates/system.xml +381 -0
- package/skills/map-subsystem/templates/walkthrough.xml +255 -0
- package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +15 -20
- package/skills/map-sys-doc/SKILL.md +90 -0
- package/skills/map-sys-doc/system.xml +381 -0
- package/skills/map-sys-doc/workflow.xml +336 -0
- package/skills/map-system/SKILL.md +85 -0
- package/skills/map-system/script.js +84 -0
- package/skills/map-system/script.test.js +73 -0
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +254 -254
- package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/wiki-readme.xml +296 -296
- package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +11 -16
- package/skills/map-walkthrough/SKILL.md +92 -0
- package/skills/map-walkthrough/walkthrough.xml +255 -0
- package/skills/plan-backlog/SKILL.md +75 -0
- package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +231 -231
- package/skills/plan-backlog/script.js +136 -0
- package/skills/plan-backlog/script.test.js +83 -0
- package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +13 -21
- package/skills/plan-feature/SKILL.md +76 -0
- package/skills/plan-feature/script.js +148 -0
- package/skills/plan-feature/script.test.js +80 -0
- package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +1487 -1495
- package/skills/plan-product-vision/SKILL.md +75 -0
- package/skills/plan-product-vision/script.js +60 -0
- package/skills/plan-product-vision/script.test.js +69 -0
- package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +4 -9
- package/skills/plan-story/SKILL.md +116 -0
- package/skills/plan-story/script.js +326 -0
- package/skills/plan-story/script.test.js +240 -0
- package/skills/plan-story/story-template.xml +451 -0
- package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1285 -944
- package/skills/research-external-solution/SKILL.md +107 -0
- package/skills/research-external-solution/script.js +238 -0
- package/skills/research-external-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +4 -6
- package/skills/research-integration-solution/SKILL.md +98 -0
- package/skills/research-integration-solution/script.js +231 -0
- package/skills/research-integration-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +3 -5
- package/skills/research-story-wiki/SKILL.md +92 -0
- package/skills/research-story-wiki/script.js +231 -0
- package/skills/research-story-wiki/script.test.js +138 -0
- package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +3 -5
- package/skills/research-technical-solution/SKILL.md +103 -0
- package/skills/research-technical-solution/script.js +231 -0
- package/skills/research-technical-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +3 -5
- package/skills/review-story/SKILL.md +100 -0
- package/skills/review-story/script.js +257 -0
- package/skills/review-story/script.test.js +169 -0
- package/skills/review-story/story-template.xml +451 -0
- package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +279 -281
- package/skills/update/SKILL.md +53 -0
- package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +12 -13
- package/agile-context-engineering/src/ace-tools.js +0 -2881
- package/agile-context-engineering/src/ace-tools.test.js +0 -1089
- package/agile-context-engineering/templates/_command.md +0 -54
- package/agile-context-engineering/templates/_workflow.xml +0 -17
- package/agile-context-engineering/templates/config.json +0 -0
- package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
- package/commands/ace/execute-story.md +0 -138
- package/commands/ace/help.md +0 -93
- package/commands/ace/init-coding-standards.md +0 -83
- package/commands/ace/map-story.md +0 -165
- package/commands/ace/map-subsystem.md +0 -140
- package/commands/ace/map-system.md +0 -92
- package/commands/ace/map-walkthrough.md +0 -127
- package/commands/ace/plan-backlog.md +0 -83
- package/commands/ace/plan-feature.md +0 -89
- package/commands/ace/plan-product-vision.md +0 -81
- package/commands/ace/plan-story.md +0 -159
- package/commands/ace/research-external-solution.md +0 -138
- package/commands/ace/research-integration-solution.md +0 -135
- package/commands/ace/research-story-wiki.md +0 -116
- package/commands/ace/research-technical-solution.md +0 -147
- package/commands/ace/review-story.md +0 -109
- package/commands/ace/update.md +0 -56
- /package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +0 -0
- /package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +0 -0
- /package/{agile-context-engineering/workflows/map-walkthrough.xml → skills/map-walkthrough/workflow.xml} +0 -0
- /package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +0 -0
|
@@ -0,0 +1,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
|
+
});
|