godpowers 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -7
- package/INSPIRATION.md +4 -2
- package/README.md +35 -22
- package/RELEASE.md +44 -23
- package/SKILL.md +2 -2
- package/agents/god-auditor.md +1 -1
- package/agents/god-debugger.md +44 -8
- package/agents/god-deps-auditor.md +11 -0
- package/agents/god-executor.md +50 -5
- package/agents/god-greenfieldifier.md +1 -1
- package/agents/god-orchestrator.md +2 -2
- package/agents/god-planner.md +14 -0
- package/agents/god-pm.md +1 -1
- package/agents/god-quality-reviewer.md +23 -1
- package/agents/god-reconciler.md +1 -1
- package/agents/god-roadmapper.md +1 -1
- package/agents/god-spec-reviewer.md +16 -0
- package/agents/god-stack-selector.md +4 -0
- package/agents/god-updater.md +1 -1
- package/bin/install.js +9 -3
- package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/manifest.json +2 -2
- package/lib/README.md +9 -2
- package/lib/agent-cache.js +25 -3
- package/lib/atomic-write.js +85 -0
- package/lib/checkpoint.js +2 -1
- package/lib/code-intelligence.js +161 -0
- package/lib/context-writer.js +1 -1
- package/lib/dashboard.js +54 -20
- package/lib/executor-repair.js +65 -0
- package/lib/extensions.js +6 -13
- package/lib/feature-awareness.js +2 -2
- package/lib/fs-async.js +2 -1
- package/lib/host-capabilities.js +4 -0
- package/lib/install-profiles.js +155 -0
- package/lib/installer-args.js +12 -0
- package/lib/installer-core.js +43 -8
- package/lib/linkage.js +2 -1
- package/lib/package-identity.js +38 -0
- package/lib/package-legitimacy.js +158 -0
- package/lib/planning-systems.js +16 -13
- package/lib/quick-proof.js +5 -2
- package/lib/repo-surface-sync.js +1 -1
- package/lib/requirements.js +2 -1
- package/lib/reverse-sync.js +2 -1
- package/lib/route-quality-sync.js +2 -0
- package/lib/source-grounding.js +177 -0
- package/lib/source-sync.js +6 -5
- package/lib/state.js +2 -1
- package/package.json +2 -2
- package/references/HAVE-NOTS.md +1 -1
- package/references/orchestration/MODE-DETECTION.md +2 -2
- package/references/shared/ORCHESTRATORS.md +3 -3
- package/routing/god-design.yaml +1 -1
- package/routing/god-extension-scaffold.yaml +25 -0
- package/routing/god-migrate.yaml +3 -3
- package/routing/god-stack.yaml +1 -1
- package/routing/recipes/add-feature-mid-arc-pause.yaml +6 -0
- package/routing/recipes/brownfield-onboarding.yaml +5 -2
- package/routing/recipes/extension-authoring.yaml +32 -0
- package/routing/recipes/greenfield-fast.yaml +3 -0
- package/routing/recipes/production-broken.yaml +4 -0
- package/routing/recipes/release-maintenance.yaml +3 -0
- package/routing/recipes/returning-after-break.yaml +3 -0
- package/routing/recipes/weekly-health-check.yaml +2 -0
- package/schema/state.v1.json +1 -1
- package/skills/god-build.md +17 -3
- package/skills/god-discuss.md +10 -5
- package/skills/god-doctor.md +9 -3
- package/skills/god-dogfood.md +2 -2
- package/skills/god-extension-scaffold.md +66 -0
- package/skills/god-init.md +6 -6
- package/skills/god-migrate.md +10 -10
- package/skills/god-sync.md +2 -2
- package/skills/god-update-deps.md +4 -2
- package/skills/god-version.md +2 -2
- package/skills/god.md +8 -11
- package/templates/IMPORTED-CONTEXT.md +1 -1
- package/templates/INITIAL-FINDINGS.md +2 -2
- /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/PROJECT.md +0 -0
- /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/REQUIREMENTS.md +0 -0
- /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/ROADMAP.md +0 -0
package/lib/dashboard.js
CHANGED
|
@@ -133,20 +133,43 @@ function hasRecentPath(projectRoot, relPath, maxAgeMs) {
|
|
|
133
133
|
return Date.now() - modified <= maxAgeMs;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
function
|
|
136
|
+
function isGodpowersRuntimeRepo(projectRoot) {
|
|
137
|
+
try {
|
|
138
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
139
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
140
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
141
|
+
return pkg.name === 'godpowers'
|
|
142
|
+
&& exists(projectRoot, 'skills')
|
|
143
|
+
&& exists(projectRoot, 'routing')
|
|
144
|
+
&& exists(projectRoot, 'agents');
|
|
145
|
+
} catch (e) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function proactiveChecks(projectRoot, changedFiles = [], opts = {}) {
|
|
137
151
|
const oneDay = 24 * 60 * 60 * 1000;
|
|
138
152
|
const thirtyDays = 30 * oneDay;
|
|
153
|
+
const initialized = opts.initialized !== false;
|
|
154
|
+
const runtimeRepo = opts.runtimeRepo !== undefined
|
|
155
|
+
? opts.runtimeRepo
|
|
156
|
+
: isGodpowersRuntimeRepo(projectRoot);
|
|
139
157
|
const reviews = reviewCount(projectRoot);
|
|
140
158
|
|
|
141
|
-
const checkpoint =
|
|
142
|
-
? (
|
|
143
|
-
|
|
159
|
+
const checkpoint = initialized
|
|
160
|
+
? (exists(projectRoot, CHECKPOINT_PATH)
|
|
161
|
+
? (hasRecentPath(projectRoot, CHECKPOINT_PATH, oneDay) ? 'fresh' : 'stale')
|
|
162
|
+
: 'missing')
|
|
163
|
+
: 'not-applicable';
|
|
144
164
|
|
|
145
|
-
const sync =
|
|
146
|
-
? (
|
|
147
|
-
|
|
165
|
+
const sync = initialized
|
|
166
|
+
? (exists(projectRoot, SYNC_LOG_PATH)
|
|
167
|
+
? (hasRecentPath(projectRoot, SYNC_LOG_PATH, oneDay) ? 'fresh' : 'stale, suggest /god-sync')
|
|
168
|
+
: 'missing, suggest /god-sync')
|
|
169
|
+
: 'not-applicable';
|
|
148
170
|
|
|
149
|
-
const hygieneFresh =
|
|
171
|
+
const hygieneFresh = initialized
|
|
172
|
+
&& exists(projectRoot, CHECKPOINT_PATH)
|
|
150
173
|
&& hasRecentPath(projectRoot, CHECKPOINT_PATH, thirtyDays);
|
|
151
174
|
|
|
152
175
|
const pkgChanged = changedFiles.some(file => [
|
|
@@ -162,14 +185,22 @@ function proactiveChecks(projectRoot, changedFiles = []) {
|
|
|
162
185
|
'auth',
|
|
163
186
|
'security'
|
|
164
187
|
]));
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
:
|
|
188
|
+
const repoDocsStatus = runtimeRepo
|
|
189
|
+
? (() => {
|
|
190
|
+
const repoDocs = repoDocSync.detect(projectRoot, { changedFiles });
|
|
191
|
+
return repoDocs.status === 'fresh'
|
|
192
|
+
? 'fresh'
|
|
193
|
+
: `${repoDocs.stale.length} stale, suggest /god-docs`;
|
|
194
|
+
})()
|
|
195
|
+
: 'not-applicable';
|
|
196
|
+
const repoSurfaceStatus = runtimeRepo
|
|
197
|
+
? (() => {
|
|
198
|
+
const repoSurface = repoSurfaceSync.detect(projectRoot);
|
|
199
|
+
return repoSurface.status === 'fresh'
|
|
200
|
+
? 'fresh'
|
|
201
|
+
: `${repoSurface.stale.length} stale, suggest /god-doctor`;
|
|
202
|
+
})()
|
|
203
|
+
: 'not-applicable';
|
|
173
204
|
const host = hostCapabilities.detect(projectRoot);
|
|
174
205
|
|
|
175
206
|
return {
|
|
@@ -183,7 +214,7 @@ function proactiveChecks(projectRoot, changedFiles = []) {
|
|
|
183
214
|
automation: automationSummary(projectRoot),
|
|
184
215
|
security: sensitiveChanged ? 'sensitive files changed, suggest /god-harden' : 'clear',
|
|
185
216
|
dependencies: pkgChanged ? 'dependency files changed, suggest /god-update-deps' : 'clear',
|
|
186
|
-
hygiene: hygieneFresh ? 'fresh' : 'stale, suggest /god-hygiene'
|
|
217
|
+
hygiene: initialized ? (hygieneFresh ? 'fresh' : 'stale, suggest /god-hygiene') : 'not-applicable'
|
|
187
218
|
};
|
|
188
219
|
}
|
|
189
220
|
|
|
@@ -237,7 +268,7 @@ function compute(projectRoot, opts = {}) {
|
|
|
237
268
|
completion: '0% workflow progress because .godpowers/state.json is missing',
|
|
238
269
|
completionBasis: 'missing .godpowers/state.json'
|
|
239
270
|
},
|
|
240
|
-
proactive: proactiveChecks(projectRoot, git.entries.map(statusPath)),
|
|
271
|
+
proactive: proactiveChecks(projectRoot, git.entries.map(statusPath), { initialized: false }),
|
|
241
272
|
host: hostCapabilities.detect(projectRoot, opts.host || {}),
|
|
242
273
|
next,
|
|
243
274
|
deliverables: { hasRequirements: false },
|
|
@@ -276,7 +307,7 @@ function compute(projectRoot, opts = {}) {
|
|
|
276
307
|
worktree: git.worktree,
|
|
277
308
|
index: git.index,
|
|
278
309
|
planning: planningVisibility(projectRoot, progress),
|
|
279
|
-
proactive: proactiveChecks(projectRoot, git.entries.map(statusPath)),
|
|
310
|
+
proactive: proactiveChecks(projectRoot, git.entries.map(statusPath), { initialized: true }),
|
|
280
311
|
host: hostCapabilities.detect(projectRoot, opts.host || {}),
|
|
281
312
|
next,
|
|
282
313
|
deliverables,
|
|
@@ -289,6 +320,7 @@ function compute(projectRoot, opts = {}) {
|
|
|
289
320
|
function actionBrief(dashboard) {
|
|
290
321
|
const proactive = dashboard.proactive || {};
|
|
291
322
|
const next = dashboard.next || {};
|
|
323
|
+
const recommended = next.command || 'describe the next intent';
|
|
292
324
|
const blockers = [];
|
|
293
325
|
for (const [label, value] of [
|
|
294
326
|
['Repo surface', proactive.repoSurface],
|
|
@@ -304,10 +336,11 @@ function actionBrief(dashboard) {
|
|
|
304
336
|
if (value === 'fresh' || value === 'none' || value === 'clear' || value === 'not-applicable') continue;
|
|
305
337
|
if (/^full on /.test(value)) continue;
|
|
306
338
|
if (/^available via /.test(value)) continue;
|
|
339
|
+
if (label === 'Sync' && recommended !== '/god-sync') continue;
|
|
340
|
+
if (label === 'Hygiene' && recommended !== '/god-hygiene') continue;
|
|
307
341
|
blockers.push(`${label}: ${value}`);
|
|
308
342
|
}
|
|
309
343
|
|
|
310
|
-
const recommended = next.command || 'describe the next intent';
|
|
311
344
|
return {
|
|
312
345
|
recommended,
|
|
313
346
|
reason: next.reason || 'No route was computed.',
|
|
@@ -414,6 +447,7 @@ module.exports = {
|
|
|
414
447
|
parseGitStatus,
|
|
415
448
|
proactiveChecks,
|
|
416
449
|
automationSummary,
|
|
450
|
+
isGodpowersRuntimeRepo,
|
|
417
451
|
actionBrief,
|
|
418
452
|
planningVisibility
|
|
419
453
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const STRATEGIES = Object.freeze({
|
|
2
|
+
RETRY: 'retry',
|
|
3
|
+
DECOMPOSE: 'decompose',
|
|
4
|
+
PRUNE: 'prune',
|
|
5
|
+
ESCALATE: 'escalate'
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
function classifyFailure(input = {}) {
|
|
9
|
+
const attempts = Number(input.attempts || 0);
|
|
10
|
+
const budget = Number(Object.prototype.hasOwnProperty.call(input, 'budget') ? input.budget : 2);
|
|
11
|
+
const error = String(input.error || '').toLowerCase();
|
|
12
|
+
const criteria = String(input.doneCriteria || '').toLowerCase();
|
|
13
|
+
|
|
14
|
+
if (attempts >= budget) {
|
|
15
|
+
return {
|
|
16
|
+
strategy: STRATEGIES.ESCALATE,
|
|
17
|
+
reason: 'repair budget exhausted'
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (/architecture|product decision|ambiguous|human/.test(error)) {
|
|
22
|
+
return {
|
|
23
|
+
strategy: STRATEGIES.ESCALATE,
|
|
24
|
+
reason: 'failure requires a human or architecture decision'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (/not found|enoent|missing dependency|cannot find module|wrong path|permission|timeout|network|econn/.test(error)) {
|
|
29
|
+
return {
|
|
30
|
+
strategy: STRATEGIES.RETRY,
|
|
31
|
+
reason: 'failure looks environmental or mechanical'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (/and|multiple|all of|end-to-end|full flow/.test(criteria) || /too broad|partial|only.*part/.test(error)) {
|
|
36
|
+
return {
|
|
37
|
+
strategy: STRATEGIES.DECOMPOSE,
|
|
38
|
+
reason: 'done criteria appears too broad for one verified step'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (/out of scope|blocked by missing prerequisite|unsupported|cannot be implemented/.test(error)) {
|
|
43
|
+
return {
|
|
44
|
+
strategy: STRATEGIES.PRUNE,
|
|
45
|
+
reason: 'task appears infeasible in the current slice'
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
strategy: STRATEGIES.RETRY,
|
|
51
|
+
reason: 'default to one focused retry before broader action'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function renderRepairLog(task, decision) {
|
|
56
|
+
const name = task || 'task';
|
|
57
|
+
const strategy = decision.strategy.toUpperCase();
|
|
58
|
+
return `[Executor Repair - ${strategy}] ${name}: ${decision.reason}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
STRATEGIES,
|
|
63
|
+
classifyFailure,
|
|
64
|
+
renderRepairLog
|
|
65
|
+
};
|
package/lib/extensions.js
CHANGED
|
@@ -27,6 +27,7 @@ const fs = require('fs');
|
|
|
27
27
|
const path = require('path');
|
|
28
28
|
|
|
29
29
|
const intentLib = require('./intent'); // reuses parseSimpleYaml
|
|
30
|
+
const { copyRecursive } = require('./installer-files');
|
|
30
31
|
|
|
31
32
|
function extensionsDir(runtimeConfigDir) {
|
|
32
33
|
return path.join(runtimeConfigDir, 'godpowers-extensions');
|
|
@@ -159,11 +160,13 @@ function list(runtimeConfigDir) {
|
|
|
159
160
|
const results = [];
|
|
160
161
|
for (const scope of fs.readdirSync(dir)) {
|
|
161
162
|
const scopePath = path.join(dir, scope);
|
|
162
|
-
|
|
163
|
+
const scopeStat = fs.lstatSync(scopePath);
|
|
164
|
+
if (scopeStat.isSymbolicLink() || !scopeStat.isDirectory()) continue;
|
|
163
165
|
if (!scope.startsWith('@')) continue;
|
|
164
166
|
for (const name of fs.readdirSync(scopePath)) {
|
|
165
167
|
const packDir = path.join(scopePath, name);
|
|
166
|
-
|
|
168
|
+
const packStat = fs.lstatSync(packDir);
|
|
169
|
+
if (packStat.isSymbolicLink() || !packStat.isDirectory()) continue;
|
|
167
170
|
const manifestFile = path.join(packDir, 'manifest.yaml');
|
|
168
171
|
if (!fs.existsSync(manifestFile)) continue;
|
|
169
172
|
const { manifest } = parseManifest(fs.readFileSync(manifestFile, 'utf8'));
|
|
@@ -220,22 +223,12 @@ function install(runtimeConfigDir, sourceDir, godpowersVersion) {
|
|
|
220
223
|
for (const sub of ['agents', 'skills', 'workflows', 'references']) {
|
|
221
224
|
const src = path.join(sourceDir, sub);
|
|
222
225
|
if (fs.existsSync(src)) {
|
|
223
|
-
|
|
226
|
+
copyRecursive(src, path.join(destDir, sub));
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
229
|
return { installed: true, path: destDir, manifest };
|
|
227
230
|
}
|
|
228
231
|
|
|
229
|
-
function copyDirRecursive(src, dest) {
|
|
230
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
231
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
232
|
-
const s = path.join(src, entry.name);
|
|
233
|
-
const d = path.join(dest, entry.name);
|
|
234
|
-
if (entry.isDirectory()) copyDirRecursive(s, d);
|
|
235
|
-
else fs.copyFileSync(s, d);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
232
|
/**
|
|
240
233
|
* Remove an installed extension by name.
|
|
241
234
|
*/
|
package/lib/feature-awareness.js
CHANGED
|
@@ -21,7 +21,7 @@ const FEATURES = [
|
|
|
21
21
|
id: 'planning-system-migration',
|
|
22
22
|
since: '1.6.15',
|
|
23
23
|
commands: ['/god-migrate', '/god-init'],
|
|
24
|
-
description: 'Detect and import
|
|
24
|
+
description: 'Detect and import legacy planning, BMAD, and Superpowers planning artifacts.'
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
id: 'source-system-sync-back',
|
|
@@ -110,7 +110,7 @@ const FEATURES = [
|
|
|
110
110
|
{
|
|
111
111
|
id: 'extension-authoring',
|
|
112
112
|
since: '1.6.22',
|
|
113
|
-
commands: ['/god-extension-
|
|
113
|
+
commands: ['/god-extension-scaffold', '/god-test-extension', '/god-extension-add'],
|
|
114
114
|
description: 'Scaffold and validate publishable Godpowers extension packs.'
|
|
115
115
|
},
|
|
116
116
|
{
|
package/lib/fs-async.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs/promises');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const atomic = require('./atomic-write');
|
|
3
4
|
|
|
4
5
|
async function exists(filePath) {
|
|
5
6
|
try {
|
|
@@ -16,7 +17,7 @@ async function readJson(filePath) {
|
|
|
16
17
|
|
|
17
18
|
async function writeJson(filePath, value) {
|
|
18
19
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
19
|
-
await
|
|
20
|
+
await atomic.writeJsonAtomicAsync(filePath, value);
|
|
20
21
|
return value;
|
|
21
22
|
}
|
|
22
23
|
|
package/lib/host-capabilities.js
CHANGED
|
@@ -10,6 +10,7 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const os = require('os');
|
|
12
12
|
const cp = require('child_process');
|
|
13
|
+
const codeIntelligence = require('./code-intelligence');
|
|
13
14
|
|
|
14
15
|
function exists(filePath) {
|
|
15
16
|
return fs.existsSync(filePath);
|
|
@@ -55,6 +56,7 @@ function detect(projectRoot, opts = {}) {
|
|
|
55
56
|
const git = commandVersion('git', ['--version'], { cwd: root });
|
|
56
57
|
const npm = commandVersion('npm', ['--version'], { cwd: root });
|
|
57
58
|
const gh = commandVersion('gh', ['--version'], { cwd: root });
|
|
59
|
+
const codeIntel = opts.codeIntelligence || codeIntelligence.detect(root, opts.codeIntelligenceOpts || {});
|
|
58
60
|
const shell = Boolean(env.SHELL || env.ComSpec);
|
|
59
61
|
const agentSpawn = Boolean(installedAgents.codex || installedAgents.claude || opts.agentSpawn);
|
|
60
62
|
const extensionAuthoring = exists(path.join(root, 'lib', 'extension-authoring.js'))
|
|
@@ -84,6 +86,7 @@ function detect(projectRoot, opts = {}) {
|
|
|
84
86
|
npm,
|
|
85
87
|
gh,
|
|
86
88
|
agentSpawn,
|
|
89
|
+
codeIntelligence: codeIntel,
|
|
87
90
|
extensionAuthoring,
|
|
88
91
|
suiteReleaseDryRun
|
|
89
92
|
},
|
|
@@ -109,6 +112,7 @@ function render(report) {
|
|
|
109
112
|
lines.push(` Git: ${report.guarantees.git || 'not detected'}`);
|
|
110
113
|
lines.push(` npm: ${report.guarantees.npm || 'not detected'}`);
|
|
111
114
|
lines.push(` GitHub CLI: ${report.guarantees.gh || 'not detected'}`);
|
|
115
|
+
lines.push(` Code intelligence: ${codeIntelligence.summary(report.guarantees.codeIntelligence)}`);
|
|
112
116
|
lines.push(` Gaps: ${report.gaps.length > 0 ? report.gaps.join('; ') : 'none'}`);
|
|
113
117
|
return lines.join('\n');
|
|
114
118
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const COMMON = [
|
|
2
|
+
'god',
|
|
3
|
+
'god-help',
|
|
4
|
+
'god-version',
|
|
5
|
+
'god-next',
|
|
6
|
+
'god-status',
|
|
7
|
+
'god-progress',
|
|
8
|
+
'god-doctor',
|
|
9
|
+
'god-settings'
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const PROFILE_SKILLS = {
|
|
13
|
+
core: [
|
|
14
|
+
...COMMON,
|
|
15
|
+
'god-init',
|
|
16
|
+
'god-mode',
|
|
17
|
+
'god-build',
|
|
18
|
+
'god-review',
|
|
19
|
+
'god-sync',
|
|
20
|
+
'god-quick',
|
|
21
|
+
'god-fast'
|
|
22
|
+
],
|
|
23
|
+
builder: [
|
|
24
|
+
...COMMON,
|
|
25
|
+
'god-init',
|
|
26
|
+
'god-mode',
|
|
27
|
+
'god-discuss',
|
|
28
|
+
'god-explore',
|
|
29
|
+
'god-list-assumptions',
|
|
30
|
+
'god-prd',
|
|
31
|
+
'god-design',
|
|
32
|
+
'god-design-impact',
|
|
33
|
+
'god-arch',
|
|
34
|
+
'god-roadmap',
|
|
35
|
+
'god-stack',
|
|
36
|
+
'god-repo',
|
|
37
|
+
'god-build',
|
|
38
|
+
'god-add-tests',
|
|
39
|
+
'god-feature',
|
|
40
|
+
'god-story',
|
|
41
|
+
'god-stories',
|
|
42
|
+
'god-story-build',
|
|
43
|
+
'god-story-verify',
|
|
44
|
+
'god-story-close',
|
|
45
|
+
'god-review',
|
|
46
|
+
'god-test-runtime',
|
|
47
|
+
'god-sync',
|
|
48
|
+
'god-quick',
|
|
49
|
+
'god-fast'
|
|
50
|
+
],
|
|
51
|
+
maintainer: [
|
|
52
|
+
...COMMON,
|
|
53
|
+
'god-hygiene',
|
|
54
|
+
'god-update-deps',
|
|
55
|
+
'god-docs',
|
|
56
|
+
'god-repair',
|
|
57
|
+
'god-lint',
|
|
58
|
+
'god-standards',
|
|
59
|
+
'god-preflight',
|
|
60
|
+
'god-audit',
|
|
61
|
+
'god-agent-audit',
|
|
62
|
+
'god-context',
|
|
63
|
+
'god-context-scan',
|
|
64
|
+
'god-locate',
|
|
65
|
+
'god-scan',
|
|
66
|
+
'god-link',
|
|
67
|
+
'god-review-changes',
|
|
68
|
+
'god-reconcile',
|
|
69
|
+
'god-reconstruct',
|
|
70
|
+
'god-migrate',
|
|
71
|
+
'god-automation-status',
|
|
72
|
+
'god-automation-setup',
|
|
73
|
+
'god-extension-scaffold',
|
|
74
|
+
'god-extension-add',
|
|
75
|
+
'god-extension-list',
|
|
76
|
+
'god-extension-info',
|
|
77
|
+
'god-extension-remove',
|
|
78
|
+
'god-test-extension',
|
|
79
|
+
'god-budget',
|
|
80
|
+
'god-cost',
|
|
81
|
+
'god-cache-clear',
|
|
82
|
+
'god-logs',
|
|
83
|
+
'god-metrics',
|
|
84
|
+
'god-trace',
|
|
85
|
+
'god-export-otel',
|
|
86
|
+
'god-dogfood',
|
|
87
|
+
'god-quick',
|
|
88
|
+
'god-fast'
|
|
89
|
+
],
|
|
90
|
+
suite: [
|
|
91
|
+
...COMMON,
|
|
92
|
+
'god-suite-init',
|
|
93
|
+
'god-suite-status',
|
|
94
|
+
'god-suite-sync',
|
|
95
|
+
'god-suite-patch',
|
|
96
|
+
'god-suite-release',
|
|
97
|
+
'god-workstream',
|
|
98
|
+
'god-pr-branch',
|
|
99
|
+
'god-sync',
|
|
100
|
+
'god-reconcile',
|
|
101
|
+
'god-review',
|
|
102
|
+
'god-quick',
|
|
103
|
+
'god-fast'
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const PROFILE_DESCRIPTIONS = {
|
|
108
|
+
core: 'front door, status, init, build, review, sync, quick edits',
|
|
109
|
+
builder: 'core plus planning, design, stories, and runtime verification',
|
|
110
|
+
maintainer: 'core plus hygiene, deps, docs, repair, automation, and extensions',
|
|
111
|
+
suite: 'core plus multi-repo suite and workstream coordination',
|
|
112
|
+
full: 'all shipped slash commands'
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
function normalizeProfiles(value) {
|
|
116
|
+
if (!value) return ['full'];
|
|
117
|
+
const raw = String(value)
|
|
118
|
+
.split(',')
|
|
119
|
+
.map(part => part.trim().toLowerCase())
|
|
120
|
+
.filter(Boolean);
|
|
121
|
+
const profiles = raw.length > 0 ? raw : ['full'];
|
|
122
|
+
for (const profile of profiles) {
|
|
123
|
+
if (profile !== 'full' && !PROFILE_SKILLS[profile]) {
|
|
124
|
+
throw new Error(`Unknown install profile: ${profile}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (profiles.includes('full')) return ['full'];
|
|
128
|
+
return [...new Set(profiles)];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function selectedSkillNames(profileValue, availableNames) {
|
|
132
|
+
const profiles = normalizeProfiles(profileValue);
|
|
133
|
+
if (profiles.includes('full')) return new Set(availableNames);
|
|
134
|
+
const selected = new Set();
|
|
135
|
+
for (const profile of profiles) {
|
|
136
|
+
for (const name of PROFILE_SKILLS[profile]) {
|
|
137
|
+
if (availableNames.includes(name)) selected.add(name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return selected;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function describeProfiles(profileValue) {
|
|
144
|
+
return normalizeProfiles(profileValue)
|
|
145
|
+
.map(profile => `${profile}: ${PROFILE_DESCRIPTIONS[profile]}`)
|
|
146
|
+
.join('; ');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
PROFILE_SKILLS,
|
|
151
|
+
PROFILE_DESCRIPTIONS,
|
|
152
|
+
normalizeProfiles,
|
|
153
|
+
selectedSkillNames,
|
|
154
|
+
describeProfiles
|
|
155
|
+
};
|
package/lib/installer-args.js
CHANGED
|
@@ -29,6 +29,7 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
29
29
|
all: false,
|
|
30
30
|
help: false,
|
|
31
31
|
uninstall: false,
|
|
32
|
+
profile: 'full',
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -62,6 +63,15 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
62
63
|
case '--all':
|
|
63
64
|
opts.all = true;
|
|
64
65
|
break;
|
|
66
|
+
case '--minimal':
|
|
67
|
+
opts.profile = 'core';
|
|
68
|
+
break;
|
|
69
|
+
case '--profile':
|
|
70
|
+
if (args[i + 1]) {
|
|
71
|
+
opts.profile = args[i + 1];
|
|
72
|
+
i++;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
65
75
|
case '-h':
|
|
66
76
|
case '--help':
|
|
67
77
|
opts.help = true;
|
|
@@ -83,6 +93,8 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
83
93
|
opts.extensionAgent = arg.slice('--agent='.length);
|
|
84
94
|
} else if (arg.startsWith('--workflow=')) {
|
|
85
95
|
opts.extensionWorkflow = arg.slice('--workflow='.length);
|
|
96
|
+
} else if (arg.startsWith('--profile=')) {
|
|
97
|
+
opts.profile = arg.slice('--profile='.length);
|
|
86
98
|
} else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
|
87
99
|
opts.runtimes.push(arg.slice(2));
|
|
88
100
|
}
|
package/lib/installer-core.js
CHANGED
|
@@ -3,8 +3,10 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
const { ensureDir, copyRecursive, copyRuntimeBundle } = require('./installer-files');
|
|
5
5
|
const { resolveRuntime } = require('./installer-runtimes');
|
|
6
|
+
const { selectedSkillNames, normalizeProfiles } = require('./install-profiles');
|
|
7
|
+
const identity = require('./package-identity');
|
|
6
8
|
|
|
7
|
-
const VERSION =
|
|
9
|
+
const VERSION = identity.PACKAGE_VERSION;
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* @typedef {Object} InstallOptions
|
|
@@ -141,6 +143,15 @@ function removeSkillEntry(skillsDir, entry) {
|
|
|
141
143
|
return false;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
function pruneGodpowersSkills(skillsDir) {
|
|
147
|
+
let removed = 0;
|
|
148
|
+
if (!fs.existsSync(skillsDir)) return removed;
|
|
149
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
150
|
+
if (removeSkillEntry(skillsDir, entry)) removed++;
|
|
151
|
+
}
|
|
152
|
+
return removed;
|
|
153
|
+
}
|
|
154
|
+
|
|
144
155
|
function installForRuntime(runtimeKey, srcDir, opts = {}) {
|
|
145
156
|
const runtime = resolveRuntime(runtimeKey, opts);
|
|
146
157
|
if (!runtime) {
|
|
@@ -151,7 +162,7 @@ function installForRuntime(runtimeKey, srcDir, opts = {}) {
|
|
|
151
162
|
log(`\n Installing for \x1b[36m${runtime.name}\x1b[0m to \x1b[36m${runtime.configDir}\x1b[0m\n`);
|
|
152
163
|
ensureDir(runtime.configDir);
|
|
153
164
|
|
|
154
|
-
installSkills(srcDir, runtimeKey, runtime);
|
|
165
|
+
installSkills(srcDir, runtimeKey, runtime, opts);
|
|
155
166
|
installAgents(srcDir, runtime);
|
|
156
167
|
installMasterSkill(srcDir, runtimeKey, runtime);
|
|
157
168
|
installDataDirs(srcDir, runtime);
|
|
@@ -159,18 +170,33 @@ function installForRuntime(runtimeKey, srcDir, opts = {}) {
|
|
|
159
170
|
|
|
160
171
|
fs.writeFileSync(path.join(runtime.configDir, 'GODPOWERS_VERSION'), VERSION);
|
|
161
172
|
success(`Wrote GODPOWERS_VERSION (${VERSION})`);
|
|
173
|
+
fs.writeFileSync(path.join(runtime.configDir, 'GODPOWERS_PROFILE'), normalizeProfiles(opts.profile).join(','));
|
|
174
|
+
success(`Wrote GODPOWERS_PROFILE (${normalizeProfiles(opts.profile).join(',')})`);
|
|
162
175
|
return true;
|
|
163
176
|
}
|
|
164
177
|
|
|
165
|
-
function
|
|
178
|
+
function availableSkillFiles(srcDir) {
|
|
179
|
+
const skillsSrc = path.join(srcDir, 'skills');
|
|
180
|
+
if (!fs.existsSync(skillsSrc)) return [];
|
|
181
|
+
return fs.readdirSync(skillsSrc)
|
|
182
|
+
.filter(file => file.endsWith('.md'))
|
|
183
|
+
.sort();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function installSkills(srcDir, runtimeKey, runtime, opts = {}) {
|
|
166
187
|
const skillsSrc = path.join(srcDir, 'skills');
|
|
167
188
|
const skillsDest = path.join(runtime.configDir, runtime.skillsDir);
|
|
168
189
|
if (!fs.existsSync(skillsSrc)) return;
|
|
169
190
|
|
|
170
191
|
ensureDir(skillsDest);
|
|
192
|
+
pruneGodpowersSkills(skillsDest);
|
|
193
|
+
const files = availableSkillFiles(srcDir);
|
|
194
|
+
const names = files.map(file => path.basename(file, '.md'));
|
|
195
|
+
const selected = selectedSkillNames(opts.profile, names);
|
|
171
196
|
let count = 0;
|
|
172
|
-
for (const file of
|
|
173
|
-
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
const name = path.basename(file, '.md');
|
|
199
|
+
if (selected.has(name)) {
|
|
174
200
|
installSkillFile(path.join(skillsSrc, file), skillsDest, runtimeKey);
|
|
175
201
|
count++;
|
|
176
202
|
}
|
|
@@ -327,8 +353,15 @@ function uninstallHooks(runtimeKey, runtime) {
|
|
|
327
353
|
}
|
|
328
354
|
|
|
329
355
|
function countInstalledSurface(srcDir) {
|
|
356
|
+
return countProfileSurface(srcDir, { profile: 'full' });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function countProfileSurface(srcDir, opts = {}) {
|
|
360
|
+
const files = availableSkillFiles(srcDir);
|
|
361
|
+
const names = files.map(file => path.basename(file, '.md'));
|
|
362
|
+
const selected = selectedSkillNames(opts.profile, names);
|
|
330
363
|
return {
|
|
331
|
-
skills:
|
|
364
|
+
skills: selected.size,
|
|
332
365
|
agents: fs.readdirSync(path.join(srcDir, 'agents')).filter(f => /^god-.*\.md$/.test(f)).length
|
|
333
366
|
};
|
|
334
367
|
}
|
|
@@ -336,10 +369,12 @@ function countInstalledSurface(srcDir) {
|
|
|
336
369
|
module.exports = {
|
|
337
370
|
installForRuntime,
|
|
338
371
|
uninstallForRuntime,
|
|
339
|
-
countInstalledSurface,
|
|
372
|
+
countInstalledSurface: countProfileSurface,
|
|
340
373
|
installSkillFile,
|
|
341
374
|
parseAgentFrontmatter,
|
|
342
375
|
stripFrontmatter,
|
|
343
376
|
writeCodexAgentToml,
|
|
344
|
-
removeSkillEntry
|
|
377
|
+
removeSkillEntry,
|
|
378
|
+
pruneGodpowersSkills,
|
|
379
|
+
installSkills
|
|
345
380
|
};
|
package/lib/linkage.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
const fs = require('fs');
|
|
25
25
|
const path = require('path');
|
|
26
|
+
const atomic = require('./atomic-write');
|
|
26
27
|
|
|
27
28
|
const ID_PATTERNS = {
|
|
28
29
|
prd: /^P-(MUST|SHOULD|COULD)-\d+$/,
|
|
@@ -77,7 +78,7 @@ function readMap(filePath) {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
function writeMap(filePath, data) {
|
|
80
|
-
|
|
81
|
+
atomic.writeJsonAtomic(filePath, data);
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
function linkKey(artifactId, filePath) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const pkg = require('../package.json');
|
|
2
|
+
|
|
3
|
+
const PACKAGE_NAME = pkg.name;
|
|
4
|
+
const PACKAGE_VERSION = pkg.version;
|
|
5
|
+
const REPOSITORY_URL = pkg.repository && pkg.repository.url
|
|
6
|
+
? pkg.repository.url
|
|
7
|
+
: 'git+https://github.com/aihxp/godpowers.git';
|
|
8
|
+
const HOMEPAGE_URL = pkg.homepage || 'https://github.com/aihxp/godpowers#readme';
|
|
9
|
+
const BUGS_URL = pkg.bugs && pkg.bugs.url
|
|
10
|
+
? pkg.bugs.url
|
|
11
|
+
: 'https://github.com/aihxp/godpowers/issues';
|
|
12
|
+
const BIN_NAME = pkg.bin && Object.keys(pkg.bin)[0]
|
|
13
|
+
? Object.keys(pkg.bin)[0]
|
|
14
|
+
: 'godpowers';
|
|
15
|
+
|
|
16
|
+
function repoSlug() {
|
|
17
|
+
const url = REPOSITORY_URL
|
|
18
|
+
.replace(/^git\+/, '')
|
|
19
|
+
.replace(/^https:\/\/github\.com\//, '')
|
|
20
|
+
.replace(/^git@github\.com:/, '')
|
|
21
|
+
.replace(/\.git$/, '');
|
|
22
|
+
return url || 'aihxp/godpowers';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function npxCommand(version = 'latest') {
|
|
26
|
+
return `npx ${PACKAGE_NAME}@${version}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
PACKAGE_NAME,
|
|
31
|
+
PACKAGE_VERSION,
|
|
32
|
+
REPOSITORY_URL,
|
|
33
|
+
HOMEPAGE_URL,
|
|
34
|
+
BUGS_URL,
|
|
35
|
+
BIN_NAME,
|
|
36
|
+
repoSlug,
|
|
37
|
+
npxCommand
|
|
38
|
+
};
|