agent-proteus 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # 🔱 Proteus
2
+
3
+ > Shape-shifting project intelligence for Claude Code
4
+
5
+ Proteus analyzes your project and generates **project-specific agents** — specialized AI assistants that understand your codebase's language, framework, conventions, and rules.
6
+
7
+ ## Why Proteus?
8
+
9
+ Claude Code works best with context. But generic agents don't know your project's:
10
+ - Coding conventions and style
11
+ - Directory structure
12
+ - Testing patterns
13
+ - Project-specific rules
14
+
15
+ **Proteus transforms into your project**, creating personalized agents that already understand everything.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # Run directly with npx
21
+ npx agent-proteus
22
+
23
+ # Or install globally
24
+ npm install -g agent-proteus
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ # In your project directory
31
+ proteus
32
+
33
+ # Preview without saving
34
+ proteus --dry-run
35
+
36
+ # Specify output language
37
+ proteus --lang ja
38
+ ```
39
+
40
+ Proteus will:
41
+ 1. Analyze your project structure
42
+ 2. Read existing CLAUDE.md (if present) for rules
43
+ 3. Suggest project-specific agents using Claude Code
44
+ 4. Generate selected agents to `.claude/agents/`
45
+ 5. Optionally generate `/proteus` skill for easy routing
46
+
47
+ ## Generated Structure
48
+
49
+ ```
50
+ .claude/
51
+ ├── agents/ # Project-specific agents
52
+ │ ├── rails-graphql-type-generator.md
53
+ │ ├── rspec-request-spec-writer.md
54
+ │ └── ...
55
+ └── skills/
56
+ └── proteus/
57
+ └── SKILL.md # Router skill for agents
58
+ ```
59
+
60
+ ## Commands
61
+
62
+ ```bash
63
+ # Default: Analyze and generate agents
64
+ proteus
65
+
66
+ # Generate CLAUDE.md only
67
+ proteus init
68
+
69
+ # Update agent list in CLAUDE.md or agents.md
70
+ proteus registry
71
+
72
+ # Just analyze (no generation)
73
+ proteus analyze
74
+ ```
75
+
76
+ ### Options
77
+
78
+ | Option | Description |
79
+ |--------|-------------|
80
+ | `-o, --output <dir>` | Output directory (default: `.claude/agents`) |
81
+ | `-l, --lang <code>` | Output language (en, ja, zh, ko, es, fr, de) |
82
+ | `-d, --dry-run` | Preview without saving |
83
+ | `-f, --force` | Skip confirmations |
84
+ | `--include-claude-md` | Also generate CLAUDE.md if not exists |
85
+
86
+ ## How It Works
87
+
88
+ ```
89
+ ┌─────────────────────────────────────┐
90
+ │ 1. Analyze Project │
91
+ │ - Language, framework, tools │
92
+ │ - Directory structure │
93
+ │ - Naming conventions │
94
+ └─────────────────────────────────────┘
95
+
96
+ ┌─────────────────────────────────────┐
97
+ │ 2. Read Existing Documents │
98
+ │ - CLAUDE.md (rules, conventions) │
99
+ │ - README.md (description) │
100
+ │ - Existing agents (avoids dupes) │
101
+ └─────────────────────────────────────┘
102
+
103
+ ┌─────────────────────────────────────┐
104
+ │ 3. Suggest Agents (via Claude) │
105
+ │ - Dynamic based on your stack │
106
+ │ - Considers existing coverage │
107
+ │ - Project-specific naming │
108
+ └─────────────────────────────────────┘
109
+
110
+ ┌─────────────────────────────────────┐
111
+ │ 4. Generate & Save │
112
+ │ - Personalized agent definitions │
113
+ │ - /proteus skill for routing │
114
+ │ - Registry in CLAUDE.md │
115
+ └─────────────────────────────────────┘
116
+ ```
117
+
118
+ ## Smart Agent Suggestions
119
+
120
+ Proteus adjusts suggestions based on existing coverage:
121
+
122
+ | Existing Agents | Max Suggestions |
123
+ |-----------------|-----------------|
124
+ | 0 | 5 agents |
125
+ | 1-2 | 3 agents |
126
+ | 3-4 | 2 agents |
127
+ | 5+ | 0-1 agents (if gaps exist) |
128
+
129
+ ## Supported Languages & Frameworks
130
+
131
+ | Language | Frameworks |
132
+ |----------|------------|
133
+ | TypeScript/JavaScript | Next.js, React, Vue, Angular, Svelte, Express, Fastify, NestJS |
134
+ | Go | Gin, Echo, Fiber |
135
+ | Python | Django, Flask, FastAPI |
136
+ | Ruby | Rails |
137
+ | Rust | Actix, Axum |
138
+ | Java | Spring |
139
+ | PHP | Laravel |
140
+
141
+ ## Example: Rails Project
142
+
143
+ ```bash
144
+ $ proteus
145
+
146
+ 🔱 PROTEUS - Shape-shifting project intelligence
147
+
148
+ ✓ Claude Code detected - using AI-powered generation
149
+ ✔ Output language: 日本語
150
+
151
+ Tech Stack:
152
+ Language: ruby
153
+ Framework: rails
154
+ Testing: rspec
155
+
156
+ Recommended agents:
157
+ 1. rails-graphql-type-generator
158
+ 2. serializable-pattern-enforcer
159
+ 3. rspec-request-spec-writer
160
+ ...
161
+
162
+ ✅ Created .claude/agents/rails-graphql-type-generator.md
163
+ ✅ Created .claude/skills/proteus/SKILL.md
164
+ ```
165
+
166
+ ## Using Generated Agents
167
+
168
+ ```bash
169
+ # Use specific agent
170
+ @rails-graphql-type-generator このResolverをレビューして
171
+
172
+ # Use /proteus to auto-route
173
+ /proteus テストを書いて # → automatically uses rspec-request-spec-writer
174
+ ```
175
+
176
+ ## Contributing
177
+
178
+ Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
179
+
180
+ ## License
181
+
182
+ MIT
183
+
184
+ ---
185
+
186
+ <p align="center">
187
+ <i>Named after Proteus, the shape-shifting Greek sea god who could transform into anything — just like this tool transforms into your project.</i>
188
+ </p>
@@ -0,0 +1,12 @@
1
+ import {
2
+ generateAgentListSection,
3
+ scanExistingAgents,
4
+ shouldUpdateRegistry,
5
+ updateAgentRegistry
6
+ } from "./chunk-XNEZQVEV.js";
7
+ export {
8
+ generateAgentListSection,
9
+ scanExistingAgents,
10
+ shouldUpdateRegistry,
11
+ updateAgentRegistry
12
+ };
@@ -0,0 +1,236 @@
1
+ // src/agent-registry.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ var I18N = {
5
+ en: {
6
+ availableAgents: "Available Agents",
7
+ noAgentsConfigured: "No agents configured yet.",
8
+ agentHeader: "Agent",
9
+ descriptionHeader: "Description",
10
+ pathHeader: "Path",
11
+ agentsAvailable: (n) => `${n} agent(s) available`,
12
+ projectAgentsTitle: "Project Agents",
13
+ projectAgentsDescription: "This file lists all available Claude Code agents for this project."
14
+ },
15
+ ja: {
16
+ availableAgents: "\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
17
+ noAgentsConfigured: "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306F\u307E\u3060\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
18
+ agentHeader: "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
19
+ descriptionHeader: "\u8AAC\u660E",
20
+ pathHeader: "\u30D1\u30B9",
21
+ agentsAvailable: (n) => `${n}\u500B\u306E\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u5229\u7528\u53EF\u80FD`,
22
+ projectAgentsTitle: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
23
+ projectAgentsDescription: "\u3053\u306E\u30D5\u30A1\u30A4\u30EB\u306F\u3001\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3067\u5229\u7528\u53EF\u80FD\u306AClaude Code\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u4E00\u89A7\u3067\u3059\u3002"
24
+ },
25
+ zh: {
26
+ availableAgents: "\u53EF\u7528\u4EE3\u7406",
27
+ noAgentsConfigured: "\u5C1A\u672A\u914D\u7F6E\u4EE3\u7406\u3002",
28
+ agentHeader: "\u4EE3\u7406",
29
+ descriptionHeader: "\u63CF\u8FF0",
30
+ pathHeader: "\u8DEF\u5F84",
31
+ agentsAvailable: (n) => `${n}\u4E2A\u4EE3\u7406\u53EF\u7528`,
32
+ projectAgentsTitle: "\u9879\u76EE\u4EE3\u7406",
33
+ projectAgentsDescription: "\u6B64\u6587\u4EF6\u5217\u51FA\u4E86\u6B64\u9879\u76EE\u6240\u6709\u53EF\u7528\u7684Claude Code\u4EE3\u7406\u3002"
34
+ },
35
+ ko: {
36
+ availableAgents: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC5D0\uC774\uC804\uD2B8",
37
+ noAgentsConfigured: "\uC544\uC9C1 \uAD6C\uC131\uB41C \uC5D0\uC774\uC804\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
38
+ agentHeader: "\uC5D0\uC774\uC804\uD2B8",
39
+ descriptionHeader: "\uC124\uBA85",
40
+ pathHeader: "\uACBD\uB85C",
41
+ agentsAvailable: (n) => `${n}\uAC1C\uC758 \uC5D0\uC774\uC804\uD2B8 \uC0AC\uC6A9 \uAC00\uB2A5`,
42
+ projectAgentsTitle: "\uD504\uB85C\uC81D\uD2B8 \uC5D0\uC774\uC804\uD2B8",
43
+ projectAgentsDescription: "\uC774 \uD30C\uC77C\uC740 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C Claude Code \uC5D0\uC774\uC804\uD2B8 \uBAA9\uB85D\uC785\uB2C8\uB2E4."
44
+ },
45
+ es: {
46
+ availableAgents: "Agentes Disponibles",
47
+ noAgentsConfigured: "No hay agentes configurados todav\xEDa.",
48
+ agentHeader: "Agente",
49
+ descriptionHeader: "Descripci\xF3n",
50
+ pathHeader: "Ruta",
51
+ agentsAvailable: (n) => `${n} agente(s) disponible(s)`,
52
+ projectAgentsTitle: "Agentes del Proyecto",
53
+ projectAgentsDescription: "Este archivo lista todos los agentes de Claude Code disponibles para este proyecto."
54
+ },
55
+ fr: {
56
+ availableAgents: "Agents Disponibles",
57
+ noAgentsConfigured: "Aucun agent configur\xE9 pour le moment.",
58
+ agentHeader: "Agent",
59
+ descriptionHeader: "Description",
60
+ pathHeader: "Chemin",
61
+ agentsAvailable: (n) => `${n} agent(s) disponible(s)`,
62
+ projectAgentsTitle: "Agents du Projet",
63
+ projectAgentsDescription: "Ce fichier liste tous les agents Claude Code disponibles pour ce projet."
64
+ },
65
+ de: {
66
+ availableAgents: "Verf\xFCgbare Agenten",
67
+ noAgentsConfigured: "Noch keine Agenten konfiguriert.",
68
+ agentHeader: "Agent",
69
+ descriptionHeader: "Beschreibung",
70
+ pathHeader: "Pfad",
71
+ agentsAvailable: (n) => `${n} Agent(en) verf\xFCgbar`,
72
+ projectAgentsTitle: "Projekt-Agenten",
73
+ projectAgentsDescription: "Diese Datei listet alle verf\xFCgbaren Claude Code Agenten f\xFCr dieses Projekt auf."
74
+ }
75
+ };
76
+ function scanExistingAgents(agentDir) {
77
+ const agents = [];
78
+ if (!fs.existsSync(agentDir)) {
79
+ return agents;
80
+ }
81
+ const files = fs.readdirSync(agentDir);
82
+ for (const file of files) {
83
+ if (!file.endsWith(".md") || file.startsWith("_") || file.toLowerCase() === "readme.md") {
84
+ continue;
85
+ }
86
+ const filePath = path.join(agentDir, file);
87
+ const content = fs.readFileSync(filePath, "utf-8");
88
+ const h1Match = content.match(/^#\s+(.+)$/m);
89
+ const name = h1Match ? h1Match[1].trim() : file.replace(".md", "");
90
+ const descMatch = content.match(/^#\s+.+\n+([^#\n].+)/m);
91
+ const description = descMatch ? descMatch[1].trim() : void 0;
92
+ agents.push({
93
+ name,
94
+ description,
95
+ path: file
96
+ });
97
+ }
98
+ return agents;
99
+ }
100
+ function generateAgentListSection(agents, agentDir, format = "table", lang = "en") {
101
+ const t = I18N[lang];
102
+ if (agents.length === 0) {
103
+ return `## ${t.availableAgents}
104
+
105
+ ${t.noAgentsConfigured}
106
+ `;
107
+ }
108
+ const relativePath = agentDir.replace(/^\.\//, "");
109
+ if (format === "table") {
110
+ let section = `## ${t.availableAgents}
111
+
112
+ `;
113
+ section += `| ${t.agentHeader} | ${t.descriptionHeader} | ${t.pathHeader} |
114
+ `;
115
+ section += "|-------|-------------|------|\n";
116
+ for (const agent of agents) {
117
+ const desc = agent.description ? truncate(agent.description, 60) : "-";
118
+ section += `| ${agent.name} | ${desc} | \`${relativePath}/${agent.path}\` |
119
+ `;
120
+ }
121
+ section += `
122
+ *${t.agentsAvailable(agents.length)}*
123
+ `;
124
+ return section;
125
+ } else {
126
+ let section = `## ${t.availableAgents}
127
+
128
+ `;
129
+ for (const agent of agents) {
130
+ section += `### ${agent.name}
131
+ `;
132
+ if (agent.description) {
133
+ section += `${agent.description}
134
+ `;
135
+ }
136
+ section += `- **${t.pathHeader}**: \`${relativePath}/${agent.path}\`
137
+
138
+ `;
139
+ }
140
+ return section;
141
+ }
142
+ }
143
+ function updateAgentRegistry(options, newAgents = []) {
144
+ const { projectPath, agentDir, format = "table", lang = "en" } = options;
145
+ const t = I18N[lang];
146
+ const existingAgents = scanExistingAgents(path.join(projectPath, agentDir));
147
+ const allAgents = [...existingAgents];
148
+ for (const newAgent of newAgents) {
149
+ const filename = `${newAgent.name}.md`;
150
+ if (!allAgents.some((a) => a.path === filename)) {
151
+ allAgents.push({
152
+ name: formatAgentName(newAgent.name),
153
+ description: newAgent.description,
154
+ path: filename
155
+ });
156
+ }
157
+ }
158
+ const agentSection = generateAgentListSection(allAgents, agentDir, format, lang);
159
+ const claudeMdPath = path.join(projectPath, "CLAUDE.md");
160
+ const agentsMdPath = path.join(projectPath, "agents.md");
161
+ if (fs.existsSync(claudeMdPath)) {
162
+ const result = updateFileWithAgentSection(claudeMdPath, agentSection, lang);
163
+ return {
164
+ file: "CLAUDE.md",
165
+ action: result.action,
166
+ agentCount: allAgents.length
167
+ };
168
+ }
169
+ if (fs.existsSync(agentsMdPath)) {
170
+ const result = updateFileWithAgentSection(agentsMdPath, agentSection, lang);
171
+ return {
172
+ file: "agents.md",
173
+ action: result.action,
174
+ agentCount: allAgents.length
175
+ };
176
+ }
177
+ const newContent = `# ${t.projectAgentsTitle}
178
+
179
+ ${t.projectAgentsDescription}
180
+
181
+ ${agentSection}`;
182
+ fs.writeFileSync(agentsMdPath, newContent, "utf-8");
183
+ return {
184
+ file: "agents.md",
185
+ action: "created",
186
+ agentCount: allAgents.length
187
+ };
188
+ }
189
+ function updateFileWithAgentSection(filePath, agentSection, lang) {
190
+ const content = fs.readFileSync(filePath, "utf-8");
191
+ const t = I18N[lang];
192
+ const allSectionHeaders = Object.values(I18N).map((i) => i.availableAgents).join("|");
193
+ const agentSectionPattern = new RegExp(`## (${allSectionHeaders})[\\s\\S]*?(?=\\n## |\\n# |$)`);
194
+ if (agentSectionPattern.test(content)) {
195
+ const newContent = content.replace(agentSectionPattern, agentSection.trim());
196
+ fs.writeFileSync(filePath, newContent, "utf-8");
197
+ return { action: "updated" };
198
+ } else {
199
+ const newContent = content.trimEnd() + "\n\n" + agentSection;
200
+ fs.writeFileSync(filePath, newContent, "utf-8");
201
+ return { action: "added-section" };
202
+ }
203
+ }
204
+ function formatAgentName(name) {
205
+ return name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
206
+ }
207
+ function truncate(str, maxLength) {
208
+ if (str.length <= maxLength) return str;
209
+ return str.slice(0, maxLength - 3) + "...";
210
+ }
211
+ function shouldUpdateRegistry(projectPath, agentDir, newAgentCount) {
212
+ const claudeMdPath = path.join(projectPath, "CLAUDE.md");
213
+ const agentsMdPath = path.join(projectPath, "agents.md");
214
+ if (!fs.existsSync(claudeMdPath) && !fs.existsSync(agentsMdPath)) {
215
+ return true;
216
+ }
217
+ const existingAgents = scanExistingAgents(path.join(projectPath, agentDir));
218
+ if (newAgentCount > 0) {
219
+ return true;
220
+ }
221
+ const registryPath = fs.existsSync(claudeMdPath) ? claudeMdPath : agentsMdPath;
222
+ const content = fs.readFileSync(registryPath, "utf-8");
223
+ const agentCountMatch = content.match(/\*(\d+)/);
224
+ if (agentCountMatch) {
225
+ const registeredCount = parseInt(agentCountMatch[1], 10);
226
+ return registeredCount !== existingAgents.length;
227
+ }
228
+ return true;
229
+ }
230
+
231
+ export {
232
+ scanExistingAgents,
233
+ generateAgentListSection,
234
+ updateAgentRegistry,
235
+ shouldUpdateRegistry
236
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node