geneseed 3.17.0 → 3.19.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 +39 -0
- package/DESIGN.md +1 -1
- package/README.md +23 -5
- package/SHIPPED.md +3 -3
- package/adapters/claude-code/skills/geneseed/SKILL.md +1 -1
- package/adapters/opencode/README.md +2 -2
- package/adapters/opencode/plugins/geneseed-context.js +19 -17
- package/adapters/opencode/plugins/geneseed-guard.js +7 -7
- package/bin/geneseed-cli.mjs +5 -0
- package/docs/concepts/exclusions.md +1 -1
- package/docs/concepts/skills.md +6 -2
- package/docs/extending.md +33 -0
- package/docs/guides/project-context.md +20 -0
- package/docs/guides/read-non-markdown.md +1 -1
- package/docs/guides/uninstall.md +1 -1
- package/docs/guides/upgrade.md +1 -1
- package/docs/guides/wiki.md +4 -4
- package/docs/opencode-plugin-setup.md +1 -1
- package/docs/reference/env-context.md +2 -2
- package/docs/reference/plugin-context.md +2 -2
- package/docs/reference/plugin-guard.md +1 -1
- package/docs/token-footprint.md +18 -15
- package/docs/understand/take-it-out.md +1 -1
- package/harness.config.json +1 -1
- package/js/README.md +4 -1
- package/js/build/args.mjs +22 -20
- package/js/build/bundle.mjs +1 -3
- package/js/build/emit-claude.mjs +5 -4
- package/js/build/emit-opencode.mjs +9 -2
- package/js/build/generate.mjs +6 -6
- package/js/build/render.mjs +19 -12
- package/js/build/source.mjs +82 -7
- package/js/build/stubs.mjs +35 -6
- package/js/build/themes.mjs +1 -3
- package/js/cli-table.json +53 -0
- package/js/hosts/hooks-context.mjs +13 -6
- package/js/hosts/hosts.mjs +1 -3
- package/js/hosts/installs.mjs +13 -7
- package/js/hosts/mcp.mjs +1 -1
- package/js/hosts/native.mjs +60 -5
- package/js/hosts/opencode.mjs +37 -6
- package/js/hosts/settings.mjs +1 -0
- package/js/hosts/shim.mjs +1 -0
- package/js/inspect/checks-authoring.mjs +4 -1
- package/js/inspect/checks-counts.mjs +8 -3
- package/js/inspect/checks-repo.mjs +166 -2
- package/js/inspect/diff.mjs +5 -1
- package/js/inspect/doctor.mjs +2 -1
- package/js/inspect/inventory.mjs +7 -9
- package/js/inspect/scorecard-checks.mjs +571 -0
- package/js/inspect/scorecard-svg.mjs +85 -0
- package/js/inspect/scorecard.mjs +839 -0
- package/js/lib/json.mjs +1 -1
- package/js/maintain/migrate.mjs +2 -1
- package/js/maintain/uninstall.mjs +2 -3
- package/js/maintain/update.mjs +10 -3
- package/js/ui/menu.mjs +2 -2
- package/js/ui/tui.mjs +1 -0
- package/js/web/api.mjs +2 -0
- package/js/web/catalog.mjs +24 -7
- package/js/web/daemon.mjs +2 -1
- package/js/web/routes.mjs +2 -0
- package/js/web/server.mjs +1 -1
- package/js/web/user-files.mjs +1 -0
- package/package.json +11 -3
- package/registry.json +12 -75
- package/src/AGENT.md.tmpl +19 -24
- package/src/agents/docs.md +1 -1
- package/src/agents/researcher.md +1 -1
- package/src/agents/reviewer.md +3 -0
- package/src/bricks/bruno-test.md +2 -2
- package/src/bricks/ci-fix.md +3 -3
- package/src/bricks/deps-audit.md +3 -3
- package/src/bricks/migration-plan.md +2 -2
- package/src/doctrines/comms.md +19 -0
- package/src/doctrines/process.md +2 -2
- package/src/laws/universal.md +2 -2
- package/src/skills/brainstorm.md +14 -10
- package/src/skills/bruno.md +84 -0
- package/src/skills/codebase-design.md +41 -23
- package/src/skills/daydream/SKILL.md +4 -3
- package/src/skills/daydream/VENDOR.md +6 -2
- package/src/skills/daydream/instructions.md +1 -1
- package/src/skills/debug/ci.md +11 -0
- package/src/skills/debug.md +11 -8
- package/src/skills/dependencies.md +65 -0
- package/src/skills/develop.md +9 -4
- package/src/skills/document-project.md +27 -22
- package/src/skills/explain-changes/SKILL.md +1 -1
- package/src/skills/fresh-eyes.md +1 -1
- package/src/skills/geneseed-code-review.md +21 -9
- package/src/skills/geneseed.md +4 -1
- package/src/skills/git-history.md +78 -0
- package/src/skills/handoff.md +4 -3
- package/src/skills/parallel-agents.md +2 -1
- package/src/skills/pipeline.md +1 -1
- package/src/skills/plan.md +5 -1
- package/src/skills/ponytail.md +1 -1
- package/src/skills/prototype.md +10 -7
- package/src/skills/react-view-transitions/SKILL.md +1 -0
- package/src/skills/react-view-transitions/VENDOR.md +4 -0
- package/src/skills/research.md +6 -1
- package/src/skills/review-response.md +8 -2
- package/src/skills/roast-me.md +8 -7
- package/src/skills/rule/consolidate.md +32 -0
- package/src/skills/rule.md +16 -4
- package/src/skills/security-audit.md +1 -1
- package/src/skills/ship.md +16 -9
- package/src/skills/teach.md +5 -3
- package/src/skills/wayfinder.md +19 -16
- package/src/skills/wiki.md +2 -2
- package/themes/_TEMPLATE.json +9 -17
- package/themes/biker.json +9 -17
- package/themes/commentator.json +9 -17
- package/themes/cyberpunk.json +9 -17
- package/themes/gamer.json +9 -17
- package/themes/imperial.json +9 -17
- package/themes/joker.json +9 -17
- package/themes/marvin.json +9 -17
- package/themes/mean.json +9 -17
- package/themes/military.json +9 -17
- package/themes/neutral.json +9 -17
- package/themes/pirate.json +9 -17
- package/themes/sports.json +9 -17
- package/themes/verstappen.json +9 -17
- package/themes/wizard.json +9 -17
- package/web/dist/assets/{Activity-DonKN1pi.js → Activity-IbwdHHyx.js} +1 -1
- package/web/dist/assets/{ActivityDetail-e47a3cdE.js → ActivityDetail-B5uivdWI.js} +1 -1
- package/web/dist/assets/{Dashboard-Cr7i0PsO.js → Dashboard-v6WQ37bD.js} +2 -2
- package/web/dist/assets/{Docs-Lne3nuzD.js → Docs-CypIfDBX.js} +1 -1
- package/web/dist/assets/FilterInput-BSfpStOZ.js +1 -0
- package/web/dist/assets/{GreenhouseView-4CbIuTpl.js → GreenhouseView-ClxP0hQG.js} +1 -1
- package/web/dist/assets/{Installs-CIzbV1Ks.js → Installs-CPkQeFjO.js} +1 -1
- package/web/dist/assets/{JournalView-BlBfuvzj.js → JournalView-BeEInhUf.js} +1 -1
- package/web/dist/assets/{Laws-B-pjcjHr.js → Laws-Ds6U5xBQ.js} +1 -1
- package/web/dist/assets/Library-BraUKPyQ.js +1 -0
- package/web/dist/assets/{Loops-DIOxW1xH.js → Loops-DWcy3mwK.js} +1 -1
- package/web/dist/assets/Markdown-C6fRd4A4.js +67 -0
- package/web/dist/assets/{OperatorHudView-D7S7z7CJ.js → OperatorHudView-CQyrJQ9m.js} +1 -1
- package/web/dist/assets/{Personal-Ws3v35AB.js → Personal-YWqs6qFL.js} +1 -1
- package/web/dist/assets/{Tabs-6Z1eziAq.js → Tabs-EhzEC-Xh.js} +1 -1
- package/web/dist/assets/{activity-Dr7ZMBI3.js → activity-dcg5vF9j.js} +1 -1
- package/web/dist/assets/{drawable-ATNeYKbT.js → drawable-CEKPNV7X.js} +1 -1
- package/web/dist/assets/{index-CDg5XUhG.js → index-CPi22b7y.js} +2 -2
- package/web/dist/assets/react-DRhoX0kS.js +9 -0
- package/web/dist/assets/{usePoll-DD8VjtSg.js → usePoll-BmR631BB.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +5 -0
- package/src/skills/bruno-collection-generator.md +0 -52
- package/src/skills/bruno-test-writer.md +0 -51
- package/src/skills/ci-fix.md +0 -49
- package/src/skills/consolidate-memory.md +0 -50
- package/src/skills/deps-audit.md +0 -46
- package/src/skills/domain-modeling.md +0 -27
- package/src/skills/git-archaeology.md +0 -36
- package/src/skills/git-rescue.md +0 -58
- package/src/skills/migrate.md +0 -34
- package/src/skills/opencode-theme.md +0 -42
- package/src/skills/repo-map.md +0 -25
- package/src/skills/tickets.md +0 -33
- package/web/dist/assets/FilterInput-CVyqmYWM.js +0 -1
- package/web/dist/assets/Library-x8Nzg6yq.js +0 -1
- package/web/dist/assets/Markdown-X2a3qvOh.js +0 -38
- package/web/dist/assets/react-B9Z_tXRV.js +0 -8
|
@@ -7,15 +7,20 @@
|
|
|
7
7
|
* itself and compares it against what is on disk.
|
|
8
8
|
*/
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
ALIASES_RE, PLUGIN_SRC, RETIRED_SKILL_IDS, ROOT, SRC, THEMES, WORKFLOW_SRC, aliasesOf, knownSkillIds,
|
|
12
|
+
} from '../build/source.mjs';
|
|
11
13
|
import { VENDORED_SKILL_DIRS } from '../hosts/native.mjs';
|
|
14
|
+
import { COMMAND_SET } from '../hosts/opencode.mjs';
|
|
12
15
|
import { hookShimPath, shimDeadPaths } from '../hosts/shim.mjs';
|
|
13
16
|
import { readText } from '../lib/fs.mjs';
|
|
14
17
|
import { formatRepr, formatValue } from '../lib/json.mjs';
|
|
15
18
|
import { splitLines } from '../lib/udiff.mjs';
|
|
16
19
|
import { ENTITY_STATUSES } from './inventory.mjs';
|
|
20
|
+
import { assessRepo } from './scorecard.mjs';
|
|
21
|
+
import { badgeUrl, renderSvg } from './scorecard-svg.mjs';
|
|
17
22
|
import { isDir, isFile, rglob, srcStems } from './scan.mjs';
|
|
18
|
-
import { readFileSync } from 'node:fs';
|
|
23
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* `_harness_build._registry_keys` — every entity the registry must describe.
|
|
@@ -106,6 +111,7 @@ export function registryProblems() {
|
|
|
106
111
|
*
|
|
107
112
|
* `(?i)` becomes the `i` flag on the last one only, which is where the Python puts it.
|
|
108
113
|
*/
|
|
114
|
+
/** @type {Array<[string, RegExp]>} */
|
|
109
115
|
const SECRET_PATTERNS = [
|
|
110
116
|
['Anthropic API key', /sk-ant-[A-Za-z0-9_-]{20,}/],
|
|
111
117
|
['OpenAI-style API key', /\bsk-[A-Za-z0-9]{32,}/],
|
|
@@ -240,6 +246,83 @@ export function vendorPinProblems() {
|
|
|
240
246
|
return problems;
|
|
241
247
|
}
|
|
242
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Every `<!-- aliases: … -->` name is a clean, unclaimed, single-owner path segment.
|
|
251
|
+
*
|
|
252
|
+
* An alias becomes a folder (`skills/<alias>/`) and a command file (`command/<alias>.md`) on
|
|
253
|
+
* every install, so a name that collides overwrites something: another skill, another
|
|
254
|
+
* skill's alias, or an OpenCode command (`COMMAND_SET`, `/ponytail`). A retired skill id is
|
|
255
|
+
* refused as an alias AND as a live skill — `RETIRED_SKILL_IDS` is the list of names that
|
|
256
|
+
* already meant something on someone's machine. The emit trusts all of this; this is where
|
|
257
|
+
* it is checked.
|
|
258
|
+
*/
|
|
259
|
+
export function aliasProblems(src = SRC) {
|
|
260
|
+
const problems = [];
|
|
261
|
+
const known = knownSkillIds(src);
|
|
262
|
+
const reserved = new Set([...Object.keys(COMMAND_SET), 'ponytail']);
|
|
263
|
+
for (const id of known) {
|
|
264
|
+
if (Object.hasOwn(RETIRED_SKILL_IDS, id)) {
|
|
265
|
+
problems.push(`[authoring] skill '${id}' reuses a retired skill id (RETIRED_SKILL_IDS)`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const owner = new Map();
|
|
269
|
+
for (const id of known) {
|
|
270
|
+
let text;
|
|
271
|
+
try { text = readText(path.join(src, 'skills', `${id}.md`)); } catch { continue; } // a folder skill
|
|
272
|
+
// `aliasesOf` reads the FIRST marker line only, so a second one would be dropped in silence.
|
|
273
|
+
if ((text.match(new RegExp(ALIASES_RE.source, 'gm')) || []).length > 1) {
|
|
274
|
+
problems.push(`[authoring] skills/${id}.md declares aliases on more than one line — merge them`);
|
|
275
|
+
}
|
|
276
|
+
for (const alias of aliasesOf(text)) {
|
|
277
|
+
const where = `skills/${id}.md alias '${alias}'`;
|
|
278
|
+
if (!/^[a-z0-9-]+$/.test(alias)) problems.push(`[authoring] ${where} is not [a-z0-9-]+`);
|
|
279
|
+
else if (known.includes(alias)) problems.push(`[authoring] ${where} is already a skill`);
|
|
280
|
+
else if (Object.hasOwn(RETIRED_SKILL_IDS, alias)) {
|
|
281
|
+
problems.push(`[authoring] ${where} is a retired skill id (RETIRED_SKILL_IDS)`);
|
|
282
|
+
} else if (reserved.has(alias)) problems.push(`[authoring] ${where} is an OpenCode command name`);
|
|
283
|
+
else if (owner.has(alias)) {
|
|
284
|
+
problems.push(`[authoring] ${where} is already declared by skills/${owner.get(alias)}.md`);
|
|
285
|
+
} else owner.set(alias, id);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return problems;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* A first-party skill's side files (`src/skills/<name>/<file>.md`, emitted beside its SKILL.md
|
|
293
|
+
* and read only when the body points at them) keep the one layout every writer assumes: a flat
|
|
294
|
+
* `<name>.md` owns the folder, files sit one level down, each is `.md` (rendered and themed),
|
|
295
|
+
* none is `SKILL.md` (the emit writes that one), and the spec links every one as
|
|
296
|
+
* `[…](<name>/<file>)` — an unlinked side file ships to every install and is never read.
|
|
297
|
+
* Vendored folders are exempt: they carry their own SKILL.md and their own layout.
|
|
298
|
+
*/
|
|
299
|
+
export function sideFileProblems(src = SRC) {
|
|
300
|
+
const problems = [];
|
|
301
|
+
const dir = path.join(src, 'skills');
|
|
302
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
303
|
+
if (!e.isDirectory() || e.name.startsWith('_') || VENDORED_SKILL_DIRS.has(e.name)) continue;
|
|
304
|
+
const name = e.name;
|
|
305
|
+
let spec;
|
|
306
|
+
try { spec = readText(path.join(dir, `${name}.md`)); } catch {
|
|
307
|
+
problems.push(`[authoring] skills/${name}/ has no skills/${name}.md — a side-file folder `
|
|
308
|
+
+ 'belongs to a flat spec (or list a folder skill in VENDORED_SKILL_DIRS)');
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
for (const f of readdirSync(path.join(dir, name), { withFileTypes: true })) {
|
|
312
|
+
const where = `skills/${name}/${f.name}`;
|
|
313
|
+
if (!f.isFile() || !f.name.endsWith('.md')) {
|
|
314
|
+
problems.push(`[authoring] ${where} — a side file is a .md one level down`);
|
|
315
|
+
} else if (f.name === 'SKILL.md') {
|
|
316
|
+
problems.push(`[authoring] ${where} collides with the SKILL.md the emit writes`);
|
|
317
|
+
} else if (!spec.includes(`](${name}/${f.name})`) && !spec.includes(`](${name}/${f.name}#`)) {
|
|
318
|
+
problems.push(`[authoring] ${where} is not linked from skills/${name}.md — `
|
|
319
|
+
+ `link it as [${name}/${f.name}](${name}/${f.name}) or delete it`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return problems.sort();
|
|
324
|
+
}
|
|
325
|
+
|
|
243
326
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
244
327
|
|
|
245
328
|
/**
|
|
@@ -359,3 +442,84 @@ export function moduleMapProblems() {
|
|
|
359
442
|
}
|
|
360
443
|
return problems;
|
|
361
444
|
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* The AI Harness Scorecard checks this repo passes, written out — the ratchet's floor.
|
|
448
|
+
*
|
|
449
|
+
* Taken from `geneseed scorecard` on main after #209-#213 (security policy, PR template, ARCHITECTURE
|
|
450
|
+
* and ADRs, the audit and commitlint jobs, Dependabot, coverage, property tests, fuzzing): 26 of 31,
|
|
451
|
+
* 81.2/100, matching upstream's Python tool check for check. Then the weekly Stryker run and the
|
|
452
|
+
* offline link check added mutation_testing and stale_doc_detection: 28 of 31, 88.7 (A); the checkJs
|
|
453
|
+
* pass added type_safety (partial: `strict` is off): 29 of 31, 90.4; calling the web job's prettier and vitest
|
|
454
|
+
* directly (the scorecard reads only the root package.json's scripts) added formatter_enforcement
|
|
455
|
+
* and lifted test_suite_exists to 3 of 3: 30 of 31, 95.3. Grouped by pillar, in upstream order.
|
|
456
|
+
* The ratchet is on `passed`, which upstream sets for any score above zero: `type_safety`
|
|
457
|
+
* passes at 1.5 of 3, and a full check sliding to a partial one is
|
|
458
|
+
* invisible here. A check this repo starts to pass is added by hand; one it stops passing is a
|
|
459
|
+
* doctor problem until fixed or deliberately removed from this list, in the same commit.
|
|
460
|
+
*/
|
|
461
|
+
export const SCORECARD_FLOOR = [
|
|
462
|
+
'architecture_doc',
|
|
463
|
+
'agent_instructions',
|
|
464
|
+
'adr_presence',
|
|
465
|
+
'module_boundary_docs',
|
|
466
|
+
'ci_pipeline_exists',
|
|
467
|
+
'linter_enforcement',
|
|
468
|
+
'formatter_enforcement',
|
|
469
|
+
'type_safety',
|
|
470
|
+
'dependency_auditing',
|
|
471
|
+
'conventional_commits',
|
|
472
|
+
'unsafe_code_policy',
|
|
473
|
+
'test_suite_exists',
|
|
474
|
+
'feature_matrix_testing',
|
|
475
|
+
'coverage_measurement',
|
|
476
|
+
'mutation_testing',
|
|
477
|
+
'property_based_testing',
|
|
478
|
+
'fuzz_testing',
|
|
479
|
+
'contract_tests',
|
|
480
|
+
'tests_blocking_ci',
|
|
481
|
+
'code_review_required',
|
|
482
|
+
'scheduled_ci',
|
|
483
|
+
'stale_doc_detection',
|
|
484
|
+
'mr_template',
|
|
485
|
+
'automated_review',
|
|
486
|
+
'doc_sync_check',
|
|
487
|
+
'ai_usage_norms',
|
|
488
|
+
'small_batch_enforcement',
|
|
489
|
+
'multiple_approach_culture',
|
|
490
|
+
'error_handling_policy',
|
|
491
|
+
'security_critical_marking',
|
|
492
|
+
];
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* The scorecard ratchet — one problem per `SCORECARD_FLOOR` check that no longer passes.
|
|
496
|
+
*
|
|
497
|
+
* DEVELOPMENT CHECKOUT ONLY: it scores `.github/` (CI, CODEOWNERS, the PR template), and
|
|
498
|
+
* `.github/` is WITHHELD from the npm package (`tests/unit/package_manifest.test.mjs`), so an npm
|
|
499
|
+
* install would fail half the floor for files it was never meant to carry. A `.git` entry (a
|
|
500
|
+
* directory in a clone, a FILE in a worktree — hence `existsSync`, not `isDir`) plus
|
|
501
|
+
* `.github/workflows` is what only a checkout of this repository has. Anything else skips: [].
|
|
502
|
+
*/
|
|
503
|
+
export function scorecardProblems(root = ROOT) {
|
|
504
|
+
if (!existsSync(path.join(root, '.git')) || !isDir(path.join(root, '.github', 'workflows'))) return [];
|
|
505
|
+
const a = assessRepo(root);
|
|
506
|
+
const checks = new Map(a.categories.flatMap((c) => c.checks).map((k) => [k.id, k]));
|
|
507
|
+
// THE README CARD IS A RENDER OF THIS SAME ASSESSMENT, compared byte for byte: a card that no
|
|
508
|
+
// longer matches the score is the stale-number problem the badges have, caught the same way.
|
|
509
|
+
const card = path.join(root, ...SCORECARD_CARD.split('/'));
|
|
510
|
+
const stale = isFile(card) && readText(card) === renderSvg(a) ? [] : [
|
|
511
|
+
`[scorecard] ${SCORECARD_CARD} does not match the current score — run: geneseed scorecard --svg ${SCORECARD_CARD}`,
|
|
512
|
+
];
|
|
513
|
+
const readme = path.join(root, 'README.md');
|
|
514
|
+
if (isFile(readme) && !readText(readme).includes(`(${badgeUrl(a)})`)) {
|
|
515
|
+
stale.push(`[scorecard] README.md's scorecard badge does not show the current score — set it to ${badgeUrl(a)}`);
|
|
516
|
+
}
|
|
517
|
+
return stale.concat(SCORECARD_FLOOR.filter((id) => !checks.get(id)?.passed).map((id) => {
|
|
518
|
+
const k = checks.get(id);
|
|
519
|
+
return `[scorecard] ${id} no longer passes — it is in SCORECARD_FLOOR (js/inspect/checks-repo.mjs)`
|
|
520
|
+
+ (k ? `: ${k.evidence}. Fix: ${k.remediation}` : ', and the scorecard has no such check');
|
|
521
|
+
}));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Where the README's scorecard card lives, and what doctor compares against a fresh render. */
|
|
525
|
+
export const SCORECARD_CARD = 'docs/assets/scorecard.svg';
|
package/js/inspect/diff.mjs
CHANGED
|
@@ -189,7 +189,10 @@ export function diffCollect({ target = null, theme = null, emit = null, footprin
|
|
|
189
189
|
// reference render names the temp tree. Mapped back to the deployed dir before comparing,
|
|
190
190
|
// or every such skill reads as edited. A deployment that has since MOVED still differs,
|
|
191
191
|
// and that is real drift — its skills point at a directory that is no longer theirs.
|
|
192
|
+
// A side-file pointer (and a global OpenCode command's) spells the same directory
|
|
193
|
+
// `/`-separated, so that spelling is mapped too.
|
|
192
194
|
const deployedDir = resolvePath(dir);
|
|
195
|
+
const posix = (p) => p.split(path.sep).join('/');
|
|
193
196
|
const rels = [...new Set([...ownedSet(dir), ...ownedSet(expected)])].sort();
|
|
194
197
|
for (const rel of rels) {
|
|
195
198
|
const a = path.join(dir, rel);
|
|
@@ -198,7 +201,8 @@ export function diffCollect({ target = null, theme = null, emit = null, footprin
|
|
|
198
201
|
// `read_text(errors="replace")` — Node's utf8 decoder substitutes U+FFFD for the
|
|
199
202
|
// same bytes, and `readText` folds the line endings the way `Path.read_text` does.
|
|
200
203
|
const ta = readText(a);
|
|
201
|
-
const tb = readText(b).split(expected).join(deployedDir)
|
|
204
|
+
const tb = readText(b).split(expected).join(deployedDir)
|
|
205
|
+
.split(posix(expected)).join(posix(deployedDir));
|
|
202
206
|
if (cmpKey(rel, ta) !== cmpKey(rel, tb)) {
|
|
203
207
|
const diff = unifiedDiff(splitLines(tb), splitLines(ta), {
|
|
204
208
|
fromfile: `source/${rel}`, tofile: `deployed/${rel}`, lineterm: '',
|
package/js/inspect/doctor.mjs
CHANGED
|
@@ -40,7 +40,7 @@ import { authoringProblems, loopProblems } from './checks-authoring.mjs';
|
|
|
40
40
|
import {
|
|
41
41
|
checkBuild, colorThemeProblems, renderedProblems, themeParityProblems,
|
|
42
42
|
} from './checks-build.mjs';
|
|
43
|
-
import { moduleMapProblems, shimProblems } from './checks-repo.mjs';
|
|
43
|
+
import { moduleMapProblems, scorecardProblems, shimProblems } from './checks-repo.mjs';
|
|
44
44
|
import { isDoctorNote, sortedProblems, sortedUnique, stemOf, withTempDir } from './scan.mjs';
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -220,6 +220,7 @@ export function doctorCollect({
|
|
|
220
220
|
problems = problems.concat(ran('loops', 'Loop catalogue', loopProblems()));
|
|
221
221
|
problems = problems.concat(ran('shim', 'Hook shim', shimProbs));
|
|
222
222
|
problems = problems.concat(ran('map', 'Module map', moduleMapProblems()));
|
|
223
|
+
problems = problems.concat(ran('scorecard', 'Scorecard floor', scorecardProblems()));
|
|
223
224
|
// P10c's `cli` check is GONE, and the reason is not that it stopped mattering. It hashed
|
|
224
225
|
// `rituals/harness.py` and compared that against a digest baked into `cli.json`, to catch a
|
|
225
226
|
// parser edited without regenerating the table. P2 made the table the OWNED document
|
package/js/inspect/inventory.mjs
CHANGED
|
@@ -92,23 +92,21 @@ export const SKILL_CLASS = {
|
|
|
92
92
|
brainstorm: 'design', plan: 'design', council: 'design',
|
|
93
93
|
workflow: 'design', 'parallel-agents': 'design', pipeline: 'design',
|
|
94
94
|
loop: 'design', 'brick-forge': 'design',
|
|
95
|
-
'codebase-design': 'design',
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
'
|
|
99
|
-
'forge-mcp': 'build', 'bruno-collection-generator': 'build', 'bruno-test-writer': 'build',
|
|
100
|
-
worktree: 'build', 'ci-fix': 'build', 'deps-audit': 'build',
|
|
95
|
+
'codebase-design': 'design', wayfinder: 'design',
|
|
96
|
+
develop: 'build', refactor: 'build', debug: 'build', dependencies: 'build',
|
|
97
|
+
'frontend-design': 'build', prototype: 'build',
|
|
98
|
+
'forge-mcp': 'build', bruno: 'build', worktree: 'build',
|
|
101
99
|
'geneseed-code-review': 'review', 'fresh-eyes': 'review', 'security-audit': 'review',
|
|
102
100
|
'roast-me': 'review', 'review-response': 'review', ponytail: 'review',
|
|
103
|
-
commit: 'ship', ship: 'ship', release: 'ship', handoff: 'ship',
|
|
104
|
-
'
|
|
101
|
+
commit: 'ship', ship: 'ship', release: 'ship', handoff: 'ship',
|
|
102
|
+
'git-history': 'understand',
|
|
105
103
|
research: 'understand', ingest: 'understand', 'document-project': 'understand',
|
|
106
104
|
prose: 'understand',
|
|
107
105
|
// `harness` — skills about the agent's own apparatus (its harness, memory, rules,
|
|
108
106
|
// knowledge base, terminal) rather than about the user's code. They sat under
|
|
109
107
|
// `understand` before, which made that class a grab-bag.
|
|
110
108
|
wiki: 'harness', geneseed: 'harness', rule: 'harness', profile: 'harness',
|
|
111
|
-
herdr: 'harness', 'skill-forge': 'harness',
|
|
109
|
+
herdr: 'harness', 'skill-forge': 'harness',
|
|
112
110
|
teach: 'learn', quiz: 'learn', 'learn-mode': 'learn',
|
|
113
111
|
};
|
|
114
112
|
|