let-them-talk 5.3.0 → 5.4.1
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 +3 -1
- package/README.md +346 -592
- package/SECURITY.md +3 -3
- package/USAGE.md +151 -0
- package/agent-contracts.js +447 -0
- package/api-agents.js +760 -0
- package/autonomy/decision-v2.js +380 -0
- package/autonomy/watchdog-policy.js +572 -0
- package/cli.js +454 -298
- package/conversation-templates/autonomous-feature.json +83 -22
- package/conversation-templates/code-review.json +69 -21
- package/conversation-templates/debug-squad.json +69 -21
- package/conversation-templates/feature-build.json +69 -21
- package/conversation-templates/research-write.json +69 -21
- package/dashboard.html +3148 -174
- package/dashboard.js +864 -786
- package/data-dir.js +58 -0
- package/docs/architecture/branch-semantics.md +157 -0
- package/docs/architecture/canonical-event-schema.md +88 -0
- package/docs/architecture/markdown-workspace.md +183 -0
- package/docs/architecture/runtime-contract.md +459 -0
- package/docs/architecture/runtime-migration-hardening.md +64 -0
- package/events/hooks.js +154 -0
- package/events/log.js +457 -0
- package/events/replay.js +33 -0
- package/events/schema.js +432 -0
- package/managed-team-integration.js +261 -0
- package/office/agents.js +704 -597
- package/office/animation.js +1 -1
- package/office/assets/arcade-cabinet.js +141 -0
- package/office/assets/archway.js +77 -0
- package/office/assets/bar-counter.js +91 -0
- package/office/assets/bar-stool.js +71 -0
- package/office/assets/beanbag.js +64 -0
- package/office/assets/bench.js +99 -0
- package/office/assets/bollard.js +87 -0
- package/office/assets/cactus.js +100 -0
- package/office/assets/carpet-tile.js +46 -0
- package/office/assets/chair.js +123 -0
- package/office/assets/chandelier.js +107 -0
- package/office/assets/coffee-machine.js +95 -0
- package/office/assets/coffee-table.js +81 -0
- package/office/assets/column.js +95 -0
- package/office/assets/desk-lamp.js +102 -0
- package/office/assets/desk.js +76 -0
- package/office/assets/dining-table.js +105 -0
- package/office/assets/door.js +70 -0
- package/office/assets/dual-monitor.js +72 -0
- package/office/assets/fence.js +76 -0
- package/office/assets/filing-cabinet.js +111 -0
- package/office/assets/floor-lamp.js +69 -0
- package/office/assets/floor-tile.js +54 -0
- package/office/assets/flower-pot.js +76 -0
- package/office/assets/foosball.js +95 -0
- package/office/assets/fridge.js +99 -0
- package/office/assets/gaming-chair.js +154 -0
- package/office/assets/gaming-desk.js +105 -0
- package/office/assets/glass-door.js +72 -0
- package/office/assets/glass-wall.js +64 -0
- package/office/assets/half-wall.js +49 -0
- package/office/assets/hanging-plant.js +112 -0
- package/office/assets/index.js +151 -0
- package/office/assets/indoor-tree.js +90 -0
- package/office/assets/l-sofa.js +153 -0
- package/office/assets/marble-floor.js +64 -0
- package/office/assets/materials.js +40 -0
- package/office/assets/meeting-table.js +88 -0
- package/office/assets/microwave.js +94 -0
- package/office/assets/monitor.js +67 -0
- package/office/assets/neon-strip.js +73 -0
- package/office/assets/painting.js +84 -0
- package/office/assets/palm-tree.js +108 -0
- package/office/assets/pc-tower.js +91 -0
- package/office/assets/pendant-light.js +67 -0
- package/office/assets/ping-pong.js +114 -0
- package/office/assets/plant.js +72 -0
- package/office/assets/planter-box.js +95 -0
- package/office/assets/pool-table.js +94 -0
- package/office/assets/printer.js +113 -0
- package/office/assets/reception-desk.js +133 -0
- package/office/assets/rug.js +78 -0
- package/office/assets/sculpture.js +85 -0
- package/office/assets/server-rack.js +98 -0
- package/office/assets/sink.js +109 -0
- package/office/assets/sofa.js +106 -0
- package/office/assets/speaker.js +83 -0
- package/office/assets/spotlight.js +83 -0
- package/office/assets/street-lamp.js +97 -0
- package/office/assets/trash-can.js +83 -0
- package/office/assets/treadmill.js +126 -0
- package/office/assets/trophy.js +89 -0
- package/office/assets/tv-screen.js +79 -0
- package/office/assets/vase.js +84 -0
- package/office/assets/wall-clock.js +84 -0
- package/office/assets/wall.js +53 -0
- package/office/assets/water-cooler.js +146 -0
- package/office/assets/whiteboard.js +115 -0
- package/office/assets.js +3 -431
- package/office/builder.js +791 -355
- package/office/campus-env.js +1012 -1119
- package/office/environment.js +2 -0
- package/office/gallery.js +997 -0
- package/office/index.js +141 -34
- package/office/navigation.js +173 -152
- package/office/player.js +178 -68
- package/office/robot-character.js +272 -0
- package/office/spectator-camera.js +33 -10
- package/office/state.js +2 -0
- package/office/world-save.js +35 -4
- package/package.json +57 -3
- package/providers/comfyui.js +383 -0
- package/providers/dalle.js +79 -0
- package/providers/gemini.js +181 -0
- package/providers/ollama.js +184 -0
- package/providers/replicate.js +115 -0
- package/providers/zai.js +183 -0
- package/runtime-descriptor.js +270 -0
- package/scripts/check-agent-contract-advisory.js +132 -0
- package/scripts/check-api-agent-parity.js +277 -0
- package/scripts/check-autonomy-v2-decision.js +207 -0
- package/scripts/check-autonomy-v2-execution.js +588 -0
- package/scripts/check-autonomy-v2-watchdog.js +224 -0
- package/scripts/check-branch-fork-snapshot.js +337 -0
- package/scripts/check-branch-isolation.js +787 -0
- package/scripts/check-branch-semantics.js +139 -0
- package/scripts/check-dashboard-control-plane.js +1304 -0
- package/scripts/check-docs-onboarding.js +490 -0
- package/scripts/check-event-schema.js +276 -0
- package/scripts/check-evidence-completion.js +239 -0
- package/scripts/check-invariants.js +992 -0
- package/scripts/check-lifecycle-hooks.js +525 -0
- package/scripts/check-managed-team-integration.js +166 -0
- package/scripts/check-markdown-workspace-export.js +548 -0
- package/scripts/check-markdown-workspace-safety.js +347 -0
- package/scripts/check-markdown-workspace.js +136 -0
- package/scripts/check-message-replay.js +429 -0
- package/scripts/check-migration-hardening.js +300 -0
- package/scripts/check-performance-indexing.js +272 -0
- package/scripts/check-provider-capabilities.js +316 -0
- package/scripts/check-runtime-contract.js +109 -0
- package/scripts/check-session-aware-context.js +172 -0
- package/scripts/check-session-lifecycle.js +210 -0
- package/scripts/export-markdown-workspace.js +84 -0
- package/scripts/fixtures/message-replay/clean.jsonl +2 -0
- package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
- package/scripts/migrate-legacy-to-canonical.js +201 -0
- package/scripts/run-verification-suite.js +242 -0
- package/scripts/sync-packaged-docs.js +69 -0
- package/server.js +9577 -7216
- package/state/agents.js +161 -0
- package/state/canonical.js +3068 -0
- package/state/dashboard-queries.js +441 -0
- package/state/evidence.js +56 -0
- package/state/io.js +69 -0
- package/state/markdown-workspace.js +951 -0
- package/state/messages.js +669 -0
- package/state/sessions.js +683 -0
- package/state/tasks-workflows.js +92 -0
- package/templates/debate.json +2 -2
- package/templates/managed.json +4 -4
- package/templates/pair.json +2 -2
- package/templates/review.json +2 -2
- package/templates/team.json +3 -3
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
VALID_CAPABILITIES,
|
|
10
|
+
} = require(path.resolve(__dirname, '..', 'runtime-descriptor.js'));
|
|
11
|
+
|
|
12
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
13
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
14
|
+
const CLI_PATH = path.join(PACKAGE_ROOT, 'cli.js');
|
|
15
|
+
const AGENT_TEMPLATE_DIR = path.join(PACKAGE_ROOT, 'templates');
|
|
16
|
+
const CONVERSATION_TEMPLATE_DIR = path.join(PACKAGE_ROOT, 'conversation-templates');
|
|
17
|
+
const PACKAGE_JSON_PATH = path.join(PACKAGE_ROOT, 'package.json');
|
|
18
|
+
const ROOT_PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'package.json');
|
|
19
|
+
|
|
20
|
+
const DOCS = Object.freeze({
|
|
21
|
+
rootReadme: Object.freeze({
|
|
22
|
+
display: 'README.md',
|
|
23
|
+
path: path.join(REPO_ROOT, 'README.md'),
|
|
24
|
+
expectedVerificationCommands: Object.freeze([
|
|
25
|
+
'npm test',
|
|
26
|
+
'npm --prefix agent-bridge run verify',
|
|
27
|
+
'npm --prefix agent-bridge run verify:contracts',
|
|
28
|
+
'npm --prefix agent-bridge run verify:replay',
|
|
29
|
+
'npm --prefix agent-bridge run verify:invariants',
|
|
30
|
+
'npm --prefix agent-bridge run verify:smoke',
|
|
31
|
+
]),
|
|
32
|
+
checkTemplateInventory: true,
|
|
33
|
+
}),
|
|
34
|
+
packageReadme: Object.freeze({
|
|
35
|
+
display: 'agent-bridge/README.md',
|
|
36
|
+
path: path.join(PACKAGE_ROOT, 'README.md'),
|
|
37
|
+
expectedVerificationCommands: Object.freeze([
|
|
38
|
+
'npm test',
|
|
39
|
+
'npm run verify',
|
|
40
|
+
'npm run verify:contracts',
|
|
41
|
+
'npm run verify:replay',
|
|
42
|
+
'npm run verify:invariants',
|
|
43
|
+
'npm run verify:smoke',
|
|
44
|
+
]),
|
|
45
|
+
checkTemplateInventory: true,
|
|
46
|
+
}),
|
|
47
|
+
usage: Object.freeze({
|
|
48
|
+
display: 'USAGE.md',
|
|
49
|
+
path: path.join(REPO_ROOT, 'USAGE.md'),
|
|
50
|
+
expectedVerificationCommands: Object.freeze([
|
|
51
|
+
'npm test',
|
|
52
|
+
'npm --prefix agent-bridge run verify',
|
|
53
|
+
'npm --prefix agent-bridge run verify:contracts',
|
|
54
|
+
'npm --prefix agent-bridge run verify:replay',
|
|
55
|
+
'npm --prefix agent-bridge run verify:invariants',
|
|
56
|
+
'npm --prefix agent-bridge run verify:smoke',
|
|
57
|
+
]),
|
|
58
|
+
checkTemplateInventory: true,
|
|
59
|
+
}),
|
|
60
|
+
claude: Object.freeze({
|
|
61
|
+
display: 'CLAUDE.md',
|
|
62
|
+
path: path.join(REPO_ROOT, 'CLAUDE.md'),
|
|
63
|
+
expectedVerificationCommands: Object.freeze([
|
|
64
|
+
'npm test',
|
|
65
|
+
'npm --prefix agent-bridge run verify',
|
|
66
|
+
'npm --prefix agent-bridge run verify:contracts',
|
|
67
|
+
'npm --prefix agent-bridge run verify:replay',
|
|
68
|
+
'npm --prefix agent-bridge run verify:invariants',
|
|
69
|
+
'npm --prefix agent-bridge run verify:smoke',
|
|
70
|
+
]),
|
|
71
|
+
checkTemplateInventory: false,
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const REQUIRED_HELP_COMMANDS = Object.freeze([
|
|
76
|
+
'npx let-them-talk init',
|
|
77
|
+
'npx let-them-talk init --claude',
|
|
78
|
+
'npx let-them-talk init --gemini',
|
|
79
|
+
'npx let-them-talk init --codex',
|
|
80
|
+
'npx let-them-talk init --all',
|
|
81
|
+
'npx let-them-talk init --ollama',
|
|
82
|
+
'npx let-them-talk init --template <name>',
|
|
83
|
+
'node .agent-bridge/launch.js',
|
|
84
|
+
'node .agent-bridge/launch.js --lan',
|
|
85
|
+
'node .agent-bridge/launch.js status',
|
|
86
|
+
'node .agent-bridge/launch.js msg <agent> <text>',
|
|
87
|
+
'node .agent-bridge/launch.js reset',
|
|
88
|
+
'npx let-them-talk dashboard',
|
|
89
|
+
'npx let-them-talk status',
|
|
90
|
+
'npx let-them-talk templates',
|
|
91
|
+
'npx let-them-talk uninstall',
|
|
92
|
+
'npx let-them-talk help',
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
const REQUIRED_PACKAGE_SCRIPTS = Object.freeze([
|
|
96
|
+
'test',
|
|
97
|
+
'verify',
|
|
98
|
+
'verify:docs-onboarding',
|
|
99
|
+
'verify:contracts',
|
|
100
|
+
'verify:replay',
|
|
101
|
+
'verify:invariants',
|
|
102
|
+
'verify:smoke',
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const SUPPORTED_STALE_KEYS = Object.freeze([
|
|
106
|
+
'launcher_lan',
|
|
107
|
+
'agent_templates',
|
|
108
|
+
'conversation_templates',
|
|
109
|
+
'runtime_descriptor',
|
|
110
|
+
'verification_surface',
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const USAGE = `Usage: node agent-bridge/scripts/check-docs-onboarding.js [--simulate-stale <${SUPPORTED_STALE_KEYS.join('|')}>]`;
|
|
114
|
+
|
|
115
|
+
function fail(lines, exitCode = 1) {
|
|
116
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
117
|
+
process.exit(exitCode);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function assert(condition, message, problems) {
|
|
121
|
+
if (!condition) problems.push(message);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseArgs(argv) {
|
|
125
|
+
if (argv.length === 0) {
|
|
126
|
+
return { simulateStaleKey: null };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (argv.length === 2 && argv[0] === '--simulate-stale') {
|
|
130
|
+
const simulateStaleKey = argv[1];
|
|
131
|
+
if (!SUPPORTED_STALE_KEYS.includes(simulateStaleKey)) {
|
|
132
|
+
fail([
|
|
133
|
+
`Unknown key for --simulate-stale: ${simulateStaleKey}`,
|
|
134
|
+
`Supported keys: ${SUPPORTED_STALE_KEYS.join(', ')}`,
|
|
135
|
+
USAGE,
|
|
136
|
+
], 2);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { simulateStaleKey };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fail([USAGE], 2);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function readText(filePath, display) {
|
|
146
|
+
if (!fs.existsSync(filePath)) {
|
|
147
|
+
fail(['Docs/onboarding validation failed.', `Missing file: ${display}`], 1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function readJson(filePath, display) {
|
|
154
|
+
if (!fs.existsSync(filePath)) {
|
|
155
|
+
fail(['Docs/onboarding validation failed.', `Missing file: ${display}`], 1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
160
|
+
} catch (error) {
|
|
161
|
+
fail(['Docs/onboarding validation failed.', `Could not parse JSON: ${display}`, error.message], 1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function normalizeCommand(line) {
|
|
166
|
+
return line
|
|
167
|
+
.trim()
|
|
168
|
+
.replace(/\s+/g, ' ')
|
|
169
|
+
.replace(/--template\s+T\b/g, '--template <name>')
|
|
170
|
+
.replace(/--template\s+<[^>]+>/g, '--template <name>')
|
|
171
|
+
.replace(/msg\s+<[^>]+>\s+<[^>]+>/g, 'msg <agent> <text>');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function extractKnownCommand(line) {
|
|
175
|
+
const trimmed = line.trim();
|
|
176
|
+
if (!trimmed) return null;
|
|
177
|
+
|
|
178
|
+
const patterns = [
|
|
179
|
+
/^npx let-them-talk init(?: --(?:claude|gemini|codex|all|ollama))?(?: --template (?:<[^>]+>|T))?/,
|
|
180
|
+
/^node \.agent-bridge\/launch\.js(?: --lan| status| reset| msg <[^>]+> <[^>]+>)?/,
|
|
181
|
+
/^npx let-them-talk (?:dashboard|status|templates|uninstall|help)/,
|
|
182
|
+
/^npm test$/,
|
|
183
|
+
/^npm(?: --prefix agent-bridge run| run) verify(?:[:\w-]+)?$/,
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
for (const pattern of patterns) {
|
|
187
|
+
const match = trimmed.match(pattern);
|
|
188
|
+
if (match) return normalizeCommand(match[0]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function extractCommandSet(text) {
|
|
195
|
+
const commands = new Set();
|
|
196
|
+
|
|
197
|
+
for (const line of text.split(/\r?\n/)) {
|
|
198
|
+
const command = extractKnownCommand(line);
|
|
199
|
+
if (command) commands.add(command);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return commands;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function describeProcessFailure(label, result) {
|
|
206
|
+
const lines = [`${label} failed.`];
|
|
207
|
+
if (typeof result.status === 'number') lines.push(`Exit code: ${result.status}`);
|
|
208
|
+
if (result.error) lines.push(`Process error: ${result.error.message}`);
|
|
209
|
+
if (result.stdout && result.stdout.trim()) {
|
|
210
|
+
lines.push('stdout:');
|
|
211
|
+
lines.push(...result.stdout.trimEnd().split(/\r?\n/).map((line) => ` ${line}`));
|
|
212
|
+
}
|
|
213
|
+
if (result.stderr && result.stderr.trim()) {
|
|
214
|
+
lines.push('stderr:');
|
|
215
|
+
lines.push(...result.stderr.trimEnd().split(/\r?\n/).map((line) => ` ${line}`));
|
|
216
|
+
}
|
|
217
|
+
return lines.join('\n');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function runNode(args, cwd) {
|
|
221
|
+
return spawnSync(process.execPath, args, {
|
|
222
|
+
cwd,
|
|
223
|
+
encoding: 'utf8',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function listTemplateEntries(dirPath, idKey, display) {
|
|
228
|
+
if (!fs.existsSync(dirPath)) {
|
|
229
|
+
fail(['Docs/onboarding validation failed.', `Missing directory: ${display}`], 1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return fs.readdirSync(dirPath)
|
|
233
|
+
.filter((fileName) => fileName.endsWith('.json'))
|
|
234
|
+
.sort((left, right) => left.localeCompare(right))
|
|
235
|
+
.map((fileName) => {
|
|
236
|
+
const filePath = path.join(dirPath, fileName);
|
|
237
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
238
|
+
const json = JSON.parse(raw);
|
|
239
|
+
return {
|
|
240
|
+
fileName,
|
|
241
|
+
raw,
|
|
242
|
+
json,
|
|
243
|
+
id: json[idKey],
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function docHasToken(docText, token) {
|
|
249
|
+
return docText.includes(`\`${token}\``) || docText.includes(token);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function checkDocCommandSurface(docSpec, docText, requiredCommands, problems) {
|
|
253
|
+
const commandSet = extractCommandSet(docText);
|
|
254
|
+
|
|
255
|
+
for (const command of requiredCommands) {
|
|
256
|
+
if (!commandSet.has(command)) {
|
|
257
|
+
problems.push(`${docSpec.display} is missing the current command reference: ${command}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const command of docSpec.expectedVerificationCommands) {
|
|
262
|
+
if (!commandSet.has(command)) {
|
|
263
|
+
problems.push(`${docSpec.display} is missing the current verification command: ${command}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function checkDocRuntimeSurface(docSpec, docText, runtimeFields, capabilityTokens, problems) {
|
|
269
|
+
for (const field of runtimeFields) {
|
|
270
|
+
if (!docHasToken(docText, field)) {
|
|
271
|
+
problems.push(`${docSpec.display} is missing runtime descriptor field guidance for ${field}.`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const capability of capabilityTokens) {
|
|
276
|
+
if (!docHasToken(docText, capability)) {
|
|
277
|
+
problems.push(`${docSpec.display} is missing capability guidance for ${capability}.`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function checkDocTemplateInventory(docSpec, docText, agentTemplateNames, conversationTemplateIds, problems) {
|
|
283
|
+
if (!docSpec.checkTemplateInventory) return;
|
|
284
|
+
|
|
285
|
+
for (const templateName of agentTemplateNames) {
|
|
286
|
+
if (!docHasToken(docText, templateName)) {
|
|
287
|
+
problems.push(`${docSpec.display} is missing agent template inventory item ${templateName}.`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (const templateId of conversationTemplateIds) {
|
|
292
|
+
if (!docHasToken(docText, templateId)) {
|
|
293
|
+
problems.push(`${docSpec.display} is missing conversation template inventory item ${templateId}.`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function launchScriptSupportsLanShortcut(launchScriptText) {
|
|
299
|
+
return launchScriptText.includes("firstArg === '--lan'")
|
|
300
|
+
&& launchScriptText.includes("['dashboard', '--lan'")
|
|
301
|
+
&& launchScriptText.includes('const forwardedArgs =');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function main() {
|
|
305
|
+
const { simulateStaleKey } = parseArgs(process.argv.slice(2));
|
|
306
|
+
const problems = [];
|
|
307
|
+
|
|
308
|
+
const packageJson = readJson(PACKAGE_JSON_PATH, 'agent-bridge/package.json');
|
|
309
|
+
const rootPackageJson = readJson(ROOT_PACKAGE_JSON_PATH, 'package.json');
|
|
310
|
+
|
|
311
|
+
for (const scriptName of REQUIRED_PACKAGE_SCRIPTS) {
|
|
312
|
+
assert(
|
|
313
|
+
packageJson && packageJson.scripts && typeof packageJson.scripts[scriptName] === 'string',
|
|
314
|
+
`agent-bridge/package.json must define script ${scriptName}.`,
|
|
315
|
+
problems
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
assert(
|
|
320
|
+
rootPackageJson && rootPackageJson.scripts && rootPackageJson.scripts.test === 'npm --prefix agent-bridge test',
|
|
321
|
+
'package.json must keep npm test wired to the grouped agent-bridge verification surface.',
|
|
322
|
+
problems
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ltt-docs-onboarding-'));
|
|
326
|
+
const fixtureProject = path.join(fixtureRoot, 'project');
|
|
327
|
+
fs.mkdirSync(fixtureProject, { recursive: true });
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const helpResult = runNode([CLI_PATH, 'help'], fixtureProject);
|
|
331
|
+
if (helpResult.error || helpResult.status !== 0) {
|
|
332
|
+
problems.push(describeProcessFailure('CLI help surface', helpResult));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const helpCommands = extractCommandSet(helpResult.stdout || '');
|
|
336
|
+
for (const command of REQUIRED_HELP_COMMANDS) {
|
|
337
|
+
assert(helpCommands.has(command), `CLI help output must include current command: ${command}`, problems);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const templatesResult = runNode([CLI_PATH, 'templates'], fixtureProject);
|
|
341
|
+
if (templatesResult.error || templatesResult.status !== 0) {
|
|
342
|
+
problems.push(describeProcessFailure('CLI template listing', templatesResult));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
assert((templatesResult.stdout || '').includes('Available Agent Templates'), 'CLI templates output must show the agent template inventory header.', problems);
|
|
346
|
+
assert((templatesResult.stdout || '').includes('Usage: npx let-them-talk init --template <name>'), 'CLI templates output must point users at init --template <name>.', problems);
|
|
347
|
+
assert((templatesResult.stdout || '').includes('agent-bridge/conversation-templates/*.json'), 'CLI templates output must explain where conversation templates live.', problems);
|
|
348
|
+
|
|
349
|
+
const initResult = runNode([CLI_PATH, 'init', '--claude', '--template', 'pair'], fixtureProject);
|
|
350
|
+
if (initResult.error || initResult.status !== 0) {
|
|
351
|
+
problems.push(describeProcessFailure('CLI init onboarding fixture', initResult));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const initOutput = initResult.stdout || '';
|
|
355
|
+
const fixtureMcpPath = path.join(fixtureProject, '.mcp.json');
|
|
356
|
+
const fixtureGitignorePath = path.join(fixtureProject, '.gitignore');
|
|
357
|
+
const fixtureLaunchPath = path.join(fixtureProject, '.agent-bridge', 'launch.js');
|
|
358
|
+
|
|
359
|
+
assert(initOutput.includes('Local launcher saved to .agent-bridge/launch.js'), 'init output must confirm the local launcher path.', problems);
|
|
360
|
+
assert(initOutput.includes('Template: pair'), 'init --template pair must print the requested agent template.', problems);
|
|
361
|
+
assert(initOutput.includes('These prompts assume current onboarding: register, get_briefing(), then get_guide() when you need the current rules.'), 'init --template output must explain the current onboarding order.', problems);
|
|
362
|
+
assert(fs.existsSync(fixtureMcpPath), 'init must write .mcp.json for the fresh local onboarding path.', problems);
|
|
363
|
+
assert(fs.existsSync(fixtureGitignorePath), 'init must write or update .gitignore for the fresh local onboarding path.', problems);
|
|
364
|
+
assert(fs.existsSync(fixtureLaunchPath), 'init must write .agent-bridge/launch.js for the fresh local onboarding path.', problems);
|
|
365
|
+
|
|
366
|
+
if (fs.existsSync(fixtureMcpPath)) {
|
|
367
|
+
const fixtureMcp = readJson(fixtureMcpPath, '.mcp.json fixture');
|
|
368
|
+
const serverEntry = fixtureMcp && fixtureMcp.mcpServers ? fixtureMcp.mcpServers['agent-bridge'] : null;
|
|
369
|
+
assert(serverEntry && serverEntry.command === 'node', 'init must write the agent-bridge MCP server command into .mcp.json.', problems);
|
|
370
|
+
assert(serverEntry && Array.isArray(serverEntry.args) && serverEntry.args.some((entry) => String(entry).endsWith('/server.js')), 'init must point .mcp.json at agent-bridge/server.js.', problems);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const launchScriptText = fs.existsSync(fixtureLaunchPath)
|
|
374
|
+
? fs.readFileSync(fixtureLaunchPath, 'utf8')
|
|
375
|
+
: '';
|
|
376
|
+
|
|
377
|
+
const launcherStatusResult = fs.existsSync(fixtureLaunchPath)
|
|
378
|
+
? runNode([fixtureLaunchPath, 'status'], fixtureProject)
|
|
379
|
+
: { status: 1, stdout: '', stderr: 'launch.js missing', error: null };
|
|
380
|
+
if (launcherStatusResult.error || launcherStatusResult.status !== 0) {
|
|
381
|
+
problems.push(describeProcessFailure('Generated local launcher status command', launcherStatusResult));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const agentTemplateEntries = listTemplateEntries(AGENT_TEMPLATE_DIR, 'name', 'agent-bridge/templates');
|
|
385
|
+
const conversationTemplateEntries = listTemplateEntries(CONVERSATION_TEMPLATE_DIR, 'id', 'agent-bridge/conversation-templates');
|
|
386
|
+
|
|
387
|
+
const runtimeFields = ['runtime_type', 'provider_id', 'model_id', 'capabilities'];
|
|
388
|
+
const capabilityTokens = [...VALID_CAPABILITIES];
|
|
389
|
+
const agentTemplateNames = agentTemplateEntries.map((entry) => entry.id);
|
|
390
|
+
const conversationTemplateIds = conversationTemplateEntries.map((entry) => entry.id);
|
|
391
|
+
|
|
392
|
+
let lanShortcutSupported = launchScriptSupportsLanShortcut(launchScriptText);
|
|
393
|
+
|
|
394
|
+
if (simulateStaleKey === 'launcher_lan') {
|
|
395
|
+
lanShortcutSupported = false;
|
|
396
|
+
}
|
|
397
|
+
if (simulateStaleKey === 'agent_templates') {
|
|
398
|
+
agentTemplateNames.push('stale-template');
|
|
399
|
+
}
|
|
400
|
+
if (simulateStaleKey === 'conversation_templates') {
|
|
401
|
+
conversationTemplateIds.push('stale-conversation');
|
|
402
|
+
}
|
|
403
|
+
if (simulateStaleKey === 'runtime_descriptor') {
|
|
404
|
+
runtimeFields.push('provider');
|
|
405
|
+
}
|
|
406
|
+
if (simulateStaleKey === 'verification_surface') {
|
|
407
|
+
REQUIRED_PACKAGE_SCRIPTS.concat(['verify:stale-reference']).forEach((scriptName) => {
|
|
408
|
+
assert(
|
|
409
|
+
packageJson && packageJson.scripts && typeof packageJson.scripts[scriptName] === 'string',
|
|
410
|
+
`agent-bridge/package.json must define script ${scriptName}.`,
|
|
411
|
+
problems
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
assert(lanShortcutSupported, 'Generated .agent-bridge/launch.js must preserve the documented --lan onboarding shortcut.', problems);
|
|
417
|
+
|
|
418
|
+
for (const templateName of agentTemplateNames) {
|
|
419
|
+
assert((templatesResult.stdout || '').includes(templateName), `CLI templates output must list built-in agent template ${templateName}.`, problems);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
for (const entry of agentTemplateEntries) {
|
|
423
|
+
const agents = Array.isArray(entry.json.agents) ? entry.json.agents : [];
|
|
424
|
+
assert(typeof entry.id === 'string' && entry.id.length > 0, `${entry.fileName} must define a template name.`, problems);
|
|
425
|
+
assert(agents.length > 0, `${entry.fileName} must contain at least one agent prompt.`, problems);
|
|
426
|
+
|
|
427
|
+
for (const agent of agents) {
|
|
428
|
+
const prompt = agent && typeof agent.prompt === 'string' ? agent.prompt : '';
|
|
429
|
+
assert(/register/i.test(prompt), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must still tell the agent to register.`, problems);
|
|
430
|
+
assert(prompt.includes('get_briefing()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include get_briefing().`, problems);
|
|
431
|
+
assert(prompt.includes('get_guide()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include get_guide().`, problems);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const pairTemplate = agentTemplateEntries.find((entry) => entry.id === 'pair');
|
|
436
|
+
const teamTemplate = agentTemplateEntries.find((entry) => entry.id === 'team');
|
|
437
|
+
const reviewTemplate = agentTemplateEntries.find((entry) => entry.id === 'review');
|
|
438
|
+
const managedTemplate = agentTemplateEntries.find((entry) => entry.id === 'managed');
|
|
439
|
+
|
|
440
|
+
assert(pairTemplate && pairTemplate.raw.includes('summary') && pairTemplate.raw.includes('verification') && pairTemplate.raw.includes('files_changed') && pairTemplate.raw.includes('confidence'), 'pair.json must keep evidence-backed handoff guidance.', problems);
|
|
441
|
+
assert(teamTemplate && teamTemplate.raw.includes('summary') && teamTemplate.raw.includes('verification') && teamTemplate.raw.includes('files_changed') && teamTemplate.raw.includes('confidence'), 'team.json must keep evidence-backed report guidance.', problems);
|
|
442
|
+
assert(reviewTemplate && reviewTemplate.raw.includes('summary') && reviewTemplate.raw.includes('verification') && reviewTemplate.raw.includes('files_changed') && reviewTemplate.raw.includes('confidence'), 'review.json must keep evidence-backed review handoff guidance.', problems);
|
|
443
|
+
assert(teamTemplate && teamTemplate.raw.includes('specific capabilities'), 'team.json must keep capability-aware coordination guidance.', problems);
|
|
444
|
+
assert(managedTemplate && managedTemplate.raw.includes('update_task(..., evidence={') && managedTemplate.raw.includes('advance_workflow(..., evidence={') && managedTemplate.raw.includes('required_capabilities') && managedTemplate.raw.includes('preferred_capabilities'), 'managed.json must keep evidence-backed completion and capability-aware assignment guidance.', problems);
|
|
445
|
+
|
|
446
|
+
for (const entry of conversationTemplateEntries) {
|
|
447
|
+
const agents = Array.isArray(entry.json.agents) ? entry.json.agents : [];
|
|
448
|
+
assert(typeof entry.id === 'string' && entry.id.length > 0, `${entry.fileName} must define a conversation template id.`, problems);
|
|
449
|
+
assert(agents.length > 0, `${entry.fileName} must contain at least one autonomous agent prompt.`, problems);
|
|
450
|
+
|
|
451
|
+
for (const agent of agents) {
|
|
452
|
+
const prompt = agent && typeof agent.prompt === 'string' ? agent.prompt : '';
|
|
453
|
+
assert(/register/i.test(prompt), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must still tell the agent to register.`, problems);
|
|
454
|
+
assert(prompt.includes('get_briefing()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include get_briefing().`, problems);
|
|
455
|
+
assert(prompt.includes('get_guide()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include get_guide().`, problems);
|
|
456
|
+
assert(prompt.includes('get_work()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include get_work().`, problems);
|
|
457
|
+
assert(prompt.includes('verify_and_advance()'), `${entry.fileName} prompt for ${agent.name || '<unknown>'} must include verify_and_advance().`, problems);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
assert(entry.raw.includes('required_capabilities') && entry.raw.includes('preferred_capabilities'), `${entry.fileName} must keep capability-aware work guidance.`, problems);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
for (const docSpec of Object.values(DOCS)) {
|
|
464
|
+
const docText = readText(docSpec.path, docSpec.display);
|
|
465
|
+
checkDocCommandSurface(docSpec, docText, REQUIRED_HELP_COMMANDS, problems);
|
|
466
|
+
checkDocRuntimeSurface(docSpec, docText, runtimeFields, capabilityTokens, problems);
|
|
467
|
+
checkDocTemplateInventory(docSpec, docText, agentTemplateNames, conversationTemplateIds, problems);
|
|
468
|
+
}
|
|
469
|
+
} finally {
|
|
470
|
+
fs.rmSync(fixtureRoot, { recursive: true, force: true });
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (problems.length > 0) {
|
|
474
|
+
const lines = ['Docs/onboarding validation failed.'];
|
|
475
|
+
if (simulateStaleKey) lines.push(`Simulated stale key: ${simulateStaleKey}`);
|
|
476
|
+
lines.push(...problems.map((problem) => `- ${problem}`));
|
|
477
|
+
fail(lines, 1);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
console.log([
|
|
481
|
+
'Docs/onboarding validation passed.',
|
|
482
|
+
'- CLI help exposes the current init, launcher, and helper commands.',
|
|
483
|
+
'- init writes the local launcher and MCP config, and the generated launcher preserves the documented --lan shortcut.',
|
|
484
|
+
'- Template listing still points users at init --template <name> and the separate conversation-templates surface.',
|
|
485
|
+
'- Built-in agent and conversation templates still carry the current register, get_briefing, get_guide, get_work, verify_and_advance, evidence, and capability-aware guidance.',
|
|
486
|
+
'- README.md, agent-bridge/README.md, USAGE.md, and CLAUDE.md still match the shipped launcher, template inventory, runtime descriptor, and grouped verification surface.',
|
|
487
|
+
].join('\n'));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
main();
|