agentix-cli 0.9.0 → 0.14.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/README.md +50 -443
- package/dist/agent-DW3NYIIL.js +2 -0
- package/dist/board-dashboard-6TKE6NMV.js +1435 -0
- package/dist/board-dashboard-6TKE6NMV.js.map +1 -0
- package/dist/chunk-6U2XOQHU.js +7 -0
- package/dist/chunk-6U2XOQHU.js.map +1 -0
- package/dist/chunk-DYOBFJLX.js +11 -0
- package/dist/chunk-DYOBFJLX.js.map +1 -0
- package/dist/chunk-HI24KAEY.js +54 -0
- package/dist/chunk-HI24KAEY.js.map +1 -0
- package/dist/chunk-ITZEVXZ4.js +2 -0
- package/dist/chunk-ITZEVXZ4.js.map +1 -0
- package/dist/chunk-M33CB2H4.js +6 -0
- package/dist/chunk-M33CB2H4.js.map +1 -0
- package/dist/chunk-MCJUAE6V.js +6 -0
- package/dist/chunk-MCJUAE6V.js.map +1 -0
- package/dist/chunk-NJR5WF5E.js +5 -0
- package/dist/chunk-NJR5WF5E.js.map +1 -0
- package/dist/chunk-O72LPMBR.js +12 -0
- package/dist/chunk-O72LPMBR.js.map +1 -0
- package/dist/chunk-QOYAX2FT.js +2 -0
- package/dist/chunk-QOYAX2FT.js.map +1 -0
- package/dist/chunk-QZP65I2H.js +45 -0
- package/dist/chunk-QZP65I2H.js.map +1 -0
- package/dist/chunk-RB73FZXJ.js +133 -0
- package/dist/chunk-RB73FZXJ.js.map +1 -0
- package/dist/chunk-UPT2UMF6.js +159 -0
- package/dist/chunk-UPT2UMF6.js.map +1 -0
- package/dist/{chunk-SBUX74OU.js → chunk-XVYMZ4M7.js} +5 -5
- package/dist/cli.js +53 -50
- package/dist/cli.js.map +1 -1
- package/dist/compaction-7I3E5QUO.js +28 -0
- package/dist/compaction-7I3E5QUO.js.map +1 -0
- package/dist/config-7QJ2QJIC.js +2 -0
- package/dist/config-mutator-GAEVGHDD.js +2 -0
- package/dist/index.d.ts +1715 -106
- package/dist/index.js +39 -35
- package/dist/index.js.map +1 -1
- package/dist/loader-YQE6XJBT.js +2 -0
- package/dist/registry-YVAG4EO5.js +2 -0
- package/dist/registry-YVAG4EO5.js.map +1 -0
- package/dist/{usage-dashboard-2TJQBFSH.js → usage-dashboard-M7AFIGMJ.js} +2 -2
- package/dist/workspace-setup-QQTCJY4B.js +2 -0
- package/dist/workspace-setup-QQTCJY4B.js.map +1 -0
- package/package.json +9 -1
- package/dist/agent-QYQVGKLM.js +0 -2
- package/dist/chunk-34UAFPK4.js +0 -10
- package/dist/chunk-34UAFPK4.js.map +0 -1
- package/dist/chunk-BAIQJHQF.js +0 -64
- package/dist/chunk-BAIQJHQF.js.map +0 -1
- package/dist/chunk-BHDLKX3G.js +0 -6
- package/dist/chunk-BHDLKX3G.js.map +0 -1
- package/dist/chunk-CJ45Y2IR.js +0 -12
- package/dist/chunk-CJ45Y2IR.js.map +0 -1
- package/dist/chunk-FRFR27IN.js +0 -3
- package/dist/chunk-FRFR27IN.js.map +0 -1
- package/dist/chunk-IVVVYXPH.js +0 -136
- package/dist/chunk-IVVVYXPH.js.map +0 -1
- package/dist/chunk-X32247GM.js +0 -2
- package/dist/chunk-X32247GM.js.map +0 -1
- package/dist/compaction-E3MQRLTL.js +0 -28
- package/dist/compaction-E3MQRLTL.js.map +0 -1
- package/dist/config-MSWKC466.js +0 -2
- package/dist/loader-6VSM6FSY.js +0 -2
- package/dist/registry-FP6FUOXF.js +0 -2
- /package/dist/{agent-QYQVGKLM.js.map → agent-DW3NYIIL.js.map} +0 -0
- /package/dist/{chunk-SBUX74OU.js.map → chunk-XVYMZ4M7.js.map} +0 -0
- /package/dist/{config-MSWKC466.js.map → config-7QJ2QJIC.js.map} +0 -0
- /package/dist/{loader-6VSM6FSY.js.map → config-mutator-GAEVGHDD.js.map} +0 -0
- /package/dist/{registry-FP6FUOXF.js.map → loader-YQE6XJBT.js.map} +0 -0
- /package/dist/{usage-dashboard-2TJQBFSH.js.map → usage-dashboard-M7AFIGMJ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/types.ts","../src/observability/tracker.ts","../src/observability/export.ts","../src/hooks/registry.ts","../src/hooks/loader.ts","../src/hooks/index.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Hook system types ---\n\nexport const HOOK_EVENTS = [\n \"pre:generate\",\n \"post:generate\",\n \"pre:file-write\",\n \"post:file-write\",\n \"pre:prompt\",\n \"post:response\",\n \"pre:command\",\n \"on:error\",\n \"pre:tool-call\",\n \"post:tool-call\",\n // Daemon events\n \"pre:channel-message\",\n \"post:channel-message\",\n \"pre:a2a-task\",\n \"pre:cron-run\",\n \"post:cron-run\",\n // GitLab-specific events (fired without agent routing — side-effect hooks\n // OR routing-modifying hooks that return `modified.dispatch` to pick the\n // agent(s) to run for this event).\n \"on:gitlab-pipeline\",\n \"on:gitlab-issue\",\n] as const\n\nexport type HookEvent = (typeof HOOK_EVENTS)[number]\n\n// Which hooks can block (return { blocked: true } to cancel the operation)\nexport const BLOCKING_EVENTS: HookEvent[] = [\n \"pre:generate\",\n \"pre:file-write\",\n \"pre:prompt\",\n \"post:response\",\n \"pre:command\",\n \"pre:tool-call\",\n // Daemon blocking events\n \"pre:channel-message\",\n \"post:channel-message\",\n \"pre:a2a-task\",\n \"pre:cron-run\",\n]\n\nexport type HookType = \"command\" | \"prompt\" | \"script\"\n\nexport const hookDefinitionSchema = z.object({\n name: z.string(),\n type: z.enum([\"command\", \"prompt\", \"script\"]),\n // For \"command\" type: shell command with {{variable}} interpolation\n command: z.string().optional(),\n // For \"prompt\" type: LLM prompt template\n prompt: z.string().optional(),\n // For \"prompt\" type: override provider/model for this hook\n provider: z.enum([\"claude-code\", \"claude\", \"openai\", \"ollama\", \"custom\"]).optional(),\n model: z.string().optional(),\n // For \"script\" type: path to JS/TS file exporting a handler\n script: z.string().optional(),\n // Hook priority (lower runs first, default 100)\n priority: z.number().default(100),\n // Whether this hook is enabled\n enabled: z.boolean().default(true),\n})\n\nexport type HookDefinition = z.infer<typeof hookDefinitionSchema>\n\nexport interface HookContext {\n event: HookEvent\n // Data varies by event\n task?: string\n file?: string\n fileContent?: string\n content?: string\n command?: string\n error?: Error\n cwd?: string\n [key: string]: unknown\n}\n\nexport interface HookResult {\n // If true, the operation is blocked/cancelled\n blocked?: boolean\n // Optional message explaining why it was blocked\n message?: string\n // Modified data to pass forward (e.g., modified file content)\n modified?: Record<string, unknown>\n}\n\nexport type HookHandler = (context: HookContext) => Promise<HookResult>\n","// --- Usage tracking: tokens, cost, per-step and per-model breakdown ---\n\nexport interface StepUsage {\n step: number\n model: string\n inputTokens: number\n outputTokens: number\n cost: number\n timestamp: number\n}\n\nexport interface ModelUsage {\n inputTokens: number\n outputTokens: number\n cost: number\n steps: number\n}\n\nexport interface UsageSummary {\n totalInputTokens: number\n totalOutputTokens: number\n totalTokens: number\n totalCost: number\n steps: StepUsage[]\n models: Record<string, ModelUsage>\n}\n\n// Cost per million tokens (input / output)\nconst MODEL_PRICING: Record<string, { input: number; output: number }> = {\n \"claude-sonnet\": { input: 3, output: 15 },\n \"claude-opus\": { input: 15, output: 75 },\n \"claude-haiku\": { input: 0.25, output: 1.25 },\n}\n\nfunction getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) return \"claude-opus\"\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\" // default\n}\n\nexport class UsageTracker {\n private steps: StepUsage[] = []\n private models: Map<string, ModelUsage> = new Map()\n\n recordStep(\n step: number,\n model: string,\n inputTokens: number,\n outputTokens: number\n ): void {\n const family = getModelFamily(model)\n const pricing = MODEL_PRICING[family] || MODEL_PRICING[\"claude-sonnet\"]\n const cost =\n (inputTokens / 1_000_000) * pricing.input +\n (outputTokens / 1_000_000) * pricing.output\n\n this.steps.push({\n step,\n model,\n inputTokens,\n outputTokens,\n cost,\n timestamp: Date.now(),\n })\n\n const existing = this.models.get(model) || {\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n steps: 0,\n }\n existing.inputTokens += inputTokens\n existing.outputTokens += outputTokens\n existing.cost += cost\n existing.steps++\n this.models.set(model, existing)\n }\n\n getSummary(): UsageSummary {\n let totalInputTokens = 0\n let totalOutputTokens = 0\n let totalCost = 0\n\n for (const step of this.steps) {\n totalInputTokens += step.inputTokens\n totalOutputTokens += step.outputTokens\n totalCost += step.cost\n }\n\n const models: Record<string, ModelUsage> = {}\n for (const [name, usage] of this.models) {\n models[name] = { ...usage }\n }\n\n return {\n totalInputTokens,\n totalOutputTokens,\n totalTokens: totalInputTokens + totalOutputTokens,\n totalCost,\n steps: [...this.steps],\n models,\n }\n }\n\n reset(): void {\n this.steps = []\n this.models.clear()\n }\n}\n\nexport const globalTracker = new UsageTracker()\n","// --- Session export: convert a session to readable markdown ---\n\nimport type { Session } from \"@/repl/session\"\nimport type { UsageSummary } from \"./tracker\"\n\nexport function exportSession(session: Session, usage?: UsageSummary): string {\n const lines: string[] = []\n\n // Header\n lines.push(`# Session: ${session.id}`)\n lines.push(\"\")\n lines.push(`- **Created**: ${session.createdAt}`)\n lines.push(`- **Updated**: ${session.updatedAt}`)\n lines.push(`- **CWD**: ${session.cwd}`)\n lines.push(`- **Messages**: ${session.messages.length}`)\n lines.push(`- **Tokens**: ${session.tokensUsed.toLocaleString()}`)\n lines.push(\"\")\n\n // Files generated\n if (session.filesGenerated.length) {\n lines.push(\"## Files Generated\")\n lines.push(\"\")\n for (const file of session.filesGenerated) {\n lines.push(`- \\`${file}\\``)\n }\n lines.push(\"\")\n }\n\n // Conversation\n if (session.messages.length) {\n lines.push(\"## Conversation\")\n lines.push(\"\")\n for (const msg of session.messages) {\n if (msg.role === \"system\") continue\n const label = msg.role === \"user\" ? \"**User**\" : \"**Assistant**\"\n lines.push(`### ${label}`)\n lines.push(\"\")\n if (msg.role === \"assistant\") {\n lines.push(msg.content)\n } else {\n lines.push(msg.content)\n }\n lines.push(\"\")\n }\n }\n\n // Cost summary\n if (usage) {\n lines.push(\"## Cost Summary\")\n lines.push(\"\")\n lines.push(`- **Total tokens**: ${usage.totalTokens.toLocaleString()}`)\n lines.push(` - Input: ${usage.totalInputTokens.toLocaleString()}`)\n lines.push(` - Output: ${usage.totalOutputTokens.toLocaleString()}`)\n lines.push(`- **Total cost**: $${usage.totalCost.toFixed(4)}`)\n lines.push(\"\")\n\n if (Object.keys(usage.models).length) {\n lines.push(\"### Per Model\")\n lines.push(\"\")\n lines.push(\"| Model | Input | Output | Cost | Steps |\")\n lines.push(\"|-------|-------|--------|------|-------|\")\n for (const [model, mu] of Object.entries(usage.models)) {\n lines.push(\n `| ${model} | ${mu.inputTokens.toLocaleString()} | ${mu.outputTokens.toLocaleString()} | $${mu.cost.toFixed(4)} | ${mu.steps} |`\n )\n }\n lines.push(\"\")\n }\n\n if (usage.steps.length) {\n lines.push(\"### Per Step\")\n lines.push(\"\")\n lines.push(\"| Step | Model | Input | Output | Cost |\")\n lines.push(\"|------|-------|-------|--------|------|\")\n for (const step of usage.steps) {\n lines.push(\n `| ${step.step} | ${step.model} | ${step.inputTokens.toLocaleString()} | ${step.outputTokens.toLocaleString()} | $${step.cost.toFixed(4)} |`\n )\n }\n lines.push(\"\")\n }\n }\n\n return lines.join(\"\\n\")\n}\n","import { execa } from \"execa\"\nimport type { HookEvent, HookDefinition, HookContext, HookResult, HookHandler } from \"./types\"\nimport { BLOCKING_EVENTS } from \"./types\"\nimport { logger } from \"@/utils/logger\"\nimport { debug } from \"@/observability\"\nimport { loadAuthConfig, resolveToken } from \"@/utils/auth-store\"\n\n// --- Hook Registry: registration, priority ordering, execution ---\n\ninterface RegisteredHook {\n event: HookEvent\n definition: HookDefinition\n handler: HookHandler\n}\n\nexport class HookRegistry {\n private hooks: Map<HookEvent, RegisteredHook[]> = new Map()\n\n /**\n * Register a hook for an event.\n */\n register(event: HookEvent, definition: HookDefinition): void {\n if (!definition.enabled) return\n\n const handler = this.createHandler(definition)\n const entry: RegisteredHook = { event, definition, handler }\n\n const existing = this.hooks.get(event) || []\n existing.push(entry)\n // Sort by priority (lower number = runs first)\n existing.sort((a, b) => a.definition.priority - b.definition.priority)\n this.hooks.set(event, existing)\n }\n\n /**\n * Execute all hooks for an event. Returns combined result.\n * For blocking events, stops at the first hook that returns { blocked: true }.\n */\n async execute(event: HookEvent, context: HookContext): Promise<HookResult> {\n const registered = this.hooks.get(event)\n if (!registered?.length) {\n return {}\n }\n\n const canBlock = BLOCKING_EVENTS.includes(event)\n let combinedModified: Record<string, unknown> = {}\n\n for (const hook of registered) {\n const start = Date.now()\n try {\n const result = await hook.handler({ ...context, ...combinedModified })\n const duration = Date.now() - start\n\n if (result.modified) {\n combinedModified = { ...combinedModified, ...result.modified }\n }\n\n if (canBlock && result.blocked) {\n debug.hook(hook.definition.name, duration, \"blocked\")\n return {\n blocked: true,\n message: result.message || `Blocked by hook: ${hook.definition.name}`,\n modified: combinedModified,\n }\n }\n\n debug.hook(hook.definition.name, duration, \"ok\")\n } catch (error: any) {\n const duration = Date.now() - start\n debug.hook(hook.definition.name, duration, `error: ${error.message}`)\n logger.warn(`Hook \"${hook.definition.name}\" failed: ${error.message}`)\n // Non-blocking hooks swallow errors; blocking hooks propagate\n if (canBlock) {\n return {\n blocked: true,\n message: `Hook \"${hook.definition.name}\" errored: ${error.message}`,\n }\n }\n }\n }\n\n return { modified: Object.keys(combinedModified).length ? combinedModified : undefined }\n }\n\n /**\n * Check if any hooks are registered for an event.\n */\n has(event: HookEvent): boolean {\n const registered = this.hooks.get(event)\n return !!registered?.length\n }\n\n /**\n * Clear all hooks (useful for testing).\n */\n clear(): void {\n this.hooks.clear()\n }\n\n /**\n * Create a HookHandler from a HookDefinition.\n */\n private createHandler(definition: HookDefinition): HookHandler {\n switch (definition.type) {\n case \"command\":\n return this.createCommandHandler(definition)\n case \"script\":\n return this.createScriptHandler(definition)\n case \"prompt\":\n return this.createPromptHandler(definition)\n default:\n throw new Error(`Unknown hook type: ${definition.type}`)\n }\n }\n\n /**\n * Command hook: runs a shell command with {{variable}} interpolation.\n * Exit code 0 = success, non-zero = blocked (for blocking events).\n */\n private createCommandHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.command) {\n return {}\n }\n\n // Interpolate {{variable}} placeholders\n let cmd = definition.command\n cmd = cmd.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key) => {\n return String(context[key] ?? \"\")\n })\n\n const result = await execa(\"sh\", [\"-c\", cmd], {\n cwd: context.cwd || process.cwd(),\n reject: false,\n timeout: 60_000,\n env: process.env,\n })\n\n if (result.exitCode !== 0) {\n return {\n blocked: true,\n message: result.stderr || result.stdout || `Command hook \"${definition.name}\" failed`,\n }\n }\n\n return {}\n }\n }\n\n /**\n * Script hook: imports and runs a JS/TS module that exports a handler function.\n */\n private createScriptHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.script) {\n return {}\n }\n\n const scriptPath = definition.script.startsWith(\"/\")\n ? definition.script\n : `${context.cwd || process.cwd()}/${definition.script}`\n\n const mod = await import(scriptPath)\n const handler = mod.default || mod.handler || mod\n\n if (typeof handler === \"function\") {\n return await handler(context)\n }\n\n return {}\n }\n }\n\n /**\n * Prompt hook: single-turn LLM call that can block or modify the operation.\n * The prompt template supports {{variable}} interpolation from hook context.\n * The LLM must respond with JSON: { \"action\": \"allow\"|\"block\", \"message\": \"...\", \"modified\": {...} }\n */\n private createPromptHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.prompt) {\n return {}\n }\n\n // Check credentials are available (don't prompt interactively)\n const providerName = definition.provider || loadAuthConfig()?.provider || \"claude-code\"\n const resolved = resolveToken()\n if (!resolved && providerName !== \"claude-code\") {\n logger.warn(\n `Prompt hook \"${definition.name}\" skipped: no credentials available for provider \"${providerName}\"`\n )\n return {}\n }\n\n try {\n // Lazy import to avoid circular deps\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(providerName as any, resolved?.token)\n\n // Interpolate {{variable}} placeholders in the prompt template\n let prompt = definition.prompt\n prompt = prompt.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key) => {\n return String(context[key] ?? \"\")\n })\n\n const systemPrompt = `You are a hook validator for agentx. Analyze the following context and respond with ONLY a JSON object (no markdown, no code fences).\n\nResponse format:\n{\"action\": \"allow\", \"message\": \"optional explanation\"}\nOR\n{\"action\": \"block\", \"message\": \"reason for blocking\"}\nOR\n{\"action\": \"allow\", \"message\": \"optional\", \"modified\": {\"key\": \"new value\"}}\n\nThe \"modified\" field is optional and can contain updated values to pass forward.`\n\n const result = await provider.generate(\n [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n {\n model: definition.model,\n maxTokens: 1024,\n }\n )\n\n // Parse the LLM response as JSON\n const content = result.content.trim()\n // Try to extract JSON from the response (handle markdown fences)\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n logger.warn(`Prompt hook \"${definition.name}\": LLM response was not valid JSON, allowing`)\n return {}\n }\n\n const parsed = JSON.parse(jsonMatch[0])\n\n if (parsed.action === \"block\") {\n return {\n blocked: true,\n message: parsed.message || `Blocked by prompt hook: ${definition.name}`,\n }\n }\n\n if (parsed.modified) {\n return { modified: parsed.modified }\n }\n\n return {}\n } catch (error: any) {\n // Don't block on hook failures — log and continue\n logger.warn(`Prompt hook \"${definition.name}\" failed: ${error.message}`)\n return {}\n }\n }\n }\n}\n","import { existsSync, readFileSync, readdirSync } from \"fs\"\nimport path from \"path\"\nimport type { HookEvent, HookDefinition } from \"./types\"\nimport { HOOK_EVENTS, hookDefinitionSchema } from \"./types\"\nimport { HookRegistry } from \"./registry\"\nimport { logger } from \"@/utils/logger\"\n\n// --- Hook Loader: load hooks from config and .agentx/hooks/ ---\n\ninterface HooksConfig {\n hooks?: Partial<Record<HookEvent, HookDefinition[]>>\n}\n\n/**\n * Load hooks from agentx.config.json and .agentx/hooks/ directory.\n */\nexport function loadHooks(cwd: string, registry: HookRegistry): void {\n loadHooksFromConfig(cwd, registry)\n loadHooksFromDirectory(cwd, registry)\n}\n\n/**\n * Load hook definitions from agentx.config.json.\n */\nfunction loadHooksFromConfig(cwd: string, registry: HookRegistry): void {\n const configPath = path.join(cwd, \"agentx.config.json\")\n if (!existsSync(configPath)) return\n\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf8\")) as HooksConfig\n if (!raw.hooks) return\n\n for (const [eventName, definitions] of Object.entries(raw.hooks)) {\n if (!HOOK_EVENTS.includes(eventName as HookEvent)) {\n logger.warn(`Unknown hook event in config: ${eventName}`)\n continue\n }\n\n const event = eventName as HookEvent\n if (!Array.isArray(definitions)) continue\n\n for (const def of definitions) {\n try {\n const parsed = hookDefinitionSchema.parse(def)\n registry.register(event, parsed)\n } catch (error: any) {\n logger.warn(`Invalid hook definition for ${event}: ${error.message}`)\n }\n }\n }\n } catch (error: any) {\n logger.warn(`Failed to load hooks from config: ${error.message}`)\n }\n}\n\n/**\n * Load hook scripts from .agentx/hooks/ directory.\n * Files are named <event>.<name>.ts (e.g., pre-file-write.format.ts).\n */\nfunction loadHooksFromDirectory(cwd: string, registry: HookRegistry): void {\n const hooksDir = path.join(cwd, \".agentx\", \"hooks\")\n if (!existsSync(hooksDir)) return\n\n try {\n const files = readdirSync(hooksDir).filter(\n (f) => f.endsWith(\".ts\") || f.endsWith(\".js\") || f.endsWith(\".mjs\")\n )\n\n for (const file of files) {\n // Parse filename: <event>.<name>.<ext>\n // Event uses dashes in filename: pre-file-write → pre:file-write\n const parts = file.replace(/\\.(ts|js|mjs)$/, \"\").split(\".\")\n if (parts.length < 2) {\n logger.warn(`Hook file \"${file}\" should be named <event>.<name>.<ext>`)\n continue\n }\n\n const eventSlug = parts[0]\n const name = parts.slice(1).join(\".\")\n\n // Convert slug to event name: pre-file-write → pre:file-write\n const eventName = eventSlug.replace(/-/g, \":\") as HookEvent\n if (!HOOK_EVENTS.includes(eventName)) {\n // Try replacing first dash only: pre-generate → pre:generate\n const altEvent = eventSlug.replace(\"-\", \":\") as HookEvent\n if (!HOOK_EVENTS.includes(altEvent)) {\n logger.warn(`Unknown hook event in filename: ${file}`)\n continue\n }\n registry.register(altEvent, {\n name,\n type: \"script\",\n script: path.join(hooksDir, file),\n priority: 100,\n enabled: true,\n })\n continue\n }\n\n registry.register(eventName, {\n name,\n type: \"script\",\n script: path.join(hooksDir, file),\n priority: 100,\n enabled: true,\n })\n }\n } catch (error: any) {\n logger.warn(`Failed to load hooks from directory: ${error.message}`)\n }\n}\n","export { HookRegistry } from \"./registry\"\nexport { loadHooks } from \"./loader\"\nexport type { HookEvent, HookDefinition, HookContext, HookResult, HookHandler } from \"./types\"\nexport { HOOK_EVENTS, BLOCKING_EVENTS } from \"./types\"\n\nimport { HookRegistry } from \"./registry\"\n\n// Singleton hook registry for the CLI process\nexport const globalHooks = new HookRegistry()\n"],"mappings":"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","names":["z","HOOK_EVENTS","BLOCKING_EVENTS","hookDefinitionSchema","MODEL_PRICING","getModelFamily","model","lower","UsageTracker","step","inputTokens","outputTokens","family","pricing","cost","existing","totalInputTokens","totalOutputTokens","totalCost","models","name","usage","globalTracker","exportSession","session","usage","lines","file","msg","label","model","mu","step","execa","HookRegistry","event","definition","handler","entry","existing","a","b","context","registered","canBlock","BLOCKING_EVENTS","combinedModified","hook","start","result","duration","debug","error","logger","cmd","_match","key","execa","mod","providerName","loadAuthConfig","resolved","resolveToken","createProvider","provider","prompt","systemPrompt","jsonMatch","parsed","existsSync","readFileSync","readdirSync","path","loadHooks","cwd","registry","loadHooksFromConfig","loadHooksFromDirectory","configPath","path","existsSync","raw","readFileSync","eventName","definitions","HOOK_EVENTS","logger","event","def","parsed","hookDefinitionSchema","error","hooksDir","files","readdirSync","f","file","parts","eventSlug","name","altEvent","globalHooks","HookRegistry"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as w}from"crypto";import{readFileSync as x,writeFileSync as I,existsSync as L}from"fs";var l=new Set(["the","a","an","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","can","shall","to","of","in","for","on","with","at","by","from","as","into","about","through","and","but","or","not","no","if","then","so","what","how","when","where","who","which","that","this","it","i","you","we","they","he","she","me","my","your","our","their","please","just","also","very","much","some","any","all"]);function h(e){return e.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(t=>t.length>2&&!l.has(t))}function p(e){let t=new Map,o=new Map,r=[],n=0;for(let s=0;s<e.length;s++){let i=h(e[s]);r.push(i.length),n+=i.length;let u=new Map,d=new Set;for(let c of i)u.set(c,(u.get(c)??0)+1),d.has(c)||(t.set(c,(t.get(c)??0)+1),d.add(c));o.set(s,u)}return{docCount:e.length,avgDocLen:e.length>0?n/e.length:0,df:t,tf:o,docLens:r}}function M(e,t,o,r){let n=r?.k1??1.2,s=r?.b??.75,i=h(e),u=o.tf.get(t);if(!u)return 0;let d=o.docLens[t],c=0;for(let g of i){let f=o.df.get(g)??0;if(f===0)continue;let a=u.get(g)??0;if(a===0)continue;let m=Math.log((o.docCount-f+.5)/(f+.5)+1),b=a*(n+1)/(a+n*(1-s+s*(d/o.avgDocLen)));c+=m*b}return c}function v(e,t,o){let r=[];for(let n=0;n<t.docCount;n++){let s=M(e,n,t,o);s>0&&r.push({docIndex:n,score:s})}return r.sort((n,s)=>s.score-n.score)}function y(e){let t=w("sha256");for(let o of e)t.update(o);return t.digest("hex").slice(0,16)}function S(e,t){let o={};for(let[n,s]of e.df)o[n]=s;let r={};for(let[n,s]of e.tf){let i={};for(let[u,d]of s)i[u]=d;r[String(n)]=i}return{hash:t,docCount:e.docCount,avgDocLen:e.avgDocLen,df:o,tf:r,docLens:e.docLens}}function C(e){let t=new Map(Object.entries(e.df).map(([r,n])=>[r,n])),o=new Map;for(let[r,n]of Object.entries(e.tf))o.set(Number(r),new Map(Object.entries(n)));return{docCount:e.docCount,avgDocLen:e.avgDocLen,df:t,tf:o,docLens:e.docLens}}function O(e,t){let o=y(e);if(L(t))try{let n=JSON.parse(x(t,"utf-8"));if(n.hash===o&&n.docCount===e.length)return C(n)}catch{}let r=p(e);try{I(t,JSON.stringify(S(r,o)))}catch{}return r}export{h as a,p as b,v as c,O as d};
|
|
2
|
+
//# sourceMappingURL=chunk-QOYAX2FT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/memory/bm25.ts"],"sourcesContent":["// --- BM25 scoring engine — zero dependencies ---\n// Drop-in replacement for word-overlap relevance scoring.\n// Standard Okapi BM25 with k1=1.2, b=0.75.\n\nexport interface BM25Index {\n docCount: number\n avgDocLen: number\n /** term → number of documents containing it */\n df: Map<string, number>\n /** docIndex → Map<term, frequency> */\n tf: Map<number, Map<string, number>>\n /** per-document token count */\n docLens: number[]\n}\n\nexport interface BM25Options {\n k1?: number // term frequency saturation, default 1.2\n b?: number // length normalization, default 0.75\n}\n\nexport const STOP_WORDS = new Set([\n \"the\", \"a\", \"an\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"will\", \"would\", \"could\",\n \"should\", \"may\", \"might\", \"can\", \"shall\", \"to\", \"of\", \"in\", \"for\",\n \"on\", \"with\", \"at\", \"by\", \"from\", \"as\", \"into\", \"about\", \"through\",\n \"and\", \"but\", \"or\", \"not\", \"no\", \"if\", \"then\", \"so\", \"what\", \"how\",\n \"when\", \"where\", \"who\", \"which\", \"that\", \"this\", \"it\", \"i\", \"you\",\n \"we\", \"they\", \"he\", \"she\", \"me\", \"my\", \"your\", \"our\", \"their\",\n \"please\", \"just\", \"also\", \"very\", \"much\", \"some\", \"any\", \"all\",\n])\n\n/**\n * Tokenize text: lowercase, split on non-alphanumeric, drop stop words and short tokens.\n */\nexport function tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 2 && !STOP_WORDS.has(w))\n}\n\n/**\n * Build a BM25 inverted index over a corpus of documents.\n */\nexport function buildIndex(docs: string[]): BM25Index {\n const df = new Map<string, number>()\n const tf = new Map<number, Map<string, number>>()\n const docLens: number[] = []\n let totalLen = 0\n\n for (let i = 0; i < docs.length; i++) {\n const tokens = tokenize(docs[i])\n docLens.push(tokens.length)\n totalLen += tokens.length\n\n const termFreq = new Map<string, number>()\n const seen = new Set<string>()\n\n for (const token of tokens) {\n termFreq.set(token, (termFreq.get(token) ?? 0) + 1)\n if (!seen.has(token)) {\n df.set(token, (df.get(token) ?? 0) + 1)\n seen.add(token)\n }\n }\n\n tf.set(i, termFreq)\n }\n\n return {\n docCount: docs.length,\n avgDocLen: docs.length > 0 ? totalLen / docs.length : 0,\n df,\n tf,\n docLens,\n }\n}\n\n/**\n * Score a single document against a query using BM25.\n */\nexport function score(\n query: string,\n docIndex: number,\n index: BM25Index,\n opts?: BM25Options,\n): number {\n const k1 = opts?.k1 ?? 1.2\n const b = opts?.b ?? 0.75\n const queryTokens = tokenize(query)\n const docTf = index.tf.get(docIndex)\n if (!docTf) return 0\n\n const docLen = index.docLens[docIndex]\n let total = 0\n\n for (const term of queryTokens) {\n const termDf = index.df.get(term) ?? 0\n if (termDf === 0) continue\n\n const termTf = docTf.get(term) ?? 0\n if (termTf === 0) continue\n\n // IDF with +1 inside ln() to prevent negative values\n const idf = Math.log(\n (index.docCount - termDf + 0.5) / (termDf + 0.5) + 1,\n )\n\n // BM25 term score\n const tfNorm =\n (termTf * (k1 + 1)) /\n (termTf + k1 * (1 - b + b * (docLen / index.avgDocLen)))\n\n total += idf * tfNorm\n }\n\n return total\n}\n\n/**\n * Score all documents against a query. Returns sorted descending, zero-score docs excluded.\n */\nexport function scoreAll(\n query: string,\n index: BM25Index,\n opts?: BM25Options,\n): Array<{ docIndex: number; score: number }> {\n const results: Array<{ docIndex: number; score: number }> = []\n\n for (let i = 0; i < index.docCount; i++) {\n const s = score(query, i, index, opts)\n if (s > 0) {\n results.push({ docIndex: i, score: s })\n }\n }\n\n return results.sort((a, b) => b.score - a.score)\n}\n\n// --- Cached BM25 Index ---\n// Persists the index to _index.json alongside a content hash.\n// Rebuilds only when documents change.\n\nimport { createHash } from \"crypto\"\nimport { readFileSync, writeFileSync, existsSync } from \"fs\"\n\ninterface SerializedIndex {\n hash: string\n docCount: number\n avgDocLen: number\n df: Record<string, number>\n tf: Record<string, Record<string, number>>\n docLens: number[]\n}\n\nfunction hashDocs(docs: string[]): string {\n const h = createHash(\"sha256\")\n for (const d of docs) h.update(d)\n return h.digest(\"hex\").slice(0, 16)\n}\n\nfunction serializeIndex(index: BM25Index, hash: string): SerializedIndex {\n const df: Record<string, number> = {}\n for (const [k, v] of index.df) df[k] = v\n\n const tf: Record<string, Record<string, number>> = {}\n for (const [docIdx, termMap] of index.tf) {\n const terms: Record<string, number> = {}\n for (const [t, f] of termMap) terms[t] = f\n tf[String(docIdx)] = terms\n }\n\n return { hash, docCount: index.docCount, avgDocLen: index.avgDocLen, df, tf, docLens: index.docLens }\n}\n\nfunction deserializeIndex(data: SerializedIndex): BM25Index {\n const df = new Map(Object.entries(data.df).map(([k, v]) => [k, v]))\n const tf = new Map<number, Map<string, number>>()\n for (const [docIdx, terms] of Object.entries(data.tf)) {\n tf.set(Number(docIdx), new Map(Object.entries(terms)))\n }\n\n return { docCount: data.docCount, avgDocLen: data.avgDocLen, df, tf, docLens: data.docLens }\n}\n\n/**\n * Build a BM25 index with disk caching.\n * Stores the index in `cachePath` and only rebuilds when the content hash changes.\n */\nexport function buildIndexCached(docs: string[], cachePath: string): BM25Index {\n const hash = hashDocs(docs)\n\n // Try loading cached index\n if (existsSync(cachePath)) {\n try {\n const cached: SerializedIndex = JSON.parse(readFileSync(cachePath, \"utf-8\"))\n if (cached.hash === hash && cached.docCount === docs.length) {\n return deserializeIndex(cached)\n }\n } catch {\n // Corrupted cache — rebuild\n }\n }\n\n // Build fresh index\n const index = buildIndex(docs)\n\n // Save to disk (best-effort)\n try {\n writeFileSync(cachePath, JSON.stringify(serializeIndex(index, hash)))\n } catch {\n // Can't write cache — still return the index\n }\n\n return index\n}\n"],"mappings":"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","names":["createHash","readFileSync","writeFileSync","existsSync","STOP_WORDS","tokenize","text","w","buildIndex","docs","df","tf","docLens","totalLen","i","tokens","termFreq","seen","token","score","query","docIndex","index","opts","k1","b","queryTokens","docTf","docLen","total","term","termDf","termTf","idf","tfNorm","scoreAll","results","a","hashDocs","h","d","serializeIndex","hash","k","v","docIdx","termMap","terms","t","f","deserializeIndex","data","buildIndexCached","cachePath","cached"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import{c as m}from"./chunk-HI24KAEY.js";import{existsSync as u,mkdirSync as y,writeFileSync as h,readFileSync as k}from"fs";import{resolve as p}from"path";function d(o,s){let n=`${o} ${s.systemPrompt||""} ${s.name}`.toLowerCase();return/cod(e|ing|er)|develop|engineer|program/.test(n)?"coding":/pm|project.?manag|product|scrum/.test(n)?"pm":/devops|deploy|infra|ops|sre|ci.?cd/.test(n)?"devops":/qa|test|quality|forensic/.test(n)?"qa":"general"}function b(o,s,n){let t=d(o,s),e=[];if(e.push(`# ${s.name}`),e.push(""),e.push("@AGENTS.md"),e.push(""),s.systemPrompt){let i=s.systemPrompt.split(`
|
|
2
|
+
`).slice(0,3).join(`
|
|
3
|
+
`);e.push("## Role"),e.push(i),e.push("")}switch(e.push("## Commands"),e.push(""),u(p(s.workspace,"package.json"))?(e.push("```bash"),e.push("npm install # install dependencies"),e.push("npm test # run tests"),e.push("npm run build # build project"),e.push("```")):u(p(s.workspace,"requirements.txt"))&&(e.push("```bash"),e.push("pip install -r requirements.txt"),e.push("pytest"),e.push("```")),e.push(""),e.push("## Conventions"),e.push(""),t){case"coding":e.push("- Write tests for new code before committing"),e.push("- Keep functions small and focused"),e.push("- Follow existing patterns in the codebase"),e.push("- Run tests before pushing");break;case"pm":e.push("- Keep responses concise (3-5 lines main message)"),e.push("- Reference issues with #IID and MRs with !IID"),e.push("- Update issue labels and milestones when relevant"),e.push("- Summarize decisions, don't narrate process");break;case"devops":e.push("- Always check service status before making changes"),e.push("- Never run destructive commands without confirmation"),e.push("- Log all deployment actions"),e.push("- Verify changes in staging before production");break;case"qa":e.push("- Document reproduction steps for every bug"),e.push("- Include expected vs actual behavior"),e.push("- Check both happy path and edge cases"),e.push("- Attach screenshots when reporting UI issues");break;default:e.push("- Be concise \u2014 lead with the answer, skip preamble"),e.push("- Follow existing patterns in the codebase")}return e.push(""),e.push("## Cross-Channel Messaging"),e.push(""),e.push("You can send messages to any channel proactively:"),e.push("```bash"),e.push(`curl -X POST http://localhost:${n}/send \\`),e.push(' -H "Content-Type: application/json" \\'),e.push(` -d '{"channel":"telegram","chatId":"<id>","text":"<message>","agentId":"${o}"}'`),e.push("```"),e.push("Channels: telegram, whatsapp, gitlab, discord"),e.push(""),e.join(`
|
|
4
|
+
`)}function w(o,s){let n={autoMemoryEnabled:!0,env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}};s.permissionMode==="bypassPermissions"||(s.permissionMode==="plan"?n.permissions={deny:["Bash(rm -rf *)","Bash(drop *)","Bash(DELETE *)"]}:n.permissions={deny:["Bash(rm -rf /)","Bash(> /dev/sda*)","Bash(mkfs*)"]});let t={};return t.Notification=[{matcher:"",hooks:[{type:"command",command:`echo "[${o}] notification: $(jq -r '.type // "unknown"')" >> /tmp/agentx-${o}.log`}]}],t.PostToolUse=[{matcher:"Bash",hooks:[{type:"command",command:`jq -r '.tool_input.command // empty' >> /tmp/agentx-${o}-commands.log`}]}],t.SessionStart=[{matcher:"compact",hooks:[{type:"command",command:`echo "Reminder: You are ${s.name} (${o}). Follow the conventions in CLAUDE.md."`}]}],n.hooks=t,n}function v(o,s){let n=d(o,s),t=[];switch(n){case"coding":t.push({name:"testing.md",content:`---
|
|
5
|
+
paths:
|
|
6
|
+
- "**/*.test.{ts,tsx,js,jsx}"
|
|
7
|
+
- "**/*.spec.{ts,tsx,js,jsx}"
|
|
8
|
+
- "test/**/*"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Testing Rules
|
|
12
|
+
|
|
13
|
+
- Every new function should have a corresponding test
|
|
14
|
+
- Test both happy path and error cases
|
|
15
|
+
- Use descriptive test names that explain what's being tested
|
|
16
|
+
- Mock external dependencies, not internal modules
|
|
17
|
+
`}),t.push({name:"code-quality.md",content:`# Code Quality
|
|
18
|
+
|
|
19
|
+
- No console.log in production code (use a logger)
|
|
20
|
+
- Handle errors explicitly \u2014 no empty catch blocks
|
|
21
|
+
- Keep files under 300 lines
|
|
22
|
+
- Extract repeated logic into shared utilities
|
|
23
|
+
`});break;case"devops":t.push({name:"deployment-safety.md",content:`# Deployment Safety
|
|
24
|
+
|
|
25
|
+
- Always verify staging before production
|
|
26
|
+
- Check disk space and memory before deploying
|
|
27
|
+
- Never expose secrets in logs or commits
|
|
28
|
+
- Use rollback-safe deployment strategies
|
|
29
|
+
`});break;case"pm":t.push({name:"communication.md",content:`# Communication Rules
|
|
30
|
+
|
|
31
|
+
- Keep GitLab comments under 5 lines for the main message
|
|
32
|
+
- Use <details> for verbose output (logs, commands, steps)
|
|
33
|
+
- Reference issues with #IID, merge requests with !IID
|
|
34
|
+
- Never mention Telegram handles on GitLab
|
|
35
|
+
`});break;case"qa":t.push({name:"bug-reports.md",content:`# Bug Report Format
|
|
36
|
+
|
|
37
|
+
Always include:
|
|
38
|
+
1. Steps to reproduce
|
|
39
|
+
2. Expected behavior
|
|
40
|
+
3. Actual behavior
|
|
41
|
+
4. Environment (browser, OS, version)
|
|
42
|
+
5. Screenshots if applicable
|
|
43
|
+
`});break}return t}function A(o,s,n="19900",t=console.error){let e=[],i=[],r=s.workspace;if(!u(r))return t(`Workspace not found: ${r}`),{created:e,skipped:i};if(s.tier!=="claude-code")return{created:e,skipped:i};let a=(c,f)=>{u(c)?i.push(c):(y(p(c,".."),{recursive:!0}),h(c,f),e.push(c))};a(p(r,"AGENTS.md"),m({name:s.name,id:o,role:s.systemPrompt?.split(`
|
|
44
|
+
`)[0],workspace:r,tier:s.tier})),a(p(r,"CLAUDE.md"),b(o,s,n));let l=p(r,".claude/settings.json");a(l,JSON.stringify(w(o,s),null,2));let g=v(o,s);for(let c of g)a(p(r,".claude/rules",c.name),c.content);return e.length>0&&t(`[${o}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:i}}function E(o,s){let n=p(o,".claude/settings.json");if(!u(n))return!1;try{let t=JSON.parse(k(n,"utf-8")),e=!1;for(let[i,r]of Object.entries(s))if(typeof r=="object"&&r!==null&&!Array.isArray(r)){t[i]||(t[i]={});for(let[a,l]of Object.entries(r))t[i][a]===void 0&&(t[i][a]=l,e=!0)}else t[i]===void 0&&(t[i]=r,e=!0);return e&&h(n,JSON.stringify(t,null,2)),e}catch{return!1}}function C(o,s="19900",n=console.error){let t=0,e=0;for(let[i,r]of Object.entries(o)){if(r.tier!=="claude-code")continue;let a=A(i,r,s,n);t+=a.created.length,E(r.workspace,{env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}})&&e++}t>0&&n(`Workspace setup: ${t} file(s) created across agent workspaces`),e>0&&n(`Workspace patch: ${e} workspace(s) updated with agent teams support`)}export{A as a,C as b};
|
|
45
|
+
//# sourceMappingURL=chunk-QZP65I2H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agents/workspace-setup.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync, readFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport type { AgentDef } from \"@/daemon/config\"\nimport { generateAgentsMd } from \"./bootstrap\"\n\n// --- Workspace Setup: align agent workspaces with Claude Code best practices ---\n//\n// Generates on daemon start (if missing):\n// CLAUDE.md — project conventions, under 200 lines, @AGENTS.md import\n// AGENTS.md — agents.md spec for cross-tool compatibility\n// .claude/settings.json — hooks, permissions, auto memory\n// .claude/rules/ — path-specific rules based on agent role\n\n/** Detect agent role from systemPrompt or agent ID */\nfunction detectRole(agentId: string, def: AgentDef): \"coding\" | \"pm\" | \"devops\" | \"qa\" | \"general\" {\n const text = `${agentId} ${def.systemPrompt || \"\"} ${def.name}`.toLowerCase()\n if (/cod(e|ing|er)|develop|engineer|program/.test(text)) return \"coding\"\n if (/pm|project.?manag|product|scrum/.test(text)) return \"pm\"\n if (/devops|deploy|infra|ops|sre|ci.?cd/.test(text)) return \"devops\"\n if (/qa|test|quality|forensic/.test(text)) return \"qa\"\n return \"general\"\n}\n\n/** Generate CLAUDE.md for an agent workspace */\nfunction generateClaudeMd(agentId: string, def: AgentDef, daemonPort: string): string {\n const role = detectRole(agentId, def)\n const lines: string[] = []\n\n lines.push(`# ${def.name}`)\n lines.push(\"\")\n lines.push(\"@AGENTS.md\")\n lines.push(\"\")\n\n // Agent identity from systemPrompt (first 3 lines)\n if (def.systemPrompt) {\n const promptLines = def.systemPrompt.split(\"\\n\").slice(0, 3).join(\"\\n\")\n lines.push(\"## Role\")\n lines.push(promptLines)\n lines.push(\"\")\n }\n\n // Build/test commands\n lines.push(\"## Commands\")\n lines.push(\"\")\n if (existsSync(resolve(def.workspace, \"package.json\"))) {\n lines.push(\"```bash\")\n lines.push(\"npm install # install dependencies\")\n lines.push(\"npm test # run tests\")\n lines.push(\"npm run build # build project\")\n lines.push(\"```\")\n } else if (existsSync(resolve(def.workspace, \"requirements.txt\"))) {\n lines.push(\"```bash\")\n lines.push(\"pip install -r requirements.txt\")\n lines.push(\"pytest\")\n lines.push(\"```\")\n }\n lines.push(\"\")\n\n // Role-specific conventions\n lines.push(\"## Conventions\")\n lines.push(\"\")\n switch (role) {\n case \"coding\":\n lines.push(\"- Write tests for new code before committing\")\n lines.push(\"- Keep functions small and focused\")\n lines.push(\"- Follow existing patterns in the codebase\")\n lines.push(\"- Run tests before pushing\")\n break\n case \"pm\":\n lines.push(\"- Keep responses concise (3-5 lines main message)\")\n lines.push(\"- Reference issues with #IID and MRs with !IID\")\n lines.push(\"- Update issue labels and milestones when relevant\")\n lines.push(\"- Summarize decisions, don't narrate process\")\n break\n case \"devops\":\n lines.push(\"- Always check service status before making changes\")\n lines.push(\"- Never run destructive commands without confirmation\")\n lines.push(\"- Log all deployment actions\")\n lines.push(\"- Verify changes in staging before production\")\n break\n case \"qa\":\n lines.push(\"- Document reproduction steps for every bug\")\n lines.push(\"- Include expected vs actual behavior\")\n lines.push(\"- Check both happy path and edge cases\")\n lines.push(\"- Attach screenshots when reporting UI issues\")\n break\n default:\n lines.push(\"- Be concise — lead with the answer, skip preamble\")\n lines.push(\"- Follow existing patterns in the codebase\")\n }\n lines.push(\"\")\n\n // Cross-channel messaging capability\n lines.push(\"## Cross-Channel Messaging\")\n lines.push(\"\")\n lines.push(\"You can send messages to any channel proactively:\")\n lines.push(\"```bash\")\n lines.push(`curl -X POST http://localhost:${daemonPort}/send \\\\`)\n lines.push(` -H \"Content-Type: application/json\" \\\\`)\n lines.push(` -d '{\"channel\":\"telegram\",\"chatId\":\"<id>\",\"text\":\"<message>\",\"agentId\":\"${agentId}\"}'`)\n lines.push(\"```\")\n lines.push('Channels: telegram, whatsapp, gitlab, discord')\n lines.push(\"\")\n\n return lines.join(\"\\n\")\n}\n\n/** Generate .claude/settings.json with hooks and permissions */\nfunction generateSettings(agentId: string, def: AgentDef): Record<string, unknown> {\n const settings: Record<string, unknown> = {\n autoMemoryEnabled: true,\n env: {\n CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\",\n },\n }\n\n // Permission mode\n if (def.permissionMode === \"bypassPermissions\") {\n // No deny rules — full access\n } else if (def.permissionMode === \"plan\") {\n settings[\"permissions\"] = {\n deny: [\"Bash(rm -rf *)\", \"Bash(drop *)\", \"Bash(DELETE *)\"],\n }\n } else {\n // Default: deny destructive operations\n settings[\"permissions\"] = {\n deny: [\n \"Bash(rm -rf /)\",\n \"Bash(> /dev/sda*)\",\n \"Bash(mkfs*)\",\n ],\n }\n }\n\n // Hooks\n const hooks: Record<string, unknown[]> = {}\n\n // Notification hook — log when agent needs input\n hooks[\"Notification\"] = [{\n matcher: \"\",\n hooks: [{\n type: \"command\",\n command: `echo \"[${agentId}] notification: $(jq -r '.type // \\\"unknown\\\"')\" >> /tmp/agentx-${agentId}.log`,\n }],\n }]\n\n // PostToolUse — log tool activity\n hooks[\"PostToolUse\"] = [{\n matcher: \"Bash\",\n hooks: [{\n type: \"command\",\n command: `jq -r '.tool_input.command // empty' >> /tmp/agentx-${agentId}-commands.log`,\n }],\n }]\n\n // SessionStart after compaction — re-inject key context\n hooks[\"SessionStart\"] = [{\n matcher: \"compact\",\n hooks: [{\n type: \"command\",\n command: `echo \"Reminder: You are ${def.name} (${agentId}). Follow the conventions in CLAUDE.md.\"`,\n }],\n }]\n\n settings[\"hooks\"] = hooks\n return settings\n}\n\n/** Generate .claude/rules/ files based on agent role */\nfunction generateRules(agentId: string, def: AgentDef): Array<{ name: string; content: string }> {\n const role = detectRole(agentId, def)\n const rules: Array<{ name: string; content: string }> = []\n\n switch (role) {\n case \"coding\":\n rules.push({\n name: \"testing.md\",\n content: `---\npaths:\n - \"**/*.test.{ts,tsx,js,jsx}\"\n - \"**/*.spec.{ts,tsx,js,jsx}\"\n - \"test/**/*\"\n---\n\n# Testing Rules\n\n- Every new function should have a corresponding test\n- Test both happy path and error cases\n- Use descriptive test names that explain what's being tested\n- Mock external dependencies, not internal modules\n`,\n })\n rules.push({\n name: \"code-quality.md\",\n content: `# Code Quality\n\n- No console.log in production code (use a logger)\n- Handle errors explicitly — no empty catch blocks\n- Keep files under 300 lines\n- Extract repeated logic into shared utilities\n`,\n })\n break\n\n case \"devops\":\n rules.push({\n name: \"deployment-safety.md\",\n content: `# Deployment Safety\n\n- Always verify staging before production\n- Check disk space and memory before deploying\n- Never expose secrets in logs or commits\n- Use rollback-safe deployment strategies\n`,\n })\n break\n\n case \"pm\":\n rules.push({\n name: \"communication.md\",\n content: `# Communication Rules\n\n- Keep GitLab comments under 5 lines for the main message\n- Use <details> for verbose output (logs, commands, steps)\n- Reference issues with #IID, merge requests with !IID\n- Never mention Telegram handles on GitLab\n`,\n })\n break\n\n case \"qa\":\n rules.push({\n name: \"bug-reports.md\",\n content: `# Bug Report Format\n\nAlways include:\n1. Steps to reproduce\n2. Expected behavior\n3. Actual behavior\n4. Environment (browser, OS, version)\n5. Screenshots if applicable\n`,\n })\n break\n }\n\n return rules\n}\n\n/**\n * Set up an agent workspace with Claude Code best practices.\n * Only creates files that don't already exist (non-destructive).\n */\nexport function setupWorkspace(\n agentId: string,\n def: AgentDef,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n): { created: string[]; skipped: string[] } {\n const created: string[] = []\n const skipped: string[] = []\n const workspace = def.workspace\n\n if (!existsSync(workspace)) {\n log(`Workspace not found: ${workspace}`)\n return { created, skipped }\n }\n\n // Only set up claude-code tier agents\n if (def.tier !== \"claude-code\") {\n return { created, skipped }\n }\n\n const writeIfMissing = (path: string, content: string) => {\n if (existsSync(path)) {\n skipped.push(path)\n } else {\n mkdirSync(resolve(path, \"..\"), { recursive: true })\n writeFileSync(path, content)\n created.push(path)\n }\n }\n\n // AGENTS.md (agents.md spec)\n writeIfMissing(\n resolve(workspace, \"AGENTS.md\"),\n generateAgentsMd({ name: def.name, id: agentId, role: def.systemPrompt?.split(\"\\n\")[0], workspace, tier: def.tier }),\n )\n\n // CLAUDE.md\n writeIfMissing(\n resolve(workspace, \"CLAUDE.md\"),\n generateClaudeMd(agentId, def, daemonPort),\n )\n\n // .claude/settings.json\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n writeIfMissing(settingsPath, JSON.stringify(generateSettings(agentId, def), null, 2))\n\n // .claude/rules/\n const rules = generateRules(agentId, def)\n for (const rule of rules) {\n writeIfMissing(resolve(workspace, \".claude/rules\", rule.name), rule.content)\n }\n\n if (created.length > 0) {\n log(`[${agentId}] workspace setup: created ${created.length} file(s)`)\n }\n\n return { created, skipped }\n}\n\n/**\n * Patch existing .claude/settings.json to add missing keys (non-destructive).\n * Used to enable new features (like agent teams) on workspaces that already have settings.\n */\nfunction patchSettings(workspace: string, patches: Record<string, unknown>): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n let changed = false\n\n for (const [key, value] of Object.entries(patches)) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // Merge objects (e.g. env: { KEY: \"value\" })\n if (!existing[key]) existing[key] = {}\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n if (existing[key][subKey] === undefined) {\n existing[key][subKey] = subValue\n changed = true\n }\n }\n } else if (existing[key] === undefined) {\n existing[key] = value\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n }\n return changed\n } catch {\n return false\n }\n}\n\n/**\n * Set up all agent workspaces on daemon start.\n */\nexport function setupAllWorkspaces(\n agents: Record<string, AgentDef>,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n): void {\n let totalCreated = 0\n let totalPatched = 0\n for (const [id, def] of Object.entries(agents)) {\n if (def.tier !== \"claude-code\") continue\n const result = setupWorkspace(id, def, daemonPort, log)\n totalCreated += result.created.length\n\n // Patch existing settings with new features (agent teams, etc.)\n if (patchSettings(def.workspace, {\n env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\" },\n })) {\n totalPatched++\n }\n }\n if (totalCreated > 0) {\n log(`Workspace setup: ${totalCreated} file(s) created across agent workspaces`)\n }\n if (totalPatched > 0) {\n log(`Workspace patch: ${totalPatched} workspace(s) updated with agent teams support`)\n }\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","resolve","detectRole","agentId","def","text","generateClaudeMd","daemonPort","role","lines","promptLines","existsSync","resolve","generateSettings","settings","hooks","generateRules","rules","setupWorkspace","log","created","skipped","workspace","writeIfMissing","path","content","mkdirSync","writeFileSync","generateAgentsMd","settingsPath","rule","patchSettings","patches","existing","readFileSync","changed","key","value","subKey","subValue","setupAllWorkspaces","agents","totalCreated","totalPatched","id","result"]}
|