@smeltjs/core 0.2.0 → 0.4.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/README.md +3 -3
- package/dist/agents/guide.d.ts +68 -0
- package/dist/agents/guide.d.ts.map +1 -0
- package/dist/agents/guide.js +72 -0
- package/dist/agents/guide.js.map +1 -0
- package/dist/agents/instructions.d.ts +133 -0
- package/dist/agents/instructions.d.ts.map +1 -0
- package/dist/agents/instructions.js +212 -0
- package/dist/agents/instructions.js.map +1 -0
- package/dist/agents/lint.d.ts +154 -0
- package/dist/agents/lint.d.ts.map +1 -0
- package/dist/agents/lint.js +761 -0
- package/dist/agents/lint.js.map +1 -0
- package/dist/agents/split.d.ts +113 -0
- package/dist/agents/split.d.ts.map +1 -0
- package/dist/agents/split.js +234 -0
- package/dist/agents/split.js.map +1 -0
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js.map +1 -1
- package/dist/cache/prefix.d.ts.map +1 -1
- package/dist/cache/prefix.js.map +1 -1
- package/dist/cli/agents.d.ts +39 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +121 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/args.d.ts +17 -70
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +18 -338
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/config.d.ts +60 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +89 -2
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/hooks.d.ts +55 -51
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +206 -658
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/init.d.ts +30 -15
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +167 -38
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/report.d.ts +23 -0
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +142 -4
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/run.d.ts +29 -90
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +34 -342
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +106 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +100 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/subcommands/agents.d.ts +72 -0
- package/dist/cli/subcommands/agents.d.ts.map +1 -0
- package/dist/cli/subcommands/agents.js +162 -0
- package/dist/cli/subcommands/agents.js.map +1 -0
- package/dist/cli/subcommands/flags.d.ts +144 -0
- package/dist/cli/subcommands/flags.d.ts.map +1 -0
- package/dist/cli/subcommands/flags.js +220 -0
- package/dist/cli/subcommands/flags.js.map +1 -0
- package/dist/cli/subcommands/hooks.d.ts +18 -0
- package/dist/cli/subcommands/hooks.d.ts.map +1 -0
- package/dist/cli/subcommands/hooks.js +80 -0
- package/dist/cli/subcommands/hooks.js.map +1 -0
- package/dist/cli/subcommands/init.d.ts +19 -0
- package/dist/cli/subcommands/init.d.ts.map +1 -0
- package/dist/cli/subcommands/init.js +44 -0
- package/dist/cli/subcommands/init.js.map +1 -0
- package/dist/cli/subcommands/map.d.ts +75 -0
- package/dist/cli/subcommands/map.d.ts.map +1 -0
- package/dist/cli/subcommands/map.js +132 -0
- package/dist/cli/subcommands/map.js.map +1 -0
- package/dist/cli/subcommands/registry.d.ts +81 -0
- package/dist/cli/subcommands/registry.d.ts.map +1 -0
- package/dist/cli/subcommands/registry.js +141 -0
- package/dist/cli/subcommands/registry.js.map +1 -0
- package/dist/cli/subcommands/retrieve.d.ts +74 -0
- package/dist/cli/subcommands/retrieve.d.ts.map +1 -0
- package/dist/cli/subcommands/retrieve.js +84 -0
- package/dist/cli/subcommands/retrieve.js.map +1 -0
- package/dist/cli/subcommands/smelt.d.ts +107 -0
- package/dist/cli/subcommands/smelt.d.ts.map +1 -0
- package/dist/cli/subcommands/smelt.js +304 -0
- package/dist/cli/subcommands/smelt.js.map +1 -0
- package/dist/cli/subcommands/stats.d.ts +43 -0
- package/dist/cli/subcommands/stats.d.ts.map +1 -0
- package/dist/cli/subcommands/stats.js +62 -0
- package/dist/cli/subcommands/stats.js.map +1 -0
- package/dist/cli/subcommands/subcommand.d.ts +113 -0
- package/dist/cli/subcommands/subcommand.d.ts.map +1 -0
- package/dist/cli/subcommands/subcommand.js +3 -0
- package/dist/cli/subcommands/subcommand.js.map +1 -0
- package/dist/cli/usage.d.ts +3 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +95 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/errors.d.ts +21 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -1
- package/dist/errors.js.map +1 -1
- package/dist/harness/aider.d.ts +10 -0
- package/dist/harness/aider.d.ts.map +1 -0
- package/dist/harness/aider.js +21 -0
- package/dist/harness/aider.js.map +1 -0
- package/dist/harness/claude-code.d.ts +3 -0
- package/dist/harness/claude-code.d.ts.map +1 -0
- package/dist/harness/claude-code.js +68 -0
- package/dist/harness/claude-code.js.map +1 -0
- package/dist/harness/cline.d.ts +3 -0
- package/dist/harness/cline.d.ts.map +1 -0
- package/dist/harness/cline.js +54 -0
- package/dist/harness/cline.js.map +1 -0
- package/dist/harness/codex.d.ts +3 -0
- package/dist/harness/codex.d.ts.map +1 -0
- package/dist/harness/codex.js +92 -0
- package/dist/harness/codex.js.map +1 -0
- package/dist/harness/cursor.d.ts +3 -0
- package/dist/harness/cursor.d.ts.map +1 -0
- package/dist/harness/cursor.js +54 -0
- package/dist/harness/cursor.js.map +1 -0
- package/dist/harness/gemini.d.ts +3 -0
- package/dist/harness/gemini.d.ts.map +1 -0
- package/dist/harness/gemini.js +60 -0
- package/dist/harness/gemini.js.map +1 -0
- package/dist/harness/grok.d.ts +3 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +47 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/hermes.d.ts +3 -0
- package/dist/harness/hermes.d.ts.map +1 -0
- package/dist/harness/hermes.js +72 -0
- package/dist/harness/hermes.js.map +1 -0
- package/dist/harness/kilocode.d.ts +3 -0
- package/dist/harness/kilocode.d.ts.map +1 -0
- package/dist/harness/kilocode.js +35 -0
- package/dist/harness/kilocode.js.map +1 -0
- package/dist/harness/opencode.d.ts +3 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/paths.d.ts +17 -0
- package/dist/harness/paths.d.ts.map +1 -0
- package/dist/harness/paths.js +45 -0
- package/dist/harness/paths.js.map +1 -0
- package/dist/harness/profile.d.ts +209 -0
- package/dist/harness/profile.d.ts.map +1 -0
- package/dist/harness/profile.js +41 -0
- package/dist/harness/profile.js.map +1 -0
- package/dist/harness/registry.d.ts +100 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +144 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/snippet.d.ts +19 -0
- package/dist/harness/snippet.d.ts.map +1 -0
- package/dist/harness/snippet.js +41 -0
- package/dist/harness/snippet.js.map +1 -0
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js.map +1 -1
- package/dist/hooks/guard-core.d.ts +14 -14
- package/dist/hooks/guard-core.d.ts.map +1 -1
- package/dist/hooks/guard-core.js +15 -37
- package/dist/hooks/guard-core.js.map +1 -1
- package/dist/hooks/shim.d.ts +95 -4
- package/dist/hooks/shim.d.ts.map +1 -1
- package/dist/hooks/shim.js +87 -2
- package/dist/hooks/shim.js.map +1 -1
- package/dist/hooks/shims/claude-code.d.ts +5 -18
- package/dist/hooks/shims/claude-code.d.ts.map +1 -1
- package/dist/hooks/shims/claude-code.js +9 -56
- package/dist/hooks/shims/claude-code.js.map +1 -1
- package/dist/hooks/shims/cline.d.ts +5 -12
- package/dist/hooks/shims/cline.d.ts.map +1 -1
- package/dist/hooks/shims/cline.js +9 -34
- package/dist/hooks/shims/cline.js.map +1 -1
- package/dist/hooks/shims/codex.d.ts +5 -18
- package/dist/hooks/shims/codex.d.ts.map +1 -1
- package/dist/hooks/shims/codex.js +9 -51
- package/dist/hooks/shims/codex.js.map +1 -1
- package/dist/hooks/shims/cursor.d.ts +5 -14
- package/dist/hooks/shims/cursor.d.ts.map +1 -1
- package/dist/hooks/shims/cursor.js +9 -42
- package/dist/hooks/shims/cursor.js.map +1 -1
- package/dist/hooks/shims/gemini.d.ts +5 -18
- package/dist/hooks/shims/gemini.d.ts.map +1 -1
- package/dist/hooks/shims/gemini.js +9 -48
- package/dist/hooks/shims/gemini.js.map +1 -1
- package/dist/hooks/shims/grok.d.ts +5 -13
- package/dist/hooks/shims/grok.d.ts.map +1 -1
- package/dist/hooks/shims/grok.js +9 -32
- package/dist/hooks/shims/grok.js.map +1 -1
- package/dist/hooks/shims/hermes.d.ts +5 -17
- package/dist/hooks/shims/hermes.d.ts.map +1 -1
- package/dist/hooks/shims/hermes.js +9 -45
- package/dist/hooks/shims/hermes.js.map +1 -1
- package/dist/index.d.ts +43 -76
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -67
- package/dist/index.js.map +1 -1
- package/dist/lang/bash.d.ts.map +1 -1
- package/dist/lang/bash.js.map +1 -1
- package/dist/lang/c.d.ts.map +1 -1
- package/dist/lang/c.js.map +1 -1
- package/dist/lang/c_sharp.d.ts.map +1 -1
- package/dist/lang/c_sharp.js.map +1 -1
- package/dist/lang/cpp.d.ts.map +1 -1
- package/dist/lang/cpp.js.map +1 -1
- package/dist/lang/go.d.ts.map +1 -1
- package/dist/lang/go.js.map +1 -1
- package/dist/lang/java.d.ts.map +1 -1
- package/dist/lang/java.js.map +1 -1
- package/dist/lang/javascript.d.ts.map +1 -1
- package/dist/lang/javascript.js.map +1 -1
- package/dist/lang/kotlin.d.ts.map +1 -1
- package/dist/lang/kotlin.js.map +1 -1
- package/dist/lang/php.d.ts.map +1 -1
- package/dist/lang/php.js.map +1 -1
- package/dist/lang/profile.d.ts.map +1 -1
- package/dist/lang/profile.js.map +1 -1
- package/dist/lang/python.d.ts.map +1 -1
- package/dist/lang/python.js.map +1 -1
- package/dist/lang/registry.d.ts.map +1 -1
- package/dist/lang/registry.js.map +1 -1
- package/dist/lang/ruby.d.ts.map +1 -1
- package/dist/lang/ruby.js.map +1 -1
- package/dist/lang/rust.d.ts.map +1 -1
- package/dist/lang/rust.js.map +1 -1
- package/dist/lang/swift.d.ts.map +1 -1
- package/dist/lang/swift.js.map +1 -1
- package/dist/lang/tsx.d.ts.map +1 -1
- package/dist/lang/tsx.js.map +1 -1
- package/dist/lang/typescript.d.ts.map +1 -1
- package/dist/lang/typescript.js.map +1 -1
- package/dist/net/policy.d.ts +18 -1
- package/dist/net/policy.d.ts.map +1 -1
- package/dist/net/policy.js +6 -4
- package/dist/net/policy.js.map +1 -1
- package/dist/ops/index.d.ts +39 -0
- package/dist/ops/index.d.ts.map +1 -0
- package/dist/ops/index.js +37 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/ops/inputs.d.ts +170 -0
- package/dist/ops/inputs.d.ts.map +1 -0
- package/dist/ops/inputs.js +142 -0
- package/dist/ops/inputs.js.map +1 -0
- package/dist/ops/verbs.d.ts +136 -0
- package/dist/ops/verbs.d.ts.map +1 -0
- package/dist/ops/verbs.js +76 -0
- package/dist/ops/verbs.js.map +1 -0
- package/dist/plan/auto.d.ts +69 -0
- package/dist/plan/auto.d.ts.map +1 -0
- package/dist/plan/auto.js +70 -0
- package/dist/plan/auto.js.map +1 -0
- package/dist/plan/budget.d.ts +21 -0
- package/dist/plan/budget.d.ts.map +1 -0
- package/dist/plan/budget.js +26 -0
- package/dist/plan/budget.js.map +1 -0
- package/dist/plan/grammar.d.ts +14 -0
- package/dist/plan/grammar.d.ts.map +1 -1
- package/dist/plan/grammar.js +47 -6
- package/dist/plan/grammar.js.map +1 -1
- package/dist/plan/lexical.d.ts.map +1 -1
- package/dist/plan/lexical.js +1 -7
- package/dist/plan/lexical.js.map +1 -1
- package/dist/plan/planners.d.ts +35 -2
- package/dist/plan/planners.d.ts.map +1 -1
- package/dist/plan/planners.js +35 -2
- package/dist/plan/planners.js.map +1 -1
- package/dist/plan/structural.d.ts +19 -5
- package/dist/plan/structural.d.ts.map +1 -1
- package/dist/plan/structural.js +180 -33
- package/dist/plan/structural.js.map +1 -1
- package/dist/repomap/cache.d.ts +48 -0
- package/dist/repomap/cache.d.ts.map +1 -1
- package/dist/repomap/cache.js +112 -13
- package/dist/repomap/cache.js.map +1 -1
- package/dist/repomap/io.d.ts +24 -0
- package/dist/repomap/io.d.ts.map +1 -0
- package/dist/repomap/io.js +34 -0
- package/dist/repomap/io.js.map +1 -0
- package/dist/repomap/map.d.ts +69 -6
- package/dist/repomap/map.d.ts.map +1 -1
- package/dist/repomap/map.js +104 -22
- package/dist/repomap/map.js.map +1 -1
- package/dist/repomap/rank.d.ts +26 -2
- package/dist/repomap/rank.d.ts.map +1 -1
- package/dist/repomap/rank.js +20 -0
- package/dist/repomap/rank.js.map +1 -1
- package/dist/repomap/reader.d.ts +73 -0
- package/dist/repomap/reader.d.ts.map +1 -0
- package/dist/repomap/reader.js +30 -0
- package/dist/repomap/reader.js.map +1 -0
- package/dist/repomap/tags.d.ts.map +1 -1
- package/dist/repomap/tags.js.map +1 -1
- package/dist/retrieve.d.ts +8 -0
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +12 -1
- package/dist/retrieve.js.map +1 -1
- package/dist/smelter.d.ts +79 -0
- package/dist/smelter.d.ts.map +1 -0
- package/dist/smelter.js +65 -0
- package/dist/smelter.js.map +1 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +2 -2
- package/dist/stages.js.map +1 -1
- package/dist/stats.d.ts +2 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +2 -1
- package/dist/stats.js.map +1 -1
- package/dist/store-dir.d.ts +43 -4
- package/dist/store-dir.d.ts.map +1 -1
- package/dist/store-dir.js +48 -5
- package/dist/store-dir.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +6 -0
- package/dist/store.js.map +1 -1
- package/dist/text/json-edit.d.ts +71 -0
- package/dist/text/json-edit.d.ts.map +1 -0
- package/dist/text/json-edit.js +219 -0
- package/dist/text/json-edit.js.map +1 -0
- package/dist/types.d.ts +35 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +11 -11
- package/dist/cli/resolve.d.ts +0 -103
- package/dist/cli/resolve.d.ts.map +0 -1
- package/dist/cli/resolve.js +0 -106
- package/dist/cli/resolve.js.map +0 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CliUsageError } from '../../errors.js';
|
|
2
|
+
import { openStore } from '../../ops/inputs.js';
|
|
3
|
+
import { readCounters } from '../../ops/verbs.js';
|
|
4
|
+
import { CLI_NAME, EXIT } from '../shell.js';
|
|
5
|
+
import { resolveStoreRun } from './retrieve.js';
|
|
6
|
+
/**
|
|
7
|
+
* The `smelt stats --json` envelope format. Its own version line for the same reason
|
|
8
|
+
* `smelt map` has one: the two envelopes carry different structures and must move
|
|
9
|
+
* independently.
|
|
10
|
+
*/
|
|
11
|
+
export const CLI_STATS_JSON_FORMAT = 'smelt-stats-cli/v1';
|
|
12
|
+
export const statsCommand = {
|
|
13
|
+
name: 'stats',
|
|
14
|
+
flags: ['json'],
|
|
15
|
+
refusal: `stats reads counters; there is nothing to budget, focus or plan.`,
|
|
16
|
+
usage: {
|
|
17
|
+
synopsis: ['stats [--json]'],
|
|
18
|
+
section: {
|
|
19
|
+
heading: 'RETRIEVE & STATS',
|
|
20
|
+
body: ` ${CLI_NAME} stats prints the same store's counters, one \`name value\` per line —\n` +
|
|
21
|
+
` elisionsStored, bytesStored, retrieveCalls, uniqueRetrieved, expansionRate,\n` +
|
|
22
|
+
` allElisionsRetrieved — and reading them is NOT counted as a retrieval. --json\n` +
|
|
23
|
+
` emits the RetrieveStats verbatim in its own versioned envelope.\n` +
|
|
24
|
+
`\n` +
|
|
25
|
+
` Both need somewhere for elisions to outlive the run that made them: a\n` +
|
|
26
|
+
` smelt.config.json with a directory store (\`${CLI_NAME} init\` writes one). With a\n` +
|
|
27
|
+
` memory store — or no config — every run's store dies with its process, so there\n` +
|
|
28
|
+
` is nothing to retrieve across runs, and that is a usage error rather than a\n` +
|
|
29
|
+
` quiet empty answer.`,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
parse(values, positionals) {
|
|
33
|
+
if (positionals.length > 1) {
|
|
34
|
+
throw new CliUsageError(`${CLI_NAME}: stats takes no further arguments, got ` +
|
|
35
|
+
`${positionals.slice(1).join(', ')}. It reports on the one configured store.`);
|
|
36
|
+
}
|
|
37
|
+
return { mode: 'stats', json: values.json === true };
|
|
38
|
+
},
|
|
39
|
+
resolve(invocation, config) {
|
|
40
|
+
return { store: resolveStoreRun('stats', config()), json: invocation.json };
|
|
41
|
+
},
|
|
42
|
+
run(resolved, io) {
|
|
43
|
+
const store = openStore({ kind: 'directory', path: resolved.store.storePath });
|
|
44
|
+
const stats = readCounters({ store });
|
|
45
|
+
if (resolved.json) {
|
|
46
|
+
const statsEnvelope = { format: CLI_STATS_JSON_FORMAT, stats };
|
|
47
|
+
io.stdout(`${JSON.stringify(statsEnvelope, null, 2)}\n`);
|
|
48
|
+
return EXIT.ok;
|
|
49
|
+
}
|
|
50
|
+
io.stdout([
|
|
51
|
+
`elisionsStored ${String(stats.elisionsStored)}`,
|
|
52
|
+
`bytesStored ${String(stats.bytesStored)}`,
|
|
53
|
+
`retrieveCalls ${String(stats.retrieveCalls)}`,
|
|
54
|
+
`uniqueRetrieved ${String(stats.uniqueRetrieved)}`,
|
|
55
|
+
`expansionRate ${String(stats.expansionRate)}`,
|
|
56
|
+
`allElisionsRetrieved ${String(stats.allElisionsRetrieved)}`,
|
|
57
|
+
'',
|
|
58
|
+
].join('\n'));
|
|
59
|
+
return EXIT.ok;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/cli/subcommands/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAiChD;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAS1D,MAAM,CAAC,MAAM,YAAY,GAAkD;IACzE,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,OAAO,EAAE,kEAAkE;IAC3E,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,gBAAgB,CAAC;QAC5B,OAAO,EAAE;YACP,OAAO,EAAE,kBAAkB;YAC3B,IAAI,EACF,KAAK,QAAQ,0EAA0E;gBACvF,iFAAiF;gBACjF,mFAAmF;gBACnF,qEAAqE;gBACrE,IAAI;gBACJ,2EAA2E;gBAC3E,iDAAiD,QAAQ,+BAA+B;gBACxF,qFAAqF;gBACrF,iFAAiF;gBACjF,uBAAuB;SAC1B;KACF;IAED,KAAK,CAAC,MAAkB,EAAE,WAA8B;QACtD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,aAAa,CACrB,GAAG,QAAQ,0CAA0C;gBACnD,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,2CAA2C,CAChF,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,CAAC,UAA2B,EAAE,MAAoB;QACvD,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC;IAC9E,CAAC;IAED,GAAG,CAAC,QAA0B,EAAE,EAAS;QACvC,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAEtC,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAClB,MAAM,aAAa,GAAyB,EAAE,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC;YACrF,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,CAAC;QAED,EAAE,CAAC,MAAM,CACP;YACE,kBAAkB,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE;YAChD,eAAe,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE;YAC1C,iBAAiB,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;YAC9C,mBAAmB,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE;YAClD,iBAAiB,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;YAC9C,wBAAwB,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE;YAC5D,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;CACF,CAAC","sourcesContent":["import { CliUsageError } from '../../errors.ts';\nimport { openStore } from '../../ops/inputs.ts';\nimport { readCounters } from '../../ops/verbs.ts';\nimport type { RetrieveStats } from '../../types.ts';\nimport { CLI_NAME, EXIT } from '../shell.ts';\nimport type { CliIo } from '../shell.ts';\n\nimport type { FlagValues } from './flags.ts';\nimport { resolveStoreRun } from './retrieve.ts';\nimport type { ResolvedStoreRun } from './retrieve.ts';\nimport type { ConfigSource, Subcommand } from './subcommand.ts';\n\n/**\n * `smelt stats` — the store's counters, without touching them.\n *\n * Reading stats does NOT count as a retrieval: `stats()` folds the journal and scans\n * the blobs, journaling nothing, so watching the expansion rate can never move it —\n * an observer that inflated its own metric would make the honest signal dishonest.\n *\n * The plain form is one `name value` per line, greppable and stable; `--json` emits\n * the {@link RetrieveStats} verbatim in its own versioned envelope\n * ({@link CLI_STATS_JSON_FORMAT}), like every other machine-read surface here.\n *\n * It resolves through `retrieve`'s {@link resolveStoreRun} because the two verbs\n * share one merge — the store leg, and the same refusal when that store cannot\n * outlive a run. Sharing the function is the point; sharing a struct with a hash\n * field `stats` would never read is not.\n */\n\n/** `smelt stats` — the store's counters, read without touching them. */\nexport interface StatsInvocation {\n readonly mode: 'stats';\n readonly json: boolean;\n}\n\n/** What `stats` runs on: the shared store leg, plus how to print it. */\nexport interface ResolvedStatsRun {\n readonly store: ResolvedStoreRun;\n readonly json: boolean;\n}\n\n/**\n * The `smelt stats --json` envelope format. Its own version line for the same reason\n * `smelt map` has one: the two envelopes carry different structures and must move\n * independently.\n */\nexport const CLI_STATS_JSON_FORMAT = 'smelt-stats-cli/v1';\n\n/** What `smelt stats --json` prints: the {@link RetrieveStats} verbatim, versioned. */\nexport interface CliStatsJsonEnvelope {\n readonly format: string;\n /** The {@link RetrieveStats} exactly as the store's `stats()` returned them. */\n readonly stats: RetrieveStats;\n}\n\nexport const statsCommand: Subcommand<StatsInvocation, ResolvedStatsRun> = {\n name: 'stats',\n flags: ['json'],\n refusal: `stats reads counters; there is nothing to budget, focus or plan.`,\n usage: {\n synopsis: ['stats [--json]'],\n section: {\n heading: 'RETRIEVE & STATS',\n body:\n ` ${CLI_NAME} stats prints the same store's counters, one \\`name value\\` per line —\\n` +\n ` elisionsStored, bytesStored, retrieveCalls, uniqueRetrieved, expansionRate,\\n` +\n ` allElisionsRetrieved — and reading them is NOT counted as a retrieval. --json\\n` +\n ` emits the RetrieveStats verbatim in its own versioned envelope.\\n` +\n `\\n` +\n ` Both need somewhere for elisions to outlive the run that made them: a\\n` +\n ` smelt.config.json with a directory store (\\`${CLI_NAME} init\\` writes one). With a\\n` +\n ` memory store — or no config — every run's store dies with its process, so there\\n` +\n ` is nothing to retrieve across runs, and that is a usage error rather than a\\n` +\n ` quiet empty answer.`,\n },\n },\n\n parse(values: FlagValues, positionals: readonly string[]): StatsInvocation {\n if (positionals.length > 1) {\n throw new CliUsageError(\n `${CLI_NAME}: stats takes no further arguments, got ` +\n `${positionals.slice(1).join(', ')}. It reports on the one configured store.`,\n );\n }\n return { mode: 'stats', json: values.json === true };\n },\n\n resolve(invocation: StatsInvocation, config: ConfigSource): ResolvedStatsRun {\n return { store: resolveStoreRun('stats', config()), json: invocation.json };\n },\n\n run(resolved: ResolvedStatsRun, io: CliIo): number {\n const store = openStore({ kind: 'directory', path: resolved.store.storePath });\n const stats = readCounters({ store });\n\n if (resolved.json) {\n const statsEnvelope: CliStatsJsonEnvelope = { format: CLI_STATS_JSON_FORMAT, stats };\n io.stdout(`${JSON.stringify(statsEnvelope, null, 2)}\\n`);\n return EXIT.ok;\n }\n\n io.stdout(\n [\n `elisionsStored ${String(stats.elisionsStored)}`,\n `bytesStored ${String(stats.bytesStored)}`,\n `retrieveCalls ${String(stats.retrieveCalls)}`,\n `uniqueRetrieved ${String(stats.uniqueRetrieved)}`,\n `expansionRate ${String(stats.expansionRate)}`,\n `allElisionsRetrieved ${String(stats.allElisionsRetrieved)}`,\n '',\n ].join('\\n'),\n );\n return EXIT.ok;\n },\n};\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { LoadedConfig } from '../config.ts';
|
|
2
|
+
import type { CliIo } from '../shell.ts';
|
|
3
|
+
import type { FlagValues, VerbFlag } from './flags.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The verbs `smelt` answers to. `'smelt'` is the default verb — the bare
|
|
6
|
+
* `smelt <file>` / `smelt < input` run — and is the only one with no word on the
|
|
7
|
+
* command line, which is why a file literally named `map` needs `./map`.
|
|
8
|
+
*
|
|
9
|
+
* Order is meaningful: it is the order `--help` renders every derived list in, and the
|
|
10
|
+
* order a refusal names owners in. Keep it stable and append new verbs at the end.
|
|
11
|
+
*/
|
|
12
|
+
export type Verb = 'smelt' | 'init' | 'map' | 'retrieve' | 'stats' | 'hooks' | 'agents';
|
|
13
|
+
/** The verb a bare invocation selects — a file or stdin, with no subcommand word. */
|
|
14
|
+
export declare const DEFAULT_VERB = "smelt";
|
|
15
|
+
/**
|
|
16
|
+
* The nearest `smelt.config.json`, loaded on demand.
|
|
17
|
+
*
|
|
18
|
+
* A thunk rather than a value, because two verbs must **not** load it: `init` and
|
|
19
|
+
* `hooks` read the config file themselves, with their own tolerance, and a wizard you
|
|
20
|
+
* run to *fix* a malformed config cannot be a wizard that refuses to start because the
|
|
21
|
+
* config is malformed. Loading eagerly in the dispatcher would quietly turn that into
|
|
22
|
+
* a usage error.
|
|
23
|
+
*/
|
|
24
|
+
export type ConfigSource = () => LoadedConfig | undefined;
|
|
25
|
+
/** What one verb contributes to `--help`. See `cli/usage.ts` for the rendering. */
|
|
26
|
+
export interface SubcommandUsage {
|
|
27
|
+
/**
|
|
28
|
+
* The command's everyday forms, rendered in the first block of USAGE in registry
|
|
29
|
+
* order. Each entry is the text *after* the CLI name.
|
|
30
|
+
*/
|
|
31
|
+
readonly synopsis: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* Forms rendered in the second block, after every command's everyday forms: the
|
|
34
|
+
* round trip (`--reconstruct`) and the one-time setup (`init`) — jobs you do
|
|
35
|
+
* occasionally rather than the shape of a normal run. It keeps USAGE reading
|
|
36
|
+
* top-to-bottom by how often you type a thing without anyone arranging the list.
|
|
37
|
+
*/
|
|
38
|
+
readonly occasional?: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* The verb's own section of the help — a heading and its body, already indented.
|
|
41
|
+
* Two verbs may declare the same heading (`retrieve` and `stats` share RETRIEVE &
|
|
42
|
+
* STATS, because the loop is one story); their bodies are joined under it in
|
|
43
|
+
* registry order.
|
|
44
|
+
*/
|
|
45
|
+
readonly section?: {
|
|
46
|
+
readonly heading: string;
|
|
47
|
+
readonly body: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One subcommand: everything the CLI knows about one verb, in one place.
|
|
52
|
+
*
|
|
53
|
+
* This is the seam the CLI was missing. A verb used to be a *shape* restated in four
|
|
54
|
+
* modules — an `*Invocation` interface and a `parse*Args` in `cli/args.ts`, a
|
|
55
|
+
* `Resolved*Run` and a `resolve*` in `cli/resolve.ts`, a `case` and a `run*` in
|
|
56
|
+
* `cli/run.ts`, plus a hand-written help block and a hand-written refusal for every
|
|
57
|
+
* flag it did not want. The refusals were the compounding cost: because no verb owned
|
|
58
|
+
* its flags, every verb refused every other verb's flags by hand, so an eleventh flag
|
|
59
|
+
* edited five messages.
|
|
60
|
+
*
|
|
61
|
+
* Now a verb is one file exporting one of these, and `SUBCOMMANDS` in `./registry.ts`
|
|
62
|
+
* is `Record<Verb, …>` — the {@link LANGUAGE_PROFILES} / {@link HARNESS_PROFILES}
|
|
63
|
+
* pattern, so totality is a compile error and every rendered list (USAGE, the sections,
|
|
64
|
+
* the `map only.` prefixes in OPTIONS) is a derived view.
|
|
65
|
+
*
|
|
66
|
+
* The type parameters are the verb's own two shapes, and they never merge:
|
|
67
|
+
*
|
|
68
|
+
* @typeParam I - what the verb's `parse` returns, e.g. `MapInvocation`. Pure data, so
|
|
69
|
+
* the parse is testable on its own.
|
|
70
|
+
* @typeParam R - what its `resolve` returns, e.g. `ResolvedMapRun`. The verb's single
|
|
71
|
+
* merge of flags + config + built-ins, each value carrying its provenance. Deliberately
|
|
72
|
+
* one type per verb: `map` has no store, no strategy and no stdin, and a shared struct
|
|
73
|
+
* whose fields are lies for half its users is not a seam, it is a coincidence.
|
|
74
|
+
*/
|
|
75
|
+
export interface Subcommand<I, R> {
|
|
76
|
+
/** The word on the command line — and the registry key. `'smelt'` has no word. */
|
|
77
|
+
readonly name: Verb;
|
|
78
|
+
/**
|
|
79
|
+
* The flags this verb owns. **The whole point.** Every flag outside this list is
|
|
80
|
+
* refused by one generated message (`refuseForeignFlags` in `./registry.ts`) rather
|
|
81
|
+
* than by prose the next verb has to write again. A flag silently ignored would be a
|
|
82
|
+
* setting the user believed was in force.
|
|
83
|
+
*/
|
|
84
|
+
readonly flags: readonly VerbFlag[];
|
|
85
|
+
/**
|
|
86
|
+
* What this verb *is*, as one sentence — the tail of the generated refusal, and the
|
|
87
|
+
* only part of it a verb writes. It answers "why not here?", which is the half of
|
|
88
|
+
* the old hand-written messages worth keeping; the offending flag, this verb's name
|
|
89
|
+
* and the verb the flag does belong to are all derived from the registry.
|
|
90
|
+
*/
|
|
91
|
+
readonly refusal: string;
|
|
92
|
+
/** What this verb contributes to `--help`. Rendered, never hand-arranged. */
|
|
93
|
+
readonly usage: SubcommandUsage;
|
|
94
|
+
/**
|
|
95
|
+
* The verb's own validation, over the flags it owns and the positionals it was
|
|
96
|
+
* given (index 0 is the verb word itself, except for the default verb).
|
|
97
|
+
*
|
|
98
|
+
* @throws {CliUsageError} on anything the user got wrong. Never guesses.
|
|
99
|
+
*/
|
|
100
|
+
parse(values: FlagValues, positionals: readonly string[]): I;
|
|
101
|
+
/**
|
|
102
|
+
* The verb's single merge of flags + config + built-ins. Precedence for this verb
|
|
103
|
+
* lives here and nowhere else, so a precedence question is answered by one function
|
|
104
|
+
* instead of by reading two files.
|
|
105
|
+
*
|
|
106
|
+
* @throws {CliUsageError} when a required value has no source — the budget, for the
|
|
107
|
+
* two verbs that need one.
|
|
108
|
+
*/
|
|
109
|
+
resolve(invocation: I, config: ConfigSource): R;
|
|
110
|
+
/** Execute the resolved run straight-line, and return the exit code. */
|
|
111
|
+
run(resolved: R, io: CliIo): number | Promise<number>;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=subcommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subcommand.d.ts","sourceRoot":"","sources":["../../../src/cli/subcommands/subcommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExF,qFAAqF;AACrF,eAAO,MAAM,YAAY,UAAyB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,YAAY,GAAG,SAAS,CAAC;AAE1D,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,EAAE,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7D;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC;IAChD,wEAAwE;IACxE,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvD","sourcesContent":["import type { LoadedConfig } from '../config.ts';\nimport type { CliIo } from '../shell.ts';\n\nimport type { FlagValues, VerbFlag } from './flags.ts';\n\n/**\n * The verbs `smelt` answers to. `'smelt'` is the default verb — the bare\n * `smelt <file>` / `smelt < input` run — and is the only one with no word on the\n * command line, which is why a file literally named `map` needs `./map`.\n *\n * Order is meaningful: it is the order `--help` renders every derived list in, and the\n * order a refusal names owners in. Keep it stable and append new verbs at the end.\n */\nexport type Verb = 'smelt' | 'init' | 'map' | 'retrieve' | 'stats' | 'hooks' | 'agents';\n\n/** The verb a bare invocation selects — a file or stdin, with no subcommand word. */\nexport const DEFAULT_VERB = 'smelt' satisfies Verb;\n\n/**\n * The nearest `smelt.config.json`, loaded on demand.\n *\n * A thunk rather than a value, because two verbs must **not** load it: `init` and\n * `hooks` read the config file themselves, with their own tolerance, and a wizard you\n * run to *fix* a malformed config cannot be a wizard that refuses to start because the\n * config is malformed. Loading eagerly in the dispatcher would quietly turn that into\n * a usage error.\n */\nexport type ConfigSource = () => LoadedConfig | undefined;\n\n/** What one verb contributes to `--help`. See `cli/usage.ts` for the rendering. */\nexport interface SubcommandUsage {\n /**\n * The command's everyday forms, rendered in the first block of USAGE in registry\n * order. Each entry is the text *after* the CLI name.\n */\n readonly synopsis: readonly string[];\n /**\n * Forms rendered in the second block, after every command's everyday forms: the\n * round trip (`--reconstruct`) and the one-time setup (`init`) — jobs you do\n * occasionally rather than the shape of a normal run. It keeps USAGE reading\n * top-to-bottom by how often you type a thing without anyone arranging the list.\n */\n readonly occasional?: readonly string[];\n /**\n * The verb's own section of the help — a heading and its body, already indented.\n * Two verbs may declare the same heading (`retrieve` and `stats` share RETRIEVE &\n * STATS, because the loop is one story); their bodies are joined under it in\n * registry order.\n */\n readonly section?: { readonly heading: string; readonly body: string };\n}\n\n/**\n * One subcommand: everything the CLI knows about one verb, in one place.\n *\n * This is the seam the CLI was missing. A verb used to be a *shape* restated in four\n * modules — an `*Invocation` interface and a `parse*Args` in `cli/args.ts`, a\n * `Resolved*Run` and a `resolve*` in `cli/resolve.ts`, a `case` and a `run*` in\n * `cli/run.ts`, plus a hand-written help block and a hand-written refusal for every\n * flag it did not want. The refusals were the compounding cost: because no verb owned\n * its flags, every verb refused every other verb's flags by hand, so an eleventh flag\n * edited five messages.\n *\n * Now a verb is one file exporting one of these, and `SUBCOMMANDS` in `./registry.ts`\n * is `Record<Verb, …>` — the {@link LANGUAGE_PROFILES} / {@link HARNESS_PROFILES}\n * pattern, so totality is a compile error and every rendered list (USAGE, the sections,\n * the `map only.` prefixes in OPTIONS) is a derived view.\n *\n * The type parameters are the verb's own two shapes, and they never merge:\n *\n * @typeParam I - what the verb's `parse` returns, e.g. `MapInvocation`. Pure data, so\n * the parse is testable on its own.\n * @typeParam R - what its `resolve` returns, e.g. `ResolvedMapRun`. The verb's single\n * merge of flags + config + built-ins, each value carrying its provenance. Deliberately\n * one type per verb: `map` has no store, no strategy and no stdin, and a shared struct\n * whose fields are lies for half its users is not a seam, it is a coincidence.\n */\nexport interface Subcommand<I, R> {\n /** The word on the command line — and the registry key. `'smelt'` has no word. */\n readonly name: Verb;\n /**\n * The flags this verb owns. **The whole point.** Every flag outside this list is\n * refused by one generated message (`refuseForeignFlags` in `./registry.ts`) rather\n * than by prose the next verb has to write again. A flag silently ignored would be a\n * setting the user believed was in force.\n */\n readonly flags: readonly VerbFlag[];\n /**\n * What this verb *is*, as one sentence — the tail of the generated refusal, and the\n * only part of it a verb writes. It answers \"why not here?\", which is the half of\n * the old hand-written messages worth keeping; the offending flag, this verb's name\n * and the verb the flag does belong to are all derived from the registry.\n */\n readonly refusal: string;\n /** What this verb contributes to `--help`. Rendered, never hand-arranged. */\n readonly usage: SubcommandUsage;\n /**\n * The verb's own validation, over the flags it owns and the positionals it was\n * given (index 0 is the verb word itself, except for the default verb).\n *\n * @throws {CliUsageError} on anything the user got wrong. Never guesses.\n */\n parse(values: FlagValues, positionals: readonly string[]): I;\n /**\n * The verb's single merge of flags + config + built-ins. Precedence for this verb\n * lives here and nowhere else, so a precedence question is answered by one function\n * instead of by reading two files.\n *\n * @throws {CliUsageError} when a required value has no source — the budget, for the\n * two verbs that need one.\n */\n resolve(invocation: I, config: ConfigSource): R;\n /** Execute the resolved run straight-line, and return the exit code. */\n run(resolved: R, io: CliIo): number | Promise<number>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subcommand.js","sourceRoot":"","sources":["../../../src/cli/subcommands/subcommand.ts"],"names":[],"mappings":"AAeA,qFAAqF;AACrF,MAAM,CAAC,MAAM,YAAY,GAAG,OAAsB,CAAC","sourcesContent":["import type { LoadedConfig } from '../config.ts';\nimport type { CliIo } from '../shell.ts';\n\nimport type { FlagValues, VerbFlag } from './flags.ts';\n\n/**\n * The verbs `smelt` answers to. `'smelt'` is the default verb — the bare\n * `smelt <file>` / `smelt < input` run — and is the only one with no word on the\n * command line, which is why a file literally named `map` needs `./map`.\n *\n * Order is meaningful: it is the order `--help` renders every derived list in, and the\n * order a refusal names owners in. Keep it stable and append new verbs at the end.\n */\nexport type Verb = 'smelt' | 'init' | 'map' | 'retrieve' | 'stats' | 'hooks' | 'agents';\n\n/** The verb a bare invocation selects — a file or stdin, with no subcommand word. */\nexport const DEFAULT_VERB = 'smelt' satisfies Verb;\n\n/**\n * The nearest `smelt.config.json`, loaded on demand.\n *\n * A thunk rather than a value, because two verbs must **not** load it: `init` and\n * `hooks` read the config file themselves, with their own tolerance, and a wizard you\n * run to *fix* a malformed config cannot be a wizard that refuses to start because the\n * config is malformed. Loading eagerly in the dispatcher would quietly turn that into\n * a usage error.\n */\nexport type ConfigSource = () => LoadedConfig | undefined;\n\n/** What one verb contributes to `--help`. See `cli/usage.ts` for the rendering. */\nexport interface SubcommandUsage {\n /**\n * The command's everyday forms, rendered in the first block of USAGE in registry\n * order. Each entry is the text *after* the CLI name.\n */\n readonly synopsis: readonly string[];\n /**\n * Forms rendered in the second block, after every command's everyday forms: the\n * round trip (`--reconstruct`) and the one-time setup (`init`) — jobs you do\n * occasionally rather than the shape of a normal run. It keeps USAGE reading\n * top-to-bottom by how often you type a thing without anyone arranging the list.\n */\n readonly occasional?: readonly string[];\n /**\n * The verb's own section of the help — a heading and its body, already indented.\n * Two verbs may declare the same heading (`retrieve` and `stats` share RETRIEVE &\n * STATS, because the loop is one story); their bodies are joined under it in\n * registry order.\n */\n readonly section?: { readonly heading: string; readonly body: string };\n}\n\n/**\n * One subcommand: everything the CLI knows about one verb, in one place.\n *\n * This is the seam the CLI was missing. A verb used to be a *shape* restated in four\n * modules — an `*Invocation` interface and a `parse*Args` in `cli/args.ts`, a\n * `Resolved*Run` and a `resolve*` in `cli/resolve.ts`, a `case` and a `run*` in\n * `cli/run.ts`, plus a hand-written help block and a hand-written refusal for every\n * flag it did not want. The refusals were the compounding cost: because no verb owned\n * its flags, every verb refused every other verb's flags by hand, so an eleventh flag\n * edited five messages.\n *\n * Now a verb is one file exporting one of these, and `SUBCOMMANDS` in `./registry.ts`\n * is `Record<Verb, …>` — the {@link LANGUAGE_PROFILES} / {@link HARNESS_PROFILES}\n * pattern, so totality is a compile error and every rendered list (USAGE, the sections,\n * the `map only.` prefixes in OPTIONS) is a derived view.\n *\n * The type parameters are the verb's own two shapes, and they never merge:\n *\n * @typeParam I - what the verb's `parse` returns, e.g. `MapInvocation`. Pure data, so\n * the parse is testable on its own.\n * @typeParam R - what its `resolve` returns, e.g. `ResolvedMapRun`. The verb's single\n * merge of flags + config + built-ins, each value carrying its provenance. Deliberately\n * one type per verb: `map` has no store, no strategy and no stdin, and a shared struct\n * whose fields are lies for half its users is not a seam, it is a coincidence.\n */\nexport interface Subcommand<I, R> {\n /** The word on the command line — and the registry key. `'smelt'` has no word. */\n readonly name: Verb;\n /**\n * The flags this verb owns. **The whole point.** Every flag outside this list is\n * refused by one generated message (`refuseForeignFlags` in `./registry.ts`) rather\n * than by prose the next verb has to write again. A flag silently ignored would be a\n * setting the user believed was in force.\n */\n readonly flags: readonly VerbFlag[];\n /**\n * What this verb *is*, as one sentence — the tail of the generated refusal, and the\n * only part of it a verb writes. It answers \"why not here?\", which is the half of\n * the old hand-written messages worth keeping; the offending flag, this verb's name\n * and the verb the flag does belong to are all derived from the registry.\n */\n readonly refusal: string;\n /** What this verb contributes to `--help`. Rendered, never hand-arranged. */\n readonly usage: SubcommandUsage;\n /**\n * The verb's own validation, over the flags it owns and the positionals it was\n * given (index 0 is the verb word itself, except for the default verb).\n *\n * @throws {CliUsageError} on anything the user got wrong. Never guesses.\n */\n parse(values: FlagValues, positionals: readonly string[]): I;\n /**\n * The verb's single merge of flags + config + built-ins. Precedence for this verb\n * lives here and nowhere else, so a precedence question is answered by one function\n * instead of by reading two files.\n *\n * @throws {CliUsageError} when a required value has no source — the budget, for the\n * two verbs that need one.\n */\n resolve(invocation: I, config: ConfigSource): R;\n /** Execute the resolved run straight-line, and return the exit code. */\n run(resolved: R, io: CliIo): number | Promise<number>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AA2EA,0EAA0E;AAC1E,wBAAgB,QAAQ,IAAI,MAAM,CAsBjC","sourcesContent":["import { CLI_NAME } from './shell.ts';\nimport { CLI_FLAGS, FLAG_HELP } from './subcommands/flags.ts';\nimport type { FlagName } from './subcommands/flags.ts';\nimport { ownersOf, SUBCOMMAND_LIST } from './subcommands/registry.ts';\nimport { DEFAULT_VERB } from './subcommands/subcommand.ts';\n\n/**\n * The help text — rendered from the registries, never hand-arranged.\n *\n * `--strategy` has rendered `STRATEGIES` and `--harness` `HARNESS_PROFILES` for a\n * while; this module finishes the job for the rest of the page. Every subcommand's\n * USAGE line, its section, and the `map only.` / `hooks only.` prefix on the flags it\n * owns come from `SUBCOMMANDS` and `CLI_FLAGS`, so a seventh verb or an eleventh flag\n * reaches the help by existing. The help text is also the closest thing the CLI has to\n * documentation, which is exactly why it must not be able to fall behind the parser.\n *\n * `test/__snapshots__/cli-usage.help.txt` pins the rendered bytes: a help change is a\n * reviewable diff, not a thing that happens.\n */\n\n/** The column an OPTIONS entry's description starts at. */\nconst OPTION_INDENT = ' '.repeat(23);\n\n/** The USAGE block: every command's everyday forms, then the occasional ones. */\nfunction renderSynopsis(): string {\n const everyday = SUBCOMMAND_LIST.flatMap((command) => command.usage.synopsis);\n const occasional = SUBCOMMAND_LIST.flatMap((command) => command.usage.occasional ?? []);\n return [...everyday, ...occasional].map((form) => ` ${CLI_NAME} ${form}`).join('\\n');\n}\n\n/**\n * The named sections, in registry order. Two verbs may declare the same heading —\n * `retrieve` and `stats` share RETRIEVE & STATS, because the loop is one story — and\n * their bodies are joined under the single heading rather than repeating it.\n */\nfunction renderSections(): string {\n const sections = new Map<string, string[]>();\n for (const command of SUBCOMMAND_LIST) {\n const section = command.usage.section;\n if (section === undefined) continue;\n const bodies = sections.get(section.heading);\n if (bodies === undefined) sections.set(section.heading, [section.body]);\n else bodies.push(section.body);\n }\n return [...sections]\n .map(([heading, bodies]) => `${heading}\\n${bodies.join('\\n\\n')}`)\n .join('\\n\\n');\n}\n\n/**\n * The OPTIONS block. The description is the flag's own; the ownership sentence in\n * front of it is generated — a flag exactly one *named* verb owns reads `map only.`\n * or `hooks only.`, and one the default verb or several verbs share reads nothing,\n * because \"which verb owns this flag\" is a fact the registry already holds.\n */\nfunction renderOptions(): string {\n return (Object.keys(CLI_FLAGS) as FlagName[])\n .map((name) => {\n const help = FLAG_HELP[name];\n const [first, ...rest] = [`${ownedBy(name)}${help.body()[0] ?? ''}`, ...help.body().slice(1)];\n return [\n ` ${help.label.padEnd(21)}${first ?? ''}`,\n ...rest.map((line) => `${OPTION_INDENT}${line}`),\n ].join('\\n');\n })\n .join('\\n');\n}\n\n/** `map only. ` for a flag one named verb owns alone; nothing for anything else. */\nfunction ownedBy(name: FlagName): string {\n const owners = ownersOf(name);\n const only = owners.length === 1 ? owners[0] : undefined;\n return only === undefined || only.name === DEFAULT_VERB ? '' : `${only.name} only. `;\n}\n\n/** The help text. Also the closest thing the CLI has to documentation. */\nexport function cliUsage(): string {\n return `${CLI_NAME} — shrink text for a model, without lying about what was removed.\n\nUSAGE\n${renderSynopsis()}\n\nSmelted text goes to stdout and the report goes to stderr, so the two can be piped\napart: ${CLI_NAME} big.log --budget 4000 > small.log\n\n${renderSections()}\n\nOPTIONS\n${renderOptions()}\n\nEXIT CODES\n 0 under budget (map is always under budget by construction)\n 1 over budget — the plan did not fit, and the report says so. Never silent.\n map never exits 1; see MAP above.\n 2 usage error\n 3 ${CLI_NAME} refused (a SmeltError: an unbuilt planner, an unknown hash, a corrupt store)\n 4 unexpected internal error\n`;\n}\n"]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { CLI_NAME } from './shell.js';
|
|
2
|
+
import { CLI_FLAGS, FLAG_HELP } from './subcommands/flags.js';
|
|
3
|
+
import { ownersOf, SUBCOMMAND_LIST } from './subcommands/registry.js';
|
|
4
|
+
import { DEFAULT_VERB } from './subcommands/subcommand.js';
|
|
5
|
+
/**
|
|
6
|
+
* The help text — rendered from the registries, never hand-arranged.
|
|
7
|
+
*
|
|
8
|
+
* `--strategy` has rendered `STRATEGIES` and `--harness` `HARNESS_PROFILES` for a
|
|
9
|
+
* while; this module finishes the job for the rest of the page. Every subcommand's
|
|
10
|
+
* USAGE line, its section, and the `map only.` / `hooks only.` prefix on the flags it
|
|
11
|
+
* owns come from `SUBCOMMANDS` and `CLI_FLAGS`, so a seventh verb or an eleventh flag
|
|
12
|
+
* reaches the help by existing. The help text is also the closest thing the CLI has to
|
|
13
|
+
* documentation, which is exactly why it must not be able to fall behind the parser.
|
|
14
|
+
*
|
|
15
|
+
* `test/__snapshots__/cli-usage.help.txt` pins the rendered bytes: a help change is a
|
|
16
|
+
* reviewable diff, not a thing that happens.
|
|
17
|
+
*/
|
|
18
|
+
/** The column an OPTIONS entry's description starts at. */
|
|
19
|
+
const OPTION_INDENT = ' '.repeat(23);
|
|
20
|
+
/** The USAGE block: every command's everyday forms, then the occasional ones. */
|
|
21
|
+
function renderSynopsis() {
|
|
22
|
+
const everyday = SUBCOMMAND_LIST.flatMap((command) => command.usage.synopsis);
|
|
23
|
+
const occasional = SUBCOMMAND_LIST.flatMap((command) => command.usage.occasional ?? []);
|
|
24
|
+
return [...everyday, ...occasional].map((form) => ` ${CLI_NAME} ${form}`).join('\n');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The named sections, in registry order. Two verbs may declare the same heading —
|
|
28
|
+
* `retrieve` and `stats` share RETRIEVE & STATS, because the loop is one story — and
|
|
29
|
+
* their bodies are joined under the single heading rather than repeating it.
|
|
30
|
+
*/
|
|
31
|
+
function renderSections() {
|
|
32
|
+
const sections = new Map();
|
|
33
|
+
for (const command of SUBCOMMAND_LIST) {
|
|
34
|
+
const section = command.usage.section;
|
|
35
|
+
if (section === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
const bodies = sections.get(section.heading);
|
|
38
|
+
if (bodies === undefined)
|
|
39
|
+
sections.set(section.heading, [section.body]);
|
|
40
|
+
else
|
|
41
|
+
bodies.push(section.body);
|
|
42
|
+
}
|
|
43
|
+
return [...sections]
|
|
44
|
+
.map(([heading, bodies]) => `${heading}\n${bodies.join('\n\n')}`)
|
|
45
|
+
.join('\n\n');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The OPTIONS block. The description is the flag's own; the ownership sentence in
|
|
49
|
+
* front of it is generated — a flag exactly one *named* verb owns reads `map only.`
|
|
50
|
+
* or `hooks only.`, and one the default verb or several verbs share reads nothing,
|
|
51
|
+
* because "which verb owns this flag" is a fact the registry already holds.
|
|
52
|
+
*/
|
|
53
|
+
function renderOptions() {
|
|
54
|
+
return Object.keys(CLI_FLAGS)
|
|
55
|
+
.map((name) => {
|
|
56
|
+
const help = FLAG_HELP[name];
|
|
57
|
+
const [first, ...rest] = [`${ownedBy(name)}${help.body()[0] ?? ''}`, ...help.body().slice(1)];
|
|
58
|
+
return [
|
|
59
|
+
` ${help.label.padEnd(21)}${first ?? ''}`,
|
|
60
|
+
...rest.map((line) => `${OPTION_INDENT}${line}`),
|
|
61
|
+
].join('\n');
|
|
62
|
+
})
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
|
65
|
+
/** `map only. ` for a flag one named verb owns alone; nothing for anything else. */
|
|
66
|
+
function ownedBy(name) {
|
|
67
|
+
const owners = ownersOf(name);
|
|
68
|
+
const only = owners.length === 1 ? owners[0] : undefined;
|
|
69
|
+
return only === undefined || only.name === DEFAULT_VERB ? '' : `${only.name} only. `;
|
|
70
|
+
}
|
|
71
|
+
/** The help text. Also the closest thing the CLI has to documentation. */
|
|
72
|
+
export function cliUsage() {
|
|
73
|
+
return `${CLI_NAME} — shrink text for a model, without lying about what was removed.
|
|
74
|
+
|
|
75
|
+
USAGE
|
|
76
|
+
${renderSynopsis()}
|
|
77
|
+
|
|
78
|
+
Smelted text goes to stdout and the report goes to stderr, so the two can be piped
|
|
79
|
+
apart: ${CLI_NAME} big.log --budget 4000 > small.log
|
|
80
|
+
|
|
81
|
+
${renderSections()}
|
|
82
|
+
|
|
83
|
+
OPTIONS
|
|
84
|
+
${renderOptions()}
|
|
85
|
+
|
|
86
|
+
EXIT CODES
|
|
87
|
+
0 under budget (map is always under budget by construction)
|
|
88
|
+
1 over budget — the plan did not fit, and the report says so. Never silent.
|
|
89
|
+
map never exits 1; see MAP above.
|
|
90
|
+
2 usage error
|
|
91
|
+
3 ${CLI_NAME} refused (a SmeltError: an unbuilt planner, an unknown hash, a corrupt store)
|
|
92
|
+
4 unexpected internal error
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"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","sourcesContent":["import { CLI_NAME } from './shell.ts';\nimport { CLI_FLAGS, FLAG_HELP } from './subcommands/flags.ts';\nimport type { FlagName } from './subcommands/flags.ts';\nimport { ownersOf, SUBCOMMAND_LIST } from './subcommands/registry.ts';\nimport { DEFAULT_VERB } from './subcommands/subcommand.ts';\n\n/**\n * The help text — rendered from the registries, never hand-arranged.\n *\n * `--strategy` has rendered `STRATEGIES` and `--harness` `HARNESS_PROFILES` for a\n * while; this module finishes the job for the rest of the page. Every subcommand's\n * USAGE line, its section, and the `map only.` / `hooks only.` prefix on the flags it\n * owns come from `SUBCOMMANDS` and `CLI_FLAGS`, so a seventh verb or an eleventh flag\n * reaches the help by existing. The help text is also the closest thing the CLI has to\n * documentation, which is exactly why it must not be able to fall behind the parser.\n *\n * `test/__snapshots__/cli-usage.help.txt` pins the rendered bytes: a help change is a\n * reviewable diff, not a thing that happens.\n */\n\n/** The column an OPTIONS entry's description starts at. */\nconst OPTION_INDENT = ' '.repeat(23);\n\n/** The USAGE block: every command's everyday forms, then the occasional ones. */\nfunction renderSynopsis(): string {\n const everyday = SUBCOMMAND_LIST.flatMap((command) => command.usage.synopsis);\n const occasional = SUBCOMMAND_LIST.flatMap((command) => command.usage.occasional ?? []);\n return [...everyday, ...occasional].map((form) => ` ${CLI_NAME} ${form}`).join('\\n');\n}\n\n/**\n * The named sections, in registry order. Two verbs may declare the same heading —\n * `retrieve` and `stats` share RETRIEVE & STATS, because the loop is one story — and\n * their bodies are joined under the single heading rather than repeating it.\n */\nfunction renderSections(): string {\n const sections = new Map<string, string[]>();\n for (const command of SUBCOMMAND_LIST) {\n const section = command.usage.section;\n if (section === undefined) continue;\n const bodies = sections.get(section.heading);\n if (bodies === undefined) sections.set(section.heading, [section.body]);\n else bodies.push(section.body);\n }\n return [...sections]\n .map(([heading, bodies]) => `${heading}\\n${bodies.join('\\n\\n')}`)\n .join('\\n\\n');\n}\n\n/**\n * The OPTIONS block. The description is the flag's own; the ownership sentence in\n * front of it is generated — a flag exactly one *named* verb owns reads `map only.`\n * or `hooks only.`, and one the default verb or several verbs share reads nothing,\n * because \"which verb owns this flag\" is a fact the registry already holds.\n */\nfunction renderOptions(): string {\n return (Object.keys(CLI_FLAGS) as FlagName[])\n .map((name) => {\n const help = FLAG_HELP[name];\n const [first, ...rest] = [`${ownedBy(name)}${help.body()[0] ?? ''}`, ...help.body().slice(1)];\n return [\n ` ${help.label.padEnd(21)}${first ?? ''}`,\n ...rest.map((line) => `${OPTION_INDENT}${line}`),\n ].join('\\n');\n })\n .join('\\n');\n}\n\n/** `map only. ` for a flag one named verb owns alone; nothing for anything else. */\nfunction ownedBy(name: FlagName): string {\n const owners = ownersOf(name);\n const only = owners.length === 1 ? owners[0] : undefined;\n return only === undefined || only.name === DEFAULT_VERB ? '' : `${only.name} only. `;\n}\n\n/** The help text. Also the closest thing the CLI has to documentation. */\nexport function cliUsage(): string {\n return `${CLI_NAME} — shrink text for a model, without lying about what was removed.\n\nUSAGE\n${renderSynopsis()}\n\nSmelted text goes to stdout and the report goes to stderr, so the two can be piped\napart: ${CLI_NAME} big.log --budget 4000 > small.log\n\n${renderSections()}\n\nOPTIONS\n${renderOptions()}\n\nEXIT CODES\n 0 under budget (map is always under budget by construction)\n 1 over budget — the plan did not fit, and the report says so. Never silent.\n map never exits 1; see MAP above.\n 2 usage error\n 3 ${CLI_NAME} refused (a SmeltError: an unbuilt planner, an unknown hash, a corrupt store)\n 4 unexpected internal error\n`;\n}\n"]}
|
package/dist/detect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,UAAU,EAEpD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAEzE"}
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,UAAU,EAEpD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAEzE","sourcesContent":["import { LANGUAGE_PROFILES, profileForPath } from './lang/registry.ts';\nimport type { DetectedLanguage, LanguageId } from './types.ts';\n\n/**\n * Every language smelt has a grammar mapping for — the registry's ids, in registry\n * order. A derived view of `LANGUAGE_PROFILES`, kept as an export because tests and\n * the CLI's rendered lists read it.\n */\nexport const SUPPORTED_LANGUAGES: readonly LanguageId[] = Object.values(LANGUAGE_PROFILES).map(\n (profile) => profile.id,\n);\n\n/**\n * Detect the language of a path, from its extension. Returns `'unknown'` for anything\n * unmapped — which is a normal outcome, not an error: it selects the lexical planner,\n * which works on anything. The extension → profile mapping itself lives on each\n * language's `LanguageProfile` (`src/lang/`); this is the id-shaped view of\n * {@link profileForPath}.\n */\nexport function detectLanguage(path: string | undefined): DetectedLanguage {\n return profileForPath(path)?.id ?? 'unknown';\n}\n"]}
|
package/dist/detect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA0B,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAC5F,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CACxB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACrD,OAAO,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,SAAS,CAAC;AAC/C,CAAC"}
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA0B,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAC5F,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CACxB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACrD,OAAO,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,SAAS,CAAC;AAC/C,CAAC","sourcesContent":["import { LANGUAGE_PROFILES, profileForPath } from './lang/registry.ts';\nimport type { DetectedLanguage, LanguageId } from './types.ts';\n\n/**\n * Every language smelt has a grammar mapping for — the registry's ids, in registry\n * order. A derived view of `LANGUAGE_PROFILES`, kept as an export because tests and\n * the CLI's rendered lists read it.\n */\nexport const SUPPORTED_LANGUAGES: readonly LanguageId[] = Object.values(LANGUAGE_PROFILES).map(\n (profile) => profile.id,\n);\n\n/**\n * Detect the language of a path, from its extension. Returns `'unknown'` for anything\n * unmapped — which is a normal outcome, not an error: it selects the lexical planner,\n * which works on anything. The extension → profile mapping itself lives on each\n * language's `LanguageProfile` (`src/lang/`); this is the id-shaped view of\n * {@link profileForPath}.\n */\nexport function detectLanguage(path: string | undefined): DetectedLanguage {\n return profileForPath(path)?.id ?? 'unknown';\n}\n"]}
|
package/dist/errors.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class NotImplementedError extends SmeltError {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Thrown when something in the elision path tried to reach a non-local resource.
|
|
19
|
-
* v1 has no network. See docs/
|
|
19
|
+
* v1 has no network. See docs/ARCHITECTURE.md § "Law 1 — zero network".
|
|
20
20
|
*/
|
|
21
21
|
export declare class NetworkPolicyError extends SmeltError {
|
|
22
22
|
readonly name = "NetworkPolicyError";
|
|
@@ -85,4 +85,24 @@ export declare class StoreCorruptionError extends SmeltError {
|
|
|
85
85
|
export declare class StoreFormatError extends SmeltError {
|
|
86
86
|
readonly name = "StoreFormatError";
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* A filesystem call the repo map made failed: a root that is not there, a directory
|
|
90
|
+
* that cannot be listed, a file that cannot be read, a cache entry that cannot be
|
|
91
|
+
* written.
|
|
92
|
+
*
|
|
93
|
+
* It exists because of a hole in the one promise the consumer contract makes about
|
|
94
|
+
* errors — **every error smelt throws is an `instanceof SmeltError`**.
|
|
95
|
+
* `buildRepoMap({ root: '/nonexistent' })` used to throw the raw `ENOENT` that
|
|
96
|
+
* `readdirSync` raises, so a caller doing precisely what the documentation says still
|
|
97
|
+
* had a bare `Error` escaping past its `catch`. The guarantee is worth nothing with an
|
|
98
|
+
* exception to it, so every `node:fs` call under `src/repomap/` now arrives here.
|
|
99
|
+
*
|
|
100
|
+
* The message names the path, because "ENOENT" without one is a bug report nobody can
|
|
101
|
+
* act on, and the original error is kept as `cause`: wrapping is meant to bring the
|
|
102
|
+
* failure inside the contract, never to hide what Node said.
|
|
103
|
+
*/
|
|
104
|
+
export declare class RepoMapIoError extends SmeltError {
|
|
105
|
+
readonly name = "RepoMapIoError";
|
|
106
|
+
constructor(operation: string, path: string, cause: unknown);
|
|
107
|
+
}
|
|
88
108
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAkB,IAAI,EAAE,MAAM,CAAgB;CAC/C;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,SAAkB,IAAI,yBAAyB;IAE/C,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAKxC;CACF;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,0FAA0F;AAC1F,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,mEAAmE;AACnE,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,SAAkB,IAAI,2BAA2B;CAClD;AAED,uEAAuE;AACvE,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,uEAAuE;AACvE,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;IAE5C,YAAY,IAAI,EAAE,MAAM,EAIvB;CACF;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;IACvD,SAAkB,IAAI,+BAA+B;IAErD,YAAY,SAAS,EAAE,MAAM,EAO5B;CACF;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,SAAkB,IAAI,mBAAmB;CAC1C;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,IAAI,EAAE,MAAM,EAKvB;CACF;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,SAAkB,IAAI,0BAA0B;IAEhD,YAAY,IAAI,EAAE,MAAM,EAMvB;CACF;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;CAC7C"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAkB,IAAI,EAAE,MAAM,CAAgB;CAC/C;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,SAAkB,IAAI,yBAAyB;IAE/C,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAKxC;CACF;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,0FAA0F;AAC1F,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,mEAAmE;AACnE,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,SAAkB,IAAI,2BAA2B;CAClD;AAED,uEAAuE;AACvE,qBAAa,uBAAwB,SAAQ,UAAU;IACrD,SAAkB,IAAI,6BAA6B;CACpD;AAED,uEAAuE;AACvE,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;IAE5C,YAAY,IAAI,EAAE,MAAM,EAIvB;CACF;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;IACvD,SAAkB,IAAI,+BAA+B;IAErD,YAAY,SAAS,EAAE,MAAM,EAO5B;CACF;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,SAAkB,IAAI,mBAAmB;CAC1C;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAkB,IAAI,wBAAwB;IAE9C,YAAY,IAAI,EAAE,MAAM,EAKvB;CACF;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,SAAkB,IAAI,0BAA0B;IAEhD,YAAY,IAAI,EAAE,MAAM,EAMvB;CACF;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAkB,IAAI,sBAAsB;CAC7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,SAAkB,IAAI,oBAAoB;IAE1C,YAAY,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAM1D;CACF","sourcesContent":["/**\n * Every error smelt throws is one of these. Consumers can `instanceof SmeltError`\n * to tell \"the library said no\" apart from \"something else blew up\".\n */\nexport class SmeltError extends Error {\n override readonly name: string = 'SmeltError';\n}\n\n/**\n * A scaffold stub. It throws instead of returning a plausible-looking wrong answer,\n * because a context optimizer that quietly returns bad output is indistinguishable\n * from one that works. See CONTRIBUTING.md § \"Silence is the enemy\".\n */\nexport class NotImplementedError extends SmeltError {\n override readonly name = 'NotImplementedError';\n\n constructor(what: string, seeAlso: string) {\n super(\n `smelt: ${what} is not implemented yet. This is a scaffold stub — it throws rather ` +\n `than returning a plausible wrong answer. See ${seeAlso}.`,\n );\n }\n}\n\n/**\n * Thrown when something in the elision path tried to reach a non-local resource.\n * v1 has no network. See docs/ARCHITECTURE.md § \"Law 1 — zero network\".\n */\nexport class NetworkPolicyError extends SmeltError {\n override readonly name = 'NetworkPolicyError';\n\n constructor(message: string) {\n super(`smelt: ${message}`);\n }\n}\n\n/** A plan asked to elide two overlapping ranges. Applying it would corrupt the output. */\nexport class OverlappingElisionError extends SmeltError {\n override readonly name = 'OverlappingElisionError';\n}\n\n/** A plan referenced a byte range that is not inside the input. */\nexport class RangeOutOfBoundsError extends SmeltError {\n override readonly name = 'RangeOutOfBoundsError';\n}\n\n/** A grammar was requested that is not installed or not registered. */\nexport class GrammarUnavailableError extends SmeltError {\n override readonly name = 'GrammarUnavailableError';\n}\n\n/** `retrieve(hash)` was called with a hash the store does not hold. */\nexport class UnknownHashError extends SmeltError {\n override readonly name = 'UnknownHashError';\n\n constructor(hash: string) {\n super(\n `smelt: no stored content for hash \"${hash}\". It was never elided, or the store was reset.`,\n );\n }\n}\n\n/**\n * A planner was handed a `PlanInput` without `pricing`. The type makes `pricing`\n * required, so TypeScript callers cannot get here; a JS caller can, and the honest\n * answer is this error rather than a guessed marker cost — a planner pricing markers\n * itself is exactly the inversion the MarkerPricing seam removed.\n */\nexport class MissingMarkerPricingError extends SmeltError {\n override readonly name = 'MissingMarkerPricingError';\n\n constructor(plannerId: string) {\n super(\n `smelt: ${plannerId} was handed a PlanInput without \\`pricing\\`. A planner never ` +\n `guesses what a marker costs — the applier renders markers, so the applier ` +\n `prices them. Build one with markerPricing(language, marker) from apply.ts and ` +\n `put it on the input; createSmelter and the CLI construct it centrally.`,\n );\n }\n}\n\n/**\n * The CLI was invoked wrongly — a missing `--budget`, an unknown flag, a budget that\n * is not a number. Distinct from every other `SmeltError` so the CLI can exit with a\n * usage code rather than pretending the library refused.\n */\nexport class CliUsageError extends SmeltError {\n override readonly name = 'CliUsageError';\n}\n\n/**\n * Two different blobs hashed to the same key. Astronomically unlikely, and yet: the\n * alternative to throwing is handing the model the wrong bytes and calling it a\n * retrieval, which is precisely the silent failure this library exists to avoid.\n */\nexport class HashCollisionError extends SmeltError {\n override readonly name = 'HashCollisionError';\n\n constructor(hash: string) {\n super(\n `smelt: hash collision on \"${hash}\" — two different blobs share a key. Refusing to ` +\n `store, because retrieving would return the wrong bytes. Please report this.`,\n );\n }\n}\n\n/**\n * A persistent store holds bytes under this hash, but they no longer hash to it — a\n * torn write, a truncation, an edit behind the store's back. Deliberately distinct from\n * {@link UnknownHashError}: \"we hold damaged bytes\" and \"never existed\" call for\n * different responses, and returning the damaged bytes as a retrieval would be the\n * silent wrong answer this library exists to refuse.\n */\nexport class StoreCorruptionError extends SmeltError {\n override readonly name = 'StoreCorruptionError';\n\n constructor(hash: string) {\n super(\n `smelt: the bytes stored under hash \"${hash}\" do not hash to \"${hash}\". Refusing ` +\n `to return them — they are damaged, not merely unknown. The store directory was ` +\n `truncated or edited outside smelt.`,\n );\n }\n}\n\n/**\n * A directory offered as a persistent store carries a format marker this version of\n * smelt does not understand — or no parseable marker at all. Refusing beats guessing:\n * reinterpreting an unknown layout could hand back the wrong bytes with no error.\n */\nexport class StoreFormatError extends SmeltError {\n override readonly name = 'StoreFormatError';\n}\n\n/**\n * A filesystem call the repo map made failed: a root that is not there, a directory\n * that cannot be listed, a file that cannot be read, a cache entry that cannot be\n * written.\n *\n * It exists because of a hole in the one promise the consumer contract makes about\n * errors — **every error smelt throws is an `instanceof SmeltError`**.\n * `buildRepoMap({ root: '/nonexistent' })` used to throw the raw `ENOENT` that\n * `readdirSync` raises, so a caller doing precisely what the documentation says still\n * had a bare `Error` escaping past its `catch`. The guarantee is worth nothing with an\n * exception to it, so every `node:fs` call under `src/repomap/` now arrives here.\n *\n * The message names the path, because \"ENOENT\" without one is a bug report nobody can\n * act on, and the original error is kept as `cause`: wrapping is meant to bring the\n * failure inside the contract, never to hide what Node said.\n */\nexport class RepoMapIoError extends SmeltError {\n override readonly name = 'RepoMapIoError';\n\n constructor(operation: string, path: string, cause: unknown) {\n super(\n `smelt: could not ${operation} \"${path}\" while building the repo map: ` +\n `${describeIoCause(cause)}.`,\n { cause },\n );\n }\n}\n\n/** The `errno` code when Node supplied one, its message otherwise. Never invented. */\nfunction describeIoCause(cause: unknown): string {\n const code = (cause as { code?: string } | null | undefined)?.code;\n if (typeof code === 'string' && code !== '') return code;\n if (cause instanceof Error && cause.message !== '') return cause.message;\n return String(cause);\n}\n"]}
|
package/dist/errors.js
CHANGED
|
@@ -19,7 +19,7 @@ export class NotImplementedError extends SmeltError {
|
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Thrown when something in the elision path tried to reach a non-local resource.
|
|
22
|
-
* v1 has no network. See docs/
|
|
22
|
+
* v1 has no network. See docs/ARCHITECTURE.md § "Law 1 — zero network".
|
|
23
23
|
*/
|
|
24
24
|
export class NetworkPolicyError extends SmeltError {
|
|
25
25
|
name = 'NetworkPolicyError';
|
|
@@ -104,4 +104,36 @@ export class StoreCorruptionError extends SmeltError {
|
|
|
104
104
|
export class StoreFormatError extends SmeltError {
|
|
105
105
|
name = 'StoreFormatError';
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* A filesystem call the repo map made failed: a root that is not there, a directory
|
|
109
|
+
* that cannot be listed, a file that cannot be read, a cache entry that cannot be
|
|
110
|
+
* written.
|
|
111
|
+
*
|
|
112
|
+
* It exists because of a hole in the one promise the consumer contract makes about
|
|
113
|
+
* errors — **every error smelt throws is an `instanceof SmeltError`**.
|
|
114
|
+
* `buildRepoMap({ root: '/nonexistent' })` used to throw the raw `ENOENT` that
|
|
115
|
+
* `readdirSync` raises, so a caller doing precisely what the documentation says still
|
|
116
|
+
* had a bare `Error` escaping past its `catch`. The guarantee is worth nothing with an
|
|
117
|
+
* exception to it, so every `node:fs` call under `src/repomap/` now arrives here.
|
|
118
|
+
*
|
|
119
|
+
* The message names the path, because "ENOENT" without one is a bug report nobody can
|
|
120
|
+
* act on, and the original error is kept as `cause`: wrapping is meant to bring the
|
|
121
|
+
* failure inside the contract, never to hide what Node said.
|
|
122
|
+
*/
|
|
123
|
+
export class RepoMapIoError extends SmeltError {
|
|
124
|
+
name = 'RepoMapIoError';
|
|
125
|
+
constructor(operation, path, cause) {
|
|
126
|
+
super(`smelt: could not ${operation} "${path}" while building the repo map: ` +
|
|
127
|
+
`${describeIoCause(cause)}.`, { cause });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** The `errno` code when Node supplied one, its message otherwise. Never invented. */
|
|
131
|
+
function describeIoCause(cause) {
|
|
132
|
+
const code = cause?.code;
|
|
133
|
+
if (typeof code === 'string' && code !== '')
|
|
134
|
+
return code;
|
|
135
|
+
if (cause instanceof Error && cause.message !== '')
|
|
136
|
+
return cause.message;
|
|
137
|
+
return String(cause);
|
|
138
|
+
}
|
|
107
139
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACjB,IAAI,GAAW,YAAY,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IAC/B,IAAI,GAAG,qBAAqB,CAAC;IAE/C,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CACH,UAAU,IAAI,sEAAsE;YAClF,gDAAgD,OAAO,GAAG,CAC7D,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAC9B,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,OAAe;QACzB,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,0FAA0F;AAC1F,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACnC,IAAI,GAAG,yBAAyB,CAAC;CACpD;AAED,mEAAmE;AACnE,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IACjC,IAAI,GAAG,uBAAuB,CAAC;CAClD;AAED,uEAAuE;AACvE,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACnC,IAAI,GAAG,yBAAyB,CAAC;CACpD;AAED,uEAAuE;AACvE,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC5B,IAAI,GAAG,kBAAkB,CAAC;IAE5C,YAAY,IAAY;QACtB,KAAK,CACH,sCAAsC,IAAI,iDAAiD,CAC5F,CAAC;IACJ,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,yBAA0B,SAAQ,UAAU;IACrC,IAAI,GAAG,2BAA2B,CAAC;IAErD,YAAY,SAAiB;QAC3B,KAAK,CACH,UAAU,SAAS,+DAA+D;YAChF,4EAA4E;YAC5E,gFAAgF;YAChF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IACzB,IAAI,GAAG,eAAe,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAC9B,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,IAAY;QACtB,KAAK,CACH,6BAA6B,IAAI,mDAAmD;YAClF,6EAA6E,CAChF,CAAC;IACJ,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,oBAAqB,SAAQ,UAAU;IAChC,IAAI,GAAG,sBAAsB,CAAC;IAEhD,YAAY,IAAY;QACtB,KAAK,CACH,uCAAuC,IAAI,qBAAqB,IAAI,cAAc;YAChF,iFAAiF;YACjF,oCAAoC,CACvC,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC5B,IAAI,GAAG,kBAAkB,CAAC;CAC7C"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Every error smelt throws is one of these. Consumers can `instanceof SmeltError`\n * to tell \"the library said no\" apart from \"something else blew up\".\n */\nexport class SmeltError extends Error {\n override readonly name: string = 'SmeltError';\n}\n\n/**\n * A scaffold stub. It throws instead of returning a plausible-looking wrong answer,\n * because a context optimizer that quietly returns bad output is indistinguishable\n * from one that works. See CONTRIBUTING.md § \"Silence is the enemy\".\n */\nexport class NotImplementedError extends SmeltError {\n override readonly name = 'NotImplementedError';\n\n constructor(what: string, seeAlso: string) {\n super(\n `smelt: ${what} is not implemented yet. This is a scaffold stub — it throws rather ` +\n `than returning a plausible wrong answer. See ${seeAlso}.`,\n );\n }\n}\n\n/**\n * Thrown when something in the elision path tried to reach a non-local resource.\n * v1 has no network. See docs/ARCHITECTURE.md § \"Law 1 — zero network\".\n */\nexport class NetworkPolicyError extends SmeltError {\n override readonly name = 'NetworkPolicyError';\n\n constructor(message: string) {\n super(`smelt: ${message}`);\n }\n}\n\n/** A plan asked to elide two overlapping ranges. Applying it would corrupt the output. */\nexport class OverlappingElisionError extends SmeltError {\n override readonly name = 'OverlappingElisionError';\n}\n\n/** A plan referenced a byte range that is not inside the input. */\nexport class RangeOutOfBoundsError extends SmeltError {\n override readonly name = 'RangeOutOfBoundsError';\n}\n\n/** A grammar was requested that is not installed or not registered. */\nexport class GrammarUnavailableError extends SmeltError {\n override readonly name = 'GrammarUnavailableError';\n}\n\n/** `retrieve(hash)` was called with a hash the store does not hold. */\nexport class UnknownHashError extends SmeltError {\n override readonly name = 'UnknownHashError';\n\n constructor(hash: string) {\n super(\n `smelt: no stored content for hash \"${hash}\". It was never elided, or the store was reset.`,\n );\n }\n}\n\n/**\n * A planner was handed a `PlanInput` without `pricing`. The type makes `pricing`\n * required, so TypeScript callers cannot get here; a JS caller can, and the honest\n * answer is this error rather than a guessed marker cost — a planner pricing markers\n * itself is exactly the inversion the MarkerPricing seam removed.\n */\nexport class MissingMarkerPricingError extends SmeltError {\n override readonly name = 'MissingMarkerPricingError';\n\n constructor(plannerId: string) {\n super(\n `smelt: ${plannerId} was handed a PlanInput without \\`pricing\\`. A planner never ` +\n `guesses what a marker costs — the applier renders markers, so the applier ` +\n `prices them. Build one with markerPricing(language, marker) from apply.ts and ` +\n `put it on the input; createSmelter and the CLI construct it centrally.`,\n );\n }\n}\n\n/**\n * The CLI was invoked wrongly — a missing `--budget`, an unknown flag, a budget that\n * is not a number. Distinct from every other `SmeltError` so the CLI can exit with a\n * usage code rather than pretending the library refused.\n */\nexport class CliUsageError extends SmeltError {\n override readonly name = 'CliUsageError';\n}\n\n/**\n * Two different blobs hashed to the same key. Astronomically unlikely, and yet: the\n * alternative to throwing is handing the model the wrong bytes and calling it a\n * retrieval, which is precisely the silent failure this library exists to avoid.\n */\nexport class HashCollisionError extends SmeltError {\n override readonly name = 'HashCollisionError';\n\n constructor(hash: string) {\n super(\n `smelt: hash collision on \"${hash}\" — two different blobs share a key. Refusing to ` +\n `store, because retrieving would return the wrong bytes. Please report this.`,\n );\n }\n}\n\n/**\n * A persistent store holds bytes under this hash, but they no longer hash to it — a\n * torn write, a truncation, an edit behind the store's back. Deliberately distinct from\n * {@link UnknownHashError}: \"we hold damaged bytes\" and \"never existed\" call for\n * different responses, and returning the damaged bytes as a retrieval would be the\n * silent wrong answer this library exists to refuse.\n */\nexport class StoreCorruptionError extends SmeltError {\n override readonly name = 'StoreCorruptionError';\n\n constructor(hash: string) {\n super(\n `smelt: the bytes stored under hash \"${hash}\" do not hash to \"${hash}\". Refusing ` +\n `to return them — they are damaged, not merely unknown. The store directory was ` +\n `truncated or edited outside smelt.`,\n );\n }\n}\n\n/**\n * A directory offered as a persistent store carries a format marker this version of\n * smelt does not understand — or no parseable marker at all. Refusing beats guessing:\n * reinterpreting an unknown layout could hand back the wrong bytes with no error.\n */\nexport class StoreFormatError extends SmeltError {\n override readonly name = 'StoreFormatError';\n}\n\n/**\n * A filesystem call the repo map made failed: a root that is not there, a directory\n * that cannot be listed, a file that cannot be read, a cache entry that cannot be\n * written.\n *\n * It exists because of a hole in the one promise the consumer contract makes about\n * errors — **every error smelt throws is an `instanceof SmeltError`**.\n * `buildRepoMap({ root: '/nonexistent' })` used to throw the raw `ENOENT` that\n * `readdirSync` raises, so a caller doing precisely what the documentation says still\n * had a bare `Error` escaping past its `catch`. The guarantee is worth nothing with an\n * exception to it, so every `node:fs` call under `src/repomap/` now arrives here.\n *\n * The message names the path, because \"ENOENT\" without one is a bug report nobody can\n * act on, and the original error is kept as `cause`: wrapping is meant to bring the\n * failure inside the contract, never to hide what Node said.\n */\nexport class RepoMapIoError extends SmeltError {\n override readonly name = 'RepoMapIoError';\n\n constructor(operation: string, path: string, cause: unknown) {\n super(\n `smelt: could not ${operation} \"${path}\" while building the repo map: ` +\n `${describeIoCause(cause)}.`,\n { cause },\n );\n }\n}\n\n/** The `errno` code when Node supplied one, its message otherwise. Never invented. */\nfunction describeIoCause(cause: unknown): string {\n const code = (cause as { code?: string } | null | undefined)?.code;\n if (typeof code === 'string' && code !== '') return code;\n if (cause instanceof Error && cause.message !== '') return cause.message;\n return String(cause);\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HarnessProfile } from './profile.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Aider — ADVISORY tier. Instructions only, and honest about it.
|
|
4
|
+
*
|
|
5
|
+
* No usable hook API, so the whole install is the shared snippet in `CONVENTIONS.md`
|
|
6
|
+
* and one caveat: Aider auto-reads no rules file, so the human has to point it at the
|
|
7
|
+
* conventions themselves. Nothing here is enforced, and the installer says so.
|
|
8
|
+
*/
|
|
9
|
+
export declare const aider: HarnessProfile;
|
|
10
|
+
//# sourceMappingURL=aider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aider.d.ts","sourceRoot":"","sources":["../../src/harness/aider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,EAAE,cAYnB,CAAC","sourcesContent":["import type { HarnessProfile } from './profile.ts';\n\n/**\n * Aider — ADVISORY tier. Instructions only, and honest about it.\n *\n * No usable hook API, so the whole install is the shared snippet in `CONVENTIONS.md`\n * and one caveat: Aider auto-reads no rules file, so the human has to point it at the\n * conventions themselves. Nothing here is enforced, and the installer says so.\n */\nexport const aider: HarnessProfile = {\n id: 'aider',\n name: 'Aider',\n tier: 'advisory',\n detect: ['.aider.conf.yml'],\n detectHome: ['.aider.conf.yml'],\n instructionFile: 'CONVENTIONS.md',\n instructions: 'snippet',\n caveats: [\n 'Aider auto-reads no rules file: add `read: CONVENTIONS.md` to .aider.conf.yml (or pass --read CONVENTIONS.md) yourself',\n ],\n install: [],\n};\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aider — ADVISORY tier. Instructions only, and honest about it.
|
|
3
|
+
*
|
|
4
|
+
* No usable hook API, so the whole install is the shared snippet in `CONVENTIONS.md`
|
|
5
|
+
* and one caveat: Aider auto-reads no rules file, so the human has to point it at the
|
|
6
|
+
* conventions themselves. Nothing here is enforced, and the installer says so.
|
|
7
|
+
*/
|
|
8
|
+
export const aider = {
|
|
9
|
+
id: 'aider',
|
|
10
|
+
name: 'Aider',
|
|
11
|
+
tier: 'advisory',
|
|
12
|
+
detect: ['.aider.conf.yml'],
|
|
13
|
+
detectHome: ['.aider.conf.yml'],
|
|
14
|
+
instructionFile: 'CONVENTIONS.md',
|
|
15
|
+
instructions: 'snippet',
|
|
16
|
+
caveats: [
|
|
17
|
+
'Aider auto-reads no rules file: add `read: CONVENTIONS.md` to .aider.conf.yml (or pass --read CONVENTIONS.md) yourself',
|
|
18
|
+
],
|
|
19
|
+
install: [],
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=aider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aider.js","sourceRoot":"","sources":["../../src/harness/aider.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,KAAK,GAAmB;IACnC,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,CAAC,iBAAiB,CAAC;IAC3B,UAAU,EAAE,CAAC,iBAAiB,CAAC;IAC/B,eAAe,EAAE,gBAAgB;IACjC,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE;QACP,wHAAwH;KACzH;IACD,OAAO,EAAE,EAAE;CACZ,CAAC","sourcesContent":["import type { HarnessProfile } from './profile.ts';\n\n/**\n * Aider — ADVISORY tier. Instructions only, and honest about it.\n *\n * No usable hook API, so the whole install is the shared snippet in `CONVENTIONS.md`\n * and one caveat: Aider auto-reads no rules file, so the human has to point it at the\n * conventions themselves. Nothing here is enforced, and the installer says so.\n */\nexport const aider: HarnessProfile = {\n id: 'aider',\n name: 'Aider',\n tier: 'advisory',\n detect: ['.aider.conf.yml'],\n detectHome: ['.aider.conf.yml'],\n instructionFile: 'CONVENTIONS.md',\n instructions: 'snippet',\n caveats: [\n 'Aider auto-reads no rules file: add `read: CONVENTIONS.md` to .aider.conf.yml (or pass --read CONVENTIONS.md) yourself',\n ],\n install: [],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/harness/claude-code.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAiD1D,eAAO,MAAM,UAAU,EAAE,qBAoBxB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Claude Code — VERIFIED tier. Schema, install and removal, end to end.\n *\n * Schema per <https://code.claude.com/docs/en/hooks> (verified 2026-09-02; the deep\n * dive is docs/research/2026-09-02-agent-enforcement.md § 1):\n *\n * - stdin: `{ hook_event_name: \"PreToolUse\", tool_name, tool_input, cwd }`. For\n * `Read`, `tool_input.file_path` is already absolute and `offset`/`limit` mark a\n * windowed read; for `Bash`, `tool_input.command` is the full command string, and\n * a relative path in it resolves against the payload's `cwd` — the *session's*\n * working directory, which after the model `cd`s differs from the hook process's\n * own cwd.\n * - deny: `hookSpecificOutput.permissionDecision: \"deny\"` with\n * `permissionDecisionReason` — which is **shown to the model**, so the guard's\n * reason (the exact replacement command, the `smelt retrieve` contract) lands in\n * the transcript as steering.\n * - rewrite (opt-in, `hooks.enforcement: \"rewrite\"`): `updatedInput` replaces the\n * entire input object of the *same* tool (v2.0.10+), so a Bash command can be\n * substituted but a Read can never become a Bash call — Reads deny in every mode.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read'],\n bashTools: ['Bash'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n cwdKey: 'cwd',\n deny: (reason) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'deny',\n permissionDecisionReason: reason,\n },\n }),\n rewrite: {\n document: ({ input, announcement }) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: announcement,\n // updatedInput replaces the whole input object — unchanged fields ride along.\n updatedInput: input,\n },\n }),\n announce: 'reason',\n },\n};\n\nexport const claudeCode: ShimmedHarnessProfile = {\n id: 'claude-code',\n name: 'Claude Code',\n tier: 'verified',\n detect: ['.claude'],\n detectHome: ['.claude'],\n instructionFile: 'CLAUDE.md',\n instructions: 'snippet',\n caveats: [],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.claude/settings.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: true,\n },\n ],\n};\n"]}
|