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,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository documentation sync.
|
|
3
|
+
*
|
|
4
|
+
* Keeps mechanical public repository claims aligned with the actual runtime
|
|
5
|
+
* surface. Narrative docs remain human or specialist-agent owned.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
const pillars = require('./pillars');
|
|
13
|
+
|
|
14
|
+
const LOG_PATH = '.godpowers/docs/REPO-DOC-SYNC.md';
|
|
15
|
+
|
|
16
|
+
function read(projectRoot, relPath) {
|
|
17
|
+
const file = path.join(projectRoot, relPath);
|
|
18
|
+
if (!fs.existsSync(file)) return '';
|
|
19
|
+
return fs.readFileSync(file, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function write(projectRoot, relPath, text) {
|
|
23
|
+
const file = path.join(projectRoot, relPath);
|
|
24
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
25
|
+
fs.writeFileSync(file, text);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function exists(projectRoot, relPath) {
|
|
29
|
+
return fs.existsSync(path.join(projectRoot, relPath));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function countFiles(projectRoot, dir, pattern) {
|
|
33
|
+
const full = path.join(projectRoot, dir);
|
|
34
|
+
if (!fs.existsSync(full)) return 0;
|
|
35
|
+
return fs.readdirSync(full).filter((name) => pattern.test(name)).length;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readPackage(projectRoot) {
|
|
39
|
+
const file = path.join(projectRoot, 'package.json');
|
|
40
|
+
if (!fs.existsSync(file)) return {};
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function packageVersion(projectRoot) {
|
|
49
|
+
return readPackage(projectRoot).version || 'unknown';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function counts(projectRoot) {
|
|
53
|
+
return {
|
|
54
|
+
skills: countFiles(projectRoot, 'skills', /^god.*\.md$/),
|
|
55
|
+
agents: countFiles(projectRoot, 'agents', /^god.*\.md$/),
|
|
56
|
+
workflows: countFiles(projectRoot, 'workflows', /\.yaml$/),
|
|
57
|
+
recipes: countFiles(projectRoot, path.join('routing', 'recipes'), /\.yaml$/)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function expectedSurface(projectRoot) {
|
|
62
|
+
const version = packageVersion(projectRoot);
|
|
63
|
+
const surfaceCounts = counts(projectRoot);
|
|
64
|
+
return {
|
|
65
|
+
version,
|
|
66
|
+
counts: surfaceCounts,
|
|
67
|
+
surface: `${surfaceCounts.skills} skills, ${surfaceCounts.agents} agents`,
|
|
68
|
+
commandSurface: `${surfaceCounts.skills} slash commands`,
|
|
69
|
+
workflowSurface: `${surfaceCounts.workflows} workflows`,
|
|
70
|
+
recipeSurface: `${surfaceCounts.recipes} recipes`,
|
|
71
|
+
minorSeries: version.split('.').slice(0, 2).join('.')
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function includes(projectRoot, relPath, expected) {
|
|
76
|
+
const text = read(projectRoot, relPath);
|
|
77
|
+
return text.includes(expected);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function makeCheck(id, relPath, expected, opts = {}) {
|
|
81
|
+
return {
|
|
82
|
+
id,
|
|
83
|
+
path: relPath,
|
|
84
|
+
expected,
|
|
85
|
+
safeFix: opts.safeFix === true,
|
|
86
|
+
owner: opts.owner || (opts.safeFix ? 'local runtime' : 'god-docs-writer'),
|
|
87
|
+
reason: opts.reason || ''
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function checkDefinitions(projectRoot) {
|
|
92
|
+
const expected = expectedSurface(projectRoot);
|
|
93
|
+
return [
|
|
94
|
+
makeCheck('package-description-surface', 'package.json',
|
|
95
|
+
`${expected.counts.skills} slash commands and ${expected.counts.agents} specialist agents`,
|
|
96
|
+
{ safeFix: true, reason: 'package metadata is a mechanical count claim' }),
|
|
97
|
+
makeCheck('readme-version-badge', 'README.md', `version-${expected.version}-blue`,
|
|
98
|
+
{ safeFix: true, reason: 'README badge mirrors package version' }),
|
|
99
|
+
makeCheck('readme-reference-counts', 'README.md',
|
|
100
|
+
`all ${expected.counts.skills} skills + ${expected.counts.agents} agents`,
|
|
101
|
+
{ safeFix: true, reason: 'README command reference count mirrors files on disk' }),
|
|
102
|
+
makeCheck('users-version', 'USERS.md', `Godpowers is at v${expected.version}. Stable release.`,
|
|
103
|
+
{ safeFix: true, reason: 'user support version mirrors package version' }),
|
|
104
|
+
makeCheck('architecture-version', 'ARCHITECTURE.md', `STABLE v${expected.version}`,
|
|
105
|
+
{ safeFix: true, reason: 'architecture release marker mirrors package version' }),
|
|
106
|
+
makeCheck('architecture-surface', 'ARCHITECTURE.md',
|
|
107
|
+
`Core: ${expected.surface}, ${expected.workflowSurface}`,
|
|
108
|
+
{ safeFix: true, reason: 'architecture surface mirrors repository counts' }),
|
|
109
|
+
makeCheck('roadmap-version', 'docs/ROADMAP.md', `Current shipped: v${expected.version}`,
|
|
110
|
+
{ safeFix: true, reason: 'roadmap current shipped marker mirrors package version' }),
|
|
111
|
+
makeCheck('roadmap-command-count', 'docs/ROADMAP.md', `**${expected.commandSurface}**`,
|
|
112
|
+
{ safeFix: true, reason: 'roadmap command count mirrors skills directory' }),
|
|
113
|
+
makeCheck('roadmap-agent-count', 'docs/ROADMAP.md',
|
|
114
|
+
`**${expected.counts.agents} specialist agents**`,
|
|
115
|
+
{ safeFix: true, reason: 'roadmap agent count mirrors agents directory' }),
|
|
116
|
+
makeCheck('reference-version', 'docs/reference.md', `reference for v${expected.version}`,
|
|
117
|
+
{ safeFix: true, reason: 'reference docs version mirrors package version' }),
|
|
118
|
+
makeCheck('reference-command-count', 'docs/reference.md',
|
|
119
|
+
`Slash commands (${expected.counts.skills} total)`,
|
|
120
|
+
{ safeFix: true, reason: 'reference command count mirrors skills directory' }),
|
|
121
|
+
makeCheck('reference-agent-count', 'docs/reference.md',
|
|
122
|
+
`Specialist agents (${expected.counts.agents} total)`,
|
|
123
|
+
{ safeFix: true, reason: 'reference agent count mirrors agents directory' }),
|
|
124
|
+
makeCheck('god-version-surface', 'skills/god-version.md',
|
|
125
|
+
`Surface: ${expected.surface}, ${expected.workflowSurface}, ${expected.recipeSurface}`,
|
|
126
|
+
{ safeFix: true, reason: '/god-version output mirrors repository counts' }),
|
|
127
|
+
makeCheck('god-doctor-skill-count', 'skills/god-doctor.md',
|
|
128
|
+
`[OK] ${expected.counts.skills} skills installed`,
|
|
129
|
+
{ safeFix: true, reason: '/god-doctor sample output mirrors skills directory' }),
|
|
130
|
+
makeCheck('god-doctor-agent-count', 'skills/god-doctor.md',
|
|
131
|
+
`[OK] ${expected.counts.agents} agents installed`,
|
|
132
|
+
{ safeFix: true, reason: '/god-doctor sample output mirrors agents directory' }),
|
|
133
|
+
makeCheck('release-notes-version', 'RELEASE.md', `Godpowers ${expected.version}`,
|
|
134
|
+
{ reason: 'release notes are narrative and should be reviewed before publish' }),
|
|
135
|
+
makeCheck('changelog-version', 'CHANGELOG.md', `## [${expected.version}]`,
|
|
136
|
+
{ reason: 'changelog entries are narrative and should be curated' }),
|
|
137
|
+
makeCheck('security-supported-series', 'SECURITY.md', `${expected.minorSeries}.x`,
|
|
138
|
+
{ reason: 'supported versions are release policy and should be reviewed' }),
|
|
139
|
+
makeCheck('contributing-release-sync', 'CONTRIBUTING.md', 'repo documentation sync',
|
|
140
|
+
{ reason: 'contributor release guidance is narrative policy' })
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function detect(projectRoot, opts = {}) {
|
|
145
|
+
const expected = expectedSurface(projectRoot);
|
|
146
|
+
const checks = checkDefinitions(projectRoot).map((check) => {
|
|
147
|
+
const present = exists(projectRoot, check.path);
|
|
148
|
+
const fresh = present && includes(projectRoot, check.path, check.expected);
|
|
149
|
+
return {
|
|
150
|
+
...check,
|
|
151
|
+
status: !present ? 'missing' : (fresh ? 'fresh' : 'stale')
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
const stale = checks.filter((check) => check.status !== 'fresh');
|
|
155
|
+
const safeFixes = stale.filter((check) => check.safeFix);
|
|
156
|
+
const prose = stale.filter((check) => !check.safeFix);
|
|
157
|
+
const changedFiles = opts.changedFiles || [];
|
|
158
|
+
const touchedDocs = changedFiles.filter((file) => isRepoDocPath(file));
|
|
159
|
+
const pillarSyncPlan = touchedDocs.length > 0
|
|
160
|
+
? pillars.planArtifactSync(projectRoot, touchedDocs, opts)
|
|
161
|
+
: [];
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
version: expected.version,
|
|
165
|
+
counts: expected.counts,
|
|
166
|
+
status: stale.length === 0 ? 'fresh' : 'stale',
|
|
167
|
+
checks,
|
|
168
|
+
stale,
|
|
169
|
+
safeFixes,
|
|
170
|
+
prose,
|
|
171
|
+
touchedDocs,
|
|
172
|
+
pillarSyncPlan,
|
|
173
|
+
adjacentOpportunities: adjacentOpportunities(),
|
|
174
|
+
spawnRecommendation: prose.length > 0
|
|
175
|
+
? {
|
|
176
|
+
agent: 'god-docs-writer',
|
|
177
|
+
reason: 'Repo documentation has narrative release, contribution, or security policy drift.',
|
|
178
|
+
paths: [...new Set(prose.map((check) => check.path))].sort()
|
|
179
|
+
}
|
|
180
|
+
: null
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function replaceOnce(text, regex, replacement) {
|
|
185
|
+
if (!regex.test(text)) return text;
|
|
186
|
+
return text.replace(regex, replacement);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function safeFixContent(relPath, text, expected) {
|
|
190
|
+
switch (relPath) {
|
|
191
|
+
case 'package.json':
|
|
192
|
+
return fixPackageDescription(text, expected);
|
|
193
|
+
case 'README.md':
|
|
194
|
+
return replaceOnce(
|
|
195
|
+
replaceOnce(text, /version-[0-9]+\.[0-9]+\.[0-9]+-blue/g, `version-${expected.version}-blue`),
|
|
196
|
+
/all [0-9]+ skills \+ [0-9]+ agents/g,
|
|
197
|
+
`all ${expected.counts.skills} skills + ${expected.counts.agents} agents`
|
|
198
|
+
);
|
|
199
|
+
case 'USERS.md':
|
|
200
|
+
return replaceOnce(text, /Godpowers is at v[0-9]+\.[0-9]+\.[0-9]+\. Stable release\./g,
|
|
201
|
+
`Godpowers is at v${expected.version}. Stable release.`);
|
|
202
|
+
case 'ARCHITECTURE.md':
|
|
203
|
+
return replaceOnce(
|
|
204
|
+
replaceOnce(text, /STABLE v[0-9]+\.[0-9]+\.[0-9]+/g, `STABLE v${expected.version}`),
|
|
205
|
+
/Core: [0-9]+ skills, [0-9]+ agents, [0-9]+ workflows/g,
|
|
206
|
+
`Core: ${expected.surface}, ${expected.workflowSurface}`
|
|
207
|
+
);
|
|
208
|
+
case 'docs/ROADMAP.md':
|
|
209
|
+
return replaceOnce(
|
|
210
|
+
replaceOnce(
|
|
211
|
+
replaceOnce(text, /Current shipped: v[0-9]+\.[0-9]+\.[0-9]+/g,
|
|
212
|
+
`Current shipped: v${expected.version}`),
|
|
213
|
+
/\*\*[0-9]+ slash commands\*\*/g,
|
|
214
|
+
`**${expected.commandSurface}**`
|
|
215
|
+
),
|
|
216
|
+
/\*\*[0-9]+ specialist agents\*\*/g,
|
|
217
|
+
`**${expected.counts.agents} specialist agents**`
|
|
218
|
+
);
|
|
219
|
+
case 'docs/reference.md':
|
|
220
|
+
return replaceOnce(
|
|
221
|
+
replaceOnce(
|
|
222
|
+
replaceOnce(text, /reference for v[0-9]+\.[0-9]+\.[0-9]+/g,
|
|
223
|
+
`reference for v${expected.version}`),
|
|
224
|
+
/Slash commands \([0-9]+ total\)/g,
|
|
225
|
+
`Slash commands (${expected.counts.skills} total)`
|
|
226
|
+
),
|
|
227
|
+
/Specialist agents \([0-9]+ total\)/g,
|
|
228
|
+
`Specialist agents (${expected.counts.agents} total)`
|
|
229
|
+
);
|
|
230
|
+
case 'skills/god-version.md':
|
|
231
|
+
return replaceOnce(text,
|
|
232
|
+
/Surface: [0-9]+ skills, [0-9]+ agents, [0-9]+ workflows, [0-9]+ recipes/g,
|
|
233
|
+
`Surface: ${expected.surface}, ${expected.workflowSurface}, ${expected.recipeSurface}`);
|
|
234
|
+
case 'skills/god-doctor.md':
|
|
235
|
+
return replaceOnce(
|
|
236
|
+
replaceOnce(text, /\[OK\] [0-9]+ skills installed/g,
|
|
237
|
+
`[OK] ${expected.counts.skills} skills installed`),
|
|
238
|
+
/\[OK\] [0-9]+ agents installed/g,
|
|
239
|
+
`[OK] ${expected.counts.agents} agents installed`
|
|
240
|
+
);
|
|
241
|
+
default:
|
|
242
|
+
return text;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function fixPackageDescription(text, expected) {
|
|
247
|
+
try {
|
|
248
|
+
const parsed = JSON.parse(text);
|
|
249
|
+
if (typeof parsed.description === 'string') {
|
|
250
|
+
parsed.description = parsed.description.replace(
|
|
251
|
+
/[0-9]+ slash commands and [0-9]+ specialist agents/g,
|
|
252
|
+
`${expected.counts.skills} slash commands and ${expected.counts.agents} specialist agents`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return `${JSON.stringify(parsed, null, 2)}\n`;
|
|
256
|
+
} catch (err) {
|
|
257
|
+
return text;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function isRepoDocPath(file) {
|
|
262
|
+
return [
|
|
263
|
+
'README.md',
|
|
264
|
+
'CHANGELOG.md',
|
|
265
|
+
'RELEASE.md',
|
|
266
|
+
'CONTRIBUTING.md',
|
|
267
|
+
'SECURITY.md',
|
|
268
|
+
'SUPPORT.md',
|
|
269
|
+
'AGENTS.md'
|
|
270
|
+
].includes(file) || file.startsWith('docs/');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function appendLog(projectRoot, before, after, applied) {
|
|
274
|
+
const now = new Date().toISOString();
|
|
275
|
+
const lines = [];
|
|
276
|
+
if (exists(projectRoot, LOG_PATH)) {
|
|
277
|
+
lines.push(read(projectRoot, LOG_PATH).replace(/\s*$/, ''));
|
|
278
|
+
lines.push('');
|
|
279
|
+
} else {
|
|
280
|
+
lines.push('# Repo Documentation Sync Log');
|
|
281
|
+
lines.push('');
|
|
282
|
+
lines.push('- [DECISION] This file records mechanical repository documentation syncs run by Godpowers.');
|
|
283
|
+
lines.push('- [DECISION] Narrative release, contribution, support, and security policy prose remains owned by humans or `god-docs-writer`.');
|
|
284
|
+
lines.push('');
|
|
285
|
+
}
|
|
286
|
+
lines.push(`## ${now}`);
|
|
287
|
+
lines.push('');
|
|
288
|
+
lines.push(`- [DECISION] Repo documentation sync status before apply was ${before.status}.`);
|
|
289
|
+
lines.push(`- [DECISION] Repo documentation sync status after apply is ${after.status}.`);
|
|
290
|
+
if (applied.length === 0) {
|
|
291
|
+
lines.push('- [DECISION] No mechanical repo documentation files were changed.');
|
|
292
|
+
} else {
|
|
293
|
+
for (const item of applied) {
|
|
294
|
+
lines.push(`- [DECISION] Refreshed ${item.path} for ${item.checks.join(', ')}.`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (after.spawnRecommendation) {
|
|
298
|
+
lines.push(`- [HYPOTHESIS] ${after.spawnRecommendation.agent} should review ${after.spawnRecommendation.paths.join(', ')}.`);
|
|
299
|
+
}
|
|
300
|
+
lines.push('');
|
|
301
|
+
write(projectRoot, LOG_PATH, lines.join('\n'));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function run(projectRoot, opts = {}) {
|
|
305
|
+
const before = detect(projectRoot, opts);
|
|
306
|
+
const expected = expectedSurface(projectRoot);
|
|
307
|
+
const byPath = new Map();
|
|
308
|
+
const applied = [];
|
|
309
|
+
|
|
310
|
+
for (const check of before.safeFixes) {
|
|
311
|
+
if (!byPath.has(check.path)) byPath.set(check.path, []);
|
|
312
|
+
byPath.get(check.path).push(check);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for (const [relPath, checks] of byPath.entries()) {
|
|
316
|
+
const original = read(projectRoot, relPath);
|
|
317
|
+
if (!original) continue;
|
|
318
|
+
const next = safeFixContent(relPath, original, expected);
|
|
319
|
+
if (next !== original) {
|
|
320
|
+
write(projectRoot, relPath, next);
|
|
321
|
+
applied.push({
|
|
322
|
+
path: relPath,
|
|
323
|
+
checks: checks.map((check) => check.id)
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const touched = applied.map((item) => item.path);
|
|
329
|
+
const pillarResults = opts.applyPillars && touched.length > 0
|
|
330
|
+
? pillars.applyArtifactSync(projectRoot, touched, opts)
|
|
331
|
+
: pillars.planArtifactSync(projectRoot, touched, opts);
|
|
332
|
+
|
|
333
|
+
const after = detect(projectRoot, { ...opts, changedFiles: touched });
|
|
334
|
+
if (opts.log !== false) appendLog(projectRoot, before, after, applied);
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
before,
|
|
338
|
+
after,
|
|
339
|
+
applied,
|
|
340
|
+
pillarResults,
|
|
341
|
+
logPath: opts.log === false ? null : LOG_PATH,
|
|
342
|
+
hash: sha(JSON.stringify({ before: before.status, after: after.status, applied }))
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function sha(input) {
|
|
347
|
+
return `sha256:${crypto.createHash('sha256').update(input).digest('hex')}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function adjacentOpportunities() {
|
|
351
|
+
return [
|
|
352
|
+
{
|
|
353
|
+
id: 'routing-surface-sync',
|
|
354
|
+
trigger: '/god-doctor, /god-help, /god-next, /god-sync',
|
|
355
|
+
behavior: 'detect missing routing YAML for installed slash-command skills',
|
|
356
|
+
escalation: 'suggest fix by default, auto-apply only under fix mode'
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: 'package-installer-sync',
|
|
360
|
+
trigger: '/god-doctor, /god-sync, release checks',
|
|
361
|
+
behavior: 'detect package file allowlist and installer smoke drift when runtime files are added',
|
|
362
|
+
escalation: 'suggest fix because package contents affect release'
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: 'agent-contract-sync',
|
|
366
|
+
trigger: '/god-agent-audit, /god-doctor, /god-sync',
|
|
367
|
+
behavior: 'compare route spawns, skill docs, agent files, and agent specs',
|
|
368
|
+
escalation: 'spawn god-auditor when ownership or handoff conflicts need judgment'
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: 'workflow-recipe-graph-sync',
|
|
372
|
+
trigger: '/god-next, /god-mode, /god-doctor',
|
|
373
|
+
behavior: 'compare workflow YAML, recipes, command flows, and orchestrator guidance',
|
|
374
|
+
escalation: 'spawn god-roadmap-reconciler when lifecycle intent is ambiguous'
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: 'extension-pack-sync',
|
|
378
|
+
trigger: '/god-extension-info, /god-extension-list, /god-doctor',
|
|
379
|
+
behavior: 'compare first-party extension manifests, READMEs, skills, agents, and workflows',
|
|
380
|
+
escalation: 'spawn god-coordinator for multi-pack release prep'
|
|
381
|
+
}
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
module.exports = {
|
|
386
|
+
LOG_PATH,
|
|
387
|
+
counts,
|
|
388
|
+
expectedSurface,
|
|
389
|
+
detect,
|
|
390
|
+
run,
|
|
391
|
+
adjacentOpportunities
|
|
392
|
+
};
|