godpowers 2.2.0 → 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 +44 -7
- package/INSPIRATION.md +4 -2
- package/README.md +25 -12
- package/RELEASE.md +23 -27
- 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 +38 -5
- package/lib/reverse-sync.js +12 -2
- 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
package/lib/README.md
CHANGED
|
@@ -22,6 +22,7 @@ package-level integrations.
|
|
|
22
22
|
| `dogfood-runner.js` | Run deterministic messy-repo scenarios against migration, host, extension, and suite release behavior. |
|
|
23
23
|
| `budget.js` | Read and enforce configured budget controls. |
|
|
24
24
|
| `cost-tracker.js` | Track token and cost estimates from event streams. |
|
|
25
|
+
| `atomic-write.js` | Write load-bearing files through temp-file validation and atomic rename. |
|
|
25
26
|
| `fs-async.js` | Promise-based file read/write helpers for non-blocking runtime paths. |
|
|
26
27
|
|
|
27
28
|
## Events and observability
|
|
@@ -45,6 +46,7 @@ package-level integrations.
|
|
|
45
46
|
| `agent-cache.js` | Cache agent metadata for faster routing. |
|
|
46
47
|
| `agent-validator.js` | Validate agent frontmatter and contracts. |
|
|
47
48
|
| `agent-refs.js` | Validate workflow agent references and scan skill/agent prose for phantom references. |
|
|
49
|
+
| `executor-repair.js` | Classify executor repair decisions as retry, decompose, prune, or escalate. |
|
|
48
50
|
| `skill-surface.js` | Derive slash-command metadata from the individual `skills/` files. |
|
|
49
51
|
|
|
50
52
|
## Artifact quality
|
|
@@ -63,7 +65,7 @@ package-level integrations.
|
|
|
63
65
|
|--------|---------|
|
|
64
66
|
| `context-writer.js` | Produce tool-specific context files. |
|
|
65
67
|
| `context-budget.js` | Keep generated context within budget. |
|
|
66
|
-
| `planning-systems.js` | Detect and import
|
|
68
|
+
| `planning-systems.js` | Detect and import legacy planning, BMAD, and Superpowers planning context. |
|
|
67
69
|
| `source-sync.js` | Write managed Godpowers progress back to source-system companion files. |
|
|
68
70
|
| `design-detector.js` | Detect design-system conventions. |
|
|
69
71
|
| `design-spec.js` | Normalize design specifications. |
|
|
@@ -74,6 +76,7 @@ package-level integrations.
|
|
|
74
76
|
| `impeccable-bridge.js` | Bridge runtime checks into impeccable quality workflows. |
|
|
75
77
|
| `extensions.js` | Load and validate extension packs. |
|
|
76
78
|
| `extension-authoring.js` | Scaffold publishable extension packs with manifest, package, README, skill, agent, and workflow files. |
|
|
79
|
+
| `package-legitimacy.js` | Assess third-party package metadata for existence, typo risk, recency, and repository signals. |
|
|
77
80
|
| `pillars.js` | Manage the Pillars project-context layer (`AGENTS.md` plus routed `agents/*.md`). |
|
|
78
81
|
|
|
79
82
|
## Repository and graph helpers
|
|
@@ -81,6 +84,7 @@ package-level integrations.
|
|
|
81
84
|
| Module | Purpose |
|
|
82
85
|
|--------|---------|
|
|
83
86
|
| `code-scanner.js` | Scan source trees for routing and quality evidence. |
|
|
87
|
+
| `source-grounding.js` | Check that build plans cite existing files and symbols before execution starts. |
|
|
84
88
|
| `cross-artifact-impact.js` | Detect relationships between changed artifacts. |
|
|
85
89
|
| `cross-repo-linkage.js` | Track suite-level repository relationships. |
|
|
86
90
|
| `drift-detector.js` | Detect context drift between artifacts and implementation. |
|
|
@@ -98,7 +102,9 @@ package-level integrations.
|
|
|
98
102
|
| `installer-core.js` | Install and uninstall the Godpowers surface for each runtime. |
|
|
99
103
|
| `installer-files.js` | File-copy helpers shared by the installer and its tests. |
|
|
100
104
|
| `installer-args.js` | Parse `bin/install.js` arguments and subcommands. |
|
|
105
|
+
| `install-profiles.js` | Select smaller role-specific slash-command install surfaces. |
|
|
101
106
|
| `installer-runtimes.js` | Map supported runtimes to their config directories. |
|
|
107
|
+
| `package-identity.js` | Centralize package name, version, repository, docs, and command identity. |
|
|
102
108
|
| `automation-providers.js` | Detect and configure host-native automation providers. |
|
|
103
109
|
| `dashboard.js` | Compute the next-step action brief and host guarantee line. |
|
|
104
110
|
| `quick-proof.js` | Render the shipped proof fixture for `godpowers quick-proof`. |
|
|
@@ -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
|
+
};
|