@ai-devkit/agent-manager 0.30.0 → 0.32.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/dist/__tests__/readiness/AgentReadiness.test.js +301 -0
- package/dist/__tests__/readiness/AgentReadiness.test.js.map +1 -0
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +5 -1
- package/dist/database/connection.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/readiness/AgentReadiness.d.ts +67 -0
- package/dist/readiness/AgentReadiness.d.ts.map +1 -0
- package/dist/readiness/AgentReadiness.js +614 -0
- package/dist/readiness/AgentReadiness.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/readiness/AgentReadiness.test.ts +194 -0
- package/src/database/connection.ts +3 -1
- package/src/index.ts +18 -0
- package/src/readiness/AgentReadiness.ts +655 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/readiness/AgentReadiness.ts"],"sourcesContent":["import { constants } from 'node:fs';\nimport { access as fsAccess, readFile as fsReadFile } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { delimiter, join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { getCodexCapacityReport } from '../capacity/index.js';\nimport { AGENTS, type StartableAgentType } from '../utils/agents.js';\n\nconst execFileAsync = promisify(execFile);\nconst READINESS_AGENT_TYPES = ['claude', 'codex', 'copilot', 'grok_cli', 'opencode', 'pi'] as const;\nconst ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\\\[[0-?]*[ -/]*[@-~]`, 'g');\n\nexport type ReadinessStatus = 'pass' | 'warn' | 'fail';\nexport type ReadinessAuthState = 'authenticated' | 'unauthenticated' | 'unknown';\nexport type ReadinessAgentType = Exclude<StartableAgentType, 'gemini_cli'>;\ntype ReadinessInfoStatus = ReadinessStatus | 'info';\n\ntype CommandResult = { stdout: string; stderr: string };\ntype ReadFile = (target: string) => Promise<string>;\ntype Access = (target: string, mode?: number) => Promise<void>;\ntype RunCommand = (command: string, args: string[]) => Promise<CommandResult>;\n\nexport interface ReadinessCheck<S extends ReadinessInfoStatus = ReadinessStatus> {\n status: S;\n errors: string[];\n}\n\nexport interface ExecutableReadinessCheck extends ReadinessCheck {\n command: string;\n path: string | null;\n}\n\nexport interface DirectoryReadinessCheck extends ReadinessCheck {\n path: string;\n present: boolean;\n readable: boolean;\n}\n\nexport interface BuiltInSkillsReadinessCheck extends ReadinessCheck<'info'> {\n path: string | null;\n required: number;\n present: number;\n missing: string[];\n}\n\nexport interface AuthReadinessCheck extends ReadinessCheck {\n state: ReadinessAuthState;\n source: string;\n provider: string | null;\n availableProviders: string[];\n}\n\nexport interface IntegrationReadinessCheck extends ReadinessCheck {\n label: string;\n installed: boolean;\n details?: Record<string, unknown>;\n}\n\nexport interface AgentReadinessReport {\n type: ReadinessAgentType;\n executable: ExecutableReadinessCheck;\n globalConfig: DirectoryReadinessCheck;\n builtInSkills: BuiltInSkillsReadinessCheck;\n auth?: AuthReadinessCheck;\n integration?: IntegrationReadinessCheck;\n status: ReadinessStatus;\n}\n\nexport interface AgentReadinessOptions {\n homeDir?: string;\n path?: string;\n assetRoot?: string;\n builtInSkillNames?: readonly string[];\n skillRoots?: Partial<Record<ReadinessAgentType, string>>;\n readFile?: ReadFile;\n access?: Access;\n runCommand?: RunCommand;\n codexAuth?: () => Promise<boolean | null>;\n}\n\ntype Runtime = Required<Omit<AgentReadinessOptions, 'assetRoot' | 'builtInSkillNames' | 'skillRoots'>> & {\n assetRoot: string | null;\n builtInSkillNames: readonly string[];\n skillRoots: Partial<Record<ReadinessAgentType, string>>;\n};\n\nconst AGENT_HOME_DIRS: Record<ReadinessAgentType, string> = {\n claude: '.claude',\n codex: '.codex',\n copilot: '.copilot',\n grok_cli: '.grok',\n opencode: '.config/opencode',\n pi: '.pi',\n};\n\nfunction runtime(options: AgentReadinessOptions): Runtime {\n return {\n homeDir: options.homeDir ?? process.env.HOME ?? '',\n path: options.path ?? process.env.PATH ?? '',\n assetRoot: options.assetRoot ?? null,\n builtInSkillNames: options.builtInSkillNames ?? [],\n skillRoots: options.skillRoots ?? {},\n readFile: options.readFile ?? (target => fsReadFile(target, 'utf8')),\n access: options.access ?? ((target, mode = constants.R_OK) => fsAccess(target, mode)),\n runCommand: options.runCommand ?? defaultRunCommand,\n codexAuth: options.codexAuth ?? (async () => (await getCodexCapacityReport()).authenticated),\n };\n}\n\nasync function defaultRunCommand(command: string, args: string[]): Promise<CommandResult> {\n const result = await execFileAsync(command, args, {\n encoding: 'utf8',\n timeout: 5000,\n maxBuffer: 1024 * 1024,\n });\n return { stdout: result.stdout, stderr: result.stderr };\n}\n\nfunction statusRank(status: ReadinessStatus): number {\n return status === 'fail' ? 2 : status === 'warn' ? 1 : 0;\n}\n\nexport function worstReadinessStatus(statuses: ReadinessStatus[]): ReadinessStatus {\n return statuses.reduce<ReadinessStatus>((worst, current) =>\n statusRank(current) > statusRank(worst) ? current : worst, 'pass');\n}\n\nfunction displayHome(target: string, homeDir: string): string {\n return target === homeDir ? '~' : target.startsWith(`${homeDir}/`) ? `~${target.slice(homeDir.length)}` : target;\n}\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown> : null;\n}\n\nfunction nonEmpty(value: unknown): boolean {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nasync function accessible(target: string, rt: Runtime, mode = constants.R_OK): Promise<boolean> {\n try {\n await rt.access(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveExecutable(command: string, rt: Runtime): Promise<string | null> {\n const candidates = rt.path.split(delimiter).filter(Boolean).map(directory => join(directory, command));\n const checks = await Promise.all(candidates.map(async target => ({\n target,\n available: await accessible(target, rt, constants.X_OK),\n })));\n return checks.find(check => check.available)?.target ?? null;\n}\n\nasync function executableCheck(agent: ReadinessAgentType, rt: Runtime): Promise<ExecutableReadinessCheck> {\n const command = AGENTS[agent].command;\n const resolvedPath = await resolveExecutable(command, rt);\n return {\n command,\n path: resolvedPath,\n status: resolvedPath ? 'pass' : 'fail',\n errors: resolvedPath ? [] : [`${command} was not found on PATH`],\n };\n}\n\nasync function directoryCheck(agent: ReadinessAgentType, rt: Runtime): Promise<DirectoryReadinessCheck> {\n const target = join(rt.homeDir, AGENT_HOME_DIRS[agent]);\n const readable = await accessible(target, rt);\n return {\n path: displayHome(target, rt.homeDir),\n present: readable,\n readable,\n status: readable ? 'pass' : 'fail',\n errors: readable ? [] : ['global configuration directory is unavailable'],\n };\n}\n\nasync function builtInSkillsCheck(agent: ReadinessAgentType, rt: Runtime): Promise<BuiltInSkillsReadinessCheck> {\n const relativeRoot = rt.skillRoots[agent];\n if (!relativeRoot) {\n return {\n path: null,\n required: rt.builtInSkillNames.length,\n present: 0,\n missing: [...rt.builtInSkillNames],\n status: 'info',\n errors: [],\n };\n }\n const root = join(rt.homeDir, relativeRoot);\n const checks = await Promise.all(rt.builtInSkillNames.map(async name => ({\n name,\n present: await accessible(join(root, name, 'SKILL.md'), rt),\n })));\n const present = checks.filter(check => check.present).map(check => check.name);\n const missing = rt.builtInSkillNames.filter(name => !present.includes(name));\n return {\n path: displayHome(root, rt.homeDir),\n required: rt.builtInSkillNames.length,\n present: present.length,\n missing: [...missing],\n status: 'info',\n errors: [],\n };\n}\n\nasync function scriptCheck(installed: string, bundled: string, rt: Runtime): Promise<ReadinessCheck & {\n path: string;\n present: boolean;\n readable: boolean;\n matchesBundledAsset: boolean;\n}> {\n let installedText: string;\n try {\n installedText = await rt.readFile(installed);\n } catch {\n return {\n path: displayHome(installed, rt.homeDir),\n present: false,\n readable: false,\n matchesBundledAsset: false,\n status: 'fail',\n errors: ['hook script is unavailable'],\n };\n }\n try {\n const bundledText = await rt.readFile(bundled);\n const matches = installedText === bundledText;\n return {\n path: displayHome(installed, rt.homeDir),\n present: true,\n readable: true,\n matchesBundledAsset: matches,\n status: matches ? 'pass' : 'fail',\n errors: matches ? [] : ['hook script differs from the bundled AI DevKit asset'],\n };\n } catch {\n return {\n path: displayHome(installed, rt.homeDir),\n present: true,\n readable: true,\n matchesBundledAsset: false,\n status: 'fail',\n errors: ['bundled hook asset is unavailable'],\n };\n }\n}\n\nfunction containsHook(root: unknown, event: string, command: string): boolean {\n const hooks = record(record(root)?.hooks);\n const entries = hooks?.[event];\n if (!Array.isArray(entries)) return false;\n return entries.some(entry => {\n const commands = record(entry)?.hooks;\n return Array.isArray(commands) && commands.some(hook => {\n const item = record(hook);\n return item?.type === 'command' && item.command === command;\n });\n });\n}\n\nasync function registrationCheck(\n target: string,\n event: string,\n command: string,\n rt: Runtime,\n): Promise<ReadinessCheck & { path: string; event: string; command: string; present: boolean; valid: boolean }> {\n try {\n const parsed = JSON.parse(await rt.readFile(target));\n const valid = containsHook(parsed, event, command);\n return {\n path: displayHome(target, rt.homeDir),\n event,\n command,\n present: true,\n valid,\n status: valid ? 'pass' : 'fail',\n errors: valid ? [] : ['required hook registration is missing'],\n };\n } catch {\n return {\n path: displayHome(target, rt.homeDir),\n event,\n command,\n present: false,\n valid: false,\n status: 'fail',\n errors: ['hook configuration is missing or invalid'],\n };\n }\n}\n\nasync function mappingCheck(target: string, rt: Runtime): Promise<ReadinessCheck & {\n path: string;\n present: boolean;\n valid: boolean;\n invalidEntries: number;\n staleEntries: number;\n}> {\n let text: string;\n try {\n text = await rt.readFile(target);\n } catch {\n return {\n path: displayHome(target, rt.homeDir),\n present: false,\n valid: false,\n invalidEntries: 0,\n staleEntries: 0,\n status: 'warn',\n errors: ['session mapping has not been created'],\n };\n }\n let parsed: Record<string, unknown> | null = null;\n try {\n parsed = record(JSON.parse(text));\n } catch {\n // Fixed safe error below.\n }\n if (!parsed) {\n return {\n path: displayHome(target, rt.homeDir),\n present: true,\n valid: false,\n invalidEntries: 0,\n staleEntries: 0,\n status: 'fail',\n errors: ['session mapping is invalid'],\n };\n }\n const entries = await Promise.all(Object.entries(parsed).map(async ([pid, sessionPath]) => {\n if (!/^\\d+$/.test(pid) || typeof sessionPath !== 'string' || !sessionPath) {\n return { valid: false, stale: false };\n }\n return { valid: true, stale: !await accessible(sessionPath, rt) };\n }));\n const invalidEntries = entries.filter(entry => !entry.valid).length;\n const staleEntries = entries.filter(entry => entry.stale).length;\n const valid = invalidEntries === 0;\n return {\n path: displayHome(target, rt.homeDir),\n present: true,\n valid,\n invalidEntries,\n staleEntries,\n status: !valid ? 'fail' : staleEntries ? 'warn' : 'pass',\n errors: !valid ? ['session mapping contains invalid entries'] : staleEntries ? ['session mapping contains stale entries'] : [],\n };\n}\n\nasync function codexAuthCheck(rt: Runtime): Promise<AuthReadinessCheck> {\n try {\n const value = await rt.codexAuth();\n return {\n state: value === true ? 'authenticated' : value === false ? 'unauthenticated' : 'unknown',\n source: displayHome(join(rt.homeDir, '.codex', 'auth.json'), rt.homeDir),\n provider: null,\n availableProviders: [],\n status: value === true ? 'pass' : value === false ? 'fail' : 'warn',\n errors: value === true ? [] : [value === false ? 'Codex is not authenticated' : 'Codex authentication is unknown'],\n };\n } catch {\n return {\n state: 'unknown',\n source: '~/.codex/auth.json',\n provider: null,\n availableProviders: [],\n status: 'warn',\n errors: ['Codex authentication probe failed'],\n };\n }\n}\n\nasync function claudeAuthCheck(rt: Runtime): Promise<AuthReadinessCheck> {\n try {\n const result = await rt.runCommand('claude', ['auth', 'status', '--json']);\n const parsed = record(JSON.parse(result.stdout));\n const authenticated = parsed?.loggedIn === true || parsed?.authenticated === true;\n const unauthenticated = parsed?.loggedIn === false || parsed?.authenticated === false;\n return {\n state: authenticated ? 'authenticated' : unauthenticated ? 'unauthenticated' : 'unknown',\n source: 'claude auth status --json',\n provider: null,\n availableProviders: [],\n status: authenticated ? 'pass' : unauthenticated ? 'fail' : 'warn',\n errors: authenticated ? [] : [unauthenticated ? 'Claude is not authenticated' : 'Claude authentication is unknown'],\n };\n } catch {\n return {\n state: 'unknown',\n source: 'claude auth status --json',\n provider: null,\n availableProviders: [],\n status: 'warn',\n errors: ['Claude authentication probe failed'],\n };\n }\n}\n\nfunction piProviderNames(parsed: Record<string, unknown>): string[] {\n const names = new Set<string>();\n if (nonEmpty(parsed.provider)) names.add((parsed.provider as string).trim());\n const providers = record(parsed.providers);\n if (providers) {\n for (const name of Object.keys(providers)) {\n if (name.trim()) names.add(name);\n }\n }\n for (const [name, value] of Object.entries(parsed)) {\n if (name === 'provider' || name === 'providers') continue;\n if (record(value) && name.trim()) names.add(name);\n }\n return [...names].sort();\n}\n\nasync function piAuthCheck(rt: Runtime): Promise<AuthReadinessCheck> {\n const sourcePath = join(rt.homeDir, '.pi', 'agent', 'auth.json');\n try {\n const parsed = record(JSON.parse(await rt.readFile(sourcePath)));\n if (!parsed) throw new Error('invalid');\n const availableProviders = piProviderNames(parsed);\n const provider = nonEmpty(parsed.provider) ? (parsed.provider as string).trim() : null;\n const authenticated = availableProviders.length > 0;\n return {\n state: authenticated ? 'authenticated' : 'unauthenticated',\n source: displayHome(sourcePath, rt.homeDir),\n provider,\n availableProviders,\n status: authenticated ? 'pass' : 'fail',\n errors: authenticated ? [] : ['Pi credential file has no configured model provider'],\n };\n } catch {\n return {\n state: 'unauthenticated',\n source: displayHome(sourcePath, rt.homeDir),\n provider: null,\n availableProviders: [],\n status: 'fail',\n errors: ['Pi credential file is missing or invalid'],\n };\n }\n}\n\nasync function opencodeAuthCheck(rt: Runtime): Promise<AuthReadinessCheck> {\n try {\n const result = await rt.runCommand('opencode', ['auth', 'list']);\n const availableProviders = opencodeAuthProviderNames(result.stdout);\n const authenticated = availableProviders.length > 0;\n return {\n state: authenticated ? 'authenticated' : 'unauthenticated',\n source: 'opencode auth list',\n provider: null,\n availableProviders,\n status: authenticated ? 'pass' : 'fail',\n errors: authenticated ? [] : ['OpenCode has no configured credentials'],\n };\n } catch {\n return {\n state: 'unknown',\n source: 'opencode auth list',\n provider: null,\n availableProviders: [],\n status: 'warn',\n errors: ['OpenCode authentication probe failed'],\n };\n }\n}\n\nasync function copilotAuthCheck(rt: Runtime): Promise<AuthReadinessCheck> {\n try {\n await rt.runCommand('gh', ['auth', 'status', '--hostname', 'github.com']);\n return {\n state: 'authenticated',\n source: 'gh auth status --hostname github.com',\n provider: 'github',\n availableProviders: ['GitHub'],\n status: 'pass',\n errors: [],\n };\n } catch {\n return {\n state: 'unknown',\n source: 'gh auth status --hostname github.com',\n provider: null,\n availableProviders: ['GitHub'],\n status: 'warn',\n errors: ['Copilot authentication could not be verified through GitHub CLI'],\n };\n }\n}\n\nfunction opencodeAuthProviderNames(output: string): string[] {\n const names = new Set<string>();\n for (const line of stripAnsi(output).split(/\\r?\\n/)) {\n const match = /^\\s*[●*+-]\\s+(.+?)\\s*$/.exec(line);\n if (!match) continue;\n const name = match[1].replace(/\\s+(?:api|oauth|[\\w-]*token|[A-Z][A-Z0-9_]+)$/i, '').trim();\n if (name && !/^\\d+\\s+(?:credentials?|environment variables?)$/i.test(name)) names.add(name);\n }\n return [...names].sort();\n}\n\nfunction stripAnsi(value: string): string {\n return value.replace(ANSI_ESCAPE_PATTERN, '');\n}\n\nasync function codexIntegrationCheck(rt: Runtime): Promise<IntegrationReadinessCheck> {\n const script = await scriptCheck(\n join(rt.homeDir, '.codex', 'hooks', 'codex-session-mapping.cjs'),\n join(rt.assetRoot ?? '', 'codex', 'codex-session-mapping.cjs'),\n rt,\n );\n const registration = await registrationCheck(\n join(rt.homeDir, '.codex', 'hooks.json'),\n 'SessionStart',\n 'node ~/.codex/hooks/codex-session-mapping.cjs',\n rt,\n );\n const mappingFile = await mappingCheck(join(rt.homeDir, '.codex', 'ai-devkit', 'sessions.json'), rt);\n const installed = script.status === 'pass' && registration.status === 'pass';\n return {\n label: 'ai-devkit hook',\n installed,\n status: worstReadinessStatus([script.status, registration.status, mappingFile.status]),\n errors: [...script.errors, ...registration.errors, ...mappingFile.errors],\n details: { sessionMappingScript: script, registration, mappingFile },\n };\n}\n\nasync function claudeIntegrationCheck(rt: Runtime): Promise<IntegrationReadinessCheck> {\n const script = await scriptCheck(\n join(rt.homeDir, '.claude', 'hooks', 'claude-prompt-hook.js'),\n join(rt.assetRoot ?? '', 'claude', 'claude-prompt-hook.js'),\n rt,\n );\n const registration = await registrationCheck(\n join(rt.homeDir, '.claude', 'settings.json'),\n 'PreToolUse',\n 'node ~/.claude/hooks/claude-prompt-hook.js',\n rt,\n );\n const installed = script.status === 'pass' && registration.status === 'pass';\n return {\n label: 'ai-devkit hook',\n installed,\n status: worstReadinessStatus([script.status, registration.status]),\n errors: [...script.errors, ...registration.errors],\n details: { promptScript: script, registration },\n };\n}\n\nasync function piIntegrationCheck(rt: Runtime): Promise<IntegrationReadinessCheck> {\n let installed = false;\n try {\n const result = await rt.runCommand('pi', ['list']);\n installed = isPiSessionTrackerListed(result.stdout);\n } catch {\n // Safe fixed result below.\n }\n const mapping = await mappingCheck(join(rt.homeDir, '.pi', 'agent', 'sessions.json'), rt);\n const mappingStatus: ReadinessStatus = mapping.valid || !mapping.present ? 'pass' : 'fail';\n const status = worstReadinessStatus([installed ? 'pass' : 'fail', mappingStatus]);\n return {\n label: 'ai-devkit plugin',\n installed,\n status,\n errors: [\n ...(!installed ? ['Pi session tracker is not registered'] : []),\n ...(mappingStatus === 'fail' ? mapping.errors : []),\n ],\n details: {\n package: '@ai-devkit/pi-session-tracker',\n registryPath: mapping.path,\n registryValid: mapping.valid,\n invalidEntries: mapping.invalidEntries,\n staleEntries: mapping.staleEntries,\n },\n };\n}\n\nfunction isPiSessionTrackerListed(output: string): boolean {\n const normalized = output.toLowerCase();\n return normalized.includes('@ai-devkit/pi-session-tracker') || /\\bsession[\\s-]+tracker\\b/.test(normalized);\n}\n\ntype AuthCheck = (rt: Runtime) => Promise<AuthReadinessCheck>;\ntype IntegrationCheck = (rt: Runtime) => Promise<IntegrationReadinessCheck>;\n\nconst AUTH_CHECKS: Partial<Record<ReadinessAgentType, AuthCheck>> = {\n claude: claudeAuthCheck,\n codex: codexAuthCheck,\n copilot: copilotAuthCheck,\n opencode: opencodeAuthCheck,\n pi: piAuthCheck,\n};\n\nconst INTEGRATION_CHECKS: Partial<Record<ReadinessAgentType, IntegrationCheck>> = {\n claude: claudeIntegrationCheck,\n codex: codexIntegrationCheck,\n pi: piIntegrationCheck,\n};\n\nasync function authCheck(agent: ReadinessAgentType, rt: Runtime): Promise<AuthReadinessCheck | undefined> {\n return AUTH_CHECKS[agent]?.(rt);\n}\n\nasync function integrationCheck(agent: ReadinessAgentType, rt: Runtime): Promise<IntegrationReadinessCheck | undefined> {\n return INTEGRATION_CHECKS[agent]?.(rt);\n}\n\nexport async function getAgentReadinessReport(\n agent: ReadinessAgentType,\n options: AgentReadinessOptions = {},\n): Promise<AgentReadinessReport> {\n return agentReadiness(agent, runtime(options));\n}\n\nasync function agentReadiness(agent: ReadinessAgentType, rt: Runtime): Promise<AgentReadinessReport> {\n const [executable, globalConfig, builtInSkills, auth, integration] = await Promise.all([\n executableCheck(agent, rt),\n directoryCheck(agent, rt),\n builtInSkillsCheck(agent, rt),\n authCheck(agent, rt),\n integrationCheck(agent, rt),\n ]);\n return {\n type: agent,\n executable,\n globalConfig,\n builtInSkills,\n auth,\n integration,\n status: worstReadinessStatus([\n executable.status,\n globalConfig.status,\n ...(auth ? [auth.status] : []),\n ...(integration ? [integration.status] : []),\n ]),\n };\n}\n\nexport async function getAgentReadinessReports(\n options: AgentReadinessOptions = {},\n): Promise<Record<ReadinessAgentType, AgentReadinessReport>> {\n const rt = runtime(options);\n const entries = await Promise.all(READINESS_AGENT_TYPES.map(async agent => [\n agent,\n await agentReadiness(agent, rt),\n ] as const));\n return Object.fromEntries(entries) as Record<ReadinessAgentType, AgentReadinessReport>;\n}\n"],"names":["constants","access","fsAccess","readFile","fsReadFile","execFile","delimiter","join","promisify","getCodexCapacityReport","AGENTS","execFileAsync","READINESS_AGENT_TYPES","ANSI_ESCAPE_PATTERN","RegExp","String","fromCharCode","AGENT_HOME_DIRS","claude","codex","copilot","grok_cli","opencode","pi","runtime","options","homeDir","process","env","HOME","path","PATH","assetRoot","builtInSkillNames","skillRoots","target","mode","R_OK","runCommand","defaultRunCommand","codexAuth","authenticated","command","args","result","encoding","timeout","maxBuffer","stdout","stderr","statusRank","status","worstReadinessStatus","statuses","reduce","worst","current","displayHome","startsWith","slice","length","record","value","Array","isArray","nonEmpty","trim","accessible","rt","resolveExecutable","candidates","split","filter","Boolean","map","directory","checks","Promise","all","available","X_OK","find","check","executableCheck","agent","resolvedPath","errors","directoryCheck","readable","present","builtInSkillsCheck","relativeRoot","required","missing","root","name","includes","scriptCheck","installed","bundled","installedText","matchesBundledAsset","bundledText","matches","containsHook","event","hooks","entries","some","entry","commands","hook","item","type","registrationCheck","parsed","JSON","parse","valid","mappingCheck","text","invalidEntries","staleEntries","Object","pid","sessionPath","test","stale","codexAuthCheck","state","source","provider","availableProviders","claudeAuthCheck","loggedIn","unauthenticated","piProviderNames","names","Set","add","providers","keys","sort","piAuthCheck","sourcePath","Error","opencodeAuthCheck","opencodeAuthProviderNames","copilotAuthCheck","output","line","stripAnsi","match","exec","replace","codexIntegrationCheck","script","registration","mappingFile","label","details","sessionMappingScript","claudeIntegrationCheck","promptScript","piIntegrationCheck","isPiSessionTrackerListed","mapping","mappingStatus","package","registryPath","registryValid","normalized","toLowerCase","AUTH_CHECKS","INTEGRATION_CHECKS","authCheck","integrationCheck","getAgentReadinessReport","agentReadiness","executable","globalConfig","builtInSkills","auth","integration","getAgentReadinessReports","fromEntries"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
AGENTS,
|
|
5
|
+
getAgentReadinessReport,
|
|
6
|
+
getAgentReadinessReports,
|
|
7
|
+
type AgentReadinessOptions,
|
|
8
|
+
type ReadinessAgentType,
|
|
9
|
+
} from '../../index.js';
|
|
10
|
+
|
|
11
|
+
type Files = Record<string, string>;
|
|
12
|
+
|
|
13
|
+
const builtInSkillNames = ['agent-management', 'memory', 'verify'];
|
|
14
|
+
const skillRoots: Record<ReadinessAgentType, string> = {
|
|
15
|
+
claude: '.claude/skills',
|
|
16
|
+
codex: '.codex/skills',
|
|
17
|
+
copilot: '.copilot/skills',
|
|
18
|
+
grok_cli: '.grok/skills',
|
|
19
|
+
opencode: '.config/opencode/skill',
|
|
20
|
+
pi: '.pi/agent/skills',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function fixture(overrides: Partial<AgentReadinessOptions> = {}) {
|
|
24
|
+
const homeDir = '/home/test';
|
|
25
|
+
const assetRoot = '/assets';
|
|
26
|
+
const files: Files = {
|
|
27
|
+
[path.join(homeDir, '.codex', 'hooks', 'codex-session-mapping.cjs')]: 'codex-hook',
|
|
28
|
+
[path.join(assetRoot, 'codex', 'codex-session-mapping.cjs')]: 'codex-hook',
|
|
29
|
+
[path.join(homeDir, '.codex', 'hooks.json')]: JSON.stringify({ hooks: { SessionStart: [{ hooks: [
|
|
30
|
+
{ type: 'command', command: 'node ~/.codex/hooks/codex-session-mapping.cjs' },
|
|
31
|
+
] }] } }),
|
|
32
|
+
[path.join(homeDir, '.codex', 'ai-devkit', 'sessions.json')]: JSON.stringify({ '123': '/sessions/codex.jsonl' }),
|
|
33
|
+
'/sessions/codex.jsonl': '',
|
|
34
|
+
[path.join(homeDir, '.claude', 'hooks', 'claude-prompt-hook.js')]: 'claude-hook',
|
|
35
|
+
[path.join(assetRoot, 'claude', 'claude-prompt-hook.js')]: 'claude-hook',
|
|
36
|
+
[path.join(homeDir, '.claude', 'settings.json')]: JSON.stringify({ hooks: { PreToolUse: [{ hooks: [
|
|
37
|
+
{ type: 'command', command: 'node ~/.claude/hooks/claude-prompt-hook.js' },
|
|
38
|
+
] }] } }),
|
|
39
|
+
[path.join(homeDir, '.pi', 'agent', 'sessions.json')]: JSON.stringify({ '456': '/sessions/missing-pi.jsonl' }),
|
|
40
|
+
[path.join(homeDir, '.pi', 'agent', 'auth.json')]: JSON.stringify({
|
|
41
|
+
anthropic: { access: 'access-secret', refresh: 'refresh-secret' },
|
|
42
|
+
litellm: { key: 'litellm-secret', env: { LITELLM_BASE_URL: 'https://litellm.example.test' } },
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
for (const directory of ['.claude', '.codex', '.copilot', '.grok', '.config/opencode', '.pi']) {
|
|
46
|
+
files[path.join(homeDir, directory)] = '<dir>';
|
|
47
|
+
}
|
|
48
|
+
for (const root of Object.values(skillRoots)) {
|
|
49
|
+
for (const skill of builtInSkillNames) {
|
|
50
|
+
files[path.join(homeDir, root, skill, 'SKILL.md')] = '# skill';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const executablePaths = Object.values(AGENTS).map(agent => path.join('/bin', agent.command));
|
|
54
|
+
const options: AgentReadinessOptions = {
|
|
55
|
+
homeDir,
|
|
56
|
+
path: '/bin',
|
|
57
|
+
assetRoot,
|
|
58
|
+
builtInSkillNames,
|
|
59
|
+
skillRoots,
|
|
60
|
+
readFile: async target => {
|
|
61
|
+
if (!(target in files)) throw new Error('missing');
|
|
62
|
+
return files[target];
|
|
63
|
+
},
|
|
64
|
+
access: async target => {
|
|
65
|
+
if (executablePaths.includes(target) || target in files) return;
|
|
66
|
+
throw new Error('missing');
|
|
67
|
+
},
|
|
68
|
+
runCommand: vi.fn(async command => {
|
|
69
|
+
if (command === 'pi') return { stdout: 'npm:@ai-devkit/pi-session-tracker\n', stderr: '' };
|
|
70
|
+
if (command === 'claude') return { stdout: JSON.stringify({ loggedIn: true }), stderr: '' };
|
|
71
|
+
if (command === 'gh') return { stdout: 'github.com\n Logged in to github.com account test-user\n', stderr: '' };
|
|
72
|
+
if (command === 'opencode') return { stdout: '● litellm api\n● OpenAI oauth\n', stderr: '' };
|
|
73
|
+
throw new Error(`unexpected command ${command}`);
|
|
74
|
+
}),
|
|
75
|
+
codexAuth: async () => true,
|
|
76
|
+
...overrides,
|
|
77
|
+
};
|
|
78
|
+
return { options, files };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('agent readiness', () => {
|
|
82
|
+
it('reports every readiness-supported adapter with generic checks and only real integrations', async () => {
|
|
83
|
+
const { options } = fixture();
|
|
84
|
+
|
|
85
|
+
const reports = await getAgentReadinessReports(options);
|
|
86
|
+
|
|
87
|
+
expect(Object.keys(reports)).toEqual(['claude', 'codex', 'copilot', 'grok_cli', 'opencode', 'pi']);
|
|
88
|
+
expect(reports).not.toHaveProperty('gemini_cli');
|
|
89
|
+
for (const [agent, report] of Object.entries(reports) as Array<[ReadinessAgentType, typeof reports[ReadinessAgentType]]>) {
|
|
90
|
+
expect(report.type).toBe(agent);
|
|
91
|
+
expect(report.executable.status).toBe('pass');
|
|
92
|
+
expect(report.globalConfig.status).toBe('pass');
|
|
93
|
+
expect(report.builtInSkills).toMatchObject({ status: 'info', present: 3, required: 3 });
|
|
94
|
+
}
|
|
95
|
+
expect(reports.codex.integration).toMatchObject({ label: 'ai-devkit hook', installed: true, status: 'pass' });
|
|
96
|
+
expect(reports.claude.integration).toMatchObject({ label: 'ai-devkit hook', installed: true, status: 'pass' });
|
|
97
|
+
expect(reports.pi.integration).toMatchObject({ label: 'ai-devkit plugin', installed: true, status: 'pass' });
|
|
98
|
+
expect(reports.copilot.integration).toBeUndefined();
|
|
99
|
+
expect(reports.grok_cli.integration).toBeUndefined();
|
|
100
|
+
expect(reports.opencode.integration).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('parses Pi provider names without returning credential values', async () => {
|
|
104
|
+
const { options } = fixture();
|
|
105
|
+
|
|
106
|
+
const report = await getAgentReadinessReport('pi', options);
|
|
107
|
+
const serialized = JSON.stringify(report);
|
|
108
|
+
|
|
109
|
+
expect(report.auth).toMatchObject({
|
|
110
|
+
state: 'authenticated',
|
|
111
|
+
provider: null,
|
|
112
|
+
availableProviders: ['anthropic', 'litellm'],
|
|
113
|
+
status: 'pass',
|
|
114
|
+
});
|
|
115
|
+
expect(serialized).not.toContain('access-secret');
|
|
116
|
+
expect(serialized).not.toContain('refresh-secret');
|
|
117
|
+
expect(serialized).not.toContain('litellm-secret');
|
|
118
|
+
expect(serialized).not.toContain('litellm.example.test');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('accepts Pi session tracker display names from pi list output', async () => {
|
|
122
|
+
const { options } = fixture({
|
|
123
|
+
runCommand: vi.fn(async command => {
|
|
124
|
+
if (command === 'pi') return { stdout: 'session tracker\n', stderr: '' };
|
|
125
|
+
if (command === 'claude') return { stdout: JSON.stringify({ loggedIn: true }), stderr: '' };
|
|
126
|
+
throw new Error(`unexpected command ${command}`);
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const report = await getAgentReadinessReport('pi', options);
|
|
131
|
+
|
|
132
|
+
expect(report.integration).toMatchObject({ label: 'ai-devkit plugin', installed: true, status: 'pass' });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('reports missing built-in skills as informational readiness', async () => {
|
|
136
|
+
const { options, files } = fixture();
|
|
137
|
+
delete files[path.join(options.homeDir!, skillRoots.codex, 'verify', 'SKILL.md')];
|
|
138
|
+
|
|
139
|
+
const report = await getAgentReadinessReport('codex', options);
|
|
140
|
+
|
|
141
|
+
expect(report.builtInSkills).toMatchObject({
|
|
142
|
+
status: 'info',
|
|
143
|
+
present: 2,
|
|
144
|
+
required: 3,
|
|
145
|
+
missing: ['verify'],
|
|
146
|
+
});
|
|
147
|
+
expect(report.status).toBe('pass');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('checks OpenCode auth from auth list output', async () => {
|
|
151
|
+
const { options } = fixture({
|
|
152
|
+
runCommand: vi.fn(async command => {
|
|
153
|
+
if (command === 'opencode') {
|
|
154
|
+
return { stdout: '\x1B[32m●\x1B[0m litellm api\n● OpenAI oauth\n● GitLab Duo GITLAB_TOKEN\n', stderr: '' };
|
|
155
|
+
}
|
|
156
|
+
if (command === 'pi') return { stdout: 'npm:@ai-devkit/pi-session-tracker\n', stderr: '' };
|
|
157
|
+
if (command === 'claude') return { stdout: JSON.stringify({ loggedIn: true }), stderr: '' };
|
|
158
|
+
throw new Error(`unexpected command ${command}`);
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const report = await getAgentReadinessReport('opencode', options);
|
|
163
|
+
|
|
164
|
+
expect(report.auth).toMatchObject({
|
|
165
|
+
state: 'authenticated',
|
|
166
|
+
source: 'opencode auth list',
|
|
167
|
+
provider: null,
|
|
168
|
+
availableProviders: ['GitLab Duo', 'OpenAI', 'litellm'],
|
|
169
|
+
status: 'pass',
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('checks Copilot auth through GitHub CLI authentication', async () => {
|
|
174
|
+
const { options } = fixture({
|
|
175
|
+
runCommand: vi.fn(async command => {
|
|
176
|
+
if (command === 'gh') return { stdout: 'github.com\n Logged in to github.com account test-user\n', stderr: '' };
|
|
177
|
+
if (command === 'pi') return { stdout: 'npm:@ai-devkit/pi-session-tracker\n', stderr: '' };
|
|
178
|
+
if (command === 'claude') return { stdout: JSON.stringify({ loggedIn: true }), stderr: '' };
|
|
179
|
+
if (command === 'opencode') return { stdout: '● litellm api\n', stderr: '' };
|
|
180
|
+
throw new Error(`unexpected command ${command}`);
|
|
181
|
+
}),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const report = await getAgentReadinessReport('copilot', options);
|
|
185
|
+
|
|
186
|
+
expect(report.auth).toMatchObject({
|
|
187
|
+
state: 'authenticated',
|
|
188
|
+
source: 'gh auth status --hostname github.com',
|
|
189
|
+
provider: 'github',
|
|
190
|
+
availableProviders: ['GitHub'],
|
|
191
|
+
status: 'pass',
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -32,6 +32,7 @@ export class DatabaseConnection {
|
|
|
32
32
|
|
|
33
33
|
this.db = new Database(this.dbPath, {
|
|
34
34
|
readonly: this.readonly,
|
|
35
|
+
timeout: 5000,
|
|
35
36
|
verbose: typeof options.verbose === 'function'
|
|
36
37
|
? options.verbose
|
|
37
38
|
: options.verbose ? console.log : undefined,
|
|
@@ -50,7 +51,8 @@ export class DatabaseConnection {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
private configure(): void {
|
|
53
|
-
this.db.pragma('journal_mode
|
|
54
|
+
const journalMode = this.db.pragma('journal_mode', { simple: true }) as string;
|
|
55
|
+
if (journalMode.toLowerCase() !== 'wal') this.db.pragma('journal_mode = WAL');
|
|
54
56
|
this.db.pragma('foreign_keys = ON');
|
|
55
57
|
this.db.pragma('synchronous = NORMAL');
|
|
56
58
|
this.db.pragma('busy_timeout = 5000');
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,24 @@ export type {
|
|
|
5
5
|
CapacityReport,
|
|
6
6
|
CapacityWindow,
|
|
7
7
|
} from './capacity/index.js';
|
|
8
|
+
export {
|
|
9
|
+
getAgentReadinessReport,
|
|
10
|
+
getAgentReadinessReports,
|
|
11
|
+
worstReadinessStatus,
|
|
12
|
+
} from './readiness/AgentReadiness.js';
|
|
13
|
+
export type {
|
|
14
|
+
AgentReadinessOptions,
|
|
15
|
+
AgentReadinessReport,
|
|
16
|
+
AuthReadinessCheck,
|
|
17
|
+
BuiltInSkillsReadinessCheck,
|
|
18
|
+
DirectoryReadinessCheck,
|
|
19
|
+
ExecutableReadinessCheck,
|
|
20
|
+
IntegrationReadinessCheck,
|
|
21
|
+
ReadinessAgentType,
|
|
22
|
+
ReadinessAuthState,
|
|
23
|
+
ReadinessCheck,
|
|
24
|
+
ReadinessStatus,
|
|
25
|
+
} from './readiness/AgentReadiness.js';
|
|
8
26
|
|
|
9
27
|
export { ClaudeCodeAdapter } from './providers/claude/ClaudeCodeAdapter.js';
|
|
10
28
|
export { CodexAdapter } from './adapters/CodexAdapter.js';
|