harness-bujang 0.7.2 → 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.
@@ -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
  }
@@ -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();
@@ -470,9 +476,9 @@ async function promptInteractive(opts, scan) {
470
476
  });
471
477
  const isPreset = (t) => opts.adapters.includes(t);
472
478
  const adaptersRaw = await checkbox({
473
- message: "\uB3C4\uAD6C \uC120\uD0DD \u2014 Claude Code \uB294 \uC790\uB3D9 \uC124\uCE58 (\uD544\uC218). \uCD94\uAC00 \uB3C4\uAD6C \uC5C6\uC73C\uBA74 \uADF8\uB0E5 Enter.",
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: [
475
- { name: "Claude Code (.claude/agents/) \u2014 \uC790\uB3D9 \uC124\uCE58 (\uD544\uC218)", value: "claude", checked: true, disabled: "(\uC790\uB3D9)" },
481
+ { name: "Claude Code (.claude/agents/)", value: "claude", checked: true },
476
482
  { name: "Cursor (.cursor/rules/bujang-*.mdc)", value: "cursor", checked: isPreset("cursor") },
477
483
  { name: "Codex / Copilot (AGENTS.md)", value: "codex", checked: isPreset("codex") },
478
484
  { name: "Cline (.clinerules/bujang-*.md)", value: "cline", checked: isPreset("cline") },
@@ -481,23 +487,65 @@ async function promptInteractive(opts, scan) {
481
487
  ],
482
488
  required: false
483
489
  });
490
+ const claudeChecked = adaptersRaw.includes("claude");
484
491
  const adapters = adaptersRaw.filter((t) => t !== "claude");
485
- const preset = await select({
486
- message: "\uC5D0\uC774\uC804\uD2B8\uBCC4 Claude \uBAA8\uB378? (.claude/agents/ \uC5D0\uB9CC \uC801\uC6A9 \u2014 \uB2E4\uB978 \uB3C4\uAD6C\uB294 \uC790\uCCB4 \uBAA8\uB378 \uAD00\uB9AC)",
487
- choices: [
488
- { name: "balanced \u2014 opus / sonnet / haiku \uADE0\uD615 \uB9E4\uD551 (\uCD94\uCC9C, \uBE44\uC6A9 ~60% \uC808\uAC10)", value: "balanced" },
489
- { name: "keep \u2014 \uAC01 \uC5D0\uC774\uC804\uD2B8 \uAE30\uBCF8 \uBAA8\uB378 \uADF8\uB300\uB85C", value: "keep" },
490
- { name: "cost \u2014 \uC804\uBD80 haiku (\uAC00\uC7A5 \uC800\uB834, \uBE60\uB984)", value: "cost" },
491
- { name: "quality \u2014 \uC804\uBD80 opus (\uAC00\uC7A5 \uBE44\uC2F8\uACE0, \uD488\uC9C8 \uCD5C\uC0C1)", value: "quality" },
492
- { name: "custom \u2014 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uC9C1\uC811 \uC120\uD0DD (18\uAC1C prompt)", value: "custom" }
493
- ],
494
- default: "balanced"
495
- });
496
492
  let modelMap = {};
497
- if (preset === "custom") {
498
- modelMap = await promptCustomModelMap();
499
- } else {
500
- 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
+ });
501
549
  }
502
550
  let installTemplate = opts.installTemplate;
503
551
  if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
@@ -506,7 +554,17 @@ async function promptInteractive(opts, scan) {
506
554
  default: true
507
555
  });
508
556
  }
509
- 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
+ };
510
568
  }
511
569
  async function promptCustomModelMap() {
512
570
  const out = {};
@@ -561,6 +619,9 @@ function parseArgs(args) {
561
619
  }
562
620
  modelMap = resolvePreset(modelsRaw);
563
621
  }
622
+ const codexModel = getFlag(args, "--codex-model");
623
+ const geminiModel = getFlag(args, "--gemini-model");
624
+ const aiderModel = getFlag(args, "--aider-model");
564
625
  return {
565
626
  lang,
566
627
  target: path2.resolve(targetRaw),
@@ -573,7 +634,10 @@ function parseArgs(args) {
573
634
  seedLearningLog: !args.includes("--no-learning-log"),
574
635
  yes: args.includes("--yes") || args.includes("-y"),
575
636
  adapters,
576
- modelMap
637
+ modelMap,
638
+ codexModel,
639
+ geminiModel,
640
+ aiderModel
577
641
  };
578
642
  }
579
643
  function resolvePreset(preset) {
@@ -692,6 +756,57 @@ function printBackendInstructions(backend, commitChat) {
692
756
  }
693
757
  console.log();
694
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
+ }
695
810
  function overrideModelFrontmatter(content, model) {
696
811
  if (!content.startsWith("---\n")) return content;
697
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-3SFH5G3M.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-JX4Z2MJG.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-OFYRMVMC.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-OFYRMVMC.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.2",
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",