@taejung3852/project-scaffold 0.1.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.
Files changed (35) hide show
  1. package/.hooks/convention-check.sh +79 -0
  2. package/.hooks/devlog-auto.sh +55 -0
  3. package/.obsidian-template/appearance.json +6 -0
  4. package/.obsidian-template/core-plugins.json +20 -0
  5. package/.obsidian-template/graph.json +22 -0
  6. package/.obsidian-template/snippets/folder-colors.css +55 -0
  7. package/AGENT.md +55 -0
  8. package/README.kr.md +247 -0
  9. package/README.md +247 -0
  10. package/SOUL.md +54 -0
  11. package/THIRD_PARTY_NOTICES.md +14 -0
  12. package/bin/project-scaffold.js +122 -0
  13. package/lib/agents-registry.js +197 -0
  14. package/lib/agents.js +203 -0
  15. package/lib/install.js +342 -0
  16. package/package.json +33 -0
  17. package/skills/brief/SKILL.md +63 -0
  18. package/skills/brief/agents/openai.yaml +4 -0
  19. package/skills/brief/references/dashboard.md +140 -0
  20. package/skills/brief/references/handoff.md +130 -0
  21. package/skills/brief/references/report.md +163 -0
  22. package/skills/review/SKILL.md +123 -0
  23. package/skills/review/agents/openai.yaml +4 -0
  24. package/skills/wiki/SKILL.md +73 -0
  25. package/skills/wiki/agents/openai.yaml +4 -0
  26. package/skills/wiki/references/ambiguity-check.md +16 -0
  27. package/skills/wiki/references/capture.md +119 -0
  28. package/skills/wiki/references/clear-path.md +82 -0
  29. package/skills/wiki/references/devlog.md +111 -0
  30. package/skills/wiki/references/ingest.md +118 -0
  31. package/skills/wiki/references/lint.md +83 -0
  32. package/skills/wiki/references/query.md +81 -0
  33. package/skills/wiki/references/setup.md +397 -0
  34. package/skills/wiki/references/unclear-path.md +51 -0
  35. package/templates/gitignore +14 -0
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/taejung3852/project-scaffold/master/assets/logo.png" width="200" alt="project-scaffold logo" />
3
+ </p>
4
+
5
+ # project-scaffold
6
+
7
+ > An LLM Wiki-based development context layer that gives AI project memory and gives humans back project control
8
+
9
+ [![Node.js](https://img.shields.io/badge/CLI-Node.js_20+-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org/)
10
+ [![Markdown](https://img.shields.io/badge/Knowledge-LLM_Wiki-000000?logo=markdown)](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)
11
+ [![Skills](https://img.shields.io/badge/Core_Skills-3-1D3461)](./skills)
12
+
13
+ <p align="center">English | <a href="README.kr.md">한국어</a></p>
14
+
15
+ ---
16
+
17
+ ## What it solves
18
+
19
+ AI coding agents move quickly, but new sessions often lose the project's character, earlier decisions, user preferences, and overall work history. Repeating all of that in a long prompt is expensive and hard for humans to follow.
20
+
21
+ project-scaffold addresses this with two layers:
22
+
23
+ - **Agent Memory Layer** — accumulates project context in `raw/` and `wiki/`.
24
+ - **Human Control Plane** — briefs the human on the current goal, recent changes, decisions, and next action.
25
+
26
+ Users do not memorize twelve commands. The agent routes each request to one of three skills: `/wiki`, `/brief`, or `/review`.
27
+
28
+ ## Quick start
29
+
30
+ Requires Node.js 20+ and a Git repository.
31
+
32
+ ### New project
33
+
34
+ ```bash
35
+ mkdir my-project && cd my-project
36
+ git init
37
+ npx @taejung3852/project-scaffold@latest init --agent claude-code,codex
38
+ ```
39
+
40
+ Then start the project interview inside your AI agent:
41
+
42
+ ```text
43
+ /wiki
44
+ ```
45
+
46
+ Because no conventions exist yet, `/wiki` recommends project setup as the first action.
47
+
48
+ ### Existing project
49
+
50
+ ```bash
51
+ cd /path/to/existing-project
52
+ npx @taejung3852/project-scaffold@latest add --agent claude-code,cursor
53
+ ```
54
+
55
+ Existing files are preserved by default. Use `--force` only when you explicitly want to replace scaffold-managed files.
56
+
57
+ When migrating from the former twelve-skill version, the following command removes legacy skills and self-evolution files. Review your diff first because customized legacy skills are also removed.
58
+
59
+ ```bash
60
+ npx @taejung3852/project-scaffold@latest add --agent claude-code,codex --force
61
+ ```
62
+
63
+ ### Add agents and verify the installation
64
+
65
+ ```bash
66
+ npx @taejung3852/project-scaffold@latest add-agent --agent codex,cursor
67
+ npx @taejung3852/project-scaffold@latest doctor
68
+ ```
69
+
70
+ Installation state is stored in `.project-scaffold/manifest.json`.
71
+
72
+ ## Three core skills
73
+
74
+ ### `/wiki` — project knowledge lifecycle
75
+
76
+ Calling `/wiki` by itself inspects the project and recommends what to do next. Users do not need to remember the internal capabilities below.
77
+
78
+ | Mode | Role |
79
+ |---|---|
80
+ | `setup` | Scan the codebase and run the 14-category convention interview |
81
+ | `capture` | Preserve meeting and decision sources in `raw/` |
82
+ | `devlog` | Draft a development log from git and conversation context |
83
+ | `ingest` | Compile pending raw sources into the structured wiki |
84
+ | `query` | Answer from project character, previous decisions, and records |
85
+ | `lint` | Detect broken links, orphan pages, contradictions, and stale knowledge |
86
+
87
+ ```text
88
+ /wiki
89
+ Why did we choose this architecture?
90
+ Record this decision.
91
+ ```
92
+
93
+ ### `/brief` — human-centered briefing
94
+
95
+ Calling `/brief` by itself inspects recent git and devlog context, then puts the most relevant briefing action first.
96
+
97
+ The default briefing answers:
98
+
99
+ 1. What are we doing now?
100
+ 2. Why are we doing it?
101
+ 3. What changed since the last check-in?
102
+ 4. What does the human need to decide?
103
+ 5. What is the next action?
104
+ 6. Where is the supporting evidence?
105
+
106
+ | Mode | Role |
107
+ |---|---|
108
+ | default | 30-second briefing, expandable to five-minute or deep views |
109
+ | `dashboard` | Refresh TODOs, milestones, recent devlogs, and warnings |
110
+ | `report` | Create meeting, interview, ADR, or sprint reports |
111
+ | `handoff` | Save and restore context for another session or agent |
112
+
113
+ ```text
114
+ /brief
115
+ Explain the recent changes in detail.
116
+ Leave a handoff for the next session.
117
+ ```
118
+
119
+ ### `/review` — convention-grounded change review
120
+
121
+ Combines static analysis with LLM review against `wiki/conventions/`:
122
+
123
+ - naming and architecture dependencies
124
+ - missing tests
125
+ - error handling
126
+ - security and hard-coded secrets
127
+ - project-specific review rules
128
+
129
+ It reports evidence and proposed fixes without automatically changing the code.
130
+
131
+ ## Users do not need to remember skill names
132
+
133
+ `AGENT.md` acts as the intent router.
134
+
135
+ | User request | Agent behavior |
136
+ |---|---|
137
+ | “Where are we now?” | `/brief` |
138
+ | “Why did we choose this database?” | `/wiki` routes to project Q&A |
139
+ | “Record this decision.” | `/wiki` routes to decision capture |
140
+ | “Summarize today's work.” | `/wiki` routes to devlog |
141
+ | “Review this before the PR.” | `/review` |
142
+
143
+ Every agent adapter uses `AGENT.md` as the shared source of truth.
144
+
145
+ ## LLM Wiki structure
146
+
147
+ ```text
148
+ raw/ immutable human-authored sources
149
+ └─ compiled by /wiki when appropriate
150
+ wiki/ agent-maintained project knowledge
151
+ ├─ conventions/ project rules
152
+ ├─ decisions/ technical and product decisions
153
+ ├─ devlog/ curated development history
154
+ ├─ sources/ raw source summaries
155
+ ├─ synthesis/ answers connecting multiple documents
156
+ ├─ index.md navigation entry point
157
+ ├─ dashboard.md current state
158
+ └─ log.md wiki operation history
159
+ AGENT.md intent routing and invariant project rules
160
+ SOUL.md user preferences and assistant persona
161
+ ```
162
+
163
+ The body of `raw/` is immutable. When the wiki finds a contradiction, it preserves both sources instead of silently replacing one.
164
+
165
+ ## Supported agents
166
+
167
+ The CLI supports all 73 agent IDs and project paths from the official [`skills@1.5.16`](https://github.com/vercel-labs/skills) registry.
168
+
169
+ | Path type | Examples |
170
+ |---|---|
171
+ | shared `.agents/skills/` | Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, Amp, Warp, Zed, and others |
172
+ | `.claude/skills/` | Claude Code |
173
+ | `.continue/skills/` | Continue |
174
+ | `.hermes/skills/` | Hermes Agent |
175
+ | `.windsurf/skills/` | Windsurf |
176
+ | other dedicated paths | Goose, Kiro CLI, Roo Code, Qwen Code, AiderDesk, and others |
177
+
178
+ ```bash
179
+ npx @taejung3852/project-scaffold@latest agents
180
+ ```
181
+
182
+ Use this command to see every ID and path. `--agent all` and `--agent '*'` are also supported.
183
+
184
+ The CLI installs canonical `skills/{wiki,brief,review}` once and deduplicates agents that share the same target directory. Without `--agent`, it recommends reliably detected local agents and falls back to universal `.agents/skills/` when none are found. Existing aliases such as `claude` and `hermes` remain supported.
185
+
186
+ ## Installed structure
187
+
188
+ ```text
189
+ [your-project]/
190
+ ├── .project-scaffold/manifest.json
191
+ ├── AGENT.md
192
+ ├── SOUL.md
193
+ ├── skills/
194
+ │ ├── wiki/
195
+ │ │ ├── SKILL.md
196
+ │ │ └── references/
197
+ │ ├── brief/
198
+ │ │ ├── SKILL.md
199
+ │ │ └── references/
200
+ │ └── review/SKILL.md
201
+ ├── wiki/
202
+ │ ├── index.md
203
+ │ ├── dashboard.md
204
+ │ ├── log.md
205
+ │ └── devlog/
206
+ ├── raw/
207
+ └── .hooks/
208
+ ```
209
+
210
+ The Node CLI is the only installer entry point. The legacy shell installers have been removed.
211
+
212
+ ## Git hooks
213
+
214
+ - `pre-commit` — checks staged blobs for secrets and available static-analysis rules, handles spaces safely, and never downloads tools
215
+ - `post-commit` — records commit messages and changed files as stable Markdown under `raw/dev-logs/`
216
+
217
+ Existing hooks are preserved by default.
218
+
219
+ ## Obsidian and Graphify
220
+
221
+ - Open `wiki/` as an Obsidian vault for human navigation.
222
+ - When Graphify is installed, update the knowledge graph after wiki changes.
223
+ - Both are optional; the core LLM Wiki workflow works without them.
224
+
225
+ ## Development and release
226
+
227
+ ```bash
228
+ npm test
229
+ npm run check
230
+ npm pack --dry-run
231
+ ```
232
+
233
+ Publish only after the full feature and documentation pass is complete.
234
+
235
+ ```bash
236
+ npm publish --access public
237
+ ```
238
+
239
+ ## Current status
240
+
241
+ | Item | Status |
242
+ |---|---|
243
+ | Node CLI (`init`, `add`, `add-agent`, `agents`, `doctor`) | ✅ Implemented |
244
+ | 73-agent registry based on skills@1.5.16 | ✅ Implemented |
245
+ | Three core skills (`wiki`, `brief`, `review`) | ✅ Implemented |
246
+ | Hermes-style usage tracking and self-evolution | 🗑️ Removed |
247
+ | Initial npm publish | ⏳ After all planned changes |
package/SOUL.md ADDED
@@ -0,0 +1,54 @@
1
+ # SOUL.md — AI 비서 페르소나
2
+
3
+ > 개인 설정 파일입니다. `/wiki`의 초기 설정이 AGENT.md를 갱신해도 이 파일은 변경되지 않습니다.
4
+ > 초기 설정 카테고리 14에서 설정하거나 직접 수정하세요.
5
+
6
+ ---
7
+
8
+ ## 말투
9
+
10
+ | 항목 | 설정 |
11
+ |---|---|
12
+ | 격식 | 비격식체 (반말 허용) |
13
+ | 길이 | 간결하게. 요약 없이 핵심만 |
14
+ | 어조 | 동료처럼. 지시하지 않고 함께 고민 |
15
+
16
+ ---
17
+
18
+ ## 전문성 가정
19
+
20
+ | 항목 | 설정 |
21
+ |---|---|
22
+ | 기본 수준 | 개발 기초는 안다고 가정 |
23
+ | 설명 방식 | WHY 중심. 코드 한 줄씩 설명 금지 |
24
+ | 모르는 게 있으면 | 먼저 물어봐서 수준 파악 |
25
+
26
+ ---
27
+
28
+ ## 판단·피드백 방식
29
+
30
+ | 항목 | 설정 |
31
+ |---|---|
32
+ | 내가 틀렸을 때 | 바로 말해. 돌려 말하지 않아도 됨 |
33
+ | 선택지가 있을 때 | 추천 하나 + 이유. 목록 나열 금지 |
34
+ | 확신 없을 때 | 모른다고 말하고 같이 찾아 |
35
+
36
+ ---
37
+
38
+ ## 불확실·위험한 상황
39
+
40
+ | 항목 | 설정 |
41
+ |---|---|
42
+ | 파괴적 작업 | 반드시 먼저 물어봐 |
43
+ | 애매한 요청 | 바로 실행하지 말고 의도 확인 |
44
+ | 큰 변경 전 | 계획 먼저 보여주고 승인 받아 |
45
+
46
+ ---
47
+
48
+ ## 언어
49
+
50
+ | 항목 | 설정 |
51
+ |---|---|
52
+ | 기본 언어 | 한국어 |
53
+ | 코드·기술 용어 | 영어 원어 유지 |
54
+ | 영어로 질문 받으면 | 한국어로 답 |
@@ -0,0 +1,14 @@
1
+ # Third-Party Notices
2
+
3
+ ## vercel-labs/skills
4
+
5
+ The agent identifiers and project skill-directory mappings in
6
+ `lib/agents-registry.js` are derived from `skills@1.5.16`.
7
+
8
+ - Source: https://github.com/vercel-labs/skills
9
+ - Package: https://www.npmjs.com/package/skills
10
+ - License: MIT
11
+
12
+ No runtime code from the `skills` package is bundled or executed by
13
+ project-scaffold. The registry is vendored so installation remains deterministic
14
+ and works without a nested network dependency.
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ import process from "node:process";
4
+ import { addAgents, registryLines } from "../lib/agents.js";
5
+ import { doctor, install } from "../lib/install.js";
6
+
7
+ const HELP = `
8
+ project-scaffold — LLM Wiki 기반 AI 개발 컨텍스트 설치 도구
9
+
10
+ 사용법:
11
+ project-scaffold init [경로] [옵션] 새 프로젝트에 초기화
12
+ project-scaffold add [경로] [옵션] 기존 프로젝트에 설치
13
+ project-scaffold add-agent [경로] [옵션] 에이전트 어댑터 추가
14
+ project-scaffold doctor [경로] 설치 상태 검사
15
+ project-scaffold agents 지원 에이전트와 설치 경로 목록
16
+
17
+ 옵션:
18
+ -a, --agent <목록> 쉼표로 구분한 에이전트 ID 또는 all
19
+ 전체 목록: project-scaffold agents
20
+ -t, --target <경로> 대상 Git 저장소 경로
21
+ -y, --yes 질문 없이 자동 감지값 사용 (미감지 시 universal)
22
+ --force 기존 scaffold 관리 파일 덮어쓰기
23
+ --skip-hooks Git hook 설치 생략
24
+ --skip-graphify Graphify 등록 생략
25
+ --json doctor 결과를 JSON으로 출력
26
+ -h, --help 도움말 출력
27
+
28
+ 예시:
29
+ npx @taejung3852/project-scaffold@latest init --agent claude-code,codex
30
+ npx @taejung3852/project-scaffold@latest add ~/workspace/my-app --agent all
31
+ npx @taejung3852/project-scaffold@latest doctor
32
+ `;
33
+
34
+ function parseArgs(argv) {
35
+ const options = {
36
+ command: "help",
37
+ target: undefined,
38
+ agents: [],
39
+ yes: false,
40
+ force: false,
41
+ hooks: true,
42
+ graphify: true,
43
+ json: false,
44
+ };
45
+
46
+ const args = [...argv];
47
+ if (args[0] && !args[0].startsWith("-")) {
48
+ options.command = args.shift();
49
+ }
50
+
51
+ while (args.length > 0) {
52
+ const arg = args.shift();
53
+ if (arg === "-h" || arg === "--help") {
54
+ options.command = "help";
55
+ } else if (arg === "-y" || arg === "--yes") {
56
+ options.yes = true;
57
+ } else if (arg === "--force") {
58
+ options.force = true;
59
+ } else if (arg === "--skip-hooks") {
60
+ options.hooks = false;
61
+ } else if (arg === "--skip-graphify") {
62
+ options.graphify = false;
63
+ } else if (arg === "--json") {
64
+ options.json = true;
65
+ } else if (arg === "-a" || arg === "--agent") {
66
+ const value = args.shift();
67
+ if (!value) throw new Error(`${arg} 뒤에 에이전트 목록이 필요합니다.`);
68
+ options.agents.push(...value.split(","));
69
+ } else if (arg.startsWith("--agent=")) {
70
+ options.agents.push(...arg.slice("--agent=".length).split(","));
71
+ } else if (arg === "-t" || arg === "--target") {
72
+ const value = args.shift();
73
+ if (!value) throw new Error(`${arg} 뒤에 경로가 필요합니다.`);
74
+ options.target = value;
75
+ } else if (arg.startsWith("--target=")) {
76
+ options.target = arg.slice("--target=".length);
77
+ } else if (!arg.startsWith("-") && !options.target) {
78
+ options.target = arg;
79
+ } else {
80
+ throw new Error(`알 수 없는 옵션: ${arg}`);
81
+ }
82
+ }
83
+
84
+ return options;
85
+ }
86
+
87
+ async function main() {
88
+ const options = parseArgs(process.argv.slice(2));
89
+
90
+ if (options.command === "help") {
91
+ process.stdout.write(HELP);
92
+ return;
93
+ }
94
+
95
+ if (options.command === "init" || options.command === "add") {
96
+ await install({ ...options, mode: options.command });
97
+ return;
98
+ }
99
+
100
+ if (options.command === "add-agent") {
101
+ await addAgents(options);
102
+ return;
103
+ }
104
+
105
+ if (options.command === "doctor") {
106
+ const result = await doctor(options);
107
+ if (!result.ok) process.exitCode = 1;
108
+ return;
109
+ }
110
+
111
+ if (options.command === "agents") {
112
+ console.log(registryLines().join("\n"));
113
+ return;
114
+ }
115
+
116
+ throw new Error(`알 수 없는 명령: ${options.command}\n${HELP}`);
117
+ }
118
+
119
+ main().catch((error) => {
120
+ console.error(`❌ ${error.message}`);
121
+ process.exitCode = 1;
122
+ });
@@ -0,0 +1,197 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ export const registrySource = Object.freeze({
6
+ package: "skills",
7
+ version: "1.5.16",
8
+ repository: "https://github.com/vercel-labs/skills",
9
+ });
10
+
11
+ const universalAgents = [
12
+ "amp",
13
+ "antigravity",
14
+ "antigravity-cli",
15
+ "cline",
16
+ "codex",
17
+ "cursor",
18
+ "deepagents",
19
+ "dexto",
20
+ "firebender",
21
+ "gemini-cli",
22
+ "github-copilot",
23
+ "kimi-code-cli",
24
+ "loaf",
25
+ "opencode",
26
+ "promptscript",
27
+ "replit",
28
+ "universal",
29
+ "warp",
30
+ "zed",
31
+ ];
32
+
33
+ const customAgentPaths = {
34
+ "aider-desk": ".aider-desk/skills",
35
+ astrbot: "data/skills",
36
+ "autohand-code": ".autohand/skills",
37
+ augment: ".augment/skills",
38
+ bob: ".bob/skills",
39
+ "claude-code": ".claude/skills",
40
+ openclaw: "skills",
41
+ "codearts-agent": ".codeartsdoer/skills",
42
+ codebuddy: ".codebuddy/skills",
43
+ codemaker: ".codemaker/skills",
44
+ codestudio: ".codestudio/skills",
45
+ "command-code": ".commandcode/skills",
46
+ continue: ".continue/skills",
47
+ cortex: ".cortex/skills",
48
+ crush: ".crush/skills",
49
+ devin: ".devin/skills",
50
+ droid: ".factory/skills",
51
+ eve: "agent/skills",
52
+ forgecode: ".forge/skills",
53
+ goose: ".goose/skills",
54
+ "hermes-agent": ".hermes/skills",
55
+ "inference-sh": ".inferencesh/skills",
56
+ jazz: ".jazz/skills",
57
+ junie: ".junie/skills",
58
+ "iflow-cli": ".iflow/skills",
59
+ kilo: ".kilocode/skills",
60
+ "kiro-cli": ".kiro/skills",
61
+ kode: ".kode/skills",
62
+ lingma: ".lingma/skills",
63
+ mcpjam: ".mcpjam/skills",
64
+ "mistral-vibe": ".vibe/skills",
65
+ moxby: ".moxby/skills",
66
+ mux: ".mux/skills",
67
+ openhands: ".openhands/skills",
68
+ ona: ".ona/skills",
69
+ pi: ".pi/skills",
70
+ qoder: ".qoder/skills",
71
+ "qoder-cn": ".qoder/skills",
72
+ "qwen-code": ".qwen/skills",
73
+ reasonix: ".reasonix/skills",
74
+ rovodev: ".rovodev/skills",
75
+ roo: ".roo/skills",
76
+ "tabnine-cli": ".tabnine/agent/skills",
77
+ terramind: ".terramind/skills",
78
+ tinycloud: ".tinycloud/skills",
79
+ trae: ".trae/skills",
80
+ "trae-cn": ".trae/skills",
81
+ windsurf: ".windsurf/skills",
82
+ zcode: ".zcode/skills",
83
+ zencoder: ".zencoder/skills",
84
+ zenflow: ".zencoder/skills",
85
+ neovate: ".neovate/skills",
86
+ pochi: ".pochi/skills",
87
+ adal: ".adal/skills",
88
+ };
89
+
90
+ const displayNameOverrides = {
91
+ "aider-desk": "AiderDesk",
92
+ "autohand-code": "Autohand Code CLI",
93
+ bob: "IBM Bob",
94
+ "claude-code": "Claude Code",
95
+ "codearts-agent": "CodeArts Agent",
96
+ codestudio: "Code Studio",
97
+ codex: "Codex",
98
+ cortex: "Cortex Code",
99
+ deepagents: "Deep Agents",
100
+ devin: "Devin for Terminal",
101
+ droid: "Droid",
102
+ "gemini-cli": "Gemini CLI",
103
+ "github-copilot": "GitHub Copilot",
104
+ "hermes-agent": "Hermes Agent",
105
+ "iflow-cli": "iFlow CLI",
106
+ "inference-sh": "inference.sh",
107
+ kilo: "Kilo Code",
108
+ "kimi-code-cli": "Kimi Code CLI",
109
+ "kiro-cli": "Kiro CLI",
110
+ "mistral-vibe": "Mistral Vibe",
111
+ opencode: "OpenCode",
112
+ openhands: "OpenHands",
113
+ "qoder-cn": "Qoder CN",
114
+ "qwen-code": "Qwen Code",
115
+ roo: "Roo Code",
116
+ "tabnine-cli": "Tabnine CLI",
117
+ "trae-cn": "Trae CN",
118
+ zcode: "ZCode",
119
+ };
120
+
121
+ export const agentAliases = Object.freeze({
122
+ claude: "claude-code",
123
+ hermes: "hermes-agent",
124
+ "continue-dev": "continue",
125
+ });
126
+
127
+ function titleCase(id) {
128
+ return id
129
+ .split("-")
130
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
131
+ .join(" ");
132
+ }
133
+
134
+ const entries = [
135
+ ...universalAgents.map((id) => [id, ".agents/skills"]),
136
+ ...Object.entries(customAgentPaths),
137
+ ].map(([id, projectSkillsDir]) => ({
138
+ id,
139
+ displayName: displayNameOverrides[id] ?? titleCase(id),
140
+ projectSkillsDir,
141
+ }));
142
+
143
+ export const agentRegistry = Object.freeze(
144
+ Object.fromEntries(entries.sort((a, b) => a.id.localeCompare(b.id)).map((entry) => [entry.id, Object.freeze(entry)])),
145
+ );
146
+
147
+ export const supportedAgentIds = Object.freeze(Object.keys(agentRegistry));
148
+
149
+ export function normalizeAgentId(value) {
150
+ const normalized = value.trim().toLowerCase();
151
+ return agentAliases[normalized] ?? normalized;
152
+ }
153
+
154
+ export function getAgent(id) {
155
+ return agentRegistry[normalizeAgentId(id)];
156
+ }
157
+
158
+ const detectionPaths = {
159
+ "aider-desk": ["~/.aider-desk"],
160
+ antigravity: ["~/.gemini/antigravity"],
161
+ "antigravity-cli": ["~/.gemini/antigravity-cli"],
162
+ "claude-code": ["~/.claude"],
163
+ codex: ["$CODEX_HOME", "~/.codex", "/etc/codex"],
164
+ continue: ["~/.continue", ".continue"],
165
+ cursor: ["~/.cursor", ".cursor"],
166
+ devin: ["~/.config/devin"],
167
+ droid: ["~/.factory"],
168
+ "gemini-cli": ["~/.gemini"],
169
+ goose: ["~/.config/goose"],
170
+ "hermes-agent": ["~/.hermes"],
171
+ "kiro-cli": ["~/.kiro"],
172
+ opencode: ["~/.config/opencode"],
173
+ windsurf: ["~/.codeium/windsurf"],
174
+ };
175
+
176
+ function expandDetectionPath(value, target, home, env) {
177
+ if (value === "$CODEX_HOME") return env.CODEX_HOME?.trim() || null;
178
+ if (value.startsWith("~/")) return path.join(home, value.slice(2));
179
+ if (path.isAbsolute(value)) return value;
180
+ return path.join(target, value);
181
+ }
182
+
183
+ export function detectAgents({ target = process.cwd(), home = os.homedir(), env = process.env } = {}) {
184
+ return Object.entries(detectionPaths)
185
+ .filter(([, candidates]) =>
186
+ candidates.some((candidate) => {
187
+ const expanded = expandDetectionPath(candidate, target, home, env);
188
+ return expanded ? fs.existsSync(expanded) : false;
189
+ }),
190
+ )
191
+ .map(([id]) => id)
192
+ .sort();
193
+ }
194
+
195
+ export function uniqueProjectSkillDirs(agentIds) {
196
+ return [...new Set(agentIds.map((id) => getAgent(id)?.projectSkillsDir).filter(Boolean))];
197
+ }