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:
commit
3b6f53bb65
168 changed files with 20744 additions and 568495 deletions
|
|
@ -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.
|
||||
|
|
@ -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
1416
.wolf/anatomy.md
1416
.wolf/anatomy.md
File diff suppressed because it is too large
Load diff
15117
.wolf/buglog.json
15117
.wolf/buglog.json
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
445
.wolf/daemon.log
445
.wolf/daemon.log
|
|
@ -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)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"captured_at": null,
|
||||
"captures": [],
|
||||
"total_size_kb": 0,
|
||||
"estimated_tokens": 0
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"type": "module"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
7409
.wolf/memory.md
7409
.wolf/memory.md
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
534258
.wolf/token-ledger.json
534258
.wolf/token-ledger.json
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue