import type { IngestTarget } from '@mmnto/totem'; import type { ConfigFormat, EmbeddingTier } from './init-detect.js'; export declare const REFLEX_VERSION = 10; export declare const REFLEX_START = ""; export declare const REFLEX_END = ""; export declare const REFLEX_VERSION_RE: RegExp; export declare const LEGACY_SENTINEL = "## Totem AI Integration (Auto-Generated)"; export declare const AI_PROMPT_BLOCK = "\n\n\n\n## Totem AI Integration (Auto-Generated)\nYou have access to the Totem MCP for long-term project memory. You MUST operate with the following reflexes:\n\n### Memory Reflexes\n1. **BLOCKING \u2014 Pull Before Coding:** Before writing or modifying code that touches more than one file, you MUST call `search_knowledge` with a query describing what you're about to change. This is not optional. The vector DB contains traps, edge cases, and architectural constraints that prevent rework. Skip this and you risk repeating a mistake that's already been solved.\n2. **Pull Before Planning:** Before writing specs, architecture, or fixing complex bugs, use `search_knowledge` to retrieve domain constraints and past traps.\n3. **Pull on Session Start:** At the beginning of every session, call `search_knowledge` with a broad query about the current task or area of work. The vector DB is your institutional memory \u2014 use it before relying on your own context window.\n4. **Proactive Anchoring (The 3 Triggers):** You must autonomously call `add_lesson` when any of the following occur \u2014 do NOT wait for the user to ask:\n - **The Trap Trigger:** If you spend >2 turns fixing a bug caused by a framework quirk, unexpected API response, or edge case. (Anchor the symptom + fix).\n - **The Pivot Trigger:** If the user introduces a new architectural pattern or deprecates an old one. (Anchor the rule).\n - **The Handoff Trigger:** At the end of a session or when wrapping up a complex feature, extract the non-obvious lessons learned and anchor them.\n5. **Tool Preference (MCP over CLI):** Always prioritize using dedicated MCP tools (e.g., GitHub, Supabase, Vercel) over executing generic shell commands (like `gh issue view` or `curl`). MCP tools provide structured, un-truncated data optimized for your context window. Only fall back to bash execution if an MCP tool is unavailable or fails.\n\nLessons are automatically re-indexed in the background after each `add_lesson` call \u2014 no manual sync needed.\n\n### Memory Classification\nWhen deciding where to store information or rules, use this decision tree:\n- If forgetting this causes a mistake on an UNRELATED task (Core Safety): Store in your root agent memory file (e.g., CLAUDE.md or GEMINI.md).\n- If it's a stable, project-wide workflow rule: Store in project config (e.g., CLAUDE.md).\n- If it's a stable syntax/style pattern: Store in the project's styleguide or linter rules.\n- If it's domain knowledge, an edge case, or a past trap: You MUST use the Totem `add_lesson` tool to anchor it into the project's LanceDB.\n\n### Workflow Orchestrator Rituals\n[FOR LOCAL CLI/TERMINAL AGENTS ONLY] Do not attempt to run these commands if you are a headless bot or operating in a cloud PR environment (e.g., Gemini Code Assist on GitHub).\nTotem provides CLI commands that map to your development lifecycle. Use them at these moments:\n1. **Start of Session:** The SessionStart hook automatically runs `totem describe` to emit the project-orientation banner (project, tier, rule/lesson counts, targets, hooks). For richer derived project state (recent merged PRs, current branch + uncommitted files, latest strategy journal pointer, package versions, rule/lesson counts), call the MCP `describe_project` tool \u2014 the derived view replaces the retired `docs/active_work.md` convention (state is observed, not declared). For a freshness check (manifest staleness, shield drift, review state), run `totem status`. Run `totem triage` if you need to pick a new task. On a seat without the SessionStart hook (a cold start, or a vehicle that doesn't run hooks), run `totem orient` to derive in-flight and parked project state from primitives.\n2. **Before Implementation:** Optionally run `totem spec ` to retrieve related context (lessons, specs, code) before writing code. Treat any generated plan as one retrieval input, never the contract \u2014 derive the actual design from primary sources (the issue, the code, project doctrine).\n3. **Before Push:** Run `totem lint` \u2014 the deterministic enforcement floor (zero LLM, ~2s). **Before PR:** `totem review` runs supplementary AI lanes over the diff (~18s) \u2014 advisory sensors, not a merge gate; known limits are disclosed in the run output (LLM window truncation on large diffs; non-code files skipped). Your team's own review discipline decides what constitutes the review of record.\n4. **End of Session:** Run `totem handoff` to generate a snapshot for the next agent session with current progress and open threads.\n5. **Managed hooks self-repair:** `totem init` distributes `.totem/prepare.cjs` and wires `package.json` `prepare` to it only when no `prepare` script exists. The wrapper runs `totem hook install` on every `pnpm install`, drift-repairing the managed Claude/Gemini hooks \u2014 no manual re-install needed.\n\n### Cloud / PR Review Bots\n[FOR CLOUD BOTS ONLY \u2014 e.g., Gemini Code Assist, GitHub Copilot PR Review]\nYou do NOT have access to the local CLI. Instead, use the Totem MCP tools directly:\n1. **Before reviewing a PR:** Call `search_knowledge` with queries about the files and patterns being changed to check for known traps and architectural constraints.\n2. **Before suggesting changes:** Call `search_knowledge` to verify your suggestion aligns with established project patterns and past lessons.\n3. **When you spot a recurring issue:** Call `add_lesson` to persist the trap so future reviews catch it automatically.\n\n### Context Management Guardrail\nYou must be highly defensive of your own context window \u2014 and you are the ONLY party holding the denominator (your model's window size and current occupancy); no Totem tool can measure it for you. Large Totem tool responses may carry a self-closing `` line stating that payload's measured size and cumulative session totals \u2014 a measurement, never a risk claim. Weigh it against your own occupancy and escalate to the user only when the pressure is genuinely material, suggesting `totem handoff` to capture mid-task state before clearing the chat. If you receive a `` tag in a tool response (e.g. index staleness or degraded retrieval), read it silently and act on it or surface it naturally. Do NOT echo raw XML tags to the user.\n\n"; export declare const TOTEM_FILE_MARKER = "// [totem] auto-generated"; /** * The end marker that CLOSES every managed whole-file session hook template * (`.claude/hooks/*.cjs`, `.gemini/hooks/*.js`). Mirrors the #2406 git-hook * bounded-ownership semantics (`TOTEM_HOOK_END` et al.) for the JS/CJS hook * family: a marker-headed file whose end marker is present with nothing after * it is a bounded totem-OWNED whole file, safe to drift-repair without * `--force`. A LEGACY file written by a pre-#2410 template carries no in-file * end marker → not bounded → declines bare repair and takes one * `totem hook install --force` (identical to the shipped #2406 git-hook * migration). Collision-free against {@link TOTEM_FILE_MARKER}: the `end ` * infix means the start marker is never a substring of the end marker. */ export declare const TOTEM_FILE_END = "// [totem] end auto-generated"; /** * Whether the totem `marker` OPENS the file — only whitespace may precede it. The * ownership GATE for the session-hook family (`.claude/hooks/*.cjs`, * `.gemini/hooks/*.js` — no shebang preamble): a user-owned file that merely QUOTES * the marker string somewhere in its body is NOT marker-headed and must never be * regenerated or overwritten, not even under `--force` (mmnto-ai/totem#2413 — the * `includes(marker)` false-positive that let a quoting user file be clobbered). * Distinct from install-hooks' `isTotemOwnedWholeFile`, which additionally tolerates * a `#!`-shebang preamble that is legitimate for git hooks but never appears here. */ export declare function markerOpensFile(content: string, marker: string): boolean; /** * Whether a marker-headed session-hook file is a bounded totem-OWNED whole file — * the precondition for a no-force drift-repair (mmnto-ai/totem#2410). The single * shared ownership checker for the session-hook family, consumed by both init's * `scaffoldFile` and install-hooks' `regenerateManagedSessionHooks` * (mmnto-ai/totem#2413 — was two divergent twins). The session-hook analog of * install-hooks' git-hook `isTotemOwnedWholeFile`, minus the shebang preamble the * JS/CJS family never carries: * - the marker must OPEN the file (only whitespace before it — no user content); * - the `endMarker` must be present; * - nothing but trailing whitespace may follow the end marker (else a whole-file * rewrite would clobber appended user content). */ export declare function isBoundedOwnedFile(content: string, marker: string, endMarker: string): boolean; export declare const BARE_REF_REGEX_SOURCE = "(? String(v).replace(/[\\x00-\\x1f\\x7f]/g, '?');\n let stdio = 'ignore';\n let logFd = null;\n try {\n try {\n // 1 MiB self-cap: the log truncates rather than growing forever.\n if (statSync(logPath).size > 1048576) writeFileSync(logPath, '');\n } catch {\n // no log yet \u2014 nothing to cap\n }\n appendFileSync(logPath, '[' + new Date().toISOString() + '] gemini spawn cwd=' + scrub(process.cwd()) + ' path-has-go-bin=' + /go[\\\\/]bin/i.test(process.env.PATH || '') + ' cwd-shadow-exe=' + existsSync(nodePath.join(process.cwd(), 'totem-status.exe')) + ' verb=refresh-gh\\n');\n logFd = openSync(logPath, 'a');\n stdio = ['ignore', logFd, logFd];\n } catch {\n // log unavailable \u2014 refresh still fires blind, as before\n }\n const refresh = spawn('totem-status', ['refresh-gh'], {\n detached: true,\n stdio,\n });\n refresh.on('error', (err) => {\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] gemini spawn-error code=' + ((err && err.code) || 'unknown') + ' verb=refresh-gh\\n');\n } catch {\n // log write failed \u2014 fall through to the stderr breadcrumb\n }\n if (err && err.code === 'ENOENT') return;\n process.stderr.write('[SessionStart] totem-status refresh-gh spawn failed (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n });\n refresh.unref();\n // Second verb, same gate and same log fd (see the banner above). Written out\n // rather than looped so the spawn, the stamp, and the breadcrumb each carry a\n // literal verb \u2014 a reader of the generated hook (or of the log) never has to\n // resolve a variable to know which refresh fired.\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] gemini spawn cwd=' + scrub(process.cwd()) + ' path-has-go-bin=' + /go[\\\\/]bin/i.test(process.env.PATH || '') + ' cwd-shadow-exe=' + existsSync(nodePath.join(process.cwd(), 'totem-status.exe')) + ' verb=refresh-obligation-store\\n');\n } catch {\n // log unavailable \u2014 this verb still fires blind, exactly as the first does\n }\n const refreshStore = spawn('totem-status', ['refresh-obligation-store'], {\n detached: true,\n stdio,\n });\n refreshStore.on('error', (err) => {\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] gemini spawn-error code=' + ((err && err.code) || 'unknown') + ' verb=refresh-obligation-store\\n');\n } catch {\n // log write failed \u2014 fall through to the stderr breadcrumb\n }\n if (err && err.code === 'ENOENT') return;\n process.stderr.write('[SessionStart] totem-status refresh-obligation-store spawn failed (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n });\n refreshStore.unref();\n // Each child holds its own copy of the fd from spawn time; release the parent's\n // once BOTH are away (an early close would hand the second spawn an EBADF).\n if (logFd !== null) {\n try {\n closeSync(logFd);\n } catch {\n // nothing to release\n }\n }\n }\n} catch (err) {\n // Block-level breadcrumb: this catch covers the whole gated block, so neither\n // verb fired \u2014 it names the SIDECAR, not one verb. The per-spawn breadcrumbs\n // inside still name their own verb.\n process.stderr.write('[SessionStart] totem-status sidecar refresh unavailable (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n}\n\n// \u2500\u2500\u2500 A.3.a: mint session ID + log session_start event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// New with mmnto-ai/totem#2468: this template never took over the A.3.a duty\n// its Claude sibling carries, so Gemini-seat sessions had no session UUID and\n// no session_start denominator row \u2014 the selection-manifest join key and the\n// recorded-absence contract both need them. Same shape as the Claude template.\n// Fire-and-forget: a ledger failure must NOT block the briefing.\nlet mintedSessionId = null;\ntry {\n const nodePath = require('path');\n const { mkdirSync, writeFileSync, appendFileSync } = require('fs');\n const { randomUUID } = require('crypto');\n const ledgerDir = nodePath.join(process.cwd(), '.totem', 'ledger');\n mkdirSync(ledgerDir, { recursive: true });\n mintedSessionId = randomUUID();\n writeFileSync(nodePath.join(ledgerDir, '.session-id'), mintedSessionId, 'utf-8');\n const selfAgentEntry = (process.env.TOTEM_SELF_AGENT || '')\n .split(',')\n .map((s) => s.trim())\n .find((s) => s.length > 0);\n const event = {\n timestamp: new Date().toISOString(),\n type: 'session_start',\n activity_name: 'SessionStart',\n source: 'bot',\n ...(selfAgentEntry ? { agent_source: selfAgentEntry } : {}),\n justification: '',\n session_id: mintedSessionId,\n };\n appendFileSync(nodePath.join(ledgerDir, 'events.ndjson'), JSON.stringify(event) + '\\n', 'utf-8');\n} catch (err) {\n process.stderr.write(\n '[SessionStart] session-start telemetry unavailable (non-fatal): ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n\n// Interactive Gemini ingests SessionStart CONTEXT only from the\n// hookSpecificOutput.additionalContext envelope \u2014 plain exit-0 stdout wraps as\n// systemMessage, which the interactive startup consumer never injects, so the\n// briefing was absent from model context in the primary dev flow even once\n// registered (mmnto-ai/totem#2613; leg-verified against @google/gemini-cli\n// 0.54.4). systemMessage rides alongside the envelope for the human surfaces:\n// interactive startup and /clear render it as a UI info item and -p echoes it\n// to stderr \u2014 so the briefing is VISIBLE to the human on those surfaces and\n// injected as context for the model (one payload, both audiences).\n// The Totem CLI writes its banner and diagnostics to STDERR, so capture takes\n// BOTH streams \u2014 the same seam the Claude-side template documents; a\n// stdout-only capture silently drops the describe leg (mmnto-ai/totem#2613\n// falsification round). A leg failure carries any partial stdout plus the\n// fail-soft note; exit is ALWAYS 0 (never blocks boot).\n// Per-leg 20s budgets cut the measured worst-case process exit from 60s to\n// 40s against Gemini's 60s DEFAULT_HOOK_TIMEOUT (which tree-kills at expiry;\n// no descendant inherits this hook's own streams).\nlet briefing = '';\ntry {\n const describeRun = spawnSync('totem describe', {\n shell: true,\n timeout: 20000,\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n // spawnSync sets .error (it does NOT throw) on spawn-level failure/timeout.\n briefing += describeRun.stdout || '';\n if (describeRun.error || describeRun.status !== 0) {\n const reason = describeRun.error\n ? (describeRun.error.message || String(describeRun.error))\n : ((describeRun.stderr || '').trim() || 'totem describe exited ' + describeRun.status);\n briefing += '[Totem] Briefing unavailable: ' + reason + '\\n';\n } else {\n briefing += describeRun.stderr || '';\n }\n} catch (err) {\n // Belt for a genuinely throwing spawnSync: same fail-soft note.\n briefing += '[Totem] Briefing unavailable: ' + (err instanceof Error ? err.message : String(err)) + '\\n';\n}\n\n// Selection-manifest block boundary (mmnto-ai/totem#2468): everything in\n// `briefing` up to here is the describe leg \u2014 including its fail-soft note,\n// which IS injected payload when it fires.\nconst describeBriefingEnd = briefing.length;\n\n// totem orient --session \u2014 live derived in-flight state, ADDITIVE to describe\n// (mmnto-ai/totem#2044 PR-3). Own try/catch; orient --session is itself boot-safe.\ntry {\n const orientRun = spawnSync('totem orient --session', {\n shell: true,\n timeout: 20000,\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n briefing += orientRun.stdout || '';\n if (orientRun.error || orientRun.status !== 0) {\n const orientReason = orientRun.error\n ? (orientRun.error.message || String(orientRun.error))\n : ((orientRun.stderr || '').trim() || 'totem orient exited ' + orientRun.status);\n // Boot-safe: orient is additive to describe; a failure never blocks session\n // start. The note rides the briefing (both keys) so the model and the human\n // both see the orient gap \u2014 a stderr-only note is invisible to the model\n // (CR round on this PR); the parent-stderr breadcrumb stays for hook\n // diagnostics.\n briefing += '[Totem] Orient briefing unavailable: ' + orientReason + '\\n';\n process.stderr.write('[SessionStart] orient briefing unavailable (non-fatal): ' + orientReason + '\\n');\n } else {\n briefing += orientRun.stderr || '';\n }\n} catch (err) {\n const orientMsg = err instanceof Error ? err.message : String(err);\n briefing += '[Totem] Orient briefing unavailable: ' + orientMsg + '\\n';\n process.stderr.write('[SessionStart] orient briefing unavailable (non-fatal): ' + orientMsg + '\\n');\n}\n\n// \u2500\u2500\u2500 selection manifest (mmnto-ai/totem#2468 M1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Inline append mirroring the A.3.a write above \u2014 this rendered hook runs\n// standalone in consumer repos with no access to @mmnto/totem's writer. The\n// row shape is BOUND to SelectionManifestRowSchema (strict): change both in\n// the same PR; the template contract test parses this row with the real\n// schema. A zero-byte block never becomes a candidate (nothing was injected).\ntry {\n const nodePath = require('path');\n const { appendFileSync } = require('fs');\n const { createHash } = require('crypto');\n const measure = (id, content, reason) => {\n const bytes = Buffer.byteLength(content, 'utf-8');\n return {\n id,\n disposition: 'selected',\n reason,\n bytes,\n approxTokens: Math.ceil(bytes / 4),\n fingerprint: createHash('sha256').update(content, 'utf-8').digest('hex').slice(0, 16),\n };\n };\n const selfAgent = (process.env.TOTEM_SELF_AGENT || '')\n .split(',')\n .map((s) => s.trim())\n .find((s) => s.length > 0);\n const row = {\n schemaVersion: 1,\n timestamp: new Date().toISOString(),\n emitter: 'session-start',\n ...(mintedSessionId ? { session_id: mintedSessionId } : {}),\n ...(selfAgent ? { agent_source: selfAgent } : {}),\n // mmnto-ai/totem#2629: provenance disclosed on every row (ruled item 1).\n // Self-minted row \u2014 the minting seat IS this process's env read, so the\n // core conflict probe is structurally inapplicable; only the two-value\n // provenance enum rides. Mirrors senseAgentAttribution's biconditional.\n agent_source_provenance: selfAgent ? 'env' : 'absent',\n context: { template: 'gemini-managed' },\n universe: 'managed-template blocks: describe + orient --session',\n costBasis: { bytes: 'utf8-length', approxTokens: 'ceil(bytes/4) approximation' },\n candidates: [\n measure('describe', briefing.slice(0, describeBriefingEnd), 'always-injected briefing'),\n measure('orient:session-block', briefing.slice(describeBriefingEnd), 'always-injected briefing'),\n ].filter((c) => c.bytes > 0),\n warnings: [],\n };\n appendFileSync(\n nodePath.join(process.cwd(), '.totem', 'ledger', 'selection-manifests.ndjson'),\n JSON.stringify(row) + '\\n',\n 'utf-8',\n );\n} catch (err) {\n process.stderr.write(\n '[SessionStart] selection manifest unavailable (non-fatal): ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n\nprocess.stdout.write(JSON.stringify({\n systemMessage: briefing,\n hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: briefing },\n}) + '\\n');\n// [totem] end auto-generated\n"; export declare const GEMINI_BEFORE_TOOL: string; export declare const GEMINI_SKILL = "\n# Totem Architect\n\nBefore designing, planning, or implementing features, query the project's memory index for relevant context:\n\n1. Use the `search_knowledge` MCP tool with a query describing what you're about to build.\n2. Review returned lessons, specs, and code patterns before writing any code.\n3. If you discover a trap or architectural constraint, factor it into your design.\n\nThis ensures you build on existing knowledge rather than repeating past mistakes.\n"; export declare const CLAUDE_SHIELD_GATE = "// [totem] auto-generated \u2014 Claude Code review gate hook\n// Intercepts git push/commit to run `totem review` before proceeding.\nconst { execSync } = require('child_process');\n\nconst input = process.env.TOOL_INPUT || '';\nif (/\bgits+(push|commit)\b/.test(input)) {\n try {\n execSync('totem lint', { encoding: 'utf-8', timeout: 60000, stdio: 'inherit' });\n } catch (err) {\n process.exit(1);\n }\n}\n"; export declare const CLAUDE_PRETOOLUSE_ENTRY: { matcher: string; hooks: { type: string; command: string; }[]; }; export declare const CLAUDE_PREWRITESHIELD: string; export declare const CLAUDE_PREWRITESHIELD_ENTRY: { matcher: string; hooks: { type: string; command: string; }[]; }; export declare const CLAUDE_SESSION_START = "// [totem] auto-generated \u2014 Claude Code SessionStart hook\n// Runs `@mmnto/cli describe` at the start of every Claude Code session.\n// Mirrors `.gemini/hooks/SessionStart.cjs`. `.cjs` extension because\n// package.json may have \"type\": \"module\" \u2014 Claude Code execs hooks via\n// plain `node`, which would otherwise treat `.js` as ESM.\n//\n// A.3.a: mints a session UUID, persists to .totem/ledger/.session-id,\n// and appends a `session_start` event to .totem/ledger/events.ndjson\n// BEFORE running `totem describe`. Subsequent MCP calls within the\n// session correlate via session_id (ADR-029 \u00A7 Session Heuristic).\n// Fire-and-forget: any ledger failure must NOT block the briefing.\nconst { spawnSync } = require('child_process');\nconst { existsSync, mkdirSync, writeFileSync, appendFileSync } = require('fs');\nconst { randomUUID } = require('crypto');\nconst { join } = require('path');\n\n// \u2500\u2500\u2500 A.3.a: mint session ID + log session_start event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Hoisted so the selection-manifest row below (mmnto-ai/totem#2468) can carry\n// the same session UUID as its join key.\nlet mintedSessionId = null;\ntry {\n const ledgerDir = join(process.cwd(), '.totem', 'ledger');\n mkdirSync(ledgerDir, { recursive: true });\n const sessionId = randomUUID();\n mintedSessionId = sessionId;\n writeFileSync(join(ledgerDir, '.session-id'), sessionId, 'utf-8');\n // Amended ADR-078 (2026-07-15): agent_source is the env-carried seat-id\n // (TOTEM_SELF_AGENT, first non-empty comma entry), never a vendor class\n // ('claude' has no reverse projection to a seat). Omitted entirely when\n // the env var is absent: stamp absence, never guess (Tenet 4). The parse\n // deliberately mirrors deriveSearchLogAttribution (packages/mcp/src/\n // search-log.ts) and parseEnvAgentList (packages/core/src/\n // orchestration-resolver.ts) \u2014 inlined because this rendered .cjs hook\n // runs standalone in consumer repos with no access to those modules; if\n // the shared parse semantics change, change this template in the same PR.\n const selfAgent = (process.env.TOTEM_SELF_AGENT || '')\n .split(',')\n .map((s) => s.trim())\n .find((s) => s.length > 0);\n const event = {\n timestamp: new Date().toISOString(),\n type: 'session_start',\n activity_name: 'SessionStart',\n source: 'bot',\n ...(selfAgent ? { agent_source: selfAgent } : {}),\n justification: '',\n session_id: sessionId,\n };\n appendFileSync(join(ledgerDir, 'events.ndjson'), JSON.stringify(event) + '\\n', 'utf-8');\n} catch (err) {\n // Fire-and-forget; ledger failures must not block the briefing. A lightweight\n // stderr breadcrumb makes hook misconfigurations diagnosable in consumer repos\n // (CR R1 catch \u2014 empty catch suppresses all signal). stderr (not stdout) so\n // the briefing path remains clean for Claude's prompt context.\n process.stderr.write(\n '[SessionStart] Session-start telemetry unavailable (non-fatal): ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n\n// \u2500\u2500\u2500 totem-status refresh-gh \u2014 GH-federation snapshot refresh \u2500\u2500\u2500\n// (mmnto-ai/totem-status#127 C3 residual; tracking mmnto-ai/totem#2556.)\n// Spawn-and-forget, detached+unref, fired BEFORE the synchronous describe/orient\n// briefings so it overlaps them: session start must never block on it\n// (mmnto-ai/totem#2059 measured ~3s of synchronous gh calls here). The verb's\n// exit-0-or-nothing contract (single-flight, atomic rename, no-clobber when gh\n// is missing) makes blind firing safe. ENOENT = the sidecar is not adopted in\n// this repo (the common non-cohort case) \u2014 zero noise; any other spawn failure\n// keeps a non-fatal stderr breadcrumb.\n// A SECOND verb rides this same block: `totem-status refresh-obligation-store`\n// (mmnto-ai/totem-status#127 slice-two residual, sibling of mmnto-ai/totem#2556)\n// writes the durable obligation store beside the GH snapshot, so it gets the same\n// session-start moment. Same primary-checkout gate, same detached+unref spawn, same\n// inherited log fd, same ENOENT-silent arm \u2014 and each firing stamps its own `verb=`\n// field, so the log records WHICH verbs fired and in what order. That does NOT\n// restore the #2570 per-child reap discriminator: both stamps are written\n// back-to-back before either child writes, and child output carries no verb tag\n// and arrives in nondeterministic order, so a silent tail attributes only to the\n// LAST verb stamped. Reopen when the sidecar tags its own output. Blind firing\n// stays safe here too: that verb is in-process single-flight only, so it races the\n// daemon exactly the way its manual invocation already does.\n// PRIMARY checkout only (.git must be a DIRECTORY): in a linked worktree .git is a\n// pointer FILE, and a detached child inheriting the worktree cwd holds a Windows\n// directory lock that breaks worktree removal; the primary's hooks + the daemon\n// cover the workspace-level snapshot (single-flight makes extra fires redundant).\n// A non-git cwd has no refresh moment at all. The stat is cwd-anchored, not a\n// walk-up: both host runtimes launch session hooks with cwd = project root, so a\n// subdirectory cwd (which would skip) does not occur in practice \u2014 and adding a\n// git walk would cost a synchronous process on the very path this block keeps free.\ntry {\n const nodePath = require('path');\n const { statSync } = require('fs');\n let primaryCheckout = false;\n try {\n primaryCheckout = statSync(nodePath.join(process.cwd(), '.git')).isDirectory();\n } catch {\n // not a git checkout (or .git unreadable) \u2014 no refresh moment here\n }\n if (primaryCheckout) {\n const { spawn } = require('child_process');\n // Observability leg (mmnto-ai/totem#2570, routed from the status seat's\n // 2026-08-03 silent no-write): under stdio:'ignore' plus the verb's\n // exit-0-or-nothing contract, a reaped or dying child leaves NO trace\n // (Windows detached is not job-object breakaway \u2014 a hook-harness\n // tree-kill takes the child mid-run). Each firing stamps a workspace-root\n // log and hands the children the same fd, so their output lands after the\n // stamps. Measured caveat now that TWO verbs share one fd: both stamps are\n // written back-to-back before either child writes, and the children's\n // output is unlabelled and interleaves nondeterministically \u2014 so a silent\n // tail no longer discriminates per child; it attributes only to the LAST\n // verb stamped. The stamps still record which verbs fired, and in what\n // order. Log failures degrade to the previous blind firing \u2014 the stamp\n // must never block or break the spawn.\n const { openSync, closeSync, appendFileSync, existsSync, writeFileSync } = require('fs');\n // REPO-LOCAL log, inside .git (falsification round: the primary-checkout\n // gate just proved .git is a directory; never tracked, dies with the\n // clone, writable wherever git itself writes, and per-repo so concurrent\n // firings from sibling repos never interleave). A workspace-parent path\n // would grow an un-gitignorable file OUTSIDE the repo tree for every\n // consumer of these published templates \u2014 including non-adopters, whose\n // ENOENT firing still stamps.\n const logPath = nodePath.join(process.cwd(), '.git', 'totem-status-refresh-hook.log');\n // Control characters are scrubbed from path-derived fields before they\n // reach the log (terminal-injection guideline: a crafted checkout path\n // must not forge stamp lines or inject terminal controls).\n const scrub = (v) => String(v).replace(/[\\x00-\\x1f\\x7f]/g, '?');\n let stdio = 'ignore';\n let logFd = null;\n try {\n try {\n // 1 MiB self-cap: the log truncates rather than growing forever.\n if (statSync(logPath).size > 1048576) writeFileSync(logPath, '');\n } catch {\n // no log yet \u2014 nothing to cap\n }\n appendFileSync(logPath, '[' + new Date().toISOString() + '] claude spawn cwd=' + scrub(process.cwd()) + ' path-has-go-bin=' + /go[\\\\/]bin/i.test(process.env.PATH || '') + ' cwd-shadow-exe=' + existsSync(nodePath.join(process.cwd(), 'totem-status.exe')) + ' verb=refresh-gh\\n');\n logFd = openSync(logPath, 'a');\n stdio = ['ignore', logFd, logFd];\n } catch {\n // log unavailable \u2014 refresh still fires blind, as before\n }\n const refresh = spawn('totem-status', ['refresh-gh'], {\n detached: true,\n stdio,\n });\n refresh.on('error', (err) => {\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] claude spawn-error code=' + ((err && err.code) || 'unknown') + ' verb=refresh-gh\\n');\n } catch {\n // log write failed \u2014 fall through to the stderr breadcrumb\n }\n if (err && err.code === 'ENOENT') return;\n process.stderr.write('[SessionStart] totem-status refresh-gh spawn failed (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n });\n refresh.unref();\n // Second verb, same gate and same log fd (see the banner above). Written out\n // rather than looped so the spawn, the stamp, and the breadcrumb each carry a\n // literal verb \u2014 a reader of the generated hook (or of the log) never has to\n // resolve a variable to know which refresh fired.\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] claude spawn cwd=' + scrub(process.cwd()) + ' path-has-go-bin=' + /go[\\\\/]bin/i.test(process.env.PATH || '') + ' cwd-shadow-exe=' + existsSync(nodePath.join(process.cwd(), 'totem-status.exe')) + ' verb=refresh-obligation-store\\n');\n } catch {\n // log unavailable \u2014 this verb still fires blind, exactly as the first does\n }\n const refreshStore = spawn('totem-status', ['refresh-obligation-store'], {\n detached: true,\n stdio,\n });\n refreshStore.on('error', (err) => {\n try {\n appendFileSync(logPath, '[' + new Date().toISOString() + '] claude spawn-error code=' + ((err && err.code) || 'unknown') + ' verb=refresh-obligation-store\\n');\n } catch {\n // log write failed \u2014 fall through to the stderr breadcrumb\n }\n if (err && err.code === 'ENOENT') return;\n process.stderr.write('[SessionStart] totem-status refresh-obligation-store spawn failed (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n });\n refreshStore.unref();\n // Each child holds its own copy of the fd from spawn time; release the parent's\n // once BOTH are away (an early close would hand the second spawn an EBADF).\n if (logFd !== null) {\n try {\n closeSync(logFd);\n } catch {\n // nothing to release\n }\n }\n }\n} catch (err) {\n // Block-level breadcrumb: this catch covers the whole gated block, so neither\n // verb fired \u2014 it names the SIDECAR, not one verb. The per-spawn breadcrumbs\n // inside still name their own verb.\n process.stderr.write('[SessionStart] totem-status sidecar refresh unavailable (non-fatal): ' + (err instanceof Error ? err.message : String(err)) + '\\n');\n}\n\n// \u2500\u2500\u2500 totem describe briefing (existing behavior) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Captured (mmnto-ai/totem#2468): the injected block is the selection-manifest\n// candidate below \u2014 the not-installed notice is a notice, not a briefing.\nlet describeText = '';\ntry {\n const cliPath = join(process.cwd(), 'node_modules', '@mmnto', 'cli', 'dist', 'index.js');\n if (existsSync(cliPath)) {\n const result = spawnSync(process.execPath, [cliPath, 'describe'], {\n encoding: 'utf-8',\n timeout: 30000,\n });\n if (result.error) {\n throw result.error;\n }\n // Totem CLI writes diagnostic output to stderr; route to stdout so the\n // session-start context lands in Claude's prompt rather than user-visible noise.\n describeText = (result.stdout || '') + (result.stderr || '');\n process.stdout.write(describeText);\n } else {\n process.stdout.write(\n '[Totem] @mmnto/cli not installed. Run `pnpm install` (or your package manager equivalent) to enable session-start orientation.\\n',\n );\n }\n} catch (err) {\n process.stdout.write(\n '[Totem] Briefing unavailable: ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n\n// \u2500\u2500\u2500 totem orient --session \u2014 live derived in-flight state (mmnto-ai/totem#2044 PR-3) \u2500\u2500\n// ADDITIVE to describe (Tenet 13: describe = static identity sensor \u2014 scope/tier/\n// counts; orient = live in-flight sensor \u2014 open PRs/issues/board/freeze). Append,\n// never replace. Its OWN try/catch so an orient failure never disturbs the describe\n// briefing or the boot; `orient --session` is itself boot-safe (emits nothing when\n// nothing is high-signal, never exits non-zero, degrades to honest \"could not derive\").\nlet orientText = '';\ntry {\n const orientCliPath = join(process.cwd(), 'node_modules', '@mmnto', 'cli', 'dist', 'index.js');\n if (existsSync(orientCliPath)) {\n const orientResult = spawnSync(process.execPath, [orientCliPath, 'orient', '--session'], {\n encoding: 'utf-8',\n timeout: 30000,\n });\n // spawnSync sets .error (it does NOT throw) on a spawn-level failure; re-throw so\n // it surfaces through the catch breadcrumb below rather than writing '' silently.\n if (orientResult.error) {\n throw orientResult.error;\n }\n orientText = (orientResult.stdout || '') + (orientResult.stderr || '');\n process.stdout.write(orientText);\n }\n} catch (err) {\n // Boot-safe: orient is additive to describe (already emitted), so a failure never\n // blocks session start \u2014 but surface a NON-fatal breadcrumb to stderr (not stdout,\n // to keep the prompt clean) for debuggability rather than swallowing silently.\n process.stderr.write(\n '[SessionStart] orient briefing unavailable (non-fatal): ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n\n// \u2500\u2500\u2500 selection manifest (mmnto-ai/totem#2468 M1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Inline append mirroring the session_start write above \u2014 this rendered hook\n// runs standalone in consumer repos with no access to @mmnto/totem's writer.\n// The row shape is BOUND to SelectionManifestRowSchema (strict): change both\n// in the same PR; the template contract test parses this row with the real\n// schema. A zero-byte block never becomes a candidate (nothing was injected).\ntry {\n const { createHash } = require('crypto');\n const measure = (id, content, reason) => {\n const bytes = Buffer.byteLength(content, 'utf-8');\n return {\n id,\n disposition: 'selected',\n reason,\n bytes,\n approxTokens: Math.ceil(bytes / 4),\n fingerprint: createHash('sha256').update(content, 'utf-8').digest('hex').slice(0, 16),\n };\n };\n const selfAgent = (process.env.TOTEM_SELF_AGENT || '')\n .split(',')\n .map((s) => s.trim())\n .find((s) => s.length > 0);\n const row = {\n schemaVersion: 1,\n timestamp: new Date().toISOString(),\n emitter: 'session-start',\n ...(mintedSessionId ? { session_id: mintedSessionId } : {}),\n ...(selfAgent ? { agent_source: selfAgent } : {}),\n // mmnto-ai/totem#2629: provenance disclosed on every row (ruled item 1).\n // Self-minted row \u2014 the minting seat IS this process's env read, so the\n // core conflict probe is structurally inapplicable; only the two-value\n // provenance enum rides. Mirrors senseAgentAttribution's biconditional.\n agent_source_provenance: selfAgent ? 'env' : 'absent',\n context: { template: 'claude-managed' },\n universe: 'managed-template blocks: describe + orient --session',\n costBasis: { bytes: 'utf8-length', approxTokens: 'ceil(bytes/4) approximation' },\n candidates: [\n measure('describe', describeText, 'always-injected briefing'),\n measure('orient:session-block', orientText, 'always-injected briefing'),\n ].filter((c) => c.bytes > 0),\n warnings: [],\n };\n appendFileSync(\n join(process.cwd(), '.totem', 'ledger', 'selection-manifests.ndjson'),\n JSON.stringify(row) + '\\n',\n 'utf-8',\n );\n} catch (err) {\n process.stderr.write(\n '[SessionStart] selection manifest unavailable (non-fatal): ' +\n (err instanceof Error ? err.message : String(err)) +\n '\\n',\n );\n}\n// [totem] end auto-generated\n"; export declare const CLAUDE_SESSION_START_ENTRY: { hooks: { type: string; command: string; timeout: number; }[]; }; export declare const CLAUDE_GATE_WRAPPER = "// [totem] auto-generated \u2014 Claude Code action-gate wrapper\n// ONE parameterized PreToolUse wrapper for the Totem gate engine (PR-C,\n// mmnto-ai/totem#2048). Reads --event from argv (baked per-entry into\n// the installed command), reads the PreToolUse stdin envelope, shells to\n// `totem gate check`, and maps the GateVerdict disposition \u2192 host exit code.\n// `.cjs` extension because package.json may have \"type\": \"module\" \u2014 Claude\n// Code execs hooks via plain `node`, which would otherwise treat `.js` as ESM.\n//\n// Exit-code contract (LOAD-BEARING \u2014 ADR-109 \u00A72; branch ONLY on disposition):\n// 0 = allow | warn | --pilot deny | NOT-APPLICABLE fail-soft\n// (unparseable/non-object envelope, no-declared-subsystem pass-through)\n// 2 = deny (--strict, Claude block convention)\n// | APPLICABLE-gate-not-evaluable fail-closed (missing CLI, non-zero\n// `gate check`, unparseable verdict, or unknown disposition)\n'use strict';\n\nconst { spawnSync } = require('child_process');\nconst { existsSync } = require('fs');\nconst { join } = require('path');\n\n// \u2500\u2500\u2500 Parse baked args (--event , optional --pilot / --strict) \u2500\u2500\u2500\u2500\u2500\n// The tier is read ONLY from argv (baked into the installed command at\n// install time). There is NO env-var override: env sourcing would be a\n// fail-open (any shell with TOTEM_GATE_TIER=pilot could silently downgrade\n// enforcement). Default (no flag) = strict, so a default install is\n// environment-immune; --pilot is an explicit install-time opt-in.\nconst argv = process.argv.slice(2);\nlet event = '';\nlet tier = 'strict';\nfor (let i = 0; i < argv.length; i++) {\n if (argv[i] === '--event') {\n event = argv[i + 1] || '';\n i++;\n } else if (argv[i] === '--pilot') {\n tier = 'pilot';\n } else if (argv[i] === '--strict') {\n tier = 'strict';\n }\n}\n\n// Read the PreToolUse stdin envelope.\nlet stdin = '';\nprocess.stdin.setEncoding('utf-8');\nprocess.stdin.on('data', (chunk) => {\n stdin += chunk;\n});\nprocess.stdin.on('end', () => {\n let parsed;\n try {\n parsed = stdin ? JSON.parse(stdin) : {};\n } catch (err) {\n // Fail-soft on a malformed envelope (mirror PreWriteShield): a broken\n // host envelope is not an applicable gate, so it must NOT block.\n process.stderr.write('[totem gate-wrapper] could not parse stdin JSON; allowing\\n');\n process.exit(0);\n }\n\n // Valid JSON can still be a non-object (the bytes `null`, `123`, or a bare\n // quoted string). Such an envelope carries no `tool_input` to dereference and\n // is NOT an applicable gate \u2192 fail-soft (exit 0). Guarding here also prevents\n // a TypeError-on-deref from leaking as exit 1.\n if (parsed === null || typeof parsed !== 'object') {\n process.stderr.write('[totem gate-wrapper] stdin JSON is not an object; allowing\\n');\n process.exit(0);\n }\n\n const input =\n typeof parsed.tool_input === 'object' && parsed.tool_input !== null ? parsed.tool_input : {};\n\n // \u2500\u2500\u2500 THE EMPTY-SUBSYSTEM GUARDRAIL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // freeze-check's predicate is on a DECLARED subsystem. A normal Edit/Write\n // carries tool_input.file_path (a path), NOT a subsystem. With no declared\n // subsystem, NO GATE APPLIES \u2192 pass through (exit 0). Do NOT shell out \u2014 a\n // blanket fail-closed here would block every ordinary edit.\n const declaredSubsystem =\n typeof input.subsystem === 'string' && input.subsystem.trim() !== ''\n ? input.subsystem.trim()\n : '';\n if (declaredSubsystem === '') {\n process.exit(0);\n }\n\n // A gate genuinely applies. Build the --payload from the declared fields.\n // NOTE: this payload projection is freeze-check-shaped (subsystem-only). The\n // wrapper is --event-parameterized, but the payload it builds is currently\n // freeze-check-specific; a future gate needing a different payload field must\n // extend this projection (e.g. branch on `event`).\n const payload = JSON.stringify({ subsystem: declaredSubsystem });\n\n // Resolve the LOCAL Totem CLI (the global `totem` binary may be stale and\n // missing deps \u2014 the known repo gotcha). Invoke node on the installed dist\n // entry.\n //\n // FAIL-CLOSED on a missing CLI: we are PAST the empty-subsystem guardrail, so\n // a gate genuinely APPLIES here. freeze-check has NO commit-time hard floor\n // (unlike PreWriteShield, whose fail-soft is backed by `totem-lint` at\n // commit), so an APPLICABLE gate that cannot be evaluated for ANY reason\n // (missing CLI OR corrupt freeze.json) must fail closed \u2014 not silently allow\n // (guardrail rule + Tenet 4 fail-closed). Fail-SOFT (exit 0) is reserved for\n // genuinely NOT-APPLICABLE inputs (unparseable/non-object envelope, no\n // declared subsystem), all of which already returned above.\n const cliPath = join(process.cwd(), 'node_modules', '@mmnto', 'cli', 'dist', 'index.js');\n if (!existsSync(cliPath)) {\n process.stderr.write(\n '[totem gate] ' +\n event +\n ' applies but the totem CLI is not resolvable; failing closed. ' +\n 'Reinstall totem or run `totem eject` to remove the gate.\\n',\n );\n process.exit(2);\n }\n\n const result = spawnSync(\n process.execPath,\n [cliPath, 'gate', 'check', '--event', event, '--payload', payload],\n { encoding: 'utf-8', timeout: 30000 },\n );\n\n // \u2500\u2500\u2500 FAIL-CLOSED \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // A gate genuinely applies (a declared subsystem was present) and the\n // evaluation itself failed (non-zero exit: corrupt freeze.json, spawn\n // error, etc.). Never silently allow when an applicable gate's source is\n // broken \u2192 exit 2. (No-declared-subsystem already returned exit 0 above,\n // so this only blocks when a subsystem was actually declared.)\n if (result.error || typeof result.status !== 'number' || result.status !== 0) {\n process.stderr.write(\n '[totem gate-wrapper] gate \"' +\n event +\n '\" evaluation failed (source broken or unavailable) \u2014 blocking (fail-closed).\\n' +\n (result.stderr || (result.error ? String(result.error.message || result.error) : '')) +\n '\\n',\n );\n process.exit(2);\n }\n\n let verdict;\n try {\n verdict = JSON.parse(result.stdout || '');\n } catch (err) {\n // The command emitted unparseable stdout despite a 0 exit \u2014 an applicable\n // gate whose verdict we cannot read is a broken source \u2192 fail-closed.\n process.stderr.write(\n '[totem gate-wrapper] gate \"' + event + '\" emitted unparseable verdict \u2014 blocking (fail-closed).\\n',\n );\n process.exit(2);\n }\n\n // \u2500\u2500\u2500 Disposition \u2192 host exit code (branch ONLY on disposition) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const disposition = verdict && typeof verdict.disposition === 'string' ? verdict.disposition : '';\n // reason/provenance are OPAQUE stderr passthrough \u2014 never parsed for control flow.\n const detail =\n (verdict && verdict.reason ? verdict.reason : '') +\n (verdict && verdict.provenance ? ' [' + JSON.stringify(verdict.provenance) + ']' : '');\n\n if (disposition === 'allow') {\n process.exit(0);\n }\n if (disposition === 'warn') {\n process.stderr.write('[totem gate-wrapper] ' + event + ' (warn): ' + detail + '\\n');\n process.exit(0);\n }\n if (disposition === 'deny') {\n process.stderr.write('[totem gate-wrapper] ' + event + ' (deny): ' + detail + '\\n');\n process.exit(tier === 'pilot' ? 0 : 2);\n }\n\n // Unknown disposition from an applicable gate \u2014 fail-closed.\n process.stderr.write(\n '[totem gate-wrapper] gate \"' + event + '\" returned unknown disposition \"' + disposition + '\" \u2014 blocking (fail-closed).\\n',\n );\n process.exit(2);\n});\n// [totem] end auto-generated\n"; export declare const CLAUDE_GATE_WRAPPER_ENTRY: { matcher: string; hooks: { type: string; command: string; }[]; }; export declare const PREPARE_SCRIPT_REL = ".totem/prepare.cjs"; export declare const PREPARE_SCRIPT_COMMAND = "node .totem/prepare.cjs"; export declare const PREPARE_WRAPPER = "// [totem] auto-generated \u2014 Totem init-distributed prepare wrapper (mmnto-ai/totem#2410)\n// Runs `totem hook install` on `pnpm install` (the npm `prepare` lifecycle) so a\n// consumer repo's managed hooks self-repair without a manual step. Dependency-free\n// CommonJS by design \u2014 Node execs a `.cjs` via plain `node`, no build step. ALL\n// logic is in Node, NEVER a shell (the Windows quoting class, mmnto-ai/totem#2351).\n//\n// CLI resolution note: @mmnto/cli's `exports` map declares only an `import` condition\n// and no `./package.json` subpath, so `require.resolve` (both the package and its\n// package.json) throws ERR_PACKAGE_PATH_NOT_EXPORTED from this CommonJS file. The\n// working alternative is a manual node_modules walk that reads package.json off disk.\n'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst { spawnSync } = require('child_process');\n\n// Walk up from each start dir looking for node_modules/@mmnto/cli/package.json.\n// Bypasses the exports gate that blocks require.resolve from a CommonJS wrapper.\nfunction findCliPackageJson(startDirs) {\n for (const start of startDirs) {\n if (typeof start !== 'string' || start.length === 0) continue;\n let dir = start;\n while (true) {\n const candidate = path.join(dir, 'node_modules', '@mmnto', 'cli', 'package.json');\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n }\n return null;\n}\n\nconst pkgJsonPath = findCliPackageJson([__dirname, process.cwd()]);\nif (pkgJsonPath === null) {\n // Declared skip (strategy#630 class): the CLI is not a dependency here, so there\n // is nothing to install \u2014 exit 0 so `prepare` (and thus `pnpm install`) succeeds.\n process.stderr.write(\n '[totem prepare] @mmnto/cli is not installed \u2014 skipping hook install ' +\n '(managed hooks will be set up once the CLI is a dependency).\\n',\n );\n process.exit(0);\n}\n\n// Read the bin entry off the resolved manifest and resolve it to an absolute path.\nlet binJs;\ntry {\n const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));\n const bin = pkg && pkg.bin;\n let binRel = null;\n if (typeof bin === 'string') {\n binRel = bin;\n } else if (bin && typeof bin === 'object') {\n binRel = bin.totem || bin[Object.keys(bin)[0]] || null;\n }\n if (typeof binRel !== 'string' || binRel.length === 0) {\n process.stderr.write(\n '[totem prepare] @mmnto/cli is installed but declares no usable bin \u2014 ' +\n 'cannot run hook install. Reinstall @mmnto/cli.\\n',\n );\n process.exit(1);\n }\n binJs = path.resolve(path.dirname(pkgJsonPath), binRel);\n} catch (err) {\n process.stderr.write(\n '[totem prepare] could not read @mmnto/cli package.json: ' +\n (err && err.message ? err.message : String(err)) +\n '\\n',\n );\n process.exit(1);\n}\n\n// Spawn `node hook install` \u2014 never a shell (the Windows quoting class).\nconst child = spawnSync(process.execPath, [binJs, 'hook', 'install'], { stdio: 'inherit' });\nif (child.error) {\n process.stderr.write(\n '[totem prepare] failed to spawn totem hook install: ' +\n (child.error.message || String(child.error)) +\n '\\n',\n );\n process.exit(1);\n}\n// Propagate the child's exit code verbatim: a genuine hook-install failure fails\n// prepare loud; the CLI's own declared skips already exit 0.\nprocess.exit(child.status == null ? 1 : child.status);\n// [totem] end auto-generated\n"; export interface ManagedSessionHook { /** Repo-relative install path (POSIX separators). */ rel: string; /** Canonical whole-file content — embeds `marker` at the head and `endMarker` at the tail. */ content: string; /** Ownership/presence marker that must OPEN the file. */ marker: string; /** End marker that must CLOSE the bounded totem-owned region. */ endMarker: string; } export declare const GEMINI_BEFORE_TOOL_REL = ".gemini/hooks/BeforeTool.cjs"; export declare const GEMINI_BEFORE_TOOL_LEGACY_REL = ".gemini/hooks/BeforeTool.js"; export declare const GEMINI_SESSION_START_REL = ".gemini/hooks/SessionStart.cjs"; export declare const GEMINI_SESSION_START_LEGACY_REL = ".gemini/hooks/SessionStart.js"; export declare const MANAGED_SESSION_HOOKS: ReadonlyArray; export interface LegacyManagedHook { /** Pre-rename repo-relative path, removed once migrated (POSIX separators). */ legacyRel: string; /** Current repo-relative path, materialized from `content`. */ successorRel: string; /** Canonical successor content — embeds `marker` at head and `endMarker` at tail. */ content: string; /** Ownership/presence marker that must OPEN the legacy file to migrate it. */ marker: string; /** End marker bounding the totem-owned region in the legacy file. */ endMarker: string; } export declare const LEGACY_MANAGED_SESSION_HOOKS: ReadonlyArray; export declare const SKILL_MARKER_START = ""; export declare const SKILL_MARKER_END = ""; export declare const SIGNOFF_SKILL_CONTENT: string; export declare const SIGNON_SKILL_CONTENT = "---\nname: signon\ndescription: Session-start \u2014 consume/derive orientation, poll mail since last signoff, re-derive carryforward gates, present next-steps for operator ruling\n---\n\n\n\nSession-start bring-up. **Read-only** \u2014 no mutations, no dispatches, no board edits until the operator rules on next steps (Proposal 295 d2: read-only orient + grounded next-work). Solo \u2014 no agent fleet (`feedback_session_start_derive_cheaply`: cheap derivation IS the validation dogfood).\n\n1. **Consume the injected orientation.** On Claude Code seats a SessionStart hook may inject a journal + carryforward, inbound mail, branch/ticket-matched context, and a bounded session-orientation slice (parked/freeze state, open PRs, board\u2194issue coherence drift, and an open-issue-count pointer) \u2014 the hook serves the seat it is CONFIGURED for, not a derived identity, so confirm the injected journal is YOURS: a visiting session receives the HOST seat's, and your carryforward derives from `.totem/orchestration//journal/` (step 3), never from a foreign journal. On a hook-seat MISMATCH (the injected journal or mail banner names another seat), consume NONE of the injected material \u2014 treat the session as hook-less: derive it all via `totem orient` plus your own seat-anchored poll (step 2). Do not re-run what the hook injected. Everything else the bring-up needs (the full board in-flight set, corpus freshness, doctrine currency) is derived on demand via `totem orient`. On a hook-less seat (other vendors, cold starts), derive it all: `totem orient`.\n\n2. **Poll mail since last signoff \u2014 seat-anchored.** Poll AS YOUR SEAT: per-shell `TOTEM_SELF_AGENT=` (the mmnto-ai/totem#2629 scope ruling \u2014 never user/machine scope; prefer the inline form `TOTEM_SELF_AGENT= totem mail` where shell state does not persist between tool calls) or `totem mail --as `. **S0 identity check:** the banner's `Self agents:` line must name exactly your seat. Since @mmnto/cli 1.117.0 an identity-less multi-seat poll gates itself (broadcast-only serve, directed mail withheld as a count, exit 2 \u2014 the mmnto-ai/totem#2204 deterministic floor), so the residual S0 catches is the WRONG-single-identity class: a mis-scoped or inherited env naming a foreign seat resolves single-seat, ungated, and serves that seat's directed mail. A banner naming a FOREIGN seat = STOP: act on nothing served, propagate nothing from it, fix the identity, re-poll. A GATED poll's LISTING is broadcast-only by construction \u2014 but warning lines can still name an unclassifiable or unresolvable file (an ECL basename is recipient + compressed subject \u2014 the CLI's named limit), so propagate nothing from a gated poll's warnings either: fix the identity and re-poll for your directed mail. Unread = inbound \u2212 handled: consumption is tracked by `processed/` marks (`feedback_check_outbox_before_replying`), so the CLI path needs no cutoff stamp. Read every hit before proceeding \u2014 new mail can reprioritize everything below. (Fallback \u2014 a seat that must stamp-poll instead derives the cutoff from the newest journal's CONTENT date, the filename stamp or frontmatter, **never file mtime**, which git resets on clone/worktree and silently reports \"inbox clean\" over waiting mail; mmnto-ai/totem-strategy#813.)\n\n3. **Re-derive the carryforward gates \u2014 don't trust the journal's framing** (Tenet 20 read-side twin). For each carryforward item in YOUR SEAT's latest journal (`.totem/orchestration//journal/` \u2014 on a multi-seat repo another seat's newer journal is not your carryforward), freshly derive its gate state (the PR it waits on, the issue, the date, the release train) via `gh` / `git` reads. Cross-repo gates resolve through the frozen cohort roster \u2014 `totem` / `strategy` / `status` / `lc` \u2192 `mmnto-ai/{totem, totem-strategy, totem-status, liquid-city}` (mmnto-ai/totem-strategy#611 gates any change). An item whose gate fired leads the next-steps list; an item still gated is reported as waiting, not worked.\n\n4. **Surface owed-now sensors.** Anything the injected/derived orientation flags as owed (corpus `\u26A0 stale`, strategy-doctrine `\u26A0 publish owed`, board drift) goes on the list as a candidate \u2014 sensors report, they don't gate (Tenet 13).\n\n5. **Present and stop.** One message: state summary (inbox, gate states, owed-now items) + ranked next-steps with a recommendation. Then wait for the operator's ruling \u2014 signon ends at the judgment handoff; mutations belong to the ruled work, not the bring-up.\n\n\n"; export declare const REVIEW_REPLY_SKILL_CONTENT = "---\nname: review-reply\ndescription: Unified PR review triage \u2014 fetch, normalize, and batch-action bot comments\n---\n\n\n\nTriage PR review comments from all bots for PR $ARGUMENTS.\n\n## Phase 1: Fetch & Categorize (Deterministic)\n\nRun the triage command to fetch, normalize, deduplicate, and categorize all bot comments:\n\n```bash\npnpm totem triage-pr $ARGUMENTS\n```\n\nThis outputs a categorized inbox grouped by blast radius (Security \u2192 Architecture \u2192 Convention \u2192 Nits) with cross-bot deduplication already applied. The heavy lifting is done in TypeScript \u2014 no LLM math needed.\n\n**STOP HERE.** Present the output to the user and wait for them to specify actions. Do NOT proceed to Phase 2 until the user replies.\n\n## Phase 2: Execute Actions (Bulk Support)\n\nThe user may type individual IDs (e.g., `fix 4, 11`) OR use bulk actions:\n\n- `fix all security`\n- `defer all nits`\n- `extract all architecture`\n\n### `fix `\n\nMark items as will-fix. No API calls \u2014 just acknowledge. The user will make code changes next.\n\n### `defer [ticket]`\n\nAuto-reply on the PR acknowledging the deferral:\n\n- **CodeRabbit items:** Reply inline to each thread with \"Tracked in #NNN\" or \"Deferred \u2014 not blocking for this PR.\"\n- **GCA items:** DO NOT reply inline. Batch ALL GCA responses into ONE issue comment: `@gemini-code-assist` followed by a numbered list addressing each finding. Use `gh pr comment $ARGUMENTS --body-file -` and pipe the comment body via stdin.\n- **ghcq items:** `github-code-quality[bot]` has no known @-listener (attested: no in-org tag attempt has drawn a response and none is documented \u2014 mmnto-ai/totem#2626) \u2014 do not tag it; treat its dispositions as audit-trail-only.\n- **SARIF items:** No reply needed (our own tool).\n\n### `nit `\n\nSame as defer but reply text is \"Acknowledged \u2014 nit / by design.\"\n\n### `extract `\n\nFor each selected finding, generate a lesson and call `mcp__totem-dev__add_lesson` (or equivalent):\n\n- Use the bot's finding as the lesson body\n- Add relevant tags from the file path and finding category\n- The lesson will automatically get `lifecycle: nursery` treatment\n\n### `done`\n\nPrint a summary of actions taken, then \u2014 when the round is being dispositioned \u2014 assemble and post the single consolidated round-disposition comment (see the section below), which EXECUTES `totem review --covariate` to carry the `local-lane:` line, on the operator's explicit go. Then exit.\n\n## CRITICAL: GCA Reply Protocol\n\n**NEVER reply individually to GCA bot comments.** GCA has a quota and will NOT respond to replies unless they contain `@gemini-code-assist`. Always batch ALL GCA responses into a single PR-level comment using the issue comments API endpoint (`/issues/{pr}/comments`), not the review comments reply endpoint.\n\n## Consolidated round-disposition comment (a concrete step, operator-gated)\n\nDisposing the round is ONE consolidated comment (single-comment ownership per bot-protocols) \u2014 a real, numbered step of the flow, NOT an optional aside. Like every GitHub mutation in this skill it is operator-gated: assemble the body, show it, and post ONLY on an explicit human go. Run this as part of `done` (or whenever the operator asks to post the round disposition):\n\n1. **Obtain the covariate line \u2014 execute the verb, never hand-author it.** Run the read-only, zero-LLM command and capture its stdout:\n\n```bash\ntotem review --covariate\n```\n\nIt resolves the current branch lineage exactly as the review fan does and prints the canonical `local-lane:` line from the core-owned renderers \u2014 the LATEST verdict artifact's line (`.totem/artifacts/verdicts/`) when the current diff is admitted, or the exact-identity admission record's `not-applicable` form (`.totem/artifacts/admissions/`, format v1.1, mmnto-ai/totem#2473) when the current diff is a deterministic skip \u2014 never trust a pasted or hand-copied value. If it reports neither a verdict nor an admission record for the current state, there is no line to carry (note that in the body and continue).\n\n2. **Assemble the single body.** One comment: @-tag EVERY bot addressed in the round \u2014 exactly ONE tag each (e.g. `@gemini-code-assist`, `@coderabbitai`, `@greptileai`) so each bot registers the disposition, and tags must be present when the comment is POSTED, never edited in (GCA's listener fires on comment-created only). One notification per bot per round: a bot with nothing addressed gets no tag, and a bot already @-tagged in this round's batch comment (the GCA defer/nit batch above) is NOT re-tagged here. ghcq (`github-code-quality[bot]`) has no known listener \u2014 it is never tagged; its items are dispositioned in the body for the audit trail only (mmnto-ai/totem#2626). Never combine a tag with ANY bot's review trigger \u2014 triggers are standalone comments, one trigger and no prose (a trigger embedded in a content-rich comment chat-routes the bot). Then the per-item dispositions (fixed / deferred / nit / extracted) followed by the non-empty `local-lane:` line from step 1, verbatim. The local `review-loop` holds this line but never posts it, so `/review-reply` is the SOLE path that carries it to GitHub.\n\n3. **Post on an explicit go.** Show the assembled body and wait for the operator; on their go, post the ONE comment with `gh pr comment $ARGUMENTS --body-file -` (pipe the body via stdin). Never mutate the PR autonomously.\n\n\n"; export declare const REVIEW_LOOP_SKILL_CONTENT = "---\nname: review-loop\ndescription: Drive the local pre-push review loop to settle \u2014 absorb findings locally before any external bot pass\n---\n\n\n\nDrive the LOCAL pre-push review loop to convergence: run the review, absorb its findings, re-run, and repeat until the CLI reports the round **settled** \u2014 before any external bot pass. The loop state (round chaining, the settle computation, lane coverage) is entirely CLI-owned; this skill is a thin driver. Do not reimplement settle logic or count rounds yourself \u2014 read what the CLI reports.\n\nThis is NOT the external-bot triage skill. `/review-reply` handles bot comments on a PR; do NOT invoke external review bots (CodeRabbit, Gemini Code Assist, Greptile) from here. This loop settles local findings first.\n\n## The loop\n\n1. **Run the review.** `totem review` runs the repo's configured lanes. Do NOT pass `--model` unless the user explicitly asked for a one-lane run \u2014 an explicit `--model` selects a single-lane invocation and never joins the configured fan. If `review.lanes` is not configured, `totem review` runs the legacy single-lane path and emits NO verdict artifact or `local-lane:` line \u2014 this loop's contract requires the verdict artifact, so configure `review.lanes` first (a single entry suffices).\n\n2. **Read the reported outcome.** The CLI reports the findings, the lane coverage (completed / attempted), the settled state, and the round number. Take them as reported \u2014 do not derive `settled` yourself.\n\n3. **If not settled: apply fixes, then re-run.** Fix the actionable findings \u2014 **WARN and CRITICAL are actionable; INFO is cosmetic** and can be skipped. Then re-run `totem review`; the CLI chains the next round automatically from the prior verdict. An explicit `--continues ` override exists for the rare case where the CLI reports a lineage fork you know is wrong (e.g. a rebase it mis-linked) \u2014 otherwise let it chain on its own.\n\n4. **Repeat until settled \u2014 or stop honestly.** Loop until the CLI reports the round **settled**. Stop and report if the CLI's max-rounds advisory fires, or a finding is disputed. Never loop forever, and never silently override a disputed finding \u2014 a dispute goes to the human.\n\n## Honesty rules\n\n- **Never use `--override` without an explicit human go.** It is trap-ledgered.\n- **A degraded round is never settled.** If completed < attempted (a lane failed), the round did not settle \u2014 say so; a dropped lane is not a pass.\n- **Report the outcome faithfully** \u2014 the findings, the counts, and the settled state exactly as the CLI reports them.\n\n## At settle: hold the covariate line locally (never post a PR comment)\n\n`review-loop` NEVER creates or posts a PR comment. The local loop runs BEFORE any external bot pass, and the round-disposition comment is ONE consolidated comment owned by the operator-invoked `/review-reply` workflow. At settle the CLI already prints the covariate line \u2014 hold and report it locally, in exactly this format:\n\n\n\n```text\nlocal-lane: round= settled= lanes=/\nlocal-lane: not-applicable () recorded= at=\n```\n\n`` is the first 8 hex characters of the verdict artifact hash the CLI reports. The second shape is the ADMISSION form (format v1.1, mmnto-ai/totem#2473): rendered when the current diff resolves to a deterministic not-applicable admission \u2014 `` addresses the admission record in `.totem/artifacts/admissions/`, and a consumer discriminates the two forms on the literal second token `not-applicable`. This line is a versioned contract consumed by a measurement pilot \u2014 do not change either shape without a spec amendment. The CLI renders both from their canonical artifacts via single core-owned renderers, so the line is re-derivable and never hand-authored \u2014 on demand, the read-only `totem review --covariate` (zero-LLM) resolves the current state and prints the verdict's line (admitted diff) or the admission record's line (deterministic skip). Inclusion of any pending `local-lane:` line in the single consolidated round-disposition comment belongs to `/review-reply` (which obtains it by running `totem review --covariate`), not to this loop \u2014 never post it to GitHub yourself.\n\n\n"; export declare const DISTRIBUTED_CLAUDE_SKILLS: readonly [{ readonly name: "signoff"; readonly content: string; }, { readonly name: "signon"; readonly content: "---\nname: signon\ndescription: Session-start — consume/derive orientation, poll mail since last signoff, re-derive carryforward gates, present next-steps for operator ruling\n---\n\n\n\nSession-start bring-up. **Read-only** — no mutations, no dispatches, no board edits until the operator rules on next steps (Proposal 295 d2: read-only orient + grounded next-work). Solo — no agent fleet (`feedback_session_start_derive_cheaply`: cheap derivation IS the validation dogfood).\n\n1. **Consume the injected orientation.** On Claude Code seats a SessionStart hook may inject a journal + carryforward, inbound mail, branch/ticket-matched context, and a bounded session-orientation slice (parked/freeze state, open PRs, board↔issue coherence drift, and an open-issue-count pointer) — the hook serves the seat it is CONFIGURED for, not a derived identity, so confirm the injected journal is YOURS: a visiting session receives the HOST seat's, and your carryforward derives from `.totem/orchestration//journal/` (step 3), never from a foreign journal. On a hook-seat MISMATCH (the injected journal or mail banner names another seat), consume NONE of the injected material — treat the session as hook-less: derive it all via `totem orient` plus your own seat-anchored poll (step 2). Do not re-run what the hook injected. Everything else the bring-up needs (the full board in-flight set, corpus freshness, doctrine currency) is derived on demand via `totem orient`. On a hook-less seat (other vendors, cold starts), derive it all: `totem orient`.\n\n2. **Poll mail since last signoff — seat-anchored.** Poll AS YOUR SEAT: per-shell `TOTEM_SELF_AGENT=` (the mmnto-ai/totem#2629 scope ruling — never user/machine scope; prefer the inline form `TOTEM_SELF_AGENT= totem mail` where shell state does not persist between tool calls) or `totem mail --as `. **S0 identity check:** the banner's `Self agents:` line must name exactly your seat. Since @mmnto/cli 1.117.0 an identity-less multi-seat poll gates itself (broadcast-only serve, directed mail withheld as a count, exit 2 — the mmnto-ai/totem#2204 deterministic floor), so the residual S0 catches is the WRONG-single-identity class: a mis-scoped or inherited env naming a foreign seat resolves single-seat, ungated, and serves that seat's directed mail. A banner naming a FOREIGN seat = STOP: act on nothing served, propagate nothing from it, fix the identity, re-poll. A GATED poll's LISTING is broadcast-only by construction — but warning lines can still name an unclassifiable or unresolvable file (an ECL basename is recipient + compressed subject — the CLI's named limit), so propagate nothing from a gated poll's warnings either: fix the identity and re-poll for your directed mail. Unread = inbound − handled: consumption is tracked by `processed/` marks (`feedback_check_outbox_before_replying`), so the CLI path needs no cutoff stamp. Read every hit before proceeding — new mail can reprioritize everything below. (Fallback — a seat that must stamp-poll instead derives the cutoff from the newest journal's CONTENT date, the filename stamp or frontmatter, **never file mtime**, which git resets on clone/worktree and silently reports \"inbox clean\" over waiting mail; mmnto-ai/totem-strategy#813.)\n\n3. **Re-derive the carryforward gates — don't trust the journal's framing** (Tenet 20 read-side twin). For each carryforward item in YOUR SEAT's latest journal (`.totem/orchestration//journal/` — on a multi-seat repo another seat's newer journal is not your carryforward), freshly derive its gate state (the PR it waits on, the issue, the date, the release train) via `gh` / `git` reads. Cross-repo gates resolve through the frozen cohort roster — `totem` / `strategy` / `status` / `lc` → `mmnto-ai/{totem, totem-strategy, totem-status, liquid-city}` (mmnto-ai/totem-strategy#611 gates any change). An item whose gate fired leads the next-steps list; an item still gated is reported as waiting, not worked.\n\n4. **Surface owed-now sensors.** Anything the injected/derived orientation flags as owed (corpus `⚠ stale`, strategy-doctrine `⚠ publish owed`, board drift) goes on the list as a candidate — sensors report, they don't gate (Tenet 13).\n\n5. **Present and stop.** One message: state summary (inbox, gate states, owed-now items) + ranked next-steps with a recommendation. Then wait for the operator's ruling — signon ends at the judgment handoff; mutations belong to the ruled work, not the bring-up.\n\n\n"; }, { readonly name: "review-reply"; readonly content: "---\nname: review-reply\ndescription: Unified PR review triage — fetch, normalize, and batch-action bot comments\n---\n\n\n\nTriage PR review comments from all bots for PR $ARGUMENTS.\n\n## Phase 1: Fetch & Categorize (Deterministic)\n\nRun the triage command to fetch, normalize, deduplicate, and categorize all bot comments:\n\n```bash\npnpm totem triage-pr $ARGUMENTS\n```\n\nThis outputs a categorized inbox grouped by blast radius (Security → Architecture → Convention → Nits) with cross-bot deduplication already applied. The heavy lifting is done in TypeScript — no LLM math needed.\n\n**STOP HERE.** Present the output to the user and wait for them to specify actions. Do NOT proceed to Phase 2 until the user replies.\n\n## Phase 2: Execute Actions (Bulk Support)\n\nThe user may type individual IDs (e.g., `fix 4, 11`) OR use bulk actions:\n\n- `fix all security`\n- `defer all nits`\n- `extract all architecture`\n\n### `fix `\n\nMark items as will-fix. No API calls — just acknowledge. The user will make code changes next.\n\n### `defer [ticket]`\n\nAuto-reply on the PR acknowledging the deferral:\n\n- **CodeRabbit items:** Reply inline to each thread with \"Tracked in #NNN\" or \"Deferred — not blocking for this PR.\"\n- **GCA items:** DO NOT reply inline. Batch ALL GCA responses into ONE issue comment: `@gemini-code-assist` followed by a numbered list addressing each finding. Use `gh pr comment $ARGUMENTS --body-file -` and pipe the comment body via stdin.\n- **ghcq items:** `github-code-quality[bot]` has no known @-listener (attested: no in-org tag attempt has drawn a response and none is documented — mmnto-ai/totem#2626) — do not tag it; treat its dispositions as audit-trail-only.\n- **SARIF items:** No reply needed (our own tool).\n\n### `nit `\n\nSame as defer but reply text is \"Acknowledged — nit / by design.\"\n\n### `extract `\n\nFor each selected finding, generate a lesson and call `mcp__totem-dev__add_lesson` (or equivalent):\n\n- Use the bot's finding as the lesson body\n- Add relevant tags from the file path and finding category\n- The lesson will automatically get `lifecycle: nursery` treatment\n\n### `done`\n\nPrint a summary of actions taken, then — when the round is being dispositioned — assemble and post the single consolidated round-disposition comment (see the section below), which EXECUTES `totem review --covariate` to carry the `local-lane:` line, on the operator's explicit go. Then exit.\n\n## CRITICAL: GCA Reply Protocol\n\n**NEVER reply individually to GCA bot comments.** GCA has a quota and will NOT respond to replies unless they contain `@gemini-code-assist`. Always batch ALL GCA responses into a single PR-level comment using the issue comments API endpoint (`/issues/{pr}/comments`), not the review comments reply endpoint.\n\n## Consolidated round-disposition comment (a concrete step, operator-gated)\n\nDisposing the round is ONE consolidated comment (single-comment ownership per bot-protocols) — a real, numbered step of the flow, NOT an optional aside. Like every GitHub mutation in this skill it is operator-gated: assemble the body, show it, and post ONLY on an explicit human go. Run this as part of `done` (or whenever the operator asks to post the round disposition):\n\n1. **Obtain the covariate line — execute the verb, never hand-author it.** Run the read-only, zero-LLM command and capture its stdout:\n\n```bash\ntotem review --covariate\n```\n\nIt resolves the current branch lineage exactly as the review fan does and prints the canonical `local-lane:` line from the core-owned renderers — the LATEST verdict artifact's line (`.totem/artifacts/verdicts/`) when the current diff is admitted, or the exact-identity admission record's `not-applicable` form (`.totem/artifacts/admissions/`, format v1.1, mmnto-ai/totem#2473) when the current diff is a deterministic skip — never trust a pasted or hand-copied value. If it reports neither a verdict nor an admission record for the current state, there is no line to carry (note that in the body and continue).\n\n2. **Assemble the single body.** One comment: @-tag EVERY bot addressed in the round — exactly ONE tag each (e.g. `@gemini-code-assist`, `@coderabbitai`, `@greptileai`) so each bot registers the disposition, and tags must be present when the comment is POSTED, never edited in (GCA's listener fires on comment-created only). One notification per bot per round: a bot with nothing addressed gets no tag, and a bot already @-tagged in this round's batch comment (the GCA defer/nit batch above) is NOT re-tagged here. ghcq (`github-code-quality[bot]`) has no known listener — it is never tagged; its items are dispositioned in the body for the audit trail only (mmnto-ai/totem#2626). Never combine a tag with ANY bot's review trigger — triggers are standalone comments, one trigger and no prose (a trigger embedded in a content-rich comment chat-routes the bot). Then the per-item dispositions (fixed / deferred / nit / extracted) followed by the non-empty `local-lane:` line from step 1, verbatim. The local `review-loop` holds this line but never posts it, so `/review-reply` is the SOLE path that carries it to GitHub.\n\n3. **Post on an explicit go.** Show the assembled body and wait for the operator; on their go, post the ONE comment with `gh pr comment $ARGUMENTS --body-file -` (pipe the body via stdin). Never mutate the PR autonomously.\n\n\n"; }, { readonly name: "review-loop"; readonly content: "---\nname: review-loop\ndescription: Drive the local pre-push review loop to settle — absorb findings locally before any external bot pass\n---\n\n\n\nDrive the LOCAL pre-push review loop to convergence: run the review, absorb its findings, re-run, and repeat until the CLI reports the round **settled** — before any external bot pass. The loop state (round chaining, the settle computation, lane coverage) is entirely CLI-owned; this skill is a thin driver. Do not reimplement settle logic or count rounds yourself — read what the CLI reports.\n\nThis is NOT the external-bot triage skill. `/review-reply` handles bot comments on a PR; do NOT invoke external review bots (CodeRabbit, Gemini Code Assist, Greptile) from here. This loop settles local findings first.\n\n## The loop\n\n1. **Run the review.** `totem review` runs the repo's configured lanes. Do NOT pass `--model` unless the user explicitly asked for a one-lane run — an explicit `--model` selects a single-lane invocation and never joins the configured fan. If `review.lanes` is not configured, `totem review` runs the legacy single-lane path and emits NO verdict artifact or `local-lane:` line — this loop's contract requires the verdict artifact, so configure `review.lanes` first (a single entry suffices).\n\n2. **Read the reported outcome.** The CLI reports the findings, the lane coverage (completed / attempted), the settled state, and the round number. Take them as reported — do not derive `settled` yourself.\n\n3. **If not settled: apply fixes, then re-run.** Fix the actionable findings — **WARN and CRITICAL are actionable; INFO is cosmetic** and can be skipped. Then re-run `totem review`; the CLI chains the next round automatically from the prior verdict. An explicit `--continues ` override exists for the rare case where the CLI reports a lineage fork you know is wrong (e.g. a rebase it mis-linked) — otherwise let it chain on its own.\n\n4. **Repeat until settled — or stop honestly.** Loop until the CLI reports the round **settled**. Stop and report if the CLI's max-rounds advisory fires, or a finding is disputed. Never loop forever, and never silently override a disputed finding — a dispute goes to the human.\n\n## Honesty rules\n\n- **Never use `--override` without an explicit human go.** It is trap-ledgered.\n- **A degraded round is never settled.** If completed < attempted (a lane failed), the round did not settle — say so; a dropped lane is not a pass.\n- **Report the outcome faithfully** — the findings, the counts, and the settled state exactly as the CLI reports them.\n\n## At settle: hold the covariate line locally (never post a PR comment)\n\n`review-loop` NEVER creates or posts a PR comment. The local loop runs BEFORE any external bot pass, and the round-disposition comment is ONE consolidated comment owned by the operator-invoked `/review-reply` workflow. At settle the CLI already prints the covariate line — hold and report it locally, in exactly this format:\n\n\n\n```text\nlocal-lane: round= settled= lanes=/\nlocal-lane: not-applicable () recorded= at=\n```\n\n`` is the first 8 hex characters of the verdict artifact hash the CLI reports. The second shape is the ADMISSION form (format v1.1, mmnto-ai/totem#2473): rendered when the current diff resolves to a deterministic not-applicable admission — `` addresses the admission record in `.totem/artifacts/admissions/`, and a consumer discriminates the two forms on the literal second token `not-applicable`. This line is a versioned contract consumed by a measurement pilot — do not change either shape without a spec amendment. The CLI renders both from their canonical artifacts via single core-owned renderers, so the line is re-derivable and never hand-authored — on demand, the read-only `totem review --covariate` (zero-LLM) resolves the current state and prints the verdict's line (admitted diff) or the admission record's line (deterministic skip). Inclusion of any pending `local-lane:` line in the single consolidated round-disposition comment belongs to `/review-reply` (which obtains it by running `totem review --covariate`), not to this loop — never post it to GitHub yourself.\n\n\n"; }]; export declare function generateConfig(targets: IngestTarget[], embeddingTier: EmbeddingTier, cwd: string): Promise; /** * Generate a YAML configuration file. */ export declare function generateYamlConfig(targets: IngestTarget[], embeddingTier: EmbeddingTier, cwd: string): Promise; /** * Generate a TOML configuration file. */ export declare function generateTomlConfig(targets: IngestTarget[], embeddingTier: EmbeddingTier, cwd: string): Promise; /** * Generate config in the specified format. */ export declare function generateConfigForFormat(format: ConfigFormat, targets: IngestTarget[], embeddingTier: EmbeddingTier, cwd: string): Promise<{ content: string; filename: string; }>; //# sourceMappingURL=init-templates.d.ts.map