@smeltjs/core 0.3.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.
Files changed (89) hide show
  1. package/dist/agents/guide.d.ts +68 -0
  2. package/dist/agents/guide.d.ts.map +1 -0
  3. package/dist/agents/guide.js +72 -0
  4. package/dist/agents/guide.js.map +1 -0
  5. package/dist/agents/instructions.d.ts +133 -0
  6. package/dist/agents/instructions.d.ts.map +1 -0
  7. package/dist/agents/instructions.js +212 -0
  8. package/dist/agents/instructions.js.map +1 -0
  9. package/dist/agents/lint.d.ts +154 -0
  10. package/dist/agents/lint.d.ts.map +1 -0
  11. package/dist/agents/lint.js +761 -0
  12. package/dist/agents/lint.js.map +1 -0
  13. package/dist/agents/split.d.ts +113 -0
  14. package/dist/agents/split.d.ts.map +1 -0
  15. package/dist/agents/split.js +234 -0
  16. package/dist/agents/split.js.map +1 -0
  17. package/dist/cli/agents.d.ts +39 -0
  18. package/dist/cli/agents.d.ts.map +1 -0
  19. package/dist/cli/agents.js +121 -0
  20. package/dist/cli/agents.js.map +1 -0
  21. package/dist/cli/args.d.ts +1 -0
  22. package/dist/cli/args.d.ts.map +1 -1
  23. package/dist/cli/args.js.map +1 -1
  24. package/dist/cli/config.d.ts +20 -0
  25. package/dist/cli/config.d.ts.map +1 -1
  26. package/dist/cli/config.js +34 -1
  27. package/dist/cli/config.js.map +1 -1
  28. package/dist/cli/hooks.d.ts +25 -16
  29. package/dist/cli/hooks.d.ts.map +1 -1
  30. package/dist/cli/hooks.js +71 -178
  31. package/dist/cli/hooks.js.map +1 -1
  32. package/dist/cli/report.d.ts +23 -0
  33. package/dist/cli/report.d.ts.map +1 -1
  34. package/dist/cli/report.js +140 -2
  35. package/dist/cli/report.js.map +1 -1
  36. package/dist/cli/run.d.ts +5 -3
  37. package/dist/cli/run.d.ts.map +1 -1
  38. package/dist/cli/run.js +2 -1
  39. package/dist/cli/run.js.map +1 -1
  40. package/dist/cli/subcommands/agents.d.ts +72 -0
  41. package/dist/cli/subcommands/agents.d.ts.map +1 -0
  42. package/dist/cli/subcommands/agents.js +162 -0
  43. package/dist/cli/subcommands/agents.js.map +1 -0
  44. package/dist/cli/subcommands/flags.d.ts +3 -0
  45. package/dist/cli/subcommands/flags.d.ts.map +1 -1
  46. package/dist/cli/subcommands/flags.js +13 -1
  47. package/dist/cli/subcommands/flags.js.map +1 -1
  48. package/dist/cli/subcommands/hooks.d.ts.map +1 -1
  49. package/dist/cli/subcommands/hooks.js +19 -3
  50. package/dist/cli/subcommands/hooks.js.map +1 -1
  51. package/dist/cli/subcommands/registry.d.ts +2 -1
  52. package/dist/cli/subcommands/registry.d.ts.map +1 -1
  53. package/dist/cli/subcommands/registry.js +3 -0
  54. package/dist/cli/subcommands/registry.js.map +1 -1
  55. package/dist/cli/subcommands/subcommand.d.ts +1 -1
  56. package/dist/cli/subcommands/subcommand.d.ts.map +1 -1
  57. package/dist/cli/subcommands/subcommand.js.map +1 -1
  58. package/dist/harness/codex.d.ts.map +1 -1
  59. package/dist/harness/codex.js +1 -0
  60. package/dist/harness/codex.js.map +1 -1
  61. package/dist/harness/gemini.d.ts.map +1 -1
  62. package/dist/harness/gemini.js +1 -0
  63. package/dist/harness/gemini.js.map +1 -1
  64. package/dist/harness/grok.d.ts.map +1 -1
  65. package/dist/harness/grok.js +1 -0
  66. package/dist/harness/grok.js.map +1 -1
  67. package/dist/harness/hermes.d.ts.map +1 -1
  68. package/dist/harness/hermes.js +1 -0
  69. package/dist/harness/hermes.js.map +1 -1
  70. package/dist/harness/profile.d.ts +26 -1
  71. package/dist/harness/profile.d.ts.map +1 -1
  72. package/dist/harness/profile.js +15 -0
  73. package/dist/harness/profile.js.map +1 -1
  74. package/dist/harness/registry.d.ts +55 -6
  75. package/dist/harness/registry.d.ts.map +1 -1
  76. package/dist/harness/registry.js +63 -6
  77. package/dist/harness/registry.js.map +1 -1
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +8 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/text/json-edit.d.ts +71 -0
  83. package/dist/text/json-edit.d.ts.map +1 -0
  84. package/dist/text/json-edit.js +219 -0
  85. package/dist/text/json-edit.js.map +1 -0
  86. package/dist/types.d.ts +0 -11
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js.map +1 -1
  89. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/cli/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nimport { CliUsageError } from '../errors.ts';\nimport { nodeCommand, portablePath, shimScriptPath, smeltBinPath } from '../harness/paths.ts';\nimport { hasShim, TIER_HONESTY } from '../harness/profile.ts';\nimport type {\n HarnessInstallContext,\n HarnessJsonHooks,\n HarnessProfile,\n} from '../harness/profile.ts';\nimport {\n GUARD_EVENTS,\n GUARD_ONLY_FILES,\n HARNESSES,\n harnessById,\n JSON_HOOK_FILES,\n LIFECYCLE_EVENTS,\n MANAGED_EVENTS,\n} from '../harness/registry.ts';\nimport {\n instructionSnippet,\n OURS_TOKEN,\n SNIPPET_END_MD,\n SNIPPET_START_MD,\n} from '../harness/snippet.ts';\nimport { DEFAULT_SUGGESTION_BUDGET_BYTES, DEFAULT_THRESHOLD_BYTES } from '../hooks/guard-core.ts';\nimport type { EnforcementMode } from '../hooks/guard-core.ts';\n\nimport { answerReader, CLI_NAME } from './shell.ts';\nimport type { AnswerStream } from './shell.ts';\nimport {\n CONFIG_FILE_NAME,\n CONFIG_VERSION,\n findConfigFile,\n parseConfig,\n renderConfig,\n} from './config.ts';\nimport type { SmeltConfig, SmeltConfigHooks } from './config.ts';\n\n/**\n * `smelt hooks install` / `smelt hooks remove` — the multi-harness guard preset.\n *\n * The design: one zero-dependency guard core\n * (`src/hooks/guard-core.ts`), thin per-harness shims mapping each harness's native\n * hook schema onto it, and this installer, which writes the harness config that wires\n * a shim in — plus an instruction-file snippet as belt and braces, because the\n * snippet is also what teaches the model to run `smelt retrieve` after a deny.\n *\n * Every per-harness fact lives in that harness's {@link HarnessProfile}\n * (`src/harness/<id>.ts`), including what to write and how to take it back out. This\n * module owns only what is the *same* for every harness: the byte-faithful JSON merge,\n * the marker-block upsert, the wizard, and the two plans below — folds over\n * `profile.install`, with no case list of its own.\n *\n * Harnesses come in three honesty tiers (docs/research/2026-09-02-harness-capability-matrix.md):\n *\n * - **verified** — Claude Code, Codex: schemas verified against primary docs and\n * exercised against recorded fixtures; first-class targets.\n * - **experimental** — Gemini, Grok, Hermes, Cursor, opencode, Cline: hook schemas\n * mapped from the capability matrix but not yet smoke-tested green against the\n * real binary. Labelled as such in code, docs, and this installer's output.\n * - **advisory** — KiloCode, Aider: no usable hook API, so what ships is\n * instructions (and, for KiloCode, a permissions/MCP sketch). Nothing enforces\n * them, and the output says so rather than implying a guard exists.\n *\n * The wizard discipline is `smelt init`'s, verbatim: every step accepts `back`,\n * nothing is written until a final confirm that lists every file, and an existing\n * file is never overwritten without an explicit per-file `yes` — guarded by\n * `test/guards/hooks-preset.test.ts`, with mutation `hooks-install-overwrite-without-consent`\n * proving the guard goes red.\n */\n\n/** Where the wizard's bytes come from and go. Injected so `runHooks` tests in-process. */\nexport interface HooksIo {\n /**\n * Scripted answers in, one line at a time. Structural on purpose; see\n * {@link AnswerStream}.\n */\n readonly input: AnswerStream;\n readonly output: (text: string) => void;\n /** Project directory: detection, config discovery, and every write are relative to it. */\n readonly cwd: string;\n /** Home directory for detection only. Tests point it at a temp dir; nothing writes here. */\n readonly home?: string;\n}\n\nexport { instructionSnippet, SNIPPET_END_MD, SNIPPET_START_MD };\n\n/** A harness whose config directory exists in the project or the home directory. */\nexport function detectedHarnesses(cwd: string, home: string): readonly HarnessProfile[] {\n return HARNESSES.filter(\n (profile) =>\n profile.detect.some((path) => existsSync(join(cwd, path))) ||\n profile.detectHome.some((path) => existsSync(join(home, path))),\n );\n}\n\n/* ------------------------------------------------------------------------------------\n * Generated content\n * ---------------------------------------------------------------------------------- */\n\n/** Claude-style hook entry: one command under an optional matcher. */\nfunction commandEntry(matcher: string | undefined, command: string): unknown {\n return {\n ...(matcher === undefined ? {} : { matcher }),\n hooks: [{ type: 'command', command }],\n };\n}\n\n/**\n * The hook command a harness's entries run: its own shim script, through node.\n *\n * @throws {Error} when a profile declares a JSON hook file but ships no shim — a\n * registry bug, pinned by `test/guards/harness-registry.test.ts`, not a user error.\n */\nfunction shimCommand(profile: HarnessProfile, cwd: string): string {\n /* v8 ignore next 5 -- unreachable: pinned by the harness-registry guard */\n if (!hasShim(profile)) {\n throw new Error(\n `smelt: harness \"${profile.id}\" wires a hook command but ships no shim script.`,\n );\n }\n return nodeCommand(cwd, shimScriptPath(profile));\n}\n\n/**\n * One harness's hook entries: the guard under each matcher its schema spells, plus\n * the two session-lifecycle hooks for the harnesses whose schema carries them. Every\n * toggle the wizard offers is a key that is present or absent here — an absent key is\n * how a re-run turns a toggle *off*, because the merge deletes what it no longer sees.\n */\nfunction jsonHookEvents(\n step: HarnessJsonHooks,\n ctx: HarnessInstallContext,\n command: string,\n): Record<string, readonly unknown[]> {\n // The trailing shell comment tags the entry as this installer's (see isOursEntry):\n // a bare `cli/bin.js` substring would also match some other npm CLI's built binary.\n const stats = `${nodeCommand(ctx.cwd, smeltBinPath(), 'stats')} 2>/dev/null || true # ${OURS_TOKEN}`;\n const map = `${nodeCommand(\n ctx.cwd,\n smeltBinPath(),\n `map . --budget ${String(ctx.budgetBytes)} --cache .smelt/tags`,\n )} 2>/dev/null || true # ${OURS_TOKEN}`;\n\n return {\n ...(ctx.guard\n ? {\n [step.event]: step.matchers.map((matcher) =>\n step.entry === 'bare-command' ? { command } : commandEntry(matcher, command),\n ),\n }\n : {}),\n ...(step.lifecycle && ctx.statsOnStop\n ? { [LIFECYCLE_EVENTS.stats]: [commandEntry(undefined, stats)] }\n : {}),\n ...(step.lifecycle && ctx.mapOnStart\n ? { [LIFECYCLE_EVENTS.map]: [commandEntry('startup|resume|clear|compact', map)] }\n : {}),\n };\n}\n\n/**\n * True for a hook entry this installer wrote. Matched on the shim script paths and the\n * `smelt:hooks` token the stats/map commands carry — never on a substring as generic\n * as `cli/bin.js`, which another npm CLI's built binary could share: remove and\n * re-install may only ever touch entries that are provably smelt's.\n */\nfunction isOursEntry(entry: unknown): boolean {\n const text = JSON.stringify(entry) ?? '';\n return text.includes('hooks/shims/') || text.includes(OURS_TOKEN);\n}\n\n/**\n * Merge our hook entries into a JSON settings file, preserving everything foreign\n * **byte-faithfully**: the merged `hooks` value is spliced into the original text, so\n * unknown top-level keys, string escapes, number spellings, indentation and key order\n * outside the `hooks` property ride through verbatim (an installer\n * that reformats somebody's settings file has edited what it was never asked to).\n * Inside `hooks`, unmanaged events and other people's entries under managed events\n * are preserved; our previous entries are replaced (that is what makes a re-run edit\n * toggles), and events left with no entries disappear. A semantic no-op returns the\n * input text unchanged. Returns `undefined` when the existing file is not a JSON\n * object — the caller skips the file rather than clobbering something it cannot\n * understand.\n */\nexport function mergeJsonHooks(\n existingText: string | undefined,\n events: Record<string, readonly unknown[]>,\n shape: { readonly version?: number } = {},\n): string | undefined {\n let root: Record<string, unknown> = {};\n if (existingText !== undefined) {\n try {\n const parsed: unknown = JSON.parse(existingText);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return undefined;\n root = parsed as Record<string, unknown>;\n } catch {\n return undefined;\n }\n }\n const hooksValue = root['hooks'];\n const existingHooks =\n typeof hooksValue === 'object' && hooksValue !== null && !Array.isArray(hooksValue)\n ? (hooksValue as Record<string, unknown>)\n : undefined;\n const hooks = { ...existingHooks };\n\n for (const event of MANAGED_EVENTS) {\n const existing = Array.isArray(hooks[event]) ? (hooks[event] as unknown[]) : [];\n const foreign = existing.filter((entry) => !isOursEntry(entry));\n const ours = events[event] ?? [];\n const merged = [...foreign, ...ours];\n if (merged.length > 0) hooks[event] = merged;\n else delete hooks[event];\n }\n\n const mergedHooks = Object.keys(hooks).length > 0 ? hooks : undefined;\n\n // A brand-new file: nothing to preserve, render fresh two-space JSON.\n if (existingText === undefined) {\n const fresh: Record<string, unknown> = {};\n if (mergedHooks !== undefined) fresh['hooks'] = mergedHooks;\n if (shape.version !== undefined) fresh['version'] = shape.version;\n return `${JSON.stringify(fresh, null, 2)}\\n`;\n }\n\n const hooksChanged =\n JSON.stringify(existingHooks ?? null) !== JSON.stringify(mergedHooks ?? null);\n const needsVersion = shape.version !== undefined && root['version'] === undefined;\n if (!hooksChanged && !needsVersion) return existingText;\n\n const newline = existingText.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const indent = /\\n([ \\t]+)\"/.exec(existingText)?.[1] ?? ' ';\n let text = existingText;\n\n if (hooksChanged) {\n const scan = scanJsonTopLevel(text);\n /* v8 ignore next -- unreachable: JSON.parse accepted the same text above */\n if (scan === undefined) return undefined;\n const property = scan.properties.find((candidate) => candidate.key === 'hooks');\n if (mergedHooks === undefined) {\n if (property !== undefined) text = removeJsonProperty(text, scan, property);\n } else {\n const rendered = renderJsonValue(mergedHooks, indent, newline);\n text =\n property !== undefined\n ? `${text.slice(0, property.valueStart)}${rendered}${text.slice(property.valueEnd)}`\n : insertJsonProperty(text, scan, 'hooks', rendered, indent, newline);\n }\n }\n if (needsVersion) {\n const scan = scanJsonTopLevel(text);\n /* v8 ignore next -- unreachable: every splice above keeps the text valid JSON */\n if (scan === undefined) return undefined;\n text = insertJsonProperty(\n text,\n scan,\n 'version',\n JSON.stringify(shape.version),\n indent,\n newline,\n );\n }\n return text;\n}\n\n/** One top-level property of a JSON object, located by offsets in its source text. */\ninterface JsonTopLevelProperty {\n readonly key: string;\n /** Offset of the key's opening quote. */\n readonly keyStart: number;\n /** Offset of the value's first byte. */\n readonly valueStart: number;\n /** Offset one past the value's last byte. */\n readonly valueEnd: number;\n}\n\ninterface JsonTopLevelScan {\n /** Offset of the root object's `{`. */\n readonly open: number;\n /** Offset of the root object's `}`. */\n readonly close: number;\n readonly properties: readonly JsonTopLevelProperty[];\n}\n\n/**\n * Locate the top-level properties of a JSON object *in its source text*, so one\n * property can be replaced, inserted or removed while every other byte of the file\n * rides through verbatim. `undefined` when the text is not an object — callers have\n * already `JSON.parse`d it, so that is belt and braces, not a validator.\n */\nfunction scanJsonTopLevel(text: string): JsonTopLevelScan | undefined {\n let i = skipJsonWhitespace(text, 0);\n if (text[i] !== '{') return undefined;\n const open = i;\n i = skipJsonWhitespace(text, i + 1);\n const properties: JsonTopLevelProperty[] = [];\n if (text[i] === '}') return { open, close: i, properties };\n for (;;) {\n if (text[i] !== '\"') return undefined;\n const keyStart = i;\n const keyEnd = skipJsonString(text, i);\n if (keyEnd === undefined) return undefined;\n const key = JSON.parse(text.slice(keyStart, keyEnd)) as string;\n i = skipJsonWhitespace(text, keyEnd);\n if (text[i] !== ':') return undefined;\n const valueStart = skipJsonWhitespace(text, i + 1);\n const valueEnd = skipJsonValue(text, valueStart);\n if (valueEnd === undefined) return undefined;\n properties.push({ key, keyStart, valueStart, valueEnd });\n i = skipJsonWhitespace(text, valueEnd);\n if (text[i] === ',') {\n i = skipJsonWhitespace(text, i + 1);\n continue;\n }\n if (text[i] === '}') return { open, close: i, properties };\n return undefined;\n }\n}\n\nfunction skipJsonWhitespace(text: string, from: number): number {\n let i = from;\n while (i < text.length && ' \\t\\r\\n'.includes(text[i]!)) i += 1;\n return i;\n}\n\n/** `from` points at `\"`; returns the offset one past the closing quote. */\nfunction skipJsonString(text: string, from: number): number | undefined {\n let i = from + 1;\n while (i < text.length) {\n if (text[i] === '\\\\') i += 2;\n else if (text[i] === '\"') return i + 1;\n else i += 1;\n }\n return undefined;\n}\n\nfunction skipJsonValue(text: string, from: number): number | undefined {\n const first = text[from];\n if (first === '\"') return skipJsonString(text, from);\n if (first === '{' || first === '[') {\n let depth = 0;\n let i = from;\n while (i < text.length) {\n const ch = text[i]!;\n if (ch === '\"') {\n const end = skipJsonString(text, i);\n if (end === undefined) return undefined;\n i = end;\n continue;\n }\n if (ch === '{' || ch === '[') depth += 1;\n else if (ch === '}' || ch === ']') {\n depth -= 1;\n if (depth === 0) return i + 1;\n }\n i += 1;\n }\n return undefined;\n }\n // number / true / false / null\n let i = from;\n while (i < text.length && !',}] \\t\\r\\n'.includes(text[i]!)) i += 1;\n return i > from ? i : undefined;\n}\n\n/** A JSON value indented for embedding at a top-level property position. */\nfunction renderJsonValue(value: unknown, indent: string, newline: string): string {\n return JSON.stringify(value, null, indent).split('\\n').join(`${newline}${indent}`);\n}\n\nfunction removeJsonProperty(\n text: string,\n scan: JsonTopLevelScan,\n property: JsonTopLevelProperty,\n): string {\n const index = scan.properties.indexOf(property);\n const next = scan.properties[index + 1];\n if (next !== undefined) {\n // Delete through the separating comma and whitespace, up to the next key.\n return text.slice(0, property.keyStart) + text.slice(next.keyStart);\n }\n const previous = scan.properties[index - 1];\n // Last (or only) property: delete the preceding comma (if any) with it.\n const from = previous !== undefined ? previous.valueEnd : scan.open + 1;\n return text.slice(0, from) + text.slice(property.valueEnd);\n}\n\nfunction insertJsonProperty(\n text: string,\n scan: JsonTopLevelScan,\n key: string,\n renderedValue: string,\n indent: string,\n newline: string,\n): string {\n const entry = `${JSON.stringify(key)}: ${renderedValue}`;\n if (scan.properties.length === 0) {\n return `${text.slice(0, scan.open + 1)}${newline}${indent}${entry}${newline}${text.slice(scan.close)}`;\n }\n const last = scan.properties[scan.properties.length - 1]!;\n return `${text.slice(0, last.valueEnd)},${newline}${indent}${entry}${text.slice(last.valueEnd)}`;\n}\n\n/** Replace this installer's marker block in `existingText`, or append it. */\nexport function upsertMarkerBlock(\n existingText: string | undefined,\n block: string,\n start: string,\n end: string,\n): string {\n if (existingText === undefined || existingText.trim() === '') return block;\n const startIndex = existingText.indexOf(start);\n const endIndex = existingText.indexOf(end);\n if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {\n const before = existingText.slice(0, startIndex);\n const after = existingText.slice(endIndex + end.length).replace(/^\\n/, '');\n return `${before}${block}${after}`;\n }\n return `${existingText.replace(/\\n*$/, '\\n\\n')}${block}`;\n}\n\n/** Remove the marker block. `undefined` when nothing (or only whitespace) remains. */\nexport function stripMarkerBlock(\n existingText: string,\n start: string,\n end: string,\n): string | undefined {\n const startIndex = existingText.indexOf(start);\n const endIndex = existingText.indexOf(end);\n if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) return existingText;\n const stripped =\n existingText.slice(0, startIndex).replace(/\\n+$/, '\\n') +\n existingText.slice(endIndex + end.length).replace(/^\\n+/, '');\n return stripped.trim() === '' ? undefined : stripped;\n}\n\n/* ------------------------------------------------------------------------------------\n * Planning\n * ---------------------------------------------------------------------------------- */\n\ninterface PlannedFile {\n /** Display path, relative to the project. */\n readonly name: string;\n readonly path: string;\n readonly content: string;\n readonly exists: boolean;\n readonly unchanged: boolean;\n /** chmod after writing (the cline hook must be executable). */\n readonly mode?: number;\n}\n\ninterface SkippedFile {\n readonly name: string;\n readonly why: string;\n}\n\ninterface PlannedRemoval {\n readonly name: string;\n readonly path: string;\n /** `'delete'` removes the file; `'modify'` writes `content` (ours stripped out). */\n readonly action: 'delete' | 'modify';\n readonly content?: string;\n}\n\nexport interface HooksChoices {\n harnesses: HarnessProfile[];\n guard: boolean;\n statsOnStop: boolean;\n mapOnStart: boolean;\n enforcement: EnforcementMode;\n thresholdBytes: number;\n}\n\ninterface InstallPlan {\n readonly files: readonly PlannedFile[];\n readonly skipped: readonly SkippedFile[];\n readonly notes: readonly string[];\n}\n\nfunction planFile(cwd: string, name: string, content: string, mode?: number): PlannedFile {\n const path = join(cwd, name);\n const exists = existsSync(path);\n const unchanged = exists && readFileSync(path, 'utf8') === content;\n return { name, path, content, exists, unchanged, ...(mode === undefined ? {} : { mode }) };\n}\n\nfunction readIfExists(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf8') : undefined;\n}\n\n/**\n * Every file `install` would write, computed against the current disk state — pure\n * planning, nothing written. A fold over each chosen profile's `install` list and its\n * instruction layer; all per-harness knowledge is in the profiles. Shared instruction\n * files (several harnesses read AGENTS.md) are planned once.\n *\n * @throws {CliUsageError} when an existing `smelt.config.json` is malformed — the\n * same refusal every other subcommand makes; an installer that guessed around a\n * broken config would write settings the guard then ignores.\n */\nexport function planInstall(cwd: string, choices: HooksChoices): InstallPlan {\n const files = new Map<string, PlannedFile>();\n const skipped: SkippedFile[] = [];\n const notes: string[] = [];\n\n // -- smelt.config.json: the guard's runtime settings live here, not in any harness\n // file, so every shim reads one source of truth.\n const configPath = findConfigFile(cwd) ?? join(cwd, CONFIG_FILE_NAME);\n const existingConfig =\n readIfExists(configPath) === undefined\n ? undefined\n : parseConfig(readFileSync(configPath, 'utf8'), configPath);\n const hooksBlock: SmeltConfigHooks = {\n thresholdBytes: choices.thresholdBytes,\n enforcement: choices.enforcement,\n };\n const budgetBytes = existingConfig?.defaultBudgetBytes ?? DEFAULT_SUGGESTION_BUDGET_BYTES;\n files.set(configPath, {\n name: portablePath(cwd, configPath),\n path: configPath,\n content: renderConfigWithHooks(existingConfig, hooksBlock),\n exists: existsSync(configPath),\n unchanged: readIfExists(configPath) === renderConfigWithHooks(existingConfig, hooksBlock),\n });\n\n const ctx: HarnessInstallContext = {\n cwd,\n guard: choices.guard,\n statsOnStop: choices.statsOnStop,\n mapOnStart: choices.mapOnStart,\n thresholdBytes: choices.thresholdBytes,\n budgetBytes,\n };\n const snippet = instructionSnippet(choices.thresholdBytes, budgetBytes);\n\n const planJsonHooks = (\n name: string,\n events: Record<string, readonly unknown[]>,\n shape: { readonly version?: number } = {},\n ): void => {\n const path = join(cwd, name);\n // Nothing to install and nothing to strip: don't create an empty hooks file.\n if (Object.keys(events).length === 0 && !existsSync(path)) return;\n const merged = mergeJsonHooks(readIfExists(path), events, shape);\n if (merged === undefined) {\n skipped.push({\n name,\n why: 'exists but is not a JSON object — fix or remove it, then re-run',\n });\n return;\n }\n files.set(path, planFile(cwd, name, merged));\n };\n\n const planBlockFile = (\n name: string,\n block: string,\n start: string,\n end: string,\n skipWhen?: { readonly contains: string; readonly why: string },\n ): void => {\n const path = join(cwd, name);\n if (files.has(path)) return; // a shared file (AGENTS.md), already planned\n const existing = readIfExists(path);\n // A file that already carries its owner's version of what this block does is\n // theirs to edit, not ours: say so, and touch nothing.\n if (\n skipWhen !== undefined &&\n existing !== undefined &&\n !existing.includes(start) &&\n existing.includes(skipWhen.contains)\n ) {\n skipped.push({ name, why: skipWhen.why });\n return;\n }\n files.set(path, planFile(cwd, name, upsertMarkerBlock(existing, block, start, end)));\n };\n\n for (const profile of choices.harnesses) {\n for (const step of profile.install) {\n switch (step.kind) {\n case 'json-hooks':\n planJsonHooks(\n step.file,\n jsonHookEvents(step, ctx, shimCommand(profile, cwd)),\n step.shape ?? {},\n );\n break;\n case 'marker-block':\n planBlockFile(step.file, step.block(ctx), step.start, step.end, step.skipWhen);\n break;\n case 'own-file':\n if (step.guardOnly && !ctx.guard) break;\n files.set(join(cwd, step.file), planFile(cwd, step.file, step.content(ctx), step.mode));\n break;\n }\n }\n\n if (profile.instructions === 'snippet') {\n planBlockFile(profile.instructionFile, snippet, SNIPPET_START_MD, SNIPPET_END_MD);\n } else {\n files.set(\n join(cwd, profile.instructionFile),\n planFile(cwd, profile.instructionFile, profile.instructions(ctx)),\n );\n }\n\n for (const caveat of profile.caveats) notes.push(`${profile.name}: ${caveat}`);\n }\n\n return { files: [...files.values()], skipped, notes };\n}\n\n/** Where the installed config points the persistent store, relative to the config file. */\nexport const DEFAULT_STORE_DIR = '.smelt/store';\n\n/**\n * Existing config re-rendered with the hooks block, other fields carried verbatim —\n * except that a config with **no** store block gains a directory store. The deny\n * reasons and the instruction snippet teach `smelt retrieve <hash>`, and retrieval\n * across processes needs a persistent store (`smelt retrieve` refuses a memory\n * store, exit 2) — an install whose own guard promises a command the installed\n * config cannot run would be the exact silent-failure shape this project refuses.\n * An *explicit* `{\"kind\":\"memory\"}` is respected; the guard then conditions its\n * retrieve promise on the store kind instead (`retrieveSentence` in guard-core).\n *\n * That store injection is this verb's **policy**, which is why it lives here; the\n * bytes are written by `renderConfig` in `config.ts`, the one writer, so a key added\n * to the schema reaches this file and `init`'s together or not at all.\n */\nexport function renderConfigWithHooks(\n existing: SmeltConfig | undefined,\n hooks: SmeltConfigHooks,\n): string {\n return renderConfig({\n smeltConfig: CONFIG_VERSION,\n ...(existing?.defaultBudgetBytes === undefined\n ? {}\n : { defaultBudgetBytes: existing.defaultBudgetBytes }),\n ...(existing?.strategy === undefined ? {} : { strategy: existing.strategy }),\n store: existing?.store ?? { kind: 'directory', path: DEFAULT_STORE_DIR },\n hooks,\n });\n}\n\n/**\n * Everything `remove` would delete or strip, computed against the current disk state.\n * The mirror image of {@link planInstall}, over the same data: each install step's\n * kind is also how it comes back out — a JSON hook file is strip-merged, a marker\n * block is stripped, a file that is entirely ours is deleted.\n */\nexport function planRemove(\n cwd: string,\n harnesses: readonly HarnessProfile[],\n): readonly PlannedRemoval[] {\n const removals = new Map<string, PlannedRemoval>();\n\n const planJsonStrip = (name: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined) return;\n const stripped = mergeJsonHooks(existing, {});\n if (stripped === undefined || stripped === existing) return;\n const remains: unknown = JSON.parse(stripped);\n const empty =\n typeof remains === 'object' &&\n remains !== null &&\n Object.keys(remains as Record<string, unknown>).filter((key) => key !== 'version').length ===\n 0;\n removals.set(\n path,\n empty && existing.includes('hooks')\n ? { name, path, action: 'delete' }\n : { name, path, action: 'modify', content: stripped },\n );\n };\n\n const planBlockStrip = (name: string, start: string, end: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined || !existing.includes(start)) return;\n const stripped = stripMarkerBlock(existing, start, end);\n removals.set(\n path,\n stripped === undefined\n ? { name, path, action: 'delete' }\n : { name, path, action: 'modify', content: stripped },\n );\n };\n\n const planWholeFileDelete = (name: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined || !existing.includes(OURS_TOKEN)) return;\n removals.set(path, { name, path, action: 'delete' });\n };\n\n for (const profile of harnesses) {\n for (const step of profile.install) {\n switch (step.kind) {\n case 'json-hooks':\n planJsonStrip(step.file);\n break;\n case 'marker-block':\n planBlockStrip(step.file, step.start, step.end);\n break;\n case 'own-file':\n planWholeFileDelete(step.file);\n break;\n }\n }\n if (profile.instructions === 'snippet') {\n planBlockStrip(profile.instructionFile, SNIPPET_START_MD, SNIPPET_END_MD);\n } else {\n planWholeFileDelete(profile.instructionFile);\n }\n }\n\n return [...removals.values()];\n}\n\n/* ------------------------------------------------------------------------------------\n * The wizard\n * ---------------------------------------------------------------------------------- */\n\ntype Asker = (prompt: string) => Promise<string>;\n\n/**\n * `smelt hooks <install|remove>`, start to finish. The same testability pattern as\n * `runInit`: a pure function over an input/output pair, exit code returned.\n */\nexport async function runHooks(\n action: 'install' | 'remove',\n harnessFlag: string | undefined,\n io: HooksIo,\n): Promise<number> {\n // Same adapter, same reason, as `runInit` — see the note on `answerReader`.\n const lines = answerReader(io.input);\n const ask = async (prompt: string): Promise<string> => {\n io.output(prompt);\n const next = await lines.next();\n if (next === undefined) {\n throw new CliUsageError(\n `${CLI_NAME} hooks: input ended before the wizard finished. ` +\n `Files already confirmed and written stay; nothing further was written.`,\n );\n }\n return next.trim();\n };\n\n try {\n return action === 'install'\n ? await installFlow(io, ask, harnessFlag)\n : await removeFlow(io, ask, harnessFlag);\n } finally {\n await lines.release();\n }\n}\n\nfunction resolveHarnessFlag(flag: string): HarnessProfile {\n const profile = harnessById(flag);\n if (profile === undefined) {\n throw new CliUsageError(\n `${CLI_NAME} hooks: unknown harness \"${flag}\". ` +\n `Known: ${HARNESSES.map((h) => h.id).join(', ')}.`,\n );\n }\n return profile;\n}\n\nfunction tierLabel(profile: HarnessProfile): string {\n return `${profile.id.padEnd(12)} ${profile.name.padEnd(14)} [${profile.tier}] — ${TIER_HONESTY[profile.tier]}`;\n}\n\nasync function installFlow(\n io: HooksIo,\n ask: Asker,\n harnessFlag: string | undefined,\n): Promise<number> {\n const home = io.home ?? homedir();\n const detected = detectedHarnesses(io.cwd, home);\n\n io.output(\n `${CLI_NAME} hooks install — wires the smelt guard into agent-harness hooks.\\n` +\n `Answer \\`back\\` at any step to return to the previous one. Nothing is written ` +\n `until you confirm at the end.\\n\\n`,\n );\n\n const choices: HooksChoices = {\n harnesses: harnessFlag !== undefined ? [resolveHarnessFlag(harnessFlag)] : [...detected],\n ...presetToggles(io.cwd),\n enforcement: 'deny',\n thresholdBytes: DEFAULT_THRESHOLD_BYTES,\n };\n\n // With --harness the selection step is skipped, so the tier label — and its one\n // line of honesty about what the tier means — is printed here instead.\n if (harnessFlag !== undefined) {\n for (const profile of choices.harnesses) io.output(` ${tierLabel(profile)}\\n`);\n }\n\n const steps: readonly ((io_: HooksIo, ask_: Asker) => Promise<'ok' | 'back'>)[] = [\n async (io_, ask_) =>\n harnessFlag !== undefined ? 'ok' : stepHarnesses(io_, ask_, choices, detected),\n async (io_, ask_) =>\n stepToggle(io_, ask_, 'PreToolUse size-guard', guardCopy(), choices.guard, (on) => {\n choices.guard = on;\n }),\n async (io_, ask_) =>\n stepToggle(\n io_,\n ask_,\n 'stats on Stop',\n `\\`smelt stats\\` runs when a session ends — the honest signal (expansion rate) ` +\n `surfaced where the turn ends. Observation only; never blocks. Wired for ` +\n `verified-tier harnesses (Claude Code, Codex).`,\n choices.statsOnStop,\n (on) => {\n choices.statsOnStop = on;\n },\n ),\n async (io_, ask_) =>\n stepToggle(\n io_,\n ask_,\n 'repo map on SessionStart',\n `\\`smelt map . --budget …\\` runs at session start and its output opens the ` +\n `context — the agent starts oriented. Costs one map build per session. ` +\n `Wired for verified-tier harnesses (Claude Code, Codex).`,\n choices.mapOnStart,\n (on) => {\n choices.mapOnStart = on;\n },\n ),\n async (io_, ask_) => stepEnforcement(io_, ask_, choices),\n async (io_, ask_) => stepThreshold(io_, ask_, choices),\n ];\n\n let index = 0;\n for (;;) {\n while (index < steps.length) {\n const outcome = await steps[index]!(io, ask);\n if (outcome === 'back') {\n if (index === 0) io.output(`This is the first step — there is nothing before it.\\n`);\n else index -= 1;\n } else {\n index += 1;\n }\n }\n if (choices.harnesses.length === 0) {\n io.output(`No harness selected. Nothing to do; nothing was written.\\n`);\n return 0;\n }\n const verdict = await confirmAndInstall(io, ask, choices);\n if (verdict !== 'back') return 0;\n index = steps.length - 1;\n }\n}\n\nfunction guardCopy(): string {\n return (\n `Denies raw Reads (and simple \\`cat\\`s) of files over the size threshold, with a ` +\n `reason naming the exact \\`smelt\\` replacement — the model still sees everything: ` +\n `smelted first, \\`smelt retrieve\\` for the rest. Windowed reads (offset/limit) ` +\n `always pass.`\n );\n}\n\nasync function stepHarnesses(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n detected: readonly HarnessProfile[],\n): Promise<'ok' | 'back'> {\n io.output(`\\nHarnesses — detected by their config directories (project or home):\\n`);\n for (const profile of HARNESSES) {\n const mark = detected.includes(profile) ? '*' : ' ';\n io.output(` ${mark} ${tierLabel(profile)}\\n`);\n }\n io.output(`(* = detected here)\\n`);\n for (;;) {\n const current = choices.harnesses.map((profile) => profile.id).join(',') || '(none)';\n const answer = await ask(\n `install for which? (comma-separated ids, Enter = ${current}, or back)\\n> `,\n );\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n const ids = answer\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id !== '');\n const chosen: HarnessProfile[] = [];\n let bad: string | undefined;\n for (const id of ids) {\n const profile = harnessById(id);\n if (profile === undefined) bad = id;\n else if (!chosen.includes(profile)) chosen.push(profile);\n }\n if (bad !== undefined) {\n io.output(`Unknown harness \"${bad}\". Known: ${HARNESSES.map((h) => h.id).join(', ')}.\\n`);\n continue;\n }\n choices.harnesses = chosen;\n return 'ok';\n }\n}\n\nasync function stepToggle(\n io: HooksIo,\n ask: Asker,\n name: string,\n copy: string,\n current: boolean,\n set: (on: boolean) => void,\n): Promise<'ok' | 'back'> {\n io.output(`\\n${name} — ${copy}\\n`);\n for (;;) {\n const answer = await ask(`${name}? (on/off) [${current ? 'on' : 'off'}] (or back)> `);\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n if (answer === 'on' || answer === 'off') {\n set(answer === 'on');\n return 'ok';\n }\n io.output(`on, off, or back.\\n`);\n }\n}\n\nasync function stepEnforcement(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'ok' | 'back'> {\n io.output(\n `\\nEnforcement — what happens when the guard catches an oversized raw read:\\n` +\n ` 1. deny — refuse with a reason naming the exact replacement command. The\\n` +\n ` transcript stays truthful; the model runs the replacement itself.\\n` +\n ` 2. rewrite — on harnesses whose hooks can modify tool input, substitute the\\n` +\n ` replacement in-flight (grep/cat piped through smelt). Never\\n` +\n ` silent: the substitution is announced in the decision reason\\n` +\n ` where the harness has one, on stderr where it does not.\\n` +\n ` Harnesses that cannot rewrite fall back to deny.\\n`,\n );\n for (;;) {\n const current = choices.enforcement === 'deny' ? '1' : '2';\n const answer = await ask(`enforcement (1/2) [${current}] (or back)> `);\n if (answer === 'back') return 'back';\n const pick = answer === '' ? current : answer;\n if (pick === '1' || pick === '2') {\n choices.enforcement = pick === '1' ? 'deny' : 'rewrite';\n return 'ok';\n }\n io.output(`1 for deny, 2 for rewrite, or back.\\n`);\n }\n}\n\nasync function stepThreshold(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'ok' | 'back'> {\n io.output(\n `\\nSize threshold — reads at or under this many bytes always pass. The ${String(\n DEFAULT_THRESHOLD_BYTES,\n )}-byte default comes from the measured validation in ` +\n `docs/research/2026-09-02-agent-enforcement.md § 5.\\n`,\n );\n for (;;) {\n const answer = await ask(`threshold in bytes [${String(choices.thresholdBytes)}] (or back)> `);\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n if (/^\\d+$/.test(answer) && Number(answer) > 0) {\n choices.thresholdBytes = Number(answer);\n return 'ok';\n }\n io.output(`A whole number of bytes greater than zero, e.g. 8192.\\n`);\n }\n}\n\n/**\n * A re-run reads the toggles back off what is actually installed — every JSON hook\n * file this installer writes, plus the guard-only shim files, both derived from the\n * registry — so it edits instead of resetting. Harnesses that only wire the guard\n * (gemini, grok, cursor, hermes, opencode, cline) persist no stats/map entries, so\n * after a re-run scoped to them those toggles read back as off; the defaults below\n * apply only when nothing of smelt's is installed at all.\n */\nfunction presetToggles(cwd: string): Pick<HooksChoices, 'guard' | 'statsOnStop' | 'mapOnStart'> {\n const defaults = { guard: true, statsOnStop: true, mapOnStart: false };\n let anyOurs = false;\n let guard = false;\n let statsOnStop = false;\n let mapOnStart = false;\n\n for (const name of JSON_HOOK_FILES) {\n const text = readIfExists(join(cwd, name));\n if (text === undefined) continue;\n let hooks: Record<string, unknown> | undefined;\n try {\n const parsed: unknown = JSON.parse(text);\n const hooksValue =\n typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)['hooks']\n : undefined;\n hooks =\n typeof hooksValue === 'object' && hooksValue !== null && !Array.isArray(hooksValue)\n ? (hooksValue as Record<string, unknown>)\n : undefined;\n } catch {\n hooks = undefined;\n }\n if (hooks === undefined) continue;\n const installed = hooks;\n const hasOurs = (event: string): boolean =>\n Array.isArray(installed[event]) &&\n (installed[event] as unknown[]).some((entry) => isOursEntry(entry));\n if (!MANAGED_EVENTS.some((event) => hasOurs(event))) continue;\n anyOurs = true;\n guard ||= GUARD_EVENTS.some((event) => hasOurs(event));\n statsOnStop ||= hasOurs('Stop');\n mapOnStart ||= hasOurs('SessionStart');\n }\n\n for (const name of GUARD_ONLY_FILES) {\n const text = readIfExists(join(cwd, name));\n if (text !== undefined && text.includes(OURS_TOKEN)) {\n anyOurs = true;\n guard = true;\n }\n }\n\n return anyOurs ? { guard, statsOnStop, mapOnStart } : defaults;\n}\n\nconst fileLabel = (file: PlannedFile): string => {\n if (file.unchanged) return 'unchanged — nothing to write';\n return file.exists ? 'exists — will ask before overwriting' : 'new';\n};\n\nasync function confirmAndInstall(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'done' | 'back'> {\n const plan = planInstall(io.cwd, choices);\n\n io.output(\n `\\nAbout to write, into ${io.cwd}:\\n` +\n plan.files.map((file) => ` ${file.name.padEnd(32)} (${fileLabel(file)})\\n`).join('') +\n plan.skipped.map((skip) => ` ${skip.name.padEnd(32)} (SKIPPED: ${skip.why})\\n`).join('') +\n `Nothing has been written yet.\\n`,\n );\n\n for (;;) {\n const answer = await ask(`confirm (yes / no / back)> `);\n if (answer === 'back') return 'back';\n if (answer === 'no') {\n io.output(`Nothing was written.\\n`);\n return 'done';\n }\n if (answer === 'yes') break;\n io.output(`yes to write, no to leave everything untouched, back to change a setting.\\n`);\n }\n\n for (const file of plan.files) {\n if (file.unchanged) {\n io.output(` ${file.name} — unchanged, not rewritten\\n`);\n continue;\n }\n if (file.exists) {\n // The one hard rule, same as `smelt init`: an existing file is never touched\n // without an explicit per-file yes — not `y`, not Enter, a literal `yes`.\n const answer = await ask(` ${file.name} exists — overwrite it? (yes/no)> `);\n if (answer !== 'yes') {\n io.output(` skipped ${file.name} — the existing file was not touched\\n`);\n continue;\n }\n }\n mkdirSync(dirname(file.path), { recursive: true });\n writeFileSync(file.path, file.content);\n if (file.mode !== undefined) chmodSync(file.path, file.mode);\n io.output(` wrote ${file.name}\\n`);\n }\n\n for (const note of plan.notes) io.output(`note: ${note}\\n`);\n io.output(\n `Done. Re-run \\`${CLI_NAME} hooks install\\` to edit toggles; ` +\n `\\`${CLI_NAME} hooks remove\\` takes it all back out.\\n`,\n );\n return 'done';\n}\n\nasync function removeFlow(\n io: HooksIo,\n ask: Asker,\n harnessFlag: string | undefined,\n): Promise<number> {\n const harnesses = harnessFlag !== undefined ? [resolveHarnessFlag(harnessFlag)] : [...HARNESSES];\n const removals = planRemove(io.cwd, harnesses);\n\n if (removals.length === 0) {\n io.output(`${CLI_NAME} hooks remove: nothing of smelt's found to remove in ${io.cwd}.\\n`);\n return 0;\n }\n\n io.output(\n `${CLI_NAME} hooks remove — takes smelt's hook wiring back out.\\n\\nPlanned:\\n` +\n removals\n .map(\n (removal) =>\n ` ${removal.name.padEnd(32)} (${\n removal.action === 'delete' ? 'delete' : 'remove smelt entries, keep the rest'\n })\\n`,\n )\n .join('') +\n `${CONFIG_FILE_NAME} is left untouched — its hooks block is your config now; ` +\n `edit or remove it there.\\nNothing has been changed yet.\\n`,\n );\n\n for (;;) {\n const answer = await ask(`confirm (yes / no)> `);\n if (answer === 'no') {\n io.output(`Nothing was changed.\\n`);\n return 0;\n }\n if (answer === 'yes') break;\n io.output(`yes to proceed, no to leave everything untouched.\\n`);\n }\n\n for (const removal of removals) {\n const verb = removal.action === 'delete' ? 'delete' : 'modify';\n const answer = await ask(` ${removal.name} — ${verb} it? (yes/no)> `);\n if (answer !== 'yes') {\n io.output(` skipped ${removal.name} — not touched\\n`);\n continue;\n }\n if (removal.action === 'delete') {\n unlinkSync(removal.path);\n io.output(` deleted ${removal.name}\\n`);\n } else {\n writeFileSync(removal.path, removal.content ?? '');\n io.output(` cleaned ${removal.name}\\n`);\n }\n }\n io.output(`Done.\\n`);\n return 0;\n}\n"]}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/cli/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nimport { CliUsageError } from '../errors.ts';\nimport { nodeCommand, portablePath, shimScriptPath, smeltBinPath } from '../harness/paths.ts';\nimport { hasShim, TIER_HONESTY } from '../harness/profile.ts';\nimport type {\n HarnessInstallContext,\n HarnessJsonHooks,\n HarnessProfile,\n} from '../harness/profile.ts';\nimport {\n GUARD_EVENTS,\n GUARD_ONLY_FILES,\n HARNESSES,\n harnessById,\n harnessNames,\n JSON_HOOK_FILES,\n LIFECYCLE_EVENTS,\n lifecycleHarnesses,\n MANAGED_EVENTS,\n} from '../harness/registry.ts';\nimport {\n instructionSnippet,\n OURS_TOKEN,\n SNIPPET_END_MD,\n SNIPPET_START_MD,\n} from '../harness/snippet.ts';\nimport { DEFAULT_SUGGESTION_BUDGET_BYTES, DEFAULT_THRESHOLD_BYTES } from '../hooks/guard-core.ts';\nimport type { EnforcementMode } from '../hooks/guard-core.ts';\nimport {\n editTopLevelProperty,\n jsonStyle,\n stripMarkerBlock,\n upsertMarkerBlock,\n} from '../text/json-edit.ts';\n\nimport { answerReader, CLI_NAME } from './shell.ts';\nimport type { AnswerStream } from './shell.ts';\nimport {\n CONFIG_FILE_NAME,\n CONFIG_VERSION,\n findConfigFile,\n parseConfig,\n renderConfig,\n} from './config.ts';\nimport type { SmeltConfig, SmeltConfigHooks } from './config.ts';\n\n/**\n * `smelt hooks install` / `smelt hooks remove` — the multi-harness guard preset.\n *\n * The design: one zero-dependency guard core\n * (`src/hooks/guard-core.ts`), thin per-harness shims mapping each harness's native\n * hook schema onto it, and this installer, which writes the harness config that wires\n * a shim in — plus an instruction-file snippet as belt and braces, because the\n * snippet is also what teaches the model to run `smelt retrieve` after a deny.\n *\n * Every per-harness fact lives in that harness's {@link HarnessProfile}\n * (`src/harness/<id>.ts`), including what to write and how to take it back out. This\n * module owns only what is the *same* for every harness: the hooks merge (which entries\n * are ours, what a re-run replaces), the wizard, and the two plans below — folds over\n * `profile.install`, with no case list of its own. The byte-faithful editing itself —\n * one top-level JSON property, one delimited text block — is `src/text/json-edit.ts`,\n * which knows nothing about harnesses.\n *\n * Harnesses come in three honesty tiers (docs/research/2026-09-02-harness-capability-matrix.md),\n * and which harness sits at which is `HarnessProfile.tier` — read through\n * `harnessesByTier()`, never listed again here:\n *\n * - **verified** — schemas verified against primary docs and exercised against\n * recorded fixtures; first-class targets.\n * - **experimental** — hook schemas mapped from the capability matrix but not yet\n * smoke-tested green against the real binary. Labelled as such in code, docs, and\n * this installer's output.\n * - **advisory** — no usable hook API, so what ships is instructions (and, for\n * KiloCode, a permissions/MCP sketch). Nothing enforces them, and the output says\n * so rather than implying a guard exists.\n *\n * The wizard discipline is `smelt init`'s, verbatim: every step accepts `back`,\n * nothing is written until a final confirm that lists every file, and an existing\n * file is never overwritten without an explicit per-file `yes` — guarded by\n * `test/guards/hooks-preset.test.ts`, with mutation `hooks-install-overwrite-without-consent`\n * proving the guard goes red.\n */\n\n/** Where the wizard's bytes come from and go. Injected so `runHooks` tests in-process. */\nexport interface HooksIo {\n /**\n * Scripted answers in, one line at a time. Structural on purpose; see\n * {@link AnswerStream}.\n */\n readonly input: AnswerStream;\n readonly output: (text: string) => void;\n /** Project directory: detection, config discovery, and every write are relative to it. */\n readonly cwd: string;\n /** Home directory for detection only. Tests point it at a temp dir; nothing writes here. */\n readonly home?: string;\n}\n\nexport { instructionSnippet, SNIPPET_END_MD, SNIPPET_START_MD };\n\n/** A harness whose config directory exists in the project or the home directory. */\nexport function detectedHarnesses(cwd: string, home: string): readonly HarnessProfile[] {\n return HARNESSES.filter(\n (profile) =>\n profile.detect.some((path) => existsSync(join(cwd, path))) ||\n profile.detectHome.some((path) => existsSync(join(home, path))),\n );\n}\n\n/* ------------------------------------------------------------------------------------\n * Generated content\n * ---------------------------------------------------------------------------------- */\n\n/** Claude-style hook entry: one command under an optional matcher. */\nfunction commandEntry(matcher: string | undefined, command: string): unknown {\n return {\n ...(matcher === undefined ? {} : { matcher }),\n hooks: [{ type: 'command', command }],\n };\n}\n\n/**\n * The hook command a harness's entries run: its own shim script, through node.\n *\n * @throws {Error} when a profile declares a JSON hook file but ships no shim — a\n * registry bug, pinned by `test/guards/harness-registry.test.ts`, not a user error.\n */\nfunction shimCommand(profile: HarnessProfile, cwd: string): string {\n /* v8 ignore next 5 -- unreachable: pinned by the harness-registry guard */\n if (!hasShim(profile)) {\n throw new Error(\n `smelt: harness \"${profile.id}\" wires a hook command but ships no shim script.`,\n );\n }\n return nodeCommand(cwd, shimScriptPath(profile));\n}\n\n/**\n * One harness's hook entries: the guard under each matcher its schema spells, plus\n * the session-lifecycle hooks for the harnesses whose schema carries them. Every\n * toggle the wizard offers is a key that is present or absent here — an absent key is\n * how a re-run turns a toggle *off*, because the merge deletes what it no longer sees.\n *\n * The two `SessionStart` toggles — the opening map and the instruction-file lint —\n * are **concatenated into one array**, not spread as two objects. Spreading would put\n * the same computed key twice in one literal, and the second would silently replace\n * the first: turning the lint on would turn the map off, with no error anywhere. It is\n * the shape of bug this file exists to refuse, one layer up from the config it writes.\n */\nfunction jsonHookEvents(\n step: HarnessJsonHooks,\n ctx: HarnessInstallContext,\n command: string,\n): Record<string, readonly unknown[]> {\n // The trailing shell comment tags the entry as this installer's (see isOursEntry):\n // a bare `cli/bin.js` substring would also match some other npm CLI's built binary.\n const stats = `${nodeCommand(ctx.cwd, smeltBinPath(), 'stats')} 2>/dev/null || true # ${OURS_TOKEN}`;\n const map = `${nodeCommand(\n ctx.cwd,\n smeltBinPath(),\n `${MAP_ON_START_ARGS} --budget ${String(ctx.budgetBytes)} --cache .smelt/tags`,\n )} 2>/dev/null || true # ${OURS_TOKEN}`;\n const lint = `${nodeCommand(\n ctx.cwd,\n smeltBinPath(),\n AGENTS_LINT_ARGS,\n )} 2>/dev/null || true # ${OURS_TOKEN}`;\n\n const sessionStart = [\n ...(ctx.mapOnStart ? [commandEntry(SESSION_START_MATCHER, map)] : []),\n ...(ctx.lintOnStart ? [commandEntry(SESSION_START_MATCHER, lint)] : []),\n ];\n\n return {\n ...(ctx.guard\n ? {\n [step.event]: step.matchers.map((matcher) =>\n step.entry === 'bare-command' ? { command } : commandEntry(matcher, command),\n ),\n }\n : {}),\n ...(step.lifecycle && ctx.statsOnStop\n ? { [LIFECYCLE_EVENTS.stats]: [commandEntry(undefined, stats)] }\n : {}),\n ...(step.lifecycle && sessionStart.length > 0 ? { [LIFECYCLE_EVENTS.map]: sessionStart } : {}),\n };\n}\n\n/** The matcher both `SessionStart` entries fire under — a session opening, however. */\nconst SESSION_START_MATCHER = 'startup|resume|clear|compact';\n\n/**\n * The two `SessionStart` commands' distinguishing arguments, and **the substrings a\n * re-run recognises each entry by**. Spelled once so the writer and the reader cannot\n * drift: `presetToggles` tells the two entries apart by the command each one runs, and\n * a wizard that wrote `agents lint .` while its reader looked for `agents lint` would\n * read every re-run's lint toggle back as off and quietly delete it.\n */\nconst MAP_ON_START_ARGS = 'map .';\nexport const AGENTS_LINT_ARGS = 'agents lint .';\n\n/**\n * True for a hook entry this installer wrote. Matched on the shim script paths and the\n * `smelt:hooks` token the stats/map commands carry — never on a substring as generic\n * as `cli/bin.js`, which another npm CLI's built binary could share: remove and\n * re-install may only ever touch entries that are provably smelt's.\n */\nfunction isOursEntry(entry: unknown): boolean {\n const text = JSON.stringify(entry) ?? '';\n return text.includes('hooks/shims/') || text.includes(OURS_TOKEN);\n}\n\n/**\n * Merge our hook entries into a JSON settings file, preserving everything foreign\n * **byte-faithfully**: the merged `hooks` value is spliced into the original text, so\n * unknown top-level keys, string escapes, number spellings, indentation and key order\n * outside the `hooks` property ride through verbatim (an installer\n * that reformats somebody's settings file has edited what it was never asked to).\n * Inside `hooks`, unmanaged events and other people's entries under managed events\n * are preserved; our previous entries are replaced (that is what makes a re-run edit\n * toggles), and events left with no entries disappear. A semantic no-op returns the\n * input text unchanged. Returns `undefined` when the existing file is not a JSON\n * object — the caller skips the file rather than clobbering something it cannot\n * understand.\n */\nexport function mergeJsonHooks(\n existingText: string | undefined,\n events: Record<string, readonly unknown[]>,\n shape: { readonly version?: number } = {},\n): string | undefined {\n let root: Record<string, unknown> = {};\n if (existingText !== undefined) {\n try {\n const parsed: unknown = JSON.parse(existingText);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return undefined;\n root = parsed as Record<string, unknown>;\n } catch {\n return undefined;\n }\n }\n const hooksValue = root['hooks'];\n const existingHooks =\n typeof hooksValue === 'object' && hooksValue !== null && !Array.isArray(hooksValue)\n ? (hooksValue as Record<string, unknown>)\n : undefined;\n const hooks = { ...existingHooks };\n\n for (const event of MANAGED_EVENTS) {\n const existing = Array.isArray(hooks[event]) ? (hooks[event] as unknown[]) : [];\n const foreign = existing.filter((entry) => !isOursEntry(entry));\n const ours = events[event] ?? [];\n const merged = [...foreign, ...ours];\n if (merged.length > 0) hooks[event] = merged;\n else delete hooks[event];\n }\n\n const mergedHooks = Object.keys(hooks).length > 0 ? hooks : undefined;\n\n // A brand-new file: nothing to preserve, render fresh two-space JSON.\n if (existingText === undefined) {\n const fresh: Record<string, unknown> = {};\n if (mergedHooks !== undefined) fresh['hooks'] = mergedHooks;\n if (shape.version !== undefined) fresh['version'] = shape.version;\n return `${JSON.stringify(fresh, null, 2)}\\n`;\n }\n\n const hooksChanged =\n JSON.stringify(existingHooks ?? null) !== JSON.stringify(mergedHooks ?? null);\n const needsVersion = shape.version !== undefined && root['version'] === undefined;\n if (!hooksChanged && !needsVersion) return existingText;\n\n // The style is read once, off the original: a second edit must match the first.\n const style = jsonStyle(existingText);\n let text: string | undefined = existingText;\n\n if (hooksChanged) {\n text = editTopLevelProperty(text, 'hooks', mergedHooks, style);\n /* v8 ignore next -- unreachable: JSON.parse accepted the same text above */\n if (text === undefined) return undefined;\n }\n if (needsVersion) {\n text = editTopLevelProperty(text, 'version', shape.version, style);\n /* v8 ignore next -- unreachable: every splice above keeps the text valid JSON */\n if (text === undefined) return undefined;\n }\n return text;\n}\n\n/* ------------------------------------------------------------------------------------\n * Planning\n * ---------------------------------------------------------------------------------- */\n\ninterface PlannedFile {\n /** Display path, relative to the project. */\n readonly name: string;\n readonly path: string;\n readonly content: string;\n readonly exists: boolean;\n readonly unchanged: boolean;\n /** chmod after writing (the cline hook must be executable). */\n readonly mode?: number;\n}\n\ninterface SkippedFile {\n readonly name: string;\n readonly why: string;\n}\n\ninterface PlannedRemoval {\n readonly name: string;\n readonly path: string;\n /** `'delete'` removes the file; `'modify'` writes `content` (ours stripped out). */\n readonly action: 'delete' | 'modify';\n readonly content?: string;\n}\n\nexport interface HooksChoices {\n harnesses: HarnessProfile[];\n guard: boolean;\n statsOnStop: boolean;\n mapOnStart: boolean;\n lintOnStart: boolean;\n enforcement: EnforcementMode;\n thresholdBytes: number;\n}\n\ninterface InstallPlan {\n readonly files: readonly PlannedFile[];\n readonly skipped: readonly SkippedFile[];\n readonly notes: readonly string[];\n}\n\nfunction planFile(cwd: string, name: string, content: string, mode?: number): PlannedFile {\n const path = join(cwd, name);\n const exists = existsSync(path);\n const unchanged = exists && readFileSync(path, 'utf8') === content;\n return { name, path, content, exists, unchanged, ...(mode === undefined ? {} : { mode }) };\n}\n\nfunction readIfExists(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf8') : undefined;\n}\n\n/**\n * Every file `install` would write, computed against the current disk state — pure\n * planning, nothing written. A fold over each chosen profile's `install` list and its\n * instruction layer; all per-harness knowledge is in the profiles. Shared instruction\n * files (several harnesses read AGENTS.md) are planned once.\n *\n * @throws {CliUsageError} when an existing `smelt.config.json` is malformed — the\n * same refusal every other subcommand makes; an installer that guessed around a\n * broken config would write settings the guard then ignores.\n */\nexport function planInstall(cwd: string, choices: HooksChoices): InstallPlan {\n const files = new Map<string, PlannedFile>();\n const skipped: SkippedFile[] = [];\n const notes: string[] = [];\n\n // -- smelt.config.json: the guard's runtime settings live here, not in any harness\n // file, so every shim reads one source of truth.\n const configPath = findConfigFile(cwd) ?? join(cwd, CONFIG_FILE_NAME);\n const existingConfig =\n readIfExists(configPath) === undefined\n ? undefined\n : parseConfig(readFileSync(configPath, 'utf8'), configPath);\n const hooksBlock: SmeltConfigHooks = {\n thresholdBytes: choices.thresholdBytes,\n enforcement: choices.enforcement,\n };\n const budgetBytes = existingConfig?.defaultBudgetBytes ?? DEFAULT_SUGGESTION_BUDGET_BYTES;\n files.set(configPath, {\n name: portablePath(cwd, configPath),\n path: configPath,\n content: renderConfigWithHooks(existingConfig, hooksBlock),\n exists: existsSync(configPath),\n unchanged: readIfExists(configPath) === renderConfigWithHooks(existingConfig, hooksBlock),\n });\n\n const ctx: HarnessInstallContext = {\n cwd,\n guard: choices.guard,\n statsOnStop: choices.statsOnStop,\n mapOnStart: choices.mapOnStart,\n lintOnStart: choices.lintOnStart,\n thresholdBytes: choices.thresholdBytes,\n budgetBytes,\n };\n const snippet = instructionSnippet(choices.thresholdBytes, budgetBytes);\n\n const planJsonHooks = (\n name: string,\n events: Record<string, readonly unknown[]>,\n shape: { readonly version?: number } = {},\n ): void => {\n const path = join(cwd, name);\n // Nothing to install and nothing to strip: don't create an empty hooks file.\n if (Object.keys(events).length === 0 && !existsSync(path)) return;\n const merged = mergeJsonHooks(readIfExists(path), events, shape);\n if (merged === undefined) {\n skipped.push({\n name,\n why: 'exists but is not a JSON object — fix or remove it, then re-run',\n });\n return;\n }\n files.set(path, planFile(cwd, name, merged));\n };\n\n const planBlockFile = (\n name: string,\n block: string,\n start: string,\n end: string,\n skipWhen?: { readonly contains: string; readonly why: string },\n ): void => {\n const path = join(cwd, name);\n if (files.has(path)) return; // a shared file (AGENTS.md), already planned\n const existing = readIfExists(path);\n // A file that already carries its owner's version of what this block does is\n // theirs to edit, not ours: say so, and touch nothing.\n if (\n skipWhen !== undefined &&\n existing !== undefined &&\n !existing.includes(start) &&\n existing.includes(skipWhen.contains)\n ) {\n skipped.push({ name, why: skipWhen.why });\n return;\n }\n files.set(path, planFile(cwd, name, upsertMarkerBlock(existing, block, start, end)));\n };\n\n for (const profile of choices.harnesses) {\n for (const step of profile.install) {\n switch (step.kind) {\n case 'json-hooks':\n planJsonHooks(\n step.file,\n jsonHookEvents(step, ctx, shimCommand(profile, cwd)),\n step.shape ?? {},\n );\n break;\n case 'marker-block':\n planBlockFile(step.file, step.block(ctx), step.start, step.end, step.skipWhen);\n break;\n case 'own-file':\n if (step.guardOnly && !ctx.guard) break;\n files.set(join(cwd, step.file), planFile(cwd, step.file, step.content(ctx), step.mode));\n break;\n }\n }\n\n if (profile.instructions === 'snippet') {\n planBlockFile(profile.instructionFile, snippet, SNIPPET_START_MD, SNIPPET_END_MD);\n } else {\n files.set(\n join(cwd, profile.instructionFile),\n planFile(cwd, profile.instructionFile, profile.instructions(ctx)),\n );\n }\n\n for (const caveat of profile.caveats) notes.push(`${profile.name}: ${caveat}`);\n }\n\n return { files: [...files.values()], skipped, notes };\n}\n\n/** Where the installed config points the persistent store, relative to the config file. */\nexport const DEFAULT_STORE_DIR = '.smelt/store';\n\n/**\n * Existing config re-rendered with the hooks block, other fields carried verbatim —\n * except that a config with **no** store block gains a directory store. The deny\n * reasons and the instruction snippet teach `smelt retrieve <hash>`, and retrieval\n * across processes needs a persistent store (`smelt retrieve` refuses a memory\n * store, exit 2) — an install whose own guard promises a command the installed\n * config cannot run would be the exact silent-failure shape this project refuses.\n * An *explicit* `{\"kind\":\"memory\"}` is respected; the guard then conditions its\n * retrieve promise on the store kind instead (`retrieveSentence` in guard-core).\n *\n * That store injection is this verb's **policy**, which is why it lives here; the\n * bytes are written by `renderConfig` in `config.ts`, the one writer, so a key added\n * to the schema reaches this file and `init`'s together or not at all.\n *\n * \"Carried verbatim\" is spelled as a spread rather than as a list of the fields to\n * copy, and that is load-bearing: the list version silently dropped every key nobody\n * remembered to add to it — `agents` was added to the schema and this function kept\n * writing configs without it, which is a setting the user believed was in force,\n * caught by `test/guards/config-writer.test.ts`. Only the two fields this verb\n * actually decides are named.\n */\nexport function renderConfigWithHooks(\n existing: SmeltConfig | undefined,\n hooks: SmeltConfigHooks,\n): string {\n return renderConfig({\n ...existing,\n smeltConfig: CONFIG_VERSION,\n store: existing?.store ?? { kind: 'directory', path: DEFAULT_STORE_DIR },\n hooks,\n });\n}\n\n/**\n * Everything `remove` would delete or strip, computed against the current disk state.\n * The mirror image of {@link planInstall}, over the same data: each install step's\n * kind is also how it comes back out — a JSON hook file is strip-merged, a marker\n * block is stripped, a file that is entirely ours is deleted.\n */\nexport function planRemove(\n cwd: string,\n harnesses: readonly HarnessProfile[],\n): readonly PlannedRemoval[] {\n const removals = new Map<string, PlannedRemoval>();\n\n const planJsonStrip = (name: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined) return;\n const stripped = mergeJsonHooks(existing, {});\n if (stripped === undefined || stripped === existing) return;\n const remains: unknown = JSON.parse(stripped);\n const empty =\n typeof remains === 'object' &&\n remains !== null &&\n Object.keys(remains as Record<string, unknown>).filter((key) => key !== 'version').length ===\n 0;\n removals.set(\n path,\n empty && existing.includes('hooks')\n ? { name, path, action: 'delete' }\n : { name, path, action: 'modify', content: stripped },\n );\n };\n\n const planBlockStrip = (name: string, start: string, end: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined || !existing.includes(start)) return;\n const stripped = stripMarkerBlock(existing, start, end);\n removals.set(\n path,\n stripped === undefined\n ? { name, path, action: 'delete' }\n : { name, path, action: 'modify', content: stripped },\n );\n };\n\n const planWholeFileDelete = (name: string): void => {\n const path = join(cwd, name);\n const existing = readIfExists(path);\n if (existing === undefined || !existing.includes(OURS_TOKEN)) return;\n removals.set(path, { name, path, action: 'delete' });\n };\n\n for (const profile of harnesses) {\n for (const step of profile.install) {\n switch (step.kind) {\n case 'json-hooks':\n planJsonStrip(step.file);\n break;\n case 'marker-block':\n planBlockStrip(step.file, step.start, step.end);\n break;\n case 'own-file':\n planWholeFileDelete(step.file);\n break;\n }\n }\n if (profile.instructions === 'snippet') {\n planBlockStrip(profile.instructionFile, SNIPPET_START_MD, SNIPPET_END_MD);\n } else {\n planWholeFileDelete(profile.instructionFile);\n }\n }\n\n return [...removals.values()];\n}\n\n/* ------------------------------------------------------------------------------------\n * The wizard\n * ---------------------------------------------------------------------------------- */\n\ntype Asker = (prompt: string) => Promise<string>;\n\n/**\n * `smelt hooks <install|remove>`, start to finish. The same testability pattern as\n * `runInit`: a pure function over an input/output pair, exit code returned.\n */\nexport async function runHooks(\n action: 'install' | 'remove',\n harnessFlag: string | undefined,\n io: HooksIo,\n): Promise<number> {\n // Same adapter, same reason, as `runInit` — see the note on `answerReader`.\n const lines = answerReader(io.input);\n const ask = async (prompt: string): Promise<string> => {\n io.output(prompt);\n const next = await lines.next();\n if (next === undefined) {\n throw new CliUsageError(\n `${CLI_NAME} hooks: input ended before the wizard finished. ` +\n `Files already confirmed and written stay; nothing further was written.`,\n );\n }\n return next.trim();\n };\n\n try {\n return action === 'install'\n ? await installFlow(io, ask, harnessFlag)\n : await removeFlow(io, ask, harnessFlag);\n } finally {\n await lines.release();\n }\n}\n\nfunction resolveHarnessFlag(flag: string): HarnessProfile {\n const profile = harnessById(flag);\n if (profile === undefined) {\n throw new CliUsageError(\n `${CLI_NAME} hooks: unknown harness \"${flag}\". ` +\n `Known: ${HARNESSES.map((h) => h.id).join(', ')}.`,\n );\n }\n return profile;\n}\n\nfunction tierLabel(profile: HarnessProfile): string {\n return `${profile.id.padEnd(12)} ${profile.name.padEnd(14)} [${profile.tier}] — ${TIER_HONESTY[profile.tier]}`;\n}\n\nasync function installFlow(\n io: HooksIo,\n ask: Asker,\n harnessFlag: string | undefined,\n): Promise<number> {\n const home = io.home ?? homedir();\n const detected = detectedHarnesses(io.cwd, home);\n\n io.output(\n `${CLI_NAME} hooks install — wires the smelt guard into agent-harness hooks.\\n` +\n `Answer \\`back\\` at any step to return to the previous one. Nothing is written ` +\n `until you confirm at the end.\\n\\n`,\n );\n\n const choices: HooksChoices = {\n harnesses: harnessFlag !== undefined ? [resolveHarnessFlag(harnessFlag)] : [...detected],\n ...presetToggles(io.cwd),\n enforcement: 'deny',\n thresholdBytes: DEFAULT_THRESHOLD_BYTES,\n };\n\n // With --harness the selection step is skipped, so the tier label — and its one\n // line of honesty about what the tier means — is printed here instead.\n if (harnessFlag !== undefined) {\n for (const profile of choices.harnesses) io.output(` ${tierLabel(profile)}\\n`);\n }\n\n const steps: readonly ((io_: HooksIo, ask_: Asker) => Promise<'ok' | 'back'>)[] = [\n async (io_, ask_) =>\n harnessFlag !== undefined ? 'ok' : stepHarnesses(io_, ask_, choices, detected),\n async (io_, ask_) =>\n stepToggle(io_, ask_, 'PreToolUse size-guard', guardCopy(), choices.guard, (on) => {\n choices.guard = on;\n }),\n async (io_, ask_) =>\n stepToggle(\n io_,\n ask_,\n 'stats on Stop',\n `\\`smelt stats\\` runs when a session ends — the honest signal (expansion rate) ` +\n `surfaced where the turn ends. Observation only; never blocks. Wired for the ` +\n `harnesses whose hooks carry session events (${harnessNames(lifecycleHarnesses())}).`,\n choices.statsOnStop,\n (on) => {\n choices.statsOnStop = on;\n },\n ),\n async (io_, ask_) =>\n stepToggle(\n io_,\n ask_,\n 'repo map on SessionStart',\n `\\`smelt map . --budget …\\` runs at session start and its output opens the ` +\n `context — the agent starts oriented. Costs one map build per session. ` +\n `Wired for the harnesses whose hooks carry session events ` +\n `(${harnessNames(lifecycleHarnesses())}).`,\n choices.mapOnStart,\n (on) => {\n choices.mapOnStart = on;\n },\n ),\n async (io_, ask_) =>\n stepToggle(\n io_,\n ask_,\n 'instruction-file lint on SessionStart',\n `\\`smelt agents lint .\\` runs at session start and reports on the AGENTS.md, ` +\n `CLAUDE.md and GEMINI.md this session is about to load on every request — ` +\n `bytes per level, and any path or link in them that no longer resolves. ` +\n `Advisory: it never blocks, and it exits 0 unless you set ` +\n `agents.budgetBytes in ${CONFIG_FILE_NAME}. Wired for verified-tier ` +\n `harnesses (Claude Code, Codex).`,\n choices.lintOnStart,\n (on) => {\n choices.lintOnStart = on;\n },\n ),\n async (io_, ask_) => stepEnforcement(io_, ask_, choices),\n async (io_, ask_) => stepThreshold(io_, ask_, choices),\n ];\n\n let index = 0;\n for (;;) {\n while (index < steps.length) {\n const outcome = await steps[index]!(io, ask);\n if (outcome === 'back') {\n if (index === 0) io.output(`This is the first step — there is nothing before it.\\n`);\n else index -= 1;\n } else {\n index += 1;\n }\n }\n if (choices.harnesses.length === 0) {\n io.output(`No harness selected. Nothing to do; nothing was written.\\n`);\n return 0;\n }\n const verdict = await confirmAndInstall(io, ask, choices);\n if (verdict !== 'back') return 0;\n index = steps.length - 1;\n }\n}\n\nfunction guardCopy(): string {\n return (\n `Denies raw Reads (and simple \\`cat\\`s) of files over the size threshold, with a ` +\n `reason naming the exact \\`smelt\\` replacement — the model still sees everything: ` +\n `smelted first, \\`smelt retrieve\\` for the rest. Windowed reads (offset/limit) ` +\n `always pass.`\n );\n}\n\nasync function stepHarnesses(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n detected: readonly HarnessProfile[],\n): Promise<'ok' | 'back'> {\n io.output(`\\nHarnesses — detected by their config directories (project or home):\\n`);\n for (const profile of HARNESSES) {\n const mark = detected.includes(profile) ? '*' : ' ';\n io.output(` ${mark} ${tierLabel(profile)}\\n`);\n }\n io.output(`(* = detected here)\\n`);\n for (;;) {\n const current = choices.harnesses.map((profile) => profile.id).join(',') || '(none)';\n const answer = await ask(\n `install for which? (comma-separated ids, Enter = ${current}, or back)\\n> `,\n );\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n const ids = answer\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id !== '');\n const chosen: HarnessProfile[] = [];\n let bad: string | undefined;\n for (const id of ids) {\n const profile = harnessById(id);\n if (profile === undefined) bad = id;\n else if (!chosen.includes(profile)) chosen.push(profile);\n }\n if (bad !== undefined) {\n io.output(`Unknown harness \"${bad}\". Known: ${HARNESSES.map((h) => h.id).join(', ')}.\\n`);\n continue;\n }\n choices.harnesses = chosen;\n return 'ok';\n }\n}\n\nasync function stepToggle(\n io: HooksIo,\n ask: Asker,\n name: string,\n copy: string,\n current: boolean,\n set: (on: boolean) => void,\n): Promise<'ok' | 'back'> {\n io.output(`\\n${name} — ${copy}\\n`);\n for (;;) {\n const answer = await ask(`${name}? (on/off) [${current ? 'on' : 'off'}] (or back)> `);\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n if (answer === 'on' || answer === 'off') {\n set(answer === 'on');\n return 'ok';\n }\n io.output(`on, off, or back.\\n`);\n }\n}\n\nasync function stepEnforcement(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'ok' | 'back'> {\n io.output(\n `\\nEnforcement — what happens when the guard catches an oversized raw read:\\n` +\n ` 1. deny — refuse with a reason naming the exact replacement command. The\\n` +\n ` transcript stays truthful; the model runs the replacement itself.\\n` +\n ` 2. rewrite — on harnesses whose hooks can modify tool input, substitute the\\n` +\n ` replacement in-flight (grep/cat piped through smelt). Never\\n` +\n ` silent: the substitution is announced in the decision reason\\n` +\n ` where the harness has one, on stderr where it does not.\\n` +\n ` Harnesses that cannot rewrite fall back to deny.\\n`,\n );\n for (;;) {\n const current = choices.enforcement === 'deny' ? '1' : '2';\n const answer = await ask(`enforcement (1/2) [${current}] (or back)> `);\n if (answer === 'back') return 'back';\n const pick = answer === '' ? current : answer;\n if (pick === '1' || pick === '2') {\n choices.enforcement = pick === '1' ? 'deny' : 'rewrite';\n return 'ok';\n }\n io.output(`1 for deny, 2 for rewrite, or back.\\n`);\n }\n}\n\nasync function stepThreshold(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'ok' | 'back'> {\n io.output(\n `\\nSize threshold — reads at or under this many bytes always pass. The ${String(\n DEFAULT_THRESHOLD_BYTES,\n )}-byte default comes from the measured validation in ` +\n `docs/research/2026-09-02-agent-enforcement.md § 5.\\n`,\n );\n for (;;) {\n const answer = await ask(`threshold in bytes [${String(choices.thresholdBytes)}] (or back)> `);\n if (answer === 'back') return 'back';\n if (answer === '') return 'ok';\n if (/^\\d+$/.test(answer) && Number(answer) > 0) {\n choices.thresholdBytes = Number(answer);\n return 'ok';\n }\n io.output(`A whole number of bytes greater than zero, e.g. 8192.\\n`);\n }\n}\n\n/**\n * A re-run reads the toggles back off what is actually installed — every JSON hook\n * file this installer writes, plus the guard-only shim files, both derived from the\n * registry — so it edits instead of resetting. Harnesses that only wire the guard\n * (gemini, grok, cursor, hermes, opencode, cline) persist no stats/map entries, so\n * after a re-run scoped to them those toggles read back as off; the defaults below\n * apply only when nothing of smelt's is installed at all.\n *\n * The two `SessionStart` toggles share one event, so they are told apart by **the\n * command each entry runs**, not by the key it sits under. Reading `SessionStart` as\n * one boolean would make a re-run with the map on and the lint off write both back —\n * or neither — which is a toggle the user believed they had set.\n */\nfunction presetToggles(\n cwd: string,\n): Pick<HooksChoices, 'guard' | 'statsOnStop' | 'mapOnStart' | 'lintOnStart'> {\n const defaults = { guard: true, statsOnStop: true, mapOnStart: false, lintOnStart: false };\n let anyOurs = false;\n let guard = false;\n let statsOnStop = false;\n let mapOnStart = false;\n let lintOnStart = false;\n\n for (const name of JSON_HOOK_FILES) {\n const text = readIfExists(join(cwd, name));\n if (text === undefined) continue;\n let hooks: Record<string, unknown> | undefined;\n try {\n const parsed: unknown = JSON.parse(text);\n const hooksValue =\n typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)['hooks']\n : undefined;\n hooks =\n typeof hooksValue === 'object' && hooksValue !== null && !Array.isArray(hooksValue)\n ? (hooksValue as Record<string, unknown>)\n : undefined;\n } catch {\n hooks = undefined;\n }\n if (hooks === undefined) continue;\n const installed = hooks;\n const oursUnder = (event: string): readonly unknown[] =>\n Array.isArray(installed[event])\n ? (installed[event] as unknown[]).filter((entry) => isOursEntry(entry))\n : [];\n const hasOurs = (event: string): boolean => oursUnder(event).length > 0;\n /** One of ours under `event` whose command carries `needle`. */\n const hasOursRunning = (event: string, needle: string): boolean =>\n oursUnder(event).some((entry) => (JSON.stringify(entry) ?? '').includes(needle));\n if (!MANAGED_EVENTS.some((event) => hasOurs(event))) continue;\n anyOurs = true;\n guard ||= GUARD_EVENTS.some((event) => hasOurs(event));\n statsOnStop ||= hasOurs(LIFECYCLE_EVENTS.stats);\n mapOnStart ||= hasOursRunning(LIFECYCLE_EVENTS.map, MAP_ON_START_ARGS);\n lintOnStart ||= hasOursRunning(LIFECYCLE_EVENTS.lint, AGENTS_LINT_ARGS);\n }\n\n for (const name of GUARD_ONLY_FILES) {\n const text = readIfExists(join(cwd, name));\n if (text !== undefined && text.includes(OURS_TOKEN)) {\n anyOurs = true;\n guard = true;\n }\n }\n\n return anyOurs ? { guard, statsOnStop, mapOnStart, lintOnStart } : defaults;\n}\n\nconst fileLabel = (file: PlannedFile): string => {\n if (file.unchanged) return 'unchanged — nothing to write';\n return file.exists ? 'exists — will ask before overwriting' : 'new';\n};\n\nasync function confirmAndInstall(\n io: HooksIo,\n ask: Asker,\n choices: HooksChoices,\n): Promise<'done' | 'back'> {\n const plan = planInstall(io.cwd, choices);\n\n io.output(\n `\\nAbout to write, into ${io.cwd}:\\n` +\n plan.files.map((file) => ` ${file.name.padEnd(32)} (${fileLabel(file)})\\n`).join('') +\n plan.skipped.map((skip) => ` ${skip.name.padEnd(32)} (SKIPPED: ${skip.why})\\n`).join('') +\n `Nothing has been written yet.\\n`,\n );\n\n for (;;) {\n const answer = await ask(`confirm (yes / no / back)> `);\n if (answer === 'back') return 'back';\n if (answer === 'no') {\n io.output(`Nothing was written.\\n`);\n return 'done';\n }\n if (answer === 'yes') break;\n io.output(`yes to write, no to leave everything untouched, back to change a setting.\\n`);\n }\n\n for (const file of plan.files) {\n if (file.unchanged) {\n io.output(` ${file.name} — unchanged, not rewritten\\n`);\n continue;\n }\n if (file.exists) {\n // The one hard rule, same as `smelt init`: an existing file is never touched\n // without an explicit per-file yes — not `y`, not Enter, a literal `yes`.\n const answer = await ask(` ${file.name} exists — overwrite it? (yes/no)> `);\n if (answer !== 'yes') {\n io.output(` skipped ${file.name} — the existing file was not touched\\n`);\n continue;\n }\n }\n mkdirSync(dirname(file.path), { recursive: true });\n writeFileSync(file.path, file.content);\n if (file.mode !== undefined) chmodSync(file.path, file.mode);\n io.output(` wrote ${file.name}\\n`);\n }\n\n for (const note of plan.notes) io.output(`note: ${note}\\n`);\n io.output(\n `Done. Re-run \\`${CLI_NAME} hooks install\\` to edit toggles; ` +\n `\\`${CLI_NAME} hooks remove\\` takes it all back out.\\n`,\n );\n return 'done';\n}\n\nasync function removeFlow(\n io: HooksIo,\n ask: Asker,\n harnessFlag: string | undefined,\n): Promise<number> {\n const harnesses = harnessFlag !== undefined ? [resolveHarnessFlag(harnessFlag)] : [...HARNESSES];\n const removals = planRemove(io.cwd, harnesses);\n\n if (removals.length === 0) {\n io.output(`${CLI_NAME} hooks remove: nothing of smelt's found to remove in ${io.cwd}.\\n`);\n return 0;\n }\n\n io.output(\n `${CLI_NAME} hooks remove — takes smelt's hook wiring back out.\\n\\nPlanned:\\n` +\n removals\n .map(\n (removal) =>\n ` ${removal.name.padEnd(32)} (${\n removal.action === 'delete' ? 'delete' : 'remove smelt entries, keep the rest'\n })\\n`,\n )\n .join('') +\n `${CONFIG_FILE_NAME} is left untouched — its hooks block is your config now; ` +\n `edit or remove it there.\\nNothing has been changed yet.\\n`,\n );\n\n for (;;) {\n const answer = await ask(`confirm (yes / no)> `);\n if (answer === 'no') {\n io.output(`Nothing was changed.\\n`);\n return 0;\n }\n if (answer === 'yes') break;\n io.output(`yes to proceed, no to leave everything untouched.\\n`);\n }\n\n for (const removal of removals) {\n const verb = removal.action === 'delete' ? 'delete' : 'modify';\n const answer = await ask(` ${removal.name} — ${verb} it? (yes/no)> `);\n if (answer !== 'yes') {\n io.output(` skipped ${removal.name} — not touched\\n`);\n continue;\n }\n if (removal.action === 'delete') {\n unlinkSync(removal.path);\n io.output(` deleted ${removal.name}\\n`);\n } else {\n writeFileSync(removal.path, removal.content ?? '');\n io.output(` cleaned ${removal.name}\\n`);\n }\n }\n io.output(`Done.\\n`);\n return 0;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { AgentsLintReport } from '../agents/lint.ts';
1
2
  import type { RepoMap } from '../repomap/map.ts';
2
3
  import type { SmeltResult } from '../types.ts';
3
4
  export interface ReportInput {
@@ -41,4 +42,26 @@ export interface MapReportInput {
41
42
  * length of what landed on stdout, and a mutation proves the assertion can go red.
42
43
  */
43
44
  export declare function formatMapReport({ map, source, budgetSource }: MapReportInput): string;
45
+ /** What `smelt agents lint` prints. */
46
+ export interface AgentsReportInput {
47
+ /** The directory named on the command line, exactly as the user wrote it. */
48
+ readonly source: string;
49
+ /** Whether `--strict` was given — it changes what the closing line promises. */
50
+ readonly strict: boolean;
51
+ }
52
+ /**
53
+ * The lint report — and unlike the other two, it goes to **stdout**, because here the
54
+ * report *is* the output. `smelt` and `smelt map` put a payload on stdout and their
55
+ * report on stderr so the two can be piped apart; a lint has no payload to separate
56
+ * from, and sending its only output to stderr would make `smelt agents lint > audit.txt`
57
+ * write an empty file.
58
+ *
59
+ * Same law as the other two, though: every number is read off the
60
+ * {@link AgentsLintReport} the library returned. The renderer counts nothing, so it
61
+ * cannot disagree with what was measured — and in particular both totals are the ones
62
+ * the lint computed over the levels, not a second tally over the printed rows. The
63
+ * guide's cited figure is likewise read from `agents/guide.ts` rather than retyped
64
+ * under the guide's name.
65
+ */
66
+ export declare function formatAgentsReport(report: AgentsLintReport, { source, strict }: AgentsReportInput): string;
44
67
  //# sourceMappingURL=report.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,WAAW,GAAG,MAAM,CAsE5F;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,MAAM,CA+BrF","sourcesContent":["import type { RepoMap } from '../repomap/map.ts';\nimport type { SmeltResult } from '../types.ts';\n\nimport { CLI_NAME } from './shell.ts';\n\nexport interface ReportInput {\n readonly result: SmeltResult;\n /** What to call the input in the header: a path, or `'<stdin>'`. */\n readonly source: string;\n /** The budget the caller asked for, so the report can say when it was missed. */\n readonly budgetBytes: number;\n /** The exact text that was smelted. Used only to count lines inside each range. */\n readonly inputText: string;\n}\n\n/** Longest explanation printed in full before it gets an ellipsis. */\nconst EXPLANATION_WIDTH = 46;\n\n/**\n * The report, for stderr.\n *\n * Every total here is read straight off the {@link SmeltResult} — `inputBytes`,\n * `outputBytes`, `elisions.length`. The CLI keeps no counters of its own, because two\n * pieces of code counting the same bytes is how a report ends up disagreeing with the\n * library it is reporting on, and the report is the thing a human believes.\n * `test/cli.test.ts` asserts the printed numbers equal the result's fields.\n */\nexport function formatReport({ result, source, budgetBytes, inputText }: ReportInput): string {\n const lines: string[] = [];\n\n lines.push([CLI_NAME, source, result.language, result.planner].join(' '));\n lines.push(\n `in ${group(result.inputBytes)} B → out ${group(result.outputBytes)} B ` +\n `(${delta(result.inputBytes, result.outputBytes)}, ${count(result.elisions.length, 'elision')})`,\n );\n\n if (result.measured !== undefined) {\n const { input, output, unit, measure } = result.measured;\n lines.push(`in ${group(input)} → out ${group(output)} ${unit} (${measure})`);\n }\n\n if (result.outputBytes > budgetBytes) {\n lines.push('');\n lines.push(\n `OVER BUDGET ${group(result.outputBytes)} B against a ${group(budgetBytes)} B budget ` +\n `— over by ${group(result.outputBytes - budgetBytes)} B.`,\n );\n lines.push(' The plan is reported as it came back. smelt did not cut the regions');\n lines.push(' you asked to keep in order to make a number look right.');\n }\n\n if (result.elisions.length === 0) {\n lines.push('');\n lines.push(' nothing elided — the input already fits, or every run was too small to');\n lines.push(' be worth a marker (a marker that costs more than the lines it replaces');\n lines.push(' makes the output bigger).');\n return `${lines.join('\\n')}\\n`;\n }\n\n const rows = result.elisions.map((elision) => ({\n rule: elision.reason.rule,\n lines: String(lineSpan(inputText, elision.range.start, elision.range.end)),\n bytes: group(elision.bytes),\n hash: elision.hash,\n explanation: clip(elision.reason.explanation, EXPLANATION_WIDTH),\n }));\n\n const ruleWidth = width(\n 'rule',\n rows.map((row) => row.rule),\n );\n const linesWidth = width(\n 'lines',\n rows.map((row) => row.lines),\n );\n const bytesWidth = width(\n 'bytes',\n rows.map((row) => row.bytes),\n );\n const hashWidth = width(\n 'hash',\n rows.map((row) => row.hash),\n );\n\n lines.push('');\n lines.push(\n ` ${'rule'.padEnd(ruleWidth)} ${'lines'.padStart(linesWidth)} ` +\n `${'bytes'.padStart(bytesWidth)} ${'hash'.padEnd(hashWidth)} explanation`,\n );\n for (const row of rows) {\n lines.push(\n ` ${row.rule.padEnd(ruleWidth)} ${row.lines.padStart(linesWidth)} ` +\n `${row.bytes.padStart(bytesWidth)} ${row.hash.padEnd(hashWidth)} ${row.explanation}`,\n );\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n\n/** What `smelt map` prints to stderr. */\nexport interface MapReportInput {\n readonly map: RepoMap;\n /** The directory named on the command line, exactly as the user wrote it. */\n readonly source: string;\n /**\n * Where the budget came from — the `ResolvedMapRun.budgetSource` receipt, printed\n * beside the budget so a surprising number can be traced to the flag or the config\n * file that set it without re-deriving the precedence by hand.\n */\n readonly budgetSource: 'flag' | 'config';\n}\n\n/**\n * The map report, for stderr — same law as {@link formatReport}: every number is\n * read straight off the {@link RepoMap} the library returned. In particular the\n * \"bytes used\" figure is `map.outputBytes`, which the library measured off the\n * rendered text — the CLI counts nothing itself, because a report that keeps its\n * own tally is a report that can disagree with the map it describes.\n * `test/guards/repo-map.test.ts` asserts the printed figure equals the actual byte\n * length of what landed on stdout, and a mutation proves the assertion can go red.\n */\nexport function formatMapReport({ map, source, budgetSource }: MapReportInput): string {\n const lines: string[] = [];\n\n lines.push([`${CLI_NAME} map`, source, map.id].join(' '));\n lines.push(\n `files scanned ${group(map.filesScanned)}` +\n (map.binarySkipped === 0 ? '' : ` (${count(map.binarySkipped, 'binary file')} skipped)`) +\n ` symbols ranked ${group(map.definitionsTotal)}`,\n );\n lines.push(\n `included ${group(map.entries.length)} of ${group(map.definitionsTotal)} symbols` +\n (map.pathOnlyTotal === 0\n ? ''\n : ` + ${group(map.pathOnly.length)} of ${group(map.pathOnlyTotal)} path-only files`),\n );\n lines.push(\n `bytes used ${group(map.outputBytes)} of ${group(map.budgetBytes)} budget (${budgetSource}) ` +\n `— the map fits itself to the budget by construction, so there is no over-budget exit`,\n );\n\n if (map.cache !== undefined) {\n lines.push(\n `cache ${count(map.cache.hits, 'hit')}, ${count(map.cache.misses, 'miss', 'es')}, ` +\n `${group(map.cache.discarded)} discarded, ${group(map.cache.pruned)} pruned`,\n );\n }\n for (const warning of map.warnings) {\n lines.push(`warning ${warning.rule}: ${warning.explanation}`);\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n\n/** How many lines a byte range covers in the input. Derived, never tallied separately. */\nfunction lineSpan(text: string, start: number, end: number): number {\n const slice = Buffer.from(text, 'utf8').subarray(start, end).toString('utf8');\n return slice.split('\\n').length;\n}\n\nfunction width(header: string, values: readonly string[]): number {\n return values.reduce((widest, value) => Math.max(widest, value.length), header.length);\n}\n\n/** Thousands separators without pulling in ICU, so the output is identical everywhere. */\nfunction group(n: number): string {\n const digits = String(Math.abs(Math.trunc(n)));\n const grouped = digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return n < 0 ? `-${grouped}` : grouped;\n}\n\nfunction delta(inputBytes: number, outputBytes: number): string {\n if (inputBytes === 0) return 'empty input';\n const percent = ((outputBytes - inputBytes) / inputBytes) * 100;\n const sign = percent > 0 ? '+' : '';\n return `${sign}${percent.toFixed(1)}%`;\n}\n\nfunction count(n: number, noun: string, pluralSuffix = 's'): string {\n return `${group(n)} ${noun}${n === 1 ? '' : pluralSuffix}`;\n}\n\nfunction clip(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max - 1)}…`;\n}\n"]}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,WAAW,GAAG,MAAM,CAsE5F;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,cAAc,GAAG,MAAM,CA+BrF;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,gBAAgB,EACxB,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,iBAAiB,GACpC,MAAM,CA8GR","sourcesContent":["import { GUIDE, GUIDE_TITLE } from '../agents/guide.ts';\nimport { overBudgetBytes } from '../agents/lint.ts';\nimport type { AgentsLintReport, AgentsMirrorReport } from '../agents/lint.ts';\nimport type { RepoMap } from '../repomap/map.ts';\nimport type { SmeltResult } from '../types.ts';\n\nimport { CONFIG_FILE_NAME } from './config.ts';\nimport { CLI_NAME } from './shell.ts';\n\nexport interface ReportInput {\n readonly result: SmeltResult;\n /** What to call the input in the header: a path, or `'<stdin>'`. */\n readonly source: string;\n /** The budget the caller asked for, so the report can say when it was missed. */\n readonly budgetBytes: number;\n /** The exact text that was smelted. Used only to count lines inside each range. */\n readonly inputText: string;\n}\n\n/** Longest explanation printed in full before it gets an ellipsis. */\nconst EXPLANATION_WIDTH = 46;\n\n/**\n * The report, for stderr.\n *\n * Every total here is read straight off the {@link SmeltResult} — `inputBytes`,\n * `outputBytes`, `elisions.length`. The CLI keeps no counters of its own, because two\n * pieces of code counting the same bytes is how a report ends up disagreeing with the\n * library it is reporting on, and the report is the thing a human believes.\n * `test/cli.test.ts` asserts the printed numbers equal the result's fields.\n */\nexport function formatReport({ result, source, budgetBytes, inputText }: ReportInput): string {\n const lines: string[] = [];\n\n lines.push([CLI_NAME, source, result.language, result.planner].join(' '));\n lines.push(\n `in ${group(result.inputBytes)} B → out ${group(result.outputBytes)} B ` +\n `(${delta(result.inputBytes, result.outputBytes)}, ${count(result.elisions.length, 'elision')})`,\n );\n\n if (result.measured !== undefined) {\n const { input, output, unit, measure } = result.measured;\n lines.push(`in ${group(input)} → out ${group(output)} ${unit} (${measure})`);\n }\n\n if (result.outputBytes > budgetBytes) {\n lines.push('');\n lines.push(\n `OVER BUDGET ${group(result.outputBytes)} B against a ${group(budgetBytes)} B budget ` +\n `— over by ${group(result.outputBytes - budgetBytes)} B.`,\n );\n lines.push(' The plan is reported as it came back. smelt did not cut the regions');\n lines.push(' you asked to keep in order to make a number look right.');\n }\n\n if (result.elisions.length === 0) {\n lines.push('');\n lines.push(' nothing elided — the input already fits, or every run was too small to');\n lines.push(' be worth a marker (a marker that costs more than the lines it replaces');\n lines.push(' makes the output bigger).');\n return `${lines.join('\\n')}\\n`;\n }\n\n const rows = result.elisions.map((elision) => ({\n rule: elision.reason.rule,\n lines: String(lineSpan(inputText, elision.range.start, elision.range.end)),\n bytes: group(elision.bytes),\n hash: elision.hash,\n explanation: clip(elision.reason.explanation, EXPLANATION_WIDTH),\n }));\n\n const ruleWidth = width(\n 'rule',\n rows.map((row) => row.rule),\n );\n const linesWidth = width(\n 'lines',\n rows.map((row) => row.lines),\n );\n const bytesWidth = width(\n 'bytes',\n rows.map((row) => row.bytes),\n );\n const hashWidth = width(\n 'hash',\n rows.map((row) => row.hash),\n );\n\n lines.push('');\n lines.push(\n ` ${'rule'.padEnd(ruleWidth)} ${'lines'.padStart(linesWidth)} ` +\n `${'bytes'.padStart(bytesWidth)} ${'hash'.padEnd(hashWidth)} explanation`,\n );\n for (const row of rows) {\n lines.push(\n ` ${row.rule.padEnd(ruleWidth)} ${row.lines.padStart(linesWidth)} ` +\n `${row.bytes.padStart(bytesWidth)} ${row.hash.padEnd(hashWidth)} ${row.explanation}`,\n );\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n\n/** What `smelt map` prints to stderr. */\nexport interface MapReportInput {\n readonly map: RepoMap;\n /** The directory named on the command line, exactly as the user wrote it. */\n readonly source: string;\n /**\n * Where the budget came from — the `ResolvedMapRun.budgetSource` receipt, printed\n * beside the budget so a surprising number can be traced to the flag or the config\n * file that set it without re-deriving the precedence by hand.\n */\n readonly budgetSource: 'flag' | 'config';\n}\n\n/**\n * The map report, for stderr — same law as {@link formatReport}: every number is\n * read straight off the {@link RepoMap} the library returned. In particular the\n * \"bytes used\" figure is `map.outputBytes`, which the library measured off the\n * rendered text — the CLI counts nothing itself, because a report that keeps its\n * own tally is a report that can disagree with the map it describes.\n * `test/guards/repo-map.test.ts` asserts the printed figure equals the actual byte\n * length of what landed on stdout, and a mutation proves the assertion can go red.\n */\nexport function formatMapReport({ map, source, budgetSource }: MapReportInput): string {\n const lines: string[] = [];\n\n lines.push([`${CLI_NAME} map`, source, map.id].join(' '));\n lines.push(\n `files scanned ${group(map.filesScanned)}` +\n (map.binarySkipped === 0 ? '' : ` (${count(map.binarySkipped, 'binary file')} skipped)`) +\n ` symbols ranked ${group(map.definitionsTotal)}`,\n );\n lines.push(\n `included ${group(map.entries.length)} of ${group(map.definitionsTotal)} symbols` +\n (map.pathOnlyTotal === 0\n ? ''\n : ` + ${group(map.pathOnly.length)} of ${group(map.pathOnlyTotal)} path-only files`),\n );\n lines.push(\n `bytes used ${group(map.outputBytes)} of ${group(map.budgetBytes)} budget (${budgetSource}) ` +\n `— the map fits itself to the budget by construction, so there is no over-budget exit`,\n );\n\n if (map.cache !== undefined) {\n lines.push(\n `cache ${count(map.cache.hits, 'hit')}, ${count(map.cache.misses, 'miss', 'es')}, ` +\n `${group(map.cache.discarded)} discarded, ${group(map.cache.pruned)} pruned`,\n );\n }\n for (const warning of map.warnings) {\n lines.push(`warning ${warning.rule}: ${warning.explanation}`);\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n\n/** What `smelt agents lint` prints. */\nexport interface AgentsReportInput {\n /** The directory named on the command line, exactly as the user wrote it. */\n readonly source: string;\n /** Whether `--strict` was given — it changes what the closing line promises. */\n readonly strict: boolean;\n}\n\n/**\n * The lint report — and unlike the other two, it goes to **stdout**, because here the\n * report *is* the output. `smelt` and `smelt map` put a payload on stdout and their\n * report on stderr so the two can be piped apart; a lint has no payload to separate\n * from, and sending its only output to stderr would make `smelt agents lint > audit.txt`\n * write an empty file.\n *\n * Same law as the other two, though: every number is read off the\n * {@link AgentsLintReport} the library returned. The renderer counts nothing, so it\n * cannot disagree with what was measured — and in particular both totals are the ones\n * the lint computed over the levels, not a second tally over the printed rows. The\n * guide's cited figure is likewise read from `agents/guide.ts` rather than retyped\n * under the guide's name.\n */\nexport function formatAgentsReport(\n report: AgentsLintReport,\n { source, strict }: AgentsReportInput,\n): string {\n const lines: string[] = [`${CLI_NAME} agents lint ${source}`];\n\n if (report.levels.length === 0) {\n lines.push('');\n lines.push(` no AGENTS.md, CLAUDE.md or GEMINI.md under ${source} — nothing is loaded on`);\n lines.push(' every request, so there is nothing to measure. That is a fine state, not a');\n lines.push(` failure; \\`${CLI_NAME} agents lint\\` has no opinion about whether you want one.`);\n return `${lines.join('\\n')}\\n`;\n }\n\n lines.push('');\n lines.push(' the instruction files in this tree');\n const labelWidth = report.levels.reduce(\n (widest, level) =>\n Math.max(widest, level.path.length, ...level.mirrors.map((m) => m.path.length)),\n Math.max(IMPERATIVES_LABEL.length, PER_REQUEST_LABEL.length, WHOLE_TREE_LABEL.length),\n );\n for (const level of report.levels) {\n lines.push(` ${level.path.padEnd(labelWidth)} ${group(level.bytes).padStart(9)} B`);\n for (const mirror of level.mirrors) {\n lines.push(\n ` ${mirror.path.padEnd(labelWidth)} ${' '.repeat(11)}${mirrorNote(mirror.standing)}`,\n );\n }\n }\n lines.push(\n ` ${PER_REQUEST_LABEL.padEnd(labelWidth)} ${group(report.perRequestBytes).padStart(9)} B`,\n );\n // Printed only when it is a different number — which is exactly when a reader could\n // otherwise mistake the sum for a per-request cost. In a single-chain repository the\n // two are equal and a second row would be noise claiming to be information.\n if (report.totalBytes !== report.perRequestBytes) {\n lines.push(\n ` ${WHOLE_TREE_LABEL.padEnd(labelWidth)} ${group(report.totalBytes).padStart(9)} B`,\n );\n lines.push(' a nested file merges with its ancestors, never with its siblings, so no');\n lines.push(' one request loads the whole tree. Per request is the heaviest chain.');\n }\n lines.push(\n ` ${IMPERATIVES_LABEL.padEnd(labelWidth)} ` +\n `${group(report.imperatives.length).padStart(9)}`,\n );\n // The figure is the guide's, so it is read from where the guide is quoted rather\n // than retyped here: an explanation that paraphrases its source drifts from it\n // silently, and this one is printed under the guide's own name.\n for (const wrapped of wrap(`${GUIDE_TITLE} cites: \"${GUIDE.instructionCeiling}\".`, 68)) {\n lines.push(` ${wrapped}`);\n }\n lines.push(' Printed as a citation, compared to nothing: the only ceiling here is');\n lines.push(' the one you set.');\n\n const over = overBudgetBytes(report);\n lines.push('');\n if (report.budgetBytes === undefined) {\n lines.push(` no budget set — add {\"agents\":{\"budgetBytes\":N}} to ${CONFIG_FILE_NAME} and`);\n lines.push(` exceeding it exits 1. ${CLI_NAME} will never invent that number for you.`);\n } else if (over === undefined) {\n lines.push(\n ` within budget ${group(report.totalBytes)} B of ${group(report.budgetBytes)} B ` +\n `(${CONFIG_FILE_NAME}: agents.budgetBytes, against the whole tree).`,\n );\n } else {\n lines.push(\n ` OVER BUDGET ${group(report.totalBytes)} B against your ${group(report.budgetBytes)} B ` +\n `budget — over by ${group(over)} B.`,\n );\n lines.push(` The budget is yours, from ${CONFIG_FILE_NAME}, and it caps the`);\n lines.push(' whole tree rather than one request — the stricter of the two, so');\n lines.push(' it cannot be met by moving bytes into another package. Exit 1 is');\n lines.push(' the same over-budget code every other smelt run uses.');\n }\n\n if (report.findings.length === 0) {\n lines.push('');\n lines.push(' no findings. Eight advisory rules ran and none matched — either the file is');\n lines.push(' in good shape, or a rule is asleep. `pnpm mutate` is how this repo tells the');\n lines.push(' difference about its own guards; a fixture per rule is how it tells it here.');\n return `${lines.join('\\n')}\\n`;\n }\n\n const rows = report.findings.map((finding) => ({\n place: `${finding.file}:${String(finding.line)}`,\n rule: finding.reason.rule,\n explanation: finding.reason.explanation,\n }));\n const ruleWidth = width(\n '',\n rows.map((row) => row.rule),\n );\n\n lines.push('');\n for (const row of rows) {\n lines.push(` ${row.rule.padEnd(ruleWidth)} ${row.place}`);\n // The explanation gets its own wrapped lines rather than a fourth column. It is\n // the *reason*, which is the part a reader actually has to read — clipped to a\n // terminal column it becomes an ellipsis, and Law 2 promises an explanation, not\n // the first 46 characters of one.\n for (const wrapped of wrap(row.explanation, EXPLANATION_WRAP)) lines.push(` ${wrapped}`);\n }\n\n lines.push('');\n lines.push(\n ` ${count(report.findings.length, 'finding')}. ` +\n (strict\n ? 'Exit 1: --strict was given.'\n : `Advisory — exit 0. Pass --strict to fail a CI run on any of them.`),\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n\n/** The label the imperative count is reported under. Never \"instructions\": R6. */\nconst IMPERATIVES_LABEL = 'imperatives (heuristic)';\n\n/** The heaviest ancestor chain — the honest answer to \"what does a request cost\". */\nconst PER_REQUEST_LABEL = 'per request (worst case)';\n\n/** Every level summed. The repository's instruction surface, and not a request's cost. */\nconst WHOLE_TREE_LABEL = 'whole tree';\n\n/** Where a finding's explanation wraps, once its six-column indent is removed. */\nconst EXPLANATION_WRAP = 84;\n\n/** Greedy word wrap. No ICU, no dependency — the output is identical everywhere. */\nfunction wrap(text: string, columns: number): readonly string[] {\n const out: string[] = [];\n let line = '';\n for (const word of text.split(' ')) {\n const candidate = line === '' ? word : `${line} ${word}`;\n if (line !== '' && candidate.length > columns) {\n out.push(line);\n line = word;\n } else {\n line = candidate;\n }\n }\n if (line !== '') out.push(line);\n return out;\n}\n\n/** How a mirror stands beside its AGENTS.md, in one clause. */\nfunction mirrorNote(standing: AgentsMirrorReport['standing']): string {\n switch (standing) {\n case 'symlink':\n return 'symlink — cannot drift, and costs no extra bytes';\n case 'copy':\n return 'copy, byte-identical today — a symlink could not drift';\n case 'drift':\n return 'DIVERGED — see mirror-drift below';\n }\n}\n\n/** How many lines a byte range covers in the input. Derived, never tallied separately. */ function lineSpan(\n text: string,\n start: number,\n end: number,\n): number {\n const slice = Buffer.from(text, 'utf8').subarray(start, end).toString('utf8');\n return slice.split('\\n').length;\n}\n\nfunction width(header: string, values: readonly string[]): number {\n return values.reduce((widest, value) => Math.max(widest, value.length), header.length);\n}\n\n/** Thousands separators without pulling in ICU, so the output is identical everywhere. */\nfunction group(n: number): string {\n const digits = String(Math.abs(Math.trunc(n)));\n const grouped = digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return n < 0 ? `-${grouped}` : grouped;\n}\n\nfunction delta(inputBytes: number, outputBytes: number): string {\n if (inputBytes === 0) return 'empty input';\n const percent = ((outputBytes - inputBytes) / inputBytes) * 100;\n const sign = percent > 0 ? '+' : '';\n return `${sign}${percent.toFixed(1)}%`;\n}\n\nfunction count(n: number, noun: string, pluralSuffix = 's'): string {\n return `${group(n)} ${noun}${n === 1 ? '' : pluralSuffix}`;\n}\n\nfunction clip(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max - 1)}…`;\n}\n"]}
@@ -1,3 +1,6 @@
1
+ import { GUIDE, GUIDE_TITLE } from '../agents/guide.js';
2
+ import { overBudgetBytes } from '../agents/lint.js';
3
+ import { CONFIG_FILE_NAME } from './config.js';
1
4
  import { CLI_NAME } from './shell.js';
2
5
  /** Longest explanation printed in full before it gets an ellipsis. */
3
6
  const EXPLANATION_WIDTH = 46;
@@ -83,8 +86,143 @@ export function formatMapReport({ map, source, budgetSource }) {
83
86
  }
84
87
  return `${lines.join('\n')}\n`;
85
88
  }
86
- /** How many lines a byte range covers in the input. Derived, never tallied separately. */
87
- function lineSpan(text, start, end) {
89
+ /**
90
+ * The lint report — and unlike the other two, it goes to **stdout**, because here the
91
+ * report *is* the output. `smelt` and `smelt map` put a payload on stdout and their
92
+ * report on stderr so the two can be piped apart; a lint has no payload to separate
93
+ * from, and sending its only output to stderr would make `smelt agents lint > audit.txt`
94
+ * write an empty file.
95
+ *
96
+ * Same law as the other two, though: every number is read off the
97
+ * {@link AgentsLintReport} the library returned. The renderer counts nothing, so it
98
+ * cannot disagree with what was measured — and in particular both totals are the ones
99
+ * the lint computed over the levels, not a second tally over the printed rows. The
100
+ * guide's cited figure is likewise read from `agents/guide.ts` rather than retyped
101
+ * under the guide's name.
102
+ */
103
+ export function formatAgentsReport(report, { source, strict }) {
104
+ const lines = [`${CLI_NAME} agents lint ${source}`];
105
+ if (report.levels.length === 0) {
106
+ lines.push('');
107
+ lines.push(` no AGENTS.md, CLAUDE.md or GEMINI.md under ${source} — nothing is loaded on`);
108
+ lines.push(' every request, so there is nothing to measure. That is a fine state, not a');
109
+ lines.push(` failure; \`${CLI_NAME} agents lint\` has no opinion about whether you want one.`);
110
+ return `${lines.join('\n')}\n`;
111
+ }
112
+ lines.push('');
113
+ lines.push(' the instruction files in this tree');
114
+ const labelWidth = report.levels.reduce((widest, level) => Math.max(widest, level.path.length, ...level.mirrors.map((m) => m.path.length)), Math.max(IMPERATIVES_LABEL.length, PER_REQUEST_LABEL.length, WHOLE_TREE_LABEL.length));
115
+ for (const level of report.levels) {
116
+ lines.push(` ${level.path.padEnd(labelWidth)} ${group(level.bytes).padStart(9)} B`);
117
+ for (const mirror of level.mirrors) {
118
+ lines.push(` ${mirror.path.padEnd(labelWidth)} ${' '.repeat(11)}${mirrorNote(mirror.standing)}`);
119
+ }
120
+ }
121
+ lines.push(` ${PER_REQUEST_LABEL.padEnd(labelWidth)} ${group(report.perRequestBytes).padStart(9)} B`);
122
+ // Printed only when it is a different number — which is exactly when a reader could
123
+ // otherwise mistake the sum for a per-request cost. In a single-chain repository the
124
+ // two are equal and a second row would be noise claiming to be information.
125
+ if (report.totalBytes !== report.perRequestBytes) {
126
+ lines.push(` ${WHOLE_TREE_LABEL.padEnd(labelWidth)} ${group(report.totalBytes).padStart(9)} B`);
127
+ lines.push(' a nested file merges with its ancestors, never with its siblings, so no');
128
+ lines.push(' one request loads the whole tree. Per request is the heaviest chain.');
129
+ }
130
+ lines.push(` ${IMPERATIVES_LABEL.padEnd(labelWidth)} ` +
131
+ `${group(report.imperatives.length).padStart(9)}`);
132
+ // The figure is the guide's, so it is read from where the guide is quoted rather
133
+ // than retyped here: an explanation that paraphrases its source drifts from it
134
+ // silently, and this one is printed under the guide's own name.
135
+ for (const wrapped of wrap(`${GUIDE_TITLE} cites: "${GUIDE.instructionCeiling}".`, 68)) {
136
+ lines.push(` ${wrapped}`);
137
+ }
138
+ lines.push(' Printed as a citation, compared to nothing: the only ceiling here is');
139
+ lines.push(' the one you set.');
140
+ const over = overBudgetBytes(report);
141
+ lines.push('');
142
+ if (report.budgetBytes === undefined) {
143
+ lines.push(` no budget set — add {"agents":{"budgetBytes":N}} to ${CONFIG_FILE_NAME} and`);
144
+ lines.push(` exceeding it exits 1. ${CLI_NAME} will never invent that number for you.`);
145
+ }
146
+ else if (over === undefined) {
147
+ lines.push(` within budget ${group(report.totalBytes)} B of ${group(report.budgetBytes)} B ` +
148
+ `(${CONFIG_FILE_NAME}: agents.budgetBytes, against the whole tree).`);
149
+ }
150
+ else {
151
+ lines.push(` OVER BUDGET ${group(report.totalBytes)} B against your ${group(report.budgetBytes)} B ` +
152
+ `budget — over by ${group(over)} B.`);
153
+ lines.push(` The budget is yours, from ${CONFIG_FILE_NAME}, and it caps the`);
154
+ lines.push(' whole tree rather than one request — the stricter of the two, so');
155
+ lines.push(' it cannot be met by moving bytes into another package. Exit 1 is');
156
+ lines.push(' the same over-budget code every other smelt run uses.');
157
+ }
158
+ if (report.findings.length === 0) {
159
+ lines.push('');
160
+ lines.push(' no findings. Eight advisory rules ran and none matched — either the file is');
161
+ lines.push(' in good shape, or a rule is asleep. `pnpm mutate` is how this repo tells the');
162
+ lines.push(' difference about its own guards; a fixture per rule is how it tells it here.');
163
+ return `${lines.join('\n')}\n`;
164
+ }
165
+ const rows = report.findings.map((finding) => ({
166
+ place: `${finding.file}:${String(finding.line)}`,
167
+ rule: finding.reason.rule,
168
+ explanation: finding.reason.explanation,
169
+ }));
170
+ const ruleWidth = width('', rows.map((row) => row.rule));
171
+ lines.push('');
172
+ for (const row of rows) {
173
+ lines.push(` ${row.rule.padEnd(ruleWidth)} ${row.place}`);
174
+ // The explanation gets its own wrapped lines rather than a fourth column. It is
175
+ // the *reason*, which is the part a reader actually has to read — clipped to a
176
+ // terminal column it becomes an ellipsis, and Law 2 promises an explanation, not
177
+ // the first 46 characters of one.
178
+ for (const wrapped of wrap(row.explanation, EXPLANATION_WRAP))
179
+ lines.push(` ${wrapped}`);
180
+ }
181
+ lines.push('');
182
+ lines.push(` ${count(report.findings.length, 'finding')}. ` +
183
+ (strict
184
+ ? 'Exit 1: --strict was given.'
185
+ : `Advisory — exit 0. Pass --strict to fail a CI run on any of them.`));
186
+ return `${lines.join('\n')}\n`;
187
+ }
188
+ /** The label the imperative count is reported under. Never "instructions": R6. */
189
+ const IMPERATIVES_LABEL = 'imperatives (heuristic)';
190
+ /** The heaviest ancestor chain — the honest answer to "what does a request cost". */
191
+ const PER_REQUEST_LABEL = 'per request (worst case)';
192
+ /** Every level summed. The repository's instruction surface, and not a request's cost. */
193
+ const WHOLE_TREE_LABEL = 'whole tree';
194
+ /** Where a finding's explanation wraps, once its six-column indent is removed. */
195
+ const EXPLANATION_WRAP = 84;
196
+ /** Greedy word wrap. No ICU, no dependency — the output is identical everywhere. */
197
+ function wrap(text, columns) {
198
+ const out = [];
199
+ let line = '';
200
+ for (const word of text.split(' ')) {
201
+ const candidate = line === '' ? word : `${line} ${word}`;
202
+ if (line !== '' && candidate.length > columns) {
203
+ out.push(line);
204
+ line = word;
205
+ }
206
+ else {
207
+ line = candidate;
208
+ }
209
+ }
210
+ if (line !== '')
211
+ out.push(line);
212
+ return out;
213
+ }
214
+ /** How a mirror stands beside its AGENTS.md, in one clause. */
215
+ function mirrorNote(standing) {
216
+ switch (standing) {
217
+ case 'symlink':
218
+ return 'symlink — cannot drift, and costs no extra bytes';
219
+ case 'copy':
220
+ return 'copy, byte-identical today — a symlink could not drift';
221
+ case 'drift':
222
+ return 'DIVERGED — see mirror-drift below';
223
+ }
224
+ }
225
+ /** How many lines a byte range covers in the input. Derived, never tallied separately. */ function lineSpan(text, start, end) {
88
226
  const slice = Buffer.from(text, 'utf8').subarray(start, end).toString('utf8');
89
227
  return slice.split('\n').length;
90
228
  }