@vortex-os/base 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,14 @@
1
+ import {
2
+ FAILURES_DIR,
3
+ GUARD_DENIAL_KEY,
4
+ failureReportSlice,
5
+ isValidFailureKey,
6
+ ladderStage,
7
+ recordFailure,
8
+ recordGuardDenial,
9
+ runFailureCli,
10
+ scanFailures
11
+ } from "./chunk-UV76ZEDC.js";
1
12
  import {
2
13
  SESSION_END_COMMAND,
3
14
  SESSION_START_COMMAND,
@@ -16,7 +27,30 @@ import {
16
27
  serializeSettings,
17
28
  sniffEffortFromTranscript,
18
29
  statuslineCommand
19
- } from "./chunk-DWANI3LV.js";
30
+ } from "./chunk-P7IMUUNY.js";
31
+ import {
32
+ GUARD_WRITE_COMMAND,
33
+ GUARD_WRITE_MATCHER,
34
+ buildDenyDecision,
35
+ findControlChar,
36
+ guardWriteDecision,
37
+ resolveInstanceRoot,
38
+ runGuardCli,
39
+ scanToolInput
40
+ } from "./chunk-2FVNWW77.js";
41
+ import {
42
+ atomicWriteFile,
43
+ dist_exports,
44
+ exclusiveCreateFile,
45
+ isVisibleAt,
46
+ loadVortexConfig,
47
+ makeContext,
48
+ normalizePrivacy,
49
+ parseFrontmatter,
50
+ resolveEnvironment,
51
+ serializeFrontmatter,
52
+ validateDataRelativePath
53
+ } from "./chunk-T53UWSTR.js";
20
54
  import {
21
55
  catchUpSessions
22
56
  } from "./chunk-3L5DLEGP.js";
@@ -24,287 +58,6 @@ import {
24
58
  __export
25
59
  } from "./chunk-PZ5AY32C.js";
26
60
 
27
- // ../core/dist/index.js
28
- var dist_exports = {};
29
- __export(dist_exports, {
30
- Privacy: () => Privacy,
31
- atomicWriteFile: () => atomicWriteFile,
32
- exclusiveCreateFile: () => exclusiveCreateFile,
33
- isVisibleAt: () => isVisibleAt,
34
- loadVortexConfig: () => loadVortexConfig,
35
- makeContext: () => makeContext,
36
- maxPrivacy: () => maxPrivacy,
37
- moduleDir: () => moduleDir,
38
- normalizePrivacy: () => normalizePrivacy,
39
- parseFrontmatter: () => parseFrontmatter,
40
- resolveEnvironment: () => resolveEnvironment,
41
- serializeFrontmatter: () => serializeFrontmatter,
42
- validateDataRelativePath: () => validateDataRelativePath,
43
- vortexConfigPath: () => vortexConfigPath
44
- });
45
-
46
- // ../core/dist/types.js
47
- var Privacy = {
48
- Public: "public",
49
- Internal: "internal",
50
- Personal: "personal"
51
- };
52
-
53
- // ../core/dist/frontmatter.js
54
- import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
55
- var FENCE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
56
- var BOM = "\uFEFF";
57
- function parseFrontmatter(source) {
58
- const cleaned = source.startsWith(BOM) ? source.slice(1) : source;
59
- const match = cleaned.match(FENCE);
60
- if (!match) {
61
- return {
62
- frontmatter: {},
63
- body: cleaned
64
- };
65
- }
66
- const yaml = match[1] ?? "";
67
- const body = cleaned.slice(match[0].length);
68
- let parsed;
69
- try {
70
- const value = parseYaml(yaml);
71
- parsed = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
72
- } catch {
73
- parsed = {};
74
- }
75
- return { frontmatter: parsed, body };
76
- }
77
- function serializeFrontmatter(doc) {
78
- const keys = Object.keys(doc.frontmatter ?? {});
79
- if (keys.length === 0)
80
- return doc.body;
81
- const yaml = stringifyYaml(doc.frontmatter).trimEnd();
82
- return `---
83
- ${yaml}
84
- ---
85
- ${doc.body}`;
86
- }
87
-
88
- // ../core/dist/privacy.js
89
- var ORDER = {
90
- public: 0,
91
- internal: 1,
92
- personal: 2
93
- };
94
- function isVisibleAt(docPrivacy, viewerPrivacy) {
95
- return ORDER[docPrivacy] <= ORDER[viewerPrivacy];
96
- }
97
- function maxPrivacy(a, b2) {
98
- return ORDER[a] >= ORDER[b2] ? a : b2;
99
- }
100
- function normalizePrivacy(value, fallback = "internal") {
101
- if (value === "public" || value === "internal" || value === "personal") {
102
- return value;
103
- }
104
- return fallback;
105
- }
106
-
107
- // ../core/dist/paths.js
108
- import { resolve, join } from "path";
109
- function makeContext(repoRoot) {
110
- const root = resolve(repoRoot);
111
- return {
112
- repoRoot: root,
113
- agentDir: join(root, ".agent"),
114
- dataDir: join(root, "data"),
115
- modulesDir: join(root, "modules"),
116
- pluginsDir: join(root, "plugins")
117
- };
118
- }
119
- function moduleDir(ctx, moduleName) {
120
- return join(ctx.modulesDir, moduleName);
121
- }
122
-
123
- // ../core/dist/config.js
124
- import { existsSync, readFileSync } from "fs";
125
- import { join as join2 } from "path";
126
- var DEFAULT_CONFIG = {
127
- 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 },
128
- updates: { check: "session" },
129
- environments: []
130
- };
131
- function vortexConfigPath(ctx) {
132
- return join2(ctx.agentDir, "vortex.json");
133
- }
134
- function parseEnvironmentRule(raw) {
135
- if (typeof raw !== "object" || raw === null || Array.isArray(raw))
136
- return null;
137
- const candidate = raw;
138
- if (typeof candidate.label !== "string")
139
- return null;
140
- const rule = {
141
- label: candidate.label
142
- };
143
- if (typeof candidate.pathExists === "string")
144
- rule.pathExists = candidate.pathExists;
145
- if (typeof candidate.hostname === "string")
146
- rule.hostname = candidate.hostname;
147
- if (typeof candidate.envVar === "string") {
148
- rule.envVar = candidate.envVar;
149
- } else if (typeof candidate.envVar === "object" && candidate.envVar !== null) {
150
- const ev = candidate.envVar;
151
- if (typeof ev.name === "string") {
152
- rule.envVar = typeof ev.equals === "string" ? { name: ev.name, equals: ev.equals } : { name: ev.name };
153
- }
154
- }
155
- return rule;
156
- }
157
- function loadVortexConfig(ctx) {
158
- const path = vortexConfigPath(ctx);
159
- if (!existsSync(path))
160
- return DEFAULT_CONFIG;
161
- try {
162
- const raw = JSON.parse(readFileSync(path, "utf8"));
163
- const environments = Array.isArray(raw.environments) ? raw.environments.map(parseEnvironmentRule).filter((rule) => rule !== null) : [];
164
- const rawUpdates = raw.updates && typeof raw.updates === "object" && !Array.isArray(raw.updates) ? raw.updates : {};
165
- const rawCheck = rawUpdates.check;
166
- const check = rawCheck === void 0 ? "session" : typeof rawCheck === "string" && rawCheck.trim().toLowerCase() === "session" ? "session" : "off";
167
- const rawAuto = raw.autoRecord && typeof raw.autoRecord === "object" && !Array.isArray(raw.autoRecord) ? raw.autoRecord : {};
168
- const vectorizeAutoDownload = rawAuto.vectorizeAutoDownload === void 0 ? true : rawAuto.vectorizeAutoDownload === true;
169
- const commitFrameworkChanges = rawAuto.commitFrameworkChanges === void 0 ? true : rawAuto.commitFrameworkChanges === true;
170
- const handoff = rawAuto.handoff === void 0 ? true : rawAuto.handoff === true;
171
- const reindex = rawAuto.reindex === void 0 ? true : rawAuto.reindex === true;
172
- const backfill = rawAuto.backfill === void 0 ? true : rawAuto.backfill === true;
173
- const rawDays = rawAuto.handoffRetentionDays;
174
- const handoffRetentionDays = typeof rawDays === "number" && Number.isFinite(rawDays) && rawDays > 0 ? Math.floor(rawDays) : DEFAULT_CONFIG.autoRecord.handoffRetentionDays;
175
- return {
176
- autoRecord: {
177
- ...DEFAULT_CONFIG.autoRecord,
178
- ...raw.autoRecord ?? {},
179
- vectorizeAutoDownload,
180
- commitFrameworkChanges,
181
- handoff,
182
- handoffRetentionDays,
183
- reindex,
184
- backfill
185
- },
186
- updates: { check },
187
- environments
188
- };
189
- } catch {
190
- return DEFAULT_CONFIG;
191
- }
192
- }
193
- function resolveEnvironment(config, signals) {
194
- const host = signals.hostname?.toLowerCase();
195
- const env = signals.env ?? {};
196
- const pathExists = signals.pathExists ?? (() => false);
197
- for (const rule of config.environments) {
198
- if (rule.pathExists && pathExists(rule.pathExists))
199
- return rule.label;
200
- if (rule.hostname && host && rule.hostname.toLowerCase() === host)
201
- return rule.label;
202
- if (rule.envVar) {
203
- if (typeof rule.envVar === "string") {
204
- if (env[rule.envVar] !== void 0)
205
- return rule.label;
206
- } else {
207
- const v2 = env[rule.envVar.name];
208
- if (v2 !== void 0 && (rule.envVar.equals === void 0 || v2 === rule.envVar.equals)) {
209
- return rule.label;
210
- }
211
- }
212
- }
213
- }
214
- return null;
215
- }
216
-
217
- // ../core/dist/safe-fs.js
218
- import { rename, writeFile } from "fs/promises";
219
- import { isAbsolute, relative, resolve as resolve2, sep } from "path";
220
- var SYSTEM_META_DIRS = /* @__PURE__ */ new Set([
221
- "worklog",
222
- "decision-log",
223
- "runbooks",
224
- "hubs",
225
- "_memory",
226
- "_templates",
227
- "_proactive-curator",
228
- // Framework metadata carve-out: `data/.vortex/` holds the update-lifecycle
229
- // ownership manifest and template backups. It is NOT user space — the curate
230
- // value loop (and any LLM-chosen write path) must never target it, even
231
- // though it does not start with `_`. (The leading-`_` rule below does not
232
- // cover a leading-dot dir, so it is listed explicitly.)
233
- ".vortex"
234
- ]);
235
- var DRIVE_QUALIFIED = /^[a-zA-Z]:/;
236
- var CONTROL_CHARS = /[\u0000-\u001F]/;
237
- function validateDataRelativePath(dataDir, rel, _options = {}) {
238
- if (typeof rel !== "string" || rel.length === 0) {
239
- throw new Error("Invalid data-relative path: must be a non-empty string.");
240
- }
241
- if (CONTROL_CHARS.test(rel)) {
242
- throw new Error("Invalid data-relative path: contains control characters.");
243
- }
244
- const normalized = rel.replace(/\\/g, "/");
245
- if (normalized === "" || normalized === ".") {
246
- throw new Error(`Invalid data-relative path: "${rel}" is empty or '.'.`);
247
- }
248
- if (DRIVE_QUALIFIED.test(normalized)) {
249
- throw new Error(`Invalid data-relative path: "${rel}" is drive-qualified (must be data-relative).`);
250
- }
251
- if (isAbsolute(normalized) || normalized.startsWith("/")) {
252
- throw new Error(`Invalid data-relative path: "${rel}" is absolute (must be data-relative).`);
253
- }
254
- const segments = normalized.split("/");
255
- for (const segment of segments) {
256
- if (segment === "" || segment === "." || segment === "..") {
257
- throw new Error(`Invalid data-relative path: "${rel}" contains an empty, '.', or '..' segment (path traversal).`);
258
- }
259
- }
260
- const filename = segments[segments.length - 1];
261
- if (filename.length === 0 || filename.includes("/") || filename.includes("\\")) {
262
- throw new Error(`Invalid data-relative path: "${rel}" has an invalid filename.`);
263
- }
264
- const first = segments[0];
265
- if (SYSTEM_META_DIRS.has(first) || first.startsWith("_")) {
266
- throw new Error(`Invalid data-relative path: "${rel}" targets a reserved system/meta directory ("${first}"). The curate value loop writes user documents only \u2014 not worklog/decision-log/runbooks/hubs or any _* directory.`);
267
- }
268
- const absPath = resolve2(dataDir, normalized);
269
- const rootResolved = resolve2(dataDir);
270
- let relToData = relative(rootResolved, absPath);
271
- let relCompare = relToData;
272
- let upPrefix = ".." + sep;
273
- if (sep === "\\") {
274
- relCompare = relToData.toLowerCase();
275
- upPrefix = upPrefix.toLowerCase();
276
- }
277
- if (relCompare === ".." || relCompare.startsWith(upPrefix) || isAbsolute(relToData)) {
278
- throw new Error(`Invalid data-relative path: "${rel}" resolves outside the data directory.`);
279
- }
280
- return absPath;
281
- }
282
- async function exclusiveCreateFile(absPath, body) {
283
- try {
284
- await writeFile(absPath, body, { encoding: "utf8", flag: "wx" });
285
- } catch (e) {
286
- if (e.code === "EEXIST") {
287
- throw new Error(`Refusing to overwrite existing file: ${absPath}. create-file is an exclusive create; to add to an existing document use append-section instead.`);
288
- }
289
- throw e;
290
- }
291
- }
292
- var atomicWriteCounter = 0;
293
- async function atomicWriteFile(absPath, body) {
294
- const tmp = `${absPath}.tmp-${process.pid}-${atomicWriteCounter++}`;
295
- try {
296
- await writeFile(tmp, body, { encoding: "utf8" });
297
- await rename(tmp, absPath);
298
- } catch (e) {
299
- try {
300
- const { rm } = await import("fs/promises");
301
- await rm(tmp, { force: true });
302
- } catch {
303
- }
304
- throw e;
305
- }
306
- }
307
-
308
61
  // ../modules/slash-commands/dist/index.js
309
62
  var dist_exports2 = {};
310
63
  __export(dist_exports2, {
@@ -418,8 +171,8 @@ var MemoryType = {
418
171
  };
419
172
 
420
173
  // ../modules/memory-system/dist/store.js
421
- import { readdir, readFile, writeFile as writeFile2, mkdir, unlink, stat } from "fs/promises";
422
- import { join as join3, basename, extname } from "path";
174
+ import { readdir, readFile, writeFile, mkdir, unlink, stat } from "fs/promises";
175
+ import { join, basename, extname } from "path";
423
176
  var MemoryStore = class {
424
177
  dir;
425
178
  constructor(dir) {
@@ -453,7 +206,7 @@ var MemoryStore = class {
453
206
  frontmatter: memory.frontmatter,
454
207
  body: memory.body
455
208
  });
456
- await writeFile2(this.pathFor(memory.id), source, "utf8");
209
+ await writeFile(this.pathFor(memory.id), source, "utf8");
457
210
  }
458
211
  /** Delete a memory. Returns false if it did not exist. */
459
212
  async delete(id) {
@@ -477,13 +230,13 @@ var MemoryStore = class {
477
230
  }
478
231
  /** Absolute path of a memory file (file may not exist). */
479
232
  pathFor(id) {
480
- return join3(this.dir, `${id}.md`);
233
+ return join(this.dir, `${id}.md`);
481
234
  }
482
235
  };
483
236
 
484
237
  // ../modules/memory-system/dist/memory-index.js
485
- import { writeFile as writeFile3 } from "fs/promises";
486
- import { join as join4 } from "path";
238
+ import { writeFile as writeFile2 } from "fs/promises";
239
+ import { join as join2 } from "path";
487
240
  async function writeMemoryIndex(store, options = {}) {
488
241
  const ids = await store.list();
489
242
  const lines = [];
@@ -495,7 +248,7 @@ async function writeMemoryIndex(store, options = {}) {
495
248
  const memory = await store.read(id);
496
249
  lines.push(`- [${memory.frontmatter.name}](${id}.md) \u2014 ${memory.frontmatter.description}`);
497
250
  }
498
- await writeFile3(join4(store.dir, "MEMORY.md"), `${lines.join("\n")}
251
+ await writeFile2(join2(store.dir, "MEMORY.md"), `${lines.join("\n")}
499
252
  `, "utf8");
500
253
  }
501
254
 
@@ -533,7 +286,7 @@ __export(dist_exports4, {
533
286
 
534
287
  // ../modules/data-lint/dist/runner.js
535
288
  import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
536
- import { join as join5 } from "path";
289
+ import { join as join3 } from "path";
537
290
  async function lintDirectory(options) {
538
291
  const start = Date.now();
539
292
  const extensions = options.extensions ?? [".md"];
@@ -570,7 +323,7 @@ async function collectFiles(dir, extensions) {
570
323
  throw e;
571
324
  }
572
325
  for (const entry of entries) {
573
- const full = join5(current, entry.name);
326
+ const full = join3(current, entry.name);
574
327
  if (entry.isDirectory()) {
575
328
  stack.push(full);
576
329
  } else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
@@ -631,7 +384,7 @@ function privacyValid() {
631
384
 
632
385
  // ../modules/data-lint/dist/rules/wiki-link-resolves.js
633
386
  import { readdir as readdir3 } from "fs/promises";
634
- import { basename as basename2, extname as extname2, join as join6 } from "path";
387
+ import { basename as basename2, extname as extname2, join as join4 } from "path";
635
388
  var WIKI_LINK = /\[\[([^\]|#]+)(?:[|#][^\]]*)?\]\]/g;
636
389
  function wikiLinkResolves(options) {
637
390
  let cache;
@@ -652,7 +405,7 @@ function wikiLinkResolves(options) {
652
405
  continue;
653
406
  }
654
407
  for (const entry of entries) {
655
- const full = join6(current, entry.name);
408
+ const full = join4(current, entry.name);
656
409
  if (entry.isDirectory()) {
657
410
  stack.push(full);
658
411
  } else if (entry.isFile()) {
@@ -737,7 +490,7 @@ __export(dist_exports5, {
737
490
 
738
491
  // ../modules/ai-coding-pitfalls/dist/catalog.js
739
492
  import { readdir as readdir4, readFile as readFile4 } from "fs/promises";
740
- import { basename as basename3, extname as extname3, join as join7 } from "path";
493
+ import { basename as basename3, extname as extname3, join as join5 } from "path";
741
494
  var PitfallCatalog = class {
742
495
  dir;
743
496
  constructor(dir) {
@@ -765,7 +518,7 @@ var PitfallCatalog = class {
765
518
  async entries() {
766
519
  try {
767
520
  const names = await readdir4(this.dir);
768
- return names.filter((n) => n.endsWith(".md")).map((n) => join7(this.dir, n));
521
+ return names.filter((n) => n.endsWith(".md")).map((n) => join5(this.dir, n));
769
522
  } catch (e) {
770
523
  if (e.code === "ENOENT")
771
524
  return [];
@@ -797,7 +550,7 @@ __export(dist_exports6, {
797
550
 
798
551
  // ../modules/tool-rules/dist/catalog.js
799
552
  import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
800
- import { basename as basename4, extname as extname4, join as join8 } from "path";
553
+ import { basename as basename4, extname as extname4, join as join6 } from "path";
801
554
  var ToolRuleCatalog = class {
802
555
  dir;
803
556
  constructor(dir) {
@@ -830,7 +583,7 @@ var ToolRuleCatalog = class {
830
583
  async entries() {
831
584
  try {
832
585
  const names = await readdir5(this.dir);
833
- return names.filter((n) => n.endsWith(".md")).map((n) => join8(this.dir, n));
586
+ return names.filter((n) => n.endsWith(".md")).map((n) => join6(this.dir, n));
834
587
  } catch (e) {
835
588
  if (e.code === "ENOENT")
836
589
  return [];
@@ -2094,7 +1847,7 @@ __export(dist_exports8, {
2094
1847
 
2095
1848
  // ../modules/worklog/dist/store.js
2096
1849
  import { readdir as readdir6, readFile as readFile6, stat as stat2 } from "fs/promises";
2097
- import { join as join9, resolve as resolve3, sep as sep2 } from "path";
1850
+ import { join as join7, resolve, sep } from "path";
2098
1851
  var FILENAME_PATTERN = /^(\d{4}-\d{2}-\d{2})(?:_(\d{4}))?-(.+)\.md$/;
2099
1852
  var MONTH_PATTERN = /^\d{2}$/;
2100
1853
  var YEAR_PATTERN = /^\d{4}$/;
@@ -2108,17 +1861,17 @@ var WorklogStore = class {
2108
1861
  const years = await this.listSubdirs(this.rootDir, YEAR_PATTERN);
2109
1862
  const entries = [];
2110
1863
  for (const year of years) {
2111
- const yearDir = join9(this.rootDir, year);
1864
+ const yearDir = join7(this.rootDir, year);
2112
1865
  const months = await this.listSubdirs(yearDir, MONTH_PATTERN);
2113
1866
  for (const month of months) {
2114
- entries.push(...await this.entriesIn(join9(yearDir, month)));
1867
+ entries.push(...await this.entriesIn(join7(yearDir, month)));
2115
1868
  }
2116
1869
  }
2117
1870
  return entries.sort(compareWorklog);
2118
1871
  }
2119
1872
  /** Entries within one calendar month. */
2120
1873
  async listByMonth(year, month) {
2121
- const monthDir = join9(this.rootDir, String(year).padStart(4, "0"), String(month).padStart(2, "0"));
1874
+ const monthDir = join7(this.rootDir, String(year).padStart(4, "0"), String(month).padStart(2, "0"));
2122
1875
  const entries = await this.entriesIn(monthDir);
2123
1876
  return entries.sort(compareWorklog);
2124
1877
  }
@@ -2131,7 +1884,7 @@ var WorklogStore = class {
2131
1884
  const [year, month] = date.split("-");
2132
1885
  if (!year || !month)
2133
1886
  return void 0;
2134
- const monthDir = join9(this.rootDir, year, month);
1887
+ const monthDir = join7(this.rootDir, year, month);
2135
1888
  const entries = (await this.entriesIn(monthDir)).filter((e) => e.date === date).sort(compareWorklog);
2136
1889
  return entries[0];
2137
1890
  }
@@ -2153,7 +1906,7 @@ var WorklogStore = class {
2153
1906
  const [year, month] = date.split("-");
2154
1907
  validateSegment("keyword", keyword);
2155
1908
  const stem = time ? `${date}_${time}-${keyword}` : `${date}-${keyword}`;
2156
- const abs = join9(this.rootDir, year, month, `${stem}.md`);
1909
+ const abs = join7(this.rootDir, year, month, `${stem}.md`);
2157
1910
  assertContained(abs, this.rootDir);
2158
1911
  return abs;
2159
1912
  }
@@ -2181,7 +1934,7 @@ var WorklogStore = class {
2181
1934
  const match = name.match(FILENAME_PATTERN);
2182
1935
  if (!match)
2183
1936
  continue;
2184
- const path = join9(monthDir, name);
1937
+ const path = join7(monthDir, name);
2185
1938
  try {
2186
1939
  const info = await stat2(path);
2187
1940
  if (!info.isFile())
@@ -2222,15 +1975,15 @@ function validateSegment(label, value) {
2222
1975
  throw new Error(`${label} must not contain NUL or control characters: ${value}`);
2223
1976
  }
2224
1977
  function assertContained(abs, rootDir) {
2225
- const root = resolve3(rootDir);
2226
- const target = resolve3(abs);
2227
- if (target !== root && !(target + sep2).startsWith(root + sep2)) {
1978
+ const root = resolve(rootDir);
1979
+ const target = resolve(abs);
1980
+ if (target !== root && !(target + sep).startsWith(root + sep)) {
2228
1981
  throw new Error(`Refusing to write outside the store directory: ${abs}`);
2229
1982
  }
2230
1983
  }
2231
1984
 
2232
1985
  // ../modules/worklog/dist/append.js
2233
- import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
1986
+ import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
2234
1987
  async function appendSection(entry, title, body) {
2235
1988
  const original = await readFile7(entry.path, "utf8");
2236
1989
  const trimmed = original.replace(/\s+$/u, "");
@@ -2241,7 +1994,7 @@ ${body.trimEnd()}
2241
1994
  const next = `${trimmed}
2242
1995
 
2243
1996
  ${section}`;
2244
- await writeFile4(entry.path, next, "utf8");
1997
+ await writeFile3(entry.path, next, "utf8");
2245
1998
  return next;
2246
1999
  }
2247
2000
 
@@ -2254,7 +2007,7 @@ __export(dist_exports9, {
2254
2007
 
2255
2008
  // ../modules/decision-log/dist/store.js
2256
2009
  import { readdir as readdir7, readFile as readFile8, stat as stat3 } from "fs/promises";
2257
- import { join as join10, resolve as resolve4, sep as sep3 } from "path";
2010
+ import { join as join8, resolve as resolve2, sep as sep2 } from "path";
2258
2011
  var FILENAME_PATTERN2 = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
2259
2012
  var DecisionStore = class {
2260
2013
  rootDir;
@@ -2276,7 +2029,7 @@ var DecisionStore = class {
2276
2029
  const match = name.match(FILENAME_PATTERN2);
2277
2030
  if (!match)
2278
2031
  continue;
2279
- const path = join10(this.rootDir, name);
2032
+ const path = join8(this.rootDir, name);
2280
2033
  try {
2281
2034
  const info = await stat3(path);
2282
2035
  if (!info.isFile())
@@ -2348,7 +2101,7 @@ var DecisionStore = class {
2348
2101
  throw new Error(`Invalid date: ${date} (expected YYYY-MM-DD)`);
2349
2102
  }
2350
2103
  validateSegment2("slug", slug);
2351
- const abs = join10(this.rootDir, `${date}-${slug}.md`);
2104
+ const abs = join8(this.rootDir, `${date}-${slug}.md`);
2352
2105
  assertContained2(abs, this.rootDir);
2353
2106
  return abs;
2354
2107
  }
@@ -2367,9 +2120,9 @@ function validateSegment2(label, value) {
2367
2120
  throw new Error(`${label} must not contain NUL or control characters: ${value}`);
2368
2121
  }
2369
2122
  function assertContained2(abs, rootDir) {
2370
- const root = resolve4(rootDir);
2371
- const target = resolve4(abs);
2372
- if (target !== root && !(target + sep3).startsWith(root + sep3)) {
2123
+ const root = resolve2(rootDir);
2124
+ const target = resolve2(abs);
2125
+ if (target !== root && !(target + sep2).startsWith(root + sep2)) {
2373
2126
  throw new Error(`Refusing to write outside the store directory: ${abs}`);
2374
2127
  }
2375
2128
  }
@@ -2434,7 +2187,7 @@ __export(dist_exports10, {
2434
2187
 
2435
2188
  // ../modules/index-generator/dist/scan.js
2436
2189
  import { readdir as readdir8, readFile as readFile9, stat as stat4 } from "fs/promises";
2437
- import { basename as basename5, extname as extname5, join as join11, relative as relative2 } from "path";
2190
+ import { basename as basename5, extname as extname5, join as join9, relative } from "path";
2438
2191
  var RESERVED_FILES = /* @__PURE__ */ new Set(["README.md", "_INDEX.md"]);
2439
2192
  var H1_PATTERN = /^#\s+(.+?)\s*$/m;
2440
2193
  async function scanDirectory(rootDir, opts = {}) {
@@ -2464,7 +2217,7 @@ async function walk(rootDir, currentDir, recursive, skipFilenames, skipPrefixes,
2464
2217
  continue;
2465
2218
  if (name.startsWith(".") || name.startsWith("_"))
2466
2219
  continue;
2467
- await walk(rootDir, join11(currentDir, name), recursive, skipFilenames, skipPrefixes, out);
2220
+ await walk(rootDir, join9(currentDir, name), recursive, skipFilenames, skipPrefixes, out);
2468
2221
  continue;
2469
2222
  }
2470
2223
  if (!dirent.isFile())
@@ -2476,7 +2229,7 @@ async function walk(rootDir, currentDir, recursive, skipFilenames, skipPrefixes,
2476
2229
  const nameNoExt = basename5(name, ".md");
2477
2230
  if (skipPrefixes.some((p) => nameNoExt.startsWith(p)))
2478
2231
  continue;
2479
- const fullPath = join11(currentDir, name);
2232
+ const fullPath = join9(currentDir, name);
2480
2233
  let info;
2481
2234
  try {
2482
2235
  info = await stat4(fullPath);
@@ -2493,7 +2246,7 @@ async function walk(rootDir, currentDir, recursive, skipFilenames, skipPrefixes,
2493
2246
  const updated = stringField(frontmatter, "updated") ?? stringField(frontmatter, "created");
2494
2247
  const scope = stringField(frontmatter, "scope");
2495
2248
  out.push({
2496
- relPath: relative2(rootDir, fullPath).split(/[\\/]/).join("/"),
2249
+ relPath: relative(rootDir, fullPath).split(/[\\/]/).join("/"),
2497
2250
  name: nameNoExt,
2498
2251
  title,
2499
2252
  description,
@@ -2632,7 +2385,7 @@ function today() {
2632
2385
 
2633
2386
  // ../modules/index-generator/dist/nested.js
2634
2387
  import { readdir as readdir9, stat as stat5 } from "fs/promises";
2635
- import { extname as extname6, join as join12 } from "path";
2388
+ import { extname as extname6, join as join10 } from "path";
2636
2389
  async function findIndexableDirs(rootDir, options = {}) {
2637
2390
  const minEntries = options.minEntries ?? 1;
2638
2391
  const skipPrefixes = options.skipPrefixes ?? [];
@@ -2668,7 +2421,7 @@ async function walk2(dir, minEntries, skipPrefixes, out) {
2668
2421
  if (skipPrefixes.some((p) => nameNoExt.startsWith(p)))
2669
2422
  continue;
2670
2423
  try {
2671
- const info = await stat5(join12(dir, dirent.name));
2424
+ const info = await stat5(join10(dir, dirent.name));
2672
2425
  if (!info.isFile())
2673
2426
  continue;
2674
2427
  } catch {
@@ -2680,7 +2433,7 @@ async function walk2(dir, minEntries, skipPrefixes, out) {
2680
2433
  out.push(dir);
2681
2434
  }
2682
2435
  for (const name of subdirs) {
2683
- await walk2(join12(dir, name), minEntries, skipPrefixes, out);
2436
+ await walk2(join10(dir, name), minEntries, skipPrefixes, out);
2684
2437
  }
2685
2438
  }
2686
2439
 
@@ -2692,7 +2445,7 @@ __export(dist_exports11, {
2692
2445
 
2693
2446
  // ../modules/runbooks/dist/store.js
2694
2447
  import { readdir as readdir10, readFile as readFile10, stat as stat6 } from "fs/promises";
2695
- import { extname as extname7, join as join13 } from "path";
2448
+ import { extname as extname7, join as join11 } from "path";
2696
2449
  var RESERVED_FILES2 = /* @__PURE__ */ new Set(["README.md", "_INDEX.md"]);
2697
2450
  var RunbookStore = class {
2698
2451
  rootDir;
@@ -2715,7 +2468,7 @@ var RunbookStore = class {
2715
2468
  continue;
2716
2469
  if (RESERVED_FILES2.has(name))
2717
2470
  continue;
2718
- const path = join13(this.rootDir, name);
2471
+ const path = join11(this.rootDir, name);
2719
2472
  try {
2720
2473
  const info = await stat6(path);
2721
2474
  if (!info.isFile())
@@ -2824,7 +2577,7 @@ function extractWikiLinks(body) {
2824
2577
 
2825
2578
  // ../modules/link-rewriter/dist/resolve.js
2826
2579
  import { readdir as readdir11 } from "fs/promises";
2827
- import { basename as basename6, dirname, extname as extname8, isAbsolute as isAbsolute2, join as join14, relative as relative3, resolve as pathResolve } from "path";
2580
+ import { basename as basename6, dirname, extname as extname8, isAbsolute, join as join12, relative as relative2, resolve as pathResolve } from "path";
2828
2581
  async function buildFileIndex(rootDir, options = {}) {
2829
2582
  const byBasename = /* @__PURE__ */ new Map();
2830
2583
  const byRelPath = /* @__PURE__ */ new Map();
@@ -2857,7 +2610,7 @@ async function walk3(rootDir, dir, byBasename, byRelPath, additionalExts) {
2857
2610
  if (dirent.isDirectory()) {
2858
2611
  if (name.startsWith("."))
2859
2612
  continue;
2860
- await walk3(rootDir, join14(dir, name), byBasename, byRelPath, additionalExts);
2613
+ await walk3(rootDir, join12(dir, name), byBasename, byRelPath, additionalExts);
2861
2614
  continue;
2862
2615
  }
2863
2616
  if (!dirent.isFile())
@@ -2866,7 +2619,7 @@ async function walk3(rootDir, dir, byBasename, byRelPath, additionalExts) {
2866
2619
  const isMd = ext === ".md";
2867
2620
  if (!isMd && !additionalExts.has(ext))
2868
2621
  continue;
2869
- const path = join14(dir, name);
2622
+ const path = join12(dir, name);
2870
2623
  const key = isMd ? basename6(name, ".md") : name;
2871
2624
  const list = byBasename.get(key);
2872
2625
  if (list) {
@@ -2874,7 +2627,7 @@ async function walk3(rootDir, dir, byBasename, byRelPath, additionalExts) {
2874
2627
  } else {
2875
2628
  byBasename.set(key, [path]);
2876
2629
  }
2877
- const relRaw = relative3(rootDir, path).split(/[\\/]/).join("/");
2630
+ const relRaw = relative2(rootDir, path).split(/[\\/]/).join("/");
2878
2631
  const rel = isMd ? relRaw.replace(/\.md$/, "") : relRaw;
2879
2632
  byRelPath.set(rel, path);
2880
2633
  }
@@ -2895,8 +2648,8 @@ function resolveLink(name, index, opts = {}) {
2895
2648
  return { kind: "not-found" };
2896
2649
  const baseDir = dirname(opts.sourcePath);
2897
2650
  const absolute = pathResolve(baseDir, normalized);
2898
- const rel = relative3(index.rootDir, absolute).split(/[\\/]/).join("/");
2899
- if (rel === ".." || rel.startsWith("../") || isAbsolute2(rel)) {
2651
+ const rel = relative2(index.rootDir, absolute).split(/[\\/]/).join("/");
2652
+ if (rel === ".." || rel.startsWith("../") || isAbsolute(rel)) {
2900
2653
  return { kind: "not-found" };
2901
2654
  }
2902
2655
  return lookupRelPath(rel, index);
@@ -2915,7 +2668,7 @@ function lookupRelPath(rel, index) {
2915
2668
  return { kind: "not-found" };
2916
2669
  }
2917
2670
  function toRel(path, rootDir) {
2918
- return relative3(rootDir, path).split(/[\\/]/).join("/");
2671
+ return relative2(rootDir, path).split(/[\\/]/).join("/");
2919
2672
  }
2920
2673
 
2921
2674
  // ../modules/link-rewriter/dist/checker.js
@@ -2965,7 +2718,7 @@ function topBrokenTargets(broken, limit = 20) {
2965
2718
  }
2966
2719
 
2967
2720
  // ../modules/link-rewriter/dist/rewrite.js
2968
- import { readFile as readFile12, writeFile as writeFile5 } from "fs/promises";
2721
+ import { readFile as readFile12, writeFile as writeFile4 } from "fs/promises";
2969
2722
  import { extname as extname10 } from "path";
2970
2723
  async function rewriteDirectory(rootDir, opts) {
2971
2724
  const { redirections, dryRun = false } = opts;
@@ -2987,7 +2740,7 @@ async function rewriteDirectory(rootDir, opts) {
2987
2740
  rewritesApplied += fileRewrites.length;
2988
2741
  details.push({ sourcePath: path, rewrites: fileRewrites });
2989
2742
  if (!dryRun) {
2990
- await writeFile5(path, newBody, "utf8");
2743
+ await writeFile4(path, newBody, "utf8");
2991
2744
  }
2992
2745
  }
2993
2746
  }
@@ -3091,18 +2844,18 @@ function normalizePath(p) {
3091
2844
  }
3092
2845
 
3093
2846
  // ../modules/proactive-curator/dist/doc-writer.js
3094
- import { existsSync as existsSync2 } from "fs";
2847
+ import { existsSync } from "fs";
3095
2848
  import { appendFile, mkdir as mkdir2, readFile as readFile13 } from "fs/promises";
3096
- import { dirname as dirname2, join as join15 } from "path";
2849
+ import { dirname as dirname2, join as join13 } from "path";
3097
2850
  async function writeDocAction(cwd, action) {
3098
- const dataDir = join15(cwd, "data");
2851
+ const dataDir = join13(cwd, "data");
3099
2852
  const abs = validateDataRelativePath(dataDir, action.targetRelPath);
3100
2853
  if (action.kind === "create-file") {
3101
2854
  await mkdir2(dirname2(abs), { recursive: true });
3102
2855
  await exclusiveCreateFile(abs, action.body);
3103
2856
  return { writtenPath: abs, kind: "create-file" };
3104
2857
  }
3105
- if (!existsSync2(abs)) {
2858
+ if (!existsSync(abs)) {
3106
2859
  throw new Error(`Cannot append-section: target file does not exist: ${action.targetRelPath}. append-section adds to an existing document; use create-file for a new one.`);
3107
2860
  }
3108
2861
  const existing = await readFile13(abs, "utf8");
@@ -3115,9 +2868,9 @@ ${action.body}`;
3115
2868
  }
3116
2869
 
3117
2870
  // ../modules/proactive-curator/dist/decline-store.js
3118
- import { existsSync as existsSync3 } from "fs";
3119
- import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile14, writeFile as writeFile6 } from "fs/promises";
3120
- import { join as join16 } from "path";
2871
+ import { existsSync as existsSync2 } from "fs";
2872
+ import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile14, writeFile as writeFile5 } from "fs/promises";
2873
+ import { join as join14 } from "path";
3121
2874
  var STORE_DIR = "data/_proactive-curator";
3122
2875
  var DECLINED_FILE = "declined.json";
3123
2876
  var ACCEPTED_LOG = "accepted.log";
@@ -3155,7 +2908,7 @@ async function recordDecline(cwd, args) {
3155
2908
  ...args.sourceDocs ? { sourceDocs: args.sourceDocs } : {}
3156
2909
  };
3157
2910
  updated[args.kind] = { ...updated[args.kind], [args.fingerprint]: entry };
3158
- await writeFile6(join16(cwd, STORE_DIR, DECLINED_FILE), JSON.stringify(updated, null, 2) + "\n", "utf8");
2911
+ await writeFile5(join14(cwd, STORE_DIR, DECLINED_FILE), JSON.stringify(updated, null, 2) + "\n", "utf8");
3159
2912
  }
3160
2913
  async function recordAcceptance(cwd, args) {
3161
2914
  await ensureStoreDir(cwd);
@@ -3167,19 +2920,19 @@ async function recordAcceptance(cwd, args) {
3167
2920
  actionKind: args.actionKind,
3168
2921
  writtenPath: args.writtenPath
3169
2922
  });
3170
- await appendFile2(join16(cwd, STORE_DIR, ACCEPTED_LOG), line + "\n", "utf8");
2923
+ await appendFile2(join14(cwd, STORE_DIR, ACCEPTED_LOG), line + "\n", "utf8");
3171
2924
  }
3172
2925
  async function resetDeclined(cwd, kind) {
3173
- const file = join16(cwd, STORE_DIR, DECLINED_FILE);
3174
- if (!existsSync3(file))
2926
+ const file = join14(cwd, STORE_DIR, DECLINED_FILE);
2927
+ if (!existsSync2(file))
3175
2928
  return;
3176
2929
  if (kind === void 0) {
3177
- await writeFile6(file, JSON.stringify(emptyDeclinedFile(), null, 2) + "\n", "utf8");
2930
+ await writeFile5(file, JSON.stringify(emptyDeclinedFile(), null, 2) + "\n", "utf8");
3178
2931
  return;
3179
2932
  }
3180
2933
  const parsed = await readDeclinedFile(cwd) ?? emptyDeclinedFile();
3181
2934
  parsed[kind] = {};
3182
- await writeFile6(file, JSON.stringify(parsed, null, 2) + "\n", "utf8");
2935
+ await writeFile5(file, JSON.stringify(parsed, null, 2) + "\n", "utf8");
3183
2936
  }
3184
2937
  function isActive(entry, nowMs) {
3185
2938
  const expiresMs = new Date(entry.expiresAt).getTime();
@@ -3200,8 +2953,8 @@ function purgeExpired(entries, now) {
3200
2953
  return out;
3201
2954
  }
3202
2955
  async function readDeclinedFile(cwd) {
3203
- const file = join16(cwd, STORE_DIR, DECLINED_FILE);
3204
- if (!existsSync3(file))
2956
+ const file = join14(cwd, STORE_DIR, DECLINED_FILE);
2957
+ if (!existsSync2(file))
3205
2958
  return null;
3206
2959
  try {
3207
2960
  const raw = await readFile14(file, "utf8");
@@ -3218,14 +2971,14 @@ function emptyDeclinedFile() {
3218
2971
  return { "capture-insight": {}, "create-hub": {} };
3219
2972
  }
3220
2973
  async function ensureStoreDir(cwd) {
3221
- await mkdir3(join16(cwd, STORE_DIR), { recursive: true });
2974
+ await mkdir3(join14(cwd, STORE_DIR), { recursive: true });
3222
2975
  }
3223
2976
 
3224
2977
  // ../modules/proactive-curator/dist/insight-proposer.js
3225
- import { existsSync as existsSync4 } from "fs";
3226
- import { mkdir as mkdir4, readFile as readFile15, readdir as readdir12, writeFile as writeFile7 } from "fs/promises";
3227
- import { dirname as dirname3, join as join17 } from "path";
3228
- var SYSTEM_META_DIRS2 = /* @__PURE__ */ new Set([
2978
+ import { existsSync as existsSync3 } from "fs";
2979
+ import { mkdir as mkdir4, readFile as readFile15, readdir as readdir12, writeFile as writeFile6 } from "fs/promises";
2980
+ import { dirname as dirname3, join as join15 } from "path";
2981
+ var SYSTEM_META_DIRS = /* @__PURE__ */ new Set([
3229
2982
  "worklog",
3230
2983
  "decision-log",
3231
2984
  "runbooks",
@@ -3435,7 +3188,7 @@ function normalizePlacementDecision(raw) {
3435
3188
  function isSystemMetaPath(p) {
3436
3189
  const normalized = p.replace(/\\/g, "/").replace(/^\/+/, "");
3437
3190
  const first = normalized.split("/")[0] ?? "";
3438
- if (SYSTEM_META_DIRS2.has(first))
3191
+ if (SYSTEM_META_DIRS.has(first))
3439
3192
  return true;
3440
3193
  if (first.startsWith("_"))
3441
3194
  return true;
@@ -3530,7 +3283,7 @@ function joinDataPath(...parts) {
3530
3283
  return parts.filter((p) => p.length > 0).map((p) => p.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "")).join("/");
3531
3284
  }
3532
3285
  async function applyAction(cwd, action) {
3533
- const dataDir = join17(cwd, "data");
3286
+ const dataDir = join15(cwd, "data");
3534
3287
  switch (action.kind) {
3535
3288
  case "create-file": {
3536
3289
  const res = await writeDocAction(cwd, {
@@ -3560,7 +3313,7 @@ async function applyAction(cwd, action) {
3560
3313
  const rel = joinDataPath(action.filePath);
3561
3314
  const file = validateDataRelativePath(dataDir, rel);
3562
3315
  await mkdir4(dirname3(file), { recursive: true });
3563
- await writeFile7(file, action.body, "utf8");
3316
+ await writeFile6(file, action.body, "utf8");
3564
3317
  return file;
3565
3318
  }
3566
3319
  }
@@ -3575,8 +3328,8 @@ function nextActionHintFor(action) {
3575
3328
  return `New file at ${action.folderPath}/${action.filename}. Add cross-links from related docs as the topic grows.`;
3576
3329
  }
3577
3330
  async function scanTopicTree(cwd, maxEntries) {
3578
- const dataDir = join17(cwd, "data");
3579
- if (!existsSync4(dataDir)) {
3331
+ const dataDir = join15(cwd, "data");
3332
+ if (!existsSync3(dataDir)) {
3580
3333
  return { folders: [], truncated: false };
3581
3334
  }
3582
3335
  const folders = [];
@@ -3603,7 +3356,7 @@ async function scanTopicTree(cwd, maxEntries) {
3603
3356
  } else if (e.isFile() && e.name.endsWith(".md")) {
3604
3357
  if (e.name === "README.md" || e.name === "_INDEX.md" || e.name === "MEMORY.md")
3605
3358
  continue;
3606
- const filePath = join17(absDir, e.name);
3359
+ const filePath = join15(absDir, e.name);
3607
3360
  let frontmatterTopic;
3608
3361
  let tags;
3609
3362
  try {
@@ -3640,14 +3393,14 @@ async function scanTopicTree(cwd, maxEntries) {
3640
3393
  return;
3641
3394
  }
3642
3395
  const childRel = joinDataPath(relDir, d2);
3643
- await visit(join17(absDir, d2), childRel);
3396
+ await visit(join15(absDir, d2), childRel);
3644
3397
  }
3645
3398
  }
3646
3399
  await visit(dataDir, "");
3647
3400
  return { folders, truncated };
3648
3401
  }
3649
3402
  function isReservedDir(name, atRoot) {
3650
- if (atRoot && SYSTEM_META_DIRS2.has(name))
3403
+ if (atRoot && SYSTEM_META_DIRS.has(name))
3651
3404
  return true;
3652
3405
  if (name.startsWith("."))
3653
3406
  return true;
@@ -3657,9 +3410,9 @@ function isReservedDir(name, atRoot) {
3657
3410
  }
3658
3411
 
3659
3412
  // ../modules/proactive-curator/dist/hub-proposer.js
3660
- import { existsSync as existsSync5 } from "fs";
3413
+ import { existsSync as existsSync4 } from "fs";
3661
3414
  import { mkdir as mkdir5, readFile as readFile16, readdir as readdir13 } from "fs/promises";
3662
- import { join as join18 } from "path";
3415
+ import { join as join16 } from "path";
3663
3416
  var DEFAULT_CATEGORIES = [
3664
3417
  "worklog",
3665
3418
  "decision-log",
@@ -3746,13 +3499,13 @@ var HubProposer = class {
3746
3499
  }
3747
3500
  };
3748
3501
  async function scanDocs(cwd, categories) {
3749
- const dataDir = join18(cwd, "data");
3750
- if (!existsSync5(dataDir))
3502
+ const dataDir = join16(cwd, "data");
3503
+ if (!existsSync4(dataDir))
3751
3504
  return [];
3752
3505
  const out = [];
3753
3506
  for (const category of categories) {
3754
- const abs = join18(dataDir, category);
3755
- if (!existsSync5(abs))
3507
+ const abs = join16(dataDir, category);
3508
+ if (!existsSync4(abs))
3756
3509
  continue;
3757
3510
  await walk4(abs, category, out);
3758
3511
  }
@@ -3769,13 +3522,13 @@ async function walk4(absDir, relPath, acc) {
3769
3522
  if (e.isDirectory()) {
3770
3523
  if (e.name.startsWith(".") || e.name.startsWith("_"))
3771
3524
  continue;
3772
- await walk4(join18(absDir, e.name), `${relPath}/${e.name}`, acc);
3525
+ await walk4(join16(absDir, e.name), `${relPath}/${e.name}`, acc);
3773
3526
  } else if (e.isFile() && e.name.endsWith(".md")) {
3774
3527
  if (e.name === "README.md" || e.name === "_INDEX.md" || e.name === "MEMORY.md")
3775
3528
  continue;
3776
3529
  if (e.name.startsWith("_TEMPLATE"))
3777
3530
  continue;
3778
- const filePath = join18(absDir, e.name);
3531
+ const filePath = join16(absDir, e.name);
3779
3532
  let frontmatterTopic;
3780
3533
  let tags = [];
3781
3534
  try {
@@ -3846,8 +3599,8 @@ function pickCluster(clusters, weakThreshold, cwd) {
3846
3599
  for (const c of clusters) {
3847
3600
  if (c.docs.length < weakThreshold)
3848
3601
  return null;
3849
- const hubPath = join18(cwd, "data", HUB_DIR, `_HUB-${c.topic}.md`);
3850
- if (existsSync5(hubPath))
3602
+ const hubPath = join16(cwd, "data", HUB_DIR, `_HUB-${c.topic}.md`);
3603
+ if (existsSync4(hubPath))
3851
3604
  continue;
3852
3605
  return c;
3853
3606
  }
@@ -3970,9 +3723,9 @@ async function applyHubCreate(cwd, action) {
3970
3723
  if (fn.trim().length === 0 || fn === "." || fn === ".." || unsafe.test(fn)) {
3971
3724
  throw new Error(`Refusing to create hub: unsafe filename "${action.filename}" \u2014 must be a plain basename with no path separators, traversal, or reserved characters (including ":").`);
3972
3725
  }
3973
- const folder = join18(cwd, "data", HUB_DIR);
3726
+ const folder = join16(cwd, "data", HUB_DIR);
3974
3727
  await mkdir5(folder, { recursive: true });
3975
- const file = join18(folder, fn);
3728
+ const file = join16(folder, fn);
3976
3729
  await exclusiveCreateFile(file, action.body);
3977
3730
  return file;
3978
3731
  }
@@ -4259,6 +4012,10 @@ var ClaudeDesktopLLMJudge = class extends InjectedLLMJudge {
4259
4012
  var dist_exports14 = {};
4260
4013
  __export(dist_exports14, {
4261
4014
  DEFAULT_GAP_WINDOW_DAYS: () => DEFAULT_GAP_WINDOW_DAYS,
4015
+ FAILURES_DIR: () => FAILURES_DIR,
4016
+ GUARD_DENIAL_KEY: () => GUARD_DENIAL_KEY,
4017
+ GUARD_WRITE_COMMAND: () => GUARD_WRITE_COMMAND,
4018
+ GUARD_WRITE_MATCHER: () => GUARD_WRITE_MATCHER,
4262
4019
  HANDOFF_ARCHIVE_DIR: () => HANDOFF_ARCHIVE_DIR,
4263
4020
  HANDOFF_DIR: () => HANDOFF_DIR,
4264
4021
  OWNERSHIP_SCHEMA: () => OWNERSHIP_SCHEMA,
@@ -4269,6 +4026,7 @@ __export(dist_exports14, {
4269
4026
  applyGlobalSetup: () => applyGlobalSetup,
4270
4027
  argvToSlash: () => argvToSlash,
4271
4028
  autoReindexMemory: () => autoReindexMemory,
4029
+ buildDenyDecision: () => buildDenyDecision,
4272
4030
  buildInstallCommand: () => buildInstallCommand,
4273
4031
  buildOwnershipManifest: () => buildOwnershipManifest,
4274
4032
  buildRegistry: () => buildRegistry,
@@ -4294,6 +4052,8 @@ __export(dist_exports14, {
4294
4052
  ensureWorklogEntry: () => ensureWorklogEntry,
4295
4053
  extractNextUp: () => extractNextUp,
4296
4054
  extractOpenTasks: () => extractOpenTasks,
4055
+ failureReportSlice: () => failureReportSlice,
4056
+ findControlChar: () => findControlChar,
4297
4057
  formatTokens: () => formatTokens,
4298
4058
  formatWindow: () => formatWindow,
4299
4059
  gapWindowSinceArg: () => gapWindowSinceArg,
@@ -4301,12 +4061,15 @@ __export(dist_exports14, {
4301
4061
  globalSettingsHasHook: () => globalSettingsHasHook,
4302
4062
  globalSettingsPath: () => globalSettingsPath,
4303
4063
  globalStatePath: () => globalStatePath,
4064
+ guardWriteDecision: () => guardWriteDecision,
4304
4065
  handoffCommand: () => handoffCommand,
4305
4066
  inspectGlobalSetup: () => inspectGlobalSetup,
4306
4067
  inspectOwnership: () => inspectOwnership,
4307
4068
  isInstanceRoot: () => isInstanceRoot,
4308
4069
  isNewer: () => isNewer,
4309
4070
  isStableUpdate: () => isStableUpdate,
4071
+ isValidFailureKey: () => isValidFailureKey,
4072
+ ladderStage: () => ladderStage,
4310
4073
  logCommand: () => logCommand,
4311
4074
  makeBar: () => makeBar,
4312
4075
  ownershipManifestPath: () => ownershipManifestPath,
@@ -4318,23 +4081,30 @@ __export(dist_exports14, {
4318
4081
  readGlobalInstancePointer: () => readGlobalInstancePointer,
4319
4082
  readInstalledBaseVersion: () => readInstalledBaseVersion,
4320
4083
  recallCommand: () => recallCommand,
4084
+ recordFailure: () => recordFailure,
4321
4085
  recordGlobalSetupDecline: () => recordGlobalSetupDecline,
4086
+ recordGuardDenial: () => recordGuardDenial,
4322
4087
  reindexCommand: () => reindexCommand,
4323
4088
  renderAgenda: () => renderAgenda,
4324
4089
  renderGlobalBlock: () => renderGlobalBlock,
4325
4090
  renderSessionStartReport: () => renderSessionStartReport,
4326
4091
  renderStatusline: () => renderStatusline,
4327
4092
  repairOwnershipManifest: () => repairOwnershipManifest,
4093
+ resolveInstanceRoot: () => resolveInstanceRoot,
4328
4094
  resolveRepoRoot: () => resolveRepoRoot,
4329
4095
  runCurateAccept: () => runCurateAccept,
4330
4096
  runCurateCandidates: () => runCurateCandidates,
4331
4097
  runCurateDecline: () => runCurateDecline,
4332
4098
  runCuratePreview: () => runCuratePreview,
4099
+ runFailureCli: () => runFailureCli,
4100
+ runGuardCli: () => runGuardCli,
4333
4101
  runStatuslineCli: () => runStatuslineCli,
4334
4102
  runTemplatesUpdate: () => runTemplatesUpdate,
4335
4103
  runVortexCli: () => runVortexCli,
4336
4104
  safeSegment: () => safeSegment,
4105
+ scanFailures: () => scanFailures,
4337
4106
  scanHandoffs: () => scanHandoffs,
4107
+ scanToolInput: () => scanToolInput,
4338
4108
  serializeSettings: () => serializeSettings,
4339
4109
  sessionStartCommand: () => sessionStartCommand,
4340
4110
  sniffEffortFromTranscript: () => sniffEffortFromTranscript,
@@ -4448,7 +4218,7 @@ function curateCommand(options) {
4448
4218
  }
4449
4219
 
4450
4220
  // ../plugins/session-rituals/dist/commands/recall.js
4451
- import { join as join19 } from "path";
4221
+ import { join as join17 } from "path";
4452
4222
  function asMode(s) {
4453
4223
  return s === "keyword" || s === "semantic" || s === "hybrid" ? s : void 0;
4454
4224
  }
@@ -4484,7 +4254,7 @@ function parseRecallArgs(rest, defaultK) {
4484
4254
  return out;
4485
4255
  }
4486
4256
  function defaultDbPath(ctx) {
4487
- return join19(ctx.dataDir, "_indexes", "memory.sqlite");
4257
+ return join17(ctx.dataDir, "_indexes", "memory.sqlite");
4488
4258
  }
4489
4259
  function recallCommand(options) {
4490
4260
  const defaultK = options.defaultK ?? 5;
@@ -4542,9 +4312,9 @@ function recallCommand(options) {
4542
4312
  }
4543
4313
 
4544
4314
  // ../plugins/session-rituals/dist/commands/decision.js
4545
- import { writeFile as writeFile8 } from "fs/promises";
4546
- import { join as join20 } from "path";
4547
- import { existsSync as existsSync6 } from "fs";
4315
+ import { writeFile as writeFile7 } from "fs/promises";
4316
+ import { join as join18 } from "path";
4317
+ import { existsSync as existsSync5 } from "fs";
4548
4318
  var decisionCommand = {
4549
4319
  name: "decision",
4550
4320
  description: "Create a new Decision Log entry from the canonical template at `data/decision-log/<today>-<slug>.md`. Refuses to overwrite an existing file.",
@@ -4562,14 +4332,14 @@ var decisionCommand = {
4562
4332
  throw new Error("`/decision` requires a title after the slug.");
4563
4333
  }
4564
4334
  const date = todayIso();
4565
- const dir = join20(input.context.dataDir, "decision-log");
4335
+ const dir = join18(input.context.dataDir, "decision-log");
4566
4336
  const store = new DecisionStore(dir);
4567
4337
  const path = store.pathFor(date, slug);
4568
- if (existsSync6(path)) {
4338
+ if (existsSync5(path)) {
4569
4339
  throw new Error(`Refusing to overwrite existing entry: ${path}`);
4570
4340
  }
4571
4341
  const body = renderTemplate({ date, slug, title });
4572
- await writeFile8(path, body, "utf8");
4342
+ await writeFile7(path, body, "utf8");
4573
4343
  return { path, date, slug };
4574
4344
  }
4575
4345
  };
@@ -4589,9 +4359,9 @@ function todayIso() {
4589
4359
  }
4590
4360
 
4591
4361
  // ../plugins/session-rituals/dist/commands/reindex.js
4592
- import { existsSync as existsSync7 } from "fs";
4593
- import { readFile as readFile17, writeFile as writeFile9, utimes } from "fs/promises";
4594
- import { join as join21 } from "path";
4362
+ import { existsSync as existsSync6 } from "fs";
4363
+ import { readFile as readFile17, writeFile as writeFile8, utimes } from "fs/promises";
4364
+ import { join as join19 } from "path";
4595
4365
  var TARGETS = [
4596
4366
  {
4597
4367
  dir: "_memory",
@@ -4701,8 +4471,8 @@ var reindexCommand = {
4701
4471
  }
4702
4472
  const results = [];
4703
4473
  for (const t of targets) {
4704
- const dir = join21(input.context.dataDir, t.dir);
4705
- if (!existsSync7(dir)) {
4474
+ const dir = join19(input.context.dataDir, t.dir);
4475
+ if (!existsSync6(dir)) {
4706
4476
  results.push({ dir: t.dir, status: "missing", entries: 0, bytes: 0 });
4707
4477
  continue;
4708
4478
  }
@@ -4717,7 +4487,7 @@ var reindexCommand = {
4717
4487
  entries,
4718
4488
  privacy: t.privacy
4719
4489
  });
4720
- const target = join21(dir, "_INDEX.md");
4490
+ const target = join19(dir, "_INDEX.md");
4721
4491
  let existing;
4722
4492
  try {
4723
4493
  existing = await readFile17(target, "utf8");
@@ -4733,7 +4503,7 @@ var reindexCommand = {
4733
4503
  });
4734
4504
  continue;
4735
4505
  }
4736
- await writeFile9(target, body, "utf8");
4506
+ await writeFile8(target, body, "utf8");
4737
4507
  results.push({
4738
4508
  dir: t.dir,
4739
4509
  status: "written",
@@ -4749,8 +4519,8 @@ async function autoReindexMemory(ctx) {
4749
4519
  const target = TARGETS.find((t) => t.dir === "_memory");
4750
4520
  if (!target)
4751
4521
  return "missing";
4752
- const dir = join21(ctx.dataDir, target.dir);
4753
- if (!existsSync7(dir))
4522
+ const dir = join19(ctx.dataDir, target.dir);
4523
+ if (!existsSync6(dir))
4754
4524
  return "missing";
4755
4525
  const entries = await scanDirectory(dir, {
4756
4526
  recursive: target.recursive,
@@ -4763,7 +4533,7 @@ async function autoReindexMemory(ctx) {
4763
4533
  entries,
4764
4534
  privacy: target.privacy
4765
4535
  });
4766
- const indexPath = join21(dir, "_INDEX.md");
4536
+ const indexPath = join19(dir, "_INDEX.md");
4767
4537
  let existing;
4768
4538
  try {
4769
4539
  existing = await readFile17(indexPath, "utf8");
@@ -4775,10 +4545,10 @@ async function autoReindexMemory(ctx) {
4775
4545
  if (sameListing) {
4776
4546
  status = "unchanged";
4777
4547
  } else {
4778
- await writeFile9(indexPath, body, "utf8");
4548
+ await writeFile8(indexPath, body, "utf8");
4779
4549
  status = "written";
4780
4550
  }
4781
- if (existsSync7(indexPath)) {
4551
+ if (existsSync6(indexPath)) {
4782
4552
  const now = /* @__PURE__ */ new Date();
4783
4553
  await utimes(indexPath, now, now);
4784
4554
  }
@@ -4792,9 +4562,9 @@ function stripIndexDate(body) {
4792
4562
  }
4793
4563
 
4794
4564
  // ../plugins/session-rituals/dist/commands/session-start.js
4795
- import { existsSync as existsSync8 } from "fs";
4565
+ import { existsSync as existsSync7 } from "fs";
4796
4566
  import { readdir as readdir14 } from "fs/promises";
4797
- import { join as join22 } from "path";
4567
+ import { join as join20 } from "path";
4798
4568
  var COUNTED_DIRS = ["_memory", "worklog", "decision-log"];
4799
4569
  var sessionStartCommand = {
4800
4570
  name: "session-start",
@@ -4804,8 +4574,8 @@ var sessionStartCommand = {
4804
4574
  const counts = {};
4805
4575
  const missing = [];
4806
4576
  for (const name of COUNTED_DIRS) {
4807
- const dir = join22(dataDir, name);
4808
- if (!existsSync8(dir)) {
4577
+ const dir = join20(dataDir, name);
4578
+ if (!existsSync7(dir)) {
4809
4579
  missing.push(name);
4810
4580
  counts[name] = 0;
4811
4581
  continue;
@@ -4837,7 +4607,7 @@ async function countMarkdown(dir, recursive) {
4837
4607
  } else if (e.isDirectory() && recursive) {
4838
4608
  if (e.name.startsWith(".") || e.name.startsWith("_"))
4839
4609
  continue;
4840
- total += await countMarkdown(join22(dir, e.name), recursive);
4610
+ total += await countMarkdown(join20(dir, e.name), recursive);
4841
4611
  }
4842
4612
  }
4843
4613
  return total;
@@ -4883,9 +4653,9 @@ function todayIso2() {
4883
4653
  }
4884
4654
 
4885
4655
  // ../plugins/session-rituals/dist/handoff.js
4886
- import { existsSync as existsSync9 } from "fs";
4887
- import { mkdir as mkdir6, open, readdir as readdir15, readFile as readFile18, rename as rename2, stat as stat7 } from "fs/promises";
4888
- import { join as join23 } from "path";
4656
+ import { existsSync as existsSync8 } from "fs";
4657
+ import { mkdir as mkdir6, open, readdir as readdir15, readFile as readFile18, rename, stat as stat7 } from "fs/promises";
4658
+ import { join as join21 } from "path";
4889
4659
 
4890
4660
  // ../plugins/session-rituals/dist/agenda.js
4891
4661
  var DEFAULT_RECENT = 7;
@@ -5083,7 +4853,7 @@ async function createHandoffSkeleton(dataDir, opts) {
5083
4853
  const now = opts?.now ?? /* @__PURE__ */ new Date();
5084
4854
  const date = isoDate(now);
5085
4855
  const time = isoTime(now);
5086
- const dir = join23(dataDir, HANDOFF_DIR);
4856
+ const dir = join21(dataDir, HANDOFF_DIR);
5087
4857
  await mkdir6(dir, { recursive: true });
5088
4858
  const stamp = `${date} ${time.slice(0, 2)}:${time.slice(2)}`;
5089
4859
  const title = (opts?.title ?? "").trim();
@@ -5091,7 +4861,7 @@ async function createHandoffSkeleton(dataDir, opts) {
5091
4861
  const base = `${date}_${time}`;
5092
4862
  for (let i = 0; i < MAX_COLLISION_TRIES; i++) {
5093
4863
  const name = i === 0 ? `${base}.md` : `${base}-${i + 1}.md`;
5094
- const abs = join23(dir, name);
4864
+ const abs = join21(dir, name);
5095
4865
  try {
5096
4866
  const fh = await open(abs, "wx");
5097
4867
  try {
@@ -5125,8 +4895,8 @@ ${heading}
5125
4895
  `;
5126
4896
  }
5127
4897
  async function scanHandoffs(dataDir, opts) {
5128
- const dir = join23(dataDir, HANDOFF_DIR);
5129
- if (!existsSync9(dir))
4898
+ const dir = join21(dataDir, HANDOFF_DIR);
4899
+ if (!existsSync8(dir))
5130
4900
  return { active: [], omitted: 0 };
5131
4901
  let names;
5132
4902
  try {
@@ -5140,7 +4910,7 @@ async function scanHandoffs(dataDir, opts) {
5140
4910
  const omitted = Math.max(0, matched.length - take.length);
5141
4911
  const active = [];
5142
4912
  for (const { name, m: m2 } of take) {
5143
- const abs = join23(dir, name);
4913
+ const abs = join21(dir, name);
5144
4914
  let title = name.replace(/\.md$/, "");
5145
4915
  let nextUp = [];
5146
4916
  try {
@@ -5160,8 +4930,8 @@ async function scanHandoffs(dataDir, opts) {
5160
4930
  async function pruneHandoffs(dataDir, opts) {
5161
4931
  const now = opts.now ?? /* @__PURE__ */ new Date();
5162
4932
  const retentionDays = opts.retentionDays;
5163
- const dir = join23(dataDir, HANDOFF_DIR);
5164
- if (!existsSync9(dir) || !(retentionDays > 0))
4933
+ const dir = join21(dataDir, HANDOFF_DIR);
4934
+ if (!existsSync8(dir) || !(retentionDays > 0))
5165
4935
  return { archived: 0 };
5166
4936
  let names;
5167
4937
  try {
@@ -5173,21 +4943,21 @@ async function pruneHandoffs(dataDir, opts) {
5173
4943
  const stale = names.map((name) => ({ name, m: name.match(HANDOFF_PATTERN) })).filter((x2) => x2.m !== null && x2.m[1] < cutoff);
5174
4944
  if (stale.length === 0)
5175
4945
  return { archived: 0 };
5176
- const archiveDir = join23(dir, HANDOFF_ARCHIVE_DIR);
4946
+ const archiveDir = join21(dir, HANDOFF_ARCHIVE_DIR);
5177
4947
  await mkdir6(archiveDir, { recursive: true });
5178
4948
  let archived = 0;
5179
4949
  for (const { name } of stale) {
5180
- const from = join23(dir, name);
5181
- let to = join23(archiveDir, name);
5182
- if (existsSync9(to)) {
4950
+ const from = join21(dir, name);
4951
+ let to = join21(archiveDir, name);
4952
+ if (existsSync8(to)) {
5183
4953
  const stem = name.replace(/\.md$/, "");
5184
4954
  let i = 2;
5185
- while (existsSync9(join23(archiveDir, `${stem}-${i}.md`)))
4955
+ while (existsSync8(join21(archiveDir, `${stem}-${i}.md`)))
5186
4956
  i++;
5187
- to = join23(archiveDir, `${stem}-${i}.md`);
4957
+ to = join21(archiveDir, `${stem}-${i}.md`);
5188
4958
  }
5189
4959
  try {
5190
- await rename2(from, to);
4960
+ await rename(from, to);
5191
4961
  archived++;
5192
4962
  } catch {
5193
4963
  }
@@ -5235,16 +5005,16 @@ var handoffCommand = {
5235
5005
 
5236
5006
  // ../plugins/session-rituals/dist/commands/vortex.js
5237
5007
  import { spawn } from "child_process";
5238
- import { constants, existsSync as existsSync12 } from "fs";
5239
- import { copyFile as copyFile2, mkdir as mkdir9, readdir as readdir16, readFile as readFile21, stat as stat8, writeFile as writeFile11 } from "fs/promises";
5240
- import { basename as basename7, dirname as dirname5, extname as extname11, join as join26, relative as relative5 } from "path";
5008
+ import { constants, existsSync as existsSync11 } from "fs";
5009
+ import { copyFile as copyFile2, mkdir as mkdir9, readdir as readdir16, readFile as readFile21, stat as stat8, writeFile as writeFile10 } from "fs/promises";
5010
+ import { basename as basename7, dirname as dirname5, extname as extname11, join as join24, relative as relative4 } from "path";
5241
5011
  import { fileURLToPath } from "url";
5242
5012
 
5243
5013
  // ../plugins/session-rituals/dist/global-setup.js
5244
5014
  import { homedir } from "os";
5245
- import { existsSync as existsSync10, readFileSync as readFileSync2 } from "fs";
5246
- import { mkdir as mkdir7, readFile as readFile19, writeFile as writeFile10 } from "fs/promises";
5247
- import { isAbsolute as isAbsolute3, join as join24 } from "path";
5015
+ import { existsSync as existsSync9, readFileSync } from "fs";
5016
+ import { mkdir as mkdir7, readFile as readFile19, writeFile as writeFile9 } from "fs/promises";
5017
+ import { isAbsolute as isAbsolute2, join as join22 } from "path";
5248
5018
  async function readFileIfExists(path) {
5249
5019
  try {
5250
5020
  return await readFile19(path, "utf8");
@@ -5255,23 +5025,23 @@ async function readFileIfExists(path) {
5255
5025
  }
5256
5026
  }
5257
5027
  function isSafeInstanceRoot(dir) {
5258
- return typeof dir === "string" && dir.trim().length > 0 && isAbsolute3(dir) && !/[\r\n`]/.test(dir) && isInstanceRoot(dir);
5028
+ return typeof dir === "string" && dir.trim().length > 0 && isAbsolute2(dir) && !/[\r\n`]/.test(dir) && isInstanceRoot(dir);
5259
5029
  }
5260
5030
  function globalClaudeDir(home = homedir()) {
5261
- return join24(home, ".claude");
5031
+ return join22(home, ".claude");
5262
5032
  }
5263
5033
  function globalSettingsPath(home = homedir()) {
5264
- return join24(globalClaudeDir(home), "settings.json");
5034
+ return join22(globalClaudeDir(home), "settings.json");
5265
5035
  }
5266
5036
  function globalStatePath(home = homedir()) {
5267
- return join24(globalClaudeDir(home), "vortex-global.json");
5037
+ return join22(globalClaudeDir(home), "vortex-global.json");
5268
5038
  }
5269
5039
  function globalMemoryPath(home = homedir()) {
5270
- return join24(globalClaudeDir(home), "CLAUDE.md");
5040
+ return join22(globalClaudeDir(home), "CLAUDE.md");
5271
5041
  }
5272
5042
  function readGlobalStateRaw(home = homedir()) {
5273
5043
  try {
5274
- const parsed = JSON.parse(readFileSync2(globalStatePath(home), "utf8"));
5044
+ const parsed = JSON.parse(readFileSync(globalStatePath(home), "utf8"));
5275
5045
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
5276
5046
  return parsed;
5277
5047
  }
@@ -5284,11 +5054,11 @@ function readGlobalInstancePointer(home = homedir()) {
5284
5054
  return typeof root === "string" && root.trim().length > 0 ? root.trim() : null;
5285
5055
  }
5286
5056
  function isInstanceRoot(dir) {
5287
- return existsSync10(join24(dir, ".agent", "vortex.json")) || existsSync10(join24(dir, "data", "_memory", "user_profile.md"));
5057
+ return existsSync9(join22(dir, ".agent", "vortex.json")) || existsSync9(join22(dir, "data", "_memory", "user_profile.md"));
5288
5058
  }
5289
5059
  function globalSettingsHasHook(home = homedir()) {
5290
5060
  try {
5291
- const settings = parseSettings(readFileSync2(globalSettingsPath(home), "utf8"));
5061
+ const settings = parseSettings(readFileSync(globalSettingsPath(home), "utf8"));
5292
5062
  const wired = (event, command) => Boolean(settings.hooks?.[event]?.some((g) => g.hooks?.some((h) => h.command === command)));
5293
5063
  return wired("SessionStart", SESSION_START_COMMAND) && wired("SessionEnd", SESSION_END_COMMAND);
5294
5064
  } catch {
@@ -5332,9 +5102,9 @@ async function applyGlobalSetup(opts) {
5332
5102
  const skipped = [];
5333
5103
  const statePath = globalStatePath(home);
5334
5104
  const settingsPath = globalSettingsPath(home);
5335
- const instSettingsPath = join24(instanceRoot, ".claude", "settings.json");
5105
+ const instSettingsPath = join22(instanceRoot, ".claude", "settings.json");
5336
5106
  const mdPath = globalMemoryPath(home);
5337
- const hadState = existsSync10(statePath);
5107
+ const hadState = existsSync9(statePath);
5338
5108
  const prevState = readGlobalStateRaw(home) ?? {};
5339
5109
  const settingsText = await readFileIfExists(settingsPath);
5340
5110
  const mergedGlobal = ensureVortexHooks(parseSettings(settingsText));
@@ -5347,27 +5117,27 @@ async function applyGlobalSetup(opts) {
5347
5117
  const { declinedAt: _wasDeclined, ...restState } = prevState;
5348
5118
  const nextState = { ...restState, instanceRoot };
5349
5119
  if (!hadState || prevState.instanceRoot !== instanceRoot || typeof prevState.declinedAt === "string") {
5350
- await writeFile10(statePath, JSON.stringify(nextState, null, 2) + "\n", "utf8");
5120
+ await writeFile9(statePath, JSON.stringify(nextState, null, 2) + "\n", "utf8");
5351
5121
  (hadState ? modified : created).push(statePath);
5352
5122
  } else {
5353
5123
  skipped.push(statePath);
5354
5124
  }
5355
5125
  if (!mergedGlobal.alreadyWired) {
5356
- await writeFile10(settingsPath, serializeSettings(mergedGlobal.settings), "utf8");
5126
+ await writeFile9(settingsPath, serializeSettings(mergedGlobal.settings), "utf8");
5357
5127
  (settingsText === null ? created : modified).push(settingsPath);
5358
5128
  } else {
5359
5129
  skipped.push(settingsPath);
5360
5130
  }
5361
5131
  if (mergedInst !== null) {
5362
5132
  if (!mergedInst.alreadyWired) {
5363
- await writeFile10(instSettingsPath, serializeSettings(mergedInst.settings), "utf8");
5133
+ await writeFile9(instSettingsPath, serializeSettings(mergedInst.settings), "utf8");
5364
5134
  modified.push(instSettingsPath);
5365
5135
  } else {
5366
5136
  skipped.push(instSettingsPath);
5367
5137
  }
5368
5138
  }
5369
5139
  if (nextMd !== mdText) {
5370
- await writeFile10(mdPath, nextMd, "utf8");
5140
+ await writeFile9(mdPath, nextMd, "utf8");
5371
5141
  (mdRead === null ? created : modified).push(mdPath);
5372
5142
  } else {
5373
5143
  skipped.push(mdPath);
@@ -5381,23 +5151,23 @@ async function recordGlobalSetupDecline(opts) {
5381
5151
  const statePath = globalStatePath(home);
5382
5152
  const prevState = readGlobalStateRaw(home) ?? {};
5383
5153
  const nextState = { ...prevState, declinedAt: now.toISOString() };
5384
- await writeFile10(statePath, JSON.stringify(nextState, null, 2) + "\n", "utf8");
5154
+ await writeFile9(statePath, JSON.stringify(nextState, null, 2) + "\n", "utf8");
5385
5155
  return statePath;
5386
5156
  }
5387
5157
 
5388
5158
  // ../plugins/session-rituals/dist/update.js
5389
5159
  import { createHash as createHash2 } from "crypto";
5390
- import { existsSync as existsSync11 } from "fs";
5160
+ import { existsSync as existsSync10 } from "fs";
5391
5161
  import { copyFile, mkdir as mkdir8, readFile as readFile20 } from "fs/promises";
5392
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join25, relative as relative4, sep as sep4 } from "path";
5162
+ import { dirname as dirname4, isAbsolute as isAbsolute3, join as join23, relative as relative3, sep as sep3 } from "path";
5393
5163
  var OWNERSHIP_SCHEMA = "vortex-ownership/2";
5394
5164
  var OWNERSHIP_SCHEMA_V1 = "vortex-ownership/1";
5395
5165
  var MANIFEST_NAME = "manifest.json";
5396
5166
  function ownershipManifestPath(ctx) {
5397
- return join25(ctx.dataDir, ".vortex", "ownership.json");
5167
+ return join23(ctx.dataDir, ".vortex", "ownership.json");
5398
5168
  }
5399
5169
  function frameworkBookkeepingPrefix(ctx) {
5400
- return toPosix(relative4(ctx.repoRoot, join25(ctx.dataDir, ".vortex"))) + "/";
5170
+ return toPosix(relative3(ctx.repoRoot, join23(ctx.dataDir, ".vortex"))) + "/";
5401
5171
  }
5402
5172
  function committableUpdatePaths(ctx, result) {
5403
5173
  const out = /* @__PURE__ */ new Set();
@@ -5408,11 +5178,11 @@ function committableUpdatePaths(ctx, result) {
5408
5178
  out.add(a.path);
5409
5179
  }
5410
5180
  }
5411
- out.add(toPosix(relative4(ctx.repoRoot, ownershipManifestPath(ctx))));
5181
+ out.add(toPosix(relative3(ctx.repoRoot, ownershipManifestPath(ctx))));
5412
5182
  return [...out];
5413
5183
  }
5414
5184
  function toPosix(p) {
5415
- return p.split(sep4).join("/");
5185
+ return p.split(sep3).join("/");
5416
5186
  }
5417
5187
  function normalizeEol(input) {
5418
5188
  const s = typeof input === "string" ? input : input.toString("utf8");
@@ -5443,24 +5213,24 @@ function templateDestRelPath(templateRelPath) {
5443
5213
  if (top === "routers")
5444
5214
  return tail;
5445
5215
  if (top === "commands")
5446
- return join25(".claude", "commands", tail);
5216
+ return join23(".claude", "commands", tail);
5447
5217
  if (top === "config")
5448
- return join25(".agent", tail);
5218
+ return join23(".agent", tail);
5449
5219
  return null;
5450
5220
  }
5451
5221
  function assertUnderRoot(rootAbs, candidateAbs) {
5452
- const rel = relative4(rootAbs, candidateAbs);
5453
- const up = ".." + sep4;
5454
- const winsensitive = sep4 === "\\";
5222
+ const rel = relative3(rootAbs, candidateAbs);
5223
+ const up = ".." + sep3;
5224
+ const winsensitive = sep3 === "\\";
5455
5225
  const cmp = winsensitive ? rel.toLowerCase() : rel;
5456
5226
  const upCmp = winsensitive ? up.toLowerCase() : up;
5457
- if (rel === ".." || cmp.startsWith(upCmp) || isAbsolute4(rel)) {
5227
+ if (rel === ".." || cmp.startsWith(upCmp) || isAbsolute3(rel)) {
5458
5228
  throw new Error(`Refusing to write outside the instance root: ${candidateAbs}`);
5459
5229
  }
5460
5230
  }
5461
5231
  async function readTemplateIndex(templatesDir) {
5462
- const indexPath = join25(templatesDir, MANIFEST_NAME);
5463
- if (!existsSync11(indexPath))
5232
+ const indexPath = join23(templatesDir, MANIFEST_NAME);
5233
+ if (!existsSync10(indexPath))
5464
5234
  return null;
5465
5235
  try {
5466
5236
  const parsed = JSON.parse(await readFile20(indexPath, "utf8"));
@@ -5484,14 +5254,14 @@ async function buildOwnershipManifest(ctx, templatesDir) {
5484
5254
  if (seenDest.has(destRel))
5485
5255
  continue;
5486
5256
  seenDest.add(destRel);
5487
- const shippedAbs = join25(templatesDir, entry.path);
5488
- if (!existsSync11(shippedAbs))
5257
+ const shippedAbs = join23(templatesDir, entry.path);
5258
+ if (!existsSync10(shippedAbs))
5489
5259
  continue;
5490
5260
  const sourceSha256 = await sha256File(shippedAbs);
5491
- const destAbs = join25(ctx.repoRoot, destRel);
5261
+ const destAbs = join23(ctx.repoRoot, destRel);
5492
5262
  assertUnderRoot(ctx.repoRoot, destAbs);
5493
5263
  let installedSha256 = null;
5494
- if (existsSync11(destAbs)) {
5264
+ if (existsSync10(destAbs)) {
5495
5265
  const onDisk = await sha256File(destAbs);
5496
5266
  installedSha256 = onDisk === sourceSha256 ? sourceSha256 : null;
5497
5267
  }
@@ -5512,14 +5282,14 @@ async function writeOwnershipManifest(ctx, templatesDir) {
5512
5282
  if (!manifest)
5513
5283
  return null;
5514
5284
  const mp = ownershipManifestPath(ctx);
5515
- await mkdir8(join25(ctx.dataDir, ".vortex"), { recursive: true });
5285
+ await mkdir8(join23(ctx.dataDir, ".vortex"), { recursive: true });
5516
5286
  await atomicWriteFile(mp, JSON.stringify(manifest, null, 2) + "\n");
5517
5287
  return { path: mp, fileCount: manifest.files.length };
5518
5288
  }
5519
5289
  async function inspectOwnership(ctx, templatesDir) {
5520
5290
  let own = await readOwnershipManifest(ctx);
5521
5291
  if (!own) {
5522
- const malformed = existsSync11(ownershipManifestPath(ctx));
5292
+ const malformed = existsSync10(ownershipManifestPath(ctx));
5523
5293
  return { present: false, malformed, total: 0, pristine: 0, modified: 0, missing: 0, unmanaged: 0 };
5524
5294
  }
5525
5295
  if (templatesDir && own.schema === OWNERSHIP_SCHEMA_V1) {
@@ -5534,8 +5304,8 @@ async function inspectOwnership(ctx, templatesDir) {
5534
5304
  unmanaged++;
5535
5305
  continue;
5536
5306
  }
5537
- const abs = join25(ctx.repoRoot, e.path);
5538
- if (!existsSync11(abs)) {
5307
+ const abs = join23(ctx.repoRoot, e.path);
5308
+ if (!existsSync10(abs)) {
5539
5309
  missing++;
5540
5310
  continue;
5541
5311
  }
@@ -5552,7 +5322,7 @@ async function inspectOwnership(ctx, templatesDir) {
5552
5322
  }
5553
5323
  async function repairOwnershipManifest(ctx, templatesDir) {
5554
5324
  const mp = ownershipManifestPath(ctx);
5555
- if (existsSync11(mp)) {
5325
+ if (existsSync10(mp)) {
5556
5326
  const existing = await readOwnershipManifest(ctx);
5557
5327
  return existing ? { status: "already-present", path: mp, fileCount: existing.files.length } : { status: "unreadable", path: mp };
5558
5328
  }
@@ -5561,7 +5331,7 @@ async function repairOwnershipManifest(ctx, templatesDir) {
5561
5331
  }
5562
5332
  async function readOwnershipManifest(ctx) {
5563
5333
  const mp = ownershipManifestPath(ctx);
5564
- if (!existsSync11(mp))
5334
+ if (!existsSync10(mp))
5565
5335
  return null;
5566
5336
  try {
5567
5337
  const parsed = JSON.parse(await readFile20(mp, "utf8"));
@@ -5585,13 +5355,13 @@ async function migrateOwnershipToV2(own, ctx, templatesDir) {
5585
5355
  if (index) {
5586
5356
  for (const idx of index.files) {
5587
5357
  if (templateDestRelPath(idx.path))
5588
- tmplAbsById.set(idx.templateId, join25(templatesDir, idx.path));
5358
+ tmplAbsById.set(idx.templateId, join23(templatesDir, idx.path));
5589
5359
  }
5590
5360
  }
5591
5361
  const files = [];
5592
5362
  for (const e of own.files) {
5593
5363
  const tmplAbs = tmplAbsById.get(e.templateId);
5594
- if (!tmplAbs || !existsSync11(tmplAbs)) {
5364
+ if (!tmplAbs || !existsSync10(tmplAbs)) {
5595
5365
  files.push(e);
5596
5366
  continue;
5597
5367
  }
@@ -5602,10 +5372,10 @@ async function migrateOwnershipToV2(own, ctx, templatesDir) {
5602
5372
  continue;
5603
5373
  }
5604
5374
  const srcUnchanged = matchesLegacyRawHash(e.sourceSha256, tmplBuf);
5605
- const destAbs = join25(ctx.repoRoot, e.path);
5375
+ const destAbs = join23(ctx.repoRoot, e.path);
5606
5376
  let diskPristine = false;
5607
5377
  let normDisk = null;
5608
- if (existsSync11(destAbs)) {
5378
+ if (existsSync10(destAbs)) {
5609
5379
  try {
5610
5380
  const diskBuf = await readFile20(destAbs);
5611
5381
  normDisk = sha256(diskBuf);
@@ -5674,15 +5444,15 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5674
5444
  continue;
5675
5445
  seenDest.add(destRel);
5676
5446
  seenTemplateIds.add(idx.templateId);
5677
- const shippedAbs = join25(templatesDir, idx.path);
5678
- if (!existsSync11(shippedAbs))
5447
+ const shippedAbs = join23(templatesDir, idx.path);
5448
+ if (!existsSync10(shippedAbs))
5679
5449
  continue;
5680
5450
  const newSource = await sha256File(shippedAbs);
5681
- const destAbs = join25(ctx.repoRoot, destRel);
5451
+ const destAbs = join23(ctx.repoRoot, destRel);
5682
5452
  assertUnderRoot(ctx.repoRoot, destAbs);
5683
5453
  const path = toPosix(destRel);
5684
5454
  const templateId = idx.templateId;
5685
- const exists = existsSync11(destAbs);
5455
+ const exists = existsSync10(destAbs);
5686
5456
  const curHash = exists ? await sha256File(destAbs) : null;
5687
5457
  const prior = ownByTemplateId.get(templateId);
5688
5458
  if (adopt.has(path)) {
@@ -5725,7 +5495,7 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5725
5495
  templateId,
5726
5496
  action: "conflict",
5727
5497
  detail: "a file already exists in a newly-shipped slot \u2014 wrote .new",
5728
- newFilePath: toPosix(relative4(ctx.repoRoot, destAbs + ".new"))
5498
+ newFilePath: toPosix(relative3(ctx.repoRoot, destAbs + ".new"))
5729
5499
  },
5730
5500
  shippedAbs,
5731
5501
  destAbs,
@@ -5783,7 +5553,7 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5783
5553
  templateId,
5784
5554
  action: "conflict",
5785
5555
  detail: "you edited this and the template changed \u2014 wrote .new; your file is untouched",
5786
- newFilePath: toPosix(relative4(ctx.repoRoot, destAbs + ".new"))
5556
+ newFilePath: toPosix(relative3(ctx.repoRoot, destAbs + ".new"))
5787
5557
  },
5788
5558
  shippedAbs,
5789
5559
  destAbs,
@@ -5805,18 +5575,18 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5805
5575
  const allOps = [...ops, ...orphanOps];
5806
5576
  const appliedActions = [];
5807
5577
  const finalEntries = [];
5808
- const backupRoot = join25(ctx.dataDir, ".vortex", "backups", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
5578
+ const backupRoot = join23(ctx.dataDir, ".vortex", "backups", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
5809
5579
  let applyError = false;
5810
5580
  const writeDotNew = async (destAbs, content) => {
5811
5581
  const newPath = destAbs + ".new";
5812
- if (existsSync11(newPath)) {
5582
+ if (existsSync10(newPath)) {
5813
5583
  if (await readFile20(newPath, "utf8") === content)
5814
5584
  return void 0;
5815
- const backupAbs = join25(backupRoot, toPosix(relative4(ctx.repoRoot, newPath)));
5585
+ const backupAbs = join23(backupRoot, toPosix(relative3(ctx.repoRoot, newPath)));
5816
5586
  await mkdir8(dirname4(backupAbs), { recursive: true });
5817
5587
  await copyFile(newPath, backupAbs);
5818
5588
  await atomicWriteFile(newPath, content);
5819
- return toPosix(relative4(ctx.repoRoot, backupAbs));
5589
+ return toPosix(relative3(ctx.repoRoot, backupAbs));
5820
5590
  }
5821
5591
  await atomicWriteFile(newPath, content);
5822
5592
  return void 0;
@@ -5831,15 +5601,15 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5831
5601
  const newSource = op.entry ? op.entry.sourceSha256 : sha256(content);
5832
5602
  if (action.action === "replace") {
5833
5603
  const prior = ownByTemplateId.get(action.templateId);
5834
- if (!existsSync11(destAbs)) {
5604
+ if (!existsSync10(destAbs)) {
5835
5605
  await mkdir8(dirname4(destAbs), { recursive: true });
5836
5606
  await atomicWriteFile(destAbs, content);
5837
5607
  } else if (await sha256File(destAbs) === (prior?.installedSha256 ?? null)) {
5838
- const backupAbs = join25(backupRoot, action.path);
5608
+ const backupAbs = join23(backupRoot, action.path);
5839
5609
  await mkdir8(dirname4(backupAbs), { recursive: true });
5840
5610
  await copyFile(destAbs, backupAbs);
5841
5611
  await atomicWriteFile(destAbs, content);
5842
- action = { ...action, backupPath: toPosix(relative4(ctx.repoRoot, backupAbs)) };
5612
+ action = { ...action, backupPath: toPosix(relative3(ctx.repoRoot, backupAbs)) };
5843
5613
  } else {
5844
5614
  const backupPath = await writeDotNew(destAbs, content);
5845
5615
  action = {
@@ -5847,20 +5617,20 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5847
5617
  templateId: action.templateId,
5848
5618
  action: "conflict",
5849
5619
  detail: "file changed since planning \u2014 wrote .new instead of overwriting",
5850
- newFilePath: toPosix(relative4(ctx.repoRoot, destAbs + ".new")),
5620
+ newFilePath: toPosix(relative3(ctx.repoRoot, destAbs + ".new")),
5851
5621
  ...backupPath ? { backupPath } : {}
5852
5622
  };
5853
5623
  entry = { templateId: action.templateId, path: action.path, sourceSha256: newSource, installedSha256: prior?.installedSha256 ?? null };
5854
5624
  }
5855
5625
  } else if (action.action === "restore" || action.action === "install") {
5856
- if (existsSync11(destAbs) && await sha256File(destAbs) !== newSource) {
5626
+ if (existsSync10(destAbs) && await sha256File(destAbs) !== newSource) {
5857
5627
  const backupPath = await writeDotNew(destAbs, content);
5858
5628
  action = {
5859
5629
  path: action.path,
5860
5630
  templateId: action.templateId,
5861
5631
  action: "conflict",
5862
5632
  detail: "target appeared since planning \u2014 wrote .new instead of overwriting",
5863
- newFilePath: toPosix(relative4(ctx.repoRoot, destAbs + ".new")),
5633
+ newFilePath: toPosix(relative3(ctx.repoRoot, destAbs + ".new")),
5864
5634
  ...backupPath ? { backupPath } : {}
5865
5635
  };
5866
5636
  entry = { templateId: action.templateId, path: action.path, sourceSha256: newSource, installedSha256: null };
@@ -5874,11 +5644,11 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5874
5644
  if (backupPath)
5875
5645
  action = { ...action, backupPath };
5876
5646
  } else if (action.action === "adopt") {
5877
- if (existsSync11(destAbs)) {
5878
- const backupAbs = join25(backupRoot, action.path);
5647
+ if (existsSync10(destAbs)) {
5648
+ const backupAbs = join23(backupRoot, action.path);
5879
5649
  await mkdir8(dirname4(backupAbs), { recursive: true });
5880
5650
  await copyFile(destAbs, backupAbs);
5881
- action = { ...action, backupPath: toPosix(relative4(ctx.repoRoot, backupAbs)) };
5651
+ action = { ...action, backupPath: toPosix(relative3(ctx.repoRoot, backupAbs)) };
5882
5652
  }
5883
5653
  await mkdir8(dirname4(destAbs), { recursive: true });
5884
5654
  await atomicWriteFile(destAbs, content);
@@ -5907,7 +5677,7 @@ async function runTemplatesUpdate(ctx, templatesDir, options = {}) {
5907
5677
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5908
5678
  files: newEntries
5909
5679
  };
5910
- await mkdir8(join25(ctx.dataDir, ".vortex"), { recursive: true });
5680
+ await mkdir8(join23(ctx.dataDir, ".vortex"), { recursive: true });
5911
5681
  await atomicWriteFile(ownershipManifestPath(ctx), JSON.stringify(manifest, null, 2) + "\n");
5912
5682
  }
5913
5683
  const summary = summarize(appliedActions);
@@ -6163,15 +5933,15 @@ var DEFAULT_INIT_TASK = "Setting up my VortEX instance";
6163
5933
  function resolveTemplatesDir() {
6164
5934
  const here = dirname5(fileURLToPath(import.meta.url));
6165
5935
  const candidates = [
6166
- join26(here, "..", "..", "templates"),
5936
+ join24(here, "..", "..", "templates"),
6167
5937
  // session-rituals: dist/commands -> templates
6168
- join26(here, "..", "templates"),
5938
+ join24(here, "..", "templates"),
6169
5939
  // base aggregate: dist -> templates
6170
- join26(here, "templates")
5940
+ join24(here, "templates")
6171
5941
  // defensive: alongside the bundle
6172
5942
  ];
6173
5943
  for (const c of candidates) {
6174
- if (existsSync12(join26(c, "commands")) || existsSync12(join26(c, "routers")))
5944
+ if (existsSync11(join24(c, "commands")) || existsSync11(join24(c, "routers")))
6175
5945
  return c;
6176
5946
  }
6177
5947
  return null;
@@ -6179,19 +5949,19 @@ function resolveTemplatesDir() {
6179
5949
  async function installCommandTemplates(repoRoot, templatesDir) {
6180
5950
  if (!templatesDir)
6181
5951
  return [];
6182
- const commandsDir = join26(templatesDir, "commands");
6183
- if (!existsSync12(commandsDir))
5952
+ const commandsDir = join24(templatesDir, "commands");
5953
+ if (!existsSync11(commandsDir))
6184
5954
  return [];
6185
- const destDir = join26(repoRoot, ".claude", "commands");
5955
+ const destDir = join24(repoRoot, ".claude", "commands");
6186
5956
  await mkdir9(destDir, { recursive: true });
6187
5957
  const written = [];
6188
5958
  for (const name of await readdir16(commandsDir)) {
6189
5959
  if (!name.endsWith(".md"))
6190
5960
  continue;
6191
- const dest = join26(destDir, name);
6192
- if (existsSync12(dest))
5961
+ const dest = join24(destDir, name);
5962
+ if (existsSync11(dest))
6193
5963
  continue;
6194
- await copyFile2(join26(commandsDir, name), dest);
5964
+ await copyFile2(join24(commandsDir, name), dest);
6195
5965
  written.push(dest);
6196
5966
  }
6197
5967
  return written;
@@ -6206,16 +5976,16 @@ var ROUTER_FILES = [
6206
5976
  async function installRouterTemplates(repoRoot, templatesDir) {
6207
5977
  if (!templatesDir)
6208
5978
  return [];
6209
- const routersDir = join26(templatesDir, "routers");
6210
- if (!existsSync12(routersDir))
5979
+ const routersDir = join24(templatesDir, "routers");
5980
+ if (!existsSync11(routersDir))
6211
5981
  return [];
6212
5982
  const written = [];
6213
5983
  for (const name of ROUTER_FILES) {
6214
- const src = join26(routersDir, name);
6215
- if (!existsSync12(src))
5984
+ const src = join24(routersDir, name);
5985
+ if (!existsSync11(src))
6216
5986
  continue;
6217
- const dest = join26(repoRoot, name);
6218
- if (existsSync12(dest))
5987
+ const dest = join24(repoRoot, name);
5988
+ if (existsSync11(dest))
6219
5989
  continue;
6220
5990
  await copyFile2(src, dest);
6221
5991
  written.push(dest);
@@ -6224,15 +5994,15 @@ async function installRouterTemplates(repoRoot, templatesDir) {
6224
5994
  }
6225
5995
  async function seedInstanceConfig(repoRoot, templatesDir) {
6226
5996
  const written = [];
6227
- const agentDir = join26(repoRoot, ".agent");
6228
- const vortexJson = join26(agentDir, "vortex.json");
6229
- if (!existsSync12(vortexJson)) {
5997
+ const agentDir = join24(repoRoot, ".agent");
5998
+ const vortexJson = join24(agentDir, "vortex.json");
5999
+ if (!existsSync11(vortexJson)) {
6230
6000
  await mkdir9(agentDir, { recursive: true });
6231
- const tmpl = templatesDir ? join26(templatesDir, "config", "vortex.json") : null;
6232
- if (tmpl && existsSync12(tmpl)) {
6001
+ const tmpl = templatesDir ? join24(templatesDir, "config", "vortex.json") : null;
6002
+ if (tmpl && existsSync11(tmpl)) {
6233
6003
  await copyFile2(tmpl, vortexJson);
6234
6004
  } else {
6235
- await writeFile11(vortexJson, JSON.stringify({
6005
+ await writeFile10(vortexJson, JSON.stringify({
6236
6006
  autoRecord: {
6237
6007
  sessionStart: true,
6238
6008
  worklog: true,
@@ -6247,9 +6017,9 @@ async function seedInstanceConfig(repoRoot, templatesDir) {
6247
6017
  }
6248
6018
  written.push(vortexJson);
6249
6019
  }
6250
- const pkgPath = join26(repoRoot, "package.json");
6251
- if (!existsSync12(pkgPath)) {
6252
- await writeFile11(pkgPath, JSON.stringify({
6020
+ const pkgPath = join24(repoRoot, "package.json");
6021
+ if (!existsSync11(pkgPath)) {
6022
+ await writeFile10(pkgPath, JSON.stringify({
6253
6023
  name: "vortex-instance",
6254
6024
  version: "0.0.0",
6255
6025
  private: true,
@@ -6266,8 +6036,8 @@ async function runInit(input, tokens) {
6266
6036
  const templatesDir = resolveTemplatesDir();
6267
6037
  const requiredDirs = ["_memory", "worklog", "decision-log", "hubs", "inbox", "runbooks"];
6268
6038
  for (const d2 of requiredDirs) {
6269
- const p = join26(dataDir, d2);
6270
- if (!existsSync12(p))
6039
+ const p = join24(dataDir, d2);
6040
+ if (!existsSync11(p))
6271
6041
  await mkdir9(p, { recursive: true });
6272
6042
  }
6273
6043
  const scaffolded = [];
@@ -6279,8 +6049,8 @@ async function runInit(input, tokens) {
6279
6049
  scaffolded.push(...await seedInstanceConfig(repoRoot, templatesDir));
6280
6050
  } catch {
6281
6051
  }
6282
- const profilePath = join26(dataDir, "_memory", "user_profile.md");
6283
- if (existsSync12(profilePath) && !args.force) {
6052
+ const profilePath = join24(dataDir, "_memory", "user_profile.md");
6053
+ if (existsSync11(profilePath) && !args.force) {
6284
6054
  const manifestNotes = [];
6285
6055
  try {
6286
6056
  const m2 = await writeOwnershipManifest(input.context, templatesDir);
@@ -6341,24 +6111,24 @@ async function runInit(input, tokens) {
6341
6111
  const name = args.name.trim();
6342
6112
  const role = args.role.trim();
6343
6113
  const task = args.task?.trim() || DEFAULT_INIT_TASK;
6344
- await writeFile11(profilePath, renderUserProfile(name, role, task, today2), "utf8");
6114
+ await writeFile10(profilePath, renderUserProfile(name, role, task, today2), "utf8");
6345
6115
  created.push(profilePath);
6346
6116
  const [year, month] = today2.split("-");
6347
- const worklogDir = join26(dataDir, "worklog", year, month);
6117
+ const worklogDir = join24(dataDir, "worklog", year, month);
6348
6118
  await mkdir9(worklogDir, { recursive: true });
6349
- const worklogPath = join26(worklogDir, `${today2}-vortex-init.md`);
6350
- await writeFile11(worklogPath, renderFirstWorklog(name, role, task, today2), "utf8");
6119
+ const worklogPath = join24(worklogDir, `${today2}-vortex-init.md`);
6120
+ await writeFile10(worklogPath, renderFirstWorklog(name, role, task, today2), "utf8");
6351
6121
  created.push(worklogPath);
6352
6122
  const hookNotes = [];
6353
6123
  try {
6354
- const settingsPath = join26(input.context.repoRoot, ".claude", "settings.json");
6355
- const existingText = existsSync12(settingsPath) ? await readFile21(settingsPath, "utf8") : null;
6356
- const { settings, added, alreadyWired } = ensureVortexHooks(parseSettings(existingText));
6124
+ const settingsPath = join24(input.context.repoRoot, ".claude", "settings.json");
6125
+ const existingText = existsSync11(settingsPath) ? await readFile21(settingsPath, "utf8") : null;
6126
+ const { settings, added, alreadyWired } = ensureVortexHooks(parseSettings(existingText), { guard: true });
6357
6127
  if (!alreadyWired) {
6358
- await mkdir9(join26(input.context.repoRoot, ".claude"), { recursive: true });
6359
- await writeFile11(settingsPath, serializeSettings(settings), "utf8");
6128
+ await mkdir9(join24(input.context.repoRoot, ".claude"), { recursive: true });
6129
+ await writeFile10(settingsPath, serializeSettings(settings), "utf8");
6360
6130
  created.push(settingsPath);
6361
- hookNotes.push(`Wired ${added.join(" + ")} hook(s) into .claude/settings.json \u2014 the VortEX boot report runs automatically at session start.`);
6131
+ hookNotes.push(`Wired ${added.join(" + ")} hook(s) into .claude/settings.json \u2014 the VortEX boot report runs automatically at session start` + (added.includes("PreToolUse") ? ", and the write guard now denies literal control bytes in file writes (remove the PreToolUse group to opt out)." : "."));
6362
6132
  } else {
6363
6133
  hookNotes.push("Session hooks already wired in .claude/settings.json.");
6364
6134
  }
@@ -6476,10 +6246,36 @@ async function runUpdate(input, tokens) {
6476
6246
  const dryRun = tokens.includes("--dry-run");
6477
6247
  const adopt = parseAdoptArgs(tokens);
6478
6248
  const templatesDir = resolveTemplatesDir();
6479
- const result = await runTemplatesUpdate(input.context, templatesDir, {
6249
+ let result = await runTemplatesUpdate(input.context, templatesDir, {
6480
6250
  dryRun,
6481
6251
  adopt: adopt.size > 0 ? adopt : void 0
6482
6252
  });
6253
+ if (!dryRun) {
6254
+ try {
6255
+ const settingsPath = join24(input.context.repoRoot, ".claude", "settings.json");
6256
+ const existingText = existsSync11(settingsPath) ? await readFile21(settingsPath, "utf8") : null;
6257
+ const { settings, added, alreadyWired } = ensureVortexHooks(parseSettings(existingText), { guard: true });
6258
+ if (!alreadyWired) {
6259
+ await mkdir9(join24(input.context.repoRoot, ".claude"), { recursive: true });
6260
+ await writeFile10(settingsPath, serializeSettings(settings), "utf8");
6261
+ result = {
6262
+ ...result,
6263
+ nextActions: [
6264
+ ...result.nextActions,
6265
+ `Wired missing ${added.join(" + ")} hook(s) into .claude/settings.json` + (added.includes("PreToolUse") ? " \u2014 the write guard now denies literal control bytes in file writes (remove the PreToolUse group to opt out)." : ".")
6266
+ ]
6267
+ };
6268
+ }
6269
+ } catch (e) {
6270
+ result = {
6271
+ ...result,
6272
+ nextActions: [
6273
+ ...result.nextActions,
6274
+ `\u26A0\uFE0F Could not verify/wire session hooks: ${e.message}`
6275
+ ]
6276
+ };
6277
+ }
6278
+ }
6483
6279
  if (dryRun || result.status === "no-manifest" || result.status === "no-templates")
6484
6280
  return result;
6485
6281
  if (!loadVortexConfig(input.context).autoRecord.commitFrameworkChanges)
@@ -6619,18 +6415,18 @@ var COUNT_KEY_TO_DIR = {
6619
6415
  };
6620
6416
  async function runStatus(input) {
6621
6417
  const { dataDir } = input.context;
6622
- const profilePath = join26(dataDir, "_memory", "user_profile.md");
6623
- const initialized = existsSync12(profilePath);
6418
+ const profilePath = join24(dataDir, "_memory", "user_profile.md");
6419
+ const initialized = existsSync11(profilePath);
6624
6420
  const counts = {
6625
- memory: await safeCount(join26(dataDir, "_memory"), false),
6626
- worklog: await safeCount(join26(dataDir, "worklog"), true),
6627
- decisionLog: await safeCount(join26(dataDir, "decision-log"), false),
6628
- runbooks: await safeCount(join26(dataDir, "runbooks"), false),
6629
- hubs: await safeCount(join26(dataDir, "hubs"), false)
6421
+ memory: await safeCount(join24(dataDir, "_memory"), false),
6422
+ worklog: await safeCount(join24(dataDir, "worklog"), true),
6423
+ decisionLog: await safeCount(join24(dataDir, "decision-log"), false),
6424
+ runbooks: await safeCount(join24(dataDir, "runbooks"), false),
6425
+ hubs: await safeCount(join24(dataDir, "hubs"), false)
6630
6426
  };
6631
6427
  let latestWorklog;
6632
6428
  try {
6633
- const store = new WorklogStore(join26(dataDir, "worklog"));
6429
+ const store = new WorklogStore(join24(dataDir, "worklog"));
6634
6430
  const latest = await store.getLatest();
6635
6431
  if (latest) {
6636
6432
  latestWorklog = {
@@ -6657,8 +6453,8 @@ async function runStatus(input) {
6657
6453
  for (const [key, count] of Object.entries(counts)) {
6658
6454
  if (count === 0) {
6659
6455
  const dirName = COUNT_KEY_TO_DIR[key];
6660
- const dirPath = join26(dataDir, dirName);
6661
- missing.push(existsSync12(dirPath) ? `${dirName}/ is empty` : `${dirName}/ does not exist`);
6456
+ const dirPath = join24(dataDir, dirName);
6457
+ missing.push(existsSync11(dirPath) ? `${dirName}/ is empty` : `${dirName}/ does not exist`);
6662
6458
  }
6663
6459
  }
6664
6460
  const nextActions = [];
@@ -6693,7 +6489,7 @@ function extractProfile(body) {
6693
6489
  return out;
6694
6490
  }
6695
6491
  async function safeCount(dir, recursive) {
6696
- if (!existsSync12(dir))
6492
+ if (!existsSync11(dir))
6697
6493
  return 0;
6698
6494
  try {
6699
6495
  return await countMarkdown2(dir, recursive);
@@ -6717,7 +6513,7 @@ async function countMarkdown2(dir, recursive) {
6717
6513
  } else if (e.isDirectory() && recursive) {
6718
6514
  if (e.name.startsWith(".") || e.name.startsWith("_"))
6719
6515
  continue;
6720
- total += await countMarkdown2(join26(dir, e.name), recursive);
6516
+ total += await countMarkdown2(join24(dir, e.name), recursive);
6721
6517
  }
6722
6518
  }
6723
6519
  return total;
@@ -6852,7 +6648,7 @@ async function runImport(input, tokens) {
6852
6648
  ]
6853
6649
  };
6854
6650
  }
6855
- if (!existsSync12(args.from)) {
6651
+ if (!existsSync11(args.from)) {
6856
6652
  return {
6857
6653
  subcommand: "import",
6858
6654
  status: "source-missing",
@@ -6886,8 +6682,8 @@ async function runImport(input, tokens) {
6886
6682
  const systemDirsCreated = [];
6887
6683
  if (!args.dryRun) {
6888
6684
  for (const d2 of systemDirs) {
6889
- const p = join26(dataDir, d2);
6890
- if (!existsSync12(p)) {
6685
+ const p = join24(dataDir, d2);
6686
+ if (!existsSync11(p)) {
6891
6687
  await mkdir9(p, { recursive: true });
6892
6688
  systemDirsCreated.push(d2);
6893
6689
  }
@@ -6983,7 +6779,7 @@ async function runImport(input, tokens) {
6983
6779
  async function walkAndImport(rootSource, currentDir, dataDir, dryRun, stats) {
6984
6780
  const entries = await readdir16(currentDir, { withFileTypes: true });
6985
6781
  for (const e of entries) {
6986
- const sourcePath = join26(currentDir, e.name);
6782
+ const sourcePath = join24(currentDir, e.name);
6987
6783
  if (e.isDirectory()) {
6988
6784
  if (IMPORT_SKIP_DIRS.has(e.name.toLowerCase()))
6989
6785
  continue;
@@ -7024,7 +6820,7 @@ async function walkAndImport(rootSource, currentDir, dataDir, dryRun, stats) {
7024
6820
  body: parsed.body
7025
6821
  });
7026
6822
  try {
7027
- await writeFile11(targetPath, out, { encoding: "utf8", flag: "wx" });
6823
+ await writeFile10(targetPath, out, { encoding: "utf8", flag: "wx" });
7028
6824
  stats.copied++;
7029
6825
  } catch (e2) {
7030
6826
  if (e2.code === "EEXIST") {
@@ -7057,7 +6853,7 @@ async function importAttachment(sourcePath, relPath, filename, dataDir, dryRun,
7057
6853
  stats.importedExtensions.add(ext);
7058
6854
  if (dryRun)
7059
6855
  return;
7060
- const targetPath = join26(dataDir, relPath);
6856
+ const targetPath = join24(dataDir, relPath);
7061
6857
  await mkdir9(dirname5(targetPath), { recursive: true });
7062
6858
  try {
7063
6859
  await copyFile2(sourcePath, targetPath, constants.COPYFILE_EXCL);
@@ -7121,27 +6917,27 @@ function computeTargetPath(category, sourcePath, rootSource, dataDir, filename)
7121
6917
  const mdName = withMdExtension(filename);
7122
6918
  if (category === "preserved") {
7123
6919
  const relPath = withMdExtension(sourcePath.substring(rootSource.length).replace(/^[/\\]/, ""));
7124
- return join26(dataDir, relPath);
6920
+ return join24(dataDir, relPath);
7125
6921
  }
7126
6922
  if (category === "worklog") {
7127
6923
  const match = mdName.match(/^(\d{4})-(\d{2})-/);
7128
6924
  if (match) {
7129
- return join26(dataDir, "worklog", match[1], match[2], mdName);
6925
+ return join24(dataDir, "worklog", match[1], match[2], mdName);
7130
6926
  }
7131
6927
  const d2 = /* @__PURE__ */ new Date();
7132
6928
  const y2 = String(d2.getFullYear());
7133
6929
  const m2 = String(d2.getMonth() + 1).padStart(2, "0");
7134
- return join26(dataDir, "worklog", y2, m2, mdName);
6930
+ return join24(dataDir, "worklog", y2, m2, mdName);
7135
6931
  }
7136
6932
  if (category === "decisionLog")
7137
- return join26(dataDir, "decision-log", mdName);
6933
+ return join24(dataDir, "decision-log", mdName);
7138
6934
  if (category === "runbooks")
7139
- return join26(dataDir, "runbooks", mdName);
6935
+ return join24(dataDir, "runbooks", mdName);
7140
6936
  if (category === "hubs")
7141
- return join26(dataDir, "hubs", mdName);
6937
+ return join24(dataDir, "hubs", mdName);
7142
6938
  if (category === "memory")
7143
- return join26(dataDir, "_memory", mdName);
7144
- return join26(dataDir, mdName);
6939
+ return join24(dataDir, "_memory", mdName);
6940
+ return join24(dataDir, mdName);
7145
6941
  }
7146
6942
  function withMdExtension(name) {
7147
6943
  const ext = extname11(name);
@@ -7288,7 +7084,7 @@ async function checkControlBytes(dataDir) {
7288
7084
  return;
7289
7085
  }
7290
7086
  for (const e of entries) {
7291
- const p = join26(dir, e.name);
7087
+ const p = join24(dir, e.name);
7292
7088
  if (e.isDirectory()) {
7293
7089
  if (SKIP_DIRS.has(e.name))
7294
7090
  continue;
@@ -7299,7 +7095,7 @@ async function checkControlBytes(dataDir) {
7299
7095
  for (let i = 0; i < buf.length; i++) {
7300
7096
  const x2 = buf[i];
7301
7097
  if (x2 < 32 && x2 !== 9 && x2 !== 10 && x2 !== 13) {
7302
- offenders.push(`${relative5(dataDir, p).replace(/\\/g, "/")} @ byte ${i}`);
7098
+ offenders.push(`${relative4(dataDir, p).replace(/\\/g, "/")} @ byte ${i}`);
7303
7099
  break;
7304
7100
  }
7305
7101
  }
@@ -7322,7 +7118,7 @@ async function checkControlBytes(dataDir) {
7322
7118
  };
7323
7119
  }
7324
7120
  function checkSystemDirs(dataDir) {
7325
- const missing = DOCTOR_SYSTEM_DIRS.filter((d2) => !existsSync12(join26(dataDir, d2)));
7121
+ const missing = DOCTOR_SYSTEM_DIRS.filter((d2) => !existsSync11(join24(dataDir, d2)));
7326
7122
  if (missing.length === 0) {
7327
7123
  return {
7328
7124
  id: "system-dirs",
@@ -7338,8 +7134,8 @@ function checkSystemDirs(dataDir) {
7338
7134
  };
7339
7135
  }
7340
7136
  function checkUserProfile(dataDir) {
7341
- const profilePath = join26(dataDir, "_memory", "user_profile.md");
7342
- if (existsSync12(profilePath)) {
7137
+ const profilePath = join24(dataDir, "_memory", "user_profile.md");
7138
+ if (existsSync11(profilePath)) {
7343
7139
  return {
7344
7140
  id: "user-profile",
7345
7141
  label: "user_profile.md exists",
@@ -7356,11 +7152,11 @@ function checkUserProfile(dataDir) {
7356
7152
  async function checkIndexes(dataDir) {
7357
7153
  const missing = [];
7358
7154
  for (const d2 of DOCTOR_SYSTEM_DIRS) {
7359
- const dirPath = join26(dataDir, d2);
7360
- if (!existsSync12(dirPath))
7155
+ const dirPath = join24(dataDir, d2);
7156
+ if (!existsSync11(dirPath))
7361
7157
  continue;
7362
- const indexPath = join26(dirPath, "_INDEX.md");
7363
- if (!existsSync12(indexPath))
7158
+ const indexPath = join24(dirPath, "_INDEX.md");
7159
+ if (!existsSync11(indexPath))
7364
7160
  missing.push(`${d2}/_INDEX.md`);
7365
7161
  }
7366
7162
  if (missing.length === 0) {
@@ -7393,7 +7189,7 @@ async function collectAttachmentExtensions(dataDir) {
7393
7189
  if (e.name.startsWith(".") || e.name === "_session-archive" || e.name === "node_modules") {
7394
7190
  continue;
7395
7191
  }
7396
- stack.push(join26(current, e.name));
7192
+ stack.push(join24(current, e.name));
7397
7193
  } else if (e.isFile()) {
7398
7194
  const ext = extname11(e.name);
7399
7195
  if (ext && ext.toLowerCase() !== ".md")
@@ -7490,8 +7286,8 @@ async function checkFrontmatterLint(dataDir, additionalExtensions = []) {
7490
7286
  }
7491
7287
  }
7492
7288
  async function checkRunbookAging(dataDir) {
7493
- const runbooksDir = join26(dataDir, "runbooks");
7494
- if (!existsSync12(runbooksDir)) {
7289
+ const runbooksDir = join24(dataDir, "runbooks");
7290
+ if (!existsSync11(runbooksDir)) {
7495
7291
  return {
7496
7292
  id: "runbook-aging",
7497
7293
  label: `runbooks tested within ${RUNBOOK_AGING_DAYS} days`,
@@ -7511,7 +7307,7 @@ async function checkRunbookAging(dataDir) {
7511
7307
  continue;
7512
7308
  }
7513
7309
  total++;
7514
- const filePath = join26(runbooksDir, e.name);
7310
+ const filePath = join24(runbooksDir, e.name);
7515
7311
  const raw = await readFile21(filePath, "utf8");
7516
7312
  const { frontmatter } = parseFrontmatter(raw);
7517
7313
  if (!frontmatter.last_tested) {
@@ -7574,8 +7370,8 @@ function checkNodeVersion() {
7574
7370
  };
7575
7371
  }
7576
7372
  async function checkGitRemote(repoRoot) {
7577
- const gitConfig = join26(repoRoot, ".git", "config");
7578
- if (!existsSync12(gitConfig)) {
7373
+ const gitConfig = join24(repoRoot, ".git", "config");
7374
+ if (!existsSync11(gitConfig)) {
7579
7375
  return {
7580
7376
  id: "git-remote",
7581
7377
  label: "git remote for sync",
@@ -7614,11 +7410,11 @@ async function detectExternalFolders(excludePath) {
7614
7410
  if (!home)
7615
7411
  return void 0;
7616
7412
  const candidates = [
7617
- join26(home, "Documents", "obsidian-vault"),
7618
- join26(home, "Documents", "notes"),
7619
- join26(home, "Documents", "Notebook"),
7620
- join26(home, "notes"),
7621
- join26(home, "Notes")
7413
+ join24(home, "Documents", "obsidian-vault"),
7414
+ join24(home, "Documents", "notes"),
7415
+ join24(home, "Documents", "Notebook"),
7416
+ join24(home, "notes"),
7417
+ join24(home, "Notes")
7622
7418
  ];
7623
7419
  const excludeNorm = excludePath.replace(/[/\\]+$/, "");
7624
7420
  const found = [];
@@ -7627,7 +7423,7 @@ async function detectExternalFolders(excludePath) {
7627
7423
  if (candNorm === excludeNorm || candNorm.startsWith(excludeNorm + "/") || candNorm.startsWith(excludeNorm + "\\") || excludeNorm.startsWith(candNorm + "/") || excludeNorm.startsWith(candNorm + "\\")) {
7628
7424
  continue;
7629
7425
  }
7630
- if (!existsSync12(candidate))
7426
+ if (!existsSync11(candidate))
7631
7427
  continue;
7632
7428
  let mdCount = 0;
7633
7429
  try {
@@ -7751,7 +7547,7 @@ async function runSync(input, tokens) {
7751
7547
  var SYNC_TAIL_LENGTH = 1e3;
7752
7548
  async function runShellCommand(cmd, cmdArgs, cwd) {
7753
7549
  const start = Date.now();
7754
- return new Promise((resolve5) => {
7550
+ return new Promise((resolve3) => {
7755
7551
  let stdout = "";
7756
7552
  let stderr = "";
7757
7553
  const child = spawn(cmd, [...cmdArgs], { cwd, shell: true });
@@ -7762,7 +7558,7 @@ async function runShellCommand(cmd, cmdArgs, cwd) {
7762
7558
  stderr += chunk.toString("utf8");
7763
7559
  });
7764
7560
  child.on("close", (code) => {
7765
- resolve5({
7561
+ resolve3({
7766
7562
  exitCode: code ?? -1,
7767
7563
  durationMs: Date.now() - start,
7768
7564
  stdoutTail: tailString(stdout, SYNC_TAIL_LENGTH),
@@ -7770,7 +7566,7 @@ async function runShellCommand(cmd, cmdArgs, cwd) {
7770
7566
  });
7771
7567
  });
7772
7568
  child.on("error", (err) => {
7773
- resolve5({
7569
+ resolve3({
7774
7570
  exitCode: -1,
7775
7571
  durationMs: Date.now() - start,
7776
7572
  stdoutTail: tailString(stdout, SYNC_TAIL_LENGTH),
@@ -7816,22 +7612,22 @@ function createRitualRegistry(options) {
7816
7612
 
7817
7613
  // ../plugins/session-rituals/dist/cli-dispatch.js
7818
7614
  import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
7819
- import { existsSync as existsSync16, readFileSync as readFileSync4, mkdirSync, openSync, writeSync, closeSync, linkSync, rmSync, statSync } from "fs";
7615
+ import { existsSync as existsSync15, readFileSync as readFileSync3, mkdirSync, openSync, writeSync, closeSync, linkSync, rmSync, statSync } from "fs";
7820
7616
  import { createRequire } from "module";
7821
7617
  import { hostname } from "os";
7822
- import { isAbsolute as isAbsolute5, join as join30 } from "path";
7618
+ import { isAbsolute as isAbsolute4, join as join28 } from "path";
7823
7619
 
7824
7620
  // ../plugins/session-rituals/dist/update-check.js
7825
7621
  import { execSync } from "child_process";
7826
- import { existsSync as existsSync13, readFileSync as readFileSync3 } from "fs";
7827
- import { join as join27 } from "path";
7622
+ import { existsSync as existsSync12, readFileSync as readFileSync2 } from "fs";
7623
+ import { join as join25 } from "path";
7828
7624
  var PKG = "@vortex-os/base";
7829
7625
  var NPM_TIMEOUT_MS = 4e3;
7830
7626
  function readInstalledBaseVersion(templatesDir = resolveTemplatesDir()) {
7831
7627
  if (!templatesDir)
7832
7628
  return null;
7833
7629
  try {
7834
- const m2 = JSON.parse(readFileSync3(join27(templatesDir, "manifest.json"), "utf8"));
7630
+ const m2 = JSON.parse(readFileSync2(join25(templatesDir, "manifest.json"), "utf8"));
7835
7631
  return typeof m2.baseVersion === "string" && parseCore(m2.baseVersion) ? m2.baseVersion.trim() : null;
7836
7632
  } catch {
7837
7633
  return null;
@@ -7903,8 +7699,8 @@ function isStableUpdate(latest, installed) {
7903
7699
  return compareSemver(latest, installed) === 1;
7904
7700
  }
7905
7701
  function buildInstallCommand(repoRoot) {
7906
- const has = (f) => existsSync13(join27(repoRoot, f));
7907
- const local = existsSync13(join27(repoRoot, "node_modules", "@vortex-os", "base"));
7702
+ const has = (f) => existsSync12(join25(repoRoot, f));
7703
+ const local = existsSync12(join25(repoRoot, "node_modules", "@vortex-os", "base"));
7908
7704
  let installPart;
7909
7705
  if (!local) {
7910
7706
  installPart = `npm i -g ${PKG}@latest`;
@@ -7932,9 +7728,9 @@ function checkBaseUpdate(ctx) {
7932
7728
  }
7933
7729
 
7934
7730
  // ../plugins/session-rituals/dist/session-start-report.js
7935
- import { existsSync as existsSync14 } from "fs";
7731
+ import { existsSync as existsSync13 } from "fs";
7936
7732
  import { readdir as readdir17, readFile as readFile22, stat as stat9 } from "fs/promises";
7937
- import { join as join28 } from "path";
7733
+ import { join as join26 } from "path";
7938
7734
  var COUNTED_DIRS2 = ["_memory", "worklog", "decision-log"];
7939
7735
  var DEFAULT_GAP_WINDOW_DAYS = 30;
7940
7736
  function gapWindowSinceArg() {
@@ -7951,8 +7747,8 @@ async function collectSessionStartReport(ctx, opts) {
7951
7747
  const counts = {};
7952
7748
  const missing = [];
7953
7749
  for (const name of COUNTED_DIRS2) {
7954
- const dir = join28(ctx.dataDir, name);
7955
- if (!existsSync14(dir)) {
7750
+ const dir = join26(ctx.dataDir, name);
7751
+ if (!existsSync13(dir)) {
7956
7752
  missing.push(name);
7957
7753
  counts[name] = 0;
7958
7754
  continue;
@@ -7962,7 +7758,7 @@ async function collectSessionStartReport(ctx, opts) {
7962
7758
  const { recent, recentGroup, recentWorklogsOmitted, dates, latestBodies } = await scanWorklog(ctx.dataDir);
7963
7759
  const cutoff = isoDate2(addDays2(now, -(opts?.gapWindowDays ?? DEFAULT_GAP_WINDOW_DAYS)));
7964
7760
  const recentWorklogDates = dates.filter((d2) => d2 >= cutoff);
7965
- const mem = await scanMemoryTiers(join28(ctx.dataDir, "_memory"));
7761
+ const mem = await scanMemoryTiers(join26(ctx.dataDir, "_memory"));
7966
7762
  const ho = await scanHandoffs(ctx.dataDir);
7967
7763
  const handoffs = ho.active.map((h) => ({
7968
7764
  date: h.date,
@@ -8029,7 +7825,7 @@ async function scanMemoryTiers(memoryDir) {
8029
7825
  for (const e of entries) {
8030
7826
  if (!e.isFile() || !e.name.endsWith(".md"))
8031
7827
  continue;
8032
- const full = join28(memoryDir, e.name);
7828
+ const full = join26(memoryDir, e.name);
8033
7829
  if (e.name === "_INDEX.md") {
8034
7830
  indexExists = true;
8035
7831
  try {
@@ -8189,6 +7985,16 @@ function renderSessionStartReport(report, extras) {
8189
7985
  if (carry && carry.uncommitted > 0) {
8190
7986
  lines.push(`- \u21A9\uFE0F ${carry.uncommitted} uncommitted change(s) carried over from a prior session \u2014 likely normal work in progress.`);
8191
7987
  }
7988
+ const fl = extras?.failures;
7989
+ if (fl && fl.warnings.length > 0) {
7990
+ lines.push(`- \u{1F501} recurring failures (open ledger entries: ${fl.totalOpen}):`);
7991
+ for (const w2 of fl.warnings) {
7992
+ const stageText = w2.stage === "promote" ? "3rd+ occurrence \u2014 PROPOSE a deterministic guard to the user (ladder: promote; never self-install)" : `2nd occurrence \u2014 the covering rule's write-time gate is MANDATORY this session${w2.rule ? ` (rule: ${w2.rule})` : ""}`;
7993
+ lines.push(` - ${w2.key} \xD7${w2.count} (last ${w2.lastDate}) \u2014 ${stageText}`);
7994
+ }
7995
+ if (fl.omitted > 0)
7996
+ lines.push(` - \u2026(+${fl.omitted} more \u2014 \`vortex failure list\`)`);
7997
+ }
8192
7998
  const cu = extras?.catchUp;
8193
7999
  if (cu && (cu.ingestedLocal > 0 || cu.indexedPulled > 0 || cu.errors > 0)) {
8194
8000
  const parts = [];
@@ -8258,15 +8064,15 @@ async function countMarkdown3(dir, recursive) {
8258
8064
  } else if (e.isDirectory() && recursive) {
8259
8065
  if (e.name.startsWith(".") || e.name.startsWith("_"))
8260
8066
  continue;
8261
- total += await countMarkdown3(join28(dir, e.name), recursive);
8067
+ total += await countMarkdown3(join26(dir, e.name), recursive);
8262
8068
  }
8263
8069
  }
8264
8070
  return total;
8265
8071
  }
8266
8072
  var MAX_GROUP_READ = 20;
8267
8073
  async function scanWorklog(dataDir) {
8268
- const root = join28(dataDir, "worklog");
8269
- if (!existsSync14(root))
8074
+ const root = join26(dataDir, "worklog");
8075
+ if (!existsSync13(root))
8270
8076
  return { recent: null, recentGroup: [], recentWorklogsOmitted: 0, dates: [], latestBodies: [] };
8271
8077
  const dates = /* @__PURE__ */ new Set();
8272
8078
  const consistent = [];
@@ -8281,7 +8087,7 @@ async function scanWorklog(dataDir) {
8281
8087
  for (const e of entries) {
8282
8088
  const childRel = rel ? `${rel}/${e.name}` : e.name;
8283
8089
  if (e.isDirectory()) {
8284
- await walk5(join28(absDir, e.name), childRel);
8090
+ await walk5(join26(absDir, e.name), childRel);
8285
8091
  } else if (e.isFile()) {
8286
8092
  const m2 = e.name.match(/^(\d{4})-(\d{2})-(\d{2})(?:_\d{4})?-.+\.md$/);
8287
8093
  if (!m2)
@@ -8310,7 +8116,7 @@ async function scanWorklog(dataDir) {
8310
8116
  const recentGroup = [];
8311
8117
  const latestBodies = [];
8312
8118
  for (const g of group) {
8313
- const { title, body } = await readWorklogTitleAndBody(join28(root, g.rel));
8119
+ const { title, body } = await readWorklogTitleAndBody(join26(root, g.rel));
8314
8120
  recentGroup.push({ path: defangReportPath(`worklog/${g.rel}`), title });
8315
8121
  latestBodies.push(body);
8316
8122
  }
@@ -8389,11 +8195,11 @@ function isoDate2(d2) {
8389
8195
  }
8390
8196
 
8391
8197
  // ../plugins/session-rituals/dist/curate-cli.js
8392
- import { existsSync as existsSync15 } from "fs";
8198
+ import { existsSync as existsSync14 } from "fs";
8393
8199
  import { createHash as createHash3 } from "crypto";
8394
8200
  import { readFile as readFile23, readdir as readdir18 } from "fs/promises";
8395
- import { join as join29 } from "path";
8396
- var SYSTEM_META_DIRS3 = /* @__PURE__ */ new Set([
8201
+ import { join as join27 } from "path";
8202
+ var SYSTEM_META_DIRS2 = /* @__PURE__ */ new Set([
8397
8203
  "worklog",
8398
8204
  "decision-log",
8399
8205
  "runbooks",
@@ -8416,7 +8222,7 @@ function firstSegment(rel) {
8416
8222
  }
8417
8223
  function isSystemMetaRel(rel) {
8418
8224
  const first = firstSegment(rel);
8419
- return SYSTEM_META_DIRS3.has(first) || first.startsWith("_");
8225
+ return SYSTEM_META_DIRS2.has(first) || first.startsWith("_");
8420
8226
  }
8421
8227
  function validateCuratePayload(payload) {
8422
8228
  const errors = [];
@@ -8472,10 +8278,10 @@ function joinRel(...parts) {
8472
8278
  }
8473
8279
  async function runCurateCandidates(repoRoot, options) {
8474
8280
  const maxEntries = options?.maxEntries ?? 200;
8475
- const dataDir = join29(repoRoot, "data");
8281
+ const dataDir = join27(repoRoot, "data");
8476
8282
  const candidates = [];
8477
8283
  let truncated = false;
8478
- if (existsSync15(dataDir)) {
8284
+ if (existsSync14(dataDir)) {
8479
8285
  async function visit(absDir, relDir) {
8480
8286
  if (candidates.length >= maxEntries) {
8481
8287
  truncated = true;
@@ -8496,9 +8302,9 @@ async function runCurateCandidates(repoRoot, options) {
8496
8302
  const atRoot = relDir === "";
8497
8303
  if (e.name.startsWith("."))
8498
8304
  continue;
8499
- if (atRoot && (SYSTEM_META_DIRS3.has(e.name) || e.name.startsWith("_")))
8305
+ if (atRoot && (SYSTEM_META_DIRS2.has(e.name) || e.name.startsWith("_")))
8500
8306
  continue;
8501
- await visit(join29(absDir, e.name), joinRel(relDir, e.name));
8307
+ await visit(join27(absDir, e.name), joinRel(relDir, e.name));
8502
8308
  } else if (e.isFile() && e.name.endsWith(".md")) {
8503
8309
  if (NON_DOC_FILES.has(e.name))
8504
8310
  continue;
@@ -8507,7 +8313,7 @@ async function runCurateCandidates(repoRoot, options) {
8507
8313
  let topic = null;
8508
8314
  let tags = [];
8509
8315
  try {
8510
- const raw = await readFile23(join29(absDir, e.name), "utf8");
8316
+ const raw = await readFile23(join27(absDir, e.name), "utf8");
8511
8317
  const parsed = parseFrontmatter(raw);
8512
8318
  if (typeof parsed.frontmatter.topic === "string") {
8513
8319
  topic = parsed.frontmatter.topic.trim().toLowerCase();
@@ -8545,7 +8351,7 @@ async function runCuratePreview(repoRoot, payload, now = /* @__PURE__ */ new Dat
8545
8351
  };
8546
8352
  }
8547
8353
  try {
8548
- validateDataRelativePath(join29(repoRoot, "data"), v2.effectiveRelPath);
8354
+ validateDataRelativePath(join27(repoRoot, "data"), v2.effectiveRelPath);
8549
8355
  } catch (e) {
8550
8356
  return {
8551
8357
  subcommand: "curate-preview",
@@ -8562,10 +8368,10 @@ async function runCuratePreview(repoRoot, payload, now = /* @__PURE__ */ new Dat
8562
8368
  let targetExists;
8563
8369
  let wouldDo;
8564
8370
  if (payload.action === "create-file") {
8565
- targetExists = existsSync15(join29(repoRoot, "data", v2.effectiveRelPath));
8371
+ targetExists = existsSync14(join27(repoRoot, "data", v2.effectiveRelPath));
8566
8372
  wouldDo = targetExists ? `create-file at ${v2.effectiveRelPath} \u2014 but the file already EXISTS, so accept would REFUSE (no overwrite).` : `create a new document at data/${v2.effectiveRelPath}.`;
8567
8373
  } else {
8568
- targetExists = existsSync15(join29(repoRoot, "data", v2.effectiveRelPath));
8374
+ targetExists = existsSync14(join27(repoRoot, "data", v2.effectiveRelPath));
8569
8375
  wouldDo = targetExists ? `append a "## ${payload.sectionHeader}" section to data/${v2.effectiveRelPath}.` : `append-section to data/${v2.effectiveRelPath} \u2014 but the file does NOT exist, so accept would FAIL (append-section never creates).`;
8570
8376
  }
8571
8377
  const nextActions = [];
@@ -8710,13 +8516,13 @@ function readCuratePayload(args) {
8710
8516
  break;
8711
8517
  }
8712
8518
  if (t === "--payload-file" && i + 1 < args.length) {
8713
- raw = readFileSync4(args[++i], "utf8");
8519
+ raw = readFileSync3(args[++i], "utf8");
8714
8520
  break;
8715
8521
  }
8716
8522
  }
8717
8523
  if (raw === null) {
8718
8524
  try {
8719
- raw = readFileSync4(0, "utf8");
8525
+ raw = readFileSync3(0, "utf8");
8720
8526
  } catch {
8721
8527
  raw = "";
8722
8528
  }
@@ -8740,11 +8546,22 @@ async function runVortexCli(argv, io) {
8740
8546
  const err = io?.stderr ?? ((s) => process.stderr.write(s));
8741
8547
  try {
8742
8548
  if (argv[0] === "statusline") {
8743
- const { runStatuslineCli: runStatuslineCli2 } = await import("./statusline-NQKJ3NWD.js");
8549
+ const { runStatuslineCli: runStatuslineCli2 } = await import("./statusline-JTSL5CCH.js");
8744
8550
  const statuslineRoot = process.env.VORTEX_REPO_ROOT?.trim() || process.cwd();
8745
8551
  return runStatuslineCli2(argv.slice(1), statuslineRoot, out, err);
8746
8552
  }
8553
+ if (argv[0] === "guard") {
8554
+ const { runGuardCli: runGuardCli2 } = await import("./guard-IMJR6ET7.js");
8555
+ return runGuardCli2(argv.slice(1), out, err);
8556
+ }
8747
8557
  const repoRoot = resolveRepoRoot();
8558
+ if (argv[0] === "failure") {
8559
+ const { runFailureCli: runFailureCli2 } = await import("./failures-PMURLMVB.js");
8560
+ const { resolveInstanceRoot: resolveInstanceRoot2 } = await import("./guard-IMJR6ET7.js");
8561
+ const root = resolveInstanceRoot2(process.cwd()) ?? repoRoot;
8562
+ const ctx = makeContext(root);
8563
+ return runFailureCli2(argv.slice(1), ctx.dataDir, out, err);
8564
+ }
8748
8565
  if (argv[0] === "session-start") {
8749
8566
  await runSessionStart(repoRoot, out);
8750
8567
  return 0;
@@ -8784,6 +8601,8 @@ ${names}
8784
8601
  session-end \u2014 no-op (kept for hook compatibility; worklog gap handling is at session-start)
8785
8602
  check-updates \u2014 check the npm registry for a newer @vortex-os/base (read-only; prints the exact update command)
8786
8603
  statusline \u2014 render the Claude Code status bar from stdin JSON (\`lite\` for 1-line; \`install [--lite]\` wires .claude/settings.json)
8604
+ failure \u2014 failure ledger (\`record\` an occurrence by recurrence key / \`list\` open groups with their escalation stage)
8605
+ guard \u2014 deterministic write-time guards (\`write\` = PreToolUse hook body; wired by \`vortex init\`)
8787
8606
 
8788
8607
  Instance shortcuts (also available as \`/vortex <sub>\`):
8789
8608
  init \u2014 first-time setup: routers + data/ + hooks + slash-commands
@@ -8850,12 +8669,12 @@ function memoryExtendedPresent() {
8850
8669
  }
8851
8670
  var VECTORIZE_LOCK_TTL_MS = 6 * 60 * 60 * 1e3;
8852
8671
  function vectorizeLockPath(ctx) {
8853
- return join30(ctx.dataDir, "_indexes", ".vectorize.lock");
8672
+ return join28(ctx.dataDir, "_indexes", ".vectorize.lock");
8854
8673
  }
8855
8674
  function vectorizeSetupInProgress(ctx) {
8856
8675
  const lock = vectorizeLockPath(ctx);
8857
8676
  try {
8858
- if (!existsSync16(lock))
8677
+ if (!existsSync15(lock))
8859
8678
  return false;
8860
8679
  return Date.now() - statSync(lock).mtimeMs < VECTORIZE_LOCK_TTL_MS;
8861
8680
  } catch {
@@ -8876,9 +8695,9 @@ function spawnVectorizeSetup(repoRoot) {
8876
8695
  }
8877
8696
  async function runVectorizeSetup(repoRoot, out, err) {
8878
8697
  const ctx = makeContext(repoRoot);
8879
- const indexDir = join30(ctx.dataDir, "_indexes");
8880
- const finalDb = join30(indexDir, "memory.sqlite");
8881
- if (existsSync16(finalDb)) {
8698
+ const indexDir = join28(ctx.dataDir, "_indexes");
8699
+ const finalDb = join28(indexDir, "memory.sqlite");
8700
+ if (existsSync15(finalDb)) {
8882
8701
  out("recall index already present \u2014 nothing to do\n");
8883
8702
  return;
8884
8703
  }
@@ -8909,7 +8728,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8909
8728
  return;
8910
8729
  }
8911
8730
  }
8912
- const tmpDb = join30(indexDir, `memory.sqlite.building-${process.pid}`);
8731
+ const tmpDb = join28(indexDir, `memory.sqlite.building-${process.pid}`);
8913
8732
  const tmpSidecars = [tmpDb + "-wal", tmpDb + "-shm", tmpDb + "-journal"];
8914
8733
  const cleanTmp = () => {
8915
8734
  rmSync(tmpDb, { force: true });
@@ -8919,7 +8738,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8919
8738
  let tokenWritten = false;
8920
8739
  const releaseLock = () => {
8921
8740
  try {
8922
- const cur = existsSync16(lockPath) ? readFileSync4(lockPath, "utf8").trim() : "";
8741
+ const cur = existsSync15(lockPath) ? readFileSync3(lockPath, "utf8").trim() : "";
8923
8742
  if (cur === token || cur === "" && !tokenWritten)
8924
8743
  rmSync(lockPath, { force: true });
8925
8744
  } catch {
@@ -8932,7 +8751,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8932
8751
  } finally {
8933
8752
  closeSync(lockFd);
8934
8753
  }
8935
- if (existsSync16(finalDb)) {
8754
+ if (existsSync15(finalDb)) {
8936
8755
  out("recall index already present \u2014 nothing to do\n");
8937
8756
  return;
8938
8757
  }
@@ -8948,7 +8767,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8948
8767
  } finally {
8949
8768
  db.close();
8950
8769
  }
8951
- if (existsSync16(tmpDb + "-wal")) {
8770
+ if (existsSync15(tmpDb + "-wal")) {
8952
8771
  throw new Error("temp index retained a WAL sidecar after consolidation; refusing to publish");
8953
8772
  }
8954
8773
  try {
@@ -9032,7 +8851,7 @@ async function runSessionStart(repoRoot, out) {
9032
8851
  let vectorized = null;
9033
8852
  let vectorizeSetupStarted = false;
9034
8853
  if (config.autoRecord.vectorize) {
9035
- const dbExists = existsSync16(join30(ctx.dataDir, "_indexes", "memory.sqlite"));
8854
+ const dbExists = existsSync15(join28(ctx.dataDir, "_indexes", "memory.sqlite"));
9036
8855
  const action = decideVectorizeAction({
9037
8856
  vectorizeOn: true,
9038
8857
  dbExists,
@@ -9055,6 +8874,16 @@ async function runSessionStart(repoRoot, out) {
9055
8874
  }
9056
8875
  }
9057
8876
  }
8877
+ let failures = null;
8878
+ if (config.autoRecord.failures) {
8879
+ try {
8880
+ const { scanFailures: scanFailures2, failureReportSlice: failureReportSlice2 } = await import("./failures-PMURLMVB.js");
8881
+ const slice = failureReportSlice2(await scanFailures2(ctx.dataDir));
8882
+ if (slice.warnings.length > 0)
8883
+ failures = slice;
8884
+ } catch {
8885
+ }
8886
+ }
9058
8887
  let templateUpdate = null;
9059
8888
  try {
9060
8889
  const td = resolveTemplatesDir();
@@ -9094,7 +8923,8 @@ async function runSessionStart(repoRoot, out) {
9094
8923
  updateCheck: updateCheck ?? void 0,
9095
8924
  globalSetupOffer: globalSetupOffer || void 0,
9096
8925
  carryover: carryover ?? void 0,
9097
- handoffPrune: handoffPrune ?? void 0
8926
+ handoffPrune: handoffPrune ?? void 0,
8927
+ failures: failures ?? void 0
9098
8928
  }));
9099
8929
  }
9100
8930
  async function runSessionEnd(_repoRoot, _out) {
@@ -9121,7 +8951,7 @@ function detectInterruptedGitOp(repoRoot) {
9121
8951
  const resolved = gitOut(repoRoot, args).split(/\r?\n/).map((s) => s.trim());
9122
8952
  for (let i = 0; i < markers.length; i++) {
9123
8953
  const p = resolved[i];
9124
- if (p && existsSync16(isAbsolute5(p) ? p : join30(repoRoot, p)))
8954
+ if (p && existsSync15(isAbsolute4(p) ? p : join28(repoRoot, p)))
9125
8955
  return markers[i];
9126
8956
  }
9127
8957
  } catch {
@@ -9141,23 +8971,23 @@ function resolveSessionEnvironment(ctx, config) {
9141
8971
  let environment = resolveEnvironment(config, {
9142
8972
  hostname: hostname(),
9143
8973
  env: process.env,
9144
- pathExists: existsSync16
8974
+ pathExists: existsSync15
9145
8975
  });
9146
8976
  if (!environment)
9147
8977
  environment = process.env.VORTEX_ENV?.trim() || null;
9148
8978
  if (!environment) {
9149
- const envFile = join30(ctx.repoRoot, ".agent", "environment");
9150
- if (existsSync16(envFile)) {
9151
- environment = readFileSync4(envFile, "utf8").split(/\r?\n/)[0]?.trim() || null;
8979
+ const envFile = join28(ctx.repoRoot, ".agent", "environment");
8980
+ if (existsSync15(envFile)) {
8981
+ environment = readFileSync3(envFile, "utf8").split(/\r?\n/)[0]?.trim() || null;
9152
8982
  }
9153
8983
  }
9154
8984
  return environment;
9155
8985
  }
9156
8986
 
9157
8987
  // ../plugins/session-rituals/dist/ambient-recall.js
9158
- import { join as join31 } from "path";
8988
+ import { join as join29 } from "path";
9159
8989
  function defaultDbPath2(ctx) {
9160
- return join31(ctx.dataDir, "_indexes", "memory.sqlite");
8990
+ return join29(ctx.dataDir, "_indexes", "memory.sqlite");
9161
8991
  }
9162
8992
  function createAmbientRecaller(ctx, options) {
9163
8993
  const resolveDb = options.dbPath ?? defaultDbPath2;
@@ -9194,14 +9024,14 @@ function createAmbientRecaller(ctx, options) {
9194
9024
  }
9195
9025
 
9196
9026
  // ../plugins/session-rituals/dist/worklog-write.js
9197
- import { mkdir as mkdir10, writeFile as writeFile12 } from "fs/promises";
9198
- import { dirname as dirname6, join as join32 } from "path";
9027
+ import { mkdir as mkdir10, writeFile as writeFile11 } from "fs/promises";
9028
+ import { dirname as dirname6, join as join30 } from "path";
9199
9029
  async function ensureWorklogEntry(ctx, opts) {
9200
9030
  const now = opts?.now ?? /* @__PURE__ */ new Date();
9201
9031
  const date = isoDate3(now);
9202
9032
  const time = isoTime2(now);
9203
9033
  const keyword = (opts?.keyword ?? "worklog").trim() || "worklog";
9204
- const store = new WorklogStore(join32(ctx.dataDir, "worklog"));
9034
+ const store = new WorklogStore(join30(ctx.dataDir, "worklog"));
9205
9035
  const existing = await store.get(date);
9206
9036
  if (existing) {
9207
9037
  return { path: existing.path, date: existing.date, keyword: existing.keyword, created: false };
@@ -9209,7 +9039,7 @@ async function ensureWorklogEntry(ctx, opts) {
9209
9039
  const path = store.pathFor(date, keyword, time);
9210
9040
  const title = opts?.title ?? `${date} worklog`;
9211
9041
  await mkdir10(dirname6(path), { recursive: true });
9212
- await writeFile12(path, renderWorklogFile(date, title, opts?.body ?? ""), "utf8");
9042
+ await writeFile11(path, renderWorklogFile(date, title, opts?.body ?? ""), "utf8");
9213
9043
  return { path, date, keyword, created: true };
9214
9044
  }
9215
9045
  function renderWorklogFile(date, title, body) {