@vortex-os/base 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
1
+ import {
2
+ __export,
3
+ catchUpSessions
4
+ } from "./chunk-6SO4DAWJ.js";
6
5
 
7
6
  // ../core/dist/index.js
8
7
  var dist_exports = {};
@@ -1873,7 +1872,7 @@ __export(dist_exports8, {
1873
1872
 
1874
1873
  // ../modules/worklog/dist/store.js
1875
1874
  import { readdir as readdir6, readFile as readFile6, stat as stat2 } from "fs/promises";
1876
- import { join as join9 } from "path";
1875
+ import { join as join9, resolve as resolve2, sep } from "path";
1877
1876
  var FILENAME_PATTERN = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
1878
1877
  var MONTH_PATTERN = /^\d{2}$/;
1879
1878
  var YEAR_PATTERN = /^\d{4}$/;
@@ -1923,11 +1922,14 @@ var WorklogStore = class {
1923
1922
  }
1924
1923
  /** Resolve the file path for a given (date, keyword), without creating it. */
1925
1924
  pathFor(date, keyword) {
1926
- const [year, month] = date.split("-");
1927
- if (!year || !month) {
1925
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
1928
1926
  throw new Error(`Invalid date: ${date} (expected YYYY-MM-DD)`);
1929
1927
  }
1930
- return join9(this.rootDir, year, month, `${date}-${keyword}.md`);
1928
+ const [year, month] = date.split("-");
1929
+ validateSegment("keyword", keyword);
1930
+ const abs = join9(this.rootDir, year, month, `${date}-${keyword}.md`);
1931
+ assertContained(abs, this.rootDir);
1932
+ return abs;
1931
1933
  }
1932
1934
  async listSubdirs(dir, pattern) {
1933
1935
  try {
@@ -1970,6 +1972,26 @@ var WorklogStore = class {
1970
1972
  return out;
1971
1973
  }
1972
1974
  };
1975
+ function validateSegment(label, value) {
1976
+ const v2 = (value ?? "").trim();
1977
+ if (v2.length === 0)
1978
+ throw new Error(`${label} is required`);
1979
+ if (v2.includes("/") || v2.includes("\\"))
1980
+ throw new Error(`${label} must not contain path separators: ${value}`);
1981
+ if (/(^|[\\/])\.\.([\\/]|$)/.test(v2) || v2 === "..")
1982
+ throw new Error(`${label} must not contain '..': ${value}`);
1983
+ if (v2.startsWith("/") || v2.startsWith("\\") || /^[a-zA-Z]:/.test(v2))
1984
+ throw new Error(`${label} must be a relative name, not an absolute path: ${value}`);
1985
+ if (/[\u0000-\u001f\u007f]/.test(v2))
1986
+ throw new Error(`${label} must not contain NUL or control characters: ${value}`);
1987
+ }
1988
+ function assertContained(abs, rootDir) {
1989
+ const root = resolve2(rootDir);
1990
+ const target = resolve2(abs);
1991
+ if (target !== root && !(target + sep).startsWith(root + sep)) {
1992
+ throw new Error(`Refusing to write outside the store directory: ${abs}`);
1993
+ }
1994
+ }
1973
1995
 
1974
1996
  // ../modules/worklog/dist/append.js
1975
1997
  import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
@@ -1996,7 +2018,7 @@ __export(dist_exports9, {
1996
2018
 
1997
2019
  // ../modules/decision-log/dist/store.js
1998
2020
  import { readdir as readdir7, readFile as readFile8, stat as stat3 } from "fs/promises";
1999
- import { join as join10 } from "path";
2021
+ import { join as join10, resolve as resolve3, sep as sep2 } from "path";
2000
2022
  var FILENAME_PATTERN2 = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
2001
2023
  var DecisionStore = class {
2002
2024
  rootDir;
@@ -2089,9 +2111,32 @@ var DecisionStore = class {
2089
2111
  if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
2090
2112
  throw new Error(`Invalid date: ${date} (expected YYYY-MM-DD)`);
2091
2113
  }
2092
- return join10(this.rootDir, `${date}-${slug}.md`);
2114
+ validateSegment2("slug", slug);
2115
+ const abs = join10(this.rootDir, `${date}-${slug}.md`);
2116
+ assertContained2(abs, this.rootDir);
2117
+ return abs;
2093
2118
  }
2094
2119
  };
2120
+ function validateSegment2(label, value) {
2121
+ const v2 = (value ?? "").trim();
2122
+ if (v2.length === 0)
2123
+ throw new Error(`${label} is required`);
2124
+ if (v2.includes("/") || v2.includes("\\"))
2125
+ throw new Error(`${label} must not contain path separators: ${value}`);
2126
+ if (/(^|[\\/])\.\.([\\/]|$)/.test(v2) || v2 === "..")
2127
+ throw new Error(`${label} must not contain '..': ${value}`);
2128
+ if (v2.startsWith("/") || v2.startsWith("\\") || /^[a-zA-Z]:/.test(v2))
2129
+ throw new Error(`${label} must be a relative name, not an absolute path: ${value}`);
2130
+ if (/[\u0000-\u001f\u007f]/.test(v2))
2131
+ throw new Error(`${label} must not contain NUL or control characters: ${value}`);
2132
+ }
2133
+ function assertContained2(abs, rootDir) {
2134
+ const root = resolve3(rootDir);
2135
+ const target = resolve3(abs);
2136
+ if (target !== root && !(target + sep2).startsWith(root + sep2)) {
2137
+ throw new Error(`Refusing to write outside the store directory: ${abs}`);
2138
+ }
2139
+ }
2095
2140
 
2096
2141
  // ../modules/decision-log/dist/template.js
2097
2142
  function renderTemplate(input) {
@@ -2838,12 +2883,15 @@ function isActive(entry, nowMs) {
2838
2883
  const expiresMs = new Date(entry.expiresAt).getTime();
2839
2884
  return Number.isFinite(expiresMs) && expiresMs > nowMs;
2840
2885
  }
2886
+ var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
2841
2887
  function purgeExpired(entries, now) {
2842
2888
  if (!entries)
2843
2889
  return {};
2844
- const out = {};
2890
+ const out = /* @__PURE__ */ Object.create(null);
2845
2891
  const nowMs = now.getTime();
2846
2892
  for (const [fp, entry] of Object.entries(entries)) {
2893
+ if (DANGEROUS_KEYS.has(fp))
2894
+ continue;
2847
2895
  if (isActive(entry, nowMs))
2848
2896
  out[fp] = entry;
2849
2897
  }
@@ -3898,6 +3946,7 @@ __export(dist_exports14, {
3898
3946
  SESSION_END_COMMAND: () => SESSION_END_COMMAND,
3899
3947
  SESSION_START_COMMAND: () => SESSION_START_COMMAND,
3900
3948
  agendaCommand: () => agendaCommand,
3949
+ buildRegistry: () => buildRegistry,
3901
3950
  catchUpSessions: () => catchUpSessions,
3902
3951
  collectAgenda: () => collectAgenda,
3903
3952
  collectSessionStartReport: () => collectSessionStartReport,
@@ -3916,6 +3965,8 @@ __export(dist_exports14, {
3916
3965
  reindexCommand: () => reindexCommand,
3917
3966
  renderAgenda: () => renderAgenda,
3918
3967
  renderSessionStartReport: () => renderSessionStartReport,
3968
+ resolveRepoRoot: () => resolveRepoRoot,
3969
+ runVortexCli: () => runVortexCli,
3919
3970
  serializeSettings: () => serializeSettings,
3920
3971
  sessionStartCommand: () => sessionStartCommand,
3921
3972
  vortexCommand: () => vortexCommand
@@ -4399,8 +4450,8 @@ import { basename as basename7, dirname as dirname4, join as join22 } from "path
4399
4450
  import { fileURLToPath } from "url";
4400
4451
 
4401
4452
  // ../plugins/session-rituals/dist/ensure-hooks.js
4402
- var SESSION_START_COMMAND = "node plugins/session-rituals/scripts/session-start-hook.mjs";
4403
- var SESSION_END_COMMAND = "node plugins/session-rituals/scripts/session-end-hook.mjs";
4453
+ var SESSION_START_COMMAND = "npx --no-install -p @vortex-os/base vortex session-start";
4454
+ var SESSION_END_COMMAND = "npx --no-install -p @vortex-os/base vortex session-end";
4404
4455
  function parseSettings(text) {
4405
4456
  const trimmed = (text ?? "").trim();
4406
4457
  if (trimmed.length === 0)
@@ -4536,43 +4587,133 @@ function runHelp() {
4536
4587
  ]
4537
4588
  };
4538
4589
  }
4539
- async function installCommandTemplates(repoRoot) {
4590
+ function resolveTemplatesDir() {
4540
4591
  const here = dirname4(fileURLToPath(import.meta.url));
4541
- const templatesDir = join22(here, "..", "..", "templates", "commands");
4542
- if (!existsSync8(templatesDir))
4592
+ const candidates = [
4593
+ join22(here, "..", "..", "templates"),
4594
+ // session-rituals: dist/commands -> templates
4595
+ join22(here, "..", "templates"),
4596
+ // base aggregate: dist -> templates
4597
+ join22(here, "templates")
4598
+ // defensive: alongside the bundle
4599
+ ];
4600
+ for (const c of candidates) {
4601
+ if (existsSync8(join22(c, "commands")) || existsSync8(join22(c, "routers")))
4602
+ return c;
4603
+ }
4604
+ return null;
4605
+ }
4606
+ async function installCommandTemplates(repoRoot, templatesDir) {
4607
+ if (!templatesDir)
4608
+ return [];
4609
+ const commandsDir = join22(templatesDir, "commands");
4610
+ if (!existsSync8(commandsDir))
4543
4611
  return [];
4544
4612
  const destDir = join22(repoRoot, ".claude", "commands");
4545
4613
  await mkdir5(destDir, { recursive: true });
4546
4614
  const written = [];
4547
- for (const name of await readdir15(templatesDir)) {
4615
+ for (const name of await readdir15(commandsDir)) {
4548
4616
  if (!name.endsWith(".md"))
4549
4617
  continue;
4550
4618
  const dest = join22(destDir, name);
4551
4619
  if (existsSync8(dest))
4552
4620
  continue;
4553
- await copyFile(join22(templatesDir, name), dest);
4621
+ await copyFile(join22(commandsDir, name), dest);
4622
+ written.push(dest);
4623
+ }
4624
+ return written;
4625
+ }
4626
+ var ROUTER_FILES = [
4627
+ "AGENT.md",
4628
+ "CLAUDE.md",
4629
+ "CODEX.md",
4630
+ "GEMINI.md",
4631
+ ".cursorrules"
4632
+ ];
4633
+ async function installRouterTemplates(repoRoot, templatesDir) {
4634
+ if (!templatesDir)
4635
+ return [];
4636
+ const routersDir = join22(templatesDir, "routers");
4637
+ if (!existsSync8(routersDir))
4638
+ return [];
4639
+ const written = [];
4640
+ for (const name of ROUTER_FILES) {
4641
+ const src = join22(routersDir, name);
4642
+ if (!existsSync8(src))
4643
+ continue;
4644
+ const dest = join22(repoRoot, name);
4645
+ if (existsSync8(dest))
4646
+ continue;
4647
+ await copyFile(src, dest);
4554
4648
  written.push(dest);
4555
4649
  }
4556
4650
  return written;
4557
4651
  }
4652
+ async function seedInstanceConfig(repoRoot, templatesDir) {
4653
+ const written = [];
4654
+ const agentDir = join22(repoRoot, ".agent");
4655
+ const vortexJson = join22(agentDir, "vortex.json");
4656
+ if (!existsSync8(vortexJson)) {
4657
+ await mkdir5(agentDir, { recursive: true });
4658
+ const tmpl = templatesDir ? join22(templatesDir, "config", "vortex.json") : null;
4659
+ if (tmpl && existsSync8(tmpl)) {
4660
+ await copyFile(tmpl, vortexJson);
4661
+ } else {
4662
+ await writeFile10(vortexJson, JSON.stringify({
4663
+ autoRecord: {
4664
+ sessionStart: true,
4665
+ worklog: true,
4666
+ decision: true,
4667
+ ambientRecall: true,
4668
+ archive: true
4669
+ },
4670
+ environments: []
4671
+ }, null, 2) + "\n", "utf8");
4672
+ }
4673
+ written.push(vortexJson);
4674
+ }
4675
+ const pkgPath = join22(repoRoot, "package.json");
4676
+ if (!existsSync8(pkgPath)) {
4677
+ await writeFile10(pkgPath, JSON.stringify({
4678
+ name: "vortex-instance",
4679
+ version: "0.0.0",
4680
+ private: true,
4681
+ type: "module",
4682
+ description: "A VortEX instance (created by `vortex init`)."
4683
+ }, null, 2) + "\n", "utf8");
4684
+ written.push(pkgPath);
4685
+ }
4686
+ return written;
4687
+ }
4558
4688
  async function runInit(input, tokens) {
4559
4689
  const args = parseInitArgs(tokens);
4560
- const { dataDir } = input.context;
4690
+ const { dataDir, repoRoot } = input.context;
4691
+ const templatesDir = resolveTemplatesDir();
4561
4692
  const requiredDirs = ["_memory", "worklog", "decision-log", "hubs", "inbox", "runbooks"];
4562
4693
  for (const d2 of requiredDirs) {
4563
4694
  const p = join22(dataDir, d2);
4564
4695
  if (!existsSync8(p))
4565
4696
  await mkdir5(p, { recursive: true });
4566
4697
  }
4698
+ const scaffolded = [];
4699
+ try {
4700
+ scaffolded.push(...await installRouterTemplates(repoRoot, templatesDir));
4701
+ } catch {
4702
+ }
4703
+ try {
4704
+ scaffolded.push(...await seedInstanceConfig(repoRoot, templatesDir));
4705
+ } catch {
4706
+ }
4567
4707
  const profilePath = join22(dataDir, "_memory", "user_profile.md");
4568
4708
  if (existsSync8(profilePath) && !args.force) {
4569
4709
  return {
4570
4710
  subcommand: "init",
4571
4711
  status: "already-initialized",
4572
- created: [],
4712
+ created: scaffolded,
4573
4713
  nextActions: [
4574
4714
  `VortEX instance is already initialized (${profilePath} exists).`,
4575
- "To re-run, pass `--force` (existing user_profile / first worklog / first hub will be overwritten).",
4715
+ scaffolded.length > 0 ? `Seeded ${scaffolded.length} missing scaffolding file(s): ${scaffolded.map((p) => p.replace(repoRoot, ".")).join(", ")}.` : "All scaffolding (routers, .agent/vortex.json, package.json) already present.",
4716
+ "To re-run, pass `--force` (existing user_profile / first worklog will be overwritten).",
4576
4717
  "To check current state, try `/session-start`."
4577
4718
  ]
4578
4719
  };
@@ -4610,7 +4751,12 @@ async function runInit(input, tokens) {
4610
4751
  };
4611
4752
  }
4612
4753
  const today2 = todayIso3();
4613
- const created = [];
4754
+ const created = [...scaffolded];
4755
+ const scaffoldNotes = [];
4756
+ if (scaffolded.length > 0) {
4757
+ const names = scaffolded.map((p) => p.replace(repoRoot, ".").replace(/\\/g, "/"));
4758
+ scaffoldNotes.push(`Seeded instance scaffolding: ${names.join(", ")}.`);
4759
+ }
4614
4760
  await writeFile10(profilePath, renderUserProfile(args.name, args.role, args.task, today2), "utf8");
4615
4761
  created.push(profilePath);
4616
4762
  const [year, month] = today2.split("-");
@@ -4636,7 +4782,7 @@ async function runInit(input, tokens) {
4636
4782
  hookNotes.push(`\u26A0\uFE0F Could not wire session hooks automatically: ${e.message} Copy .claude/settings.example.json to .claude/settings.json by hand to enable the boot report.`);
4637
4783
  }
4638
4784
  try {
4639
- const cmds = await installCommandTemplates(input.context.repoRoot);
4785
+ const cmds = await installCommandTemplates(input.context.repoRoot, templatesDir);
4640
4786
  created.push(...cmds);
4641
4787
  if (cmds.length > 0) {
4642
4788
  hookNotes.push(`Installed ${cmds.length} slash command(s) into .claude/commands/ (${cmds.map((c) => "/" + basename7(c, ".md")).join(", ")}).`);
@@ -4647,6 +4793,7 @@ async function runInit(input, tokens) {
4647
4793
  const externalFolders = await detectExternalFolders(input.context.repoRoot);
4648
4794
  const baseNext = [
4649
4795
  `Done. Created ${created.length} files.`,
4796
+ ...scaffoldNotes,
4650
4797
  ...hookNotes,
4651
4798
  "Next 3 things you can try right now:",
4652
4799
  " /log <one-line update> \u2014 append a section to today's worklog",
@@ -5653,7 +5800,7 @@ async function runSync(input, tokens) {
5653
5800
  var SYNC_TAIL_LENGTH = 1e3;
5654
5801
  async function runShellCommand(cmd, cmdArgs, cwd) {
5655
5802
  const start = Date.now();
5656
- return new Promise((resolve2) => {
5803
+ return new Promise((resolve4) => {
5657
5804
  let stdout = "";
5658
5805
  let stderr = "";
5659
5806
  const child = spawn(cmd, [...cmdArgs], { cwd, shell: true });
@@ -5664,7 +5811,7 @@ async function runShellCommand(cmd, cmdArgs, cwd) {
5664
5811
  stderr += chunk.toString("utf8");
5665
5812
  });
5666
5813
  child.on("close", (code) => {
5667
- resolve2({
5814
+ resolve4({
5668
5815
  exitCode: code ?? -1,
5669
5816
  durationMs: Date.now() - start,
5670
5817
  stdoutTail: tailString(stdout, SYNC_TAIL_LENGTH),
@@ -5672,7 +5819,7 @@ async function runShellCommand(cmd, cmdArgs, cwd) {
5672
5819
  });
5673
5820
  });
5674
5821
  child.on("error", (err) => {
5675
- resolve2({
5822
+ resolve4({
5676
5823
  exitCode: -1,
5677
5824
  durationMs: Date.now() - start,
5678
5825
  stdoutTail: tailString(stdout, SYNC_TAIL_LENGTH),
@@ -5864,43 +6011,16 @@ function createRitualRegistry(options) {
5864
6011
  return registry;
5865
6012
  }
5866
6013
 
5867
- // ../plugins/session-rituals/dist/ambient-recall.js
5868
- import { join as join23 } from "path";
5869
- function defaultDbPath2(ctx) {
5870
- return join23(ctx.dataDir, "_indexes", "memory.sqlite");
5871
- }
5872
- function createAmbientRecaller(ctx, options) {
5873
- const resolveDb = options.dbPath ?? defaultDbPath2;
5874
- const dbPath = resolveDb(ctx);
5875
- return new AmbientRecaller({
5876
- ...options.minScore !== void 0 ? { minScore: options.minScore } : {},
5877
- ...options.maxSuggestions !== void 0 ? { maxSuggestions: options.maxSuggestions } : {},
5878
- ...options.minQueryChars !== void 0 ? { minQueryChars: options.minQueryChars } : {},
5879
- recall: async (query, opts) => {
5880
- const { sqlite, vector, recall: recallEngine } = await import("@vortex-os/memory-extended");
5881
- const sqlStore = new sqlite.MemorySqliteStore(dbPath);
5882
- const vecStore = new vector.MemoryVectorStore({ db: dbPath });
5883
- const chunkStore = new vector.SessionChunkStore(dbPath);
5884
- try {
5885
- const result = await recallEngine.recall({
5886
- query,
5887
- ...opts?.k !== void 0 ? { k: opts.k } : {},
5888
- ...options.source !== void 0 ? { source: options.source } : {}
5889
- }, { sqlite: sqlStore, vector: vecStore, embed: options.embed, sessionChunks: chunkStore });
5890
- return { hits: result.hits };
5891
- } finally {
5892
- chunkStore.close();
5893
- vecStore.close();
5894
- sqlStore.close();
5895
- }
5896
- }
5897
- });
5898
- }
6014
+ // ../plugins/session-rituals/dist/cli-dispatch.js
6015
+ import { execFileSync } from "child_process";
6016
+ import { existsSync as existsSync10, readFileSync as readFileSync2 } from "fs";
6017
+ import { hostname } from "os";
6018
+ import { join as join25 } from "path";
5899
6019
 
5900
6020
  // ../plugins/session-rituals/dist/session-start-report.js
5901
6021
  import { existsSync as existsSync9 } from "fs";
5902
6022
  import { readdir as readdir16, readFile as readFile18 } from "fs/promises";
5903
- import { join as join24 } from "path";
6023
+ import { join as join23 } from "path";
5904
6024
  var COUNTED_DIRS2 = ["_memory", "worklog", "decision-log"];
5905
6025
  var DEFAULT_GAP_WINDOW_DAYS = 30;
5906
6026
  var BOOT_BANNER = String.raw`
@@ -5914,7 +6034,7 @@ async function collectSessionStartReport(ctx, opts) {
5914
6034
  const counts = {};
5915
6035
  const missing = [];
5916
6036
  for (const name of COUNTED_DIRS2) {
5917
- const dir = join24(ctx.dataDir, name);
6037
+ const dir = join23(ctx.dataDir, name);
5918
6038
  if (!existsSync9(dir)) {
5919
6039
  missing.push(name);
5920
6040
  counts[name] = 0;
@@ -5986,13 +6106,13 @@ async function countMarkdown3(dir, recursive) {
5986
6106
  } else if (e.isDirectory() && recursive) {
5987
6107
  if (e.name.startsWith(".") || e.name.startsWith("_"))
5988
6108
  continue;
5989
- total += await countMarkdown3(join24(dir, e.name), recursive);
6109
+ total += await countMarkdown3(join23(dir, e.name), recursive);
5990
6110
  }
5991
6111
  }
5992
6112
  return total;
5993
6113
  }
5994
6114
  async function scanWorklog(dataDir) {
5995
- const root = join24(dataDir, "worklog");
6115
+ const root = join23(dataDir, "worklog");
5996
6116
  if (!existsSync9(root))
5997
6117
  return { recent: null, dates: [] };
5998
6118
  let bestRel = null;
@@ -6007,7 +6127,7 @@ async function scanWorklog(dataDir) {
6007
6127
  for (const e of entries) {
6008
6128
  const childRel = rel ? `${rel}/${e.name}` : e.name;
6009
6129
  if (e.isDirectory()) {
6010
- await walk5(join24(absDir, e.name), childRel);
6130
+ await walk5(join23(absDir, e.name), childRel);
6011
6131
  } else if (e.isFile()) {
6012
6132
  const m2 = e.name.match(/^(\d{4}-\d{2}-\d{2})-.+\.md$/);
6013
6133
  if (!m2)
@@ -6019,7 +6139,7 @@ async function scanWorklog(dataDir) {
6019
6139
  }
6020
6140
  }
6021
6141
  await walk5(root, "");
6022
- const recent = bestRel === null ? null : { path: `worklog/${bestRel}`, title: await readTitle(join24(root, bestRel)) };
6142
+ const recent = bestRel === null ? null : { path: `worklog/${bestRel}`, title: await readTitle(join23(root, bestRel)) };
6023
6143
  return { recent, dates: [...dates] };
6024
6144
  }
6025
6145
  async function readTitle(absPath) {
@@ -6047,11 +6167,11 @@ function isoDate(d2) {
6047
6167
 
6048
6168
  // ../plugins/session-rituals/dist/worklog-write.js
6049
6169
  import { mkdir as mkdir6, writeFile as writeFile11 } from "fs/promises";
6050
- import { dirname as dirname5, join as join25 } from "path";
6170
+ import { dirname as dirname5, join as join24 } from "path";
6051
6171
  async function ensureWorklogEntry(ctx, opts) {
6052
6172
  const date = isoDate2(opts?.now ?? /* @__PURE__ */ new Date());
6053
6173
  const keyword = (opts?.keyword ?? "worklog").trim() || "worklog";
6054
- const store = new WorklogStore(join25(ctx.dataDir, "worklog"));
6174
+ const store = new WorklogStore(join24(ctx.dataDir, "worklog"));
6055
6175
  const existing = await store.get(date);
6056
6176
  if (existing) {
6057
6177
  return { path: existing.path, date: existing.date, keyword: existing.keyword, created: false };
@@ -6083,25 +6203,205 @@ function isoDate2(d2) {
6083
6203
  return `${y2}-${m2}-${day}`;
6084
6204
  }
6085
6205
 
6086
- // ../plugins/session-rituals/dist/catch-up.js
6087
- async function catchUpSessions(ctx, opts) {
6088
- const { sessionArchive } = await import("@vortex-os/memory-extended");
6089
- const dataDir = ctx.dataDir;
6090
- const cwd = opts?.cwd ?? ctx.repoRoot;
6091
- const adapters = opts?.adapters ?? [sessionArchive.claudeCodeAdapter];
6092
- const ingestResult = await sessionArchive.ingest({ adapters, dataDir, cwd, env: opts?.env });
6093
- const store = new sessionArchive.SessionArchiveStore(dataDir);
6094
- let indexedPulled = 0;
6206
+ // ../plugins/session-rituals/dist/cli-dispatch.js
6207
+ var VORTEX_SUBCOMMANDS = ["init", "status", "import", "doctor", "sync"];
6208
+ async function buildRegistry() {
6095
6209
  try {
6096
- indexedPulled = store.reindexFromNormalized().indexed;
6097
- } finally {
6098
- store.close();
6210
+ const { vector } = await import("@vortex-os/memory-extended");
6211
+ return createRitualRegistry({ recall: { embed: vector.createLocalEmbedder() } });
6212
+ } catch {
6213
+ return createRitualRegistry();
6099
6214
  }
6100
- return {
6101
- ingestedLocal: ingestResult.sessionsIngested,
6102
- indexedPulled,
6103
- errors: ingestResult.errors.length
6104
- };
6215
+ }
6216
+ function resolveRepoRoot() {
6217
+ return process.env.VORTEX_REPO_ROOT?.trim() || process.cwd();
6218
+ }
6219
+ function requote(token) {
6220
+ if (!/\s/.test(token))
6221
+ return token;
6222
+ if (token.includes('"') && !token.includes("'"))
6223
+ return `'${token}'`;
6224
+ return `"${token.replace(/"/g, "")}"`;
6225
+ }
6226
+ async function runVortexCli(argv, io) {
6227
+ const out = io?.stdout ?? ((s) => process.stdout.write(s));
6228
+ const err = io?.stderr ?? ((s) => process.stderr.write(s));
6229
+ const repoRoot = resolveRepoRoot();
6230
+ try {
6231
+ if (argv[0] === "session-start") {
6232
+ await runSessionStart(repoRoot, out);
6233
+ return 0;
6234
+ }
6235
+ if (argv[0] === "session-end") {
6236
+ await runSessionEnd(repoRoot, out);
6237
+ return 0;
6238
+ }
6239
+ const registry = await buildRegistry();
6240
+ if (argv.length === 0 || argv[0] === "--list" || argv[0] === "--help") {
6241
+ const names = registry.list().map((c) => ` ${c.name} \u2014 ${c.description}`).join("\n");
6242
+ err(`vortex \u2014 headless ritual runner
6243
+
6244
+ Commands:
6245
+ ${names}
6246
+ session-start \u2014 emit the start-of-session boot report (git pull + data counts + catch-up)
6247
+ session-end \u2014 worklog safety net (create today's worklog if work happened and none exists)
6248
+
6249
+ Instance shortcuts (also available as \`/vortex <sub>\`):
6250
+ init \u2014 first-time setup: routers + data/ + hooks + slash-commands
6251
+ status \u2014 instance state report
6252
+ import \u2014 bring an existing notes folder into data/
6253
+ doctor \u2014 health diagnosis
6254
+
6255
+ Usage: vortex <command> [args...]
6256
+ `);
6257
+ return 0;
6258
+ }
6259
+ const name = argv[0];
6260
+ const isVortexSub = VORTEX_SUBCOMMANDS.includes(name);
6261
+ const body = (isVortexSub ? argv : argv.slice(1)).map(requote).join(" ");
6262
+ const slash = isVortexSub ? `/vortex ${body}` : `/${name} ${body}`;
6263
+ const context = makeContext(repoRoot);
6264
+ const result = await runSlash(slash.trim(), { registry, context });
6265
+ out(JSON.stringify(result, null, 2) + "\n");
6266
+ return 0;
6267
+ } catch (e) {
6268
+ if (e instanceof CommandNotFoundError) {
6269
+ err(`[vortex] unknown command: ${e.message}
6270
+ Run \`vortex --list\` to see available commands.
6271
+ `);
6272
+ } else {
6273
+ err(`[vortex] error: ${e?.message ?? e}
6274
+ `);
6275
+ }
6276
+ return 1;
6277
+ }
6278
+ }
6279
+ async function runSessionStart(repoRoot, out) {
6280
+ const ctx = makeContext(repoRoot);
6281
+ const config = loadVortexConfig(ctx);
6282
+ if (!config.autoRecord.sessionStart)
6283
+ return;
6284
+ const environment = resolveSessionEnvironment(ctx, config);
6285
+ let git = null;
6286
+ try {
6287
+ const remotes = gitOut(repoRoot, ["remote"]).trim();
6288
+ if (remotes) {
6289
+ try {
6290
+ const pulled = gitOut(repoRoot, ["pull", "--ff-only"]);
6291
+ const lastLine = pulled.trim().split(/\r?\n/).pop() || "up to date";
6292
+ git = { ran: true, summary: lastLine, conflict: false };
6293
+ } catch {
6294
+ git = {
6295
+ ran: true,
6296
+ summary: "fast-forward pull failed (diverged or dirty tree)",
6297
+ conflict: true
6298
+ };
6299
+ }
6300
+ }
6301
+ } catch {
6302
+ }
6303
+ const report = await collectSessionStartReport(ctx, { environment });
6304
+ let missingWorklogDays = [];
6305
+ try {
6306
+ const log = gitOut(repoRoot, ["log", "--since=7 days ago", "--pretty=%cd", "--date=short"]);
6307
+ const commitDays = log.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
6308
+ missingWorklogDays = detectWorklogGaps(commitDays, report.recentWorklogDates);
6309
+ } catch {
6310
+ }
6311
+ let catchUp = null;
6312
+ if (config.autoRecord.archive) {
6313
+ try {
6314
+ const { catchUpSessions: catchUpSessions2 } = await import("./catch-up-ZQN7HMMN.js");
6315
+ catchUp = await catchUpSessions2(ctx);
6316
+ } catch {
6317
+ }
6318
+ }
6319
+ out(renderSessionStartReport(report, {
6320
+ git,
6321
+ missingWorklogDays,
6322
+ catchUp: catchUp ?? void 0
6323
+ }));
6324
+ }
6325
+ async function runSessionEnd(repoRoot, out) {
6326
+ const ctx = makeContext(repoRoot);
6327
+ const config = loadVortexConfig(ctx);
6328
+ if (config.autoRecord.worklog && hadActivityToday(repoRoot)) {
6329
+ const res = await ensureWorklogEntry(ctx, {
6330
+ body: "_Auto-created at session end (work detected but no worklog written). Enrich with the session's work, or remove if there is nothing to log._"
6331
+ });
6332
+ if (res.created)
6333
+ out(`VortEX: created worklog ${res.path}
6334
+ `);
6335
+ }
6336
+ }
6337
+ function gitOut(cwd, gitArgs) {
6338
+ return execFileSync("git", [...gitArgs], {
6339
+ cwd,
6340
+ encoding: "utf8",
6341
+ stdio: ["ignore", "pipe", "ignore"]
6342
+ });
6343
+ }
6344
+ function hadActivityToday(repoRoot) {
6345
+ try {
6346
+ const dirty = gitOut(repoRoot, ["status", "--porcelain"]).trim();
6347
+ if (dirty)
6348
+ return true;
6349
+ const since = /* @__PURE__ */ new Date();
6350
+ since.setHours(0, 0, 0, 0);
6351
+ const commits = gitOut(repoRoot, ["log", "--oneline", `--since=${since.toISOString()}`]).trim();
6352
+ return commits.length > 0;
6353
+ } catch {
6354
+ return false;
6355
+ }
6356
+ }
6357
+ function resolveSessionEnvironment(ctx, config) {
6358
+ let environment = resolveEnvironment(config, {
6359
+ hostname: hostname(),
6360
+ env: process.env,
6361
+ pathExists: existsSync10
6362
+ });
6363
+ if (!environment)
6364
+ environment = process.env.VORTEX_ENV?.trim() || null;
6365
+ if (!environment) {
6366
+ const envFile = join25(ctx.repoRoot, ".agent", "environment");
6367
+ if (existsSync10(envFile)) {
6368
+ environment = readFileSync2(envFile, "utf8").split(/\r?\n/)[0]?.trim() || null;
6369
+ }
6370
+ }
6371
+ return environment;
6372
+ }
6373
+
6374
+ // ../plugins/session-rituals/dist/ambient-recall.js
6375
+ import { join as join26 } from "path";
6376
+ function defaultDbPath2(ctx) {
6377
+ return join26(ctx.dataDir, "_indexes", "memory.sqlite");
6378
+ }
6379
+ function createAmbientRecaller(ctx, options) {
6380
+ const resolveDb = options.dbPath ?? defaultDbPath2;
6381
+ const dbPath = resolveDb(ctx);
6382
+ return new AmbientRecaller({
6383
+ ...options.minScore !== void 0 ? { minScore: options.minScore } : {},
6384
+ ...options.maxSuggestions !== void 0 ? { maxSuggestions: options.maxSuggestions } : {},
6385
+ ...options.minQueryChars !== void 0 ? { minQueryChars: options.minQueryChars } : {},
6386
+ recall: async (query, opts) => {
6387
+ const { sqlite, vector, recall: recallEngine } = await import("@vortex-os/memory-extended");
6388
+ const sqlStore = new sqlite.MemorySqliteStore(dbPath);
6389
+ const vecStore = new vector.MemoryVectorStore({ db: dbPath });
6390
+ const chunkStore = new vector.SessionChunkStore(dbPath);
6391
+ try {
6392
+ const result = await recallEngine.recall({
6393
+ query,
6394
+ ...opts?.k !== void 0 ? { k: opts.k } : {},
6395
+ ...options.source !== void 0 ? { source: options.source } : {}
6396
+ }, { sqlite: sqlStore, vector: vecStore, embed: options.embed, sessionChunks: chunkStore });
6397
+ return { hits: result.hits };
6398
+ } finally {
6399
+ chunkStore.close();
6400
+ vecStore.close();
6401
+ sqlStore.close();
6402
+ }
6403
+ }
6404
+ });
6105
6405
  }
6106
6406
  export {
6107
6407
  dist_exports5 as aiCodingPitfalls,