@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
|
-
|
|
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
|
-
|
|
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:]]
|
|
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 '
|
|
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
|
-
|
|
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;
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
528
|
+
var fs27 = __require("fs");
|
|
529
529
|
za = __dirname + "/";
|
|
530
530
|
Ba = (a) => {
|
|
531
531
|
a = Ca(a) ? new URL(a) : a;
|
|
532
|
-
return
|
|
532
|
+
return fs27.readFileSync(a);
|
|
533
533
|
};
|
|
534
534
|
Aa = async (a) => {
|
|
535
535
|
a = Ca(a) ? new URL(a) : a;
|
|
536
|
-
return
|
|
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 =
|
|
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
|
|
4295
|
-
const firstPart =
|
|
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,
|
|
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
|
|
13470
|
-
import * as
|
|
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
|
|
13474
|
-
import * as
|
|
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 =
|
|
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(
|
|
13615
|
+
const globalConfig = loadHabitsYaml(path24.join(home, ".paradigm", "habits.yaml"));
|
|
13597
13616
|
if (globalConfig) mergeHabits(habitsById, globalConfig);
|
|
13598
|
-
const projectConfig = loadHabitsYaml(
|
|
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 (!
|
|
13622
|
+
if (!fs22.existsSync(filePath)) return null;
|
|
13604
13623
|
try {
|
|
13605
|
-
const content =
|
|
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) ||
|
|
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
|
|
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 =
|
|
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 =
|
|
14068
|
-
if (!
|
|
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 =
|
|
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 ${
|
|
14090
|
-
file:
|
|
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:
|
|
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 =
|
|
14157
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
14242
|
-
} else if (
|
|
14243
|
-
|
|
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
|
|
14263
|
-
import * as
|
|
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 ||
|
|
14603
|
-
const paradigmDir =
|
|
14604
|
-
if (!
|
|
14605
|
-
|
|
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 =
|
|
14616
|
-
|
|
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 =
|
|
14620
|
-
|
|
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 =
|
|
14630
|
-
|
|
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) =>
|
|
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) =>
|
|
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 =
|
|
14687
|
-
if (
|
|
14705
|
+
const gitignorePath = path27.join(rootDir, ".gitignore");
|
|
14706
|
+
if (fs24.existsSync(gitignorePath)) {
|
|
14688
14707
|
try {
|
|
14689
|
-
const content =
|
|
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 =
|
|
14757
|
+
const dir = path27.dirname(pf);
|
|
14739
14758
|
return dir.toLowerCase().includes(symbol.id.toLowerCase());
|
|
14740
14759
|
});
|
|
14741
14760
|
if (matchingPurpose) {
|
|
14742
|
-
symbolMap[symbolId] =
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
14837
|
-
import * as
|
|
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 =
|
|
14844
|
-
if (!
|
|
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 =
|
|
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 =
|
|
14854
|
-
const files =
|
|
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 =
|
|
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 =
|
|
14871
|
-
if (
|
|
14889
|
+
const entryPath = path28.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
|
|
14890
|
+
if (fs25.existsSync(entryPath)) {
|
|
14872
14891
|
try {
|
|
14873
|
-
const content =
|
|
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 =
|
|
14885
|
-
if (!
|
|
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 =
|
|
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 =
|
|
14915
|
+
const lorePath = path28.join(rootDir, LORE_DIR);
|
|
14897
14916
|
const dateStr = entry.timestamp.slice(0, 10);
|
|
14898
|
-
const datePath =
|
|
14899
|
-
if (!
|
|
14900
|
-
|
|
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 =
|
|
14906
|
-
|
|
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 =
|
|
14912
|
-
const entriesPath =
|
|
14913
|
-
if (!
|
|
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 =
|
|
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 =
|
|
14921
|
-
const files =
|
|
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 =
|
|
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 =
|
|
14937
|
-
if (
|
|
14955
|
+
const configPath = path28.join(rootDir, ".paradigm", "config.yaml");
|
|
14956
|
+
if (fs25.existsSync(configPath)) {
|
|
14938
14957
|
try {
|
|
14939
|
-
const config = yaml15.load(
|
|
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 (!
|
|
14952
|
-
|
|
14970
|
+
if (!fs25.existsSync(lorePath)) {
|
|
14971
|
+
fs25.mkdirSync(lorePath, { recursive: true });
|
|
14953
14972
|
}
|
|
14954
|
-
|
|
14955
|
-
|
|
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 =
|
|
14961
|
-
if (!
|
|
14962
|
-
const rootFiles =
|
|
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 =
|
|
14966
|
-
const stat =
|
|
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 =
|
|
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 =
|
|
14974
|
-
if (!
|
|
14975
|
-
|
|
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
|
-
|
|
15001
|
-
|
|
15019
|
+
fs25.writeFileSync(
|
|
15020
|
+
path28.join(datePath, `${id}.yaml`),
|
|
15002
15021
|
yaml15.dump(v2Entry, { lineWidth: -1, noRefs: true })
|
|
15003
15022
|
);
|
|
15004
|
-
|
|
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 =
|
|
15016
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
15042
|
-
if (!
|
|
15043
|
-
|
|
15044
|
-
const dateDir =
|
|
15045
|
-
const remaining =
|
|
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
|
-
|
|
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 =
|
|
15089
|
-
if (!
|
|
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 =
|
|
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
|
|
15596
|
-
import * as
|
|
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 =
|
|
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
|
-
|
|
15868
|
+
fs26.writeFileSync(markerPath, blocking.join("\n"), "utf8");
|
|
15850
15869
|
} else if (trigger === "on-stop") {
|
|
15851
|
-
if (
|
|
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
|
|
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 =
|
|
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";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a-company/paradigm",
|
|
3
|
-
"version": "3.1.
|
|
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",
|