harness-bujang 0.5.8 → 0.5.10
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/dist/adapt-VPWOYF6W.js +404 -0
- package/dist/chat-6VQQYMBV.js +588 -0
- package/dist/chunk-7DAHO2GN.js +17 -0
- package/dist/chunk-FE2GLGVF.js +592 -0
- package/dist/index.js +43 -2123
- package/dist/init-S3YF6RT4.js +9 -0
- package/dist/migrate-PISZFX6C.js +201 -0
- package/dist/status-UE2TQQPU.js +132 -0
- package/dist/update-4WS42LXD.js +181 -0
- package/package.json +1 -1
- package/templates/agents/en/cofounder.md +8 -0
- package/templates/agents/en/director.md +11 -1
- package/templates/agents/ko/cofounder.md +8 -0
- package/templates/agents/ko/director.md +11 -1
- package/templates/templates/en/CLAUDE.md.harness-section.template +17 -6
- package/templates/templates/ko/CLAUDE.md.harness-section.template +17 -6
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
// src/adapt.ts
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
var c = {
|
|
5
|
+
bold: (s) => `\x1B[1m${s}\x1B[22m`,
|
|
6
|
+
dim: (s) => `\x1B[2m${s}\x1B[22m`,
|
|
7
|
+
green: (s) => `\x1B[32m${s}\x1B[39m`,
|
|
8
|
+
red: (s) => `\x1B[31m${s}\x1B[39m`,
|
|
9
|
+
yellow: (s) => `\x1B[33m${s}\x1B[39m`,
|
|
10
|
+
cyan: (s) => `\x1B[36m${s}\x1B[39m`
|
|
11
|
+
};
|
|
12
|
+
async function runAdapt(args) {
|
|
13
|
+
const opts = parseArgs(args);
|
|
14
|
+
const agentsDir = path.join(opts.target, ".claude/agents");
|
|
15
|
+
if (!await exists(agentsDir)) {
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(c.red("\u2716 No .claude/agents/ directory found at " + agentsDir));
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(" Run " + c.bold("npx harness-bujang init") + " first to install the canonical agents,");
|
|
20
|
+
console.log(" then re-run this command to adapt them to your editor.");
|
|
21
|
+
console.log();
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const agentFiles = await loadAgents(agentsDir);
|
|
26
|
+
if (agentFiles.length === 0) {
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(c.red("\u2716 .claude/agents/ exists but contains no .md files."));
|
|
29
|
+
console.log();
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(c.bold("\u{1F501} Harness-Bujang adapt"));
|
|
35
|
+
console.log(c.dim(` Target: ${opts.target}`));
|
|
36
|
+
console.log(c.dim(` Agents: ${agentFiles.length} files at .claude/agents/`));
|
|
37
|
+
console.log(c.dim(` Adapting: ${opts.to.join(", ")}`));
|
|
38
|
+
console.log();
|
|
39
|
+
for (const target of opts.to) {
|
|
40
|
+
if (target === "cursor") await adaptCursor(opts.target, agentFiles, opts.yes);
|
|
41
|
+
if (target === "cline") await adaptCline(opts.target, agentFiles, opts.yes);
|
|
42
|
+
if (target === "aider") await adaptAider(opts.target, agentFiles, opts.yes);
|
|
43
|
+
if (target === "codex") await adaptCodex(opts.target, agentFiles, opts.yes);
|
|
44
|
+
if (target === "gemini") await adaptGemini(opts.target, agentFiles, opts.yes);
|
|
45
|
+
}
|
|
46
|
+
console.log(c.bold(c.green("\u2705 Done.")));
|
|
47
|
+
console.log();
|
|
48
|
+
console.log("Next:");
|
|
49
|
+
if (opts.to.includes("cursor")) {
|
|
50
|
+
console.log(` ${c.cyan("\u2022")} Cursor users: open the project \u2014 rules in ${c.bold(".cursor/rules/")} are auto-loaded`);
|
|
51
|
+
}
|
|
52
|
+
if (opts.to.includes("cline")) {
|
|
53
|
+
console.log(` ${c.cyan("\u2022")} Cline users: rules in ${c.bold(".clinerules/")} are auto-loaded by Cline`);
|
|
54
|
+
}
|
|
55
|
+
if (opts.to.includes("aider")) {
|
|
56
|
+
console.log(` ${c.cyan("\u2022")} Aider users: ${c.bold("CONVENTIONS.md")} is loaded via ${c.bold(".aider.conf.yml")} (read:)`);
|
|
57
|
+
}
|
|
58
|
+
if (opts.to.includes("codex")) {
|
|
59
|
+
console.log(` ${c.cyan("\u2022")} Codex / Copilot Coding Agent users: ${c.bold("AGENTS.md")} at the project root is auto-loaded`);
|
|
60
|
+
}
|
|
61
|
+
if (opts.to.includes("gemini")) {
|
|
62
|
+
console.log(` ${c.cyan("\u2022")} Antigravity / Gemini CLI / Code Assist: ${c.bold("GEMINI.md")} (highest precedence) + ${c.bold(".gemini/styleguide.md")} (PR reviews)`);
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(c.dim(" When you change .claude/agents/ later, re-run this command to refresh."));
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
68
|
+
async function adaptCursor(target, agents, overwrite) {
|
|
69
|
+
const dst = path.join(target, ".cursor/rules");
|
|
70
|
+
await fs.mkdir(dst, { recursive: true });
|
|
71
|
+
console.log(c.bold("\u{1F4C2} Cursor \u2014 .cursor/rules/"));
|
|
72
|
+
for (const a of agents) {
|
|
73
|
+
const file = path.join(dst, `bujang-${a.slug}.mdc`);
|
|
74
|
+
if (await exists(file) && !overwrite) {
|
|
75
|
+
console.log(` ${c.yellow("\u26A0")} bujang-${a.slug}.mdc ${c.dim("(exists, skipped \u2014 use --yes to overwrite)")}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const description = a.frontmatter.description || `Harness-Bujang ${a.slug}`;
|
|
79
|
+
const out = `---
|
|
80
|
+
description: "Harness-Bujang ${a.slug}: ${escapeYamlString(description.replace(/\n/g, " ").slice(0, 240))}"
|
|
81
|
+
alwaysApply: false
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
# Harness-Bujang \u2014 ${a.slug} role guide
|
|
85
|
+
|
|
86
|
+
> Source of truth: \`.claude/agents/${a.slug}.md\` \u2014 re-run \`bujang adapt --to=cursor\` to sync.
|
|
87
|
+
|
|
88
|
+
When the user request matches this role's domain (see description above), follow this guide as your primary system prompt for the response. Other rules under this directory describe sibling roles in the same harness.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
` + a.body.trim() + `
|
|
93
|
+
`;
|
|
94
|
+
await fs.writeFile(file, out);
|
|
95
|
+
console.log(` ${c.green("\u2713")} bujang-${a.slug}.mdc`);
|
|
96
|
+
}
|
|
97
|
+
console.log();
|
|
98
|
+
}
|
|
99
|
+
async function adaptCline(target, agents, overwrite) {
|
|
100
|
+
const dst = path.join(target, ".clinerules");
|
|
101
|
+
await fs.mkdir(dst, { recursive: true });
|
|
102
|
+
console.log(c.bold("\u{1F4C2} Cline \u2014 .clinerules/"));
|
|
103
|
+
for (const a of agents) {
|
|
104
|
+
const file = path.join(dst, `bujang-${a.slug}.md`);
|
|
105
|
+
if (await exists(file) && !overwrite) {
|
|
106
|
+
console.log(` ${c.yellow("\u26A0")} bujang-${a.slug}.md ${c.dim("(exists, skipped \u2014 use --yes to overwrite)")}`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const description = a.frontmatter.description || "";
|
|
110
|
+
const out = `# Harness-Bujang \u2014 ${a.slug}
|
|
111
|
+
|
|
112
|
+
` + (description ? `${description}
|
|
113
|
+
|
|
114
|
+
` : "") + `> Source of truth: \`.claude/agents/${a.slug}.md\` \u2014 re-run \`bujang adapt --to=cline\` to sync.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
` + a.body.trim() + `
|
|
119
|
+
`;
|
|
120
|
+
await fs.writeFile(file, out);
|
|
121
|
+
console.log(` ${c.green("\u2713")} bujang-${a.slug}.md`);
|
|
122
|
+
}
|
|
123
|
+
console.log();
|
|
124
|
+
}
|
|
125
|
+
async function adaptAider(target, agents, overwrite) {
|
|
126
|
+
console.log(c.bold("\u{1F4C2} Aider \u2014 CONVENTIONS.md + .aider.conf.yml"));
|
|
127
|
+
const conventionsPath = path.join(target, "CONVENTIONS.md");
|
|
128
|
+
const conventionsExisted = await exists(conventionsPath);
|
|
129
|
+
if (conventionsExisted && !overwrite) {
|
|
130
|
+
console.log(` ${c.yellow("\u26A0")} CONVENTIONS.md ${c.dim("(exists, skipped \u2014 use --yes to overwrite)")}`);
|
|
131
|
+
} else {
|
|
132
|
+
let body = `# Project Conventions \u2014 Harness-Bujang
|
|
133
|
+
|
|
134
|
+
`;
|
|
135
|
+
body += `> Source of truth: \`.claude/agents/*.md\` \u2014 re-run \`bujang adapt --to=aider\` to sync.
|
|
136
|
+
|
|
137
|
+
`;
|
|
138
|
+
body += `This file collects the multi-agent harness role guides into a single conventions file that Aider can load via \`.aider.conf.yml\`. Aider does not natively dispatch to subagents, so when the user's request matches a specific role's domain, internally adopt that role's instructions for the response.
|
|
139
|
+
|
|
140
|
+
`;
|
|
141
|
+
body += `## Roles
|
|
142
|
+
|
|
143
|
+
`;
|
|
144
|
+
for (const a of agents) {
|
|
145
|
+
const desc = a.frontmatter.description || "";
|
|
146
|
+
body += `- **${a.slug}**${desc ? ` \u2014 ${desc.replace(/\n/g, " ").slice(0, 200)}` : ""}
|
|
147
|
+
`;
|
|
148
|
+
}
|
|
149
|
+
body += `
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
`;
|
|
153
|
+
for (const a of agents) {
|
|
154
|
+
body += `## ${a.slug}
|
|
155
|
+
|
|
156
|
+
`;
|
|
157
|
+
body += a.body.trim() + `
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
await fs.writeFile(conventionsPath, body);
|
|
164
|
+
console.log(` ${c.green("\u2713")} CONVENTIONS.md ${c.dim(`(${agents.length} roles concatenated)`)}`);
|
|
165
|
+
}
|
|
166
|
+
const aiderConfPath = path.join(target, ".aider.conf.yml");
|
|
167
|
+
const existing = await exists(aiderConfPath) ? await fs.readFile(aiderConfPath, "utf8") : "";
|
|
168
|
+
if (existing.includes("CONVENTIONS.md")) {
|
|
169
|
+
console.log(` ${c.dim("\u2022")} .aider.conf.yml already references CONVENTIONS.md \u2014 left untouched`);
|
|
170
|
+
} else if (existing && !overwrite) {
|
|
171
|
+
console.log(` ${c.yellow("\u26A0")} .aider.conf.yml exists and does NOT reference CONVENTIONS.md \u2014 skipped`);
|
|
172
|
+
console.log(` ${c.dim("Add manually:")} read: CONVENTIONS.md`);
|
|
173
|
+
} else {
|
|
174
|
+
const out = existing ? existing.trimEnd() + `
|
|
175
|
+
|
|
176
|
+
# Added by harness-bujang adapt
|
|
177
|
+
read: CONVENTIONS.md
|
|
178
|
+
` : `# Aider config \u2014 auto-loads Harness-Bujang conventions
|
|
179
|
+
read: CONVENTIONS.md
|
|
180
|
+
`;
|
|
181
|
+
await fs.writeFile(aiderConfPath, out);
|
|
182
|
+
console.log(` ${c.green("\u2713")} .aider.conf.yml ${c.dim("(read: CONVENTIONS.md)")}`);
|
|
183
|
+
}
|
|
184
|
+
console.log();
|
|
185
|
+
}
|
|
186
|
+
async function adaptCodex(target, agents, overwrite) {
|
|
187
|
+
console.log(c.bold("\u{1F4C2} Codex / Copilot Agent \u2014 AGENTS.md (project root)"));
|
|
188
|
+
const filePath = path.join(target, "AGENTS.md");
|
|
189
|
+
if (await exists(filePath) && !overwrite) {
|
|
190
|
+
console.log(` ${c.yellow("\u26A0")} AGENTS.md ${c.dim("(exists, skipped \u2014 use --yes to overwrite)")}`);
|
|
191
|
+
console.log();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
let body = `# AGENTS.md \u2014 Harness-Bujang multi-agent harness
|
|
195
|
+
|
|
196
|
+
`;
|
|
197
|
+
body += `> Source of truth: \`.claude/agents/*.md\` \u2014 re-run \`bujang adapt --to=codex\` to sync.
|
|
198
|
+
|
|
199
|
+
`;
|
|
200
|
+
body += `This file follows the AGENTS.md convention adopted by OpenAI Codex CLI, GitHub Copilot Coding Agent, and several other agentic coding tools. It collects the harness role guides into a single document.
|
|
201
|
+
|
|
202
|
+
`;
|
|
203
|
+
body += `When the user's request matches one of the role domains below, internally adopt that role's instructions for the response. If the request spans multiple domains, follow the **director** role's dispatch logic.
|
|
204
|
+
|
|
205
|
+
`;
|
|
206
|
+
body += `## Roles
|
|
207
|
+
|
|
208
|
+
`;
|
|
209
|
+
for (const a of agents) {
|
|
210
|
+
const desc = a.frontmatter.description || "";
|
|
211
|
+
body += `- **${a.slug}**${desc ? ` \u2014 ${desc.replace(/\n/g, " ").slice(0, 200)}` : ""}
|
|
212
|
+
`;
|
|
213
|
+
}
|
|
214
|
+
body += `
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
`;
|
|
218
|
+
for (const a of agents) {
|
|
219
|
+
body += `## ${a.slug}
|
|
220
|
+
|
|
221
|
+
`;
|
|
222
|
+
body += a.body.trim() + `
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
`;
|
|
227
|
+
}
|
|
228
|
+
await fs.writeFile(filePath, body);
|
|
229
|
+
console.log(` ${c.green("\u2713")} AGENTS.md ${c.dim(`(${agents.length} roles concatenated, ${(body.length / 1024).toFixed(1)} KB)`)}`);
|
|
230
|
+
console.log();
|
|
231
|
+
}
|
|
232
|
+
async function adaptGemini(target, agents, overwrite) {
|
|
233
|
+
console.log(c.bold("\u{1F4C2} Gemini / Antigravity \u2014 GEMINI.md + .gemini/styleguide.md"));
|
|
234
|
+
const geminiMdPath = path.join(target, "GEMINI.md");
|
|
235
|
+
if (await exists(geminiMdPath) && !overwrite) {
|
|
236
|
+
console.log(` ${c.yellow("\u26A0")} GEMINI.md ${c.dim("(exists, skipped \u2014 use --yes to overwrite)")}`);
|
|
237
|
+
} else {
|
|
238
|
+
let body = `# GEMINI.md \u2014 Harness-Bujang multi-agent harness
|
|
239
|
+
|
|
240
|
+
`;
|
|
241
|
+
body += `> Source of truth: \`.claude/agents/*.md\` \u2014 re-run \`bujang adapt --to=gemini\` to sync.
|
|
242
|
+
|
|
243
|
+
`;
|
|
244
|
+
body += `This file is read by Google Antigravity (workspace highest priority), Gemini CLI, and Gemini Code Assist (workspace customization). It collects the harness role guides into a single document.
|
|
245
|
+
|
|
246
|
+
`;
|
|
247
|
+
body += `When the user's request matches one of the role domains below, internally adopt that role's instructions for the response. If the request spans multiple domains, follow the **director** role's dispatch logic.
|
|
248
|
+
|
|
249
|
+
`;
|
|
250
|
+
body += `## Roles
|
|
251
|
+
|
|
252
|
+
`;
|
|
253
|
+
for (const a of agents) {
|
|
254
|
+
const desc = a.frontmatter.description || "";
|
|
255
|
+
body += `- **${a.slug}**${desc ? ` \u2014 ${desc.replace(/\n/g, " ").slice(0, 200)}` : ""}
|
|
256
|
+
`;
|
|
257
|
+
}
|
|
258
|
+
body += `
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
`;
|
|
262
|
+
for (const a of agents) {
|
|
263
|
+
body += `## ${a.slug}
|
|
264
|
+
|
|
265
|
+
`;
|
|
266
|
+
body += a.body.trim() + `
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
await fs.writeFile(geminiMdPath, body);
|
|
273
|
+
console.log(` ${c.green("\u2713")} GEMINI.md ${c.dim(`(${agents.length} roles concatenated, ${(body.length / 1024).toFixed(1)} KB)`)}`);
|
|
274
|
+
}
|
|
275
|
+
const styleguideDir = path.join(target, ".gemini");
|
|
276
|
+
await fs.mkdir(styleguideDir, { recursive: true });
|
|
277
|
+
const styleguidePath = path.join(styleguideDir, "styleguide.md");
|
|
278
|
+
if (await exists(styleguidePath) && !overwrite) {
|
|
279
|
+
console.log(` ${c.yellow("\u26A0")} .gemini/styleguide.md ${c.dim("(exists, skipped)")}`);
|
|
280
|
+
} else {
|
|
281
|
+
const reviewRoles = ["code-review-team", "security-team", "db-guard-team", "verifier-team"];
|
|
282
|
+
const reviewAgents = agents.filter((a) => reviewRoles.includes(a.slug));
|
|
283
|
+
let body = `# Code Review Style Guide \u2014 Harness-Bujang
|
|
284
|
+
|
|
285
|
+
`;
|
|
286
|
+
body += `> Source of truth: \`.claude/agents/*.md\` \u2014 re-run \`bujang adapt --to=gemini\` to sync.
|
|
287
|
+
|
|
288
|
+
`;
|
|
289
|
+
body += `This style guide is read by Gemini Code Assist for GitHub when reviewing PRs. It distills the review-relevant subset of the Harness-Bujang harness (code review, security, DB guard, verifier teams) into review criteria.
|
|
290
|
+
|
|
291
|
+
`;
|
|
292
|
+
body += `When reviewing a PR, apply the following audit lenses in order:
|
|
293
|
+
|
|
294
|
+
`;
|
|
295
|
+
for (const a of reviewAgents) {
|
|
296
|
+
body += `## ${a.slug}
|
|
297
|
+
|
|
298
|
+
`;
|
|
299
|
+
body += a.body.trim() + `
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
if (reviewAgents.length === 0) {
|
|
306
|
+
body += `_(No review-team agents found in .claude/agents/. Re-run init to install the canonical set.)_
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
await fs.writeFile(styleguidePath, body);
|
|
310
|
+
console.log(` ${c.green("\u2713")} .gemini/styleguide.md ${c.dim(`(${reviewAgents.length} review roles)`)}`);
|
|
311
|
+
}
|
|
312
|
+
console.log();
|
|
313
|
+
}
|
|
314
|
+
async function loadAgents(agentsDir) {
|
|
315
|
+
const entries = await fs.readdir(agentsDir);
|
|
316
|
+
const out = [];
|
|
317
|
+
for (const name of entries) {
|
|
318
|
+
if (!name.endsWith(".md")) continue;
|
|
319
|
+
const src = path.join(agentsDir, name);
|
|
320
|
+
const raw = await fs.readFile(src, "utf8");
|
|
321
|
+
const slug = name.replace(/\.md$/, "");
|
|
322
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
323
|
+
out.push({ slug, frontmatter, body, src });
|
|
324
|
+
}
|
|
325
|
+
out.sort((a, b) => {
|
|
326
|
+
if (a.slug === "director") return -1;
|
|
327
|
+
if (b.slug === "director") return 1;
|
|
328
|
+
return a.slug.localeCompare(b.slug);
|
|
329
|
+
});
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
function splitFrontmatter(raw) {
|
|
333
|
+
if (!raw.startsWith("---\n")) {
|
|
334
|
+
return { frontmatter: {}, body: raw };
|
|
335
|
+
}
|
|
336
|
+
const end = raw.indexOf("\n---\n", 4);
|
|
337
|
+
if (end < 0) {
|
|
338
|
+
return { frontmatter: {}, body: raw };
|
|
339
|
+
}
|
|
340
|
+
const fmRaw = raw.slice(4, end);
|
|
341
|
+
const body = raw.slice(end + 5);
|
|
342
|
+
const frontmatter = {};
|
|
343
|
+
const lines = fmRaw.split(/\r?\n/);
|
|
344
|
+
let currentKey = null;
|
|
345
|
+
for (const line of lines) {
|
|
346
|
+
const m = /^([a-zA-Z_-]+):\s?(.*)$/.exec(line);
|
|
347
|
+
if (m && m[1] && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
348
|
+
currentKey = m[1];
|
|
349
|
+
frontmatter[currentKey] = m[2] ?? "";
|
|
350
|
+
} else if (currentKey && (line.startsWith(" ") || line.startsWith(" "))) {
|
|
351
|
+
frontmatter[currentKey] = (frontmatter[currentKey] ?? "") + " " + line.trim();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return { frontmatter, body };
|
|
355
|
+
}
|
|
356
|
+
function escapeYamlString(s) {
|
|
357
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
358
|
+
}
|
|
359
|
+
function parseArgs(args) {
|
|
360
|
+
const targetRaw = getFlag(args, "--target") ?? ".";
|
|
361
|
+
const toRaw = getFlag(args, "--to");
|
|
362
|
+
if (!toRaw) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`--to=<cursor|cline|aider|codex|gemini|all> is required. Examples:
|
|
365
|
+
bujang adapt --to=cursor
|
|
366
|
+
bujang adapt --to=codex # AGENTS.md at project root
|
|
367
|
+
bujang adapt --to=gemini # GEMINI.md + .gemini/styleguide.md
|
|
368
|
+
bujang adapt --to=cursor,aider # multiple
|
|
369
|
+
bujang adapt --to=all # cursor + cline + aider + codex + gemini`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const targets = toRaw === "all" ? ["cursor", "cline", "aider", "codex", "gemini"] : toRaw.split(",").map((t) => t.trim());
|
|
373
|
+
for (const t of targets) {
|
|
374
|
+
if (!["cursor", "cline", "aider", "codex", "gemini"].includes(t)) {
|
|
375
|
+
throw new Error(`Unknown adapter target "${t}" \u2014 expected one of: cursor, cline, aider, codex, gemini, all`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
target: path.resolve(targetRaw),
|
|
380
|
+
to: targets,
|
|
381
|
+
yes: args.includes("--yes") || args.includes("-y")
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function getFlag(args, name) {
|
|
385
|
+
for (const a of args) {
|
|
386
|
+
if (a.startsWith(`${name}=`)) return a.slice(name.length + 1);
|
|
387
|
+
}
|
|
388
|
+
const idx = args.indexOf(name);
|
|
389
|
+
if (idx >= 0 && idx + 1 < args.length && !args[idx + 1].startsWith("--")) {
|
|
390
|
+
return args[idx + 1];
|
|
391
|
+
}
|
|
392
|
+
return void 0;
|
|
393
|
+
}
|
|
394
|
+
async function exists(p) {
|
|
395
|
+
try {
|
|
396
|
+
await fs.access(p);
|
|
397
|
+
return true;
|
|
398
|
+
} catch {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
export {
|
|
403
|
+
runAdapt
|
|
404
|
+
};
|