godpowers 1.6.16 → 1.6.19
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 +58 -0
- package/README.md +12 -10
- package/RELEASE.md +49 -32
- package/SKILL.md +21 -2
- package/agents/god-orchestrator.md +6 -1
- package/lib/README.md +2 -0
- package/lib/dashboard.js +23 -5
- package/lib/feature-awareness.js +12 -0
- package/lib/pillars.js +9 -0
- package/lib/repo-doc-sync.js +392 -0
- package/lib/repo-surface-sync.js +512 -0
- package/package.json +2 -2
- package/routing/god-export-otel.yaml +24 -0
- package/skills/god-docs.md +13 -0
- package/skills/god-doctor.md +27 -0
- package/skills/god-mode.md +14 -0
- package/skills/god-next.md +7 -2
- package/skills/god-status.md +13 -5
- package/skills/god-sync.md +19 -3
- package/skills/god-version.md +2 -2
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository surface sync.
|
|
3
|
+
*
|
|
4
|
+
* Detects structural drift between commands, routing, package metadata,
|
|
5
|
+
* agent contracts, workflows, recipes, extensions, and release policy docs.
|
|
6
|
+
* The helper is read-only by default. The apply path writes only safe local
|
|
7
|
+
* logs and optional missing routing stubs when explicitly requested.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const { parseSimpleYaml } = require('./intent');
|
|
14
|
+
const extensions = require('./extensions');
|
|
15
|
+
const repoDocSync = require('./repo-doc-sync');
|
|
16
|
+
|
|
17
|
+
const LOG_PATH = '.godpowers/surface/REPO-SURFACE-SYNC.md';
|
|
18
|
+
|
|
19
|
+
const REQUIRED_PACKAGE_FILE_ENTRIES = [
|
|
20
|
+
'bin/',
|
|
21
|
+
'skills/',
|
|
22
|
+
'agents/god-*.md',
|
|
23
|
+
'templates/',
|
|
24
|
+
'references/',
|
|
25
|
+
'routing/',
|
|
26
|
+
'workflows/',
|
|
27
|
+
'schema/',
|
|
28
|
+
'lib/',
|
|
29
|
+
'extensions/',
|
|
30
|
+
'RELEASE.md',
|
|
31
|
+
'SKILL.md',
|
|
32
|
+
'AGENTS.md',
|
|
33
|
+
'CHANGELOG.md',
|
|
34
|
+
'LICENSE'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const REQUIRED_PACKAGE_CHECKS = [
|
|
38
|
+
'lib/feature-awareness.js',
|
|
39
|
+
'lib/repo-doc-sync.js',
|
|
40
|
+
'lib/repo-surface-sync.js',
|
|
41
|
+
'routing/god-export-otel.yaml'
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function rel(projectRoot, absPath) {
|
|
45
|
+
return path.relative(projectRoot, absPath).split(path.sep).join('/');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function exists(projectRoot, relPath) {
|
|
49
|
+
return fs.existsSync(path.join(projectRoot, relPath));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function read(projectRoot, relPath) {
|
|
53
|
+
const file = path.join(projectRoot, relPath);
|
|
54
|
+
if (!fs.existsSync(file)) return '';
|
|
55
|
+
return fs.readFileSync(file, 'utf8');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function write(projectRoot, relPath, content) {
|
|
59
|
+
const file = path.join(projectRoot, relPath);
|
|
60
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
61
|
+
fs.writeFileSync(file, content);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function listFiles(projectRoot, relDir, pattern) {
|
|
65
|
+
const dir = path.join(projectRoot, relDir);
|
|
66
|
+
if (!fs.existsSync(dir)) return [];
|
|
67
|
+
return fs.readdirSync(dir)
|
|
68
|
+
.filter((name) => pattern.test(name))
|
|
69
|
+
.sort()
|
|
70
|
+
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readJson(projectRoot, relPath) {
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(read(projectRoot, relPath));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function routeForSkill(skillPath) {
|
|
82
|
+
const base = path.basename(skillPath, '.md');
|
|
83
|
+
return `routing/${base}.yaml`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function commandForSkill(skillPath) {
|
|
87
|
+
return `/${path.basename(skillPath, '.md')}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function addCheck(checks, area, id, status, relPath, message, opts = {}) {
|
|
91
|
+
checks.push({
|
|
92
|
+
area,
|
|
93
|
+
id,
|
|
94
|
+
status,
|
|
95
|
+
path: relPath,
|
|
96
|
+
message,
|
|
97
|
+
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
98
|
+
safeFix: opts.safeFix === true,
|
|
99
|
+
spawn: opts.spawn || null
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function routingChecks(projectRoot) {
|
|
104
|
+
const checks = [];
|
|
105
|
+
const skills = listFiles(projectRoot, 'skills', /^god.*\.md$/);
|
|
106
|
+
const routes = listFiles(projectRoot, 'routing', /^god.*\.yaml$/);
|
|
107
|
+
const skillRoutes = new Set(skills.map(routeForSkill));
|
|
108
|
+
const routeSet = new Set(routes);
|
|
109
|
+
|
|
110
|
+
for (const skill of skills) {
|
|
111
|
+
const route = routeForSkill(skill);
|
|
112
|
+
addCheck(
|
|
113
|
+
checks,
|
|
114
|
+
'routing',
|
|
115
|
+
`route-for-${path.basename(skill, '.md')}`,
|
|
116
|
+
routeSet.has(route) ? 'fresh' : 'stale',
|
|
117
|
+
route,
|
|
118
|
+
routeSet.has(route)
|
|
119
|
+
? `${commandForSkill(skill)} has routing metadata.`
|
|
120
|
+
: `${commandForSkill(skill)} is missing routing metadata.`,
|
|
121
|
+
{ safeFix: !routeSet.has(route) }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const route of routes) {
|
|
126
|
+
if (!skillRoutes.has(route)) {
|
|
127
|
+
addCheck(
|
|
128
|
+
checks,
|
|
129
|
+
'routing',
|
|
130
|
+
`skill-for-${path.basename(route, '.yaml')}`,
|
|
131
|
+
'stale',
|
|
132
|
+
route,
|
|
133
|
+
`${route} has no matching skill file.`,
|
|
134
|
+
{ severity: 'warning' }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return checks;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function packageChecks(projectRoot) {
|
|
142
|
+
const checks = [];
|
|
143
|
+
const pkg = readJson(projectRoot, 'package.json') || {};
|
|
144
|
+
const lock = readJson(projectRoot, 'package-lock.json');
|
|
145
|
+
const fileEntries = Array.isArray(pkg.files) ? pkg.files : [];
|
|
146
|
+
|
|
147
|
+
for (const entry of REQUIRED_PACKAGE_FILE_ENTRIES) {
|
|
148
|
+
addCheck(
|
|
149
|
+
checks,
|
|
150
|
+
'package',
|
|
151
|
+
`package-files-${entry.replace(/[^a-z0-9]+/gi, '-')}`,
|
|
152
|
+
fileEntries.includes(entry) ? 'fresh' : 'stale',
|
|
153
|
+
'package.json',
|
|
154
|
+
fileEntries.includes(entry)
|
|
155
|
+
? `package.json includes ${entry}.`
|
|
156
|
+
: `package.json files is missing ${entry}.`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const packageCheckText = read(projectRoot, 'scripts/check-package-contents.js');
|
|
161
|
+
for (const required of REQUIRED_PACKAGE_CHECKS) {
|
|
162
|
+
addCheck(
|
|
163
|
+
checks,
|
|
164
|
+
'package',
|
|
165
|
+
`package-check-${required.replace(/[^a-z0-9]+/gi, '-')}`,
|
|
166
|
+
packageCheckText.includes(required) ? 'fresh' : 'stale',
|
|
167
|
+
'scripts/check-package-contents.js',
|
|
168
|
+
packageCheckText.includes(required)
|
|
169
|
+
? `package contents check requires ${required}.`
|
|
170
|
+
: `package contents check does not require ${required}.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (lock && lock.version) {
|
|
175
|
+
addCheck(
|
|
176
|
+
checks,
|
|
177
|
+
'package',
|
|
178
|
+
'package-lock-version',
|
|
179
|
+
lock.version === pkg.version ? 'fresh' : 'stale',
|
|
180
|
+
'package-lock.json',
|
|
181
|
+
lock.version === pkg.version
|
|
182
|
+
? 'package-lock.json version matches package.json.'
|
|
183
|
+
: `package-lock.json version ${lock.version} does not match package.json ${pkg.version}.`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return checks;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseRoute(projectRoot, routePath) {
|
|
191
|
+
try {
|
|
192
|
+
return parseSimpleYaml(read(projectRoot, routePath));
|
|
193
|
+
} catch (err) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function agentChecks(projectRoot) {
|
|
199
|
+
const checks = [];
|
|
200
|
+
const agents = new Set(listFiles(projectRoot, 'agents', /^god.*\.md$/)
|
|
201
|
+
.map((file) => path.basename(file, '.md')));
|
|
202
|
+
const routes = listFiles(projectRoot, 'routing', /^god.*\.yaml$/);
|
|
203
|
+
const missing = new Set();
|
|
204
|
+
|
|
205
|
+
for (const route of routes) {
|
|
206
|
+
const parsed = parseRoute(projectRoot, route);
|
|
207
|
+
const spawns = parsed && parsed.execution && Array.isArray(parsed.execution.spawns)
|
|
208
|
+
? parsed.execution.spawns
|
|
209
|
+
: [];
|
|
210
|
+
for (const spawn of spawns) {
|
|
211
|
+
if (!String(spawn).startsWith('god-')) continue;
|
|
212
|
+
if (!/^god-[a-z0-9-]+$/.test(String(spawn))) continue;
|
|
213
|
+
if (!agents.has(spawn)) {
|
|
214
|
+
missing.add(`${route}:${spawn}`);
|
|
215
|
+
addCheck(
|
|
216
|
+
checks,
|
|
217
|
+
'agents',
|
|
218
|
+
`missing-agent-${spawn}`,
|
|
219
|
+
'stale',
|
|
220
|
+
route,
|
|
221
|
+
`${route} references missing agent ${spawn}.`,
|
|
222
|
+
{ spawn: 'god-auditor' }
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (missing.size === 0) {
|
|
229
|
+
addCheck(checks, 'agents', 'route-spawn-targets', 'fresh',
|
|
230
|
+
'routing/', 'All routed specialist spawns resolve to agent files.');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return checks;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function workflowRecipeChecks(projectRoot) {
|
|
237
|
+
const checks = [];
|
|
238
|
+
const workflows = listFiles(projectRoot, 'workflows', /\.yaml$/);
|
|
239
|
+
const recipes = listFiles(projectRoot, path.join('routing', 'recipes'), /\.yaml$/);
|
|
240
|
+
const commandFlows = read(projectRoot, 'docs/command-flows.md');
|
|
241
|
+
|
|
242
|
+
for (const workflow of workflows) {
|
|
243
|
+
const text = read(projectRoot, workflow);
|
|
244
|
+
const parsed = parseSimpleYaml(text);
|
|
245
|
+
const hasMetadata = Boolean(parsed.apiVersion || parsed.name || parsed.metadata);
|
|
246
|
+
addCheck(
|
|
247
|
+
checks,
|
|
248
|
+
'workflow',
|
|
249
|
+
`workflow-metadata-${path.basename(workflow, '.yaml')}`,
|
|
250
|
+
hasMetadata ? 'fresh' : 'stale',
|
|
251
|
+
workflow,
|
|
252
|
+
hasMetadata ? `${workflow} has metadata.` : `${workflow} is missing parseable metadata.`,
|
|
253
|
+
{ spawn: hasMetadata ? null : 'god-roadmap-reconciler' }
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const recipe of recipes) {
|
|
258
|
+
const text = read(projectRoot, recipe);
|
|
259
|
+
const hasCommand = /\/god-[a-z0-9-]+/.test(text);
|
|
260
|
+
const recipeName = path.basename(recipe, '.yaml');
|
|
261
|
+
addCheck(
|
|
262
|
+
checks,
|
|
263
|
+
'workflow',
|
|
264
|
+
`recipe-command-${recipeName}`,
|
|
265
|
+
hasCommand ? 'fresh' : 'stale',
|
|
266
|
+
recipe,
|
|
267
|
+
hasCommand ? `${recipe} includes a slash-command route.` : `${recipe} has no slash-command route.`,
|
|
268
|
+
{ spawn: hasCommand ? null : 'god-roadmap-reconciler' }
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (workflows.length > 0) {
|
|
273
|
+
addCheck(
|
|
274
|
+
checks,
|
|
275
|
+
'workflow',
|
|
276
|
+
'command-flows-workflows',
|
|
277
|
+
commandFlows.includes('/god-docs') && commandFlows.includes('/god-sync') ? 'fresh' : 'stale',
|
|
278
|
+
'docs/command-flows.md',
|
|
279
|
+
'docs/command-flows.md includes core docs and sync flows.',
|
|
280
|
+
{ spawn: 'god-roadmap-reconciler' }
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return checks;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function extensionChecks(projectRoot) {
|
|
287
|
+
const checks = [];
|
|
288
|
+
const extRoot = path.join(projectRoot, 'extensions');
|
|
289
|
+
const pkg = readJson(projectRoot, 'package.json') || {};
|
|
290
|
+
if (!fs.existsSync(extRoot)) return checks;
|
|
291
|
+
for (const entry of fs.readdirSync(extRoot, { withFileTypes: true })) {
|
|
292
|
+
if (!entry.isDirectory()) continue;
|
|
293
|
+
const packRel = `extensions/${entry.name}`;
|
|
294
|
+
const manifestRel = `${packRel}/manifest.yaml`;
|
|
295
|
+
const packageRel = `${packRel}/package.json`;
|
|
296
|
+
const manifestText = read(projectRoot, manifestRel);
|
|
297
|
+
const packPkg = readJson(projectRoot, packageRel);
|
|
298
|
+
const parsed = manifestText ? extensions.parseManifest(manifestText).manifest : null;
|
|
299
|
+
const validation = parsed ? extensions.validateManifest(parsed, pkg.version || '0.0.0') : ['missing manifest'];
|
|
300
|
+
|
|
301
|
+
addCheck(
|
|
302
|
+
checks,
|
|
303
|
+
'extensions',
|
|
304
|
+
`extension-manifest-${entry.name}`,
|
|
305
|
+
validation.length === 0 ? 'fresh' : 'stale',
|
|
306
|
+
manifestRel,
|
|
307
|
+
validation.length === 0
|
|
308
|
+
? `${entry.name} manifest validates against current Godpowers.`
|
|
309
|
+
: `${entry.name} manifest validation failed: ${validation.join('; ')}.`,
|
|
310
|
+
{ spawn: validation.length === 0 ? null : 'god-coordinator' }
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (parsed && packPkg) {
|
|
314
|
+
const sameName = parsed.metadata && parsed.metadata.name === packPkg.name;
|
|
315
|
+
const sameVersion = parsed.metadata && parsed.metadata.version === packPkg.version;
|
|
316
|
+
const sameEngine = packPkg.peerDependencies
|
|
317
|
+
&& packPkg.peerDependencies.godpowers === parsed.engines.godpowers;
|
|
318
|
+
addCheck(checks, 'extensions', `extension-name-${entry.name}`,
|
|
319
|
+
sameName ? 'fresh' : 'stale', packageRel,
|
|
320
|
+
sameName ? `${entry.name} package name matches manifest.` : `${entry.name} package name does not match manifest.`,
|
|
321
|
+
{ spawn: sameName ? null : 'god-coordinator' });
|
|
322
|
+
addCheck(checks, 'extensions', `extension-version-${entry.name}`,
|
|
323
|
+
sameVersion ? 'fresh' : 'stale', packageRel,
|
|
324
|
+
sameVersion ? `${entry.name} package version matches manifest.` : `${entry.name} package version does not match manifest.`,
|
|
325
|
+
{ spawn: sameVersion ? null : 'god-coordinator' });
|
|
326
|
+
addCheck(checks, 'extensions', `extension-engine-${entry.name}`,
|
|
327
|
+
sameEngine ? 'fresh' : 'stale', packageRel,
|
|
328
|
+
sameEngine ? `${entry.name} peer dependency matches manifest engine.` : `${entry.name} peer dependency does not match manifest engine.`,
|
|
329
|
+
{ spawn: sameEngine ? null : 'god-coordinator' });
|
|
330
|
+
for (const kind of ['skills', 'agents', 'workflows']) {
|
|
331
|
+
const provided = parsed.provides && Array.isArray(parsed.provides[kind])
|
|
332
|
+
? parsed.provides[kind]
|
|
333
|
+
: [];
|
|
334
|
+
for (const item of provided) {
|
|
335
|
+
const ext = kind === 'workflows' ? 'yaml' : 'md';
|
|
336
|
+
const providedRel = `${packRel}/${kind}/${item}.${ext}`;
|
|
337
|
+
addCheck(checks, 'extensions', `extension-${kind}-${entry.name}-${item}`,
|
|
338
|
+
exists(projectRoot, providedRel) ? 'fresh' : 'stale',
|
|
339
|
+
providedRel,
|
|
340
|
+
exists(projectRoot, providedRel)
|
|
341
|
+
? `${providedRel} exists.`
|
|
342
|
+
: `${providedRel} is missing.`,
|
|
343
|
+
{ spawn: exists(projectRoot, providedRel) ? null : 'god-coordinator' });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return checks;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function releasePolicyChecks(projectRoot) {
|
|
352
|
+
const checks = [];
|
|
353
|
+
const docs = repoDocSync.detect(projectRoot);
|
|
354
|
+
addCheck(
|
|
355
|
+
checks,
|
|
356
|
+
'release',
|
|
357
|
+
'repo-doc-sync-fresh',
|
|
358
|
+
docs.status === 'fresh' ? 'fresh' : 'stale',
|
|
359
|
+
'docs/repo-doc-sync.md',
|
|
360
|
+
docs.status === 'fresh'
|
|
361
|
+
? 'Repository documentation sync reports fresh.'
|
|
362
|
+
: `Repository documentation sync reports ${docs.stale.length} stale checks.`,
|
|
363
|
+
{ spawn: docs.status === 'fresh' ? null : 'god-docs-writer' }
|
|
364
|
+
);
|
|
365
|
+
addCheck(
|
|
366
|
+
checks,
|
|
367
|
+
'release',
|
|
368
|
+
'release-checklist-surface-sync',
|
|
369
|
+
read(projectRoot, 'docs/RELEASE-CHECKLIST.md').includes('repo-surface-sync'),
|
|
370
|
+
'docs/RELEASE-CHECKLIST.md',
|
|
371
|
+
'Release checklist references repo-surface-sync readiness.',
|
|
372
|
+
{ spawn: 'god-docs-writer' }
|
|
373
|
+
);
|
|
374
|
+
return checks.map((check) => ({
|
|
375
|
+
...check,
|
|
376
|
+
status: check.status === true ? 'fresh' : (check.status === false ? 'stale' : check.status)
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function detect(projectRoot) {
|
|
381
|
+
const checks = [
|
|
382
|
+
...routingChecks(projectRoot),
|
|
383
|
+
...packageChecks(projectRoot),
|
|
384
|
+
...agentChecks(projectRoot),
|
|
385
|
+
...workflowRecipeChecks(projectRoot),
|
|
386
|
+
...extensionChecks(projectRoot),
|
|
387
|
+
...releasePolicyChecks(projectRoot)
|
|
388
|
+
];
|
|
389
|
+
const stale = checks.filter((check) => check.status !== 'fresh');
|
|
390
|
+
const byArea = {};
|
|
391
|
+
for (const check of checks) {
|
|
392
|
+
if (!byArea[check.area]) byArea[check.area] = { total: 0, stale: 0 };
|
|
393
|
+
byArea[check.area].total++;
|
|
394
|
+
if (check.status !== 'fresh') byArea[check.area].stale++;
|
|
395
|
+
}
|
|
396
|
+
const spawnRecommendations = [...new Set(stale.map((check) => check.spawn).filter(Boolean))]
|
|
397
|
+
.map((agent) => ({
|
|
398
|
+
agent,
|
|
399
|
+
reason: `Repo surface drift requires ${agent} judgment.`,
|
|
400
|
+
paths: [...new Set(stale.filter((check) => check.spawn === agent).map((check) => check.path))].sort()
|
|
401
|
+
}));
|
|
402
|
+
return {
|
|
403
|
+
status: stale.length === 0 ? 'fresh' : 'stale',
|
|
404
|
+
checks,
|
|
405
|
+
stale,
|
|
406
|
+
byArea,
|
|
407
|
+
spawnRecommendations
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function routeTemplate(command) {
|
|
412
|
+
const name = command.replace(/^\//, '');
|
|
413
|
+
return [
|
|
414
|
+
'apiVersion: godpowers/v1',
|
|
415
|
+
'kind: CommandRouting',
|
|
416
|
+
'metadata:',
|
|
417
|
+
` command: ${command}`,
|
|
418
|
+
' description: ',
|
|
419
|
+
' tier: 0',
|
|
420
|
+
'',
|
|
421
|
+
'prerequisites:',
|
|
422
|
+
' required: []',
|
|
423
|
+
'',
|
|
424
|
+
'execution:',
|
|
425
|
+
' spawns: [built-in]',
|
|
426
|
+
' context: fresh',
|
|
427
|
+
' writes: []',
|
|
428
|
+
'',
|
|
429
|
+
'success-path:',
|
|
430
|
+
' next-recommended: /god-status',
|
|
431
|
+
'',
|
|
432
|
+
'failure-path:',
|
|
433
|
+
' on-error: /god-doctor',
|
|
434
|
+
'',
|
|
435
|
+
'endoff:',
|
|
436
|
+
' state-update: none',
|
|
437
|
+
' events: [agent.start, agent.end]',
|
|
438
|
+
''
|
|
439
|
+
].join('\n').replace('description: ', `description: Route metadata for /${name}`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function applySafeFixes(projectRoot, report, opts = {}) {
|
|
443
|
+
const applied = [];
|
|
444
|
+
if (!opts.fixRouting) return applied;
|
|
445
|
+
for (const check of report.stale) {
|
|
446
|
+
if (check.area !== 'routing' || !check.safeFix || !check.path.startsWith('routing/')) continue;
|
|
447
|
+
if (exists(projectRoot, check.path)) continue;
|
|
448
|
+
const command = `/${path.basename(check.path, '.yaml')}`;
|
|
449
|
+
write(projectRoot, check.path, routeTemplate(command));
|
|
450
|
+
applied.push({ path: check.path, check: check.id });
|
|
451
|
+
}
|
|
452
|
+
return applied;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function appendLog(projectRoot, before, after, applied) {
|
|
456
|
+
const now = new Date().toISOString();
|
|
457
|
+
const lines = [];
|
|
458
|
+
if (exists(projectRoot, LOG_PATH)) {
|
|
459
|
+
lines.push(read(projectRoot, LOG_PATH).replace(/\s*$/, ''));
|
|
460
|
+
lines.push('');
|
|
461
|
+
} else {
|
|
462
|
+
lines.push('# Repo Surface Sync Log');
|
|
463
|
+
lines.push('');
|
|
464
|
+
lines.push('- [DECISION] This file records structural repository surface sync checks run by Godpowers.');
|
|
465
|
+
lines.push('- [DECISION] Detection is read-only by default and safe fixes require explicit fix options.');
|
|
466
|
+
lines.push('');
|
|
467
|
+
}
|
|
468
|
+
lines.push(`## ${now}`);
|
|
469
|
+
lines.push('');
|
|
470
|
+
lines.push(`- [DECISION] Repo surface sync status before apply was ${before.status}.`);
|
|
471
|
+
lines.push(`- [DECISION] Repo surface sync status after apply is ${after.status}.`);
|
|
472
|
+
if (applied.length === 0) {
|
|
473
|
+
lines.push('- [DECISION] No structural repository surface files were changed.');
|
|
474
|
+
} else {
|
|
475
|
+
for (const item of applied) {
|
|
476
|
+
lines.push(`- [DECISION] Created or refreshed ${item.path} for ${item.check}.`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const rec of after.spawnRecommendations) {
|
|
480
|
+
lines.push(`- [HYPOTHESIS] ${rec.agent} should review ${rec.paths.join(', ')}.`);
|
|
481
|
+
}
|
|
482
|
+
lines.push('');
|
|
483
|
+
write(projectRoot, LOG_PATH, lines.join('\n'));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function run(projectRoot, opts = {}) {
|
|
487
|
+
const before = detect(projectRoot);
|
|
488
|
+
const applied = applySafeFixes(projectRoot, before, opts);
|
|
489
|
+
const after = detect(projectRoot);
|
|
490
|
+
if (opts.log !== false) appendLog(projectRoot, before, after, applied);
|
|
491
|
+
return {
|
|
492
|
+
before,
|
|
493
|
+
after,
|
|
494
|
+
applied,
|
|
495
|
+
logPath: opts.log === false ? null : LOG_PATH
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function summary(report) {
|
|
500
|
+
if (report.status === 'fresh') return 'fresh';
|
|
501
|
+
return `${report.stale.length} stale across ${Object.keys(report.byArea).length} areas`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
module.exports = {
|
|
505
|
+
LOG_PATH,
|
|
506
|
+
REQUIRED_PACKAGE_CHECKS,
|
|
507
|
+
REQUIRED_PACKAGE_FILE_ENTRIES,
|
|
508
|
+
detect,
|
|
509
|
+
run,
|
|
510
|
+
summary,
|
|
511
|
+
routeTemplate
|
|
512
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godpowers",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.19",
|
|
4
4
|
"description": "AI-powered development system: 109 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
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "node scripts/validate-skills.js && node scripts/test-doc-surface-counts.js && bash scripts/smoke.sh && node scripts/test-runtime.js && node scripts/test-router.js && node scripts/test-recipes.js && node scripts/test-context-writer.js && node scripts/test-pillars.js && node scripts/test-artifact-linter.js && node scripts/test-artifact-diff.js && node scripts/test-design-foundation.js && node scripts/test-linkage.js && node scripts/test-impact.js && node scripts/test-reverse-sync.js && node scripts/test-planning-systems.js && node scripts/test-feature-awareness.js && node scripts/test-integration.js && node scripts/test-cross-artifact.js && node scripts/test-awesome-design.js && node scripts/test-skillui-bridge.js && node scripts/test-runtime-verification.js && node scripts/test-agent-browser.js && node scripts/test-mode-d.js && node scripts/test-runtime-heuristics.js && node scripts/test-agent-validator.js && node scripts/test-story-validator.js && node scripts/test-state.js && node scripts/test-dashboard.js && node scripts/test-automation-providers.js && node scripts/test-intent.js && node scripts/test-events.js && node scripts/test-golden-artifacts.js && node scripts/test-install-smoke.js && node scripts/test-checkpoint.js && node scripts/test-extensions.js && node scripts/test-event-reader.js && node scripts/test-state-lock.js && node scripts/test-cost-saver.js && node scripts/test-budget-onoff.js && node scripts/test-workflow-runner.js && npm run test:e2e && node scripts/test-otel-exporter.js && node scripts/test-extensions-publish.js",
|
|
9
|
+
"test": "node scripts/validate-skills.js && node scripts/test-doc-surface-counts.js && bash scripts/smoke.sh && node scripts/test-runtime.js && node scripts/test-router.js && node scripts/test-recipes.js && node scripts/test-context-writer.js && node scripts/test-pillars.js && node scripts/test-artifact-linter.js && node scripts/test-artifact-diff.js && node scripts/test-design-foundation.js && node scripts/test-linkage.js && node scripts/test-impact.js && node scripts/test-reverse-sync.js && node scripts/test-planning-systems.js && node scripts/test-feature-awareness.js && node scripts/test-repo-doc-sync.js && node scripts/test-repo-surface-sync.js && node scripts/test-integration.js && node scripts/test-cross-artifact.js && node scripts/test-awesome-design.js && node scripts/test-skillui-bridge.js && node scripts/test-runtime-verification.js && node scripts/test-agent-browser.js && node scripts/test-mode-d.js && node scripts/test-runtime-heuristics.js && node scripts/test-agent-validator.js && node scripts/test-story-validator.js && node scripts/test-state.js && node scripts/test-dashboard.js && node scripts/test-automation-providers.js && node scripts/test-intent.js && node scripts/test-events.js && node scripts/test-golden-artifacts.js && node scripts/test-install-smoke.js && node scripts/test-checkpoint.js && node scripts/test-extensions.js && node scripts/test-event-reader.js && node scripts/test-state-lock.js && node scripts/test-cost-saver.js && node scripts/test-budget-onoff.js && node scripts/test-workflow-runner.js && npm run test:e2e && node scripts/test-otel-exporter.js && node scripts/test-extensions-publish.js",
|
|
10
10
|
"prepublishOnly": "npm test",
|
|
11
11
|
"validate-skills": "node scripts/validate-skills.js",
|
|
12
12
|
"test:surface": "node scripts/test-doc-surface-counts.js",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
apiVersion: godpowers/v1
|
|
2
|
+
kind: CommandRouting
|
|
3
|
+
metadata:
|
|
4
|
+
command: /god-export-otel
|
|
5
|
+
description: Export Godpowers events to OpenTelemetry OTLP JSON traces
|
|
6
|
+
tier: 0
|
|
7
|
+
|
|
8
|
+
prerequisites:
|
|
9
|
+
required: []
|
|
10
|
+
|
|
11
|
+
execution:
|
|
12
|
+
spawns: [built-in]
|
|
13
|
+
context: fresh
|
|
14
|
+
writes: []
|
|
15
|
+
|
|
16
|
+
success-path:
|
|
17
|
+
next-recommended: /god-status
|
|
18
|
+
|
|
19
|
+
failure-path:
|
|
20
|
+
on-error: /god-doctor
|
|
21
|
+
|
|
22
|
+
endoff:
|
|
23
|
+
state-update: none
|
|
24
|
+
events: [agent.start, agent.end]
|
package/skills/god-docs.md
CHANGED
|
@@ -23,6 +23,15 @@ Documentation work. Docs that don't lie.
|
|
|
23
23
|
|
|
24
24
|
## Orchestration
|
|
25
25
|
|
|
26
|
+
First call `lib/repo-doc-sync.run(projectRoot)` for mechanical repository
|
|
27
|
+
documentation claims such as README badges, version references, public surface
|
|
28
|
+
counts, and `/god-doctor` sample counts. Report this as `Agent: none, local
|
|
29
|
+
runtime only`.
|
|
30
|
+
|
|
31
|
+
Then call `lib/repo-surface-sync.run(projectRoot)` so documentation work sees
|
|
32
|
+
whether route, package, agent, workflow, recipe, extension, or release policy
|
|
33
|
+
surfaces disagree before prose claims are rewritten.
|
|
34
|
+
|
|
26
35
|
Spawn **god-docs-writer** in fresh context.
|
|
27
36
|
|
|
28
37
|
The agent:
|
|
@@ -56,6 +65,8 @@ Godpowers may invoke docs work proactively in two ways:
|
|
|
56
65
|
|---|---|---|
|
|
57
66
|
| Docs changed after code changed | Spawn `god-docs-writer` in drift-check mode when current workflow owns docs | Do not invent new docs scope |
|
|
58
67
|
| Code changed after docs that claim current behavior | Suggest `/god-docs` or spawn drift-check inside `/god-mode`, `/god-feature`, `/god-refactor`, or `/god-sync` closeout | Verify claims against code before editing |
|
|
68
|
+
| Repo docs surface drift | Run `lib/repo-doc-sync.run` for safe mechanical fixes, then spawn `god-docs-writer` for prose | Do not auto-invent changelog or release notes |
|
|
69
|
+
| Repo structural surface drift | Run `lib/repo-surface-sync.run` and include findings in docs scope | Do not invent routing or agent ownership prose without evidence |
|
|
59
70
|
| `REVIEW-REQUIRED.md` contains docs drift items | Suggest `/god-review-changes` first | Do not auto-clear review items |
|
|
60
71
|
|
|
61
72
|
When auto-invoked, show:
|
|
@@ -65,6 +76,8 @@ Auto-invoked:
|
|
|
65
76
|
Trigger: docs and code changed in the same workflow
|
|
66
77
|
Agent: god-docs-writer
|
|
67
78
|
Local syncs:
|
|
79
|
+
+ repo-doc-sync: <safe mechanical fixes, prose review needed, or no-op>
|
|
80
|
+
+ repo-surface-sync: <structural surface fresh, scoped findings, or no-op>
|
|
68
81
|
+ docs-drift-check: <N claims checked, N drift items>
|
|
69
82
|
Artifacts: .godpowers/docs/UPDATE-LOG.md or no-op
|
|
70
83
|
Log: .godpowers/docs/UPDATE-LOG.md
|
package/skills/god-doctor.md
CHANGED
|
@@ -114,6 +114,30 @@ as a read-only diagnostic. It reports:
|
|
|
114
114
|
`/god-doctor --fix` may call `lib/feature-awareness.run(projectRoot)` because
|
|
115
115
|
that helper writes only safe state metadata and managed context fences.
|
|
116
116
|
|
|
117
|
+
## Repo Documentation Sync
|
|
118
|
+
|
|
119
|
+
For initialized projects, `/god-doctor` calls `lib/repo-doc-sync.detect` as a
|
|
120
|
+
read-only diagnostic. It reports stale README badges, public surface counts,
|
|
121
|
+
release notes, changelog entries, contribution guidance, security policy, and
|
|
122
|
+
Pillars sync planning for changed repo docs.
|
|
123
|
+
|
|
124
|
+
`/god-doctor --fix` may call `lib/repo-doc-sync.run(projectRoot)` because that
|
|
125
|
+
helper writes only safe mechanical version, badge, and count claims. It should
|
|
126
|
+
recommend `god-docs-writer` when narrative release, contribution, support, or
|
|
127
|
+
security prose needs judgment.
|
|
128
|
+
|
|
129
|
+
## Repo Surface Sync
|
|
130
|
+
|
|
131
|
+
For initialized projects, `/god-doctor` calls `lib/repo-surface-sync.detect`
|
|
132
|
+
as a read-only diagnostic. It reports structural drift across command routing,
|
|
133
|
+
package payload rules, agent spawn targets, workflow metadata, recipe command
|
|
134
|
+
routes, extension packs, and release policy checks.
|
|
135
|
+
|
|
136
|
+
`/god-doctor --fix` may call
|
|
137
|
+
`lib/repo-surface-sync.run(projectRoot, { fixRouting: true })` to create
|
|
138
|
+
missing routing metadata for shipped slash-command skills. Other structural
|
|
139
|
+
findings should recommend the scoped specialist named by the helper.
|
|
140
|
+
|
|
117
141
|
## Implementation
|
|
118
142
|
|
|
119
143
|
Built-in, no spawned agent. Reads:
|
|
@@ -122,6 +146,9 @@ Built-in, no spawned agent. Reads:
|
|
|
122
146
|
- `.godpowers/state.json`, `intent.yaml`, `log`, `linkage.json`
|
|
123
147
|
- `lib/feature-awareness.detect(projectRoot)` for existing-project upgrade
|
|
124
148
|
awareness
|
|
149
|
+
- `lib/repo-doc-sync.detect(projectRoot)` for repo documentation freshness
|
|
150
|
+
- `lib/repo-surface-sync.detect(projectRoot)` for structural repo surface
|
|
151
|
+
freshness
|
|
125
152
|
- `bin/install.js` VERSION constant
|
|
126
153
|
|
|
127
154
|
## Exit codes
|
package/skills/god-mode.md
CHANGED
|
@@ -306,6 +306,8 @@ Sync status:
|
|
|
306
306
|
Local syncs:
|
|
307
307
|
+ feature-awareness: <recorded runtime features, refreshed context, or no-op>
|
|
308
308
|
+ reverse-sync: <counts and result>
|
|
309
|
+
+ repo-doc-sync: <refreshed repo docs, recommended god-docs-writer, or no-op>
|
|
310
|
+
+ repo-surface-sync: <checked structural surfaces, recommended scoped agents, or no-op>
|
|
309
311
|
+ pillars-sync: <counts and result>
|
|
310
312
|
+ checkpoint-sync: <created, updated, no-op, or skipped>
|
|
311
313
|
+ context-refresh: <spawned, no-op, or skipped>
|
|
@@ -324,6 +326,16 @@ When `/god-mode` resumes an existing `.godpowers` project, it auto-invokes
|
|
|
324
326
|
keeps upgraded projects aware of new runtime features, current context fences,
|
|
325
327
|
and migration routes without rewriting user artifacts.
|
|
326
328
|
|
|
329
|
+
The mandatory final sync also receives repo documentation sync through
|
|
330
|
+
`/god-sync`. This keeps README badges, release surfaces, contribution guidance,
|
|
331
|
+
security policy checks, and Pillars context planning arc-ready before the
|
|
332
|
+
project run is declared complete.
|
|
333
|
+
|
|
334
|
+
The mandatory final sync also receives repo surface sync through `/god-sync`.
|
|
335
|
+
This keeps routes, packages, agent handoffs, workflow metadata, recipe routes,
|
|
336
|
+
extension packs, and release policy checks aligned before the project run is
|
|
337
|
+
declared complete.
|
|
338
|
+
|
|
327
339
|
If `/god-mode` resumes an existing `.godpowers` project that lacks Pillars,
|
|
328
340
|
it Pillar-izes the project before continuing. Existing `.godpowers` artifacts
|
|
329
341
|
become managed source references in the relevant `agents/*.md` files.
|
|
@@ -345,6 +357,8 @@ Sync status:
|
|
|
345
357
|
Local syncs:
|
|
346
358
|
+ feature-awareness: <recorded runtime features, refreshed context, or no-op>
|
|
347
359
|
+ reverse-sync: <counts and result>
|
|
360
|
+
+ repo-doc-sync: <refreshed repo docs, recommended god-docs-writer, or no-op>
|
|
361
|
+
+ repo-surface-sync: <checked structural surfaces, recommended scoped agents, or no-op>
|
|
348
362
|
+ pillars-sync: <counts and result>
|
|
349
363
|
+ checkpoint-sync: <created, updated, no-op, or skipped>
|
|
350
364
|
+ context-refresh: <spawned, no-op, or skipped>
|