diff --git a/LifeOS/install/LIFEOS/DOCUMENTATION/Delegation/DelegationSystem.md b/LifeOS/install/LIFEOS/DOCUMENTATION/Delegation/DelegationSystem.md index 10a4796738..3ed38fc7a0 100755 --- a/LifeOS/install/LIFEOS/DOCUMENTATION/Delegation/DelegationSystem.md +++ b/LifeOS/install/LIFEOS/DOCUMENTATION/Delegation/DelegationSystem.md @@ -198,6 +198,87 @@ Three primitives for non-blocking work. Pick the right one: - Use `TaskStop` to cancel a monitor early - Selective filters only — never pipe raw logs. Monitors producing too many events get auto-stopped. +### Recipe: Agent Watchdog (auto-triggered by hook) + +The Pulse agent-guard hook automatically injects a watchdog reminder when background agents are spawned. The watchdog monitors tool-activity.jsonl for silence while agents are active: + +```bash +Monitor({ + description: "Agent watchdog", + persistent: true, + timeout_ms: 3600000, + command: "bun $HOME/.claude/LIFEOS/TOOLS/AgentWatchdog.ts" +}) +``` + +Alerts when no tool calls detected for 90 seconds with active agents. Rate-limited to one alert per 60 seconds. Runs for the session lifetime — covers all background agents. + +### Recipe: Deploy Monitoring + +Watch a Cloudflare Pages or Workers deploy for completion or errors: + +```bash +# Monitor wrangler deploy output (run deploy with Bash(run_in_background), then tail its output) +Monitor({ + description: "Cloudflare deploy status", + persistent: false, + timeout_ms: 300000, + command: "tail -f /tmp/deploy.log | grep --line-buffered -E '(Published|Error|Failed|SUCCESS)'" +}) +``` + +### Recipe: Pulse Log Tailing + +Watch Pulse daemon logs for errors during a debugging session: + +```bash +Monitor({ + description: "Pulse error watcher", + persistent: true, + timeout_ms: 300000, + command: "tail -f ~/.claude/Pulse/logs/pulse-stdout.log | grep --line-buffered -i -E '(error|fatal|crash|unhandled)'" +}) +``` + +### Recipe: Build/Test Watching + +Monitor a long test suite and get notified on failures: + +```bash +Monitor({ + description: "Test failure watcher", + persistent: false, + timeout_ms: 600000, + command: "tail -f /tmp/test-output.log | grep --line-buffered -E '(FAIL|ERROR|✗|AssertionError)'" +}) +``` + +### Recipe: PR/CI Status Monitoring + +Poll GitHub for CI status changes on a PR: + +```bash +Monitor({ + description: "CI status for PR #42", + persistent: true, + timeout_ms: 3600000, + command: "last_status=''; while true; do status=$(gh pr checks 42 --json state --jq '.[].state' 2>/dev/null | sort -u | tr '\\n' ','); if [ \"$status\" != \"$last_status\" ]; then echo \"CI: $status\"; last_status=\"$status\"; fi; sleep 30; done" +}) +``` + +### Recipe: Security Scan Watching + +Tail security scan results for critical findings: + +```bash +Monitor({ + description: "Security scan critical findings", + persistent: false, + timeout_ms: 600000, + command: "tail -f /tmp/security-scan.log | grep --line-buffered -i 'CRITICAL'" +}) +``` + --- ## Knowledge Archive Access diff --git a/LifeOS/install/LIFEOS/DOCUMENTATION/LifeosSystemArchitecture.md b/LifeOS/install/LIFEOS/DOCUMENTATION/LifeosSystemArchitecture.md index 18f206a907..b55abd66ed 100755 --- a/LifeOS/install/LIFEOS/DOCUMENTATION/LifeosSystemArchitecture.md +++ b/LifeOS/install/LIFEOS/DOCUMENTATION/LifeosSystemArchitecture.md @@ -587,7 +587,7 @@ System file inventory by pipeline. When you modify a file, trace its pipeline to | **Security** | `LIFEOS/LIFEOS_SYSTEM_PROMPT.md` § Security Protocol (constitutional rule), `settings.json` `permissions.deny` (native harness denylist), `hooks/Safety.hook.ts` (consolidated PermissionRequest + PostToolUse on WebFetch \| WebSearch), `hooks/lib/safety-classifier.ts` (shape catalog + shell-aware classifier with single-quote pre-pass) | | **Algorithm** | `Algorithm/LATEST` → `Algorithm/v{VERSION}.md` (currently v8.4.0 — the claims restructure: Loop preamble + 15 teeth-annotated done-claims + AlgorithmNudge event layer (unified 2026-07-11: run-scoped + always-on skill-routing/late-ISA/spend; depth-directive row added v8.4.0, 2026-07-12); capabilities.md removed at v7, the system-prompt skill list is the sole capability inventory), `Algorithm/mode-detection.md`, `hooks/ISASync.hook.ts` → `MEMORY/WORK/{slug}/ISA.md`, `skills/ISA/` (canonical Scaffold/Append/Reconcile workflows); **work registry event-sourced (2026-06-10):** all `work.json` writes go through `isa-utils.writeRegistry` → field-level diff events appended to `MEMORY/STATE/work-events.jsonl` (`hooks/lib/work-events.ts`) → locked fold to the derived `work.json` snapshot (offset-stamped, 1MB compaction); `readRegistry` serves snapshot+suffix live views; Pulse SSE triggers off `fs.watch` on STATE with the 100ms poll as fallback; model choice is per-dispatch judgment (2026-07-11 baseline); `EFFORT_MODEL` in `LIFEOS/TOOLS/models.ts` remains the `Inference.ts --level` dial | | **Memory** | `hooks/WorkCompletionLearning.hook.ts`, `hooks/SatisfactionCapture.hook.ts` (RelationshipMemory hook deleted 7.0.0 — dead code), `Tools/KnowledgeHarvester.ts` → `MEMORY/KNOWLEDGE/`, `MEMORY/LEARNING/`; `Tools/SessionHarvester.ts --mine` → `KNOWLEDGE/_harvest-queue/`; `Tools/MemoryRetriever.ts` (BM25 retrieval over typed-item corpus including `_MEMORY.md` hot-layer files), `Tools/KnowledgeGraph.ts` (graph navigation) — read-only. **Autonomic loop (2026-05):** `hooks/MemoryTurnStart.hook.ts` (UserPromptSubmit) + `hooks/MemoryReviewFire.hook.ts` (Stop; cadence merged 2026-07-11) drive `Tools/MemoryReviewer.ts` on cadence (turn≥8 ∧ minutes≥30 ∧ idle≥2). Reviewer emits typed items routed by `Tools/MemorySystem.ts` (single `add(item)` API) over the `Tools/MemoryTypes.ts` registry; `Tools/MutationTier.ts` gates by tier A/B/C/D. Tier-C proposals enqueue to `MEMORY/OBSERVABILITY/pending-proposals.jsonl`; `PULSE/lib/telegram-proposals.ts` + `PULSE/modules/telegram.ts` surface them as `yes/no/edit #id` Telegram replies. `Tools/MemoryStatus.ts` is the read-only `kai status` CLI. **kb-v3 knowledge schema (2026-07-05):** `Tools/KnowledgeSchema.ts` is the pure-data SoT for the KNOWLEDGE archive object-schema (`person\|company\|idea\|blog\|research` — distinct from the write-registry above) + body-safe parse/normalize/validate; `Tools/KnowledgeLint.ts` validates conformance (envelope % vs per-type completeness); `Tools/MigrateKnowledge.ts` migrated ~4,400 notes onto it (body-byte-preserving, idempotent, dry-run default); `Tools/KnowledgeQuery.ts` (`kb query`) filters/sorts on the now-consistent typed fields; `Tools/GenerateKnowledgeSchemaDoc.ts` regenerates `MEMORY/KNOWLEDGE/_schema.md` from the schema; `MemorySystem.renderInitialNote` emits the kb-v3 envelope so new autonomic notes are born conformant. | -| **Router** (RETIRED 2026-07-11) | Classify → route-effort stages retired 2026-07-11 with the mode/tier abolition (`TheRouter.hook.ts` deleted; MINIMAL/NATIVE/ALGORITHM + E1–E5 gone, no successor classifier). **Surviving model routing:** `LIFEOS/TOOLS/models.ts` `EFFORT_MODEL` maps level→model (max→fable / high→opus / medium→sonnet / low→haiku; `LEVEL_TO_HARNESS_EFFORT`; cross-vendor pins; egress-class ceilings) → **dispatch** via `model` param on `Agent()` / `Workflow agent()`, injected by `hooks/AgentInvocation.hook.ts` on unspecified dispatches. `LIFEOS/TOOLS/Inference.ts` applies model selection to utility inference, and is the genuine `max`/Fable carrier (subprocess spawns `claude --model claude-fable-5`; Agent `model:fable` dispatch downgrades to Opus). It verifies the executed model against the JSON envelope's `modelUsage` and logs downgrades to `MEMORY/OBSERVABILITY/model-verification.jsonl` (v6.29.0 — reports what RAN, not what was requested). Full doc (history only): `LIFEOS/DOCUMENTATION/Router/RouterSystem.md` | +| **Router** (RETIRED 2026-07-11) | Classify → route-effort stages retired 2026-07-11 with the mode/tier abolition (`TheRouter.hook.ts` deleted; MINIMAL/NATIVE/ALGORITHM + E1–E5 gone, no successor classifier). **Surviving model routing:** `LIFEOS/TOOLS/models.ts` `EFFORT_MODEL` maps level→model (max→fable / high→opus / medium→sonnet / low→haiku; `LEVEL_TO_HARNESS_EFFORT`; cross-vendor pins; egress-class ceilings) → **dispatch** via `model` param on `Agent()` / `Workflow agent()`, injected by `hooks/AgentInvocation.hook.ts` on unspecified dispatches. `LIFEOS/TOOLS/Inference.ts` applies model selection to utility inference and is the genuine `max`/Fable carrier with executed-model verification — mechanism: `LIFEOS/DOCUMENTATION/Router/RouterSystem.md` § Carrier + verification (also the full Router doc, history only). | | **Hooks** | `hooks/*.hook.ts`, `hooks/handlers/*.ts`, `hooks/lib/*.ts`, `settings.json` | | **Observability** | `hooks/EventLogger.hook.ts` (consolidated 2026-07-11 — absorbed ToolActivityTracker, ToolFailureTracker, SkillExecutionLog, ConfigAudit, StopFailureHandler; appends directly via `fs.appendFileSync`) → `MEMORY/OBSERVABILITY/*.jsonl` | | **Pulse** | `Pulse/pulse.ts` (port 31337), `Pulse/modules/{observability,hooks,wiki,imessage,telegram,user-index,da,work,bunker}.ts`, `Pulse/PULSE.toml`, `Pulse/Observability/src/`, `Pulse/Assistant/module.ts` | diff --git a/LifeOS/install/LIFEOS/DOCUMENTATION/Notifications/NotificationSystem.md b/LifeOS/install/LIFEOS/DOCUMENTATION/Notifications/NotificationSystem.md index 19b722380a..4c4cb46e60 100755 --- a/LifeOS/install/LIFEOS/DOCUMENTATION/Notifications/NotificationSystem.md +++ b/LifeOS/install/LIFEOS/DOCUMENTATION/Notifications/NotificationSystem.md @@ -124,6 +124,35 @@ curl -s -X POST http://localhost:31337/notify \ --- +## Long-form Narration + +The same `/notify` endpoint reads longer text aloud — "read this to me", "narrate this", "speak this", "perform this". It is a Pulse route, not a callable tool, so it lives here rather than in the tool index. + +```bash +# Single narration segment +curl -X POST http://localhost:31337/notify \ + -H "Content-Type: application/json" \ + -d '{ + "message": "Your text here", + "voice_id": "$ELEVENLABS_VOICE_ID", + "title": "Voice Narrative" + }' + +# Pause between segments +sleep 2 +``` + +**Narration settings:** +- **Voice ID:** `ELEVENLABS_VOICE_ID` environment variable +- **Stability:** 0.55 (natural variation in storytelling) +- **Similarity Boost:** 0.85 (maintains authentic sound) +- **Max segment:** 450 characters — split longer text +- **Pause between:** 2 seconds, for storytelling flow + +Pulse must be running; the voice handler lives at `~/.claude/LIFEOS/PULSE/VoiceServer/voice.ts` on port 31337, and calls ElevenLabs under the hood. + +--- + ## Copy-Paste Templates ### Template A: Skills WITH Workflows diff --git a/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/FlatTools.md b/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/FlatTools.md new file mode 100644 index 0000000000..161fd163af --- /dev/null +++ b/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/FlatTools.md @@ -0,0 +1,143 @@ +# Flat Tools — generated registry + +> Generated by `LIFEOS/TOOLS/ToolsRegistry.ts` from the tools' own headers — do not hand-edit. +> Regenerate: `bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts`. Contract: `Tools.md` § Placement doctrine. + +134 tools in the LIFEOS/TOOLS root. 32 headers short of the element contract — `ToolsRegistry.ts --audit` lists them. + +| Tool | Class | Purpose | Docs | +|---|---|---|---| +| ActivityParser.ts | cli | Parse session activity for LifeOS repo update documentation | header | +| AddBg.ts | cli | — | ~/.claude/LIFEOS/DOCUMENTATION/Tools/Tools.md | +| AgentWatchdog.ts | cli | Monitor script for detecting hung background agents | header | +| AlgoPhase.ts | cli | atomic Algorithm phase emitter | header | +| algorithm.ts | cli | — | header | +| AlgorithmPhaseReport.ts | cli | Writes algorithm state to algorithm-phase.json | header | +| ApproveCurrentStateEntries.ts | cli | review and commit proposed CURRENT_STATE entries. | header | +| ArchDecisionHarvest.ts | cli | Harvest architecture decisions from ISA files into LifeosSystemArchitecture.md | header | +| ArchitectureSummaryGenerator.ts | cli | Generate LIFEOS_ARCHITECTURE_SUMMARY.md from source docs | header | +| Banner.ts | cli | — | header | +| BannerMatrix.ts | cli | Matrix Digital Rain LifeOS Banner | header | +| BannerNeofetch.ts | cli | Modern Neofetch-Style LifeOS Banner | header | +| BannerPrototypes.ts | cli | — | header | +| BannerRetro.ts | cli | Retro BBS/DOS Terminal Banner for LifeOS | header | +| BannerTokyo.ts | cli | — | header | +| BillingPathAssertion.ts | cli | verify which credential path a `claude -p` run actually used. | header | +| BlogDiscovery.ts | cli | harvest, topic-score, and queue small/indie blogs for the Feed | header | +| BookmarkSweep.ts | daemon | Periodic sweep that turns X bookmarks into UL ideas. | header | +| BudgetCheck.ts | cli | backpressure against always-on context re-accretion. | header | +| CarrierProbe.ts | cli | end-to-end probe of the Agent-dispatch carrier reality. | header | +| CheckFileBoundary.ts | cli | on-demand SystemFileGuard check. | header | +| Checkpoint.ts | cli | inspection and PREVIEW-ONLY rollback CLI for ISC checkpoints | header | +| CodexUpdate.ts | cli | Keep the OpenAI Codex CLI current. | header | +| CommitmentDetect.ts | cli | Haiku-backed detector for outbound commitments. | header | +| CommitmentLog.ts | cli | Manual capture CLI. Creates a GitHub Issue tagged Type:commitment. | header | +| CommitmentSweep.ts | daemon | Daily sweep over Type:commitment issues. | header | +| ComputeGap.ts | cli | computed view of Current→Ideal delta per dimension. | header | +| ContextAudit.ts | cli | read-only quality audit for constitutional context files. | header | +| CostTracker.ts | cli | Anthropic cost observability for LifeOS | header | +| CrossVendorAudit.ts | cli | Forge audit-mode tool (cross-vendor audit; formerly Cato) | header | +| current-work-dir.ts | cli | — | header | +| DAGrowth.ts | cli | — | header | +| DAInterview.ts | cli | — | header | +| DASchedule.ts | cli | CLI for managing DA scheduled tasks | header | +| DeriveDenyHashes.ts | cli | turn the principal's PRIVATE corpus into a SALTED-HASH | header | +| DerivedSync.ts | cli | Detect manual USER source edits and regenerate derived LifeOS artifacts. | header | +| DocCheck.ts | cli | Documentation integrity verifier | header | +| Doctor.ts | cli | LifeOS capability prober and manifest writer. | header | +| DoctrineReplay.ts | cli | sampled doctrine-coverage replay over archived ISAs. | header | +| extract-transcript.py | cli | — | header | +| ExtractTranscript.ts | cli | — | header | +| FailureCapture.ts | cli | Full Context Failure Analysis System | header | +| FeatureRegistry.ts | cli | — | header | +| ForgeProgress.ts | cli | — | header | +| FreshnessCache.ts | cli | render-path cache for the statusline FRESH section. | header | +| GenerateKnowledgeSchemaDoc.ts | cli | regenerate `MEMORY/KNOWLEDGE/_schema.md` FROM | header | +| GenerateTelosSummary.ts | cli | Reads TELOS source files and generates a compressed | header | +| GetCounts.ts | cli | Single Source of Truth for LifeOS System Counts | header | +| GetTranscript.ts | cli | Extract transcript from YouTube video | header | +| gmail.ts | cli | — | header | +| Grok.ts | cli | xAI Grok search client (Agent Tools API) | header | +| GrokAudit.ts | cli | Grok cross-vendor audit voice (xAI lineage) | header | +| HarvestExecutor.ts | cli | — | header | +| HealthSnapshot.ts | cli | — | header | +| HealthSync.ts | cli | — | header | +| Inference.ts | cli | Unified inference tool with four run levels | header | +| InstallBlogDiscovery.ts | installer | Materialize com.lifeos.blogdiscovery.plist.template and bootstrap it. | header | +| InstallBookmarkSweep.ts | installer | Materialize com.lifeos.bookmarksweep.plist.template and bootstrap it. | header | +| InstallCodexUpdate.ts | installer | Materialize com.lifeos.codexupdate.plist.template and bootstrap it. | header | +| InstallCommitmentSweep.ts | installer | Materialize com.lifeos.commitmentsweep.plist.template and bootstrap. | header | +| InstallConveyorRunner.ts | installer | Materialize com.lifeos.conveyor-runner.plist.template and bootstrap it. | header | +| InstallConveyorWatcher.ts | installer | Materialize com.lifeos.conveyor-watcher.plist.template and bootstrap it. | header | +| InstallDerivedSync.ts | installer | Materialize com.lifeos.derivedsync.plist.template and bootstrap it. | header | +| InstallHealthSync.ts | installer | Materialize com.lifeos.healthsync.plist.template and bootstrap it. | header | +| InstallUsageAggregator.ts | installer | Materialize com.lifeos.usage-aggregator.plist.template | header | +| InstallWorkSweep.ts | installer | Materialize com.lifeos.worksweep.plist.template and bootstrap it. | header | +| IntegrityCheck.ts | cli | Full-system integrity orchestrator for the LifeOS system. | header | +| IntegrityMaintenance.ts | cli | Background script for system integrity and update documentation | header | +| InterviewIdealState.ts | cli | agenda tracker for seeding IDEAL_STATE + preference files. | header | +| InterviewScan.ts | cli | comprehensive completeness scanner across unified TELOS | header | +| IsaReconcile.ts | cli | Sweep every MEMORY/WORK//ISA.md and reconcile work.json. | header | +| ISARender.ts | cli | Render an ISA.md to a branded sibling ISA.html. | header | +| KnowledgeGraph.ts | cli | Associative graph navigation over LifeOS's knowledge archive | header | +| KnowledgeHarvester.ts | cli | Harvest knowledge from LifeOS memory into KNOWLEDGE/ | header | +| KnowledgeLint.ts | cli | validates the Knowledge Archive against the kb-v3 contract | header | +| KnowledgeQuery.ts | cli | the `kb query` surface over the Knowledge Archive. | header | +| KnowledgeSchema.ts | cli | the single source of truth for the LifeOS Knowledge Archive | header | +| LearningPatternSynthesis.ts | cli | Aggregate ratings into actionable patterns | header | +| lifeos.ts | cli | the LifeOS launcher CLI (aliased as `k`) | header | +| LifeosConfig.ts | cli | typed user-config loader. | header | +| LifeosLogo.ts | cli | — | header | +| LifeosUpgrade.ts | cli | idempotent migration runner + diagnostic harness for LifeOS rebuild. | header | +| LoadSkillConfig.ts | cli | Shared utility for loading skill configurations with user customizations | header | +| MemoryGraph.ts | cli | a first-class graph layer over the WHOLE LifeOS memory system. | header | +| MemoryHealthCheck.ts | cli | Autonomic memory subsystem health check. | header | +| MemoryInsights.ts | cli | `kai insights` CLI for autonomic memory delta view. | header | +| MemoryRestore.ts | cli | recover a hot-layer memory file from the per-write snapshot | header | +| MemoryRetriever.ts | cli | Compressed context retrieval over LifeOS's knowledge archive | header | +| MemoryReviewer.ts | cli | single-pass autonomic reviewer for the typed-item memory system. | header | +| MemoryStatus.ts | cli | read-only viewer for LifeOS's memory subsystem. | header | +| MemorySystem.ts | cli | single public API for the LifeOS memory subsystem. | header | +| MemoryTypes.ts | cli | frozen type registry for LifeOS's unified memory subsystem. | header | +| MemoryWriter.ts | cli | set-overwrite writer for PRINCIPAL_MEMORY.md / DA_MEMORY.md. | header | +| MergeSettings.ts | cli | — | header | +| MigrateApprove.ts | cli | review and commit proposed migration chunks from external | header | +| MigrateContextFreshness.ts | cli | adds pai-freshness-v1 frontmatter to constitutional | header | +| MigrateKnowledge.ts | cli | one-time (idempotent) migration of the Knowledge Archive | header | +| MigrateScan.ts | cli | intake content from external sources (other LifeOS installs, other | header | +| MigrateTelosFreshness.ts | cli | one-shot migration that adds the freshness convention | header | +| models.ts | cli | single source of truth for model IDs across LifeOS | header | +| MutationTier.ts | cli | four-tier autonomic-mutation boundary classifier. | header | +| NeofetchBanner.ts | cli | LifeOS System Banner in Neofetch Style | header | +| OpenRouter.ts | cli | frontier open-model access via the OpenRouter broker | header | +| PangramScore.ts | cli | score text for AI-detectability via the Pangram API. | header | +| PerplexitySearch.ts | cli | Perplexity Sonar API web research client | header | +| PipelineOrchestrator.ts | cli | — | header | +| PiSync.sh | cli | bring ~/.pi/agent/ in line with current ~/.claude/LIFEOS/ | header | +| PreviewMarkdown.ts | cli | — | header | +| ProposalGC.ts | cli | self-healing garbage collection for auto-appended memory proposals. | header | +| ProposeCurrentStateEntry.ts | cli | Pollers and _LIFELOG extractors enqueue proposals here. | header | +| Recommend.ts | cli | recency-aware picker for restaurants, movies, books. | header | +| ReferenceCheck.ts | cli | Full-surface reference validator for the LifeOS system. | header | +| RemoveBg.ts | cli | — | header | +| RetagSweepTypes.ts | cli | one-shot backfill of canonical Type:* labels onto the | header | +| SecretScan.ts | cli | Secret Scanning CLI | ~/.claude/skills/_LIFEOS/Workflows/SecretScanning.md | +| Services.ts | cli | the one-shot control surface for every LifeOS background service. | header | +| SessionHarvester.ts | cli | Extract learnings from Claude Code session transcripts | header | +| SessionProgress.ts | cli | — | header | +| SessionRename.ts | cli | rename a session in work.json and session-names.json. | header | +| SettingsBackport.ts | cli | propagate direct edits on the GENERATED settings.json | header | +| SplitAndTranscribe.ts | cli | — | header | +| SyncIdentityToSettings.ts | cli | Mirror PRINCIPAL_IDENTITY.md frontmatter | header | +| TelosFreshness.ts | cli | canonical reader/writer for TELOS staleness signal. | header | +| TlpArchive.ts | cli | scrape The Last Psychiatrist blog (thelastpsychiatrist.com) | header | +| ToolsRegistry.ts | cli | regenerate the flat-tool registry (FlatTools.md) from headers. | ~/.claude/LIFEOS/DOCUMENTATION/Tools/Tools.md | +| TranscriptParser.ts | cli | Claude transcript parsing utilities | header | +| UpdateLifeosState.ts | cli | Writes LIFEOS_STATE.json with per-dimension pct scores read by | header | +| UpdateModels.ts | cli | drift check + safe registry bump for model IDs | header | +| UsageAggregator.ts | cli | roll Claude Code usage into a DURABLE per-day store. | header | +| WisdomCrossFrameSynthesizer.ts | cli | Extract shared principles across Wisdom Frames | header | +| WisdomDomainClassifier.ts | cli | Route requests to relevant Wisdom Frames | header | +| WisdomFrameUpdater.ts | cli | Update Wisdom Frames with new observations | header | +| WorkSweep.ts | daemon | Periodic sweep that catches what event-driven hooks miss. | header | +| YouTubeApi.ts | cli | YouTube Data API v3 client | header | diff --git a/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/Tools.md b/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/Tools.md index 3e33ad626c..621565e8c7 100755 --- a/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/Tools.md +++ b/LifeOS/install/LIFEOS/DOCUMENTATION/Tools/Tools.md @@ -1,16 +1,87 @@ --- -version: 1.8.0 +version: 1.9.0 --- -# LifeOS Tools - CLI Utilities Reference +# LifeOS Tools — the tool index > CLI-first is how the Life OS stays deterministic (`LIFEOS/DOCUMENTATION/LifeOs/LifeOsThesis.md`): the hill-climb's moves are code you can script, test, and trust — prompts orchestrate, code executes. -This file documents single-purpose CLI utilities that have been consolidated from individual skills. These are pure command-line tools that wrap APIs or external commands. +This is the index of LifeOS **tools** — the counterpart to `skills/`. A tool is a thing an agent or the principal *invokes*. Skills carry judgment; tools carry determinism. -**Philosophy:** Simple utilities don't need separate skills. Document them here, execute them directly. +## Placement doctrine -**Model:** Following the `Tools/fabric/` pattern - 242+ Fabric patterns documented as utilities rather than individual skills. +The one rule set for what lands in this file and in what shape. Generated flat-tool registry: `FlatTools.md`. + +### What belongs + +A **callable tool** — something you can run: + +| Tier | What it is | Registration | +|---|---|---| +| **Flat tool** | a single file (`*.ts`, `*.sh`, `*.py`) in the `LIFEOS/TOOLS` root | exhaustive in generated `FlatTools.md`; **selective** here | +| **Directory tool** | a subsystem under `LIFEOS/TOOLS//` with its own `package.json` / `src/` / `test/` | **exhaustive over the adopted set** — one pointer section per adopted tool | +| **External tool** | a binary or app LifeOS doctrine depends on but does not ship (`trufflehog`, `rtk`) | **exhaustive** here — nothing else indexes it | + +**The adopted set.** A directory tool registers here when the instance has adopted it — decided that this instance runs it — never merely because the directory exists on disk. An unadopted directory under `LIFEOS/TOOLS/` gets no section until that call is made. + +**The selective flat policy.** A flat tool earns a chapter here only when invocation needs more than one line: non-obvious flags, a small fixed dimension worth a table, or a routing imperative ("use this, never the raw SDK"). Everything else is a row in `FlatTools.md`, generated from the tool's own header and never hand-edited. Absence from this file is not absence from the system — check `FlatTools.md` first. + +### What does not belong + +| Not a tool | Its home | +|---|---| +| Hooks | `../Hooks/HookSystem.md` § Quick Reference Card | +| HTTP routes and Pulse modules | the owning subsystem's doc (`../Pulse/PulseSystem.md`, `../Notifications/NotificationSystem.md`) | +| Claude Code harness primitives (`Monitor`, `Agent`, `Workflow`) | `../Delegation/DelegationSystem.md` § Async Primitives | +| Changelogs, version history, design-decision archaeology | the tool's own `ISA.md` § Decisions / § Changelog | +| "Re-add after every update" inventories for a local deviation | `USER/CUSTOMIZATIONS/` | +| Skills | `skills/` + `../Skills/SkillSystem.md` | + +### The entry template + +Same skeleton at every tier; the tier decides which rows are present. + +```text +H2 (H3 when nested inside a chapter) +**Location:** + +**Usage:** + +**When to use:** +**Depth:** +**Deps:** +``` + +- **Flat tool** — identity · Location · what-it-is · Usage · optional table · When-to-use · Deps. Depth row only if the tool has its own doc. +- **Directory tool** — identity · Location · what-it-is · the stable entrypoint · **Depth** (mandatory: README or ISA). No file inventory, no module list, no migration history — the tool's own docs own all of it. +- **External tool** — identity · Location or install line · what-it-is · Usage · When-to-use. No Depth row; there is nothing local to point at. + +### Anti-bloat rules + +- **A pointer, not a mini-PRD.** Location, one-line identity, how to run it, one depth pointer. Working ceiling, every tier: ~15 non-blank lines per entry (a pointer-only section lands well under it). Longer means the depth belongs in the tool's own docs — the structural bans below, not the line count, are the operative rule. +- **No archaeology.** No design-corpus citations, no migration lists, no decision codes, no test counts, no per-module file inventories, no "born from" origin stories, no dated remediation paragraphs. An entry describes the tool as it is *now*. +- **One entry per tool, edited in place.** A rewrite replaces the entry; it never appends a "current state" block under a stale one. +- **Tables earn their place.** At most one per entry, and only for a small fixed dimension. + +### The flat-tool header + +`FlatTools.md` is rendered from each tool's leading comment block, so that header is the registry's source of truth. Three elements, machine-checked by `bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts --audit`: + +- **Identity line** — ``, separated by an em dash or ` - `. The text after the separator becomes the registry's Purpose cell. +- **Purpose prose** — at least one line beyond the identity line saying what the tool does. +- **Usage block, or a library marker** — a `Usage:` block for anything callable; `not a CLI` or `Consumed by:` for a module that is imported rather than run. + +Optional and judgment-scoped, so it is not machine-enforced: `@see ` naming the tool's governing doc, which becomes the registry's Docs cell. + +### Adding a tool + +1. **Class it** — flat · directory · external. Directory tools are normal, not an exception; the older "keep the directory flat — NO subdirectories" rule governed single-file utilities only and never applied to subsystems. +2. **Flat** — Title-Case filename in the `LIFEOS/TOOLS` root (`GetTranscript.ts`, not `get-transcript.ts`), header block per above, then `bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts` to regenerate `FlatTools.md`. Add a chapter here only under the selective policy. +3. **Directory** — its own doc set (a README, plus an ISA where the tool has a design contract), then one pointer section here. +4. **External** — an entry here with its install line. +5. **Close the build** — run the tool from its final path, and leave `ToolsRegistry.ts --check` clean. + +**Don't create a separate skill** if the entire functionality is just a CLI command with parameters. --- @@ -22,7 +93,7 @@ This file documents single-purpose CLI utilities that have been consolidated fro Single inference tool with four run levels for different speed/capability trade-offs — the same four-level abstraction as `EFFORT_MODEL` in `models.ts` (max→fable, high→opus, medium→sonnet, low→haiku; one-line mapping edit on a lineup change). -**Executed-model verification (v6.29.0).** `--level max` genuinely runs Fable (spawns `claude --model claude-fable-5`), unlike an `Agent(model:fable)` dispatch which downgrades to Opus — so this is the real Fable carrier for E4/E5 reasoning. Every run reads the executed model back from the JSON envelope's `modelUsage` (`verifyExecutedModel` — filters Claude Code's per-turn background haiku pass, then takes the highest-output model as the answer's author and checks its family; presence alone can't tell a tiny classifier pass from real authorship). The result carries `executedModel` + `modelDowngraded`; the CLI prints a `[model] requested=… → executed=…` line to stderr (stdout stays the clean answer); any downgrade is logged to `MEMORY/OBSERVABILITY/model-verification.jsonl`. The tool reports what RAN, never what it requested. +`--level max` is the genuine Fable carrier, and every run verifies the model that actually executed — mechanism and rationale: `LIFEOS/DOCUMENTATION/Router/RouterSystem.md` § Carrier + verification (that doc's banner retires the classify/route-effort stages; select-model, which this section applies, is the stage that survived). **Usage:** ```bash @@ -252,47 +323,9 @@ bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts find architecture --- -## Voice Server API - Generate Voice Narration - -**Location:** Voice server at `http://localhost:31337/notify` - -Send text to the voice server running on localhost for TTS using a configured voice clone. - -**Usage:** -```bash -# Single narration segment -curl -X POST http://localhost:31337/notify \ - -H "Content-Type: application/json" \ - -d '{ - "message": "Your text here", - "voice_id": "$ELEVENLABS_VOICE_ID", - "title": "Voice Narrative" - }' - -# Pause between segments -sleep 2 -``` - -**Voice Configuration:** -- **Voice ID:** Set via `ELEVENLABS_VOICE_ID` environment variable -- **Stability:** 0.55 (natural variation in storytelling) -- **Similarity Boost:** 0.85 (maintains authentic sound) -- **Server:** `http://localhost:31337/notify` -- **Max Segment:** 450 characters -- **Pause Between:** 2 seconds +## Voice narration — not a tool -**When to Use:** -- "read this to me" -- "voice narrative" -- "speak this" -- "narrate this" -- "perform this" - -**Technical Details:** -- Pulse must be running (voice handler lives at `~/.claude/LIFEOS/PULSE/VoiceServer/voice.ts`, port 31337) -- Segments longer than 450 chars should be split -- Natural 2-second pauses between segments for storytelling flow -- Uses ElevenLabs API under the hood +`POST localhost:31337/notify` is a Pulse route, not a callable tool. Narration triggers, payload, and the tuning knobs live in `../Notifications/NotificationSystem.md` § Long-form Narration. --- @@ -493,97 +526,9 @@ rtk verify # Check installation integrity --- -## Monitor Tool — Event-Driven Background Watching - -The Monitor tool starts a background script whose stdout lines become chat notifications. Instead of polling in a loop (burning tokens), the script runs independently and wakes you when something happens. Zero token cost between events. - -**Key rules:** -- Each stdout line = one notification. Stderr goes to output file only. -- Always use `grep --line-buffered` in pipes (otherwise pipe buffering delays events). -- Poll intervals: 30s+ for remote APIs, 0.5-1s for local checks. -- Handle transient failures: `curl ... || true` in poll loops. -- Set `persistent: true` for session-length watches. Cancel with `TaskStop`. - -### Recipe: Agent Watchdog (auto-triggered by hook) - -The Pulse agent-guard hook automatically injects a watchdog reminder when background agents are spawned. The watchdog monitors tool-activity.jsonl for silence while agents are active: - -```bash -Monitor({ - description: "Agent watchdog", - persistent: true, - timeout_ms: 3600000, - command: "bun $HOME/.claude/LIFEOS/TOOLS/AgentWatchdog.ts" -}) -``` - -Alerts when no tool calls detected for 90 seconds with active agents. Rate-limited to one alert per 60 seconds. Runs for the session lifetime — covers all background agents. - -### Recipe: Deploy Monitoring - -Watch a Cloudflare Pages or Workers deploy for completion or errors: - -```bash -# Monitor wrangler deploy output (run deploy with Bash(run_in_background), then tail its output) -Monitor({ - description: "Cloudflare deploy status", - persistent: false, - timeout_ms: 300000, - command: "tail -f /tmp/deploy.log | grep --line-buffered -E '(Published|Error|Failed|SUCCESS)'" -}) -``` - -### Recipe: Pulse Log Tailing - -Watch Pulse daemon logs for errors during a debugging session: - -```bash -Monitor({ - description: "Pulse error watcher", - persistent: true, - timeout_ms: 300000, - command: "tail -f ~/.claude/Pulse/logs/pulse-stdout.log | grep --line-buffered -i -E '(error|fatal|crash|unhandled)'" -}) -``` - -### Recipe: Build/Test Watching - -Monitor a long test suite and get notified on failures: - -```bash -Monitor({ - description: "Test failure watcher", - persistent: false, - timeout_ms: 600000, - command: "tail -f /tmp/test-output.log | grep --line-buffered -E '(FAIL|ERROR|✗|AssertionError)'" -}) -``` - -### Recipe: PR/CI Status Monitoring +## Monitor — not a tool -Poll GitHub for CI status changes on a PR: - -```bash -Monitor({ - description: "CI status for PR #42", - persistent: true, - timeout_ms: 3600000, - command: "last_status=''; while true; do status=$(gh pr checks 42 --json state --jq '.[].state' 2>/dev/null | sort -u | tr '\\n' ','); if [ \"$status\" != \"$last_status\" ]; then echo \"CI: $status\"; last_status=\"$status\"; fi; sleep 30; done" -}) -``` - -### Recipe: Security Scan Watching - -Tail security scan results for critical findings: - -```bash -Monitor({ - description: "Security scan critical findings", - persistent: false, - timeout_ms: 600000, - command: "tail -f /tmp/security-scan.log | grep --line-buffered -i 'CRITICAL'" -}) -``` +`Monitor` is a Claude Code harness primitive, not something this repo ships. The rules, the pick-your-primitive table, and the recipe cookbook live in `../Delegation/DelegationSystem.md` § Async Primitives. --- @@ -600,7 +545,7 @@ Monitor({ ### Research Skill - YouTube transcripts: `GetTranscript.ts` - Audio/video transcription: `extract-transcript.py` -- Voice narration: Voice server API +- Voice narration: `../Notifications/NotificationSystem.md` § Long-form Narration ### Metrics Skill - YouTube analytics: `YouTubeApi.ts` @@ -610,38 +555,11 @@ Monitor({ --- -## Adding New Tools - -When adding a new utility tool to this system: - -1. **Add tool file:** Place `.ts` or `.py` file directly in `~/.claude/LIFEOS/TOOLS/` - - Use **Title Case** for filenames (e.g., `GetTranscript.ts`, not `get-transcript.ts`) - - Keep the directory flat - NO subdirectories - -2. **Document here:** Add section to this file with: - - Tool location (e.g., `~/.claude/`) - - Usage examples - - When to use triggers - - Environment variables (if any) - -3. **Update `CLAUDE.md` routing table:** Ensure TOOLS.md is referenced in the documentation index - -4. **Test:** Verify tool works from new location - -**Don't create a separate skill** if the entire functionality is just a CLI command with parameters. - ---- - ## Deprecated Skills -The following skills have been consolidated into this Tools system: +A row belongs here only while the retired name still resolves to something in the tree — otherwise it is archaeology that makes a dead name look live. -- **Images** → `Tools/RemoveBg.ts`, `Tools/AddBg.ts` (2024-12-22) -- **VideoTranscript** → `Tools/GetTranscript.ts` (2024-12-22) -- **VoiceNarration** → Voice server API (2024-12-22) - **ExtractTranscript** → `Tools/extract-transcript.py`, `Tools/ExtractTranscript.ts` (2024-12-22) -- **YouTube** → `Tools/YouTubeApi.ts` (2024-12-22) -- **Sensitive** → `trufflehog` system tool (2024-12-22) Archived skill files have been removed. @@ -869,6 +787,7 @@ MCP tool results are truncated by default. Servers can override this by adding ` ## Related Documentation +- **Flat-tool registry**: `~/.claude/LIFEOS/DOCUMENTATION/Tools/FlatTools.md` (generated — every tool in the `LIFEOS/TOOLS` root) - **Architecture**: `~/.claude/LIFEOS/DOCUMENTATION/LifeosSystemArchitecture.md` (master architecture reference) - **CLI Tools**: `~/.claude/LIFEOS/DOCUMENTATION/Tools/Cli.md` (Algorithm CLI, Arbol CLI) diff --git a/LifeOS/install/LIFEOS/TOOLS/AddBg.ts b/LifeOS/install/LIFEOS/TOOLS/AddBg.ts index a04dee866f..27eda2ce65 100755 --- a/LifeOS/install/LIFEOS/TOOLS/AddBg.ts +++ b/LifeOS/install/LIFEOS/TOOLS/AddBg.ts @@ -4,13 +4,13 @@ * add-bg - Add Background Color CLI * * Add a solid background color to transparent PNG images. - * Part of the Images skill for LifeOS system. + * Companion to RemoveBg.ts; driven by the Art skill's background workflows. * * Usage: * add-bg input.png "#EAE9DF" output.png * add-bg input.png --ul-brand output.png * - * @see ~/.claude/skills/Images/SKILL.md + * @see ~/.claude/LIFEOS/DOCUMENTATION/Tools/Tools.md */ import { existsSync } from "node:fs"; diff --git a/LifeOS/install/LIFEOS/TOOLS/GetTranscript.ts b/LifeOS/install/LIFEOS/TOOLS/GetTranscript.ts index fdad6bcb53..bdd6d9b382 100755 --- a/LifeOS/install/LIFEOS/TOOLS/GetTranscript.ts +++ b/LifeOS/install/LIFEOS/TOOLS/GetTranscript.ts @@ -4,12 +4,12 @@ * GetTranscript.ts - Extract transcript from YouTube video * * Usage: - * bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts - * bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts --save + * bun ~/.claude/LIFEOS/TOOLS/GetTranscript.ts + * bun ~/.claude/LIFEOS/TOOLS/GetTranscript.ts --save * * Examples: - * bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts "https://www.youtube.com/watch?v=abc123" - * bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts "https://youtu.be/abc123" --save transcript.txt + * bun ~/.claude/LIFEOS/TOOLS/GetTranscript.ts "https://www.youtube.com/watch?v=abc123" + * bun ~/.claude/LIFEOS/TOOLS/GetTranscript.ts "https://youtu.be/abc123" --save transcript.txt * * @author LifeOS System * @version 1.0.0 diff --git a/LifeOS/install/LIFEOS/TOOLS/RemoveBg.ts b/LifeOS/install/LIFEOS/TOOLS/RemoveBg.ts index a3c0eb6ee8..48cf81442e 100755 --- a/LifeOS/install/LIFEOS/TOOLS/RemoveBg.ts +++ b/LifeOS/install/LIFEOS/TOOLS/RemoveBg.ts @@ -4,7 +4,7 @@ * remove-bg - Background Removal CLI * * Remove backgrounds from images using local rembg. - * Part of the Images skill for LifeOS system. + * Companion to AddBg.ts; driven by the Art skill's background workflows. * * Usage: * remove-bg input.png # Overwrites original diff --git a/LifeOS/install/LIFEOS/TOOLS/ToolsRegistry.ts b/LifeOS/install/LIFEOS/TOOLS/ToolsRegistry.ts new file mode 100644 index 0000000000..43f5a8abb7 --- /dev/null +++ b/LifeOS/install/LIFEOS/TOOLS/ToolsRegistry.ts @@ -0,0 +1,68 @@ +#!/usr/bin/env bun +/** + * ToolsRegistry.ts — regenerate the flat-tool registry (FlatTools.md) from headers. + * + * Scans the LIFEOS/TOOLS root, parses each flat tool's leading header block + * against the element contract, and writes the generated registry table to + * DOCUMENTATION/Tools/FlatTools.md. The headers are the single source of + * truth; the registry is derived and never hand-edited. Deterministic output + * (no timestamps) — regeneration on an unchanged tree is a zero diff. + * + * Usage: + * bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts (write FlatTools.md) + * bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts --check (exit 1 + report if stale; writes nothing) + * bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts --stdout (print, write nothing) + * bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts --audit (per-file contract report for the whole tier) + * + * @see ~/.claude/LIFEOS/DOCUMENTATION/Tools/Tools.md + */ + +import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { getLifeosDir } from '../../hooks/lib/paths'; +import { + renderFlatTools, + listFlatTools, + parseFlatToolHeader, + checkHeaderContract, +} from '../../hooks/lib/flat-tool-header'; + +const LIFEOS = getLifeosDir(); +const TOOLS_ROOT = join(LIFEOS, 'TOOLS'); +const REGISTRY = join(LIFEOS, 'DOCUMENTATION', 'Tools', 'FlatTools.md'); + +const args = new Set(process.argv.slice(2)); + +if (args.has('--audit')) { + let bad = 0; + for (const f of listFlatTools(TOOLS_ROOT)) { + const header = parseFlatToolHeader(readFileSync(join(TOOLS_ROOT, f), 'utf-8'), f); + const missing = checkHeaderContract(header); + if (missing.length) { + bad++; + console.log(`${f}: missing ${missing.join(', ')}`); + } + } + console.log(bad === 0 ? 'audit: all flat tools contract-compliant' : `audit: ${bad} non-compliant`); + process.exit(bad === 0 ? 0 : 1); +} + +const rendered = renderFlatTools({ toolsRoot: TOOLS_ROOT }); + +if (args.has('--stdout')) { + console.log(rendered); + process.exit(0); +} + +if (args.has('--check')) { + const onDisk = existsSync(REGISTRY) ? readFileSync(REGISTRY, 'utf-8') : null; + if (onDisk === rendered) { + console.log('FlatTools.md: current'); + process.exit(0); + } + console.log(onDisk === null ? 'FlatTools.md: missing — run ToolsRegistry.ts' : 'FlatTools.md: stale — run ToolsRegistry.ts'); + process.exit(1); +} + +writeFileSync(REGISTRY, rendered); +console.log(`wrote ${REGISTRY}`); diff --git a/LifeOS/install/LIFEOS/TOOLS/YouTubeApi.ts b/LifeOS/install/LIFEOS/TOOLS/YouTubeApi.ts index a8a9550f0f..384862c4f1 100755 --- a/LifeOS/install/LIFEOS/TOOLS/YouTubeApi.ts +++ b/LifeOS/install/LIFEOS/TOOLS/YouTubeApi.ts @@ -10,7 +10,7 @@ for (const __k of ["LIFEOS_DIR", "LIFEOS_CONFIG_DIR", "PROJECTS_DIR"]) { * YouTubeApi.ts - YouTube Data API v3 client * * Usage: - * bun ~/.claude/skills/YouTube/Tools/YouTubeApi.ts [options] + * bun ~/.claude/LIFEOS/TOOLS/YouTubeApi.ts [options] * * Commands: * channel Get channel statistics diff --git a/LifeOS/install/hooks/lib/flat-tool-header.ts b/LifeOS/install/hooks/lib/flat-tool-header.ts new file mode 100644 index 0000000000..d565c581ed --- /dev/null +++ b/LifeOS/install/hooks/lib/flat-tool-header.ts @@ -0,0 +1,207 @@ +/** + * flat-tool-header.ts — parser + registry renderer for the flat-tool tier + * + * PURPOSE: + * Single source of truth for reading a flat tool's leading header block and + * judging it against the element contract in `DOCUMENTATION/Tools/Tools.md` + * § Placement doctrine (identity line, purpose prose, usage-or-library-marked). + * Also renders the generated registry table (`DOCUMENTATION/Tools/FlatTools.md`). + * Consumed by `LIFEOS/TOOLS/ToolsRegistry.ts`. Pure functions, no side effects; + * the pointer element (@see) is judgment-scoped ("where a governing doc exists") + * and deliberately NOT machine-enforced. + * + * TRIGGER: n/a (shared lib — no stdin, no registration) + * + * USAGE: + * import { parseFlatToolHeader, checkHeaderContract, renderFlatTools } from './lib/flat-tool-header'; + */ + +import { readdirSync, readFileSync, statSync } from 'fs'; +import { join } from 'path'; + +export interface FlatHeader { + /** Leading doc-comment lines (shebang + env-preamble skipped). */ + headerLines: string[]; + style: 'jsdoc' | 'line' | 'none'; + /** `.ts — one-liner` (or ALL-CAPS banner) identity line, if found. */ + identity: string | null; + /** One-line purpose extracted from the identity line (text after the dash). */ + purpose: string | null; + hasUsage: boolean; + /** Header self-declares library shape ("not a CLI" / "Consumed by:"). */ + libMarked: boolean; + /** Header self-declares daemon shape (a `com.lifeos.*.plist` schedule). */ + daemonMarked: boolean; + /** First @see target, if any. */ + pointer: string | null; +} + +/** Regex-escape a filename stem for embedding. */ +const esc = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + +/** + * Extract the leading header block: shebang skipped, the #1404 env-normalization + * preamble (comment + for-loop) skipped, comment blocks collected until the + * first real code line. + */ +export function parseFlatToolHeader(source: string, filename: string): FlatHeader { + const stem = filename.replace(/\.(ts|sh|py)$/, ''); + const hashComments = filename.endsWith('.sh') || filename.endsWith('.py'); + const lines = source.split('\n'); + const LIMIT = Math.min(lines.length, 80); + let i = 0; + if (lines[0]?.startsWith('#!')) i = 1; + + const blocks: string[][] = []; + let cur: string[] = []; + let sawJsdoc = false; + while (i < LIMIT) { + const l = lines[i].trim(); + if (l.startsWith('//') || (l.startsWith('#') && hashComments)) { + cur.push(l); + i++; + } else if (l.startsWith('/*') || (l.startsWith('"""') && filename.endsWith('.py'))) { + if (cur.length) { blocks.push(cur); cur = []; } + const close = l.startsWith('/*') ? '*/' : '"""'; + const js: string[] = []; + const first = i; + while (i < LIMIT) { + js.push(lines[i].trim()); + if (lines[i].includes(close) && !(i === first && lines[i].trim() === close)) { i++; break; } + i++; + } + blocks.push(js); + sawJsdoc = true; + } else if (l === '') { + if (cur.length) { blocks.push(cur); cur = []; } + i++; + } else if (/^for \(const __k of/.test(l)) { + if (cur.length) { blocks.push(cur); cur = []; } + let depth = (l.match(/\{/g) ?? []).length - (l.match(/\}/g) ?? []).length; + i++; + while (i < LIMIT && depth > 0) { + depth += (lines[i].match(/\{/g) ?? []).length - (lines[i].match(/\}/g) ?? []).length; + i++; + } + } else { + break; + } + } + if (cur.length) blocks.push(cur); + + const real = blocks.filter(b => !/Normalize env path vars/.test(b.join('\n'))); + const headerLines = real.flat(); + const h = headerLines.join('\n'); + const style: FlatHeader['style'] = headerLines.length === 0 ? 'none' : sawJsdoc ? 'jsdoc' : 'line'; + + const content = headerLines + .map(l => l.replace(/^\/\*+|\*+\/$|^\/\/+|^#+|^\*+/g, '').trim()) + .filter(l => l.length > 0); + + // Identity: a line leading with the stem + a dash separator, or an ALL-CAPS + // banner line naming the stem. An ASCII hyphen must be followed by a space, + // so a usage line (`algorithm -m loop …`) is not mistaken for the identity. + const sep = '(?:[—–]|-(?=\\s))\\s*'; + const idRe = new RegExp(`^${esc(stem)}(\\.(ts|sh|py))?\\s*${sep}`, 'i'); + const bannerRe = new RegExp(`^${esc(stem.toUpperCase())}\\b\\s*${sep}`); + let identity: string | null = null; + let purpose: string | null = null; + for (const l of content) { + const m = idRe.exec(l) ?? bannerRe.exec(l); + if (m) { + identity = l; + purpose = l.slice(m[0].length).trim() || null; + break; + } + } + + const hasUsage = + /USAGE\s*:/.test(h) || /Usage\s*:/i.test(h) || /Subcommands\s*:/i.test(h) || + /bun\s+\S*\.ts/.test(h) || new RegExp(`${esc(stem)}\\.(ts|sh|py)\\s+(\\w|<|\\[|-)`).test(h); + const libMarked = /not a CLI/i.test(h) || /Consumed by\s*:/i.test(h); + // A launchd-scheduled tool names its own plist in the header — either as an + // explicit `Trigger:` line or as prose ("Triggered by …com.lifeos.x.plist"). + const daemonMarked = /Trigger\s*:.*com\.lifeos\./i.test(h) || /com\.lifeos\.[\w.-]+\.plist/i.test(h); + const pointer = h.match(/@see\s+(\S+)/)?.[1] ?? null; + + return { headerLines, style, identity, purpose, hasUsage, libMarked, daemonMarked, pointer }; +} + +/** + * Machine-checkable subset of the element contract. Returns missing-element + * names; empty array = compliant. The @see pointer is not checked (requires + * judging whether a governing doc exists). + */ +export function checkHeaderContract(header: FlatHeader): string[] { + const missing: string[] = []; + if (!header.identity) missing.push('identity line'); + // Purpose prose: at least one content line beyond the identity line. + const prose = header.headerLines + .map(l => l.replace(/^\/\*+|\*+\/$|^\/\/+|^#+|^\*+/g, '').trim()) + .filter(l => l.length > 20); + if (header.identity && prose.length < 2) missing.push('purpose prose'); + if (!header.hasUsage && !header.libMarked) missing.push('usage (or library marker)'); + return missing; +} + +export interface RenderOptions { + /** Directory holding the flat tools. */ + toolsRoot: string; + /** Files exempt from the header-contract audit (still listed in the table). */ + deferred?: string[]; + /** Class overrides for files whose headers cannot carry a marker yet. */ + classOverrides?: Record; +} + +/** Enumerate the flat tier: root *.ts (excluding *.test.ts), *.sh and *.py. */ +export function listFlatTools(toolsRoot: string): string[] { + return readdirSync(toolsRoot) + .filter(f => /\.(ts|sh|py)$/.test(f) && !f.endsWith('.test.ts')) + .filter(f => { + try { return statSync(join(toolsRoot, f)).isFile(); } catch { return false; } + }) + .sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' })); +} + +/** Class a flat tool from what its own header declares. */ +export function classifyFlatTool(filename: string, header: FlatHeader): string { + if (filename.startsWith('Install')) return 'installer'; + if (header.daemonMarked) return 'daemon'; + if (header.libMarked) return header.hasUsage ? 'dual' : 'library'; + return 'cli'; +} + +/** Render the FlatTools.md registry. Deterministic: no timestamps. */ +export function renderFlatTools(opts: RenderOptions): string { + const deferred = new Set(opts.deferred ?? []); + const overrides = opts.classOverrides ?? {}; + + const rows: string[] = []; + let gaps = 0; + for (const f of listFlatTools(opts.toolsRoot)) { + const header = parseFlatToolHeader(readFileSync(join(opts.toolsRoot, f), 'utf-8'), f); + if (!deferred.has(f) && checkHeaderContract(header).length) gaps++; + const cls = overrides[f] ?? classifyFlatTool(f, header); + const purpose = (header.purpose ?? '—').replace(/\|/g, '\\|'); + const docs = header.pointer ? header.pointer.replace(/\|/g, '\\|') : 'header'; + rows.push(`| ${f} | ${cls} | ${purpose} | ${docs} |`); + } + + const gapLine = gaps === 0 + ? 'Every header meets the element contract.' + : `${gaps} header${gaps === 1 ? '' : 's'} short of the element contract — \`ToolsRegistry.ts --audit\` lists them.`; + + return [ + '# Flat Tools — generated registry', + '', + '> Generated by `LIFEOS/TOOLS/ToolsRegistry.ts` from the tools\' own headers — do not hand-edit.', + '> Regenerate: `bun ~/.claude/LIFEOS/TOOLS/ToolsRegistry.ts`. Contract: `Tools.md` § Placement doctrine.', + '', + `${rows.length} tools in the LIFEOS/TOOLS root. ${gapLine}`, + '', + '| Tool | Class | Purpose | Docs |', + '|---|---|---|---|', + ...rows, + '', + ].join('\n'); +} diff --git a/LifeOS/install/skills/Art/Workflows/Essay.md b/LifeOS/install/skills/Art/Workflows/Essay.md index 49d04e5979..a7453bcd48 100644 --- a/LifeOS/install/skills/Art/Workflows/Essay.md +++ b/LifeOS/install/skills/Art/Workflows/Essay.md @@ -719,7 +719,7 @@ thumbnail: https://example.com/images/my-header.png For non-blog images that only need transparency, or to remove backgrounds after generation: ```bash -# Use the Images Skill for background removal +# Standalone background removal bun ~/.claude/LIFEOS/TOOLS/RemoveBg.ts /path/to/output.png # Or batch process multiple images diff --git a/LifeOS/install/skills/Art/Workflows/Mermaid.md b/LifeOS/install/skills/Art/Workflows/Mermaid.md index a2bda0c74a..83ee1fe87c 100644 --- a/LifeOS/install/skills/Art/Workflows/Mermaid.md +++ b/LifeOS/install/skills/Art/Workflows/Mermaid.md @@ -667,7 +667,7 @@ ONE-OFF / QUICK PREVIEW: Keep white background (#FFFFFF) GOING INTO BLOG/WEBSITE: Remove background for transparency ``` -**For blog/website use** — use the **Images skill** for background removal: +**For blog/website use** — use `RemoveBg.ts` for background removal: ```bash bun ~/.claude/LIFEOS/TOOLS/RemoveBg.ts /path/to/mermaid-diagram.png diff --git a/LifeOS/install/skills/Art/Workflows/Visualize.md b/LifeOS/install/skills/Art/Workflows/Visualize.md index e0a1de445b..7605a31d53 100644 --- a/LifeOS/install/skills/Art/Workflows/Visualize.md +++ b/LifeOS/install/skills/Art/Workflows/Visualize.md @@ -90,12 +90,12 @@ STYLE: Excalidraw whiteboard sketch with rich graphics ``` DEFAULT: Light Cream/Sepia #F5E6D3 (matches blog aesthetic) WHITE ONLY IF: User explicitly requests "white background" in prompt -TRANSPARENT: Use Images skill to remove background for overlay use +TRANSPARENT: Use RemoveBg.ts to remove background for overlay use ``` **Light Cream (#F5E6D3) is the DEFAULT background.** Only use white (#FFFFFF) if the user explicitly requests it. -**For transparent background** — use the **Images skill** for background removal: +**For transparent background** — use `RemoveBg.ts` for background removal: ```bash bun ~/.claude/LIFEOS/TOOLS/RemoveBg.ts /path/to/visualization.png