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: "Existing harness install detected. Overwrite all files to apply your selections?",
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(` Models: ${describeModelMap(opts.modelMap)}`));
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: "Proceed with these settings?", default: true });
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: "Agent language / \uC5D0\uC774\uC804\uD2B8 \uC5B8\uC5B4",
462
+ message: "\uC5D0\uC774\uC804\uD2B8 \uC5B8\uC5B4 / Agent language",
457
463
  choices: [
458
- { name: "\uD55C\uAD6D\uC5B4 \u2014 full \uBD80\uC7A5 persona (Korean)", value: "ko" },
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: "Chat backend",
470
+ message: "\uD1A1\uBC29 \uBC31\uC5D4\uB4DC",
465
471
  choices: [
466
- { name: "SQLite \u2014 local file, zero setup (recommended)", value: "sqlite" },
467
- { name: "Supabase \u2014 cloud Postgres for team sharing", value: "supabase" }
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 adapters = await checkbox({
473
- message: "Extra tool adapters? (Claude Code is always installed at .claude/agents/ \u2014 these add files for OTHER tools)",
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 preset = await select({
484
- message: "Per-agent Claude model? (only affects .claude/agents/ \u2014 other tools manage models themselves)",
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 (preset === "custom") {
496
- modelMap = await promptCustomModelMap();
497
- } else {
498
- modelMap = resolvePreset(preset);
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: "Install chat-room UI (Next.js admin route at /admin/harness)?",
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 { ...opts, lang, chatBackend, installTemplate, adapters, modelMap };
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 mapping \u2014 pick a model for each of ${slugs.length} agents.`));
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 (heaviest, smartest)", value: "opus" },
519
- { name: "sonnet (balanced)", value: "sonnet" },
520
- { name: "haiku (lightest, fastest, cheap)", value: "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-4G7R63DX.js")).runInit(args.slice(1));
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-KHDRQZQE.js")).runUpdate(args.slice(1));
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));
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  printRestartReminder,
3
3
  runInit
4
- } from "./chunk-6LROBVZM.js";
4
+ } from "./chunk-M2UYCSZE.js";
5
5
  import "./chunk-7DAHO2GN.js";
6
6
  export {
7
7
  printRestartReminder,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  printRestartReminder,
3
3
  scanProject
4
- } from "./chunk-6LROBVZM.js";
4
+ } from "./chunk-M2UYCSZE.js";
5
5
  import {
6
6
  renderTemplate
7
7
  } from "./chunk-7DAHO2GN.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-bujang",
3
- "version": "0.7.1",
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",