pantheon-opencode 1.1.1-beta.8 → 1.1.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.
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * generate-routing-docs.mjs — Generate routing docs from routing.yml
4
- *
5
- * Reads the canonical routing.yml and outputs markdown documentation
6
- * to stdout. Pipe to a file to save: node generate-routing-docs.mjs > ROUTING.md
7
- */
8
-
9
- import { existsSync, readFileSync } from 'node:fs'
10
- import { dirname, join } from 'node:path'
11
- import { fileURLToPath } from 'node:url'
12
- import yaml from 'js-yaml'
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url))
15
- const ROOT = join(__dirname, '..')
16
-
17
- const routingPath = join(ROOT, 'routing.yml')
18
- if (!existsSync(routingPath)) {
19
- console.error('❌ routing.yml not found')
20
- process.exit(1)
21
- }
22
- const routing = yaml.load(readFileSync(routingPath, 'utf8'))
23
-
24
- const agents = routing.agents || {}
25
- const matrix = routing.routing_matrix || []
26
- const handoffs = routing.handoffs || {}
27
-
28
- let doc = ''
29
-
30
- // Title
31
- doc += `# Pantheon Routing Reference\n\n`
32
- doc += `Auto-generated from \`routing.yml\` (v${routing.version || '?'})\n\n`
33
- doc += `${Object.keys(agents).length} agents · ${matrix.length} routing rules · ${Object.keys(handoffs).length} agent handoff groups\n\n`
34
-
35
- // Agent Registry
36
- doc += `---\n\n## Agent Registry\n\n`
37
- doc += `All ${Object.keys(agents).length} Pantheon agents with roles, model tiers, and capabilities.\n\n`
38
- doc += `| Agent | Role | Model Tier | User-Invocable | Delegates To | Skills |\n`
39
- doc += `|-------|------|-----------|----------------|-------------|--------|\n`
40
-
41
- for (const [name, info] of Object.entries(agents)) {
42
- const role = info.role || '—'
43
- const tier = info.model_tier || '—'
44
- const invocable = info.user_invocable ? '✅ Yes' : '❌ No'
45
- const delegates = (info.subagent_can_delegate_to || []).join(', ') || '—'
46
- const agentSkills = (info.skills || []).map((s) => `\`${s}\``).join(', ') || '—'
47
- doc += `| **@${name}** | ${role} | ${tier} | ${invocable} | ${delegates} | ${agentSkills} |\n`
48
- }
49
-
50
- // Routing Matrix
51
- doc += `\n---\n\n## Routing Matrix\n\n`
52
- doc += `Task category → primary agent mapping. Use this to decide which agent to invoke.\n\n`
53
- doc += `| Category | Primary Agent | Model Tier | Can Run Parallel With |\n`
54
- doc += `|----------|--------------|-----------|----------------------|\n`
55
-
56
- for (const entry of matrix) {
57
- const primary = `@${entry.primary_agent}`
58
- const tier = entry.model_tier || '—'
59
- const parallel = entry.parallel_with ? entry.parallel_with.map((a) => `@${a}`).join(', ') : '—'
60
- doc += `| ${entry.category} | ${primary} | ${tier} | ${parallel} |\n`
61
- }
62
-
63
- // Handoff Reference
64
- doc += `\n---\n\n## Handoff Reference\n\n`
65
- doc += `Structured delegation contracts between agents.\n\n`
66
-
67
- for (const [agentName, agentHandoffs] of Object.entries(handoffs)) {
68
- doc += `### @${agentName}\n\n`
69
- doc += `| Handoff | Target Agent | Model Tier | Description |\n`
70
- doc += `|---------|-------------|-----------|-------------|\n`
71
-
72
- for (const [key, handoff] of Object.entries(agentHandoffs)) {
73
- if (typeof handoff !== 'object') continue
74
- const target = `@${handoff.agent}`
75
- const tier = handoff.model_tier || '—'
76
- const desc = handoff.description || handoff.prompt?.substring(0, 80) || '—'
77
- doc += `| **${key}** | ${target} | ${tier} | ${desc} |\n`
78
- }
79
- doc += `\n`
80
- }
81
-
82
- // Deployment platforms
83
- doc += `---\n\n## Deployment Platforms\n\n`
84
- doc += `Supported platforms and their configuration details.\n\n`
85
- doc += `| Platform | Directory | Format | Handoff Strategy |\n`
86
- doc += `|----------|-----------|--------|-----------------|\n`
87
-
88
- const PLATFORM_INFO = {
89
- opencode: { dir: '.opencode/agents/', format: '.md with YAML', handoff: 'native (YAML)' },
90
- claude: { dir: '.claude/agents/', format: '.md with YAML', handoff: 'embed (body)' },
91
- cursor: { dir: '.cursor/rules/', format: '.mdc rule files', handoff: 'embed (body)' },
92
- windsurf: { dir: '.windsurf/rules/', format: '.md rule files', handoff: 'embed (body)' },
93
- cline: { dir: '.clinerules/', format: 'plain text', handoff: 'embed (body)' },
94
- copilot: { dir: '.github/agents/', format: '.agent.md with YAML', handoff: 'native (YAML)' },
95
- continue: { dir: '.continue/rules/', format: '.md rules', handoff: 'embed (body)' },
96
- }
97
-
98
- for (const [platform, info] of Object.entries(PLATFORM_INFO)) {
99
- doc += `| **${platform}** | ${info.dir} | ${info.format} | ${info.handoff} |\n`
100
- }
101
-
102
- doc += `\n---\n\n*Generated on ${new Date().toISOString().split('T')[0]} from \`routing.yml\`*\n`
103
-
104
- console.log(doc)
@@ -1,597 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * manifest.mjs — /pantheon-manifest command
4
- *
5
- * One-command pipeline: sync canonical agents → install to target → verify integrity
6
- *
7
- * Steps:
8
- * 1. SYNC: Generate platform-specific agent files from canonical sources
9
- * 2. INSTALL: Copy generated files + config to target OpenCode directory
10
- * 3. VERIFY: Agent file count, MCP connectivity, config integrity, agent sources
11
- * 4. REPORT: Structured pass/fail output with colored status
12
- *
13
- * Usage:
14
- * node scripts/manifest.mjs # default: ~/.config/opencode, opencode platform
15
- * node scripts/manifest.mjs --check-only # verify only, no sync/install
16
- * node scripts/manifest.mjs --target ~/.config/opencode
17
- * node scripts/manifest.mjs --platforms opencode,claude
18
- * node scripts/manifest.mjs --skip-sync # sync already done, just install+verify
19
- * node scripts/manifest.mjs --skip-install # sync+verify, no install
20
- * node scripts/manifest.mjs --verbose
21
- * node scripts/manifest.mjs --help
22
- *
23
- * Exit codes:
24
- * 0 = all checks pass
25
- * 1 = warnings (non-blocking)
26
- * 2 = errors (blocking failures)
27
- */
28
-
29
- // ---------------------------------------------------------------------------
30
- // Imports
31
- // ---------------------------------------------------------------------------
32
-
33
- import { execSync, spawnSync } from 'node:child_process'
34
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
35
- import { homedir } from 'node:os'
36
- import { join, resolve } from 'node:path'
37
- import { fileURLToPath } from 'node:url'
38
-
39
- const __dirname = fileURLToPath(new URL('.', import.meta.url))
40
- const ROOT = join(__dirname, '..')
41
-
42
- // ---------------------------------------------------------------------------
43
- // State
44
- // ---------------------------------------------------------------------------
45
-
46
- const counts = { pass: 0, warn: 0, error: 0, info: 0 }
47
- const results = []
48
- let exitCode = 0
49
-
50
- // ---------------------------------------------------------------------------
51
- // ANSI color helpers
52
- // ---------------------------------------------------------------------------
53
-
54
- const C = {
55
- reset: '\x1b[0m',
56
- bold: '\x1b[1m',
57
- green: '\x1b[32m',
58
- yellow: '\x1b[33m',
59
- red: '\x1b[31m',
60
- cyan: '\x1b[36m',
61
- dim: '\x1b[2m',
62
- gray: '\x1b[90m',
63
- }
64
-
65
- function colored(text, color) {
66
- return `${color}${text}${C.reset}`
67
- }
68
-
69
- // ---------------------------------------------------------------------------
70
- // CLI arg parsing
71
- // ---------------------------------------------------------------------------
72
-
73
- function parseArgs(argv) {
74
- const args = {
75
- target: null,
76
- platforms: ['opencode'],
77
- checkOnly: false,
78
- skipSync: false,
79
- skipInstall: false,
80
- verbose: false,
81
- help: false,
82
- }
83
-
84
- for (let i = 2; i < argv.length; i++) {
85
- switch (argv[i]) {
86
- case '--target':
87
- args.target = argv[++i]
88
- break
89
- case '--platforms':
90
- args.platforms = argv[++i].split(',').map((s) => s.trim().toLowerCase())
91
- break
92
- case '--check-only':
93
- args.checkOnly = true
94
- break
95
- case '--skip-sync':
96
- args.skipSync = true
97
- break
98
- case '--skip-install':
99
- args.skipInstall = true
100
- break
101
- case '--verbose':
102
- args.verbose = true
103
- break
104
- case '--help':
105
- case '-h':
106
- args.help = true
107
- break
108
- default:
109
- console.warn(`⚠️ Unknown option: ${argv[i]}`)
110
- break
111
- }
112
- }
113
-
114
- // Resolve target
115
- if (!args.target) {
116
- const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), '.config')
117
- args.target = join(xdgConfig, 'opencode')
118
- }
119
- if (!args.target.startsWith('/')) {
120
- args.target = resolve(args.target)
121
- }
122
-
123
- // --check-only implies --skip-sync --skip-install
124
- if (args.checkOnly) {
125
- args.skipSync = true
126
- args.skipInstall = true
127
- }
128
-
129
- return args
130
- }
131
-
132
- // ---------------------------------------------------------------------------
133
- // Output helpers
134
- // ---------------------------------------------------------------------------
135
-
136
- function emit(category, message) {
137
- counts[category]++
138
- results.push({ category, message })
139
- const icon = {
140
- pass: colored('\u2713', C.green),
141
- warn: colored('\u26A0', C.yellow),
142
- error: colored('\u2717', C.red),
143
- info: colored('\u2139', C.cyan),
144
- }[category]
145
- const label = {
146
- pass: colored('PASS', C.green),
147
- warn: colored('WARN', C.yellow),
148
- error: colored('FAIL', C.red),
149
- info: colored('INFO', C.cyan),
150
- }[category]
151
- if (category === 'pass' && !process.argv.includes('--verbose') && !process.argv.includes('-v')) {
152
- return // quiet pass unless verbose
153
- }
154
- console.log(` ${icon} ${label} ${message}`)
155
- }
156
-
157
- function pass(msg) {
158
- emit('pass', msg)
159
- }
160
- function warn(msg) {
161
- emit('warn', msg)
162
- if (exitCode < 1) exitCode = 1
163
- }
164
- function fail(msg) {
165
- emit('error', msg)
166
- exitCode = 2
167
- }
168
- function info(msg) {
169
- emit('info', msg)
170
- }
171
-
172
- function showHelp() {
173
- console.log(`
174
- ${colored('/pantheon-manifest', C.bold)} — Sync, install, and verify Pantheon agents
175
-
176
- ${colored('USAGE', C.cyan)}
177
- node scripts/manifest.mjs default pipeline
178
- node scripts/manifest.mjs --check-only verify only
179
- node scripts/manifest.mjs --target PATH custom target
180
- node scripts/manifest.mjs --platforms opencode,claude multi-platform
181
- node scripts/manifest.mjs --skip-sync skip sync step
182
- node scripts/manifest.mjs --skip-install skip install step
183
- node scripts/manifest.mjs --verbose show all output
184
- node scripts/manifest.mjs --help this help
185
-
186
- ${colored('PIPELINE', C.cyan)}
187
- 1. SYNC — [removed in v1.0] platform sync via sync-platforms.mjs
188
- 2. INSTALL — Deploy agents, config, skills to target directory
189
- 3. VERIFY — Check agent count, MCP servers, config integrity
190
- 4. REPORT — Structured pass/fail summary
191
-
192
- ${colored('DEFAULTS', C.cyan)}
193
- --target ~/.config/opencode
194
- --platforms opencode
195
-
196
- ${colored('EXIT CODES', C.cyan)}
197
- 0 = all checks pass
198
- 1 = warnings (non-blocking)
199
- 2 = errors (blocking failures)
200
- `)
201
- }
202
-
203
- // ---------------------------------------------------------------------------
204
- // Step helpers
205
- // ---------------------------------------------------------------------------
206
-
207
- function section(title) {
208
- console.log(`\n${colored('='.repeat(60), C.dim)}`)
209
- console.log(` ${colored(title, C.bold)}`)
210
- console.log(`${colored('='.repeat(60), C.dim)}`)
211
- }
212
-
213
- function _step(label, fn) {
214
- console.log(`\n${colored('>>>', C.cyan)} ${colored(label, C.bold)}`)
215
- return fn()
216
- }
217
-
218
- // ---------------------------------------------------------------------------
219
- // Step 1: Sync
220
- // ---------------------------------------------------------------------------
221
-
222
- function syncPlatforms(_targetPlatforms) {
223
- // sync-platforms.mjs removed in v1.0 (OpenCode-only)
224
- info('Platform sync step skipped — sync-platforms.mjs was removed in v1.0')
225
- return true
226
- }
227
-
228
- // ---------------------------------------------------------------------------
229
- // Step 2: Install
230
- // ---------------------------------------------------------------------------
231
-
232
- function installManifest(target, targetPlatforms) {
233
- const installScript = join(ROOT, 'scripts', 'install.mjs')
234
- if (!existsSync(installScript)) {
235
- fail(`install.mjs not found at ${installScript}`)
236
- return false
237
- }
238
-
239
- const platformArg = targetPlatforms.join(',')
240
- console.log(
241
- ` ${colored('Installing to', C.cyan)} ${colored(target, C.bold)} ${colored(`(platforms: ${platformArg})`, C.dim)}...`,
242
- )
243
-
244
- try {
245
- const result = spawnSync(
246
- process.execPath,
247
- [installScript, '--target', target, '--platforms', platformArg],
248
- {
249
- cwd: ROOT,
250
- stdio: 'inherit',
251
- timeout: 120_000,
252
- },
253
- )
254
- if (result.status === 0) {
255
- pass(`Install completed`)
256
- return true
257
- } else {
258
- fail(`Install failed (exit ${result.status})`)
259
- // Print stderr if available
260
- if (result.stderr && result.stderr.length > 0) {
261
- const lines = result.stderr.toString().split('\n').filter(Boolean)
262
- for (const line of lines.slice(-5)) {
263
- console.error(` ${colored(line, C.red)}`)
264
- }
265
- }
266
- return false
267
- }
268
- } catch (err) {
269
- fail(`Install error: ${err.message}`)
270
- return false
271
- }
272
- }
273
-
274
- // ---------------------------------------------------------------------------
275
- // Step 3: Verify
276
- // ---------------------------------------------------------------------------
277
-
278
- /**
279
- * Get canonical agent names from pantheon agents/ directory.
280
- */
281
- function getCanonicalAgentNames() {
282
- const agentsDir = join(ROOT, 'agents')
283
- if (!existsSync(agentsDir)) return []
284
- return readdirSync(agentsDir)
285
- .filter((f) => f.endsWith('.agent.md'))
286
- .map((f) => f.replace(/\.agent\.md$/, ''))
287
- .sort()
288
- }
289
-
290
- /**
291
- * Get installed agent file names from target platform agents directory.
292
- */
293
- function getInstalledAgentNames(target, _platform) {
294
- // Global install: agents/ directory at target root
295
- // Project install: .opencode/agents/ at target root
296
- const candidates = [join(target, 'agents'), join(target, '.opencode', 'agents')]
297
- for (const dir of candidates) {
298
- if (existsSync(dir)) {
299
- return readdirSync(dir)
300
- .filter((f) => f.endsWith('.md') && statSync(join(dir, f)).isFile())
301
- .map((f) => f.replace(/\.md$/, ''))
302
- .sort()
303
- }
304
- }
305
- return []
306
- }
307
-
308
- /**
309
- * Count agents with mcp_tools in frontmatter.
310
- */
311
- function countAgentsWithMcpTools(target) {
312
- const candidates = [join(target, 'agents'), join(target, '.opencode', 'agents')]
313
- let count = 0
314
- for (const dir of candidates) {
315
- if (!existsSync(dir)) continue
316
- const files = readdirSync(dir).filter((f) => f.endsWith('.md'))
317
- for (const f of files) {
318
- const content = readFileSync(join(dir, f), 'utf8')
319
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---/)
320
- if (fmMatch?.[1].includes('mcp_tools')) {
321
- count++
322
- }
323
- }
324
- }
325
- return count
326
- }
327
-
328
- /**
329
- * Check MCP server connectivity via `opencode mcp list`.
330
- */
331
- function checkMCPConnectivity() {
332
- try {
333
- const result = execSync('opencode mcp list', {
334
- encoding: 'utf8',
335
- timeout: 15_000,
336
- stdio: ['pipe', 'pipe', 'pipe'],
337
- })
338
- const output = result.trim()
339
-
340
- // Count server lines: lines starting with "●" containing ✓ or ✗
341
- const serverLines = output.split('\n').filter((l) => l.includes('\u25CF'))
342
- const connectedLines = serverLines.filter((l) => l.includes('\u2713'))
343
- const failedLines = serverLines.filter((l) => l.includes('\u2717'))
344
- const totalServers = serverLines.length
345
- const connectedCount = connectedLines.length
346
- const failedCount = failedLines.length
347
-
348
- if (failedCount === 0 && connectedCount > 0) {
349
- pass(`${connectedCount} MCP server(s) connected`)
350
- if (process.argv.includes('--verbose')) {
351
- console.log(`\n${output}\n`)
352
- }
353
- } else if (connectedCount > 0 && failedCount > 0) {
354
- warn(`${connectedCount} connected, ${failedCount} failed MCP server(s)`)
355
- if (process.argv.includes('--verbose')) {
356
- console.log(`\n${output}\n`)
357
- }
358
- } else if (totalServers === 0) {
359
- info('No MCP servers found in `opencode mcp list`')
360
- } else {
361
- warn(`0 of ${totalServers} MCP server(s) connected`)
362
- if (process.argv.includes('--verbose')) {
363
- console.log(`\n${output}\n`)
364
- }
365
- }
366
- return { connected: connectedCount, failed: failedCount, total: totalServers }
367
- } catch (err) {
368
- // opencode not installed or mcp list not available
369
- warn(`Could not check MCP connectivity: ${err.message}`)
370
- return { connected: 0, failed: 0, total: 0 }
371
- }
372
- }
373
-
374
- /**
375
- * Check target opencode.json config integrity.
376
- */
377
- function checkConfigIntegrity(target) {
378
- const configPath = join(target, 'opencode.json')
379
- if (!existsSync(configPath)) {
380
- fail(`opencode.json not found at ${configPath}`)
381
- return false
382
- }
383
-
384
- try {
385
- const config = JSON.parse(readFileSync(configPath, 'utf8'))
386
- const allOk = true
387
-
388
- // Check default agent
389
- if (config.default_agent) {
390
- pass(`default_agent: ${config.default_agent}`)
391
- } else {
392
- warn('No default_agent set in opencode.json')
393
- }
394
-
395
- // Check MCP config
396
- if (config.mcp && typeof config.mcp === 'object') {
397
- const mcpCount = Object.keys(config.mcp).length
398
- const pantheonMcps = Object.keys(config.mcp).filter((k) => k.startsWith('pantheon-'))
399
- pass(`${mcpCount} MCP server(s) configured, ${pantheonMcps.length} pantheon-*`)
400
- } else {
401
- warn('No MCP section in opencode.json — run install')
402
- }
403
-
404
- // Check permission
405
- if (config.permission?.mcp) {
406
- const mcpPermCount = Object.keys(config.permission.mcp).length
407
- pass(`${mcpPermCount} MCP permission(s) configured`)
408
- } else {
409
- warn('No permission.mcp section in opencode.json')
410
- }
411
-
412
- // Check instructions
413
- if (Array.isArray(config.instructions) && config.instructions.length > 0) {
414
- pass(`${config.instructions.length} instruction path(s) configured`)
415
- } else {
416
- warn('No instructions paths in opencode.json')
417
- }
418
-
419
- return allOk
420
- } catch (err) {
421
- fail(`opencode.json parse error: ${err.message}`)
422
- return false
423
- }
424
- }
425
-
426
- /**
427
- * Verify agent count consistency and mcp_tools coverage.
428
- */
429
- function verifyAgentFiles(target) {
430
- const canonical = getCanonicalAgentNames()
431
- const installed = getInstalledAgentNames(target, 'opencode')
432
-
433
- if (canonical.length === 0) {
434
- fail('No canonical agent files found in agents/')
435
- return false
436
- }
437
-
438
- if (installed.length === 0) {
439
- fail(`No installed agent files found in ${target}/agents/`)
440
- return false
441
- }
442
-
443
- // Count match
444
- if (installed.length === canonical.length) {
445
- pass(`${installed.length} of ${canonical.length} agent files installed`)
446
- } else {
447
- warn(`${installed.length} installed vs ${canonical.length} canonical agent files`)
448
- }
449
-
450
- // Check for missing agents
451
- const missing = canonical.filter((a) => !installed.includes(a))
452
- if (missing.length > 0) {
453
- fail(`${missing.length} agent(s) missing from install: ${missing.join(', ')}`)
454
- }
455
-
456
- // Check for phantom agents (in install but not canonical)
457
- const phantom = installed.filter((a) => !canonical.includes(a))
458
- if (phantom.length > 0) {
459
- warn(`${phantom.length} stale/phantom agent(s) in install: ${phantom.join(', ')}`)
460
- }
461
-
462
- // Verify each installed file has valid frontmatter
463
- let invalidCount = 0
464
- const candidateDirs = [join(target, 'agents'), join(target, '.opencode', 'agents')]
465
- for (const dir of candidateDirs) {
466
- if (!existsSync(dir)) continue
467
- for (const name of installed) {
468
- const filePath = join(dir, `${name}.md`)
469
- if (!existsSync(filePath)) continue
470
- const content = readFileSync(filePath, 'utf8')
471
- if (!content.startsWith('---')) {
472
- warn(`${name}.md: missing frontmatter`)
473
- invalidCount++
474
- }
475
- }
476
- }
477
- if (invalidCount === 0 && installed.length > 0) {
478
- pass('All installed agents have valid frontmatter')
479
- }
480
-
481
- // mcp_tools coverage
482
- const mcpCount = countAgentsWithMcpTools(target)
483
- if (mcpCount > 0) {
484
- info(`${mcpCount} of ${installed.length} agent files have mcp_tools configured`)
485
- }
486
-
487
- return missing.length === 0
488
- }
489
-
490
- // ---------------------------------------------------------------------------
491
- // Step 4: Report
492
- // ---------------------------------------------------------------------------
493
-
494
- function printSummary() {
495
- console.log(`\n${colored('='.repeat(60), C.dim)}`)
496
- console.log(` ${colored('MANIFEST SUMMARY', C.bold)}`)
497
- console.log(`${colored('='.repeat(60), C.dim)}`)
498
- console.log(` ${colored('\u2713', C.green)} ${counts.pass} passed`)
499
- console.log(` ${colored('\u26A0', C.yellow)} ${counts.warn} warnings`)
500
- console.log(` ${colored('\u2717', C.red)} ${counts.error} errors`)
501
- console.log(` ${colored('\u2139', C.cyan)} ${counts.info} info`)
502
- console.log('')
503
-
504
- if (exitCode === 0) {
505
- console.log(
506
- ` ${colored('\u2713', C.green)} ${colored('MANIFEST COMPLETE', C.bold)} — all checks passed`,
507
- )
508
- } else if (exitCode === 1) {
509
- console.log(
510
- ` ${colored('\u26A0', C.yellow)} ${colored('MANIFEST COMPLETE WITH WARNINGS', C.bold)} — review above`,
511
- )
512
- } else {
513
- console.log(
514
- ` ${colored('\u2717', C.red)} ${colored('MANIFEST FAILED', C.bold)} — errors must be resolved`,
515
- )
516
- }
517
- console.log('')
518
- }
519
-
520
- // ---------------------------------------------------------------------------
521
- // Main
522
- // ---------------------------------------------------------------------------
523
-
524
- function main() {
525
- const args = parseArgs(process.argv)
526
-
527
- if (args.help) {
528
- showHelp()
529
- process.exit(0)
530
- }
531
-
532
- // Validate target directory exists
533
- if (!existsSync(args.target)) {
534
- console.error(`${colored('ERROR', C.red)}: Target directory does not exist: ${args.target}`)
535
- console.error(` Run: mkdir -p ${args.target}`)
536
- process.exit(2)
537
- }
538
-
539
- // Validate we're in the Pantheon root
540
- if (!existsSync(join(ROOT, 'AGENTS.md')) || !existsSync(join(ROOT, 'agents'))) {
541
- console.error(
542
- `${colored('ERROR', C.red)}: Not in Pantheon repository root — AGENTS.md or agents/ not found`,
543
- )
544
- console.error(` Expected root at: ${ROOT}`)
545
- process.exit(2)
546
- }
547
-
548
- const mode = args.checkOnly
549
- ? colored('CHECK-ONLY MODE', C.yellow)
550
- : colored('FULL PIPELINE', C.green)
551
- console.log(`\n${colored('Pantheon Manifest', C.bold)} ${colored('v3', C.dim)} — ${mode}`)
552
- console.log(` ${colored('Target:', C.dim)} ${args.target}`)
553
- console.log(` ${colored('Platforms:', C.dim)} ${args.platforms.join(', ')}`)
554
-
555
- // ── Step 1: Sync ──
556
- if (!args.skipSync) {
557
- section('STEP 1: SYNC')
558
- syncPlatforms(args.platforms)
559
- } else if (!args.checkOnly) {
560
- info('Sync step skipped (--skip-sync)')
561
- }
562
-
563
- // ── Step 2: Install ──
564
- if (!args.skipInstall) {
565
- section('STEP 2: INSTALL')
566
- installManifest(args.target, args.platforms)
567
- } else if (!args.checkOnly) {
568
- info('Install step skipped (--skip-install)')
569
- }
570
-
571
- // ── Step 3: Verify ──
572
- section('STEP 3: VERIFY')
573
- verifyAgentFiles(args.target)
574
- checkMCPConnectivity()
575
- checkConfigIntegrity(args.target)
576
-
577
- // ── Step 4: Report ──
578
- section('STEP 4: REPORT')
579
- printSummary()
580
-
581
- // ── Final status line ──
582
- if (exitCode === 0) {
583
- console.log(
584
- ` ${colored('\u2728', C.green)} Pantheon manifest is ${colored('UP TO DATE', C.bold + C.green)}`,
585
- )
586
- } else if (exitCode === 1) {
587
- console.log(` ${colored('\u26A0', C.yellow)} Pantheon manifest has warnings (review above)`)
588
- } else {
589
- console.log(
590
- ` ${colored('\u26D4', C.red)} Pantheon manifest has errors — run with --verbose for details`,
591
- )
592
- }
593
-
594
- process.exit(exitCode)
595
- }
596
-
597
- main()