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/mcp/tools.mjs
CHANGED
|
@@ -19,976 +19,13 @@
|
|
|
19
19
|
// failure progressive disclosure exists to prevent, and a transport makes it easier to
|
|
20
20
|
// cause by accident because the caller cannot see the size until it has paid for it.
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { discover } from '../compare/discover.mjs';
|
|
25
|
-
import { buildRows, resolveConcurrency, summarise } from '../compare/row.mjs';
|
|
26
|
-
import { GATES } from '../hooks/gates.mjs';
|
|
27
|
-
import { checkRepo } from '../commands/sync.mjs';
|
|
28
|
-
import { diagnose, serialisedFindings } from '../commands/doctor.mjs';
|
|
29
|
-
import { rulesCatalogue, rulesWhyFacts } from '../commands/rules.mjs';
|
|
30
|
-
import { skillsBriefFacts, skillsFindFacts, skillsShowFacts, skillsWhyFacts } from '../commands/skills.mjs';
|
|
31
|
-
import { assembleBrief } from '../brief/assemble.mjs';
|
|
32
|
-
import { DEFAULT_BRIEF_BYTES } from '../brief/packet.mjs';
|
|
33
|
-
import { federationAccess, readFederation } from '../federation/config.mjs';
|
|
34
|
-
import { consult } from '../federation/query.mjs';
|
|
35
|
-
import { planNext, planDone } from '../plan/index.mjs';
|
|
36
|
-
import { buildPrBody } from '../prbody.mjs';
|
|
37
|
-
import { configPaths, readUserConfig, resolveWorkspace } from '../workspace.mjs';
|
|
38
|
-
import { measureSkill } from '../graph/load.mjs';
|
|
39
|
-
import { CLI } from '../name.mjs';
|
|
22
|
+
import { TOOLS_READ } from './tools-read.mjs';
|
|
23
|
+
import { TOOLS_FACTS } from './tools-facts.mjs';
|
|
40
24
|
|
|
41
|
-
const
|
|
25
|
+
export const TOOLS = [...TOOLS_READ, ...TOOLS_FACTS];
|
|
42
26
|
|
|
43
|
-
|
|
44
|
-
const noArgs = { type: 'object', properties: {}, required: [] };
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Output schemas, one per tool, describing what a SUCCESSFUL call returns.
|
|
48
|
-
*
|
|
49
|
-
* Every one of these tools is a reader: none writes, and none writes twice. The
|
|
50
|
-
* annotations say so, and the schemas say what comes back — because a client that can
|
|
51
|
-
* validate an answer against a declared shape catches a wrong answer at the boundary,
|
|
52
|
-
* while one handed a bag of JSON has to trust that the tool meant it.
|
|
53
|
-
*
|
|
54
|
-
* Properties are required unless the payload can legitimately omit them: `error` is
|
|
55
|
-
* absent when there is none, `body` is null when there is none, and a schema that
|
|
56
|
-
* required the exception would describe a shape that never occurs. `additionalProperties`
|
|
57
|
-
* is left open, which is what the 2026-07-28 revision permits and what a reader wants:
|
|
58
|
-
* these payloads are data, and a new field from a future release should not make an
|
|
59
|
-
* older client's validation fail.
|
|
60
|
-
*/
|
|
61
|
-
/**
|
|
62
|
-
* The shape of a failure, exported because a client validating an answer needs it and
|
|
63
|
-
* because the test that checks every tool's answer needs it for the failures. Two
|
|
64
|
-
* copies of this schema would drift, and the copy that drifted would be the one nobody
|
|
65
|
-
* validates against - which is exactly how a remedy quietly stops being a string.
|
|
66
|
-
*/
|
|
67
|
-
export const ERROR_SHAPE = {
|
|
68
|
-
type: 'object',
|
|
69
|
-
properties: { error: { type: 'string' }, remedy: { type: ['string', 'null'] } },
|
|
70
|
-
required: ['error'],
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const output = {
|
|
74
|
-
doctor: {
|
|
75
|
-
// The `doctor --json` document minus its envelope: the same findings, the
|
|
76
|
-
// same workspace answer, the same fingerprint. (Q15)
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
root: { type: 'string' },
|
|
80
|
-
stack: { type: 'array', items: { type: 'string' } },
|
|
81
|
-
facts: { type: 'array', items: { type: 'string' } },
|
|
82
|
-
factEvidence: { type: 'object', additionalProperties: { type: ['string', 'null'] } },
|
|
83
|
-
signals: { type: 'array', items: { type: 'string' } },
|
|
84
|
-
// One entry per signal, naming the file that proved it. The payload is a list of
|
|
85
|
-
// paths, not of records - `fingerprint` returns evidence as the paths themselves.
|
|
86
|
-
evidence: { type: 'array', items: { type: 'string' } },
|
|
87
|
-
manifest: { type: ['object', 'null'] },
|
|
88
|
-
error: { type: ['string', 'null'] },
|
|
89
|
-
workspace: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
properties: {
|
|
92
|
-
// Null is the honest value for a repository that is not inside a workspace:
|
|
93
|
-
// there is no root to name, and naming the repository as one would be a claim
|
|
94
|
-
// about the reader's directory layout that nothing established.
|
|
95
|
-
root: { type: ['string', 'null'] },
|
|
96
|
-
source: { type: ['string', 'null'] },
|
|
97
|
-
detail: { type: ['string', 'null'] },
|
|
98
|
-
error: { type: ['string', 'null'] },
|
|
99
|
-
},
|
|
100
|
-
required: ['root', 'source'],
|
|
101
|
-
},
|
|
102
|
-
shared: { type: ['object', 'null'] },
|
|
103
|
-
findings: {
|
|
104
|
-
type: 'array',
|
|
105
|
-
items: {
|
|
106
|
-
type: 'object',
|
|
107
|
-
properties: {
|
|
108
|
-
id: { type: 'string' },
|
|
109
|
-
severity: { type: 'string' },
|
|
110
|
-
detail: { type: 'string' },
|
|
111
|
-
remedy: { type: ['string', 'null'] },
|
|
112
|
-
why: { type: ['string', 'null'] },
|
|
113
|
-
safe: { type: 'boolean' },
|
|
114
|
-
fixed: { type: 'boolean' },
|
|
115
|
-
},
|
|
116
|
-
required: ['id', 'severity', 'detail', 'safe', 'fixed'],
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
fix: {
|
|
120
|
-
type: 'object',
|
|
121
|
-
properties: {
|
|
122
|
-
requested: { type: 'boolean' },
|
|
123
|
-
applied: { type: 'boolean' },
|
|
124
|
-
fixed: { type: 'array', items: { type: 'string' } },
|
|
125
|
-
failed: { type: 'array', items: { type: 'object' } },
|
|
126
|
-
},
|
|
127
|
-
required: ['requested', 'applied', 'fixed', 'failed'],
|
|
128
|
-
},
|
|
129
|
-
remedy: { type: ['string', 'null'] },
|
|
130
|
-
},
|
|
131
|
-
required: ['root', 'workspace', 'stack', 'facts', 'signals', 'evidence', 'findings'],
|
|
132
|
-
},
|
|
133
|
-
skills_index: {
|
|
134
|
-
type: 'object',
|
|
135
|
-
properties: {
|
|
136
|
-
skills: { type: 'number' },
|
|
137
|
-
totalBytes: { type: 'number' },
|
|
138
|
-
deferred: { type: 'array', items: { type: 'string' } },
|
|
139
|
-
workspace: { type: 'object' },
|
|
140
|
-
verified: { type: 'boolean' },
|
|
141
|
-
entries: {
|
|
142
|
-
type: 'array',
|
|
143
|
-
items: {
|
|
144
|
-
type: 'object',
|
|
145
|
-
properties: {
|
|
146
|
-
name: { type: 'string' },
|
|
147
|
-
summary: { type: 'string' },
|
|
148
|
-
bytes: { type: 'number' },
|
|
149
|
-
stack: { type: 'array', items: { type: 'string' } },
|
|
150
|
-
deferred: { type: 'boolean' },
|
|
151
|
-
},
|
|
152
|
-
required: ['name', 'bytes'],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
required: ['skills', 'totalBytes', 'entries'],
|
|
157
|
-
},
|
|
158
|
-
// The `skills brief --json` document minus its envelope, or the `--all` one.
|
|
159
|
-
// The CLI keeps its field names; this adopts them. (Q15)
|
|
160
|
-
skills_brief: {
|
|
161
|
-
anyOf: [
|
|
162
|
-
{
|
|
163
|
-
type: 'object',
|
|
164
|
-
properties: {
|
|
165
|
-
repo: {
|
|
166
|
-
type: 'object',
|
|
167
|
-
properties: {
|
|
168
|
-
root: { type: 'string' },
|
|
169
|
-
stack: { type: 'array', items: { type: 'string' } },
|
|
170
|
-
facts: { type: 'array', items: { type: 'string' } },
|
|
171
|
-
},
|
|
172
|
-
required: ['root', 'stack', 'facts'],
|
|
173
|
-
},
|
|
174
|
-
workspace: { type: 'string' },
|
|
175
|
-
task: { type: ['string', 'null'] },
|
|
176
|
-
budget: { type: 'number' },
|
|
177
|
-
inlineBytes: { type: 'number' },
|
|
178
|
-
resolved: {
|
|
179
|
-
type: 'array',
|
|
180
|
-
items: {
|
|
181
|
-
type: 'object',
|
|
182
|
-
properties: {
|
|
183
|
-
name: { type: 'string' },
|
|
184
|
-
bytes: { type: 'number' },
|
|
185
|
-
summary: { type: 'string' },
|
|
186
|
-
why: { type: 'array', items: { type: 'string' } },
|
|
187
|
-
},
|
|
188
|
-
required: ['name', 'bytes', 'why'],
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
deferred: {
|
|
192
|
-
type: 'array',
|
|
193
|
-
items: {
|
|
194
|
-
type: 'object',
|
|
195
|
-
properties: { name: { type: 'string' }, bytes: { type: 'number' }, reason: { type: 'string' } },
|
|
196
|
-
required: ['name', 'bytes', 'reason'],
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
excluded: {
|
|
200
|
-
type: 'array',
|
|
201
|
-
items: {
|
|
202
|
-
type: 'object',
|
|
203
|
-
properties: { name: { type: 'string' }, reason: { type: 'string' } },
|
|
204
|
-
required: ['name', 'reason'],
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
warning: { type: 'string' },
|
|
208
|
-
},
|
|
209
|
-
required: ['repo', 'budget', 'inlineBytes', 'resolved', 'deferred', 'excluded'],
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
type: 'object',
|
|
213
|
-
properties: {
|
|
214
|
-
all: { type: 'boolean', enum: [true] },
|
|
215
|
-
repoStack: { type: 'array', items: { type: 'string' } },
|
|
216
|
-
inline: { type: 'array', items: { type: 'string' } },
|
|
217
|
-
deferred: { type: 'array', items: { type: 'string' } },
|
|
218
|
-
warning: { type: 'string' },
|
|
219
|
-
},
|
|
220
|
-
required: ['all', 'repoStack', 'inline', 'deferred'],
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
// The `skills show --json` document minus its envelope, plus the body text the
|
|
225
|
-
// command prints below the metadata. (Q15)
|
|
226
|
-
skills_show: {
|
|
227
|
-
type: 'object',
|
|
228
|
-
properties: {
|
|
229
|
-
name: { type: 'string' },
|
|
230
|
-
summary: { type: 'string' },
|
|
231
|
-
bytes: { type: 'number' },
|
|
232
|
-
capabilities: { type: 'array', items: { type: 'string' } },
|
|
233
|
-
stack: { type: 'array', items: { type: 'string' } },
|
|
234
|
-
keywords: { type: 'array', items: { type: 'string' } },
|
|
235
|
-
deferred: { type: 'boolean' },
|
|
236
|
-
requires: { type: 'array', items: { type: 'string' } },
|
|
237
|
-
variants: {
|
|
238
|
-
type: 'array',
|
|
239
|
-
items: {
|
|
240
|
-
type: 'object',
|
|
241
|
-
properties: { id: { type: 'string' }, stack: { type: 'array', items: { type: 'string' } }, bytes: { type: 'number' } },
|
|
242
|
-
required: ['id'],
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
sources: { type: 'array', items: { type: 'object' } },
|
|
246
|
-
aliasOf: { type: ['string', 'null'] },
|
|
247
|
-
variantOf: { type: ['string', 'null'] },
|
|
248
|
-
workspace: { type: 'string' },
|
|
249
|
-
body: { type: ['string', 'null'] },
|
|
250
|
-
bodyFrom: { type: 'string' },
|
|
251
|
-
bodyText: { type: 'string' },
|
|
252
|
-
},
|
|
253
|
-
required: ['name', 'bytes', 'bodyFrom', 'requires', 'variants'],
|
|
254
|
-
},
|
|
255
|
-
skills_find: {
|
|
256
|
-
type: 'object',
|
|
257
|
-
properties: {
|
|
258
|
-
query: { type: 'string' },
|
|
259
|
-
results: {
|
|
260
|
-
type: 'array',
|
|
261
|
-
items: {
|
|
262
|
-
type: 'object',
|
|
263
|
-
properties: {
|
|
264
|
-
name: { type: 'string' },
|
|
265
|
-
score: { type: 'number' },
|
|
266
|
-
summary: { type: 'string' },
|
|
267
|
-
reasons: { type: 'array', items: { type: 'string' } },
|
|
268
|
-
},
|
|
269
|
-
required: ['name', 'score', 'reasons'],
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
required: ['query', 'results'],
|
|
274
|
-
},
|
|
275
|
-
// The `skills why --json` document minus its envelope. (Q15)
|
|
276
|
-
skills_why: {
|
|
277
|
-
type: 'object',
|
|
278
|
-
properties: {
|
|
279
|
-
skill: { type: 'string' },
|
|
280
|
-
score: { type: 'number' },
|
|
281
|
-
compatible: { type: 'boolean' },
|
|
282
|
-
gate: { type: 'string' },
|
|
283
|
-
repo: {
|
|
284
|
-
type: 'object',
|
|
285
|
-
properties: {
|
|
286
|
-
root: { type: 'string' },
|
|
287
|
-
stack: { type: 'array', items: { type: 'string' } },
|
|
288
|
-
facts: { type: 'array', items: { type: 'string' } },
|
|
289
|
-
evidence: { type: 'array', items: { type: 'string' } },
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
workspace: { type: 'object' },
|
|
293
|
-
paths: { type: 'array', items: { type: 'string' } },
|
|
294
|
-
taskTokens: { type: 'array', items: { type: 'string' } },
|
|
295
|
-
signals: { type: 'array', items: { type: 'string' } },
|
|
296
|
-
variants: { type: 'array', items: { type: 'object' } },
|
|
297
|
-
aliasOf: { type: ['string', 'null'] },
|
|
298
|
-
variantOf: { type: ['string', 'null'] },
|
|
299
|
-
requires: { type: 'array', items: { type: 'string' } },
|
|
300
|
-
},
|
|
301
|
-
required: ['skill', 'score', 'compatible', 'signals'],
|
|
302
|
-
},
|
|
303
|
-
rules_list: {
|
|
304
|
-
type: 'object',
|
|
305
|
-
properties: {
|
|
306
|
-
sha: { type: 'string' },
|
|
307
|
-
notes: { type: 'array', items: { type: 'string' } },
|
|
308
|
-
rules: {
|
|
309
|
-
type: 'array',
|
|
310
|
-
items: {
|
|
311
|
-
type: 'object',
|
|
312
|
-
properties: {
|
|
313
|
-
id: { type: ['string', 'null'] },
|
|
314
|
-
title: { type: ['string', 'null'] },
|
|
315
|
-
// A profile may name one signal or several, and the frontmatter keeps both
|
|
316
|
-
// spellings. Describing only the list would make a conforming rule look
|
|
317
|
-
// broken.
|
|
318
|
-
applies: { anyOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }] },
|
|
319
|
-
origin: { type: 'string' },
|
|
320
|
-
source: { type: 'string' },
|
|
321
|
-
bytes: { type: 'number' },
|
|
322
|
-
adrs: { type: 'array', items: { type: 'string' } },
|
|
323
|
-
error: { type: ['string', 'null'] },
|
|
324
|
-
fallback: { type: 'boolean' },
|
|
325
|
-
selected: { type: ['boolean', 'null'] },
|
|
326
|
-
},
|
|
327
|
-
required: ['source', 'bytes', 'selected'],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
required: ['sha', 'rules'],
|
|
332
|
-
},
|
|
333
|
-
// The `rules why --json` document minus its envelope, either direction. (Q15)
|
|
334
|
-
rules_why: {
|
|
335
|
-
type: 'object',
|
|
336
|
-
properties: {
|
|
337
|
-
direction: { type: 'string', enum: ['rule', 'decision'] },
|
|
338
|
-
id: { type: 'string' },
|
|
339
|
-
rule: { type: 'string' },
|
|
340
|
-
title: { type: ['string', 'null'] },
|
|
341
|
-
source: { type: 'string' },
|
|
342
|
-
origin: { type: 'string' },
|
|
343
|
-
adrs: { type: 'array', items: { type: 'object' } },
|
|
344
|
-
uncited: { type: 'boolean' },
|
|
345
|
-
others: { type: 'array', items: { type: 'string' } },
|
|
346
|
-
adr: { type: 'object' },
|
|
347
|
-
supersededBy: { type: ['object', 'null'] },
|
|
348
|
-
inForce: { type: 'object' },
|
|
349
|
-
rules: { type: 'array', items: { type: 'string' } },
|
|
350
|
-
},
|
|
351
|
-
required: ['direction', 'id'],
|
|
352
|
-
},
|
|
353
|
-
check: {
|
|
354
|
-
type: 'object',
|
|
355
|
-
properties: {
|
|
356
|
-
root: { type: 'string' },
|
|
357
|
-
ok: { type: 'boolean' },
|
|
358
|
-
findings: {
|
|
359
|
-
type: 'array',
|
|
360
|
-
items: {
|
|
361
|
-
type: 'object',
|
|
362
|
-
properties: {
|
|
363
|
-
file: { type: 'string' },
|
|
364
|
-
status: { type: 'string' },
|
|
365
|
-
detail: { type: ['string', 'null'] },
|
|
366
|
-
remedy: { type: ['string', 'null'] },
|
|
367
|
-
},
|
|
368
|
-
required: ['file', 'status'],
|
|
369
|
-
},
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
required: ['root', 'ok', 'findings'],
|
|
373
|
-
},
|
|
374
|
-
compare: {
|
|
375
|
-
type: 'object',
|
|
376
|
-
properties: {
|
|
377
|
-
root: { type: 'string' },
|
|
378
|
-
shape: { type: 'string', enum: ['repo', 'workspace'] },
|
|
379
|
-
expectedSha: { type: 'string' },
|
|
380
|
-
summary: { type: 'object' },
|
|
381
|
-
repos: { type: 'array', items: { type: 'object' } },
|
|
382
|
-
worktrees: { type: 'array', items: { type: 'object' } },
|
|
383
|
-
directories: { type: 'array', items: { type: 'string' } },
|
|
384
|
-
partial: { type: 'boolean' },
|
|
385
|
-
cursor: { type: ['string', 'null'] },
|
|
386
|
-
total: { type: 'number' },
|
|
387
|
-
},
|
|
388
|
-
required: ['root', 'shape', 'expectedSha', 'summary', 'repos'],
|
|
389
|
-
},
|
|
390
|
-
plan_next: {
|
|
391
|
-
type: 'object',
|
|
392
|
-
properties: {
|
|
393
|
-
root: { type: 'string' },
|
|
394
|
-
next: { type: ['object', 'null'] },
|
|
395
|
-
reason: { type: ['string', 'null'] },
|
|
396
|
-
},
|
|
397
|
-
required: ['root', 'next', 'reason'],
|
|
398
|
-
},
|
|
399
|
-
plan_done: {
|
|
400
|
-
type: 'object',
|
|
401
|
-
properties: {
|
|
402
|
-
root: { type: 'string' },
|
|
403
|
-
id: { type: 'string' },
|
|
404
|
-
doc: { type: 'string' },
|
|
405
|
-
line: { type: 'number' },
|
|
406
|
-
from: { type: 'string' },
|
|
407
|
-
to: { type: 'string' },
|
|
408
|
-
wouldChange: { type: 'boolean' },
|
|
409
|
-
dryRun: { type: 'boolean' },
|
|
410
|
-
remedy: { type: ['string', 'null'] },
|
|
411
|
-
},
|
|
412
|
-
required: ['root', 'id', 'wouldChange', 'dryRun'],
|
|
413
|
-
},
|
|
414
|
-
brief: {
|
|
415
|
-
type: 'object',
|
|
416
|
-
properties: {
|
|
417
|
-
root: { type: 'string' },
|
|
418
|
-
kind: { type: 'string' },
|
|
419
|
-
basis: { type: 'string' },
|
|
420
|
-
task: { type: 'string' },
|
|
421
|
-
scope: { type: 'array', items: { type: 'string' } },
|
|
422
|
-
budget: { type: 'number' },
|
|
423
|
-
bytes: { type: 'number' },
|
|
424
|
-
before: { type: 'number' },
|
|
425
|
-
floorHeld: { type: 'boolean' },
|
|
426
|
-
cut: { type: 'array', items: { type: 'string' } },
|
|
427
|
-
sections: { type: 'object', additionalProperties: { type: 'array', items: { type: 'string' } } },
|
|
428
|
-
text: { type: 'string' },
|
|
429
|
-
},
|
|
430
|
-
required: ['root', 'kind', 'basis', 'task', 'scope', 'budget', 'bytes', 'before', 'floorHeld', 'cut', 'sections', 'text'],
|
|
431
|
-
},
|
|
432
|
-
pr_body: {
|
|
433
|
-
type: 'object',
|
|
434
|
-
properties: {
|
|
435
|
-
root: { type: 'string' },
|
|
436
|
-
base: { type: ['string', 'null'] },
|
|
437
|
-
tree: { type: ['string', 'null'] },
|
|
438
|
-
verdict: { type: 'string' },
|
|
439
|
-
unreviewed: { type: 'array', items: { type: 'string' } },
|
|
440
|
-
paths: { type: 'array', items: { type: 'string' } },
|
|
441
|
-
decisions: {
|
|
442
|
-
type: 'array',
|
|
443
|
-
items: {
|
|
444
|
-
type: 'object',
|
|
445
|
-
properties: {
|
|
446
|
-
path: { type: 'string' },
|
|
447
|
-
id: { type: 'string' },
|
|
448
|
-
title: { type: 'string' },
|
|
449
|
-
status: { type: 'string' },
|
|
450
|
-
},
|
|
451
|
-
required: ['path', 'id', 'title', 'status'],
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
gates: {
|
|
455
|
-
type: 'object',
|
|
456
|
-
properties: {
|
|
457
|
-
declared: { type: 'array', items: { type: 'string' } },
|
|
458
|
-
evidenceMode: { type: ['string', 'null'] },
|
|
459
|
-
run: { type: ['object', 'null'] },
|
|
460
|
-
rulesOk: { type: ['boolean', 'null'] },
|
|
461
|
-
hooksOk: { type: ['boolean', 'null'] },
|
|
462
|
-
blocking: { type: 'number' },
|
|
463
|
-
warnings: { type: 'number' },
|
|
464
|
-
cached: { type: 'boolean' },
|
|
465
|
-
},
|
|
466
|
-
required: ['declared', 'blocking', 'warnings', 'cached'],
|
|
467
|
-
},
|
|
468
|
-
evidence: {
|
|
469
|
-
type: 'object',
|
|
470
|
-
properties: {
|
|
471
|
-
entries: { type: 'array', items: { type: 'object' } },
|
|
472
|
-
errors: { type: 'array', items: { type: 'string' } },
|
|
473
|
-
},
|
|
474
|
-
required: ['entries', 'errors'],
|
|
475
|
-
},
|
|
476
|
-
},
|
|
477
|
-
required: ['root', 'base', 'tree', 'verdict', 'unreviewed', 'decisions', 'gates', 'evidence', 'paths'],
|
|
478
|
-
},
|
|
479
|
-
federation_query: {
|
|
480
|
-
type: 'object',
|
|
481
|
-
properties: {
|
|
482
|
-
root: { type: 'string' },
|
|
483
|
-
query: { type: 'string' },
|
|
484
|
-
servers: { type: 'array', items: { type: 'string' } },
|
|
485
|
-
answers: {
|
|
486
|
-
type: 'array',
|
|
487
|
-
items: {
|
|
488
|
-
type: 'object',
|
|
489
|
-
properties: {
|
|
490
|
-
server: { type: 'string' },
|
|
491
|
-
tool: { type: 'string' },
|
|
492
|
-
query: { type: 'string' },
|
|
493
|
-
text: { type: 'string' },
|
|
494
|
-
},
|
|
495
|
-
required: ['server', 'tool', 'query', 'text'],
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
findings: { type: 'array', items: { type: 'string' } },
|
|
499
|
-
},
|
|
500
|
-
required: ['root', 'query', 'servers', 'answers', 'findings'],
|
|
501
|
-
},
|
|
502
|
-
/** The shape of every tool failure, which is a result and not a protocol error. */
|
|
503
|
-
error: ERROR_SHAPE,
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
const rootArg = {
|
|
507
|
-
type: 'object',
|
|
508
|
-
properties: { root: { type: 'string', description: 'repository root; defaults to the editor cwd' } },
|
|
509
|
-
required: [],
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
function rootOf(args) {
|
|
513
|
-
return typeof args?.root === 'string' && args.root.length > 0 ? args.root : process.cwd();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
async function registry(args) {
|
|
517
|
-
const { registry, errors, ok, verified, workspace } = await loadRegistry(null, args ?? {});
|
|
518
|
-
if (!ok) {
|
|
519
|
-
throw Object.assign(new Error(errors[0]), {
|
|
520
|
-
remedy: `run: ${CLI} graph lint`,
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
return { registry, verified, workspace };
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
export const TOOLS = [
|
|
527
|
-
{
|
|
528
|
-
outputSchema: output.doctor,
|
|
529
|
-
name: 'doctor',
|
|
530
|
-
description:
|
|
531
|
-
'Classify a repository: stack tags, emitted facts with the file that proved each, and manifest presence. Call this first when the repo is unknown.',
|
|
532
|
-
inputSchema: rootArg,
|
|
533
|
-
async run(args) {
|
|
534
|
-
const root = rootOf(args);
|
|
535
|
-
const { result, ws, shared, findings, failure } = await diagnose(root, args ?? {});
|
|
536
|
-
return {
|
|
537
|
-
root,
|
|
538
|
-
...result,
|
|
539
|
-
workspace: { root: ws.root ?? null, source: ws.source, detail: ws.detail, error: ws.error ?? null },
|
|
540
|
-
shared,
|
|
541
|
-
findings: serialisedFindings(findings),
|
|
542
|
-
// Read-only here: the command applies fixes behind `--fix`, the tool only reports.
|
|
543
|
-
fix: { requested: false, applied: false, fixed: [], failed: [] },
|
|
544
|
-
...(failure ?? {}),
|
|
545
|
-
};
|
|
546
|
-
},
|
|
547
|
-
},
|
|
548
|
-
|
|
549
|
-
{
|
|
550
|
-
outputSchema: output.skills_index,
|
|
551
|
-
name: 'skills_index',
|
|
552
|
-
description:
|
|
553
|
-
'Full skill catalogue: every skill with summary, size, stack tags and deferred flag. The fallback when brief returns nothing useful.',
|
|
554
|
-
inputSchema: {
|
|
555
|
-
type: 'object',
|
|
556
|
-
properties: {
|
|
557
|
-
registry: { type: 'string', description: 'path to skills.graph.json; else --registry / SIMT_REGISTRY / default tiers' },
|
|
558
|
-
},
|
|
559
|
-
required: [],
|
|
560
|
-
},
|
|
561
|
-
async run(args) {
|
|
562
|
-
const { registry: reg, verified, workspace } = await registry(args);
|
|
563
|
-
return {
|
|
564
|
-
skills: reg.skills.length,
|
|
565
|
-
totalBytes: reg.skills.reduce((n, s) => n + s.bytes, 0),
|
|
566
|
-
deferred: reg.skills.filter((s) => s.deferred).map((s) => s.name),
|
|
567
|
-
workspace,
|
|
568
|
-
verified,
|
|
569
|
-
entries: [...reg.skills]
|
|
570
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
571
|
-
.map((s) => ({ name: s.name, summary: s.summary, bytes: s.bytes, stack: s.stack, deferred: Boolean(s.deferred) })),
|
|
572
|
-
};
|
|
573
|
-
},
|
|
574
|
-
},
|
|
575
|
-
|
|
576
|
-
{
|
|
577
|
-
outputSchema: output.skills_brief,
|
|
578
|
-
name: 'skills_brief',
|
|
579
|
-
description:
|
|
580
|
-
'THE entry point. Resolve the skill set for a task: selected with why, deferred, and not selected with reason - the reasons let you override the resolver instead of guessing at it.',
|
|
581
|
-
inputSchema: {
|
|
582
|
-
type: 'object',
|
|
583
|
-
properties: {
|
|
584
|
-
root: { type: 'string' },
|
|
585
|
-
task: { type: 'string', description: 'what you are about to do, in your own words' },
|
|
586
|
-
paths: { type: 'string', description: 'comma-separated globs; defaults to the git diff' },
|
|
587
|
-
budget: { type: 'number', description: 'context budget in bytes (default 20480)' },
|
|
588
|
-
skill: { type: 'string', description: 'force a skill in, comma-separated' },
|
|
589
|
-
all: { type: 'boolean', description: 'ignore the budget' },
|
|
590
|
-
registry: { type: 'string', description: 'path to skills.graph.json; else --registry / SIMT_REGISTRY / default tiers' },
|
|
591
|
-
},
|
|
592
|
-
required: [],
|
|
593
|
-
},
|
|
594
|
-
async run(args) {
|
|
595
|
-
const root = rootOf(args);
|
|
596
|
-
const facts = await skillsBriefFacts(root, args ?? {});
|
|
597
|
-
// No body is ever returned by this tool: a caller that wants one calls
|
|
598
|
-
// skills_show, which makes the cost visible before it is paid.
|
|
599
|
-
if (facts.kind === 'all') {
|
|
600
|
-
return {
|
|
601
|
-
all: true,
|
|
602
|
-
repoStack: facts.repoStack,
|
|
603
|
-
inline: facts.inline.map((s) => s.name),
|
|
604
|
-
deferred: facts.deferred.map((s) => s.name),
|
|
605
|
-
...(facts.warning ? { warning: facts.warning } : {}),
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
const { shown, shownDeferred, shownUsed, pointerOnly, excludedEntries, workspaceRoot, kind, ...doc } = facts;
|
|
609
|
-
return doc;
|
|
610
|
-
},
|
|
611
|
-
},
|
|
612
|
-
|
|
613
|
-
{
|
|
614
|
-
outputSchema: output.skills_show,
|
|
615
|
-
name: 'skills_show',
|
|
616
|
-
description: 'One skill body and the metadata needed to judge it: source path, the location it was read from, and its cited decisions.',
|
|
617
|
-
inputSchema: {
|
|
618
|
-
type: 'object',
|
|
619
|
-
properties: {
|
|
620
|
-
name: { type: 'string' },
|
|
621
|
-
root: { type: 'string' },
|
|
622
|
-
maxBytes: { type: 'number', description: 'hard ceiling on the returned text' },
|
|
623
|
-
registry: { type: 'string', description: 'path to skills.graph.json; else --registry / SIMT_REGISTRY / default tiers' },
|
|
624
|
-
},
|
|
625
|
-
required: ['name'],
|
|
626
|
-
},
|
|
627
|
-
async run(args) {
|
|
628
|
-
const root = rootOf(args);
|
|
629
|
-
const facts = await skillsShowFacts(root, args?.name, args ?? {});
|
|
630
|
-
return {
|
|
631
|
-
...facts.skill,
|
|
632
|
-
workspace: facts.workspace,
|
|
633
|
-
body: facts.body,
|
|
634
|
-
bodyFrom: facts.bodyFrom,
|
|
635
|
-
bodyText: facts.bodyText,
|
|
636
|
-
};
|
|
637
|
-
},
|
|
638
|
-
},
|
|
639
|
-
|
|
640
|
-
{
|
|
641
|
-
outputSchema: output.skills_find,
|
|
642
|
-
name: 'skills_find',
|
|
643
|
-
description: 'Raw search across capability, keyword and summary. Use when skills_brief has nothing and you suspect a skill exists.',
|
|
644
|
-
inputSchema: {
|
|
645
|
-
type: 'object',
|
|
646
|
-
properties: {
|
|
647
|
-
query: { type: 'string' },
|
|
648
|
-
root: { type: 'string' },
|
|
649
|
-
registry: { type: 'string', description: 'path to skills.graph.json; else --registry / SIMT_REGISTRY / default tiers' },
|
|
650
|
-
},
|
|
651
|
-
required: ['query'],
|
|
652
|
-
},
|
|
653
|
-
async run(args) {
|
|
654
|
-
const root = rootOf(args);
|
|
655
|
-
return skillsFindFacts(root, typeof args?.query === 'string' ? args.query : '', args ?? {});
|
|
656
|
-
},
|
|
657
|
-
},
|
|
658
|
-
|
|
659
|
-
{
|
|
660
|
-
outputSchema: output.skills_why,
|
|
661
|
-
name: 'skills_why',
|
|
662
|
-
description:
|
|
663
|
-
'The signal chain for one skill in one repository: its score, the reasons, whether the stack gate admits it, and what would select it.',
|
|
664
|
-
inputSchema: {
|
|
665
|
-
type: 'object',
|
|
666
|
-
properties: {
|
|
667
|
-
name: { type: 'string' },
|
|
668
|
-
root: { type: 'string' },
|
|
669
|
-
task: { type: 'string' },
|
|
670
|
-
registry: { type: 'string', description: 'path to skills.graph.json; else --registry / SIMT_REGISTRY / default tiers' },
|
|
671
|
-
},
|
|
672
|
-
required: ['name'],
|
|
673
|
-
},
|
|
674
|
-
async run(args) {
|
|
675
|
-
const root = rootOf(args);
|
|
676
|
-
return skillsWhyFacts(root, args?.name, args ?? {});
|
|
677
|
-
},
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
{
|
|
681
|
-
outputSchema: output.rules_list,
|
|
682
|
-
name: 'rules_list',
|
|
683
|
-
description:
|
|
684
|
-
'The rule catalogue, and which rules a repository actually receives. Pass root for the selection, not just the catalogue.',
|
|
685
|
-
inputSchema: {
|
|
686
|
-
type: 'object',
|
|
687
|
-
properties: { root: { type: 'string' } },
|
|
688
|
-
required: [],
|
|
689
|
-
},
|
|
690
|
-
async run(args) {
|
|
691
|
-
const root = typeof args?.root === 'string' && args.root ? args.root : null;
|
|
692
|
-
const { rules, notes } = await rulesCatalogue(root, { selectFor: root });
|
|
693
|
-
return {
|
|
694
|
-
sha: await provenanceDigest(),
|
|
695
|
-
notes,
|
|
696
|
-
rules,
|
|
697
|
-
};
|
|
698
|
-
},
|
|
699
|
-
},
|
|
700
|
-
|
|
701
|
-
{
|
|
702
|
-
outputSchema: output.rules_why,
|
|
703
|
-
name: 'rules_why',
|
|
704
|
-
description:
|
|
705
|
-
'Why a rule exists: a rule id for the decisions that settled it, or an ADR id for the rules that repeat it. An uncited rule is a finding, reported so.',
|
|
706
|
-
inputSchema: {
|
|
707
|
-
type: 'object',
|
|
708
|
-
properties: { what: { type: 'string', description: 'a rule id or ADR-NNN' } },
|
|
709
|
-
required: ['what'],
|
|
710
|
-
},
|
|
711
|
-
async run(args) {
|
|
712
|
-
const what = typeof args?.what === 'string' ? args.what : '';
|
|
713
|
-
return rulesWhyFacts(null, what, {});
|
|
714
|
-
},
|
|
715
|
-
},
|
|
716
|
-
|
|
717
|
-
{
|
|
718
|
-
outputSchema: output.check,
|
|
719
|
-
name: 'check',
|
|
720
|
-
description:
|
|
721
|
-
'The gate for one repository: region current, unmanaged, drifted or malformed, and rule files lacking an id. Read-only - it never repairs.',
|
|
722
|
-
inputSchema: rootArg,
|
|
723
|
-
async run(args) {
|
|
724
|
-
const root = rootOf(args);
|
|
725
|
-
const { ok, findings } = await checkRepo(root, {});
|
|
726
|
-
return { root, ok, findings };
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
|
|
730
|
-
{
|
|
731
|
-
outputSchema: output.compare,
|
|
732
|
-
name: 'compare',
|
|
733
|
-
description:
|
|
734
|
-
'The workspace matrix: adoption, region, SHA, hooks and gates per repo, plus problems. Pages with limit/cursor; honors progressToken and cancellation.',
|
|
735
|
-
inputSchema: {
|
|
736
|
-
type: 'object',
|
|
737
|
-
properties: {
|
|
738
|
-
root: { type: 'string', description: 'a workspace directory, or one repository' },
|
|
739
|
-
workspace: { type: 'string', description: 'workspace root when root is not given' },
|
|
740
|
-
limit: { type: 'number', description: 'page size; without it every row is built' },
|
|
741
|
-
cursor: { type: 'string', description: 'opaque page start from a previous partial answer' },
|
|
742
|
-
concurrency: { type: 'number', description: 'rows built at once (default 4)' },
|
|
743
|
-
},
|
|
744
|
-
required: [],
|
|
745
|
-
},
|
|
746
|
-
async run(args, ctx = {}) {
|
|
747
|
-
const root = args?.root ?? args?.workspace ?? (await resolveWorkspace(args ?? {})).root ?? process.cwd();
|
|
748
|
-
const found = await discover(root);
|
|
749
|
-
if (found.error) throw Object.assign(new Error(`${root}: ${found.error}`), { remedy: 'pass root' });
|
|
750
|
-
const expectedSha = await provenanceDigest();
|
|
751
|
-
const context = { expectedSha, gates: new Set(Object.keys(GATES)), gateKinds: GATE_KINDS };
|
|
752
|
-
// A page names its start as an index into discovery order, which is
|
|
753
|
-
// stable between calls on an unchanged estate; garbage parses to 0
|
|
754
|
-
// rather than erroring, because a dashboard that refuses to run over a
|
|
755
|
-
// stale cursor is worse than one that restarts the listing.
|
|
756
|
-
const start = Math.max(0, parseInt(typeof args?.cursor === 'string' ? args.cursor : '0', 10) || 0);
|
|
757
|
-
const limit = typeof args?.limit === 'number' && args.limit >= 1 ? Math.floor(args.limit) : null;
|
|
758
|
-
const page = limit === null ? found.repos : found.repos.slice(start, start + limit);
|
|
759
|
-
const rows = await buildRows(page, context, {
|
|
760
|
-
concurrency: resolveConcurrency(args ?? {}),
|
|
761
|
-
signal: ctx?.signal ?? null,
|
|
762
|
-
onBatch: ctx?.onProgress ? (done, total) => ctx.onProgress(start + done, found.repos.length) : null,
|
|
763
|
-
});
|
|
764
|
-
const next = limit !== null && start + page.length < found.repos.length ? String(start + page.length) : null;
|
|
765
|
-
return {
|
|
766
|
-
root,
|
|
767
|
-
shape: found.shape,
|
|
768
|
-
expectedSha,
|
|
769
|
-
summary: summarise(rows, { root, shape: found.shape, expectedSha }),
|
|
770
|
-
repos: rows,
|
|
771
|
-
worktrees: found.worktrees.map((w) => ({ name: w.name, owner: w.ownerName })),
|
|
772
|
-
directories: found.directories,
|
|
773
|
-
...(limit === null ? {} : { partial: next !== null, cursor: next, total: found.repos.length }),
|
|
774
|
-
};
|
|
775
|
-
},
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
outputSchema: output.plan_next,
|
|
779
|
-
name: 'plan_next',
|
|
780
|
-
description: "The next queued slice whose dependencies are complete; its design section only. Pass id for one slice whether or not it is ready.",
|
|
781
|
-
inputSchema: {
|
|
782
|
-
type: 'object',
|
|
783
|
-
properties: {
|
|
784
|
-
root: { type: 'string', description: 'repository root; defaults to the editor cwd' },
|
|
785
|
-
id: { type: 'string', description: 'slice id; when given, return that slice' },
|
|
786
|
-
},
|
|
787
|
-
required: [],
|
|
788
|
-
},
|
|
789
|
-
async run(args) {
|
|
790
|
-
const root = rootOf(args);
|
|
791
|
-
const id = typeof args?.id === 'string' && args.id.length > 0 ? args.id : null;
|
|
792
|
-
const result = await planNext(root, id);
|
|
793
|
-
if (result.error) throw Object.assign(new Error(result.error), { remedy: result.remedy ?? null });
|
|
794
|
-
return { root: result.root, next: result.next, reason: result.reason };
|
|
795
|
-
},
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
outputSchema: output.plan_done,
|
|
799
|
-
name: 'plan_done',
|
|
800
|
-
// Same function as the command, and deliberately a preview. This surface does
|
|
801
|
-
// not write: the row change is a diff, and the command applies it behind --yes.
|
|
802
|
-
description: 'Preview the one-row rewrite for a slice. The write stays on the plan done command, behind --yes.',
|
|
803
|
-
inputSchema: {
|
|
804
|
-
type: 'object',
|
|
805
|
-
properties: {
|
|
806
|
-
root: { type: 'string', description: 'repository root; defaults to the editor cwd' },
|
|
807
|
-
id: { type: 'string', description: 'slice id' },
|
|
808
|
-
sha: { type: 'string', description: 'commit sha that closed the slice' },
|
|
809
|
-
test: { type: 'string', description: 'test command that closed it' },
|
|
810
|
-
state: { type: 'string', description: 'target state; omitted means the completed state of the repository template (Complete for tracker)' },
|
|
811
|
-
},
|
|
812
|
-
required: ['id'],
|
|
813
|
-
},
|
|
814
|
-
async run(args) {
|
|
815
|
-
const root = rootOf(args);
|
|
816
|
-
const result = await planDone(root, args?.id, {
|
|
817
|
-
sha: args?.sha ?? null,
|
|
818
|
-
test: args?.test ?? null,
|
|
819
|
-
state: args?.state ?? null,
|
|
820
|
-
dryRun: true,
|
|
821
|
-
});
|
|
822
|
-
if (result.error) throw Object.assign(new Error(result.error), { remedy: result.remedy ?? null });
|
|
823
|
-
return {
|
|
824
|
-
root: result.root,
|
|
825
|
-
id: result.id,
|
|
826
|
-
doc: result.doc,
|
|
827
|
-
line: result.line,
|
|
828
|
-
from: result.from,
|
|
829
|
-
to: result.to,
|
|
830
|
-
wouldChange: result.wouldChange,
|
|
831
|
-
dryRun: true,
|
|
832
|
-
remedy: result.remedy,
|
|
833
|
-
};
|
|
834
|
-
},
|
|
835
|
-
},
|
|
836
|
-
// The PR-flow entry point over this surface: the same engine the `pr-body`
|
|
837
|
-
// command renders, returning JSON for a model. Read-only like everything
|
|
838
|
-
// here — it names the tree, reads the ledger and the check cache, and writes
|
|
839
|
-
// nothing, so the exit-code-never-carries-the-verdict contract stays intact.
|
|
840
|
-
{
|
|
841
|
-
outputSchema: output.pr_body,
|
|
842
|
-
name: 'pr_body',
|
|
843
|
-
// R41's 2 KB description budget is shared, not per tool: this sentence was
|
|
844
|
-
// paid for by shortening `brief` and `federation_query`, and the test on the
|
|
845
|
-
// total holds every future tool to the same trade.
|
|
846
|
-
description:
|
|
847
|
-
'The pull request body: ledger evidence for this tree, the decisions the diff touches, gate state, and the ADR-008 verdict with what was not reviewed.',
|
|
848
|
-
inputSchema: {
|
|
849
|
-
type: 'object',
|
|
850
|
-
properties: {
|
|
851
|
-
root: { type: 'string', description: 'repository root; defaults to the editor cwd' },
|
|
852
|
-
base: { type: 'string', description: 'the ref the body diffs against; defaults to origin/main, then main' },
|
|
853
|
-
},
|
|
854
|
-
required: [],
|
|
855
|
-
},
|
|
856
|
-
async run(args) {
|
|
857
|
-
const root = rootOf(args);
|
|
858
|
-
const base = typeof args?.base === 'string' && args.base.length > 0 ? args.base : null;
|
|
859
|
-
const report = await buildPrBody(root, { base });
|
|
860
|
-
if (report.error !== null) {
|
|
861
|
-
throw Object.assign(new Error(report.error), {
|
|
862
|
-
remedy: `run: ${CLI} pr-body --base <ref> with a ref git knows`,
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
return {
|
|
866
|
-
root,
|
|
867
|
-
base: report.base,
|
|
868
|
-
tree: report.tree,
|
|
869
|
-
verdict: report.verdict,
|
|
870
|
-
unreviewed: report.unreviewed,
|
|
871
|
-
paths: report.paths,
|
|
872
|
-
decisions: report.decisions,
|
|
873
|
-
gates: report.gates,
|
|
874
|
-
evidence: report.evidence,
|
|
875
|
-
};
|
|
876
|
-
},
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
outputSchema: output.brief,
|
|
880
|
-
name: 'brief',
|
|
881
|
-
description:
|
|
882
|
-
'The packet for one task in prompt-sized bytes: kind, commands, files, tests, decisions, skills. With no task it describes the repository.',
|
|
883
|
-
inputSchema: {
|
|
884
|
-
type: 'object',
|
|
885
|
-
properties: {
|
|
886
|
-
root: { type: 'string', description: 'repository root; defaults to the editor cwd' },
|
|
887
|
-
task: { type: 'string', description: "what the agent is about to do, in the user's own words" },
|
|
888
|
-
paths: { type: 'string', description: 'comma-separated paths to scope the packet to, instead of the files git reports as modified' },
|
|
889
|
-
budget: { type: 'number', description: 'byte ceiling; 0 means the whole packet (default 2048)' },
|
|
890
|
-
},
|
|
891
|
-
required: [],
|
|
892
|
-
},
|
|
893
|
-
async run(args) {
|
|
894
|
-
const root = rootOf(args);
|
|
895
|
-
const budget = Number.isFinite(args?.budget) && args.budget >= 0 ? args.budget : DEFAULT_BRIEF_BYTES;
|
|
896
|
-
// Read, never build: every tool here declares itself read-only, and a call that
|
|
897
|
-
// indexed the tree would make that declaration false. A brief with no index to read
|
|
898
|
-
// says which command makes one - the same rule the delivered packet follows, so a
|
|
899
|
-
// pull and a push pay the same price and neither one writes.
|
|
900
|
-
const result = await assembleBrief({
|
|
901
|
-
root,
|
|
902
|
-
task: typeof args?.task === 'string' ? args.task : '',
|
|
903
|
-
paths: typeof args?.paths === 'string' ? args.paths : null,
|
|
904
|
-
budget,
|
|
905
|
-
build: false,
|
|
906
|
-
});
|
|
907
|
-
if (result.error) throw Object.assign(new Error('the index could not be read'), { remedy: `run: ${CLI} index` });
|
|
908
|
-
if (result.cold) throw Object.assign(new Error('this repository has no index to answer from'), { remedy: `run: ${CLI} index` });
|
|
909
|
-
return {
|
|
910
|
-
root: result.root,
|
|
911
|
-
kind: result.kind,
|
|
912
|
-
basis: result.basis,
|
|
913
|
-
task: result.task,
|
|
914
|
-
scope: result.scope,
|
|
915
|
-
budget: result.budget,
|
|
916
|
-
bytes: result.packed.after,
|
|
917
|
-
before: result.packed.before,
|
|
918
|
-
floorHeld: result.packed.floorHeld,
|
|
919
|
-
cut: result.packed.cut,
|
|
920
|
-
sections: result.sections,
|
|
921
|
-
text: result.packed.text,
|
|
922
|
-
};
|
|
923
|
-
},
|
|
924
|
-
},
|
|
925
|
-
{
|
|
926
|
-
outputSchema: output.federation_query,
|
|
927
|
-
name: 'federation_query',
|
|
928
|
-
description:
|
|
929
|
-
'Ask a declared code-graph server one structural question; the answer names the server. Starts only servers simt.json names; none declared is an error.',
|
|
930
|
-
// This tool spawns the repo manifest's `command`, so it is not read-only: a client
|
|
931
|
-
// that cached its answer as one would replay a process spawn as a pure read (Q13).
|
|
932
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
933
|
-
inputSchema: {
|
|
934
|
-
type: 'object',
|
|
935
|
-
properties: {
|
|
936
|
-
root: { type: 'string', description: 'repository root; defaults to the editor cwd' },
|
|
937
|
-
query: { type: 'string', description: "the structural question to ask, in the user's own words" },
|
|
938
|
-
server: { type: 'string', description: 'ask only this declared server, by name; omit to ask every one of them' },
|
|
939
|
-
},
|
|
940
|
-
required: ['query'],
|
|
941
|
-
},
|
|
942
|
-
async run(args) {
|
|
943
|
-
const root = rootOf(args);
|
|
944
|
-
const query = typeof args?.query === 'string' ? args.query.trim() : '';
|
|
945
|
-
if (query.length === 0) {
|
|
946
|
-
throw Object.assign(new Error('the query is empty'), {
|
|
947
|
-
remedy: 'pass a query, for example the symbol or file to look up',
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
|
-
const { servers, findings } = await readFederation(root);
|
|
951
|
-
if (servers.length === 0) {
|
|
952
|
-
throw Object.assign(new Error('this repository declares no federation server'), {
|
|
953
|
-
remedy: 'add a "federation" block naming a server to simt.json, or use the brief tool',
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
const only = typeof args?.server === 'string' && args.server.length > 0 ? args.server : null;
|
|
957
|
-
const chosen = only === null ? servers : servers.filter((server) => server.name === only);
|
|
958
|
-
if (only !== null && chosen.length === 0) {
|
|
959
|
-
throw Object.assign(new Error(`no declared federation server is named ${only}`), {
|
|
960
|
-
remedy: `declared servers: ${servers.map((server) => server.name).join(', ')}`,
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
// A model-supplied root can point at a directory with a crafted manifest, and a
|
|
964
|
-
// repo manifest alone must not authorise a spawn on the operator's machine (Q13).
|
|
965
|
-
const ws = await resolveWorkspace(args ?? {});
|
|
966
|
-
const user = await readUserConfig();
|
|
967
|
-
const denied = federationAccess({
|
|
968
|
-
root,
|
|
969
|
-
cwd: process.cwd(),
|
|
970
|
-
workspaceRoot: ws.root,
|
|
971
|
-
serverNames: chosen.map((server) => server.name),
|
|
972
|
-
userConfig: user.config,
|
|
973
|
-
configPath: user.path ?? configPaths()[0] ?? '~/.config/simt-dev/config.json',
|
|
974
|
-
});
|
|
975
|
-
if (denied !== null) {
|
|
976
|
-
throw Object.assign(new Error(denied.message), { remedy: denied.remedy });
|
|
977
|
-
}
|
|
978
|
-
const { answers, findings: failures } = await consult({ root, query, servers: chosen });
|
|
979
|
-
return {
|
|
980
|
-
root,
|
|
981
|
-
query,
|
|
982
|
-
servers: chosen.map((server) => server.name),
|
|
983
|
-
answers,
|
|
984
|
-
findings: [...findings, ...failures],
|
|
985
|
-
};
|
|
986
|
-
},
|
|
987
|
-
},
|
|
988
|
-
];
|
|
27
|
+
export { ERROR_SHAPE } from './schemas.mjs';
|
|
989
28
|
|
|
990
29
|
export function toolNames() {
|
|
991
30
|
return TOOLS.map((t) => t.name);
|
|
992
31
|
}
|
|
993
|
-
|
|
994
|
-
export { measureSkill };
|