simt-dev 0.7.0 → 0.8.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 +191 -870
- package/README.md +188 -213
- package/bin/simt.mjs +19 -55
- package/docs/decisions/ADR-014-workspace-is-configuration.md +2 -2
- package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +1 -1
- package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +1 -1
- package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +1 -1
- package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +1 -1
- package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +1 -1
- package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +1 -1
- package/docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md +1 -1
- package/docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md +81 -0
- package/docs/decisions/ADR-118-the-bench-corpus-is-keyed-on-history-is-append-only-and-is-portable-as-a-sealed-document.md +91 -0
- package/docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md +90 -0
- package/docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md +96 -0
- package/docs/decisions/ADR-122-the-shims-move-into-the-state-directory-and-the-legacy-directory-removes-itself.md +94 -0
- package/docs/decisions/ADR-123-a-surplus-positional-is-not-refused-because-arity-is-not-in-the-source.md +57 -0
- package/docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md +64 -0
- package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md +65 -0
- package/docs/decisions/ADR-126-the-mcp-surface-stays-a-reader-so-there-is-no-write-path-to-build.md +68 -0
- package/docs/decisions/ADR-127-the-mcp-tool-list-is-written-by-hand-and-roots-is-not-asked-for.md +65 -0
- package/docs/decisions/ADR-128-a-research-plan-is-a-plan-and-the-tracker-reads-it.md +42 -0
- package/ecosystems/bun.json +6 -0
- package/ecosystems/cargo.json +25 -0
- package/ecosystems/composer.json +6 -0
- package/ecosystems/dart.json +24 -0
- package/ecosystems/deno.json +21 -0
- package/ecosystems/devcontainers.json +6 -0
- package/ecosystems/docker-compose.json +6 -0
- package/ecosystems/docker.json +6 -0
- package/ecosystems/dotnet.json +25 -0
- package/ecosystems/elixir.json +23 -0
- package/ecosystems/gitsubmodule.json +6 -0
- package/ecosystems/go.json +25 -0
- package/ecosystems/gradle.json +13 -0
- package/ecosystems/maven.json +23 -0
- package/ecosystems/node.json +25 -0
- package/ecosystems/python.json +20 -0
- package/ecosystems/ruby.json +23 -0
- package/ecosystems/swift.json +25 -0
- package/ecosystems/terraform.json +6 -0
- package/ecosystems/uv.json +6 -0
- package/harnesses/antigravity.json +162 -0
- package/harnesses/claude.json +206 -0
- package/harnesses/codex.json +136 -0
- package/harnesses/copilot.json +88 -0
- package/harnesses/cursor.json +140 -0
- package/harnesses/deepseek.json +99 -0
- package/harnesses/gemini.json +122 -0
- package/harnesses/opencode.json +173 -0
- package/package.json +12 -2
- package/rules/core.md +7 -0
- package/rules/mdc/intake-questions.mdc +8 -0
- package/rules/mdc/slice-commit.mdc +57 -4
- package/src/advice/advice.mjs +3 -2
- package/src/agenthooks/prompt.mjs +5 -2
- package/src/agenthooks/render.mjs +86 -180
- package/src/agenthooks/shim.mjs +1 -1
- package/src/agents/render.mjs +15 -18
- package/src/api.mjs +31 -0
- package/src/argv.mjs +48 -3
- package/src/bench/ablate.mjs +3 -4
- package/src/bench/adapters.json +163 -105
- package/src/bench/adapters.mjs +466 -0
- package/src/bench/arms.mjs +333 -0
- package/src/bench/grade.mjs +33 -73
- package/src/bench/graders.mjs +42 -0
- package/src/bench/mine.mjs +121 -475
- package/src/bench/probe-ledger.mjs +149 -0
- package/src/bench/probes.catalog.mjs +918 -0
- package/src/bench/probes.mjs +79 -1064
- package/src/bench/render.mjs +111 -0
- package/src/bench/report.mjs +120 -962
- package/src/bench/run.mjs +185 -802
- package/src/bench/variants.mjs +4 -5
- package/src/bench/workspace.mjs +160 -0
- package/src/brief/assemble.mjs +48 -15
- package/src/ci/dependabot.mjs +25 -55
- package/src/ci/workflow.mjs +30 -7
- package/src/cli.mjs +45 -0
- package/src/commands/agent.mjs +4 -13
- package/src/commands/agenthook.mjs +70 -40
- package/src/commands/bench.mjs +191 -18
- package/src/commands/brief.mjs +5 -4
- package/src/commands/ci.mjs +13 -14
- package/src/commands/compare.mjs +42 -42
- package/src/commands/completions.mjs +2 -6
- package/src/commands/decide.mjs +4 -4
- package/src/commands/distill.mjs +4 -3
- package/src/commands/docs.mjs +88 -0
- package/src/commands/doctor.mjs +11 -7
- package/src/commands/evidence.mjs +6 -9
- package/src/commands/explain.mjs +0 -1
- package/src/commands/gate.mjs +26 -25
- package/src/commands/graph.mjs +2 -3
- package/src/commands/harness.mjs +6 -5
- package/src/commands/help.mjs +168 -19
- package/src/commands/hooks.mjs +119 -49
- package/src/commands/ignore.mjs +13 -8
- package/src/commands/impact.mjs +8 -15
- package/src/commands/index.mjs +318 -193
- package/src/commands/init-scan.mjs +90 -0
- package/src/commands/init.mjs +124 -105
- package/src/commands/lease.mjs +73 -25
- package/src/commands/lessons.mjs +10 -9
- package/src/commands/local.mjs +3 -3
- package/src/commands/map.mjs +24 -14
- package/src/commands/mcp.mjs +2 -18
- package/src/commands/pack.mjs +9 -6
- package/src/commands/plan.mjs +6 -5
- package/src/commands/plugin.mjs +2 -2
- package/src/commands/prbody.mjs +3 -3
- package/src/commands/repoindex.mjs +12 -11
- package/src/commands/review.mjs +2 -2
- package/src/commands/rules.mjs +37 -16
- package/src/commands/shared.mjs +14 -19
- package/src/commands/skills.mjs +33 -32
- package/src/commands/skillscompile.mjs +7 -8
- package/src/commands/skillsinstall.mjs +7 -6
- package/src/commands/slice.mjs +220 -74
- package/src/commands/spec.mjs +167 -0
- package/src/commands/status.mjs +27 -18
- package/src/commands/sync.mjs +27 -25
- package/src/commands/tests.mjs +18 -21
- package/src/commands/tokens.mjs +21 -26
- package/src/commands/undo.mjs +5 -15
- package/src/commands/upgrade.mjs +15 -7
- package/src/commands/why.mjs +15 -21
- package/src/compare/hooksstate.mjs +2 -6
- package/src/compare/learn.mjs +3 -1
- package/src/compare/row.mjs +0 -2
- package/src/context.mjs +64 -0
- package/src/decisions/index-doc.mjs +46 -0
- package/src/decisions/index.mjs +3 -2
- package/src/diff.mjs +1 -1
- package/src/distill/distill.mjs +14 -1
- package/src/distill/engine.mjs +5 -1
- package/src/docs/commands.mjs +65 -0
- package/src/ecosystem/records.mjs +219 -0
- package/src/explain.mjs +31 -0
- package/src/gates/index.mjs +385 -0
- package/src/graph/compile.mjs +8 -4
- package/src/graph/load.mjs +6 -2
- package/src/graph/spec.mjs +10 -8
- package/src/harness/records.mjs +143 -0
- package/src/hooks/capability.mjs +7 -5
- package/src/hooks/dispatcher.mjs +209 -60
- package/src/hooks/gates.mjs +10 -43
- package/src/hooks/resolve.mjs +4 -14
- package/src/hooks/shim.mjs +5 -2
- package/src/hooks/where.mjs +2 -7
- package/src/identity.mjs +31 -12
- package/src/index/coverage.mjs +58 -0
- package/src/index/extract.mjs +6 -16
- package/src/index/gate.mjs +1 -1
- package/src/index/govern.mjs +26 -9
- package/src/index/impact.mjs +4 -8
- package/src/index/map.mjs +14 -15
- package/src/index/open.mjs +173 -0
- package/src/index/persist.mjs +224 -57
- package/src/index/store.mjs +82 -39
- package/src/index/tests.mjs +2 -6
- package/src/journal.mjs +94 -20
- package/src/kernel/card.mjs +37 -63
- package/src/kernel/checks.mjs +4 -10
- package/src/lease.mjs +102 -7
- package/src/ledger/index.mjs +150 -14
- package/src/local/client.mjs +4 -6
- package/src/locate.mjs +33 -9
- package/src/manifest/schema.mjs +213 -0
- package/src/mcp/paginate.mjs +33 -0
- package/src/mcp/resources.mjs +7 -10
- package/src/mcp/schemas.mjs +512 -0
- package/src/mcp/server.mjs +119 -25
- package/src/mcp/tools-facts.mjs +326 -0
- package/src/mcp/tools-read.mjs +221 -0
- package/src/mcp/tools.mjs +4 -967
- package/src/measure/corpus.mjs +150 -0
- package/src/measure/metrics.mjs +236 -0
- package/src/measure/records.mjs +211 -0
- package/src/measure/session.mjs +3 -2
- package/src/measure/stats.mjs +538 -0
- package/src/measure/task-schema.mjs +125 -0
- package/src/measure/tasks.mjs +69 -0
- package/src/memory/decide.mjs +6 -15
- package/src/memory/lesson-text.mjs +61 -0
- package/src/memory/lessons.mjs +29 -82
- package/src/name.mjs +7 -0
- package/src/node-version.mjs +37 -0
- package/src/output/index.mjs +209 -10
- package/src/packs/manage.mjs +32 -12
- package/src/packs.mjs +20 -2
- package/src/plan/index.mjs +1 -1
- package/src/plugins.mjs +22 -16
- package/src/prbody.mjs +7 -11
- package/src/projection/loaders.mjs +7 -692
- package/src/projection/lock.mjs +11 -1
- package/src/projection/manifest.mjs +29 -468
- package/src/projection/mdc.mjs +7 -8
- package/src/projection/project.mjs +126 -221
- package/src/projection/region.mjs +561 -0
- package/src/projection/rules.mjs +20 -550
- package/src/projection/surfaces.mjs +43 -210
- package/src/projection/targets.mjs +5 -5
- package/src/repo-root.mjs +31 -0
- package/src/resolve/eval.mjs +2 -1
- package/src/resolve/fingerprint.mjs +19 -22
- package/src/resolve/resolve.mjs +1 -41
- package/src/review/critic.mjs +10 -13
- package/src/run.mjs +98 -0
- package/src/secrets.mjs +14 -22
- package/src/state/events.mjs +129 -0
- package/src/status-snapshot.mjs +233 -0
- package/src/status.mjs +2 -6
- package/src/sys/git.mjs +82 -0
- package/src/sys/proc.mjs +151 -0
- package/src/testing/parsers.mjs +265 -0
- package/src/testing/patches.mjs +23 -0
- package/src/testing/paths.mjs +49 -0
- package/src/testing/runners.mjs +186 -0
- package/src/testing/vault.mjs +68 -0
- package/src/text/frontmatter.mjs +49 -0
- package/src/text/tokens.mjs +52 -0
- package/src/toolwords.mjs +3 -0
- package/src/util/fs.mjs +20 -0
- package/src/util/sort.mjs +12 -0
- package/src/workspace.mjs +58 -11
- package/src/write/plan.mjs +82 -0
- package/templates/plan/tracker.json +1 -0
- package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +0 -82
- package/docs/decisions/ADR-102-the-two-queued-review-rows-are-closed-by-ruling.md +0 -49
- package/docs/decisions/README.md +0 -600
- package/src/bench/tasks.mjs +0 -36
package/src/graph/compile.mjs
CHANGED
|
@@ -15,10 +15,14 @@ import { createHash } from 'node:crypto';
|
|
|
15
15
|
import { checkSkill, nameProblems, readFrontmatter } from './spec.mjs';
|
|
16
16
|
import { matchesGlob } from '../resolve/resolve.mjs';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import {
|
|
19
|
+
BODY_BYTE_BUDGET,
|
|
20
|
+
BODY_LINE_BUDGET,
|
|
21
|
+
BODY_TOKEN_BUDGET,
|
|
22
|
+
BYTES_PER_TOKEN,
|
|
23
|
+
} from './spec.mjs';
|
|
24
|
+
|
|
25
|
+
export { BODY_BYTE_BUDGET, BODY_LINE_BUDGET, BODY_TOKEN_BUDGET, BYTES_PER_TOKEN };
|
|
22
26
|
|
|
23
27
|
const TARGET_MAIN_BYTES = 12_000;
|
|
24
28
|
const TARGET_MAIN_LINES = 350;
|
package/src/graph/load.mjs
CHANGED
|
@@ -18,7 +18,8 @@ import { existsSync } from 'node:fs';
|
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
|
|
20
20
|
import { KNOWN_TAGS } from '../resolve/fingerprint.mjs';
|
|
21
|
-
import { matchesGlob, syntheticPathFor
|
|
21
|
+
import { matchesGlob, syntheticPathFor } from '../resolve/resolve.mjs';
|
|
22
|
+
import { tokenize } from '../text/tokens.mjs';
|
|
22
23
|
import { PACKAGE_ROOT, resolveWorkspace, WORKSPACE_REMEDY } from '../workspace.mjs';
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -117,7 +118,9 @@ async function defaultRegistry(flags) {
|
|
|
117
118
|
* failure to resolve is a WARNING, not 38 `ENOENT` lines (ADR-014).
|
|
118
119
|
*
|
|
119
120
|
* Memoised per the inputs that decide the answer: the `--workspace` flag, the
|
|
120
|
-
* `SIMT_DEV_WORKSPACE` environment
|
|
121
|
+
* `SIMT_DEV_WORKSPACE` environment, the user-config location, and the current
|
|
122
|
+
* directory, whose manifest declares the workspace the variable may point into
|
|
123
|
+
* (ADR-119). A single
|
|
121
124
|
* first-call-wins promise served one caller's workspace to every later caller with
|
|
122
125
|
* different flags (Q14) — `skills_index({workspace: ws})` followed by
|
|
123
126
|
* `skills_index({})` answered `ws` twice. Callers with the same inputs still share
|
|
@@ -129,6 +132,7 @@ export function workspace(flags = {}) {
|
|
|
129
132
|
flags?.workspace ?? null,
|
|
130
133
|
process.env.SIMT_DEV_WORKSPACE ?? null,
|
|
131
134
|
process.env.XDG_CONFIG_HOME ?? null,
|
|
135
|
+
process.cwd(),
|
|
132
136
|
]);
|
|
133
137
|
if (!workspacePromises.has(key)) workspacePromises.set(key, resolveWorkspace(flags));
|
|
134
138
|
return workspacePromises.get(key);
|
package/src/graph/spec.mjs
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
// error the reader cannot act on is a papercut that trains people to skip the gate, so
|
|
10
10
|
// every message names the rule it broke and the fix.
|
|
11
11
|
|
|
12
|
+
import { parseFrontmatter } from '../text/frontmatter.mjs';
|
|
13
|
+
|
|
12
14
|
/** The one specification this knows, and the name `--spec` accepts. */
|
|
13
15
|
export const SPEC = 'agentskills';
|
|
14
16
|
|
|
@@ -19,9 +21,10 @@ const SPEC_FIELDS = ['name', 'description', 'license', 'compatibility', 'metadat
|
|
|
19
21
|
|
|
20
22
|
/** Tokens, not bytes, because the specification counts tokens and every number here is
|
|
21
23
|
* the same four-bytes-per-token estimate the context meter uses. */
|
|
22
|
-
const BODY_TOKEN_BUDGET = 5000;
|
|
23
|
-
const BODY_LINE_BUDGET = 500;
|
|
24
|
-
const BYTES_PER_TOKEN = 4;
|
|
24
|
+
export const BODY_TOKEN_BUDGET = 5000;
|
|
25
|
+
export const BODY_LINE_BUDGET = 500;
|
|
26
|
+
export const BYTES_PER_TOKEN = 4;
|
|
27
|
+
export const BODY_BYTE_BUDGET = BODY_TOKEN_BUDGET * BYTES_PER_TOKEN;
|
|
25
28
|
|
|
26
29
|
/** The `name` rules, as one predicate with the rule it enforces. */
|
|
27
30
|
const NAME_RULES = {
|
|
@@ -167,10 +170,9 @@ export function checkSkill({ directoryName, fileName = 'SKILL.md', text, at }) {
|
|
|
167
170
|
export function readFrontmatter(text) {
|
|
168
171
|
const meta = {};
|
|
169
172
|
const present = [];
|
|
170
|
-
const body = text.replace(/^/, '');
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const lines = match[1].split(/\r?\n/);
|
|
173
|
+
const { front, body } = parseFrontmatter(text.replace(/^/, ''));
|
|
174
|
+
if (front === null) return { meta, present, body };
|
|
175
|
+
const lines = front.split(/\r?\n/);
|
|
174
176
|
|
|
175
177
|
for (let i = 0; i < lines.length; i += 1) {
|
|
176
178
|
const line = lines[i];
|
|
@@ -215,7 +217,7 @@ export function readFrontmatter(text) {
|
|
|
215
217
|
}
|
|
216
218
|
meta[key] = scalar(raw);
|
|
217
219
|
}
|
|
218
|
-
return { meta, present, body
|
|
220
|
+
return { meta, present, body };
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
/** Strip the quoting and the trailing comment a scalar may carry. */
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Harness data records.
|
|
2
|
+
//
|
|
3
|
+
// ADR-013: content is data, the engine is code. A harness is a record under
|
|
4
|
+
// `harnesses/`, not a row in a table the engine edits. A new harness arrives as a new
|
|
5
|
+
// file; the engine — this loader and the views over it — never changes shape to make a
|
|
6
|
+
// product work. The surface row, the reader row, the hook dialect and the plugin layout
|
|
7
|
+
// of one harness land together in one file and cannot drift apart, which is the failure
|
|
8
|
+
// the five-module spread they used to occupy made routine (review 0003 Q71).
|
|
9
|
+
//
|
|
10
|
+
// Records are sparse: a facet is present only when the harness has one. DeepSeek has a
|
|
11
|
+
// reader row and nothing else; Copilot has a surface row, a reader row and a scoping key;
|
|
12
|
+
// Claude has all seven. Absence is a fact — a facet that is not there is one the vendor
|
|
13
|
+
// documents none of — so the views filter, they do not fill.
|
|
14
|
+
//
|
|
15
|
+
// The shipped records live in `harnesses/` at the package root. A repository may override
|
|
16
|
+
// them under `.simt/harnesses/` the way it overrides `ecosystems/`: a same-id record is a
|
|
17
|
+
// shallow merge over the shipped one (arrays replaced whole), a new id is appended, and a
|
|
18
|
+
// record that does not parse is skipped, never a crash.
|
|
19
|
+
//
|
|
20
|
+
// The package root is computed here rather than imported from workspace.mjs for the same
|
|
21
|
+
// reason `ecosystem/records.mjs` computes it: workspace.mjs is part of the import graph
|
|
22
|
+
// that builds these tables at evaluation time, and an import cycle would leave the binding
|
|
23
|
+
// in the temporal dead zone. This file is two levels below the root (src/harness/), so
|
|
24
|
+
// two levels up is the root — a fixed path, not a heuristic.
|
|
25
|
+
|
|
26
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
27
|
+
import { dirname, join, resolve } from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
|
|
30
|
+
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
31
|
+
const SHIPPED_DIR = join(PACKAGE_ROOT, 'harnesses');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The records in force for a repository: the shipped set, with a repo-local
|
|
35
|
+
* `.simt/harnesses/` merged over it. `null` is the shipped set alone, which is what the
|
|
36
|
+
* const-iterating tests and every call without a root read.
|
|
37
|
+
*/
|
|
38
|
+
export function loadHarnesses(root = null) {
|
|
39
|
+
const base = shipped();
|
|
40
|
+
if (!root) return base;
|
|
41
|
+
const localDir = join(root, '.simt', 'harnesses');
|
|
42
|
+
if (!existsSync(localDir)) return base;
|
|
43
|
+
const byId = new Map(base.map((r) => [r.id, r]));
|
|
44
|
+
for (const file of readdirSync(localDir)) {
|
|
45
|
+
if (!file.endsWith('.json')) continue;
|
|
46
|
+
const id = file.slice(0, -5);
|
|
47
|
+
const rec = parse(join(localDir, file));
|
|
48
|
+
if (!rec) continue;
|
|
49
|
+
// Same id: a shallow merge over the shipped record, so a local override that omits
|
|
50
|
+
// a field keeps the shipped one. New id: the record as-is.
|
|
51
|
+
byId.set(id, byId.has(id) ? { ...byId.get(id), ...rec } : rec);
|
|
52
|
+
}
|
|
53
|
+
return [...byId.values()];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The shipped records, read once per process and cached. */
|
|
57
|
+
function shipped() {
|
|
58
|
+
if (shippedCache) return shippedCache;
|
|
59
|
+
const map = new Map();
|
|
60
|
+
if (existsSync(SHIPPED_DIR)) readRecords(SHIPPED_DIR, map);
|
|
61
|
+
shippedCache = [...map.values()];
|
|
62
|
+
return shippedCache;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let shippedCache = null;
|
|
66
|
+
|
|
67
|
+
/** Read one directory of records into `into`, keyed by the file stem. */
|
|
68
|
+
function readRecords(dir, into) {
|
|
69
|
+
for (const file of readdirSync(dir)) {
|
|
70
|
+
if (!file.endsWith('.json')) continue;
|
|
71
|
+
const id = file.slice(0, -5);
|
|
72
|
+
const rec = parse(join(dir, file));
|
|
73
|
+
// The id must match the file stem: a mismatch is a record pointing at the wrong
|
|
74
|
+
// harness, and shipping it would make two names mean one record.
|
|
75
|
+
if (rec && rec.id === id) into.set(id, rec);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One record, or null when the file is absent, unreadable or does not parse. */
|
|
80
|
+
function parse(path) {
|
|
81
|
+
try {
|
|
82
|
+
const rec = JSON.parse(readFileSync(path, 'utf8'));
|
|
83
|
+
return rec && typeof rec === 'object' ? rec : null;
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// The views. Each returns the rows the old module constant held, in the order
|
|
91
|
+
// the const-iterating tests pin: the records' `*Order` fields, not readdir's.
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
/** The surface rows, in the order `project` iterates them (ADR-020's union order). */
|
|
95
|
+
export function harnessSurfaces(root = null) {
|
|
96
|
+
return loadHarnesses(root)
|
|
97
|
+
.filter((r) => r.surface)
|
|
98
|
+
.sort((a, b) => a.surfaceOrder - b.surfaceOrder)
|
|
99
|
+
.map((r) => r.surface);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The pointer rows, in their table order. */
|
|
103
|
+
export function harnessPointers(root = null) {
|
|
104
|
+
return loadHarnesses(root)
|
|
105
|
+
.filter((r) => r.pointer)
|
|
106
|
+
.sort((a, b) => a.pointerOrder - b.pointerOrder)
|
|
107
|
+
.map((r) => r.pointer);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The reader rows, in the order the reader table lists its harnesses. */
|
|
111
|
+
export function harnessLoaders(root = null) {
|
|
112
|
+
return loadHarnesses(root)
|
|
113
|
+
.filter((r) => r.loader)
|
|
114
|
+
.sort((a, b) => a.loaderOrder - b.loaderOrder)
|
|
115
|
+
.map((r) => r.loader);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Surface ids whose scoping key was read in the vendor's own documentation. */
|
|
119
|
+
export function harnessScopeKeys(root = null) {
|
|
120
|
+
return new Set(loadHarnesses(root).filter((r) => r.scopeKey === true).map((r) => r.id));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The hook dialect per harness, keyed by id: the data a config document and a decision are shaped by. */
|
|
124
|
+
export function harnessHooks(root = null) {
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const r of loadHarnesses(root)) if (r.hooks) out[r.id] = r.hooks;
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The payload extraction contract per harness, keyed by id: how a hook payload names its tool, its input and its writes. */
|
|
131
|
+
export function harnessPayloads(root = null) {
|
|
132
|
+
const out = {};
|
|
133
|
+
for (const r of loadHarnesses(root)) if (r.payload) out[r.id] = r.payload;
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The plugin layouts, in the order the `simt plugin` help line names its kinds. */
|
|
138
|
+
export function harnessPlugins(root = null) {
|
|
139
|
+
return loadHarnesses(root)
|
|
140
|
+
.filter((r) => r.plugin)
|
|
141
|
+
.sort((a, b) => a.pluginOrder - b.pluginOrder)
|
|
142
|
+
.map((r) => ({ id: r.id, ...r.plugin }));
|
|
143
|
+
}
|
package/src/hooks/capability.mjs
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
import { constants as fsConstants } from 'node:fs';
|
|
22
22
|
import { access, readdir, readFile, stat } from 'node:fs/promises';
|
|
23
23
|
import { join } from 'node:path';
|
|
24
|
-
import {
|
|
24
|
+
import { ecosystemGateRows } from '../ecosystem/records.mjs';
|
|
25
|
+
import { loadManifest } from '../manifest/schema.mjs';
|
|
25
26
|
import { resolveNodeRunner } from '../ecosystem/node-runner.mjs';
|
|
26
27
|
|
|
27
28
|
/** Binaries the shared dispatcher shells out to directly, rather than via a script. */
|
|
@@ -81,7 +82,7 @@ async function executableFile(path) {
|
|
|
81
82
|
/**
|
|
82
83
|
* Can this machine run the ecosystem's program?
|
|
83
84
|
*
|
|
84
|
-
* Two kinds of evidence, matching the two kinds of tool in
|
|
85
|
+
* Two kinds of evidence, matching the two kinds of tool in the ecosystem records: a binary on
|
|
85
86
|
* PATH, probed directory by directory the way the shell would find it, and a file the
|
|
86
87
|
* repository commits (the Maven wrapper), probed in place the way `gradlew` is probed.
|
|
87
88
|
* Either answer that is not a positive YES is NO: a seed that guesses a toolchain is
|
|
@@ -157,7 +158,9 @@ export async function readCapability(root) {
|
|
|
157
158
|
// file in the repo cannot: a Cargo.toml without cargo installed is an ecosystem that
|
|
158
159
|
// is here but whose gates would all fail closed. The probe is per-row, because a row
|
|
159
160
|
// that marks its tool `file` lives in the repository, not on PATH.
|
|
160
|
-
|
|
161
|
+
// Q70: the rows come from the records, and `root` lets a repository's own
|
|
162
|
+
// `.simt/ecosystems/` add a tool the shipped set does not probe.
|
|
163
|
+
for (const eco of ecosystemGateRows(root)) {
|
|
161
164
|
if (await probeTool(root, eco)) capability.tools.push(eco.tool);
|
|
162
165
|
}
|
|
163
166
|
|
|
@@ -170,8 +173,7 @@ export async function readCapability(root) {
|
|
|
170
173
|
// so the only evidence that can prove it is the declaration itself. A repo with no
|
|
171
174
|
// manifest simply has no declared commands.
|
|
172
175
|
try {
|
|
173
|
-
|
|
174
|
-
capability.commands = declaredCommands(manifest);
|
|
176
|
+
capability.commands = declaredCommands((await loadManifest(root)).manifest);
|
|
175
177
|
} catch {
|
|
176
178
|
capability.commands = {};
|
|
177
179
|
}
|