@glrs-dev/cli 0.1.0 → 0.3.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.
Files changed (28) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -1
  3. package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.md +12 -2
  4. package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-planner.md +20 -5
  5. package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +1 -1
  6. package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +5 -5
  7. package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +3 -3
  8. package/dist/vendor/harness-opencode/dist/agents/prompts/qa-reviewer.md +1 -1
  9. package/dist/vendor/harness-opencode/dist/agents/prompts/qa-thorough.md +1 -1
  10. package/dist/vendor/harness-opencode/dist/agents/prompts/research-auto.md +37 -0
  11. package/dist/vendor/harness-opencode/dist/agents/prompts/research-local.md +33 -0
  12. package/dist/vendor/harness-opencode/dist/agents/prompts/research-web.md +32 -0
  13. package/dist/vendor/harness-opencode/dist/agents/prompts/research.md +15 -20
  14. package/dist/vendor/harness-opencode/dist/{chunk-BDFZGIY7.js → chunk-CZMAJISX.js} +41 -13
  15. package/dist/vendor/harness-opencode/dist/{chunk-UDB4NQ2R.js → chunk-VJUETC6A.js} +1 -1
  16. package/dist/vendor/harness-opencode/dist/{chunk-V3KJY6CN.js → chunk-WBBN7OVN.js} +166 -6
  17. package/dist/vendor/harness-opencode/dist/cli.js +97 -18
  18. package/dist/vendor/harness-opencode/dist/index.d.ts +1 -1
  19. package/dist/vendor/harness-opencode/dist/index.js +5 -5
  20. package/dist/vendor/harness-opencode/dist/install-X5KEANRB.js +13 -0
  21. package/dist/vendor/harness-opencode/dist/skills/pilot-planning/SKILL.md +8 -4
  22. package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/qa-expectations.md +120 -0
  23. package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/self-review.md +2 -2
  24. package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/setup-authoring.md +68 -0
  25. package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/verify-design.md +4 -0
  26. package/dist/vendor/harness-opencode/package.json +2 -2
  27. package/package.json +2 -2
  28. package/dist/vendor/harness-opencode/dist/install-GDCZ7VFK.js +0 -9
@@ -3,7 +3,7 @@ import {
3
3
  inspectCachePin,
4
4
  readOurPackageVersion,
5
5
  refreshPluginCache
6
- } from "./chunk-UDB4NQ2R.js";
6
+ } from "./chunk-VJUETC6A.js";
7
7
 
8
8
  // src/cli/install.ts
9
9
  import * as fs3 from "fs";
@@ -181,7 +181,7 @@ import * as fs2 from "fs";
181
181
  import * as path2 from "path";
182
182
  import * as os from "os";
183
183
  import { select, checkbox, confirm } from "@inquirer/prompts";
184
- var PLUGIN_NAME = "@glrs-dev/harness-opencode";
184
+ var PLUGIN_NAME = "@glrs-dev/harness-plugin-opencode";
185
185
  function getOpencodeConfigPath() {
186
186
  const configHome = process.env["XDG_CONFIG_HOME"] ?? path2.join(os.homedir(), ".config");
187
187
  return path2.join(configHome, "opencode", "opencode.json");
@@ -257,7 +257,7 @@ async function requirePlugin() {
257
257
  );
258
258
  process.exit(1);
259
259
  }
260
- const { install: install2 } = await import("./install-GDCZ7VFK.js");
260
+ const { install: install2 } = await import("./install-X5KEANRB.js");
261
261
  await install2({ nonInteractive: true });
262
262
  }
263
263
 
@@ -361,7 +361,7 @@ async function fetchModelsDevProviders() {
361
361
  }
362
362
 
363
363
  // src/cli/install.ts
364
- var PLUGIN_NAME2 = "@glrs-dev/harness-opencode";
364
+ var PLUGIN_NAME2 = "@glrs-dev/harness-plugin-opencode";
365
365
  var c = {
366
366
  reset: "\x1B[0m",
367
367
  green: "\x1B[32m",
@@ -505,6 +505,116 @@ function migrateHarnessKeyToPluginOptions(configPath) {
505
505
  } catch {
506
506
  }
507
507
  }
508
+ function deepEqual(a, b) {
509
+ if (a === b) return true;
510
+ if (typeof a !== typeof b) return false;
511
+ if (a === null || b === null) return a === b;
512
+ if (typeof a !== "object") return false;
513
+ const aObj = a;
514
+ const bObj = b;
515
+ const aKeys = Object.keys(aObj);
516
+ const bKeys = Object.keys(bObj);
517
+ if (aKeys.length !== bKeys.length) return false;
518
+ for (const key of aKeys) {
519
+ if (!bKeys.includes(key)) return false;
520
+ if (!deepEqual(aObj[key], bObj[key])) return false;
521
+ }
522
+ return true;
523
+ }
524
+ function writePluginOption(configPath, subKey, value, opts) {
525
+ try {
526
+ if (!fs3.existsSync(configPath)) {
527
+ return { changed: false };
528
+ }
529
+ const raw = fs3.readFileSync(configPath, "utf8");
530
+ const config = JSON.parse(raw);
531
+ if (!Array.isArray(config.plugin)) {
532
+ return { changed: false };
533
+ }
534
+ const pluginIdx = config.plugin.findIndex((entry) => {
535
+ const name = typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : null;
536
+ return name === PLUGIN_NAME2 || String(name ?? "").startsWith(`${PLUGIN_NAME2}@`);
537
+ });
538
+ if (pluginIdx < 0) {
539
+ return { changed: false };
540
+ }
541
+ const current = config.plugin[pluginIdx];
542
+ const existingName = typeof current === "string" ? current : Array.isArray(current) ? current[0] : PLUGIN_NAME2;
543
+ const existingOpts = Array.isArray(current) && current.length >= 2 ? current[1] : {};
544
+ if (deepEqual(existingOpts[subKey], value)) {
545
+ return { changed: false };
546
+ }
547
+ const newOpts = { ...existingOpts, [subKey]: value };
548
+ if (opts.dryRun) {
549
+ info(`[dry-run] Would reconfigure ${subKey} in plugin options`);
550
+ return { changed: true };
551
+ }
552
+ const bakPath = `${configPath}.bak.${Date.now()}-${process.pid}`;
553
+ fs3.copyFileSync(configPath, bakPath);
554
+ config.plugin[pluginIdx] = [existingName, newOpts];
555
+ fs3.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
556
+ ok(`Reconfigured ${subKey}`);
557
+ info(`Backup: ${bakPath}`);
558
+ return { changed: true, bakPath };
559
+ } catch {
560
+ return { changed: false };
561
+ }
562
+ }
563
+ function writeMcpToggles(configPath, enabledSet, opts) {
564
+ try {
565
+ if (!fs3.existsSync(configPath)) {
566
+ return { changed: false };
567
+ }
568
+ const raw = fs3.readFileSync(configPath, "utf8");
569
+ const config = JSON.parse(raw);
570
+ const toggleNames = new Set(MCP_TOGGLES.map((t) => t.name));
571
+ const existingMcp = config.mcp && typeof config.mcp === "object" ? { ...config.mcp } : {};
572
+ const newMcp = {};
573
+ let hasChanges = false;
574
+ for (const [key, val] of Object.entries(existingMcp)) {
575
+ if (!toggleNames.has(key)) {
576
+ newMcp[key] = val;
577
+ }
578
+ }
579
+ for (const toggleName of toggleNames) {
580
+ if (enabledSet.has(toggleName)) {
581
+ newMcp[toggleName] = { enabled: true };
582
+ if (!deepEqual(existingMcp[toggleName], { enabled: true })) {
583
+ hasChanges = true;
584
+ }
585
+ } else {
586
+ if (existingMcp[toggleName] !== void 0) {
587
+ hasChanges = true;
588
+ }
589
+ }
590
+ }
591
+ if (!hasChanges && Object.keys(newMcp).length === Object.keys(existingMcp).length) {
592
+ const allKeysMatch = Object.keys(newMcp).every(
593
+ (k) => deepEqual(newMcp[k], existingMcp[k])
594
+ );
595
+ if (allKeysMatch) {
596
+ return { changed: false };
597
+ }
598
+ }
599
+ if (opts.dryRun) {
600
+ info(`[dry-run] Would reconfigure MCP toggles`);
601
+ return { changed: true };
602
+ }
603
+ const bakPath = `${configPath}.bak.${Date.now()}-${process.pid}`;
604
+ fs3.copyFileSync(configPath, bakPath);
605
+ if (Object.keys(newMcp).length > 0) {
606
+ config.mcp = newMcp;
607
+ } else {
608
+ delete config.mcp;
609
+ }
610
+ fs3.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
611
+ ok("Reconfigured MCPs");
612
+ info(`Backup: ${bakPath}`);
613
+ return { changed: true, bakPath };
614
+ } catch {
615
+ return { changed: false };
616
+ }
617
+ }
508
618
  async function install(opts = {}) {
509
619
  const { dryRun = false, pin = false, nonInteractive = false } = opts;
510
620
  const configPath = getOpencodeConfigPath2();
@@ -522,7 +632,7 @@ async function install(opts = {}) {
522
632
  const existingOpts = extractPluginOptions(existing);
523
633
  let hasModels = !!(existingOpts?.models ?? existing?.harness?.models);
524
634
  console.log(`
525
- ${c.bold}${c.blue}@glrs-dev/harness-opencode${c.reset} setup
635
+ ${c.bold}${c.blue}@glrs-dev/harness-plugin-opencode${c.reset} setup
526
636
  `);
527
637
  if (hasPlugin) {
528
638
  ok("Plugin already registered");
@@ -533,6 +643,10 @@ ${c.bold}${c.blue}@glrs-dev/harness-opencode${c.reset} setup
533
643
  if (existingMcps.size > 0) {
534
644
  ok(`MCPs: ${[...existingMcps].join(", ")} enabled`);
535
645
  }
646
+ let reconfigureModels = false;
647
+ let reconfigureMcps = false;
648
+ let newModelsValue = null;
649
+ let newMcpEnabledSet = /* @__PURE__ */ new Set();
536
650
  if (hasPlugin && (existingProvider || hasModels)) {
537
651
  const unconfiguredMcps = MCP_TOGGLES.filter(
538
652
  (t) => !existingMcps.has(t.name) && !existing?.mcp?.[t.name]
@@ -544,8 +658,20 @@ ${c.bold}${c.blue}@glrs-dev/harness-opencode${c.reset} setup
544
658
  0
545
659
  );
546
660
  if (reconfigure === 1) {
661
+ reconfigureModels = true;
547
662
  hasModels = false;
548
- } else if (unconfiguredMcps.length === 0) {
663
+ }
664
+ if (existingMcps.size > 0) {
665
+ const reconfigureMcpChoice = await promptChoice(
666
+ " Reconfigure MCPs?",
667
+ ["No, keep current config", "Yes, reconfigure MCPs"],
668
+ 0
669
+ );
670
+ if (reconfigureMcpChoice === 1) {
671
+ reconfigureMcps = true;
672
+ }
673
+ }
674
+ if (!reconfigureModels && !reconfigureMcps && unconfiguredMcps.length === 0) {
549
675
  console.log(`
550
676
  ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
551
677
  `);
@@ -632,6 +758,11 @@ ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
632
758
  mid: [preset.mid],
633
759
  fast: [preset.fast]
634
760
  };
761
+ newModelsValue = {
762
+ deep: [preset.deep],
763
+ mid: [preset.mid],
764
+ fast: [preset.fast]
765
+ };
635
766
  ok(`Models configured`);
636
767
  } else if (!pluginOpts._skipModels) {
637
768
  info("Enter model IDs in <provider>/<model-id> format (e.g. amazon-bedrock/global.anthropic.claude-opus-4-7)");
@@ -645,6 +776,11 @@ ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
645
776
  mid: [midModel || deepModel],
646
777
  fast: [fastModel || midModel || deepModel]
647
778
  };
779
+ newModelsValue = {
780
+ deep: [deepModel],
781
+ mid: [midModel || deepModel],
782
+ fast: [fastModel || midModel || deepModel]
783
+ };
648
784
  ok("Models: custom");
649
785
  } else {
650
786
  ok("Models: OpenCode defaults");
@@ -653,6 +789,22 @@ ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
653
789
  delete pluginOpts._skipModels;
654
790
  console.log();
655
791
  }
792
+ if (interactive && reconfigureMcps) {
793
+ console.log(`${c.dim}Reconfigure MCP servers${c.reset}`);
794
+ const currentEnabled = new Set(existingMcps);
795
+ const selected = await promptMulti(
796
+ " Select MCPs to enable:",
797
+ MCP_TOGGLES.map((t) => ({ label: t.label, defaultOn: currentEnabled.has(t.name) }))
798
+ );
799
+ newMcpEnabledSet = new Set([...selected].map((i) => MCP_TOGGLES[i].name));
800
+ const names = [...newMcpEnabledSet].join(", ");
801
+ if (newMcpEnabledSet.size > 0) {
802
+ ok(`MCPs to enable: ${names}`);
803
+ } else {
804
+ ok("MCPs: all disabled");
805
+ }
806
+ console.log();
807
+ }
656
808
  const pluginValue = Object.keys(pluginOpts).length > 0 ? [pluginEntry, pluginOpts] : pluginEntry;
657
809
  const config = {
658
810
  $schema: "https://opencode.ai/config.json",
@@ -683,6 +835,12 @@ ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
683
835
  console.log();
684
836
  }
685
837
  }
838
+ if (reconfigureModels && newModelsValue) {
839
+ writePluginOption(configPath, "models", newModelsValue, { dryRun });
840
+ }
841
+ if (reconfigureMcps) {
842
+ writeMcpToggles(configPath, newMcpEnabledSet, { dryRun });
843
+ }
686
844
  if (!fs3.existsSync(configPath)) {
687
845
  if (dryRun) {
688
846
  info(`[dry-run] Would create ${configPath}`);
@@ -727,5 +885,7 @@ ${c.bold}Ready.${c.reset} Run ${c.green}opencode${c.reset} to start.
727
885
  export {
728
886
  requirePlugin,
729
887
  MODEL_PRESETS,
888
+ writePluginOption,
889
+ writeMcpToggles,
730
890
  install
731
891
  };
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  createAgents,
4
4
  validateModelOverride
5
- } from "./chunk-BDFZGIY7.js";
5
+ } from "./chunk-CZMAJISX.js";
6
6
  import {
7
7
  install,
8
8
  requirePlugin
9
- } from "./chunk-V3KJY6CN.js";
10
- import "./chunk-UDB4NQ2R.js";
9
+ } from "./chunk-WBBN7OVN.js";
10
+ import "./chunk-VJUETC6A.js";
11
11
 
12
12
  // src/cli.ts
13
13
  import {
@@ -27,7 +27,7 @@ import {
27
27
  import * as fs from "fs";
28
28
  import * as path from "path";
29
29
  import * as os from "os";
30
- var PLUGIN_NAME = "@glrs-dev/harness-opencode";
30
+ var PLUGIN_NAME = "@glrs-dev/harness-plugin-opencode";
31
31
  function getOpencodeConfigPath() {
32
32
  const configHome = process.env["XDG_CONFIG_HOME"] ?? path.join(os.homedir(), ".config");
33
33
  return path.join(configHome, "opencode", "opencode.json");
@@ -101,7 +101,7 @@ ${c2.blue}Uninstalling ${PLUGIN_NAME}${c2.reset}
101
101
  ok(`Removed "${PLUGIN_NAME}" from ${configPath}`);
102
102
  info(`Backup: ${bakPath}`);
103
103
  console.log(`
104
- To fully remove the package: bun remove @glrs-dev/harness-opencode
104
+ To fully remove the package: bun remove @glrs-dev/harness-plugin-opencode
105
105
  `);
106
106
  }
107
107
 
@@ -110,7 +110,7 @@ import * as fs2 from "fs";
110
110
  import * as path2 from "path";
111
111
  import * as os2 from "os";
112
112
  import { execSync } from "child_process";
113
- var PLUGIN_NAME2 = "@glrs-dev/harness-opencode";
113
+ var PLUGIN_NAME2 = "@glrs-dev/harness-plugin-opencode";
114
114
  function getOpencodeConfigPath2() {
115
115
  const configHome = process.env["XDG_CONFIG_HOME"] ?? path2.join(os2.homedir(), ".config");
116
116
  return path2.join(configHome, "opencode", "opencode.json");
@@ -514,6 +514,7 @@ var PlanSchema = z.object({
514
514
  branch_prefix: z.string().min(1).optional(),
515
515
  defaults: DefaultsSchema,
516
516
  milestones: z.array(MilestoneSchema).default([]),
517
+ setup: z.array(VerifyCommandSchema).default([]),
517
518
  tasks: z.array(TaskSchema).min(1, "plan must declare at least one task")
518
519
  }).strict();
519
520
  function parsePlan(input) {
@@ -1111,7 +1112,7 @@ async function runPlan(opts) {
1111
1112
  }
1112
1113
  process.stdout.write(
1113
1114
  `Plan ready at ${newest.path}
1114
- Build with: bunx @glrs-dev/harness-opencode pilot build
1115
+ Build with: bunx @glrs-dev/harness-plugin-opencode pilot build
1115
1116
  `
1116
1117
  );
1117
1118
  return 0;
@@ -2224,7 +2225,8 @@ var WorktreePool = class {
2224
2225
  path: "",
2225
2226
  // filled by prepare
2226
2227
  prepared: false,
2227
- preserved: false
2228
+ preserved: false,
2229
+ setupCompleted: false
2228
2230
  };
2229
2231
  this.slots.set(n, stub);
2230
2232
  return stub;
@@ -2442,7 +2444,7 @@ function kickoffPrompt(task, ctx) {
2442
2444
  sections.push(
2443
2445
  `# Pilot task: ${task.id} \u2014 ${task.title}`,
2444
2446
  ``,
2445
- `You are running unattended as the **pilot-builder** agent under the pilot subsystem of \`@glrs-dev/harness-opencode\`. This is task **${task.id}** of the plan **"${ctx.planName}"**` + (ctx.milestone ? ` (milestone: **${ctx.milestone}**)` : "") + `.`,
2447
+ `You are running unattended as the **pilot-builder** agent under the pilot subsystem of \`@glrs-dev/harness-plugin-opencode\`. This is task **${task.id}** of the plan **"${ctx.planName}"**` + (ctx.milestone ? ` (milestone: **${ctx.milestone}**)` : "") + `.`,
2446
2448
  ``,
2447
2449
  `## Workspace`,
2448
2450
  ``,
@@ -2862,6 +2864,8 @@ async function runWorker(deps) {
2862
2864
  const attempted = [];
2863
2865
  const maxAttempts = deps.maxAttempts ?? 3;
2864
2866
  const stallMs = deps.stallMs ?? 60 * 60 * 1e3;
2867
+ let setupAborted = false;
2868
+ const depsWithAbort = deps;
2865
2869
  while (true) {
2866
2870
  if (deps.abortSignal?.aborted) {
2867
2871
  return { aborted: true, attempted };
@@ -2871,7 +2875,10 @@ async function runWorker(deps) {
2871
2875
  return { aborted: false, attempted };
2872
2876
  }
2873
2877
  attempted.push(pick.task.id);
2874
- await runOneTask(deps, pick.task, { maxAttempts, stallMs });
2878
+ await runOneTask(depsWithAbort, pick.task, { maxAttempts, stallMs });
2879
+ if (depsWithAbort.setupAborted) {
2880
+ return { aborted: false, attempted };
2881
+ }
2875
2882
  const row = getTask(deps.db, deps.runId, pick.task.id);
2876
2883
  if (row && (row.status === "failed" || row.status === "aborted")) {
2877
2884
  const blocked = deps.scheduler.cascadeFail(
@@ -2957,6 +2964,78 @@ async function runOneTask(deps, task, opts) {
2957
2964
  });
2958
2965
  return;
2959
2966
  }
2967
+ const setupCommands = deps.plan.setup ?? [];
2968
+ if (setupCommands.length > 0 && !slot.setupCompleted) {
2969
+ const setupStart = Date.now();
2970
+ appendEvent(deps.db, {
2971
+ runId: deps.runId,
2972
+ taskId: task.id,
2973
+ kind: "slot.setup.started",
2974
+ payload: {
2975
+ slotIndex: slot.index,
2976
+ commands: deps.plan.setup,
2977
+ taskId: task.id
2978
+ }
2979
+ });
2980
+ const setupResult = await runVerify(setupCommands, {
2981
+ cwd: prepared.path,
2982
+ abortSignal: deps.abortSignal,
2983
+ onLine: deps.onVerifyLine
2984
+ });
2985
+ if (!setupResult.ok) {
2986
+ const durationMs = Date.now() - setupStart;
2987
+ const failure = setupResult.failure;
2988
+ const reason2 = `setup failed: ${failure.command} \u2192 exit ${failure.exitCode}`;
2989
+ appendEvent(deps.db, {
2990
+ runId: deps.runId,
2991
+ taskId: task.id,
2992
+ kind: "slot.setup.failed",
2993
+ payload: {
2994
+ slotIndex: slot.index,
2995
+ command: failure.command,
2996
+ exitCode: failure.exitCode,
2997
+ output: failure.output.slice(0, 4096),
2998
+ // truncate
2999
+ durationMs
3000
+ }
3001
+ });
3002
+ deps.pool.preserveOnFailure(slot);
3003
+ markFailedSafe(deps.db, deps.runId, task.id, reason2);
3004
+ const blocked = new Set(
3005
+ deps.scheduler.cascadeFail(task.id, reason2)
3006
+ );
3007
+ for (const row of listTasks(deps.db, deps.runId)) {
3008
+ if (row.task_id === task.id) continue;
3009
+ if (blocked.has(row.task_id)) continue;
3010
+ if (row.status !== "pending" && row.status !== "ready") continue;
3011
+ try {
3012
+ markBlocked(deps.db, deps.runId, row.task_id, reason2);
3013
+ blocked.add(row.task_id);
3014
+ } catch {
3015
+ }
3016
+ }
3017
+ for (const blockedId of blocked) {
3018
+ appendEvent(deps.db, {
3019
+ runId: deps.runId,
3020
+ taskId: blockedId,
3021
+ kind: "task.blocked",
3022
+ payload: { reason: reason2, failedDep: task.id }
3023
+ });
3024
+ }
3025
+ deps.setupAborted = true;
3026
+ return;
3027
+ }
3028
+ slot.setupCompleted = true;
3029
+ appendEvent(deps.db, {
3030
+ runId: deps.runId,
3031
+ taskId: task.id,
3032
+ kind: "slot.setup.completed",
3033
+ payload: {
3034
+ slotIndex: slot.index,
3035
+ durationMs: Date.now() - setupStart
3036
+ }
3037
+ });
3038
+ }
2960
3039
  let sessionId;
2961
3040
  try {
2962
3041
  const created = await deps.client.session.create({
@@ -3831,7 +3910,7 @@ function startStreamingLogger(args) {
3831
3910
  );
3832
3911
  if (id !== null) {
3833
3912
  writeRaw(
3834
- ` run \`bunx @glrs-dev/harness-opencode pilot logs ${id} --run ${runId}\` for full logs`
3913
+ ` run \`bunx @glrs-dev/harness-plugin-opencode pilot logs ${id} --run ${runId}\` for full logs`
3835
3914
  );
3836
3915
  }
3837
3916
  break;
@@ -3846,7 +3925,7 @@ function startStreamingLogger(args) {
3846
3925
  write(`task.stopped ${id ?? "?"} ${suffix}`);
3847
3926
  if (id !== null) {
3848
3927
  writeRaw(
3849
- ` run \`bunx @glrs-dev/harness-opencode pilot logs ${id} --run ${runId}\` for full logs`
3928
+ ` run \`bunx @glrs-dev/harness-plugin-opencode pilot logs ${id} --run ${runId}\` for full logs`
3850
3929
  );
3851
3930
  }
3852
3931
  break;
@@ -3974,8 +4053,8 @@ Failed tasks (${failed.length}):
3974
4053
  process.stdout.write(
3975
4054
  ` plan: ${planPath}
3976
4055
  run dir: ${runDir}
3977
- status: bunx @glrs-dev/harness-opencode pilot status --run ${runId}
3978
- logs: bunx @glrs-dev/harness-opencode pilot logs --run ${runId} <task-id>
4056
+ status: bunx @glrs-dev/harness-plugin-opencode pilot status --run ${runId}
4057
+ logs: bunx @glrs-dev/harness-plugin-opencode pilot logs --run ${runId} <task-id>
3979
4058
  `
3980
4059
  );
3981
4060
  }
@@ -4793,7 +4872,7 @@ import * as path12 from "path";
4793
4872
  import * as os5 from "os";
4794
4873
  import { spawn as spawn3 } from "child_process";
4795
4874
  import { fileURLToPath as fileURLToPath2 } from "url";
4796
- var PACKAGE_NAME = "@glrs-dev/harness-opencode";
4875
+ var PACKAGE_NAME = "@glrs-dev/harness-plugin-opencode";
4797
4876
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
4798
4877
  var c = {
4799
4878
  reset: "\x1B[0m",
@@ -4973,7 +5052,7 @@ Upgrade Node or run via a compatible Bun runtime. See the "engines" field in pac
4973
5052
  var VERSION = "0.1.0";
4974
5053
  var installCmd = command11({
4975
5054
  name: "install",
4976
- description: 'Add "@glrs-dev/harness-opencode" to your opencode.json plugin array.',
5055
+ description: 'Add "@glrs-dev/harness-plugin-opencode" to your opencode.json plugin array.',
4977
5056
  args: {
4978
5057
  dryRun: flag8({
4979
5058
  long: "dry-run",
@@ -4990,7 +5069,7 @@ var installCmd = command11({
4990
5069
  });
4991
5070
  var uninstallCmd = command11({
4992
5071
  name: "uninstall",
4993
- description: 'Remove "@glrs-dev/harness-opencode" from your opencode.json plugin array.',
5072
+ description: 'Remove "@glrs-dev/harness-plugin-opencode" from your opencode.json plugin array.',
4994
5073
  args: {
4995
5074
  dryRun: flag8({
4996
5075
  long: "dry-run",
@@ -5062,7 +5141,7 @@ var planDirCmd2 = command11({
5062
5141
  });
5063
5142
  var installPluginCmd = command11({
5064
5143
  name: "install-plugin",
5065
- description: 'Add "@glrs-dev/harness-opencode" to your opencode.json plugin array.',
5144
+ description: 'Add "@glrs-dev/harness-plugin-opencode" to your opencode.json plugin array.',
5066
5145
  args: {
5067
5146
  dryRun: flag8({
5068
5147
  long: "dry-run",
@@ -1,7 +1,7 @@
1
1
  import { Plugin } from '@opencode-ai/plugin';
2
2
 
3
3
  /**
4
- * @glrs-dev/harness-opencode — OpenCode plugin entry point.
4
+ * @glrs-dev/harness-plugin-opencode — OpenCode plugin entry point.
5
5
  *
6
6
  * Registers agents, commands, MCPs, tools, and skills at runtime via the
7
7
  * OpenCode plugin `config` hook. Zero filesystem writes to user space.
@@ -3,12 +3,12 @@ import {
3
3
  createAgents,
4
4
  formatModelOverrideWarning,
5
5
  validateModelOverride
6
- } from "./chunk-BDFZGIY7.js";
6
+ } from "./chunk-CZMAJISX.js";
7
7
  import {
8
8
  PACKAGE_NAME,
9
9
  readOurPackageVersion,
10
10
  refreshPluginCache
11
- } from "./chunk-UDB4NQ2R.js";
11
+ } from "./chunk-VJUETC6A.js";
12
12
 
13
13
  // src/config-hook.ts
14
14
  import * as fs from "fs";
@@ -85,7 +85,7 @@ function createMcpConfig() {
85
85
  // Use node's require.resolve to find the bundled launcher inside the
86
86
  // installed package, then exec it. Works because the MCP command runs
87
87
  // in a CJS-compatible shell context.
88
- `exec bash "$(node -e 'process.stdout.write(require.resolve("@glrs-dev/harness-opencode/dist/bin/memory-mcp-launcher.sh"))')"`
88
+ `exec bash "$(node -e 'process.stdout.write(require.resolve("@glrs-dev/harness-plugin-opencode/dist/bin/memory-mcp-launcher.sh"))')"`
89
89
  ];
90
90
  return {
91
91
  serena: {
@@ -1849,8 +1849,8 @@ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync a
1849
1849
  import { join as join8 } from "path";
1850
1850
  var APP_KEY = "A-US-3617699429";
1851
1851
  var ENDPOINT = "https://us.aptabase.com/api/v0/event";
1852
- var PKG_NAME = "@glrs-dev/harness-opencode";
1853
- var PKG_VERSION = true ? "1.0.0" : "dev";
1852
+ var PKG_NAME = "@glrs-dev/harness-plugin-opencode";
1853
+ var PKG_VERSION = true ? "0.3.1" : "dev";
1854
1854
  var DISABLED = process.env.HARNESS_OPENCODE_TELEMETRY === "0" || process.env.HARNESS_OPENCODE_TELEMETRY === "false" || process.env.DO_NOT_TRACK === "1" || process.env.CI === "true";
1855
1855
  var SESSION_ID = randomUUID();
1856
1856
  function getInstallId() {
@@ -0,0 +1,13 @@
1
+ import {
2
+ MODEL_PRESETS,
3
+ install,
4
+ writeMcpToggles,
5
+ writePluginOption
6
+ } from "./chunk-WBBN7OVN.js";
7
+ import "./chunk-VJUETC6A.js";
8
+ export {
9
+ MODEL_PRESETS,
10
+ install,
11
+ writeMcpToggles,
12
+ writePluginOption
13
+ };
@@ -11,7 +11,7 @@ A good plan trades a planning-session's worth of patient thought for hours of un
11
11
 
12
12
  ## Workflow
13
13
 
14
- Apply these eight rules in order. Each rule has its own file in `rules/` for the full text:
14
+ Apply these ten rules in order. Each rule has its own file in `rules/` for the full text:
15
15
 
16
16
  1. [`first-principles.md`](rules/first-principles.md) — Frame the task FROM the user's intent, not from a templated checklist. Ask "what does the user actually want done?" before "what files might change?"
17
17
 
@@ -29,11 +29,15 @@ Apply these eight rules in order. Each rule has its own file in `rules/` for the
29
29
 
30
30
  8. [`task-context.md`](rules/task-context.md) — Every non-trivial task carries a `context:` block. Thin plans fail because the builder works each task from scratch with no carry-over; rich context pre-loads what the builder needs to work confidently. Cover outcome, rationale, code pointers, acceptance.
31
31
 
32
+ 9. [`setup-authoring.md`](rules/setup-authoring.md) — Detect → propose → confirm the top-level `setup:` block. Covers package manager install, docker-compose services, and migration tooling detection.
33
+
34
+ 10. [`qa-expectations.md`](rules/qa-expectations.md) — Detect → propose → confirm per-surface verify patterns for UI, API, DB, integration, browser-based component, and CLI surfaces.
35
+
32
36
  ## After applying the rules
33
37
 
34
- 1. Save the YAML to the path returned by `bunx @glrs-dev/harness-opencode pilot plan-dir`.
35
- 2. Run `bunx @glrs-dev/harness-opencode pilot validate <path>` and fix every error / warning.
36
- 3. Hand off to the user with: `Plan saved to <path>. Next: bunx @glrs-dev/harness-opencode pilot build`.
38
+ 1. Save the YAML to the path returned by `bunx @glrs-dev/harness-plugin-opencode pilot plan-dir`.
39
+ 2. Run `bunx @glrs-dev/harness-plugin-opencode pilot validate <path>` and fix every error / warning.
40
+ 3. Hand off to the user with: `Plan saved to <path>. Next: bunx @glrs-dev/harness-plugin-opencode pilot build`.
37
41
 
38
42
  Do NOT summarize the plan in chat. The user can read the YAML.
39
43