simt-dev 0.6.2 → 0.7.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 +83 -1
- package/README.md +46 -17
- package/agents/simt-agent.md +84 -0
- package/bin/simt.mjs +19 -6
- package/docs/decisions/ADR-009-skill-layout.md +1 -1
- package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
- package/docs/decisions/ADR-100-a-staged-diff-is-the-last-gate-before-history-and-it-runs-inside-the-tool.md +94 -0
- package/docs/decisions/ADR-101-a-file-lease-belongs-to-one-session-and-the-pre-edit-hook-refuses-another.md +95 -0
- package/docs/decisions/ADR-102-the-two-queued-review-rows-are-closed-by-ruling.md +49 -0
- package/docs/decisions/ADR-103-co-projected-duplicates-merge-down.md +74 -0
- package/docs/decisions/ADR-104-the-pre-tool-use-policy-reads-the-command-the-way-a-shell-reads-it.md +76 -0
- package/docs/decisions/ADR-105-the-json-ceiling-shrinks-arrays-and-never-cuts-bytes.md +44 -0
- package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
- package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
- package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
- package/docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md +47 -0
- package/docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md +43 -0
- package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
- package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
- package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
- package/docs/decisions/ADR-114-the-bench-pairs-at-the-task-level-a-missing-cost-is-not-zero-and-an-unasked-cross-group-compare-is-refused.md +74 -0
- package/docs/decisions/ADR-115-the-index-stamps-its-schema-version-migrates-older-databases-in-place-and-serves-newer-ones-from-a-sidecar.md +69 -0
- package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
- package/docs/decisions/README.md +53 -0
- package/package.json +4 -2
- package/rules/core.md +2 -2
- package/rules/mdc/preview-postflight.mdc +5 -6
- package/rules/mdc/test-gate.mdc +2 -0
- package/rules/stacks/android.md +10 -11
- package/rules/stacks/cloudflare-workers.md +1 -1
- package/rules/stacks/connectiq.md +2 -2
- package/rules/stacks/container.md +2 -0
- package/rules/stacks/dart.md +1 -1
- package/rules/stacks/deno.md +1 -1
- package/rules/stacks/dotnet.md +1 -1
- package/rules/stacks/elixir.md +1 -1
- package/rules/stacks/go.md +1 -1
- package/rules/stacks/jvm.md +4 -3
- package/rules/stacks/node.md +2 -2
- package/rules/stacks/python.md +7 -6
- package/rules/stacks/react-native.md +3 -5
- package/rules/stacks/react-web.md +9 -9
- package/rules/stacks/ruby.md +1 -1
- package/rules/stacks/rust.md +1 -1
- package/rules/stacks/swift.md +1 -1
- package/rules/stacks/typescript.md +2 -2
- package/rules/stacks/uv.md +3 -2
- package/skills/simt-commands/SKILL.md +70 -0
- package/src/advice/advice.mjs +7 -3
- package/src/agenthooks/decide.mjs +238 -15
- package/src/agenthooks/opencode.mjs +72 -0
- package/src/agenthooks/policy.mjs +81 -5
- package/src/agenthooks/prompt.mjs +6 -2
- package/src/agenthooks/render.mjs +186 -19
- package/src/agenthooks/shell.mjs +523 -0
- package/src/agenthooks/shim.mjs +55 -0
- package/src/agents/render.mjs +277 -0
- package/src/argv.mjs +62 -9
- package/src/bench/ablate.mjs +31 -14
- package/src/bench/mine.mjs +48 -1
- package/src/bench/probes.mjs +5 -6
- package/src/bench/report.mjs +243 -80
- package/src/bench/run.mjs +12 -11
- package/src/bench/variants.mjs +112 -40
- package/src/brief/assemble.mjs +6 -0
- package/src/ci/workflow.mjs +16 -7
- package/src/commands/agent.mjs +206 -0
- package/src/commands/agenthook.mjs +133 -17
- package/src/commands/bench.mjs +2 -1
- package/src/commands/ci.mjs +28 -3
- package/src/commands/compare.mjs +8 -7
- package/src/commands/completions.mjs +27 -0
- package/src/commands/decide.mjs +20 -6
- package/src/commands/doctor.mjs +367 -0
- package/src/commands/evidence.mjs +13 -3
- package/src/commands/explain.mjs +52 -0
- package/src/commands/gate.mjs +85 -19
- package/src/commands/graph.mjs +18 -5
- package/src/commands/help.mjs +96 -26
- package/src/commands/hooks.mjs +258 -67
- package/src/commands/ignore.mjs +152 -0
- package/src/commands/impact.mjs +4 -2
- package/src/commands/index.mjs +69 -102
- package/src/commands/init.mjs +239 -37
- package/src/commands/lease.mjs +304 -0
- package/src/commands/lessons.mjs +77 -9
- package/src/commands/map.mjs +33 -18
- package/src/commands/mcp.mjs +2 -2
- package/src/commands/pack.mjs +9 -0
- package/src/commands/plan.mjs +13 -1
- package/src/commands/plugin.mjs +17 -2
- package/src/commands/prbody.mjs +45 -0
- package/src/commands/repoindex.mjs +4 -0
- package/src/commands/rules.mjs +219 -105
- package/src/commands/shared.mjs +36 -5
- package/src/commands/skills.mjs +329 -206
- package/src/commands/skillscompile.mjs +8 -7
- package/src/commands/skillsinstall.mjs +112 -30
- package/src/commands/slice.mjs +712 -0
- package/src/commands/status.mjs +62 -0
- package/src/commands/sync.mjs +146 -36
- package/src/commands/tests.mjs +5 -2
- package/src/commands/undo.mjs +114 -0
- package/src/commands/upgrade.mjs +103 -9
- package/src/commands/why.mjs +10 -4
- package/src/compare/hooksstate.mjs +23 -1
- package/src/compare/learn.mjs +82 -57
- package/src/compare/parallel.mjs +50 -0
- package/src/compare/row.mjs +26 -1
- package/src/completions.mjs +196 -0
- package/src/decisions/dir.mjs +84 -0
- package/src/decisions/index.mjs +109 -11
- package/src/diff.mjs +254 -0
- package/src/distill/distill.mjs +3 -1
- package/src/ecosystem/node-runner.mjs +144 -0
- package/src/explain.mjs +270 -0
- package/src/federation/config.mjs +44 -0
- package/src/graph/load.mjs +52 -24
- package/src/hooks/capability.mjs +14 -1
- package/src/hooks/dispatcher.mjs +441 -9
- package/src/hooks/gates.mjs +14 -3
- package/src/hooks/resolve.mjs +23 -3
- package/src/hooks/shim.mjs +12 -7
- package/src/identity.mjs +67 -14
- package/src/ignore.mjs +133 -0
- package/src/index/extract.mjs +192 -11
- package/src/index/persist.mjs +372 -55
- package/src/index/store.mjs +263 -60
- package/src/index/tests.mjs +6 -35
- package/src/journal.mjs +545 -0
- package/src/kernel/card.mjs +37 -12
- package/src/kernel/checks.mjs +44 -9
- package/src/kernel/variants.mjs +27 -16
- package/src/lease.mjs +195 -0
- package/src/ledger/index.mjs +91 -25
- package/src/mcp/resources.mjs +5 -3
- package/src/mcp/server.mjs +86 -13
- package/src/mcp/tools.mjs +340 -264
- package/src/memory/decide.mjs +98 -38
- package/src/memory/lessons.mjs +180 -33
- package/src/name.mjs +9 -0
- package/src/output/index.mjs +82 -12
- package/src/package-version.mjs +49 -1
- package/src/packs/manage.mjs +13 -4
- package/src/plan/index.mjs +37 -8
- package/src/plan/template.mjs +27 -5
- package/src/plugins.mjs +33 -8
- package/src/prbody.mjs +253 -0
- package/src/projection/baselines.mjs +1 -1
- package/src/projection/disclosure.mjs +2 -2
- package/src/projection/loaders.mjs +41 -1
- package/src/projection/lock.mjs +21 -14
- package/src/projection/manifest.mjs +82 -21
- package/src/projection/markers.mjs +43 -3
- package/src/projection/mdc.mjs +34 -7
- package/src/projection/project.mjs +162 -18
- package/src/projection/rules.mjs +54 -3
- package/src/projection/shared.mjs +4 -1
- package/src/projection/surfaces.mjs +9 -1
- package/src/repo-root.mjs +69 -0
- package/src/resolve/body.mjs +4 -0
- package/src/resolve/fingerprint.mjs +5 -0
- package/src/resolve/local.mjs +58 -0
- package/src/review/critic.mjs +4 -2
- package/src/secrets.mjs +374 -0
- package/src/state.mjs +76 -0
- package/src/status.mjs +157 -0
- package/src/toolwords.mjs +5 -0
- package/src/tsconfig.mjs +39 -99
- package/templates/plan/tracker.json +1 -1
package/src/mcp/server.mjs
CHANGED
|
@@ -103,12 +103,17 @@ const LIST_CACHE = { ttlMs: 3600000, cacheScope: 'private' };
|
|
|
103
103
|
* caller names the root and the default is the process's cwd, which is what an editor's
|
|
104
104
|
* stdio server sits in.
|
|
105
105
|
*
|
|
106
|
+
* `signal` aborts the one in-flight `tools/call` (wired by `serve` to
|
|
107
|
+
* `notifications/cancelled`); `notify` emits `notifications/progress` while it
|
|
108
|
+
* runs. Both are null in tests that call this directly, and every tool must
|
|
109
|
+
* treat a missing context as "run to completion quietly".
|
|
110
|
+
*
|
|
106
111
|
* @param {object|null} request a parsed JSON-RPC message
|
|
107
112
|
* @param {object[]} tools
|
|
108
|
-
* @param {{packageVersion?: string, state?: {version: string|null}, root?: string}} options
|
|
113
|
+
* @param {{packageVersion?: string, state?: {version: string|null}, root?: string, signal?: AbortSignal|null, notify?: ((message: object) => void)|null}} options
|
|
109
114
|
* @returns {Promise<object|null>}
|
|
110
115
|
*/
|
|
111
|
-
export async function handle(request, tools, { packageVersion, state, root } = {}) {
|
|
116
|
+
export async function handle(request, tools, { packageVersion, state, root, signal = null, notify = null } = {}) {
|
|
112
117
|
if (request === null || typeof request !== 'object' || Array.isArray(request)) {
|
|
113
118
|
return error(null, RPC.REQUEST, 'request must be a JSON object');
|
|
114
119
|
}
|
|
@@ -195,8 +200,24 @@ export async function handle(request, tools, { packageVersion, state, root } = {
|
|
|
195
200
|
return error(id, RPC.METHOD, `no tool named "${name}"; call tools/list first`);
|
|
196
201
|
}
|
|
197
202
|
const args = params?.arguments ?? {};
|
|
203
|
+
// The one impurity in this file, and scoped to it: progress has to reach
|
|
204
|
+
// the client while the tool runs, not after it returns. Nothing else
|
|
205
|
+
// writes here - diagnostics still go to stderr, never this stream.
|
|
206
|
+
const progressToken = params?._meta?.progressToken;
|
|
207
|
+
const toolCtx = {
|
|
208
|
+
signal,
|
|
209
|
+
onProgress:
|
|
210
|
+
progressToken !== undefined && progressToken !== null && typeof notify === 'function'
|
|
211
|
+
? (progress, total) =>
|
|
212
|
+
notify({
|
|
213
|
+
jsonrpc: '2.0',
|
|
214
|
+
method: 'notifications/progress',
|
|
215
|
+
params: { progressToken, progress, total },
|
|
216
|
+
})
|
|
217
|
+
: null,
|
|
218
|
+
};
|
|
198
219
|
try {
|
|
199
|
-
const payload = await tool.run(args);
|
|
220
|
+
const payload = await tool.run(args, toolCtx);
|
|
200
221
|
// Both blocks, always. `structuredContent` is what a client validates against
|
|
201
222
|
// `outputSchema` and what it can index; the text block is what a client that
|
|
202
223
|
// predates structured output reads. Emitting one without the other leaves half
|
|
@@ -384,19 +405,71 @@ function error(id, code, message) {
|
|
|
384
405
|
export async function serve(tools, { input = process.stdin, output = process.stdout, packageVersion, onError } = {}) {
|
|
385
406
|
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
386
407
|
const state = { version: null };
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
408
|
+
// The in-flight call a `notifications/cancelled` can abort. Read this way
|
|
409
|
+
// because the loop below is blocked awaiting the tool while the client
|
|
410
|
+
// decides: the event loop still turns on subprocess waits, so the peek
|
|
411
|
+
// fires mid-run and the tool checks the signal between rows. The async
|
|
412
|
+
// iterator below yields the same line afterwards and `handle` answers it
|
|
413
|
+
// with null, as every notification is answered - the peek consumes nothing.
|
|
414
|
+
let inflight = null;
|
|
415
|
+
const peekCancel = (line) => {
|
|
416
|
+
if (!inflight || line.trim() === '') return;
|
|
417
|
+
let message;
|
|
391
418
|
try {
|
|
392
|
-
|
|
393
|
-
} catch
|
|
394
|
-
|
|
419
|
+
message = JSON.parse(line);
|
|
420
|
+
} catch {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (message?.method === 'notifications/cancelled' && message?.params?.requestId === inflight.id) {
|
|
424
|
+
inflight.controller.abort();
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
rl.on('line', peekCancel);
|
|
428
|
+
try {
|
|
429
|
+
for await (const line of rl) {
|
|
430
|
+
if (line.trim() === '') continue;
|
|
431
|
+
|
|
432
|
+
// Parse and handling fail differently and must answer differently. A line that is
|
|
433
|
+
// not JSON has no id to answer to, so it is a parse error with `id: null`; a
|
|
434
|
+
// throw inside `handle` belongs to the request it was answering, so it keeps
|
|
435
|
+
// that request's id — answering it as a parse error hangs the client that is
|
|
436
|
+
// still waiting for its answer (Q12).
|
|
437
|
+
let response;
|
|
438
|
+
let message;
|
|
439
|
+
try {
|
|
440
|
+
message = JSON.parse(line);
|
|
441
|
+
} catch (parseFailure) {
|
|
442
|
+
response = error(null, RPC.PARSE, `invalid JSON: ${parseFailure.message}`);
|
|
443
|
+
}
|
|
444
|
+
if (response === undefined) {
|
|
445
|
+
// A notification never runs: without an id there is nothing to cancel
|
|
446
|
+
// and nothing to answer, so `notifications/cancelled` for an idle server
|
|
447
|
+
// is a no-op rather than an error.
|
|
448
|
+
const isNotification =
|
|
449
|
+
message === null || typeof message !== 'object' || Array.isArray(message) || message.id === undefined || message.id === null;
|
|
450
|
+
const controller = isNotification ? null : new AbortController();
|
|
451
|
+
if (controller) inflight = { id: message.id, controller };
|
|
452
|
+
try {
|
|
453
|
+
response = await handle(message, tools, {
|
|
454
|
+
packageVersion,
|
|
455
|
+
state,
|
|
456
|
+
signal: controller?.signal ?? null,
|
|
457
|
+
notify: (notification) => output.write(`${JSON.stringify(notification)}\n`),
|
|
458
|
+
});
|
|
459
|
+
} catch (handlerFailure) {
|
|
460
|
+
const id = message !== null && typeof message === 'object' && 'id' in message ? message.id : null;
|
|
461
|
+
response = error(id, RPC.INTERNAL, `internal error: ${handlerFailure?.message ?? handlerFailure}`);
|
|
462
|
+
} finally {
|
|
463
|
+
if (controller) inflight = null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (response === null) continue;
|
|
467
|
+
output.write(`${JSON.stringify(response)}\n`);
|
|
395
468
|
}
|
|
396
|
-
|
|
397
|
-
|
|
469
|
+
return EXIT_OK;
|
|
470
|
+
} finally {
|
|
471
|
+
rl.off('line', peekCancel);
|
|
398
472
|
}
|
|
399
|
-
return EXIT_OK;
|
|
400
473
|
}
|
|
401
474
|
|
|
402
475
|
const EXIT_OK = 0;
|