@a-company/paradigm 3.1.2 → 3.1.4

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.
@@ -4,100 +4,8 @@
4
4
  import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import chalk from "chalk";
7
- var POST_COMMIT_HOOK = `#!/bin/sh
8
- # Paradigm post-commit hook - captures history from commits
9
- # Installed by: paradigm hooks install
10
-
11
- # Get the commit message and hash
12
- COMMIT_HASH=$(git rev-parse HEAD)
13
- COMMIT_MSG=$(git log -1 --pretty=%B)
14
-
15
- # Get changed files
16
- CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD)
17
-
18
- # Extract symbols from changed files (look for .purpose files)
19
- extract_symbols() {
20
- local symbols=""
21
- for file in $CHANGED_FILES; do
22
- # Check if there's a .purpose file in the directory
23
- dir=$(dirname "$file")
24
- while [ "$dir" != "." ]; do
25
- if [ -f "$dir/.purpose" ]; then
26
- # Extract feature/component names from .purpose
27
- purpose_symbols=$(grep -E '^(features|components|gates|flows):' "$dir/.purpose" -A 10 2>/dev/null | grep -E '^ - (name|id):' | sed 's/.*: //' | tr '\\n' ',' | sed 's/,$//')
28
- if [ -n "$purpose_symbols" ]; then
29
- symbols="$symbols,$purpose_symbols"
30
- fi
31
- break
32
- fi
33
- dir=$(dirname "$dir")
34
- done
35
- done
36
- echo "$symbols" | sed 's/^,//' | tr ',' '\\n' | sort -u | tr '\\n' ',' | sed 's/,$//'
37
- }
38
-
39
- SYMBOLS=$(extract_symbols)
40
-
41
- # Extract symbols from commit message Symbols: trailer
42
- MSG_SYMBOLS=$(echo "$COMMIT_MSG" | grep -E '^Symbols:' | sed 's/^Symbols: //' | tr -d ' ')
43
- if [ -n "$MSG_SYMBOLS" ]; then
44
- if [ -n "$SYMBOLS" ]; then
45
- SYMBOLS="$SYMBOLS,$MSG_SYMBOLS"
46
- else
47
- SYMBOLS="$MSG_SYMBOLS"
48
- fi
49
- # Deduplicate
50
- SYMBOLS=$(echo "$SYMBOLS" | tr ',' '\\n' | sort -u | tr '\\n' ',' | sed 's/,$//')
51
- fi
52
-
53
- # Determine intent from commit message
54
- determine_intent() {
55
- case "$COMMIT_MSG" in
56
- feat*|feature*|add*) echo "feature" ;;
57
- fix*|bug*) echo "fix" ;;
58
- refactor*) echo "refactor" ;;
59
- *) echo "feature" ;;
60
- esac
61
- }
62
-
63
- INTENT=$(determine_intent)
64
-
65
- # Record if we found symbols (from .purpose or commit message) and .paradigm/history exists
66
- if [ -n "$SYMBOLS" ] && [ -d ".paradigm/history" ]; then
67
- # Generate entry ID
68
- if [ -f ".paradigm/history/log.jsonl" ]; then
69
- COUNT=$(wc -l < ".paradigm/history/log.jsonl" | tr -d ' ')
70
- COUNT=$((COUNT + 1))
71
- else
72
- COUNT=1
73
- fi
74
- ID=$(printf "h%04d" $COUNT)
75
-
76
- # Create entry
77
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
78
- AUTHOR=$(git config user.name || echo "unknown")
79
7
 
80
- # Format symbols as JSON array
81
- SYMBOLS_JSON=$(echo "$SYMBOLS" | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')
82
-
83
- # Format files as JSON array
84
- FILES_JSON=$(echo "$CHANGED_FILES" | tr '\\n' ',' | sed 's/,$//' | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')
85
-
86
- # Write entry
87
- echo "{\\"id\\":\\"$ID\\",\\"ts\\":\\"$TIMESTAMP\\",\\"type\\":\\"implement\\",\\"symbols\\":[$SYMBOLS_JSON],\\"author\\":{\\"type\\":\\"human\\",\\"id\\":\\"$AUTHOR\\"},\\"commit\\":\\"$COMMIT_HASH\\",\\"intent\\":\\"$INTENT\\",\\"files\\":[$FILES_JSON],\\"description\\":\\"$(echo "$COMMIT_MSG" | head -1 | sed 's/"/\\\\"/g')\\"}" >> .paradigm/history/log.jsonl
88
-
89
- echo "[paradigm] History entry $ID recorded"
90
- fi
91
- `;
92
- var PRE_PUSH_HOOK = `#!/bin/sh
93
- # Paradigm pre-push hook - reindex history before pushing
94
- # Installed by: paradigm hooks install
95
-
96
- if [ -d ".paradigm/history" ] && [ -f ".paradigm/history/log.jsonl" ]; then
97
- echo "[paradigm] Reindexing history..."
98
- npx paradigm history reindex 2>/dev/null || true
99
- fi
100
- `;
8
+ // src/commands/hooks/generated-hooks.ts
101
9
  var CLAUDE_CODE_STOP_HOOK = `#!/bin/sh
102
10
  # Paradigm Claude Code Stop Hook (v2)
103
11
  # Validates paradigm compliance before allowing the agent to finish.
@@ -242,6 +150,7 @@ fi
242
150
  # --- Check 4: Aspect anchor files that no longer exist ---
243
151
  for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null); do
244
152
  if grep -q "anchors:" "$purpose_file" 2>/dev/null; then
153
+ purpose_dir=$(dirname "$purpose_file")
245
154
  in_anchors=false
246
155
  while IFS= read -r line; do
247
156
  case "$line" in
@@ -250,7 +159,8 @@ for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not
250
159
  if [ "$in_anchors" = true ]; then
251
160
  anchor_path=$(echo "$line" | sed 's/.*- //' | sed 's/:.*//' | tr -d ' ')
252
161
  if [ -n "$anchor_path" ]; then
253
- if [ ! -f "$anchor_path" ]; then
162
+ # Try relative to .purpose dir first, then project root
163
+ if [ ! -f "$purpose_dir/$anchor_path" ] && [ ! -f "./$anchor_path" ]; then
254
164
  VIOLATIONS="$VIOLATIONS
255
165
  - Aspect anchor '$anchor_path' in $purpose_file does not exist.
256
166
  Update the anchor or remove the stale aspect."
@@ -566,8 +476,8 @@ var CURSOR_STOP_HOOK = `#!/bin/sh
566
476
  # 4. Aspect anchor files that no longer exist
567
477
  # 5. Per-directory .purpose freshness (tracked via .pending-review)
568
478
  # 6. Aspect coverage advisory
569
- # 8. Blocking habits not satisfied (from paradigm_habits_check)
570
479
  # 7. Lore entry expected for significant sessions (3+ source files)
480
+ # 8. Blocking habits not satisfied (from paradigm_habits_check)
571
481
 
572
482
  # Read JSON from stdin (hook input)
573
483
  INPUT=$(cat)
@@ -576,7 +486,7 @@ INPUT=$(cat)
576
486
  if command -v jq >/dev/null 2>&1; then
577
487
  CWD=$(echo "$INPUT" | jq -r '.workspace_roots[0] // empty' 2>/dev/null)
578
488
  else
579
- CWD=$(echo "$INPUT" | grep -o '"workspace_roots"[[:space:]]*:[[:space:]]*\\\\["[^"]*"' | head -1 | sed 's/.*\\\\["//' | sed 's/"$//')
489
+ CWD=$(echo "$INPUT" | grep -o '"workspace_roots"[[:space:]]*:[[:space:]]*\\["[^"]*"' | head -1 | sed 's/.*\\["//' | sed 's/"$//')
580
490
  fi
581
491
 
582
492
  if [ -z "$CWD" ]; then
@@ -675,7 +585,7 @@ else
675
585
  case "$file" in
676
586
  *.ts|*.js|*.tsx|*.jsx|*.py|*.rs|*.go)
677
587
  if [ -f "$file" ]; then
678
- if grep -qE '\\\\.(get|post|put|patch|delete)\\\\s*\\\\(|router\\\\.|app\\\\.(get|post|put|delete)|@(Get|Post|Put|Delete)|#\\\\[actix_web::(get|post)' "$file" 2>/dev/null; then
588
+ if grep -qE '\\.(get|post|put|patch|delete)\\s*\\(|router\\.|app\\.(get|post|put|delete)|@(Get|Post|Put|Delete)|#\\[actix_web::(get|post)' "$file" 2>/dev/null; then
679
589
  ROUTE_FILES="$ROUTE_FILES $file"
680
590
  fi
681
591
  fi
@@ -695,6 +605,7 @@ fi
695
605
  # --- Check 4: Aspect anchor files that no longer exist ---
696
606
  for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null); do
697
607
  if grep -q "anchors:" "$purpose_file" 2>/dev/null; then
608
+ purpose_dir=$(dirname "$purpose_file")
698
609
  in_anchors=false
699
610
  while IFS= read -r line; do
700
611
  case "$line" in
@@ -703,7 +614,8 @@ for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not
703
614
  if [ "$in_anchors" = true ]; then
704
615
  anchor_path=$(echo "$line" | sed 's/.*- //' | sed 's/:.*//' | tr -d ' ')
705
616
  if [ -n "$anchor_path" ]; then
706
- if [ ! -f "$anchor_path" ]; then
617
+ # Try relative to .purpose dir first, then project root
618
+ if [ ! -f "$purpose_dir/$anchor_path" ] && [ ! -f "./$anchor_path" ]; then
707
619
  VIOLATIONS="$VIOLATIONS
708
620
  - Aspect anchor '$anchor_path' in $purpose_file does not exist.
709
621
  Update the anchor or remove the stale aspect."
@@ -1004,6 +916,102 @@ done
1004
916
  # Never block \u2014 exit 0
1005
917
  exit 0
1006
918
  `;
919
+
920
+ // src/commands/hooks/index.ts
921
+ var POST_COMMIT_HOOK = `#!/bin/sh
922
+ # Paradigm post-commit hook - captures history from commits
923
+ # Installed by: paradigm hooks install
924
+
925
+ # Get the commit message and hash
926
+ COMMIT_HASH=$(git rev-parse HEAD)
927
+ COMMIT_MSG=$(git log -1 --pretty=%B)
928
+
929
+ # Get changed files
930
+ CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD)
931
+
932
+ # Extract symbols from changed files (look for .purpose files)
933
+ extract_symbols() {
934
+ local symbols=""
935
+ for file in $CHANGED_FILES; do
936
+ # Check if there's a .purpose file in the directory
937
+ dir=$(dirname "$file")
938
+ while [ "$dir" != "." ]; do
939
+ if [ -f "$dir/.purpose" ]; then
940
+ # Extract feature/component names from .purpose
941
+ purpose_symbols=$(grep -E '^(features|components|gates|flows):' "$dir/.purpose" -A 10 2>/dev/null | grep -E '^ - (name|id):' | sed 's/.*: //' | tr '\\n' ',' | sed 's/,$//')
942
+ if [ -n "$purpose_symbols" ]; then
943
+ symbols="$symbols,$purpose_symbols"
944
+ fi
945
+ break
946
+ fi
947
+ dir=$(dirname "$dir")
948
+ done
949
+ done
950
+ echo "$symbols" | sed 's/^,//' | tr ',' '\\n' | sort -u | tr '\\n' ',' | sed 's/,$//'
951
+ }
952
+
953
+ SYMBOLS=$(extract_symbols)
954
+
955
+ # Extract symbols from commit message Symbols: trailer
956
+ MSG_SYMBOLS=$(echo "$COMMIT_MSG" | grep -E '^Symbols:' | sed 's/^Symbols: //' | tr -d ' ')
957
+ if [ -n "$MSG_SYMBOLS" ]; then
958
+ if [ -n "$SYMBOLS" ]; then
959
+ SYMBOLS="$SYMBOLS,$MSG_SYMBOLS"
960
+ else
961
+ SYMBOLS="$MSG_SYMBOLS"
962
+ fi
963
+ # Deduplicate
964
+ SYMBOLS=$(echo "$SYMBOLS" | tr ',' '\\n' | sort -u | tr '\\n' ',' | sed 's/,$//')
965
+ fi
966
+
967
+ # Determine intent from commit message
968
+ determine_intent() {
969
+ case "$COMMIT_MSG" in
970
+ feat*|feature*|add*) echo "feature" ;;
971
+ fix*|bug*) echo "fix" ;;
972
+ refactor*) echo "refactor" ;;
973
+ *) echo "feature" ;;
974
+ esac
975
+ }
976
+
977
+ INTENT=$(determine_intent)
978
+
979
+ # Record if we found symbols (from .purpose or commit message) and .paradigm/history exists
980
+ if [ -n "$SYMBOLS" ] && [ -d ".paradigm/history" ]; then
981
+ # Generate entry ID
982
+ if [ -f ".paradigm/history/log.jsonl" ]; then
983
+ COUNT=$(wc -l < ".paradigm/history/log.jsonl" | tr -d ' ')
984
+ COUNT=$((COUNT + 1))
985
+ else
986
+ COUNT=1
987
+ fi
988
+ ID=$(printf "h%04d" $COUNT)
989
+
990
+ # Create entry
991
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
992
+ AUTHOR=$(git config user.name || echo "unknown")
993
+
994
+ # Format symbols as JSON array
995
+ SYMBOLS_JSON=$(echo "$SYMBOLS" | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')
996
+
997
+ # Format files as JSON array
998
+ FILES_JSON=$(echo "$CHANGED_FILES" | tr '\\n' ',' | sed 's/,$//' | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')
999
+
1000
+ # Write entry
1001
+ echo "{\\"id\\":\\"$ID\\",\\"ts\\":\\"$TIMESTAMP\\",\\"type\\":\\"implement\\",\\"symbols\\":[$SYMBOLS_JSON],\\"author\\":{\\"type\\":\\"human\\",\\"id\\":\\"$AUTHOR\\"},\\"commit\\":\\"$COMMIT_HASH\\",\\"intent\\":\\"$INTENT\\",\\"files\\":[$FILES_JSON],\\"description\\":\\"$(echo "$COMMIT_MSG" | head -1 | sed 's/"/\\\\"/g')\\"}" >> .paradigm/history/log.jsonl
1002
+
1003
+ echo "[paradigm] History entry $ID recorded"
1004
+ fi
1005
+ `;
1006
+ var PRE_PUSH_HOOK = `#!/bin/sh
1007
+ # Paradigm pre-push hook - reindex history before pushing
1008
+ # Installed by: paradigm hooks install
1009
+
1010
+ if [ -d ".paradigm/history" ] && [ -f ".paradigm/history/log.jsonl" ]; then
1011
+ echo "[paradigm] Reindexing history..."
1012
+ npx paradigm history reindex 2>/dev/null || true
1013
+ fi
1014
+ `;
1007
1015
  async function hooksInstallCommand(options = {}) {
1008
1016
  const rootDir = process.cwd();
1009
1017
  const onlyClaudeCode = options.claudeCode && !options.postCommit && !options.prePush && !options.cursor;
@@ -3,7 +3,7 @@ import {
3
3
  hooksInstallCommand,
4
4
  hooksStatusCommand,
5
5
  hooksUninstallCommand
6
- } from "./chunk-AQZSUGL3.js";
6
+ } from "./chunk-6HZ7PZGG.js";
7
7
  import "./chunk-MO4EEYFW.js";
8
8
  export {
9
9
  hooksInstallCommand,
package/dist/index.js CHANGED
@@ -109,7 +109,7 @@ ${chalk2.magenta("\u2569 ")}${chalk2.cyan("\u2534 \u2534\u2534\u2514\u2500\u253
109
109
  program.name("paradigm").description("Unified developer tools ecosystem").version(VERSION).addHelpText("before", banner);
110
110
  program.command("init").description("Initialize Paradigm in the current project").option("-f, --force", "Overwrite existing files").option("--name <name>", "Project name").option("--ide <ide>", "Target IDE: cursor, copilot, windsurf, claude").option("--migrate", "Output migration prompt for existing IDE files").option("--quick", "Non-interactive mode with smart defaults").option("--dry-run", "Show what would be created without creating").action(initCommand);
111
111
  program.command("shift").description("Full project setup in one command (init + team init + scan + sync all IDEs + doctor)").option("-f, --force", "Reinitialize even if already setup").option("-q, --quick", "Skip slow operations (scan)").option("--verify", "Run health checks after setup").option("--ide <ide>", "Target specific IDE instead of all").option("--configure-models", "Force model configuration prompts for team agents").action(async (options) => {
112
- const { shiftCommand } = await import("./shift-JDBRTHWO.js");
112
+ const { shiftCommand } = await import("./shift-OCNF4575.js");
113
113
  await shiftCommand(options);
114
114
  });
115
115
  program.command("setup [path]").description("Interactive setup wizard for Paradigm").option("-y, --yes", "Accept all defaults (non-interactive)").option("-f, --force", "Overwrite existing .paradigm config").action(async (path2, options) => {
@@ -470,19 +470,19 @@ historyCmd.option("--json", "Output as JSON").action(async (options) => {
470
470
  });
471
471
  var hooksCmd = program.command("hooks").description("Git hooks for automatic history capture");
472
472
  hooksCmd.command("install").description("Install git hooks, Claude Code hooks, and Cursor hooks").option("-f, --force", "Overwrite existing hooks").option("--post-commit", "Only install post-commit hook").option("--pre-push", "Only install pre-push hook").option("--claude-code", "Only install Claude Code hooks (stop + pre-commit)").option("--cursor", "Only install Cursor hooks (.cursor/hooks.json)").action(async (options) => {
473
- const { hooksInstallCommand } = await import("./hooks-YXPQV4SP.js");
473
+ const { hooksInstallCommand } = await import("./hooks-QGUF77MB.js");
474
474
  await hooksInstallCommand(options);
475
475
  });
476
476
  hooksCmd.command("uninstall").description("Remove paradigm hooks (git hooks, or --cursor for Cursor hooks)").option("--cursor", "Remove Cursor hooks instead of git hooks").action(async (options) => {
477
- const { hooksUninstallCommand } = await import("./hooks-YXPQV4SP.js");
477
+ const { hooksUninstallCommand } = await import("./hooks-QGUF77MB.js");
478
478
  await hooksUninstallCommand(options);
479
479
  });
480
480
  hooksCmd.command("status").description("Check git hooks status").action(async () => {
481
- const { hooksStatusCommand } = await import("./hooks-YXPQV4SP.js");
481
+ const { hooksStatusCommand } = await import("./hooks-QGUF77MB.js");
482
482
  await hooksStatusCommand();
483
483
  });
484
484
  hooksCmd.action(async () => {
485
- const { hooksStatusCommand } = await import("./hooks-YXPQV4SP.js");
485
+ const { hooksStatusCommand } = await import("./hooks-QGUF77MB.js");
486
486
  await hooksStatusCommand();
487
487
  });
488
488
  var triageCmd = program.command("triage").description("Semantic error triage - incident management and pattern matching");
package/dist/mcp.js CHANGED
@@ -525,15 +525,15 @@ var require_sql_wasm = __commonJS({
525
525
  "undefined" != typeof __filename ? ya = __filename : ba && (ya = self.location.href);
526
526
  var za = "", Aa, Ba;
527
527
  if (ca) {
528
- var fs26 = __require("fs");
528
+ var fs27 = __require("fs");
529
529
  za = __dirname + "/";
530
530
  Ba = (a) => {
531
531
  a = Ca(a) ? new URL(a) : a;
532
- return fs26.readFileSync(a);
532
+ return fs27.readFileSync(a);
533
533
  };
534
534
  Aa = async (a) => {
535
535
  a = Ca(a) ? new URL(a) : a;
536
- return fs26.readFileSync(a, void 0);
536
+ return fs27.readFileSync(a, void 0);
537
537
  };
538
538
  1 < process.argv.length && (wa = process.argv[1].replace(/\\/g, "/"));
539
539
  process.argv.slice(2);
@@ -815,7 +815,7 @@ var require_sql_wasm = __commonJS({
815
815
  if (ca) {
816
816
  var b = Buffer.alloc(256), c = 0, d = process.stdin.fd;
817
817
  try {
818
- c = fs26.readSync(d, b, 0, 256);
818
+ c = fs27.readSync(d, b, 0, 256);
819
819
  } catch (e) {
820
820
  if (e.toString().includes("EOF")) c = 0;
821
821
  else throw e;
@@ -4291,8 +4291,8 @@ var SessionTracker = class {
4291
4291
  * Extract resource type from URI
4292
4292
  */
4293
4293
  extractResourceType(uri) {
4294
- const path30 = uri.replace("paradigm://", "");
4295
- const firstPart = path30.split("/")[0];
4294
+ const path31 = uri.replace("paradigm://", "");
4295
+ const firstPart = path31.split("/")[0];
4296
4296
  return firstPart || "unknown";
4297
4297
  }
4298
4298
  /**
@@ -7003,7 +7003,7 @@ function navigateExplore(config, target, rootDir) {
7003
7003
  }
7004
7004
  if (result.paths.length === 0) {
7005
7005
  const areaSymbols = Object.entries(config.symbols).filter(
7006
- ([sym, path30]) => sym.toLowerCase().includes(targetLower) || path30.toLowerCase().includes(targetLower)
7006
+ ([sym, path31]) => sym.toLowerCase().includes(targetLower) || path31.toLowerCase().includes(targetLower)
7007
7007
  ).slice(0, 10);
7008
7008
  result.paths = [...new Set(areaSymbols.map(([, p]) => p))];
7009
7009
  result.symbols = areaSymbols.map(([s]) => s);
@@ -12095,6 +12095,10 @@ async function handleTagsTool(name, args, ctx) {
12095
12095
  }
12096
12096
  }
12097
12097
 
12098
+ // ../paradigm-mcp/src/tools/purpose-portal.ts
12099
+ import * as fs21 from "fs";
12100
+ import * as path23 from "path";
12101
+
12098
12102
  // ../paradigm-mcp/src/utils/purpose-writer.ts
12099
12103
  import * as fs19 from "fs";
12100
12104
  import * as path21 from "path";
@@ -13067,6 +13071,21 @@ async function handleAddAspect(args, ctx, reloadContext2) {
13067
13071
  }
13068
13072
  }
13069
13073
  const filePath = resolvePurposeFilePath(purposeFile, ctx.rootDir);
13074
+ const purposeDir = path23.dirname(filePath);
13075
+ for (const anchor of anchors) {
13076
+ const anchorFile = anchor.replace(/:.*$/, "");
13077
+ const resolved = path23.resolve(purposeDir, anchorFile);
13078
+ if (!fs21.existsSync(resolved)) {
13079
+ const rootResolved = path23.resolve(ctx.rootDir, anchorFile);
13080
+ if (fs21.existsSync(rootResolved)) {
13081
+ const corrected = path23.relative(purposeDir, rootResolved);
13082
+ const idx = anchors.indexOf(anchor);
13083
+ anchors[idx] = anchor.replace(anchorFile, corrected);
13084
+ } else {
13085
+ return err(`Anchor file not found: "${anchorFile}". Anchors must be relative to the .purpose file directory (${purposeDir}).`);
13086
+ }
13087
+ }
13088
+ }
13070
13089
  const data = readPurposeFile(filePath);
13071
13090
  if (!data.aspects) data.aspects = {};
13072
13091
  const bareId = stripSymbolPrefix(id);
@@ -13466,12 +13485,12 @@ async function handleValidate(args, ctx) {
13466
13485
  }
13467
13486
 
13468
13487
  // ../paradigm-mcp/src/tools/pm.ts
13469
- import * as fs22 from "fs";
13470
- import * as path25 from "path";
13488
+ import * as fs23 from "fs";
13489
+ import * as path26 from "path";
13471
13490
 
13472
13491
  // ../paradigm-mcp/src/utils/habits-loader.ts
13473
- import * as fs21 from "fs";
13474
- import * as path23 from "path";
13492
+ import * as fs22 from "fs";
13493
+ import * as path24 from "path";
13475
13494
  import * as yaml13 from "js-yaml";
13476
13495
  var SEED_HABITS = [
13477
13496
  {
@@ -13578,7 +13597,7 @@ var SEED_HABITS = [
13578
13597
  var HABITS_CACHE_TTL_MS = 30 * 1e3;
13579
13598
  var habitsCache = /* @__PURE__ */ new Map();
13580
13599
  function loadHabits(rootDir) {
13581
- const absoluteRoot = path23.resolve(rootDir);
13600
+ const absoluteRoot = path24.resolve(rootDir);
13582
13601
  const cached = habitsCache.get(absoluteRoot);
13583
13602
  if (cached && Date.now() - cached.loadedAt < HABITS_CACHE_TTL_MS) {
13584
13603
  return cached.habits;
@@ -13593,16 +13612,16 @@ function loadHabitsFresh(rootDir) {
13593
13612
  habitsById.set(seed.id, { ...seed });
13594
13613
  }
13595
13614
  const home = process.env.HOME || process.env.USERPROFILE || "~";
13596
- const globalConfig = loadHabitsYaml(path23.join(home, ".paradigm", "habits.yaml"));
13615
+ const globalConfig = loadHabitsYaml(path24.join(home, ".paradigm", "habits.yaml"));
13597
13616
  if (globalConfig) mergeHabits(habitsById, globalConfig);
13598
- const projectConfig = loadHabitsYaml(path23.join(rootDir, ".paradigm", "habits.yaml"));
13617
+ const projectConfig = loadHabitsYaml(path24.join(rootDir, ".paradigm", "habits.yaml"));
13599
13618
  if (projectConfig) mergeHabits(habitsById, projectConfig);
13600
13619
  return Array.from(habitsById.values());
13601
13620
  }
13602
13621
  function loadHabitsYaml(filePath) {
13603
- if (!fs21.existsSync(filePath)) return null;
13622
+ if (!fs22.existsSync(filePath)) return null;
13604
13623
  try {
13605
- const content = fs21.readFileSync(filePath, "utf8");
13624
+ const content = fs22.readFileSync(filePath, "utf8");
13606
13625
  return yaml13.load(content);
13607
13626
  } catch {
13608
13627
  return null;
@@ -13749,7 +13768,7 @@ function evalFileModified(habit, ctx) {
13749
13768
  const patterns = habit.check.params.patterns || [];
13750
13769
  if (patterns.length === 0) return { habit, result: "followed", reason: "No patterns specified" };
13751
13770
  const matched = ctx.filesModified.filter(
13752
- (f) => patterns.some((p) => f.includes(p) || path23.basename(f) === p)
13771
+ (f) => patterns.some((p) => f.includes(p) || path24.basename(f) === p)
13753
13772
  );
13754
13773
  if (matched.length > 0) {
13755
13774
  return { habit, result: "followed", reason: `Matching files: ${matched.join(", ")}`, evidence: matched };
@@ -13768,12 +13787,12 @@ function evalGitClean(habit, ctx) {
13768
13787
  }
13769
13788
 
13770
13789
  // ../paradigm-mcp/src/utils/practice-store.ts
13771
- import * as path24 from "path";
13790
+ import * as path25 from "path";
13772
13791
  var storageInstance = null;
13773
13792
  var storageInitialized2 = false;
13774
13793
  async function getStorage2(rootDir) {
13775
13794
  if (!storageInstance) {
13776
- const dbPath = path24.join(rootDir, ".paradigm", "sentinel", "sentinel.db");
13795
+ const dbPath = path25.join(rootDir, ".paradigm", "sentinel", "sentinel.db");
13777
13796
  storageInstance = new SentinelStorage(dbPath);
13778
13797
  await storageInstance.ensureReady();
13779
13798
  storageInitialized2 = true;
@@ -14064,11 +14083,11 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14064
14083
  const violations = [];
14065
14084
  const declaredRoutes = ctx.gateConfig?.routes ? Object.keys(ctx.gateConfig.routes) : [];
14066
14085
  for (const file of filesModified) {
14067
- const absPath = path25.isAbsolute(file) ? file : path25.join(ctx.rootDir, file);
14068
- if (!fs22.existsSync(absPath)) continue;
14086
+ const absPath = path26.isAbsolute(file) ? file : path26.join(ctx.rootDir, file);
14087
+ if (!fs23.existsSync(absPath)) continue;
14069
14088
  let content;
14070
14089
  try {
14071
- content = fs22.readFileSync(absPath, "utf-8");
14090
+ content = fs23.readFileSync(absPath, "utf-8");
14072
14091
  } catch {
14073
14092
  continue;
14074
14093
  }
@@ -14086,8 +14105,8 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14086
14105
  violations.push({
14087
14106
  type: "missing-portal-gate",
14088
14107
  severity: "warning",
14089
- message: `Route "${routePath}" in ${path25.relative(ctx.rootDir, absPath)} not in portal.yaml`,
14090
- file: path25.relative(ctx.rootDir, absPath),
14108
+ message: `Route "${routePath}" in ${path26.relative(ctx.rootDir, absPath)} not in portal.yaml`,
14109
+ file: path26.relative(ctx.rootDir, absPath),
14091
14110
  suggestion: "Add route to portal.yaml with ^gates. Use paradigm_gates_for_route for suggestions."
14092
14111
  });
14093
14112
  } else if (!ctx.gateConfig && routePath.startsWith("/api/")) {
@@ -14095,7 +14114,7 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14095
14114
  type: "missing-portal-gate",
14096
14115
  severity: "warning",
14097
14116
  message: `API route "${routePath}" found but no portal.yaml exists`,
14098
- file: path25.relative(ctx.rootDir, absPath),
14117
+ file: path26.relative(ctx.rootDir, absPath),
14099
14118
  suggestion: "Create portal.yaml to declare gates for API routes."
14100
14119
  });
14101
14120
  }
@@ -14153,8 +14172,8 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14153
14172
  });
14154
14173
  } else {
14155
14174
  for (const anchor of anchors) {
14156
- const filePath = path25.isAbsolute(anchor.path) ? anchor.path : path25.join(ctx.rootDir, anchor.path);
14157
- if (!fs22.existsSync(filePath)) {
14175
+ const filePath = path26.isAbsolute(anchor.path) ? anchor.path : path26.join(ctx.rootDir, anchor.path);
14176
+ if (!fs23.existsSync(filePath)) {
14158
14177
  violations.push({
14159
14178
  type: "stale-aspect",
14160
14179
  severity: "warning",
@@ -14235,12 +14254,12 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14235
14254
  reason: e.reason
14236
14255
  }))
14237
14256
  };
14238
- const markerPath = path25.join(ctx.rootDir, ".paradigm", ".habits-blocking");
14257
+ const markerPath = path26.join(ctx.rootDir, ".paradigm", ".habits-blocking");
14239
14258
  if (evalResult.blocksCompletion) {
14240
14259
  const blocking = evalResult.evaluations.filter((e) => e.result === "skipped" && e.habit.severity === "block").map((e) => `${e.habit.name}: ${e.reason}`);
14241
- fs22.writeFileSync(markerPath, blocking.join("\n"), "utf8");
14242
- } else if (fs22.existsSync(markerPath)) {
14243
- fs22.unlinkSync(markerPath);
14260
+ fs23.writeFileSync(markerPath, blocking.join("\n"), "utf8");
14261
+ } else if (fs23.existsSync(markerPath)) {
14262
+ fs23.unlinkSync(markerPath);
14244
14263
  }
14245
14264
  } catch {
14246
14265
  }
@@ -14259,8 +14278,8 @@ function runPostflightCheck(filesModified, symbolsTouched, ctx) {
14259
14278
  }
14260
14279
 
14261
14280
  // ../paradigm-mcp/src/tools/reindex.ts
14262
- import * as fs23 from "fs";
14263
- import * as path26 from "path";
14281
+ import * as fs24 from "fs";
14282
+ import * as path27 from "path";
14264
14283
  import * as yaml14 from "js-yaml";
14265
14284
 
14266
14285
  // ../probe/core/dist/generator.js
@@ -14599,10 +14618,10 @@ async function rebuildStaticFiles(rootDir, ctx) {
14599
14618
  } else {
14600
14619
  aggregation = await aggregateFromDirectory(rootDir);
14601
14620
  }
14602
- const projectName = ctx?.projectName || path26.basename(rootDir);
14603
- const paradigmDir = path26.join(rootDir, ".paradigm");
14604
- if (!fs23.existsSync(paradigmDir)) {
14605
- fs23.mkdirSync(paradigmDir, { recursive: true });
14621
+ const projectName = ctx?.projectName || path27.basename(rootDir);
14622
+ const paradigmDir = path27.join(rootDir, ".paradigm");
14623
+ if (!fs24.existsSync(paradigmDir)) {
14624
+ fs24.mkdirSync(paradigmDir, { recursive: true });
14606
14625
  }
14607
14626
  const scanIndex = generateScanIndex(
14608
14627
  {
@@ -14612,12 +14631,12 @@ async function rebuildStaticFiles(rootDir, ctx) {
14612
14631
  },
14613
14632
  { projectName }
14614
14633
  );
14615
- const scanIndexPath = path26.join(paradigmDir, "scan-index.json");
14616
- fs23.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
14634
+ const scanIndexPath = path27.join(paradigmDir, "scan-index.json");
14635
+ fs24.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
14617
14636
  filesWritten.push(".paradigm/scan-index.json");
14618
14637
  const navigatorData = buildNavigatorData(rootDir, aggregation);
14619
- const navigatorPath = path26.join(paradigmDir, "navigator.yaml");
14620
- fs23.writeFileSync(
14638
+ const navigatorPath = path27.join(paradigmDir, "navigator.yaml");
14639
+ fs24.writeFileSync(
14621
14640
  navigatorPath,
14622
14641
  yaml14.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
14623
14642
  "utf8"
@@ -14626,8 +14645,8 @@ async function rebuildStaticFiles(rootDir, ctx) {
14626
14645
  const flowIndex = generateFlowIndex(rootDir, aggregation.purposeFiles);
14627
14646
  let flowCount = 0;
14628
14647
  if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
14629
- const flowIndexPath = path26.join(paradigmDir, "flow-index.json");
14630
- fs23.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
14648
+ const flowIndexPath = path27.join(paradigmDir, "flow-index.json");
14649
+ fs24.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
14631
14650
  filesWritten.push(".paradigm/flow-index.json");
14632
14651
  flowCount = Object.keys(flowIndex.flows).length;
14633
14652
  }
@@ -14656,7 +14675,7 @@ function buildNavigatorData(rootDir, aggregation) {
14656
14675
  function buildStructure(rootDir) {
14657
14676
  const structure = {};
14658
14677
  for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
14659
- const existingPaths = patterns.filter((p) => fs23.existsSync(path26.join(rootDir, p)));
14678
+ const existingPaths = patterns.filter((p) => fs24.existsSync(path27.join(rootDir, p)));
14660
14679
  if (existingPaths.length > 0) {
14661
14680
  const symbolInfo = Object.values(SYMBOL_CATEGORIES).find((s) => s.category === category);
14662
14681
  structure[category] = { paths: existingPaths, symbol: symbolInfo?.prefix || "@" };
@@ -14667,7 +14686,7 @@ function buildStructure(rootDir) {
14667
14686
  function buildKeyFiles(rootDir) {
14668
14687
  const keyFiles = {};
14669
14688
  for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
14670
- const existingPaths = patterns.filter((p) => fs23.existsSync(path26.join(rootDir, p)));
14689
+ const existingPaths = patterns.filter((p) => fs24.existsSync(path27.join(rootDir, p)));
14671
14690
  if (existingPaths.length > 0) {
14672
14691
  keyFiles[category] = existingPaths;
14673
14692
  }
@@ -14683,10 +14702,10 @@ function buildSkipPatterns(rootDir) {
14683
14702
  unless_testing: [...DEFAULT_SKIP_PATTERNS.unless_testing],
14684
14703
  unless_docs: [...DEFAULT_SKIP_PATTERNS.unless_docs]
14685
14704
  };
14686
- const gitignorePath = path26.join(rootDir, ".gitignore");
14687
- if (fs23.existsSync(gitignorePath)) {
14705
+ const gitignorePath = path27.join(rootDir, ".gitignore");
14706
+ if (fs24.existsSync(gitignorePath)) {
14688
14707
  try {
14689
- const content = fs23.readFileSync(gitignorePath, "utf8");
14708
+ const content = fs24.readFileSync(gitignorePath, "utf8");
14690
14709
  const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter(
14691
14710
  (line) => line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some((p) => line.includes(p))
14692
14711
  ).slice(0, 20);
@@ -14735,11 +14754,11 @@ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
14735
14754
  symbolMap[symbolId] = symbol.filePath;
14736
14755
  } else {
14737
14756
  const matchingPurpose = purposeFiles.find((pf) => {
14738
- const dir = path26.dirname(pf);
14757
+ const dir = path27.dirname(pf);
14739
14758
  return dir.toLowerCase().includes(symbol.id.toLowerCase());
14740
14759
  });
14741
14760
  if (matchingPurpose) {
14742
- symbolMap[symbolId] = path26.dirname(matchingPurpose) + "/";
14761
+ symbolMap[symbolId] = path27.dirname(matchingPurpose) + "/";
14743
14762
  }
14744
14763
  }
14745
14764
  }
@@ -14750,7 +14769,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
14750
14769
  const symbolToFlows = {};
14751
14770
  for (const filePath of purposeFiles) {
14752
14771
  try {
14753
- const content = fs23.readFileSync(filePath, "utf8");
14772
+ const content = fs24.readFileSync(filePath, "utf8");
14754
14773
  const data = yaml14.load(content);
14755
14774
  if (!data?.flows) continue;
14756
14775
  if (Array.isArray(data.flows)) {
@@ -14764,7 +14783,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
14764
14783
  id: flowId,
14765
14784
  description: flow.description || "",
14766
14785
  steps,
14767
- definedIn: path26.relative(rootDir, filePath)
14786
+ definedIn: path27.relative(rootDir, filePath)
14768
14787
  };
14769
14788
  indexFlowSymbols(flowId, steps, symbolToFlows);
14770
14789
  }
@@ -14780,7 +14799,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
14780
14799
  trigger: flowDef.trigger,
14781
14800
  steps,
14782
14801
  validation: flowDef.validation,
14783
- definedIn: path26.relative(rootDir, filePath)
14802
+ definedIn: path27.relative(rootDir, filePath)
14784
14803
  };
14785
14804
  indexFlowSymbols(flowId, steps, symbolToFlows);
14786
14805
  }
@@ -14833,28 +14852,28 @@ function indexFlowSymbols(flowId, steps, symbolToFlows) {
14833
14852
  }
14834
14853
 
14835
14854
  // ../paradigm-mcp/src/utils/lore-loader.ts
14836
- import * as fs24 from "fs";
14837
- import * as path27 from "path";
14855
+ import * as fs25 from "fs";
14856
+ import * as path28 from "path";
14838
14857
  import * as yaml15 from "js-yaml";
14839
14858
  var LORE_DIR = ".paradigm/lore";
14840
14859
  var ENTRIES_DIR = "entries";
14841
14860
  var TIMELINE_FILE = "timeline.yaml";
14842
14861
  async function loadLoreEntries(rootDir, filter) {
14843
- const entriesPath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR);
14844
- if (!fs24.existsSync(entriesPath)) {
14862
+ const entriesPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR);
14863
+ if (!fs25.existsSync(entriesPath)) {
14845
14864
  return [];
14846
14865
  }
14847
14866
  migrateLegacyEntries(rootDir);
14848
14867
  const entries = [];
14849
- const dateDirs = fs24.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort().reverse();
14868
+ const dateDirs = fs25.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort().reverse();
14850
14869
  for (const dateDir of dateDirs) {
14851
14870
  if (filter?.dateFrom && dateDir < filter.dateFrom.slice(0, 10)) continue;
14852
14871
  if (filter?.dateTo && dateDir > filter.dateTo.slice(0, 10)) continue;
14853
- const dirPath = path27.join(entriesPath, dateDir);
14854
- const files = fs24.readdirSync(dirPath).filter((f) => f.endsWith(".yaml")).sort();
14872
+ const dirPath = path28.join(entriesPath, dateDir);
14873
+ const files = fs25.readdirSync(dirPath).filter((f) => f.endsWith(".yaml")).sort();
14855
14874
  for (const file of files) {
14856
14875
  try {
14857
- const content = fs24.readFileSync(path27.join(dirPath, file), "utf8");
14876
+ const content = fs25.readFileSync(path28.join(dirPath, file), "utf8");
14858
14877
  const entry = yaml15.load(content);
14859
14878
  entries.push(entry);
14860
14879
  } catch {
@@ -14867,10 +14886,10 @@ async function loadLoreEntry(rootDir, entryId) {
14867
14886
  const dateMatch = entryId.match(/^L-(\d{4}-\d{2}-\d{2})-/);
14868
14887
  if (dateMatch) {
14869
14888
  const dateStr = dateMatch[1];
14870
- const entryPath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
14871
- if (fs24.existsSync(entryPath)) {
14889
+ const entryPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
14890
+ if (fs25.existsSync(entryPath)) {
14872
14891
  try {
14873
- const content = fs24.readFileSync(entryPath, "utf8");
14892
+ const content = fs25.readFileSync(entryPath, "utf8");
14874
14893
  return yaml15.load(content);
14875
14894
  } catch {
14876
14895
  return null;
@@ -14881,47 +14900,47 @@ async function loadLoreEntry(rootDir, entryId) {
14881
14900
  return entries.find((e) => e.id === entryId) || null;
14882
14901
  }
14883
14902
  async function loadLoreTimeline(rootDir) {
14884
- const timelinePath = path27.join(rootDir, LORE_DIR, TIMELINE_FILE);
14885
- if (!fs24.existsSync(timelinePath)) {
14903
+ const timelinePath = path28.join(rootDir, LORE_DIR, TIMELINE_FILE);
14904
+ if (!fs25.existsSync(timelinePath)) {
14886
14905
  return null;
14887
14906
  }
14888
14907
  try {
14889
- const content = fs24.readFileSync(timelinePath, "utf8");
14908
+ const content = fs25.readFileSync(timelinePath, "utf8");
14890
14909
  return yaml15.load(content);
14891
14910
  } catch {
14892
14911
  return null;
14893
14912
  }
14894
14913
  }
14895
14914
  async function recordLoreEntry(rootDir, entry) {
14896
- const lorePath = path27.join(rootDir, LORE_DIR);
14915
+ const lorePath = path28.join(rootDir, LORE_DIR);
14897
14916
  const dateStr = entry.timestamp.slice(0, 10);
14898
- const datePath = path27.join(lorePath, ENTRIES_DIR, dateStr);
14899
- if (!fs24.existsSync(datePath)) {
14900
- fs24.mkdirSync(datePath, { recursive: true });
14917
+ const datePath = path28.join(lorePath, ENTRIES_DIR, dateStr);
14918
+ if (!fs25.existsSync(datePath)) {
14919
+ fs25.mkdirSync(datePath, { recursive: true });
14901
14920
  }
14902
14921
  if (!entry.id) {
14903
14922
  entry.id = generateLoreId(rootDir, dateStr);
14904
14923
  }
14905
- const entryPath = path27.join(datePath, `${entry.id}.yaml`);
14906
- fs24.writeFileSync(entryPath, yaml15.dump(entry, { lineWidth: -1, noRefs: true }));
14924
+ const entryPath = path28.join(datePath, `${entry.id}.yaml`);
14925
+ fs25.writeFileSync(entryPath, yaml15.dump(entry, { lineWidth: -1, noRefs: true }));
14907
14926
  await rebuildTimeline(rootDir);
14908
14927
  return entry.id;
14909
14928
  }
14910
14929
  async function rebuildTimeline(rootDir) {
14911
- const lorePath = path27.join(rootDir, LORE_DIR);
14912
- const entriesPath = path27.join(lorePath, ENTRIES_DIR);
14913
- if (!fs24.existsSync(entriesPath)) return;
14930
+ const lorePath = path28.join(rootDir, LORE_DIR);
14931
+ const entriesPath = path28.join(lorePath, ENTRIES_DIR);
14932
+ if (!fs25.existsSync(entriesPath)) return;
14914
14933
  migrateLegacyEntries(rootDir);
14915
14934
  const authors = /* @__PURE__ */ new Set();
14916
14935
  let entryCount = 0;
14917
14936
  let lastUpdated = "";
14918
- const dateDirs = fs24.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d));
14937
+ const dateDirs = fs25.readdirSync(entriesPath).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d));
14919
14938
  for (const dateDir of dateDirs) {
14920
- const dirPath = path27.join(entriesPath, dateDir);
14921
- const files = fs24.readdirSync(dirPath).filter((f) => f.endsWith(".yaml"));
14939
+ const dirPath = path28.join(entriesPath, dateDir);
14940
+ const files = fs25.readdirSync(dirPath).filter((f) => f.endsWith(".yaml"));
14922
14941
  for (const file of files) {
14923
14942
  try {
14924
- const content = fs24.readFileSync(path27.join(dirPath, file), "utf8");
14943
+ const content = fs25.readFileSync(path28.join(dirPath, file), "utf8");
14925
14944
  const entry = yaml15.load(content);
14926
14945
  authors.add(entry.author.id);
14927
14946
  entryCount++;
@@ -14933,10 +14952,10 @@ async function rebuildTimeline(rootDir) {
14933
14952
  }
14934
14953
  }
14935
14954
  let project = "unknown";
14936
- const configPath = path27.join(rootDir, ".paradigm", "config.yaml");
14937
- if (fs24.existsSync(configPath)) {
14955
+ const configPath = path28.join(rootDir, ".paradigm", "config.yaml");
14956
+ if (fs25.existsSync(configPath)) {
14938
14957
  try {
14939
- const config = yaml15.load(fs24.readFileSync(configPath, "utf8"));
14958
+ const config = yaml15.load(fs25.readFileSync(configPath, "utf8"));
14940
14959
  project = config.project || config.name || "unknown";
14941
14960
  } catch {
14942
14961
  }
@@ -14948,31 +14967,31 @@ async function rebuildTimeline(rootDir) {
14948
14967
  last_updated: lastUpdated || (/* @__PURE__ */ new Date()).toISOString(),
14949
14968
  authors: Array.from(authors)
14950
14969
  };
14951
- if (!fs24.existsSync(lorePath)) {
14952
- fs24.mkdirSync(lorePath, { recursive: true });
14970
+ if (!fs25.existsSync(lorePath)) {
14971
+ fs25.mkdirSync(lorePath, { recursive: true });
14953
14972
  }
14954
- fs24.writeFileSync(
14955
- path27.join(lorePath, TIMELINE_FILE),
14973
+ fs25.writeFileSync(
14974
+ path28.join(lorePath, TIMELINE_FILE),
14956
14975
  yaml15.dump(timeline, { lineWidth: -1, noRefs: true })
14957
14976
  );
14958
14977
  }
14959
14978
  function migrateLegacyEntries(rootDir) {
14960
- const entriesPath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR);
14961
- if (!fs24.existsSync(entriesPath)) return 0;
14962
- const rootFiles = fs24.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml") && !f.startsWith("."));
14979
+ const entriesPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR);
14980
+ if (!fs25.existsSync(entriesPath)) return 0;
14981
+ const rootFiles = fs25.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml") && !f.startsWith("."));
14963
14982
  let migrated = 0;
14964
14983
  for (const file of rootFiles) {
14965
- const filePath = path27.join(entriesPath, file);
14966
- const stat = fs24.statSync(filePath);
14984
+ const filePath = path28.join(entriesPath, file);
14985
+ const stat = fs25.statSync(filePath);
14967
14986
  if (!stat.isFile()) continue;
14968
14987
  try {
14969
- const content = fs24.readFileSync(filePath, "utf8");
14988
+ const content = fs25.readFileSync(filePath, "utf8");
14970
14989
  const raw = yaml15.load(content);
14971
14990
  if (raw.author && typeof raw.author === "object") continue;
14972
14991
  const dateStr = typeof raw.date === "string" ? raw.date.slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
14973
- const datePath = path27.join(entriesPath, dateStr);
14974
- if (!fs24.existsSync(datePath)) {
14975
- fs24.mkdirSync(datePath, { recursive: true });
14992
+ const datePath = path28.join(entriesPath, dateStr);
14993
+ if (!fs25.existsSync(datePath)) {
14994
+ fs25.mkdirSync(datePath, { recursive: true });
14976
14995
  }
14977
14996
  const id = generateLoreId(rootDir, dateStr);
14978
14997
  const oldType = String(raw.type || "agent-session");
@@ -14997,11 +15016,11 @@ function migrateLegacyEntries(rootDir) {
14997
15016
  ...verification ? { verification } : {},
14998
15017
  tags: ["migrated", oldType]
14999
15018
  };
15000
- fs24.writeFileSync(
15001
- path27.join(datePath, `${id}.yaml`),
15019
+ fs25.writeFileSync(
15020
+ path28.join(datePath, `${id}.yaml`),
15002
15021
  yaml15.dump(v2Entry, { lineWidth: -1, noRefs: true })
15003
15022
  );
15004
- fs24.unlinkSync(filePath);
15023
+ fs25.unlinkSync(filePath);
15005
15024
  migrated++;
15006
15025
  } catch {
15007
15026
  }
@@ -15012,8 +15031,8 @@ async function updateLoreEntry(rootDir, entryId, partial) {
15012
15031
  const entry = await loadLoreEntry(rootDir, entryId);
15013
15032
  if (!entry) return false;
15014
15033
  const dateStr = entry.timestamp.slice(0, 10);
15015
- const entryPath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
15016
- if (!fs24.existsSync(entryPath)) return false;
15034
+ const entryPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
15035
+ if (!fs25.existsSync(entryPath)) return false;
15017
15036
  if (partial.title !== void 0) entry.title = partial.title;
15018
15037
  if (partial.summary !== void 0) entry.summary = partial.summary;
15019
15038
  if (partial.type !== void 0) entry.type = partial.type;
@@ -15030,7 +15049,7 @@ async function updateLoreEntry(rootDir, entryId, partial) {
15030
15049
  if (partial.learnings !== void 0) entry.learnings = partial.learnings;
15031
15050
  if (partial.verification !== void 0) entry.verification = partial.verification;
15032
15051
  if (partial.tags !== void 0) entry.tags = partial.tags;
15033
- fs24.writeFileSync(entryPath, yaml15.dump(entry, { lineWidth: -1, noRefs: true }));
15052
+ fs25.writeFileSync(entryPath, yaml15.dump(entry, { lineWidth: -1, noRefs: true }));
15034
15053
  await rebuildTimeline(rootDir);
15035
15054
  return true;
15036
15055
  }
@@ -15038,13 +15057,13 @@ async function deleteLoreEntry(rootDir, entryId) {
15038
15057
  const entry = await loadLoreEntry(rootDir, entryId);
15039
15058
  if (!entry) return false;
15040
15059
  const dateStr = entry.timestamp.slice(0, 10);
15041
- const entryPath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
15042
- if (!fs24.existsSync(entryPath)) return false;
15043
- fs24.unlinkSync(entryPath);
15044
- const dateDir = path27.dirname(entryPath);
15045
- const remaining = fs24.readdirSync(dateDir).filter((f) => f.endsWith(".yaml"));
15060
+ const entryPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
15061
+ if (!fs25.existsSync(entryPath)) return false;
15062
+ fs25.unlinkSync(entryPath);
15063
+ const dateDir = path28.dirname(entryPath);
15064
+ const remaining = fs25.readdirSync(dateDir).filter((f) => f.endsWith(".yaml"));
15046
15065
  if (remaining.length === 0) {
15047
- fs24.rmdirSync(dateDir);
15066
+ fs25.rmdirSync(dateDir);
15048
15067
  }
15049
15068
  await rebuildTimeline(rootDir);
15050
15069
  return true;
@@ -15085,11 +15104,11 @@ function applyFilter(entries, filter) {
15085
15104
  return result;
15086
15105
  }
15087
15106
  function generateLoreId(rootDir, dateStr) {
15088
- const datePath = path27.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
15089
- if (!fs24.existsSync(datePath)) {
15107
+ const datePath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr);
15108
+ if (!fs25.existsSync(datePath)) {
15090
15109
  return `L-${dateStr}-001`;
15091
15110
  }
15092
- const existing = fs24.readdirSync(datePath).filter((f) => f.startsWith("L-") && f.endsWith(".yaml")).map((f) => {
15111
+ const existing = fs25.readdirSync(datePath).filter((f) => f.startsWith("L-") && f.endsWith(".yaml")).map((f) => {
15093
15112
  const match = f.match(/L-\d{4}-\d{2}-\d{2}-(\d+)\.yaml/);
15094
15113
  return match ? parseInt(match[1], 10) : 0;
15095
15114
  });
@@ -15592,8 +15611,8 @@ function summarizeEntry(entry) {
15592
15611
  }
15593
15612
 
15594
15613
  // ../paradigm-mcp/src/tools/habits.ts
15595
- import * as fs25 from "fs";
15596
- import * as path28 from "path";
15614
+ import * as fs26 from "fs";
15615
+ import * as path29 from "path";
15597
15616
  import { execSync as execSync2 } from "child_process";
15598
15617
  function getHabitsToolsList() {
15599
15618
  return [
@@ -15842,13 +15861,13 @@ async function handleHabitsCheck(args, ctx) {
15842
15861
  } catch {
15843
15862
  }
15844
15863
  }
15845
- const markerPath = path28.join(ctx.rootDir, ".paradigm", ".habits-blocking");
15864
+ const markerPath = path29.join(ctx.rootDir, ".paradigm", ".habits-blocking");
15846
15865
  try {
15847
15866
  if (trigger === "on-stop" && evaluation.blocksCompletion) {
15848
15867
  const blocking = evaluation.evaluations.filter((e) => e.result === "skipped" && e.habit.severity === "block").map((e) => `${e.habit.name}: ${e.reason}`);
15849
- fs25.writeFileSync(markerPath, blocking.join("\n"), "utf8");
15868
+ fs26.writeFileSync(markerPath, blocking.join("\n"), "utf8");
15850
15869
  } else if (trigger === "on-stop") {
15851
- if (fs25.existsSync(markerPath)) fs25.unlinkSync(markerPath);
15870
+ if (fs26.existsSync(markerPath)) fs26.unlinkSync(markerPath);
15852
15871
  }
15853
15872
  } catch {
15854
15873
  }
@@ -16061,7 +16080,7 @@ async function handlePracticeContext(args, ctx) {
16061
16080
  }
16062
16081
 
16063
16082
  // ../paradigm-mcp/src/tools/fallback-grep.ts
16064
- import * as path29 from "path";
16083
+ import * as path30 from "path";
16065
16084
  import { execSync as execSync3 } from "child_process";
16066
16085
  function grepForReferences(rootDir, symbol, options = {}) {
16067
16086
  const { maxResults = 20 } = options;
@@ -16090,7 +16109,7 @@ function grepForReferences(rootDir, symbol, options = {}) {
16090
16109
  const match = line.match(/^(.+?):(\d+):(.*)$/);
16091
16110
  if (match) {
16092
16111
  const [, filePath, lineNum, content] = match;
16093
- const relativePath = path29.relative(rootDir, filePath);
16112
+ const relativePath = path30.relative(rootDir, filePath);
16094
16113
  let context2 = "unknown";
16095
16114
  if (relativePath.includes(".purpose") || relativePath.includes("portal.yaml")) {
16096
16115
  context2 = "purpose";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-PMXRGPRQ.js";
13
13
  import {
14
14
  hooksInstallCommand
15
- } from "./chunk-AQZSUGL3.js";
15
+ } from "./chunk-6HZ7PZGG.js";
16
16
  import {
17
17
  indexCommand,
18
18
  initCommand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a-company/paradigm",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Unified CLI for Paradigm developer tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,8 @@
15
15
  "lore-ui/dist"
16
16
  ],
17
17
  "scripts": {
18
- "build": "tsup && npm run build:university-assets",
18
+ "build": "node scripts/generate-hooks.mjs && tsup && npm run build:university-assets",
19
+ "check:hooks": "node scripts/generate-hooks.mjs --check",
19
20
  "build:lore-ui": "cd lore-ui && npx vite build",
20
21
  "build:university-assets": "rm -rf dist/university-ui dist/university-content && cp -r ../university/ui/dist dist/university-ui && cp -r ../university/src/content dist/university-content",
21
22
  "dev": "tsup src/index.ts --format esm --dts --watch",