godpowers 2.3.0 → 2.4.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 +61 -0
- package/README.md +28 -16
- package/RELEASE.md +35 -27
- package/SKILL.md +26 -3
- package/agents/god-debugger.md +44 -8
- package/agents/god-executor.md +28 -0
- package/agents/god-quality-reviewer.md +23 -1
- package/agents/god-spec-reviewer.md +6 -0
- package/bin/install.js +6 -0
- package/lib/README.md +5 -2
- package/lib/agent-cache.js +25 -3
- package/lib/code-intelligence.js +161 -0
- package/lib/command-families.js +375 -0
- package/lib/dashboard.js +54 -20
- package/lib/extensions.js +6 -13
- package/lib/feature-awareness.js +2 -2
- package/lib/host-capabilities.js +4 -0
- package/lib/install-profiles.js +1 -0
- package/lib/planning-systems.js +7 -4
- package/lib/quick-proof.js +5 -2
- package/lib/release-surface-sync.js +4 -1
- package/lib/route-quality-sync.js +42 -12
- package/lib/router.js +32 -0
- package/lib/workflow-helper-groups.js +50 -0
- package/lib/workflow-runner.js +6 -1
- package/package.json +2 -2
- package/routing/god-add-backlog.yaml +1 -0
- package/routing/god-add-tests.yaml +1 -0
- package/routing/god-add-todo.yaml +1 -0
- package/routing/god-agent-audit.yaml +6 -0
- package/routing/god-arch.yaml +1 -0
- package/routing/god-archaeology.yaml +1 -0
- package/routing/god-audit.yaml +1 -0
- package/routing/god-automation-setup.yaml +1 -0
- package/routing/god-automation-status.yaml +1 -0
- package/routing/god-budget.yaml +6 -0
- package/routing/god-build-agent.yaml +1 -0
- package/routing/god-build.yaml +1 -0
- package/routing/god-cache-clear.yaml +6 -0
- package/routing/god-check-todos.yaml +6 -0
- package/routing/god-context-scan.yaml +6 -0
- package/routing/god-context.yaml +1 -0
- package/routing/god-cost.yaml +6 -0
- package/routing/god-debug.yaml +1 -0
- package/routing/god-deploy.yaml +1 -0
- package/routing/god-design-impact.yaml +6 -0
- package/routing/god-design.yaml +1 -0
- package/routing/god-discuss.yaml +12 -0
- package/routing/god-docs.yaml +1 -0
- package/routing/god-doctor.yaml +6 -0
- package/routing/god-dogfood.yaml +1 -0
- package/routing/god-explore.yaml +1 -0
- package/routing/god-export-otel.yaml +1 -0
- package/routing/god-extension-add.yaml +6 -0
- package/routing/god-extension-info.yaml +6 -0
- package/routing/god-extension-list.yaml +6 -0
- package/routing/god-extension-remove.yaml +6 -0
- package/routing/god-extension-scaffold.yaml +26 -0
- package/routing/god-extract-learnings.yaml +1 -0
- package/routing/god-fast.yaml +1 -0
- package/routing/god-feature.yaml +1 -0
- package/routing/god-graph.yaml +6 -0
- package/routing/god-harden.yaml +1 -0
- package/routing/god-help.yaml +6 -0
- package/routing/god-hotfix.yaml +1 -0
- package/routing/god-hygiene.yaml +1 -0
- package/routing/god-init.yaml +1 -0
- package/routing/god-intel.yaml +1 -0
- package/routing/god-launch.yaml +6 -0
- package/routing/god-lifecycle.yaml +12 -0
- package/routing/god-link.yaml +1 -0
- package/routing/god-lint.yaml +1 -0
- package/routing/god-list-assumptions.yaml +6 -0
- package/routing/god-locate.yaml +6 -0
- package/routing/god-logs.yaml +12 -0
- package/routing/god-map-codebase.yaml +1 -0
- package/routing/god-metrics.yaml +6 -0
- package/routing/god-migrate.yaml +1 -0
- package/routing/god-mode.yaml +6 -0
- package/routing/god-next.yaml +12 -0
- package/routing/god-note.yaml +1 -0
- package/routing/god-observe.yaml +1 -0
- package/routing/god-org-context.yaml +1 -0
- package/routing/god-party.yaml +1 -0
- package/routing/god-pause-work.yaml +6 -0
- package/routing/god-plant-seed.yaml +1 -0
- package/routing/god-postmortem.yaml +1 -0
- package/routing/god-pr-branch.yaml +1 -0
- package/routing/god-prd.yaml +1 -0
- package/routing/god-preflight.yaml +1 -0
- package/routing/god-progress.yaml +7 -0
- package/routing/god-quick.yaml +1 -0
- package/routing/god-reconcile.yaml +6 -0
- package/routing/god-reconstruct.yaml +1 -0
- package/routing/god-redo.yaml +6 -0
- package/routing/god-refactor.yaml +1 -0
- package/routing/god-repair.yaml +1 -0
- package/routing/god-repo.yaml +1 -0
- package/routing/god-restore.yaml +1 -0
- package/routing/god-resume-work.yaml +6 -0
- package/routing/god-review-changes.yaml +1 -0
- package/routing/god-review.yaml +1 -0
- package/routing/god-roadmap-check.yaml +6 -0
- package/routing/god-roadmap-update.yaml +1 -0
- package/routing/god-roadmap.yaml +1 -0
- package/routing/god-rollback.yaml +1 -0
- package/routing/god-scan.yaml +1 -0
- package/routing/god-set-profile.yaml +1 -0
- package/routing/god-settings.yaml +1 -0
- package/routing/god-skip.yaml +1 -0
- package/routing/god-smite.yaml +1 -0
- package/routing/god-spike.yaml +1 -0
- package/routing/god-sprint.yaml +6 -0
- package/routing/god-stack.yaml +2 -1
- package/routing/god-standards.yaml +1 -0
- package/routing/god-status.yaml +1 -0
- package/routing/god-stories.yaml +1 -0
- package/routing/god-story-build.yaml +1 -0
- package/routing/god-story-close.yaml +1 -0
- package/routing/god-story-verify.yaml +1 -0
- package/routing/god-story.yaml +1 -0
- package/routing/god-suite-init.yaml +1 -0
- package/routing/god-suite-patch.yaml +1 -0
- package/routing/god-suite-release.yaml +1 -0
- package/routing/god-suite-status.yaml +6 -0
- package/routing/god-suite-sync.yaml +1 -0
- package/routing/god-sync.yaml +1 -0
- package/routing/god-tech-debt.yaml +1 -0
- package/routing/god-test-extension.yaml +6 -0
- package/routing/god-test-runtime.yaml +1 -0
- package/routing/god-thread.yaml +6 -0
- package/routing/god-trace.yaml +6 -0
- package/routing/god-undo.yaml +1 -0
- package/routing/god-update-deps.yaml +1 -0
- package/routing/god-upgrade.yaml +1 -0
- package/routing/god-version.yaml +1 -0
- package/routing/god-workstream.yaml +6 -0
- package/routing/god.yaml +6 -0
- 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/routing.v1.json +62 -4
- package/schema/workflow.v1.json +14 -0
- package/skills/god-discuss.md +10 -5
- package/skills/god-doctor.md +9 -3
- package/skills/god-extension-scaffold.md +66 -0
- package/skills/god-help.md +38 -3
- package/skills/god-next.md +19 -2
- package/skills/god-status.md +13 -0
- package/skills/god-sync.md +1 -1
- package/skills/god-version.md +2 -2
- package/skills/god.md +61 -12
- package/workflows/audit-only.yaml +2 -2
- package/workflows/bluefield-arc.yaml +3 -7
- package/workflows/brownfield-arc.yaml +4 -8
- package/workflows/deps-audit.yaml +2 -5
- package/workflows/docs-arc.yaml +2 -2
- package/workflows/feature-arc.yaml +3 -6
- package/workflows/full-arc.yaml +5 -11
- package/workflows/hotfix-arc.yaml +3 -6
- package/workflows/hygiene.yaml +2 -2
- package/workflows/migration-arc.yaml +3 -5
- package/workflows/postmortem.yaml +2 -5
- package/workflows/refactor-arc.yaml +3 -6
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
|
@@ -51,7 +51,7 @@ const FEATURES = [
|
|
|
51
51
|
id: 'route-quality-sync',
|
|
52
52
|
since: '1.6.19',
|
|
53
53
|
commands: ['/god-sync', '/god-doctor', '/god-status', '/god-mode'],
|
|
54
|
-
description: 'Detect symbolic route spawns, unresolved agent targets, and
|
|
54
|
+
description: 'Detect symbolic route spawns, unresolved agent targets, and untyped contextual route exits.'
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
id: 'recipe-coverage-sync',
|
|
@@ -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/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
|
}
|
package/lib/install-profiles.js
CHANGED
package/lib/planning-systems.js
CHANGED
|
@@ -112,7 +112,7 @@ function hashFiles(projectRoot, files) {
|
|
|
112
112
|
for (const file of files.map((f) => f.path).sort()) {
|
|
113
113
|
const full = path.join(projectRoot, file);
|
|
114
114
|
h.update(file);
|
|
115
|
-
if (fs.existsSync(full) && fs.
|
|
115
|
+
if (fs.existsSync(full) && fs.lstatSync(full).isFile()) {
|
|
116
116
|
h.update(fs.readFileSync(full));
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -125,8 +125,10 @@ function isTextFile(filePath) {
|
|
|
125
125
|
|
|
126
126
|
function readText(projectRoot, relPath) {
|
|
127
127
|
const full = path.join(projectRoot, relPath);
|
|
128
|
-
if (!fs.existsSync(full)
|
|
129
|
-
const
|
|
128
|
+
if (!fs.existsSync(full)) return '';
|
|
129
|
+
const stat = fs.lstatSync(full);
|
|
130
|
+
if (!stat.isFile()) return '';
|
|
131
|
+
const size = stat.size;
|
|
130
132
|
const buffer = fs.readFileSync(full);
|
|
131
133
|
const raw = buffer.slice(0, Math.min(size, MAX_FILE_BYTES)).toString('utf8');
|
|
132
134
|
return raw.replace(/\r\n/g, '\n');
|
|
@@ -134,7 +136,8 @@ function readText(projectRoot, relPath) {
|
|
|
134
136
|
|
|
135
137
|
function walkFiles(rootDir, projectRoot, out = []) {
|
|
136
138
|
if (!fs.existsSync(rootDir) || out.length >= MAX_SYSTEM_FILES) return out;
|
|
137
|
-
const stat = fs.
|
|
139
|
+
const stat = fs.lstatSync(rootDir);
|
|
140
|
+
if (stat.isSymbolicLink()) return out;
|
|
138
141
|
if (stat.isFile()) {
|
|
139
142
|
if (isTextFile(rootDir)) out.push(rel(projectRoot, rootDir));
|
|
140
143
|
return out;
|
package/lib/quick-proof.js
CHANGED
|
@@ -93,6 +93,9 @@ function render(proof, opts = {}) {
|
|
|
93
93
|
const next = proof.dashboard.next || {};
|
|
94
94
|
const progress = proof.dashboard.progress || {};
|
|
95
95
|
const planning = proof.dashboard.planning || {};
|
|
96
|
+
const projectCommandRoot = path.resolve(proof.projectRoot) === path.resolve(proof.fixtureRoot)
|
|
97
|
+
? '.'
|
|
98
|
+
: proof.projectRoot;
|
|
96
99
|
|
|
97
100
|
if (opts.brief) {
|
|
98
101
|
return [
|
|
@@ -140,8 +143,8 @@ function render(proof, opts = {}) {
|
|
|
140
143
|
` npx godpowers next --project=${proof.fixtureRoot} --brief`,
|
|
141
144
|
'',
|
|
142
145
|
'Try it on your project:',
|
|
143
|
-
` npx godpowers status --project=${
|
|
144
|
-
` npx godpowers next --project=${
|
|
146
|
+
` npx godpowers status --project=${projectCommandRoot} --brief`,
|
|
147
|
+
` npx godpowers next --project=${projectCommandRoot} --brief`
|
|
145
148
|
].join('\n');
|
|
146
149
|
}
|
|
147
150
|
|
|
@@ -12,15 +12,18 @@ const path = require('path');
|
|
|
12
12
|
const LOG_PATH = '.godpowers/surface/RELEASE-SURFACE-SYNC.md';
|
|
13
13
|
|
|
14
14
|
const REQUIRED_PACKAGE_GUARDS = [
|
|
15
|
+
'lib/command-families.js',
|
|
15
16
|
'lib/dogfood-runner.js',
|
|
16
17
|
'lib/extension-authoring.js',
|
|
17
18
|
'lib/host-capabilities.js',
|
|
18
19
|
'lib/route-quality-sync.js',
|
|
19
20
|
'lib/recipe-coverage-sync.js',
|
|
20
|
-
'lib/release-surface-sync.js'
|
|
21
|
+
'lib/release-surface-sync.js',
|
|
22
|
+
'lib/workflow-helper-groups.js'
|
|
21
23
|
];
|
|
22
24
|
|
|
23
25
|
const REQUIRED_RELEASE_TESTS = [
|
|
26
|
+
'scripts/test-command-families.js',
|
|
24
27
|
'scripts/test-dogfood-runner.js',
|
|
25
28
|
'scripts/test-extension-authoring.js',
|
|
26
29
|
'scripts/test-host-capabilities.js',
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Route quality sync.
|
|
3
3
|
*
|
|
4
4
|
* Detects disconnected route automation surfaces: symbolic spawn tokens,
|
|
5
|
-
* unresolved agent targets, contextual exits without
|
|
6
|
-
*
|
|
5
|
+
* unresolved agent targets, contextual exits without typed outcomes, missing
|
|
6
|
+
* standards coverage, and agent-spawn routes without trace events.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const fs = require('fs');
|
|
@@ -12,6 +12,21 @@ const path = require('path');
|
|
|
12
12
|
const { parseSimpleYaml } = require('./intent');
|
|
13
13
|
|
|
14
14
|
const LOG_PATH = '.godpowers/surface/ROUTE-QUALITY-SYNC.md';
|
|
15
|
+
const CONTEXTUAL_NEXT_VALUES = new Set([
|
|
16
|
+
'varies',
|
|
17
|
+
'varies-by-verdict',
|
|
18
|
+
'steady-state',
|
|
19
|
+
'session-end'
|
|
20
|
+
]);
|
|
21
|
+
const OUTCOME_TYPES = new Set([
|
|
22
|
+
'contextual',
|
|
23
|
+
'verdict-based',
|
|
24
|
+
'steady-state',
|
|
25
|
+
'session-end',
|
|
26
|
+
'requires-selection',
|
|
27
|
+
'no-next-command',
|
|
28
|
+
'explicit-command'
|
|
29
|
+
]);
|
|
15
30
|
|
|
16
31
|
const CONTEXTUAL_NEXT_ALLOWED = new Set([
|
|
17
32
|
'/god',
|
|
@@ -21,6 +36,7 @@ const CONTEXTUAL_NEXT_ALLOWED = new Set([
|
|
|
21
36
|
'/god-check-todos',
|
|
22
37
|
'/god-context-scan',
|
|
23
38
|
'/god-cost',
|
|
39
|
+
'/god-design-impact',
|
|
24
40
|
'/god-discuss',
|
|
25
41
|
'/god-doctor',
|
|
26
42
|
'/god-extension-add',
|
|
@@ -33,10 +49,17 @@ const CONTEXTUAL_NEXT_ALLOWED = new Set([
|
|
|
33
49
|
'/god-list-assumptions',
|
|
34
50
|
'/god-locate',
|
|
35
51
|
'/god-logs',
|
|
52
|
+
'/god-launch',
|
|
36
53
|
'/god-metrics',
|
|
54
|
+
'/god-mode',
|
|
37
55
|
'/god-next',
|
|
56
|
+
'/god-pause-work',
|
|
38
57
|
'/god-redo',
|
|
58
|
+
'/god-reconcile',
|
|
39
59
|
'/god-resume-work',
|
|
60
|
+
'/god-roadmap-check',
|
|
61
|
+
'/god-sprint',
|
|
62
|
+
'/god-suite-status',
|
|
40
63
|
'/god-test-extension',
|
|
41
64
|
'/god-thread',
|
|
42
65
|
'/god-trace',
|
|
@@ -61,6 +84,7 @@ const STANDARDS_EXEMPT_COMMANDS = new Set([
|
|
|
61
84
|
'/god-progress',
|
|
62
85
|
'/god-reconstruct',
|
|
63
86
|
'/god-roadmap-check',
|
|
87
|
+
'/god-extension-scaffold',
|
|
64
88
|
'/god-smite',
|
|
65
89
|
'/god-tech-debt'
|
|
66
90
|
]);
|
|
@@ -139,7 +163,7 @@ function detect(projectRoot) {
|
|
|
139
163
|
.map((file) => path.basename(file, '.md')));
|
|
140
164
|
let symbolicCount = 0;
|
|
141
165
|
let unresolvedCount = 0;
|
|
142
|
-
let
|
|
166
|
+
let typedOutcomeCount = 0;
|
|
143
167
|
let standardsExemptCount = 0;
|
|
144
168
|
let traceEventMissingCount = 0;
|
|
145
169
|
|
|
@@ -192,18 +216,22 @@ function detect(projectRoot) {
|
|
|
192
216
|
);
|
|
193
217
|
}
|
|
194
218
|
|
|
195
|
-
const
|
|
219
|
+
const successPath = route['success-path'] || {};
|
|
220
|
+
const next = successPath['next-recommended'];
|
|
196
221
|
const conditionalNext = route['success-path'] && arr(route['success-path']['conditional-next']);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
222
|
+
const outcome = successPath.outcome || {};
|
|
223
|
+
const needsTypedOutcome = next
|
|
224
|
+
&& (CONTEXTUAL_NEXT_VALUES.has(String(next)) || /\s+or\s+/.test(String(next)));
|
|
225
|
+
if (needsTypedOutcome) {
|
|
226
|
+
if (outcome.type && OUTCOME_TYPES.has(String(outcome.type))) {
|
|
227
|
+
typedOutcomeCount++;
|
|
200
228
|
} else {
|
|
201
229
|
addCheck(
|
|
202
230
|
checks,
|
|
203
|
-
`
|
|
231
|
+
`missing-route-outcome-${command.replace(/[^a-z0-9]+/gi, '-')}`,
|
|
204
232
|
'stale',
|
|
205
233
|
routePath,
|
|
206
|
-
`${command} uses next
|
|
234
|
+
`${command} uses contextual next route ${next} without a typed success-path.outcome.`,
|
|
207
235
|
{ spawn: 'god-roadmap-reconciler' }
|
|
208
236
|
);
|
|
209
237
|
}
|
|
@@ -236,10 +264,10 @@ function detect(projectRoot) {
|
|
|
236
264
|
addCheck(
|
|
237
265
|
checks,
|
|
238
266
|
'contextual-exit-policy',
|
|
239
|
-
checks.some((check) => check.id.startsWith('
|
|
267
|
+
checks.some((check) => check.id.startsWith('missing-route-outcome-')) ? 'stale' : 'fresh',
|
|
240
268
|
'routing/',
|
|
241
|
-
`${
|
|
242
|
-
{ spawn: checks.some((check) => check.id.startsWith('
|
|
269
|
+
`${typedOutcomeCount} contextual route exits have typed outcomes and all other next routes are explicit.`,
|
|
270
|
+
{ spawn: checks.some((check) => check.id.startsWith('missing-route-outcome-')) ? 'god-roadmap-reconciler' : null }
|
|
243
271
|
);
|
|
244
272
|
addCheck(
|
|
245
273
|
checks,
|
|
@@ -307,6 +335,8 @@ function summary(report) {
|
|
|
307
335
|
module.exports = {
|
|
308
336
|
LOG_PATH,
|
|
309
337
|
CONTEXTUAL_NEXT_ALLOWED,
|
|
338
|
+
CONTEXTUAL_NEXT_VALUES,
|
|
339
|
+
OUTCOME_TYPES,
|
|
310
340
|
STANDARDS_EXEMPT_COMMANDS,
|
|
311
341
|
detect,
|
|
312
342
|
run,
|
package/lib/router.js
CHANGED
|
@@ -12,6 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { parseSimpleYaml } = require('./intent');
|
|
14
14
|
const state = require('./state');
|
|
15
|
+
const commandFamilies = require('./command-families');
|
|
15
16
|
|
|
16
17
|
const ROUTING_DIR = path.join(__dirname, '..', 'routing');
|
|
17
18
|
const SAFE_SYNC_PLAN = '.godpowers/sync/SAFE-SYNC-PLAN.md';
|
|
@@ -186,6 +187,33 @@ function getNextCommand(command, opts = {}) {
|
|
|
186
187
|
return sp['next-recommended'] || null;
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
function inferOutcomeType(next) {
|
|
191
|
+
if (!next) return 'no-next-command';
|
|
192
|
+
if (next === 'varies') return 'contextual';
|
|
193
|
+
if (next === 'varies-by-verdict') return 'verdict-based';
|
|
194
|
+
if (next === 'steady-state') return 'steady-state';
|
|
195
|
+
if (next === 'session-end') return 'session-end';
|
|
196
|
+
if (/\s+or\s+/.test(next)) return 'requires-selection';
|
|
197
|
+
if (/^\/god(?:-[a-z-]+)?(?:\s.*)?$/.test(next)) return 'explicit-command';
|
|
198
|
+
return 'contextual';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getRouteOutcome(command) {
|
|
202
|
+
const routing = getRouting(command);
|
|
203
|
+
if (!routing) return null;
|
|
204
|
+
const sp = routing['success-path'] || {};
|
|
205
|
+
const outcome = sp.outcome || {};
|
|
206
|
+
const next = sp['next-recommended'] || null;
|
|
207
|
+
const type = outcome.type || inferOutcomeType(next);
|
|
208
|
+
return {
|
|
209
|
+
type,
|
|
210
|
+
label: outcome.label || type,
|
|
211
|
+
reason: outcome.reason || 'Route outcome is inferred from success-path.next-recommended.',
|
|
212
|
+
next,
|
|
213
|
+
allowedNext: outcome['allowed-next'] || []
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
189
217
|
/**
|
|
190
218
|
* Evaluate a routing condition against the project state.
|
|
191
219
|
*/
|
|
@@ -368,9 +396,13 @@ module.exports = {
|
|
|
368
396
|
getRouting,
|
|
369
397
|
checkPrerequisites,
|
|
370
398
|
getNextCommand,
|
|
399
|
+
getRouteOutcome,
|
|
371
400
|
getAlternatives,
|
|
372
401
|
getStandards,
|
|
373
402
|
getSpawnedAgents,
|
|
403
|
+
getCommandFamily: commandFamilies.familyForCommand,
|
|
404
|
+
resolveTrigger: commandFamilies.resolveTrigger,
|
|
405
|
+
commandFamilies,
|
|
374
406
|
suggestNext,
|
|
375
407
|
evaluateCheck,
|
|
376
408
|
resolveProjectRelative,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow helper groups.
|
|
3
|
+
*
|
|
4
|
+
* Workflow YAML can use these groups to avoid repeating long closeout helper
|
|
5
|
+
* lists. Plans still expand them into exact helper names for visibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const HELPER_GROUPS = {
|
|
9
|
+
'standard-closeout': [
|
|
10
|
+
'repo-doc-sync',
|
|
11
|
+
'repo-surface-sync',
|
|
12
|
+
'checkpoint-sync',
|
|
13
|
+
'pillars-sync-plan'
|
|
14
|
+
],
|
|
15
|
+
'repo-maintenance-closeout': [
|
|
16
|
+
'repo-doc-sync',
|
|
17
|
+
'repo-surface-sync'
|
|
18
|
+
],
|
|
19
|
+
'runtime-awareness-closeout': [
|
|
20
|
+
'feature-awareness',
|
|
21
|
+
'host-capabilities'
|
|
22
|
+
],
|
|
23
|
+
'release-readiness-closeout': [
|
|
24
|
+
'route-quality-sync',
|
|
25
|
+
'recipe-coverage-sync',
|
|
26
|
+
'release-surface-sync'
|
|
27
|
+
],
|
|
28
|
+
'source-sync-closeout': [
|
|
29
|
+
'source-sync-back'
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function expand(groups = [], helpers = []) {
|
|
34
|
+
const expanded = [];
|
|
35
|
+
const names = Array.isArray(groups) ? groups : [groups].filter(Boolean);
|
|
36
|
+
for (const group of names) {
|
|
37
|
+
for (const helper of HELPER_GROUPS[group] || []) {
|
|
38
|
+
if (!expanded.includes(helper)) expanded.push(helper);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const helper of helpers || []) {
|
|
42
|
+
if (!expanded.includes(helper)) expanded.push(helper);
|
|
43
|
+
}
|
|
44
|
+
return expanded;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
HELPER_GROUPS,
|
|
49
|
+
expand
|
|
50
|
+
};
|
package/lib/workflow-runner.js
CHANGED
|
@@ -31,6 +31,7 @@ const path = require('path');
|
|
|
31
31
|
const parser = require('./workflow-parser');
|
|
32
32
|
const asyncFs = require('./fs-async');
|
|
33
33
|
const agentRefs = require('./agent-refs');
|
|
34
|
+
const helperGroups = require('./workflow-helper-groups');
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* @typedef {Object} WorkflowPlanStep
|
|
@@ -134,7 +135,8 @@ function plan(workflow, ctx = {}) {
|
|
|
134
135
|
review: job.review || null,
|
|
135
136
|
'on-pass': job['on-pass'] || null,
|
|
136
137
|
'on-fail': job['on-fail'] || null,
|
|
137
|
-
|
|
138
|
+
localHelperGroups: job['local-helper-groups'] || [],
|
|
139
|
+
localHelpers: helperGroups.expand(job['local-helper-groups'] || [], job['local-helpers'] || []),
|
|
138
140
|
with: job.with || null
|
|
139
141
|
});
|
|
140
142
|
}
|
|
@@ -221,6 +223,9 @@ function serializePlan(p) {
|
|
|
221
223
|
if (step.tier) lines.push(` tier: ${step.tier}`);
|
|
222
224
|
if (step.agent) lines.push(` agent: ${step.agent}`);
|
|
223
225
|
if (step.uses) lines.push(` uses: ${step.uses}`);
|
|
226
|
+
if (step.localHelperGroups && step.localHelperGroups.length) {
|
|
227
|
+
lines.push(` local-helper-groups: [${step.localHelperGroups.join(', ')}]`);
|
|
228
|
+
}
|
|
224
229
|
if (step.localHelpers && step.localHelpers.length) {
|
|
225
230
|
lines.push(` local-helpers: [${step.localHelpers.join(', ')}]`);
|
|
226
231
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godpowers",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI-powered development system:
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "AI-powered development system: 112 slash commands and 40 specialist agents that take a project from raw idea to hardened production. Runs inside Claude Code, Codex, Cursor, Windsurf, Gemini, and 10+ other AI coding tools.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"godpowers": "./bin/install.js"
|
|
7
7
|
},
|
|
@@ -4,6 +4,7 @@ metadata:
|
|
|
4
4
|
command: /god-agent-audit
|
|
5
5
|
description: Validate agents/*.md against agent contract
|
|
6
6
|
tier: 0
|
|
7
|
+
family: verify
|
|
7
8
|
|
|
8
9
|
prerequisites:
|
|
9
10
|
required: []
|
|
@@ -15,6 +16,11 @@ execution:
|
|
|
15
16
|
|
|
16
17
|
success-path:
|
|
17
18
|
next-recommended: varies
|
|
19
|
+
outcome:
|
|
20
|
+
type: contextual
|
|
21
|
+
label: Context-specific next route
|
|
22
|
+
reason: The next route depends on current disk state, command arguments, or user choice.
|
|
23
|
+
allowed-next: [/god-status, /god-next, /god-help]
|
|
18
24
|
|
|
19
25
|
failure-path:
|
|
20
26
|
on-error: /god-doctor
|
package/routing/god-arch.yaml
CHANGED
package/routing/god-audit.yaml
CHANGED
package/routing/god-budget.yaml
CHANGED
|
@@ -4,6 +4,7 @@ metadata:
|
|
|
4
4
|
command: /god-budget
|
|
5
5
|
description:
|
|
6
6
|
tier: 0
|
|
7
|
+
family: configure
|
|
7
8
|
|
|
8
9
|
prerequisites:
|
|
9
10
|
required: []
|
|
@@ -15,6 +16,11 @@ execution:
|
|
|
15
16
|
|
|
16
17
|
success-path:
|
|
17
18
|
next-recommended: varies
|
|
19
|
+
outcome:
|
|
20
|
+
type: contextual
|
|
21
|
+
label: Context-specific next route
|
|
22
|
+
reason: The next route depends on current disk state, command arguments, or user choice.
|
|
23
|
+
allowed-next: [/god-status, /god-next, /god-help]
|
|
18
24
|
|
|
19
25
|
failure-path:
|
|
20
26
|
on-error: /god-doctor
|
package/routing/god-build.yaml
CHANGED
|
@@ -4,6 +4,7 @@ metadata:
|
|
|
4
4
|
command: /god-cache-clear
|
|
5
5
|
description:
|
|
6
6
|
tier: 0
|
|
7
|
+
family: configure
|
|
7
8
|
|
|
8
9
|
prerequisites:
|
|
9
10
|
required: []
|
|
@@ -15,6 +16,11 @@ execution:
|
|
|
15
16
|
|
|
16
17
|
success-path:
|
|
17
18
|
next-recommended: varies
|
|
19
|
+
outcome:
|
|
20
|
+
type: contextual
|
|
21
|
+
label: Context-specific next route
|
|
22
|
+
reason: The next route depends on current disk state, command arguments, or user choice.
|
|
23
|
+
allowed-next: [/god-status, /god-next, /god-help]
|
|
18
24
|
|
|
19
25
|
failure-path:
|
|
20
26
|
on-error: /god-doctor
|
|
@@ -4,6 +4,7 @@ metadata:
|
|
|
4
4
|
command: /god-check-todos
|
|
5
5
|
description: List pending todos; optionally route to one
|
|
6
6
|
tier: 0
|
|
7
|
+
family: capture
|
|
7
8
|
|
|
8
9
|
prerequisites:
|
|
9
10
|
required: []
|
|
@@ -15,6 +16,11 @@ execution:
|
|
|
15
16
|
|
|
16
17
|
success-path:
|
|
17
18
|
next-recommended: varies
|
|
19
|
+
outcome:
|
|
20
|
+
type: contextual
|
|
21
|
+
label: Context-specific next route
|
|
22
|
+
reason: The next route depends on current disk state, command arguments, or user choice.
|
|
23
|
+
allowed-next: [/god-status, /god-next, /god-help]
|
|
18
24
|
|
|
19
25
|
failure-path:
|
|
20
26
|
on-error: /god-doctor
|
|
@@ -4,6 +4,7 @@ metadata:
|
|
|
4
4
|
command: /god-context-scan
|
|
5
5
|
description:
|
|
6
6
|
tier: 0
|
|
7
|
+
family: maintain
|
|
7
8
|
|
|
8
9
|
prerequisites:
|
|
9
10
|
required: []
|
|
@@ -15,6 +16,11 @@ execution:
|
|
|
15
16
|
|
|
16
17
|
success-path:
|
|
17
18
|
next-recommended: varies
|
|
19
|
+
outcome:
|
|
20
|
+
type: contextual
|
|
21
|
+
label: Context-specific next route
|
|
22
|
+
reason: The next route depends on current disk state, command arguments, or user choice.
|
|
23
|
+
allowed-next: [/god-status, /god-next, /god-help]
|
|
18
24
|
|
|
19
25
|
failure-path:
|
|
20
26
|
on-error: /god-doctor
|