harness-bujang 0.8.0 → 0.8.1

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.
@@ -170,6 +170,62 @@ If installed via npm, try reinstalling. If running from source, run "npm run bui
170
170
  );
171
171
  }
172
172
  var ALL_ADAPTERS = ["cursor", "cline", "aider", "codex", "gemini"];
173
+ var CODEX_BALANCED = {
174
+ director: "gpt-5",
175
+ cofounder: "gpt-5",
176
+ "architect-team": "gpt-5",
177
+ consultant: "gpt-5",
178
+ "security-team": "o1",
179
+ "db-guard-team": "o1",
180
+ "dev-team": "gpt-5-codex",
181
+ "code-review-team": "gpt-5-codex",
182
+ "qa-team": "gpt-4-turbo",
183
+ "verifier-team": "gpt-4-turbo",
184
+ "doc-sync-team": "gpt-4-turbo",
185
+ "research-team": "gpt-5",
186
+ "analysis-team": "gpt-5",
187
+ "script-team": "gpt-4-turbo",
188
+ "image-team": "o1-mini",
189
+ "voice-team": "o1-mini",
190
+ "edit-team": "o1-mini",
191
+ "content-qa-team": "o1-mini"
192
+ };
193
+ var GEMINI_BALANCED = {
194
+ director: "gemini-2.5-pro",
195
+ cofounder: "gemini-2.5-pro",
196
+ "architect-team": "gemini-2.5-pro",
197
+ consultant: "gemini-2.5-pro",
198
+ "security-team": "gemini-2.5-pro",
199
+ "db-guard-team": "gemini-2.5-pro",
200
+ "dev-team": "gemini-2.5-pro",
201
+ "code-review-team": "gemini-2.5-pro",
202
+ "qa-team": "gemini-2.5-flash",
203
+ "verifier-team": "gemini-2.5-flash",
204
+ "doc-sync-team": "gemini-2.5-flash",
205
+ "research-team": "gemini-2.5-pro",
206
+ "analysis-team": "gemini-2.5-pro",
207
+ "script-team": "gemini-2.5-flash",
208
+ "image-team": "gemini-2.5-flash",
209
+ "voice-team": "gemini-2.5-flash",
210
+ "edit-team": "gemini-2.5-flash",
211
+ "content-qa-team": "gemini-2.5-flash"
212
+ };
213
+ function resolveCodexPreset(preset) {
214
+ if (preset === "keep") return {};
215
+ if (preset === "balanced") return { ...CODEX_BALANCED };
216
+ const tier = preset === "cost" ? "gpt-4-turbo" : "gpt-5";
217
+ const out = {};
218
+ for (const k of Object.keys(CODEX_BALANCED)) out[k] = tier;
219
+ return out;
220
+ }
221
+ function resolveGeminiPreset(preset) {
222
+ if (preset === "keep") return {};
223
+ if (preset === "balanced") return { ...GEMINI_BALANCED };
224
+ const tier = preset === "cost" ? "gemini-2.5-flash" : "gemini-2.5-pro";
225
+ const out = {};
226
+ for (const k of Object.keys(GEMINI_BALANCED)) out[k] = tier;
227
+ return out;
228
+ }
173
229
  var BALANCED_MAPPING = {
174
230
  director: "opus",
175
231
  cofounder: "opus",
@@ -233,9 +289,9 @@ async function runInit(args) {
233
289
  console.log(c.dim(` Language: ${opts.lang}`));
234
290
  console.log(c.dim(` Chat backend: ${opts.chatBackend}${opts.chatBackend === "sqlite" ? " (local file)" : " (cloud Postgres)"}`));
235
291
  console.log(c.dim(` Tools: claude${opts.adapters.length > 0 ? ` + ${opts.adapters.join(", ")}` : " (only)"}`));
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)`));
292
+ console.log(c.dim(` Claude models: ${describeModelMap(opts.modelMap)}`));
293
+ if (opts.codexModelMap) console.log(c.dim(` Codex models: ${describeAnyMap(opts.codexModelMap)}`));
294
+ if (opts.geminiModelMap) console.log(c.dim(` Gemini models: ${describeAnyMap(opts.geminiModelMap)}`));
239
295
  if (opts.aiderModel && opts.aiderModel !== "(skip)") console.log(c.dim(` Aider model: ${opts.aiderModel} (.aider.conf.yml)`));
240
296
  if (scan.framework.startsWith("Next.js")) {
241
297
  console.log(c.dim(` Chat-room UI: ${opts.installTemplate ? "install" : "skip"}`));
@@ -419,8 +475,8 @@ async function runInit(args) {
419
475
  `--target=${opts.target}`,
420
476
  "--yes"
421
477
  ]);
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);
478
+ if (opts.codexModelMap && Object.keys(opts.codexModelMap).length > 0) await injectCodexModelMemos(opts.target, opts.codexModelMap);
479
+ if (opts.geminiModelMap && Object.keys(opts.geminiModelMap).length > 0) await injectGeminiModelMemos(opts.target, opts.geminiModelMap);
424
480
  if (opts.aiderModel && opts.aiderModel !== "(skip)") await setAiderModel(opts.target, opts.aiderModel);
425
481
  }
426
482
  console.log(c.bold(c.green("\u2705 Done.")));
@@ -504,34 +560,35 @@ async function promptInteractive(opts, scan) {
504
560
  });
505
561
  modelMap = preset === "custom" ? await promptCustomModelMap() : resolvePreset(preset);
506
562
  }
507
- let codexModel;
563
+ let codexModelMap;
508
564
  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]",
565
+ const preset = await select({
566
+ message: "\u{1F7E2} Codex \uC5D0\uC774\uC804\uD2B8 \uBAA8\uB378 \uB9E4\uD551? [AGENTS.md \uC758 \uAC01 \uC5D0\uC774\uC804\uD2B8 \uC139\uC158\uC5D0 \uBA54\uBAA8\uB85C \uBC15\uD798 \u2014 \uAC00\uC774\uB4DC\uC6A9]",
511
567
  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" }
568
+ { name: "balanced \u2014 gpt-5 / gpt-5-codex / o1 / gpt-4-turbo \uC5ED\uD560\uBCC4 \uB9E4\uD551 (\uCD94\uCC9C)", value: "balanced" },
569
+ { name: "keep \u2014 \uBA54\uBAA8 \uC548 \uBC15\uC74C (\uC0AC\uC6A9\uC790\uAC00 \uCF54\uB371\uC2A4 \uC548\uC5D0\uC11C \uD53D)", value: "keep" },
570
+ { name: "cost \u2014 \uC804\uBD80 gpt-4-turbo (\uAC00\uC7A5 \uC800\uB834)", value: "cost" },
571
+ { name: "quality \u2014 \uC804\uBD80 gpt-5 (\uAC00\uC7A5 \uB611\uB611)", value: "quality" },
572
+ { name: "custom \u2014 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uC9C1\uC811 \uC120\uD0DD (18\uAC1C prompt)", value: "custom" }
518
573
  ],
519
- default: "gpt-5-codex"
574
+ default: "balanced"
520
575
  });
576
+ codexModelMap = preset === "custom" ? await promptCustomCodexMap() : resolveCodexPreset(preset);
521
577
  }
522
- let geminiModel;
578
+ let geminiModelMap;
523
579
  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]",
580
+ const preset = await select({
581
+ message: "\u{1F535} Gemini \uC5D0\uC774\uC804\uD2B8 \uBAA8\uB378 \uB9E4\uD551? [GEMINI.md \uC758 \uAC01 \uC5D0\uC774\uC804\uD2B8 \uC139\uC158\uC5D0 \uBA54\uBAA8\uB85C \uBC15\uD798 \u2014 \uAC00\uC774\uB4DC\uC6A9]",
526
582
  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" }
583
+ { name: "balanced \u2014 pro / flash \uC5ED\uD560\uBCC4 \uB9E4\uD551 (\uCD94\uCC9C)", value: "balanced" },
584
+ { name: "keep \u2014 \uBA54\uBAA8 \uC548 \uBC15\uC74C (Gemini \uB3C4\uAD6C \uC548\uC5D0\uC11C \uD53D)", value: "keep" },
585
+ { name: "cost \u2014 \uC804\uBD80 gemini-2.5-flash (\uAC00\uC7A5 \uBE60\uB974\uACE0 \uC800\uB834)", value: "cost" },
586
+ { name: "quality \u2014 \uC804\uBD80 gemini-2.5-pro (\uAC00\uC7A5 \uB611\uB611)", value: "quality" },
587
+ { name: "custom \u2014 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uC9C1\uC811 \uC120\uD0DD (18\uAC1C prompt)", value: "custom" }
532
588
  ],
533
- default: "gemini-2.5-pro"
589
+ default: "balanced"
534
590
  });
591
+ geminiModelMap = preset === "custom" ? await promptCustomGeminiMap() : resolveGeminiPreset(preset);
535
592
  }
536
593
  let aiderModel;
537
594
  if (adapters.includes("aider")) {
@@ -561,11 +618,52 @@ async function promptInteractive(opts, scan) {
561
618
  installTemplate,
562
619
  adapters,
563
620
  modelMap,
564
- codexModel,
565
- geminiModel,
621
+ codexModelMap,
622
+ geminiModelMap,
566
623
  aiderModel
567
624
  };
568
625
  }
626
+ async function promptCustomCodexMap() {
627
+ const out = {};
628
+ const slugs = Object.keys(CODEX_BALANCED);
629
+ console.log();
630
+ console.log(c.dim(` Codex custom \uB9E4\uD551 \u2014 ${slugs.length}\uAC1C \uC5D0\uC774\uC804\uD2B8\uB9C8\uB2E4 \uBAA8\uB378\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.`));
631
+ for (const slug of slugs) {
632
+ const tier = await select({
633
+ message: `${slug.padEnd(20)}`,
634
+ choices: [
635
+ { name: "gpt-5 (\uCD5C\uC2E0, \uD070 \uACB0\uC815)", value: "gpt-5" },
636
+ { name: "gpt-5-codex (\uCF54\uB529 \uD2B9\uD654)", value: "gpt-5-codex" },
637
+ { name: "gpt-4-turbo (\uADE0\uD615)", value: "gpt-4-turbo" },
638
+ { name: "o1 (\uCD94\uB860 \uD2B9\uD654)", value: "o1" },
639
+ { name: "o1-mini (\uAC00\uBCBC\uC6B4, \uBE60\uB984)", value: "o1-mini" }
640
+ ],
641
+ default: CODEX_BALANCED[slug] ?? "gpt-4-turbo"
642
+ });
643
+ out[slug] = tier;
644
+ }
645
+ return out;
646
+ }
647
+ async function promptCustomGeminiMap() {
648
+ const out = {};
649
+ const slugs = Object.keys(GEMINI_BALANCED);
650
+ console.log();
651
+ console.log(c.dim(` Gemini custom \uB9E4\uD551 \u2014 ${slugs.length}\uAC1C \uC5D0\uC774\uC804\uD2B8\uB9C8\uB2E4 \uBAA8\uB378\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.`));
652
+ for (const slug of slugs) {
653
+ const tier = await select({
654
+ message: `${slug.padEnd(20)}`,
655
+ choices: [
656
+ { name: "gemini-2.5-pro (\uCD5C\uC2E0, \uAC00\uC7A5 \uB611\uB611)", value: "gemini-2.5-pro" },
657
+ { name: "gemini-2.5-flash (\uBE60\uB974\uACE0 \uC800\uB834)", value: "gemini-2.5-flash" },
658
+ { name: "gemini-2.0-pro", value: "gemini-2.0-pro" },
659
+ { name: "gemini-2.0-flash", value: "gemini-2.0-flash" }
660
+ ],
661
+ default: GEMINI_BALANCED[slug] ?? "gemini-2.5-flash"
662
+ });
663
+ out[slug] = tier;
664
+ }
665
+ return out;
666
+ }
569
667
  async function promptCustomModelMap() {
570
668
  const out = {};
571
669
  const slugs = Object.keys(BALANCED_MAPPING);
@@ -619,8 +717,22 @@ function parseArgs(args) {
619
717
  }
620
718
  modelMap = resolvePreset(modelsRaw);
621
719
  }
622
- const codexModel = getFlag(args, "--codex-model");
623
- const geminiModel = getFlag(args, "--gemini-model");
720
+ const codexPresetRaw = getFlag(args, "--codex-models");
721
+ const geminiPresetRaw = getFlag(args, "--gemini-models");
722
+ let codexModelMap;
723
+ let geminiModelMap;
724
+ if (codexPresetRaw) {
725
+ if (!["balanced", "cost", "quality", "keep"].includes(codexPresetRaw)) {
726
+ throw new Error(`--codex-models must be one of: balanced, cost, quality, keep (got "${codexPresetRaw}")`);
727
+ }
728
+ codexModelMap = resolveCodexPreset(codexPresetRaw);
729
+ }
730
+ if (geminiPresetRaw) {
731
+ if (!["balanced", "cost", "quality", "keep"].includes(geminiPresetRaw)) {
732
+ throw new Error(`--gemini-models must be one of: balanced, cost, quality, keep (got "${geminiPresetRaw}")`);
733
+ }
734
+ geminiModelMap = resolveGeminiPreset(geminiPresetRaw);
735
+ }
624
736
  const aiderModel = getFlag(args, "--aider-model");
625
737
  return {
626
738
  lang,
@@ -635,8 +747,8 @@ function parseArgs(args) {
635
747
  yes: args.includes("--yes") || args.includes("-y"),
636
748
  adapters,
637
749
  modelMap,
638
- codexModel,
639
- geminiModel,
750
+ codexModelMap,
751
+ geminiModelMap,
640
752
  aiderModel
641
753
  };
642
754
  }
@@ -756,41 +868,42 @@ function printBackendInstructions(backend, commitChat) {
756
868
  }
757
869
  console.log();
758
870
  }
759
- async function injectCodexModelMemo(target, model) {
871
+ async function injectCodexModelMemos(target, modelMap) {
760
872
  const fp = path2.join(target, "AGENTS.md");
761
873
  if (!await exists2(fp)) return;
762
874
  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}`));
875
+ const updated = injectPerAgentMemos(raw, modelMap, "\uCF54\uB371\uC2A4 / Copilot \uC548\uC5D0\uC11C \uC774 \uBAA8\uB378\uB85C \uC791\uC5C5");
876
+ await fs2.writeFile(fp, updated);
877
+ const count = Object.keys(modelMap).length;
878
+ console.log(c.dim(` \u2713 AGENTS.md \u2190 Codex \uAD8C\uC7A5 \uBAA8\uB378 \uBA54\uBAA8 ${count}\uAC74 (\uAC01 \uC5D0\uC774\uC804\uD2B8 \uC139\uC158 \uC704)`));
776
879
  }
777
- async function injectGeminiModelMemo(target, model) {
880
+ async function injectGeminiModelMemos(target, modelMap) {
778
881
  const fp = path2.join(target, "GEMINI.md");
779
882
  if (!await exists2(fp)) return;
780
883
  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;
884
+ const updated = injectPerAgentMemos(raw, modelMap, "Gemini CLI / Antigravity \uC548\uC5D0\uC11C \uC774 \uBAA8\uB378\uB85C \uC791\uC5C5");
885
+ await fs2.writeFile(fp, updated);
886
+ const count = Object.keys(modelMap).length;
887
+ console.log(c.dim(` \u2713 GEMINI.md \u2190 Gemini \uAD8C\uC7A5 \uBAA8\uB378 \uBA54\uBAA8 ${count}\uAC74 (\uAC01 \uC5D0\uC774\uC804\uD2B8 \uC139\uC158 \uC704)`));
888
+ }
889
+ function injectPerAgentMemos(raw, modelMap, hint) {
785
890
  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}`));
891
+ const out = [];
892
+ for (let i = 0; i < lines.length; i++) {
893
+ const line = lines[i];
894
+ const m = /^##\s+([a-z][a-z0-9-]*)\s*$/.exec(line);
895
+ if (m) {
896
+ const slug = m[1];
897
+ const model = modelMap[slug];
898
+ const prev = out[out.length - 1] ?? "";
899
+ if (model && !prev.includes("\u{1F4A1} Recommended")) {
900
+ out.push(`> \u{1F4A1} Recommended model: \`${model}\` \u2014 ${hint}`);
901
+ out.push("");
902
+ }
903
+ }
904
+ out.push(line);
905
+ }
906
+ return out.join("\n");
794
907
  }
795
908
  async function setAiderModel(target, model) {
796
909
  const fp = path2.join(target, ".aider.conf.yml");
@@ -827,6 +940,13 @@ function describeModelMap(map) {
827
940
  }
828
941
  return parts.join(" \xB7 ");
829
942
  }
943
+ function describeAnyMap(map) {
944
+ if (Object.keys(map).length === 0) return "keep (\uBA54\uBAA8 \uC548 \uBC15\uC74C)";
945
+ const counts = {};
946
+ for (const v of Object.values(map)) counts[v] = (counts[v] ?? 0) + 1;
947
+ const parts = Object.entries(counts).sort((a, b) => b[1] - a[1]).map(([model, n]) => `${n} ${model}`);
948
+ return parts.join(" \xB7 ");
949
+ }
830
950
  function stackReviewRules(framework) {
831
951
  if (framework.startsWith("Next.js")) {
832
952
  return `Next.js App Router rules:
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-HUQA2RDS.js")).runInit(args.slice(1));
192
+ await (await import("./init-WAVPUHNL.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-35E4M6TJ.js")).runUpdate(args.slice(1));
204
+ await (await import("./update-HAGOKTNL.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-M2UYCSZE.js";
4
+ } from "./chunk-MKHLUAWE.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-M2UYCSZE.js";
4
+ } from "./chunk-MKHLUAWE.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.8.0",
3
+ "version": "0.8.1",
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",