godpowers 2.2.1 → 2.3.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/CHANGELOG.md +33 -7
- package/INSPIRATION.md +4 -2
- package/README.md +25 -12
- package/RELEASE.md +23 -19
- package/SKILL.md +2 -2
- package/agents/god-auditor.md +1 -1
- package/agents/god-deps-auditor.md +11 -0
- package/agents/god-executor.md +22 -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-reconciler.md +1 -1
- package/agents/god-roadmapper.md +1 -1
- package/agents/god-spec-reviewer.md +10 -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 +7 -1
- package/lib/atomic-write.js +85 -0
- package/lib/checkpoint.js +2 -1
- package/lib/context-writer.js +1 -1
- package/lib/executor-repair.js +65 -0
- package/lib/feature-awareness.js +1 -1
- package/lib/fs-async.js +2 -1
- package/lib/install-profiles.js +154 -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 +9 -9
- package/lib/repo-surface-sync.js +1 -1
- package/lib/requirements.js +2 -1
- package/lib/reverse-sync.js +2 -1
- package/lib/source-grounding.js +177 -0
- package/lib/source-sync.js +6 -5
- package/lib/state.js +2 -1
- package/package.json +1 -1
- 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-migrate.yaml +3 -3
- package/schema/state.v1.json +1 -1
- package/skills/god-build.md +17 -3
- package/skills/god-doctor.md +2 -2
- package/skills/god-dogfood.md +2 -2
- 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 +1 -1
- 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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const fsp = require('fs/promises');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function tempPathFor(filePath) {
|
|
6
|
+
const dir = path.dirname(filePath);
|
|
7
|
+
const base = path.basename(filePath);
|
|
8
|
+
return path.join(dir, `.${base}.${process.pid}.${Date.now()}.tmp`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function validateContent(content, validate) {
|
|
12
|
+
if (typeof validate === 'function') validate(content);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeFileAtomic(filePath, content, opts = {}) {
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
const tmp = tempPathFor(filePath);
|
|
18
|
+
try {
|
|
19
|
+
fs.writeFileSync(tmp, content);
|
|
20
|
+
validateContent(content, opts.validateContent);
|
|
21
|
+
if (typeof opts.validateFile === 'function') opts.validateFile(tmp);
|
|
22
|
+
fs.renameSync(tmp, filePath);
|
|
23
|
+
return filePath;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
try {
|
|
26
|
+
fs.rmSync(tmp, { force: true });
|
|
27
|
+
} catch (_) {
|
|
28
|
+
// Best-effort cleanup. The original file is still untouched.
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function writeFileAtomicAsync(filePath, content, opts = {}) {
|
|
35
|
+
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
36
|
+
const tmp = tempPathFor(filePath);
|
|
37
|
+
try {
|
|
38
|
+
await fsp.writeFile(tmp, content);
|
|
39
|
+
validateContent(content, opts.validateContent);
|
|
40
|
+
if (typeof opts.validateFile === 'function') await opts.validateFile(tmp);
|
|
41
|
+
await fsp.rename(tmp, filePath);
|
|
42
|
+
return filePath;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
try {
|
|
45
|
+
await fsp.rm(tmp, { force: true });
|
|
46
|
+
} catch (_) {
|
|
47
|
+
// Best-effort cleanup. The original file is still untouched.
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function jsonContent(value) {
|
|
54
|
+
return JSON.stringify(value, null, 2) + '\n';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function writeJsonAtomic(filePath, value, opts = {}) {
|
|
58
|
+
const content = jsonContent(value);
|
|
59
|
+
return writeFileAtomic(filePath, content, {
|
|
60
|
+
...opts,
|
|
61
|
+
validateContent: text => {
|
|
62
|
+
JSON.parse(text);
|
|
63
|
+
validateContent(text, opts.validateContent);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function writeJsonAtomicAsync(filePath, value, opts = {}) {
|
|
69
|
+
const content = jsonContent(value);
|
|
70
|
+
return writeFileAtomicAsync(filePath, content, {
|
|
71
|
+
...opts,
|
|
72
|
+
validateContent: text => {
|
|
73
|
+
JSON.parse(text);
|
|
74
|
+
validateContent(text, opts.validateContent);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
tempPathFor,
|
|
81
|
+
writeFileAtomic,
|
|
82
|
+
writeFileAtomicAsync,
|
|
83
|
+
writeJsonAtomic,
|
|
84
|
+
writeJsonAtomicAsync
|
|
85
|
+
};
|
package/lib/checkpoint.js
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
const fs = require('fs');
|
|
50
50
|
const path = require('path');
|
|
51
51
|
const crypto = require('crypto');
|
|
52
|
+
const atomic = require('./atomic-write');
|
|
52
53
|
|
|
53
54
|
const MAX_ACTIONS = 20;
|
|
54
55
|
const MAX_FACTS = 10;
|
|
@@ -224,7 +225,7 @@ function write(projectRoot, state) {
|
|
|
224
225
|
''
|
|
225
226
|
].filter(line => line !== null).join('\n');
|
|
226
227
|
|
|
227
|
-
|
|
228
|
+
atomic.writeFileAtomic(file, fm + body);
|
|
228
229
|
return file;
|
|
229
230
|
}
|
|
230
231
|
|
package/lib/context-writer.js
CHANGED
|
@@ -99,7 +99,7 @@ function buildCanonicalContent(state, opts = {}) {
|
|
|
99
99
|
lines.push('- `/god-next` - what to run next, with reason');
|
|
100
100
|
lines.push('- `/god-mode` - run the full autonomous project run');
|
|
101
101
|
lines.push('- `/god-sync` - refresh artifacts, context, and source-system sync-back');
|
|
102
|
-
lines.push('- `/god-migrate` - import or sync
|
|
102
|
+
lines.push('- `/god-migrate` - import or sync legacy planning, BMAD, or Superpowers context');
|
|
103
103
|
lines.push('- `/god-context refresh` - refresh AI-tool awareness for this project');
|
|
104
104
|
lines.push('');
|
|
105
105
|
|
|
@@ -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/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',
|
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
|
|
|
@@ -0,0 +1,154 @@
|
|
|
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-add',
|
|
74
|
+
'god-extension-list',
|
|
75
|
+
'god-extension-info',
|
|
76
|
+
'god-extension-remove',
|
|
77
|
+
'god-test-extension',
|
|
78
|
+
'god-budget',
|
|
79
|
+
'god-cost',
|
|
80
|
+
'god-cache-clear',
|
|
81
|
+
'god-logs',
|
|
82
|
+
'god-metrics',
|
|
83
|
+
'god-trace',
|
|
84
|
+
'god-export-otel',
|
|
85
|
+
'god-dogfood',
|
|
86
|
+
'god-quick',
|
|
87
|
+
'god-fast'
|
|
88
|
+
],
|
|
89
|
+
suite: [
|
|
90
|
+
...COMMON,
|
|
91
|
+
'god-suite-init',
|
|
92
|
+
'god-suite-status',
|
|
93
|
+
'god-suite-sync',
|
|
94
|
+
'god-suite-patch',
|
|
95
|
+
'god-suite-release',
|
|
96
|
+
'god-workstream',
|
|
97
|
+
'god-pr-branch',
|
|
98
|
+
'god-sync',
|
|
99
|
+
'god-reconcile',
|
|
100
|
+
'god-review',
|
|
101
|
+
'god-quick',
|
|
102
|
+
'god-fast'
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const PROFILE_DESCRIPTIONS = {
|
|
107
|
+
core: 'front door, status, init, build, review, sync, quick edits',
|
|
108
|
+
builder: 'core plus planning, design, stories, and runtime verification',
|
|
109
|
+
maintainer: 'core plus hygiene, deps, docs, repair, automation, and extensions',
|
|
110
|
+
suite: 'core plus multi-repo suite and workstream coordination',
|
|
111
|
+
full: 'all shipped slash commands'
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function normalizeProfiles(value) {
|
|
115
|
+
if (!value) return ['full'];
|
|
116
|
+
const raw = String(value)
|
|
117
|
+
.split(',')
|
|
118
|
+
.map(part => part.trim().toLowerCase())
|
|
119
|
+
.filter(Boolean);
|
|
120
|
+
const profiles = raw.length > 0 ? raw : ['full'];
|
|
121
|
+
for (const profile of profiles) {
|
|
122
|
+
if (profile !== 'full' && !PROFILE_SKILLS[profile]) {
|
|
123
|
+
throw new Error(`Unknown install profile: ${profile}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (profiles.includes('full')) return ['full'];
|
|
127
|
+
return [...new Set(profiles)];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function selectedSkillNames(profileValue, availableNames) {
|
|
131
|
+
const profiles = normalizeProfiles(profileValue);
|
|
132
|
+
if (profiles.includes('full')) return new Set(availableNames);
|
|
133
|
+
const selected = new Set();
|
|
134
|
+
for (const profile of profiles) {
|
|
135
|
+
for (const name of PROFILE_SKILLS[profile]) {
|
|
136
|
+
if (availableNames.includes(name)) selected.add(name);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return selected;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function describeProfiles(profileValue) {
|
|
143
|
+
return normalizeProfiles(profileValue)
|
|
144
|
+
.map(profile => `${profile}: ${PROFILE_DESCRIPTIONS[profile]}`)
|
|
145
|
+
.join('; ');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
PROFILE_SKILLS,
|
|
150
|
+
PROFILE_DESCRIPTIONS,
|
|
151
|
+
normalizeProfiles,
|
|
152
|
+
selectedSkillNames,
|
|
153
|
+
describeProfiles
|
|
154
|
+
};
|
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
|
+
};
|