@soleur/opencode 1.0.0 → 3.354.2
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/commands/go.md +6 -5
- package/commands/sync.md +2 -2
- package/install.js +6 -85
- package/lib/harness.ts +1 -1
- package/lib/heartbeat-manifest.ts +19 -0
- package/lib/pr-merge-poll.ts +2 -2
- package/lib/workflow-fidelity.ts +2 -2
- package/opencode/INSTRUCTIONS.md +19 -0
- package/opencode/plugin.ts +343 -18
- package/package.json +1 -1
- package/scripts/admin-merge-ready.sh +1 -1
- package/scripts/admin-merge-ready.test.sh +0 -0
- package/scripts/affected-tests.sh +0 -0
- package/scripts/alpha-metrics.sh +0 -0
- package/scripts/alpha-metrics.test.sh +0 -0
- package/scripts/check-red-on-main.sh +0 -0
- package/scripts/ci-head-verdict.sh +54 -6
- package/scripts/cloud-detect.sh +0 -0
- package/scripts/delegation-verify.sh +0 -0
- package/scripts/delegation-verify.test.sh +0 -0
- package/scripts/deploy-arm.sh +0 -0
- package/scripts/domain-model-drift.sh +0 -0
- package/scripts/emit-decision.sh +0 -0
- package/scripts/emit-decision.test.sh +0 -0
- package/scripts/ext-filter-divergence.sh +0 -0
- package/scripts/grok-fidelity-gate.sh +0 -0
- package/scripts/grok-pre-push-gate.sh +0 -0
- package/scripts/install-opencode-adapter.ts +118 -129
- package/scripts/lib/affected-tests/common.sh +0 -0
- package/scripts/lib/affected-tests/go.sh +0 -0
- package/scripts/lib/affected-tests/js.sh +0 -0
- package/scripts/lib/affected-tests/nx-turbo.sh +0 -0
- package/scripts/lib/affected-tests/python.sh +0 -0
- package/scripts/lib/session-state.sh +0 -0
- package/scripts/lib/tmp-classify.sh +0 -0
- package/scripts/monitor-pr-checks.sh +1 -1
- package/scripts/pipeline-tally.sh +0 -0
- package/scripts/plugin-version.sh +0 -0
- package/scripts/precommit-guard.sh +3 -1
- package/scripts/precommit-guard.test.sh +0 -0
- package/scripts/render-c4-model.sh +0 -0
- package/scripts/resolve-regenerable-conflicts.sh +0 -0
- package/scripts/run-in-pid-namespace.sh +0 -0
- package/scripts/setup-ralph-loop.sh +0 -0
- package/scripts/sync-npm-package.ts +74 -87
- package/scripts/sync-opencode-adapter.ts +19 -0
- package/scripts/sync-pr-behind.sh +183 -70
- package/scripts/taste-profile-update.sh +0 -0
- package/scripts/taste-profile-update.test.sh +0 -0
- package/scripts/write-budget-marker.sh +0 -0
- package/skills/deepen-plan/SKILL.md +1 -1
- package/skills/deploy/references/hetzner-setup.md +5 -3
- package/skills/drain-prs/SKILL.md +3 -2
- package/skills/fix-issue/SKILL.md +8 -8
- package/skills/fix-issue/references/agent-authored-exclusion.md +4 -2
- package/skills/fix-issue/references/exclude-label-jq-snippet.md +4 -3
- package/skills/merge-pr/SKILL.md +5 -3
- package/skills/model-launch-review/SKILL.md +4 -4
- package/skills/one-shot/SKILL.md +1 -1
- package/skills/plan/SKILL.md +1 -1
- package/skills/plan/references/plan-sharp-edges.md +7 -1
- package/skills/postmerge/SKILL.md +2 -2
- package/skills/postmerge/references/deploy-status-debugging.md +3 -0
- package/skills/review/SKILL.md +14 -15
- package/skills/review/references/review-evidence-comment.md +36 -0
- package/skills/review/references/risk-tier-and-fix-rounds.md +2 -0
- package/skills/ship/SKILL.md +14 -13
- package/skills/ship/references/cleanup-merged-archival.md +39 -0
- package/skills/ship/references/delegated-act-authoring-form.md +7 -0
- package/skills/ship/references/epic-close-sweep.md +62 -0
- package/skills/ship/references/merge-queue-dequeue.md +1 -1
- package/skills/ship/references/operator-holds.md +30 -0
- package/skills/ship/references/queue-mode.md +1 -1
- package/skills/ship/references/ready-run-wait.md +3 -3
- package/skills/test-fix-loop/SKILL.md +1 -1
- package/skills/verification-scaffold/SKILL.md +73 -0
- package/skills/verification-scaffold/references/gitleaks.toml.template +13 -0
- package/skills/verification-scaffold/references/guardrail-audit.md +156 -0
- package/skills/verification-scaffold/references/guards/block-stash-hook.sh.template +72 -0
- package/skills/verification-scaffold/references/guards/commit-on-main-hook.sh.template +82 -0
- package/skills/verification-scaffold/references/guards/orphan-test-check.sh.template +102 -0
- package/skills/verification-scaffold/references/guards/precommit-guard.sh.template +115 -0
- package/skills/verification-scaffold/references/guards/secret-scan-hook.sh.template +176 -0
- package/skills/verification-scaffold/references/guards/verification-gates.yml.template +49 -0
- package/skills/verification-scaffold/references/guards/wrapper-boundary.sh.template +47 -0
- package/skills/verification-scaffold/references/guards.tsv +21 -0
- package/skills/verification-scaffold/references/verification-readme.template +55 -0
- package/skills/verification-scaffold/scripts/verification-scaffold.sh +785 -0
- package/skills/verification-scaffold/test/bite-proof.test.sh +159 -0
- package/skills/verification-scaffold/test/generator.test.sh +319 -0
- package/skills/verification-scaffold/test/manifest.test.sh +144 -0
- package/skills/work/SKILL.md +2 -1
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Materialize and verify the `@soleur/opencode` npm package payload from the canonical tree.
|
|
4
4
|
*
|
|
5
5
|
* bun plugins/soleur/scripts/sync-npm-package.ts
|
|
6
6
|
* bun plugins/soleur/scripts/sync-npm-package.ts --check
|
|
7
|
+
* bun plugins/soleur/scripts/sync-npm-package.ts --out-dir DIR
|
|
7
8
|
*
|
|
8
|
-
* The npm distribution
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* with no second tree to drift? Because npm has no checkout to point at. The package has to carry
|
|
15
|
-
* the bytes it installs, so a copy is unavoidable at the boundary; what ADR-245's drift obligation
|
|
16
|
-
* buys us instead is that the copy cannot be *wrong*: `--check` compares it against a fresh walk
|
|
17
|
-
* and exits 1 naming the path, so a canonical skill, agent, script or hook edited without
|
|
18
|
-
* regenerating the package reds in CI rather than shipping stale.
|
|
19
|
-
*
|
|
20
|
-
* `PAYLOAD_DIRS` is imported from the installer rather than restated here: the set the copy
|
|
21
|
-
* carries and the set the installer owns inside a config dir are the same set, and two lists
|
|
22
|
-
* would drift apart silently.
|
|
9
|
+
* The npm distribution carries a materialized copy of `PAYLOAD_DIRS` because `npm publish` packs
|
|
10
|
+
* files from the package directory. To avoid duplicating ~800 files in git, the generated payload
|
|
11
|
+
* subtrees under `packages/opencode-plugin/` are gitignored and materialized at build/publish time
|
|
12
|
+
* by this script. `PAYLOAD_DIRS` and `expectedPayloadEntries` are imported from
|
|
13
|
+
* `install-opencode-adapter.ts` so the package builder and the vendored installer share one
|
|
14
|
+
* definition of the payload and preserve executable file bits (`0755`).
|
|
23
15
|
*/
|
|
24
16
|
|
|
25
|
-
import { existsSync, mkdirSync,
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
26
18
|
import { dirname, join, resolve } from "path";
|
|
27
|
-
import { PAYLOAD_DIRS } from "./install-opencode-adapter";
|
|
19
|
+
import { PAYLOAD_DIRS, expectedPayloadEntries, walkPayloadFiles } from "./install-opencode-adapter";
|
|
28
20
|
|
|
29
21
|
/** The canonical plugin root — the copy's source. */
|
|
30
22
|
const CANONICAL_ROOT = resolve(dirname(import.meta.filename), "..");
|
|
@@ -32,123 +24,105 @@ const CANONICAL_ROOT = resolve(dirname(import.meta.filename), "..");
|
|
|
32
24
|
/** Repo-relative location of the npm package. */
|
|
33
25
|
const PACKAGE_RELATIVE = "packages/opencode-plugin";
|
|
34
26
|
|
|
35
|
-
/**
|
|
27
|
+
/** Hand-written source files at the package root that this script does NOT own. */
|
|
36
28
|
export const PACKAGE_SOURCE_FILES = ["package.json", "README.md", "install.js", "bin"] as const;
|
|
37
29
|
|
|
38
|
-
function
|
|
30
|
+
function defaultPackageRoot(repoRoot: string): string {
|
|
39
31
|
return join(repoRoot, PACKAGE_RELATIVE);
|
|
40
32
|
}
|
|
41
33
|
|
|
42
|
-
/**
|
|
43
|
-
function walk(root: string, relativeDir: string): Map<string, string> {
|
|
44
|
-
const found = new Map<string, string>();
|
|
45
|
-
const visit = (current: string): void => {
|
|
46
|
-
let entries;
|
|
47
|
-
try {
|
|
48
|
-
entries = readdirSync(join(root, current), { withFileTypes: true });
|
|
49
|
-
} catch {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
53
|
-
const path = `${current}/${entry.name}`;
|
|
54
|
-
if (entry.isDirectory()) {
|
|
55
|
-
visit(path);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
found.set(path, readFileSync(join(root, path), "utf-8"));
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
visit(relativeDir);
|
|
62
|
-
return found;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** The canonical payload, keyed package-relative — the whole truth `--check` compares against. */
|
|
34
|
+
/** The canonical payload, keyed package-relative — the truth `--check` compares against. */
|
|
66
35
|
export function expectedPackage(repoRoot: string): Map<string, string> {
|
|
67
36
|
const canonical = join(repoRoot, "plugins", "soleur");
|
|
68
37
|
const expected = new Map<string, string>();
|
|
69
|
-
for (const
|
|
70
|
-
|
|
38
|
+
for (const [path, entry] of expectedPayloadEntries(canonical)) {
|
|
39
|
+
expected.set(path, entry.content);
|
|
71
40
|
}
|
|
72
41
|
return expected;
|
|
73
42
|
}
|
|
74
43
|
|
|
75
|
-
/**
|
|
76
|
-
function
|
|
44
|
+
/** Materialized payload files under `pkgRoot`, package-relative. */
|
|
45
|
+
function materializedPackage(pkgRoot: string): string[] {
|
|
77
46
|
const found: string[] = [];
|
|
78
47
|
for (const dir of PAYLOAD_DIRS) {
|
|
79
|
-
for (const path of
|
|
48
|
+
for (const [path] of walkPayloadFiles(pkgRoot, dir)) found.push(path);
|
|
80
49
|
}
|
|
81
50
|
return found.sort();
|
|
82
51
|
}
|
|
83
52
|
|
|
84
|
-
/** Drift between
|
|
85
|
-
export function packageProblems(repoRoot: string): string[] {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
53
|
+
/** Drift between a materialized package root and a fresh walk of the canonical tree. */
|
|
54
|
+
export function packageProblems(repoRoot: string, outDir?: string): string[] {
|
|
55
|
+
const canonical = join(repoRoot, "plugins", "soleur");
|
|
56
|
+
const expected = expectedPayloadEntries(canonical);
|
|
57
|
+
const root = outDir ?? defaultPackageRoot(repoRoot);
|
|
88
58
|
const problems: string[] = [];
|
|
89
|
-
for (const [path,
|
|
59
|
+
for (const [path, entry] of expected) {
|
|
60
|
+
const full = join(root, path);
|
|
90
61
|
let current: string | undefined;
|
|
62
|
+
let currentExec = false;
|
|
91
63
|
try {
|
|
92
|
-
current = readFileSync(
|
|
64
|
+
current = readFileSync(full, "utf-8");
|
|
65
|
+
currentExec = (statSync(full).mode & 0o111) !== 0;
|
|
93
66
|
} catch {
|
|
94
67
|
current = undefined;
|
|
95
68
|
}
|
|
96
69
|
if (current === undefined) problems.push(`MISSING: ${path}`);
|
|
97
|
-
else if (current !== content) problems.push(`DRIFT: ${path}`);
|
|
70
|
+
else if (current !== entry.content || (entry.executable && !currentExec)) problems.push(`DRIFT: ${path}`);
|
|
98
71
|
}
|
|
99
|
-
for (const path of
|
|
72
|
+
for (const path of materializedPackage(root)) {
|
|
100
73
|
if (!expected.has(path)) problems.push(`EXTRA: ${path}`);
|
|
101
74
|
}
|
|
102
|
-
// The hand-written files are what makes the mirror a package. Their absence is a defect a
|
|
103
|
-
// payload-only comparison cannot see: a directory of the right bytes with no bin installs
|
|
104
|
-
// nothing.
|
|
105
75
|
for (const path of PACKAGE_SOURCE_FILES) {
|
|
106
76
|
if (!existsSync(join(root, path))) problems.push(`MISSING-SOURCE: ${path}`);
|
|
107
77
|
}
|
|
108
78
|
return problems.sort();
|
|
109
79
|
}
|
|
110
80
|
|
|
111
|
-
/** Write the payload
|
|
112
|
-
export function writePackage(repoRoot: string): string[] {
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
81
|
+
/** Write the payload into `outDir` (default: `packages/opencode-plugin`), preserving executable bits. */
|
|
82
|
+
export function writePackage(repoRoot: string, outDir?: string): string[] {
|
|
83
|
+
const canonical = join(repoRoot, "plugins", "soleur");
|
|
84
|
+
const root = outDir ?? defaultPackageRoot(repoRoot);
|
|
85
|
+
const expected = expectedPayloadEntries(canonical);
|
|
86
|
+
for (const [path, entry] of expected) {
|
|
116
87
|
const full = join(root, path);
|
|
117
88
|
mkdirSync(dirname(full), { recursive: true });
|
|
118
|
-
writeFileSync(full, content);
|
|
89
|
+
writeFileSync(full, entry.content);
|
|
90
|
+
if (entry.executable) chmodSync(full, 0o755);
|
|
119
91
|
}
|
|
120
|
-
|
|
121
|
-
// so a retired skill cannot keep installing from the package.
|
|
122
|
-
for (const path of committedPackage(repoRoot)) {
|
|
92
|
+
for (const path of materializedPackage(root)) {
|
|
123
93
|
if (!expected.has(path)) rmSync(join(root, path), { force: true });
|
|
124
94
|
}
|
|
125
|
-
return packageProblems(repoRoot);
|
|
95
|
+
return packageProblems(repoRoot, root);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Remove generated payload subtrees from `outDir` (default: `packages/opencode-plugin`). */
|
|
99
|
+
export function cleanPackage(repoRoot: string, outDir?: string): void {
|
|
100
|
+
const root = outDir ?? defaultPackageRoot(repoRoot);
|
|
101
|
+
for (const dir of PAYLOAD_DIRS) {
|
|
102
|
+
rmSync(join(root, dir), { recursive: true, force: true });
|
|
103
|
+
}
|
|
126
104
|
}
|
|
127
105
|
|
|
128
|
-
function
|
|
129
|
-
const index = argv.indexOf(
|
|
130
|
-
if (index === -1) return
|
|
131
|
-
|
|
132
|
-
if (value === undefined) throw new Error("--repo-root requires a directory");
|
|
133
|
-
return resolve(value);
|
|
106
|
+
function parseArg(argv: readonly string[], flag: string): string | undefined {
|
|
107
|
+
const index = argv.indexOf(flag);
|
|
108
|
+
if (index === -1) return undefined;
|
|
109
|
+
return argv[index + 1];
|
|
134
110
|
}
|
|
135
111
|
|
|
136
112
|
const USAGE = [
|
|
137
|
-
"Usage: bun plugins/soleur/scripts/sync-npm-package.ts [--repo-root DIR] [--check] [--help]",
|
|
113
|
+
"Usage: bun plugins/soleur/scripts/sync-npm-package.ts [--repo-root DIR] [--out-dir DIR] [--check] [--clean] [--help]",
|
|
138
114
|
"",
|
|
139
115
|
" --repo-root DIR repository root containing plugins/soleur and packages/opencode-plugin",
|
|
140
116
|
" (default: this checkout)",
|
|
141
|
-
" --
|
|
117
|
+
" --out-dir DIR package root to materialize/check (default: <repo-root>/packages/opencode-plugin)",
|
|
118
|
+
" --check compare the materialized package against a fresh walk; write nothing",
|
|
119
|
+
" --clean remove generated payload directories from the package root",
|
|
142
120
|
" --help, -h print this text and exit 0 without writing",
|
|
143
121
|
].join("\n");
|
|
144
122
|
|
|
145
|
-
/**
|
|
146
|
-
* Every argument must be known. A silently ignored typo (`--chek`) takes the WRITE path while the
|
|
147
|
-
* operator believes they asked for a check — the direction that overwrites the package.
|
|
148
|
-
*/
|
|
149
123
|
function validateArgs(argv: readonly string[]): void {
|
|
150
|
-
const withValue = new Set(["--repo-root"]);
|
|
151
|
-
const known = new Set([...withValue, "--check", "--help", "-h"]);
|
|
124
|
+
const withValue = new Set(["--repo-root", "--out-dir"]);
|
|
125
|
+
const known = new Set([...withValue, "--check", "--clean", "--help", "-h"]);
|
|
152
126
|
for (let i = 0; i < argv.length; i += 1) {
|
|
153
127
|
const arg = argv[i];
|
|
154
128
|
if (arg === "--help" || arg === "-h") {
|
|
@@ -159,15 +133,28 @@ function validateArgs(argv: readonly string[]): void {
|
|
|
159
133
|
console.error(`unknown argument: ${arg}\n${USAGE}`);
|
|
160
134
|
process.exit(2);
|
|
161
135
|
}
|
|
162
|
-
if (withValue.has(arg))
|
|
136
|
+
if (withValue.has(arg)) {
|
|
137
|
+
const next = argv[i + 1];
|
|
138
|
+
if (next === undefined || next.startsWith("-")) {
|
|
139
|
+
console.error(`${arg} requires a directory\n${USAGE}`);
|
|
140
|
+
process.exit(2);
|
|
141
|
+
}
|
|
142
|
+
i += 1;
|
|
143
|
+
}
|
|
163
144
|
}
|
|
164
145
|
}
|
|
165
146
|
|
|
166
147
|
function main(): void {
|
|
167
148
|
const argv = process.argv.slice(2);
|
|
168
149
|
validateArgs(argv);
|
|
169
|
-
const root =
|
|
170
|
-
const
|
|
150
|
+
const root = resolve(parseArg(argv, "--repo-root") ?? resolve(CANONICAL_ROOT, "..", ".."));
|
|
151
|
+
const explicitOut = parseArg(argv, "--out-dir");
|
|
152
|
+
const outDir = explicitOut !== undefined ? resolve(explicitOut) : undefined;
|
|
153
|
+
if (argv.includes("--clean")) {
|
|
154
|
+
cleanPackage(root, outDir);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const problems = argv.includes("--check") ? packageProblems(root, outDir) : writePackage(root, outDir);
|
|
171
158
|
if (problems.length > 0) {
|
|
172
159
|
for (const problem of problems) console.error(problem);
|
|
173
160
|
process.exit(1);
|
|
@@ -302,6 +302,25 @@ function instructions(): string {
|
|
|
302
302
|
"JavaScript rather than a command and are not decided by the Bash guards — a limitation this",
|
|
303
303
|
"harness shares with the guard scripts' own input contract.",
|
|
304
304
|
"",
|
|
305
|
+
"**One canonical gate is absent here, and its consequence is a refusal you cannot talk around.**",
|
|
306
|
+
"`hooks/operator-stage-approval.sh` is what mints the digest-bound approval receipt a staged",
|
|
307
|
+
"`--apply` consumes, and no OpenCode surface can both rewrite the command and ask in the same",
|
|
308
|
+
"decision (ADR-264). So `plugins/soleur/scripts/lib/operator-script.sh`'s `soleur_op_stage_gate`",
|
|
309
|
+
"finds no receipt and exits **75** (`SOLEUR_BOOTSTRAP_APPROVAL_REQUIRED`). The typed-`yes`",
|
|
310
|
+
"fallback is gated on `-t 0`/`-t 1` **and** `soleur_op_in_agent_harness`, which recognises only",
|
|
311
|
+
"`CLAUDECODE`/`CLAUDE_CODE_ENTRYPOINT` — neither is set under OpenCode — so that arm is open",
|
|
312
|
+
"here whenever the spawned shell has a tty, and a terminal confirmation from an agent is not",
|
|
313
|
+
"evidence of a person. **No receipt can be minted on this harness**, so a staged `--apply`",
|
|
314
|
+
"refuses rather than approving. Nothing is approved silently.",
|
|
315
|
+
"",
|
|
316
|
+
"A degraded hook decision — a shell call whose `workdir` could not be correlated, or a runtime",
|
|
317
|
+
"with no `tool.execute.before` surface — is announced on stderr **and** written to the event log",
|
|
318
|
+
"when `SOLEUR_OPENCODE_EVENT_LOG` names a file (its parent directory must exist; unset means no",
|
|
319
|
+
"log). Whether your terminal surfaces plugin stderr is not something this adapter measured, so",
|
|
320
|
+
"arm the log when you need certainty. A `shell` call whose `workdir` cannot be used is",
|
|
321
|
+
"**prompted** rather than run unchecked — the guards still run, against the session directory,",
|
|
322
|
+
"and only their declining becomes the prompt.",
|
|
323
|
+
"",
|
|
305
324
|
"The post-compaction re-read is **armed, not guaranteed**, unlike Codex and Devin:",
|
|
306
325
|
"`ctx.session.hook(\"compaction\")` sets state and the next `ctx.session.hook(\"context\")`",
|
|
307
326
|
"re-injects the rule corpus plus the re-read directive. §12.3 captured that `context` fire",
|