openwolf-enhanced 1.9.3

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 (127) hide show
  1. package/CHANGELOG.md +386 -0
  2. package/LICENSE +663 -0
  3. package/NOTICE +48 -0
  4. package/README.md +283 -0
  5. package/dist/bin/openwolf.js +10 -0
  6. package/dist/bin/openwolf.js.map +1 -0
  7. package/dist/dashboard/assets/AISuggestions-DHXOgHMM.js +9 -0
  8. package/dist/dashboard/assets/ActivityTimeline-BXLwacZV.js +1 -0
  9. package/dist/dashboard/assets/AnatomyBrowser-CUyC8M2I.js +1 -0
  10. package/dist/dashboard/assets/BugLog-Bxvy1sPW.js +1 -0
  11. package/dist/dashboard/assets/CerebrumViewer-DCySVmzA.js +1 -0
  12. package/dist/dashboard/assets/CronStatus-BA4fmQX3.js +1 -0
  13. package/dist/dashboard/assets/DesignQC-BhNEKryS.js +1 -0
  14. package/dist/dashboard/assets/MemoryViewer-0y9lsDUO.js +1 -0
  15. package/dist/dashboard/assets/ProjectOverview-CC51rVRe.js +1 -0
  16. package/dist/dashboard/assets/StatusBadge-4NJI_F2G.js +1 -0
  17. package/dist/dashboard/assets/TokenUsage-7pCxlnCX.js +55 -0
  18. package/dist/dashboard/assets/index-4O5rk-qB.css +2 -0
  19. package/dist/dashboard/assets/index-jfgWxxsn.js +13 -0
  20. package/dist/dashboard/assets/utils-BqE_aDxV.js +1 -0
  21. package/dist/dashboard/index.html +16 -0
  22. package/dist/hooks/post-read.js +85 -0
  23. package/dist/hooks/post-write.js +520 -0
  24. package/dist/hooks/pre-read.js +107 -0
  25. package/dist/hooks/pre-write.js +120 -0
  26. package/dist/hooks/session-start.js +76 -0
  27. package/dist/hooks/shared.js +0 -0
  28. package/dist/hooks/stop.js +219 -0
  29. package/dist/src/buglog/bug-matcher.js +3 -0
  30. package/dist/src/buglog/bug-matcher.js.map +1 -0
  31. package/dist/src/buglog/bug-tracker.js +89 -0
  32. package/dist/src/buglog/bug-tracker.js.map +1 -0
  33. package/dist/src/cli/bug-cmd.js +31 -0
  34. package/dist/src/cli/bug-cmd.js.map +1 -0
  35. package/dist/src/cli/cron-cmd.js +107 -0
  36. package/dist/src/cli/cron-cmd.js.map +1 -0
  37. package/dist/src/cli/daemon-cmd.js +180 -0
  38. package/dist/src/cli/daemon-cmd.js.map +1 -0
  39. package/dist/src/cli/dashboard.js +88 -0
  40. package/dist/src/cli/dashboard.js.map +1 -0
  41. package/dist/src/cli/designqc-cmd.js +31 -0
  42. package/dist/src/cli/designqc-cmd.js.map +1 -0
  43. package/dist/src/cli/doctor-cmd.js +94 -0
  44. package/dist/src/cli/doctor-cmd.js.map +1 -0
  45. package/dist/src/cli/index.js +158 -0
  46. package/dist/src/cli/index.js.map +1 -0
  47. package/dist/src/cli/init.js +493 -0
  48. package/dist/src/cli/init.js.map +1 -0
  49. package/dist/src/cli/registry.js +93 -0
  50. package/dist/src/cli/registry.js.map +1 -0
  51. package/dist/src/cli/scan.js +39 -0
  52. package/dist/src/cli/scan.js.map +1 -0
  53. package/dist/src/cli/status.js +98 -0
  54. package/dist/src/cli/status.js.map +1 -0
  55. package/dist/src/cli/update.js +435 -0
  56. package/dist/src/cli/update.js.map +1 -0
  57. package/dist/src/daemon/cron-engine.js +321 -0
  58. package/dist/src/daemon/cron-engine.js.map +1 -0
  59. package/dist/src/daemon/file-watcher.js +64 -0
  60. package/dist/src/daemon/file-watcher.js.map +1 -0
  61. package/dist/src/daemon/health.js +23 -0
  62. package/dist/src/daemon/health.js.map +1 -0
  63. package/dist/src/daemon/wolf-daemon.js +440 -0
  64. package/dist/src/daemon/wolf-daemon.js.map +1 -0
  65. package/dist/src/designqc/designqc-capture.js +278 -0
  66. package/dist/src/designqc/designqc-capture.js.map +1 -0
  67. package/dist/src/designqc/designqc-engine.js +159 -0
  68. package/dist/src/designqc/designqc-engine.js.map +1 -0
  69. package/dist/src/designqc/designqc-types.js +5 -0
  70. package/dist/src/designqc/designqc-types.js.map +1 -0
  71. package/dist/src/hooks/post-read.js +86 -0
  72. package/dist/src/hooks/post-read.js.map +1 -0
  73. package/dist/src/hooks/post-write.js +521 -0
  74. package/dist/src/hooks/post-write.js.map +1 -0
  75. package/dist/src/hooks/pre-read.js +108 -0
  76. package/dist/src/hooks/pre-read.js.map +1 -0
  77. package/dist/src/hooks/pre-write.js +121 -0
  78. package/dist/src/hooks/pre-write.js.map +1 -0
  79. package/dist/src/hooks/session-start.js +77 -0
  80. package/dist/src/hooks/session-start.js.map +1 -0
  81. package/dist/src/hooks/shared.js +0 -0
  82. package/dist/src/hooks/shared.js.map +1 -0
  83. package/dist/src/hooks/stop.js +220 -0
  84. package/dist/src/hooks/stop.js.map +1 -0
  85. package/dist/src/scanner/anatomy-scanner.js +278 -0
  86. package/dist/src/scanner/anatomy-scanner.js.map +1 -0
  87. package/dist/src/scanner/description-extractor.js +1007 -0
  88. package/dist/src/scanner/description-extractor.js.map +1 -0
  89. package/dist/src/scanner/project-root.js +52 -0
  90. package/dist/src/scanner/project-root.js.map +1 -0
  91. package/dist/src/tracker/token-estimator.js +20 -0
  92. package/dist/src/tracker/token-estimator.js.map +1 -0
  93. package/dist/src/tracker/token-ledger.js +55 -0
  94. package/dist/src/tracker/token-ledger.js.map +1 -0
  95. package/dist/src/tracker/waste-detector.js +101 -0
  96. package/dist/src/tracker/waste-detector.js.map +1 -0
  97. package/dist/src/utils/dashboard-auth.js +50 -0
  98. package/dist/src/utils/dashboard-auth.js.map +1 -0
  99. package/dist/src/utils/fs-safe.js +135 -0
  100. package/dist/src/utils/fs-safe.js.map +1 -0
  101. package/dist/src/utils/hooks-deploy.js +70 -0
  102. package/dist/src/utils/hooks-deploy.js.map +1 -0
  103. package/dist/src/utils/logger.js +48 -0
  104. package/dist/src/utils/logger.js.map +1 -0
  105. package/dist/src/utils/maintenance.js +385 -0
  106. package/dist/src/utils/maintenance.js.map +1 -0
  107. package/dist/src/utils/paths.js +23 -0
  108. package/dist/src/utils/paths.js.map +1 -0
  109. package/dist/src/utils/platform.js +14 -0
  110. package/dist/src/utils/platform.js.map +1 -0
  111. package/dist/src/utils/seed.js +61 -0
  112. package/dist/src/utils/seed.js.map +1 -0
  113. package/package.json +82 -0
  114. package/src/templates/OPENWOLF.md +147 -0
  115. package/src/templates/STATUS.md +48 -0
  116. package/src/templates/anatomy.md +5 -0
  117. package/src/templates/buglog.json +4 -0
  118. package/src/templates/cerebrum.md +22 -0
  119. package/src/templates/claude-md-snippet.md +5 -0
  120. package/src/templates/claude-rules-openwolf.md +17 -0
  121. package/src/templates/config.json +83 -0
  122. package/src/templates/cron-manifest.json +97 -0
  123. package/src/templates/cron-state.json +7 -0
  124. package/src/templates/identity.md +9 -0
  125. package/src/templates/memory.md +4 -0
  126. package/src/templates/reframe-frameworks.md +597 -0
  127. package/src/templates/token-ledger.json +21 -0
@@ -0,0 +1,220 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { getWolfDir, ensureWolfDir, readJSON, writeJSON, appendMarkdown, timeShort, getRetention, compactMemoryIfLarge, countSemanticEntries, withLock } from "./shared.js";
4
+ async function main() {
5
+ ensureWolfDir();
6
+ const wolfDir = getWolfDir();
7
+ const hooksDir = path.join(wolfDir, "hooks");
8
+ const sessionFile = path.join(hooksDir, "_session.json");
9
+ const session = readJSON(sessionFile, {
10
+ session_id: "",
11
+ started: "",
12
+ files_read: {},
13
+ files_written: [],
14
+ edit_counts: {},
15
+ anatomy_hits: 0,
16
+ anatomy_misses: 0,
17
+ repeated_reads_warned: 0,
18
+ cerebrum_warnings: 0,
19
+ stop_count: 0,
20
+ });
21
+ session.stop_count++;
22
+ // Only write to ledger if there's been activity
23
+ const readCount = Object.keys(session.files_read).length;
24
+ const writeCount = session.files_written.length;
25
+ if (readCount === 0 && writeCount === 0) {
26
+ writeJSON(sessionFile, session);
27
+ process.exit(0);
28
+ return;
29
+ }
30
+ // Collect end-of-turn reminders. These are surfaced via additionalContext (stdout) at the
31
+ // very end so they land in Claude's next context window — stderr would only hit the terminal.
32
+ // Each reminder type fires at most once per session so it doesn't re-nag every turn.
33
+ const candidates = [
34
+ { key: "buglog", msg: checkForMissingBugLogs(wolfDir, session) },
35
+ { key: "cerebrum", msg: checkCerebrumFreshness(wolfDir, session) },
36
+ { key: "summary", msg: checkSemanticSummaries(wolfDir, writeCount) },
37
+ { key: "status", msg: checkStatusFreshness(wolfDir, session) },
38
+ ].filter((c) => c.msg !== null);
39
+ const alreadyShown = new Set(session.reminders_shown ?? []);
40
+ const fresh = candidates.filter((c) => !alreadyShown.has(c.key));
41
+ for (const c of fresh)
42
+ alreadyShown.add(c.key);
43
+ session.reminders_shown = [...alreadyShown];
44
+ const reminders = fresh.map((c) => c.msg);
45
+ // Build session entry for ledger
46
+ const reads = Object.entries(session.files_read).map(([file, data]) => ({
47
+ file,
48
+ tokens_estimated: data.tokens,
49
+ was_repeated: data.count > 1,
50
+ anatomy_had_description: false, // simplified
51
+ }));
52
+ const writes = session.files_written.map((w) => ({
53
+ file: w.file,
54
+ tokens_estimated: w.tokens,
55
+ action: w.action,
56
+ }));
57
+ const inputTokens = reads.reduce((sum, r) => sum + r.tokens_estimated, 0);
58
+ const outputTokens = writes.reduce((sum, w) => sum + w.tokens_estimated, 0);
59
+ const sessionEntry = {
60
+ id: session.session_id,
61
+ started: session.started,
62
+ ended: new Date().toISOString(),
63
+ reads,
64
+ writes,
65
+ totals: {
66
+ input_tokens_estimated: inputTokens,
67
+ output_tokens_estimated: outputTokens,
68
+ reads_count: readCount,
69
+ writes_count: writeCount,
70
+ repeated_reads_blocked: session.repeated_reads_warned,
71
+ anatomy_lookups: session.anatomy_hits,
72
+ },
73
+ };
74
+ // Update token-ledger.json — lock the read-modify-write so concurrent sessions and the cron
75
+ // token report don't clobber each other (M1).
76
+ const ret = getRetention(wolfDir);
77
+ const ledgerPath = path.join(wolfDir, "token-ledger.json");
78
+ withLock(ledgerPath, () => {
79
+ const ledger = readJSON(ledgerPath, {
80
+ version: 1,
81
+ created_at: "",
82
+ lifetime: {
83
+ total_tokens_estimated: 0,
84
+ total_reads: 0,
85
+ total_writes: 0,
86
+ total_sessions: 0,
87
+ anatomy_hits: 0,
88
+ anatomy_misses: 0,
89
+ repeated_reads_blocked: 0,
90
+ estimated_savings_vs_bare_cli: 0,
91
+ },
92
+ sessions: [],
93
+ daemon_usage: [],
94
+ waste_flags: [],
95
+ optimization_report: { last_generated: null, patterns: [] },
96
+ });
97
+ // Keep token-ledger.json bounded: cap per-session arrays and total session count.
98
+ // Without this, sessions[] (each embedding full reads[]/writes[]) grows without limit
99
+ // and writeJSON's full-file rewrite becomes quadratic over time. Limits are tunable
100
+ // via config.json openwolf.retention.
101
+ if (Array.isArray(sessionEntry.reads) && sessionEntry.reads.length > ret.session_io_max) {
102
+ sessionEntry.reads = sessionEntry.reads.slice(-ret.session_io_max);
103
+ }
104
+ if (Array.isArray(sessionEntry.writes) && sessionEntry.writes.length > ret.session_io_max) {
105
+ sessionEntry.writes = sessionEntry.writes.slice(-ret.session_io_max);
106
+ }
107
+ ledger.sessions.push(sessionEntry);
108
+ if (ledger.sessions.length > ret.token_ledger_max_sessions) {
109
+ ledger.sessions = ledger.sessions.slice(-ret.token_ledger_max_sessions);
110
+ }
111
+ ledger.lifetime.total_reads += readCount;
112
+ ledger.lifetime.total_writes += writeCount;
113
+ ledger.lifetime.total_tokens_estimated += inputTokens + outputTokens;
114
+ ledger.lifetime.anatomy_hits += session.anatomy_hits;
115
+ ledger.lifetime.anatomy_misses += session.anatomy_misses;
116
+ ledger.lifetime.repeated_reads_blocked += session.repeated_reads_warned;
117
+ // Estimate savings: anatomy hits save ~200 tokens each, repeated reads blocked save their token count
118
+ const savedFromAnatomy = session.anatomy_hits * 200;
119
+ const savedFromRepeats = Object.values(session.files_read)
120
+ .filter((r) => r.count > 1)
121
+ .reduce((sum, r) => sum + r.tokens * (r.count - 1), 0);
122
+ ledger.lifetime.estimated_savings_vs_bare_cli += savedFromAnatomy + savedFromRepeats;
123
+ writeJSON(ledgerPath, ledger);
124
+ });
125
+ // Write a session summary line to memory.md if there was meaningful activity
126
+ if (writeCount > 0) {
127
+ try {
128
+ const uniqueFiles = new Set(session.files_written.map(w => path.basename(w.file)));
129
+ const fileList = [...uniqueFiles].slice(0, 5).join(", ");
130
+ const memoryPath = path.join(wolfDir, "memory.md");
131
+ appendMarkdown(memoryPath, `| ${timeShort()} | Session end: ${writeCount} writes across ${uniqueFiles.size} files (${fileList}) | ${readCount} reads | ~${inputTokens + outputTokens} tok |\n`);
132
+ }
133
+ catch { }
134
+ }
135
+ // Opportunistic self-maintenance: keep memory.md bounded even when the daemon
136
+ // (which normally runs the consolidation cron) isn't running. Stat-gated → cheap.
137
+ try {
138
+ compactMemoryIfLarge(wolfDir, ret.memory_max_bytes);
139
+ }
140
+ catch { }
141
+ writeJSON(sessionFile, session);
142
+ // Surface reminders into Claude's next context window (Stop hooks can inject via stdout JSON).
143
+ if (reminders.length > 0) {
144
+ const additionalContext = `⚠️ OpenWolf end-of-turn reminders:\n${reminders.map((r) => `• ${r}`).join("\n")}`;
145
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "Stop", additionalContext } }));
146
+ }
147
+ process.exit(0);
148
+ }
149
+ /**
150
+ * Check if files were edited multiple times but buglog.json wasn't updated.
151
+ * Emit a stderr reminder so Claude sees it in the next turn.
152
+ */
153
+ function checkForMissingBugLogs(wolfDir, session) {
154
+ if (!session.edit_counts)
155
+ return null;
156
+ const multiEditFiles = Object.entries(session.edit_counts)
157
+ .filter(([, count]) => count >= 3)
158
+ .map(([file]) => path.basename(file));
159
+ if (multiEditFiles.length === 0)
160
+ return null;
161
+ // Check if buglog was written to this session
162
+ const buglogWritten = session.files_written.some(w => w.file.includes("buglog.json"));
163
+ if (!buglogWritten) {
164
+ return `Files edited 3+ times this session (${multiEditFiles.join(", ")}) but buglog.json was not updated. If you fixed bugs, log them to .wolf/buglog.json.`;
165
+ }
166
+ return null;
167
+ }
168
+ /**
169
+ * Check if cerebrum.md was updated recently. If it hasn't been updated in
170
+ * a while and there was significant activity, return a gentle reminder.
171
+ */
172
+ function checkCerebrumFreshness(wolfDir, session) {
173
+ const cerebrumPath = path.join(wolfDir, "cerebrum.md");
174
+ try {
175
+ const stat = fs.statSync(cerebrumPath);
176
+ const hoursSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60);
177
+ // If cerebrum hasn't been updated in 24h+ and there were significant writes
178
+ if (hoursSinceUpdate > 24 && session.files_written.length >= 3) {
179
+ return `cerebrum.md hasn't been updated in ${Math.floor(hoursSinceUpdate)}h. Did you learn any user preferences, conventions, or gotchas this session? Consider updating .wolf/cerebrum.md.`;
180
+ }
181
+ }
182
+ catch {
183
+ // cerebrum.md doesn't exist, that's ok
184
+ }
185
+ return null;
186
+ }
187
+ /**
188
+ * If STATUS.md is older than the session start (or missing) and there was meaningful code
189
+ * activity, nudge Claude to update the handoff doc so the next /clear resumes cheaply. (upstream #40)
190
+ */
191
+ function checkStatusFreshness(wolfDir, session) {
192
+ const statusPath = path.join(wolfDir, "STATUS.md");
193
+ const codeWrites = session.files_written.filter((w) => !w.file.includes("/.wolf/") && !w.file.endsWith(".tmp"));
194
+ if (codeWrites.length < 3)
195
+ return null;
196
+ try {
197
+ const stat = fs.statSync(statusPath);
198
+ const sessionStartMs = session.started ? Date.parse(session.started) : 0;
199
+ if (sessionStartMs && stat.mtimeMs < sessionStartMs) {
200
+ return `STATUS.md wasn't updated this session despite ${codeWrites.length} code writes. Update .wolf/STATUS.md (✅ done / 🚀 next quest) before /clear so the next session resumes in one read.`;
201
+ }
202
+ }
203
+ catch {
204
+ return `.wolf/STATUS.md is missing. Create it with the current quest summary + next steps so /clear stays cheap.`;
205
+ }
206
+ return null;
207
+ }
208
+ /**
209
+ * If there were meaningful edits this session but no non-mechanical memory.md summary
210
+ * was written, nudge Claude to record what it did. (upstream #55)
211
+ */
212
+ function checkSemanticSummaries(wolfDir, writeCount) {
213
+ if (writeCount < 3)
214
+ return null;
215
+ if (countSemanticEntries(wolfDir) > 0)
216
+ return null;
217
+ return `${writeCount} files were changed this session but no meaningful summary was written to memory.md. Consider recording what you did and why.`;
218
+ }
219
+ main().catch(() => process.exit(0));
220
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../src/hooks/stop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,278 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { extractDescription, capDescription } from "./description-extractor.js";
4
+ import { readJSON } from "../utils/fs-safe.js";
5
+ import { writeText } from "../utils/fs-safe.js";
6
+ import { normalizePath } from "../utils/paths.js";
7
+ import { loadIgnore } from "../utils/maintenance.js";
8
+ const BINARY_EXTENSIONS = new Set([
9
+ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".svg",
10
+ ".woff", ".woff2", ".ttf", ".eot", ".otf",
11
+ ".zip", ".tar", ".gz", ".bz2", ".7z", ".rar",
12
+ ".exe", ".dll", ".so", ".dylib", ".bin",
13
+ ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
14
+ ".mp3", ".mp4", ".avi", ".mov", ".webm", ".ogg",
15
+ ".sqlite", ".db",
16
+ ".wasm",
17
+ ".lock",
18
+ ]);
19
+ const CODE_EXTENSIONS = new Set([
20
+ ".ts", ".js", ".tsx", ".jsx", ".py", ".rs", ".go", ".java",
21
+ ".c", ".cpp", ".h", ".css", ".scss", ".sql", ".sh", ".yaml",
22
+ ".yml", ".json", ".toml", ".xml",
23
+ ]);
24
+ const PROSE_EXTENSIONS = new Set([".md", ".txt", ".rst", ".adoc"]);
25
+ function estimateTokens(text, filePath) {
26
+ const ext = path.extname(filePath).toLowerCase();
27
+ let ratio = 3.75;
28
+ if (CODE_EXTENSIONS.has(ext))
29
+ ratio = 3.5;
30
+ if (PROSE_EXTENSIONS.has(ext))
31
+ ratio = 4.0;
32
+ return Math.ceil(text.length / ratio);
33
+ }
34
+ // Files that should never appear in anatomy (secrets, env files)
35
+ const ALWAYS_EXCLUDE_FILES = new Set([".env", ".env.local", ".env.production", ".env.staging", ".env.development"]);
36
+ // Private keys, certs, keystores, credential files — must never be indexed (upstream #54).
37
+ const SECRET_EXTS = new Set([".pem", ".key", ".p8", ".p12", ".pfx", ".keystore", ".jks", ".crt", ".cer", ".der", ".asc", ".gpg", ".pgp", ".ppk", ".kdbx"]);
38
+ const SECRET_NAMES = new Set(["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519", "credentials", ".netrc", ".pgpass", ".htpasswd", ".npmrc", ".pypirc"]);
39
+ function isSecretFile(basename) {
40
+ const b = basename.toLowerCase();
41
+ if (b === ".env" || b.startsWith(".env."))
42
+ return true;
43
+ if (SECRET_NAMES.has(b))
44
+ return true;
45
+ const dot = b.lastIndexOf(".");
46
+ const ext = dot > 0 ? b.slice(dot) : "";
47
+ return SECRET_EXTS.has(ext);
48
+ }
49
+ function shouldExclude(relPath, excludePatterns) {
50
+ const parts = relPath.split("/");
51
+ const basename = parts[parts.length - 1];
52
+ // Always exclude sensitive files regardless of config
53
+ if (ALWAYS_EXCLUDE_FILES.has(basename))
54
+ return true;
55
+ // Also exclude .env.* variants and any secret-bearing file (keys/certs/keystores).
56
+ if (isSecretFile(basename))
57
+ return true;
58
+ for (const pattern of excludePatterns) {
59
+ // Simple glob: check if any path segment matches
60
+ if (pattern.startsWith("*.")) {
61
+ const ext = pattern.slice(1);
62
+ if (relPath.endsWith(ext))
63
+ return true;
64
+ }
65
+ else {
66
+ if (parts.includes(pattern))
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+ function walkDir(dir, rootDir, excludePatterns, maxFiles, entries, ignore) {
73
+ let totalFiles = 0;
74
+ for (const [, list] of entries)
75
+ totalFiles += list.length;
76
+ if (totalFiles >= maxFiles)
77
+ return;
78
+ let items;
79
+ try {
80
+ items = fs.readdirSync(dir, { withFileTypes: true });
81
+ }
82
+ catch {
83
+ return;
84
+ }
85
+ items.sort((a, b) => a.name.localeCompare(b.name));
86
+ for (const item of items) {
87
+ const fullPath = path.join(dir, item.name);
88
+ const relPath = normalizePath(path.relative(rootDir, fullPath));
89
+ if (shouldExclude(relPath, excludePatterns))
90
+ continue;
91
+ if (ignore(relPath))
92
+ continue; // .wolfignore
93
+ if (item.isDirectory()) {
94
+ walkDir(fullPath, rootDir, excludePatterns, maxFiles, entries, ignore);
95
+ }
96
+ else if (item.isFile()) {
97
+ const ext = path.extname(item.name).toLowerCase();
98
+ if (BINARY_EXTENSIONS.has(ext))
99
+ continue;
100
+ // Skip files > 1MB
101
+ try {
102
+ const stat = fs.statSync(fullPath);
103
+ if (stat.size > 1024 * 1024)
104
+ continue;
105
+ }
106
+ catch {
107
+ continue;
108
+ }
109
+ // Read file for token estimation
110
+ let content;
111
+ try {
112
+ content = fs.readFileSync(fullPath, "utf-8");
113
+ }
114
+ catch {
115
+ continue;
116
+ }
117
+ const desc = capDescription(extractDescription(fullPath));
118
+ const tokens = estimateTokens(content, fullPath);
119
+ const section = normalizePath(path.relative(rootDir, dir)) || ".";
120
+ const sectionKey = section === "." ? "./" : section + "/";
121
+ if (!entries.has(sectionKey)) {
122
+ entries.set(sectionKey, []);
123
+ }
124
+ entries.get(sectionKey).push({
125
+ file: item.name,
126
+ description: desc,
127
+ tokens,
128
+ });
129
+ totalFiles++;
130
+ if (totalFiles >= maxFiles)
131
+ return;
132
+ }
133
+ }
134
+ }
135
+ export function serializeAnatomy(sections, metadata) {
136
+ const lines = [
137
+ "# anatomy.md",
138
+ "",
139
+ `> Auto-maintained by OpenWolf. Last scanned: ${metadata.lastScanned}`,
140
+ `> Files: ${metadata.fileCount} tracked | Anatomy hits: ${metadata.hits} | Misses: ${metadata.misses}`,
141
+ "",
142
+ ];
143
+ const sortedKeys = [...sections.keys()].sort();
144
+ for (const key of sortedKeys) {
145
+ lines.push(`## ${key}`);
146
+ lines.push("");
147
+ const entries = sections.get(key);
148
+ entries.sort((a, b) => a.file.localeCompare(b.file));
149
+ for (const entry of entries) {
150
+ const desc = entry.description ? ` — ${entry.description}` : "";
151
+ lines.push(`- \`${entry.file}\`${desc} (~${entry.tokens} tok)`);
152
+ }
153
+ lines.push("");
154
+ }
155
+ return lines.join("\n");
156
+ }
157
+ export function parseAnatomy(content) {
158
+ const sections = new Map();
159
+ let currentSection = "";
160
+ // Split on \r?\n so CRLF files don't leave a trailing \r that breaks the entry regex (upstream #50).
161
+ for (const line of content.split(/\r?\n/)) {
162
+ const sectionMatch = line.match(/^## (.+)/);
163
+ if (sectionMatch) {
164
+ currentSection = sectionMatch[1].trim();
165
+ if (!sections.has(currentSection)) {
166
+ sections.set(currentSection, []);
167
+ }
168
+ continue;
169
+ }
170
+ if (!currentSection)
171
+ continue;
172
+ const entryMatch = line.match(/^- `([^`]+)`(?:\s+—\s+(.+?))?\s*\(~(\d+)\s+tok\)$/);
173
+ if (entryMatch) {
174
+ sections.get(currentSection).push({
175
+ file: entryMatch[1],
176
+ description: entryMatch[2] || "",
177
+ tokens: parseInt(entryMatch[3], 10),
178
+ });
179
+ }
180
+ }
181
+ return sections;
182
+ }
183
+ /**
184
+ * Scan the project and return the anatomy content and file count WITHOUT writing to disk.
185
+ */
186
+ export function buildAnatomy(wolfDir, projectRoot) {
187
+ const configPath = path.join(wolfDir, "config.json");
188
+ const config = readJSON(configPath, {
189
+ version: 1,
190
+ openwolf: {
191
+ anatomy: {
192
+ max_description_length: 100,
193
+ max_files: 500,
194
+ exclude_patterns: ["node_modules", ".git", "dist", "build", ".wolf"],
195
+ },
196
+ token_audit: { chars_per_token_code: 3.5, chars_per_token_prose: 4.0 },
197
+ },
198
+ });
199
+ const entries = new Map();
200
+ const ignore = loadIgnore(projectRoot);
201
+ walkDir(projectRoot, projectRoot, config.openwolf.anatomy.exclude_patterns, config.openwolf.anatomy.max_files, entries, ignore);
202
+ let fileCount = 0;
203
+ for (const [, list] of entries)
204
+ fileCount += list.length;
205
+ const serialized = serializeAnatomy(entries, {
206
+ lastScanned: new Date().toISOString(),
207
+ fileCount,
208
+ hits: 0,
209
+ misses: 0,
210
+ });
211
+ return { content: serialized, fileCount };
212
+ }
213
+ export function scanProject(wolfDir, projectRoot) {
214
+ const { content, fileCount } = buildAnatomy(wolfDir, projectRoot);
215
+ const anatomyPath = path.join(wolfDir, "anatomy.md");
216
+ writeText(anatomyPath, content);
217
+ return fileCount;
218
+ }
219
+ export function updateAnatomyEntry(wolfDir, filePath, projectRoot, action) {
220
+ const anatomyPath = path.join(wolfDir, "anatomy.md");
221
+ let content;
222
+ try {
223
+ content = fs.readFileSync(anatomyPath, "utf-8");
224
+ }
225
+ catch {
226
+ content = "# anatomy.md\n\n> Auto-maintained by OpenWolf.\n";
227
+ }
228
+ const sections = parseAnatomy(content);
229
+ const relPath = normalizePath(path.relative(projectRoot, filePath));
230
+ const dir = path.dirname(relPath);
231
+ const fileName = path.basename(relPath);
232
+ const sectionKey = dir === "." ? "./" : dir + "/";
233
+ if (action === "delete") {
234
+ const entries = sections.get(sectionKey);
235
+ if (entries) {
236
+ const idx = entries.findIndex((e) => e.file === fileName);
237
+ if (idx !== -1)
238
+ entries.splice(idx, 1);
239
+ if (entries.length === 0)
240
+ sections.delete(sectionKey);
241
+ }
242
+ }
243
+ else {
244
+ // upsert
245
+ let fileContent;
246
+ try {
247
+ fileContent = fs.readFileSync(filePath, "utf-8");
248
+ }
249
+ catch {
250
+ return;
251
+ }
252
+ const desc = capDescription(extractDescription(filePath));
253
+ const tokens = estimateTokens(fileContent, filePath);
254
+ const entry = { file: fileName, description: desc, tokens };
255
+ if (!sections.has(sectionKey)) {
256
+ sections.set(sectionKey, []);
257
+ }
258
+ const entries = sections.get(sectionKey);
259
+ const idx = entries.findIndex((e) => e.file === fileName);
260
+ if (idx !== -1) {
261
+ entries[idx] = entry;
262
+ }
263
+ else {
264
+ entries.push(entry);
265
+ }
266
+ }
267
+ let fileCount = 0;
268
+ for (const [, list] of sections)
269
+ fileCount += list.length;
270
+ const serialized = serializeAnatomy(sections, {
271
+ lastScanned: new Date().toISOString(),
272
+ fileCount,
273
+ hits: 0,
274
+ misses: 0,
275
+ });
276
+ writeText(anatomyPath, serialized);
277
+ }
278
+ //# sourceMappingURL=anatomy-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anatomy-scanner.js","sourceRoot":"","sources":["../../../src/scanner/anatomy-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAuBrD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACzC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAC5C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM;IACvC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;IACzD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAC/C,SAAS,EAAE,KAAK;IAChB,OAAO;IACP,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO;IAC1D,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAC3D,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;CACjC,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnE,SAAS,cAAc,CAAC,IAAY,EAAE,QAAgB;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,KAAK,GAAG,GAAG,CAAC;IAC1C,IAAI,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,KAAK,GAAG,GAAG,CAAC;IAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,iEAAiE;AACjE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC,CAAC;AACpH,2FAA2F;AAC3F,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC3J,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AACnJ,SAAS,YAAY,CAAC,QAAgB;IACpC,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,OAAO,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,aAAa,CACpB,OAAe,EACf,eAAyB;IAEzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEzC,sDAAsD;IACtD,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,mFAAmF;IACnF,IAAI,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,iDAAiD;QACjD,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CACd,GAAW,EACX,OAAe,EACf,eAAyB,EACzB,QAAgB,EAChB,OAAoC,EACpC,MAAoC;IAEpC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,OAAO;QAAE,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC;IAC1D,IAAI,UAAU,IAAI,QAAQ;QAAE,OAAO;IAEnC,IAAI,KAAkB,CAAC;IACvB,IAAI,CAAC;QACH,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QAEhE,IAAI,aAAa,CAAC,OAAO,EAAE,eAAe,CAAC;YAAE,SAAS;QACtD,IAAI,MAAM,CAAC,OAAO,CAAC;YAAE,SAAS,CAAC,cAAc;QAE7C,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACzE,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAClD,IAAI,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAEzC,mBAAmB;YACnB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACnC,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI;oBAAE,SAAS;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YAED,iCAAiC;YACjC,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,cAAc,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;YAClE,MAAM,UAAU,GAAG,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;YAE1D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC9B,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,UAAU,CAAE,CAAC,IAAI,CAAC;gBAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI;gBACjB,MAAM;aACP,CAAC,CAAC;YAEH,UAAU,EAAE,CAAC;YACb,IAAI,UAAU,IAAI,QAAQ;gBAAE,OAAO;QACrC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAAqC,EACrC,QAAkF;IAElF,MAAM,KAAK,GAAa;QACtB,cAAc;QACd,EAAE;QACF,gDAAgD,QAAQ,CAAC,WAAW,EAAE;QACtE,YAAY,QAAQ,CAAC,SAAS,4BAA4B,QAAQ,CAAC,IAAI,cAAc,QAAQ,CAAC,MAAM,EAAE;QACtG,EAAE;KACH,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACrD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,IAAI,MAAM,KAAK,CAAC,MAAM,OAAO,CAAC,CAAC;QAClE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;IACnD,IAAI,cAAc,GAAG,EAAE,CAAC;IAExB,qGAAqG;IACrG,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,YAAY,EAAE,CAAC;YACjB,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;gBAClC,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,CAAC,cAAc;YAAE,SAAS;QAE9B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACnF,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAE,CAAC,IAAI,CAAC;gBACjC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;gBACnB,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE;gBAChC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;aACpC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,WAAmB;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,QAAQ,CAAa,UAAU,EAAE;QAC9C,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE;YACR,OAAO,EAAE;gBACP,sBAAsB,EAAE,GAAG;gBAC3B,SAAS,EAAE,GAAG;gBACd,gBAAgB,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;aACrE;YACD,WAAW,EAAE,EAAE,oBAAoB,EAAE,GAAG,EAAE,qBAAqB,EAAE,GAAG,EAAE;SACvE;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACvC,OAAO,CACL,WAAW,EACX,WAAW,EACX,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EACxC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EACjC,OAAO,EACP,MAAM,CACP,CAAC;IAEF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,OAAO;QAAE,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC;IAEzD,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,EAAE;QAC3C,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,SAAS;QACT,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,WAAmB;IAC9D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACrD,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,QAAgB,EAChB,WAAmB,EACnB,MAA2B;IAE3B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,kDAAkD,CAAC;IAC/D,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;IAElD,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAC1D,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,SAAS;QACT,IAAI,WAAmB,CAAC;QACxB,IAAI,CAAC;YACH,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,cAAc,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACrD,MAAM,KAAK,GAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAE1E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC1D,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,QAAQ;QAAE,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC;IAE1D,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE;QAC5C,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,SAAS;QACT,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;IAEH,SAAS,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACrC,CAAC"}