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.
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/SKILL.md +395 -0
- package/data/registry.json +42 -0
- package/package.json +40 -0
- package/references/capability-registry.md +92 -0
- package/references/delegate-integration.md +115 -0
- package/references/handoff-schema.md +137 -0
- package/references/routing-rules.md +99 -0
- package/scripts/discover.mjs +132 -0
- package/scripts/resume.mjs +70 -0
- package/scripts/route.mjs +244 -0
- package/scripts/run-state.mjs +343 -0
- package/scripts/setup.mjs +123 -0
- package/scripts/smoke-test.mjs +89 -0
|
@@ -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')
|