harness-bujang 0.7.1 → 0.8.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.
|
@@ -216,7 +216,7 @@ async function runInit(args) {
|
|
|
216
216
|
opts = await promptInteractive(opts, scan);
|
|
217
217
|
if (await isExistingInstall(opts.target)) {
|
|
218
218
|
const overwrite = await confirm({
|
|
219
|
-
message: "
|
|
219
|
+
message: "\uAE30\uC874 \uD558\uB124\uC2A4 \uC124\uCE58\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC120\uD0DD\uD55C \uC124\uC815\uC744 \uC801\uC6A9\uD558\uAE30 \uC704\uD574 \uBAA8\uB4E0 \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F8\uAE4C\uC694?",
|
|
220
220
|
default: false
|
|
221
221
|
});
|
|
222
222
|
if (overwrite) opts.yes = true;
|
|
@@ -233,7 +233,10 @@ async function runInit(args) {
|
|
|
233
233
|
console.log(c.dim(` Language: ${opts.lang}`));
|
|
234
234
|
console.log(c.dim(` Chat backend: ${opts.chatBackend}${opts.chatBackend === "sqlite" ? " (local file)" : " (cloud Postgres)"}`));
|
|
235
235
|
console.log(c.dim(` Tools: claude${opts.adapters.length > 0 ? ` + ${opts.adapters.join(", ")}` : " (only)"}`));
|
|
236
|
-
console.log(c.dim(`
|
|
236
|
+
console.log(c.dim(` Claude model: ${describeModelMap(opts.modelMap)}`));
|
|
237
|
+
if (opts.codexModel && opts.codexModel !== "skip") console.log(c.dim(` Codex model: ${opts.codexModel} (memo)`));
|
|
238
|
+
if (opts.geminiModel && opts.geminiModel !== "skip") console.log(c.dim(` Gemini model: ${opts.geminiModel} (memo)`));
|
|
239
|
+
if (opts.aiderModel && opts.aiderModel !== "(skip)") console.log(c.dim(` Aider model: ${opts.aiderModel} (.aider.conf.yml)`));
|
|
237
240
|
if (scan.framework.startsWith("Next.js")) {
|
|
238
241
|
console.log(c.dim(` Chat-room UI: ${opts.installTemplate ? "install" : "skip"}`));
|
|
239
242
|
}
|
|
@@ -241,7 +244,7 @@ async function runInit(args) {
|
|
|
241
244
|
console.log();
|
|
242
245
|
if (interactive) {
|
|
243
246
|
try {
|
|
244
|
-
const proceed = await confirm({ message: "
|
|
247
|
+
const proceed = await confirm({ message: "\uC774 \uC124\uC815\uC73C\uB85C \uC9C4\uD589\uD560\uAE4C\uC694?", default: true });
|
|
245
248
|
if (!proceed) {
|
|
246
249
|
console.log(c.dim(" (aborted)"));
|
|
247
250
|
return;
|
|
@@ -416,6 +419,9 @@ async function runInit(args) {
|
|
|
416
419
|
`--target=${opts.target}`,
|
|
417
420
|
"--yes"
|
|
418
421
|
]);
|
|
422
|
+
if (opts.codexModel && opts.codexModel !== "skip") await injectCodexModelMemo(opts.target, opts.codexModel);
|
|
423
|
+
if (opts.geminiModel && opts.geminiModel !== "skip") await injectGeminiModelMemo(opts.target, opts.geminiModel);
|
|
424
|
+
if (opts.aiderModel && opts.aiderModel !== "(skip)") await setAiderModel(opts.target, opts.aiderModel);
|
|
419
425
|
}
|
|
420
426
|
console.log(c.bold(c.green("\u2705 Done.")));
|
|
421
427
|
console.log();
|
|
@@ -453,25 +459,26 @@ function printRestartReminder(lang) {
|
|
|
453
459
|
}
|
|
454
460
|
async function promptInteractive(opts, scan) {
|
|
455
461
|
const lang = await select({
|
|
456
|
-
message: "
|
|
462
|
+
message: "\uC5D0\uC774\uC804\uD2B8 \uC5B8\uC5B4 / Agent language",
|
|
457
463
|
choices: [
|
|
458
|
-
{ name: "\uD55C\uAD6D\uC5B4 \u2014
|
|
459
|
-
{ name: "English", value: "en" }
|
|
464
|
+
{ name: "\uD55C\uAD6D\uC5B4 \u2014 \uBD80\uC7A5 \uD398\uB974\uC18C\uB098 \uD480 (Korean)", value: "ko" },
|
|
465
|
+
{ name: "English \u2014 Director persona", value: "en" }
|
|
460
466
|
],
|
|
461
467
|
default: opts.lang
|
|
462
468
|
});
|
|
463
469
|
const chatBackend = await select({
|
|
464
|
-
message: "
|
|
470
|
+
message: "\uD1A1\uBC29 \uBC31\uC5D4\uB4DC",
|
|
465
471
|
choices: [
|
|
466
|
-
{ name: "SQLite \u2014
|
|
467
|
-
{ name: "Supabase \u2014
|
|
472
|
+
{ name: "SQLite \u2014 \uB85C\uCEEC \uD30C\uC77C, \uC14B\uC5C5 \uBD88\uD544\uC694 (\uCD94\uCC9C)", value: "sqlite" },
|
|
473
|
+
{ name: "Supabase \u2014 \uD074\uB77C\uC6B0\uB4DC Postgres (\uD300 \uACF5\uC720\uC6A9)", value: "supabase" }
|
|
468
474
|
],
|
|
469
475
|
default: opts.chatBackend
|
|
470
476
|
});
|
|
471
477
|
const isPreset = (t) => opts.adapters.includes(t);
|
|
472
|
-
const
|
|
473
|
-
message: "
|
|
478
|
+
const adaptersRaw = await checkbox({
|
|
479
|
+
message: "\uB3C4\uAD6C \uC120\uD0DD \u2014 \uCCB4\uD06C\uB41C \uB3C4\uAD6C\uB9CC \uC14B\uC5C5\uB429\uB2C8\uB2E4. (.claude/agents/ \uB294 \uC5B4\uB311\uD130 SoT \uB77C \uD56D\uC0C1 \uAE54\uB9BD\uB2C8\uB2E4)",
|
|
474
480
|
choices: [
|
|
481
|
+
{ name: "Claude Code (.claude/agents/)", value: "claude", checked: true },
|
|
475
482
|
{ name: "Cursor (.cursor/rules/bujang-*.mdc)", value: "cursor", checked: isPreset("cursor") },
|
|
476
483
|
{ name: "Codex / Copilot (AGENTS.md)", value: "codex", checked: isPreset("codex") },
|
|
477
484
|
{ name: "Cline (.clinerules/bujang-*.md)", value: "cline", checked: isPreset("cline") },
|
|
@@ -480,44 +487,97 @@ async function promptInteractive(opts, scan) {
|
|
|
480
487
|
],
|
|
481
488
|
required: false
|
|
482
489
|
});
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
choices: [
|
|
486
|
-
{ name: "balanced \u2014 opus / sonnet / haiku mix (recommended, ~60% cost cut)", value: "balanced" },
|
|
487
|
-
{ name: "keep \u2014 leave each agent's default model untouched", value: "keep" },
|
|
488
|
-
{ name: "cost \u2014 all haiku (cheapest, fastest)", value: "cost" },
|
|
489
|
-
{ name: "quality \u2014 all opus (most expensive, highest quality)", value: "quality" },
|
|
490
|
-
{ name: "custom \u2014 pick model per agent (18 prompts)", value: "custom" }
|
|
491
|
-
],
|
|
492
|
-
default: "balanced"
|
|
493
|
-
});
|
|
490
|
+
const claudeChecked = adaptersRaw.includes("claude");
|
|
491
|
+
const adapters = adaptersRaw.filter((t) => t !== "claude");
|
|
494
492
|
let modelMap = {};
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
493
|
+
if (claudeChecked) {
|
|
494
|
+
const preset = await select({
|
|
495
|
+
message: "\u{1F7E3} Claude \uC5D0\uC774\uC804\uD2B8 \uBAA8\uB378 \uB9E4\uD551? [.claude/agents/ frontmatter \uC5D0 \uBC15\uD798 \u2014 Claude Code \uAC00 \uC9C4\uC9DC \uC801\uC6A9]",
|
|
496
|
+
choices: [
|
|
497
|
+
{ name: "balanced \u2014 opus / sonnet / haiku \uADE0\uD615 \uB9E4\uD551 (\uCD94\uCC9C, \uBE44\uC6A9 ~60% \uC808\uAC10)", value: "balanced" },
|
|
498
|
+
{ name: "keep \u2014 \uAC01 \uC5D0\uC774\uC804\uD2B8 \uAE30\uBCF8 \uBAA8\uB378 \uADF8\uB300\uB85C", value: "keep" },
|
|
499
|
+
{ name: "cost \u2014 \uC804\uBD80 haiku (\uAC00\uC7A5 \uC800\uB834, \uBE60\uB984)", value: "cost" },
|
|
500
|
+
{ name: "quality \u2014 \uC804\uBD80 opus (\uAC00\uC7A5 \uBE44\uC2F8\uACE0, \uD488\uC9C8 \uCD5C\uC0C1)", value: "quality" },
|
|
501
|
+
{ name: "custom \u2014 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uC9C1\uC811 \uC120\uD0DD (18\uAC1C prompt)", value: "custom" }
|
|
502
|
+
],
|
|
503
|
+
default: "balanced"
|
|
504
|
+
});
|
|
505
|
+
modelMap = preset === "custom" ? await promptCustomModelMap() : resolvePreset(preset);
|
|
506
|
+
}
|
|
507
|
+
let codexModel;
|
|
508
|
+
if (adapters.includes("codex")) {
|
|
509
|
+
codexModel = await select({
|
|
510
|
+
message: "\u{1F7E2} Codex \uAD8C\uC7A5 \uBAA8\uB378? [AGENTS.md \uC0C1\uB2E8 \uBA54\uBAA8\uB85C \uBC15\uD798 \u2014 \uC2E4\uC81C \uBAA8\uB378\uC740 \uCF54\uB371\uC2A4 \uC548\uC5D0\uC11C \uC0AC\uC6A9\uC790\uAC00 \uD53D]",
|
|
511
|
+
choices: [
|
|
512
|
+
{ name: "gpt-5 (\uCD5C\uC2E0)", value: "gpt-5" },
|
|
513
|
+
{ name: "gpt-5-codex (\uCF54\uB529 \uD2B9\uD654)", value: "gpt-5-codex" },
|
|
514
|
+
{ name: "gpt-4-turbo", value: "gpt-4-turbo" },
|
|
515
|
+
{ name: "o1 (\uCD94\uB860 \uD2B9\uD654)", value: "o1" },
|
|
516
|
+
{ name: "o1-mini (\uAC00\uBCBC\uC6B4 \uCD94\uB860)", value: "o1-mini" },
|
|
517
|
+
{ name: "skip (\uBA54\uBAA8 \uC548 \uBC15\uC74C)", value: "skip" }
|
|
518
|
+
],
|
|
519
|
+
default: "gpt-5-codex"
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
let geminiModel;
|
|
523
|
+
if (adapters.includes("gemini")) {
|
|
524
|
+
geminiModel = await select({
|
|
525
|
+
message: "\u{1F535} Gemini \uAD8C\uC7A5 \uBAA8\uB378? [GEMINI.md \uC0C1\uB2E8 \uBA54\uBAA8\uB85C \uBC15\uD798 \u2014 \uC2E4\uC81C \uBAA8\uB378\uC740 Gemini \uB3C4\uAD6C \uC548\uC5D0\uC11C \uC0AC\uC6A9\uC790\uAC00 \uD53D]",
|
|
526
|
+
choices: [
|
|
527
|
+
{ name: "gemini-2.5-pro (\uCD5C\uC2E0, \uAC00\uC7A5 \uB611\uB611)", value: "gemini-2.5-pro" },
|
|
528
|
+
{ name: "gemini-2.5-flash (\uBE60\uB974\uACE0 \uC800\uB834)", value: "gemini-2.5-flash" },
|
|
529
|
+
{ name: "gemini-2.0-pro", value: "gemini-2.0-pro" },
|
|
530
|
+
{ name: "gemini-2.0-flash", value: "gemini-2.0-flash" },
|
|
531
|
+
{ name: "skip (\uBA54\uBAA8 \uC548 \uBC15\uC74C)", value: "skip" }
|
|
532
|
+
],
|
|
533
|
+
default: "gemini-2.5-pro"
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
let aiderModel;
|
|
537
|
+
if (adapters.includes("aider")) {
|
|
538
|
+
aiderModel = await select({
|
|
539
|
+
message: "\u{1F7E1} Aider \uBAA8\uB378? [.aider.conf.yml \uC758 model \uD544\uB4DC\uC5D0 \uBC15\uD798 \u2014 Aider \uAC00 \uC2DC\uC791 \uC2DC \uC9C4\uC9DC \uC801\uC6A9]",
|
|
540
|
+
choices: [
|
|
541
|
+
{ name: "claude-opus-4-7 (\uCD5C\uACE0 \uD488\uC9C8)", value: "claude-opus-4-7" },
|
|
542
|
+
{ name: "claude-sonnet-4-6 (\uADE0\uD615)", value: "claude-sonnet-4-6" },
|
|
543
|
+
{ name: "gpt-5 (OpenAI \uCD5C\uC2E0)", value: "gpt-5" },
|
|
544
|
+
{ name: "gemini-2.5-pro (Google \uCD5C\uC2E0)", value: "gemini-2.5-pro" },
|
|
545
|
+
{ name: "(skip) (model \uD544\uB4DC \uC548 \uBC15\uC74C)", value: "(skip)" }
|
|
546
|
+
],
|
|
547
|
+
default: "claude-sonnet-4-6"
|
|
548
|
+
});
|
|
499
549
|
}
|
|
500
550
|
let installTemplate = opts.installTemplate;
|
|
501
551
|
if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
|
|
502
552
|
installTemplate = await confirm({
|
|
503
|
-
message: "
|
|
553
|
+
message: "\uD1A1\uBC29 UI \uC124\uCE58? (Next.js admin \uB77C\uC6B0\uD2B8 /admin/harness)",
|
|
504
554
|
default: true
|
|
505
555
|
});
|
|
506
556
|
}
|
|
507
|
-
return {
|
|
557
|
+
return {
|
|
558
|
+
...opts,
|
|
559
|
+
lang,
|
|
560
|
+
chatBackend,
|
|
561
|
+
installTemplate,
|
|
562
|
+
adapters,
|
|
563
|
+
modelMap,
|
|
564
|
+
codexModel,
|
|
565
|
+
geminiModel,
|
|
566
|
+
aiderModel
|
|
567
|
+
};
|
|
508
568
|
}
|
|
509
569
|
async function promptCustomModelMap() {
|
|
510
570
|
const out = {};
|
|
511
571
|
const slugs = Object.keys(BALANCED_MAPPING);
|
|
512
572
|
console.log();
|
|
513
|
-
console.log(c.dim(` Custom
|
|
573
|
+
console.log(c.dim(` Custom \uB9E4\uD551 \u2014 ${slugs.length}\uAC1C \uC5D0\uC774\uC804\uD2B8\uB9C8\uB2E4 \uBAA8\uB378\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.`));
|
|
514
574
|
for (const slug of slugs) {
|
|
515
575
|
const tier = await select({
|
|
516
576
|
message: `${slug.padEnd(20)}`,
|
|
517
577
|
choices: [
|
|
518
|
-
{ name: "opus (
|
|
519
|
-
{ name: "sonnet (
|
|
520
|
-
{ name: "haiku (
|
|
578
|
+
{ name: "opus (\uAC00\uC7A5 \uB611\uB611, \uBE44\uC2FC)", value: "opus" },
|
|
579
|
+
{ name: "sonnet (\uADE0\uD615)", value: "sonnet" },
|
|
580
|
+
{ name: "haiku (\uAC00\uC7A5 \uBE60\uB974\uACE0 \uC800\uB834)", value: "haiku" }
|
|
521
581
|
],
|
|
522
582
|
default: BALANCED_MAPPING[slug] ?? "sonnet"
|
|
523
583
|
});
|
|
@@ -559,6 +619,9 @@ function parseArgs(args) {
|
|
|
559
619
|
}
|
|
560
620
|
modelMap = resolvePreset(modelsRaw);
|
|
561
621
|
}
|
|
622
|
+
const codexModel = getFlag(args, "--codex-model");
|
|
623
|
+
const geminiModel = getFlag(args, "--gemini-model");
|
|
624
|
+
const aiderModel = getFlag(args, "--aider-model");
|
|
562
625
|
return {
|
|
563
626
|
lang,
|
|
564
627
|
target: path2.resolve(targetRaw),
|
|
@@ -571,7 +634,10 @@ function parseArgs(args) {
|
|
|
571
634
|
seedLearningLog: !args.includes("--no-learning-log"),
|
|
572
635
|
yes: args.includes("--yes") || args.includes("-y"),
|
|
573
636
|
adapters,
|
|
574
|
-
modelMap
|
|
637
|
+
modelMap,
|
|
638
|
+
codexModel,
|
|
639
|
+
geminiModel,
|
|
640
|
+
aiderModel
|
|
575
641
|
};
|
|
576
642
|
}
|
|
577
643
|
function resolvePreset(preset) {
|
|
@@ -690,6 +756,57 @@ function printBackendInstructions(backend, commitChat) {
|
|
|
690
756
|
}
|
|
691
757
|
console.log();
|
|
692
758
|
}
|
|
759
|
+
async function injectCodexModelMemo(target, model) {
|
|
760
|
+
const fp = path2.join(target, "AGENTS.md");
|
|
761
|
+
if (!await exists2(fp)) return;
|
|
762
|
+
const raw = await fs2.readFile(fp, "utf8");
|
|
763
|
+
const memo = `
|
|
764
|
+
> \u{1F4A1} **Recommended model**: \`${model}\` \u2014 pick this in your Codex / Copilot settings for best results with this harness.
|
|
765
|
+
`;
|
|
766
|
+
if (raw.includes("Recommended model")) return;
|
|
767
|
+
const lines = raw.split("\n");
|
|
768
|
+
const h1Idx = lines.findIndex((l) => l.startsWith("# "));
|
|
769
|
+
if (h1Idx < 0) {
|
|
770
|
+
await fs2.writeFile(fp, memo + raw);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
lines.splice(h1Idx + 1, 0, memo);
|
|
774
|
+
await fs2.writeFile(fp, lines.join("\n"));
|
|
775
|
+
console.log(c.dim(` \u2713 AGENTS.md \u2190 Codex \uAD8C\uC7A5 \uBAA8\uB378 \uBA54\uBAA8: ${model}`));
|
|
776
|
+
}
|
|
777
|
+
async function injectGeminiModelMemo(target, model) {
|
|
778
|
+
const fp = path2.join(target, "GEMINI.md");
|
|
779
|
+
if (!await exists2(fp)) return;
|
|
780
|
+
const raw = await fs2.readFile(fp, "utf8");
|
|
781
|
+
const memo = `
|
|
782
|
+
> \u{1F4A1} **Recommended model**: \`${model}\` \u2014 pick this in Gemini CLI / Antigravity / Code Assist settings.
|
|
783
|
+
`;
|
|
784
|
+
if (raw.includes("Recommended model")) return;
|
|
785
|
+
const lines = raw.split("\n");
|
|
786
|
+
const h1Idx = lines.findIndex((l) => l.startsWith("# "));
|
|
787
|
+
if (h1Idx < 0) {
|
|
788
|
+
await fs2.writeFile(fp, memo + raw);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
lines.splice(h1Idx + 1, 0, memo);
|
|
792
|
+
await fs2.writeFile(fp, lines.join("\n"));
|
|
793
|
+
console.log(c.dim(` \u2713 GEMINI.md \u2190 Gemini \uAD8C\uC7A5 \uBAA8\uB378 \uBA54\uBAA8: ${model}`));
|
|
794
|
+
}
|
|
795
|
+
async function setAiderModel(target, model) {
|
|
796
|
+
const fp = path2.join(target, ".aider.conf.yml");
|
|
797
|
+
if (!await exists2(fp)) return;
|
|
798
|
+
let raw = await fs2.readFile(fp, "utf8");
|
|
799
|
+
if (/^model:\s*\S+/m.test(raw)) {
|
|
800
|
+
raw = raw.replace(/^model:\s*\S+/m, `model: ${model}`);
|
|
801
|
+
} else {
|
|
802
|
+
raw = raw.trimEnd() + `
|
|
803
|
+
# Added by harness-bujang init
|
|
804
|
+
model: ${model}
|
|
805
|
+
`;
|
|
806
|
+
}
|
|
807
|
+
await fs2.writeFile(fp, raw);
|
|
808
|
+
console.log(c.dim(` \u2713 .aider.conf.yml \u2190 model: ${model}`));
|
|
809
|
+
}
|
|
693
810
|
function overrideModelFrontmatter(content, model) {
|
|
694
811
|
if (!content.startsWith("---\n")) return content;
|
|
695
812
|
const end = content.indexOf("\n---\n", 4);
|
package/dist/index.js
CHANGED
|
@@ -189,7 +189,7 @@ async function main() {
|
|
|
189
189
|
const command = args[0];
|
|
190
190
|
switch (command) {
|
|
191
191
|
case "init":
|
|
192
|
-
await (await import("./init-
|
|
192
|
+
await (await import("./init-HUQA2RDS.js")).runInit(args.slice(1));
|
|
193
193
|
break;
|
|
194
194
|
case "status":
|
|
195
195
|
await (await import("./status-UE2TQQPU.js")).runStatus(args.slice(1));
|
|
@@ -201,7 +201,7 @@ async function main() {
|
|
|
201
201
|
await (await import("./adapt-VPWOYF6W.js")).runAdapt(args.slice(1));
|
|
202
202
|
break;
|
|
203
203
|
case "update":
|
|
204
|
-
await (await import("./update-
|
|
204
|
+
await (await import("./update-35E4M6TJ.js")).runUpdate(args.slice(1));
|
|
205
205
|
break;
|
|
206
206
|
case "migrate":
|
|
207
207
|
await (await import("./migrate-PISZFX6C.js")).runMigrate(args.slice(1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-bujang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Install the Harness-Bujang multi-agent harness into any project — Director, 7 specialist teams, real-time chat-room UI. Korean and English personas. Works with Claude Code, Cursor, Cline, Aider, or any tool that reads .claude/agents/.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|