@vortex-os/base 0.7.0 → 0.7.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 vortex-os-project
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vortex-os-project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -40,7 +40,7 @@ npx vortex init # scaffold the instance
40
40
 
41
41
  - the per-agent files — `AGENTS.md` (the thin Codex-CLI entry, auto-loaded by Codex and other `AGENTS.md`-aware tools) plus thin routers `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, all pointing at `AI-RULES.md` (the single source of truth for shared rules) — so any agent host finds VortEX's behavior contract (these are generic templates you personalize over time);
42
42
  - the `data/` skeleton (`_memory/`, `worklog/`, `decision-log/`, `runbooks/`, `hubs/`, `inbox/`), your user-profile memory, and today's first worklog;
43
- - `.claude/settings.json` with the session hooks wired as `npx --no-install -p @vortex-os/base vortex session-start` / `… session-end` (the `--no-install` flag and explicit `-p` package make the auto-firing hook fail closed rather than install or shadow), plus the agent-mediated slash-commands in `.claude/commands/`;
43
+ - `.claude/settings.json` with the session hooks wired as `npx --no-install vortex session-start` / `… session-end` (the `--no-install` flag makes the auto-firing hook fail closed rather than install on a cache miss; resolving the bare `vortex` bin — local `node_modules/.bin` first, then PATH — lets the `global-setup` hook fire from any folder, not only where a local install exists), plus the agent-mediated slash-commands in `.claude/commands/`;
44
44
  - `.agent/vortex.json` (auto-record config) and a minimal `package.json` with `"type":"module"` if none exists.
45
45
 
46
46
  `vortex import --from <folder>` brings an existing notes folder in — markdown is auto-classified (worklog / decision-log / …) and **attachments (PDFs, images, …) are copied byte-for-byte** into the same layout; credential files (`*.key`, `.env`, `secrets/` …) and oversized files are skipped and reported. As the framework improves, `vortex update` refreshes the installed templates **without ever overwriting a file you edited** (your edit is parked at `<file>.new`); `vortex doctor` checks instance health.
package/bin/vortex.mjs CHANGED
@@ -1,17 +1,17 @@
1
- #!/usr/bin/env node
2
- // `vortex` — the CLI shipped by @vortex-os/base.
3
- //
4
- // `npm i @vortex-os/base` puts this on the instance's path (node_modules/.bin),
5
- // so `npx vortex init` / `npx vortex session-start` / `npx vortex --list` work
6
- // without any monorepo checkout. It is a thin wrapper over the canonical
7
- // dispatch (`runVortexCli`), which is bundled into base from
8
- // `@vortex-os/session-rituals` — exactly one source of truth for the CLI logic.
9
- //
10
- // The dispatch lazily probes the optional `@vortex-os/memory-extended` add-on:
11
- // when it is installed alongside base, `/recall` lights up; on a lean base-only
12
- // install the probe is caught and the CLI runs with every other command.
13
-
14
- import { sessionRituals } from "../dist/index.js";
15
-
16
- const code = await sessionRituals.runVortexCli(process.argv.slice(2));
17
- process.exitCode = code;
1
+ #!/usr/bin/env node
2
+ // `vortex` — the CLI shipped by @vortex-os/base.
3
+ //
4
+ // `npm i @vortex-os/base` puts this on the instance's path (node_modules/.bin),
5
+ // so `npx vortex init` / `npx vortex session-start` / `npx vortex --list` work
6
+ // without any monorepo checkout. It is a thin wrapper over the canonical
7
+ // dispatch (`runVortexCli`), which is bundled into base from
8
+ // `@vortex-os/session-rituals` — exactly one source of truth for the CLI logic.
9
+ //
10
+ // The dispatch lazily probes the optional `@vortex-os/memory-extended` add-on:
11
+ // when it is installed alongside base, `/recall` lights up; on a lean base-only
12
+ // install the probe is caught and the CLI runs with every other command.
13
+
14
+ import { sessionRituals } from "../dist/index.js";
15
+
16
+ const code = await sessionRituals.runVortexCli(process.argv.slice(2));
17
+ process.exitCode = code;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  catchUpSessions
3
- } from "./chunk-3L5DLEGP.js";
3
+ } from "./chunk-7SNLVGBO.js";
4
4
  import "./chunk-PZ5AY32C.js";
5
5
  export {
6
6
  catchUpSessions
7
7
  };
8
- //# sourceMappingURL=catch-up-GDDKPZHJ.js.map
8
+ //# sourceMappingURL=catch-up-KIHTAUPX.js.map
@@ -26,4 +26,4 @@ async function catchUpSessions(ctx, opts) {
26
26
  export {
27
27
  catchUpSessions
28
28
  };
29
- //# sourceMappingURL=chunk-3L5DLEGP.js.map
29
+ //# sourceMappingURL=chunk-7SNLVGBO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../plugins/session-rituals/src/catch-up.ts"],"sourcesContent":["import type { ModuleContext } from \"@vortex-os/core\";\r\n// Type-only — erased at compile time, so importing it does NOT pull the\r\n// `@vortex-os/memory-extended` add-on (or its native sqlite/level deps) into\r\n// the module graph of consumers that only need the types. The runtime engine\r\n// is loaded lazily inside the function body via `await import(...)`.\r\nimport type { sessionArchive } from \"@vortex-os/memory-extended\";\r\n\r\n/**\r\n * Start-of-session \"catch-up\": fold conversation transcripts into the local\r\n * search archive without the user ever having to wrap up a session.\r\n *\r\n * Two sources, one pass:\r\n * - **local (a)** — this machine's own transcripts that are not archived yet,\r\n * read from every detected agent host's transcript store (Claude Code,\r\n * Codex, Gemini) and scoped to the current project. Because all hosts are\r\n * swept on every start, a single Claude Code session-start also folds in the\r\n * Codex/Gemini sessions you ran in the same project, into one archive.\r\n * - **pulled (b)** — transcripts created on another machine that arrived as\r\n * normalized text via git sync. Their text is present but this machine's\r\n * DB (local, derived, gitignored) has never indexed them.\r\n *\r\n * Text only — vectorization is deferred to recall/rebuild so session start\r\n * stays fast. The whole step is gated by `autoRecord.archive` at the call site\r\n * and is best-effort: callers should treat a thrown archive backend (e.g. the\r\n * native sqlite module not built) as \"skip\", never as a fatal start error.\r\n */\r\nexport interface CatchUpResult {\r\n /** Local transcripts newly archived this run (source a). */\r\n readonly ingestedLocal: number;\r\n /** Normalized transcripts from another machine newly indexed (source b). */\r\n readonly indexedPulled: number;\r\n /** Per-session ingest errors (source a). */\r\n readonly errors: number;\r\n}\r\n\r\nexport interface CatchUpOptions {\r\n /** Restrict local ingest to one project's transcripts. Default: `ctx.repoRoot`. */\r\n readonly cwd?: string;\r\n /**\r\n * Transcript adapters for local ingest. Default: all CLI hosts — Claude Code,\r\n * Codex, and Gemini. Each adapter's `detect()` returns false when its host is\r\n * absent, so registering all three is ~free on a machine that only uses one.\r\n * (Claude Desktop is opt-in — it needs the `classic-level` dependency — so it\r\n * is not in the default set; a caller can add it.) Tests inject fakes (or a\r\n * sandbox `env.home`) so the scan never touches the real home directory.\r\n */\r\n readonly adapters?: sessionArchive.IngestParams[\"adapters\"];\r\n /** Adapter environment override (e.g. a sandbox HOME). Tests use this. */\r\n readonly env?: sessionArchive.IngestParams[\"env\"];\r\n}\r\n\r\nexport async function catchUpSessions(\r\n ctx: ModuleContext,\r\n opts?: CatchUpOptions,\r\n): Promise<CatchUpResult> {\r\n // Lazy-load the optional add-on. Base ships without `memory-extended`; this\r\n // import resolves only when the add-on is installed alongside it. The call\r\n // site already gates this step on `autoRecord.archive` and treats a thrown\r\n // backend as \"skip\", so a missing add-on surfaces as a normal load error\r\n // the caller can catch.\r\n const { sessionArchive } = await import(\"@vortex-os/memory-extended\");\r\n\r\n const dataDir = ctx.dataDir;\r\n const cwd = opts?.cwd ?? ctx.repoRoot;\r\n const adapters = opts?.adapters ?? [\r\n sessionArchive.claudeCodeAdapter,\r\n sessionArchive.codexAdapter,\r\n sessionArchive.geminiAdapter,\r\n ];\r\n\r\n // (a) Ingest this machine's not-yet-archived transcripts for the current\r\n // project. Writes the normalized copy into the archive (which git syncs) and\r\n // a local DB row. Text only — no vectorization here.\r\n const ingestResult = await sessionArchive.ingest({ adapters, dataDir, cwd, env: opts?.env });\r\n\r\n // (b) Index normalized transcripts that arrived from another machine via git\r\n // pull — their text is on disk but this machine's DB has no row yet.\r\n const store = new sessionArchive.SessionArchiveStore(dataDir);\r\n let indexedPulled = 0;\r\n try {\r\n indexedPulled = store.reindexFromNormalized().indexed;\r\n } finally {\r\n store.close();\r\n }\r\n\r\n return {\r\n ingestedLocal: ingestResult.sessionsIngested,\r\n indexedPulled,\r\n errors: ingestResult.errors.length,\r\n };\r\n}\r\n"],"mappings":";AAmDA,eAAsB,gBACpB,KACA,MAAqB;AAOrB,QAAM,EAAE,eAAc,IAAK,MAAM,OAAO,4BAA4B;AAEpE,QAAM,UAAU,IAAI;AACpB,QAAM,MAAM,MAAM,OAAO,IAAI;AAC7B,QAAM,WAAW,MAAM,YAAY;IACjC,eAAe;IACf,eAAe;IACf,eAAe;;AAMjB,QAAM,eAAe,MAAM,eAAe,OAAO,EAAE,UAAU,SAAS,KAAK,KAAK,MAAM,IAAG,CAAE;AAI3F,QAAM,QAAQ,IAAI,eAAe,oBAAoB,OAAO;AAC5D,MAAI,gBAAgB;AACpB,MAAI;AACF,oBAAgB,MAAM,sBAAqB,EAAG;EAChD;AACE,UAAM,MAAK;EACb;AAEA,SAAO;IACL,eAAe,aAAa;IAC5B;IACA,QAAQ,aAAa,OAAO;;AAEhC;","names":[]}
package/dist/index.d.ts CHANGED
@@ -131,6 +131,21 @@ interface AutoRecordConfig {
131
131
  * `vectorize` is off, nothing runs regardless.
132
132
  */
133
133
  readonly vectorizeAutoDownload: boolean;
134
+ /**
135
+ * Auto-commit framework bookkeeping. When `vortex update` refreshes
136
+ * framework-owned files it also rewrites the ownership manifest
137
+ * (`data/.vortex/ownership.json`); on its own that leaves an uncommitted
138
+ * "trail" that the next session reports as carried-over work, even though it
139
+ * is plumbing the user never touched. With this on, `vortex update` commits
140
+ * exactly the framework files it changed (the manifest + the templates it
141
+ * refreshed) — and nothing else in the working tree — so an update leaves a
142
+ * clean tree. Best-effort: a non-git folder, or any git failure, is a silent
143
+ * no-op (the command still succeeds). On by default; set `false` to keep the
144
+ * commit manual (e.g. you prefer to review and commit framework changes
145
+ * yourself). Conflicts (`<file>.new`) are never auto-committed — those still
146
+ * wait for you to merge.
147
+ */
148
+ readonly commitFrameworkChanges: boolean;
134
149
  }
135
150
  /**
136
151
  * One environment label plus the signal that selects it. Rules are evaluated
@@ -2653,7 +2668,7 @@ declare function detectInterruptedGitOp(repoRoot: string): string | null;
2653
2668
  * null when there is nothing to report (clean tree / not a git repo). Exported
2654
2669
  * for tests. (decision-log 2026-06-04-session-recovery-two-tier.)
2655
2670
  */
2656
- declare function collectCarryover(repoRoot: string): {
2671
+ declare function collectCarryover(repoRoot: string, ignore?: (repoRelPosixPath: string) => boolean): {
2657
2672
  uncommitted: number;
2658
2673
  interrupted: string | null;
2659
2674
  } | null;
@@ -2669,8 +2684,8 @@ declare function collectCarryover(repoRoot: string): {
2669
2684
  * file read/write around them. Keeping them pure makes the merge unit-testable
2670
2685
  * and the "writes only what's missing" guarantee verifiable.
2671
2686
  */
2672
- declare const SESSION_START_COMMAND = "npx --no-install -p @vortex-os/base vortex session-start || exit 0";
2673
- declare const SESSION_END_COMMAND = "npx --no-install -p @vortex-os/base vortex session-end || exit 0";
2687
+ declare const SESSION_START_COMMAND = "npx --no-install vortex session-start || exit 0";
2688
+ declare const SESSION_END_COMMAND = "npx --no-install vortex session-end || exit 0";
2674
2689
  interface HookCommand {
2675
2690
  readonly type: "command";
2676
2691
  readonly command: string;
@@ -3084,8 +3099,14 @@ declare function detectWorklogGaps(commitDays: readonly string[], presentDates:
3084
3099
  * non-empty line. Pure: the hook runs git; this turns its stdout into the
3085
3100
  * Tier-1 carryover count. (`--porcelain` emits exactly one line per changed
3086
3101
  * path, including untracked, so a line count is the change count.)
3102
+ *
3103
+ * `ignore`, when given, drops paths it returns true for from the count — used to
3104
+ * exclude framework-generated bookkeeping (the ownership manifest under
3105
+ * `data/.vortex/`), which is auto-maintained plumbing the user never edits, so
3106
+ * it must not be reported as carried-over WIP. A line whose path can't be parsed
3107
+ * is counted (fail toward surfacing, not hiding).
3087
3108
  */
3088
- declare function countUncommitted(porcelain: string): number;
3109
+ declare function countUncommitted(porcelain: string, ignore?: (repoRelPosixPath: string) => boolean): number;
3089
3110
  /**
3090
3111
  * Render a session-start report as a compact markdown block for a host hook
3091
3112
  * to inject as session context. A pull conflict and any worklog gaps are
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  catchUpSessions
3
- } from "./chunk-3L5DLEGP.js";
3
+ } from "./chunk-7SNLVGBO.js";
4
4
  import {
5
5
  __export
6
6
  } from "./chunk-PZ5AY32C.js";
@@ -105,7 +105,7 @@ function moduleDir(ctx, moduleName) {
105
105
  import { existsSync, readFileSync } from "fs";
106
106
  import { join as join2 } from "path";
107
107
  var DEFAULT_CONFIG = {
108
- autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, vectorize: true, vectorizeAutoDownload: true },
108
+ autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true },
109
109
  updates: { check: "session" },
110
110
  environments: []
111
111
  };
@@ -147,11 +147,13 @@ function loadVortexConfig(ctx) {
147
147
  const check = rawCheck === void 0 ? "session" : typeof rawCheck === "string" && rawCheck.trim().toLowerCase() === "session" ? "session" : "off";
148
148
  const rawAuto = raw.autoRecord && typeof raw.autoRecord === "object" && !Array.isArray(raw.autoRecord) ? raw.autoRecord : {};
149
149
  const vectorizeAutoDownload = rawAuto.vectorizeAutoDownload === void 0 ? true : rawAuto.vectorizeAutoDownload === true;
150
+ const commitFrameworkChanges = rawAuto.commitFrameworkChanges === void 0 ? true : rawAuto.commitFrameworkChanges === true;
150
151
  return {
151
152
  autoRecord: {
152
153
  ...DEFAULT_CONFIG.autoRecord,
153
154
  ...raw.autoRecord ?? {},
154
- vectorizeAutoDownload
155
+ vectorizeAutoDownload,
156
+ commitFrameworkChanges
155
157
  },
156
158
  updates: { check },
157
159
  environments
@@ -4778,11 +4780,17 @@ import { basename as basename7, dirname as dirname5, extname as extname11, join
4778
4780
  import { fileURLToPath } from "url";
4779
4781
 
4780
4782
  // ../plugins/session-rituals/dist/ensure-hooks.js
4781
- var SESSION_START_COMMAND = "npx --no-install -p @vortex-os/base vortex session-start || exit 0";
4782
- var SESSION_END_COMMAND = "npx --no-install -p @vortex-os/base vortex session-end || exit 0";
4783
+ var SESSION_START_COMMAND = "npx --no-install vortex session-start || exit 0";
4784
+ var SESSION_END_COMMAND = "npx --no-install vortex session-end || exit 0";
4783
4785
  var LEGACY_COMMANDS = {
4784
- SessionStart: "npx --no-install -p @vortex-os/base vortex session-start",
4785
- SessionEnd: "npx --no-install -p @vortex-os/base vortex session-end"
4786
+ SessionStart: [
4787
+ "npx --no-install -p @vortex-os/base vortex session-start || exit 0",
4788
+ "npx --no-install -p @vortex-os/base vortex session-start"
4789
+ ],
4790
+ SessionEnd: [
4791
+ "npx --no-install -p @vortex-os/base vortex session-end || exit 0",
4792
+ "npx --no-install -p @vortex-os/base vortex session-end"
4793
+ ]
4786
4794
  };
4787
4795
  function parseSettings(text) {
4788
4796
  const trimmed = (text ?? "").trim();
@@ -4812,7 +4820,7 @@ function ensureVortexHooks(existing) {
4812
4820
  for (const g of src) {
4813
4821
  const hookList = [];
4814
4822
  for (const h of g.hooks ?? []) {
4815
- const migrated = h.command === legacy;
4823
+ const migrated = legacy.includes(h.command);
4816
4824
  const cmd = migrated ? command : h.command;
4817
4825
  if (cmd === command) {
4818
4826
  if (kept) {
@@ -5005,6 +5013,21 @@ var MANIFEST_NAME = "manifest.json";
5005
5013
  function ownershipManifestPath(ctx) {
5006
5014
  return join24(ctx.dataDir, ".vortex", "ownership.json");
5007
5015
  }
5016
+ function frameworkBookkeepingPrefix(ctx) {
5017
+ return toPosix(relative4(ctx.repoRoot, join24(ctx.dataDir, ".vortex"))) + "/";
5018
+ }
5019
+ function committableUpdatePaths(ctx, result) {
5020
+ const out = /* @__PURE__ */ new Set();
5021
+ for (const a of result.actions) {
5022
+ if (a.error)
5023
+ continue;
5024
+ if (a.action === "replace" || a.action === "restore" || a.action === "install" || a.action === "adopt") {
5025
+ out.add(a.path);
5026
+ }
5027
+ }
5028
+ out.add(toPosix(relative4(ctx.repoRoot, ownershipManifestPath(ctx))));
5029
+ return [...out];
5030
+ }
5008
5031
  function toPosix(p) {
5009
5032
  return p.split(sep4).join("/");
5010
5033
  }
@@ -5030,6 +5053,8 @@ function templateDestRelPath(templateRelPath) {
5030
5053
  const parts = templateRelPath.split("/");
5031
5054
  if (parts.length < 2)
5032
5055
  return null;
5056
+ if (parts.some((p) => p === ".." || p === "."))
5057
+ return null;
5033
5058
  const [top, ...rest] = parts;
5034
5059
  const tail = rest.join("/");
5035
5060
  if (top === "routers")
@@ -5608,6 +5633,41 @@ function buildNextActions(status, summary, actions, dryRun, fromVersion, toVersi
5608
5633
  return out;
5609
5634
  }
5610
5635
 
5636
+ // ../plugins/session-rituals/dist/git-commit.js
5637
+ import { execFileSync } from "child_process";
5638
+ function git(repoRoot, args) {
5639
+ return execFileSync("git", [...args], {
5640
+ cwd: repoRoot,
5641
+ encoding: "utf8",
5642
+ // GIT_LITERAL_PATHSPECS=1 makes every `-- <path>` a LITERAL filename, not a
5643
+ // pathspec: it disables glob magic (`*` `?` `[…]`) and `:(…)` prefixes. So
5644
+ // even a path carrying those bytes (e.g. from a malformed template index)
5645
+ // can only ever match the exact file named — never a wider set.
5646
+ env: { ...process.env, GIT_LITERAL_PATHSPECS: "1" },
5647
+ // Suppress git's own stderr; the caller treats a non-zero exit as "no commit".
5648
+ stdio: ["ignore", "pipe", "ignore"]
5649
+ });
5650
+ }
5651
+ function commitFrameworkPaths(repoRoot, paths, message) {
5652
+ if (paths.length === 0)
5653
+ return { committed: false, reason: "no-paths" };
5654
+ try {
5655
+ git(repoRoot, ["rev-parse", "--git-dir"]);
5656
+ } catch {
5657
+ return { committed: false, reason: "not-a-git-repo" };
5658
+ }
5659
+ try {
5660
+ const status = git(repoRoot, ["status", "--porcelain", "--", ...paths]).trim();
5661
+ if (!status)
5662
+ return { committed: false, reason: "nothing-to-commit" };
5663
+ git(repoRoot, ["add", "--", ...paths]);
5664
+ git(repoRoot, ["commit", "-m", message, "--", ...paths]);
5665
+ return { committed: true };
5666
+ } catch (e) {
5667
+ return { committed: false, reason: "git-error", error: e?.message ?? String(e) };
5668
+ }
5669
+ }
5670
+
5611
5671
  // ../plugins/session-rituals/dist/commands/vortex.js
5612
5672
  var PLANNED_SUBS = [];
5613
5673
  var vortexCommand = {
@@ -6033,10 +6093,25 @@ async function runUpdate(input, tokens) {
6033
6093
  const dryRun = tokens.includes("--dry-run");
6034
6094
  const adopt = parseAdoptArgs(tokens);
6035
6095
  const templatesDir = resolveTemplatesDir();
6036
- return runTemplatesUpdate(input.context, templatesDir, {
6096
+ const result = await runTemplatesUpdate(input.context, templatesDir, {
6037
6097
  dryRun,
6038
6098
  adopt: adopt.size > 0 ? adopt : void 0
6039
6099
  });
6100
+ if (dryRun || result.status === "no-manifest" || result.status === "no-templates")
6101
+ return result;
6102
+ if (!loadVortexConfig(input.context).autoRecord.commitFrameworkChanges)
6103
+ return result;
6104
+ const paths = committableUpdatePaths(input.context, result);
6105
+ const commit = commitFrameworkPaths(input.context.repoRoot, paths, `chore(vortex): sync framework templates to base ${result.toVersion ?? "?"}`);
6106
+ if (!commit.committed)
6107
+ return result;
6108
+ return {
6109
+ ...result,
6110
+ nextActions: [
6111
+ ...result.nextActions,
6112
+ `Committed the framework changes so nothing is left uncommitted (autoRecord.commitFrameworkChanges; set false to commit these yourself).`
6113
+ ]
6114
+ };
6040
6115
  }
6041
6116
  function parseAdoptArgs(tokens) {
6042
6117
  const adopt = /* @__PURE__ */ new Set();
@@ -7505,7 +7580,7 @@ function createRitualRegistry(options) {
7505
7580
  }
7506
7581
 
7507
7582
  // ../plugins/session-rituals/dist/cli-dispatch.js
7508
- import { execFileSync, spawn as spawn2 } from "child_process";
7583
+ import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
7509
7584
  import { existsSync as existsSync15, readFileSync as readFileSync4, mkdirSync, openSync, writeSync, closeSync, linkSync, rmSync, statSync } from "fs";
7510
7585
  import { createRequire } from "module";
7511
7586
  import { hostname } from "os";
@@ -7767,8 +7842,28 @@ function detectWorklogGaps(commitDays, presentDates) {
7767
7842
  const present = new Set(presentDates);
7768
7843
  return [...new Set(commitDays)].filter((d2) => d2 && !present.has(d2)).sort();
7769
7844
  }
7770
- function countUncommitted(porcelain) {
7771
- return porcelain.split(/\r?\n/).filter((l3) => l3.trim().length > 0).length;
7845
+ function porcelainPath(line) {
7846
+ const body = line.slice(3);
7847
+ if (!body)
7848
+ return null;
7849
+ const arrow = body.indexOf(" -> ");
7850
+ const raw = (arrow >= 0 ? body.slice(arrow + 4) : body).trim();
7851
+ if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"'))
7852
+ return raw.slice(1, -1);
7853
+ return raw;
7854
+ }
7855
+ function countUncommitted(porcelain, ignore) {
7856
+ const lines = porcelain.split(/\r?\n/).filter((l3) => l3.trim().length > 0);
7857
+ if (!ignore)
7858
+ return lines.length;
7859
+ let n = 0;
7860
+ for (const l3 of lines) {
7861
+ const p = porcelainPath(l3);
7862
+ if (p && ignore(p))
7863
+ continue;
7864
+ n++;
7865
+ }
7866
+ return n;
7772
7867
  }
7773
7868
  function renderSessionStartReport(report, extras) {
7774
7869
  const lines = [
@@ -7779,9 +7874,9 @@ function renderSessionStartReport(report, extras) {
7779
7874
  ];
7780
7875
  const env = report.environment ? ` \xB7 env: ${envLabel(report.environment)}` : "";
7781
7876
  lines.push(`- time: ${report.localTime ?? report.time}${env}`);
7782
- const git = extras?.git;
7783
- if (git?.ran) {
7784
- lines.push(git.conflict ? `- git: \u26A0\uFE0F ${git.summary} \u2014 resolve manually (not auto-resolved)` : `- git: ${git.summary}`);
7877
+ const git2 = extras?.git;
7878
+ if (git2?.ran) {
7879
+ lines.push(git2.conflict ? `- git: \u26A0\uFE0F ${git2.summary} \u2014 resolve manually (not auto-resolved)` : `- git: ${git2.summary}`);
7785
7880
  }
7786
7881
  const countStr = COUNTED_DIRS2.map((d2) => `${d2} ${report.counts[d2] ?? 0}`).join(" \xB7 ");
7787
7882
  const miss = report.missing.length ? ` (missing: ${report.missing.join(", ")})` : "";
@@ -8558,7 +8653,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8558
8653
  return;
8559
8654
  }
8560
8655
  cleanTmp();
8561
- const { vectorizeIndex } = await import("./vectorize-PN4Y7XMO.js");
8656
+ const { vectorizeIndex } = await import("./vectorize-RBDBTSTW.js");
8562
8657
  const result = await vectorizeIndex(ctx, { dbPath: tmpDb, allowDownload: true });
8563
8658
  const sqliteSpecifier = "better-sqlite3";
8564
8659
  const mod = await import(sqliteSpecifier);
@@ -8599,16 +8694,16 @@ async function runSessionStart(repoRoot, out) {
8599
8694
  if (!config.autoRecord.sessionStart)
8600
8695
  return;
8601
8696
  const environment = resolveSessionEnvironment(ctx, config);
8602
- let git = null;
8697
+ let git2 = null;
8603
8698
  try {
8604
8699
  const remotes = gitOut(repoRoot, ["remote"]).trim();
8605
8700
  if (remotes) {
8606
8701
  try {
8607
8702
  const pulled = gitOut(repoRoot, ["pull", "--ff-only"]);
8608
8703
  const lastLine = pulled.trim().split(/\r?\n/).pop() || "up to date";
8609
- git = { ran: true, summary: lastLine, conflict: false };
8704
+ git2 = { ran: true, summary: lastLine, conflict: false };
8610
8705
  } catch {
8611
- git = {
8706
+ git2 = {
8612
8707
  ran: true,
8613
8708
  summary: "fast-forward pull failed (diverged or dirty tree)",
8614
8709
  conflict: true
@@ -8617,7 +8712,8 @@ async function runSessionStart(repoRoot, out) {
8617
8712
  }
8618
8713
  } catch {
8619
8714
  }
8620
- const carryover = collectCarryover(repoRoot);
8715
+ const bookkeepingPrefix = frameworkBookkeepingPrefix(ctx);
8716
+ const carryover = collectCarryover(repoRoot, (p) => p.startsWith(bookkeepingPrefix));
8621
8717
  const report = await collectSessionStartReport(ctx, { environment });
8622
8718
  let missingWorklogDays = [];
8623
8719
  try {
@@ -8629,7 +8725,7 @@ async function runSessionStart(repoRoot, out) {
8629
8725
  let catchUp = null;
8630
8726
  if (config.autoRecord.archive) {
8631
8727
  try {
8632
- const { catchUpSessions: catchUpSessions2 } = await import("./catch-up-GDDKPZHJ.js");
8728
+ const { catchUpSessions: catchUpSessions2 } = await import("./catch-up-KIHTAUPX.js");
8633
8729
  catchUp = await catchUpSessions2(ctx);
8634
8730
  } catch {
8635
8731
  }
@@ -8648,7 +8744,7 @@ async function runSessionStart(repoRoot, out) {
8648
8744
  });
8649
8745
  if (action === "inline") {
8650
8746
  try {
8651
- const { vectorizeIndex } = await import("./vectorize-PN4Y7XMO.js");
8747
+ const { vectorizeIndex } = await import("./vectorize-RBDBTSTW.js");
8652
8748
  vectorized = await vectorizeIndex(ctx);
8653
8749
  } catch {
8654
8750
  }
@@ -8688,7 +8784,7 @@ async function runSessionStart(repoRoot, out) {
8688
8784
  } catch {
8689
8785
  }
8690
8786
  out(renderSessionStartReport(report, {
8691
- git,
8787
+ git: git2,
8692
8788
  missingWorklogDays,
8693
8789
  catchUp: catchUp ?? void 0,
8694
8790
  vectorized: vectorized ?? void 0,
@@ -8712,7 +8808,7 @@ async function runSessionEnd(repoRoot, out) {
8712
8808
  }
8713
8809
  }
8714
8810
  function gitOut(cwd, gitArgs) {
8715
- return execFileSync("git", [...gitArgs], {
8811
+ return execFileSync2("git", [...gitArgs], {
8716
8812
  cwd,
8717
8813
  encoding: "utf8",
8718
8814
  stdio: ["ignore", "pipe", "ignore"]
@@ -8740,11 +8836,11 @@ function detectInterruptedGitOp(repoRoot) {
8740
8836
  }
8741
8837
  return null;
8742
8838
  }
8743
- function collectCarryover(repoRoot) {
8839
+ function collectCarryover(repoRoot, ignore) {
8744
8840
  const interrupted = detectInterruptedGitOp(repoRoot);
8745
8841
  let uncommitted = 0;
8746
8842
  try {
8747
- uncommitted = countUncommitted(gitOut(repoRoot, ["status", "--porcelain"]));
8843
+ uncommitted = countUncommitted(gitOut(repoRoot, ["status", "--porcelain"]), ignore);
8748
8844
  } catch {
8749
8845
  }
8750
8846
  return uncommitted > 0 || interrupted ? { uncommitted, interrupted } : null;