adaptive-director-skill 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * setup.mjs
4
+ * ──────────
5
+ * Interactive setup: discovers environment, shows summary, saves config.
6
+ *
7
+ * Usage:
8
+ * node scripts/setup.mjs
9
+ * node scripts/setup.mjs --yes (non-interactive, accept defaults)
10
+ */
11
+
12
+ import { execFileSync } from 'node:child_process'
13
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs'
14
+ import { join } from 'node:path'
15
+ import { homedir } from 'node:os'
16
+ import { createInterface } from 'node:readline'
17
+
18
+ const CONFIG_DIR = join(homedir(), '.adaptive-director')
19
+ const CONFIG_PATH = join(CONFIG_DIR, 'config.yaml')
20
+
21
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
22
+
23
+ function run(script, args = []) {
24
+ try {
25
+ const out = execFileSync(process.execPath, [script, ...args], {
26
+ encoding: 'utf8', timeout: 15000,
27
+ cwd: process.cwd(),
28
+ })
29
+ return JSON.parse(out.trim())
30
+ } catch (e) {
31
+ return null
32
+ }
33
+ }
34
+
35
+ function ask(rl, question) {
36
+ return new Promise(resolve => rl.question(question, resolve))
37
+ }
38
+
39
+ function icon(ok) { return ok ? '✓' : '✗' }
40
+
41
+ // ─── Main ─────────────────────────────────────────────────────────────────────
42
+
43
+ async function main() {
44
+ const nonInteractive = process.argv.includes('--yes')
45
+ const scriptDir = new URL('.', import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')
46
+ const discoverScript = join(scriptDir, 'discover.mjs')
47
+
48
+ console.log('\n Adaptive Director — Setup\n')
49
+ console.log(' Discovering environment...\n')
50
+
51
+ // ── 1. Run discovery ──────────────────────────────────────────────────────
52
+ const discovery = run(discoverScript)
53
+
54
+ if (!discovery) {
55
+ console.error(' Error: discovery failed. Make sure Node.js 18+ is available.')
56
+ process.exit(1)
57
+ }
58
+
59
+ // ── 2. Print agents ───────────────────────────────────────────────────────
60
+ console.log(' Agents:')
61
+ for (const [id, info] of Object.entries(discovery.agents ?? {})) {
62
+ const mark = info.available ? '✓' : info.installed ? '~' : '✗'
63
+ const note = info.available ? (info.version ?? 'available')
64
+ : info.installed ? 'installed (auth unknown)'
65
+ : 'not installed'
66
+ console.log(` ${mark} ${id.padEnd(14)} ${note}`)
67
+ }
68
+
69
+ // ── 3. Delegate fleet ────────────────────────────────────────────────────
70
+ console.log('')
71
+ const ds = discovery.delegateSkills ?? { installed: false, lanes: {} }
72
+ if (ds.installed) {
73
+ const laneNames = Object.keys(ds.lanes ?? {})
74
+ console.log(` ✓ delegate-skills fleet detected (${laneNames.length} lane(s))`)
75
+ for (const [name, lane] of Object.entries(ds.lanes ?? {})) {
76
+ console.log(` ${name} → ${lane.implementer ?? lane.agent ?? '?'}${lane.model ? ' / ' + lane.model : ''}`)
77
+ }
78
+ } else {
79
+ console.log(' ✗ delegate-skills fleet not detected')
80
+ if (!nonInteractive) {
81
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
82
+ const ans = await ask(rl, '\n Install delegate-skills now? [y/N] ')
83
+ rl.close()
84
+ if (ans.trim().toLowerCase() === 'y') {
85
+ console.log('\n Run: npx skills add amElnagdy/delegate-skills')
86
+ console.log(' Then: node scripts/setup.mjs\n')
87
+ process.exit(0)
88
+ }
89
+ }
90
+ }
91
+
92
+ // ── 4. Write config ───────────────────────────────────────────────────────
93
+ mkdirSync(CONFIG_DIR, { recursive: true })
94
+
95
+ const config = [
96
+ '# Adaptive Orchestrator config',
97
+ '# Generated by setup.mjs',
98
+ '',
99
+ 'defaultBudget: balanced',
100
+ '',
101
+ '# Agent overrides (uncomment and edit to override routing):',
102
+ '# agentOverrides.plan: claude',
103
+ '# agentOverrides.implement: codex',
104
+ '# agentOverrides.review: claude',
105
+ '# agentOverrides.verify: claude',
106
+ '',
107
+ ].join('\n')
108
+
109
+ writeFileSync(CONFIG_PATH, config, 'utf8')
110
+
111
+ console.log('\n Default policies:')
112
+ console.log(' Budget: balanced')
113
+ console.log(' Delegate: disabled (use --delegate at runtime)')
114
+ console.log(' Max: disabled (use --allow-max at runtime)')
115
+ console.log('')
116
+ console.log(` Setup complete. Config: ${CONFIG_PATH}`)
117
+ console.log('')
118
+ }
119
+
120
+ main().catch(err => {
121
+ console.error('Setup error:', err.message)
122
+ process.exit(1)
123
+ })
@@ -0,0 +1,89 @@
1
+ import { execFileSync } from 'node:child_process'
2
+ import { join } from 'node:path'
3
+
4
+ const scriptDir = process.cwd()
5
+
6
+ function runScript(script, args) {
7
+ const out = execFileSync(process.execPath, [join(scriptDir, script), ...args], {
8
+ encoding: 'utf8', timeout: 10000, cwd: scriptDir
9
+ })
10
+ return JSON.parse(out.trim())
11
+ }
12
+
13
+ function routePhase(taskSize, phase, budget = 'balanced', allowMax = false, delegateEnabled = false) {
14
+ const input = JSON.stringify({ taskSize, phase, budget, allowMax, delegateEnabled })
15
+ const out = execFileSync(
16
+ process.execPath,
17
+ [join(scriptDir, 'scripts/route.mjs'), '--input', input],
18
+ { encoding: 'utf8', timeout: 10000, cwd: scriptDir }
19
+ )
20
+ return JSON.parse(out.trim())
21
+ }
22
+
23
+ function runStateCmd(command, args) {
24
+ const out = execFileSync(process.execPath, [join(scriptDir, 'scripts/run-state.mjs'), command, ...args], {
25
+ encoding: 'utf8', timeout: 10000, cwd: scriptDir
26
+ })
27
+ return JSON.parse(out.trim())
28
+ }
29
+
30
+ console.log('\n=== Smoke Tests ===\n')
31
+
32
+ // Test 1: route — medium / review
33
+ const r1 = routePhase('medium', 'review', 'balanced', false, false)
34
+ console.log('route medium/review/balanced:', JSON.stringify(r1))
35
+ console.assert(r1.effort === 'high', 'review effort should be high in balanced mode')
36
+
37
+ // Test 2: route — small / implement
38
+ const r2 = routePhase('small', 'implement', 'conservative', false, false)
39
+ console.log('route small/implement/conservative:', JSON.stringify(r2))
40
+ console.assert(r2.effort === 'medium', 'implement effort should be medium in conservative mode')
41
+
42
+ // Test 3: route — large / plan / quality
43
+ const r3 = routePhase('large', 'plan', 'quality', false, false)
44
+ console.log('route large/plan/quality:', JSON.stringify(r3))
45
+ console.assert(r3.effort === 'high', 'plan effort should be high in quality mode')
46
+
47
+ // Test 4: run-state init + update + read
48
+ const initResult = runStateCmd('init', ['--task', 'Test task', '--size', 'small', '--budget', 'balanced'])
49
+ console.log('\nrun-state init:', JSON.stringify(initResult))
50
+ const runId = initResult.runId
51
+ console.assert(runId.startsWith('run-'), 'runId should start with run-')
52
+
53
+ runStateCmd('update', ['--run-id', runId, '--status', 'running', '--phase', 'implement'])
54
+
55
+ const meta = runStateCmd('read', ['--run-id', runId])
56
+ console.log('run-state read status:', meta.status, '| phase:', meta.currentPhase)
57
+ console.assert(meta.status === 'running', 'status should be running')
58
+ console.assert(meta.currentPhase === 'implement', 'phase should be implement')
59
+
60
+ // Test 5: write-phase + read-phase
61
+ const findings = JSON.stringify([{severity:'critical',title:'Test finding',description:'A test critical issue'}])
62
+ runStateCmd('write-phase', [
63
+ '--run-id', runId, '--phase', 'review',
64
+ '--status', 'completed', '--summary', 'Found 1 critical issue',
65
+ '--findings-json', findings
66
+ ])
67
+ const phaseResult = runStateCmd('read-phase', ['--run-id', runId, '--phase', 'review'])
68
+ console.log('\nwrite/read-phase:', phaseResult.phase, '| findings:', phaseResult.findings?.length)
69
+ console.assert(phaseResult.findings?.length === 1, 'should have 1 finding')
70
+ console.assert(phaseResult.findings[0].severity === 'critical', 'finding should be critical')
71
+
72
+ // Test 6: build-brief
73
+ const { execFileSync: ef2 } = await import('node:child_process')
74
+ const brief = ef2(process.execPath, [join(scriptDir, 'scripts/run-state.mjs'), 'build-brief', '--run-id', runId, '--phase', 'review'], {
75
+ encoding: 'utf8', timeout: 10000, cwd: scriptDir
76
+ })
77
+ console.log('\nbuild-brief review (first 80 chars):', brief.slice(0, 80).replace(/\n/g, ' '))
78
+ console.assert(brief.includes('Independent Reviewer'), 'brief should contain reviewer role')
79
+
80
+ // Test 7: resume (should find our running run)
81
+ const { execFileSync: ef3 } = await import('node:child_process')
82
+ const resumeOut = ef3(process.execPath, [join(scriptDir, 'scripts/resume.mjs')], {
83
+ encoding: 'utf8', timeout: 10000, cwd: scriptDir
84
+ })
85
+ const resumeResult = JSON.parse(resumeOut.trim())
86
+ console.log('\nresume found:', resumeResult?.runId, '| status:', resumeResult?.status)
87
+ console.assert(resumeResult?.runId === runId || resumeResult !== null, 'should find a resumable run')
88
+
89
+ console.log('\n=== All tests passed ✓ ===\n')