rebar-mcp 2.0.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/.claude/agents/template-writer.md +43 -0
- package/.claude/agents/test-runner.md +47 -0
- package/.claude/mcp.json +9 -0
- package/.claude/settings.json +29 -0
- package/.claude/skills/ /SKILL.md +21 -0
- package/.claude/skills/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SKILL.md +21 -0
- package/.claude/skills/bmmibwetxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwjgvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwsesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwxufxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibx3r9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmji0lrkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiniphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjio86zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiolfbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjit1lvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjita1qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibweu3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwjh4/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwsey/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwxup/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibx3rg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmji0lrp/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjinipm/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjio875/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjiolfg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjit1m3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjita1x/SKILL.md +21 -0
- package/.claude/skills/coercion-test/SKILL.md +50 -0
- package/.claude/skills/large-skill/SKILL.md +21 -0
- package/.claude/skills/long-desc-skill/SKILL.md +21 -0
- package/.claude/skills/mcp-dev/SKILL.md +61 -0
- package/.claude/skills/nl-mmibweus/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwjhf/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwsf7/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwxvq/SKILL.md +25 -0
- package/.claude/skills/nl-mmibx3rt/SKILL.md +25 -0
- package/.claude/skills/nl-mmji0lrz/SKILL.md +25 -0
- package/.claude/skills/nl-mmjinipx/SKILL.md +25 -0
- package/.claude/skills/nl-mmjio87f/SKILL.md +25 -0
- package/.claude/skills/nl-mmjiolfs/SKILL.md +25 -0
- package/.claude/skills/nl-mmjit1mc/SKILL.md +25 -0
- package/.claude/skills/nl-mmjita26/SKILL.md +25 -0
- package/.claude/skills/rapid-1/SKILL.md +21 -0
- package/.claude/skills/rapid-2/SKILL.md +21 -0
- package/.claude/skills/rapid-3/SKILL.md +21 -0
- package/.claude/skills/rapid-4/SKILL.md +21 -0
- package/.claude/skills/rapid-5/SKILL.md +21 -0
- package/.claude/skills/test/", /"malicious/": /"true/SKILL.md" +69 -0
- package/.claude/skills/test-emoji-/360/237/230/200-skill/SKILL.md +69 -0
- package/.claude/skills/test-skill/SKILL.md +69 -0
- package/.claude/skills/test; rm -rf /; skill/SKILL.md +69 -0
- package/.claude/skills/test<script>alert(1)</script>skill/SKILL.md +69 -0
- package/.claudeignore +5 -0
- package/.mcp.json +3 -0
- package/CHANGELOG.md +29 -0
- package/CLAUDE.md +76 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/ROADMAP.md +526 -0
- package/ccboot-PRD-v1.0.docx.md +732 -0
- package/ccboot-v1.2.0-enforcement-spec.md +1272 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +674 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +118 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +62 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +15 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/scaffolding.d.ts +277 -0
- package/dist/schemas/scaffolding.d.ts.map +1 -0
- package/dist/schemas/scaffolding.js +133 -0
- package/dist/schemas/scaffolding.js.map +1 -0
- package/dist/services/claudemd-generator.d.ts +16 -0
- package/dist/services/claudemd-generator.d.ts.map +1 -0
- package/dist/services/claudemd-generator.js +426 -0
- package/dist/services/claudemd-generator.js.map +1 -0
- package/dist/services/codex-generator.d.ts +6 -0
- package/dist/services/codex-generator.d.ts.map +1 -0
- package/dist/services/codex-generator.js +35 -0
- package/dist/services/codex-generator.js.map +1 -0
- package/dist/services/cursor-generator.d.ts +15 -0
- package/dist/services/cursor-generator.d.ts.map +1 -0
- package/dist/services/cursor-generator.js +134 -0
- package/dist/services/cursor-generator.js.map +1 -0
- package/dist/services/file-ops.d.ts +48 -0
- package/dist/services/file-ops.d.ts.map +1 -0
- package/dist/services/file-ops.js +153 -0
- package/dist/services/file-ops.js.map +1 -0
- package/dist/services/output-formatter.d.ts +57 -0
- package/dist/services/output-formatter.d.ts.map +1 -0
- package/dist/services/output-formatter.js +88 -0
- package/dist/services/output-formatter.js.map +1 -0
- package/dist/services/platform-detect.d.ts +14 -0
- package/dist/services/platform-detect.d.ts.map +1 -0
- package/dist/services/platform-detect.js +63 -0
- package/dist/services/platform-detect.js.map +1 -0
- package/dist/services/project-analyzer.d.ts +71 -0
- package/dist/services/project-analyzer.d.ts.map +1 -0
- package/dist/services/project-analyzer.js +595 -0
- package/dist/services/project-analyzer.js.map +1 -0
- package/dist/services/rules-engine.d.ts +41 -0
- package/dist/services/rules-engine.d.ts.map +1 -0
- package/dist/services/rules-engine.js +304 -0
- package/dist/services/rules-engine.js.map +1 -0
- package/dist/services/strictness.d.ts +37 -0
- package/dist/services/strictness.d.ts.map +1 -0
- package/dist/services/strictness.js +182 -0
- package/dist/services/strictness.js.map +1 -0
- package/dist/services/template-engine.d.ts +16 -0
- package/dist/services/template-engine.d.ts.map +1 -0
- package/dist/services/template-engine.js +85 -0
- package/dist/services/template-engine.js.map +1 -0
- package/dist/services/validation.d.ts +41 -0
- package/dist/services/validation.d.ts.map +1 -0
- package/dist/services/validation.js +104 -0
- package/dist/services/validation.js.map +1 -0
- package/dist/services/windsurf-generator.d.ts +15 -0
- package/dist/services/windsurf-generator.d.ts.map +1 -0
- package/dist/services/windsurf-generator.js +127 -0
- package/dist/services/windsurf-generator.js.map +1 -0
- package/dist/tests/enforcement.test.d.ts +2 -0
- package/dist/tests/enforcement.test.d.ts.map +1 -0
- package/dist/tests/enforcement.test.js +541 -0
- package/dist/tests/enforcement.test.js.map +1 -0
- package/dist/tests/enterprise.test.d.ts +2 -0
- package/dist/tests/enterprise.test.d.ts.map +1 -0
- package/dist/tests/enterprise.test.js +353 -0
- package/dist/tests/enterprise.test.js.map +1 -0
- package/dist/tests/fuzzing.test.d.ts +2 -0
- package/dist/tests/fuzzing.test.d.ts.map +1 -0
- package/dist/tests/fuzzing.test.js +596 -0
- package/dist/tests/fuzzing.test.js.map +1 -0
- package/dist/tests/knowledge.test.d.ts +2 -0
- package/dist/tests/knowledge.test.d.ts.map +1 -0
- package/dist/tests/knowledge.test.js +292 -0
- package/dist/tests/knowledge.test.js.map +1 -0
- package/dist/tests/management.test.d.ts +2 -0
- package/dist/tests/management.test.d.ts.map +1 -0
- package/dist/tests/management.test.js +338 -0
- package/dist/tests/management.test.js.map +1 -0
- package/dist/tests/scaffolding.test.d.ts +2 -0
- package/dist/tests/scaffolding.test.d.ts.map +1 -0
- package/dist/tests/scaffolding.test.js +419 -0
- package/dist/tests/scaffolding.test.js.map +1 -0
- package/dist/tests/test-utils.d.ts +76 -0
- package/dist/tests/test-utils.d.ts.map +1 -0
- package/dist/tests/test-utils.js +171 -0
- package/dist/tests/test-utils.js.map +1 -0
- package/dist/tests/tool-harness.d.ts +18 -0
- package/dist/tests/tool-harness.d.ts.map +1 -0
- package/dist/tests/tool-harness.js +51 -0
- package/dist/tests/tool-harness.js.map +1 -0
- package/dist/tools/enterprise.d.ts +8 -0
- package/dist/tools/enterprise.d.ts.map +1 -0
- package/dist/tools/enterprise.js +571 -0
- package/dist/tools/enterprise.js.map +1 -0
- package/dist/tools/knowledge.d.ts +7 -0
- package/dist/tools/knowledge.d.ts.map +1 -0
- package/dist/tools/knowledge.js +120 -0
- package/dist/tools/knowledge.js.map +1 -0
- package/dist/tools/management.d.ts +10 -0
- package/dist/tools/management.d.ts.map +1 -0
- package/dist/tools/management.js +1541 -0
- package/dist/tools/management.js.map +1 -0
- package/dist/tools/scaffolding.d.ts +8 -0
- package/dist/tools/scaffolding.d.ts.map +1 -0
- package/dist/tools/scaffolding.js +736 -0
- package/dist/tools/scaffolding.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/landing/app/layout.tsx +30 -0
- package/landing/app/page.tsx +944 -0
- package/landing/next-env.d.ts +6 -0
- package/landing/next.config.js +6 -0
- package/landing/package-lock.json +896 -0
- package/landing/package.json +20 -0
- package/landing/tsconfig.json +40 -0
- package/package.json +49 -0
- package/rebar-v2.0.0-platform-spec.md +1567 -0
- package/server.json +20 -0
- package/src/cli.ts +735 -0
- package/src/constants.ts +131 -0
- package/src/index.ts +54 -0
- package/src/schemas/common.ts +22 -0
- package/src/schemas/scaffolding.ts +161 -0
- package/src/services/claudemd-generator.ts +481 -0
- package/src/services/codex-generator.ts +44 -0
- package/src/services/cursor-generator.ts +153 -0
- package/src/services/file-ops.ts +172 -0
- package/src/services/platform-detect.ts +80 -0
- package/src/services/project-analyzer.ts +690 -0
- package/src/services/rules-engine.ts +353 -0
- package/src/services/strictness.ts +202 -0
- package/src/services/template-engine.ts +119 -0
- package/src/services/validation.ts +138 -0
- package/src/services/windsurf-generator.ts +145 -0
- package/src/tests/enforcement.test.ts +794 -0
- package/src/tests/enterprise.test.ts +483 -0
- package/src/tests/fuzzing.test.ts +690 -0
- package/src/tests/knowledge.test.ts +371 -0
- package/src/tests/management.test.ts +451 -0
- package/src/tests/scaffolding.test.ts +575 -0
- package/src/tests/test-utils.ts +206 -0
- package/src/tests/tool-harness.ts +70 -0
- package/src/tools/enterprise.ts +666 -0
- package/src/tools/knowledge.ts +162 -0
- package/src/tools/management.ts +1706 -0
- package/src/tools/scaffolding.ts +909 -0
- package/src/types.ts +93 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/templates/agents/explore.md +41 -0
- package/templates/agents/plan.md +73 -0
- package/templates/agents/security-auditor.md +77 -0
- package/templates/agents/test-runner.md +60 -0
- package/templates/claudemd/fastapi.md +49 -0
- package/templates/claudemd/monorepo.md +48 -0
- package/templates/claudemd/nextjs.md +52 -0
- package/templates/claudemd/react-spa.md +50 -0
- package/templates/claudemd/springboot.md +50 -0
- package/templates/hooks/danger-blocker.json +11 -0
- package/templates/hooks/format-on-write.json +17 -0
- package/templates/hooks/lint-on-write.json +16 -0
- package/templates/hooks/secret-detector.json +11 -0
- package/templates/skills/code-review.md +68 -0
- package/templates/skills/documentation.md +62 -0
- package/templates/skills/performance-audit.md +80 -0
- package/templates/skills/security-scan.md +66 -0
- package/templates/skills/test-writer.md +56 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File operations service with atomic writes and safety checks
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from "node:fs/promises";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import * as os from "node:os";
|
|
7
|
+
import * as crypto from "node:crypto";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ensures a directory exists, creating it recursively if needed.
|
|
11
|
+
*/
|
|
12
|
+
export async function ensureDir(dirPath: string): Promise<void> {
|
|
13
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Writes a file atomically: writes to a temp file first, then renames.
|
|
18
|
+
* This prevents partial writes from corrupting existing files.
|
|
19
|
+
*/
|
|
20
|
+
export async function atomicWrite(filePath: string, content: string): Promise<void> {
|
|
21
|
+
const dir = path.dirname(filePath);
|
|
22
|
+
await ensureDir(dir);
|
|
23
|
+
|
|
24
|
+
const tmpName = `.tmp-${crypto.randomBytes(8).toString("hex")}`;
|
|
25
|
+
const tmpPath = path.join(dir, tmpName);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await fs.writeFile(tmpPath, content, "utf-8");
|
|
29
|
+
await fs.rename(tmpPath, filePath);
|
|
30
|
+
} catch (err: unknown) {
|
|
31
|
+
// Clean up temp file on failure
|
|
32
|
+
try {
|
|
33
|
+
await fs.unlink(tmpPath);
|
|
34
|
+
} catch {
|
|
35
|
+
// Ignore cleanup errors
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a file exists.
|
|
43
|
+
*/
|
|
44
|
+
export async function fileExists(filePath: string): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
await fs.access(filePath);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reads a file and returns its content, or null if it doesn't exist.
|
|
55
|
+
*/
|
|
56
|
+
export async function readFileSafe(filePath: string): Promise<string | null> {
|
|
57
|
+
try {
|
|
58
|
+
return await fs.readFile(filePath, "utf-8");
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Lists files in a directory matching an optional extension filter.
|
|
66
|
+
*/
|
|
67
|
+
export async function listFiles(
|
|
68
|
+
dirPath: string,
|
|
69
|
+
extension?: string
|
|
70
|
+
): Promise<string[]> {
|
|
71
|
+
try {
|
|
72
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
73
|
+
return entries
|
|
74
|
+
.filter((e) => e.isFile() && (!extension || e.name.endsWith(extension)))
|
|
75
|
+
.map((e) => path.join(dirPath, e.name));
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Recursively lists all files under a directory.
|
|
83
|
+
*/
|
|
84
|
+
export async function listFilesRecursive(
|
|
85
|
+
dirPath: string,
|
|
86
|
+
extension?: string
|
|
87
|
+
): Promise<string[]> {
|
|
88
|
+
const results: string[] = [];
|
|
89
|
+
try {
|
|
90
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
const sub = await listFilesRecursive(fullPath, extension);
|
|
95
|
+
results.push(...sub);
|
|
96
|
+
} else if (!extension || entry.name.endsWith(extension)) {
|
|
97
|
+
results.push(fullPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
// Directory doesn't exist or not readable
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolves a project path to an absolute path.
|
|
108
|
+
*/
|
|
109
|
+
export function resolveProjectPath(projectPath: string): string {
|
|
110
|
+
if (path.isAbsolute(projectPath)) {
|
|
111
|
+
return path.normalize(projectPath);
|
|
112
|
+
}
|
|
113
|
+
return path.resolve(process.cwd(), projectPath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sanitizes a file path to prevent directory traversal attacks.
|
|
118
|
+
* Ensures the resolved path is within the project root.
|
|
119
|
+
*/
|
|
120
|
+
export function sanitizePath(basePath: string, relativePath: string): string {
|
|
121
|
+
const resolved = path.resolve(basePath, relativePath);
|
|
122
|
+
const normalizedBase = path.normalize(basePath);
|
|
123
|
+
if (!resolved.startsWith(normalizedBase)) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Path traversal detected: "${relativePath}" resolves outside project root`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return resolved;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Writes a file only if it doesn't exist or mergeExisting is true.
|
|
133
|
+
* Returns info about what was done.
|
|
134
|
+
*/
|
|
135
|
+
export async function safeWriteFile(
|
|
136
|
+
filePath: string,
|
|
137
|
+
content: string,
|
|
138
|
+
mergeExisting: boolean
|
|
139
|
+
): Promise<{ written: boolean; merged: boolean; message: string }> {
|
|
140
|
+
const exists = await fileExists(filePath);
|
|
141
|
+
|
|
142
|
+
if (exists && !mergeExisting) {
|
|
143
|
+
return {
|
|
144
|
+
written: false,
|
|
145
|
+
merged: false,
|
|
146
|
+
message: `File already exists: ${filePath}. Set merge_existing=true to overwrite.`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (exists && mergeExisting) {
|
|
151
|
+
await atomicWrite(filePath, content);
|
|
152
|
+
return {
|
|
153
|
+
written: true,
|
|
154
|
+
merged: true,
|
|
155
|
+
message: `Merged/updated: ${filePath}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await atomicWrite(filePath, content);
|
|
160
|
+
return {
|
|
161
|
+
written: true,
|
|
162
|
+
merged: false,
|
|
163
|
+
message: `Created: ${filePath}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Creates a temp directory for intermediate operations.
|
|
169
|
+
*/
|
|
170
|
+
export async function createTempDir(): Promise<string> {
|
|
171
|
+
return await fs.mkdtemp(path.join(os.tmpdir(), "rebar-"));
|
|
172
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection for multi-AI-tool support.
|
|
3
|
+
*
|
|
4
|
+
* Detects which AI coding platforms are configured in a project:
|
|
5
|
+
* - Claude Code: .claude/ directory or CLAUDE.md
|
|
6
|
+
* - Cursor: .cursor/ directory or .cursorrules
|
|
7
|
+
* - Windsurf: .windsurf/ directory
|
|
8
|
+
* - Codex CLI: AGENTS.md
|
|
9
|
+
*/
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { fileExists } from "./file-ops.js";
|
|
12
|
+
|
|
13
|
+
export type AIPlatform = "claude-code" | "cursor" | "windsurf" | "codex";
|
|
14
|
+
|
|
15
|
+
export interface DetectedPlatforms {
|
|
16
|
+
platforms: AIPlatform[];
|
|
17
|
+
evidence: Record<AIPlatform, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Detects which AI coding platforms are configured in a project.
|
|
22
|
+
*/
|
|
23
|
+
export async function detectPlatforms(projectPath: string): Promise<DetectedPlatforms> {
|
|
24
|
+
const platforms: AIPlatform[] = [];
|
|
25
|
+
const evidence: Partial<Record<AIPlatform, string>> = {};
|
|
26
|
+
|
|
27
|
+
// Claude Code: .claude/ directory or CLAUDE.md exists
|
|
28
|
+
if (await fileExists(path.join(projectPath, ".claude")) || await fileExists(path.join(projectPath, "CLAUDE.md"))) {
|
|
29
|
+
platforms.push("claude-code");
|
|
30
|
+
evidence["claude-code"] = "Found .claude/ directory or CLAUDE.md";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Cursor: .cursor/ directory or .cursorrules exists
|
|
34
|
+
if (await fileExists(path.join(projectPath, ".cursor")) || await fileExists(path.join(projectPath, ".cursorrules"))) {
|
|
35
|
+
platforms.push("cursor");
|
|
36
|
+
evidence["cursor"] = "Found .cursor/ directory or .cursorrules";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Windsurf: .windsurf/ directory exists
|
|
40
|
+
if (await fileExists(path.join(projectPath, ".windsurf"))) {
|
|
41
|
+
platforms.push("windsurf");
|
|
42
|
+
evidence["windsurf"] = "Found .windsurf/ directory";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Codex: AGENTS.md exists
|
|
46
|
+
if (await fileExists(path.join(projectPath, "AGENTS.md"))) {
|
|
47
|
+
platforms.push("codex");
|
|
48
|
+
evidence["codex"] = "Found AGENTS.md";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If nothing detected, default to Claude Code (since we're an MCP server running in it)
|
|
52
|
+
if (platforms.length === 0) {
|
|
53
|
+
platforms.push("claude-code");
|
|
54
|
+
evidence["claude-code"] = "Default (no platform detected)";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
platforms,
|
|
59
|
+
evidence: evidence as Record<AIPlatform, string>,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolves "all" platform specifier to actual platforms.
|
|
65
|
+
*/
|
|
66
|
+
export async function resolvePlatforms(
|
|
67
|
+
projectPath: string,
|
|
68
|
+
specified: string[]
|
|
69
|
+
): Promise<AIPlatform[]> {
|
|
70
|
+
if (specified.includes("all")) {
|
|
71
|
+
// Detect existing platforms, or generate for all if none found
|
|
72
|
+
const detected = await detectPlatforms(projectPath);
|
|
73
|
+
if (detected.platforms.length <= 1 && detected.evidence["claude-code"]?.includes("Default")) {
|
|
74
|
+
// Nothing detected, generate for all platforms
|
|
75
|
+
return ["claude-code", "cursor", "windsurf", "codex"];
|
|
76
|
+
}
|
|
77
|
+
return detected.platforms;
|
|
78
|
+
}
|
|
79
|
+
return specified.filter((p) => p !== "all") as AIPlatform[];
|
|
80
|
+
}
|