@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.
- package/CHANGELOG.md +31 -0
- package/README.md +1 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.md +12 -2
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-planner.md +20 -5
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +1 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +5 -5
- package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +3 -3
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-reviewer.md +1 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-thorough.md +1 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/research-auto.md +37 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/research-local.md +33 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/research-web.md +32 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/research.md +15 -20
- package/dist/vendor/harness-opencode/dist/{chunk-BDFZGIY7.js → chunk-CZMAJISX.js} +41 -13
- package/dist/vendor/harness-opencode/dist/{chunk-UDB4NQ2R.js → chunk-VJUETC6A.js} +1 -1
- package/dist/vendor/harness-opencode/dist/{chunk-V3KJY6CN.js → chunk-WBBN7OVN.js} +166 -6
- package/dist/vendor/harness-opencode/dist/cli.js +97 -18
- package/dist/vendor/harness-opencode/dist/index.d.ts +1 -1
- package/dist/vendor/harness-opencode/dist/index.js +5 -5
- package/dist/vendor/harness-opencode/dist/install-X5KEANRB.js +13 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/SKILL.md +8 -4
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/qa-expectations.md +120 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/self-review.md +2 -2
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/setup-authoring.md +68 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/verify-design.md +4 -0
- package/dist/vendor/harness-opencode/package.json +2 -2
- package/package.json +2 -2
- 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-
|
|
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-
|
|
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
|
-
}
|
|
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-
|
|
5
|
+
} from "./chunk-CZMAJISX.js";
|
|
6
6
|
import {
|
|
7
7
|
install,
|
|
8
8
|
requirePlugin
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
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(
|
|
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-
|
|
6
|
+
} from "./chunk-CZMAJISX.js";
|
|
7
7
|
import {
|
|
8
8
|
PACKAGE_NAME,
|
|
9
9
|
readOurPackageVersion,
|
|
10
10
|
refreshPluginCache
|
|
11
|
-
} from "./chunk-
|
|
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 ? "
|
|
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() {
|
|
@@ -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
|
|
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
|
|