oh-my-agent 4.30.0 → 4.30.2
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 +14 -14
- package/bin/cli.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,6 @@ Pick a preset and you're ready:
|
|
|
40
40
|
| **oma-architecture** | Architectural tradeoffs, boundaries, ADR/ATAM/CBAM-aware analysis |
|
|
41
41
|
| **oma-backend** | APIs in Python, Node.js, or Rust |
|
|
42
42
|
| **oma-brainstorm** | Explores ideas before you commit to building |
|
|
43
|
-
| **oma-scm** | SCM (software configuration management) — branching, merges, worktrees, baselines; Conventional Commits |
|
|
44
43
|
| **oma-db** | Schema design, migrations, indexing, vector DB |
|
|
45
44
|
| **oma-debug** | Root cause analysis, fixes, regression tests |
|
|
46
45
|
| **oma-design** | Design systems, tokens, accessibility, responsive |
|
|
@@ -51,7 +50,8 @@ Pick a preset and you're ready:
|
|
|
51
50
|
| **oma-pdf** | PDF to Markdown conversion |
|
|
52
51
|
| **oma-pm** | Plans tasks, breaks down requirements, defines API contracts |
|
|
53
52
|
| **oma-qa** | OWASP security, performance, accessibility review |
|
|
54
|
-
| **oma-
|
|
53
|
+
| **oma-scm** | SCM (software configuration management) — branching, merges, worktrees, baselines; Conventional Commits |
|
|
54
|
+
| **oma-tf-infra** | Multi-cloud Terraform IaC (Infrastructure as Code) |
|
|
55
55
|
| **oma-translator** | Natural multilingual translation |
|
|
56
56
|
|
|
57
57
|
## How It Works
|
|
@@ -70,18 +70,18 @@ You: "Build a TODO app with user authentication"
|
|
|
70
70
|
|
|
71
71
|
Or use slash commands for structured workflows:
|
|
72
72
|
|
|
73
|
-
| Command | What It Does |
|
|
74
|
-
|
|
75
|
-
| `/
|
|
76
|
-
| `/
|
|
77
|
-
| `/
|
|
78
|
-
| `/
|
|
79
|
-
| `/
|
|
80
|
-
| `/
|
|
81
|
-
| `/
|
|
82
|
-
| `/
|
|
83
|
-
| `/
|
|
84
|
-
| `/scm` | SCM + Git workflow and Conventional Commit support |
|
|
73
|
+
| Step | Command | What It Does |
|
|
74
|
+
|------|---------|-------------|
|
|
75
|
+
| 1 | `/brainstorm` | Free-form ideation |
|
|
76
|
+
| 2 | `/architecture` | Software architecture review, tradeoffs, ADR/ATAM/CBAM-style analysis |
|
|
77
|
+
| 3 | `/design` | 7-phase design system workflow |
|
|
78
|
+
| 4 | `/plan` | PM breaks down your feature into tasks |
|
|
79
|
+
| 5 | `/work` | Step-by-step multi-agent execution |
|
|
80
|
+
| 5 | `/orchestrate` | Automated parallel agent spawning |
|
|
81
|
+
| 5 | `/ultrawork` | 5-phase quality workflow with 11 review gates |
|
|
82
|
+
| 6 | `/review` | Security + performance + accessibility audit |
|
|
83
|
+
| 7 | `/debug` | Structured root-cause debugging |
|
|
84
|
+
| 8 | `/scm` | SCM + Git workflow and Conventional Commit support |
|
|
85
85
|
|
|
86
86
|
**Auto-detection**: You don't even need slash commands — keywords like "architecture", "plan", "review", and "debug" in your message (in 11 languages!) auto-activate the right workflow.
|
|
87
87
|
|
package/bin/cli.js
CHANGED
|
@@ -357,7 +357,7 @@ languages:
|
|
|
357
357
|
- python
|
|
358
358
|
- typescript
|
|
359
359
|
- dart
|
|
360
|
-
- terraform${J.slice(G)}`;Vw(z,N),console.error(`[Bridge] Fixed ${z}`)}}}}catch(I){console.error(`[Bridge] Warning: Failed to validate Serena configs: ${I instanceof Error?I.message:I}`)}}async function JO($){zO();let I=$||Pw,U=new URL(I),u=U.protocol==="https:"?Zw:Ww,g=null,z=!1,J=null,G=!1;async function N(){let M=U.hostname==="localhost"?[I,I.replace("localhost","127.0.0.1")]:[I];for(let b of M)if(await new Promise((C)=>{let i=u.get(b,(w)=>{C(!0),i.destroy()});i.setTimeout(Sw,()=>{i.destroy(),C(!1)}),i.on("error",()=>{C(!1)}),i.end()}))return!0;return!1}async function X(){let M=U.port||"12341",b=U.hostname||"0.0.0.0";console.error(`Starting Serena server on ${b}:${M}...`),g=bw("uvx",["--from","git+https://github.com/oraios/serena","serena-mcp-server","--transport","streamable-http","--host",b,"--port",M,"--context","ide","--open-web-dashboard","false"],{stdio:"pipe",detached:!1});let C=g;if(g.stderr)g.stderr.on("data",(w)=>{process.stderr.write(`[Serena] ${w}`)});if(g.stdout)g.stdout.on("data",()=>{});C.on("error",(w)=>{console.error("Failed to start Serena server:",w),process.exit(1)}),C.on("exit",(w,J$)=>{if(console.error(`Serena server exited with code ${String(w)} signal ${String(J$)}`),!z)process.exit(typeof w==="number"?w:1)}),console.error("Waiting for Serena to be ready...");let i=Math.max(1,Math.ceil(jw/gO));for(let w=0;w<i;w++){if(await N()){console.error("Serena server is ready!");return}await new Promise((J$)=>setTimeout(J$,gO))}console.error("Timed out waiting for Serena server to start."),process.exit(1)}function q(M,b){let E=new URL(I),C={"Content-Type":"application/json",Accept:"application/json, text/event-stream","Content-Length":String(Buffer.byteLength(M))};if(J)C["Mcp-Session-Id"]=J;let i={hostname:E.hostname,port:E.port,path:E.pathname,method:"POST",headers:C},w=u.request(i,b);w.on("error",(J$)=>{console.error("POST error:",J$.message)}),w.write(M),w.end()}function Q(M,b){let E="";M.on("data",(C)=>{E+=C.toString(),E=E.replace(/\r\n/g,`
|
|
360
|
+
- terraform${J.slice(G)}`;Vw(z,N),console.error(`[Bridge] Fixed ${z}`)}}}}catch(I){console.error(`[Bridge] Warning: Failed to validate Serena configs: ${I instanceof Error?I.message:I}`)}}async function JO($){zO();let I=$||Pw,U=new URL(I),u=U.protocol==="https:"?Zw:Ww,g=null,z=!1,J=null,G=!1;async function N(){let M=U.hostname==="localhost"?[I,I.replace("localhost","127.0.0.1")]:[I];for(let b of M)if(await new Promise((C)=>{let i=u.get(b,(w)=>{C(!0),i.destroy()});i.setTimeout(Sw,()=>{i.destroy(),C(!1)}),i.on("error",()=>{C(!1)}),i.end()}))return!0;return!1}async function X(){let M=U.port||"12341",b=U.hostname||"0.0.0.0";console.error(`Starting Serena server on ${b}:${M}...`),g=bw("uvx",["--from","git+https://github.com/oraios/serena","serena","start-mcp-server","--transport","streamable-http","--host",b,"--port",M,"--context","ide","--open-web-dashboard","false"],{stdio:"pipe",detached:!1});let C=g;if(g.stderr)g.stderr.on("data",(w)=>{process.stderr.write(`[Serena] ${w}`)});if(g.stdout)g.stdout.on("data",()=>{});C.on("error",(w)=>{console.error("Failed to start Serena server:",w),process.exit(1)}),C.on("exit",(w,J$)=>{if(console.error(`Serena server exited with code ${String(w)} signal ${String(J$)}`),!z)process.exit(typeof w==="number"?w:1)}),console.error("Waiting for Serena to be ready...");let i=Math.max(1,Math.ceil(jw/gO));for(let w=0;w<i;w++){if(await N()){console.error("Serena server is ready!");return}await new Promise((J$)=>setTimeout(J$,gO))}console.error("Timed out waiting for Serena server to start."),process.exit(1)}function q(M,b){let E=new URL(I),C={"Content-Type":"application/json",Accept:"application/json, text/event-stream","Content-Length":String(Buffer.byteLength(M))};if(J)C["Mcp-Session-Id"]=J;let i={hostname:E.hostname,port:E.port,path:E.pathname,method:"POST",headers:C},w=u.request(i,b);w.on("error",(J$)=>{console.error("POST error:",J$.message)}),w.write(M),w.end()}function Q(M,b){let E="";M.on("data",(C)=>{E+=C.toString(),E=E.replace(/\r\n/g,`
|
|
361
361
|
`);let i=E.split(`
|
|
362
362
|
|
|
363
363
|
`);E=i.pop()||"";for(let w of i){let J$=w.split(`
|
|
@@ -518,7 +518,7 @@ source: migrated
|
|
|
518
518
|
</html>`;function TM(){let $=yx();if(!ix($))lx($,{recursive:!0});let I=nx((z,J)=>{if(z.url==="/api/state")J.writeHead(200,{"Content-Type":"application/json"}),J.end(JSON.stringify(UX($)));else J.writeHead(200,{"Content-Type":"text/html"}),J.end(ox)}),U=new uX.default({server:I}),_=null;function u(z,J){if(_)clearTimeout(_);_=setTimeout(()=>{let G=JSON.stringify({type:"update",event:z,file:J,data:UX($)});U.clients.forEach((N)=>{if(N.readyState===IX.default.OPEN)N.send(G)})},100)}let g=Fg($,{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:50}});g.on("all",(z,J)=>u(z,YM(J))),U.on("connection",(z)=>{z.send(JSON.stringify({type:"full",data:UX($)})),z.on("error",()=>z.terminate())}),process.on("SIGINT",()=>{console.log(`
|
|
519
519
|
Shutting down...`),g.close(),U.clients.forEach((z)=>{z.terminate()}),U.close(()=>I.close(()=>process.exit(0))),setTimeout(()=>process.exit(1),3000).unref()}),process.on("SIGTERM",()=>process.emit("SIGINT")),I.listen(OM,()=>{console.log(VI.magenta(`
|
|
520
520
|
\uD83D\uDEF8 Serena Memory Dashboard`)),console.log(VI.white(` http://localhost:${OM}`)),console.log(VI.dim(` Watching: ${$}
|
|
521
|
-
`))})}var MM={name:"oh-my-agent",version:"4.30.
|
|
521
|
+
`))})}var MM={name:"oh-my-agent",version:"4.30.2",description:"Portable multi-agent harness for .agents-based skills and workflows across Antigravity, Claude Code, Codex, OpenCode, and more",type:"module",bin:{"oh-my-agent":"./bin/cli.js",oma:"./bin/cli.js"},files:["bin"],keywords:["oh-my-agent","antigravity",".agents","agent","skills","agent-skills","multi-agent","orchestrator","claude","claude-code","codex","opencode","copilot","cursor","chatgpt","pm","frontend","backend","mobile","qa","debug","terraform","database","workflow","bug-fixing","gemini"],author:"our.first.fluke <our.first.fluke@gmail.com>",contributors:["gracefullight <gracefullight.dev@gmail.com>","gahyun-git <go4it.gh@gmail.com>"],license:"MIT",funding:[{type:"github",url:"https://github.com/sponsors/first-fluke"},{type:"buymeacoffee",url:"https://buymeacoffee.com/firstfluke"}],scripts:{"sync:readme":"node ./scripts/sync-readme.mjs",build:"bun run sync:readme && bun build cli.ts --outfile bin/cli.js --target node --minify",dev:"bun run cli.ts",lint:"biome check --write --unsafe .",test:"vitest run",prepublishOnly:"bun run build"},dependencies:{"@clack/prompts":"^1.1.0",chokidar:"^5.0.0",commander:"^14.0.3","p-map":"^7.0.4",picocolors:"^1.1.1",ws:"^8.18.0",yaml:"^2.8.2",zod:"^4.3.6"},devDependencies:{"@biomejs/biome":"2.4.5","@types/bun":"^1.3.10","@types/ws":"^8.18.1",vitest:"^4.0.18"},peerDependencies:{typescript:"^6"},repository:{type:"git",url:"https://github.com/first-fluke/oh-my-agent"},antigravity:{skillsPath:".agents/skills",skills:["oma-architecture","oma-brainstorm","oma-coordination","oma-pm","oma-frontend","oma-backend","oma-db","oma-mobile","oma-qa","oma-debug","oma-orchestrator","oma-dev-workflow","oma-tf-infra","oma-scm","oma-pdf"]}};import{existsSync as tx,mkdirSync as ax,readdirSync as zu,readFileSync as ex,statSync as JX}from"node:fs";import{basename as sx,join as Z4}from"node:path";var y$=H$(Z$(),1),$i="●",Ii="✓",ui="✗",Ui="○",_i="◌";function gi(){if(process.env.MEMORIES_DIR)return process.env.MEMORIES_DIR;let $=process.argv[3];if($)return Z4($,".serena","memories");return Z4(process.cwd(),".serena","memories")}function Ju($){try{return ex($,"utf-8")}catch{return""}}function zi($){try{let I=zu($);if(I.includes("orchestrator-session.md"))return Z4($,"orchestrator-session.md");let U=I.filter((_)=>/^session-.*\.md$/.test(_)).map((_)=>({name:_,mtime:JX(Z4($,_)).mtimeMs})).sort((_,u)=>u.mtime-_.mtime);if(U.length>0&&U[0])return Z4($,U[0].name)}catch{}return null}function Ji($){let I=zi($);if(!I)return{id:"N/A",status:"UNKNOWN"};let U=Ju(I);if(!U)return{id:"N/A",status:"UNKNOWN"};let _=(U.match(/session-id:\s*(.+)/i)||[])[1]||(U.match(/# Session:\s*(.+)/i)||[])[1]||U.match(/(session-\d{8}-\d{6})/)?.[1]||sx(I,".md"),u="UNKNOWN";if(/IN PROGRESS|RUNNING|## Active|\[IN PROGRESS\]/i.test(U))u="RUNNING";else if(/COMPLETED|DONE|## Completed|\[COMPLETED\]/i.test(U))u="COMPLETED";else if(/FAILED|ERROR|## Failed|\[FAILED\]/i.test(U))u="FAILED";else if(/Step \d+:.*\[/i.test(U))u="RUNNING";return{id:(_||"N/A").trim(),status:u}}function Gi($){let I=Ju(Z4($,"task-board.md"));if(!I)return[];let U=[],_=I.split(`
|
|
522
522
|
`);for(let u of _){if(!u.startsWith("|")||/^\|\s*-+/.test(u))continue;let g=u.split("|").map((N)=>N.trim()).filter(Boolean),z=g[0];if(g.length<2||!z||/^agent$/i.test(z))continue;let J=g[1],G=g[2];U.push({agent:z,status:J||"pending",task:G||""})}return U}function zX($,I){try{let U=zu($).filter((g)=>g.startsWith(`progress-${I}`)&&g.endsWith(".md")).sort().reverse();if(U.length===0||!U[0])return null;let u=Ju(Z4($,U[0])).match(/turn[:\s]*(\d+)/i);return u?.[1]?parseInt(u[1],10):null}catch{return null}}function Ni($){try{let I=zu($).filter((_)=>_.endsWith(".md")&&_!==".gitkeep").map((_)=>({name:_,mtime:JX(Z4($,_)).mtimeMs})).sort((_,u)=>u.mtime-_.mtime).slice(0,5),U=[];for(let _ of I){let u=_.name.replace(/^(progress|result|session|debug|task)-?/,"").replace(/[-_]agent/,"").replace(/[-_]completion/,"").replace(/\.md$/,"").replace(/[-_]/g," ").trim()||_.name.replace(/\.md$/,""),z=Ju(Z4($,_.name)).split(`
|
|
523
523
|
`).map((G)=>G.trim()).filter((G)=>G&&!G.startsWith("---")&&G.length>3),J="";for(let G=z.length-1;G>=0;G--){let N=z[G];if(!N)continue;if(/^\*\*|^#+|^-|^\d+\.|Status|Result|Action|Step/i.test(N)){if(J=N.replace(/^[#*\-\d.]+\s*/,"").replace(/\*\*/g,"").trim(),J.length>5)break}}if(J.length>52)J=`${J.substring(0,49)}...`;if(J)U.push({agent:u,message:J})}return U}catch{return[]}}function qi($){let I=[],U=new Set;try{let _=zu($).filter((u)=>u.endsWith(".md")&&u!==".gitkeep").map((u)=>({name:u,mtime:JX(Z4($,u)).mtimeMs})).sort((u,g)=>g.mtime-u.mtime);for(let u of _){let g=Ju(Z4($,u.name)),z=g.match(/\*\*Agent\*\*:\s*(.+)/i)||g.match(/Agent:\s*(.+)/i)||g.match(/^#+\s*(.+?)\s*Agent/im),J=null;if(z?.[1])J=z[1].trim();else if(/_agent|agent_|-agent/i.test(u.name))J=u.name.replace(/\.md$/,"").replace(/[-_]completion|[-_]progress|[-_]result/gi,"").replace(/[-_]/g," ").trim();if(J&&!U.has(J.toLowerCase())){U.add(J.toLowerCase());let G="unknown";if(/\[COMPLETED\]|## Completed|## Results/i.test(g))G="completed";else if(/\[IN PROGRESS\]|## Progress|IN PROGRESS/i.test(g))G="running";else if(/\[FAILED\]|## Failed|ERROR/i.test(g))G="failed";let N=g.match(/## Task\s*\n+(.+)/i)||g.match(/\*\*Task\*\*:\s*(.+)/i),X=N?.[1]?N[1].trim().substring(0,20):"";I.push({agent:J,status:G,task:X,turn:zX($,J)})}}}catch{}return I}function Xi($){let I=$.toLowerCase();if(["running","active","in_progress","in-progress"].includes(I))return`${y$.default.green($i)} running`;else if(["completed","done","finished"].includes(I))return`${y$.default.cyan(Ii)} completed`;else if(["failed","error"].includes(I))return`${y$.default.red(ui)} failed`;else if(["blocked","waiting"].includes(I))return`${y$.default.yellow(Ui)} blocked`;return`${y$.default.dim(_i)} pending`}function DM($){console.clear();let I=Ji($),_=Gi($).map((v)=>({...v,turn:zX($,v.agent)}));if(_.length===0)_=qi($);if(_.length===0)try{let v=zu($).filter((Y)=>Y.startsWith("progress-")&&Y.endsWith(".md"));for(let Y of v){let T=Y.replace(/^progress-/,"").replace(/\.md$/,"");_.push({agent:T,status:"running",task:"",turn:zX($,T)})}}catch{}let u=56,g="═".repeat(u),z=(v)=>" ".repeat(Math.max(0,v)),J=(v)=>y$.default.magenta(v),G=(v)=>y$.default.bold(v),N=(v)=>y$.default.dim(v),X=y$.default.yellow;if(I.status==="RUNNING")X=y$.default.green;else if(I.status==="COMPLETED")X=y$.default.cyan;else if(I.status==="FAILED")X=y$.default.red;console.log(`${J(`╔${g}╗`)}`),console.log(`${J("║")} ${G(J("Serena Memory Dashboard"))}${z(u-25)}${J("║")}`);let q=`Session: ${G(I.id.padEnd(20))} [${X(I.status)}]`;if(console.log(`${J("║")} ${q}${z(u-4-q.length-9)}${J("║")}`),console.log(`${J(`╠${g}╣`)}`),console.log(`${J("║")} ${G(`${"Agent".padEnd(12)} ${"Status".padEnd(12)} ${"Turn".padEnd(6)} ${"Task".padEnd(20)}`)} ${J("║")}`),console.log(`${J("║")} ${N(`${"──────────".padEnd(12)} ${"──────────".padEnd(12)} ${"────".padEnd(6)} ${"──────────────────".padEnd(20)}`)} ${J("║")}`),_.length===0)console.log(`${J("║")} ${N(`No agents detected yet${z(32)}`)}${J("║")}`);else for(let v of _){let Y=Xi(v.status),T=v.turn!=null?String(v.turn):"-",K=v.task.substring(0,20);console.log(`${J("║")} ${v.agent.padEnd(12)} ${Y.padEnd(22)} ${T.padEnd(6)} ${K.padEnd(20)}${J("║")}`)}console.log(`${J(`╠${g}╣`)}`),console.log(`${J("║")} ${G("Latest Activity:")}${z(u-18)}${J("║")}`);let Q=Ni($);if(Q.length===0)console.log(`${J("║")} ${N(`No activity yet${z(38)}`)}${J("║")}`);else for(let v of Q){let Y=`[${v.agent}] ${v.message}`;console.log(`${J("║")} ${N(Y.substring(0,52).padEnd(52))}${J("║")}`)}console.log(`${J(`╠${g}╣`)}`);let O=`Updated: ${new Date().toLocaleString("en-US",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})} | Ctrl+C to exit`;console.log(`${J("║")} ${N(O)}${z(u-4-O.length)}${J("║")}`),console.log(`${J(`╚${g}╝`)}`)}async function LM(){let $=gi();if(!tx($))ax($,{recursive:!0}),console.log(y$.default.yellow(`Created ${$} — waiting for memory files...`));console.log(y$.default.magenta(`
|
|
524
524
|
\uD83D\uDEF8 Serena Terminal Dashboard`)),console.log(y$.default.dim(` Watching: ${$}
|
package/package.json
CHANGED