geneseed 3.1.3 → 3.3.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 +180 -0
- package/README.md +24 -14
- package/SETUP.md +38 -7
- package/SHIPPED.md +6 -5
- package/adapters/bob/README.md +40 -10
- package/adapters/claude-code/README.md +5 -0
- package/adapters/claude-code/settings.json +8 -1
- package/adapters/copilot/README.md +50 -24
- package/adapters/opencode/README.md +5 -1
- package/adapters/opencode/plugins/geneseed-guard.js +40 -6
- package/bin/build-driver.mjs +143 -89
- package/bin/geneseed-cli.mjs +63 -106
- package/bin/geneseed-hook.mjs +26 -16
- package/docs/candidate-hosts.md +74 -0
- package/docs/extending.md +6 -5
- package/docs/token-footprint.md +32 -21
- package/docs/web-ui.md +5 -13
- package/harness.config.json +1 -1
- package/js/README.md +5 -15
- package/js/build/emit-claude.mjs +26 -12
- package/js/build/emit-common.mjs +63 -114
- package/js/build/emit-opencode.mjs +74 -66
- package/js/build/generate.mjs +4 -6
- package/js/build/render.mjs +50 -32
- package/js/build/source.mjs +29 -8
- package/js/build/stubs.mjs +25 -33
- package/js/build/themes.mjs +1 -1
- package/js/build/version.mjs +1 -3
- package/js/cli-table.json +84 -0
- package/js/hosts/hooks.mjs +243 -81
- package/js/hosts/hosts.mjs +57 -5
- package/js/hosts/installs.mjs +35 -28
- package/js/hosts/link.mjs +2 -2
- package/js/hosts/mcp.mjs +3 -1
- package/js/hosts/native.mjs +54 -19
- package/js/hosts/opencode.mjs +39 -28
- package/js/hosts/settings.mjs +206 -115
- package/js/inspect/checks-authoring.mjs +5 -4
- package/js/inspect/checks-build.mjs +1 -1
- package/js/inspect/diff.mjs +1 -3
- package/js/inspect/doctor.mjs +59 -44
- package/js/inspect/excludes.mjs +5 -11
- package/js/inspect/inventory.mjs +38 -56
- package/js/inspect/registry.mjs +4 -7
- package/js/inspect/scan.mjs +8 -15
- package/js/inspect/status.mjs +51 -3
- package/js/lib/fs.mjs +34 -0
- package/js/lib/json.mjs +28 -1
- package/js/lib/paths.mjs +0 -10
- package/js/lib/udiff.mjs +11 -16
- package/js/maintain/memory.mjs +2 -4
- package/js/maintain/migrate.mjs +3 -3
- package/js/maintain/uninstall.mjs +127 -88
- package/js/maintain/update.mjs +4 -4
- package/js/ui/anim.mjs +1 -1
- package/js/ui/menu.mjs +1 -1
- package/js/ui/tui.mjs +3 -20
- package/js/web/actions.mjs +230 -217
- package/js/web/activity.mjs +71 -89
- package/js/web/api.mjs +130 -226
- package/js/web/daemon.mjs +92 -110
- package/js/web/docs.mjs +59 -126
- package/js/web/handler.mjs +94 -123
- package/js/web/jobs.mjs +116 -178
- package/js/web/routes.mjs +47 -192
- package/js/web/server.mjs +47 -82
- package/package.json +1 -1
- package/registry.json +14 -0
- package/src/AGENT.md.tmpl +55 -16
- package/src/agents/explorer.md +2 -1
- package/src/doctrines/README.md +3 -3
- package/src/doctrines/process.md +4 -2
- package/src/doctrines/rigor.md +13 -0
- package/src/laws/universal.md +133 -43
- package/src/ontology/universal.md +38 -13
- package/src/skills/_template.md +5 -1
- package/src/skills/bruno-collection-generator.md +63 -0
- package/src/skills/bruno-test-writer.md +62 -0
- package/src/skills/clarify.md +1 -1
- package/src/skills/crash-course.md +1 -0
- package/src/skills/decode.md +1 -0
- package/src/skills/drill.md +1 -0
- package/src/skills/feynman.md +1 -0
- package/src/skills/forge-mcp.md +2 -1
- package/src/skills/fresh-eyes.md +1 -1
- package/src/skills/herdr.md +1 -0
- package/src/skills/ingest.md +2 -1
- package/src/skills/learning-path.md +1 -0
- package/src/skills/opencode-theme.md +1 -0
- package/src/skills/parallel-agents.md +1 -1
- package/src/skills/ponytail.md +2 -2
- package/src/skills/research.md +4 -2
- package/themes/_TEMPLATE.json +5 -0
- package/themes/biker.json +5 -0
- package/themes/commentator.json +5 -0
- package/themes/cyberpunk.json +5 -0
- package/themes/gamer.json +5 -0
- package/themes/imperial.json +5 -0
- package/themes/joker.json +5 -0
- package/themes/marvin.json +5 -0
- package/themes/mean.json +5 -0
- package/themes/military.json +5 -0
- package/themes/neutral.json +5 -0
- package/themes/pirate.json +5 -0
- package/themes/sports.json +5 -0
- package/themes/verstappen.json +5 -0
- package/themes/wizard.json +5 -0
- package/web/dist/assets/Activity-BfVcLrda.js +1 -0
- package/web/dist/assets/ActivityDetail-CQXccGPl.js +1 -0
- package/web/dist/assets/CatalogRow-DALEQImZ.js +1 -0
- package/web/dist/assets/Diff-BIjxXx46.js +5 -0
- package/web/dist/assets/Doctor-CJLbgsrw.js +1 -0
- package/web/dist/assets/FilterInput-CkUCk_lY.js +1 -0
- package/web/dist/assets/GreenhouseView-DfC5IILJ.js +2 -0
- package/web/dist/assets/Harness-Dm5CCU-v.js +1 -0
- package/web/dist/assets/JournalView-Cpu2eI4s.js +1 -0
- package/web/dist/assets/Laws-DDDVFuQz.js +3 -0
- package/web/dist/assets/Library-Dao3T9na.js +1 -0
- package/web/dist/assets/{marked.esm-JKvyS0Pp.js → Markdown-C__nuWX5.js} +15 -15
- package/web/dist/assets/OperatorHudView-DEpCtAjP.js +2 -0
- package/web/dist/assets/Profile-BlXtUlh2.js +1 -0
- package/web/dist/assets/{Rules-CfsV1EVy.js → Rules-DIre6gxR.js} +1 -1
- package/web/dist/assets/Skills-rY13ZPhM.js +1 -0
- package/web/dist/assets/{StatusBadge-C9bZOEZk.js → StatusBadge-wW_RS_my.js} +1 -1
- package/web/dist/assets/{activity-Cc0n2jwx.js → activity-DpYPKNrA.js} +1 -1
- package/web/dist/assets/index-BEZvPvpF.js +1 -0
- package/web/dist/assets/index-DNQE5ydy.js +10 -0
- package/web/dist/assets/index-DzdQjvM1.css +1 -0
- package/web/dist/assets/index-PmQGRS1X.js +2 -0
- package/web/dist/assets/jobBuckets-DfTUJUxR.js +1 -0
- package/web/dist/assets/lawCats-DUS9UTH4.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +119 -130
- package/js/web/graph.mjs +0 -0
- package/web/dist/assets/Activity-B49fE_ug.js +0 -1
- package/web/dist/assets/ActivityDetail-abt7hzD2.js +0 -1
- package/web/dist/assets/Diff-D3_bVkih.js +0 -5
- package/web/dist/assets/Doctor-hXp7srsx.js +0 -1
- package/web/dist/assets/Graph-DvYMtlvl.js +0 -1
- package/web/dist/assets/Harness-BIWei7N5.js +0 -1
- package/web/dist/assets/Laws-CxmnRdwt.js +0 -3
- package/web/dist/assets/Library-MjbOkHIW.js +0 -1
- package/web/dist/assets/Markdown-Dx_w0faR.js +0 -1
- package/web/dist/assets/Profile-BRFvv2gr.js +0 -1
- package/web/dist/assets/Skills-DArA6IHS.js +0 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +0 -1
- package/web/dist/assets/index-DC3-GlCY.js +0 -1
- package/web/dist/assets/index-DP035wPa.js +0 -12
- package/web/dist/assets/index-dHi-7M-F.js +0 -2
- package/web/dist/assets/roman-DkYN0LX9.js +0 -1
package/js/hosts/native.mjs
CHANGED
|
@@ -149,6 +149,20 @@ function isReadonly(text) {
|
|
|
149
149
|
return text.includes('Read-only');
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* A skill only the USER may open. The marker is an HTML comment in the source spec
|
|
154
|
+
* (`<!-- invocation: user -->`, placed under the purpose blockquote), and it renders to
|
|
155
|
+
* Claude Code's `disable-model-invocation: true`: the skill still answers `/name`, but its
|
|
156
|
+
* description leaves the model's always-on catalogue and the model never self-triggers it.
|
|
157
|
+
* Same precedent as `isReadonly` — a phrase in the spec, not a second manifest to keep in
|
|
158
|
+
* step. Which skills carry it is an authoring decision: the teaching skills a human runs on
|
|
159
|
+
* themselves, and the ones gated on an environment the model cannot see (a herdr pane).
|
|
160
|
+
*/
|
|
161
|
+
const USER_INVOKED_RE = /<!--\s*invocation:\s*user\s*-->/;
|
|
162
|
+
export function isUserInvokedOnly(text) {
|
|
163
|
+
return USER_INVOKED_RE.test(text);
|
|
164
|
+
}
|
|
165
|
+
|
|
152
166
|
/** `_build_emit._strip_skill_body_links`. */
|
|
153
167
|
export function stripSkillBodyLinks(body) {
|
|
154
168
|
return body.replace(SKILL_BODY_LINK_RE, '$1');
|
|
@@ -173,14 +187,14 @@ function agentColorMap(theme) {
|
|
|
173
187
|
cleaned[k] = 'secondary';
|
|
174
188
|
}
|
|
175
189
|
}
|
|
176
|
-
if (!Object.
|
|
190
|
+
if (!Object.hasOwn(cleaned, '_default')) cleaned._default = 'secondary';
|
|
177
191
|
return cleaned;
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
/** `_build_emit._agent_color`. */
|
|
181
195
|
function agentColor(stem, theme) {
|
|
182
196
|
const colors = agentColorMap(theme);
|
|
183
|
-
return Object.
|
|
197
|
+
return Object.hasOwn(colors, stem) ? colors[stem] : colors._default;
|
|
184
198
|
}
|
|
185
199
|
|
|
186
200
|
function isPlainObject(v) {
|
|
@@ -195,7 +209,7 @@ function given(v) {
|
|
|
195
209
|
/**
|
|
196
210
|
* `overrides.get(stem) or {}`.
|
|
197
211
|
*
|
|
198
|
-
* The `
|
|
212
|
+
* The `Object.hasOwn` guard is not ceremony: a bare `overrides[stem]` reaches
|
|
199
213
|
* `Object.prototype` for an agent named `constructor`, `toString` or `valueOf`, and would
|
|
200
214
|
* hand back a function where Python hands back `None`.
|
|
201
215
|
*
|
|
@@ -206,7 +220,7 @@ function given(v) {
|
|
|
206
220
|
* difference on any path the gate covers — a difference in which malformed input crashes.
|
|
207
221
|
*/
|
|
208
222
|
function agentOverride(overrides, stem) {
|
|
209
|
-
return (Object.
|
|
223
|
+
return (Object.hasOwn(overrides, stem) && overrides[stem]) || {};
|
|
210
224
|
}
|
|
211
225
|
|
|
212
226
|
/**
|
|
@@ -230,30 +244,48 @@ export function pushOverrideLines(fm, ov) {
|
|
|
230
244
|
if (given(ov.steps)) fm.push(`steps: ${formatValue(ov.steps)}`);
|
|
231
245
|
}
|
|
232
246
|
|
|
233
|
-
/**
|
|
234
|
-
|
|
247
|
+
/**
|
|
248
|
+
* The readonly-tool line `claudeAgentFrontmatter`/`copilotAgentFrontmatter` each emit — a
|
|
249
|
+
* DENYLIST for Claude, an ALLOWLIST for Copilot — differing only in the key, the base list,
|
|
250
|
+
* the one extra entry the `<!-- bash: allow -->` marker toggles, and whether the value is
|
|
251
|
+
* bracketed. `extraWhenBashAllowed` is `false` for Claude (Bash is denied UNLESS the marker
|
|
252
|
+
* allows it) and `true` for Copilot (`execute` is allowed ONLY IF the marker is present) —
|
|
253
|
+
* the same marker, read in the opposite sense a denylist and an allowlist require.
|
|
254
|
+
*/
|
|
255
|
+
const READONLY_TOOLS = {
|
|
256
|
+
claude: {
|
|
257
|
+
key: 'disallowedTools', tools: ['Write', 'Edit', 'NotebookEdit', 'WebFetch'],
|
|
258
|
+
extra: 'Bash', extraWhenBashAllowed: false, brackets: false,
|
|
259
|
+
},
|
|
260
|
+
copilot: {
|
|
261
|
+
key: 'tools', tools: ['read', 'search', 'todo', 'agent'],
|
|
262
|
+
extra: 'execute', extraWhenBashAllowed: true, brackets: true,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/** `_build_emit._claude_agent_frontmatter` / `._copilot_agent_frontmatter`. */
|
|
267
|
+
function claudeCopilotAgentFrontmatter(host, stem, text, overrides) {
|
|
235
268
|
const fm = [`name: ${stem}`, `description: ${jsonDumps(descOf(text))}`];
|
|
236
269
|
const ov = agentOverride(overrides, stem);
|
|
237
270
|
if (isTruthy(ov.model)) fm.push(`model: ${formatValue(ov.model)}`);
|
|
238
271
|
if (isReadonly(text)) {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
272
|
+
const {
|
|
273
|
+
key, tools, extra, extraWhenBashAllowed, brackets,
|
|
274
|
+
} = READONLY_TOOLS[host];
|
|
275
|
+
const list = [...tools];
|
|
276
|
+
if (text.includes('<!-- bash: allow -->') === extraWhenBashAllowed) list.push(extra);
|
|
277
|
+
fm.push(`${key}: ${brackets ? `[${list.join(', ')}]` : list.join(', ')}`);
|
|
242
278
|
}
|
|
243
279
|
return fm;
|
|
244
280
|
}
|
|
245
281
|
|
|
246
|
-
|
|
282
|
+
function claudeAgentFrontmatter(stem, text, overrides) {
|
|
283
|
+
return claudeCopilotAgentFrontmatter('claude', stem, text, overrides);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** `tools:` is an ALLOWLIST, not a denylist. */
|
|
247
287
|
function copilotAgentFrontmatter(stem, text, overrides) {
|
|
248
|
-
|
|
249
|
-
const ov = agentOverride(overrides, stem);
|
|
250
|
-
if (isTruthy(ov.model)) fm.push(`model: ${formatValue(ov.model)}`);
|
|
251
|
-
if (isReadonly(text)) {
|
|
252
|
-
const allowed = ['read', 'search', 'todo', 'agent'];
|
|
253
|
-
if (text.includes('<!-- bash: allow -->')) allowed.push('execute');
|
|
254
|
-
fm.push(`tools: [${allowed.join(', ')}]`);
|
|
255
|
-
}
|
|
256
|
-
return fm;
|
|
288
|
+
return claudeCopilotAgentFrontmatter('copilot', stem, text, overrides);
|
|
257
289
|
}
|
|
258
290
|
|
|
259
291
|
/** `_build_emit._opencode_agent_frontmatter`. */
|
|
@@ -401,7 +433,10 @@ export function writeNativeLayer(items, agentsDir, skillsDir, overrides = null,
|
|
|
401
433
|
kind = 'agent';
|
|
402
434
|
} else {
|
|
403
435
|
// Skills are BYTE-IDENTICAL across hosts: name + description, body link-stripped.
|
|
436
|
+
// The user-only key is emitted for every host too — Claude Code and Bob honour it,
|
|
437
|
+
// OpenCode and Copilot ignore an unknown key — so the identity holds.
|
|
404
438
|
fm = [`name: ${stem}`, `description: ${jsonDumps(descOf(text))}`];
|
|
439
|
+
if (isUserInvokedOnly(text)) fm.push('disable-model-invocation: true');
|
|
405
440
|
body = stripSkillBodyLinks(body);
|
|
406
441
|
dest = path.join(skillsDir, stem, 'SKILL.md');
|
|
407
442
|
kind = 'skill';
|
package/js/hosts/opencode.mjs
CHANGED
|
@@ -115,36 +115,47 @@ export function sourceReleaseVersion(cfg) {
|
|
|
115
115
|
return '0.0.0';
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
const GRAY = 8;
|
|
119
|
+
const GREEN = 2;
|
|
120
|
+
const RED = 1;
|
|
121
|
+
const YEL = 3;
|
|
122
|
+
const MAG = 5;
|
|
123
|
+
const NONE = 'none';
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* `_build_emit._theme_json`'s role -> value map, one entry per `PALETTE_ROLES` member (19 —
|
|
127
|
+
* not the 17 the plan that asked for this derivation guessed; logged as a wrong-plan-claim
|
|
128
|
+
* row). `acc` is the one theme-tinted value; `fn` and `type` happen to share it today but are
|
|
129
|
+
* kept as their own role entries rather than aliased to `accent`, since nothing requires a
|
|
130
|
+
* future theme to tint them together.
|
|
131
|
+
*
|
|
132
|
+
* ONE SLOT IS NOT A PURE FUNCTION OF ITS ROLE, and folding it in here would be wrong rather
|
|
133
|
+
* than tidy: `syntaxPunctuation` carries role `fgMuted`, the same role as `textMuted`,
|
|
134
|
+
* `diffContext`, `diffLineNumber` and `markdownBlockQuote` — all four of which render GRAY —
|
|
135
|
+
* but `syntaxPunctuation` alone has always rendered NONE. A real asymmetry this theme has
|
|
136
|
+
* shipped with, reproduced below as a one-slot override rather than smoothed away, the same
|
|
137
|
+
* pattern `colorThemeJson` already uses for its own `TRANSPARENT_NONE` slots.
|
|
138
|
+
*/
|
|
139
|
+
const roleAnsi = (acc) => ({
|
|
140
|
+
accent: acc, secondary: MAG, err: RED, warn: YEL, ok: GREEN,
|
|
141
|
+
fg: NONE, fgMuted: GRAY, border: GRAY,
|
|
142
|
+
bg: NONE, bgPanel: NONE, bgElement: NONE, addBg: NONE, delBg: NONE,
|
|
143
|
+
comment: GRAY, kw: MAG, fn: acc, str: GREEN, num: MAG, type: acc,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/** The one slot `roleAnsi`'s role lookup gets wrong on its own — forced to NONE after. */
|
|
147
|
+
const THEME_JSON_NONE_OVERRIDE = new Set(['syntaxPunctuation']);
|
|
148
|
+
|
|
118
149
|
/** `_build_emit._theme_json` — a COMPLETE terminal-native theme tinted by ACCENT. */
|
|
119
150
|
export function themeJson(theme) {
|
|
120
151
|
const raw = theme && theme.ACCENT !== undefined ? theme.ACCENT : 'cyan';
|
|
121
152
|
const key = String(raw).toLowerCase();
|
|
122
|
-
const acc = Object.
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const NONE = 'none';
|
|
129
|
-
const t = {
|
|
130
|
-
primary: acc, secondary: MAG, accent: acc,
|
|
131
|
-
error: RED, warning: YEL, success: GREEN, info: acc,
|
|
132
|
-
text: NONE, textMuted: GRAY,
|
|
133
|
-
background: NONE, backgroundPanel: NONE, backgroundElement: NONE,
|
|
134
|
-
border: GRAY, borderActive: acc, borderSubtle: GRAY,
|
|
135
|
-
diffAdded: GREEN, diffRemoved: RED, diffContext: GRAY,
|
|
136
|
-
diffHunkHeader: acc, diffHighlightAdded: GREEN, diffHighlightRemoved: RED,
|
|
137
|
-
diffAddedBg: NONE, diffRemovedBg: NONE, diffContextBg: NONE,
|
|
138
|
-
diffLineNumber: GRAY, diffAddedLineNumberBg: NONE, diffRemovedLineNumberBg: NONE,
|
|
139
|
-
markdownText: NONE, markdownHeading: acc, markdownLink: MAG,
|
|
140
|
-
markdownLinkText: acc, markdownCode: GREEN, markdownBlockQuote: GRAY,
|
|
141
|
-
markdownEmph: YEL, markdownStrong: YEL, markdownHorizontalRule: GRAY,
|
|
142
|
-
markdownListItem: acc, markdownListEnumeration: acc, markdownImage: MAG,
|
|
143
|
-
markdownImageText: acc, markdownCodeBlock: NONE,
|
|
144
|
-
syntaxComment: GRAY, syntaxKeyword: MAG, syntaxFunction: acc,
|
|
145
|
-
syntaxVariable: NONE, syntaxString: GREEN, syntaxNumber: MAG,
|
|
146
|
-
syntaxType: acc, syntaxOperator: MAG, syntaxPunctuation: NONE,
|
|
147
|
-
};
|
|
153
|
+
const acc = Object.hasOwn(ANSI, key) ? ANSI[key] : 6;
|
|
154
|
+
const roles = roleAnsi(acc);
|
|
155
|
+
const t = {};
|
|
156
|
+
for (const [slot, role] of Object.entries(SLOT_ROLE)) {
|
|
157
|
+
t[slot] = THEME_JSON_NONE_OVERRIDE.has(slot) ? NONE : roles[role];
|
|
158
|
+
}
|
|
148
159
|
return { $schema: 'https://opencode.ai/theme.json', theme: t };
|
|
149
160
|
}
|
|
150
161
|
|
|
@@ -177,7 +188,7 @@ export function colorThemeJson(palette, transparent) {
|
|
|
177
188
|
// `palette[role]` is a plain subscript in Python: a palette missing a role raises
|
|
178
189
|
// KeyError and aborts the emit. A bare JS lookup would yield undefined, which
|
|
179
190
|
// JSON.stringify DROPS — silently emitting a theme with missing slots.
|
|
180
|
-
if (!Object.
|
|
191
|
+
if (!Object.hasOwn(palette, role)) {
|
|
181
192
|
throw new Error(`palette has no role '${role}' (slot '${slot}')`);
|
|
182
193
|
}
|
|
183
194
|
t[slot] = palette[role];
|
|
@@ -257,7 +268,7 @@ export function writePrimaryAgent(cfg, agentsDir, overrides) {
|
|
|
257
268
|
const desc = 'Primary orchestrator — works by the harness Rules and delegates to the '
|
|
258
269
|
+ 'capability subagents.';
|
|
259
270
|
const fm = [`description: ${jsonDumps(desc)}`, 'mode: primary', 'color: primary'];
|
|
260
|
-
const ov = (Object.
|
|
271
|
+
const ov = (Object.hasOwn(overrides, 'orchestrator')
|
|
261
272
|
&& overrides.orchestrator) || {};
|
|
262
273
|
pushOverrideLines(fm, ov);
|
|
263
274
|
const dest = path.join(agentsDir, 'orchestrator.md');
|