agentix-cli 0.9.0 → 0.13.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 +51 -1
- package/dist/agent-5KIMJI3F.js +2 -0
- package/dist/{chunk-SBUX74OU.js → chunk-D6EDMQRV.js} +5 -5
- package/dist/chunk-HI24KAEY.js +54 -0
- package/dist/chunk-HI24KAEY.js.map +1 -0
- package/dist/chunk-MCJUAE6V.js +6 -0
- package/dist/chunk-MCJUAE6V.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-T25UEXVK.js +126 -0
- package/dist/chunk-T25UEXVK.js.map +1 -0
- package/dist/chunk-VH23CDHL.js +157 -0
- package/dist/chunk-VH23CDHL.js.map +1 -0
- package/dist/chunk-WJ6ZF6EZ.js +6 -0
- package/dist/chunk-WJ6ZF6EZ.js.map +1 -0
- package/dist/cli.js +26 -29
- 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-HAD5E4YX.js +2 -0
- package/dist/index.d.ts +1084 -76
- package/dist/index.js +39 -35
- package/dist/index.js.map +1 -1
- package/dist/loader-YQE6XJBT.js +2 -0
- package/dist/registry-CDWY7CLL.js +2 -0
- package/dist/workspace-setup-QQTCJY4B.js +2 -0
- package/dist/workspace-setup-QQTCJY4B.js.map +1 -0
- package/package.json +1 -1
- package/dist/agent-QYQVGKLM.js +0 -2
- 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-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-5KIMJI3F.js.map} +0 -0
- /package/dist/{chunk-SBUX74OU.js.map → chunk-D6EDMQRV.js.map} +0 -0
- /package/dist/{config-MSWKC466.js.map → config-HAD5E4YX.js.map} +0 -0
- /package/dist/{loader-6VSM6FSY.js.map → loader-YQE6XJBT.js.map} +0 -0
- /package/dist/{registry-FP6FUOXF.js.map → registry-CDWY7CLL.js.map} +0 -0
package/README.md
CHANGED
|
@@ -77,6 +77,32 @@ curl -X POST http://localhost:19900/send \
|
|
|
77
77
|
|
|
78
78
|
This enables **H2A2H chains**: a human asks an agent on GitLab to notify someone on WhatsApp. The agent calls `/send` to deliver the message cross-channel.
|
|
79
79
|
|
|
80
|
+
### Automated Client Services
|
|
81
|
+
|
|
82
|
+
Define predictable request-response services that intercept messages BEFORE agent routing — no LLM needed for known patterns:
|
|
83
|
+
|
|
84
|
+
```jsonc
|
|
85
|
+
"services": {
|
|
86
|
+
"monthly-report": {
|
|
87
|
+
"name": "Monthly Client Report",
|
|
88
|
+
"triggers": [
|
|
89
|
+
{ "pattern": "monthly report|client records|تقرير الشهر", "channel": "whatsapp" }
|
|
90
|
+
],
|
|
91
|
+
"allowedContacts": ["+966"],
|
|
92
|
+
"agent": "atlas",
|
|
93
|
+
"prompt": "Run the monthly report SQL query and export as CSV...",
|
|
94
|
+
"schedule": "0 9 1 * *",
|
|
95
|
+
"notify": { "channel": "whatsapp", "chatId": "+966...@s.whatsapp.net" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **Regex triggers** with optional channel filter (Arabic + English patterns)
|
|
101
|
+
- **Contact allowlist** — only authorized contacts can trigger
|
|
102
|
+
- **Predefined prompt** — service sends a known prompt, not the user's raw message
|
|
103
|
+
- **Optional cron schedule** — also runs automatically on a schedule
|
|
104
|
+
- **Cross-channel notify** — results delivered to any channel
|
|
105
|
+
|
|
80
106
|
## Features
|
|
81
107
|
|
|
82
108
|
### Channels
|
|
@@ -86,7 +112,7 @@ This enables **H2A2H chains**: a human asks an agent on GitLab to notify someone
|
|
|
86
112
|
| **Telegram** | Multi-account bots, streaming edits, bot-to-bot delegation, media handling |
|
|
87
113
|
| **WhatsApp** | Baileys integration, QR pairing, per-contact/group routing, agent delegation (shared number, name-prefixed) |
|
|
88
114
|
| **Discord** | Mention-based routing, DM support, agent delegation |
|
|
89
|
-
| **GitLab** | Per-agent identity via PAT tokens, @mention routing
|
|
115
|
+
| **GitLab** | Per-agent identity via PAT tokens, @mention routing, bot-to-bot handoff, image attachments, cascade prevention |
|
|
90
116
|
| **Webhooks** | Generic `POST /webhook/:agentId` for Stripe, Sentry, GitHub, etc. |
|
|
91
117
|
|
|
92
118
|
### GitLab Integration
|
|
@@ -98,6 +124,7 @@ Agents participate in GitLab as first-class team members:
|
|
|
98
124
|
- **Eye reaction** — Agents react with 👀 using their own token (never the global token)
|
|
99
125
|
- **Cascade prevention** — Hidden signature `<!-- agentx:agentId -->`, sent-note dedup, bot-user detection
|
|
100
126
|
- **Human mention filtering** — If @mentioned user isn't a known agent, the note is ignored
|
|
127
|
+
- **Image attachments** — Screenshots and diagrams in comments are downloaded and passed to agents for analysis
|
|
101
128
|
|
|
102
129
|
### Context Compaction
|
|
103
130
|
|
|
@@ -260,6 +287,28 @@ agentx daemon send devops "check disk space" --peer server-2
|
|
|
260
287
|
- Wiki sync across peers
|
|
261
288
|
- Cross-machine agent delegation via A2A protocol
|
|
262
289
|
|
|
290
|
+
### MCP Server
|
|
291
|
+
|
|
292
|
+
AgentX exposes its full capabilities as MCP tools, usable from Claude Code, Cursor, or Windsurf:
|
|
293
|
+
|
|
294
|
+
| Tool | Description |
|
|
295
|
+
|------|-------------|
|
|
296
|
+
| `agentx_generate` | Generate code/components/APIs with tech stack awareness |
|
|
297
|
+
| `agentx_inspect` | Analyze project tech stack and schemas |
|
|
298
|
+
| `agentx_send` | Send messages to any channel (cross-channel routing) |
|
|
299
|
+
| `agentx_task` | Delegate work to a specific agent |
|
|
300
|
+
| `agentx_agents` | List agents and their status |
|
|
301
|
+
| `agentx_health` | Daemon health check |
|
|
302
|
+
| `agentx_crons` | Cron job health with error details |
|
|
303
|
+
| `agentx_debug` | Toggle debug mode with categories |
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Start the MCP server (stdio transport)
|
|
307
|
+
agentx mcp
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Configure in Claude Code's `.claude/settings.json` or Cursor's MCP settings.
|
|
311
|
+
|
|
263
312
|
## CLI Reference
|
|
264
313
|
|
|
265
314
|
```bash
|
|
@@ -382,6 +431,7 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
|
|
|
382
431
|
| `/channels` | GET | List registered channels |
|
|
383
432
|
| `/crons` | GET | List cron jobs |
|
|
384
433
|
| `/crons/health` | GET | Cron health: healthy/failing/disabled/missed |
|
|
434
|
+
| `/services` | GET | List registered automated services |
|
|
385
435
|
| `/task` | POST | `{ "agent": "id", "message": "..." }` |
|
|
386
436
|
| `/send` | POST | `{ "channel": "telegram", "chatId": "...", "text": "...", "agentId": "..." }` |
|
|
387
437
|
| `/mesh/task` | POST | `{ "peer": "name", "message": "..." }` |
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{s as a,t as b,u as c}from"./chunk-D6EDMQRV.js";import"./chunk-SFQUP3BP.js";import"./chunk-CJ45Y2IR.js";import"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-MCJUAE6V.js";import"./chunk-DSYKYZMT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{a as createAgentContext,b as generate,c as generateStream};
|
|
2
|
+
//# sourceMappingURL=agent-5KIMJI3F.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as Ce,b as Se,c as ge}from"./chunk-SFQUP3BP.js";import{d as X,h as p}from"./chunk-CJ45Y2IR.js";import{a as
|
|
1
|
+
import{a as Ce,b as Se,c as ge}from"./chunk-SFQUP3BP.js";import{d as X,h as p}from"./chunk-CJ45Y2IR.js";import{a as de,c as ue,g as me}from"./chunk-KXZMYLHQ.js";import{a as F,b as ce,f as pe}from"./chunk-M7HKBG3V.js";import{a as Te,d as fe}from"./chunk-MCJUAE6V.js";import{e as v}from"./chunk-DSYKYZMT.js";import{b as he,c as ye}from"./chunk-QOYAX2FT.js";import{z as R}from"zod";var Re=R.object({provider:R.enum(["claude-code","claude","openai","ollama","custom"]).default("claude-code"),model:R.string().optional(),apiKey:R.string().optional(),skills:R.array(R.string()).default([]),output:R.object({dir:R.string().default("./generated")}).default({}),context7:R.object({enabled:R.boolean().default(!0),apiKey:R.string().optional()}).default({}),agentic:R.object({maxIterations:R.number().default(20),enabledTools:R.array(R.string()).default(["create_files","ask_user","read_file","search_files","list_directory","run_command","edit_file"]),disabledTools:R.array(R.string()).default([])}).default({})}),dt=["component","page","api","website","document","script","config","skill","media","report","test","workflow","schema","email","diagram","auto"],ut={component:"UI component (any framework)",page:"Full page or screen",api:"API endpoint, route handler, or service",website:"Multi-page website or app",document:"Markdown, documentation, or specification",script:"Standalone script or utility",config:"Configuration file or setup",skill:"Agent skill (SKILL.md format for skills.sh)",media:"Media generation prompt (image/audio/video description)",report:"Analysis report or audit",test:"Test suite, test fixtures, or test data",workflow:"CI/CD pipeline, GitHub Actions, or automation",schema:"Database schema, Zod validators, or GraphQL types",email:"Email template (React Email, MJML, HTML)",diagram:"Mermaid, D2, or PlantUML diagram",auto:"Auto-detect the best output type"};import{existsSync as Ye,promises as Je}from"fs";import B from"path";import Qe from"fast-glob";var Ae={"prisma/schema.prisma":{type:"prisma",category:"database"},"drizzle/schema.ts":{type:"drizzle",category:"database"},"schema.graphql":{type:"graphql",category:"api"},"schema.gql":{type:"graphql",category:"api"},"openapi.yaml":{type:"openapi",category:"api"},"openapi.json":{type:"openapi",category:"api"},"swagger.yaml":{type:"openapi",category:"api"},"swagger.json":{type:"openapi",category:"api"}};async function Me(o){let e={},t=await Qe.glob("**/*",{cwd:o,deep:4,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/target/**","**/__pycache__/**","**/vendor/**","**/.git/**"],onlyFiles:!0});for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="database"){let a=await K(B.resolve(o,l));if(a){e.database={type:i.type,content:Z(a,3e3),tables:Ve(a,i.type)};break}}}for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="api"){let a=await K(B.resolve(o,l));if(a){e.api={type:i.type,content:Z(a,3e3)};break}}}if(!e.api){let s=t.find(i=>i.includes("trpc")&&(i.endsWith("router.ts")||i.endsWith("router.js")));if(s){let i=await K(B.resolve(o,s));i&&(e.api={type:"trpc",content:Z(i,3e3)})}}let n=t.find(s=>s===".env.example"||s===".env.local.example"||s===".env.template");if(n){let s=await K(B.resolve(o,n));s&&(e.env=Xe(s))}let r=t.filter(s=>(s.includes("models")||s.includes("types")||s.includes("schemas"))&&(s.endsWith(".ts")||s.endsWith(".py")||s.endsWith(".rs")||s.endsWith(".go")));if(r.length){e.models=[];for(let s of r.slice(0,5)){let i=await K(B.resolve(o,s));i&&e.models.push({path:s,content:Z(i,2e3),type:B.extname(s).slice(1)})}}return e}function Ve(o,e){if(e==="prisma"){let t=o.match(/model\s+(\w+)\s*\{/g);return t?t.map(n=>n.replace(/model\s+/,"").replace(/\s*\{/,"")):[]}return[]}function Xe(o){return{variables:o.split(`
|
|
2
2
|
`).filter(n=>n.trim()&&!n.trim().startsWith("#")).map(n=>{let[r]=n.split("="),s=r.trim(),i=n.includes("=")&&n.split("=")[1]?.trim().length>0;return{key:s,required:!i}})}}async function K(o){try{return Ye(o)?await Je.readFile(o,"utf8"):null}catch{return null}}function Z(o,e){return o.length<=e?o:o.slice(0,e)+`
|
|
3
3
|
... (truncated)`}function Ie(o){let e=[];if(o.database&&e.push(`## Database Schema (${o.database.type})
|
|
4
4
|
`+(o.database.tables?.length?`Tables: ${o.database.tables.join(", ")}
|
|
@@ -38,7 +38,7 @@ ${s.join(`
|
|
|
38
38
|
... (output truncated)`:i;return s.exitCode!==0?{tool_use_id:e.id,content:`Command exited with code ${s.exitCode}:
|
|
39
39
|
${l}`,is_error:!0}:{tool_use_id:e.id,content:l||"(no output)"}}async editFile(e){let t=String(e.input.path||""),n=e.input.edits,r=re.resolve(this.cwd,t);if(!n||n.length===0)return{tool_use_id:e.id,content:"No edits provided.",is_error:!0};let s=await N.checkFileWrite(t);if(s==="deny")return{tool_use_id:e.id,content:`File write blocked by permissions: ${t}`,is_error:!0};if(s==="skip")return{tool_use_id:e.id,content:`File write skipped (plan mode): ${t}`};if(p.has("pre:file-write")){let a=await p.execute("pre:file-write",{event:"pre:file-write",file:r,cwd:this.cwd});if(a.blocked)return{tool_use_id:e.id,content:a.message||`File edit blocked by pre:file-write hook: ${t}`,is_error:!0}}let i=await J.readFile(r,"utf8"),l=[];for(let a of n)i.includes(a.old_text)?(i=i.replace(a.old_text,a.new_text),l.push(`Replaced: "${a.old_text.slice(0,40)}..."`)):l.push(`Not found: "${a.old_text.slice(0,40)}..."`);return this.options.dryRun||await J.writeFile(r,i,"utf8"),p.has("post:file-write")&&await p.execute("post:file-write",{event:"post:file-write",file:r,fileContent:i,cwd:this.cwd}),{tool_use_id:e.id,content:`Edited ${t}:
|
|
40
40
|
${l.join(`
|
|
41
|
-
`)}`}}async createFiles(e){let t=e.input,n=t.files||[];return{tool_use_id:e.id,content:t.summary||`Queued ${n.length} file(s) for creation.`,files:n}}async spawnAgent(e){let t=String(e.input.agent_id||""),n=String(e.input.prompt||""),r=Number(e.input.timeout_seconds)||300;if(!t||!n)return{tool_use_id:e.id,content:"agent_id and prompt are required.",is_error:!0};try{let{SubAgentManager:s}=await import("./subagent-WV7QKQ3L.js"),{getGlobalRegistry:i}=await import("./registry-
|
|
41
|
+
`)}`}}async createFiles(e){let t=e.input,n=t.files||[];return{tool_use_id:e.id,content:t.summary||`Queued ${n.length} file(s) for creation.`,files:n}}async spawnAgent(e){let t=String(e.input.agent_id||""),n=String(e.input.prompt||""),r=Number(e.input.timeout_seconds)||300;if(!t||!n)return{tool_use_id:e.id,content:"agent_id and prompt are required.",is_error:!0};try{let{SubAgentManager:s}=await import("./subagent-WV7QKQ3L.js"),{getGlobalRegistry:i}=await import("./registry-CDWY7CLL.js"),l=i();if(!l)return{tool_use_id:e.id,content:"Sub-agent spawning requires the daemon to be running.",is_error:!0};let h=await new s(l).spawn({targetAgentId:t,prompt:n,parentAgentId:"orchestrator",timeout:r*1e3});return h.success?{tool_use_id:e.id,content:`Sub-agent "${t}" completed (${Math.round(h.duration/1e3)}s):
|
|
42
42
|
|
|
43
43
|
${h.content}`}:{tool_use_id:e.id,content:`Sub-agent "${t}" failed: ${h.error}`,is_error:!0}}catch(s){return{tool_use_id:e.id,content:`Failed to spawn sub-agent: ${s.message}`,is_error:!0}}}async askUser(e){let t=String(e.input.question||""),n=e.input.options,r=t;return n?.length&&(r+=`
|
|
44
44
|
Options: ${n.join(", ")}`),{tool_use_id:e.id,content:"Question sent to user.",followUp:r}}};async function ve(o){let{provider:e,systemPrompt:t,messages:n,providerOptions:r,cwd:s,maxIterations:i=20,enabledTools:l,interactive:a=!0,overwrite:h=!1,dryRun:c=!1,onProgress:f}=o;if(!e.generateRaw)return We(o);let x=new Q(s,{interactive:a,overwrite:h,dryRun:c}),b=de(l),$=n.filter(k=>k.role!=="system").map(k=>({role:k.role,content:k.content})),G=[],C=0,S="",O,M=0;for(;M<i;){M++,f?.({type:"iteration_start",iteration:M}),v.step(M,`Agentic loop iteration (${b.length} tools available)`);let k;try{k=await e.generateRaw($,t,b,r)}catch(y){if(y.message?.includes("not available"))return We(o);throw y}C+=k.usage.input_tokens+k.usage.output_tokens;for(let y of k.content)y.type==="text"&&(S+=y.text,f?.({type:"text_delta",text:y.text}));if(k.stop_reason==="end_turn"||k.stop_reason==="max_tokens")break;if(k.stop_reason==="tool_use"){let y=k.content.filter(_=>_.type==="tool_use");if(y.length===0)break;$.push({role:"assistant",content:k.content});let I=[];for(let _ of y){f?.({type:"tool_call",name:_.name,id:_.id,input:_.input}),v.step(M,`Tool call: ${_.name}`);let w=await x.execute({name:_.name,id:_.id,input:_.input});f?.({type:"tool_result",name:_.name,id:_.id,content:w.content.slice(0,200),is_error:w.is_error}),w.files?.length&&(G.push(...w.files),f?.({type:"files_created",files:w.files})),w.followUp&&(O=w.followUp),I.push({type:"tool_result",tool_use_id:w.tool_use_id,content:w.content,is_error:w.is_error})}if($.push({role:"user",content:I}),O)break;continue}break}return f?.({type:"complete",iterations:M,totalTokens:C}),{files:G,content:S,followUp:O,tokensUsed:C,iterations:M}}async function We(o){let{provider:e,systemPrompt:t,messages:n,providerOptions:r,maxIterations:s=5}=o,l=[{role:"system",content:t+`
|
|
@@ -47,7 +47,7 @@ Options: ${n.join(", ")}`),{tool_use_id:e.id,content:"Question sent to user.",fo
|
|
|
47
47
|
`);l.push({role:"assistant",content:b.content+(G?`
|
|
48
48
|
|
|
49
49
|
Files created:
|
|
50
|
-
${G}`:"")}),l.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}return{files:a,content:c,followUp:f,tokensUsed:h,iterations:x}}function _e(o){return typeof o.generateRaw=="function"}var se=class{queue=[];resolvers=[];closed=!1;push(e){if(this.closed)return;let t=this.resolvers.shift();if(t){t({value:e,done:!1});return}this.queue.push(e)}close(){if(!this.closed){this.closed=!0;for(let e of this.resolvers.splice(0))e({value:void 0,done:!0})}}async next(){return this.queue.length?{value:this.queue.shift(),done:!1}:this.closed?{value:void 0,done:!0}:new Promise(e=>{this.resolvers.push(e)})}async*[Symbol.asyncIterator](){for(;;){let{value:e,done:t}=await this.next();if(t)return;yield e}}};async function ze(o,e,t){let n=Re.parse(t||{}),r=new Y(o);await r.load();let[s,i,l]=await Promise.all([Ce(o),Me(o),Te(o)]),a="";if(n.context7.enabled)try{a=await Oe(s,e,n.context7.apiKey)}catch{}let h=r.buildMemoryContext(e),c=ke(o);return v.context("memory",h?"loaded":"empty"),v.context("instructions",c?"loaded from project":"none"),{techStack:s,schemas:i,skills:l,docs:a,config:n,memoryContext:h,projectInstructions:c}}async function
|
|
50
|
+
${G}`:"")}),l.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}return{files:a,content:c,followUp:f,tokensUsed:h,iterations:x}}function _e(o){return typeof o.generateRaw=="function"}var se=class{queue=[];resolvers=[];closed=!1;push(e){if(this.closed)return;let t=this.resolvers.shift();if(t){t({value:e,done:!1});return}this.queue.push(e)}close(){if(!this.closed){this.closed=!0;for(let e of this.resolvers.splice(0))e({value:void 0,done:!0})}}async next(){return this.queue.length?{value:this.queue.shift(),done:!1}:this.closed?{value:void 0,done:!0}:new Promise(e=>{this.resolvers.push(e)})}async*[Symbol.asyncIterator](){for(;;){let{value:e,done:t}=await this.next();if(t)return;yield e}}};async function ze(o,e,t){let n=Re.parse(t||{}),r=new Y(o);await r.load();let[s,i,l]=await Promise.all([Ce(o),Me(o),Te(o)]),a="";if(n.context7.enabled)try{a=await Oe(s,e,n.context7.apiKey)}catch{}let h=r.buildMemoryContext(e),c=ke(o);return v.context("memory",h?"loaded":"empty"),v.context("instructions",c?"loaded from project":"none"),{techStack:s,schemas:i,skills:l,docs:a,config:n,memoryContext:h,projectInstructions:c}}async function Uo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let u=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:prompt hook");u.modified?.task&&(c=String(u.modified.task))}if(p.has("pre:generate")){let u=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:generate hook")}F.info("Analyzing project...");let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);F.info(`Output type: ${x}`);let b=fe(f.skills,c,x);b.length&&F.info(`Loaded ${b.length} relevant skill(s): ${b.map(u=>u.skill.frontmatter.name).join(", ")}`);let $=He(f,x,b.map(u=>u.skill));if(!await pe(l))throw new Error("No credentials configured. Run `agentx model` to set up.");let C=me(s,l),S=i||ce()?.model,O=[...o.sessionMessages||[],{role:"user",content:c}];F.info("Generating...");let M=_e(C),k=o.maxSteps??(M?20:5),y=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...O],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:k,enabledTools:f.config.agentic.enabledTools.filter(u=>!f.config.agentic.disabledTools.includes(u)),interactive:h,overwrite:n,dryRun:r,onProgress:u=>{u.type==="iteration_start"&&u.iteration>1&&F.info(`Step ${u.iteration}/${k}...`),u.type==="tool_call"&&v.step(0,`Tool: ${u.name}`)}});if(y.tokensUsed){let u=S||"claude-sonnet-4-20250514",L=Math.round(y.tokensUsed*.3),ie=y.tokensUsed-L;X.recordStep(1,u,L,ie)}let{content:I}=y,{followUp:_,tokensUsed:w}=y;if(y.iterations>1&&F.info(`Completed in ${y.iterations} step(s)`),p.has("post:response")){let u=await p.execute("post:response",{event:"post:response",content:I,task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by post:response hook");u.modified?.content&&(I=String(u.modified.content))}if(_&&h)return{files:{written:[],skipped:[],errors:[]},content:I,outputType:x,followUp:_,tokensUsed:w};let U=new Map;for(let u of y.files)U.set(u.path,u);let A=ne(x,f.techStack,o.outputDir),T=await oe(Array.from(U.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:A}),j;if(o.heal!==!1&&!r&&T.written.length>0){let{HealEngine:u}=await import("./heal-OTGT5HHJ.js");j=await new u(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(T.written,c),!j.healed&&j.error&&await p.execute("on:error",{event:"on:error",error:new Error(j.error),task:c,cwd:t})}return p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:I,cwd:t}),{files:T,content:I,outputType:x,tokensUsed:w,healResult:j}}async function*Eo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let d=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:prompt hook"};return}d.modified?.task&&(c=String(d.modified.task))}if(p.has("pre:generate")){let d=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:generate hook"};return}}let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);yield{type:"context_ready",outputType:x};let b=fe(f.skills,c,x),$=He(f,x,b.map(d=>d.skill));if(!await pe(l)){yield{type:"error",error:"No credentials configured. Run `agentx model` to set up."};return}let C=me(s,l),S=i||ce()?.model,O=_e(C),M=o.maxSteps??(O?20:5),k=[...o.sessionMessages||[],{role:"user",content:c}];if(O){let d=new se,P,g,q=(async()=>{try{P=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...k],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:M,enabledTools:f.config.agentic.enabledTools.filter(m=>!f.config.agentic.disabledTools.includes(m)),interactive:h,overwrite:n,dryRun:r,onProgress:m=>{m.type==="text_delta"&&d.push({type:"text_delta",text:m.text}),m.type==="iteration_start"&&d.push({type:"iteration",iteration:m.iteration}),m.type==="tool_call"&&d.push({type:"tool_call",name:m.name,id:m.id}),m.type==="tool_result"&&d.push({type:"tool_result",name:m.name,id:m.id,is_error:m.is_error}),m.type==="files_created"&&d.push({type:"step_complete",step:0,filesCount:m.files.length})}})}catch(m){g=m}finally{d.close()}})();for await(let m of d)yield m;if(await q,g){yield{type:"error",error:g instanceof Error?g.message:String(g)};return}if(!P){yield{type:"error",error:"Agentic loop failed without a result"};return}yield{type:"done",result:{content:P.content,files:P.files,tokensUsed:P.tokensUsed,followUp:P.followUp}};let{content:E}=P;if(p.has("post:response")){let m=await p.execute("post:response",{event:"post:response",content:E,task:c,cwd:t});if(m.blocked){yield{type:"error",error:m.message||"Blocked by post:response hook"};return}m.modified?.content&&(E=String(m.modified.content))}if(P.followUp&&h){yield{type:"generate_result",result:{files:{written:[],skipped:[],errors:[]},content:E,outputType:x,followUp:P.followUp,tokensUsed:P.tokensUsed}};return}let z=new Map;for(let m of P.files)z.set(m.path,m);let le=ne(x,f.techStack,o.outputDir),V=await oe(Array.from(z.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:le}),H;if(o.heal!==!1&&!r&&V.written.length>0){let{HealEngine:m}=await import("./heal-OTGT5HHJ.js");H=await new m(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(V.written,c)}p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:E,cwd:t}),yield{type:"generate_result",result:{files:V,content:E,outputType:x,tokensUsed:P.tokensUsed,healResult:H}};return}let y=[{role:"system",content:$},...k],I=[],_=0,w="",U,A=0;A++,v.step(A,`Starting generation (model: ${S||"default"})`);let T;if(C.stream){let d="",P;for await(let g of C.stream(y,{model:S,maxTokens:8192}))yield g,g.type==="text_delta"&&(d+=g.text),g.type==="done"&&(P=g.result);T=P||{content:d,files:[],tokensUsed:0}}else{let d=await C.generate(y,{model:S,maxTokens:8192});d.content&&(yield{type:"text_delta",text:d.content}),yield{type:"done",result:d},T=d}_+=T.tokensUsed||0,w=T.content;let j=T.tokensUsed||0,u=S||"claude-sonnet-4-20250514",L=Math.round(j*.3),ie=j-L;if(X.recordStep(A,u,L,ie),v.api("generate",u,j),v.step(A,`Generated ${T.files.length} file(s), ${j} tokens`),T.files.length&&I.push(...T.files),T.followUp&&(U=T.followUp),yield{type:"step_complete",step:A,filesCount:T.files.length},!U&&(w.includes("[CONTINUE]")||w.includes("Next, I'll")||w.includes("Now let me")||w.includes("I'll also generate"))&&T.files.length>0){let P=T.files.map(g=>`Created: ${g.path}${g.description?` \u2014 ${g.description}`:""}`).join(`
|
|
51
51
|
`);for(y.push({role:"assistant",content:w+(P?`
|
|
52
52
|
|
|
53
53
|
Files created:
|
|
@@ -104,5 +104,5 @@ ${i.instructions}`).join(`
|
|
|
104
104
|
${o.projectInstructions}`),o.memoryContext&&n.push(o.memoryContext),n.push(`# Output Type: ${e}
|
|
105
105
|
Generate output appropriate for: ${e}. Use the \`create_files\` tool to output all files.`),n.join(`
|
|
106
106
|
|
|
107
|
-
`)}export{Re as a,dt as b,ut as c,Me as d,Ie as e,Oe as f,Ue as g,we as h,Ee as i,De as j,be as k,ee as l,N as m,Y as n,qe as o,ke as p,xe as q,Q as r,ze as s,
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
`)}export{Re as a,dt as b,ut as c,Me as d,Ie as e,Oe as f,Ue as g,we as h,Ee as i,De as j,be as k,ee as l,N as m,Y as n,qe as o,ke as p,xe as q,Q as r,ze as s,Uo as t,Eo as u};
|
|
108
|
+
//# sourceMappingURL=chunk-D6EDMQRV.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import{readFileSync as a,existsSync as c,unlinkSync as u,readdirSync as g}from"fs";import{resolve as o}from"path";function p(t,e){let n={},s=r=>{let i=o(t,r);if(c(i))try{return a(i,"utf-8").trim()||void 0}catch{return}};if(e){let r=s(`SOUL.${e}.md`);r?(n.soul=r,n.soulProfile=e):n.soul=s("SOUL.md")}else n.soul=s("SOUL.md");if(n.identity=s("IDENTITY.md"),n.user=s("USER.md"),n.agents=s("AGENTS.md"),n.bootstrap=s("BOOTSTRAP.md"),n.bootstrap)try{u(o(t,"BOOTSTRAP.md"))}catch{}return n}function h(t){let e=[];return t.identity&&e.push(`[Identity]
|
|
2
|
+
${t.identity}`),t.soul&&e.push(`[Personality & Boundaries]
|
|
3
|
+
${t.soul}`),t.agents&&e.push(`[Operating Rules]
|
|
4
|
+
${t.agents}`),t.user&&e.push(`[User Profile]
|
|
5
|
+
${t.user}`),t.bootstrap&&e.push(`[First-Run Instructions \u2014 one time only]
|
|
6
|
+
${t.bootstrap}`),e.length===0?"":e.join(`
|
|
7
|
+
|
|
8
|
+
`)}function f(t){return`# ${t.name}
|
|
9
|
+
|
|
10
|
+
> Agent ID: \`${t.id}\` | Tier: ${t.tier||"claude-code"}
|
|
11
|
+
|
|
12
|
+
## Role
|
|
13
|
+
|
|
14
|
+
${t.role||"AI agent managed by AgentX orchestrator."}
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
This workspace is managed by AgentX. The agent runs via:
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
# Direct execution
|
|
21
|
+
agentx daemon send ${t.id} "your task here"
|
|
22
|
+
|
|
23
|
+
# Or via any connected channel (Telegram, WhatsApp, GitLab)
|
|
24
|
+
\`\`\`
|
|
25
|
+
|
|
26
|
+
## Code Style
|
|
27
|
+
|
|
28
|
+
- Follow existing patterns in the codebase
|
|
29
|
+
- Use the language and framework conventions already present
|
|
30
|
+
- Keep changes minimal and focused
|
|
31
|
+
|
|
32
|
+
## Testing
|
|
33
|
+
|
|
34
|
+
Run tests before committing:
|
|
35
|
+
\`\`\`bash
|
|
36
|
+
npm test # or the project's test command
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
## PR Guidelines
|
|
40
|
+
|
|
41
|
+
- One logical change per commit
|
|
42
|
+
- Reference issue numbers when applicable
|
|
43
|
+
- Keep PR descriptions concise
|
|
44
|
+
|
|
45
|
+
## AgentX Integration
|
|
46
|
+
|
|
47
|
+
This agent is part of an AgentX mesh. It can:
|
|
48
|
+
- Receive tasks from messaging channels (Telegram, WhatsApp, Discord)
|
|
49
|
+
- Respond to GitLab @mentions on issues and MRs
|
|
50
|
+
- Delegate to other agents by mentioning their handles
|
|
51
|
+
- Send cross-channel messages via the /send API
|
|
52
|
+
- Access its wiki knowledge base for context
|
|
53
|
+
`}function S(t){let e=t.match(/\/soul\s+(\w+)/i);return e?e[1].toLowerCase():/\/soul\s*$/i.test(t)?"default":null}export{p as a,h as b,f as c,S as d};
|
|
54
|
+
//# sourceMappingURL=chunk-HI24KAEY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agents/bootstrap.ts"],"sourcesContent":["import { readFileSync, existsSync, unlinkSync, readdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Bootstrap Identity Files ---\n//\n// Support for structured workspace identity files (inspired by OpenClaw).\n// These files live in the agent's workspace and are auto-loaded into context.\n//\n// Supported files:\n// SOUL.md — default personality, tone, boundaries\n// SOUL.{profile}.md — named soul profiles (e.g. SOUL.finance.md, SOUL.legal.md)\n// IDENTITY.md — name, role, emoji, tagline (persistent)\n// USER.md — user profile, preferences (persistent)\n// AGENTS.md — operating rules, standing orders (persistent)\n// BOOTSTRAP.md — one-time first-run ritual (auto-deleted after first load)\n//\n// Soul switching: send \"/soul finance\" to swap to SOUL.finance.md mid-session.\n\nexport interface BootstrapFiles {\n soul?: string\n /** Active soul profile name (undefined = default SOUL.md) */\n soulProfile?: string\n identity?: string\n user?: string\n agents?: string\n bootstrap?: string\n}\n\nconst BOOTSTRAP_FILE_NAMES = [\n \"SOUL.md\",\n \"IDENTITY.md\",\n \"USER.md\",\n \"AGENTS.md\",\n \"BOOTSTRAP.md\",\n] as const\n\n/**\n * Load bootstrap identity files from an agent's workspace.\n * @param soulProfile — optional soul profile name (e.g. \"finance\" loads SOUL.finance.md)\n */\nexport function loadBootstrapFiles(workspace: string, soulProfile?: string): BootstrapFiles {\n const result: BootstrapFiles = {}\n\n const tryLoad = (filename: string): string | undefined => {\n const filePath = resolve(workspace, filename)\n if (!existsSync(filePath)) return undefined\n try {\n const content = readFileSync(filePath, \"utf-8\").trim()\n return content || undefined\n } catch {\n return undefined\n }\n }\n\n // Load soul: profile-specific if requested, fallback to default\n if (soulProfile) {\n const profileSoul = tryLoad(`SOUL.${soulProfile}.md`)\n if (profileSoul) {\n result.soul = profileSoul\n result.soulProfile = soulProfile\n } else {\n // Profile not found — fall back to default and note it\n result.soul = tryLoad(\"SOUL.md\")\n }\n } else {\n result.soul = tryLoad(\"SOUL.md\")\n }\n\n result.identity = tryLoad(\"IDENTITY.md\")\n result.user = tryLoad(\"USER.md\")\n result.agents = tryLoad(\"AGENTS.md\")\n result.bootstrap = tryLoad(\"BOOTSTRAP.md\")\n\n // BOOTSTRAP.md is one-time: delete after loading\n if (result.bootstrap) {\n try {\n unlinkSync(resolve(workspace, \"BOOTSTRAP.md\"))\n } catch {\n // Best-effort deletion\n }\n }\n\n return result\n}\n\n/**\n * Build context string from bootstrap files.\n * Returns empty string if no files found.\n */\nexport function buildBootstrapContext(files: BootstrapFiles): string {\n const sections: string[] = []\n\n if (files.identity) {\n sections.push(`[Identity]\\n${files.identity}`)\n }\n\n if (files.soul) {\n sections.push(`[Personality & Boundaries]\\n${files.soul}`)\n }\n\n if (files.agents) {\n sections.push(`[Operating Rules]\\n${files.agents}`)\n }\n\n if (files.user) {\n sections.push(`[User Profile]\\n${files.user}`)\n }\n\n if (files.bootstrap) {\n sections.push(`[First-Run Instructions — one time only]\\n${files.bootstrap}`)\n }\n\n if (sections.length === 0) return \"\"\n\n return sections.join(\"\\n\\n\")\n}\n\n/**\n * Check which bootstrap files exist in a workspace (for status/diagnostics).\n */\nexport function listBootstrapFiles(workspace: string): string[] {\n return BOOTSTRAP_FILE_NAMES.filter((name) =>\n existsSync(resolve(workspace, name))\n )\n}\n\n/**\n * List available soul profiles in a workspace.\n * Returns profile names (e.g. [\"finance\", \"legal\", \"creative\"]).\n */\nexport function listSoulProfiles(workspace: string): string[] {\n try {\n return readdirSync(workspace)\n .filter(f => f.startsWith(\"SOUL.\") && f.endsWith(\".md\") && f !== \"SOUL.md\")\n .map(f => f.slice(5, -3)) // \"SOUL.finance.md\" → \"finance\"\n } catch {\n return []\n }\n}\n\n/**\n * Generate a standard AGENTS.md for a new agent workspace.\n * Follows the agents.md community convention (https://agents.md/).\n * Readable by Claude Code, Codex, Cursor, Amp, Gemini CLI, etc.\n */\nexport function generateAgentsMd(agent: {\n name: string\n id: string\n role?: string\n workspace: string\n tier?: string\n}): string {\n return `# ${agent.name}\n\n> Agent ID: \\`${agent.id}\\` | Tier: ${agent.tier || \"claude-code\"}\n\n## Role\n\n${agent.role || \"AI agent managed by AgentX orchestrator.\"}\n\n## Setup\n\nThis workspace is managed by AgentX. The agent runs via:\n\\`\\`\\`bash\n# Direct execution\nagentx daemon send ${agent.id} \"your task here\"\n\n# Or via any connected channel (Telegram, WhatsApp, GitLab)\n\\`\\`\\`\n\n## Code Style\n\n- Follow existing patterns in the codebase\n- Use the language and framework conventions already present\n- Keep changes minimal and focused\n\n## Testing\n\nRun tests before committing:\n\\`\\`\\`bash\nnpm test # or the project's test command\n\\`\\`\\`\n\n## PR Guidelines\n\n- One logical change per commit\n- Reference issue numbers when applicable\n- Keep PR descriptions concise\n\n## AgentX Integration\n\nThis agent is part of an AgentX mesh. It can:\n- Receive tasks from messaging channels (Telegram, WhatsApp, Discord)\n- Respond to GitLab @mentions on issues and MRs\n- Delegate to other agents by mentioning their handles\n- Send cross-channel messages via the /send API\n- Access its wiki knowledge base for context\n`\n}\n\n/**\n * Detect a /soul command in a message.\n * Returns the requested profile name, or null if no /soul command found.\n * \"/soul finance\" → \"finance\"\n * \"/soul\" or \"/soul default\" → \"default\" (reset to SOUL.md)\n */\nexport function detectSoulSwitch(message: string): string | null {\n const match = message.match(/\\/soul\\s+(\\w+)/i)\n if (match) return match[1].toLowerCase()\n if (/\\/soul\\s*$/i.test(message)) return \"default\"\n return null\n}\n"],"mappings":"AAAA,OAAS,gBAAAA,EAAc,cAAAC,EAAY,cAAAC,EAAY,eAAAC,MAAmB,KAClE,OAAS,WAAAC,MAAe,OAuCjB,SAASC,EAAmBC,EAAmBC,EAAsC,CAC1F,IAAMC,EAAyB,CAAC,EAE1BC,EAAWC,GAAyC,CACxD,IAAMC,EAAWC,EAAQN,EAAWI,CAAQ,EAC5C,GAAKG,EAAWF,CAAQ,EACxB,GAAI,CAEF,OADgBG,EAAaH,EAAU,OAAO,EAAE,KAAK,GACnC,MACpB,MAAE,CACA,MACF,CACF,EAGA,GAAIJ,EAAa,CACf,IAAMQ,EAAcN,EAAQ,QAAQF,MAAgB,EAChDQ,GACFP,EAAO,KAAOO,EACdP,EAAO,YAAcD,GAGrBC,EAAO,KAAOC,EAAQ,SAAS,OAGjCD,EAAO,KAAOC,EAAQ,SAAS,EASjC,GANAD,EAAO,SAAWC,EAAQ,aAAa,EACvCD,EAAO,KAAOC,EAAQ,SAAS,EAC/BD,EAAO,OAASC,EAAQ,WAAW,EACnCD,EAAO,UAAYC,EAAQ,cAAc,EAGrCD,EAAO,UACT,GAAI,CACFQ,EAAWJ,EAAQN,EAAW,cAAc,CAAC,CAC/C,MAAE,CAEF,CAGF,OAAOE,CACT,CAMO,SAASS,EAAsBC,EAA+B,CACnE,IAAMC,EAAqB,CAAC,EAsB5B,OApBID,EAAM,UACRC,EAAS,KAAK;AAAA,EAAeD,EAAM,UAAU,EAG3CA,EAAM,MACRC,EAAS,KAAK;AAAA,EAA+BD,EAAM,MAAM,EAGvDA,EAAM,QACRC,EAAS,KAAK;AAAA,EAAsBD,EAAM,QAAQ,EAGhDA,EAAM,MACRC,EAAS,KAAK;AAAA,EAAmBD,EAAM,MAAM,EAG3CA,EAAM,WACRC,EAAS,KAAK;AAAA,EAA6CD,EAAM,WAAW,EAG1EC,EAAS,SAAW,EAAU,GAE3BA,EAAS,KAAK;AAAA;AAAA,CAAM,CAC7B,CA8BO,SAASC,EAAiBC,EAMtB,CACT,MAAO,KAAKA,EAAM;AAAA;AAAA,gBAEJA,EAAM,gBAAgBA,EAAM,MAAQ;AAAA;AAAA;AAAA;AAAA,EAIlDA,EAAM,MAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAOKA,EAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAiC3B,CAQO,SAASC,EAAiBC,EAAgC,CAC/D,IAAMC,EAAQD,EAAQ,MAAM,iBAAiB,EAC7C,OAAIC,EAAcA,EAAM,CAAC,EAAE,YAAY,EACnC,cAAc,KAAKD,CAAO,EAAU,UACjC,IACT","names":["readFileSync","existsSync","unlinkSync","readdirSync","resolve","loadBootstrapFiles","workspace","soulProfile","result","tryLoad","filename","filePath","resolve","existsSync","readFileSync","profileSoul","unlinkSync","buildBootstrapContext","files","sections","generateAgentsMd","agent","detectSoulSwitch","message","match"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{existsSync as S,promises as v}from"fs";import h from"path";import x from"fast-glob";import{z as l}from"zod";var k=l.object({name:l.string(),description:l.string(),version:l.string().optional(),author:l.string().optional(),tags:l.array(l.string()).optional(),globs:l.array(l.string()).optional(),triggers:l.array(l.object({pattern:l.string(),description:l.string().optional()})).optional(),autoInject:l.boolean().optional()});var L=[".skills",".claude/skills","skills"],d="SKILL.md";async function T(a){let n=[];for(let r of L){let o=h.resolve(a,r);if(!S(o))continue;let c=await x.glob(`**/${d}`,{cwd:o,deep:3});for(let s of c){let t=h.resolve(o,s),e=await w(t);e&&(e.source="local",e.path=t,n.push(e))}}let m=h.resolve(a,d);if(S(m)){let r=await w(m);r&&(r.source="local",r.path=m,n.push(r))}return n}async function w(a){try{let n=await v.readFile(a,"utf8");return $(n)}catch{return null}}function $(a){try{let n=a.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!n)return{frontmatter:{name:"unnamed",description:"No description"},instructions:a.trim(),source:"local"};let[,m,r]=n,o=F(m);return{frontmatter:k.parse(o),instructions:r.trim(),source:"local"}}catch{return null}}function F(a){let n={},m=a.split(`
|
|
2
|
+
`),r="",o=!1,c=[];for(let s of m){let t=s.trim();if(!t)continue;if(t.startsWith("- ")&&o){c.push(t.slice(2).trim().replace(/^["']|["']$/g,""));continue}o&&r&&(n[r]=c,o=!1,c=[]);let e=t.match(/^(\w+)\s*:\s*(.*)$/);if(e){let[,f,p]=e;r=f,p.trim()===""?(o=!0,c=[]):n[f]=p.trim().replace(/^["']|["']$/g,"")}}return o&&r&&(n[r]=c),n}function M(a,n,m){let r=[],o=n.toLowerCase(),c=new Set(o.split(/\s+/));for(let s of a){let t=0,e="";if(s.frontmatter.triggers)for(let i of s.frontmatter.triggers)try{new RegExp(i.pattern,"i").test(n)&&(t=Math.max(t,.9),e=`Trigger match: ${i.description||i.pattern}`)}catch{o.includes(i.pattern.toLowerCase())&&(t=Math.max(t,.7),e=`Keyword match: ${i.pattern}`)}if(s.frontmatter.tags){let i=s.frontmatter.tags.filter(g=>c.has(g.toLowerCase())||o.includes(g.toLowerCase()));if(i.length){let g=Math.min(i.length*.3,.8);g>t&&(t=g,e=`Tag match: ${i.join(", ")}`)}}let f=s.frontmatter.name.toLowerCase().split(/[-_\s]+/),p=s.frontmatter.description.toLowerCase().split(/\s+/),y=new Set([...f,...p]),u=[...c].filter(i=>y.has(i)&&i.length>3);if(u.length>0){let i=Math.min(u.length*.2,.6);i>t&&(t=i,e=`Content match: ${u.join(", ")}`)}t>.1&&r.push({skill:s,relevance:t,matchReason:e})}return r.sort((s,t)=>t.relevance-s.relevance)}function W(a,n,m=2e3){let r=a.filter(e=>e.frontmatter.autoInject);if(r.length===0)return"";let o=M(r,n);if(o.length===0)return"";let c=m*4,s=["[Auto-Injected Skills \u2014 matched to current task]"],t=s[0].length;for(let e of o){let f=`
|
|
3
|
+
## ${e.skill.frontmatter.name} (${Math.round(e.relevance*100)}% match)
|
|
4
|
+
${e.skill.instructions}`;if(t+f.length>c)break;s.push(f),t+=f.length}return s.length===1?"":s.join(`
|
|
5
|
+
`)}export{T as a,w as b,$ as c,M as d,W as e};
|
|
6
|
+
//# sourceMappingURL=chunk-MCJUAE6V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent/skills/loader.ts","../src/agent/skills/types.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport fg from \"fast-glob\"\nimport { logger } from \"@/utils/logger\"\nimport type { Skill, SkillFrontmatter, SkillMatch } from \"./types\"\nimport { skillFrontmatterSchema } from \"./types\"\n\n// --- Load skills from local files and remote packages ---\n\nconst SKILL_DIRS = [\".skills\", \".claude/skills\", \"skills\"]\nconst SKILL_FILE = \"SKILL.md\"\n\nexport async function loadLocalSkills(cwd: string): Promise<Skill[]> {\n const skills: Skill[] = []\n\n for (const dir of SKILL_DIRS) {\n const skillDir = path.resolve(cwd, dir)\n if (!existsSync(skillDir)) continue\n\n const skillFiles = await fg.glob(`**/${SKILL_FILE}`, {\n cwd: skillDir,\n deep: 3,\n })\n\n for (const file of skillFiles) {\n const fullPath = path.resolve(skillDir, file)\n const skill = await parseSkillFile(fullPath)\n if (skill) {\n skill.source = \"local\"\n skill.path = fullPath\n skills.push(skill)\n }\n }\n }\n\n // Also check for a single SKILL.md at project root\n const rootSkill = path.resolve(cwd, SKILL_FILE)\n if (existsSync(rootSkill)) {\n const skill = await parseSkillFile(rootSkill)\n if (skill) {\n skill.source = \"local\"\n skill.path = rootSkill\n skills.push(skill)\n }\n }\n\n return skills\n}\n\nexport async function parseSkillFile(filePath: string): Promise<Skill | null> {\n try {\n const content = await fs.readFile(filePath, \"utf8\")\n return parseSkillContent(content)\n } catch {\n return null\n }\n}\n\nexport function parseSkillContent(content: string): Skill | null {\n try {\n // Parse YAML frontmatter\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n\n if (!frontmatterMatch) {\n // No frontmatter - treat entire content as instructions with minimal metadata\n return {\n frontmatter: { name: \"unnamed\", description: \"No description\" },\n instructions: content.trim(),\n source: \"local\",\n }\n }\n\n const [, frontmatterRaw, instructions] = frontmatterMatch\n const frontmatter = parseYamlFrontmatter(frontmatterRaw)\n\n const validated = skillFrontmatterSchema.parse(frontmatter)\n\n return {\n frontmatter: validated,\n instructions: instructions.trim(),\n source: \"local\",\n }\n } catch {\n return null\n }\n}\n\nfunction parseYamlFrontmatter(raw: string): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n const lines = raw.split(\"\\n\")\n\n let currentKey = \"\"\n let inArray = false\n let arrayValues: string[] = []\n\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n\n // Array item\n if (trimmed.startsWith(\"- \") && inArray) {\n arrayValues.push(trimmed.slice(2).trim().replace(/^[\"']|[\"']$/g, \"\"))\n continue\n }\n\n // Save previous array if we were in one\n if (inArray && currentKey) {\n result[currentKey] = arrayValues\n inArray = false\n arrayValues = []\n }\n\n // Key-value pair\n const kvMatch = trimmed.match(/^(\\w+)\\s*:\\s*(.*)$/)\n if (kvMatch) {\n const [, key, value] = kvMatch\n currentKey = key\n\n if (value.trim() === \"\") {\n // Could be start of an array or nested object\n inArray = true\n arrayValues = []\n } else {\n // Simple value\n result[key] = value.trim().replace(/^[\"']|[\"']$/g, \"\")\n }\n }\n }\n\n // Save last array if any\n if (inArray && currentKey) {\n result[currentKey] = arrayValues\n }\n\n return result\n}\n\nexport function matchSkillsToTask(\n skills: Skill[],\n taskDescription: string,\n outputType?: string\n): SkillMatch[] {\n const matches: SkillMatch[] = []\n const taskLower = taskDescription.toLowerCase()\n const taskWords = new Set(taskLower.split(/\\s+/))\n\n for (const skill of skills) {\n let relevance = 0\n let matchReason = \"\"\n\n // Check trigger patterns\n if (skill.frontmatter.triggers) {\n for (const trigger of skill.frontmatter.triggers) {\n try {\n const regex = new RegExp(trigger.pattern, \"i\")\n if (regex.test(taskDescription)) {\n relevance = Math.max(relevance, 0.9)\n matchReason = `Trigger match: ${trigger.description || trigger.pattern}`\n }\n } catch {\n // Invalid regex - try simple string match\n if (taskLower.includes(trigger.pattern.toLowerCase())) {\n relevance = Math.max(relevance, 0.7)\n matchReason = `Keyword match: ${trigger.pattern}`\n }\n }\n }\n }\n\n // Check tags overlap\n if (skill.frontmatter.tags) {\n const tagOverlap = skill.frontmatter.tags.filter(\n (tag) => taskWords.has(tag.toLowerCase()) || taskLower.includes(tag.toLowerCase())\n )\n if (tagOverlap.length) {\n const tagRelevance = Math.min(tagOverlap.length * 0.3, 0.8)\n if (tagRelevance > relevance) {\n relevance = tagRelevance\n matchReason = `Tag match: ${tagOverlap.join(\", \")}`\n }\n }\n }\n\n // Check name/description overlap\n const nameWords = skill.frontmatter.name.toLowerCase().split(/[-_\\s]+/)\n const descWords = skill.frontmatter.description.toLowerCase().split(/\\s+/)\n const allSkillWords = new Set([...nameWords, ...descWords])\n\n const overlap = [...taskWords].filter((w) => allSkillWords.has(w) && w.length > 3)\n if (overlap.length > 0) {\n const wordRelevance = Math.min(overlap.length * 0.2, 0.6)\n if (wordRelevance > relevance) {\n relevance = wordRelevance\n matchReason = `Content match: ${overlap.join(\", \")}`\n }\n }\n\n if (relevance > 0.1) {\n matches.push({ skill, relevance, matchReason })\n }\n }\n\n return matches.sort((a, b) => b.relevance - a.relevance)\n}\n\n/**\n * Find auto-injectable skills that match the current message.\n * Only skills with `autoInject: true` in frontmatter participate.\n * Returns skill content to inject into context, capped at maxTokens.\n */\nexport function getAutoInjectSkills(\n skills: Skill[],\n message: string,\n maxTokens: number = 2000,\n): string {\n const autoSkills = skills.filter(s => s.frontmatter.autoInject)\n if (autoSkills.length === 0) return \"\"\n\n const matches = matchSkillsToTask(autoSkills, message)\n if (matches.length === 0) return \"\"\n\n // Inject matched skills within token budget\n const maxChars = maxTokens * 4\n const sections: string[] = [\"[Auto-Injected Skills — matched to current task]\"]\n let totalChars = sections[0].length\n\n for (const match of matches) {\n const content = `\\n## ${match.skill.frontmatter.name} (${Math.round(match.relevance * 100)}% match)\\n${match.skill.instructions}`\n if (totalChars + content.length > maxChars) break\n sections.push(content)\n totalChars += content.length\n }\n\n if (sections.length === 1) return \"\" // no skills fit in budget\n return sections.join(\"\\n\")\n}\n","import { z } from \"zod\"\n\n// --- Skill types aligned with skills.sh / Agent Skills spec ---\n\nexport const skillFrontmatterSchema = z.object({\n name: z.string(),\n description: z.string(),\n version: z.string().optional(),\n author: z.string().optional(),\n tags: z.array(z.string()).optional(),\n globs: z.array(z.string()).optional(),\n // When to automatically apply this skill\n triggers: z\n .array(\n z.object({\n pattern: z.string(), // regex or keyword pattern\n description: z.string().optional(),\n })\n )\n .optional(),\n /** If true, skill is auto-injected into context when triggers match (per-turn) */\n autoInject: z.boolean().optional(),\n})\n\nexport type SkillFrontmatter = z.infer<typeof skillFrontmatterSchema>\n\nexport interface Skill {\n frontmatter: SkillFrontmatter\n instructions: string // Markdown body\n source: \"local\" | \"remote\" | \"generated\"\n path?: string // Local file path\n packageId?: string // e.g., \"intellectronica/agent-skills\"\n}\n\nexport interface SkillMatch {\n skill: Skill\n relevance: number // 0-1 how relevant to current task\n matchReason: string\n}\n\nexport interface SkillPackage {\n owner: string\n repo: string\n skills: Skill[]\n}\n"],"mappings":"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","names":["existsSync","fs","path","fg","z","skillFrontmatterSchema","SKILL_DIRS","SKILL_FILE","loadLocalSkills","cwd","skills","dir","skillDir","path","existsSync","skillFiles","fg","file","fullPath","skill","parseSkillFile","rootSkill","filePath","content","fs","parseSkillContent","frontmatterMatch","frontmatterRaw","instructions","frontmatter","parseYamlFrontmatter","skillFrontmatterSchema","raw","result","lines","currentKey","inArray","arrayValues","line","trimmed","kvMatch","key","value","matchSkillsToTask","taskDescription","outputType","matches","taskLower","taskWords","relevance","matchReason","trigger","tagOverlap","tag","tagRelevance","nameWords","descWords","allSkillWords","overlap","w","wordRelevance","a","b","getAutoInjectSkills","message","maxTokens","autoSkills","s","maxChars","sections","totalChars","match"]}
|
|
@@ -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"]}
|