merge: sync Rongsong's scheduler-framework (122 commits, 070c7869..f01ae353)

Introduces WAL replay field-completeness fixes (bug-1687..1697, 1731), scheduler snapshot job-allocation restore, heap UAF fixes (vtask_enq, xcealloc, builtin_fcfs, limit), HA fixes (bug-1694/1695/1696), xceadmin retry, worker signal-exit severity, and their test suites.

Conflicts resolved keeping both sides (drop-audit passed):
- worker.c: mark_worker_jobs_exit (bug-1509 B1) + fetchfile_signal_worker_disconnect (bug-1693)
- handler.go: DefaultBroker.Publish (EventJobFinished) + bug-1731 logging
- config_repl.c / test_config_repl.c: our ns-resolution comment / unique-tmpdir
- test_dispatch_commit.c: both B3-UNKWN and bug-1697 start_time assertions

Excluded Rongsong's bookkeeping per policy: .wolf/, .kiro/, CHANGES.md, reports/;
.claude/ + CLAUDE.md kept ours (his settings.json has machine-absolute paths).
This commit is contained in:
lei.chen 2026-08-31 14:24:17 +00:00
commit 3b6f53bb65
168 changed files with 20744 additions and 568495 deletions

View file

@ -1,139 +0,0 @@
# OpenWolf Operating Protocol
You are working in an OpenWolf-managed project. These rules apply every turn.
## File Navigation
1. Check `.wolf/anatomy.md` BEFORE reading any file. It has a 2-3 line description and token estimate for every file in the project.
2. If the description in anatomy.md is sufficient for your task, do NOT read the full file.
3. If a file is not in anatomy.md, search with Grep/Glob, then update anatomy.md with the new entry.
## Code Generation
1. Before generating code, read `.wolf/cerebrum.md` and respect every entry.
2. Check the `## Do-Not-Repeat` section — these are past mistakes that must not recur.
3. Follow all conventions in `## Key Learnings` and `## User Preferences`.
## After Actions
1. After every significant action, append a one-line entry to `.wolf/memory.md`:
`| HH:MM | description | file(s) | outcome | ~tokens |`
2. After creating, deleting, or renaming files: update `.wolf/anatomy.md`.
## Cerebrum Learning (MANDATORY — every session)
OpenWolf's value comes from learning across sessions. You MUST update `.wolf/cerebrum.md` whenever you learn something useful. This is not optional.
**Update `## User Preferences` when the user:**
- Corrects your approach ("no, do it this way instead")
- Expresses a style preference (naming, structure, formatting)
- Shows a preferred workflow or tool choice
- Rejects a suggestion — record what they preferred instead
- Asks for more/less detail, verbosity, explanation
**Update `## Key Learnings` when you discover:**
- A project convention not obvious from the code (e.g., "tests go in __tests__/ not test/")
- A framework-specific pattern this project uses
- An API behavior that surprised you
- A dependency quirk or version constraint
- How modules connect or data flows through the system
**Update `## Do-Not-Repeat` (with date) when:**
- The user corrects a mistake you made
- You try something that fails and find the right approach
- You discover a gotcha that would trip up a fresh session
**Update `## Decision Log` when:**
- A significant architectural or technical choice is made
- The user explains why they chose approach A over B
- A trade-off is explicitly discussed
**The bar is LOW.** If in doubt, add it. A cerebrum entry that's slightly redundant costs nothing. A missing entry means the next session repeats the same discovery process.
## Bug Logging (MANDATORY)
**Log a bug to `.wolf/buglog.json` whenever ANY of these happen:**
- The user reports an error, bug, or problem
- A test fails or a command produces an error
- You fix something that was broken
- You edit a file more than twice to get it right
- An import, module, or dependency is missing or wrong
- A runtime error, type error, or syntax error occurs
- A build or lint command fails
- A feature doesn't work as expected
- You change error handling, try/catch blocks, or validation logic
- The user says something "doesn't work", "is broken", or "shows wrong X"
**Before fixing:** Read `.wolf/buglog.json` first — the fix may already be known.
**Before appending a new entry:** search existing bugs for the same `file` + overlapping `root_cause`/`error_message`. If a match exists, update THAT entry (`occurrences += 1`, `last_seen = now`, append context to `fix` if the resolution differs) instead of creating a new `bug-NNN`. Never create a second entry under an id that already exists — assign the next unused number after the current max `bug-NNN` in the file (never restart from `bug-001`; a restarted counter collides with existing history and is unrecoverable without a git-history merge). If you fixed something genuinely new, append with this structure:
```json
{
"id": "bug-NNN",
"timestamp": "ISO date",
"error_message": "exact error or user complaint",
"file": "file that was fixed",
"root_cause": "why it broke",
"fix": "what you changed to fix it",
"tags": ["relevant", "keywords"],
"related_bugs": [],
"occurrences": 1,
"last_seen": "ISO date"
}
```
Always preserve the top-level `{"version": 1, "bugs": [...]}` wrapper — never write a bare array.
**The threshold is LOW.** When in doubt, log it. A false positive in the bug log costs nothing. A missed bug means repeating the same mistake later.
**Integrity is automated, not optional:** a Stop hook (`.wolf/hooks/stop.js`) validates `.wolf/buglog.json` (JSON validity, wrapper format, duplicate IDs, ID count/max going backwards vs. the last commit) after every turn and, if it's valid and grew, commits it standalone (`chore(wolf): checkpoint buglog.json (N bugs)`) so git history is always a recoverable backup. If it shrank or fails validation, the hook refuses to commit and prints why — treat that warning as a stop-and-investigate signal, not noise to scroll past.
## Token Discipline
- Never re-read a file already read this session unless it was modified since.
- Prefer anatomy.md descriptions over full file reads when possible.
- Prefer targeted Grep over full file reads when searching for specific code.
- If appending to a file, do not read the entire file first.
## Design QC
When the user asks you to check, evaluate, or improve the design/UI of their app:
1. Run `openwolf designqc` via Bash to capture screenshots.
- The command auto-detects a running dev server, or starts one from package.json if needed
- Use `--url <url>` only if auto-detection fails
- The command saves compressed JPEG screenshots to `.wolf/designqc-captures/`
- Full pages are captured as sectioned viewport-height images (top, section2, ..., bottom)
2. Read the captured screenshot images from `.wolf/designqc-captures/` using the Read tool.
3. Evaluate the design against modern standards (Shadcn UI, Tailwind, clean React patterns):
- Spacing and whitespace consistency
- Typography hierarchy and readability
- Color contrast and accessibility (WCAG)
- Visual hierarchy and focal points
- Component consistency
- Whether the design looks "dull" or "white-coded" (generic, no personality)
4. Provide specific, actionable feedback with fix suggestions.
5. If the user approves, implement the fixes directly in their code.
6. After fixes, re-run `openwolf designqc` to capture new screenshots and verify improvement.
**Token awareness:** Each screenshot costs ~2500 tokens. The command compresses images (JPEG quality 70, max width 1200px) to minimize cost. For large apps, use `--routes / /specific-page` to limit captures.
## Reframe — UI Framework Selection
When the user asks to change, pick, migrate, or "reframe" their project's UI framework:
1. Read `.wolf/reframe-frameworks.md` for the full framework knowledge base.
2. Ask the user the decision questions from the file (current stack, priority, Tailwind usage, theme preference, app type). Stop early once the choice narrows to 1-2 options.
3. Present a recommendation with reasoning based on the comparison matrix.
4. Once the user confirms, use the selected framework's prompt from the file — **adapted to the actual project** using `.wolf/anatomy.md` for real file paths, routes, and components.
5. Execute the migration: install dependencies, update config, refactor components.
6. After migration, run `openwolf designqc` to verify the new look.
**Do NOT read the entire reframe-frameworks.md into context upfront.** Read the decision questions and comparison matrix first (~50 lines). Only read the specific framework's prompt section after the user chooses.
## Session End
Before ending or when asked to wrap up:
1. Write a session summary to `.wolf/memory.md`.
2. Review the session: did you learn anything? Did the user correct you? Did you fix a bug? If yes, update `.wolf/cerebrum.md` and/or `.wolf/buglog.json`.
3. Do NOT manually `git add`/commit `.wolf/buglog.json` yourself as part of a feature/task commit — the Stop hook checkpoints it standalone once it validates cleanly (see Bug Logging above). Mixing it into a feature commit just duplicates that history under an unrelated message.

View file

@ -1,5 +0,0 @@
{
"last_scanned": "2026-08-17T01:00:00.081Z",
"git_head": "397d8e7f9c760ffc8c51df73948ee03aa5f7dfff",
"file_count": 504
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,73 +0,0 @@
{
"version": 1,
"openwolf": {
"enabled": true,
"anatomy": {
"auto_scan_on_init": true,
"rescan_interval_hours": 6,
"max_description_length": 100,
"max_files": 500,
"exclude_patterns": [
"node_modules",
".git",
"dist",
"build",
".wolf",
".next",
".nuxt",
"coverage",
"__pycache__",
".cache",
"target",
".vscode",
".idea",
".turbo",
".vercel",
".netlify",
".output",
"*.min.js",
"*.min.css"
]
},
"token_audit": {
"enabled": true,
"report_frequency": "weekly",
"waste_threshold_percent": 15,
"chars_per_token_code": 3.5,
"chars_per_token_prose": 4.0
},
"cron": {
"enabled": true,
"max_retry_attempts": 3,
"dead_letter_enabled": true,
"heartbeat_interval_minutes": 30,
"use_claude_p": true,
"api_key_env": null
},
"memory": {
"consolidation_after_days": 7,
"max_entries_before_consolidation": 200
},
"cerebrum": {
"max_tokens": 2000,
"reflection_frequency": "weekly"
},
"daemon": {
"port": 18790,
"log_level": "info"
},
"dashboard": {
"enabled": true,
"port": 18791
},
"designqc": {
"enabled": true,
"viewports": [
{ "name": "desktop", "width": 1440, "height": 900 },
{ "name": "mobile", "width": 375, "height": 812 }
],
"max_screenshots": 6,
"chrome_path": null
}
}
}

View file

@ -1,97 +0,0 @@
{
"version": 1,
"tasks": [
{
"id": "anatomy-rescan",
"name": "Full anatomy rescan",
"schedule": "0 */6 * * *",
"description": "Re-scans project filesystem and reconciles anatomy.md",
"action": { "type": "scan_project" },
"retry": {
"max_attempts": 3,
"backoff": "exponential",
"base_delay_seconds": 30
},
"failsafe": {
"on_failure": "log_and_continue",
"alert_after_consecutive_failures": 2,
"dead_letter": true
},
"enabled": true
},
{
"id": "memory-consolidation",
"name": "Consolidate old memory",
"schedule": "0 2 * * *",
"description": "Compress memory.md entries older than 7 days",
"action": {
"type": "consolidate_memory",
"params": { "older_than_days": 7 }
},
"retry": {
"max_attempts": 2,
"backoff": "exponential",
"base_delay_seconds": 60
},
"failsafe": {
"on_failure": "skip_and_retry_next_cycle",
"dead_letter": false
},
"enabled": true
},
{
"id": "token-audit",
"name": "Token audit report",
"schedule": "0 0 * * 1",
"description": "Weekly waste pattern detection",
"action": { "type": "generate_token_report" },
"retry": {
"max_attempts": 2,
"backoff": "linear",
"base_delay_seconds": 60
},
"failsafe": { "on_failure": "log_and_continue", "dead_letter": true },
"enabled": true
},
{
"id": "cerebrum-reflection",
"name": "Cerebrum reflection",
"schedule": "0 3 * * 0",
"description": "Weekly AI review of cerebrum.md — prune stale entries, consolidate duplicates",
"action": {
"type": "ai_task",
"params": {
"prompt": "Review this cerebrum.md. Remove duplicate preferences (keep newer). Remove Do-Not-Repeat entries older than 90 days if no longer relevant. Consolidate Key Learnings that overlap. Keep the file under 2000 tokens. Return the cleaned file content only.",
"context_files": [".wolf/cerebrum.md"]
}
},
"retry": {
"max_attempts": 1,
"backoff": "none",
"base_delay_seconds": 0
},
"failsafe": { "on_failure": "log_and_continue", "dead_letter": false },
"enabled": true
},
{
"id": "project-suggestions",
"name": "AI suggestions",
"schedule": "0 4 * * 1",
"description": "Weekly AI analysis with project improvement suggestions",
"action": {
"type": "ai_task",
"params": {
"prompt": "Based on the recent memory entries and current project structure, provide: 1) Key achievements this week, 2) Code improvements to consider, 3) Logical next tasks, 4) Technical debt or risks. Be specific and actionable. Return as JSON: {achievements:[], improvements:[], next_tasks:[], risks:[]}",
"context_files": [".wolf/memory.md", ".wolf/anatomy.md"]
}
},
"retry": {
"max_attempts": 1,
"backoff": "none",
"base_delay_seconds": 0
},
"failsafe": { "on_failure": "log_and_continue", "dead_letter": false },
"enabled": true
}
]
}

View file

@ -1,570 +0,0 @@
{
"last_heartbeat": "2026-08-17T15:18:42.178Z",
"engine_status": "running",
"execution_log": [
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-21T07:00:00.623Z",
"duration_ms": 37
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-05-21T08:54:59.810Z",
"duration_ms": 120927,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-05-21T08:59:01.535Z",
"duration_ms": 241724,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-05-21T08:59:01.536Z",
"duration_ms": 120843,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-05-21T09:00:00.558Z",
"duration_ms": 1
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-21T13:00:00.039Z",
"duration_ms": 32
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-05-21T13:48:25.789Z",
"duration_ms": 120850,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-21T19:00:00.445Z",
"duration_ms": 29
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-22T01:00:00.198Z",
"duration_ms": 31
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-22T07:00:00.675Z",
"duration_ms": 31
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-05-22T09:00:00.789Z",
"duration_ms": 3
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-22T13:00:00.217Z",
"duration_ms": 29
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-22T19:00:00.644Z",
"duration_ms": 25
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-23T01:00:00.097Z",
"duration_ms": 20
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-23T07:00:00.825Z",
"duration_ms": 24
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-05-23T09:00:00.492Z",
"duration_ms": 0
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-23T13:00:00.641Z",
"duration_ms": 29
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-23T19:00:00.942Z",
"duration_ms": 27
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-24T01:00:00.488Z",
"duration_ms": 23
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-24T07:00:00.783Z",
"duration_ms": 33
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-05-24T09:00:00.882Z",
"duration_ms": 1
},
{
"task_id": "cerebrum-reflection",
"status": "success",
"timestamp": "2026-05-24T10:01:38.148Z",
"duration_ms": 97165
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-24T13:00:00.732Z",
"duration_ms": 26
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-24T19:00:00.183Z",
"duration_ms": 25
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-05-25T01:00:00.439Z",
"duration_ms": 22
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-02T09:00:00.411Z",
"duration_ms": 3
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-08T09:00:00.899Z",
"duration_ms": 4
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-10T00:19:56.961Z",
"duration_ms": 9810,
"error": "claude -p failed: Exit code 1: SessionEnd hook [export PATH=\"$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH\"; _R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R=\"${_R%/}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/thedotmack/plugin\"; node \"$_R/scripts/bun-runner.js\" \"$_R/scripts/worker-service.cjs\" hook claude-code session-complete] failed: Hook cancelled"
},
{
"task_id": "token-audit",
"status": "success",
"timestamp": "2026-06-10T00:20:16.187Z",
"duration_ms": 53
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-10T00:20:16.188Z",
"duration_ms": 19227,
"error": "claude -p failed: Exit code 1: SessionEnd hook [export PATH=\"$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH\"; _R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R=\"${_R%/}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/thedotmack/plugin\"; node \"$_R/scripts/bun-runner.js\" \"$_R/scripts/worker-service.cjs\" hook claude-code session-complete] failed: Hook cancelled"
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-06-10T00:20:16.188Z",
"duration_ms": 9557,
"error": "claude -p failed: Exit code 1: SessionEnd hook [export PATH=\"$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH\"; _R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R=\"${_R%/}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/thedotmack/plugin\"; node \"$_R/scripts/bun-runner.js\" \"$_R/scripts/worker-service.cjs\" hook claude-code session-complete] failed: Hook cancelled"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-10T01:00:00.580Z",
"duration_ms": 50
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-10T07:00:00.203Z",
"duration_ms": 77
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-10T09:00:00.338Z",
"duration_ms": 2
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-10T09:17:09.247Z",
"duration_ms": 1
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-06-10T09:19:23.180Z",
"duration_ms": 122206,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-06-10T09:21:25.363Z",
"duration_ms": 122181,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-10T11:06:51.759Z",
"duration_ms": 122186,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-10T11:10:56.094Z",
"duration_ms": 244332,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-10T11:10:56.094Z",
"duration_ms": 122130,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-10T13:00:00.551Z",
"duration_ms": 41
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-10T19:00:00.364Z",
"duration_ms": 25
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-11T01:00:00.149Z",
"duration_ms": 27
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-11T07:00:00.342Z",
"duration_ms": 33
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-11T09:00:00.463Z",
"duration_ms": 3
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-11T13:00:00.757Z",
"duration_ms": 39
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-11T19:00:00.284Z",
"duration_ms": 43
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-12T01:00:00.909Z",
"duration_ms": 31
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-12T07:00:00.886Z",
"duration_ms": 74
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-12T09:00:00.069Z",
"duration_ms": 2
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-12T19:00:00.361Z",
"duration_ms": 50
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-13T01:00:00.989Z",
"duration_ms": 72
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-13T07:00:00.706Z",
"duration_ms": 49
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-13T09:00:00.819Z",
"duration_ms": 3
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-13T13:00:01.073Z",
"duration_ms": 65
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-13T19:00:00.496Z",
"duration_ms": 25
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-14T01:00:00.983Z",
"duration_ms": 84
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-14T07:00:00.492Z",
"duration_ms": 57
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-14T09:00:00.686Z",
"duration_ms": 61
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-06-14T10:02:02.337Z",
"duration_ms": 122215,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-14T13:00:00.912Z",
"duration_ms": 111
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-14T19:00:00.194Z",
"duration_ms": 42
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-14T23:45:24.291Z",
"duration_ms": 2
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-15T01:00:00.635Z",
"duration_ms": 82
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-15T09:00:00.984Z",
"duration_ms": 4
},
{
"task_id": "token-audit",
"status": "success",
"timestamp": "2026-06-15T09:09:22.749Z",
"duration_ms": 56
},
{
"task_id": "token-audit",
"status": "success",
"timestamp": "2026-06-15T09:09:23.810Z",
"duration_ms": 42
},
{
"task_id": "project-suggestions",
"status": "failed",
"timestamp": "2026-06-15T11:02:02.349Z",
"duration_ms": 122204,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-15T13:00:00.498Z",
"duration_ms": 101
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-15T19:00:01.072Z",
"duration_ms": 80
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-16T01:00:00.585Z",
"duration_ms": 25
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-16T07:00:00.511Z",
"duration_ms": 53
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-06-27T11:20:23.754Z",
"duration_ms": 120641,
"error": "claude -p failed: spawnSync /bin/sh ETIMEDOUT"
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-27T11:21:06.179Z",
"duration_ms": 99
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-06-27T11:21:07.514Z",
"duration_ms": 7
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-06-27T13:00:00.671Z",
"duration_ms": 45
},
{
"task_id": "project-suggestions",
"status": "success",
"timestamp": "2026-08-15T13:04:20.520Z",
"duration_ms": 74147
},
{
"task_id": "project-suggestions",
"status": "success",
"timestamp": "2026-08-15T13:06:46.442Z",
"duration_ms": 145921
},
{
"task_id": "project-suggestions",
"status": "success",
"timestamp": "2026-08-15T13:06:46.442Z",
"duration_ms": 81092
},
{
"task_id": "cerebrum-reflection",
"status": "failed",
"timestamp": "2026-08-15T13:18:59.222Z",
"duration_ms": 122567,
"error": "claude -p failed: spawnSync claude ETIMEDOUT"
},
{
"task_id": "token-audit",
"status": "success",
"timestamp": "2026-08-15T13:18:59.603Z",
"duration_ms": 376
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-08-15T13:18:59.621Z",
"duration_ms": 10
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-15T13:18:59.749Z",
"duration_ms": 123
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-15T19:00:00.088Z",
"duration_ms": 71
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-16T01:00:00.099Z",
"duration_ms": 72
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-16T07:00:00.069Z",
"duration_ms": 58
},
{
"task_id": "memory-consolidation",
"status": "success",
"timestamp": "2026-08-16T09:00:00.009Z",
"duration_ms": 3
},
{
"task_id": "cerebrum-reflection",
"status": "success",
"timestamp": "2026-08-16T10:01:48.349Z",
"duration_ms": 108347
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-16T13:00:00.063Z",
"duration_ms": 59
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-16T19:00:00.061Z",
"duration_ms": 50
},
{
"task_id": "anatomy-rescan",
"status": "success",
"timestamp": "2026-08-17T01:00:00.082Z",
"duration_ms": 52
}
],
"dead_letter_queue": [],
"upcoming": []
}

View file

@ -1,445 +0,0 @@
[2026-05-21T04:35:43.991Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-05-21T04:35:43.992Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-05-21T04:35:43.992Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-05-21T04:35:43.992Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-05-21T04:35:43.993Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-05-21T04:35:43.994Z] [INFO] File watcher started on .wolf/
[2026-05-21T04:35:43.994Z] [INFO] OpenWolf daemon started
[2026-05-21T04:35:43.995Z] [INFO] Dashboard server listening on port 18791
[2026-05-21T04:36:03.472Z] [INFO] WebSocket client connected
[2026-05-21T04:38:32.931Z] [INFO] WebSocket client connected
[2026-05-21T04:42:03.306Z] [INFO] WebSocket client connected
[2026-05-21T07:00:00.586Z] [INFO] Executing task: Full anatomy rescan
[2026-05-21T07:00:00.623Z] [INFO] Task Full anatomy rescan completed in 37ms
[2026-05-21T08:52:58.883Z] [INFO] Executing task: AI suggestions
[2026-05-21T08:54:59.810Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-05-21T08:54:59.811Z] [INFO] Executing task: AI suggestions
[2026-05-21T08:57:00.693Z] [INFO] Executing task: AI suggestions
[2026-05-21T08:59:01.535Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-05-21T08:59:01.536Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-05-21T09:00:00.557Z] [INFO] Executing task: Consolidate old memory
[2026-05-21T09:00:00.558Z] [INFO] Task Consolidate old memory completed in 1ms
[2026-05-21T12:40:53.745Z] [INFO] WebSocket client connected
[2026-05-21T13:00:00.007Z] [INFO] Executing task: Full anatomy rescan
[2026-05-21T13:00:00.039Z] [INFO] Task Full anatomy rescan completed in 32ms
[2026-05-21T13:46:24.939Z] [INFO] Executing task: AI suggestions
[2026-05-21T13:48:25.789Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-05-21T19:00:00.416Z] [INFO] Executing task: Full anatomy rescan
[2026-05-21T19:00:00.445Z] [INFO] Task Full anatomy rescan completed in 29ms
[2026-05-22T01:00:00.167Z] [INFO] Executing task: Full anatomy rescan
[2026-05-22T01:00:00.198Z] [INFO] Task Full anatomy rescan completed in 31ms
[2026-05-22T07:00:00.644Z] [INFO] Executing task: Full anatomy rescan
[2026-05-22T07:00:00.675Z] [INFO] Task Full anatomy rescan completed in 31ms
[2026-05-22T09:00:00.786Z] [INFO] Executing task: Consolidate old memory
[2026-05-22T09:00:00.789Z] [INFO] Task Consolidate old memory completed in 3ms
[2026-05-22T13:00:00.188Z] [INFO] Executing task: Full anatomy rescan
[2026-05-22T13:00:00.217Z] [INFO] Task Full anatomy rescan completed in 29ms
[2026-05-22T19:00:00.619Z] [INFO] Executing task: Full anatomy rescan
[2026-05-22T19:00:00.644Z] [INFO] Task Full anatomy rescan completed in 25ms
[2026-05-23T01:00:00.077Z] [INFO] Executing task: Full anatomy rescan
[2026-05-23T01:00:00.097Z] [INFO] Task Full anatomy rescan completed in 20ms
[2026-05-23T07:00:00.801Z] [INFO] Executing task: Full anatomy rescan
[2026-05-23T07:00:00.826Z] [INFO] Task Full anatomy rescan completed in 24ms
[2026-05-23T09:00:00.492Z] [INFO] Executing task: Consolidate old memory
[2026-05-23T09:00:00.493Z] [INFO] Task Consolidate old memory completed in 0ms
[2026-05-23T13:00:00.613Z] [INFO] Executing task: Full anatomy rescan
[2026-05-23T13:00:00.641Z] [INFO] Task Full anatomy rescan completed in 29ms
[2026-05-23T19:00:00.915Z] [INFO] Executing task: Full anatomy rescan
[2026-05-23T19:00:00.942Z] [INFO] Task Full anatomy rescan completed in 27ms
[2026-05-24T01:00:00.465Z] [INFO] Executing task: Full anatomy rescan
[2026-05-24T01:00:00.488Z] [INFO] Task Full anatomy rescan completed in 23ms
[2026-05-24T07:00:00.750Z] [INFO] Executing task: Full anatomy rescan
[2026-05-24T07:00:00.783Z] [INFO] Task Full anatomy rescan completed in 33ms
[2026-05-24T09:00:00.881Z] [INFO] Executing task: Consolidate old memory
[2026-05-24T09:00:00.882Z] [INFO] Task Consolidate old memory completed in 1ms
[2026-05-24T10:00:00.983Z] [INFO] Executing task: Cerebrum reflection
[2026-05-24T10:01:38.148Z] [INFO] Task Cerebrum reflection completed in 97165ms
[2026-05-24T13:00:00.706Z] [INFO] Executing task: Full anatomy rescan
[2026-05-24T13:00:00.733Z] [INFO] Task Full anatomy rescan completed in 26ms
[2026-05-24T19:00:00.158Z] [INFO] Executing task: Full anatomy rescan
[2026-05-24T19:00:00.183Z] [INFO] Task Full anatomy rescan completed in 25ms
[2026-05-25T01:00:00.417Z] [INFO] Executing task: Full anatomy rescan
[2026-05-25T01:00:00.439Z] [INFO] Task Full anatomy rescan completed in 22ms
[2026-06-02T04:08:51.473Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-02T04:08:51.474Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-02T04:08:51.475Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-02T04:08:51.475Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-02T04:08:51.475Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-02T04:08:51.476Z] [INFO] File watcher started on .wolf/
[2026-06-02T04:08:51.477Z] [INFO] OpenWolf daemon started
[2026-06-02T04:08:51.477Z] [INFO] Dashboard server listening on port 18791
[2026-06-02T04:09:36.978Z] [INFO] WebSocket client connected
[2026-06-02T04:18:54.844Z] [INFO] WebSocket client connected
[2026-06-02T07:04:11.391Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-02T07:04:11.392Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-02T07:04:11.392Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-02T07:04:11.392Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-02T07:04:11.392Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-02T07:04:11.393Z] [INFO] File watcher started on .wolf/
[2026-06-02T07:04:11.394Z] [INFO] OpenWolf daemon started
[2026-06-02T07:04:11.394Z] [INFO] Dashboard server listening on port 18791
[2026-06-02T07:58:22.561Z] [INFO] WebSocket client connected
[2026-06-02T09:00:00.408Z] [INFO] Executing task: Consolidate old memory
[2026-06-02T09:00:00.412Z] [INFO] Task Consolidate old memory completed in 3ms
[2026-06-08T08:03:14.764Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-08T08:03:14.766Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-08T08:03:14.766Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-08T08:03:14.766Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-08T08:03:14.766Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-08T08:03:14.767Z] [INFO] File watcher started on .wolf/
[2026-06-08T08:03:14.768Z] [INFO] OpenWolf daemon started
[2026-06-08T08:03:14.768Z] [INFO] Dashboard server listening on port 18791
[2026-06-08T09:00:00.895Z] [INFO] Executing task: Consolidate old memory
[2026-06-08T09:00:00.900Z] [INFO] Task Consolidate old memory completed in 4ms
[2026-06-10T00:06:27.655Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-10T00:06:27.657Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-10T00:06:27.657Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-10T00:06:27.657Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-10T00:06:27.657Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-10T00:06:27.659Z] [INFO] File watcher started on .wolf/
[2026-06-10T00:06:27.659Z] [INFO] OpenWolf daemon started
[2026-06-10T00:06:27.660Z] [INFO] Dashboard server listening on port 18791
[2026-06-10T00:07:13.306Z] [INFO] WebSocket client connected
[2026-06-10T00:12:13.316Z] [INFO] WebSocket client connected
[2026-06-10T00:19:47.150Z] [INFO] Executing task: AI suggestions
[2026-06-10T00:19:56.961Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: Exit code 1: SessionEnd hook [export PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH"; _R="${CLAUDE_PLUGIN_ROOT}"; [ -z "$_R" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R="${_R%/}"; [ -z "$_R" ] && _R="$HOME/.claude/plugins/marketplaces/thedotmack/plugin"; node "$_R/scripts/bun-runner.js" "$_R/scripts/worker-service.cjs" hook claude-code session-complete] failed: Hook cancelled
[2026-06-10T00:19:56.961Z] [INFO] Executing task: AI suggestions
[2026-06-10T00:20:06.631Z] [INFO] Executing task: Cerebrum reflection
[2026-06-10T00:20:16.134Z] [INFO] Executing task: Token audit report
[2026-06-10T00:20:16.188Z] [INFO] Task Token audit report completed in 53ms
[2026-06-10T00:20:16.188Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: Exit code 1: SessionEnd hook [export PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH"; _R="${CLAUDE_PLUGIN_ROOT}"; [ -z "$_R" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R="${_R%/}"; [ -z "$_R" ] && _R="$HOME/.claude/plugins/marketplaces/thedotmack/plugin"; node "$_R/scripts/bun-runner.js" "$_R/scripts/worker-service.cjs" hook claude-code session-complete] failed: Hook cancelled
[2026-06-10T00:20:16.188Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: Exit code 1: SessionEnd hook [export PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH"; _R="${CLAUDE_PLUGIN_ROOT}"; [ -z "$_R" ] && _R=$(ls -dt $HOME/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 2>/dev/null | head -1); _R="${_R%/}"; [ -z "$_R" ] && _R="$HOME/.claude/plugins/marketplaces/thedotmack/plugin"; node "$_R/scripts/bun-runner.js" "$_R/scripts/worker-service.cjs" hook claude-code session-complete] failed: Hook cancelled
[2026-06-10T01:00:00.530Z] [INFO] Executing task: Full anatomy rescan
[2026-06-10T01:00:00.580Z] [INFO] Task Full anatomy rescan completed in 50ms
[2026-06-10T07:00:00.126Z] [INFO] Executing task: Full anatomy rescan
[2026-06-10T07:00:00.204Z] [INFO] Task Full anatomy rescan completed in 77ms
[2026-06-10T09:00:00.335Z] [INFO] Executing task: Consolidate old memory
[2026-06-10T09:00:00.338Z] [INFO] Task Consolidate old memory completed in 2ms
[2026-06-10T09:16:02.961Z] [INFO] WebSocket client connected
[2026-06-10T09:17:09.246Z] [INFO] Executing task: Consolidate old memory
[2026-06-10T09:17:09.248Z] [INFO] Task Consolidate old memory completed in 1ms
[2026-06-10T09:17:20.973Z] [INFO] Executing task: Cerebrum reflection
[2026-06-10T09:19:23.179Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-10T09:19:23.181Z] [INFO] Executing task: Cerebrum reflection
[2026-06-10T09:21:25.362Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-10T09:21:25.366Z] [INFO] WebSocket client connected
[2026-06-10T09:21:25.366Z] [INFO] WebSocket client connected
[2026-06-10T09:21:30.087Z] [INFO] WebSocket client connected
[2026-06-10T09:30:07.203Z] [INFO] WebSocket client connected
[2026-06-10T11:02:56.697Z] [INFO] WebSocket client connected
[2026-06-10T11:04:49.573Z] [INFO] Executing task: AI suggestions
[2026-06-10T11:06:51.759Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-10T11:06:51.761Z] [INFO] Executing task: AI suggestions
[2026-06-10T11:08:53.964Z] [INFO] Executing task: AI suggestions
[2026-06-10T11:10:56.093Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-10T11:10:56.094Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-10T13:00:00.509Z] [INFO] Executing task: Full anatomy rescan
[2026-06-10T13:00:00.551Z] [INFO] Task Full anatomy rescan completed in 41ms
[2026-06-10T19:00:00.339Z] [INFO] Executing task: Full anatomy rescan
[2026-06-10T19:00:00.364Z] [INFO] Task Full anatomy rescan completed in 25ms
[2026-06-11T01:00:00.122Z] [INFO] Executing task: Full anatomy rescan
[2026-06-11T01:00:00.149Z] [INFO] Task Full anatomy rescan completed in 27ms
[2026-06-11T07:00:00.309Z] [INFO] Executing task: Full anatomy rescan
[2026-06-11T07:00:00.343Z] [INFO] Task Full anatomy rescan completed in 33ms
[2026-06-11T09:00:00.460Z] [INFO] Executing task: Consolidate old memory
[2026-06-11T09:00:00.463Z] [INFO] Task Consolidate old memory completed in 3ms
[2026-06-11T13:00:00.718Z] [INFO] Executing task: Full anatomy rescan
[2026-06-11T13:00:00.757Z] [INFO] Task Full anatomy rescan completed in 39ms
[2026-06-11T19:00:00.241Z] [INFO] Executing task: Full anatomy rescan
[2026-06-11T19:00:00.284Z] [INFO] Task Full anatomy rescan completed in 43ms
[2026-06-12T01:00:00.878Z] [INFO] Executing task: Full anatomy rescan
[2026-06-12T01:00:00.909Z] [INFO] Task Full anatomy rescan completed in 31ms
[2026-06-12T07:00:00.812Z] [INFO] Executing task: Full anatomy rescan
[2026-06-12T07:00:00.886Z] [INFO] Task Full anatomy rescan completed in 74ms
[2026-06-12T09:00:00.067Z] [INFO] Executing task: Consolidate old memory
[2026-06-12T09:00:00.069Z] [INFO] Task Consolidate old memory completed in 2ms
[2026-06-12T19:00:00.311Z] [INFO] Executing task: Full anatomy rescan
[2026-06-12T19:00:00.361Z] [INFO] Task Full anatomy rescan completed in 50ms
[2026-06-13T01:00:00.917Z] [INFO] Executing task: Full anatomy rescan
[2026-06-13T01:00:00.989Z] [INFO] Task Full anatomy rescan completed in 72ms
[2026-06-13T07:00:00.656Z] [INFO] Executing task: Full anatomy rescan
[2026-06-13T07:00:00.706Z] [INFO] Task Full anatomy rescan completed in 49ms
[2026-06-13T09:00:00.816Z] [INFO] Executing task: Consolidate old memory
[2026-06-13T09:00:00.820Z] [INFO] Task Consolidate old memory completed in 3ms
[2026-06-13T13:00:01.008Z] [INFO] Executing task: Full anatomy rescan
[2026-06-13T13:00:01.074Z] [INFO] Task Full anatomy rescan completed in 65ms
[2026-06-13T19:00:00.471Z] [INFO] Executing task: Full anatomy rescan
[2026-06-13T19:00:00.496Z] [INFO] Task Full anatomy rescan completed in 25ms
[2026-06-14T01:00:00.899Z] [INFO] Executing task: Full anatomy rescan
[2026-06-14T01:00:00.983Z] [INFO] Task Full anatomy rescan completed in 84ms
[2026-06-14T07:00:00.435Z] [INFO] Executing task: Full anatomy rescan
[2026-06-14T07:00:00.492Z] [INFO] Task Full anatomy rescan completed in 57ms
[2026-06-14T09:00:00.624Z] [INFO] Executing task: Consolidate old memory
[2026-06-14T09:00:00.686Z] [INFO] Task Consolidate old memory completed in 61ms
[2026-06-14T10:00:00.122Z] [INFO] Executing task: Cerebrum reflection
[2026-06-14T10:02:02.337Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-14T13:00:00.801Z] [INFO] Executing task: Full anatomy rescan
[2026-06-14T13:00:00.913Z] [INFO] Task Full anatomy rescan completed in 111ms
[2026-06-14T19:00:00.152Z] [INFO] Executing task: Full anatomy rescan
[2026-06-14T19:00:00.194Z] [INFO] Task Full anatomy rescan completed in 42ms
[2026-06-14T23:45:24.288Z] [INFO] Executing task: Consolidate old memory
[2026-06-14T23:45:24.291Z] [INFO] Task Consolidate old memory completed in 2ms
[2026-06-15T01:00:00.552Z] [INFO] Executing task: Full anatomy rescan
[2026-06-15T01:00:00.635Z] [INFO] Task Full anatomy rescan completed in 82ms
[2026-06-15T08:19:54.500Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-15T08:19:54.500Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-15T08:19:54.501Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-15T08:19:54.501Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-15T08:19:54.501Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-15T08:19:54.502Z] [INFO] File watcher started on .wolf/
[2026-06-15T08:19:54.502Z] [INFO] OpenWolf daemon started
[2026-06-15T08:19:54.503Z] [INFO] Dashboard server listening on port 18791
[2026-06-15T09:00:00.979Z] [INFO] Executing task: Consolidate old memory
[2026-06-15T09:00:00.984Z] [INFO] Task Consolidate old memory completed in 4ms
[2026-06-15T09:07:55.657Z] [INFO] WebSocket client connected
[2026-06-15T09:09:22.693Z] [INFO] Executing task: Token audit report
[2026-06-15T09:09:22.750Z] [INFO] Task Token audit report completed in 56ms
[2026-06-15T09:09:23.768Z] [INFO] Executing task: Token audit report
[2026-06-15T09:09:23.810Z] [INFO] Task Token audit report completed in 42ms
[2026-06-15T11:00:00.144Z] [INFO] Executing task: AI suggestions
[2026-06-15T11:02:02.348Z] [ERROR] Task AI suggestions failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-15T13:00:00.397Z] [INFO] Executing task: Full anatomy rescan
[2026-06-15T13:00:00.498Z] [INFO] Task Full anatomy rescan completed in 101ms
[2026-06-15T19:00:00.992Z] [INFO] Executing task: Full anatomy rescan
[2026-06-15T19:00:01.072Z] [INFO] Task Full anatomy rescan completed in 80ms
[2026-06-16T01:00:00.560Z] [INFO] Executing task: Full anatomy rescan
[2026-06-16T01:00:00.585Z] [INFO] Task Full anatomy rescan completed in 25ms
[2026-06-16T02:48:16.930Z] [INFO] WebSocket client connected
[2026-06-16T07:00:00.458Z] [INFO] Executing task: Full anatomy rescan
[2026-06-16T07:00:00.512Z] [INFO] Task Full anatomy rescan completed in 53ms
[2026-06-27T11:16:46.212Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-06-27T11:16:46.213Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-06-27T11:16:46.213Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-06-27T11:16:46.213Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-06-27T11:16:46.213Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-06-27T11:16:46.214Z] [INFO] File watcher started on .wolf/
[2026-06-27T11:16:46.215Z] [INFO] OpenWolf daemon started
[2026-06-27T11:16:46.215Z] [INFO] Dashboard server listening on port 18791
[2026-06-27T11:17:54.220Z] [INFO] WebSocket client connected
[2026-06-27T11:18:23.112Z] [INFO] Executing task: Cerebrum reflection
[2026-06-27T11:20:23.753Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: spawnSync /bin/sh ETIMEDOUT
[2026-06-27T11:20:23.764Z] [INFO] WebSocket client connected
[2026-06-27T11:20:23.766Z] [INFO] WebSocket client connected
[2026-06-27T11:20:23.767Z] [INFO] WebSocket client connected
[2026-06-27T11:20:23.768Z] [INFO] WebSocket client connected
[2026-06-27T11:20:23.771Z] [INFO] WebSocket client connected
[2026-06-27T11:20:23.802Z] [INFO] WebSocket client connected
[2026-06-27T11:20:27.060Z] [INFO] WebSocket client connected
[2026-06-27T11:21:06.080Z] [INFO] Executing task: Full anatomy rescan
[2026-06-27T11:21:06.180Z] [INFO] Task Full anatomy rescan completed in 99ms
[2026-06-27T11:21:07.507Z] [INFO] Executing task: Consolidate old memory
[2026-06-27T11:21:07.515Z] [INFO] Task Consolidate old memory completed in 7ms
[2026-06-27T13:00:00.626Z] [INFO] Executing task: Full anatomy rescan
[2026-06-27T13:00:00.671Z] [INFO] Task Full anatomy rescan completed in 45ms
[2026-08-15T13:01:28.414Z] [INFO] Scheduled task: Full anatomy rescan (0 */6 * * *)
[2026-08-15T13:01:28.415Z] [INFO] Scheduled task: Consolidate old memory (0 2 * * *)
[2026-08-15T13:01:28.415Z] [INFO] Scheduled task: Token audit report (0 0 * * 1)
[2026-08-15T13:01:28.415Z] [INFO] Scheduled task: Cerebrum reflection (0 3 * * 0)
[2026-08-15T13:01:28.416Z] [INFO] Scheduled task: AI suggestions (0 4 * * 1)
[2026-08-15T13:01:28.417Z] [INFO] File watcher started on .wolf/
[2026-08-15T13:01:28.417Z] [INFO] OpenWolf daemon started
[2026-08-15T13:01:28.420Z] [INFO] Dashboard server listening on 127.0.0.1:18791
[2026-08-15T13:01:34.596Z] [INFO] WebSocket client connected
[2026-08-15T13:03:06.373Z] [INFO] Executing task: AI suggestions
[2026-08-15T13:04:20.520Z] [INFO] Task AI suggestions completed in 74147ms
[2026-08-15T13:04:20.521Z] [INFO] Executing task: AI suggestions
[2026-08-15T13:05:25.350Z] [INFO] Executing task: AI suggestions
[2026-08-15T13:06:46.442Z] [INFO] Task AI suggestions completed in 145921ms
[2026-08-15T13:06:46.443Z] [INFO] Task AI suggestions completed in 81092ms
[2026-08-15T13:06:46.956Z] [WARN] Skipping broadcast for large file: token-ledger.json (24571127 bytes)
[2026-08-15T13:07:28.520Z] [WARN] Skipping broadcast for large file: token-ledger.json (24573304 bytes)
[2026-08-15T13:16:56.654Z] [INFO] Executing task: Cerebrum reflection
[2026-08-15T13:18:59.222Z] [ERROR] Task Cerebrum reflection failed (attempt 1): claude -p failed: spawnSync claude ETIMEDOUT
[2026-08-15T13:18:59.227Z] [INFO] Executing task: Token audit report
[2026-08-15T13:18:59.604Z] [INFO] Task Token audit report completed in 376ms
[2026-08-15T13:18:59.611Z] [INFO] Executing task: Consolidate old memory
[2026-08-15T13:18:59.624Z] [INFO] Task Consolidate old memory completed in 10ms
[2026-08-15T13:18:59.626Z] [INFO] Executing task: Full anatomy rescan
[2026-08-15T13:18:59.749Z] [INFO] Task Full anatomy rescan completed in 123ms
[2026-08-15T13:19:00.152Z] [WARN] Skipping broadcast for large file: token-ledger.json (24876869 bytes)
[2026-08-15T13:24:12.596Z] [WARN] Skipping broadcast for large file: token-ledger.json (24877689 bytes)
[2026-08-15T13:27:58.605Z] [WARN] Skipping broadcast for large file: token-ledger.json (24878754 bytes)
[2026-08-15T13:52:33.306Z] [WARN] Skipping broadcast for large file: token-ledger.json (24879819 bytes)
[2026-08-15T13:54:11.597Z] [WARN] Skipping broadcast for large file: token-ledger.json (24880884 bytes)
[2026-08-15T14:21:47.572Z] [WARN] Skipping broadcast for large file: token-ledger.json (24883043 bytes)
[2026-08-15T14:26:25.189Z] [WARN] Skipping broadcast for large file: token-ledger.json (24885202 bytes)
[2026-08-15T14:27:54.858Z] [WARN] Skipping broadcast for large file: token-ledger.json (24887622 bytes)
[2026-08-15T14:30:14.219Z] [WARN] Skipping broadcast for large file: token-ledger.json (24887622 bytes)
[2026-08-15T14:54:01.006Z] [WARN] Skipping broadcast for large file: token-ledger.json (24888193 bytes)
[2026-08-15T14:57:34.473Z] [WARN] Skipping broadcast for large file: token-ledger.json (24888931 bytes)
[2026-08-15T15:00:38.569Z] [WARN] Skipping broadcast for large file: token-ledger.json (24889669 bytes)
[2026-08-15T19:00:00.017Z] [INFO] Executing task: Full anatomy rescan
[2026-08-15T19:00:00.089Z] [INFO] Task Full anatomy rescan completed in 71ms
[2026-08-15T22:07:02.636Z] [WARN] Skipping broadcast for large file: token-ledger.json (24890666 bytes)
[2026-08-15T22:09:00.084Z] [WARN] Skipping broadcast for large file: token-ledger.json (24891663 bytes)
[2026-08-15T22:11:38.636Z] [WARN] Skipping broadcast for large file: token-ledger.json (24891663 bytes)
[2026-08-15T22:58:39.561Z] [WARN] Skipping broadcast for large file: token-ledger.json (24892786 bytes)
[2026-08-16T01:00:00.027Z] [INFO] Executing task: Full anatomy rescan
[2026-08-16T01:00:00.100Z] [INFO] Task Full anatomy rescan completed in 72ms
[2026-08-16T03:09:42.740Z] [WARN] Skipping broadcast for large file: token-ledger.json (24892786 bytes)
[2026-08-16T03:10:29.533Z] [WARN] Skipping broadcast for large file: token-ledger.json (24892786 bytes)
[2026-08-16T03:25:49.699Z] [WARN] Skipping broadcast for large file: token-ledger.json (24892786 bytes)
[2026-08-16T03:29:20.075Z] [WARN] Skipping broadcast for large file: token-ledger.json (24894217 bytes)
[2026-08-16T03:33:25.395Z] [WARN] Skipping broadcast for large file: token-ledger.json (24896084 bytes)
[2026-08-16T03:42:22.230Z] [WARN] Skipping broadcast for large file: token-ledger.json (24898138 bytes)
[2026-08-16T03:57:37.555Z] [WARN] Skipping broadcast for large file: token-ledger.json (24901175 bytes)
[2026-08-16T04:03:04.277Z] [WARN] Skipping broadcast for large file: token-ledger.json (24904212 bytes)
[2026-08-16T04:07:33.891Z] [WARN] Skipping broadcast for large file: token-ledger.json (24907249 bytes)
[2026-08-16T04:13:37.260Z] [WARN] Skipping broadcast for large file: token-ledger.json (24911570 bytes)
[2026-08-16T04:29:52.676Z] [WARN] Skipping broadcast for large file: token-ledger.json (24917616 bytes)
[2026-08-16T04:30:32.246Z] [WARN] Skipping broadcast for large file: token-ledger.json (24923662 bytes)
[2026-08-16T07:00:00.011Z] [INFO] Executing task: Full anatomy rescan
[2026-08-16T07:00:00.069Z] [INFO] Task Full anatomy rescan completed in 58ms
[2026-08-16T07:54:39.750Z] [WARN] Skipping broadcast for large file: buglog.json (1051264 bytes)
[2026-08-16T07:55:39.821Z] [WARN] Skipping broadcast for large file: token-ledger.json (24945063 bytes)
[2026-08-16T07:56:19.583Z] [WARN] Skipping broadcast for large file: token-ledger.json (24966464 bytes)
[2026-08-16T09:00:00.005Z] [INFO] Executing task: Consolidate old memory
[2026-08-16T09:00:00.009Z] [INFO] Task Consolidate old memory completed in 3ms
[2026-08-16T09:39:05.129Z] [WARN] Skipping broadcast for large file: token-ledger.json (24966464 bytes)
[2026-08-16T09:43:06.232Z] [WARN] Skipping broadcast for large file: token-ledger.json (24969223 bytes)
[2026-08-16T09:44:26.295Z] [WARN] Skipping broadcast for large file: token-ledger.json (24973180 bytes)
[2026-08-16T09:48:34.756Z] [WARN] Skipping broadcast for large file: token-ledger.json (24978982 bytes)
[2026-08-16T09:50:30.590Z] [WARN] Skipping broadcast for large file: token-ledger.json (24985073 bytes)
[2026-08-16T09:54:06.633Z] [WARN] Skipping broadcast for large file: buglog.json (1051719 bytes)
[2026-08-16T09:54:32.541Z] [WARN] Skipping broadcast for large file: token-ledger.json (24991760 bytes)
[2026-08-16T09:57:57.949Z] [WARN] Skipping broadcast for large file: token-ledger.json (24998447 bytes)
[2026-08-16T10:00:00.002Z] [INFO] Executing task: Cerebrum reflection
[2026-08-16T10:01:48.349Z] [INFO] Task Cerebrum reflection completed in 108347ms
[2026-08-16T10:01:48.855Z] [WARN] Skipping broadcast for large file: token-ledger.json (24998447 bytes)
[2026-08-16T10:26:56.030Z] [WARN] Skipping broadcast for large file: buglog.json (1052041 bytes)
[2026-08-16T10:44:47.597Z] [WARN] Skipping broadcast for large file: buglog.json (1055323 bytes)
[2026-08-16T10:48:37.601Z] [WARN] Skipping broadcast for large file: token-ledger.json (25001929 bytes)
[2026-08-16T10:54:14.787Z] [WARN] Skipping broadcast for large file: token-ledger.json (25005801 bytes)
[2026-08-16T10:55:56.197Z] [WARN] Skipping broadcast for large file: token-ledger.json (25009673 bytes)
[2026-08-16T11:02:23.822Z] [WARN] Skipping broadcast for large file: token-ledger.json (25013545 bytes)
[2026-08-16T11:06:33.554Z] [WARN] Skipping broadcast for large file: token-ledger.json (25017417 bytes)
[2026-08-16T11:09:16.737Z] [WARN] Skipping broadcast for large file: token-ledger.json (25021289 bytes)
[2026-08-16T11:11:16.095Z] [WARN] Skipping broadcast for large file: token-ledger.json (25025161 bytes)
[2026-08-16T11:12:54.330Z] [WARN] Skipping broadcast for large file: buglog.json (1055840 bytes)
[2026-08-16T11:13:46.190Z] [WARN] Skipping broadcast for large file: token-ledger.json (25030520 bytes)
[2026-08-16T11:16:45.229Z] [WARN] Skipping broadcast for large file: token-ledger.json (25036905 bytes)
[2026-08-16T11:19:18.137Z] [WARN] Skipping broadcast for large file: token-ledger.json (25044773 bytes)
[2026-08-16T12:12:34.901Z] [WARN] Skipping broadcast for large file: buglog.json (1056374 bytes)
[2026-08-16T12:12:47.529Z] [WARN] Skipping broadcast for large file: buglog.json (1056374 bytes)
[2026-08-16T12:12:58.126Z] [WARN] Skipping broadcast for large file: buglog.json (1056852 bytes)
[2026-08-16T12:13:24.334Z] [WARN] Skipping broadcast for large file: token-ledger.json (25054423 bytes)
[2026-08-16T13:00:00.003Z] [INFO] Executing task: Full anatomy rescan
[2026-08-16T13:00:00.063Z] [INFO] Task Full anatomy rescan completed in 59ms
[2026-08-16T19:00:00.011Z] [INFO] Executing task: Full anatomy rescan
[2026-08-16T19:00:00.062Z] [INFO] Task Full anatomy rescan completed in 50ms
[2026-08-17T01:00:00.030Z] [INFO] Executing task: Full anatomy rescan
[2026-08-17T01:00:00.082Z] [INFO] Task Full anatomy rescan completed in 52ms
[2026-08-17T03:12:49.332Z] [WARN] Skipping broadcast for large file: token-ledger.json (25054423 bytes)
[2026-08-17T03:24:41.458Z] [WARN] Skipping broadcast for large file: token-ledger.json (25055744 bytes)
[2026-08-17T03:32:41.910Z] [WARN] Skipping broadcast for large file: token-ledger.json (25057534 bytes)
[2026-08-17T03:39:11.757Z] [WARN] Skipping broadcast for large file: buglog.json (1063044 bytes)
[2026-08-17T03:39:56.974Z] [WARN] Skipping broadcast for large file: token-ledger.json (25060120 bytes)
[2026-08-17T03:40:20.550Z] [WARN] Skipping broadcast for large file: token-ledger.json (25062706 bytes)
[2026-08-17T03:45:49.920Z] [WARN] Skipping broadcast for large file: buglog.json (1061406 bytes)
[2026-08-17T03:48:12.843Z] [WARN] Skipping broadcast for large file: token-ledger.json (25066131 bytes)
[2026-08-17T03:49:55.640Z] [WARN] Skipping broadcast for large file: buglog.json (1065275 bytes)
[2026-08-17T03:50:20.223Z] [WARN] Skipping broadcast for large file: token-ledger.json (25069839 bytes)
[2026-08-17T03:59:28.024Z] [WARN] Skipping broadcast for large file: token-ledger.json (25073547 bytes)
[2026-08-17T04:15:01.342Z] [WARN] Skipping broadcast for large file: token-ledger.json (25077733 bytes)
[2026-08-17T04:16:57.886Z] [WARN] Skipping broadcast for large file: token-ledger.json (25081919 bytes)
[2026-08-17T04:20:46.730Z] [WARN] Skipping broadcast for large file: token-ledger.json (25086105 bytes)
[2026-08-17T04:21:14.128Z] [WARN] Skipping broadcast for large file: token-ledger.json (25090291 bytes)
[2026-08-17T04:24:03.241Z] [WARN] Skipping broadcast for large file: token-ledger.json (25094477 bytes)
[2026-08-17T04:28:50.732Z] [WARN] Skipping broadcast for large file: token-ledger.json (25098663 bytes)
[2026-08-17T04:34:02.459Z] [WARN] Skipping broadcast for large file: token-ledger.json (25102849 bytes)
[2026-08-17T04:39:49.985Z] [WARN] Skipping broadcast for large file: token-ledger.json (25107035 bytes)
[2026-08-17T04:40:55.933Z] [WARN] Skipping broadcast for large file: token-ledger.json (25111221 bytes)
[2026-08-17T04:59:40.319Z] [WARN] Skipping broadcast for large file: buglog.json (1063673 bytes)
[2026-08-17T05:01:34.303Z] [WARN] Skipping broadcast for large file: buglog.json (1064135 bytes)
[2026-08-17T05:03:43.681Z] [WARN] Skipping broadcast for large file: buglog.json (1070435 bytes)
[2026-08-17T05:04:02.941Z] [WARN] Skipping broadcast for large file: token-ledger.json (25116887 bytes)
[2026-08-17T05:06:32.564Z] [WARN] Skipping broadcast for large file: token-ledger.json (25122553 bytes)
[2026-08-17T05:09:21.444Z] [WARN] Skipping broadcast for large file: token-ledger.json (25128219 bytes)
[2026-08-17T05:12:24.303Z] [WARN] Skipping broadcast for large file: token-ledger.json (25133885 bytes)
[2026-08-17T05:15:58.232Z] [WARN] Skipping broadcast for large file: token-ledger.json (25139551 bytes)
[2026-08-17T05:16:46.024Z] [WARN] Skipping broadcast for large file: token-ledger.json (25145217 bytes)
[2026-08-17T05:20:18.750Z] [WARN] Skipping broadcast for large file: token-ledger.json (25150883 bytes)
[2026-08-17T05:24:51.506Z] [WARN] Skipping broadcast for large file: token-ledger.json (25156549 bytes)
[2026-08-17T05:30:07.822Z] [WARN] Skipping broadcast for large file: token-ledger.json (25162215 bytes)
[2026-08-17T05:35:53.930Z] [WARN] Skipping broadcast for large file: token-ledger.json (25167881 bytes)
[2026-08-17T05:36:48.520Z] [WARN] Skipping broadcast for large file: buglog.json (1070838 bytes)
[2026-08-17T05:37:23.462Z] [WARN] Skipping broadcast for large file: token-ledger.json (25173547 bytes)
[2026-08-17T06:35:15.163Z] [WARN] Skipping broadcast for large file: token-ledger.json (25179213 bytes)
[2026-08-17T06:41:49.133Z] [WARN] Skipping broadcast for large file: buglog.json (1069203 bytes)
[2026-08-17T06:44:51.542Z] [WARN] Skipping broadcast for large file: buglog.json (1069675 bytes)
[2026-08-17T06:45:18.377Z] [WARN] Skipping broadcast for large file: buglog.json (1069747 bytes)
[2026-08-17T06:46:32.073Z] [WARN] Skipping broadcast for large file: token-ledger.json (25187054 bytes)
[2026-08-17T06:51:14.293Z] [WARN] Skipping broadcast for large file: buglog.json (1070213 bytes)
[2026-08-17T06:52:22.301Z] [WARN] Skipping broadcast for large file: buglog.json (1070674 bytes)
[2026-08-17T06:53:24.185Z] [WARN] Skipping broadcast for large file: buglog.json (1070785 bytes)
[2026-08-17T06:53:42.080Z] [WARN] Skipping broadcast for large file: buglog.json (1070888 bytes)
[2026-08-17T06:54:46.689Z] [WARN] Skipping broadcast for large file: token-ledger.json (25197814 bytes)
[2026-08-17T07:00:05.799Z] [WARN] Skipping broadcast for large file: buglog.json (1071346 bytes)
[2026-08-17T07:00:21.583Z] [WARN] Skipping broadcast for large file: buglog.json (1071859 bytes)
[2026-08-17T07:01:07.462Z] [WARN] Skipping broadcast for large file: token-ledger.json (25209540 bytes)
[2026-08-17T07:15:26.751Z] [WARN] Skipping broadcast for large file: buglog.json (1072346 bytes)
[2026-08-17T07:15:59.445Z] [WARN] Skipping broadcast for large file: buglog.json (1072929 bytes)
[2026-08-17T07:20:40.296Z] [WARN] Skipping broadcast for large file: buglog.json (1080799 bytes)
[2026-08-17T07:23:09.680Z] [WARN] Skipping broadcast for large file: token-ledger.json (25222829 bytes)
[2026-08-17T07:24:31.538Z] [WARN] Skipping broadcast for large file: token-ledger.json (25236118 bytes)
[2026-08-17T07:25:25.121Z] [WARN] Skipping broadcast for large file: token-ledger.json (25249690 bytes)
[2026-08-17T07:28:49.540Z] [WARN] Skipping broadcast for large file: token-ledger.json (25263262 bytes)
[2026-08-17T07:33:33.866Z] [WARN] Skipping broadcast for large file: token-ledger.json (25276834 bytes)
[2026-08-17T07:38:51.141Z] [WARN] Skipping broadcast for large file: token-ledger.json (25290406 bytes)
[2026-08-17T07:44:30.081Z] [WARN] Skipping broadcast for large file: token-ledger.json (25303978 bytes)
[2026-08-17T07:45:26.246Z] [WARN] Skipping broadcast for large file: token-ledger.json (25317550 bytes)
[2026-08-17T07:47:54.626Z] [WARN] Skipping broadcast for large file: token-ledger.json (25331122 bytes)
[2026-08-17T07:49:21.007Z] [WARN] Skipping broadcast for large file: token-ledger.json (25344694 bytes)
[2026-08-17T07:50:37.592Z] [WARN] Skipping broadcast for large file: token-ledger.json (25358266 bytes)
[2026-08-17T07:53:59.415Z] [WARN] Skipping broadcast for large file: token-ledger.json (25371838 bytes)
[2026-08-17T07:57:26.795Z] [WARN] Skipping broadcast for large file: token-ledger.json (25385410 bytes)
[2026-08-17T07:59:35.036Z] [WARN] Skipping broadcast for large file: token-ledger.json (25398982 bytes)
[2026-08-17T08:02:07.627Z] [WARN] Skipping broadcast for large file: buglog.json (1079144 bytes)
[2026-08-17T08:02:16.190Z] [WARN] Skipping broadcast for large file: buglog.json (1079144 bytes)
[2026-08-17T08:02:23.187Z] [WARN] Skipping broadcast for large file: buglog.json (1079144 bytes)
[2026-08-17T08:03:27.512Z] [WARN] Skipping broadcast for large file: buglog.json (1079612 bytes)
[2026-08-17T08:04:41.883Z] [WARN] Skipping broadcast for large file: token-ledger.json (25414806 bytes)
[2026-08-17T08:06:41.587Z] [WARN] Skipping broadcast for large file: token-ledger.json (25430630 bytes)
[2026-08-17T08:07:19.858Z] [WARN] Skipping broadcast for large file: token-ledger.json (25446454 bytes)
[2026-08-17T08:09:29.541Z] [WARN] Skipping broadcast for large file: token-ledger.json (25462278 bytes)
[2026-08-17T08:17:55.298Z] [WARN] Skipping broadcast for large file: buglog.json (1083228 bytes)
[2026-08-17T08:18:40.853Z] [WARN] Skipping broadcast for large file: buglog.json (1081583 bytes)
[2026-08-17T08:19:13.835Z] [WARN] Skipping broadcast for large file: buglog.json (1082054 bytes)
[2026-08-17T08:19:31.320Z] [WARN] Skipping broadcast for large file: buglog.json (1082577 bytes)
[2026-08-17T08:21:06.888Z] [WARN] Skipping broadcast for large file: token-ledger.json (25479243 bytes)
[2026-08-17T08:24:49.636Z] [WARN] Skipping broadcast for large file: buglog.json (1083102 bytes)
[2026-08-17T08:28:11.278Z] [WARN] Skipping broadcast for large file: token-ledger.json (25497387 bytes)
[2026-08-17T08:29:19.790Z] [WARN] Skipping broadcast for large file: buglog.json (1088405 bytes)
[2026-08-17T08:31:31.095Z] [WARN] Skipping broadcast for large file: token-ledger.json (25516688 bytes)
[2026-08-17T08:34:43.697Z] [WARN] Skipping broadcast for large file: token-ledger.json (25516688 bytes)
[2026-08-17T08:57:37.363Z] [WARN] Skipping broadcast for large file: buglog.json (1093741 bytes)
[2026-08-17T08:58:03.456Z] [WARN] Skipping broadcast for large file: token-ledger.json (25517562 bytes)
[2026-08-17T09:02:04.430Z] [WARN] Skipping broadcast for large file: token-ledger.json (25518436 bytes)
[2026-08-17T09:14:32.289Z] [WARN] Skipping broadcast for large file: buglog.json (1092086 bytes)
[2026-08-17T09:14:36.236Z] [WARN] Skipping broadcast for large file: buglog.json (1092675 bytes)
[2026-08-17T09:45:13.845Z] [WARN] Skipping broadcast for large file: buglog.json (1098429 bytes)
[2026-08-17T09:46:28.111Z] [WARN] Skipping broadcast for large file: token-ledger.json (25527422 bytes)
[2026-08-17T10:03:41.154Z] [WARN] Skipping broadcast for large file: buglog.json (1097133 bytes)
[2026-08-17T10:04:00.430Z] [WARN] Skipping broadcast for large file: token-ledger.json (25537430 bytes)
[2026-08-17T10:14:50.744Z] [WARN] Skipping broadcast for large file: buglog.json (1095780 bytes)
[2026-08-17T10:16:21.747Z] [WARN] Skipping broadcast for large file: buglog.json (1096245 bytes)
[2026-08-17T10:16:40.562Z] [WARN] Skipping broadcast for large file: buglog.json (1096332 bytes)
[2026-08-17T10:16:56.772Z] [WARN] Skipping broadcast for large file: buglog.json (1096443 bytes)
[2026-08-17T10:31:46.868Z] [WARN] Skipping broadcast for large file: buglog.json (1096903 bytes)
[2026-08-17T10:34:06.915Z] [WARN] Skipping broadcast for large file: buglog.json (1101568 bytes)
[2026-08-17T10:35:48.230Z] [WARN] Skipping broadcast for large file: token-ledger.json (25550565 bytes)
[2026-08-17T10:38:08.101Z] [WARN] Skipping broadcast for large file: token-ledger.json (25563700 bytes)
[2026-08-17T10:42:24.689Z] [WARN] Skipping broadcast for large file: token-ledger.json (25576835 bytes)
[2026-08-17T10:51:01.356Z] [WARN] Skipping broadcast for large file: buglog.json (1107851 bytes)
[2026-08-17T11:03:03.751Z] [WARN] Skipping broadcast for large file: token-ledger.json (25576835 bytes)
[2026-08-17T12:00:29.788Z] [WARN] Skipping broadcast for large file: buglog.json (1107482 bytes)
[2026-08-17T12:00:40.773Z] [WARN] Skipping broadcast for large file: buglog.json (1107538 bytes)
[2026-08-17T12:02:57.737Z] [WARN] Skipping broadcast for large file: token-ledger.json (25578195 bytes)
[2026-08-17T12:36:45.126Z] [WARN] Skipping broadcast for large file: buglog.json (1111201 bytes)
[2026-08-17T12:36:55.172Z] [WARN] Skipping broadcast for large file: buglog.json (1111248 bytes)
[2026-08-17T12:38:44.269Z] [WARN] Skipping broadcast for large file: token-ledger.json (25583333 bytes)
[2026-08-17T14:44:08.311Z] [WARN] Skipping broadcast for large file: token-ledger.json (25592840 bytes)
[2026-08-17T14:50:00.084Z] [WARN] Skipping broadcast for large file: token-ledger.json (25603695 bytes)
[2026-08-17T14:53:37.553Z] [WARN] Skipping broadcast for large file: token-ledger.json (25615433 bytes)
[2026-08-17T15:00:29.231Z] [WARN] Skipping broadcast for large file: token-ledger.json (25627432 bytes)
[2026-08-17T15:13:14.029Z] [WARN] Skipping broadcast for large file: token-ledger.json (25641990 bytes)

View file

@ -1,6 +0,0 @@
{
"captured_at": null,
"captures": [],
"total_size_kb": 0,
"estimated_tokens": 0
}

View file

@ -1,12 +0,0 @@
{
"session_id": "session-2026-08-13-0213",
"started": "2026-08-13T02:13:41.065Z",
"files_read": {},
"files_written": [],
"edit_counts": {},
"anatomy_hits": 0,
"anatomy_misses": 0,
"repeated_reads_warned": 0,
"cerebrum_warnings": 0,
"stop_count": 2
}

View file

@ -1,3 +0,0 @@
{
"type": "module"
}

View file

@ -1,69 +0,0 @@
import * as path from "node:path";
import { getWolfDir, ensureWolfDir, readJSON, writeJSON, readMarkdown, parseAnatomy, estimateTokens, readStdin, normalizePath } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
const hooksDir = path.join(wolfDir, "hooks");
const sessionFile = path.join(hooksDir, "_session.json");
const raw = await readStdin();
let input;
try {
input = JSON.parse(raw);
}
catch {
process.exit(0);
return;
}
const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
const content = input.tool_output?.content ?? "";
if (!filePath) {
process.exit(0);
return;
}
const normalizedFile = normalizePath(filePath);
// Skip tracking for .wolf/ internal files — consistent with pre-read
const projectDir = normalizePath(process.env.CLAUDE_PROJECT_DIR || process.cwd());
const relToProject = normalizedFile.startsWith(projectDir)
? normalizedFile.slice(projectDir.length).replace(/^\//, "")
: "";
if (relToProject.startsWith(".wolf/") || relToProject.startsWith(".wolf\\")) {
process.exit(0);
return;
}
const ext = path.extname(filePath).toLowerCase();
const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".rs", ".go", ".java", ".c", ".cpp", ".css", ".json", ".yaml", ".yml"]);
const proseExts = new Set([".md", ".txt", ".rst"]);
const type = codeExts.has(ext) ? "code" : proseExts.has(ext) ? "prose" : "mixed";
let tokens = content ? estimateTokens(content, type) : 0;
// Fallback: if tool_output had no content, use anatomy token estimate
if (tokens === 0) {
const anatomyContent = readMarkdown(path.join(wolfDir, "anatomy.md"));
const sections = parseAnatomy(anatomyContent);
for (const [sectionKey, entries] of sections) {
for (const entry of entries) {
const entryRelPath = normalizePath(path.join(sectionKey, entry.file));
if (normalizedFile.endsWith(entryRelPath) || normalizedFile.endsWith("/" + entryRelPath)) {
tokens = entry.tokens;
break;
}
}
if (tokens > 0)
break;
}
}
const session = readJSON(sessionFile, { files_read: {} });
if (session.files_read[normalizedFile]) {
session.files_read[normalizedFile].tokens = tokens;
}
else {
session.files_read[normalizedFile] = {
count: 1,
tokens,
first_read: new Date().toISOString(),
};
}
writeJSON(sessionFile, session);
process.exit(0);
}
main().catch(() => process.exit(0));
//# sourceMappingURL=post-read.js.map

View file

@ -1,503 +0,0 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as crypto from "node:crypto";
import { getWolfDir, ensureWolfDir, readJSON, writeJSON, parseAnatomy, serializeAnatomy, extractDescription, estimateTokens, appendMarkdown, timeShort, readStdin, normalizePath } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
const hooksDir = path.join(wolfDir, "hooks");
const sessionFile = path.join(hooksDir, "_session.json");
const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
const raw = await readStdin();
let input;
try {
input = JSON.parse(raw);
}
catch {
process.exit(0);
return;
}
const toolName = input.tool_name ?? "Write";
const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
if (!filePath) {
process.exit(0);
return;
}
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath);
// Skip processing for .wolf/ internal files to avoid slow self-referential updates
const relPath = normalizePath(path.relative(projectRoot, absolutePath));
if (relPath.startsWith(".wolf/")) {
process.exit(0);
return;
}
// Never track .env files in anatomy — they contain secrets
const baseName = path.basename(absolutePath);
if (baseName === ".env" || baseName.startsWith(".env.")) {
process.exit(0);
return;
}
const oldStr = input.tool_input?.old_string ?? "";
const newStr = input.tool_input?.new_string ?? "";
// 1. Update anatomy.md
try {
const anatomyPath = path.join(wolfDir, "anatomy.md");
let anatomyContent;
try {
anatomyContent = fs.readFileSync(anatomyPath, "utf-8");
}
catch {
anatomyContent = "# anatomy.md\n\n> Auto-maintained by OpenWolf.\n";
}
const sections = parseAnatomy(anatomyContent);
const relPathLocal = normalizePath(path.relative(projectRoot, absolutePath));
const dir = path.dirname(relPathLocal);
const fileName = path.basename(relPathLocal);
const sectionKey = dir === "." ? "./" : dir + "/";
let fileContent = "";
try {
fileContent = fs.readFileSync(absolutePath, "utf-8");
}
catch {
fileContent = input.tool_input?.content ?? "";
}
const desc = extractDescription(absolutePath).slice(0, 100);
const ext = path.extname(absolutePath).toLowerCase();
const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".json", ".yaml", ".yml", ".css"]);
const proseExts = new Set([".md", ".txt", ".rst"]);
const type = codeExts.has(ext) ? "code" : proseExts.has(ext) ? "prose" : "mixed";
const tokens = estimateTokens(fileContent, type);
if (!sections.has(sectionKey))
sections.set(sectionKey, []);
const entries = sections.get(sectionKey);
const idx = entries.findIndex((e) => e.file === fileName);
if (idx !== -1) {
entries[idx] = { file: fileName, description: desc, tokens };
}
else {
entries.push({ file: fileName, description: desc, tokens });
}
let fileCount = 0;
for (const [, list] of sections)
fileCount += list.length;
const serialized = serializeAnatomy(sections, {
lastScanned: new Date().toISOString(),
fileCount,
hits: 0,
misses: 0,
});
const tmp = anatomyPath + "." + crypto.randomBytes(4).toString("hex") + ".tmp";
try {
fs.writeFileSync(tmp, serialized, "utf-8");
fs.renameSync(tmp, anatomyPath);
}
catch {
try {
fs.writeFileSync(anatomyPath, serialized, "utf-8");
}
catch { }
try {
fs.unlinkSync(tmp);
}
catch { }
}
}
catch { }
// 2. Append richer entry to memory.md
try {
const action = toolName === "Write" ? "Created" : toolName === "MultiEdit" ? "Multi-edited" : "Edited";
const relFile = normalizePath(path.relative(projectRoot, absolutePath));
const fileContent = input.tool_input?.content ?? "";
const ext = path.extname(absolutePath).toLowerCase();
const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".json", ".yaml", ".yml", ".css"]);
const type = codeExts.has(ext) ? "code" : "mixed";
const writeTokens = estimateTokens(fileContent || newStr, type);
let changeDesc = "";
if (oldStr && newStr) {
changeDesc = summarizeEdit(oldStr, newStr, baseName);
}
const memoryPath = path.join(wolfDir, "memory.md");
const outcome = changeDesc || "—";
appendMarkdown(memoryPath, `| ${timeShort()} | ${action} ${relFile} | ${outcome} | ~${writeTokens} |\n`);
}
catch { }
// 3. Record in session tracker + track edit counts
try {
const session = readJSON(sessionFile, { files_written: [], edit_counts: {} });
if (!session.edit_counts)
session.edit_counts = {};
const normalizedFile = normalizePath(filePath);
const action = toolName === "Write" ? "create" : "edit";
const fileContent = input.tool_input?.content ?? "";
const tokens = estimateTokens(fileContent || newStr, "code");
session.files_written.push({
file: normalizedFile,
action,
tokens,
at: new Date().toISOString(),
});
const editKey = normalizePath(path.relative(projectRoot, absolutePath));
session.edit_counts[editKey] = (session.edit_counts[editKey] || 0) + 1;
writeJSON(sessionFile, session);
if (session.edit_counts[editKey] >= 3) {
process.stderr.write(`⚠️ OpenWolf: ${baseName} has been edited ${session.edit_counts[editKey]} times this session. If you're fixing a bug, remember to log it to .wolf/buglog.json.\n`);
}
}
catch { }
// 4. Auto-detect bug-fix patterns and log them
try {
if (oldStr && newStr) {
autoDetectBugFix(wolfDir, absolutePath, projectRoot, oldStr, newStr);
}
}
catch { }
process.exit(0);
}
// ─── Edit Summarizer ─────────────────────────────────────────────
function summarizeEdit(oldStr, newStr, filename) {
const oldLines = oldStr.split("\n");
const newLines = newStr.split("\n");
const oldCount = oldLines.length;
const newCount = newLines.length;
const ext = path.extname(filename).toLowerCase();
// --- Structural fixes ---
if (newStr.includes("try") && newStr.includes("catch") && !oldStr.includes("catch")) {
return "added error handling";
}
if (newStr.includes("?.") && !oldStr.includes("?."))
return "added optional chaining";
if (newStr.includes("?? ") && !oldStr.includes("?? "))
return "added nullish coalescing";
// --- Deleted code ---
if (!newStr.trim() || newStr.trim().length < oldStr.trim().length * 0.2) {
return `removed ${oldCount} lines`;
}
// --- Import changes ---
const oldImports = oldLines.filter(l => /^\s*(import|require|use |from )/.test(l)).length;
const newImports = newLines.filter(l => /^\s*(import|require|use |from )/.test(l)).length;
if (newImports > oldImports && Math.abs(newCount - oldCount) <= newImports - oldImports + 1) {
return `added ${newImports - oldImports} import(s)`;
}
// --- Value/string replacement (common bug fix: wrong value) ---
if (oldCount === 1 && newCount === 1) {
const o = oldStr.trim();
const n = newStr.trim();
// String literal change
const oStr = o.match(/['"`]([^'"`]+)['"`]/);
const nStr = n.match(/['"`]([^'"`]+)['"`]/);
if (oStr && nStr && oStr[1] !== nStr[1]) {
return `"${oStr[1].slice(0, 25)}" → "${nStr[1].slice(0, 25)}"`;
}
// Number change
const oNum = o.match(/\b(\d+\.?\d*)\b/);
const nNum = n.match(/\b(\d+\.?\d*)\b/);
if (oNum && nNum && oNum[1] !== nNum[1] && o.replace(oNum[1], "") === n.replace(nNum[1], "")) {
return `${oNum[1]} → ${nNum[1]}`;
}
return "inline fix";
}
// --- Method/function call changes ---
const oldCalls = extractCalls(oldStr);
const newCalls = extractCalls(newStr);
const addedCalls = newCalls.filter(c => !oldCalls.includes(c));
const removedCalls = oldCalls.filter(c => !newCalls.includes(c));
if (removedCalls.length === 1 && addedCalls.length === 1) {
return `${removedCalls[0]}() → ${addedCalls[0]}()`;
}
// --- CSS/style changes ---
if (ext === ".css" || ext === ".scss" || ext === ".vue" || ext === ".tsx" || ext === ".jsx") {
const oldProps = (oldStr.match(/[\w-]+\s*:/g) || []).map(p => p.replace(/\s*:/, ""));
const newProps = (newStr.match(/[\w-]+\s*:/g) || []).map(p => p.replace(/\s*:/, ""));
const changed = newProps.filter(p => !oldProps.includes(p));
if (changed.length > 0 && changed.length <= 3) {
return `CSS: ${changed.join(", ")}`;
}
}
// --- Condition changes ---
const oldConds = (oldStr.match(/if\s*\(([^)]+)\)/g) || []);
const newConds = (newStr.match(/if\s*\(([^)]+)\)/g) || []);
if (newConds.length > oldConds.length) {
return `added ${newConds.length - oldConds.length} condition(s)`;
}
// --- Function modified ---
const fnMatch = newStr.match(/(?:function|def|fn|func|async\s+function)\s+(\w+)/);
if (fnMatch) {
return `modified ${fnMatch[1]}()`;
}
// --- Class/method context ---
const methodMatch = newStr.match(/(?:public|private|protected)?\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*[:{]/);
if (methodMatch) {
return `modified ${methodMatch[1]}()`;
}
// --- Size-based fallback ---
if (newCount > oldCount + 5)
return `expanded (+${newCount - oldCount} lines)`;
if (oldCount > newCount + 5)
return `reduced (-${oldCount - newCount} lines)`;
return `${oldCount}→${newCount} lines`;
}
function extractCalls(code) {
return [...new Set((code.match(/(\w+)\s*\(/g) || [])
.map(m => m.match(/(\w+)/)?.[1] || "")
.filter(n => n.length > 2 && !["if", "for", "while", "switch", "catch", "function", "return", "new", "typeof", "instanceof", "const", "let", "var"].includes(n)))];
}
// ─── Auto Bug Detection ──────────────────────────────────────────
function autoDetectBugFix(wolfDir, absolutePath, projectRoot, oldStr, newStr) {
const bugLogPath = path.join(wolfDir, "buglog.json");
const bugLog = readJSON(bugLogPath, { version: 1, bugs: [] });
const relFile = normalizePath(path.relative(projectRoot, absolutePath));
const basename = path.basename(absolutePath);
const ext = path.extname(basename).toLowerCase();
// Detect what kind of fix this is
const detection = detectFixPattern(oldStr, newStr, ext);
if (!detection)
return;
// Check for recent duplicate (same file + same category within 5 min)
const recentDupe = bugLog.bugs.find(b => {
if (path.basename(b.file) !== basename)
return false;
if (!b.tags.includes("auto-detected"))
return false;
if (!b.tags.includes(detection.category))
return false;
const bugTime = new Date(b.last_seen).getTime();
return (Date.now() - bugTime) < 5 * 60 * 1000;
});
if (recentDupe) {
recentDupe.occurrences++;
recentDupe.last_seen = new Date().toISOString();
// Append additional context
if (detection.context && !recentDupe.fix.includes(detection.context)) {
recentDupe.fix += ` | Also: ${detection.context}`;
}
writeJSON(bugLogPath, bugLog);
return;
}
const nextId = `bug-${String(bugLog.bugs.length + 1).padStart(3, "0")}`;
bugLog.bugs.push({
id: nextId,
timestamp: new Date().toISOString(),
error_message: detection.summary,
file: relFile,
root_cause: detection.rootCause,
fix: detection.fix,
tags: ["auto-detected", detection.category, ext.replace(".", "") || "unknown"],
related_bugs: [],
occurrences: 1,
last_seen: new Date().toISOString(),
});
writeJSON(bugLogPath, bugLog);
}
function detectFixPattern(oldStr, newStr, ext) {
const oldLines = oldStr.split("\n");
const newLines = newStr.split("\n");
// --- Error handling added ---
if (newStr.includes("catch") && !oldStr.includes("catch")) {
const fn = newStr.match(/(?:function|def|async)\s+(\w+)/)?.[1] || "unknown";
return {
category: "error-handling",
summary: `Missing error handling in ${path.basename(fn)}`,
rootCause: "Code path had no error handling — exceptions would propagate uncaught",
fix: `Added try/catch block`,
context: extractChangedLines(oldStr, newStr),
};
}
// --- Null/undefined safety ---
if ((newStr.includes("?.") && !oldStr.includes("?.")) ||
(newStr.includes("?? ") && !oldStr.includes("?? ")) ||
(/!==?\s*(null|undefined)/.test(newStr) && !/!==?\s*(null|undefined)/.test(oldStr))) {
return {
category: "null-safety",
summary: `Null/undefined access in ${path.basename(path.basename(""))}`,
rootCause: "Property access on potentially null/undefined value",
fix: `Added null safety (optional chaining or null check)`,
context: extractChangedLines(oldStr, newStr),
};
}
// --- Guard clause / early return added ---
if (/if\s*\([^)]*\)\s*(return|throw|continue|break)/.test(newStr) &&
!/if\s*\([^)]*\)\s*(return|throw|continue|break)/.test(oldStr)) {
const condition = newStr.match(/if\s*\(([^)]+)\)/)?.[1]?.trim().slice(0, 60) || "condition";
return {
category: "guard-clause",
summary: `Missing guard clause`,
rootCause: `No early return/throw for edge case: ${condition}`,
fix: `Added guard clause: if (${condition.slice(0, 40)})`,
};
}
// --- Wrong value / string fix (very common bug) ---
if (oldLines.length <= 3 && newLines.length <= 3) {
const oldJoined = oldStr.trim();
const newJoined = newStr.trim();
// String literal changed
const oStrs = oldJoined.match(/['"`]([^'"`]{2,})['"`]/g) || [];
const nStrs = newJoined.match(/['"`]([^'"`]{2,})['"`]/g) || [];
if (oStrs.length > 0 && nStrs.length > 0) {
for (let i = 0; i < Math.min(oStrs.length, nStrs.length); i++) {
if (oStrs[i] !== nStrs[i]) {
return {
category: "wrong-value",
summary: `Incorrect value in code`,
rootCause: `Had ${oStrs[i].slice(0, 50)}`,
fix: `Changed to ${nStrs[i].slice(0, 50)}`,
};
}
}
}
// Variable name / method call changed
const oldTokens = tokenizeCode(oldJoined);
const newTokens = tokenizeCode(newJoined);
const changed = [];
for (let i = 0; i < Math.min(oldTokens.length, newTokens.length); i++) {
if (oldTokens[i] !== newTokens[i]) {
changed.push([oldTokens[i], newTokens[i]]);
}
}
if (changed.length === 1 && changed[0][0].length > 2) {
return {
category: "wrong-reference",
summary: `Wrong reference: ${changed[0][0]} should be ${changed[0][1]}`,
rootCause: `Used "${changed[0][0]}" instead of "${changed[0][1]}"`,
fix: `Changed ${changed[0][0]} → ${changed[0][1]}`,
};
}
}
// --- Logic fix (condition changed) ---
const oldCond = oldStr.match(/if\s*\(([^)]+)\)/)?.[1];
const newCond = newStr.match(/if\s*\(([^)]+)\)/)?.[1];
if (oldCond && newCond && oldCond !== newCond && oldLines.length <= 5) {
return {
category: "logic-fix",
summary: `Wrong condition in logic`,
rootCause: `Condition was: if (${oldCond.slice(0, 50)})`,
fix: `Changed to: if (${newCond.slice(0, 50)})`,
};
}
// --- Operator fix (=== vs ==, > vs >=, etc.) ---
const opChange = findOperatorChange(oldStr, newStr);
if (opChange) {
return {
category: "operator-fix",
summary: `Wrong operator: ${opChange.old} should be ${opChange.new}`,
rootCause: `Used "${opChange.old}" instead of "${opChange.new}"`,
fix: `Changed operator ${opChange.old} → ${opChange.new}`,
};
}
// --- Missing import/require ---
const oldImports = new Set((oldStr.match(/(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g) || []).map(m => m));
const newImports = (newStr.match(/(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g) || []);
const addedImports = newImports.filter(i => !oldImports.has(i));
if (addedImports.length > 0 && newLines.length - oldLines.length <= addedImports.length + 2) {
const modules = addedImports.map(i => i.match(/['"]([^'"]+)['"]/)?.[1] || "").filter(Boolean);
return {
category: "missing-import",
summary: `Missing import: ${modules.join(", ")}`,
rootCause: `Module(s) not imported: ${modules.join(", ")}`,
fix: `Added import(s) for ${modules.join(", ")}`,
};
}
// --- Return value fix ---
const oldReturn = oldStr.match(/return\s+(.+)/)?.[1]?.trim();
const newReturn = newStr.match(/return\s+(.+)/)?.[1]?.trim();
if (oldReturn && newReturn && oldReturn !== newReturn && oldLines.length <= 5) {
return {
category: "return-value",
summary: `Wrong return value`,
rootCause: `Was returning: ${oldReturn.slice(0, 50)}`,
fix: `Now returns: ${newReturn.slice(0, 50)}`,
};
}
// --- Async/await fix ---
if (newStr.includes("await ") && !oldStr.includes("await ")) {
return {
category: "async-fix",
summary: `Missing await`,
rootCause: `Async call without await — returned Promise instead of value`,
fix: `Added await to async call`,
context: extractChangedLines(oldStr, newStr),
};
}
if (newStr.includes("async ") && !oldStr.includes("async ")) {
return {
category: "async-fix",
summary: `Function not marked async`,
rootCause: `Function uses await but wasn't declared async`,
fix: `Added async modifier`,
};
}
// --- Type annotation/cast fix ---
if (ext === ".ts" || ext === ".tsx") {
if ((newStr.includes(" as ") && !oldStr.includes(" as ")) ||
(newStr.includes(": ") && !oldStr.includes(": ") && oldLines.length <= 3)) {
return {
category: "type-fix",
summary: `Type error`,
rootCause: `Missing or incorrect type annotation`,
fix: `Added type assertion/annotation`,
context: extractChangedLines(oldStr, newStr),
};
}
}
// --- CSS/style fix ---
if (ext === ".css" || ext === ".scss" || ext === ".vue" || ext === ".tsx" || ext === ".jsx") {
const oldProps = extractCSSProps(oldStr);
const newProps = extractCSSProps(newStr);
const changedProps = [...newProps.entries()].filter(([k, v]) => oldProps.get(k) !== v && oldProps.has(k));
if (changedProps.length > 0 && changedProps.length <= 3) {
const desc = changedProps.map(([k, v]) => `${k}: ${oldProps.get(k)} → ${v}`).join("; ");
return {
category: "style-fix",
summary: `CSS fix: ${changedProps.map(([k]) => k).join(", ")}`,
rootCause: desc,
fix: `Changed ${desc}`,
};
}
}
// --- Significant diff (catch-all for substantial edits) ---
const diffRatio = Math.abs(newStr.length - oldStr.length) / Math.max(oldStr.length, 1);
if (diffRatio > 0.3 && oldLines.length >= 3 && newLines.length >= 3) {
// Only log if there's meaningful structural change, not just additions
const removedLines = oldLines.filter(l => l.trim() && !newLines.some(nl => nl.trim() === l.trim()));
if (removedLines.length >= 2) {
return {
category: "refactor",
summary: `Significant refactor of ${path.basename("")}`,
rootCause: `${removedLines.length} lines replaced/restructured`,
fix: `Rewrote ${oldLines.length}→${newLines.length} lines (${removedLines.length} removed)`,
context: removedLines.slice(0, 2).map(l => l.trim().slice(0, 50)).join("; "),
};
}
}
return null;
}
function extractChangedLines(oldStr, newStr) {
const oldLines = new Set(oldStr.split("\n").map(l => l.trim()).filter(Boolean));
const newLines = newStr.split("\n").map(l => l.trim()).filter(Boolean);
const added = newLines.filter(l => !oldLines.has(l));
return added.slice(0, 2).map(l => l.slice(0, 60)).join("; ");
}
function tokenizeCode(code) {
return code.replace(/[^\w$]/g, " ").split(/\s+/).filter(t => t.length > 0);
}
function findOperatorChange(oldStr, newStr) {
const operators = ["===", "!==", "==", "!=", ">=", "<=", ">>", "<<", "&&", "||", "??"];
for (const op of operators) {
if (oldStr.includes(op) && !newStr.includes(op)) {
for (const op2 of operators) {
if (op2 !== op && newStr.includes(op2) && !oldStr.includes(op2)) {
return { old: op, new: op2 };
}
}
}
}
return null;
}
function extractCSSProps(code) {
const props = new Map();
const matches = code.matchAll(/([\w-]+)\s*:\s*([^;}\n]+)/g);
for (const m of matches) {
props.set(m[1].trim(), m[2].trim());
}
return props;
}
main().catch(() => process.exit(0));
//# sourceMappingURL=post-write.js.map

View file

@ -1,80 +0,0 @@
import * as path from "node:path";
import { getWolfDir, ensureWolfDir, readJSON, writeJSON, readMarkdown, parseAnatomy, readStdin, normalizePath } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
const hooksDir = path.join(wolfDir, "hooks");
const sessionFile = path.join(hooksDir, "_session.json");
const raw = await readStdin();
let input;
try {
input = JSON.parse(raw);
}
catch {
process.exit(0);
return;
}
const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
if (!filePath) {
process.exit(0);
return;
}
const normalizedFile = normalizePath(filePath);
// Skip tracking for .wolf/ internal files — they're infrastructure, not project files.
// Counting them inflates anatomy miss rates since .wolf/ is excluded from anatomy scanning.
const projectDir = normalizePath(process.env.CLAUDE_PROJECT_DIR || process.cwd());
const relToProject = normalizedFile.startsWith(projectDir)
? normalizedFile.slice(projectDir.length).replace(/^\//, "")
: "";
if (relToProject.startsWith(".wolf/") || relToProject.startsWith(".wolf\\")) {
process.exit(0);
return;
}
const session = readJSON(sessionFile, {
session_id: "", files_read: {}, anatomy_hits: 0, anatomy_misses: 0,
repeated_reads_warned: 0,
});
// Check if already read this session
if (session.files_read[normalizedFile]) {
const prev = session.files_read[normalizedFile];
process.stderr.write(`⚡ OpenWolf: ${path.basename(normalizedFile)} was already read this session (~${prev.tokens} tokens). Consider using your existing knowledge of this file.\n`);
session.files_read[normalizedFile].count++;
session.repeated_reads_warned++;
writeJSON(sessionFile, session);
process.exit(0);
return;
}
// Check anatomy.md for this file
const anatomyContent = readMarkdown(path.join(wolfDir, "anatomy.md"));
const sections = parseAnatomy(anatomyContent);
let found = false;
for (const [sectionKey, entries] of sections) {
for (const entry of entries) {
// Build the full relative path from the section key + filename for accurate matching
const entryRelPath = normalizePath(path.join(sectionKey, entry.file));
if (normalizedFile.endsWith(entryRelPath) || normalizedFile.endsWith("/" + entryRelPath)) {
process.stderr.write(`📋 OpenWolf anatomy: ${entry.file} — ${entry.description} (~${entry.tokens} tok)\n`);
found = true;
break;
}
}
if (found)
break;
}
if (found) {
session.anatomy_hits++;
}
else {
session.anatomy_misses++;
}
// Record initial read entry (tokens will be updated in post-read)
session.files_read[normalizedFile] = {
count: 1,
tokens: 0,
first_read: new Date().toISOString(),
};
writeJSON(sessionFile, session);
process.exit(0);
}
main().catch(() => process.exit(0));
//# sourceMappingURL=pre-read.js.map

View file

@ -1,121 +0,0 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { getWolfDir, ensureWolfDir, readJSON, readMarkdown, readStdin } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
const raw = await readStdin();
let input;
try {
input = JSON.parse(raw);
}
catch {
process.exit(0);
return;
}
// For Edit tool, the meaningful content is old_string + new_string
const content = input.tool_input?.content ?? "";
const oldStr = input.tool_input?.old_string ?? "";
const newStr = input.tool_input?.new_string ?? "";
const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
const allContent = [content, oldStr, newStr].join("\n");
if (!allContent.trim()) {
process.exit(0);
return;
}
// 1. Cerebrum Do-Not-Repeat check
checkCerebrum(wolfDir, allContent);
// 2. Bug log: search for similar past bugs when editing code
// This fires when Claude is about to edit a file — if the edit looks like a fix
// (changing error handling, modifying catch blocks, etc.), check the bug log
if (filePath && (oldStr || content)) {
checkBugLog(wolfDir, filePath, oldStr, newStr, content);
}
process.exit(0);
}
function checkCerebrum(wolfDir, content) {
const cerebrumContent = readMarkdown(path.join(wolfDir, "cerebrum.md"));
const doNotRepeatSection = cerebrumContent.split("## Do-Not-Repeat")[1];
if (!doNotRepeatSection)
return;
const entries = doNotRepeatSection.split("## ")[0];
const lines = entries.split("\n").filter((l) => l.trim().startsWith("[") || l.trim().startsWith("-"));
for (const line of lines) {
const trimmed = line.trim().replace(/^[-*]\s*/, "").replace(/^\[[\d-]+\]\s*/, "");
if (!trimmed)
continue;
const patterns = [];
const quotedMatches = trimmed.match(/"([^"]+)"/g) || trimmed.match(/'([^']+)'/g) || trimmed.match(/`([^`]+)`/g);
if (quotedMatches) {
for (const qm of quotedMatches) {
patterns.push(qm.replace(/["'`]/g, ""));
}
}
const neverMatch = trimmed.match(/(?:never use|avoid|don't use|do not use)\s+(\w+)/i);
if (neverMatch)
patterns.push(neverMatch[1]);
for (const pattern of patterns) {
try {
const regex = new RegExp(`\\b${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i");
if (regex.test(content)) {
process.stderr.write(`⚠️ OpenWolf cerebrum warning: "${trimmed}" — check your code before proceeding.\n`);
}
}
catch { }
}
}
}
// Common words that appear in most code — must be excluded from similarity matching
const STOP_WORDS = new Set([
"error", "function", "return", "const", "this", "that", "with", "from",
"import", "export", "class", "interface", "type", "undefined", "null",
"true", "false", "string", "number", "object", "array", "value",
"file", "path", "name", "data", "response", "request", "result",
"should", "must", "does", "have", "been", "will", "would", "could",
"when", "then", "else", "each", "some", "every", "only",
]);
function checkBugLog(wolfDir, filePath, oldStr, newStr, content) {
const bugLogPath = path.join(wolfDir, "buglog.json");
if (!fs.existsSync(bugLogPath))
return;
const bugLog = readJSON(bugLogPath, { version: 1, bugs: [] });
if (bugLog.bugs.length === 0)
return;
const basename = path.basename(filePath);
// ONLY surface bugs that match the SAME file being edited.
// Cross-file matching is too noisy and risks misdirecting Claude.
const fileMatches = bugLog.bugs.filter(b => {
const bugBasename = path.basename(b.file);
return bugBasename === basename;
});
if (fileMatches.length === 0)
return;
// Further filter: require tag or error_message overlap with the edit content
const editText = (oldStr + " " + newStr + " " + content).toLowerCase();
const editTokens = tokenize(editText);
const relevant = fileMatches.filter(bug => {
// Check if any bug tag appears in the edit content
const tagHit = bug.tags.some(t => editText.includes(t.toLowerCase()));
if (tagHit)
return true;
// Check meaningful word overlap (excluding stop words)
const bugTokens = tokenize(bug.error_message + " " + bug.root_cause);
const overlap = [...editTokens].filter(t => bugTokens.has(t));
// Require at least 3 meaningful overlapping words
return overlap.length >= 3;
});
if (relevant.length === 0)
return;
// Surface as a FYI, not a directive — Claude should evaluate, not blindly apply
process.stderr.write(`📋 OpenWolf buglog: ${relevant.length} past bug(s) found for ${basename} — review for context, do NOT apply blindly:\n`);
for (const bug of relevant.slice(0, 2)) {
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`);
}
}
function tokenize(text) {
return new Set(text.replace(/[^\w\s]/g, " ").split(/\s+/)
.filter(w => w.length > 3 && !STOP_WORDS.has(w.toLowerCase()))
.map(w => w.toLowerCase()));
}
main().catch(() => process.exit(0));
//# sourceMappingURL=pre-write.js.map

View file

@ -1,77 +0,0 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { getWolfDir, ensureWolfDir, writeJSON, appendMarkdown, readJSON, timestamp, timeShort } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
// Clean up stale .tmp files left from failed atomic writes
try {
const files = fs.readdirSync(wolfDir);
for (const f of files) {
if (f.endsWith(".tmp")) {
try {
fs.unlinkSync(path.join(wolfDir, f));
}
catch { }
}
}
}
catch { }
const hooksDir = path.join(wolfDir, "hooks");
const sessionFile = path.join(hooksDir, "_session.json");
const now = new Date();
const sessionId = `session-${now.toISOString().slice(0, 10)}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
// Create fresh session state
writeJSON(sessionFile, {
session_id: sessionId,
started: timestamp(),
files_read: {},
files_written: [],
edit_counts: {},
anatomy_hits: 0,
anatomy_misses: 0,
repeated_reads_warned: 0,
cerebrum_warnings: 0,
stop_count: 0,
});
// Append session header to memory.md
const memoryPath = path.join(wolfDir, "memory.md");
const header = `\n## Session: ${now.toISOString().slice(0, 10)} ${timeShort()}\n\n| Time | Action | File(s) | Outcome | ~Tokens |\n|------|--------|---------|---------|--------|\n`;
appendMarkdown(memoryPath, header);
// Check cerebrum freshness — remind Claude to learn
try {
const cerebrumPath = path.join(wolfDir, "cerebrum.md");
const cerebrumContent = fs.readFileSync(cerebrumPath, "utf-8");
const stat = fs.statSync(cerebrumPath);
const daysSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24);
// Count actual entries (non-comment, non-empty lines in content sections)
const entryLines = cerebrumContent.split("\n").filter(l => {
const t = l.trim();
return t.startsWith("- ") || t.startsWith("* ") || (t.startsWith("[") && t.includes("]"));
});
if (entryLines.length < 3) {
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`);
}
else if (daysSinceUpdate > 3) {
process.stderr.write(`💡 OpenWolf: cerebrum.md hasn't been updated in ${Math.floor(daysSinceUpdate)} days. Look for opportunities to add learnings this session.\n`);
}
}
catch { }
// Check buglog — remind if empty
try {
const buglogPath = path.join(wolfDir, "buglog.json");
const buglog = readJSON(buglogPath, { bugs: [] });
if (buglog.bugs.length === 0) {
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`);
}
}
catch { }
// Increment total_sessions in token-ledger
const ledgerPath = path.join(wolfDir, "token-ledger.json");
const ledger = readJSON(ledgerPath, { version: 1, lifetime: { total_sessions: 0 } });
ledger.lifetime.total_sessions++;
writeJSON(ledgerPath, ledger);
process.exit(0);
}
main().catch(() => process.exit(0));
//# sourceMappingURL=session-start.js.map

View file

@ -1,614 +0,0 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as crypto from "node:crypto";
export function getWolfDir() {
// Prefer CLAUDE_PROJECT_DIR so hooks work even if CWD changes during a session
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
return path.join(projectDir, ".wolf");
}
/**
* Bail out silently if .wolf/ directory doesn't exist in the current project.
* Call this at the top of every hook to avoid crashes in non-OpenWolf projects.
*/
export function ensureWolfDir() {
const wolfDir = getWolfDir();
if (!fs.existsSync(wolfDir)) {
process.exit(0);
}
}
export function readJSON(filePath, fallback) {
try {
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
catch {
return fallback;
}
}
export function writeJSON(filePath, data) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir))
fs.mkdirSync(dir, { recursive: true });
const tmp = filePath + "." + crypto.randomBytes(4).toString("hex") + ".tmp";
try {
fs.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
fs.renameSync(tmp, filePath);
}
catch {
// On Windows, rename can fail if another process holds a handle.
// Fall back to direct write and clean up the tmp file.
try {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
}
catch { }
try {
fs.unlinkSync(tmp);
}
catch { }
}
}
export function readMarkdown(filePath) {
try {
return fs.readFileSync(filePath, "utf-8");
}
catch {
return "";
}
}
export function appendMarkdown(filePath, line) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir))
fs.mkdirSync(dir, { recursive: true });
fs.appendFileSync(filePath, line, "utf-8");
}
export function parseAnatomy(content) {
const sections = new Map();
let currentSection = "";
for (const line of content.split("\n")) {
const sm = line.match(/^## (.+)/);
if (sm) {
currentSection = sm[1].trim();
if (!sections.has(currentSection))
sections.set(currentSection, []);
continue;
}
if (!currentSection)
continue;
const em = line.match(/^- `([^`]+)`(?:\s+—\s+(.+?))?\s*\(~(\d+)\s+tok\)$/);
if (em) {
sections.get(currentSection).push({
file: em[1],
description: em[2] || "",
tokens: parseInt(em[3], 10),
});
}
}
return sections;
}
export function serializeAnatomy(sections, metadata) {
const lines = [
"# anatomy.md",
"",
`> Auto-maintained by OpenWolf. Last scanned: ${metadata.lastScanned}`,
`> Files: ${metadata.fileCount} tracked | Anatomy hits: ${metadata.hits} | Misses: ${metadata.misses}`,
"",
];
const keys = [...sections.keys()].sort();
for (const key of keys) {
lines.push(`## ${key}`);
lines.push("");
const entries = sections.get(key).sort((a, b) => a.file.localeCompare(b.file));
for (const e of entries) {
const desc = e.description ? ` — ${e.description}` : "";
lines.push(`- \`${e.file}\`${desc} (~${e.tokens} tok)`);
}
lines.push("");
}
return lines.join("\n");
}
export function extractDescription(filePath) {
const MAX_DESC = 150;
const basename = path.basename(filePath);
const ext = path.extname(basename).toLowerCase();
const known = {
"package.json": "Node.js package manifest",
"tsconfig.json": "TypeScript configuration",
".gitignore": "Git ignore rules",
"README.md": "Project documentation",
"composer.json": "PHP package manifest",
"requirements.txt": "Python dependencies",
"schema.sql": "Database schema",
"Dockerfile": "Docker container definition",
"docker-compose.yml": "Docker Compose services",
"Cargo.toml": "Rust package manifest",
"go.mod": "Go module definition",
"Gemfile": "Ruby dependencies",
"pubspec.yaml": "Dart/Flutter package manifest",
};
if (known[basename])
return known[basename];
let content;
try {
const fd = fs.openSync(filePath, "r");
const buf = Buffer.alloc(12288); // 12KB
const n = fs.readSync(fd, buf, 0, 12288, 0);
fs.closeSync(fd);
content = buf.subarray(0, n).toString("utf-8");
}
catch {
return "";
}
if (!content.trim())
return "";
const cap = (s) => s.length <= MAX_DESC ? s : s.slice(0, MAX_DESC - 3) + "...";
// Markdown heading
if (ext === ".md" || ext === ".mdx") {
const m = content.match(/^#{1,2}\s+(.+)$/m);
if (m)
return cap(m[1].trim());
}
// HTML title
if (ext === ".html" || ext === ".htm") {
const m = content.match(/<title[^>]*>([^<]+)<\/title>/i);
if (m)
return cap(m[1].trim());
}
// JSDoc / PHPDoc / Javadoc — first meaningful line
const jm = content.match(/\/\*\*\s*\n?\s*\*?\s*(.+)/);
if (jm) {
const l = jm[1].replace(/\*\/$/, "").trim();
if (l && !l.startsWith("@") && l.length > 5)
return cap(l);
}
// Python docstring
if (ext === ".py") {
const dm = content.match(/^(?:#[^\n]*\n)*\s*(?:"""(.+?)"""|'''(.+?)''')/s);
if (dm) {
const first = (dm[1] || dm[2]).split("\n")[0].trim();
if (first && first.length > 3)
return cap(first);
}
}
// Rust doc comments
if (ext === ".rs") {
const lines = content.split("\n");
for (const line of lines.slice(0, 20)) {
const m = line.match(/^\s*(?:\/\/\/|\/\/!)\s*(.+)/);
if (m && m[1].length > 5)
return cap(m[1].trim());
}
}
// Go package comment
if (ext === ".go") {
const m = content.match(/\/\/\s*Package\s+\w+\s+(.*)/);
if (m)
return cap(m[1].trim());
}
// C# XML doc
if (ext === ".cs") {
const m = content.match(/<summary>\s*([\s\S]*?)\s*<\/summary>/);
if (m) {
const text = m[1].replace(/\/\/\/\s*/g, "").replace(/\s+/g, " ").trim();
if (text.length > 5)
return cap(text);
}
}
// Elixir @moduledoc
if (ext === ".ex" || ext === ".exs") {
const m = content.match(/@moduledoc\s+"""\s*\n\s*(.*)/);
if (m)
return cap(m[1].trim());
}
// Header comment (skip generic ones)
const hdrLines = content.split("\n");
for (const line of hdrLines.slice(0, 15)) {
const t = line.trim();
if (!t || t === "<?php" || t.startsWith("#!") || t.startsWith("namespace") || t.startsWith("use ") || t.startsWith("import ") || t.startsWith("from ") || t.startsWith("require") || t.startsWith("module "))
continue;
const cm = t.match(/^(?:\/\/|#|--)\s*(.+)/);
if (cm) {
const text = cm[1].trim();
const lower = text.toLowerCase();
if (text.length > 5 && !lower.startsWith("copyright") && !lower.startsWith("license") && !lower.startsWith("@") && !lower.startsWith("strict") && !lower.startsWith("generated") && !lower.startsWith("eslint-") && !lower.startsWith("nolint")) {
return cap(text);
}
}
if (!t.startsWith("//") && !t.startsWith("#") && !t.startsWith("/*") && !t.startsWith("*") && !t.startsWith("--"))
break;
}
// ─── PHP / Laravel ───────────────────────────────────────
if (ext === ".php") {
if (basename.endsWith(".blade.php")) {
const ext2 = content.match(/@extends\(\s*['"]([^'"]+)['"]\s*\)/);
const sections = (content.match(/@section\(\s*['"](\w+)['"]/g) || []).map(s => s.match(/['"](\w+)['"]/)?.[1]).filter(Boolean);
const parts = [];
if (ext2)
parts.push(`extends ${ext2[1]}`);
if (sections.length)
parts.push(`sections: ${sections.join(", ")}`);
return cap(parts.length ? `Blade: ${parts.join(", ")}` : "Blade template");
}
const classM = content.match(/class\s+(\w+)(?:\s+extends\s+(\w+))?/);
const className = classM?.[1] || "";
const parent = classM?.[2] || "";
const pubMethods = (content.match(/public\s+function\s+(\w+)/g) || [])
.map(m => m.match(/public\s+function\s+(\w+)/)?.[1])
.filter(n => n && n !== "__construct" && n !== "middleware");
if (basename.endsWith("Controller.php") || parent === "Controller") {
if (pubMethods.length > 0) {
const display = pubMethods.slice(0, 5).join(", ");
return cap(pubMethods.length > 5 ? `${display} + ${pubMethods.length - 5} more` : display);
}
}
if (parent === "Model" || parent === "Authenticatable") {
const parts = [];
const tbl = content.match(/\$table\s*=\s*['"]([^'"]+)['"]/);
if (tbl)
parts.push(`table: ${tbl[1]}`);
const fill = content.match(/\$fillable\s*=\s*\[([^\]]*)\]/s);
if (fill) {
const c = (fill[1].match(/['"]/g) || []).length / 2;
parts.push(`${Math.floor(c)} fields`);
}
const rels = (content.match(/\$this->(hasMany|hasOne|belongsTo|belongsToMany|morphMany|morphTo)\(/g) || []).length;
if (rels)
parts.push(`${rels} rels`);
return cap(parts.length ? `Model — ${parts.join(", ")}` : `Model: ${className}`);
}
if (basename.match(/^\d{4}_\d{2}_\d{2}/)) {
const create = content.match(/Schema::create\(\s*['"]([^'"]+)['"]/);
if (create)
return `Migration: create ${create[1]} table`;
const alter = content.match(/Schema::table\(\s*['"]([^'"]+)['"]/);
if (alter)
return `Migration: alter ${alter[1]} table`;
return "Database migration";
}
if (className && pubMethods.length > 0) {
const display = pubMethods.slice(0, 4).join(", ");
return cap(pubMethods.length > 4 ? `${className}: ${display} + ${pubMethods.length - 4} more` : `${className}: ${display}`);
}
}
// ─── TS/JS/React/Next.js ─────────────────────────────────
if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") {
// React component
if (ext === ".tsx" || ext === ".jsx") {
const comp = content.match(/(?:export\s+(?:default\s+)?)?(?:function|const)\s+(\w+)/);
const parts = [];
if (comp)
parts.push(comp[1]);
const renders = [];
if (/<(?:form|Form)/i.test(content))
renders.push("form");
if (/<(?:table|Table|DataTable)/i.test(content))
renders.push("table");
if (/<(?:dialog|Dialog|Modal|Drawer)/i.test(content))
renders.push("modal");
if (renders.length)
parts.push(`renders ${renders.join(", ")}`);
if (parts.length)
return cap(parts.join(" — "));
}
// Next.js conventions
if (basename === "page.tsx" || basename === "page.js")
return "Next.js page component";
if (basename === "layout.tsx" || basename === "layout.js")
return "Next.js layout";
if (basename === "route.ts" || basename === "route.js") {
const methods = [...new Set((content.match(/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)/g) || [])
.map(m => m.match(/(GET|POST|PUT|PATCH|DELETE)/)?.[1]))].filter(Boolean);
return methods.length ? `Next.js API route: ${methods.join(", ")}` : "Next.js API route";
}
// Express/Fastify routes
const routeHits = content.match(/\.(get|post|put|patch|delete)\s*\(\s*['"`]/g);
if (routeHits && routeHits.length > 0) {
const methods = [...new Set(routeHits.map(r => r.match(/\.(get|post|put|patch|delete)/)?.[1]?.toUpperCase()))];
return cap(`API routes: ${methods.join(", ")} (${routeHits.length} endpoints)`);
}
// tRPC router
if (content.includes("createTRPCRouter") || content.includes("publicProcedure")) {
const procs = (content.match(/\.(query|mutation|subscription)\s*\(/g) || []).length;
return procs ? `tRPC router: ${procs} procedures` : "tRPC router";
}
// Zod schemas
if (content.includes("z.object") || content.includes("z.string")) {
const schemas = (content.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*z\./g) || [])
.map(s => s.match(/(?:const|let)\s+(\w+)/)?.[1]).filter(Boolean);
if (schemas.length)
return cap(`Zod schemas: ${schemas.slice(0, 4).join(", ")}${schemas.length > 4 ? ` + ${schemas.length - 4} more` : ""}`);
}
// Exports summary
const exports = (content.match(/export\s+(?:async\s+)?(?:function|class|const|interface|type|enum)\s+(\w+)/g) || [])
.map(e => e.match(/(\w+)$/)?.[1]).filter(Boolean);
if (exports.length > 0 && exports.length <= 5)
return `Exports ${exports.join(", ")}`;
if (exports.length > 5)
return cap(`Exports ${exports.slice(0, 4).join(", ")} + ${exports.length - 4} more`);
}
// ─── Python / Django / FastAPI / Flask ────────────────────
if (ext === ".py") {
// Django model
if (content.includes("models.Model")) {
const cls = content.match(/class\s+(\w+)\(.*models\.Model\)/);
const fields = (content.match(/^\s+\w+\s*=\s*models\.\w+/gm) || []).length;
return cap(`Model: ${cls?.[1] || "unknown"}, ${fields} fields`);
}
// FastAPI/Flask routes
if (content.includes("@router.") || content.includes("@app.")) {
const routes = (content.match(/@(?:router|app)\.(get|post|put|patch|delete)\s*\(/g) || []);
return cap(routes.length ? `API: ${routes.length} endpoints` : "API router");
}
// Pydantic
if (content.includes("BaseModel") && content.includes("Field(")) {
const cls = content.match(/class\s+(\w+)\(.*BaseModel\)/);
return cls ? `Pydantic: ${cls[1]}` : "Pydantic model";
}
// Celery
if (content.includes("@shared_task") || content.includes("@app.task")) {
const tasks = (content.match(/def\s+(\w+)/g) || []).map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_"));
return cap(tasks.length ? `Celery tasks: ${tasks.join(", ")}` : "Celery task");
}
// Generic
const pyClass = content.match(/class\s+(\w+)/);
const funcs = (content.match(/def\s+(\w+)/g) || []).map(f => f.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_"));
if (pyClass && funcs.length > 0)
return cap(funcs.length > 4 ? `${pyClass[1]}: ${funcs.slice(0, 4).join(", ")} + ${funcs.length - 4} more` : `${pyClass[1]}: ${funcs.join(", ")}`);
if (funcs.length > 0)
return cap(funcs.slice(0, 4).join(", "));
}
// ─── Go ──────────────────────────────────────────────────
if (ext === ".go") {
const handlers = (content.match(/func\s+(\w+)\s*\(\s*\w+\s+http\.ResponseWriter/g) || [])
.map(m => m.match(/func\s+(\w+)/)?.[1]).filter(Boolean);
if (handlers.length)
return cap(`HTTP handlers: ${handlers.slice(0, 5).join(", ")}`);
const iface = content.match(/type\s+(\w+)\s+interface\s*\{/);
if (iface)
return `Interface: ${iface[1]}`;
const structM = content.match(/type\s+(\w+)\s+struct\s*\{/);
if (structM)
return `Struct: ${structM[1]}`;
const funcs = (content.match(/^func\s+(\w+)/gm) || []).map(m => m.match(/func\s+(\w+)/)?.[1]).filter(n => n && n[0] === n[0].toUpperCase());
if (funcs.length)
return cap(funcs.slice(0, 5).join(", "));
}
// ─── Rust ────────────────────────────────────────────────
if (ext === ".rs") {
const structM = content.match(/pub\s+struct\s+(\w+)/);
if (structM) {
const methods = (content.match(/pub\s+(?:async\s+)?fn\s+(\w+)/g) || []).map(m => m.match(/fn\s+(\w+)/)?.[1]).filter(Boolean);
return cap(methods.length ? `${structM[1]}: ${methods.slice(0, 4).join(", ")}` : `Struct: ${structM[1]}`);
}
const traitM = content.match(/pub\s+trait\s+(\w+)/);
if (traitM)
return `Trait: ${traitM[1]}`;
const enumM = content.match(/pub\s+enum\s+(\w+)/);
if (enumM)
return `Enum: ${enumM[1]}`;
const fns = (content.match(/pub\s+(?:async\s+)?fn\s+(\w+)/g) || []).map(m => m.match(/fn\s+(\w+)/)?.[1]).filter(Boolean);
if (fns.length)
return cap(fns.slice(0, 5).join(", "));
}
// ─── Java / Spring ───────────────────────────────────────
if (ext === ".java") {
const cls = content.match(/(?:public\s+)?class\s+(\w+)/);
const className = cls?.[1] || basename.replace(".java", "");
const annotations = (content.match(/@(RestController|Controller|Service|Repository|Component|Entity|Configuration)/g) || []).map(a => a.slice(1));
const mappings = (content.match(/@(?:Get|Post|Put|Patch|Delete|Request)Mapping/g) || []).length;
if (mappings)
return cap(`${annotations[0] || "Spring"}: ${className} (${mappings} endpoints)`);
if (annotations.length)
return `${annotations[0]}: ${className}`;
if (content.includes("@Entity"))
return `Entity: ${className}`;
const methods = (content.match(/public\s+(?:static\s+)?(?:\w+(?:<[\w,\s]+>)?)\s+(\w+)\s*\(/g) || [])
.map(m => m.match(/(\w+)\s*\(/)?.[1]).filter(n => n && n !== className);
if (methods.length)
return cap(`${className}: ${methods.slice(0, 4).join(", ")}`);
return className ? `Class: ${className}` : "";
}
// ─── Kotlin ──────────────────────────────────────────────
if (ext === ".kt" || ext === ".kts") {
const cls = content.match(/(?:data\s+)?class\s+(\w+)/);
if (content.match(/data\s+class/))
return `Data class: ${cls?.[1] || basename.replace(/\.kts?$/, "")}`;
if (content.includes("routing {"))
return "Ktor routing";
const fns = (content.match(/fun\s+(\w+)/g) || []).map(m => m.match(/fun\s+(\w+)/)?.[1]).filter(Boolean);
if (cls && fns.length)
return cap(`${cls[1]}: ${fns.slice(0, 4).join(", ")}`);
if (fns.length)
return cap(fns.slice(0, 5).join(", "));
}
// ─── C# / .NET ───────────────────────────────────────────
if (ext === ".cs") {
const cls = content.match(/(?:public\s+)?(?:partial\s+)?class\s+(\w+)(?:\s*:\s*(\w+))?/);
const className = cls?.[1] || basename.replace(".cs", "");
const parent = cls?.[2] || "";
if (parent === "Controller" || parent === "ControllerBase" || content.includes("[ApiController]")) {
const actions = (content.match(/\[Http(Get|Post|Put|Patch|Delete)\]/g) || []).map(a => a.match(/Http(\w+)/)?.[1]).filter(Boolean);
return cap(actions.length ? `API Controller: ${className} (${[...new Set(actions)].join(", ")})` : `Controller: ${className}`);
}
if (parent === "DbContext" || content.includes("DbSet<")) {
const sets = (content.match(/DbSet<(\w+)>/g) || []).map(s => s.match(/<(\w+)>/)?.[1]).filter(Boolean);
return cap(sets.length ? `DbContext: ${sets.join(", ")}` : `DbContext: ${className}`);
}
return className ? `Class: ${className}` : "";
}
// ─── Ruby / Rails ────────────────────────────────────────
if (ext === ".rb") {
const cls = content.match(/class\s+(\w+)(?:\s*<\s*(\w+(?:::\w+)?))?/);
const className = cls?.[1] || "";
const parent = cls?.[2] || "";
if (parent?.includes("Controller")) {
const actions = (content.match(/def\s+(index|show|new|create|edit|update|destroy|\w+)/g) || [])
.map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_"));
return cap(actions.length ? `Controller: ${actions.join(", ")}` : `Controller: ${className}`);
}
if (parent === "ApplicationRecord" || parent === "ActiveRecord::Base")
return `Model: ${className}`;
if (basename.match(/^\d{14}_/)) {
const create = content.match(/create_table\s+:(\w+)/);
return create ? `Migration: create ${create[1]}` : "Database migration";
}
const methods = (content.match(/def\s+(\w+)/g) || []).map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_"));
if (cls && methods.length)
return cap(`${className}: ${methods.slice(0, 4).join(", ")}`);
}
// ─── Swift ───────────────────────────────────────────────
if (ext === ".swift") {
if (content.includes(": View") || content.includes("some View")) {
const name = content.match(/struct\s+(\w+)\s*:\s*View/);
return name ? `SwiftUI view: ${name[1]}` : "SwiftUI view";
}
const proto = content.match(/protocol\s+(\w+)/);
if (proto)
return `Protocol: ${proto[1]}`;
const struct = content.match(/(?:public\s+)?struct\s+(\w+)/);
const cls = content.match(/(?:public\s+)?class\s+(\w+)/);
const name = struct?.[1] || cls?.[1] || "";
if (name)
return `${struct ? "Struct" : "Class"}: ${name}`;
}
// ─── Dart / Flutter ──────────────────────────────────────
if (ext === ".dart") {
if (content.includes("StatefulWidget") || content.includes("StatelessWidget")) {
const name = content.match(/class\s+(\w+)\s+extends\s+(?:Stateful|Stateless)Widget/);
return name ? `${content.includes("StatefulWidget") ? "Stateful" : "Stateless"} widget: ${name[1]}` : "Flutter widget";
}
const cls = content.match(/class\s+(\w+)/);
if (cls)
return `Class: ${cls[1]}`;
}
// ─── Vue / Svelte / Astro ────────────────────────────────
if (ext === ".vue") {
const name = content.match(/name:\s*['"]([^'"]+)['"]/);
const setup = content.includes("<script setup");
const parts = [];
if (name)
parts.push(name[1]);
if (setup)
parts.push("setup");
return cap(parts.length ? `Vue: ${parts.join(", ")}` : "Vue component");
}
if (ext === ".svelte")
return `Svelte: ${basename.replace(".svelte", "")}`;
if (ext === ".astro")
return `Astro: ${basename.replace(".astro", "")}`;
// ─── CSS / SCSS / Less ───────────────────────────────────
if (ext === ".css" || ext === ".scss" || ext === ".less") {
const rules = (content.match(/^[.#@][^\n{]+/gm) || []).length;
const vars = (content.match(/--[\w-]+\s*:/g) || []).length;
const parts = [];
if (rules)
parts.push(`${rules} rules`);
if (vars)
parts.push(`${vars} vars`);
return cap(parts.length ? `Styles: ${parts.join(", ")}` : "Stylesheet");
}
// ─── SQL ─────────────────────────────────────────────────
if (ext === ".sql") {
const creates = (content.match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)/gi) || [])
.map(m => m.match(/(?:TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?)([`"']?\w+)/i)?.[1]?.replace(/[`"']/g, "")).filter(Boolean);
if (creates.length)
return cap(`SQL: tables: ${creates.slice(0, 4).join(", ")}`);
}
// ─── Proto / GraphQL ─────────────────────────────────────
if (ext === ".proto") {
const msgs = (content.match(/message\s+(\w+)/g) || []).map(m => m.match(/message\s+(\w+)/)?.[1]).filter(Boolean);
const services = (content.match(/service\s+(\w+)/g) || []).map(m => m.match(/service\s+(\w+)/)?.[1]).filter(Boolean);
const parts = [];
if (msgs.length)
parts.push(`messages: ${msgs.slice(0, 3).join(", ")}`);
if (services.length)
parts.push(`services: ${services.join(", ")}`);
return cap(parts.length ? `Proto: ${parts.join(", ")}` : "");
}
if (ext === ".graphql" || ext === ".gql") {
const types = (content.match(/type\s+(\w+)/g) || []).map(m => m.match(/type\s+(\w+)/)?.[1]).filter(Boolean);
return cap(types.length ? `GraphQL: types: ${types.slice(0, 4).join(", ")}` : "GraphQL schema");
}
// ─── YAML ────────────────────────────────────────────────
if (ext === ".yaml" || ext === ".yml") {
if (content.includes("runs-on:")) {
const name = content.match(/^name:\s*(.+)$/m);
return cap(name ? `CI: ${name[1].trim()}` : "GitHub Actions workflow");
}
if (content.includes("apiVersion:") && content.includes("kind:")) {
const kind = content.match(/kind:\s*(\w+)/);
return cap(kind ? `K8s ${kind[1]}` : "Kubernetes manifest");
}
if (content.includes("services:") && (basename.includes("docker") || basename.includes("compose"))) {
const services = (content.match(/^\s{2}\w+:/gm) || []).length;
return `Docker Compose: ${services} services`;
}
}
// ─── TOML ────────────────────────────────────────────────
if (ext === ".toml") {
const desc = content.match(/^description\s*=\s*"([^"]+)"/m);
if (desc)
return cap(desc[1]);
}
// ─── Elixir ──────────────────────────────────────────────
if (ext === ".ex" || ext === ".exs") {
const mod = content.match(/defmodule\s+([\w.]+)/);
if (content.includes("Phoenix.LiveView"))
return cap(mod ? `LiveView: ${mod[1]}` : "Phoenix LiveView");
if (content.includes("Controller"))
return cap(mod ? `Phoenix controller: ${mod[1]}` : "Phoenix controller");
const fns = (content.match(/def\s+(\w+)/g) || []).map(m => m.match(/def\s+(\w+)/)?.[1]).filter(Boolean);
if (mod && fns.length)
return cap(`${mod[1]}: ${fns.slice(0, 4).join(", ")}`);
if (mod)
return mod[1];
}
// ─── Lua ─────────────────────────────────────────────────
if (ext === ".lua") {
const fns = (content.match(/function\s+(?:\w+[.:])?(\w+)/g) || []).map(m => m.match(/(\w+)\s*$/)?.[1]).filter(Boolean);
if (fns.length)
return cap(fns.slice(0, 5).join(", "));
}
// ─── Zig ─────────────────────────────────────────────────
if (ext === ".zig") {
const fns = (content.match(/pub\s+fn\s+(\w+)/g) || []).map(m => m.match(/fn\s+(\w+)/)?.[1]).filter(Boolean);
if (fns.length)
return cap(fns.slice(0, 5).join(", "));
}
// Last resort
const declM = content.match(/(?:function|class|const|interface|type|enum)\s+(\w+)/);
if (declM) {
const name = declM[1];
const methods = (content.match(/(?:public\s+)?(?:async\s+)?(?:function\s+|(?:get|set)\s+)(\w+)\s*\(/g) || [])
.map(m => m.match(/(\w+)\s*\(/)?.[1]).filter(n => n && n !== name && n !== "__construct" && n !== "constructor");
if (methods.length > 0 && methods.length <= 5)
return cap(`${name}: ${methods.join(", ")}`);
if (methods.length > 5)
return cap(`${name}: ${methods.slice(0, 3).join(", ")} + ${methods.length - 3} more`);
return `Declares ${name}`;
}
return "";
}
export function estimateTokens(text, type = "mixed") {
const ratio = type === "code" ? 3.5 : type === "prose" ? 4.0 : 3.75;
return Math.ceil(text.length / ratio);
}
export function timestamp() {
return new Date().toISOString();
}
export function timeShort() {
const d = new Date();
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
}
export function readStdin() {
return new Promise((resolve) => {
const chunks = [];
process.stdin.on("data", (chunk) => chunks.push(chunk));
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
// If no stdin data after 4s, resolve with whatever we have so far.
// On Windows, stdin delivery from Claude Code hooks can be slow.
setTimeout(() => resolve(chunks.length ? Buffer.concat(chunks).toString("utf-8") : "{}"), 4000);
});
}
export function normalizePath(p) {
return p.replace(/\\/g, "/");
}
//# sourceMappingURL=shared.js.map

View file

@ -1,236 +0,0 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { execFileSync } from "node:child_process";
import { getWolfDir, ensureWolfDir, readJSON, writeJSON, appendMarkdown, timeShort } from "./shared.js";
async function main() {
ensureWolfDir();
const wolfDir = getWolfDir();
const hooksDir = path.join(wolfDir, "hooks");
const sessionFile = path.join(hooksDir, "_session.json");
const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
// Runs every stop regardless of read/write activity this turn, and
// regardless of whether buglog.json was edited via the Write/Edit tool
// or via a raw script (the common path for a file this large) — this
// is the only guard that can't be bypassed by which tool touched it.
checkAndCheckpointBuglog(wolfDir, projectRoot);
const session = readJSON(sessionFile, {
session_id: "",
started: "",
files_read: {},
files_written: [],
edit_counts: {},
anatomy_hits: 0,
anatomy_misses: 0,
repeated_reads_warned: 0,
cerebrum_warnings: 0,
stop_count: 0,
});
session.stop_count++;
// Only write to ledger if there's been activity
const readCount = Object.keys(session.files_read).length;
const writeCount = session.files_written.length;
if (readCount === 0 && writeCount === 0) {
writeJSON(sessionFile, session);
process.exit(0);
return;
}
// Check for files edited many times without a buglog entry
checkForMissingBugLogs(wolfDir, session);
// Check if cerebrum was updated this session (it should be if there were edits)
checkCerebrumFreshness(wolfDir, session);
// Build session entry for ledger
const reads = Object.entries(session.files_read).map(([file, data]) => ({
file,
tokens_estimated: data.tokens,
was_repeated: data.count > 1,
anatomy_had_description: false, // simplified
}));
const writes = session.files_written.map((w) => ({
file: w.file,
tokens_estimated: w.tokens,
action: w.action,
}));
const inputTokens = reads.reduce((sum, r) => sum + r.tokens_estimated, 0);
const outputTokens = writes.reduce((sum, w) => sum + w.tokens_estimated, 0);
const sessionEntry = {
id: session.session_id,
started: session.started,
ended: new Date().toISOString(),
reads,
writes,
totals: {
input_tokens_estimated: inputTokens,
output_tokens_estimated: outputTokens,
reads_count: readCount,
writes_count: writeCount,
repeated_reads_blocked: session.repeated_reads_warned,
anatomy_lookups: session.anatomy_hits,
},
};
// Update token-ledger.json
const ledgerPath = path.join(wolfDir, "token-ledger.json");
const ledger = readJSON(ledgerPath, {
version: 1,
created_at: "",
lifetime: {
total_tokens_estimated: 0,
total_reads: 0,
total_writes: 0,
total_sessions: 0,
anatomy_hits: 0,
anatomy_misses: 0,
repeated_reads_blocked: 0,
estimated_savings_vs_bare_cli: 0,
},
sessions: [],
daemon_usage: [],
waste_flags: [],
optimization_report: { last_generated: null, patterns: [] },
});
ledger.sessions.push(sessionEntry);
ledger.lifetime.total_reads += readCount;
ledger.lifetime.total_writes += writeCount;
ledger.lifetime.total_tokens_estimated += inputTokens + outputTokens;
ledger.lifetime.anatomy_hits += session.anatomy_hits;
ledger.lifetime.anatomy_misses += session.anatomy_misses;
ledger.lifetime.repeated_reads_blocked += session.repeated_reads_warned;
// Estimate savings: anatomy hits save ~200 tokens each, repeated reads blocked save their token count
const savedFromAnatomy = session.anatomy_hits * 200;
const savedFromRepeats = Object.values(session.files_read)
.filter((r) => r.count > 1)
.reduce((sum, r) => sum + r.tokens * (r.count - 1), 0);
ledger.lifetime.estimated_savings_vs_bare_cli += savedFromAnatomy + savedFromRepeats;
writeJSON(ledgerPath, ledger);
// Write a session summary line to memory.md if there was meaningful activity
if (writeCount > 0) {
try {
const uniqueFiles = new Set(session.files_written.map(w => path.basename(w.file)));
const fileList = [...uniqueFiles].slice(0, 5).join(", ");
const memoryPath = path.join(wolfDir, "memory.md");
appendMarkdown(memoryPath, `| ${timeShort()} | Session end: ${writeCount} writes across ${uniqueFiles.size} files (${fileList}) | ${readCount} reads | ~${inputTokens + outputTokens} tok |\n`);
}
catch { }
}
writeJSON(sessionFile, session);
process.exit(0);
}
/**
* Check if files were edited multiple times but buglog.json wasn't updated.
* Emit a stderr reminder so Claude sees it in the next turn.
*/
function checkForMissingBugLogs(wolfDir, session) {
if (!session.edit_counts)
return;
const multiEditFiles = Object.entries(session.edit_counts)
.filter(([, count]) => count >= 3)
.map(([file]) => path.basename(file));
if (multiEditFiles.length === 0)
return;
// Check if buglog was written to this session
const buglogWritten = session.files_written.some(w => w.file.includes("buglog.json"));
if (!buglogWritten) {
process.stderr.write(`⚠️ OpenWolf: Files edited 3+ times this session (${multiEditFiles.join(", ")}) but buglog.json was not updated. If you fixed bugs, please log them.\n`);
}
}
/**
* Check if cerebrum.md was updated recently. If it hasn't been updated in
* a while and there was significant activity, emit a gentle reminder.
*/
function checkCerebrumFreshness(wolfDir, session) {
const cerebrumPath = path.join(wolfDir, "cerebrum.md");
try {
const stat = fs.statSync(cerebrumPath);
const hoursSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60);
// If cerebrum hasn't been updated in 24h+ and there were significant writes
if (hoursSinceUpdate > 24 && session.files_written.length >= 3) {
process.stderr.write(`💡 OpenWolf: 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.\n`);
}
}
catch {
// cerebrum.md doesn't exist, that's ok
}
}
/**
* Validate .wolf/buglog.json on disk and, if it's well-formed and grew
* (or stayed the same) relative to the last commit, checkpoint it as its
* own standalone commit. Never commits a shrinking or malformed file —
* that would enshrine data loss (or corruption) into git history, which
* is exactly the failure mode this guard exists to catch before it repeats.
*/
function checkAndCheckpointBuglog(wolfDir, projectRoot) {
const buglogPath = path.join(wolfDir, "buglog.json");
if (!fs.existsSync(buglogPath))
return;
let raw;
try {
raw = fs.readFileSync(buglogPath, "utf-8");
}
catch {
return;
}
let parsed;
try {
parsed = JSON.parse(raw);
}
catch (e) {
process.stderr.write(`🛑 OpenWolf: .wolf/buglog.json is not valid JSON (${e.message}). NOT checkpointing — fix this before it's lost for good.\n`);
return;
}
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.bugs)) {
process.stderr.write(`🛑 OpenWolf: .wolf/buglog.json must be {"version":1,"bugs":[...]}, not a bare array or other shape. NOT checkpointing.\n`);
return;
}
const bugs = parsed.bugs;
const idNum = (id) => {
const m = /^bug-(\d+)$/.exec(id || "");
return m ? parseInt(m[1], 10) : 0;
};
const currentMaxId = bugs.reduce((max, b) => Math.max(max, idNum(b.id)), 0);
const idCounts = {};
for (const b of bugs)
idCounts[b.id] = (idCounts[b.id] || 0) + 1;
const dupes = Object.keys(idCounts).filter((id) => idCounts[id] > 1);
if (dupes.length > 0) {
process.stderr.write(`⚠️ OpenWolf: buglog.json has duplicate ids (${dupes.slice(0, 5).join(", ")}${dupes.length > 5 ? ", ..." : ""}). Before adding a new bug, search for an existing entry with the same file+root_cause and bump its occurrences instead of appending a new one under a colliding or fresh id.\n`);
}
let lastCount = null;
let lastMaxId = null;
try {
const committed = execFileSync("git", ["show", "HEAD:.wolf/buglog.json"], {
cwd: projectRoot,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
});
const lastParsed = JSON.parse(committed);
const lastBugs = Array.isArray(lastParsed) ? lastParsed : (lastParsed.bugs || []);
lastCount = lastBugs.length;
lastMaxId = lastBugs.reduce((max, b) => Math.max(max, idNum(b.id)), 0);
}
catch {
// No committed version yet (or git unavailable) — nothing to compare against.
}
if (lastCount !== null && bugs.length < lastCount) {
process.stderr.write(`🛑 OpenWolf: buglog.json SHRANK from ${lastCount} to ${bugs.length} bugs since the last commit. NOT auto-committing — this looks like data loss, investigate (check git history / other worktrees) before committing manually.\n`);
return;
}
if (lastMaxId !== null && currentMaxId < lastMaxId && bugs.length > 0) {
process.stderr.write(`🛑 OpenWolf: buglog.json's highest id (bug-${currentMaxId}) is lower than the last commit's (bug-${lastMaxId}) — looks like the id counter was reset. NOT auto-committing.\n`);
return;
}
try {
const status = execFileSync("git", ["status", "--porcelain", "--", ".wolf/buglog.json"], {
cwd: projectRoot,
encoding: "utf-8",
});
if (!status.trim())
return; // nothing changed since last commit
execFileSync("git", ["add", ".wolf/buglog.json"], { cwd: projectRoot });
execFileSync("git", ["commit", "-m", `chore(wolf): checkpoint buglog.json (${bugs.length} bugs)`], { cwd: projectRoot });
process.stderr.write(`✅ OpenWolf: checkpointed buglog.json (${bugs.length} bugs) to git.\n`);
}
catch (e) {
process.stderr.write(`⚠️ OpenWolf: buglog.json is valid but the checkpoint commit failed: ${e.message}\n`);
}
}
main().catch(() => process.exit(0));
//# sourceMappingURL=stop.js.map

View file

@ -1,9 +0,0 @@
# Identity
- **Name:** persistent_ha
- **Role:** AI development assistant for persistent_ha
- **Tone:** Direct, concise, technically precise
- **Constraints:**
- Never modify .env or secret files without explicit user confirmation
- Never delete files without explicit user confirmation
- Always explain why before making architectural changes

File diff suppressed because it is too large Load diff

View file

@ -1,597 +0,0 @@
# OpenWolf Reframe — UI Framework Knowledge Base
When the user asks to change, pick, migrate, or "reframe" their UI framework, use this file to guide the conversation and generate the right migration prompt.
## Decision Questions
Ask these in order. Stop early if the answer narrows to 1-2 frameworks.
1. **What framework/library does your project currently use?** (React, Vue, Svelte, plain HTML, etc.)
2. **What's your priority?** (a) Stunning animations, (b) Fast setup / ship quickly, (c) Full design control, (d) Accessibility-first, (e) Business/enterprise look
3. **Do you use Tailwind CSS already?** (Yes → most options work. No → Chakra UI or DaisyUI are easier without it)
4. **Light mode, dark mode, or both?**
5. **Is this a landing page, a dashboard/app, or both?**
## Quick Selection Guide
| Priority | Recommended |
|----------|-------------|
| Fastest setup | DaisyUI, Preline UI |
| Stunning animations | Aceternity UI, Magic UI |
| Maximum control | shadcn/ui, Headless UI |
| Most free components | Origin UI (400+) |
| Multi-framework (React + Vue + Svelte) | Park UI, DaisyUI, Flowbite |
| AI product aesthetic | Cult UI |
| Polished defaults + accessibility | HeroUI, Chakra UI |
| Business/enterprise | Flowbite, shadcn/ui |
## Comparison Matrix
| Framework | Styling | Animation | Setup | Best For | Cost |
|-----------|---------|-----------|-------|----------|------|
| shadcn/ui | Tailwind + Radix | Minimal | Medium | Full apps, dashboards | Free |
| Aceternity UI | Tailwind + Framer Motion | Heavy, cinematic | Easy (copy-paste) | Animated landing pages | Free / Pro |
| Magic UI | Tailwind + Motion | Purposeful, polished | Easy (shadcn CLI) | SaaS marketing | Free / Pro |
| DaisyUI | Tailwind plugin | CSS transitions | Very easy | Rapid prototyping | Free |
| HeroUI | Tailwind + React Aria | Smooth built-in | Easy | React apps, accessible | Free |
| Chakra UI | CSS-in-JS (Emotion) | Built-in transitions | Easy | Themed React apps | Free |
| Flowbite | Tailwind plugin | CSS transitions | Very easy | Multi-framework, business | Free / Pro |
| Preline UI | Tailwind plugin | CSS + minimal JS | Very easy | Speed-focused builds | Free / Pro |
| Park UI | Tailwind + Ark UI | Minimal | Medium | Multi-framework, design systems | Free |
| Origin UI | Tailwind + shadcn | Minimal | Easy (copy-paste) | Max component variety | Free |
| Headless UI | Custom Tailwind | Custom (Transition) | Medium-Hard | Full design control | Free |
| Cult UI | Tailwind + shadcn | Modern, subtle | Medium | AI apps, full-stack | Free |
---
## Framework Prompts
After the user selects a framework, use the corresponding prompt below. **Adapt it to the user's actual project** — replace generic references with their real file structure, existing routes, and components from `.wolf/anatomy.md`.
---
### shadcn/ui
**Stack:** React, Tailwind CSS, Radix UI
**Install:** `npx shadcn@latest init`
**Site:** ui.shadcn.com
```
Build a modern, minimalist landing page using Next.js 14+ (App Router), TypeScript, Tailwind CSS, and shadcn/ui components.
ARCHITECTURE:
- Use Next.js App Router with server components by default, client components only when interactivity is needed
- Organize: /app for routes, /components/ui for shadcn primitives, /components/sections for page sections, /lib for utilities
- Use CSS variables for theming via shadcn's built-in system
DESIGN PRINCIPLES:
- Minimalist with generous whitespace (py-24 to py-32 section spacing)
- Max-width container (max-w-6xl mx-auto) with responsive padding
- Typography hierarchy: text-5xl/6xl hero → text-3xl section titles → text-lg body
- Muted color palette with ONE bold accent color for CTAs
- Subtle micro-interactions on hover (scale, opacity transitions)
- Dark mode support via shadcn's theme provider
SECTIONS TO BUILD:
1. Navbar — sticky, transparent-to-solid on scroll, logo left, nav center, CTA button right
2. Hero — large headline (max 8 words), subtext (max 2 lines), primary CTA button + secondary ghost button, optional hero image or abstract illustration
3. Social proof — logo cloud of 5-6 partner/client logos in grayscale
4. Features — 3-column bento grid with icon, title, description per card using shadcn Card component
5. How it works — 3-step numbered process with connecting line
6. Testimonials — carousel or grid of 3 testimonial cards with avatar, quote, name, role
7. Pricing — 3-tier pricing table using shadcn Card, highlight the recommended plan with ring/border accent
8. FAQ — accordion using shadcn Accordion component
9. CTA — full-width section with headline, subtext, and primary action button
10. Footer — 4-column grid (brand, product links, company links, legal), copyright at bottom
COMPONENT USAGE:
- Button (primary, secondary, ghost, outline variants)
- Card, CardHeader, CardContent, CardFooter
- Accordion, AccordionItem, AccordionTrigger, AccordionContent
- Badge for labels/tags
- Separator for dividers
- NavigationMenu for navbar
CODE QUALITY:
- Fully responsive (mobile-first)
- Semantic HTML (section, nav, main, footer)
- Accessible (proper aria labels, keyboard navigation)
- Performant (lazy load images, optimize fonts via next/font)
- Clean component separation (each section is its own component)
```
---
### Aceternity UI
**Stack:** React, Next.js, Tailwind CSS, Framer Motion
**Install:** Copy-paste or `npx shadcn@latest add [component]`
**Site:** ui.aceternity.com
```
Build a visually stunning, animation-rich landing page using Next.js 14+, TypeScript, Tailwind CSS, and Aceternity UI components with Framer Motion.
ARCHITECTURE:
- Next.js App Router with client components for animated sections
- Organize: /app, /components/aceternity (copied components), /components/sections, /lib
- Install Framer Motion for animation engine
DESIGN PRINCIPLES:
- Dark theme as primary (bg-black/bg-slate-950 backgrounds)
- Dramatic contrast with glowing accents (cyan, purple, or emerald)
- Layered depth via gradients, glows, and glassmorphism
- Cinematic scroll-triggered animations with staggered reveals
- Generous padding (py-32+), large typography (text-6xl/7xl hero)
- Noise/grain texture overlays for visual richness
SECTIONS TO BUILD WITH ACETERNITY COMPONENTS:
1. Navbar — floating navbar with backdrop blur, animated on scroll
2. Hero — use Aceternity's "Spotlight" or "Lamp" effect as background, massive headline with TextGenerateEffect or TypewriterEffect, gradient text on key words, two CTAs with HoverBorderGradient buttons
3. Logo cloud — use InfiniteMovingCards for auto-scrolling partner logos
4. Features — BentoGrid layout with animated cards, each card has BackgroundGradient or CardSpotlight hover effect
5. Product showcase — use Aceternity's 3DCard or ParallaxScroll for product screenshots/mockups
6. Testimonials — AnimatedTestimonials or InfiniteMovingCards with quote, avatar, name
7. How it works — use TracingBeam component for a connected step-by-step flow
8. Pricing — dark cards with GlowingStarsBackground, highlighted tier has animated border (MovingBorder)
9. CTA — full-width with BackgroundBeams or Meteors effect behind headline and button
10. Footer — clean 4-column layout with subtle separator
ANIMATION GUIDELINES:
- Page load: stagger hero elements (title → subtitle → buttons) with 0.15s delay each
- Scroll: use Framer Motion's whileInView for reveal animations (fade up + slight scale)
- Hover: cards lift (translateY -4px) with glow intensification
- Keep animations performant (use transform/opacity only, will-change where needed)
- Respect prefers-reduced-motion
CODE QUALITY:
- Mobile-first responsive design
- Lazy load heavy animation components
- Each section is a standalone component file
- Accessible despite heavy animation (aria labels, focus states)
```
---
### Magic UI
**Stack:** React, TypeScript, Tailwind CSS, Motion (Framer Motion)
**Install:** `npx shadcn@latest add [component]` (shadcn CLI compatible)
**Site:** magicui.design
```
Build a polished, trend-forward SaaS landing page using Next.js 14+, TypeScript, Tailwind CSS, and Magic UI animated components.
ARCHITECTURE:
- Next.js App Router, server components where possible
- Magic UI components installed via shadcn CLI into /components/magicui
- Section components in /components/sections
- Shared config in /lib (cn utility, site config)
DESIGN PRINCIPLES:
- Clean and modern, inspired by Linear/Vercel aesthetic
- Light or dark mode with smooth toggle transition
- Restrained animation — purposeful motion that guides attention, not distracts
- High contrast text, muted backgrounds (slate-50 light / slate-950 dark)
- Monospace or geometric sans-serif for headings, clean sans for body
- ONE signature color (blue-500, violet-500, or emerald-500) for accents
SECTIONS TO BUILD WITH MAGIC UI COMPONENTS:
1. Navbar — clean horizontal nav, use MagicUI ShimmerButton for primary CTA
2. Hero — large headline with AnimatedGradientText on key phrase, NumberTicker for a live stat ("10,000+ users"), DotPattern or GridPattern as subtle background, two buttons (ShimmerButton primary + outline secondary)
3. Social proof — Marquee component for auto-scrolling logos horizontally
4. Features — use MagicCard components in a 3-column grid, each with icon + title + description, subtle hover glow effect
5. Bento showcase — BentoGrid layout showing product capabilities, mix of large and small tiles with different content types (text, mini-demos, stats)
6. Metrics — AnimatedList showing live activity feed OR NumberTicker showing key stats (users, uptime, speed)
7. Testimonials — Marquee of testimonial cards (vertical or horizontal scroll)
8. Pricing — 3 tiers with clean card design, NeonGradientCard for featured plan, BorderBeam on hover
9. CTA — centered layout with AnimatedShinyText headline, Particles or Meteors background effect
10. Footer — minimal 3-4 column grid, muted text, social icons
ANIMATION STRATEGY:
- Use BlurIn / FadeIn for section entrance animations
- NumberTicker for any statistics or metrics
- Marquee for any horizontally scrolling content
- Keep transitions under 600ms, use ease-out curves
- Mobile: reduce or disable particle/meteor effects for performance
CODE QUALITY:
- Fully responsive with mobile breakpoints
- Semantic HTML throughout
- next/font for optimized typography
- Image optimization via next/image
- Component-per-section architecture
```
---
### DaisyUI
**Stack:** Tailwind CSS plugin (framework-agnostic)
**Install:** `npm i -D daisyui` + add to tailwind.config
**Site:** daisyui.com
```
Build a clean, professional landing page using Tailwind CSS and DaisyUI component classes.
ARCHITECTURE:
- Framework of choice with Tailwind CSS + DaisyUI plugin
- Use DaisyUI's semantic class system (btn, card, hero, navbar, etc.)
- Select a DaisyUI theme (or create custom) in tailwind.config.js
- Organize by sections in /components/sections
DESIGN PRINCIPLES:
- Pick ONE DaisyUI theme as base (e.g., "winter" for clean light, "night" for dark, "corporate" for professional)
- Customize theme colors in tailwind.config to match brand
- Minimalist layout with clean spacing (p-8 to p-16 sections)
- Let DaisyUI's built-in design system handle consistency
- Typography via DaisyUI's prose class for content sections
SECTIONS TO BUILD WITH DAISYUI CLASSES:
1. Navbar — "navbar" component with "dropdown" for mobile menu, "btn btn-primary" for CTA
2. Hero — "hero" component with "hero-content", large headline, subtext, "btn btn-primary" + "btn btn-ghost"
3. Social proof — logo row using "flex" with "opacity-50 grayscale" on images
4. Features — "card" components in a responsive grid, each with "card-body", uses "bg-base-200" for contrast
5. Stats — "stats" component with "stat" items showing key metrics
6. Steps — "steps" component (horizontal on desktop, vertical on mobile)
7. Testimonials — "chat" component styled as testimonials OR "card" grid with "avatar"
8. Pricing — "card" components with "badge badge-primary" for recommended plan
9. FAQ — "collapse" or "collapse-plus" components for expandable Q&A
10. Footer — "footer" component with 3-4 columns, "footer-title" for headings
CODE QUALITY:
- Fully responsive using Tailwind breakpoints
- Zero custom CSS needed (DaisyUI handles it)
- Accessible by default (DaisyUI follows ARIA patterns)
- Fast load times (CSS-only, no JS shipped)
- Works with any framework or plain HTML
```
---
### HeroUI (formerly NextUI)
**Stack:** React, Tailwind CSS, React Aria
**Install:** `npm i @heroui/react`
**Site:** heroui.com
```
Build an elegant, accessible landing page using Next.js 14+, TypeScript, Tailwind CSS, and HeroUI components.
ARCHITECTURE:
- Next.js App Router with HeroUI Provider wrapping the app
- Import only needed components from @heroui/react (tree-shakeable)
- Organize: /app, /components/sections, /lib
- Use HeroUI's built-in theme system for consistent styling
DESIGN PRINCIPLES:
- Soft, modern aesthetic with rounded corners and smooth surfaces
- Light mode primary with dark mode support via HeroUI's theme toggle
- Subtle shadows and blur effects (HeroUI's built-in elevation system)
- Clean sans-serif typography, generous line heights
- Smooth transitions on all interactive elements (HeroUI provides these by default)
SECTIONS TO BUILD WITH HEROUI COMPONENTS:
1. Navbar — HeroUI Navbar with NavbarBrand, NavbarContent, NavbarItem, NavbarMenuToggle for mobile
2. Hero — large headline, descriptive subtext, Button (color="primary" size="lg") + Button (variant="bordered")
3. Social proof — Avatar group for client logos or Chip components for trust badges
4. Features — Card components in grid, each with CardHeader, CardBody, use Divider between sections
5. Product demo — Tabs component to show different product views/features interactively
6. Testimonials — Card grid with User component (avatar + name + role) inside each card
7. Pricing — Card components with Chip for "Popular" badge, Listbox for feature lists
8. FAQ — Accordion component with AccordionItem for each question
9. CTA — centered section with Input + Button combo for email capture
10. Footer — clean grid with Link components, Divider above copyright
CODE QUALITY:
- Fully responsive with HeroUI's responsive props
- Accessible by default (React Aria foundation)
- Server-side rendering compatible
- Optimized bundle (import individual components)
- TypeScript strict mode
```
---
### Chakra UI
**Stack:** React, Emotion (CSS-in-JS)
**Install:** `npm i @chakra-ui/react`
**Site:** chakra-ui.com
```
Build a clean, accessible landing page using Next.js 14+, TypeScript, and Chakra UI v3.
ARCHITECTURE:
- Next.js App Router with ChakraProvider at root
- Use Chakra's design tokens and theme extension for brand customization
- Organize: /app, /components/sections, /theme (custom theme config)
DESIGN PRINCIPLES:
- Clean and warm aesthetic using Chakra's polished defaults
- Consistent spacing using Chakra's space scale (4, 8, 12, 16, 20, 24)
- Typography: use Chakra's Heading and Text components
- Color mode support (light/dark) via useColorMode hook
- Accessible-first: every component follows WAI-ARIA by default
SECTIONS TO BUILD WITH CHAKRA COMPONENTS:
1. Navbar — Flex with HStack, IconButton for mobile menu, Button for CTA
2. Hero — VStack centered, Heading size="4xl", Text, HStack with two Buttons
3. Social proof — HStack/Wrap of grayscale Image components
4. Features — SimpleGrid columns={{base:1, md:3}} of VStack cards with Icon, Heading, Text
5. Stats — Stat component group with StatLabel, StatNumber, StatHelpText
6. Testimonials — SimpleGrid of Card with Avatar, Text (quote), Heading (name)
7. Pricing — SimpleGrid of Card, Badge for "Popular", List for features
8. FAQ — Accordion with AccordionItem, AccordionButton, AccordionPanel
9. CTA — Box with bg gradient, VStack centered, Heading, Text, Button
10. Footer — SimpleGrid columns={{base:2, md:4}} with VStack, Heading, Link
THEME EXTENSION:
const theme = extendTheme({
colors: { brand: { 50: '...', 500: '...', 900: '...' } },
fonts: { heading: 'Your Font', body: 'Your Font' },
components: { Button: { defaultProps: { colorScheme: 'brand' } } }
})
CODE QUALITY:
- Fully responsive using Chakra's responsive array/object syntax
- Dark mode ready with useColorModeValue
- Accessible by default
- TypeScript throughout with proper prop types
```
---
### Flowbite
**Stack:** Tailwind CSS (React, Vue, Svelte, Angular)
**Install:** `npm i flowbite flowbite-react`
**Site:** flowbite.com
```
Build a professional, conversion-optimized landing page using Next.js 14+, Tailwind CSS, and Flowbite React components.
ARCHITECTURE:
- Next.js App Router with Flowbite React components
- Import from flowbite-react (Button, Card, Navbar, Footer, etc.)
- Flowbite Tailwind plugin in tailwind.config for interactive styles
DESIGN PRINCIPLES:
- Business-professional aesthetic (clean, trustworthy, conversion-focused)
- Cool neutral palette (slate/gray) with strong brand accent
- Focus on readability and clear information hierarchy
SECTIONS TO BUILD WITH FLOWBITE COMPONENTS:
1. Navbar — Flowbite Navbar with Navbar.Brand, Navbar.Toggle, Navbar.Collapse
2. Hero — Jumbotron-style, large Heading, Button group
3. Social proof — row of client logos
4. Features — Card components in responsive grid with icon badge, title, description
5. Content — alternating image + text rows
6. Testimonials — Blockquote or Card with Rating component for stars
7. Pricing — Card with List for features, Badge for highlighted plan
8. FAQ — Accordion component
9. Newsletter CTA — TextInput + Button in a styled Banner
10. Footer — Flowbite Footer with Footer.LinkGroup, Footer.Brand, Footer.Copyright
CODE QUALITY:
- Fully responsive (components are responsive by default)
- Accessible (follows WAI-ARIA)
- Works server-side with Next.js
- Production-ready component patterns
```
---
### Preline UI
**Stack:** Tailwind CSS (HTML-first, React/Vue plugins)
**Install:** `npm i preline` + add plugin to tailwind.config
**Site:** preline.co
```
Build a sleek, modern landing page using Next.js 14+, Tailwind CSS, and Preline UI components.
ARCHITECTURE:
- Next.js App Router with Preline's Tailwind plugin
- Use Preline's HTML component patterns (class-based, minimal JS)
- Import preline JS for interactive components (dropdowns, modals, accordions)
DESIGN PRINCIPLES:
- Modern SaaS aesthetic (clean lines, airy spacing, professional)
- Neutral base (white/slate) with vibrant accent for CTAs
- Large section padding (py-16 to py-24)
SECTIONS TO BUILD WITH PRELINE PATTERNS:
1. Navbar — header component with mega menu support, sticky on scroll
2. Hero — centered or split layout, large headline with gradient text, two CTA buttons
3. Social proof — logo ticker or static row with grayscale filter
4. Features — icon + text cards in 3-column grid
5. Tabs showcase — tab component showing different product features per tab
6. Testimonials — grid or slider of testimonial cards
7. Pricing — pricing table pattern with toggle for monthly/annual
8. Stats — counter section with large numbers + labels
9. FAQ — accordion component with smooth expand/collapse
10. CTA + Footer — email input + button CTA above multi-column footer
CODE QUALITY:
- Fully responsive with Tailwind breakpoints
- Accessible (follows ARIA standards)
- Lightweight (CSS + minimal JS)
- Works with any framework via HTML classes
```
---
### Park UI
**Stack:** React/Vue/Solid, Ark UI (headless), Tailwind CSS
**Install:** Copy-paste components (shadcn-style)
**Site:** park-ui.com
```
Build a refined, design-system-driven landing page using Next.js 14+, TypeScript, Tailwind CSS, and Park UI components.
ARCHITECTURE:
- Next.js App Router with Park UI components (Ark UI + Tailwind CSS)
- Copy-paste component installation (like shadcn)
- Organize: /app, /components/ui (Park UI primitives), /components/sections
DESIGN PRINCIPLES:
- Polished and systematic (design-system quality)
- Neutral, sophisticated palette with deliberate accent usage
- Consistent spacing and sizing via Park UI's token scale
- Light/dark mode via Park UI's built-in theme tokens
SECTIONS TO BUILD WITH PARK UI COMPONENTS:
1. Navbar — navigation components, clean horizontal layout, mobile drawer
2. Hero — Heading + Text with proper token sizing, Button pair (solid + outline)
3. Social proof — subtle logo row with muted styling
4. Features — Card components in grid with Icon, Heading, Text
5. Tabs showcase — Tabs component for interactive product feature display
6. Testimonials — Card-based layout with Avatar + quote
7. Pricing — Card grid with Badge for recommended tier
8. FAQ — Accordion component (Ark UI primitive, styled via Park UI)
9. CTA — centered section with Input + Button
10. Footer — grid layout with Link groups and Separator
CODE QUALITY:
- Fully responsive and accessible
- Ark UI headless primitives for rock-solid accessibility
- TypeScript strict mode
- Component-per-section architecture
```
---
### Origin UI
**Stack:** React, Tailwind CSS, shadcn/ui foundation
**Install:** Copy-paste (400+ free components)
**Site:** originui.com
```
Build a polished, component-rich landing page using Next.js 14+, TypeScript, Tailwind CSS, and Origin UI components (built on shadcn/ui).
ARCHITECTURE:
- Next.js App Router with shadcn/ui base + Origin UI component variants
- Copy-paste Origin UI components into /components/ui
- Origin UI extends shadcn patterns with 400+ variant options
DESIGN PRINCIPLES:
- Professional and versatile (many style variants per component)
- Pick a cohesive subset of variants that match your brand
- Consistent spacing and typography aligned with shadcn's token system
- Focus on component quality and consistency over flashy animation
SECTIONS TO BUILD:
1. Navbar — choose from Origin UI's multiple navbar variants
2. Hero — select a hero variant (centered, split, with background pattern)
3. Social proof — logo cloud variant
4. Features — pick feature section variant (grid, list, bento, with images)
5. Content — alternating image/text sections
6. Testimonials — choose testimonial variant (cards, quotes, carousel)
7. Pricing — select pricing table variant (simple, comparison, with toggle)
8. FAQ — accordion variant
9. CTA — pick CTA variant (centered, with input, split layout)
10. Footer — choose footer variant (simple, multi-column, with newsletter)
CODE QUALITY:
- Fully responsive (all components are mobile-first)
- Accessible (shadcn/ui + Radix UI foundation)
- TypeScript throughout
- Minimal dependencies
```
---
### Headless UI
**Stack:** React or Vue, Tailwind CSS (by Tailwind Labs)
**Install:** `npm i @headlessui/react`
**Site:** headlessui.com
```
Build a custom-designed, fully accessible landing page using Next.js 14+, TypeScript, Tailwind CSS, and Headless UI for interactive components.
ARCHITECTURE:
- Next.js App Router with Tailwind CSS for all styling
- Headless UI only for interactive behavior (menus, dialogs, tabs, disclosure, transitions)
- All visual design is custom Tailwind — Headless UI provides zero styling
- Organize: /app, /components/ui (custom-styled wrappers), /components/sections
DESIGN PRINCIPLES:
- Completely custom aesthetic — you control every pixel
- Minimalist, editorial-quality design (think Stripe, Linear, Vercel)
- Monochrome base with ONE signature accent color
- Large whitespace, precise typography, meticulous alignment
- Every interactive element is keyboard-navigable and screen-reader friendly
SECTIONS TO BUILD:
1. Navbar — custom with Headless UI Popover for dropdowns, Disclosure for mobile menu
2. Hero — pure Tailwind, dramatic typography, clean CTAs
3. Social proof — pure Tailwind logo row
4. Features — custom card grid, optional Tab component for tabbed showcase
5. Product showcase — TabGroup for interactive product views
6. Testimonials — pure Tailwind card grid
7. Pricing — RadioGroup for plan selection, Switch for monthly/annual toggle
8. FAQ — Disclosure component for accessible expand/collapse
9. CTA — pure Tailwind centered section
10. Footer — pure Tailwind grid layout
HEADLESS UI COMPONENTS TO LEVERAGE:
- Popover — navbar dropdowns
- Disclosure — mobile menu, FAQ items
- Dialog — modals/overlays
- Transition — smooth enter/leave animations
- RadioGroup — plan selection
- Switch — toggles
- TabGroup — tabbed content
CODE QUALITY:
- 100% accessible (Headless UI's core guarantee)
- Fully responsive custom Tailwind design
- Keyboard navigation on all interactive elements
- TypeScript with proper generics
- Zero design dependencies — total creative control
```
---
### Cult UI
**Stack:** React, Next.js, Tailwind CSS, shadcn/ui foundation
**Install:** Copy-paste or CLI
**Site:** cult-ui.com
```
Build a modern, AI-forward landing page using Next.js 14+, TypeScript, Tailwind CSS, and Cult UI components.
ARCHITECTURE:
- Next.js App Router with Cult UI components (extends shadcn/ui)
- Full-stack ready (Cult UI includes backend-aware patterns)
- Use Cult UI's AI-specific blocks if building an AI product
DESIGN PRINCIPLES:
- Tech-forward, developer-centric aesthetic
- Dark mode primary with high-contrast accents
- Modern gradients and subtle glow effects
- Clean code-like typography (mono fonts for accents)
- Purposeful animation that communicates AI/tech sophistication
SECTIONS TO BUILD:
1. Navbar — modern floating nav with glass effect
2. Hero — bold headline with animated text effect, live demo or interactive element, gradient mesh background
3. Features — AI blocks for feature cards, animated icons or micro-interactions
4. Live demo — interactive component showing product in action
5. Code showcase — syntax-highlighted code examples using Cult UI's code block components
6. Testimonials — modern card layout with subtle animation
7. Pricing — clean tier comparison with feature matrix
8. Open source / community — GitHub stars counter, contributor avatars
9. CTA — compelling action section with email input or waitlist signup
10. Footer — developer-friendly footer with docs links, API reference, status page
CODE QUALITY:
- Full-stack patterns (API routes + client components)
- TypeScript strict mode throughout
- Accessible interactive components
- Production-ready architecture patterns
```

View file

@ -1,30 +0,0 @@
{
"generated_at": "2026-08-15T13:06:46.441Z",
"achievements": [
"scheduler-core spec: extracted xce_sched_core_t as a controller-I/O-free scheduling engine, implemented the builtin_fcfs picker plugin behind a stable xce_job_picker_iface_t, and wired it through 15+ rounds of TDD with a link-time isolation test (test_core_isolation.c) proving zero controller coupling",
"scheduler-core: built a full session-replay subsystem (recording.c/recording_events.c/recording_session.c) with hardened path validation that closed two real CWE-59 symlink/hardlink-alias vulnerabilities (found and fixed across 3 review rounds), plus a simulation-mode replay engine (sim.c/sim_loop.c/sim_verify.c) and an interactive REPL debugger (repl.c/repl_break.c/repl_info.c/repl_params.c) with breakpoints and equivalence testing against real recorded sessions",
"scheduler-core: added OPCode_schedAdmin end-to-end — scheduler-side handler, controller-side dispatch/timeout, and xceadmin CLI (`scheduler record start/stop/status`, `scheduler dump`) — closing the loop for remote recording control",
"component-failover-adoption: unified the scheduler's multi-candidate connect/rejoin pipeline (decision-publish retry-until-success, decision redelivery, backpressure handling) and rewrote the apiserver's Go-side candidate/reconnect logic (candidates.go, clib.go, reconnect.go, heartbeat.go) with 100+ new Go unit tests, all verified against real xce-build.sh ASAN builds",
"Documentation integrity sweep (Aug 8): audited and fixed 40+ drifted claims across deployment.md, api-standards.md, ha-async-io.md, and ~15 closed HA-spec design.md files (stale line citations, missing 'superseded by' notes tying old specs to raft-drive-async/ha-commit-durability/config-upgrade-snapshot)",
"Landed and live-soak-verified several concrete bug fixes: bug-1462 (config-replication commit-tag clamp), bug-1454/1456 (scheduler --peers dangling-pointer regression), and multiple xceadmin config-tool correctness fixes (resource-unit mapping, config dump/build round-trip)",
"Caught a real production-blocking gap before shipping: /kiro-validate-impl on component-failover-adoption returned NO-GO because the apiserver never re-issues its application-level rejoin after the transport layer recovers from a controller failover (bug-1547/1548) — validation process worked as intended"
],
"improvements": [
"Fix bug-1548: wire apiserver's heartbeat/health-check loop to call checkAndPerformRejoin() on transport-reconnect, not only on candidate-generation change — this is the sole blocker for component-failover-adoption's GO",
"Environment fragility is now a repeat offender: bug-1439 (concurrent xce-build.sh runs clobbering the shared xce.build dir) and bug-918 (nix capnpc hash-pin drift) have each recurred 3+ times across sessions — worth a durable fix (per-invocation build dirs or a build lockfile) instead of continuing to manually retry",
"Apply the Unity nested-BEGIN/END fix (UnitySetTestFile) as a lint/CI check rather than a one-off sweep, since it was masking real test failures across ~80 controller test files until caught",
"scheduler-core's REPL/sim modules (repl_info.c, repl_break.c, sim_loop.c) are growing fast under the 800-line-per-file convention — worth a periodic split-check as the spec approaches closure"
],
"next_tasks": [
"Land the apiserver rejoin-after-reconnect fix for bug-1548 and re-run /kiro-validate-impl on component-failover-adoption to flip it to GO",
"Continue scheduler-core to closure — remaining REPL/sim equivalence coverage and a final full-suite + live-HA-soak validation pass before marking implementation-complete",
"Resume the scaffolded-but-not-started specs: ha-client-multimaster and admin-config-tool (both have requirements.md but no design/tasks yet)",
"Check status of clusterconfig-schema-split (tasks.md exists) — confirm whether it's still blocked or ready for implementation given recent config-replication changes"
],
"risks": [
"component-failover-adoption is currently NO-GO in production terms: shipping without the apiserver rejoin fix leaves a real HA failover gap (apiserver silently stops recovering after a controller election)",
"Repeated build/toolchain fragility (nix capnpc pinning, concurrent xce-build.sh races) has cost multiple sessions of lost time and produced false negatives in validation runs — this is now a recurring operational risk, not a one-off",
"The buglog has grown very large (1500+ entries) with a number of items explicitly logged as 'not fixed, out of scope, needs owner' — worth confirming these aren't silently accumulating into an unaddressed backlog",
"scheduler-core is one of the largest specs run (30+ tasks across many subagent sessions); integration risk is high where it intersects with the concurrently-evolving component-failover-adoption and HA specs — needs a dedicated cross-spec regression pass before both are declared done"
]
}

File diff suppressed because it is too large Load diff