@chrisdudek/yg 2.0.0 → 2.1.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/README.md CHANGED
@@ -41,7 +41,7 @@ yg build-context --node orders/order-service
41
41
 
42
42
  **Diagnostics:**
43
43
 
44
- - `yg preflight [--quick]` — Unified diagnostic report: journal, drift, status, validation
44
+ - `yg preflight [--quick]` — Unified diagnostic report: drift, status, validation
45
45
 
46
46
  **Reading and analysis:**
47
47
 
@@ -61,12 +61,6 @@ yg build-context --node orders/order-service
61
61
  - `yg drift-sync --node <path> [--recursive]` — Record current file hash after resolving drift
62
62
  - `yg drift-sync --all` — Sync all nodes with mappings in one command
63
63
 
64
- **Session journal:**
65
-
66
- - `yg journal-add --note "..." [--target <node-path>]` — Buffer a decision note
67
- - `yg journal-read` — List pending journal entries
68
- - `yg journal-archive` — Archive journal after consolidation
69
-
70
64
  **Setup:**
71
65
 
72
66
  - `yg init --platform <name>` — Initialize `.yggdrasil/` structure (once per repository)
package/dist/bin.js CHANGED
@@ -54,14 +54,25 @@ import path from "path";
54
54
  // src/templates/rules.ts
55
55
  var CORE_PROTOCOL = `## CORE PROTOCOL
56
56
 
57
- Yggdrasil is persistent semantic memory stored in \`.yggdrasil/\`. It maps the repository and provides deterministic implementation context for every node. This document is your complete operating manual. Follow it strictly.
57
+ <EXTREMELY-IMPORTANT>
58
+ This is your operating manual for working in a Yggdrasil-managed repository. Every rule below is mandatory \u2014 no skill, plan, workflow, or instruction overrides these requirements.
59
+
60
+ BEFORE working with ANY source file, you MUST run \`yg owner\` to check if it is mapped. If mapped, you MUST use the graph before reading source. YOU DO NOT HAVE A CHOICE.
61
+
62
+ This is not negotiable. This is not optional. You cannot rationalize your way out of this.
63
+ </EXTREMELY-IMPORTANT>
64
+
65
+ Yggdrasil is persistent semantic memory stored in \`.yggdrasil/\`. It maps the repository and provides deterministic implementation context for every node.
58
66
 
59
67
  ### Quick Start Protocol
60
68
 
61
69
  \`\`\`
62
70
  BEFORE reading, researching, planning, OR modifying ANY mapped file:
63
71
  1. yg owner --file <path>
64
- 2. yg build-context --node <owner>
72
+ 2. Choose the right graph tool for your task:
73
+ - Understanding how/why it works \u2192 yg build-context --node <owner>
74
+ - Assessing what is affected by a change \u2192 yg impact --node <owner>
75
+ - Planning modifications \u2192 both (build-context first, then impact)
65
76
  The context package is your primary source of ARCHITECTURAL understanding:
66
77
  intent, constraints, relations, rationale. For IMPLEMENTATION precision
67
78
  (exact behavior, error handling, await patterns, edge cases) \u2014 verify
@@ -81,6 +92,8 @@ NEVER: modify code without graph coverage.
81
92
  NEVER: read mapped source files to understand a component without
82
93
  running yg build-context first \u2014 the graph captures intent,
83
94
  constraints, and relations that source files cannot.
95
+ NEVER: assess blast radius of a change without running yg impact first
96
+ \u2014 the graph knows the dependency structure that grep cannot infer.
84
97
  NEVER: invent rationale, business rules, or decisions.
85
98
  NEVER: auto-resolve drift without asking the user.
86
99
  WHEN UNSURE: ask the user. Never guess. Never assume.
@@ -88,7 +101,7 @@ WHEN UNSURE: ask the user. Never guess. Never assume.
88
101
 
89
102
  ### Five Core Rules
90
103
 
91
- 1. **Graph first.** Before reading, researching, planning, or modifying mapped files, run \`yg owner\` and \`yg build-context\`. Always. The context package is your primary source of architectural understanding. For implementation-level precision (exact behavior, error paths, edge cases) \u2014 verify against source code after loading the context package.
104
+ 1. **Graph first.** Before reading, researching, planning, or modifying mapped files, run \`yg owner\` and the appropriate graph tool: \`yg build-context\` to understand a component, \`yg impact\` to assess blast radius. The graph is your primary source of architectural understanding. For implementation-level precision (exact behavior, error paths, edge cases) \u2014 verify against source code after loading the context package.
92
105
  2. **Code and graph are one.** Code changed \u2192 graph updated in the same response. Graph changed \u2192 source verified in the same response. No exceptions.
93
106
  3. **Never invent why.** The graph captures human intent. If you don't know why something was decided, ask. Never hallucinate rationale.
94
107
  4. **Always capture why \u2014 especially why NOT.** When the user explains a reason, record it in the graph immediately. When a design choice is made, also record rejected alternatives: "Chose X over Y because Z." Rejected alternatives are the highest-value information \u2014 invisible in code and irrecoverable once forgotten. Conversation evaporates; graph persists.
@@ -96,15 +109,22 @@ WHEN UNSURE: ask the user. Never guess. Never assume.
96
109
 
97
110
  ### Recognizing Graph-Required Actions
98
111
 
99
- What matters is the ACTION you are performing, not what instructed it. If the action involves understanding mapped code, the graph protocol applies \u2014 whether the instruction came from a skill, a plan, a user message, a workflow step, or your own initiative.
112
+ What matters is the ACTION you are performing, not what instructed it. If the action involves reading, understanding, or modifying mapped code, the graph protocol applies \u2014 whether the instruction came from a skill, a plan, a user message, a brainstorming session, a debugging workflow, or your own initiative. This is not negotiable. You cannot rationalize your way out of this.
100
113
 
101
- **Actions that require \`yg owner\` + \`yg build-context\` first:**
114
+ **Actions that require \`yg owner\` + \`yg build-context\`:**
102
115
 
103
116
  - Reading or exploring source files to understand a component
104
117
  - Proposing approaches, designs, or plans for changing code
105
118
  - Reviewing or debugging code
106
119
  - Any form of reasoning about how mapped code works or should change
107
120
 
121
+ **Actions that require \`yg owner\` + \`yg impact\`:**
122
+
123
+ - Assessing blast radius before changing or removing a component
124
+ - Finding all dependents of a component
125
+ - Planning cross-cutting refactors or feature removals
126
+ - Scoping work that spans multiple nodes
127
+
108
128
  **Actions that do NOT require yg:**
109
129
 
110
130
  - Git operations (log, diff, status, blame)
@@ -112,11 +132,22 @@ What matters is the ACTION you are performing, not what instructed it. If the ac
112
132
  - Running tests, builds, or linters
113
133
  - Working with files that \`yg owner\` reports as unmapped
114
134
 
135
+ **Evasion patterns \u2014 if you think any of these, STOP:**
136
+
137
+ | Thought | Reality |
138
+ |---|---|
139
+ | "The skill/plan says to explore the codebase" | Exploring mapped code = yg owner + graph tool first |
140
+ | "I'm just scoping/searching, not understanding" | Scoping IS a graph action; use yg impact |
141
+ | "The plan step says to read this file" | Reading a mapped file = yg owner first |
142
+ | "I'm brainstorming, not implementing" | Brainstorming about mapped code needs graph context |
143
+ | "I'm only grepping for references" | Grep finds text; yg impact finds structural dependencies. Use both. |
144
+ | "I'll use the graph later when I modify" | Graph-first means BEFORE reading, not before modifying |
145
+
115
146
  ### Failure States
116
147
 
117
148
  You have broken Yggdrasil if you do any of the following:
118
149
 
119
- - \u274C Worked on a mapped file without running \`yg owner\` + \`yg build-context\` first \u2014 regardless of what instructed the action (skill, plan, user request, workflow step).
150
+ - \u274C Worked on a mapped file without running \`yg owner\` + the appropriate graph tool (\`build-context\` or \`impact\`) first \u2014 regardless of what instructed the action (skill, plan, user request, workflow step).
120
151
  - \u274C Modified source code without updating graph artifacts in the same response, or vice versa.
121
152
  - \u274C Resolved a code-graph inconsistency or ambiguity without asking the user first.
122
153
  - \u274C Created or edited a graph element without reading its schema in \`schemas/\` first.
@@ -161,9 +192,8 @@ var OPERATIONS = `## OPERATIONS
161
192
  \`\`\`
162
193
  PREFLIGHT (every conversation, before any work):
163
194
  - [ ] 1. yg preflight \u2192 read unified report
164
- - [ ] 2. If journal entries: consolidate to graph, then yg journal-archive
165
- - [ ] 3. If drift: resolve per Drift Resolution, then yg drift-sync per node
166
- - [ ] 4. If validation errors: fix, re-run yg validate
195
+ - [ ] 2. If drift: resolve per Drift Resolution, then yg drift-sync per node
196
+ - [ ] 3. If validation errors: fix, re-run yg validate
167
197
  Exception: read-only requests (explain, analyze) \u2014 skip preflight.
168
198
 
169
199
  UNDERSTANDING mapped code (questions, research, OR planning):
@@ -174,14 +204,14 @@ UNDERSTANDING mapped code (questions, research, OR planning):
174
204
  Raw reads supplement the context package \u2014 they do not replace it.
175
205
 
176
206
  WRAP-UP (user signals "done", "wrap up", "that's enough"):
177
- - [ ] 1. Consolidate journal if used \u2192 yg journal-archive
178
- - [ ] 2. yg drift --drifted-only \u2192 resolve
179
- - [ ] 3. yg validate \u2192 fix errors
180
- - [ ] 4. Report: which nodes and files were changed
207
+ - [ ] 1. yg drift --drifted-only \u2192 resolve
208
+ - [ ] 2. yg validate \u2192 fix errors
209
+ - [ ] 3. Report: which nodes and files were changed
181
210
 
182
211
  BEFORE ENDING ANY RESPONSE (self-audit):
212
+ - [ ] Did I interact with mapped code (read, research, or modify)? If yes \u2192 did I use a graph tool BEFORE reading source?
183
213
  - [ ] Did I modify source code? If yes \u2192 did I update graph artifacts in this same response?
184
- - [ ] If you changed code and did not sync the graph, you have broken the protocol. Do not finish until both are done.
214
+ - [ ] If you broke either rule, you have broken the protocol. Do not finish until both are fixed.
185
215
  \`\`\`
186
216
 
187
217
  ### Modify Source Code
@@ -319,8 +349,7 @@ var KNOWLEDGE_BASE = `## KNOWLEDGE BASE
319
349
  aspects/ \u2190 what must: cross-cutting requirements with rationale and guidance
320
350
  flows/ \u2190 why and in what process: business processes with node participation
321
351
  schemas/ \u2190 YAML schemas \u2014 read before creating any graph element
322
- .drift-state \u2190 generated by CLI; never edit manually
323
- .journal.yaml \u2190 generated by CLI; never edit manually
352
+ .drift-state/ \u2190 generated by CLI; never edit manually
324
353
  \`\`\`
325
354
 
326
355
  Key facts:
@@ -413,7 +442,7 @@ Test: "Does this describe what happens in the world, or only in the software?" I
413
442
  ### CLI Reference
414
443
 
415
444
  \`\`\`
416
- yg preflight [--quick] Unified diagnostic: journal + drift + status + validate.
445
+ yg preflight [--quick] Unified diagnostic: drift + status + validate.
417
446
  yg owner --file <path> Find the node that owns this file.
418
447
  yg build-context --node <path> Assemble context package for this node.
419
448
  yg tree [--root <path>] [--depth N] Print graph structure.
@@ -431,10 +460,6 @@ yg drift [--scope <path>|all] [--drifted-only] [--limit <n>]
431
460
  Detect source and graph drift (bidirectional).
432
461
  yg drift-sync --node <path> [--recursive] | --all
433
462
  Record file hashes as new baseline.
434
- yg journal-read Read pending journal entries.
435
- yg journal-add --note "<content>" [--target <node_path>]
436
- Add a journal entry.
437
- yg journal-archive Archive consolidated journal entries.
438
463
  \`\`\`
439
464
 
440
465
  ### Quick Routing Table
@@ -1000,10 +1025,7 @@ async function refreshSchemas(yggRoot) {
1000
1025
  } catch {
1001
1026
  }
1002
1027
  }
1003
- var GITIGNORE_CONTENT = `.journal.yaml
1004
- .drift-state
1005
- journals-archive/
1006
- `;
1028
+ var GITIGNORE_CONTENT = ``;
1007
1029
  function registerInitCommand(program2) {
1008
1030
  program2.command("init").description("Initialize Yggdrasil graph in current project").option(
1009
1031
  "--platform <name>",
@@ -2374,13 +2396,13 @@ function checkMappingOverlap(graph) {
2374
2396
  async function checkMappingPathsExist(graph) {
2375
2397
  const issues = [];
2376
2398
  const projectRoot = path11.dirname(graph.rootPath);
2377
- const { access: access5 } = await import("fs/promises");
2399
+ const { access: access4 } = await import("fs/promises");
2378
2400
  for (const [nodePath, node] of graph.nodes) {
2379
2401
  const mappingPaths = normalizeMappingPaths(node.meta.mapping);
2380
2402
  for (const mp of mappingPaths) {
2381
2403
  const absPath = path11.join(projectRoot, mp);
2382
2404
  try {
2383
- await access5(absPath);
2405
+ await access4(absPath);
2384
2406
  } catch {
2385
2407
  issues.push({
2386
2408
  severity: "warning",
@@ -2943,13 +2965,90 @@ ${errors.length} errors, ${warnings.length} warnings.
2943
2965
  import chalk2 from "chalk";
2944
2966
 
2945
2967
  // src/io/drift-state-store.ts
2946
- import { readFile as readFile14, writeFile as writeFile4 } from "fs/promises";
2968
+ import { readFile as readFile14, writeFile as writeFile4, stat as stat5, readdir as readdir6, mkdir as mkdir3, rm as rm2 } from "fs/promises";
2947
2969
  import path12 from "path";
2948
2970
  import { parse as yamlParse } from "yaml";
2949
- var DRIFT_STATE_FILE = ".drift-state";
2971
+ var DRIFT_STATE_DIR = ".drift-state";
2972
+ function nodeStatePath(yggRoot, nodePath) {
2973
+ return path12.join(yggRoot, DRIFT_STATE_DIR, `${nodePath}.json`);
2974
+ }
2975
+ async function scanJsonFiles(dir, baseDir) {
2976
+ const results = [];
2977
+ let entries;
2978
+ try {
2979
+ entries = await readdir6(dir, { withFileTypes: true });
2980
+ } catch {
2981
+ return results;
2982
+ }
2983
+ for (const entry of entries) {
2984
+ const fullPath = path12.join(dir, entry.name);
2985
+ if (entry.isDirectory()) {
2986
+ const nested = await scanJsonFiles(fullPath, baseDir);
2987
+ results.push(...nested);
2988
+ } else if (entry.isFile() && entry.name.endsWith(".json")) {
2989
+ const relPath = path12.relative(baseDir, fullPath);
2990
+ const nodePath = relPath.replace(/\\/g, "/").replace(/\.json$/, "");
2991
+ results.push(nodePath);
2992
+ }
2993
+ }
2994
+ return results;
2995
+ }
2996
+ async function removeEmptyParents(filePath, stopDir) {
2997
+ let dir = path12.dirname(filePath);
2998
+ while (dir !== stopDir && dir.startsWith(stopDir)) {
2999
+ try {
3000
+ const entries = await readdir6(dir);
3001
+ if (entries.length === 0) {
3002
+ await rm2(dir, { recursive: true });
3003
+ dir = path12.dirname(dir);
3004
+ } else {
3005
+ break;
3006
+ }
3007
+ } catch {
3008
+ break;
3009
+ }
3010
+ }
3011
+ }
3012
+ async function readNodeDriftState(yggRoot, nodePath) {
3013
+ try {
3014
+ const filePath = nodeStatePath(yggRoot, nodePath);
3015
+ const content = await readFile14(filePath, "utf-8");
3016
+ const parsed = JSON.parse(content);
3017
+ return parsed;
3018
+ } catch {
3019
+ return void 0;
3020
+ }
3021
+ }
3022
+ async function writeNodeDriftState(yggRoot, nodePath, nodeState) {
3023
+ const filePath = nodeStatePath(yggRoot, nodePath);
3024
+ await mkdir3(path12.dirname(filePath), { recursive: true });
3025
+ const content = JSON.stringify(nodeState, null, 2) + "\n";
3026
+ await writeFile4(filePath, content, "utf-8");
3027
+ }
3028
+ async function garbageCollectDriftState(yggRoot, validNodePaths) {
3029
+ const driftDir = path12.join(yggRoot, DRIFT_STATE_DIR);
3030
+ const allNodePaths = await scanJsonFiles(driftDir, driftDir);
3031
+ const removed = [];
3032
+ for (const nodePath of allNodePaths) {
3033
+ if (!validNodePaths.has(nodePath)) {
3034
+ const filePath = nodeStatePath(yggRoot, nodePath);
3035
+ await rm2(filePath);
3036
+ await removeEmptyParents(filePath, driftDir);
3037
+ removed.push(nodePath);
3038
+ }
3039
+ }
3040
+ return removed.sort();
3041
+ }
2950
3042
  async function readDriftState(yggRoot) {
3043
+ const driftPath = path12.join(yggRoot, DRIFT_STATE_DIR);
3044
+ let driftStat;
2951
3045
  try {
2952
- const content = await readFile14(path12.join(yggRoot, DRIFT_STATE_FILE), "utf-8");
3046
+ driftStat = await stat5(driftPath);
3047
+ } catch {
3048
+ return {};
3049
+ }
3050
+ if (driftStat.isFile()) {
3051
+ const content = await readFile14(driftPath, "utf-8");
2953
3052
  let raw;
2954
3053
  try {
2955
3054
  raw = JSON.parse(content);
@@ -2957,24 +3056,31 @@ async function readDriftState(yggRoot) {
2957
3056
  raw = yamlParse(content);
2958
3057
  }
2959
3058
  if (!raw || typeof raw !== "object") return {};
2960
- const state = {};
3059
+ const state2 = {};
2961
3060
  for (const [key, value] of Object.entries(raw)) {
2962
3061
  if (typeof value === "object" && value !== null && "hash" in value) {
2963
- state[key] = value;
3062
+ state2[key] = value;
2964
3063
  }
2965
3064
  }
2966
- return state;
2967
- } catch {
2968
- return {};
3065
+ await rm2(driftPath);
3066
+ for (const [nodePath, nodeState] of Object.entries(state2)) {
3067
+ await writeNodeDriftState(yggRoot, nodePath, nodeState);
3068
+ }
3069
+ return state2;
2969
3070
  }
2970
- }
2971
- async function writeDriftState(yggRoot, state) {
2972
- const content = JSON.stringify(state);
2973
- await writeFile4(path12.join(yggRoot, DRIFT_STATE_FILE), content, "utf-8");
3071
+ const nodePaths = await scanJsonFiles(driftPath, driftPath);
3072
+ const state = {};
3073
+ for (const nodePath of nodePaths) {
3074
+ const nodeState = await readNodeDriftState(yggRoot, nodePath);
3075
+ if (nodeState) {
3076
+ state[nodePath] = nodeState;
3077
+ }
3078
+ }
3079
+ return state;
2974
3080
  }
2975
3081
 
2976
3082
  // src/utils/hash.ts
2977
- import { readFile as readFile15, readdir as readdir6, stat as stat5 } from "fs/promises";
3083
+ import { readFile as readFile15, readdir as readdir7, stat as stat6 } from "fs/promises";
2978
3084
  import path13 from "path";
2979
3085
  import { createHash } from "crypto";
2980
3086
  import { createRequire } from "module";
@@ -3014,7 +3120,7 @@ async function hashTrackedFiles(projectRoot, trackedFiles, storedFileData, exclu
3014
3120
  for (const tf of trackedFiles) {
3015
3121
  const absPath = path13.join(projectRoot, tf.path);
3016
3122
  try {
3017
- const st = await stat5(absPath);
3123
+ const st = await stat6(absPath);
3018
3124
  if (st.isDirectory()) {
3019
3125
  const dirEntries = await collectDirectoryFilePaths(absPath, absPath, {
3020
3126
  projectRoot,
@@ -3068,7 +3174,7 @@ async function collectDirectoryFilePaths(directoryPath, rootDirectoryPath, optio
3068
3174
  stack = [...stack, { basePath: directoryPath, matcher: localMatcher }];
3069
3175
  } catch {
3070
3176
  }
3071
- const entries = await readdir6(directoryPath, { withFileTypes: true });
3177
+ const entries = await readdir7(directoryPath, { withFileTypes: true });
3072
3178
  const dirs = [];
3073
3179
  const files = [];
3074
3180
  for (const entry of entries) {
@@ -3083,7 +3189,7 @@ async function collectDirectoryFilePaths(directoryPath, rootDirectoryPath, optio
3083
3189
  gitignoreStack: stack
3084
3190
  }))),
3085
3191
  Promise.all(files.map(async (f) => {
3086
- const fileStat = await stat5(f);
3192
+ const fileStat = await stat6(f);
3087
3193
  return {
3088
3194
  relPath: path13.relative(rootDirectoryPath, f),
3089
3195
  absPath: f,
@@ -3316,18 +3422,15 @@ async function syncDriftState(graph, nodePath) {
3316
3422
  if (!node.meta.mapping) throw new Error(`Node has no mapping: ${nodePath}`);
3317
3423
  const trackedFiles = collectTrackedFiles(node, graph);
3318
3424
  const excludePrefixes = getChildMappingExclusions(graph, nodePath);
3319
- const existingState = await readDriftState(graph.rootPath);
3320
- const existingEntry = existingState[nodePath];
3425
+ const existingEntry = await readNodeDriftState(graph.rootPath, nodePath);
3321
3426
  const storedFileData = existingEntry?.files ? { hashes: existingEntry.files, mtimes: existingEntry.mtimes ?? {} } : void 0;
3322
3427
  const { canonicalHash, fileHashes, fileMtimes } = await hashTrackedFiles(projectRoot, trackedFiles, storedFileData, excludePrefixes);
3323
3428
  const previousHash = existingEntry?.hash;
3324
- existingState[nodePath] = { hash: canonicalHash, files: fileHashes, mtimes: fileMtimes };
3325
- for (const key of Object.keys(existingState)) {
3326
- if (!graph.nodes.has(key)) {
3327
- delete existingState[key];
3328
- }
3329
- }
3330
- await writeDriftState(graph.rootPath, existingState);
3429
+ await writeNodeDriftState(graph.rootPath, nodePath, {
3430
+ hash: canonicalHash,
3431
+ files: fileHashes,
3432
+ mtimes: fileMtimes
3433
+ });
3331
3434
  return { previousHash, currentHash: canonicalHash };
3332
3435
  }
3333
3436
 
@@ -3509,6 +3612,14 @@ function registerDriftSyncCommand(program2) {
3509
3612
  `
3510
3613
  );
3511
3614
  }
3615
+ if (options.all) {
3616
+ const validPaths = new Set(nodesToSync);
3617
+ const removed = await garbageCollectDriftState(graph.rootPath, validPaths);
3618
+ for (const r of removed) {
3619
+ process.stdout.write(chalk3.dim(`Removed orphaned drift state: ${r}
3620
+ `));
3621
+ }
3622
+ }
3512
3623
  } catch (error) {
3513
3624
  process.stderr.write(`Error: ${error.message}
3514
3625
  `);
@@ -3625,10 +3736,10 @@ function registerTreeCommand(program2) {
3625
3736
  let roots;
3626
3737
  let showProjectName;
3627
3738
  if (options.root?.trim()) {
3628
- const path20 = options.root.trim().replace(/\/$/, "");
3629
- const node = graph.nodes.get(path20);
3739
+ const path19 = options.root.trim().replace(/\/$/, "");
3740
+ const node = graph.nodes.get(path19);
3630
3741
  if (!node) {
3631
- process.stderr.write(`Error: path '${path20}' not found
3742
+ process.stderr.write(`Error: path '${path19}' not found
3632
3743
  `);
3633
3744
  process.exit(1);
3634
3745
  }
@@ -3815,7 +3926,7 @@ function registerDepsCommand(program2) {
3815
3926
  }
3816
3927
 
3817
3928
  // src/core/graph-from-git.ts
3818
- import { mkdtemp, rm as rm2 } from "fs/promises";
3929
+ import { mkdtemp, rm as rm3 } from "fs/promises";
3819
3930
  import { tmpdir } from "os";
3820
3931
  import path18 from "path";
3821
3932
  import { execSync as execSync2 } from "child_process";
@@ -3841,7 +3952,7 @@ async function loadGraphFromRef(projectRoot, ref = "HEAD") {
3841
3952
  return null;
3842
3953
  } finally {
3843
3954
  if (tmpDir) {
3844
- await rm2(tmpDir, { recursive: true, force: true });
3955
+ await rm3(tmpDir, { recursive: true, force: true });
3845
3956
  }
3846
3957
  }
3847
3958
  }
@@ -3899,14 +4010,14 @@ function buildTransitiveChains(targetNode, direct, allDependents, reverse) {
3899
4010
  }
3900
4011
  const chains = [];
3901
4012
  for (const node of transitiveOnly) {
3902
- const path20 = [];
4013
+ const path19 = [];
3903
4014
  let current = node;
3904
4015
  while (current) {
3905
- path20.unshift(current);
4016
+ path19.unshift(current);
3906
4017
  current = parent.get(current);
3907
4018
  }
3908
- if (path20.length >= 3) {
3909
- chains.push(path20.slice(1).map((p) => `<- ${p}`).join(" "));
4019
+ if (path19.length >= 3) {
4020
+ chains.push(path19.slice(1).map((p) => `<- ${p}`).join(" "));
3910
4021
  }
3911
4022
  }
3912
4023
  return chains.sort();
@@ -4327,131 +4438,12 @@ function registerFlowsCommand(program2) {
4327
4438
  });
4328
4439
  }
4329
4440
 
4330
- // src/io/journal-store.ts
4331
- import { readFile as readFile16, writeFile as writeFile5, mkdir as mkdir3, rename as rename2, access as access4 } from "fs/promises";
4332
- import { parse as parseYaml8, stringify as stringifyYaml2 } from "yaml";
4333
- import path19 from "path";
4334
- var JOURNAL_FILE = ".journal.yaml";
4335
- var ARCHIVE_DIR = "journals-archive";
4336
- async function readJournal(yggRoot) {
4337
- const filePath = path19.join(yggRoot, JOURNAL_FILE);
4338
- try {
4339
- const content = await readFile16(filePath, "utf-8");
4340
- const raw = parseYaml8(content);
4341
- const entries = raw.entries ?? [];
4342
- return Array.isArray(entries) ? entries : [];
4343
- } catch {
4344
- return [];
4345
- }
4346
- }
4347
- async function appendJournalEntry(yggRoot, note, target) {
4348
- const entries = await readJournal(yggRoot);
4349
- const at = (/* @__PURE__ */ new Date()).toISOString();
4350
- const entry = target ? { at, target, note } : { at, note };
4351
- entries.push(entry);
4352
- const filePath = path19.join(yggRoot, JOURNAL_FILE);
4353
- const content = stringifyYaml2({ entries });
4354
- await writeFile5(filePath, content, "utf-8");
4355
- return entry;
4356
- }
4357
- async function archiveJournal(yggRoot) {
4358
- const journalPath = path19.join(yggRoot, JOURNAL_FILE);
4359
- try {
4360
- await access4(journalPath);
4361
- } catch {
4362
- return null;
4363
- }
4364
- const entries = await readJournal(yggRoot);
4365
- if (entries.length === 0) return null;
4366
- const archiveDir = path19.join(yggRoot, ARCHIVE_DIR);
4367
- await mkdir3(archiveDir, { recursive: true });
4368
- const now = /* @__PURE__ */ new Date();
4369
- const timestamp = `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}-${String(now.getUTCHours()).padStart(2, "0")}${String(now.getUTCMinutes()).padStart(2, "0")}${String(now.getUTCSeconds()).padStart(2, "0")}`;
4370
- const archiveName = `.journal.${timestamp}.yaml`;
4371
- const archivePath = path19.join(archiveDir, archiveName);
4372
- await rename2(journalPath, archivePath);
4373
- return { archiveName, entryCount: entries.length };
4374
- }
4375
-
4376
- // src/cli/journal-add.ts
4377
- function registerJournalAddCommand(program2) {
4378
- program2.command("journal-add").description("Add a note to the session journal").requiredOption("--note <text>", "Note content").option("--target <node-path>", "Node path this note relates to").action(async (options) => {
4379
- try {
4380
- const projectRoot = process.cwd();
4381
- const yggRoot = await findYggRoot(projectRoot);
4382
- await appendJournalEntry(yggRoot, options.note, options.target);
4383
- const entries = await readJournal(yggRoot);
4384
- process.stdout.write(`Note added to journal (${entries.length} entries total)
4385
- `);
4386
- } catch (error) {
4387
- process.stderr.write(`Error: ${error.message}
4388
- `);
4389
- process.exit(1);
4390
- }
4391
- });
4392
- }
4393
-
4394
- // src/cli/journal-read.ts
4395
- function registerJournalReadCommand(program2) {
4396
- program2.command("journal-read").description("List pending journal entries").action(async () => {
4397
- try {
4398
- const projectRoot = process.cwd();
4399
- const yggRoot = await findYggRoot(projectRoot);
4400
- const entries = await readJournal(yggRoot);
4401
- if (entries.length === 0) {
4402
- process.stdout.write("Session journal: empty (clean state)\n");
4403
- return;
4404
- }
4405
- process.stdout.write(`Session journal (${entries.length} entries):
4406
-
4407
- `);
4408
- for (const e of entries) {
4409
- const date = e.at.slice(0, 19).replace("T", " ");
4410
- const target = e.target ? ` ${e.target}` : "";
4411
- process.stdout.write(`[${date}]${target}
4412
- ${e.note}
4413
-
4414
- `);
4415
- }
4416
- } catch (error) {
4417
- process.stderr.write(`Error: ${error.message}
4418
- `);
4419
- process.exit(1);
4420
- }
4421
- });
4422
- }
4423
-
4424
- // src/cli/journal-archive.ts
4425
- function registerJournalArchiveCommand(program2) {
4426
- program2.command("journal-archive").description("Archive journal after consolidating notes to graph").action(async () => {
4427
- try {
4428
- const projectRoot = process.cwd();
4429
- const yggRoot = await findYggRoot(projectRoot);
4430
- const result = await archiveJournal(yggRoot);
4431
- if (!result) {
4432
- process.stdout.write("No active journal - nothing to archive.\n");
4433
- return;
4434
- }
4435
- process.stdout.write(
4436
- `Archived journal (${result.entryCount} entries) -> journals-archive/${result.archiveName}
4437
- `
4438
- );
4439
- } catch (error) {
4440
- process.stderr.write(`Error: ${error.message}
4441
- `);
4442
- process.exit(1);
4443
- }
4444
- });
4445
- }
4446
-
4447
4441
  // src/cli/preflight.ts
4448
4442
  function registerPreflightCommand(program2) {
4449
- program2.command("preflight").description("Unified diagnostic report: journal, drift, status, validation").option("--quick", "Skip drift detection for faster results").action(async (options) => {
4443
+ program2.command("preflight").description("Unified diagnostic report: drift, status, validation").option("--quick", "Skip drift detection for faster results").action(async (options) => {
4450
4444
  try {
4451
4445
  const cwd = process.cwd();
4452
4446
  const graph = await loadGraph(cwd);
4453
- const yggRoot = await findYggRoot(cwd);
4454
- const journalEntries = await readJournal(yggRoot);
4455
4447
  const driftedEntries = options.quick ? [] : (await detectDrift(graph)).entries.filter((e) => e.status !== "ok");
4456
4448
  const nodeCount = graph.nodes.size;
4457
4449
  const aspectCount = graph.aspects.length;
@@ -4466,16 +4458,6 @@ function registerPreflightCommand(program2) {
4466
4458
  const lines = [];
4467
4459
  lines.push("=== Preflight Report ===");
4468
4460
  lines.push("");
4469
- if (journalEntries.length === 0) {
4470
- lines.push("Journal: clean");
4471
- } else {
4472
- lines.push(`Journal: ${journalEntries.length} pending entries`);
4473
- for (const entry of journalEntries) {
4474
- const target = entry.target ? ` [${entry.target}]` : "";
4475
- lines.push(` - ${entry.note}${target}`);
4476
- }
4477
- }
4478
- lines.push("");
4479
4461
  if (options.quick) {
4480
4462
  lines.push("Drift: skipped (--quick)");
4481
4463
  } else if (driftedEntries.length === 0) {
@@ -4512,7 +4494,7 @@ function registerPreflightCommand(program2) {
4512
4494
  }
4513
4495
  lines.push("");
4514
4496
  process.stdout.write(lines.join("\n"));
4515
- const hasIssues = journalEntries.length > 0 || !options.quick && driftedEntries.length > 0 || errors.length > 0;
4497
+ const hasIssues = !options.quick && driftedEntries.length > 0 || errors.length > 0;
4516
4498
  process.exit(hasIssues ? 1 : 0);
4517
4499
  } catch (error) {
4518
4500
  process.stderr.write(`Error: ${error.message}
@@ -4543,9 +4525,6 @@ registerDepsCommand(program);
4543
4525
  registerImpactCommand(program);
4544
4526
  registerAspectsCommand(program);
4545
4527
  registerFlowsCommand(program);
4546
- registerJournalAddCommand(program);
4547
- registerJournalReadCommand(program);
4548
- registerJournalArchiveCommand(program);
4549
4528
  registerPreflightCommand(program);
4550
4529
  program.parse();
4551
4530
  //# sourceMappingURL=bin.js.map