openwolf 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +232 -0
  3. package/dist/bin/openwolf.js +10 -0
  4. package/dist/bin/openwolf.js.map +1 -0
  5. package/dist/dashboard/assets/AISuggestions-DzE-DQkR.js +1 -0
  6. package/dist/dashboard/assets/ActivityTimeline-DGVjujnt.js +1 -0
  7. package/dist/dashboard/assets/AnatomyBrowser-S-2rmYtw.js +1 -0
  8. package/dist/dashboard/assets/BugLog-CG2zDHJc.js +1 -0
  9. package/dist/dashboard/assets/CerebrumViewer-Dlgoy69U.js +1 -0
  10. package/dist/dashboard/assets/CronStatus-DxUF1iW_.js +1 -0
  11. package/dist/dashboard/assets/DesignQC-BGXn_aq8.js +1 -0
  12. package/dist/dashboard/assets/MemoryViewer-CGqkTyvQ.js +1 -0
  13. package/dist/dashboard/assets/ProjectOverview-DlFhu69i.js +1 -0
  14. package/dist/dashboard/assets/TokenUsage-DDsQiVIq.js +68 -0
  15. package/dist/dashboard/assets/index-CzK9GUjV.css +1 -0
  16. package/dist/dashboard/assets/index-PYeNGjkN.js +52 -0
  17. package/dist/dashboard/index.html +16 -0
  18. package/dist/hooks/post-read.js +68 -0
  19. package/dist/hooks/post-write.js +502 -0
  20. package/dist/hooks/pre-read.js +79 -0
  21. package/dist/hooks/pre-write.js +120 -0
  22. package/dist/hooks/session-start.js +76 -0
  23. package/dist/hooks/shared.js +613 -0
  24. package/dist/hooks/stop.js +146 -0
  25. package/dist/src/buglog/bug-matcher.js +3 -0
  26. package/dist/src/buglog/bug-matcher.js.map +1 -0
  27. package/dist/src/buglog/bug-tracker.js +81 -0
  28. package/dist/src/buglog/bug-tracker.js.map +1 -0
  29. package/dist/src/cli/bug-cmd.js +28 -0
  30. package/dist/src/cli/bug-cmd.js.map +1 -0
  31. package/dist/src/cli/cron-cmd.js +106 -0
  32. package/dist/src/cli/cron-cmd.js.map +1 -0
  33. package/dist/src/cli/daemon-cmd.js +177 -0
  34. package/dist/src/cli/daemon-cmd.js.map +1 -0
  35. package/dist/src/cli/dashboard.js +84 -0
  36. package/dist/src/cli/dashboard.js.map +1 -0
  37. package/dist/src/cli/designqc-cmd.js +31 -0
  38. package/dist/src/cli/designqc-cmd.js.map +1 -0
  39. package/dist/src/cli/index.js +149 -0
  40. package/dist/src/cli/index.js.map +1 -0
  41. package/dist/src/cli/init.js +506 -0
  42. package/dist/src/cli/init.js.map +1 -0
  43. package/dist/src/cli/registry.js +93 -0
  44. package/dist/src/cli/registry.js.map +1 -0
  45. package/dist/src/cli/scan.js +39 -0
  46. package/dist/src/cli/scan.js.map +1 -0
  47. package/dist/src/cli/status.js +85 -0
  48. package/dist/src/cli/status.js.map +1 -0
  49. package/dist/src/cli/update.js +414 -0
  50. package/dist/src/cli/update.js.map +1 -0
  51. package/dist/src/daemon/cron-engine.js +300 -0
  52. package/dist/src/daemon/cron-engine.js.map +1 -0
  53. package/dist/src/daemon/file-watcher.js +53 -0
  54. package/dist/src/daemon/file-watcher.js.map +1 -0
  55. package/dist/src/daemon/health.js +23 -0
  56. package/dist/src/daemon/health.js.map +1 -0
  57. package/dist/src/daemon/wolf-daemon.js +294 -0
  58. package/dist/src/daemon/wolf-daemon.js.map +1 -0
  59. package/dist/src/designqc/designqc-capture.js +235 -0
  60. package/dist/src/designqc/designqc-capture.js.map +1 -0
  61. package/dist/src/designqc/designqc-engine.js +141 -0
  62. package/dist/src/designqc/designqc-engine.js.map +1 -0
  63. package/dist/src/designqc/designqc-types.js +5 -0
  64. package/dist/src/designqc/designqc-types.js.map +1 -0
  65. package/dist/src/hooks/post-read.js +69 -0
  66. package/dist/src/hooks/post-read.js.map +1 -0
  67. package/dist/src/hooks/post-write.js +503 -0
  68. package/dist/src/hooks/post-write.js.map +1 -0
  69. package/dist/src/hooks/pre-read.js +80 -0
  70. package/dist/src/hooks/pre-read.js.map +1 -0
  71. package/dist/src/hooks/pre-write.js +121 -0
  72. package/dist/src/hooks/pre-write.js.map +1 -0
  73. package/dist/src/hooks/session-start.js +77 -0
  74. package/dist/src/hooks/session-start.js.map +1 -0
  75. package/dist/src/hooks/shared.js +614 -0
  76. package/dist/src/hooks/shared.js.map +1 -0
  77. package/dist/src/hooks/stop.js +147 -0
  78. package/dist/src/hooks/stop.js.map +1 -0
  79. package/dist/src/scanner/anatomy-scanner.js +260 -0
  80. package/dist/src/scanner/anatomy-scanner.js.map +1 -0
  81. package/dist/src/scanner/description-extractor.js +1007 -0
  82. package/dist/src/scanner/description-extractor.js.map +1 -0
  83. package/dist/src/scanner/project-root.js +42 -0
  84. package/dist/src/scanner/project-root.js.map +1 -0
  85. package/dist/src/tracker/token-estimator.js +20 -0
  86. package/dist/src/tracker/token-estimator.js.map +1 -0
  87. package/dist/src/tracker/token-ledger.js +45 -0
  88. package/dist/src/tracker/token-ledger.js.map +1 -0
  89. package/dist/src/tracker/waste-detector.js +101 -0
  90. package/dist/src/tracker/waste-detector.js.map +1 -0
  91. package/dist/src/utils/fs-safe.js +74 -0
  92. package/dist/src/utils/fs-safe.js.map +1 -0
  93. package/dist/src/utils/logger.js +48 -0
  94. package/dist/src/utils/logger.js.map +1 -0
  95. package/dist/src/utils/paths.js +23 -0
  96. package/dist/src/utils/paths.js.map +1 -0
  97. package/dist/src/utils/platform.js +14 -0
  98. package/dist/src/utils/platform.js.map +1 -0
  99. package/package.json +77 -0
  100. package/src/templates/OPENWOLF.md +135 -0
  101. package/src/templates/anatomy.md +5 -0
  102. package/src/templates/buglog.json +4 -0
  103. package/src/templates/cerebrum.md +22 -0
  104. package/src/templates/claude-md-snippet.md +5 -0
  105. package/src/templates/claude-rules-openwolf.md +15 -0
  106. package/src/templates/config.json +73 -0
  107. package/src/templates/cron-manifest.json +97 -0
  108. package/src/templates/cron-state.json +7 -0
  109. package/src/templates/identity.md +9 -0
  110. package/src/templates/memory.md +4 -0
  111. package/src/templates/reframe-frameworks.md +597 -0
  112. package/src/templates/token-ledger.json +21 -0
@@ -0,0 +1,120 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { getWolfDir, ensureWolfDir, readJSON, readMarkdown, readStdin } from "./shared.js";
4
+ async function main() {
5
+ ensureWolfDir();
6
+ const wolfDir = getWolfDir();
7
+ const raw = await readStdin();
8
+ let input;
9
+ try {
10
+ input = JSON.parse(raw);
11
+ }
12
+ catch {
13
+ process.exit(0);
14
+ return;
15
+ }
16
+ // For Edit tool, the meaningful content is old_string + new_string
17
+ const content = input.tool_input?.content ?? "";
18
+ const oldStr = input.tool_input?.old_string ?? "";
19
+ const newStr = input.tool_input?.new_string ?? "";
20
+ const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
21
+ const allContent = [content, oldStr, newStr].join("\n");
22
+ if (!allContent.trim()) {
23
+ process.exit(0);
24
+ return;
25
+ }
26
+ // 1. Cerebrum Do-Not-Repeat check
27
+ checkCerebrum(wolfDir, allContent);
28
+ // 2. Bug log: search for similar past bugs when editing code
29
+ // This fires when Claude is about to edit a file — if the edit looks like a fix
30
+ // (changing error handling, modifying catch blocks, etc.), check the bug log
31
+ if (filePath && (oldStr || content)) {
32
+ checkBugLog(wolfDir, filePath, oldStr, newStr, content);
33
+ }
34
+ process.exit(0);
35
+ }
36
+ function checkCerebrum(wolfDir, content) {
37
+ const cerebrumContent = readMarkdown(path.join(wolfDir, "cerebrum.md"));
38
+ const doNotRepeatSection = cerebrumContent.split("## Do-Not-Repeat")[1];
39
+ if (!doNotRepeatSection)
40
+ return;
41
+ const entries = doNotRepeatSection.split("## ")[0];
42
+ const lines = entries.split("\n").filter((l) => l.trim().startsWith("[") || l.trim().startsWith("-"));
43
+ for (const line of lines) {
44
+ const trimmed = line.trim().replace(/^[-*]\s*/, "").replace(/^\[[\d-]+\]\s*/, "");
45
+ if (!trimmed)
46
+ continue;
47
+ const patterns = [];
48
+ const quotedMatches = trimmed.match(/"([^"]+)"/g) || trimmed.match(/'([^']+)'/g) || trimmed.match(/`([^`]+)`/g);
49
+ if (quotedMatches) {
50
+ for (const qm of quotedMatches) {
51
+ patterns.push(qm.replace(/["'`]/g, ""));
52
+ }
53
+ }
54
+ const neverMatch = trimmed.match(/(?:never use|avoid|don't use|do not use)\s+(\w+)/i);
55
+ if (neverMatch)
56
+ patterns.push(neverMatch[1]);
57
+ for (const pattern of patterns) {
58
+ try {
59
+ const regex = new RegExp(`\\b${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i");
60
+ if (regex.test(content)) {
61
+ process.stderr.write(`⚠️ OpenWolf cerebrum warning: "${trimmed}" — check your code before proceeding.\n`);
62
+ }
63
+ }
64
+ catch { }
65
+ }
66
+ }
67
+ }
68
+ // Common words that appear in most code — must be excluded from similarity matching
69
+ const STOP_WORDS = new Set([
70
+ "error", "function", "return", "const", "this", "that", "with", "from",
71
+ "import", "export", "class", "interface", "type", "undefined", "null",
72
+ "true", "false", "string", "number", "object", "array", "value",
73
+ "file", "path", "name", "data", "response", "request", "result",
74
+ "should", "must", "does", "have", "been", "will", "would", "could",
75
+ "when", "then", "else", "each", "some", "every", "only",
76
+ ]);
77
+ function checkBugLog(wolfDir, filePath, oldStr, newStr, content) {
78
+ const bugLogPath = path.join(wolfDir, "buglog.json");
79
+ if (!fs.existsSync(bugLogPath))
80
+ return;
81
+ const bugLog = readJSON(bugLogPath, { version: 1, bugs: [] });
82
+ if (bugLog.bugs.length === 0)
83
+ return;
84
+ const basename = path.basename(filePath);
85
+ // ONLY surface bugs that match the SAME file being edited.
86
+ // Cross-file matching is too noisy and risks misdirecting Claude.
87
+ const fileMatches = bugLog.bugs.filter(b => {
88
+ const bugBasename = path.basename(b.file);
89
+ return bugBasename === basename;
90
+ });
91
+ if (fileMatches.length === 0)
92
+ return;
93
+ // Further filter: require tag or error_message overlap with the edit content
94
+ const editText = (oldStr + " " + newStr + " " + content).toLowerCase();
95
+ const editTokens = tokenize(editText);
96
+ const relevant = fileMatches.filter(bug => {
97
+ // Check if any bug tag appears in the edit content
98
+ const tagHit = bug.tags.some(t => editText.includes(t.toLowerCase()));
99
+ if (tagHit)
100
+ return true;
101
+ // Check meaningful word overlap (excluding stop words)
102
+ const bugTokens = tokenize(bug.error_message + " " + bug.root_cause);
103
+ const overlap = [...editTokens].filter(t => bugTokens.has(t));
104
+ // Require at least 3 meaningful overlapping words
105
+ return overlap.length >= 3;
106
+ });
107
+ if (relevant.length === 0)
108
+ return;
109
+ // Surface as a FYI, not a directive — Claude should evaluate, not blindly apply
110
+ process.stderr.write(`📋 OpenWolf buglog: ${relevant.length} past bug(s) found for ${basename} — review for context, do NOT apply blindly:\n`);
111
+ for (const bug of relevant.slice(0, 2)) {
112
+ process.stderr.write(` [${bug.id}] "${bug.error_message.slice(0, 70)}"\n Cause: ${bug.root_cause.slice(0, 80)}\n Fix: ${bug.fix.slice(0, 80)}\n`);
113
+ }
114
+ }
115
+ function tokenize(text) {
116
+ return new Set(text.replace(/[^\w\s]/g, " ").split(/\s+/)
117
+ .filter(w => w.length > 3 && !STOP_WORDS.has(w.toLowerCase()))
118
+ .map(w => w.toLowerCase()));
119
+ }
120
+ main().catch(() => process.exit(0));
@@ -0,0 +1,76 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { getWolfDir, ensureWolfDir, writeJSON, appendMarkdown, readJSON, timestamp, timeShort } from "./shared.js";
4
+ async function main() {
5
+ ensureWolfDir();
6
+ const wolfDir = getWolfDir();
7
+ // Clean up stale .tmp files left from failed atomic writes
8
+ try {
9
+ const files = fs.readdirSync(wolfDir);
10
+ for (const f of files) {
11
+ if (f.endsWith(".tmp")) {
12
+ try {
13
+ fs.unlinkSync(path.join(wolfDir, f));
14
+ }
15
+ catch { }
16
+ }
17
+ }
18
+ }
19
+ catch { }
20
+ const hooksDir = path.join(wolfDir, "hooks");
21
+ const sessionFile = path.join(hooksDir, "_session.json");
22
+ const now = new Date();
23
+ const sessionId = `session-${now.toISOString().slice(0, 10)}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
24
+ // Create fresh session state
25
+ writeJSON(sessionFile, {
26
+ session_id: sessionId,
27
+ started: timestamp(),
28
+ files_read: {},
29
+ files_written: [],
30
+ edit_counts: {},
31
+ anatomy_hits: 0,
32
+ anatomy_misses: 0,
33
+ repeated_reads_warned: 0,
34
+ cerebrum_warnings: 0,
35
+ stop_count: 0,
36
+ });
37
+ // Append session header to memory.md
38
+ const memoryPath = path.join(wolfDir, "memory.md");
39
+ const header = `\n## Session: ${now.toISOString().slice(0, 10)} ${timeShort()}\n\n| Time | Action | File(s) | Outcome | ~Tokens |\n|------|--------|---------|---------|--------|\n`;
40
+ appendMarkdown(memoryPath, header);
41
+ // Check cerebrum freshness — remind Claude to learn
42
+ try {
43
+ const cerebrumPath = path.join(wolfDir, "cerebrum.md");
44
+ const cerebrumContent = fs.readFileSync(cerebrumPath, "utf-8");
45
+ const stat = fs.statSync(cerebrumPath);
46
+ const daysSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24);
47
+ // Count actual entries (non-comment, non-empty lines in content sections)
48
+ const entryLines = cerebrumContent.split("\n").filter(l => {
49
+ const t = l.trim();
50
+ return t.startsWith("- ") || t.startsWith("* ") || (t.startsWith("[") && t.includes("]"));
51
+ });
52
+ if (entryLines.length < 3) {
53
+ process.stderr.write(`💡 OpenWolf: cerebrum.md has only ${entryLines.length} entries. Learn from this session — record user preferences, project conventions, and mistakes to .wolf/cerebrum.md.\n`);
54
+ }
55
+ else if (daysSinceUpdate > 3) {
56
+ process.stderr.write(`💡 OpenWolf: cerebrum.md hasn't been updated in ${Math.floor(daysSinceUpdate)} days. Look for opportunities to add learnings this session.\n`);
57
+ }
58
+ }
59
+ catch { }
60
+ // Check buglog — remind if empty
61
+ try {
62
+ const buglogPath = path.join(wolfDir, "buglog.json");
63
+ const buglog = readJSON(buglogPath, { bugs: [] });
64
+ if (buglog.bugs.length === 0) {
65
+ process.stderr.write(`📋 OpenWolf: buglog.json is empty. If you encounter or fix any bugs, errors, or failed tests this session, log them to .wolf/buglog.json.\n`);
66
+ }
67
+ }
68
+ catch { }
69
+ // Increment total_sessions in token-ledger
70
+ const ledgerPath = path.join(wolfDir, "token-ledger.json");
71
+ const ledger = readJSON(ledgerPath, { version: 1, lifetime: { total_sessions: 0 } });
72
+ ledger.lifetime.total_sessions++;
73
+ writeJSON(ledgerPath, ledger);
74
+ process.exit(0);
75
+ }
76
+ main().catch(() => process.exit(0));