@vortex-os/base 0.12.0 → 0.13.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.
@@ -57,8 +57,9 @@ function moduleDir(ctx, moduleName) {
57
57
  import { existsSync, readFileSync } from "fs";
58
58
  import { join as join2 } from "path";
59
59
  var DEFAULT_CONFIG = {
60
- autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },
60
+ autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, archiveCommit: true, archiveAtEnd: true, archiveRawRetentionDays: 30, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },
61
61
  updates: { check: "session" },
62
+ sync: { autoPush: false },
62
63
  environments: []
63
64
  };
64
65
  function vortexConfigPath(ctx) {
@@ -104,6 +105,12 @@ function loadVortexConfig(ctx) {
104
105
  const reindex = rawAuto.reindex === void 0 ? true : rawAuto.reindex === true;
105
106
  const backfill = rawAuto.backfill === void 0 ? true : rawAuto.backfill === true;
106
107
  const failures = rawAuto.failures === void 0 ? true : rawAuto.failures === true;
108
+ const archiveCommit = rawAuto.archiveCommit === void 0 ? true : rawAuto.archiveCommit === true;
109
+ const archiveAtEnd = rawAuto.archiveAtEnd === void 0 ? true : rawAuto.archiveAtEnd === true;
110
+ const rawRetention = rawAuto.archiveRawRetentionDays;
111
+ const archiveRawRetentionDays = typeof rawRetention === "number" && Number.isFinite(rawRetention) && rawRetention >= 0 ? Math.floor(rawRetention) : rawRetention === false ? 0 : DEFAULT_CONFIG.autoRecord.archiveRawRetentionDays;
112
+ const rawSync = raw.sync && typeof raw.sync === "object" && !Array.isArray(raw.sync) ? raw.sync : {};
113
+ const autoPush = rawSync.autoPush === true;
107
114
  const rawDays = rawAuto.handoffRetentionDays;
108
115
  const handoffRetentionDays = typeof rawDays === "number" && Number.isFinite(rawDays) && rawDays > 0 ? Math.floor(rawDays) : DEFAULT_CONFIG.autoRecord.handoffRetentionDays;
109
116
  return {
@@ -116,9 +123,13 @@ function loadVortexConfig(ctx) {
116
123
  handoffRetentionDays,
117
124
  reindex,
118
125
  backfill,
119
- failures
126
+ failures,
127
+ archiveCommit,
128
+ archiveAtEnd,
129
+ archiveRawRetentionDays
120
130
  },
121
131
  updates: { check },
132
+ sync: { autoPush },
122
133
  environments
123
134
  };
124
135
  } catch {
@@ -298,4 +309,4 @@ export {
298
309
  atomicWriteFile,
299
310
  dist_exports
300
311
  };
301
- //# sourceMappingURL=chunk-T53UWSTR.js.map
312
+ //# sourceMappingURL=chunk-IZIAMK3L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/frontmatter.ts","../../core/src/paths.ts","../../core/src/config.ts","../../core/src/index.ts","../../core/src/types.ts","../../core/src/privacy.ts","../../core/src/safe-fs.ts"],"sourcesContent":["import { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport type { FrontmatterDoc, Privacy } from \"./types.js\";\n\nconst FENCE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\nconst BOM = \"\";\n\n/**\n * Parse a markdown source into its YAML frontmatter and body. If no frontmatter\n * fence is present, returns an empty frontmatter object and the source unchanged.\n *\n * A leading UTF-8 BOM is stripped before matching. Windows-authored files\n * frequently begin with ``, which would otherwise prevent the fence\n * regex from anchoring to `---` at byte 0.\n */\nexport function parseFrontmatter<T = Record<string, unknown>>(\n source: string,\n): FrontmatterDoc<T> {\n const cleaned = source.startsWith(BOM) ? source.slice(1) : source;\n const match = cleaned.match(FENCE);\n if (!match) {\n return {\n frontmatter: {} as T & { privacy?: Privacy },\n body: cleaned,\n };\n }\n const yaml = match[1] ?? \"\";\n const body = cleaned.slice(match[0].length);\n let parsed: T & { privacy?: Privacy };\n try {\n const value: unknown = parseYaml(yaml);\n // Frontmatter must be a mapping. Scalars, arrays, and null (e.g. a fence\n // holding only a string, a list, or nothing) are normalized to an empty\n // object so the result keeps its record shape and downstream code that\n // assumes key access cannot crash.\n parsed =\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as T & { privacy?: Privacy })\n : ({} as T & { privacy?: Privacy });\n } catch {\n // The fence was present but its YAML body did not parse — often the\n // result of copy/paste artifacts (a stray `---` mid-document, headings\n // accidentally pasted inside the fence). Treat as no frontmatter so\n // callers can still read the body; the fence content is discarded.\n parsed = {} as T & { privacy?: Privacy };\n }\n return { frontmatter: parsed, body };\n}\n\n/**\n * Serialize a `FrontmatterDoc` back into markdown text. If the frontmatter is\n * empty, the body is returned unchanged (no empty fence is emitted).\n */\nexport function serializeFrontmatter<T = Record<string, unknown>>(\n doc: FrontmatterDoc<T>,\n): string {\n const keys = Object.keys(doc.frontmatter ?? {});\n if (keys.length === 0) return doc.body;\n const yaml = stringifyYaml(doc.frontmatter).trimEnd();\n return `---\\n${yaml}\\n---\\n${doc.body}`;\n}\n","import { resolve, join } from \"node:path\";\nimport type { ModuleContext } from \"./types.js\";\n\n/**\n * Build a `ModuleContext` for the given VortEX repository root. The root is\n * resolved to an absolute path; standard subdirectories are derived by\n * convention and are not guaranteed to exist on disk.\n */\nexport function makeContext(repoRoot: string): ModuleContext {\n const root = resolve(repoRoot);\n return {\n repoRoot: root,\n agentDir: join(root, \".agent\"),\n dataDir: join(root, \"data\"),\n modulesDir: join(root, \"modules\"),\n pluginsDir: join(root, \"plugins\"),\n };\n}\n\n/**\n * Resolve the directory of a named module within the repository.\n */\nexport function moduleDir(ctx: ModuleContext, moduleName: string): string {\n return join(ctx.modulesDir, moduleName);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport type { ModuleContext } from \"./types.js\";\r\n\r\n/**\r\n * Instance configuration for VortEX, read from `<agentDir>/vortex.json`.\r\n *\r\n * The config exists to give the user control over the **auto-maintained**\r\n * operational behaviors (see `AI-RULES.md` → \"Default behaviors\") without\r\n * touching code: toggle any auto-record off, and declare how this machine's\r\n * environment is detected. A fresh instance has no config file — everything\r\n * is on, no environment — so the framework works out of the box and the file\r\n * is purely opt-in tuning.\r\n */\r\n\r\n/** Which auto-maintained operational records are enabled. Default: all on. */\r\nexport interface AutoRecordConfig {\r\n readonly sessionStart: boolean;\r\n readonly worklog: boolean;\r\n readonly decision: boolean;\r\n readonly ambientRecall: boolean;\r\n /**\r\n * Catch-up: at session start, ingest conversation transcripts that have not\r\n * been archived yet (the host's own machine only). Text is stored\r\n * immediately; vectorization is deferred to recall/rebuild so start stays\r\n * fast. Off → no automatic ingest; the archive only grows when explicitly\r\n * rebuilt.\r\n */\r\n readonly archive: boolean;\r\n /**\r\n * Auto-commit the conversation archive. After catch-up writes new transcripts\r\n * into `data/_session-archive/`, commit exactly that pathspec — and nothing\r\n * else in the working tree — so archived conversations never linger as the\r\n * \"N uncommitted change(s)\" noise in the next session-start report. Runs at\r\n * session start, session end, and `vortex catch-up`; gated additionally on a\r\n * healthy repo (no in-progress merge/rebase/lock, not a detached HEAD).\r\n * Best-effort: any git failure is a silent skip. On by default; off → the\r\n * archive still grows but you commit it yourself.\r\n */\r\n readonly archiveCommit: boolean;\r\n /**\r\n * Run catch-up (ingest + the auto-commit above) at session END too, not only\r\n * at start. The ending session itself is usually still skipped (its\r\n * transcript was just written, so the live-session guard catches it; if it\r\n * is ingested after a long idle pause, later transcript writes advance the\r\n * source mtime and the next catch-up re-ingests — the archive self-heals),\r\n * but every OTHER session closed earlier lands in the archive the moment you\r\n * wrap up, instead of waiting for the next session start. Crash-safe: if the\r\n * end hook never fires, the next start catches up as before. On by default.\r\n */\r\n readonly archiveAtEnd: boolean;\r\n /**\r\n * Days to keep the RAW transcript copies (`_session-archive/raw/`) before the\r\n * sweep deletes them from the local working tree. Raw files are byte-for-byte\r\n * duplicates kept for debugging/fidelity; recall, reindex, and cross-machine\r\n * sync all use only the NORMALIZED files (which are kept forever), so this\r\n * deletion loses nothing. Default 30; `0` disables the sweep entirely. A\r\n * malformed value falls back to the default.\r\n */\r\n readonly archiveRawRetentionDays: number;\r\n /**\r\n * Auto-vectorize: at session start (after catch-up), embed any\r\n * newly-archived sessions and memories so semantic recall — including the\r\n * ambient \"did we discuss this?\" path — stays current without a manual\r\n * rebuild. **Gated on the recall index already existing** (`_indexes/\r\n * memory.sqlite`): until the user has set recall up once (which downloads the\r\n * embedding model), this is a silent no-op, so it never triggers a download\r\n * on its own. On by default; off → vectors only update on an explicit\r\n * `/recall` / rebuild.\r\n */\r\n readonly vectorize: boolean;\r\n /**\r\n * Auto-download the embedding model the FIRST time recall is set up. When\r\n * `@vortex-os/memory-extended` is installed but the recall index does not\r\n * exist yet, session start kicks off a **background** worker that downloads\r\n * the model (~470 MB, once) and builds the index — so installing the add-on\r\n * is itself the opt-in and search turns on without a manual `/recall`. The\r\n * download never blocks session start (it runs detached). On by default; set\r\n * `false` — or env `VORTEX_VECTORIZE_AUTO_DOWNLOAD=0` — to keep the first\r\n * download manual (metered connections, CI). Independent of `vectorize`: if\r\n * `vectorize` is off, nothing runs regardless.\r\n */\r\n readonly vectorizeAutoDownload: boolean;\r\n /**\r\n * Auto-commit framework bookkeeping. When `vortex update` refreshes\r\n * framework-owned files it also rewrites the ownership manifest\r\n * (`data/.vortex/ownership.json`); on its own that leaves an uncommitted\r\n * \"trail\" that the next session reports as carried-over work, even though it\r\n * is plumbing the user never touched. With this on, `vortex update` commits\r\n * exactly the framework files it changed (the manifest + the templates it\r\n * refreshed) — and nothing else in the working tree — so an update leaves a\r\n * clean tree. Best-effort: a non-git folder, or any git failure, is a silent\r\n * no-op (the command still succeeds). On by default; set `false` to keep the\r\n * commit manual (e.g. you prefer to review and commit framework changes\r\n * yourself). Conflicts (`<file>.new`) are never auto-committed — those still\r\n * wait for you to merge.\r\n */\r\n readonly commitFrameworkChanges: boolean;\r\n /**\r\n * Use the dedicated session hand-off store (`data/_handoff/`) — a forward-\r\n * looking baton kept separate from the worklog. On by default. When off,\r\n * hand-offs fall back to the legacy `## 다음 작업` worklog section and no\r\n * auto-archiving runs.\r\n */\r\n readonly handoff: boolean;\r\n /**\r\n * Days a hand-off stays \"active\" before session start sweeps it to\r\n * `_handoff/_archive/` (deterministic, age-based — git keeps the history).\r\n * Default 7 (covers a week-long absence); set lower to tidy faster, higher to\r\n * keep more around. A non-positive / malformed value falls back to the default.\r\n */\r\n readonly handoffRetentionDays: number;\r\n /**\r\n * Auto-reindex the on-demand memory index (`_memory/_INDEX.md`) at session\r\n * start when it looks stale (a memory file is newer than the index).\r\n * Mechanical and idempotent — it only rewrites `_INDEX.md` from each memory's\r\n * frontmatter, never a memory's content, and writes only when the rendered\r\n * index actually changes (then refreshes the index's mtime so the stale flag\r\n * clears). On by default; off → the start report just flags the stale index\r\n * for the agent to fix instead of fixing it automatically.\r\n */\r\n readonly reindex: boolean;\r\n /**\r\n * Auto-backfill a missed worklog. When a past day had commits but no worklog,\r\n * the in-session agent reconstructs and writes it at the next session start\r\n * (zero-worklog days only, capped, labeled as reconstructed — see AI-RULES.md\r\n * \"Default behaviors\"). Gated by `worklog` as well: turning `worklog` off\r\n * suppresses backfill too. On by default; off → the start report still flags\r\n * the gap but the agent leaves it for you.\r\n */\r\n readonly backfill: boolean;\r\n /**\r\n * The failure ledger (`data/_failures/`) — the self-improvement loop's\r\n * evidence store. With this on, the agent records noticed failures\r\n * (append-only, one file per occurrence) and the session-start report\r\n * surfaces recurring keys with their escalation-ladder stage (2nd occurrence\r\n * → the covering rule's gate is mandatory; 3rd+ → propose a deterministic\r\n * guard — always a proposal, never auto-installed). On by default; off → no\r\n * recording ritual and no report section ( `vortex failure` still works when\r\n * invoked explicitly).\r\n */\r\n readonly failures: boolean;\r\n}\r\n\r\n/**\r\n * One environment label plus the signal that selects it. Rules are evaluated\r\n * in order; the first match wins. Generalizes the operator's home/work\r\n * `Test-Path` pattern into a portable, declarative form.\r\n */\r\nexport interface EnvironmentRule {\r\n readonly label: string;\r\n /** Match when this absolute path exists on the machine. */\r\n readonly pathExists?: string;\r\n /** Match when the OS hostname equals this (case-insensitive). */\r\n readonly hostname?: string;\r\n /**\r\n * Match when an environment variable is set. A bare string matches on\r\n * presence; the object form additionally requires a specific value.\r\n */\r\n readonly envVar?: string | { readonly name: string; readonly equals?: string };\r\n}\r\n\r\n/**\r\n * Update-awareness behavior. `check` controls the once-per-session check for a\r\n * newer published `@vortex-os/base`:\r\n * - `\"session\"` (default): check the npm registry once at session start and,\r\n * when a newer version exists, surface it (the agent then asks before any\r\n * install — nothing is installed automatically). Offline → silent skip.\r\n * - `\"off\"`: no automatic check, no network contact for updates.\r\n * On either setting the local \"templates not yet applied\" notice (no network)\r\n * and the on-demand `vortex check-updates` still work.\r\n */\r\nexport interface UpdatesConfig {\r\n readonly check: \"session\" | \"off\";\r\n}\r\n\r\n/**\r\n * Git-sync behavior. `autoPush` is the ONE exception to the \"push stays\r\n * explicit\" default, and only when you opt in: after an archive auto-commit\r\n * (session start/end, `vortex catch-up`), fast-forward-pull then `git push`.\r\n * IMPORTANT semantics: git cannot push a single commit — a push sends EVERY\r\n * unpushed commit on the current branch, including your own manual ones. Off\r\n * by default; only an explicit `true` enables it (fail-closed: any other\r\n * value, typo included, keeps push manual).\r\n */\r\nexport interface SyncConfig {\r\n readonly autoPush: boolean;\r\n}\r\n\r\nexport interface VortexConfig {\r\n readonly autoRecord: AutoRecordConfig;\r\n readonly updates: UpdatesConfig;\r\n readonly sync: SyncConfig;\r\n readonly environments: readonly EnvironmentRule[];\r\n}\r\n\r\nconst DEFAULT_CONFIG: VortexConfig = {\r\n autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, archiveCommit: true, archiveAtEnd: true, archiveRawRetentionDays: 30, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },\r\n updates: { check: \"session\" },\r\n sync: { autoPush: false },\r\n environments: [],\r\n};\r\n\r\n/** Path of the instance config file: `<agentDir>/vortex.json`. */\r\nexport function vortexConfigPath(ctx: ModuleContext): string {\r\n return join(ctx.agentDir, \"vortex.json\");\r\n}\r\n\r\n/**\r\n * Validate one raw environment rule, returning a clean `EnvironmentRule` or\r\n * `null` when it is unusable. A rule must be a plain object with a string\r\n * `label`; `hostname`/`pathExists` are accepted only as strings (dropped\r\n * otherwise) and `envVar` only as a string or an object with a string `name`\r\n * (with an optional string `equals`). Dropping malformed rules keeps a bad\r\n * config from throwing later in `resolveEnvironment`.\r\n */\r\nfunction parseEnvironmentRule(raw: unknown): EnvironmentRule | null {\r\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\r\n const candidate = raw as Record<string, unknown>;\r\n if (typeof candidate.label !== \"string\") return null;\r\n const rule: { -readonly [K in keyof EnvironmentRule]: EnvironmentRule[K] } = {\r\n label: candidate.label,\r\n };\r\n if (typeof candidate.pathExists === \"string\") rule.pathExists = candidate.pathExists;\r\n if (typeof candidate.hostname === \"string\") rule.hostname = candidate.hostname;\r\n if (typeof candidate.envVar === \"string\") {\r\n rule.envVar = candidate.envVar;\r\n } else if (typeof candidate.envVar === \"object\" && candidate.envVar !== null) {\r\n const ev = candidate.envVar as Record<string, unknown>;\r\n if (typeof ev.name === \"string\") {\r\n rule.envVar =\r\n typeof ev.equals === \"string\" ? { name: ev.name, equals: ev.equals } : { name: ev.name };\r\n }\r\n }\r\n return rule;\r\n}\r\n\r\n/**\r\n * Load the instance config, merged over defaults. A missing, unreadable, or\r\n * invalid file yields defaults (everything on, no environments) rather than\r\n * throwing — config is opt-in tuning, never a prerequisite. Environment rules\r\n * are validated individually; malformed ones are dropped so that a partly bad\r\n * config still resolves rather than throwing in `resolveEnvironment`.\r\n */\r\nexport function loadVortexConfig(ctx: ModuleContext): VortexConfig {\r\n const path = vortexConfigPath(ctx);\r\n if (!existsSync(path)) return DEFAULT_CONFIG;\r\n try {\r\n const raw = JSON.parse(readFileSync(path, \"utf8\")) as Partial<VortexConfig>;\r\n const environments = Array.isArray(raw.environments)\r\n ? raw.environments\r\n .map(parseEnvironmentRule)\r\n .filter((rule): rule is EnvironmentRule => rule !== null)\r\n : [];\r\n // Resolve `updates.check`, normalizing and FAILING CLOSED toward no network:\r\n // - missing / a malformed `updates` shape → the default (\"session\", on);\r\n // - an explicit \"session\" (any case/whitespace) stays on;\r\n // - ANY other explicit value (incl. \"off\", \"OFF\", \" off \", a typo, or a\r\n // non-string) DISABLES the check — the only reason to set a non-default\r\n // value is to turn it off, so a mistyped opt-out must not silently keep\r\n // phoning home.\r\n const rawUpdates =\r\n raw.updates && typeof raw.updates === \"object\" && !Array.isArray(raw.updates)\r\n ? (raw.updates as unknown as Record<string, unknown>)\r\n : {};\r\n const rawCheck = rawUpdates.check;\r\n const check: UpdatesConfig[\"check\"] =\r\n rawCheck === undefined\r\n ? \"session\"\r\n : typeof rawCheck === \"string\" && rawCheck.trim().toLowerCase() === \"session\"\r\n ? \"session\"\r\n : \"off\";\r\n // `vectorizeAutoDownload` fails CLOSED toward NOT downloading: absent → on\r\n // (the default opt-in); explicit `true` → on; ANY other explicit value\r\n // (false, \"off\", a typo, a non-boolean) → off, so a mistyped opt-out never\r\n // silently keeps the ~470 MB auto-download on.\r\n const rawAuto =\r\n raw.autoRecord && typeof raw.autoRecord === \"object\" && !Array.isArray(raw.autoRecord)\r\n ? (raw.autoRecord as unknown as Record<string, unknown>)\r\n : {};\r\n const vectorizeAutoDownload =\r\n rawAuto.vectorizeAutoDownload === undefined ? true : rawAuto.vectorizeAutoDownload === true;\r\n // `commitFrameworkChanges` fails CLOSED toward NOT committing: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value (false,\r\n // \"off\", a typo, a non-boolean) → off — so a mistyped opt-out never leaves\r\n // the framework silently committing into the user's repo.\r\n const commitFrameworkChanges =\r\n rawAuto.commitFrameworkChanges === undefined ? true : rawAuto.commitFrameworkChanges === true;\r\n // `handoff` fails CLOSED toward the legacy worklog-section behavior: absent →\r\n // on (the default), explicit `true` → on, ANY other explicit value → off.\r\n const handoff = rawAuto.handoff === undefined ? true : rawAuto.handoff === true;\r\n // `reindex` / `backfill` fail CLOSED toward NOT auto-writing: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value (false,\r\n // \"off\", a typo, a non-boolean) → off — so a mistyped opt-out never leaves\r\n // the framework auto-writing the memory index or a backfilled worklog.\r\n const reindex = rawAuto.reindex === undefined ? true : rawAuto.reindex === true;\r\n const backfill = rawAuto.backfill === undefined ? true : rawAuto.backfill === true;\r\n // `failures` (the failure ledger) fails CLOSED toward NOT auto-writing,\r\n // same shape as reindex/backfill: absent → on, explicit `true` → on, ANY\r\n // other explicit value → off.\r\n const failures = rawAuto.failures === undefined ? true : rawAuto.failures === true;\r\n // `archiveCommit` / `archiveAtEnd` fail CLOSED toward NOT auto-writing into\r\n // the user's git history / NOT running work at session end: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value → off.\r\n const archiveCommit =\r\n rawAuto.archiveCommit === undefined ? true : rawAuto.archiveCommit === true;\r\n const archiveAtEnd = rawAuto.archiveAtEnd === undefined ? true : rawAuto.archiveAtEnd === true;\r\n // `archiveRawRetentionDays`: a finite number ≥ 0 is honored (floored; 0 =\r\n // sweep off). Anything else falls back to the default — EXCEPT explicit\r\n // `false`, which reads as \"don't delete\" and maps to 0, because the risky\r\n // side of this option is deletion, so an opt-out spelled as a boolean must\r\n // not silently fall back to the deleting default.\r\n const rawRetention = rawAuto.archiveRawRetentionDays;\r\n const archiveRawRetentionDays =\r\n typeof rawRetention === \"number\" && Number.isFinite(rawRetention) && rawRetention >= 0\r\n ? Math.floor(rawRetention)\r\n : rawRetention === false\r\n ? 0\r\n : DEFAULT_CONFIG.autoRecord.archiveRawRetentionDays;\r\n // `sync.autoPush` fails CLOSED toward NOT pushing (the network-visible,\r\n // hardest-to-undo action here): only an explicit `true` enables it.\r\n const rawSync =\r\n raw.sync && typeof raw.sync === \"object\" && !Array.isArray(raw.sync)\r\n ? (raw.sync as unknown as Record<string, unknown>)\r\n : {};\r\n const autoPush = rawSync.autoPush === true;\r\n // `handoffRetentionDays`: a positive finite number is honored (floored);\r\n // anything else falls back to the default, so a typo never disables pruning\r\n // silently or sets a nonsensical window.\r\n const rawDays = rawAuto.handoffRetentionDays;\r\n const handoffRetentionDays =\r\n typeof rawDays === \"number\" && Number.isFinite(rawDays) && rawDays > 0\r\n ? Math.floor(rawDays)\r\n : DEFAULT_CONFIG.autoRecord.handoffRetentionDays;\r\n return {\r\n autoRecord: {\r\n ...DEFAULT_CONFIG.autoRecord,\r\n ...(raw.autoRecord ?? {}),\r\n vectorizeAutoDownload,\r\n commitFrameworkChanges,\r\n handoff,\r\n handoffRetentionDays,\r\n reindex,\r\n backfill,\r\n failures,\r\n archiveCommit,\r\n archiveAtEnd,\r\n archiveRawRetentionDays,\r\n },\r\n updates: { check },\r\n sync: { autoPush },\r\n environments,\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\n/**\r\n * Resolve the active environment label from the config rules (first match\r\n * wins), or null when none match. Pure — the caller injects the signals\r\n * (hostname, env vars, a path-existence probe) so this stays testable and\r\n * free of direct OS access.\r\n */\r\nexport function resolveEnvironment(\r\n config: VortexConfig,\r\n signals: {\r\n readonly hostname?: string;\r\n readonly env?: Record<string, string | undefined>;\r\n readonly pathExists?: (p: string) => boolean;\r\n },\r\n): string | null {\r\n const host = signals.hostname?.toLowerCase();\r\n const env = signals.env ?? {};\r\n const pathExists = signals.pathExists ?? (() => false);\r\n for (const rule of config.environments) {\r\n if (rule.pathExists && pathExists(rule.pathExists)) return rule.label;\r\n if (rule.hostname && host && rule.hostname.toLowerCase() === host) return rule.label;\r\n if (rule.envVar) {\r\n if (typeof rule.envVar === \"string\") {\r\n if (env[rule.envVar] !== undefined) return rule.label;\r\n } else {\r\n const v = env[rule.envVar.name];\r\n if (v !== undefined && (rule.envVar.equals === undefined || v === rule.envVar.equals)) {\r\n return rule.label;\r\n }\r\n }\r\n }\r\n }\r\n return null;\r\n}\r\n","export { Privacy } from \"./types.js\";\r\nexport type { FrontmatterDoc, ModuleContext } from \"./types.js\";\r\nexport { parseFrontmatter, serializeFrontmatter } from \"./frontmatter.js\";\r\nexport { isVisibleAt, maxPrivacy, normalizePrivacy } from \"./privacy.js\";\r\nexport { makeContext, moduleDir } from \"./paths.js\";\r\nexport { loadVortexConfig, resolveEnvironment, vortexConfigPath } from \"./config.js\";\r\nexport type { AutoRecordConfig, EnvironmentRule, UpdatesConfig, VortexConfig } from \"./config.js\";\r\nexport { atomicWriteFile, exclusiveCreateFile, validateDataRelativePath } from \"./safe-fs.js\";\r\nexport type { ValidateDataRelativePathOptions } from \"./safe-fs.js\";\r\n","/**\n * Three-tier privacy classification used across VortEX.\n *\n * - `public` — safe to share with anyone, including in published repositories.\n * - `internal` — visible to the operator and their organization; not for public release.\n * - `personal` — personal data; never shared, never published.\n */\nexport const Privacy = {\n Public: \"public\",\n Internal: \"internal\",\n Personal: \"personal\",\n} as const;\n\nexport type Privacy = (typeof Privacy)[keyof typeof Privacy];\n\n/**\n * Parsed markdown document with separated YAML frontmatter and body.\n */\nexport interface FrontmatterDoc<T = Record<string, unknown>> {\n frontmatter: T & { privacy?: Privacy };\n body: string;\n}\n\n/**\n * Resolved paths for a VortEX repository root.\n *\n * Every module receives a `ModuleContext` from the host (CLI, plugin runtime,\n * or test harness) and resolves its own paths against it. Modules must not\n * derive paths by string manipulation against an assumed layout.\n */\nexport interface ModuleContext {\n repoRoot: string;\n agentDir: string;\n dataDir: string;\n modulesDir: string;\n pluginsDir: string;\n}\n","import type { Privacy } from \"./types.js\";\n\nconst ORDER: Record<Privacy, number> = {\n public: 0,\n internal: 1,\n personal: 2,\n};\n\n/**\n * Returns true if a document with `docPrivacy` is visible to a viewer authorized\n * at `viewerPrivacy`. Viewers see content at or below their own level.\n *\n * - `public` viewer sees → public only\n * - `internal` viewer sees → public + internal\n * - `personal` viewer sees → all three\n */\nexport function isVisibleAt(docPrivacy: Privacy, viewerPrivacy: Privacy): boolean {\n return ORDER[docPrivacy] <= ORDER[viewerPrivacy];\n}\n\n/**\n * Returns the more-restrictive of two privacy levels.\n * Useful when combining content from multiple sources for sharing.\n */\nexport function maxPrivacy(a: Privacy, b: Privacy): Privacy {\n return ORDER[a] >= ORDER[b] ? a : b;\n}\n\n/**\n * Coerce an unknown value (e.g. user input or untyped frontmatter) into a\n * valid `Privacy`. Falls back to `internal` by default — the safest default\n * for unclassified content.\n */\nexport function normalizePrivacy(value: unknown, fallback: Privacy = \"internal\"): Privacy {\n if (value === \"public\" || value === \"internal\" || value === \"personal\") {\n return value;\n }\n return fallback;\n}\n","import { rename, writeFile } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\n\n/**\n * Path-safety primitives shared by every VortEX write path.\n *\n * Two concerns are deliberately separated:\n *\n * - {@link validateDataRelativePath} is the *gate* — it turns an\n * untrusted, possibly-LLM-chosen `data/`-relative path into a vetted\n * absolute path under `dataDir`, or throws. It must run before ANY write\n * so a traversal/absolute/drive-qualified/system-dir path can never reach\n * the filesystem.\n * - {@link exclusiveCreateFile} is the *write* — an exclusive create that\n * refuses to overwrite, so an accidental collision surfaces as an error\n * rather than silent data loss.\n *\n * The validator is the security boundary. The proactive-curator's placement\n * decisions come from an LLM; the CLI accept path takes a serialized payload\n * from an agent. Neither is trusted to stay inside `data/` on its own.\n */\n\n/**\n * Directories at the top of `data/` that VortEX maintains structurally and\n * that the curate value loop must never write into. Mirrors the\n * proactive-curator's own `SYSTEM_META_DIRS`, plus the leading-`_` rule\n * (any `_*` first segment is reserved) enforced separately below.\n */\nconst SYSTEM_META_DIRS = new Set([\n \"worklog\",\n \"decision-log\",\n \"runbooks\",\n \"hubs\",\n \"_memory\",\n \"_templates\",\n \"_proactive-curator\",\n // Framework metadata carve-out: `data/.vortex/` holds the update-lifecycle\n // ownership manifest and template backups. It is NOT user space — the curate\n // value loop (and any LLM-chosen write path) must never target it, even\n // though it does not start with `_`. (The leading-`_` rule below does not\n // cover a leading-dot dir, so it is listed explicitly.)\n \".vortex\",\n]);\n\n/** Match a drive-qualified prefix such as `C:` or `c:/...` (Windows). */\nconst DRIVE_QUALIFIED = /^[a-zA-Z]:/;\n\n/** Control characters (U+0000–U+001F) are never valid in a path we accept. */\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS = /[\\u0000-\\u001F]/;\n\n/**\n * Options for {@link validateDataRelativePath}. Shaped so a future\n * symlink-aware mode can be added WITHOUT changing any caller: today every\n * caller passes no options (or `{}`) and gets the lexical check. When the\n * realpath mode lands it becomes `{ resolveSymlinks: true }` and callers that\n * want it opt in; the default stays lexical-only.\n *\n * The symlink/realpath ancestor check is intentionally DEFERRED for v1 — the\n * lexical containment check (resolve + `path.relative`) already blocks\n * traversal, absolute, and drive-qualified paths. A symlink *inside* `data/`\n * pointing outside it is the remaining gap; closing it needs an async\n * `realpath` walk, which this synchronous API leaves room for.\n */\nexport interface ValidateDataRelativePathOptions {\n /**\n * Reserved for the deferred realpath/symlink-ancestor mode. Currently\n * ignored — accepted now so enabling it later is not a breaking change.\n */\n readonly resolveSymlinks?: boolean;\n}\n\n/**\n * Validate an untrusted `data/`-relative path and return the vetted absolute\n * path under `dataDir`, or throw a clear {@link Error}.\n *\n * Rejection rules (in order — the cheap lexical checks run before the\n * resolve so a hostile input never even reaches `path.resolve`):\n *\n * 1. Not a non-empty string.\n * 2. Contains a control character (U+0000–U+001F).\n * 3. After normalizing `\\` → `/`: empty, `.`, drive-qualified (`C:`),\n * absolute, or has a leading separator.\n * 4. Any segment is empty (collapsed `//`), `.`, or `..` (traversal).\n * 5. The final segment (the filename) is empty.\n * 6. The first segment is a system/meta dir (worklog, decision-log,\n * runbooks, hubs, _memory, _templates, _proactive-curator) or starts\n * with `_` (any reserved/meta dir).\n *\n * Then it resolves against `dataDir` and confirms containment using\n * `path.relative` (NOT a string prefix — `data` vs `data-evil` would fool a\n * prefix check). On Windows the relative check is done case-insensitively.\n *\n * @param dataDir Absolute path of the instance's `data/` directory.\n * @param rel Untrusted `data/`-relative path (LLM- or agent-supplied).\n * @returns The validated absolute path, guaranteed under `dataDir`.\n */\nexport function validateDataRelativePath(\n dataDir: string,\n rel: string,\n // Options are accepted for forward-compatibility (deferred symlink mode);\n // unused today. Underscore-prefixed so noUnusedParameters stays happy.\n _options: ValidateDataRelativePathOptions = {},\n): string {\n if (typeof rel !== \"string\" || rel.length === 0) {\n throw new Error(\"Invalid data-relative path: must be a non-empty string.\");\n }\n if (CONTROL_CHARS.test(rel)) {\n throw new Error(\"Invalid data-relative path: contains control characters.\");\n }\n\n const normalized = rel.replace(/\\\\/g, \"/\");\n\n if (normalized === \"\" || normalized === \".\") {\n throw new Error(`Invalid data-relative path: \"${rel}\" is empty or '.'.`);\n }\n if (DRIVE_QUALIFIED.test(normalized)) {\n throw new Error(`Invalid data-relative path: \"${rel}\" is drive-qualified (must be data-relative).`);\n }\n if (isAbsolute(normalized) || normalized.startsWith(\"/\")) {\n throw new Error(`Invalid data-relative path: \"${rel}\" is absolute (must be data-relative).`);\n }\n\n const segments = normalized.split(\"/\");\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\" || segment === \"..\") {\n throw new Error(\n `Invalid data-relative path: \"${rel}\" contains an empty, '.', or '..' segment (path traversal).`,\n );\n }\n }\n\n // The final segment is the filename; it must not itself be a separator-\n // bearing value (defensive — split already removed separators) and must\n // be non-empty (covered by the segment loop, asserted here for clarity).\n const filename = segments[segments.length - 1]!;\n if (filename.length === 0 || filename.includes(\"/\") || filename.includes(\"\\\\\")) {\n throw new Error(`Invalid data-relative path: \"${rel}\" has an invalid filename.`);\n }\n\n const first = segments[0]!;\n if (SYSTEM_META_DIRS.has(first) || first.startsWith(\"_\")) {\n throw new Error(\n `Invalid data-relative path: \"${rel}\" targets a reserved system/meta directory (\"${first}\"). ` +\n \"The curate value loop writes user documents only — not worklog/decision-log/runbooks/hubs or any _* directory.\",\n );\n }\n\n const absPath = resolve(dataDir, normalized);\n const rootResolved = resolve(dataDir);\n\n // Containment via path.relative — a true ancestor relationship, not a\n // string prefix (which would accept a sibling like `<dataDir>-evil`).\n let relToData = relative(rootResolved, absPath);\n let relCompare = relToData;\n let upPrefix = \"..\" + sep;\n if (sep === \"\\\\\") {\n // Windows: filesystem is case-insensitive — compare case-folded so a\n // drive-letter/segment casing difference does not read as \"escaped\".\n relCompare = relToData.toLowerCase();\n upPrefix = upPrefix.toLowerCase();\n }\n if (\n relCompare === \"..\" ||\n relCompare.startsWith(upPrefix) ||\n isAbsolute(relToData)\n ) {\n throw new Error(`Invalid data-relative path: \"${rel}\" resolves outside the data directory.`);\n }\n\n return absPath;\n}\n\n/**\n * Create a file exclusively (refusing to overwrite). Writes `body` with the\n * `wx` flag so an existing target throws `EEXIST`, which is re-thrown as a\n * clear \"refusing to overwrite\" error. Use for the create-file action where\n * clobbering an existing document would be silent data loss.\n *\n * The caller is responsible for ensuring the parent directory exists (the\n * create-file path in the curator does its own `mkdir` after validation).\n *\n * @param absPath Absolute target path (already validated by\n * {@link validateDataRelativePath}).\n * @param body File contents (UTF-8).\n */\nexport async function exclusiveCreateFile(absPath: string, body: string): Promise<void> {\n try {\n await writeFile(absPath, body, { encoding: \"utf8\", flag: \"wx\" });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new Error(\n `Refusing to overwrite existing file: ${absPath}. ` +\n \"create-file is an exclusive create; to add to an existing document use append-section instead.\",\n );\n }\n throw e;\n }\n}\n\n/**\n * Write `body` to `absPath` atomically: stage to a sibling temp file, then\n * `rename` over the target. A reader of `absPath` therefore sees either the old\n * bytes or the new bytes in full — never a partially-written file. The temp\n * file is a sibling (same directory ⇒ same filesystem) so the rename is atomic;\n * `counter` disambiguates concurrent writers in the same process.\n *\n * This is the write primitive for the update lifecycle's transaction model —\n * template replacements and the ownership manifest must never leave a half-\n * written file on a crash mid-update. The caller ensures the parent directory\n * exists.\n *\n * @param absPath Absolute target path.\n * @param body File contents (UTF-8).\n */\nlet atomicWriteCounter = 0;\nexport async function atomicWriteFile(absPath: string, body: string): Promise<void> {\n const tmp = `${absPath}.tmp-${process.pid}-${atomicWriteCounter++}`;\n try {\n await writeFile(tmp, body, { encoding: \"utf8\" });\n await rename(tmp, absPath);\n } catch (e) {\n // Best-effort cleanup of the temp file if the rename never happened.\n try {\n const { rm } = await import(\"node:fs/promises\");\n await rm(tmp, { force: true });\n } catch {\n // ignore — the temp file is named distinctly and harmless if it lingers\n }\n throw e;\n }\n}\n"],"mappings":"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","names":["join","resolve"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadVortexConfig,
3
3
  makeContext
4
- } from "./chunk-T53UWSTR.js";
4
+ } from "./chunk-IZIAMK3L.js";
5
5
 
6
6
  // ../plugins/session-rituals/dist/guard.js
7
7
  import { existsSync, readFileSync } from "fs";
@@ -125,7 +125,7 @@ async function runGuardCli(argv, out, err) {
125
125
  if (loadVortexConfig(ctx).autoRecord.failures) {
126
126
  const filePath = toolInput.file_path;
127
127
  const fileNote = typeof filePath === "string" && filePath ? ` (target: ${filePath})` : "";
128
- const { recordGuardDenial } = await import("./failures-PMURLMVB.js");
128
+ const { recordGuardDenial } = await import("./failures-DBXJKFYX.js");
129
129
  await recordGuardDenial(ctx.dataDir, `write guard denied a literal control character in ${finding.field}${fileNote}`);
130
130
  }
131
131
  }
@@ -163,4 +163,4 @@ export {
163
163
  runGuardCli,
164
164
  resolveInstanceRoot
165
165
  };
166
- //# sourceMappingURL=chunk-2FVNWW77.js.map
166
+ //# sourceMappingURL=chunk-JKQAB5OK.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  GUARD_WRITE_COMMAND,
3
3
  GUARD_WRITE_MATCHER
4
- } from "./chunk-2FVNWW77.js";
4
+ } from "./chunk-JKQAB5OK.js";
5
5
 
6
6
  // ../plugins/session-rituals/dist/statusline.js
7
7
  import { execFileSync } from "child_process";
@@ -147,7 +147,7 @@ function parseStatuslineInput(text) {
147
147
  modelName: rawName.replace(/^Claude\s+/, ""),
148
148
  effortLevel: str(effort.level),
149
149
  transcriptPath: str(root.transcript_path),
150
- dir: str(workspace.current_dir) ?? str(root.cwd),
150
+ dir: str(workspace.project_dir) ?? str(workspace.current_dir) ?? str(root.cwd),
151
151
  contextWindowSize: windowSize > 0 ? windowSize : 2e5,
152
152
  usedPercentage: clampPct(num(ctx.used_percentage)),
153
153
  cacheReadTokens: nonneg(usage.cache_read_input_tokens),
@@ -498,4 +498,4 @@ export {
498
498
  ensureStatusline,
499
499
  runStatuslineCli
500
500
  };
501
- //# sourceMappingURL=chunk-EAKDR5B2.js.map
501
+ //# sourceMappingURL=chunk-OFID33QA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../plugins/session-rituals/src/statusline.ts","../../plugins/session-rituals/src/ensure-hooks.ts"],"sourcesContent":["/**\n * `vortex statusline` — a Claude Code statusLine renderer.\n *\n * Claude Code pipes a JSON snapshot of the session (model, context window,\n * cost, rate limits, workspace) to the configured statusLine command on every\n * refresh and renders whatever the command prints. This module turns that\n * snapshot into a colored status bar:\n *\n * full (default, 3 lines + a VortEX line inside an instance):\n * 🧠 Fable 5 │ █ max │ █░░░░░░░░░ 12% · 120K/1M │ 🕐 22:25 │ 💰 $9.22\n * 5h ██████░░ 75%(2h13m) │ 7d ███████░ 96%(0d8h) │ 📦 cache 98%\n * 📁 my-project │ ⎇ main 2b0949d │ ⏱ 1h19m │ +78 -38 │ ⧉ 1\n * 🌀 VortEX v0.11.0 │ last: 2026-06-10_0452-….md\n *\n * lite (1 line):\n * 📁 my-project │ ⎇ main │ 🧠 Fable 5 │ █ max │ ░░░░░░░░ 12% · 120K/1M │ 5h 75% · 7d 96% │ ⧉ 1 │ 🌀 v0.11.0 │ 🕐 22:25\n *\n * Design notes:\n * - Rendering is PURE (`renderStatusline(data, probes, mode)`) — every\n * environment lookup (git, process list, clock, VortEX instance files) is\n * collected separately in `collectStatuslineProbes`, so the composition is\n * unit-testable without a repo or a terminal.\n * - Reasoning effort is shown as one bar whose height + color encode the\n * level. Claude Code reports `ultracode` as plain `xhigh` (ultracode is\n * \"xhigh + workflow orchestration\"), so when the level reads `xhigh` we\n * additionally sniff the session transcript for the last `/effort` change\n * notice — a best-effort heuristic that degrades to showing `xhigh`.\n * - Every probe is wrapped: a statusline must NEVER throw or block the bar —\n * on any failure a segment silently falls back to a neutral value.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { closeSync, existsSync, openSync, readSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseSettings, serializeSettings, type ClaudeSettings } from \"./ensure-hooks.js\";\n\n// --- ANSI palette (kept as raw escapes so the bar renders in any ANSI terminal) ---\nconst RST = \"\\x1b[0m\";\nconst CYAN = \"\\x1b[36m\";\nconst GREEN = \"\\x1b[32m\";\nconst YELLOW = \"\\x1b[33m\";\nconst RED = \"\\x1b[31m\";\nconst DIM = \"\\x1b[2m\";\nconst BOLD = \"\\x1b[1m\";\nconst WHITE = \"\\x1b[37m\";\nconst MAGENTA = \"\\x1b[35m\";\nconst BLUE = \"\\x1b[34m\";\nconst GREY = \"\\x1b[38;5;242m\";\nconst ORANGE = \"\\x1b[38;5;208m\";\n\nconst SEP = ` ${DIM}│${RST} `;\n\n/** The slice of Claude Code's statusLine input JSON this renderer consumes. */\nexport interface StatuslineData {\n readonly modelName: string;\n /** Reasoning effort level as reported (`effort.level`), or null. */\n readonly effortLevel: string | null;\n readonly transcriptPath: string | null;\n /**\n * Project ROOT directory. `workspace.project_dir` first — `current_dir` and\n * `cwd` FOLLOW the session shell as it `cd`s (measured), which made the bar\n * show a sibling repo and lose the VortEX line mid-session. The bar's\n * identity (project name, git, instance line) stays pinned to the project.\n */\n readonly dir: string | null;\n readonly contextWindowSize: number;\n /** Context used, percent — kept as reported (may be fractional). */\n readonly usedPercentage: number;\n readonly cacheReadTokens: number;\n readonly cacheCreationTokens: number;\n readonly costUsd: number;\n readonly durationMs: number;\n readonly linesAdded: number;\n readonly linesRemoved: number;\n readonly fiveHourUsedPct: number;\n readonly fiveHourResetsAt: number;\n readonly sevenDayUsedPct: number;\n readonly sevenDayResetsAt: number;\n}\n\n/** Environment lookups the renderer composes in — collected impurely, injected purely. */\nexport interface StatuslineProbes {\n readonly gitBranch: string;\n readonly gitHash: string;\n readonly sessionCount: number;\n /** Set when `dir` is a VortEX instance root; lastWorklog is the newest worklog file name. */\n readonly vortex: { readonly version: string | null; readonly lastWorklog: string | null } | null;\n /** Effort level after the ultracode transcript sniff (null → use data.effortLevel). */\n readonly effortLevel: string | null;\n readonly now: Date;\n}\n\nfunction num(v: unknown, fallback = 0): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n/** Non-negative numeric field (counters, costs, sizes — a negative is sender garbage). */\nfunction nonneg(v: unknown, fallback = 0): number {\n return Math.max(0, num(v, fallback));\n}\n/** Clamp a reported percentage into the displayable 0..100 range. */\nfunction clampPct(v: number): number {\n return Math.min(100, Math.max(0, v));\n}\n/**\n * Make a dynamic string safe to put in a single-line ANSI bar: drop control\n * chars (incl. ESC — no injected sequences/newlines), collapse whitespace, and\n * truncate long values. The bar must render intact whatever a branch name,\n * model name, or worklog filename contains.\n */\nexport function safeSegment(s: string, max = 60): string {\n // eslint-disable-next-line no-control-regex\n const cleaned = s.replace(/[\\u0000-\\u001f\\u007f]/g, \" \").replace(/\\s+/g, \" \").trim();\n return cleaned.length > max ? cleaned.slice(0, max - 1) + \"…\" : cleaned;\n}\nfunction str(v: unknown): string | null {\n return typeof v === \"string\" && v.length > 0 ? v : null;\n}\nfunction obj(v: unknown): Record<string, unknown> {\n return typeof v === \"object\" && v !== null ? (v as Record<string, unknown>) : {};\n}\n\n/** Parse Claude Code's statusLine stdin JSON into the fields the bar uses. Never throws on shape — only on non-JSON. */\nexport function parseStatuslineInput(text: string): StatuslineData {\n const root = obj(JSON.parse(text));\n const model = obj(root.model);\n const effort = obj(root.effort);\n const workspace = obj(root.workspace);\n const cost = obj(root.cost);\n const ctx = obj(root.context_window);\n const usage = obj(ctx.current_usage);\n const limits = obj(root.rate_limits);\n const five = obj(limits.five_hour);\n const seven = obj(limits.seven_day);\n\n const rawName = str(model.display_name) ?? \"Claude\";\n const windowSize = nonneg(ctx.context_window_size, 200_000);\n return {\n modelName: rawName.replace(/^Claude\\s+/, \"\"),\n effortLevel: str(effort.level),\n transcriptPath: str(root.transcript_path),\n dir: str(workspace.project_dir) ?? str(workspace.current_dir) ?? str(root.cwd),\n contextWindowSize: windowSize > 0 ? windowSize : 200_000,\n usedPercentage: clampPct(num(ctx.used_percentage)),\n cacheReadTokens: nonneg(usage.cache_read_input_tokens),\n cacheCreationTokens: nonneg(usage.cache_creation_input_tokens),\n costUsd: nonneg(cost.total_cost_usd),\n durationMs: nonneg(cost.total_duration_ms),\n linesAdded: nonneg(cost.total_lines_added),\n linesRemoved: nonneg(cost.total_lines_removed),\n fiveHourUsedPct: clampPct(num(five.used_percentage)),\n fiveHourResetsAt: nonneg(five.resets_at),\n sevenDayUsedPct: clampPct(num(seven.used_percentage)),\n sevenDayResetsAt: nonneg(seven.resets_at),\n };\n}\n\n/** One-character effort meter: height + color encode the level (`█✦` for ultracode). */\nexport function effortMeter(level: string | null): { meter: string; color: string } | null {\n switch (level) {\n case \"low\":\n return { meter: \"▂\", color: GREY };\n case \"medium\":\n return { meter: \"▄\", color: GREEN };\n case \"high\":\n return { meter: \"▆\", color: YELLOW };\n case \"xhigh\":\n return { meter: \"▇\", color: ORANGE };\n case \"max\":\n return { meter: \"█\", color: RED };\n case \"ultracode\":\n return { meter: \"█✦\", color: MAGENTA };\n default:\n return null;\n }\n}\n\n/** `7` → `7`, `70_000` → `70K`, `1_200_000` → `1.2M` (token quantities). */\nexport function formatTokens(t: number): string {\n if (t >= 1_000_000) return `${Math.floor(t / 1_000_000)}.${Math.floor((t % 1_000_000) / 100_000)}M`;\n if (t >= 1_000) return `${Math.floor(t / 1_000)}K`;\n return String(Math.floor(t));\n}\n\n/** Window sizes render without a decimal: `200_000` → `200K`, `1_000_000` → `1M`. */\nexport function formatWindow(t: number): string {\n if (t >= 1_000_000) return `${Math.floor(t / 1_000_000)}M`;\n if (t >= 1_000) return `${Math.floor(t / 1_000)}K`;\n return String(Math.floor(t));\n}\n\n/** `█`-filled gauge, `width` cells, floor-scaled so 100% and only 100% fills it. */\nexport function makeBar(pct: number, width: number): string {\n const filled = Math.min(width, Math.max(0, Math.floor((pct * width) / 100)));\n return \"█\".repeat(filled) + \"░\".repeat(width - filled);\n}\n\n/** Color for a USAGE percentage (high = bad): <50 green, <80 yellow, else red. */\nfunction usageColor(pct: number): string {\n if (pct >= 80) return RED;\n if (pct >= 50) return YELLOW;\n return GREEN;\n}\n\n/** Color for a REMAINING percentage (low = bad): <=30 red, <=60 yellow, else green. */\nfunction healthColor(remaining: number): string {\n if (remaining <= 30) return RED;\n if (remaining <= 60) return YELLOW;\n return GREEN;\n}\n\nfunction formatDuration(ms: number): string {\n const totalMin = Math.floor(ms / 60_000);\n if (totalMin >= 60) return `${Math.floor(totalMin / 60)}h${totalMin % 60}m`;\n return `${totalMin}m${Math.floor(ms / 1000) % 60}s`;\n}\n\n/** `(2h13m)` until an epoch-seconds reset, or \"\" when absent/past. */\nfunction untilReset(resetsAtSec: number, now: Date): string {\n if (resetsAtSec <= 0) return \"\";\n const diffSec = resetsAtSec - Math.floor(now.getTime() / 1000);\n if (diffSec <= 0) return \"\";\n if (diffSec >= 86_400) return `${Math.floor(diffSec / 86_400)}d${Math.floor((diffSec % 86_400) / 3600)}h`;\n return `${Math.floor(diffSec / 3600)}h${Math.floor((diffSec % 3600) / 60)}m`;\n}\n\nfunction pad2(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\n/** Compose the status bar. Pure — see `collectStatuslineProbes` for the impure half. */\nexport function renderStatusline(\n d: StatuslineData,\n p: StatuslineProbes,\n mode: \"full\" | \"lite\" = \"full\",\n): string {\n const usedPctDisplay = Math.round(d.usedPercentage);\n const ctxColor = usageColor(usedPctDisplay);\n const ctxUsedTokens = Math.round((d.usedPercentage * d.contextWindowSize) / 100);\n const ctxText = `${WHITE}${formatTokens(ctxUsedTokens)}/${formatWindow(d.contextWindowSize)}${RST}`;\n const clock = `🕐 ${WHITE}${pad2(p.now.getHours())}:${pad2(p.now.getMinutes())}${RST}`;\n\n const level = p.effortLevel ?? d.effortLevel;\n const effort = effortMeter(level);\n const effortSeg = effort\n ? `${effort.color}${effort.meter}${RST} ${GREY}${safeSegment(level ?? \"\")}${RST}`\n : null;\n const modelSeg = `🧠 ${BOLD}${CYAN}${safeSegment(d.modelName, 30)}${RST}`;\n\n const fiveRemain = Math.max(0, 100 - Math.round(d.fiveHourUsedPct));\n const sevenRemain = Math.max(0, 100 - Math.round(d.sevenDayUsedPct));\n const fiveColor = healthColor(fiveRemain);\n const sevenColor = healthColor(sevenRemain);\n const sessionSeg = `${WHITE}⧉ ${Math.max(1, Math.floor(p.sessionCount))}${RST}`;\n\n const project = safeSegment(\n p.vortex || d.dir ? (d.dir ?? \"\").replace(/[\\\\/]+$/, \"\").split(/[\\\\/]/).pop() || \"?\" : \"?\",\n 40,\n );\n const gitBranch = safeSegment(p.gitBranch, 40);\n const gitHash = safeSegment(p.gitHash, 16);\n\n if (mode === \"lite\") {\n const parts = [\n `📁 ${BLUE}${project}${RST}`,\n `${WHITE}⎇${RST} ${YELLOW}${gitBranch}${RST}`,\n effortSeg ? `${modelSeg}${SEP}${effortSeg}` : modelSeg,\n `${ctxColor}${makeBar(usedPctDisplay, 8)} ${usedPctDisplay}%${RST} ${DIM}·${RST} ${ctxText}`,\n `${GREY}5h${RST} ${fiveColor}${fiveRemain}%${RST} ${DIM}·${RST} ${GREY}7d${RST} ${sevenColor}${sevenRemain}%${RST}`,\n sessionSeg,\n ...(p.vortex?.version ? [`🌀 ${MAGENTA}v${safeSegment(p.vortex.version, 20)}${RST}`] : []),\n clock,\n ];\n return parts.join(SEP);\n }\n\n const l1 = [\n effortSeg ? `${modelSeg}${SEP}${effortSeg}` : modelSeg,\n `${ctxColor}${makeBar(usedPctDisplay, 10)} ${usedPctDisplay}%${RST} ${DIM}·${RST} ${ctxText}`,\n clock,\n `💰 ${BOLD}${YELLOW}$${d.costUsd.toFixed(2)}${RST}`,\n ].join(SEP);\n\n const cacheTotal = d.cacheReadTokens + d.cacheCreationTokens;\n const cachePct = cacheTotal > 0 ? Math.floor((d.cacheReadTokens * 100) / cacheTotal) : 0;\n const fiveReset = untilReset(d.fiveHourResetsAt, p.now);\n const sevenReset = untilReset(d.sevenDayResetsAt, p.now);\n const l2 = [\n `${GREY}5h${RST} ${fiveColor}${makeBar(fiveRemain, 8)} ${fiveRemain}%${RST}` +\n (fiveReset ? `${GREY}(${fiveReset})${RST}` : \"\"),\n `${GREY}7d${RST} ${sevenColor}${makeBar(sevenRemain, 8)} ${sevenRemain}%${RST}` +\n (sevenReset ? `${GREY}(${sevenReset})${RST}` : \"\"),\n `📦 ${GREEN}cache ${cachePct}%${RST}`,\n ].join(SEP);\n\n const l3 = [\n `📁 ${BLUE}${project}${RST}`,\n `${WHITE}⎇${RST} ${YELLOW}${gitBranch}${RST} ${GREY}${gitHash}${RST}`,\n `⏱ ${MAGENTA}${formatDuration(d.durationMs)}${RST}`,\n `${GREEN}+${d.linesAdded}${RST} ${RED}-${d.linesRemoved}${RST}`,\n sessionSeg,\n ].join(SEP);\n\n const lines = [l1, l2, l3];\n if (p.vortex) {\n let vx = `🌀 ${BOLD}${MAGENTA}VortEX${RST}`;\n if (p.vortex.version) vx += ` ${GREY}v${safeSegment(p.vortex.version, 20)}${RST}`;\n if (p.vortex.lastWorklog) vx += `${SEP}${GREY}last:${RST} ${safeSegment(p.vortex.lastWorklog, 70)}`;\n lines.push(vx);\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Impure probes — every lookup is individually guarded; a failure falls back.\n// ---------------------------------------------------------------------------\n\nfunction gitOut(dir: string, args: readonly string[]): string {\n return execFileSync(\"git\", [\"-C\", dir, ...args], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n}\n\n/**\n * Claude Code reports `ultracode` as `xhigh`; the transcript keeps the last\n * `/effort` change notice. Read the transcript tail and return the level named\n * by the LAST such notice, or null. Best-effort by design — a format change\n * simply degrades the display to `xhigh`.\n */\nexport function sniffEffortFromTranscript(transcriptPath: string, maxBytes = 4_000_000): string | null {\n try {\n const size = statSync(transcriptPath).size;\n const start = Math.max(0, size - maxBytes);\n const length = size - start;\n if (length <= 0) return null;\n const buf = Buffer.alloc(length);\n const fd = openSync(transcriptPath, \"r\");\n try {\n readSync(fd, buf, 0, length, start);\n } finally {\n closeSync(fd);\n }\n const text = buf.toString(\"utf8\");\n const matches = text.match(/Set effort level to [a-z]+/g);\n if (!matches || matches.length === 0) return null;\n return matches[matches.length - 1]!.slice(\"Set effort level to \".length);\n } catch {\n return null;\n }\n}\n\n/** Count open Claude Code sessions (one `claude` process per session). Falls back to 1. */\nfunction countClaudeSessions(): number {\n try {\n if (process.platform === \"win32\") {\n const out = execFileSync(\"tasklist\", [], { encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] });\n const n = (out.match(/claude\\.exe/gi) ?? []).length;\n return n > 0 ? n : 1;\n }\n const out = execFileSync(\"pgrep\", [\"-x\", \"claude\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n const n = out.split(/\\r?\\n/).filter(Boolean).length;\n return n > 0 ? n : 1;\n } catch {\n return 1;\n }\n}\n\n/** Newest worklog file name under `data/worklog` — by name, which sorts chronologically. */\nfunction newestWorklogName(repoRoot: string): string | null {\n try {\n const dir = join(repoRoot, \"data\", \"worklog\");\n const entries = readdirSync(dir, { recursive: true }) as string[];\n let best: string | null = null;\n for (const rel of entries) {\n const s = String(rel);\n if (!s.endsWith(\".md\") || s.endsWith(\"_INDEX.md\")) continue;\n if (best === null || s > best) best = s;\n }\n return best ? best.replace(/\\\\/g, \"/\").split(\"/\").pop()! : null;\n } catch {\n return null;\n }\n}\n\n/** Collect every environment lookup the renderer needs for this input. */\nexport function collectStatuslineProbes(d: StatuslineData, now = new Date()): StatuslineProbes {\n const dir = d.dir ?? process.cwd();\n\n let gitBranch = \"-\";\n let gitHash = \"-\";\n try {\n const branch = gitOut(dir, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n gitBranch = branch === \"HEAD\" ? \"detached\" : branch || \"-\";\n gitHash = gitOut(dir, [\"rev-parse\", \"--short\", \"HEAD\"]) || \"-\";\n } catch {\n // not a git repo / git absent — keep \"-\"\n }\n\n let vortex: StatuslineProbes[\"vortex\"] = null;\n try {\n if (existsSync(join(dir, \".agent\", \"vortex.json\"))) {\n let version: string | null = null;\n try {\n const pkg = JSON.parse(\n readFileSync(join(dir, \"node_modules\", \"@vortex-os\", \"base\", \"package.json\"), \"utf8\"),\n ) as { version?: string };\n version = typeof pkg.version === \"string\" ? pkg.version : null;\n } catch {\n // base not installed locally — show the VortEX line without a version\n }\n vortex = { version, lastWorklog: newestWorklogName(dir) };\n }\n } catch {\n vortex = null;\n }\n\n let effortLevel: string | null = null;\n if (d.effortLevel === \"xhigh\" && d.transcriptPath) {\n const sniffed = sniffEffortFromTranscript(d.transcriptPath);\n if (sniffed === \"ultracode\") effortLevel = \"ultracode\";\n }\n\n return {\n gitBranch,\n gitHash,\n sessionCount: countClaudeSessions(),\n vortex,\n effortLevel,\n now,\n };\n}\n\n// ---------------------------------------------------------------------------\n// `vortex statusline install [--lite]` — merge-safe settings wiring.\n// ---------------------------------------------------------------------------\n\n/**\n * The statusLine command written by `install`.\n *\n * The host runs the statusLine command in the session's CURRENT working\n * directory — which MOVES as the session `cd`s around (measured: the hook cwd\n * followed the shell into a sibling repo). Anything cwd-relative (bare `npx`\n * lookup, `./node_modules/...`) therefore silently resolves nothing the moment\n * the session leaves the project, and the bar just vanishes. So:\n * - with a local install (`directRoot`): invoke the bin by ABSOLUTE path —\n * immune to cwd, and ~0.5s faster per refresh than npx (the other observed\n * disappearing-bar mode: npx latency under load) — with the npx lookup\n * chained as a fallback for when the repo has moved;\n * - global-only installs: the npx form alone (cwd-sensitive, but there is no\n * local path to pin).\n * Always self-silencing (`|| exit 0`).\n */\nexport function statuslineCommand(lite: boolean, directRoot?: string): string {\n const sub = `statusline${lite ? \" lite\" : \"\"}`;\n const npxForm = `npx --no-install vortex ${sub}`;\n if (directRoot) {\n const bin = `${directRoot.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\")}/node_modules/@vortex-os/base/bin/vortex.mjs`;\n return `node \"${bin}\" ${sub} || ${npxForm} || exit 0`;\n }\n return `${npxForm} || exit 0`;\n}\n\n/** Relative path of the locally-installed bin, probed by `install` to pick the direct form. */\nexport const LOCAL_BIN_RELPATH = [\"node_modules\", \"@vortex-os\", \"base\", \"bin\", \"vortex.mjs\"] as const;\n\n/**\n * Is this `statusLine.command` one of OURS (any machine's absolute direct form,\n * with or without the npx fallback, full or lite — or the plain npx form)?\n * Used so `install` updates our own bar in place but never clobbers a foreign one.\n */\nexport function isOurStatuslineCommand(cmd: string | undefined): boolean {\n if (typeof cmd !== \"string\") return false;\n return /^(?:node \".*\\/node_modules\\/@vortex-os\\/base\\/bin\\/vortex\\.mjs\" statusline(?: lite)? \\|\\| )?npx --no-install vortex statusline(?: lite)? \\|\\| exit 0$/.test(\n cmd,\n );\n}\n\nexport interface StatuslineInstallResult {\n readonly status: \"installed\" | \"already-ours\" | \"kept-existing\";\n readonly settingsPath: string;\n readonly command?: string;\n /** Present when status is \"kept-existing\": the command we refused to overwrite. */\n readonly existing?: string;\n}\n\n/**\n * Merge `statusLine` into a settings object. NON-DESTRUCTIVE: an existing\n * statusLine that is not ours is kept (the user owns their bar) unless `force`.\n * Switching full↔lite of our own command counts as ours and is updated.\n */\nexport function ensureStatusline(\n existing: ClaudeSettings,\n lite: boolean,\n force = false,\n directRoot?: string,\n): { settings: ClaudeSettings; status: StatuslineInstallResult[\"status\"]; existing?: string } {\n const command = statuslineCommand(lite, directRoot);\n const current = existing.statusLine as { type?: string; command?: string } | undefined;\n const currentCmd = typeof current?.command === \"string\" ? current.command : undefined;\n // Ours = any of our shapes (full/lite × npx/any-machine-direct) — switching\n // between them (incl. the npx→direct upgrade) is an update, never a \"foreign bar\".\n const isOurs = isOurStatuslineCommand(currentCmd);\n // `already-ours` requires the whole entry to be well-formed — a matching\n // command under a wrong/missing `type` is rewritten (repaired) below.\n if (currentCmd === command && current?.type === \"command\") {\n return { settings: existing, status: \"already-ours\" };\n }\n if (current && !isOurs && !force) {\n return { settings: existing, status: \"kept-existing\", existing: currentCmd ?? JSON.stringify(current) };\n }\n return {\n settings: { ...existing, statusLine: { type: \"command\", command } },\n status: \"installed\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// CLI entry — `vortex statusline [lite] [install [--lite] [--force]]`\n// ---------------------------------------------------------------------------\n\n/**\n * Run the statusline CLI. `install` wires `.claude/settings.json` (at\n * `repoRoot`); anything else renders the bar from stdin JSON. A render must\n * never break the bar: bad/absent input prints nothing and exits 0.\n */\nexport async function runStatuslineCli(\n argv: readonly string[],\n repoRoot: string,\n out: (s: string) => void,\n err: (s: string) => void,\n): Promise<number> {\n if (argv[0] === \"install\") {\n const lite = argv.includes(\"--lite\");\n const force = argv.includes(\"--force\");\n // Prefer the absolute direct-node form when the package is installed\n // locally — immune to the host's cwd-following statusline execution and\n // ~0.5s faster per refresh than npx. Global-only installs keep npx.\n const directRoot = existsSync(join(repoRoot, ...LOCAL_BIN_RELPATH)) ? repoRoot : undefined;\n const settingsPath = join(repoRoot, \".claude\", \"settings.json\");\n const existingText = existsSync(settingsPath) ? readFileSync(settingsPath, \"utf8\") : null;\n const parsed = parseSettings(existingText);\n const result = ensureStatusline(parsed, lite, force, directRoot);\n if (result.status === \"installed\") {\n const { mkdirSync, writeFileSync } = await import(\"node:fs\");\n mkdirSync(join(repoRoot, \".claude\"), { recursive: true });\n writeFileSync(settingsPath, serializeSettings(result.settings), \"utf8\");\n }\n const payload: StatuslineInstallResult = {\n status: result.status,\n settingsPath,\n ...(result.status !== \"kept-existing\" ? { command: statuslineCommand(lite, directRoot) } : {}),\n ...(result.existing !== undefined ? { existing: result.existing } : {}),\n };\n out(JSON.stringify(payload, null, 2) + \"\\n\");\n if (result.status === \"kept-existing\") {\n err(\n \"[vortex] statusLine already configured with a different command — kept it. Re-run with --force to replace.\\n\",\n );\n }\n return 0;\n }\n\n const mode: \"full\" | \"lite\" = argv[0] === \"lite\" ? \"lite\" : \"full\";\n if (process.stdin.isTTY) {\n // Typed by hand with no piped JSON — don't block waiting on stdin.\n err(\n \"[vortex] statusline renders Claude Code's statusLine stdin JSON — pipe it in, \" +\n \"or wire it up with `vortex statusline install [--lite]`.\\n\",\n );\n return 0;\n }\n let raw = \"\";\n try {\n raw = readFileSync(0, \"utf8\");\n } catch {\n raw = \"\";\n }\n if (!raw.trim()) return 0; // no input — print nothing, never an error in the bar\n let data: StatuslineData;\n try {\n data = parseStatuslineInput(raw);\n } catch {\n return 0; // malformed input — keep the bar blank rather than erroring\n }\n out(renderStatusline(data, collectStatuslineProbes(data), mode));\n return 0;\n}\n","/**\r\n * Hook wiring for `/vortex init`: make sure the instance's\r\n * `.claude/settings.json` registers the VortEX SessionStart / SessionEnd hooks,\r\n * so the boot report + worklog-net fire automatically without the user knowing\r\n * any command. NON-DESTRUCTIVE — like the MCP install merge, this preserves\r\n * every other hook and top-level field and only adds our two entries if absent.\r\n *\r\n * Pure functions here (parse / merge / detect); the command does the actual\r\n * file read/write around them. Keeping them pure makes the merge unit-testable\r\n * and the \"writes only what's missing\" guarantee verifiable.\r\n */\r\n\r\n// Hook commands invoke the published CLI via `npx`, NOT checkout-relative\r\n// `node plugins/...` paths. An npm-installed instance (`npm i @vortex-os/base`)\r\n// has no monorepo checkout — but it does have the `vortex` bin on disk (the\r\n// instance's local `node_modules/.bin`, or the global npm bin on PATH), so\r\n// `npx --no-install vortex session-{start,end}` runs it. Two deliberate choices:\r\n// • `--no-install` — a SessionStart/End hook fires automatically; bare\r\n// `npx vortex` would silently install an arbitrary `vortex` package from the\r\n// network on a cache miss. `--no-install` fails closed instead.\r\n// • Resolve by BIN NAME (no `-p @vortex-os/base`) — npx searches the current\r\n// folder's `node_modules/.bin` FIRST, then PATH, which includes a global\r\n// `npm i -g @vortex-os/base`. This is what makes `vortex global-setup` fire in\r\n// EVERY folder: a `-p <pkg>` form (this command's earlier shape) resolves only\r\n// a LOCAL install and ignores the global one — so the GLOBAL hook silently\r\n// no-opped everywhere except the instance folder, defeating global-setup's one\r\n// job. Resolving the bin instead keeps \"local install wins\" (so the instance's\r\n// pinned version still runs there) while letting the global bin cover all other\r\n// folders. The cost: a same-named `vortex` bin earlier on PATH/local could\r\n// shadow ours — but `--no-install` still blocks the network-install case and a\r\n// colliding bin is unlikely, an acceptable trade for the any-folder guarantee.\r\n// These map to the `session-start` / `session-end` subcommands of the CLI.\r\n//\r\n// The trailing `|| exit 0` makes the hook SELF-SILENCING: the global hook fires in\r\n// every folder, but `npx --no-install` still fails where NO `vortex` is resolvable\r\n// (no local install AND nothing on PATH) — and Claude Code surfaces a non-zero\r\n// SessionStart hook as a \"hook error\" to the user. `|| exit 0` swallows that\r\n// (exit 0 → no error notice; stderr not injected) so such folders stay quiet.\r\n// `||` + `exit 0` are valid in both cmd.exe (the default Windows child-process\r\n// shell) and POSIX sh. Keeping the per-instance and global commands BYTE-IDENTICAL\r\n// is what lets Claude Code dedup them (no double session-start in the instance).\r\nexport const SESSION_START_COMMAND =\r\n \"npx --no-install vortex session-start || exit 0\";\r\nexport const SESSION_END_COMMAND =\r\n \"npx --no-install vortex session-end || exit 0\";\r\n\r\n// PreToolUse guard wired by `vortex init` (NOT global-setup — see\r\n// `ensureVortexHooks`): denies literal control bytes in to-be-written text.\r\n// Same conventions as the session hooks: bare bin name, `--no-install`\r\n// fail-closed, `|| exit 0` self-silencing (a deny is JSON on stdout, exit 0,\r\n// so the wrapper never masks it). Kept in sync with guard.ts.\r\nexport { GUARD_WRITE_COMMAND, GUARD_WRITE_MATCHER } from \"./guard.js\";\r\nimport { GUARD_WRITE_COMMAND, GUARD_WRITE_MATCHER } from \"./guard.js\";\r\n\r\n// Older command shapes a prior `init`/`global-setup` may have written. On the next\r\n// `ensureVortexHooks` run each migrates IN PLACE to the current command, so the\r\n// per-instance hook keeps matching the global one (and stays dedup-able). Covers\r\n// the `-p @vortex-os/base` form (self-silencing and its pre-`|| exit 0` variant)\r\n// that this command carried before resolution moved to the bare bin name.\r\nconst LEGACY_COMMANDS: Record<\"SessionStart\" | \"SessionEnd\", readonly string[]> = {\r\n SessionStart: [\r\n \"npx --no-install -p @vortex-os/base vortex session-start || exit 0\",\r\n \"npx --no-install -p @vortex-os/base vortex session-start\",\r\n ],\r\n SessionEnd: [\r\n \"npx --no-install -p @vortex-os/base vortex session-end || exit 0\",\r\n \"npx --no-install -p @vortex-os/base vortex session-end\",\r\n ],\r\n};\r\n\r\ninterface HookCommand {\r\n readonly type: \"command\";\r\n readonly command: string;\r\n}\r\ninterface HookGroup {\r\n readonly hooks: readonly HookCommand[];\r\n readonly matcher?: string;\r\n}\r\nexport interface ClaudeSettings {\r\n hooks?: {\r\n SessionStart?: HookGroup[];\r\n SessionEnd?: HookGroup[];\r\n [event: string]: HookGroup[] | undefined;\r\n };\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface EnsureHooksResult {\r\n readonly settings: ClaudeSettings;\r\n /**\r\n * Hook events that CHANGED — a VortEX entry was added, or a legacy one was\r\n * migrated/de-duplicated in place (empty when nothing changed). Callers should\r\n * key \"did we need to write?\" off `alreadyWired`, not the name \"added\".\r\n */\r\n readonly added: readonly string[];\r\n /** True when nothing changed — every VortEX hook was already present. */\r\n readonly alreadyWired: boolean;\r\n}\r\n\r\n/**\r\n * Parse existing settings.json text. Empty/whitespace → `{}` (fresh). Throws on\r\n * malformed JSON so the caller aborts rather than clobbering a hand-edited file.\r\n */\r\nexport function parseSettings(text: string | null | undefined): ClaudeSettings {\r\n const trimmed = (text ?? \"\").trim();\r\n if (trimmed.length === 0) return {};\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(trimmed);\r\n } catch (e) {\r\n throw new Error(\r\n `.claude/settings.json is not valid JSON — refusing to overwrite. Fix or remove it first. (${(e as Error).message})`,\r\n );\r\n }\r\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\r\n throw new Error(\".claude/settings.json is not a JSON object — refusing to overwrite.\");\r\n }\r\n return parsed as ClaudeSettings;\r\n}\r\n\r\n/**\r\n * Merge the VortEX hooks into an existing settings object WITHOUT mutating the\r\n * input. A hook event is left untouched if it already references our command\r\n * (idempotent); otherwise our group is appended alongside any existing groups.\r\n *\r\n * `opts.guard` additionally wires the PreToolUse control-byte guard\r\n * (`vortex guard write`) for the file-writing tools. It is requested by\r\n * `vortex init` (instance settings) but NOT by `vortex global-setup`: a\r\n * machine-global deny hook would fire in every project on the machine, which\r\n * is a stronger default than \"the instance opted into VortEX conventions\".\r\n */\r\nexport function ensureVortexHooks(\r\n existing: ClaudeSettings | null | undefined,\r\n opts?: { readonly guard?: boolean },\r\n): EnsureHooksResult {\r\n const base: ClaudeSettings = existing && typeof existing === \"object\" ? existing : {};\r\n const hooks = { ...(base.hooks ?? {}) };\r\n const added: string[] = [];\r\n\r\n const wire = (event: \"SessionStart\" | \"SessionEnd\", command: string) => {\r\n const legacy = LEGACY_COMMANDS[event];\r\n const src = hooks[event] ?? [];\r\n let changed = false;\r\n let kept = false; // already retained one entry with the current command?\r\n const groups: HookGroup[] = [];\r\n for (const g of src) {\r\n const hookList: HookCommand[] = [];\r\n for (const h of g.hooks ?? []) {\r\n // Migrate a LEGACY VortEX command to the current one (in place, so the\r\n // group's other hooks and its `matcher` are preserved).\r\n const migrated = legacy.includes(h.command);\r\n const cmd = migrated ? command : h.command;\r\n if (cmd === command) {\r\n if (kept) {\r\n changed = true; // drop a duplicate VortEX hook (e.g. legacy + current)\r\n continue;\r\n }\r\n kept = true;\r\n if (migrated) changed = true;\r\n hookList.push(migrated ? { ...h, command } : h);\r\n } else {\r\n hookList.push(h);\r\n }\r\n }\r\n if (hookList.length > 0) groups.push({ ...g, hooks: hookList });\r\n else changed = true; // group held only legacy/duplicate VortEX hooks → drop it\r\n }\r\n if (!kept) {\r\n groups.push({ hooks: [{ type: \"command\", command }] });\r\n changed = true;\r\n }\r\n hooks[event] = groups;\r\n if (changed) added.push(event);\r\n };\r\n\r\n wire(\"SessionStart\", SESSION_START_COMMAND);\r\n wire(\"SessionEnd\", SESSION_END_COMMAND);\r\n\r\n // PreToolUse control-byte guard (matcher-scoped to the file-writing tools).\r\n // Idempotency is by COMMAND: if any PreToolUse group already carries our\r\n // command — even under a user-customized matcher — it counts as wired and is\r\n // left exactly as the user shaped it.\r\n if (opts?.guard) {\r\n const src = hooks.PreToolUse ?? [];\r\n const wired = src.some((g) => (g.hooks ?? []).some((h) => h.command === GUARD_WRITE_COMMAND));\r\n if (!wired) {\r\n hooks.PreToolUse = [\r\n ...src,\r\n { matcher: GUARD_WRITE_MATCHER, hooks: [{ type: \"command\", command: GUARD_WRITE_COMMAND }] },\r\n ];\r\n added.push(\"PreToolUse\");\r\n }\r\n }\r\n\r\n const settings: ClaudeSettings = { ...base, hooks };\r\n return { settings, added, alreadyWired: added.length === 0 };\r\n}\r\n\r\n/** Serialize settings the way Claude writes them (2-space, trailing newline). */\r\nexport function serializeSettings(settings: ClaudeSettings): string {\r\n return JSON.stringify(settings, null, 2) + \"\\n\";\r\n}\r\n"],"mappings":"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","names":["out","n"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-T53UWSTR.js";
3
+ } from "./chunk-IZIAMK3L.js";
4
4
 
5
5
  // ../plugins/session-rituals/dist/failures.js
6
6
  import { mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
@@ -289,4 +289,4 @@ export {
289
289
  recordGuardDenial,
290
290
  runFailureCli
291
291
  };
292
- //# sourceMappingURL=chunk-UV76ZEDC.js.map
292
+ //# sourceMappingURL=chunk-T4TJZUPP.js.map
@@ -8,8 +8,8 @@ import {
8
8
  recordGuardDenial,
9
9
  runFailureCli,
10
10
  scanFailures
11
- } from "./chunk-UV76ZEDC.js";
12
- import "./chunk-T53UWSTR.js";
11
+ } from "./chunk-T4TJZUPP.js";
12
+ import "./chunk-IZIAMK3L.js";
13
13
  import "./chunk-PZ5AY32C.js";
14
14
  export {
15
15
  FAILURES_DIR,
@@ -22,4 +22,4 @@ export {
22
22
  runFailureCli,
23
23
  scanFailures
24
24
  };
25
- //# sourceMappingURL=failures-PMURLMVB.js.map
25
+ //# sourceMappingURL=failures-DBXJKFYX.js.map
@@ -7,8 +7,8 @@ import {
7
7
  resolveInstanceRoot,
8
8
  runGuardCli,
9
9
  scanToolInput
10
- } from "./chunk-2FVNWW77.js";
11
- import "./chunk-T53UWSTR.js";
10
+ } from "./chunk-JKQAB5OK.js";
11
+ import "./chunk-IZIAMK3L.js";
12
12
  import "./chunk-PZ5AY32C.js";
13
13
  export {
14
14
  GUARD_WRITE_COMMAND,
@@ -20,4 +20,4 @@ export {
20
20
  runGuardCli,
21
21
  scanToolInput
22
22
  };
23
- //# sourceMappingURL=guard-IMJR6ET7.js.map
23
+ //# sourceMappingURL=guard-S6NGMDXR.js.map