geneseed 3.20.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +9 -1
- package/SHIPPED.md +1 -1
- package/adapters/bob/README.md +31 -12
- package/adapters/claude-code/README.md +24 -17
- package/adapters/claude-code/settings.json +4 -6
- package/adapters/openclaude/README.md +13 -5
- package/adapters/opencode/plugins/geneseed-guard.js +291 -174
- package/bin/geneseed-hook.mjs +36 -5
- package/docs/README.md +3 -1
- package/docs/candidate-hosts.md +0 -8
- package/docs/concepts/enforcement-table.md +66 -0
- package/docs/concepts/hook-gates.md +2 -2
- package/docs/concepts/hooks.md +6 -6
- package/docs/concepts/host-matrix.md +4 -3
- package/docs/concepts/loop-branch.md +6 -1
- package/docs/concepts/rules.md +1 -1
- package/docs/design-history.md +59 -0
- package/docs/extending.md +11 -3
- package/docs/guides/install-opencode.md +1 -1
- package/docs/guides/loop-running.md +1 -1
- package/docs/reference/env-context.md +3 -2
- package/docs/reference/environment.md +2 -1
- package/docs/reference/plugin-guard.md +3 -3
- package/docs/reference/troubleshoot-hooks.md +34 -0
- package/docs/understand/design-choices-engineering.md +29 -0
- package/docs/understand/design-choices-gates.md +23 -0
- package/docs/understand/design-choices-hosts.md +21 -0
- package/docs/understand/design-choices-install.md +19 -0
- package/docs/understand/design-choices-shape.md +21 -0
- package/docs/understand/design-choices.md +34 -0
- package/docs/understand/enforced-vs-asked.md +5 -4
- package/docs/understand/machine-bob.md +1 -1
- package/docs/understand/machine-opencode.md +2 -2
- package/harness.config.json +1 -1
- package/js/build/driver.mjs +42 -16
- package/js/build/emit-claude.mjs +24 -8
- package/js/build/emit-opencode.mjs +33 -15
- package/js/build/generate.mjs +38 -9
- package/js/build/source.mjs +39 -0
- package/js/cli-table.json +6 -6
- package/js/hosts/gitref.mjs +3 -3
- package/js/hosts/hooks-context.mjs +11 -30
- package/js/hosts/hooks-learn.mjs +89 -8
- package/js/hosts/hooks.mjs +212 -18
- package/js/hosts/hosts.mjs +217 -13
- package/js/hosts/installs.mjs +259 -46
- package/js/hosts/native.mjs +48 -12
- package/js/hosts/opencode.mjs +47 -17
- package/js/hosts/settings.mjs +366 -42
- package/js/hosts/shim.mjs +37 -0
- package/js/inspect/checks-authoring.mjs +81 -1
- package/js/inspect/checks-constitution.mjs +2 -1
- package/js/inspect/checks-repo.mjs +68 -4
- package/js/inspect/doctor.mjs +40 -9
- package/js/inspect/scan.mjs +5 -4
- package/js/inspect/status.mjs +62 -12
- package/js/lib/text.mjs +23 -0
- package/js/loop/catalog.mjs +7 -2
- package/js/maintain/migrate.mjs +42 -5
- package/js/maintain/uninstall.mjs +150 -38
- package/js/maintain/update.mjs +22 -2
- package/js/web/actions.mjs +28 -10
- package/js/web/api.mjs +5 -3
- package/js/web/state.mjs +28 -15
- package/package.json +1 -1
- package/src/agents/_template.md +1 -1
- package/src/bricks/_template.md +1 -1
- package/src/doctrines/ops.md +9 -7
- package/src/laws/universal.md +1 -1
- package/src/skills/_template.md +3 -3
- package/src/skills/herdr.md +1 -1
- package/web/dist/assets/{Activity-IbwdHHyx.js → Activity-BCtqxjfy.js} +1 -1
- package/web/dist/assets/{ActivityDetail-B5uivdWI.js → ActivityDetail-Ba3Oo4dE.js} +1 -1
- package/web/dist/assets/{Dashboard-v6WQ37bD.js → Dashboard-DpHza68-.js} +2 -2
- package/web/dist/assets/{Docs-CypIfDBX.js → Docs-Bw_-UK0O.js} +1 -1
- package/web/dist/assets/{GreenhouseView-ClxP0hQG.js → GreenhouseView-DnJ1Ggp1.js} +1 -1
- package/web/dist/assets/{Installs-CPkQeFjO.js → Installs-CWoNKFBk.js} +1 -1
- package/web/dist/assets/{JournalView-BeEInhUf.js → JournalView-g6-bw4c8.js} +1 -1
- package/web/dist/assets/{Laws-Ds6U5xBQ.js → Laws-DkVWR1tb.js} +1 -1
- package/web/dist/assets/{Library-BraUKPyQ.js → Library-CK9HZzjV.js} +1 -1
- package/web/dist/assets/{Loops-DWcy3mwK.js → Loops-BP98SMOH.js} +1 -1
- package/web/dist/assets/{OperatorHudView-CQyrJQ9m.js → OperatorHudView-DI_n12M9.js} +1 -1
- package/web/dist/assets/{Personal-YWqs6qFL.js → Personal-Bk89f13a.js} +1 -1
- package/web/dist/assets/{Tabs-EhzEC-Xh.js → Tabs-3WuYIE1b.js} +1 -1
- package/web/dist/assets/{drawable-CEKPNV7X.js → drawable-oCUeq9e5.js} +1 -1
- package/web/dist/assets/{index-CPi22b7y.js → index-CiXwChvf.js} +2 -2
- package/web/dist/index.html +1 -1
package/js/hosts/opencode.mjs
CHANGED
|
@@ -28,7 +28,10 @@ import { jsonDumps, jsonDumpsIndent, parseJson } from '../lib/json.mjs';
|
|
|
28
28
|
import { comparePaths } from '../lib/paths.mjs';
|
|
29
29
|
import { sourceReleaseVersion } from '../build/version.mjs';
|
|
30
30
|
import { aliasesOf } from '../build/source.mjs';
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
firstBlockquote, isUserInvokedOnly, skillDescription, stripSkillBodyLinks, pointSideFiles,
|
|
33
|
+
pushOverrideLines,
|
|
34
|
+
} from './native.mjs';
|
|
32
35
|
|
|
33
36
|
// Re-exported for `js/web/api.mjs` and the tests, which import it from here.
|
|
34
37
|
export { sourceReleaseVersion };
|
|
@@ -169,10 +172,19 @@ export function themeJson(theme) {
|
|
|
169
172
|
return { $schema: 'https://opencode.ai/theme.json', theme: t };
|
|
170
173
|
}
|
|
171
174
|
|
|
172
|
-
/**
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
/**
|
|
176
|
+
* `_build_emit._write_theme` — `<themes_dir>/geneseed-<theme>.json`.
|
|
177
|
+
*
|
|
178
|
+
* `claim` — the same `claimer` closure the native layer and command writers share (see
|
|
179
|
+
* `writePrimaryAgent`'s docblock). Without it a user's own same-named theme file was
|
|
180
|
+
* overwritten and recorded in `owned`, so uninstall deleted it next. Default: claim
|
|
181
|
+
* everything, for a caller with no manifest to consult. Returns `null`, not the dest,
|
|
182
|
+
* when the claim is refused — so the caller does not push a non-write into `owned`.
|
|
183
|
+
*/
|
|
184
|
+
export function writeTheme(themesDir, themeName, theme, claim = (_dest) => true) {
|
|
175
185
|
const dest = path.join(themesDir, `geneseed-${themeName}.json`);
|
|
186
|
+
if (!claim(dest)) return null;
|
|
187
|
+
mkdirSync(themesDir, { recursive: true });
|
|
176
188
|
writeText(dest, `${jsonDumpsIndent(themeJson(theme))}\n`);
|
|
177
189
|
return dest;
|
|
178
190
|
}
|
|
@@ -215,8 +227,8 @@ export function colorThemeFiles(cfg) {
|
|
|
215
227
|
.sort(comparePaths);
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
/** `_build_emit._write_color_themes` — each curated palette in both flavours. */
|
|
219
|
-
export function writeColorThemes(cfg, themesDir) {
|
|
230
|
+
/** `_build_emit._write_color_themes` — each curated palette in both flavours. `claim` — see `writeTheme`. */
|
|
231
|
+
export function writeColorThemes(cfg, themesDir, claim = (_dest) => true) {
|
|
220
232
|
mkdirSync(themesDir, { recursive: true });
|
|
221
233
|
const written = [];
|
|
222
234
|
for (const src of colorThemeFiles(cfg)) {
|
|
@@ -224,6 +236,7 @@ export function writeColorThemes(cfg, themesDir) {
|
|
|
224
236
|
const palette = spec.palette;
|
|
225
237
|
for (const [flavour, transparent] of [['solid', false], ['transparent', true]]) {
|
|
226
238
|
const dest = path.join(themesDir, `geneseed-${spec.name}-${flavour}.json`);
|
|
239
|
+
if (!claim(dest)) continue;
|
|
227
240
|
writeText(dest, `${jsonDumpsIndent(colorThemeJson(palette, transparent))}\n`);
|
|
228
241
|
written.push(dest);
|
|
229
242
|
}
|
|
@@ -336,9 +349,17 @@ export function writeCommandLayer(cfg, items, commandDir, claim = (_dest) => tru
|
|
|
336
349
|
* second copy of its target. UNCONDITIONAL, unlike the `GENESEED_COMMANDS` set — an old name
|
|
337
350
|
* that only works behind an opt-in is a name that broke. Read off the already-filtered
|
|
338
351
|
* `items`, so excluding the target drops its aliases.
|
|
352
|
+
*
|
|
353
|
+
* `alsoUserOnly` (Bob host-compat O2/R1): Bob's skills page documents no model-invocation
|
|
354
|
+
* key either, so a genuinely user-only skill (not just an alias) needs the same treatment —
|
|
355
|
+
* `writeNativeLayer({ bobDialect: true })` already skipped its SKILL.md, and this is the one
|
|
356
|
+
* place left to give `/<stem>` a home. Default `false`: OpenCode's own call site leaves this
|
|
357
|
+
* off, because a real user-only skill there is handled by `permission.skill` deny instead
|
|
358
|
+
* (Task 4) — it keeps its SKILL.md and does not need a second, command-shaped copy.
|
|
339
359
|
*/
|
|
340
360
|
export function writeAliasCommands(cfg, items, commandDir, claim = (_dest) => true,
|
|
341
|
-
skillDirOf = (n) => path.join(path.dirname(commandDir), 'skills', n).split(path.sep).join('/')
|
|
361
|
+
skillDirOf = (n) => path.join(path.dirname(commandDir), 'skills', n).split(path.sep).join('/'),
|
|
362
|
+
alsoUserOnly = false) {
|
|
342
363
|
const written = [];
|
|
343
364
|
for (const { text, src } of items) {
|
|
344
365
|
if (text === null) continue;
|
|
@@ -346,11 +367,13 @@ export function writeAliasCommands(cfg, items, commandDir, claim = (_dest) => tr
|
|
|
346
367
|
if (sp.length !== 2 || sp[0] !== 'skills' || !sp[1].endsWith('.md') || sp[1].startsWith('_')) continue;
|
|
347
368
|
const stem = sp[1].slice(0, -3);
|
|
348
369
|
const body = stripSkillBodyLinks(pointSideFiles(text.replace(/^\n+/, ''), stem, skillDirOf(stem)));
|
|
349
|
-
|
|
350
|
-
|
|
370
|
+
const names = alsoUserOnly && isUserInvokedOnly(text) ? [...aliasesOf(text), stem] : aliasesOf(text);
|
|
371
|
+
for (const name of names) {
|
|
372
|
+
const dest = path.join(commandDir, `${name}.md`);
|
|
351
373
|
if (!claim(dest)) continue;
|
|
352
374
|
mkdirSync(path.dirname(dest), { recursive: true });
|
|
353
|
-
|
|
375
|
+
const desc = name === stem ? skillDescription(text) : `Alias of ${stem}.`;
|
|
376
|
+
writeText(dest, `---\ndescription: ${jsonDumps(desc)}\n---\n\n${body}`);
|
|
354
377
|
written.push(dest);
|
|
355
378
|
}
|
|
356
379
|
}
|
|
@@ -367,8 +390,13 @@ export function writePonytailCommand(commandDir, claim = (_dest) => true) {
|
|
|
367
390
|
return dest;
|
|
368
391
|
}
|
|
369
392
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
393
|
+
/**
|
|
394
|
+
* `_copy_plugins` / `_copy_workflows` — maintained files, copied verbatim (copy2, so LF).
|
|
395
|
+
*
|
|
396
|
+
* `claim` — see `writeTheme`. A file the claim refuses is skipped: not copied, not counted,
|
|
397
|
+
* not pushed into `owned`, exactly as a declined native-layer write is.
|
|
398
|
+
*/
|
|
399
|
+
function copyJsDir(srcDir, dst, owned, prefix, claim = (_dest) => true) {
|
|
372
400
|
let n = 0;
|
|
373
401
|
if (existsSync(srcDir) && statSync(srcDir).isDirectory()) {
|
|
374
402
|
mkdirSync(dst, { recursive: true });
|
|
@@ -377,7 +405,9 @@ function copyJsDir(srcDir, dst, owned, prefix) {
|
|
|
377
405
|
.map((name) => path.join(srcDir, name))
|
|
378
406
|
.sort(comparePaths);
|
|
379
407
|
for (const js of files) {
|
|
380
|
-
|
|
408
|
+
const dest = path.join(dst, path.basename(js));
|
|
409
|
+
if (!claim(dest)) continue;
|
|
410
|
+
copyFile(js, dest);
|
|
381
411
|
if (owned !== null && owned !== undefined) owned.push(`${prefix}/${path.basename(js)}`);
|
|
382
412
|
n += 1;
|
|
383
413
|
}
|
|
@@ -386,11 +416,11 @@ function copyJsDir(srcDir, dst, owned, prefix) {
|
|
|
386
416
|
}
|
|
387
417
|
|
|
388
418
|
/** `_build_emit._copy_plugins`. */
|
|
389
|
-
export function copyPlugins(cfg, dst, owned = null) {
|
|
390
|
-
return copyJsDir(cfg.pluginSrc, dst, owned, 'plugins');
|
|
419
|
+
export function copyPlugins(cfg, dst, owned = null, claim = (_dest) => true) {
|
|
420
|
+
return copyJsDir(cfg.pluginSrc, dst, owned, 'plugins', claim);
|
|
391
421
|
}
|
|
392
422
|
|
|
393
423
|
/** `_build_emit._copy_workflows`. */
|
|
394
|
-
export function copyWorkflows(cfg, dst, owned = null) {
|
|
395
|
-
return copyJsDir(cfg.workflowSrc, dst, owned, 'workflows');
|
|
424
|
+
export function copyWorkflows(cfg, dst, owned = null, claim = (_dest) => true) {
|
|
425
|
+
return copyJsDir(cfg.workflowSrc, dst, owned, 'workflows', claim);
|
|
396
426
|
}
|