oh-my-agent 2.3.0 → 2.4.1

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.
Files changed (3) hide show
  1. package/README.md +48 -7
  2. package/bin/cli.js +1 -1
  3. package/package.json +13 -3
package/README.md CHANGED
@@ -1,16 +1,19 @@
1
- # oh-my-agent: Multi-Agent Harness
1
+ # oh-my-agent: Portable Multi-Agent Harness
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/oh-my-agent?color=cb3837&logo=npm)](https://www.npmjs.com/package/oh-my-agent) [![npm downloads](https://img.shields.io/npm/dm/oh-my-agent?color=cb3837&logo=npm)](https://www.npmjs.com/package/oh-my-agent) [![GitHub stars](https://img.shields.io/github/stars/first-fluke/oh-my-agent?style=flat&logo=github)](https://github.com/first-fluke/oh-my-agent) [![License](https://img.shields.io/github/license/first-fluke/oh-my-agent)](https://github.com/first-fluke/oh-my-agent/blob/main/LICENSE) [![Last Updated](https://img.shields.io/github/last-commit/first-fluke/oh-my-agent?label=updated&logo=git)](https://github.com/first-fluke/oh-my-agent/commits/main)
4
4
 
5
- [한국어](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ko.md) | [中文](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.zh.md) | [Português](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.pt.md) | [日本語](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ja.md) | [Français](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.fr.md) | [Español](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.es.md) | [Nederlands](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.nl.md) | [Polski](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.pl.md) | [Українська](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.uk.md) | [Русский](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ru.md) | [Deutsch](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.de.md)
5
+ [한국어](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ko.md) | [中文](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.zh.md) | [Português](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.pt.md) | [日本語](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ja.md) | [Français](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.fr.md) | [Español](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.es.md) | [Nederlands](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.nl.md) | [Polski](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.pl.md) | [Русский](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.ru.md) | [Deutsch](https://github.com/first-fluke/oh-my-agent/blob/main/docs/README.de.md)
6
6
 
7
- The Ultimate Agent Orchestrator for agentic coding.
7
+ The portable, role-based agent harness for serious AI-assisted engineering.
8
8
 
9
- Orchestrate 9 specialized domain agents (PM, Frontend, Backend, Mobile, QA, Debug, Brainstorm, DevWorkflow, Terraform) via **Serena Memory**. Features parallel CLI execution, real-time observability dashboards, and zero-config progressive skill loading. The batteries-included solution for agentic coding.
9
+ Orchestrate 10 specialized domain agents (PM, Frontend, Backend, DB, Mobile, QA, Debug, Brainstorm, DevWorkflow, Terraform) via **Serena Memory**. `oh-my-agent` uses `.agents/` as the source of truth for portable skills and workflows, then projects compatibility to other AI IDEs and CLIs. It combines role-based agents, explicit workflows, real-time observability, and standards-aware guidance for teams that want less AI slop and more disciplined execution.
10
10
 
11
11
  ## Table of Contents
12
12
 
13
13
  - [Architecture](#architecture)
14
+ - [Why Different](#why-different)
15
+ - [Compatibility](#compatibility)
16
+ - [The `.agents` Spec](#the-agents-spec)
14
17
  - [What Is This?](#what-is-this)
15
18
  - [Quick Start](#quick-start)
16
19
  - [Sponsors](#sponsors)
@@ -42,6 +45,7 @@ flowchart TD
42
45
  direction TB
43
46
  FE[frontend-agent]
44
47
  BE[backend-agent]
48
+ DB[db-agent]
45
49
  MB[mobile-agent]
46
50
  TF[tf-infra-agent]
47
51
  end
@@ -58,9 +62,45 @@ flowchart TD
58
62
  Quality --> CMT([commit])
59
63
  ```
60
64
 
65
+ ## Why Different
66
+
67
+ - **`.agents/` is the source of truth**: skills, workflows, shared resources, and config live in one portable project structure instead of being trapped inside one IDE plugin.
68
+ - **Role-shaped agent teams**: PM, QA, DB, Infra, Frontend, Backend, Mobile, Debug, and Workflow agents are modeled like an engineering org, not just a pile of prompts.
69
+ - **Workflow-first orchestration**: planning, review, debug, and coordinated execution are first-class workflows, not afterthoughts.
70
+ - **Standards-aware by design**: agents now carry focused guidance for ISO-driven planning, QA, database continuity/security, and infrastructure governance.
71
+ - **Built for verification**: dashboards, manifest generation, shared execution protocols, and structured outputs favor traceability over vibe-only generation.
72
+
73
+ ## Compatibility
74
+
75
+ `oh-my-agent` is designed around `.agents/` and then bridges to other tool-specific skill folders when needed.
76
+
77
+ | Tool / IDE | Skills Source | Interop Mode | Notes |
78
+ |------------|---------------|--------------|-------|
79
+ | Antigravity | `.agents/skills/` | Native | Primary source-of-truth layout |
80
+ | Claude Code | `.claude/skills/` | Symlink to `.agents/skills/` | Managed by installer |
81
+ | OpenCode | `.agents/skills/` | Native-compatible | Uses the same project-level skill source |
82
+ | Amp | `.agents/skills/` | Native-compatible | Shares the same project-level source |
83
+ | Codex CLI | `.agents/skills/` | Native-compatible | Works from the same project skill source |
84
+ | Cursor | `.agents/skills/` | Native-compatible | Can consume the same project-level skill source |
85
+ | GitHub Copilot | `.github/skills/` | Optional symlink | Installed when selected during setup |
86
+
87
+ See [SUPPORTED_AGENTS.md](https://github.com/first-fluke/oh-my-agent/blob/main/docs/SUPPORTED_AGENTS.md) for the current support matrix and interoperability notes.
88
+
89
+ ## The `.agents` Spec
90
+
91
+ `oh-my-agent` treats `.agents/` as a portable project convention for agent skills, workflows, and shared context.
92
+
93
+ - Skills live in `.agents/skills/<skill-name>/SKILL.md`
94
+ - Shared resources live in `.agents/skills/_shared/`
95
+ - Workflows live in `.agents/workflows/*.md`
96
+ - Project config lives in `.agents/config/`
97
+ - CLI metadata and packaging stay aligned through generated manifests
98
+
99
+ See [AGENTS_SPEC.md](https://github.com/first-fluke/oh-my-agent/blob/main/docs/AGENTS_SPEC.md) for the project layout, required files, interoperability rules, and source-of-truth model.
100
+
61
101
  ## What Is This?
62
102
 
63
- A collection of **Agent Skills** enabling collaborative multi-agent development. Work is distributed across expert agents:
103
+ A collection of **Agent Skills** enabling collaborative multi-agent development. Work is distributed across expert agents with explicit roles, workflows, and verification boundaries:
64
104
 
65
105
  | Agent | Specialization | Triggers |
66
106
  |-------|---------------|----------|
@@ -69,6 +109,7 @@ A collection of **Agent Skills** enabling collaborative multi-agent development.
69
109
  | **PM Agent** | Requirements analysis, task decomposition, architecture | "plan", "break down", "what should we build" |
70
110
  | **Frontend Agent** | React/Next.js, TypeScript, Tailwind CSS | "UI", "component", "styling" |
71
111
  | **Backend Agent** | FastAPI, PostgreSQL, JWT authentication | "API", "database", "authentication" |
112
+ | **DB Agent** | SQL/NoSQL modeling, normalization, integrity, backup, capacity | "ERD", "schema", "database design", "index tuning" |
72
113
  | **Mobile Agent** | Flutter cross-platform development | "mobile app", "iOS/Android" |
73
114
  | **QA Agent** | OWASP Top 10 security, performance, accessibility | "review security", "audit", "check performance" |
74
115
  | **Debug Agent** | Bug diagnosis, root cause analysis, regression tests | "bug", "error", "crash" |
@@ -102,9 +143,9 @@ Select your project type and skills will be installed to `.agents/skills/`, with
102
143
  | Preset | Skills |
103
144
  |--------|--------|
104
145
  | ✨ All | Everything |
105
- | 🌐 Fullstack | brainstorm, frontend, backend, pm, qa, debug, commit |
146
+ | 🌐 Fullstack | brainstorm, frontend, backend, db, pm, qa, debug, commit |
106
147
  | 🎨 Frontend | brainstorm, frontend, pm, qa, debug, commit |
107
- | ⚙️ Backend | brainstorm, backend, pm, qa, debug, commit |
148
+ | ⚙️ Backend | brainstorm, backend, db, pm, qa, debug, commit |
108
149
  | 📱 Mobile | brainstorm, mobile, pm, qa, debug, commit |
109
150
  | 🚀 DevOps | brainstorm, tf-infra, dev-workflow, pm, qa, debug, commit |
110
151
 
package/bin/cli.js CHANGED
@@ -319,7 +319,7 @@ ${J}`,initialValue:!0});if(D$(G)||!G)return{shouldCleanupBrain:!1,shouldCleanupI
319
319
  `).filter((R)=>R.trim()),B=!1;for(let R of Y){let[V,j]=R.split(":"),L=parseInt(V?.trim()||"",10);if(Number.isNaN(L))continue;if(G(L))B=!0,D(`Killing orphaned parallel agent PID=${L} (${j?.trim()||"unknown"})`),await q(L),J(S)}if(!B)D(`Removing stale PID list: ${S}`),J(S);else if(!$){await new Promise((R)=>setTimeout(R,1000));try{o5(S,{force:!0})}catch{}}}}catch{}else N(`No results directory found: ${I}`);if(z.shouldCleanupBrain||z.shouldCleanupImplicit||z.shouldCleanupKnowledge){let T=Z$(U,".gemini","antigravity");if(z.shouldCleanupBrain){let X=Z$(T,"brain");try{if(iu(X)){let S=iI(X);for(let P of S){let Y=Z$(X,P);D(`Removing brain file: ${Y}`),J(Y)}}}catch{}}if(z.shouldCleanupImplicit){let X=Z$(T,"implicit");try{if(iu(X)){let S=iI(X);for(let P of S){let Y=Z$(X,P);D(`Removing implicit file: ${Y}`),J(Y)}}}catch{}}if(z.shouldCleanupKnowledge){let X=Z$(T,"knowledge");try{if(iu(X)){let S=iI(X);for(let P of S){let Y=Z$(X,P);D(`Removing knowledge file: ${Y}`),J(Y)}}}catch{}}}if(u){console.log(JSON.stringify(v,null,2));return}if(console.clear(),H$(c$.default.bgMagenta(c$.default.white(" \uD83E\uDDF9 oh-my-agent cleanup "))),$)f(c$.default.yellow("Dry-run mode — no changes will be made"),"Mode");if(v.details.length>0){let T=[c$.default.bold("Cleanup Details"),...v.details.map((X)=>{if(X.startsWith("[DRY-RUN]"))return c$.default.yellow(X);if(X.startsWith("[CLEAN]"))return c$.default.green(X);return c$.default.cyan(X)})].join(`
320
320
  `);f(T,"Details")}let Q=[c$.default.bold("Summary"),"┌─────────┬────────┐",`│ ${c$.default.bold("Action")} │ ${c$.default.bold("Count")} │`,"├─────────┼────────┤",`│ Cleaned │ ${String(v.cleaned).padEnd(6)} │`,`│ Skipped │ ${String(v.skipped).padEnd(6)} │`,"└─────────┴────────┘"].join(`
321
321
  `);if(f(Q,"Results"),$)t(c$.default.yellow("Run without --dry-run to apply changes"));else t(c$.default.green("Cleanup complete!"))}import{execSync as EG}from"node:child_process";import{existsSync as CI,readdirSync as vQ,readFileSync as SW}from"node:fs";import{join as Ng}from"node:path";var x=T$(C$(),1);import{execSync as FG}from"node:child_process";import{execSync as t5}from"node:child_process";import{existsSync as nu,lstatSync as UW,mkdirSync as EI,readdirSync as _W,readlinkSync as vW,symlinkSync as zW,unlinkSync as DW,writeFileSync as nI}from"node:fs";import{dirname as a5,join as K$,relative as NW,resolve as wG}from"node:path";var y$="first-fluke/oh-my-agent",e5=`https://raw.githubusercontent.com/${y$}/main/.agents/skills`,GU=`https://raw.githubusercontent.com/${y$}/main/.agents`,Iu=".agents/skills";function JW(){try{return t5("gh --version",{stdio:"ignore"}),!0}catch{return!1}}async function GW($,u){try{return t5(`gh api repos/${y$}/contents/.agents/skills/${$}/${u} --jq '.[] | select(.type == "file") | .name'`,{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim().split(`
322
- `).filter(Boolean).map((I)=>`${u}/${I}`)}catch{return[]}}async function XW($,u){let g=`https://api.github.com/repos/${y$}/contents/.agents/skills/${$}/${u}`,U=await fetch(g);if(!U.ok)return[];return(await U.json()).filter((_)=>_.type==="file").map((_)=>`${u}/${_.name}`)}async function qW($,u){if(JW())return GW($,u);return XW($,u)}var D4={domain:[{name:"frontend-agent",desc:"React/Next.js UI specialist"},{name:"backend-agent",desc:"FastAPI/SQLAlchemy API specialist"},{name:"mobile-agent",desc:"Flutter/Dart mobile specialist"}],coordination:[{name:"brainstorm",desc:"Design-first ideation before planning"},{name:"pm-agent",desc:"Product manager - task decomposition"},{name:"qa-agent",desc:"QA - OWASP, Lighthouse, WCAG"},{name:"workflow-guide",desc:"Manual multi-agent orchestration"},{name:"orchestrator",desc:"Automated parallel CLI execution"}],utility:[{name:"debug-agent",desc:"Bug fixing specialist"},{name:"commit",desc:"Conventional Commits helper"}],infrastructure:[{name:"tf-infra-agent",desc:"Multi-cloud infrastructure with Terraform - AWS, GCP, Azure, OCI support"},{name:"dev-workflow",desc:"Monorepo developer workflows - mise tasks, git hooks, CI/CD, release automation"}]},s5={fullstack:["brainstorm","frontend-agent","backend-agent","pm-agent","qa-agent","debug-agent","commit","tf-infra-agent","dev-workflow"],frontend:["brainstorm","frontend-agent","pm-agent","qa-agent","debug-agent","commit"],backend:["brainstorm","backend-agent","pm-agent","qa-agent","debug-agent","commit","dev-workflow"],mobile:["brainstorm","mobile-agent","pm-agent","qa-agent","debug-agent","commit"],devops:["brainstorm","tf-infra-agent","dev-workflow","pm-agent","qa-agent","debug-agent","commit"],all:[...D4.domain,...D4.coordination,...D4.utility,...D4.infrastructure].map(($)=>$.name)},QW=["resources","config","scripts","templates"];async function TW($){let u=["SKILL.md"];for(let g of QW){let U=await qW($,g);u.push(...U)}return u}async function XU($,u){let g=K$(u,Iu,$),U=await TW($);for(let I of U){let _=`${e5}/${$}/${I}`,v=await fetch(_);if(!v.ok)continue;let z=await v.text(),D=K$(g,I),N=a5(D);if(!nu(N))EI(N,{recursive:!0});nI(D,z,"utf-8")}return!0}async function qU($){let u=K$($,Iu,"_shared"),g=["reasoning-templates.md","clarification-protocol.md","context-loading.md","skill-routing.md"];if(!nu(u))EI(u,{recursive:!0});for(let U of g){let I=`${e5}/_shared/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}async function $Q($){let u=K$($,".agents","workflows"),g=["brainstorm.md","coordinate.md","coordinate-pro.md","debug.md","orchestrate.md","plan.md","review.md","setup.md","tools.md"];if(!nu(u))EI(u,{recursive:!0});for(let U of g){let I=`${GU}/workflows/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}async function uQ($){let u=K$($,".agents","config"),g=K$($,".agents");if(!nu(u))EI(u,{recursive:!0});let U="user-preferences.yaml",I=`${GU}/config/${U}`,_=await fetch(I);if(_.ok){let N=await _.text();nI(K$(u,U),N,"utf-8")}let v="mcp.json",z=`${GU}/${v}`,D=await fetch(z);if(D.ok){let N=await D.text();nI(K$(g,v),N,"utf-8")}}function fI(){return[...D4.domain,...D4.coordination,...D4.utility,...D4.infrastructure]}var IQ={claude:".claude/skills",copilot:".github/skills"};function QU($,u,g){let U=[],I=[],_=wG($,Iu);for(let v of u){let z=IQ[v],D=K$($,z);if(!nu(D))EI(D,{recursive:!0});for(let N of g){let J=K$(_,N),G=K$(D,N);if(!nu(J)){I.push(`${z}/${N} (source missing)`);continue}try{if(UW(G).isSymbolicLink()){if(wG(a5(G),vW(G))===wG(J)){I.push(`${z}/${N} (already linked)`);continue}DW(G)}else{I.push(`${z}/${N} (real dir exists)`);continue}}catch(Q){}let q=NW(D,J);zW(q,G,"dir"),U.push(`${z}/${N}`)}}return{created:U,skipped:I}}async function gQ(){let $=process.env.HOME||process.env.USERPROFILE||"",u=K$($,".gemini","antigravity","global_workflows");if(!nu(u))EI(u,{recursive:!0});let g=["brainstorm.md","coordinate.md","coordinate-pro.md","debug.md","orchestrate.md","plan.md","review.md","setup.md","tools.md"];for(let U of g){let I=`${GU}/workflows/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}function UQ($){let u=K$($,Iu);if(!nu(u))return[];return _W(u,{withFileTypes:!0}).filter((g)=>g.isDirectory()&&!g.name.startsWith("_")).map((g)=>g.name)}function _Q($){let u=[];for(let[g,U]of Object.entries(IQ))if(nu(K$($,U)))u.push(g);return u}function iG(){try{return FG("gh --version",{stdio:"ignore"}),!0}catch{return!1}}function TU(){try{return FG("gh auth status",{stdio:"ignore"}),!0}catch{return!1}}function nG(){try{return FG(`gh api user/starred/${y$}`,{stdio:"ignore"}),!0}catch{return!1}}function SU(){return iG()&&TU()&&nG()}async function HU($,u,g){try{let U=EG(`${u} --version`,{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim();return{name:$,installed:!0,version:U,installCmd:g}}catch{return{name:$,installed:!1,installCmd:g}}}async function HW($){let u=process.env.HOME||process.env.USERPROFILE||"",U={gemini:{path:`${u}/.gemini/settings.json`,type:"json"},claude:{path:`${u}/.claude.json`,type:"json"},codex:{path:`${u}/.codex/config.toml`,type:"toml"}}[$];if(!U)return{configured:!1};if(CI(U.path))try{let I=SW(U.path,"utf-8");if(U.type==="json"){let _=JSON.parse(I);return{configured:!!(_.mcpServers||_.mcp),path:U.path}}return{configured:!0,path:U.path}}catch{return{configured:!1}}return{configured:!1}}async function PW(){let $=Ng(process.cwd(),Iu);if(!CI($))return[];let u=fI(),g=[];for(let U of u){let I=Ng($,U.name),_=Ng(I,"SKILL.md");g.push({name:U.name,installed:CI(I),hasSkillMd:CI(_)})}return g}async function YW(){let $=process.env.HOME||process.env.USERPROFILE||"",u=Ng($,".gemini","antigravity","global_workflows");if(!CI(u))return{installed:!1,count:0};try{return{installed:!0,count:vQ(u).filter((U)=>U.endsWith(".md")).length}}catch{return{installed:!1,count:0}}}async function zQ($=!1){let u=process.cwd(),g=await Promise.all([HU("gemini","gemini","bun install --global @google/gemini-cli"),HU("claude","claude","bun install --global @anthropic-ai/claude-code"),HU("codex","codex","bun install --global @openai/codex"),HU("qwen","qwen","bun install --global @qwen-code/qwen-code")]),U=await Promise.all(g.filter((T)=>T.installed).map(async(T)=>{let X=await HW(T.name);return{...T,mcp:X}})),I=await PW(),_=await YW(),v=!1;try{v=EG("git config --get rerere.enabled",{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim()==="true"}catch{}let z=Ng(u,".serena","memories"),D=CI(z),N=0;if(D)try{N=vQ(z).length}catch{}let J=g.filter((T)=>!T.installed),G=I.length>0?I.filter((T)=>!T.installed||!T.hasSkillMd):fI().map((T)=>({name:T.name,installed:!1,hasSkillMd:!1})),q=J.length+G.length+(_.installed?0:1)+(v?0:1);if($){let T={ok:q===0,issues:q,clis:g.map((X)=>({name:X.name,installed:X.installed,version:X.version||null})),mcp:U.map((X)=>({name:X.name,configured:X.mcp.configured,path:X.mcp.path||null})),skills:I.length>0?I.map((X)=>({name:X.name,installed:X.installed,complete:X.hasSkillMd})):[],missingSkills:G.map((X)=>X.name),globalWorkflows:{installed:_.installed,count:_.count},serena:{exists:D,fileCount:N},gitRerere:{enabled:v}};console.log(JSON.stringify(T,null,2)),process.exit(q===0?0:1)}console.clear(),H$(x.default.bgMagenta(x.default.white(" \uD83E\uDE7A oh-my-agent doctor ")));let Q=Mu();try{let T=[x.default.bold("\uD83D\uDD0D CLI Installation Status"),"┌─────────┬──────────┬─────────────┐",`│ ${x.default.bold("CLI")} │ ${x.default.bold("Status")} │ ${x.default.bold("Version")} │`,"├─────────┼──────────┼─────────────┤",...g.map((P)=>{let Y=P.installed?x.default.green("✅ Installed"):x.default.red("❌ Missing"),B=P.version||"-";return`│ ${P.name.padEnd(7)} │ ${Y.padEnd(8)} │ ${B.padEnd(11)} │`}),"└─────────┴──────────┴─────────────┘"].join(`
322
+ `).filter(Boolean).map((I)=>`${u}/${I}`)}catch{return[]}}async function XW($,u){let g=`https://api.github.com/repos/${y$}/contents/.agents/skills/${$}/${u}`,U=await fetch(g);if(!U.ok)return[];return(await U.json()).filter((_)=>_.type==="file").map((_)=>`${u}/${_.name}`)}async function qW($,u){if(JW())return GW($,u);return XW($,u)}var D4={domain:[{name:"frontend-agent",desc:"React/Next.js UI specialist"},{name:"backend-agent",desc:"FastAPI/SQLAlchemy API specialist"},{name:"db-agent",desc:"SQL/NoSQL data modeling, normalization, integrity, and capacity specialist"},{name:"mobile-agent",desc:"Flutter/Dart mobile specialist"}],coordination:[{name:"brainstorm",desc:"Design-first ideation before planning"},{name:"pm-agent",desc:"Product manager - task decomposition"},{name:"qa-agent",desc:"QA - OWASP, Lighthouse, WCAG"},{name:"workflow-guide",desc:"Manual multi-agent orchestration"},{name:"orchestrator",desc:"Automated parallel CLI execution"}],utility:[{name:"debug-agent",desc:"Bug fixing specialist"},{name:"commit",desc:"Conventional Commits helper"}],infrastructure:[{name:"tf-infra-agent",desc:"Multi-cloud infrastructure with Terraform - AWS, GCP, Azure, OCI support"},{name:"dev-workflow",desc:"Monorepo developer workflows - mise tasks, git hooks, CI/CD, release automation"}]},s5={fullstack:["brainstorm","frontend-agent","backend-agent","db-agent","pm-agent","qa-agent","debug-agent","commit","tf-infra-agent","dev-workflow"],frontend:["brainstorm","frontend-agent","pm-agent","qa-agent","debug-agent","commit"],backend:["brainstorm","backend-agent","db-agent","pm-agent","qa-agent","debug-agent","commit","dev-workflow"],mobile:["brainstorm","mobile-agent","pm-agent","qa-agent","debug-agent","commit"],devops:["brainstorm","tf-infra-agent","dev-workflow","pm-agent","qa-agent","debug-agent","commit"],all:[...D4.domain,...D4.coordination,...D4.utility,...D4.infrastructure].map(($)=>$.name)},QW=["resources","config","scripts","templates"];async function TW($){let u=["SKILL.md"];for(let g of QW){let U=await qW($,g);u.push(...U)}return u}async function XU($,u){let g=K$(u,Iu,$),U=await TW($);for(let I of U){let _=`${e5}/${$}/${I}`,v=await fetch(_);if(!v.ok)continue;let z=await v.text(),D=K$(g,I),N=a5(D);if(!nu(N))EI(N,{recursive:!0});nI(D,z,"utf-8")}return!0}async function qU($){let u=K$($,Iu,"_shared"),g=["reasoning-templates.md","clarification-protocol.md","context-loading.md","skill-routing.md"];if(!nu(u))EI(u,{recursive:!0});for(let U of g){let I=`${e5}/_shared/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}async function $Q($){let u=K$($,".agents","workflows"),g=["brainstorm.md","coordinate.md","coordinate-pro.md","debug.md","orchestrate.md","plan.md","review.md","setup.md","tools.md"];if(!nu(u))EI(u,{recursive:!0});for(let U of g){let I=`${GU}/workflows/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}async function uQ($){let u=K$($,".agents","config"),g=K$($,".agents");if(!nu(u))EI(u,{recursive:!0});let U="user-preferences.yaml",I=`${GU}/config/${U}`,_=await fetch(I);if(_.ok){let N=await _.text();nI(K$(u,U),N,"utf-8")}let v="mcp.json",z=`${GU}/${v}`,D=await fetch(z);if(D.ok){let N=await D.text();nI(K$(g,v),N,"utf-8")}}function fI(){return[...D4.domain,...D4.coordination,...D4.utility,...D4.infrastructure]}var IQ={claude:".claude/skills",copilot:".github/skills"};function QU($,u,g){let U=[],I=[],_=wG($,Iu);for(let v of u){let z=IQ[v],D=K$($,z);if(!nu(D))EI(D,{recursive:!0});for(let N of g){let J=K$(_,N),G=K$(D,N);if(!nu(J)){I.push(`${z}/${N} (source missing)`);continue}try{if(UW(G).isSymbolicLink()){if(wG(a5(G),vW(G))===wG(J)){I.push(`${z}/${N} (already linked)`);continue}DW(G)}else{I.push(`${z}/${N} (real dir exists)`);continue}}catch(Q){}let q=NW(D,J);zW(q,G,"dir"),U.push(`${z}/${N}`)}}return{created:U,skipped:I}}async function gQ(){let $=process.env.HOME||process.env.USERPROFILE||"",u=K$($,".gemini","antigravity","global_workflows");if(!nu(u))EI(u,{recursive:!0});let g=["brainstorm.md","coordinate.md","coordinate-pro.md","debug.md","orchestrate.md","plan.md","review.md","setup.md","tools.md"];for(let U of g){let I=`${GU}/workflows/${U}`,_=await fetch(I);if(!_.ok)continue;let v=await _.text();nI(K$(u,U),v,"utf-8")}}function UQ($){let u=K$($,Iu);if(!nu(u))return[];return _W(u,{withFileTypes:!0}).filter((g)=>g.isDirectory()&&!g.name.startsWith("_")).map((g)=>g.name)}function _Q($){let u=[];for(let[g,U]of Object.entries(IQ))if(nu(K$($,U)))u.push(g);return u}function iG(){try{return FG("gh --version",{stdio:"ignore"}),!0}catch{return!1}}function TU(){try{return FG("gh auth status",{stdio:"ignore"}),!0}catch{return!1}}function nG(){try{return FG(`gh api user/starred/${y$}`,{stdio:"ignore"}),!0}catch{return!1}}function SU(){return iG()&&TU()&&nG()}async function HU($,u,g){try{let U=EG(`${u} --version`,{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim();return{name:$,installed:!0,version:U,installCmd:g}}catch{return{name:$,installed:!1,installCmd:g}}}async function HW($){let u=process.env.HOME||process.env.USERPROFILE||"",U={gemini:{path:`${u}/.gemini/settings.json`,type:"json"},claude:{path:`${u}/.claude.json`,type:"json"},codex:{path:`${u}/.codex/config.toml`,type:"toml"}}[$];if(!U)return{configured:!1};if(CI(U.path))try{let I=SW(U.path,"utf-8");if(U.type==="json"){let _=JSON.parse(I);return{configured:!!(_.mcpServers||_.mcp),path:U.path}}return{configured:!0,path:U.path}}catch{return{configured:!1}}return{configured:!1}}async function PW(){let $=Ng(process.cwd(),Iu);if(!CI($))return[];let u=fI(),g=[];for(let U of u){let I=Ng($,U.name),_=Ng(I,"SKILL.md");g.push({name:U.name,installed:CI(I),hasSkillMd:CI(_)})}return g}async function YW(){let $=process.env.HOME||process.env.USERPROFILE||"",u=Ng($,".gemini","antigravity","global_workflows");if(!CI(u))return{installed:!1,count:0};try{return{installed:!0,count:vQ(u).filter((U)=>U.endsWith(".md")).length}}catch{return{installed:!1,count:0}}}async function zQ($=!1){let u=process.cwd(),g=await Promise.all([HU("gemini","gemini","bun install --global @google/gemini-cli"),HU("claude","claude","bun install --global @anthropic-ai/claude-code"),HU("codex","codex","bun install --global @openai/codex"),HU("qwen","qwen","bun install --global @qwen-code/qwen-code")]),U=await Promise.all(g.filter((T)=>T.installed).map(async(T)=>{let X=await HW(T.name);return{...T,mcp:X}})),I=await PW(),_=await YW(),v=!1;try{v=EG("git config --get rerere.enabled",{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim()==="true"}catch{}let z=Ng(u,".serena","memories"),D=CI(z),N=0;if(D)try{N=vQ(z).length}catch{}let J=g.filter((T)=>!T.installed),G=I.length>0?I.filter((T)=>!T.installed||!T.hasSkillMd):fI().map((T)=>({name:T.name,installed:!1,hasSkillMd:!1})),q=J.length+G.length+(_.installed?0:1)+(v?0:1);if($){let T={ok:q===0,issues:q,clis:g.map((X)=>({name:X.name,installed:X.installed,version:X.version||null})),mcp:U.map((X)=>({name:X.name,configured:X.mcp.configured,path:X.mcp.path||null})),skills:I.length>0?I.map((X)=>({name:X.name,installed:X.installed,complete:X.hasSkillMd})):[],missingSkills:G.map((X)=>X.name),globalWorkflows:{installed:_.installed,count:_.count},serena:{exists:D,fileCount:N},gitRerere:{enabled:v}};console.log(JSON.stringify(T,null,2)),process.exit(q===0?0:1)}console.clear(),H$(x.default.bgMagenta(x.default.white(" \uD83E\uDE7A oh-my-agent doctor ")));let Q=Mu();try{let T=[x.default.bold("\uD83D\uDD0D CLI Installation Status"),"┌─────────┬──────────┬─────────────┐",`│ ${x.default.bold("CLI")} │ ${x.default.bold("Status")} │ ${x.default.bold("Version")} │`,"├─────────┼──────────┼─────────────┤",...g.map((P)=>{let Y=P.installed?x.default.green("✅ Installed"):x.default.red("❌ Missing"),B=P.version||"-";return`│ ${P.name.padEnd(7)} │ ${Y.padEnd(8)} │ ${B.padEnd(11)} │`}),"└─────────┴──────────┴─────────────┘"].join(`
323
323
  `);if(f(T,"CLI Status"),J.length>0)f(J.map((P)=>`${x.default.yellow("→")} ${P.name}: ${x.default.dim(P.installCmd)}`).join(`
324
324
  `),"Install missing CLIs");if(U.length>0){let P=[x.default.bold("\uD83D\uDD17 MCP Connection Status"),"┌─────────┬──────────┬─────────────────────┐",`│ ${x.default.bold("CLI")} │ ${x.default.bold("MCP Config")} │ ${x.default.bold("Path")} │`,"├─────────┼──────────┼─────────────────────┤",...U.map((Y)=>{let B=Y.mcp.configured?x.default.green("✅ Configured"):x.default.yellow("⚠️ Not configured"),R=Y.mcp.path?Y.mcp.path.split("/").pop()||"":"-";return`│ ${Y.name.padEnd(7)} │ ${B.padEnd(8)} │ ${R.padEnd(19)} │`}),"└─────────┴──────────┴─────────────────────┘"].join(`
325
325
  `);f(P,"MCP Status")}let X=I.filter((P)=>P.installed).length,S=I.filter((P)=>P.hasSkillMd).length;if(I.length>0){let P=[x.default.bold(`\uD83D\uDCE6 Skills (${X}/${I.length} installed, ${S} complete)`),"┌────────────────────┬──────────┬─────────────┐",`│ ${x.default.bold("Skill")} │ ${x.default.bold("Installed")} │ ${x.default.bold("SKILL.md")} │`,"├────────────────────┼──────────┼─────────────┤",...I.map((Y)=>{let B=Y.installed?x.default.green("✅"):x.default.red("❌"),R=Y.hasSkillMd?x.default.green("✅"):x.default.red("❌");return`│ ${Y.name.padEnd(18)} │ ${B.padEnd(8)} │ ${R.padEnd(11)} │`}),"└────────────────────┴──────────┴─────────────┘"].join(`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-agent",
3
- "version": "2.3.0",
4
- "description": "Professional agent skills for Google Antigravity IDE - PM, Frontend, Backend, Mobile, QA, and Debug specialists that work together seamlessly",
3
+ "version": "2.4.1",
4
+ "description": "Portable multi-agent harness for .agents-based skills and workflows across Antigravity, Claude Code, Codex, OpenCode, and more",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "oh-my-ag": "./bin/cli.js",
@@ -13,6 +13,7 @@
13
13
  "keywords": [
14
14
  "oh-my-agent",
15
15
  "antigravity",
16
+ ".agents",
16
17
  "agent",
17
18
  "skills",
18
19
  "agent-skills",
@@ -21,6 +22,8 @@
21
22
  "claude",
22
23
  "claude-code",
23
24
  "codex",
25
+ "opencode",
26
+ "copilot",
24
27
  "cursor",
25
28
  "chatgpt",
26
29
  "pm",
@@ -29,6 +32,9 @@
29
32
  "mobile",
30
33
  "qa",
31
34
  "debug",
35
+ "terraform",
36
+ "database",
37
+ "workflow",
32
38
  "bug-fixing",
33
39
  "gemini"
34
40
  ],
@@ -87,10 +93,14 @@
87
93
  "pm-agent",
88
94
  "frontend-agent",
89
95
  "backend-agent",
96
+ "db-agent",
90
97
  "mobile-agent",
91
98
  "qa-agent",
92
99
  "debug-agent",
93
- "orchestrator"
100
+ "orchestrator",
101
+ "dev-workflow",
102
+ "tf-infra-agent",
103
+ "commit"
94
104
  ]
95
105
  }
96
106
  }