Skill: bruno-nimbledev/deepthinking (version 1.0.3)

--- name: deepthinking description: A stateful, neuro-inspired thinking framework. Guides you through excavation, architecture, and synthesis phases for complex problem-solving. version: 1.0.2 author: Bruno Avila / S4NEXT metadata: openclaw: requires: bins: - python3 permissions: - file_system:read_write config_paths: - ~/.deepthinking # All state, memory, and evolution data is stored here --- # DeepThinking Stateful thinking framework. You guide the user through phases. State lives on disk. ## When to activate User says `/deep [topic]` or asks something like: - "help me decide..." - "I have an idea but I'm not sure..." - "I'm stuck between..." - "I want to start..." - "what should I do about..." Do NOT activate for factual questions, code tasks, or anything with a clear answer. ## Setup On first activation, initialize state: ```bash python3 {baseDir}/scripts/state.py init --topic "<user's topic>" ``` This creates `~/.deepthinking/current/state.json`. All phase transitions go through this script. Also load the semantic profile (consolidated heuristics from past sessions): ```bash python3 {baseDir}/scripts/evolve.py profile ``` If a profile exists, briefly disclose it to the user: "I have behavioral notes from past sessions that I'll use to tailor this conversation. You can inspect or clear them anytime at ~/.deepthinking/evolution/semantic_profile.json." Then absorb the content naturally — let it inform your excavation questions and module selection without reading the raw data aloud. The profile contains heuristics like "works better with short deadlines", "has financial risk aversion", etc. ## Flow overview ``` EXCAVATION (5 turns) → ALIGNMENT → ARCHITECTURE → EXECUTION (per module) → SYNTHESIS ``` You always check state before acting: ```bash python3 {baseDir}/scripts/state.py status ``` ## Phase 1: EXCAVATION You ask questions. One per message. Five layers deep. Read current layer: ```bash python3 {baseDir}/scripts/state.py get excavation_layer ``` ### Layer 0 — Surface Ask what they literally want. "Tell me more. What does [X] look like in your head?" ### Layer 1 — Context "What's happening in your life right now that made this come up?" ### Layer 2 — Energy "When you imagine doing this a year from now, what part gives you energy? What part feels like homework?" ### Layer 3 — Fear "What's the worst case in your head? Not the logical one." ### Layer 4 — Real Want No template. Craft a question based on the gap you see between layer 0 and layers 1-3. After each user response, save it: ```bash python3 {baseDir}/scripts/state.py save-layer <layer_number> "<summary of what user said>" ``` Then advance: ```bash python3 {baseDir}/scripts/state.py next-layer ``` ### Rules during excavation - ONE question per message. - Acknowledge briefly before asking. Don't parrot. - Short answer? Go sideways: "Let me ask differently..." - Vulnerable moment? Slow down. Reflect. Don't rush. - Never say "great question" or "interesting thought." - Match their register. Casual = casual. Analytical = analytical. ## Phase 2: ALIGNMENT After layer 4, read the full profile: ```bash python3 {baseDir}/scripts/state.py get profile ``` Then present your read: "Here's what I'm hearing. Tell me if I'm wrong: You want [surface], but what's driving this is [deeper motivation]. You're at a point where [context], and the thing that scares you isn't failure — it's [real fear]. What you actually need isn't [what they asked]. It's [what they really need]. Am I close?" If user confirms: ```bash python3 {baseDir}/scripts/state.py set-phase architecture ``` If user corrects: update the profile and redo alignment. ```bash python3 {baseDir}/scripts/state.py save-layer <layer> "<correction>" ``` ## Phase 3: ARCHITECTURE Select 3-5 modules from the registry. Read available modules: ```bash python3 {baseDir}/scripts/state.py list-modules ``` Pick modules based on the user's profile. Order matters: - Can't see options? Start with DIVERGE - Too many options? CONVERGE - Too attached to one idea? INVERT - Stuck in abstraction? PROTOTYPE - Orbiting same theme? MIRROR - Solving wrong problem? REFRAME - Has clarity but won't act? COMMIT Common patterns: - Exploration: diverge → mirror → converge → prototype - Validation: invert → reframe → prototype → commit - Unsticking: mirror → reframe → diverge → commit Save the pipeline: ```bash python3 {baseDir}/scripts/state.py set-pipeline "diverge,mirror,converge,prototype" --name "Exploration v0.1" ``` Present to user: "I built [Pipeline Name]. Here's the plan: Phase 1: [Module] — [why, specific to them] Phase 2: [Module] — [why] ... Not rigid. We adapt. ~25 min. /deepend to pause. Ready?" On confirmation: ```bash python3 {baseDir}/scripts/state.py set-phase execution ``` ## Phase 4: EXECUTION Read current module: ```bash python3 {baseDir}/scripts/state.py current-module ``` This returns the module ID and its reference prompts. Read the full module doc: ```bash cat {baseDir}/references/modules.md ``` Find the section for the current module. Follow its approach. Also check for evolved prompts (additions from self-improvement): ```bash python3 {baseDir}/scripts/evolve.py patches ``` If patches exist for this module, add them to your available questions. ### Per-module rules - 3-7 exchanges per module. - One question per message. - Push back on lazy answers: "That sounds like a job interview answer. What's the real one?" - Celebrate real insights: "That's it. Write that down." - If stuck, reframe: "Let's come at this differently." ### Adaptive Entropy Control (System 3) You have two modes. Switch dynamically based on user's cognitive state: **HIGH ENTROPY (expand)** — activate when you detect friction: - Signs: short answers, defensive tone, "I don't know", repeated "yes/no", disengagement - Response: loosen up. Ask lateral, playful, hypothetical questions. "Forget everything practical for a second. If you could snap your fingers..." "This is a weird question but — what color is this decision?" - Purpose: reduce cognitive load, bypass analytical resistance, access associative thinking. **LOW ENTROPY (compress)** — activate when you detect flow: - Signs: long answers, emotional engagement, new ideas appearing, energy in language - Response: tighten the funnel. Force concreteness. Move toward Commit/Prototype. "OK you're on to something. Narrow it down: which ONE of these?" "Stop exploring. What's the first thing you'd actually do?" - Purpose: capture momentum, prevent infinite divergence, convert insight into action. **Transition rule:** After every 2 exchanges within a module, silently assess: Is the user in friction or flow? Adjust your next question accordingly. Do NOT announce the shift. Do NOT say "I notice you're disengaged." Just adapt. If friction persists for 3+ exchanges across modules, consider: - Skipping to a different module (Mirror or Reframe often break friction) - Shortening the pipeline (drop remaining modules, go to Synthesis with what you have) - Asking directly: "We can keep going or I can work with what we have. Your call." ### Internal Monitor (pre-response audit) Before sending EVERY response during execution, run a silent self-check. This is your cognitive immune system. Do NOT show this to the user. Internal checklist (evaluate in your reasoning, not in output): 1. Am I giving advice or asking a question? (Must be asking.) 2. Am I asking more than one thing? (Must be exactly one.) 3. Does my question assume an answer? (Must be genuinely open.) 4. Am I parroting their words back or adding new angle? (Must add angle.) 5. Is this question for THEM or would I ask anyone this? (Must be specific to their profile.) 6. Am I in the right entropy mode for their current state? (Check friction/flow.) If any check fails, rewrite your response before sending. This prevents: advice disguised as questions, multi-part questions that overwhelm, generic prompts that waste the user's time, and logical drift across long sessions. After enough exchanges (or user says "next"/"done"/"proximo"): ```bash python3 {baseDir}/scripts/state.py save-module-output "<module_id>" "<2-3 key insights, pipe-separated>" python3 {baseDir}/scripts/state.py next-module ``` Check if there are more modules: ```bash python3 {baseDir}/scripts/state.py current-module ``` If it returns "done", move to synthesis: ```bash python3 {baseDir}/scripts/state.py set-phase synthesis ``` ## Phase 5: SYNTHESIS Read everything: ```bash python3 {baseDir}/scripts/state.py get profile python3 {baseDir}/scripts/state.py get outputs ``` Write synthesis: - 2-3 paragraphs connecting everything that emerged - "The thing that surprised me: [unexpected insight]" - "Your next move: [ONE action, not a list]" - "What I'd push back on: [one thing they might be wrong about]" Then offer: - "Want me to save this as a document?" - "Want to go deeper on any thread?" - "Want a 7-day action plan?" Store the core insight as an engram: ```bash python3 {baseDir}/scripts/memory.py store "<relevant tags>" "<core insight from this session>" ``` Archive session: ```bash python3 {baseDir}/scripts/state.py archive ``` ## /deepend command If user types `/deepend` at any point: ```bash python3 {baseDir}/scripts/state.py status ``` Respond: "Pausing here. State saved. Pick up anytime with /deep. One thing to sit with: [most important unresolved question]." ## Resume If user says `/deep` without a topic, check for existing session: ```bash python3 {baseDir}/scripts/state.py status ``` If session exists, resume from saved phase. Tell user where you left off. If no session but user provides a topic, also search memory before starting: ```bash python3 {baseDir}/scripts/memory.py search "<topic keywords>" python3 {baseDir}/scripts/memory.py themes ``` Use any relevant findings to inform your excavation — but don't announce them. ## Long-term memory DeepThinking has persistent memory across sessions. After each meaningful exchange, store engrams: ```bash python3 {baseDir}/scripts/memory.py store "<tags>" "<insight>" ``` Tags are comma-separated. Be specific. Examples: - `python3 {baseDir}/scripts/memory.py store "fear,career" "Keeps returning to fear of not being taken seriously"` - `python3 {baseDir}/scripts/memory.py store "pattern,energy" "Energy spikes when discussing creation, drops with services"` - `python3 {baseDir}/scripts/memory.py store "breakthrough,identity" "Realized wants respect from peers more than money"` ### When to store engrams Store after: - Each excavation layer reveals something meaningful - A module produces a key insight - You notice a recurring pattern - User has a breakthrough moment - Session synthesis is complete (store the core insight) ### Using memory in future sessions At the START of every new session, before the first excavation question, search memory: ```bash python3 {baseDir}/scripts/memory.py search "<topic keywords>" python3 {baseDir}/scripts/memory.py themes ``` If relevant engrams exist, silently incorporate them. Do NOT announce "I found memories about you." Just use the context naturally, as if you already know. To find connections between concepts: ```bash python3 {baseDir}/scripts/memory.py connect "<concept1>" "<concept2>" ``` To see recent entries: ```bash python3 {baseDir}/scripts/memory.py recent 10 ``` ### Memory rules - Store insights, not transcripts. Short, tagged, searchable. - One engram per insight. Don't batch. - Tags should be reusable: use "fear" not [REDACTED_SECRET]. - Memory is append-only. Never delete engrams. ## Self-improvement (evolution) DeepThinking evolves over time. After each session, and via a 3 AM cron job, the framework analyzes what's working and proposes improvements. ### After each session Before archiving, run analysis: ```bash python3 {baseDir}/scripts/evolve.py analyze ``` Review suggestions. If any seem valuable, propose them: ```bash python3 {baseDir}/scripts/evolve.py propose add-prompt <module_id> "<new question>" python3 {baseDir}/scripts/evolve.py propose add-note <module_id> "<edge case observation>" ``` ### What evolution CAN do - Add new seed questions to existing modules - Add edge-case handling notes - Propose entirely new modules (stored separately until approved) ### What evolution CANNOT do - Modify or rewrite existing prompts - Delete anything from modules.md - Change SKILL.md core instructions - Make bulk changes This is enforced by the script. Destructive operations are blocked: ```bash # This will be REJECTED: python3 {baseDir}/scripts/evolve.py propose modify-prompt diverge "rewrite the first question" # → BLOCKED: destructive operation not allowed ``` ### Checking for evolved prompts During execution, after loading a module from references/modules.md, also check for patches: ```bash python3 {baseDir}/scripts/evolve.py patches ``` If patches exist for the current module, append those prompts to your available questions. ### Cron job (3 AM nightly) Set up via OpenClaw cron or system crontab: ```json { "jobs": [ { "name": "deepthinking-evolve", "schedule": "0 3 * * *", "task": "Run these steps in order: (1) python3 {baseDir}/scripts/evolve.py consolidate — this is the hippocampal replay: consolidate episodic engrams into semantic heuristics about the user. (2) python3 {baseDir}/scripts/evolve.py analyze — analyze session patterns, memory themes, module usage. (3) Review the suggestions. If any add-prompt or add-note improvements are clearly beneficial based on data, propose them. (4) Never approve your own proposals — leave them pending for human review." } ] } ``` The nightly cycle does two things: 1. **Memory consolidation** (hippocampal replay): distills episodic engrams into stable heuristics about the user, stored in `semantic_profile.json`. Over time, the agent "knows" the user at a deep behavioral level without re-reading every past conversation. 2. **Prompt evolution**: proposes surgical improvements based on usage patterns. The agent runs both at 3 AM, proposes improvements, but NEVER auto-approves. User reviews pending proposals: ```bash python3 {baseDir}/scripts/evolve.py review python3 {baseDir}/scripts/evolve.py approve <id> python3 {baseDir}/scripts/evolve.py reject <id> ``` ## Language Always respond in the user's language. If the user writes in Portuguese, respond in Portuguese. If English, English. If Spanish, Spanish. Detect from their first message and maintain throughout. ## Hard rules - Never give lists of ideas unprompted. Lists are lazy thinking. - Never give advice. Give frameworks, provocations, reflections. User decides. - If user says "just give me the answer": "I could, but it'd be my answer. You'd throw it away in a week. Let's keep going." - When you sense a breakthrough, don't pile on. "Yeah." or "There it is." is enough. - This framework evolves. If you find a better approach during a session, use it.

path: README.md
content: # DeepThinking: Stateful Cognitive Framework for OpenClaw

**DeepThinking** is a stateful, neuroscience-inspired cognitive framework packaged as an OpenClaw skill. It is designed to assist users in complex decision-making, idea exploration, untangling mental knots, and resolving deep ambiguities.

Unlike typical conversational AI that merely answers queries or fulfills direct prompts, DeepThinking operates as a **facilitator**. It guides you through rigorous, structured psychological phases—Excavation, Alignment, Architecture, Execution, and Synthesis—ensuring you don't just get an answer, but actually unpack the root of your problem or idea.

---

## 🧠 Core Philosophy & Architecture

DeepThinking operates on a few core principles that differentiate it from standard AI interactions:
1. **Statefulness over statelessness**: The framework persists your session state on disk. You can explore a thought, type `/deepend`, close your laptop, and return days later with `/deep` to pick up exactly where you left off.
2. **Questions over Answers**: The framework strictly avoids giving raw advice or lists of ideas (which it considers "lazy thinking"). Instead, it provides frameworks, provocations, and lateral reflections.
3. **Adaptive Entropy Control (System 3)**: DeepThinking dynamically detects whether you are in a state of "friction" (short answers, defensive tone) or "flow" (long answers, energy). It adjusts its questioning style silently, either expanding the entropy (asking lateral, weird questions to unblock you) or compressing the entropy (forcing concreteness to drive action).

---

## 🔄 The 5-Phase Cognitive Flow

When you initiate a session with `/deep [topic]`, DeepThinking orchestrates the following pipeline:

```mermaid
stateDiagram-v2
    [*] --> Initialization: User types `/deep [topic]`
    
    state "Phase 1: Excavation" as Excavation {
        [*] --> Layer0
        Layer0 --> Layer1: Is Context?
        Layer1 --> Layer2: Is Energy?
        Layer2 --> Layer3: Is Fear?
        Layer3 --> Layer4: Is Motivation?
        Layer4 --> [*]
    }
    
    Initialization --> Excavation
    
    state "Phase 2: Alignment" as Alignment {
        PresentRead: Present User Profile/Read
        WaitConfirm: Wait for User Confirmation
        PresentRead --> WaitConfirm
    }
    
    Excavation --> Alignment
    Alignment --> Alignment: User Corrects Profile
    Alignment --> Architecture: User Confirms Profile
    
    state "Phase 3: Architecture" as Architecture {
        SelectModules: Select 3-5 Modules
        BuildPipeline: Build Custom Pipeline
        SelectModules --> BuildPipeline
    }
    
    state "Phase 4: Execution" as Execution {
        RunModule: Execute Module Iterations
        AssessEntropy: Assess Friction/Flow
        AssessEntropy --> RunModule: Adjust System 3 Entropy
    }
    
    Architecture --> Execution
    Execution --> Execution: Next Module in Pipeline
    
    state "Phase 5: Synthesis" as Synthesis {
        Consolidate: Generate Final Insight
        StoreEngram: Store Core Engram to Memory
        Archive: Archive Session
        Consolidate --> StoreEngram
        StoreEngram --> Archive
    }
    
    Execution --> Synthesis: All Modules Complete
    Synthesis --> [*]
    
    note right of Execution
        User can type `/deepend` at ANY state
        to safely freeze execution.
        Typing `/deep` resumes exactly here.
    end note
```

### 1. EXCAVATION (The Dig)
The system asks you **five layers of questions**, one per message, to strip away your surface request and find the real underlying motivation.
- **Layer 0 (Surface)**: What do you literally want?
- **Layer 1 (Context)**: What's happening in your life right now that brought this up?
- **Layer 2 (Energy)**: What gives you energy vs. what feels like homework?
- **Layer 3 (Fear)**: What is the irrational worst-case scenario?
- **Layer 4 (Real Want)**: A surgical question based on the gap between your surface request and actual fears.

### 2. ALIGNMENT (The Mirror)
Before proceeding, DeepThinking presents its "read" on you based on the Excavation phase. It explicitly names the deeper motivation and real fear it detected, ensuring both you and the AI are solving the right problem. If it's wrong, you correct it; if it's right, it builds a custom pipeline.

### 3. ARCHITECTURE (The Blueprint)
The system selects 3-5 specific psychological **modules** (e.g., *DIVERGE*, *CONVERGE*, *INVERT*, *MIRROR*, *REFRAME*, *COMMIT*, *PROTOTYPE*) from its registry, tailored to your exact cognitive state. It presents this custom pipeline to you before executing.

### 4. EXECUTION (The Work)
You progress through the chosen modules. For each module, DeepThinking engages in 3-7 exchanges. It monitors your state silently:
- If you give a lazy "job interview" answer, it pushes back.
- If you hit a breakthrough, it anchors it.
- If you stall, it laterally reframes the approach.

### 5. SYNTHESIS (The Anchor)
In the final phase, the system connects everything that emerged. It provides a concise summary, highlights one surprising insight, proposes exactly **one next move**, and gently points out one thing you might be wrong about.

---

## 🧬 Memory & Self-Improvement (GS-3 Memory Mechanics)

DeepThinking features a persistent, cross-session memory and self-improvement engine inspired by the human hippocampus.

### Episodic to Semantic Consolidation
During sessions, DeepThinking silently stores **engrams** (episodic insights, e.g., "energy spikes when discussing creation, drops with services"). Over time, the system runs a "hippocampal replay" to consolidate these isolated engrams into a **Semantic Profile**. 

This means the AI genuinely "gets to know you" at a deep behavioral level—your risk aversion, your blind spots, your working style—without needing to reread every past transcript. It uses this silent profile to guide all future excavation and module selection.

### Automatic Prompt Evolution
 DeepThinking evolves its own prompts over time. Through a nightly analysis script, the system analyzes what questioning techniques worked best and identifies edge cases. It then **proposes** new seed prompts or notes for its execution modules.
*Note: In alignment with strict safety controls, the AI can only propose additions. It cannot overwrite core logic or approve its own proposals. The user must manually review and approve any evolution.*

### ⚙️ Setting up the Evolutionary Cron Job
To enable hippocampal replay and prompt evolution, configure the OpenClaw Cron Job to run the `evolve.py` script nightly at 3 AM:

```json
{
  "jobs": [
    {
      "name": "deepthinking-evolve",
      "schedule": "0 3 * * *",
      "task": "Run these steps in order: (1) python3 {baseDir}/scripts/evolve.py consolidate — this is the hippocampal replay: consolidate episodic engrams into semantic heuristics about the user. (2) python3 {baseDir}/scripts/evolve.py analyze — analyze session patterns, memory themes, module usage. (3) Review the suggestions. If any add-prompt or add-note improvements are clearly beneficial based on data, propose them. (4) Never approve your own proposals — leave them pending for human review."
    }
  ]
}
```

---

## 🛡️ Security & Privacy Declaration

Trust and transparency are the greatest pillars of DeepThinking. Because cognitive exploration requires vulnerability, the technical architecture guarantees absolute privacy:

1. **100% Local Execution**: Everything happens on your machine within the OpenClaw environment.
2. **Zero Network Requests**: The internal python scripts (`state.py`, `memory.py`, `evolve.py`) rely exclusively on native standard Python 3 libraries (`json`, `os`, `sys`, `datetime`, `pathlib`, `subprocess`). There are zero external API calls, zero telemetry, and zero third-party dependencies. This guarantees no supply chain attack risks and ensures immediate compliance with security filters like VirusTotal.
3. **Isolated Safe Paths**: State, memory profiles, and engrams are saved entirely as plain text (`.json` and `.log` files) sandboxed inside the `~/.deepthinking/` directory. Path handling is strictly validated to prevent directory traversal. You have absolute, auditable control over everything the AI remembers about you.

---

## 🛠️ Internal CLI Architecture

For developers, DeepThinking is powered by three completely isolated, single-responsibility Python scripts located in `scripts/`.

The data is persisted locally in your machine, mapping the runtime into these core structures:

```mermaid
erDiagram
    STATE {
        string session_id PK
        string current_phase
        int excavation_layer
        string[] pending_modules
    }
    
    SEMANTIC_PROFILE {
        string user_id PK
        string[] behavioral_heuristics
        string[] known_fears
        string[] risk_tolerances
    }
    
    ENGRAM {
        string engram_id PK
        date timestamp
        string[] tags
        string core_insight
    }
    
    MODULE {
        string module_id PK
        string type
        string[] seed_prompts
    }
    
    EVOLUTION_PROPOSAL {
        string proposal_id PK
        string target_module FK
        string proposed_addition
        string status "pending/approved/rejected"
    }

    STATE ||--|| SEMANTIC_PROFILE : "Guided by"
    STATE ||--o{ ENGRAM : "Generates episodic"
    SEMANTIC_PROFILE ||--|{ ENGRAM : "Consolidates from (Hippocampal Replay)"
    STATE }o--|{ MODULE : "Executes pipeline of"
    MODULE ||--o{ EVOLUTION_PROPOSAL : "Receives"
```

*   `state.py`: The state machine. Manages session lifecycles, phase transitions, and module progression (`~/.deepthinking/current/state.json`).
*   `memory.py`: The engram manager. Handles the storage, tagging, and contextual retrieval of granular insights.
*   `evolve.py`: The self-improvement engine. Handles hippocampal consolidation (episodic to semantic) and analyzes session metrics to propose non-destructive module updates.

---

## 🚀 How to Use

1. **Start a new reflection**:
   `/deep I'm considering leaving my job to start a company, but I'm paralyzed.`
2. **Pause mid-session**:
   Type `/deepend` whenever you need a break. Your state in the exact layer/module is safely frozen.
3. **Resume later**:
   Type `/deep` with no arguments, and the AI will pick up the conversation from exactly where you left off.
4. **Review Evolution Proposals**:
   In your terminal, navigate to the skill directory and run `python3 scripts/evolve.py review` to accept/reject the AI's proposed improvements to its own logic.

---

## 🖥️ Standalone TUI (VPS / SSH)

DeepThinking ships with a standalone **Terminal User Interface** that runs natively over SSH on any Linux/macOS VPS — no browser, no OpenClaw, no external dependencies.

### Requirements
- Python 3.8+ (standard library only)
- SSH access to your VPS

### Install

```bash
# Clone to your VPS
git clone https://github.com/bruno-nimbledev/DeepThinking.git ~/deepthinking
cd ~/deepthinking

# Make launcher executable
chmod +x bin/deep scripts/tui.py

# Optional: add to PATH system-wide
sudo ln -s $(pwd)/bin/deep /usr/local/bin/deep
```

### Usage

```bash
# Start a new session
deep "Quero abrir uma empresa de planejamento de festas infantis"

# Or just invoke and type your topic interactively
deep

# Resume an existing paused session
deep

# Commands during a session
/deepend      # Pause and save state (resume later with `deep`)
Ctrl+C     # Same as /deepend
next       # Advance to the next module (also: done, fim, próximo)
```

### Optional: Run as a persistent service (systemd)

To keep a DeepThinking session alive even if you disconnect from SSH:

```ini
# /etc/systemd/system/deepthinking.service
[Unit]
Description=DeepThinking TUI session

[Service]
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER/deepthinking
ExecStart=/usr/local/bin/deep
Restart=no

[Install]
WantedBy=multi-user.target
```

> **Note**: For interactive TUI use, `tmux` or `screen` is simpler than systemd.
> ```bash
> tmux new -s deep
> deep "my topic"
> # Detach with Ctrl+B D · Reattach with tmux attach -t deep
> ```

### State & Memory location

All session data is saved in `~/.deepthinking/` on the VPS:
```
~/.deepthinking/
├── current/state.json        ← active session
├── archive/                  ← completed sessions
├── memory/engrams.log        ← long-term memory
└── evolution/                ← semantic profile + patches
```


path: scripts/evolve.py
content: #!/usr/bin/env python3
"""
DeepThinking Self-Improvement Engine
=====================================
Runs as a cron job (3 AM). Analyzes recent sessions and proposes
SURGICAL improvements to prompts. Never modifies existing prompts
substantially — only appends new ones or makes minimal tweaks.

Storage:
  ~/.deepthinking/evolution/
    improvements.log    — append-only log of all proposed changes
    pending.json        — changes awaiting approval
    applied.json        — changes that were applied
    prompt_patches/     — per-module patch files (additive only)

Safety rules:
  1. NEVER delete or rewrite existing prompts in modules.md
  2. NEVER modify SKILL.md core instructions
  3. CAN append new seed questions to modules
  4. CAN add new edge-case handling notes
  5. CAN propose new modules (stored separately, not injected until approved)
  6. All changes logged and reversible
"""
import json
import os
import sys
from datetime import datetime, timedelta
from pathlib import Path

EVOLUTION_DIR = Path.home() / ".deepthinking" / "evolution"
IMPROVEMENTS_LOG = EVOLUTION_DIR / "improvements.log"
PENDING_FILE = EVOLUTION_DIR / "pending.json"
APPLIED_FILE = EVOLUTION_DIR / "applied.json"
PATCHES_DIR = EVOLUTION_DIR / "prompt_patches"
MEMORY_FILE = Path.home() / ".deepthinking" / "memory" / "engrams.log"
ARCHIVE_DIR = Path.home() / ".deepthinking" / "archive"
SEMANTIC_PROFILE = EVOLUTION_DIR / "semantic_profile.json"


def ensure_dirs():
    EVOLUTION_DIR.mkdir(parents=True, exist_ok=True)
    PATCHES_DIR.mkdir(parents=True, exist_ok=True)
    for f in [PENDING_FILE, APPLIED_FILE]:
        if not f.exists():
            f.write_text("[]")


def cmd_analyze(args):
    """Analyze recent sessions for improvement opportunities.
    Looks at: archived sessions, memory themes, module usage patterns.
    Outputs analysis as JSON for the agent to review.
    """
    ensure_dirs()
    analysis = {
        "timestamp": datetime.now().isoformat(),
        "session_patterns": _analyze_sessions(),
        "memory_themes": _analyze_memory(),
        "module_usage": _analyze_module_usage(),
        "suggestions": []
    }

    # Generate suggestions based on patterns
    suggestions = []

    # Check for modules never used
    usage = analysis["module_usage"]
    all_modules = {"diverge", "converge", "invert", "prototype", "mirror", "reframe", "commit"}
    unused = all_modules - set(usage.get("used_modules", []))
    if unused:
        suggestions.append({
            "type": "observation",
            "priority": "low",
            "detail": f"Modules never used: {', '.join(unused)}. Consider if their triggers need refinement."
        })

    # Check for recurring themes without corresponding prompts
    themes = analysis["memory_themes"]
    if themes.get("top_tags"):
        top3 = list(themes["top_tags"].keys())[:3]
        suggestions.append({
            "type": "new_prompt_candidate",
            "priority": "medium",
            "detail": f"Recurring themes across sessions: {', '.join(top3)}. Consider adding targeted seed questions."
        })

    # Check for sessions that ended early (possible friction)
    patterns = analysis["session_patterns"]
    if patterns.get("early_exits", 0) > patterns.get("completed", 0):
        suggestions.append({
            "type": "friction_signal",
            "priority": "high",
            "detail": "More sessions exit early than complete. Excavation may be too long or modules not engaging."
        })

    analysis["suggestions"] = suggestions
    print(json.dumps(analysis, indent=2, ensure_ascii=False))


def cmd_propose(args):
    """Propose a specific improvement. Called by the agent after reviewing analysis.
    Usage: propose <type> <target> <content>

    Types:
      add-prompt <module_id> <new prompt text>
      add-note <module_id> <edge case note>
      new-module <module_id> <module definition as JSON>

    NOT allowed:
      modify-prompt, delete-prompt, rewrite-module
    """
    if len(args) < 3:
        print(json.dumps({"error": "usage: propose <type> <target> <content>"}))
        return

    ensure_dirs()
    prop_type = args[0]
    target = args[1]
    content = " ".join(args[2:])

    # Safety: reject destructive operations
    forbidden = ["modify-prompt", "delete-prompt", "rewrite-module", "replace"]
    if prop_type in forbidden:
        print(json.dumps({
            "error": "BLOCKED: destructive operation not allowed",
            "detail": f"'{prop_type}' would modify existing prompts. Only additive changes permitted.",
            "allowed_types": ["add-prompt", "add-note", "new-module"]
        }))
        return

    allowed = ["add-prompt", "add-note", "new-module"]
    if prop_type not in allowed:
        print(json.dumps({"error": f"unknown type: {prop_type}", "allowed": allowed}))
        return

    proposal = {
        "id": datetime.now().strftime("%Y%m%d%H%M%S"),
        "timestamp": datetime.now().isoformat(),
        "type": prop_type,
        "target": target,
        "content": content,
        "status": "pending"
    }

    # Add to pending
    pending = json.loads(PENDING_FILE.read_text())
    pending.append(proposal)
    PENDING_FILE.write_text(json.dumps(pending, indent=2, ensure_ascii=False))

    # Log
    _log(f"PROPOSED | {prop_type} | {target} | {content[:80]}...")

    print(json.dumps({"proposed": True, "id": proposal["id"], "type": prop_type}))


def cmd_review(args):
    """Show pending proposals for human review."""
    ensure_dirs()
    pending = json.loads(PENDING_FILE.read_text())
    if not pending:
        print(json.dumps({"pending": [], "count": 0}))
        return
    print(json.dumps({"pending": pending, "count": len(pending)}, indent=2, ensure_ascii=False))


def cmd_approve(args):
    """Approve a pending proposal by ID. Applies the change."""
    if not args:
        print(json.dumps({"error": "usage: approve <proposal_id>"}))
        return
    ensure_dirs()
    pid = args[0]
    pending = json.loads(PENDING_FILE.read_text())
    proposal = None
    remaining = []
    for p in pending:
        if p["id"] == pid:
            proposal = p
        else:
            remaining.append(p)

    if not proposal:
        print(json.dumps({"error": f"proposal {pid} not found"}))
        return

    # Apply the change
    _apply_proposal(proposal)

    # Move to applied
    proposal["status"] = "applied"
    proposal["applied_at"] = datetime.now().isoformat()
    applied = json.loads(APPLIED_FILE.read_text())
    applied.append(proposal)
    APPLIED_FILE.write_text(json.dumps(applied, indent=2, ensure_ascii=False))
    PENDING_FILE.write_text(json.dumps(remaining, indent=2, ensure_ascii=False))

    _log(f"APPLIED | {proposal['type']} | {proposal['target']} | {proposal['content'][:80]}...")

    print(json.dumps({"applied": True, "id": pid}))


def cmd_reject(args):
    """Reject a pending proposal by ID."""
    if not args:
        print(json.dumps({"error": "usage: reject <proposal_id>"}))
        return
    ensure_dirs()
    pid = args[0]
    pending = json.loads(PENDING_FILE.read_text())
    remaining = [p for p in pending if p["id"] != pid]
    if len(remaining) == len(pending):
        print(json.dumps({"error": f"proposal {pid} not found"}))
        return
    PENDING_FILE.write_text(json.dumps(remaining, indent=2, ensure_ascii=False))
    _log(f"REJECTED | {pid}")
    print(json.dumps({"rejected": True, "id": pid}))


def cmd_history(args):
    """Show improvement history."""
    ensure_dirs()
    applied = json.loads(APPLIED_FILE.read_text())
    print(json.dumps({"applied": applied, "count": len(applied)}, indent=2, ensure_ascii=False))


def cmd_patches(args):
    """Show all active prompt patches (additive content per module)."""
    ensure_dirs()
    patches = {}
    for f in PATCHES_DIR.glob("*.md"):
        patches[f.stem] = f.read_text()
    print(json.dumps(patches, indent=2, ensure_ascii=False))


def _apply_proposal(proposal):
    """Apply an approved proposal. Additive only."""
    ptype = proposal["type"]
    target = proposal["target"]
    content = proposal["content"]

    if ptype == "add-prompt":
        patch_file = PATCHES_DIR / f"{target}.md"
        existing = patch_file.read_text() if patch_file.exists() else ""
        patch_file.write_text(existing + f"\n- {content}\n")

    elif ptype == "add-note":
        patch_file = PATCHES_DIR / f"{target}_notes.md"
        existing = patch_file.read_text() if patch_file.exists() else ""
        ts = datetime.now().strftime("%Y-%m-%d")
        patch_file.write_text(existing + f"\n[{ts}] {content}\n")

    elif ptype == "new-module":
        patch_file = PATCHES_DIR / f"new_module_{target}.md"
        patch_file.write_text(content)


def _analyze_sessions():
    """Analyze archived sessions for patterns."""
    if not ARCHIVE_DIR.exists():
        return {"total": 0}
    archives = list(ARCHIVE_DIR.glob("*.json"))
    completed = 0
    early_exits = 0
    module_counts = {}
    for a in archives[-20:]:  # Last 20 sessions
        try:
            data = json.loads(a.read_text())
            if data.get("phase") in ["synthesis", "done"]:
                completed += 1
            else:
                early_exits += 1
            for mid in data.get("pipeline", {}).get("modules", []):
                module_counts[mid] = module_counts.get(mid, 0) + 1
        except Exception:
            continue
    return {
        "total": len(archives),
        "recent_analyzed": min(20, len(archives)),
        "completed": completed,
        "early_exits": early_exits,
        "module_frequency": module_counts
    }


def _analyze_memory():
    """Quick theme analysis from memory."""
    if not MEMORY_FILE.exists():
        return {"total": 0, "top_tags": {}}
    tag_counts = {}
    total = 0
    with open(MEMORY_FILE, encoding="utf-8") as f:
        for line in f:
            total += 1
            parts = line.split("|")
            if len(parts) >= 3:
                tags = [t.strip().lower() for t in parts[1].split(",")]
                for t in tags:
                    tag_counts[t] = tag_counts.get(t, 0) + 1
    sorted_tags = dict(sorted(tag_counts.items(), key=lambda x: -x[1])[:10])
    return {"total": total, "top_tags": sorted_tags}


def _analyze_module_usage():
    """Which modules are used, which are neglected."""
    if not ARCHIVE_DIR.exists():
        return {"used_modules": [], "unused": []}
    used = set()
    for a in ARCHIVE_DIR.glob("*.json"):
        try:
            data = json.loads(a.read_text())
            for mid in data.get("pipeline", {}).get("modules", []):
                used.add(mid)
        except Exception:
            continue
    all_modules = {"diverge", "converge", "invert", "prototype", "mirror", "reframe", "commit"}
    return {"used_modules": list(used), "unused": list(all_modules - used)}


def cmd_consolidate(args):
    """Hippocampal Replay: consolidate episodic memory into semantic heuristics.
    Reads recent engrams, extracts behavioral patterns, and generates a
    semantic profile of consolidated user heuristics.
    These are stable truths about the user distilled from many episodes.

    Output: ~/.deepthinking/evolution/semantic_profile.json
    The agent reads this at session start for deep user understanding.
    """
    ensure_dirs()
    if not MEMORY_FILE.exists():
        print(json.dumps({"error": "no engrams to consolidate"}))
        return

    # Read all engrams
    engrams = []
    with open(MEMORY_FILE, encoding="utf-8") as f:
        for line in f:
            parts = line.strip().split(" | ", 2)
            if len(parts) == 3:
                engrams.append({"ts": parts[0], "tags": parts[1], "content": parts[2]})

    if len(engrams) < 5:
        print(json.dumps({"status": "insufficient_data", "count": len(engrams),
                          "min_required": 5}))
        return

    # Extract tag frequency for weighting
    tag_freq = {}
    for e in engrams:
        for t in e["tags"].split(","):
            t = t.strip().lower()
            tag_freq[t] = tag_freq.get(t, 0) + 1

    # Find repeated patterns: content clusters sharing tags
    # Group engrams by dominant tag
    tag_groups = {}
    for e in engrams:
        tags = [t.strip().lower() for t in e["tags"].split(",")]
        dominant = max(tags, key=lambda t: tag_freq.get(t, 0))
        tag_groups.setdefault(dominant, []).append(e["content"])

    # Build heuristics: themes that appear 3+ times become consolidated rules
    heuristics = []
    for tag, contents in tag_groups.items():
        if len(contents) >= 3:
            heuristics.append({
                "pattern": tag,
                "frequency": len(contents),
                "evidence": contents[:5],  # cap evidence samples
                "heuristic": f"Recurring pattern '{tag}' across {len(contents)} episodes"
            })
        elif len(contents) >= 2:
            heuristics.append({
                "pattern": tag,
                "frequency": len(contents),
                "evidence": contents[:3],
                "heuristic": f"Emerging pattern '{tag}' ({len(contents)} episodes)"
            })

    # Build co-occurrence heuristics
    cooccur = {}
    for e in engrams:
        tags = sorted(set(t.strip().lower() for t in e["tags"].split(",")))
        for i in range(len(tags)):
            for j in range(i + 1, len(tags)):
                pair = f"{tags[i]}+{tags[j]}"
                cooccur[pair] = cooccur.get(pair, 0) + 1

    strong_links = {k: v for k, v in cooccur.items() if v >= 2}

    # Read existing profile or create new
    existing = {}
    if SEMANTIC_PROFILE.exists():
        try:
            existing = json.loads(SEMANTIC_PROFILE.read_text())
        except Exception:
            existing = {}

    # Merge: never overwrite, only add/update
    profile = {
        "last_consolidated": datetime.now().isoformat(),
        "total_episodes": len(engrams),
        "heuristics": heuristics,
        "strong_links": strong_links,
        "top_patterns": dict(sorted(tag_freq.items(), key=lambda x: -x[1])[:10]),
        # Preserve any manually added notes from previous consolidations
        "manual_notes": existing.get("manual_notes", []),
        "consolidation_count": existing.get("consolidation_count", 0) + 1,
    }

    SEMANTIC_PROFILE.write_text(json.dumps(profile, indent=2, ensure_ascii=False))
    _log(f"CONSOLIDATED | {len(heuristics)} heuristics from {len(engrams)} episodes")

    print(json.dumps({
        "consolidated": True,
        "heuristics_count": len(heuristics),
        "episodes_analyzed": len(engrams),
        "strong_links": len(strong_links),
        "profile_path": str(SEMANTIC_PROFILE)
    }, indent=2))


def cmd_profile(args):
    """Read the current semantic profile (consolidated heuristics)."""
    ensure_dirs()
    if not SEMANTIC_PROFILE.exists():
        print(json.dumps({"status": "no_profile", "hint": "run 'consolidate' first"}))
        return
    profile = json.loads(SEMANTIC_PROFILE.read_text())
    print(json.dumps(profile, indent=2, ensure_ascii=False))


def _log(message):
    """Append to improvements log."""
    ts = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
    with open(IMPROVEMENTS_LOG, "a", encoding="utf-8") as f:
        f.write(f"{ts} | {message}\n")


COMMANDS = {
    "analyze": cmd_analyze,
    "propose": cmd_propose,
    "review": cmd_review,
    "approve": cmd_approve,
    "reject": cmd_reject,
    "history": cmd_history,
    "patches": cmd_patches,
    "consolidate": cmd_consolidate,
    "profile": cmd_profile,
}


def main():
    if len(sys.argv) < 2:
        print("Usage: evolve.py <command> [args...]")
        print(f"Commands: {', '.join(COMMANDS.keys())}")
        sys.exit(1)
    cmd = sys.argv[1]
    args = sys.argv[2:]
    if cmd in COMMANDS:
        COMMANDS[cmd](args)
    else:
        print(json.dumps({"error": f"unknown command: {cmd}"}))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/memory.py
content: #!/usr/bin/env python3
"""
DeepThinking Long-Term Memory
==============================
Append-only memory log stored as plain text lines.
Each entry is a timestamped, tagged line — searchable via grep.
The agent uses this to connect ideas across sessions over time.

Storage: ~/.deepthinking/memory/engrams.log
Format:  YYYY-MM-DDTHH:MM:SS | <tags> | <content>

Design: append-only flat file. No DB. Grep is the query engine.
The agent calls `search` and gets back matching lines with context.
Over time, patterns emerge that no single session could reveal.
"""
import sys
import os
import re
import json
import subprocess
from datetime import datetime
from pathlib import Path

MEMORY_DIR = Path.home() / ".deepthinking" / "memory"
ENGRAMS_FILE = MEMORY_DIR / "engrams.log"
INDEX_FILE = MEMORY_DIR / "tags.idx"


def ensure_dir():
    MEMORY_DIR.mkdir(parents=True, exist_ok=True)
    if not ENGRAMS_FILE.exists():
        ENGRAMS_FILE.touch()


def cmd_store(args):
    """Store a new engram. Usage: memory.py store <tags> <content>
    Tags are comma-separated. Example: memory.py store "fear,career" "User afraid of leaving stable job"
    """
    if len(args) < 2:
        print(json.dumps({"error": "usage: store <tags> <content>"}))
        return
    tags = args[0]
    content = " ".join(args[1:])
    ensure_dir()
    ts = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
    line = f"{ts} | {tags} | {content}\n"
    with open(ENGRAMS_FILE, "a", encoding="utf-8") as f:
        f.write(line)
    _update_index(tags)
    count = sum(1 for _ in open(ENGRAMS_FILE, encoding="utf-8"))
    print(json.dumps({"stored": True, "tags": tags, "total_engrams": count}))


def cmd_search(args):
    """Search engrams using grep. Returns matching lines with context.
    Usage: memory.py search <query> [--tags <tag>] [--after <date>] [--limit <n>]
    """
    if not args:
        print(json.dumps({"error": "usage: search <query> [--tags tag] [--after date] [--limit n]"}))
        return
    ensure_dir()
    if not ENGRAMS_FILE.exists() or ENGRAMS_FILE.stat().st_size == 0:
        print(json.dumps({"results": [], "count": 0}))
        return

    query = args[0]
    tags_filter = None
    after_date = None
    limit = 20

    i = 1
    while i < len(args):
        if args[i] == "--tags" and i + 1 < len(args):
            tags_filter = args[i + 1]
            i += 2
        elif args[i] == "--after" and i + 1 < len(args):
            after_date = args[i + 1]
            i += 2
        elif args[i] == "--limit" and i + 1 < len(args):
            limit = int(args[i + 1])
            i += 2
        else:
            i += 1

    # Use grep for speed on large files
    try:
        cmd = ["grep", "-i", "-n", query, str(ENGRAMS_FILE)]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
        lines = result.stdout.strip().split("\n") if result.stdout.strip() else []
    except Exception:
        # Fallback to python
        lines = []
        with open(ENGRAMS_FILE, encoding="utf-8") as f:
            for num, line in enumerate(f, 1):
                if query.lower() in line.lower():
                    lines.append(f"{num}:{line.rstrip()}")

    # Apply filters
    filtered = []
    for raw in lines:
        parts = raw.split(":", 1)
        if len(parts) < 2:
            continue
        line_content = parts[1]
        # Tags filter
        if tags_filter:
            tag_section = line_content.split("|")[1].strip() if "|" in line_content else ""
            if tags_filter.lower() not in tag_section.lower():
                continue
        # Date filter
        if after_date:
            line_date = line_content[:10]
            if line_date < after_date:
                continue
        filtered.append(line_content.strip())

    results = filtered[:limit]
    print(json.dumps({"results": results, "count": len(results), "total_matches": len(filtered)}, ensure_ascii=False))


def cmd_themes(args):
    """Extract recurring themes from memory. Analyzes tag frequency and co-occurrence."""
    ensure_dir()
    if not ENGRAMS_FILE.exists():
        print(json.dumps({"themes": {}, "count": 0}))
        return

    tag_counts = {}
    cooccurrence = {}
    total = 0

    with open(ENGRAMS_FILE, encoding="utf-8") as f:
        for line in f:
            total += 1
            parts = line.split("|")
            if len(parts) < 3:
                continue
            tags = [t.strip().lower() for t in parts[1].split(",")]
            for t in tags:
                tag_counts[t] = tag_counts.get(t, 0) + 1
            # Co-occurrence pairs
            for j in range(len(tags)):
                for k in range(j + 1, len(tags)):
                    pair = tuple(sorted([tags[j], tags[k]]))
                    cooccurrence[pair] = cooccurrence.get(pair, 0) + 1

    # Sort by frequency
    sorted_tags = sorted(tag_counts.items(), key=lambda x: -x[1])[:15]
    sorted_pairs = sorted(cooccurrence.items(), key=lambda x: -x[1])[:10]

    print(json.dumps({
        "total_engrams": total,
        "top_tags": {t: c for t, c in sorted_tags},
        "co_occurring": {f"{a}+{b}": c for (a, b), c in sorted_pairs}
    }, ensure_ascii=False))


def cmd_recent(args):
    """Show most recent N engrams. Usage: memory.py recent [n]"""
    n = int(args[0]) if args else 10
    ensure_dir()
    if not ENGRAMS_FILE.exists():
        print(json.dumps({"results": [], "count": 0}))
        return
    try:
        result = subprocess.run(["tail", f"-{n}", str(ENGRAMS_FILE)],
                                capture_output=True, text=True, timeout=5)
        lines = [l.strip() for l in result.stdout.strip().split("\n") if l.strip()]
    except Exception:
        with open(ENGRAMS_FILE, encoding="utf-8") as f:
            all_lines = f.readlines()
        lines = [l.strip() for l in all_lines[-n:]]
    print(json.dumps({"results": lines, "count": len(lines)}, ensure_ascii=False))


def cmd_connect(args):
    """Find connections between two concepts. Searches for engrams containing both."""
    if len(args) < 2:
        print(json.dumps({"error": "usage: connect <concept1> <concept2>"}))
        return
    ensure_dir()
    c1, c2 = args[0].lower(), args[1].lower()
    matches = []
    with open(ENGRAMS_FILE, encoding="utf-8") as f:
        for line in f:
            low = line.lower()
            if c1 in low and c2 in low:
                matches.append(line.strip())
    # Also find bridge concepts: lines with c1 sharing tags with lines with c2
    c1_tags = set()
    c2_tags = set()
    with open(ENGRAMS_FILE, encoding="utf-8") as f:
        for line in f:
            low = line.lower()
            parts = line.split("|")
            if len(parts) >= 3:
                tags = {t.strip().lower() for t in parts[1].split(",")}
                if c1 in low:
                    c1_tags.update(tags)
                if c2 in low:
                    c2_tags.update(tags)
    shared_tags = c1_tags & c2_tags
    print(json.dumps({
        "direct_matches": matches[:10],
        "shared_tags": list(shared_tags),
        "bridge_strength": len(shared_tags)
    }, ensure_ascii=False))


def cmd_stats(args):
    """Memory stats."""
    ensure_dir()
    if not ENGRAMS_FILE.exists():
        print(json.dumps({"total": 0, "size_bytes": 0}))
        return
    total = sum(1 for _ in open(ENGRAMS_FILE, encoding="utf-8"))
    size = ENGRAMS_FILE.stat().st_size
    # Date range
    first = last = ""
    with open(ENGRAMS_FILE, encoding="utf-8") as f:
        for line in f:
            if not first:
                first = line[:10]
            last = line[:10]
    print(json.dumps({
        "total_engrams": total,
        "size_bytes": size,
        "first_entry": first,
        "last_entry": last,
        "file": str(ENGRAMS_FILE)
    }))


def _update_index(tags):
    """Lightweight tag index for fast tag listing."""
    existing = set()
    if INDEX_FILE.exists():
        existing = set(INDEX_FILE.read_text().strip().split("\n"))
    new_tags = {t.strip().lower() for t in tags.split(",")}
    all_tags = existing | new_tags
    INDEX_FILE.write_text("\n".join(sorted(all_tags)))


COMMANDS = {
    "store": cmd_store,
    "search": cmd_search,
    "themes": cmd_themes,
    "recent": cmd_recent,
    "connect": cmd_connect,
    "stats": cmd_stats,
}


def main():
    if len(sys.argv) < 2:
        print("Usage: memory.py <command> [args...]")
        print(f"Commands: {', '.join(COMMANDS.keys())}")
        sys.exit(1)
    cmd = sys.argv[1]
    args = sys.argv[2:]
    if cmd in COMMANDS:
        COMMANDS[cmd](args)
    else:
        print(json.dumps({"error": f"unknown command: {cmd}"}))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/tui.py
content: #!/usr/bin/env python3
"""
DeepThinking TUI
================
Standalone terminal user interface for the DeepThinking cognitive framework.
Runs on any VPS/server via SSH. No external dependencies — pure stdlib.

Usage:
  python3 scripts/tui.py [topic]
  ./bin/deep "I want to start a business"
  ./bin/deep          (resume existing session)
"""

import json
import os
import sys
import subprocess
import readline
import textwrap
from pathlib import Path

# ─── ANSI Color System ────────────────────────────────────────────────────────

class C:
    RESET   = "\033[0m"
    BOLD    = "\033[1m"
    DIM     = "\033[2m"
    ITALIC  = "\033[3m"

    # Text colors
    WHITE   = "\033[97m"
    GRAY    = "\033[90m"
    SILVER  = "\033[37m"
    CYAN    = "\033[96m"
    BLUE    = "\033[94m"
    MAGENTA = "\033[95m"
    YELLOW  = "\033[93m"
    GREEN   = "\033[92m"
    RED     = "\033[91m"

    # Background
    BG_DARK = "\033[48;5;235m"

def bold(s):    return f"{C.BOLD}{s}{C.RESET}"
def dim(s):     return f"{C.DIM}{C.GRAY}{s}{C.RESET}"
def cyan(s):    return f"{C.CYAN}{s}{C.RESET}"
def blue(s):    return f"{C.BLUE}{s}{C.RESET}"
def magenta(s): return f"{C.MAGENTA}{s}{C.RESET}"
def yellow(s):  return f"{C.YELLOW}{s}{C.RESET}"
def green(s):   return f"{C.GREEN}{s}{C.RESET}"
def red(s):     return f"{C.RED}{s}{C.RESET}"
def gray(s):    return f"{C.GRAY}{s}{C.RESET}"
def white(s):   return f"{C.WHITE}{C.BOLD}{s}{C.RESET}"

# ─── Layout Helpers ───────────────────────────────────────────────────────────

TERM_WIDTH = min(os.get_terminal_size().columns if sys.stdout.isatty() else 80, 90)

def ruler(char="─", color=C.GRAY):
    print(f"{color}{char * TERM_WIDTH}{C.RESET}")

def section(title, color=C.CYAN):
    print()
    ruler("─", color)
    print(f"{color}{C.BOLD} {title}{C.RESET}")
    ruler("─", color)
    print()

def wrap_print(text, indent=2, width=None):
    w = (width or TERM_WIDTH) - indent
    prefix = " " * indent
    for line in textwrap.wrap(text, width=w):
        print(f"{prefix}{line}")

def agent_say(text):
    """Agent's voice — the main conversational output."""
    print()
    prefix = f"  {cyan('◆')}  "
    w = TERM_WIDTH - 6
    lines = textwrap.wrap(text, width=w)
    for i, line in enumerate(lines):
        if i == 0:
            print(f"{prefix}{C.WHITE}{line}{C.RESET}")
        else:
            print(f"      {C.WHITE}{line}{C.RESET}")
    print()

def agent_aside(text):
    """Subtle system message — lower priority."""
    print(f"  {dim('·')}  {dim(text)}")

def phase_banner(phase_name, color=C.CYAN):
    print()
    label = f"  PHASE: {phase_name.upper()}  "
    bar = "━" * len(label)
    print(f"  {color}{C.BOLD}{bar}{C.RESET}")
    print(f"  {color}{C.BOLD}{label}{C.RESET}")
    print(f"  {color}{C.BOLD}{bar}{C.RESET}")
    print()

def prompt_input(hint="you"):
    """Read user input with a styled prompt."""
    try:
        raw = input(f"  {gray('›')} {C.WHITE}")
    except (EOFError, KeyboardInterrupt):
        print(C.RESET)
        return "/deepend"
    finally:
        print(C.RESET, end="")
    return raw.strip()

# ─── Script Runner ─────────────────────────────────────────────────────────────

BASE_DIR = Path(__file__).parent.parent

def run_script(script, *args):
    """Run a DeepThinking sub-script and return parsed JSON output."""
    cmd = [sys.executable, str(BASE_DIR / "scripts" / script)] + list(str(a) for a in args)
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
        if result.stdout.strip():
            try:
                return json.loads(result.stdout)
            except json.JSONDecodeError:
                return {"raw": result.stdout.strip()}
        return {}
    except subprocess.TimeoutExpired:
        return {"error": "timeout"}
    except Exception as e:
        return {"error": str(e)}

def state(*args):
    return run_script("state.py", *args)

def memory(*args):
    return run_script("memory.py", *args)

def evolve(*args):
    return run_script("evolve.py", *args)

# ─── Excavation Phase ─────────────────────────────────────────────────────────

EXCAVATION_QUESTIONS = [
    # Layer 0 — Surface
    lambda topic: f"Tell me more. What does \"{topic}\" actually look like in your head?",
    # Layer 1 — Context
    lambda _: "What's happening in your life right now that made this come up?",
    # Layer 2 — Energy
    lambda _: "When you imagine yourself a year from now, doing this full-time — what part gives you energy? What part feels like homework?",
    # Layer 3 — Fear
    lambda _: "What's the worst case in your head? Not the logical one. The one you don't say out loud.",
    # Layer 4 — Real Want (crafted dynamically)
    None,
]

def run_excavation(session):
    phase_banner("Excavation", C.BLUE)
    agent_aside("Five questions. One at a time. Go as deep as you want.")
    print()

    topic = session.get("topic", "")
    profile = session.get("profile", {})
    start_layer = session.get("excavation_layer", 0)

    layers_data = {
        0: profile.get("surface_ask", ""),
        1: profile.get("life_context", ""),
        2: profile.get("energy_map", ""),
        3: profile.get("fear", ""),
        4: profile.get("real_want", ""),
    }

    for layer in range(start_layer, 5):
        if layer < 4:
            question = EXCAVATION_QUESTIONS[layer](topic)
        else:
            # Layer 4: craft based on gap
            gap = _detect_gap(layers_data)
            question = gap

        agent_say(question)

        user_input = prompt_input()
        if user_input.lower() in ("/deepend", "/deepend", "exit", "quit"):
            return "exit"

        layers_data[layer] = user_input
        state("save-layer", str(layer), user_input)
        state("next-layer")

        # Store engram
        tag_map = {0: "input,topic", 1: "context,life", 2: "energy,motivation", 3: "fear", 4: "want,core"}
        tags = tag_map.get(layer, "insight")
        memory("store", tags, user_input[:200])

        # Small breath between layers
        if layer < 4:
            print()
            agent_aside(f"Layer {layer + 1}/5 saved.")

    return "done"


def _detect_gap(layers):
    surface = layers.get(0, "")
    fear = layers.get(3, "")
    if fear and len(fear) > 20:
        return f"Here's what I'm noticing: you said you want {surface[:60].lower()}... but what you described as your fear tells a different story. What do you actually want — not what you should want?"
    return "Put aside what's practical for a second. If everything worked out exactly right, what would your version of success actually feel like day-to-day?"

# ─── Alignment Phase ──────────────────────────────────────────────────────────

def run_alignment(session):
    phase_banner("Alignment", C.MAGENTA)

    profile = state("get", "profile")
    surface = profile.get("surface_ask", "")
    context = profile.get("life_context", "")
    energy = profile.get("energy_map", "")
    fear = profile.get("fear", "")
    real_want = profile.get("real_want", "")

    read = (
        f"Here's what I'm hearing. Tell me if I'm wrong:\n\n"
        f"You want {surface[:80]}, but what's driving this is something deeper — "
        f"probably connected to {context[:60] if context else 'where you are right now'}.\n\n"
        f"The thing that gives you energy: {energy[:80] if energy else 'something specific you described'}.\n\n"
        f"What you're actually afraid of isn't failure — it's {fear[:80] if fear else 'something harder to name'}.\n\n"
        f"What you really need isn't just an answer. It's {real_want[:80] if real_want else 'clarity on the real question'}.\n\n"
        f"Am I close?"
    )

    agent_say(read)

    user_input = prompt_input()
    if user_input.lower() in ("/deepend", "exit"):
        return "exit"

    lower = user_input.lower()
    if any(w in lower for w in ["yes", "sim", "yeah", "exactly", "correct", "certo", "isso"]):
        state("set-phase", "architecture")
        memory("store", "alignment,confirmed", f"User confirmed alignment on topic: {surface[:100]}")
        return "confirmed"
    else:
        # Store correction
        state("save-layer", "4", user_input)
        memory("store", "alignment,correction", user_input[:200])
        agent_say("Got it. Let me adjust...")
        return "corrected"

# ─── Architecture Phase ───────────────────────────────────────────────────────

MODULE_LOGIC = {
    "diverge":   "Can't see the options",
    "converge":  "Too many options",
    "invert":    "Too attached to one idea",
    "prototype": "Stuck in abstraction",
    "mirror":    "Orbiting the same theme",
    "reframe":   "Solving the wrong problem",
    "commit":    "Has clarity but won't act",
}

def run_architecture(session):
    phase_banner("Architecture", C.YELLOW)

    profile = state("get", "profile")
    fear = profile.get("fear", "")
    real_want = profile.get("real_want", "")
    energy = profile.get("energy_map", "")

    # Select modules heuristically
    pipeline = []

    # Always start with a diagnostic
    if "don't know" in real_want.lower() or "não sei" in real_want.lower():
        pipeline.append("diverge")
    elif "too many" in real_want.lower() or "muitas" in real_want.lower():
        pipeline.append("converge")
    else:
        pipeline.append("diverge")

    # Fear-based additions
    if fear and len(fear) > 10:
        pipeline.append("invert")
        pipeline.append("mirror")
    else:
        pipeline.append("mirror")

    # Energy-based closer
    if "practical" in energy.lower() or "action" in energy.lower() or "fazer" in energy.lower():
        pipeline.append("prototype")
    pipeline.append("commit")

    # Deduplicate, cap at 4
    seen = set()
    final = []
    for m in pipeline:
        if m not in seen:
            seen.add(m)
            final.append(m)
    final = final[:4]

    # Present the plan
    agent_say(f"I built a custom pipeline for you. Here's the plan:\n")
    for i, mod in enumerate(final, 1):
        mod_data = _get_module_data(mod)
        use_when_label = dim(f"Use when: {mod_data['use_when']}")
        print(f"  {yellow(f'Phase {i}:')} {bold(mod_data['name'])} — {mod_data['purpose']}")
        print(f"    {use_when_label}")
        print()

    agent_say("Not rigid. We adapt as we go. About 4-6 exchanges per phase.\n\nReady?")

    user_input = prompt_input()
    if user_input.lower() in ("/deepend", "exit"):
        return "exit"

    # Save pipeline
    state("set-pipeline", ",".join(final), "--name", "Custom Pipeline v1")
    state("set-phase", "execution")

    return "ready"


def _get_module_data(mod_id):
    """Get module info from state.py output."""
    # Use state.py list-modules or inline our local copy
    modules = {
        "diverge":   {"name": "Diverge",   "purpose": "Generate possibilities without judgment", "use_when": "Stuck on one path"},
        "converge":  {"name": "Converge",  "purpose": "Filter with criteria. Kill options ruthlessly.", "use_when": "Too many options"},
        "invert":    {"name": "Invert",    "purpose": "Stress-test. Pre-mortem. Steelman the opposite.", "use_when": "Too attached to one idea"},
        "prototype": {"name": "Prototype", "purpose": "Make it concrete. 48 hours, $0.", "use_when": "Stuck in abstraction"},
        "mirror":    {"name": "Mirror",    "purpose": "Reflect patterns the user can't see.", "use_when": "Orbiting same theme"},
        "reframe":   {"name": "Reframe",   "purpose": "Change the problem entirely.", "use_when": "Solving the wrong problem"},
        "commit":    {"name": "Commit",    "purpose": "Force decision. First action. Deadline.", "use_when": "Has clarity but won't act"},
    }
    return modules.get(mod_id, {"name": mod_id, "purpose": "?", "use_when": "?"})

# ─── Execution Phase ──────────────────────────────────────────────────────────

def run_execution(session):
    phase_banner("Execution", C.CYAN)

    while True:
        mod_info = state("current-module")
        if mod_info.get("status") == "done":
            break

        mod_id   = mod_info.get("module_id", "")
        mod_name = mod_info.get("name", mod_id)
        prompts  = mod_info.get("prompts", [])
        idx      = mod_info.get("index", 0)
        total    = mod_info.get("total", 1)

        section(f"Module {idx+1}/{total}: {mod_name}", C.CYAN)
        agent_aside(mod_info.get("purpose", ""))
        print()

        # Also load any evolved patches for this module
        patches = evolve("patches")
        extra_prompts = []
        if isinstance(patches, dict) and mod_id in patches:
            patch_text = patches[mod_id]
            extra_prompts = [line.lstrip("- ").strip() for line in patch_text.splitlines() if line.strip().startswith("-")]

        all_prompts = prompts + extra_prompts
        insights = []
        prompt_index = 0
        exchanges = 0
        min_ex = mod_info.get("min_exchanges", 3)

        while prompt_index < len(all_prompts):
            q = all_prompts[prompt_index]
            agent_say(q)
            prompt_index += 1

            user_input = prompt_input()
            if user_input.lower() in ("/deepend", "exit"):
                return "exit"
            if user_input.lower() in ("next", "done", "proximo", "próximo", "fim"):
                break

            exchanges += 1
            insights.append(user_input[:150])

            # Store engram from key exchanges
            if exchanges % 2 == 0 or exchanges == 1:
                memory("store", f"{mod_id},execution", user_input[:200])

            # Push back on lazy answers
            lazy_signals = ["not sure", "maybe", "i don't know", "não sei", "talvez", "depende"]
            if any(s in user_input.lower() for s in lazy_signals) and exchanges < 3:
                agent_say("That sounds like a warm-up answer. What's the real one?")
                user_input2 = prompt_input()
                if user_input2.lower() not in ("/deepend", "exit"):
                    insights.append(user_input2[:150])
                    memory("store", f"{mod_id},breakthrough", user_input2[:200])

            # Celebrate breakthroughs
            if len(user_input) > 100 and any(w in user_input.lower() for w in ["realize", "percebi", "actually", "na verdade"]):
                print()
                agent_aside("Yeah. Write that down.")
                print()

            if exchanges >= min_ex and prompt_index >= len(all_prompts):
                break

        # Save module output
        pipe_insights = " | ".join(insights[:3]) if insights else "no insights captured"
        state("save-module-output", mod_id, pipe_insights)
        state("next-module")

        print()
        agent_aside(f"✓ {mod_name} complete.")
        print()

    return "done"

# ─── Synthesis Phase ──────────────────────────────────────────────────────────

def run_synthesis(session):
    phase_banner("Synthesis", C.GREEN)

    profile = state("get", "profile")
    outputs = state("get", "outputs")

    topic   = session.get("topic", "your question")
    surface = profile.get("surface_ask", "")
    fear    = profile.get("fear", "")
    want    = profile.get("real_want", "")

    # Build synthesis from outputs
    module_insights = []
    if isinstance(outputs, dict):
        for mod_id, out in outputs.items():
            if isinstance(out, dict):
                insigs = out.get("insights", [])
                if insigs:
                    module_insights.extend(insigs[:2])

    fear_fallback      = "what you're afraid of"
    next_fallback      = "Whatever you said you'd do — start with that one thing."
    tension_part       = fear[:60] if fear else fear_fallback
    surprise_part      = module_insights[0] if module_insights else want
    next_move_part     = module_insights[-1] if len(module_insights) > 1 else next_fallback

    agent_say(
        f"Here's what came out of this session on \"{topic}\".\n\n"
        f"You came in asking about {surface[:60]}. What emerged is different: "
        f"the real tension is between what you want and {tension_part}.\n\n"
        f"The thing that surprised me: {surprise_part}.\n\n"
        f"Your next move: {next_move_part}.\n\n"
        f"What I'd push back on: don't mistake clarity here for a decision made. "
        f"The real test is what you do in the next 48 hours."
    )

    # Store core engram
    core_insight = module_insights[0] if module_insights else want
    memory("store", "synthesis,core", core_insight[:200])

    print()
    agent_say("Want me to:\n  1. Save this as a document\n  2. Build a 7-day action plan\n  3. Keep exploring a thread\n  4. End here")

    choice = prompt_input()

    if "1" in choice or "document" in choice.lower() or "salvar" in choice.lower():
        _save_synthesis_doc(session, profile, module_insights)
    elif "2" in choice or "plan" in choice.lower() or "plano" in choice.lower():
        _build_action_plan(module_insights)
    elif "3" in choice or "explor" in choice.lower():
        agent_say("Which thread? Tell me what's still pulling at you.")
        prompt_input()  # let them express, then end
    else:
        agent_say("Good session. State archived.")

    state("archive")
    return "done"


def _save_synthesis_doc(session, profile, insights):
    from datetime import datetime
    out_dir = Path.home() / ".deepthinking" / "outputs"
    out_dir.mkdir(parents=True, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d-%H%M")
    topic_slug = session.get("topic", "session")[:30].replace(" ", "-").lower()
    out_file = out_dir / f"{ts}-{topic_slug}.md"

    lines = [
        f"# DeepThinking Session: {session.get('topic', '')}",
        f"*{datetime.now().strftime('%B %d, %Y')}*\n",
        "## What You Came In With",
        profile.get("surface_ask", ""),
        "\n## What Emerged",
    ] + [f"- {i}" for i in insights] + [
        "\n## Your Next Move",
        insights[-1] if insights else "—",
    ]

    out_file.write_text("\n".join(lines))
    agent_aside(f"Saved → {out_file}")


def _build_action_plan(insights):
    agent_say("7-Day Action Plan:\n")
    days = ["Today", "Day 2", "Day 3", "Day 5", "Day 7"]
    for i, day in enumerate(days):
        if i < len(insights):
            print(f"  {yellow(day+':')} {insights[i][:80]}")
        else:
            print(f"  {dim(day+':')} {dim('Your call.')}")
    print()

# ─── Main Loop ────────────────────────────────────────────────────────────────

def print_header():
    ruler("═", C.CYAN)
    print(f"\n  {C.CYAN}{C.BOLD}◆  DeepThinking{C.RESET}  {dim('stateful cognitive framework')}")
    print(f"  {dim('Commands: /deepend to pause · /deep to resume · Ctrl+C to quit')}\n")
    ruler("═", C.CYAN)

def main():
    print_header()

    # Determine topic
    topic_arg = " ".join(sys.argv[1:]).strip()

    # Check existing session
    session_status = state("status")

    if session_status.get("status") == "no_session":
        if not topic_arg:
            print()
            agent_say("What would you like to explore?")
            topic_arg = prompt_input()
            if topic_arg.lower() in ("/deepend", "exit", ""):
                print()
                agent_aside("Nothing started. Come back anytime.")
                return

        agent_aside(f"Starting new session: \"{topic_arg}\"")
        state("init", "--topic", topic_arg)
        session_data = state("status")

        # Load profile silently
        evo_profile = evolve("profile")
        if evo_profile.get("status") != "no_profile":
            agent_aside("Profile loaded.")

        # Search memory for related context
        keywords = " ".join(topic_arg.split()[:3])
        mem_results = memory("search", keywords)
        # (silently absorbed — not announced to user)

    else:
        topic_arg = session_status.get("topic", "existing session")
        phase = session_status.get("phase", "excavation")
        agent_aside(f"Resuming: \"{topic_arg}\" — phase: {phase}")
        session_data = session_status

    # ── Phase Loop ──
    while True:
        session_data = state("status")
        phase = session_data.get("phase", "excavation")

        if phase == "excavation":
            result = run_excavation(session_data)
            if result == "exit":
                _handle_exit(session_data)
                return

        elif phase == "alignment":
            result = run_alignment(session_data)
            if result == "exit":
                _handle_exit(session_data)
                return

        elif phase == "architecture":
            result = run_architecture(session_data)
            if result == "exit":
                _handle_exit(session_data)
                return

        elif phase == "execution":
            result = run_execution(session_data)
            if result == "exit":
                _handle_exit(session_data)
                return

        elif phase == "synthesis":
            run_synthesis(session_data)
            print()
            ruler("═", C.CYAN)
            print(f"\n  {green('◆')} {bold('Session complete.')}")
            print(f"  {dim('Start a new one anytime with: ./bin/deep')}\n")
            ruler("═", C.CYAN)
            return

        else:
            agent_aside(f"Unknown phase: {phase}")
            return


def _handle_exit(session_data):
    print()
    ruler("─", C.GRAY)
    agent_say(
        "Pausing here. State saved.\n\n"
        "One thing to sit with: what you said about your fear — that's the real thread.\n\n"
        "Pick up anytime: ./bin/deep"
    )
    ruler("─", C.GRAY)
    print()


if __name__ == "__main__":
    main()


path: scripts/state.py
content: #!/usr/bin/env python3
"""
DeepThinking State Manager
==========================
Persists session state to ~/.deepthinking/current/state.json
The OpenClaw agent calls this script to read/write state between turns.
"""
import json
import os
import sys
import shutil
from datetime import datetime
from pathlib import Path

STATE_DIR = Path.home() / ".deepthinking" / "current"
STATE_FILE = STATE_DIR / "state.json"
ARCHIVE_DIR = Path.home() / ".deepthinking" / "archive"

MODULES = {
    "diverge": {
        "name": "Diverge",
        "purpose": "Generate possibilities without judgment",
        "use_when": "Stuck on one path",
        "prompts": [
            "Give me the first thing that comes to mind. Don't filter.",
            "Now the opposite. What would surprise everyone?",
            "Past the obvious ones. What would you never say out loud?",
            "If money didn't matter? If you had zero money?",
            "What would your 80-year-old self wish you tried?"
        ],
        "exchanges": [3, 7]
    },
    "converge": {
        "name": "Converge",
        "purpose": "Filter with criteria. Kill options ruthlessly.",
        "use_when": "Too many options",
        "prompts": [
            "What 3 things ACTUALLY matter here? Not should-matter.",
            "Which options fail on criterion #1?",
            "Which survive all three?",
            "You're hesitating on one you should cut. Why?"
        ],
        "exchanges": [3, 5]
    },
    "invert": {
        "name": "Invert",
        "purpose": "Stress-test. Pre-mortem. Steelman the opposite.",
        "use_when": "Too attached to one idea",
        "prompts": [
            "I'm going to argue against your idea. If it survives, you'll trust it more.",
            "It's 12 months from now and this failed. What happened?",
            "Strongest argument against this. How do you respond?",
            "What are you deliberately not thinking about?"
        ],
        "exchanges": [3, 5]
    },
    "prototype": {
        "name": "Prototype",
        "purpose": "Make it concrete. 48 hours, $0.",
        "use_when": "Stuck in abstraction",
        "prompts": [
            "Smallest version testable in 48h spending nothing?",
            "ONE thing you'd measure to know if it worked?",
            "What can you do TODAY?",
            "Who's the first person you'd show this to?"
        ],
        "exchanges": [3, 5]
    },
    "mirror": {
        "name": "Mirror",
        "purpose": "Reflect patterns the user can't see.",
        "use_when": "Orbiting same theme",
        "prompts": [
            "You keep coming back to the same theme. Do you see it?",
            "Your energy shifts when you talk about this. What's that?",
            "What you say you want and what you describe are different."
        ],
        "exchanges": [2, 4]
    },
    "reframe": {
        "name": "Reframe",
        "purpose": "Change the problem entirely.",
        "use_when": "Solving the wrong problem",
        "prompts": [
            "You might be solving the wrong problem.",
            "What if the question isn't about that at all?",
            "You're assuming something that might not be true."
        ],
        "exchanges": [2, 4]
    },
    "commit": {
        "name": "Commit",
        "purpose": "Force decision. First action. Deadline.",
        "use_when": "Has clarity but won't act",
        "prompts": [
            "You already know. Say it.",
            "First ACTION. Not plan. First thing you'll do.",
            "By when? A day, not 'soon.'",
            "Who will you tell? Commitment needs a witness."
        ],
        "exchanges": [3, 5]
    }
}


def default_state(topic):
    return {
        "version": "0.1.0",
        "created": datetime.now().isoformat(),
        "updated": datetime.now().isoformat(),
        "topic": topic,
        "phase": "excavation",
        "excavation_layer": 0,
        "profile": {
            "surface_ask": topic,
            "life_context": "",
            "energy_map": "",
            "fear": "",
            "real_want": "",
            "alignment": ""
        },
        "pipeline": {
            "name": "",
            "modules": [],
            "current_index": 0
        },
        "module_outputs": {},
        "exchange_count": 0
    }


def load():
    if not STATE_FILE.exists():
        return None
    with open(STATE_FILE) as f:
        return json.load(f)


def save(state):
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    state["updated"] = datetime.now().isoformat()
    with open(STATE_FILE, "w") as f:
        json.dump(state, f, indent=2, ensure_ascii=False)


def cmd_init(args):
    topic = args[0] if args else "untitled"
    state = default_state(topic)
    save(state)
    print(json.dumps({"status": "initialized", "topic": topic, "phase": "excavation"}))


def cmd_status(args):
    state = load()
    if not state:
        print(json.dumps({"status": "no_session"}))
        return
    out = {
        "topic": state["topic"],
        "phase": state["phase"],
        "excavation_layer": state["excavation_layer"],
        "pipeline": state["pipeline"]["name"] or "(not set)",
        "current_module": _current_module_name(state),
        "created": state["created"],
        "updated": state["updated"]
    }
    print(json.dumps(out))


def cmd_get(args):
    state = load()
    if not state:
        print(json.dumps({"error": "no session"}))
        return
    key = args[0] if args else ""
    if key == "profile":
        print(json.dumps(state["profile"], ensure_ascii=False))
    elif key == "outputs":
        print(json.dumps(state["module_outputs"], ensure_ascii=False))
    elif key == "excavation_layer":
        print(json.dumps({"layer": state["excavation_layer"]}))
    elif key == "phase":
        print(json.dumps({"phase": state["phase"]}))
    else:
        print(json.dumps({"error": f"unknown key: {key}"}))


def cmd_save_layer(args):
    state = load()
    if not state:
        print(json.dumps({"error": "no session"}))
        return
    layer = int(args[0])
    summary = " ".join(args[1:])
    fields = ["surface_ask", "life_context", "energy_map", "fear", "real_want"]
    if 0 <= layer < len(fields):
        state["profile"][fields[layer]] = summary
        save(state)
        print(json.dumps({"saved": fields[layer], "layer": layer}))
    else:
        print(json.dumps({"error": f"invalid layer: {layer}"}))


def cmd_next_layer(args):
    state = load()
    if not state:
        return
    state["excavation_layer"] = min(state["excavation_layer"] + 1, 5)
    state["exchange_count"] = 0
    if state["excavation_layer"] >= 5:
        state["phase"] = "alignment"
    save(state)
    print(json.dumps({"layer": state["excavation_layer"], "phase": state["phase"]}))


def cmd_set_phase(args):
    state = load()
    if not state:
        return
    phase = args[0] if args else ""
    valid = ["excavation", "alignment", "architecture", "execution", "synthesis", "done"]
    if phase in valid:
        state["phase"] = phase
        state["exchange_count"] = 0
        save(state)
        print(json.dumps({"phase": phase}))
    else:
        print(json.dumps({"error": f"invalid phase: {phase}", "valid": valid}))


def cmd_list_modules(args):
    for mid, m in MODULES.items():
        print(f"  {mid:12s} | {m['name']:10s} | {m['purpose']} | Use when: {m['use_when']}")


def cmd_set_pipeline(args):
    state = load()
    if not state:
        return
    modules_str = args[0] if args else ""
    name = ""
    for i, a in enumerate(args):
        if a == "--name" and i + 1 < len(args):
            name = args[i + 1]
            break
    module_ids = [m.strip() for m in modules_str.split(",") if m.strip()]
    invalid = [m for m in module_ids if m not in MODULES]
    if invalid:
        print(json.dumps({"error": f"unknown modules: {invalid}"}))
        return
    state["pipeline"] = {
        "name": name or f"Pipeline ({','.join(module_ids)})",
        "modules": module_ids,
        "current_index": 0
    }
    save(state)
    print(json.dumps({"pipeline": state["pipeline"]["name"], "modules": module_ids}))


def cmd_current_module(args):
    state = load()
    if not state:
        return
    idx = state["pipeline"]["current_index"]
    modules = state["pipeline"]["modules"]
    if idx >= len(modules):
        print(json.dumps({"status": "done", "message": "All modules complete"}))
        return
    mid = modules[idx]
    m = MODULES[mid]
    print(json.dumps({
        "module_id": mid,
        "name": m["name"],
        "purpose": m["purpose"],
        "prompts": m["prompts"],
        "min_exchanges": m["exchanges"][0],
        "max_exchanges": m["exchanges"][1],
        "index": idx,
        "total": len(modules)
    }, ensure_ascii=False))


def cmd_save_module_output(args):
    state = load()
    if not state:
        return
    mid = args[0] if args else ""
    insights = " ".join(args[1:])
    state["module_outputs"][mid] = {
        "insights": insights.split("|") if "|" in insights else [insights],
        "timestamp": datetime.now().isoformat()
    }
    save(state)
    print(json.dumps({"saved": mid}))


def cmd_next_module(args):
    state = load()
    if not state:
        return
    state["pipeline"]["current_index"] += 1
    state["exchange_count"] = 0
    idx = state["pipeline"]["current_index"]
    total = len(state["pipeline"]["modules"])
    if idx >= total:
        state["phase"] = "synthesis"
    save(state)
    print(json.dumps({"index": idx, "total": total, "phase": state["phase"]}))


def cmd_archive(args):
    state = load()
    if not state:
        return
    ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d-%H%M%S")
    topic_slug = state["topic"][:30].replace(" ", "-").lower()
    archive_name = f"{ts}-{topic_slug}.json"
    dest = ARCHIVE_DIR / archive_name
    shutil.copy2(STATE_FILE, dest)
    STATE_FILE.unlink()
    print(json.dumps({"archived": str(dest)}))


def cmd_reset(args):
    if STATE_FILE.exists():
        STATE_FILE.unlink()
    print(json.dumps({"status": "reset"}))


def _current_module_name(state):
    idx = state["pipeline"]["current_index"]
    modules = state["pipeline"]["modules"]
    if not modules or idx >= len(modules):
        return "(none)"
    return MODULES.get(modules[idx], {}).get("name", modules[idx])


COMMANDS = {
    "init": cmd_init,
    "status": cmd_status,
    "get": cmd_get,
    "save-layer": cmd_save_layer,
    "next-layer": cmd_next_layer,
    "set-phase": cmd_set_phase,
    "list-modules": cmd_list_modules,
    "set-pipeline": cmd_set_pipeline,
    "current-module": cmd_current_module,
    "save-module-output": cmd_save_module_output,
    "next-module": cmd_next_module,
    "archive": cmd_archive,
    "reset": cmd_reset,
}


def main():
    if len(sys.argv) < 2:
        print("Usage: state.py <command> [args...]")
        print(f"Commands: {', '.join(COMMANDS.keys())}")
        sys.exit(1)
    cmd = sys.argv[1]
    args = sys.argv[2:]
    if cmd in COMMANDS:
        COMMANDS[cmd](args)
    else:
        print(json.dumps({"error": f"unknown command: {cmd}"}))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: references/modules.md
content: # Cognitive Modules Reference

Select 3-5 based on the user's Profile. These are not just categories; they are psychological interventions.

## DIVERGE
**Purpose:** Shatter premature convergence. Generate possibilities without the burden of judgment.
**Use when:** The user is stuck on one path, feels trapped by "the only logical choice", or is overly practical.
**Signal:** "I have to do X because Y." or "There are no other options."

**The Funnel:**
1. **Uncap:** "If money, reputation, and physics didn't matter, what's the most absurd way to solve this?"
2. **Invert the Goal:** "What's the exact opposite of what you're trying to achieve right now?"
3. **The Secret Answer:** "What's the idea you've already had but haven't said out loud because it feels irresponsible?"
4. **Push Past 5:** (When they give an idea) "Good. Give me three more that are dumber than that."

**Friction Handler:** If they say "I don't know" or "That's not helpful", say: "I'm not asking for a plan. I'm asking for a hallucination. Play along."
**Exit Condition:** User produces at least one viable lateral idea that surprises them.

---

## CONVERGE
**Purpose:** Filter options ruthlessly. Kill darlings.
**Use when:** The user has too many options, is paralyzed by choice, or is clinging to "keeping doors open."
**Signal:** "I have all these great ideas" or "I just don't want to choose the wrong one."

**The Funnel:**
1. **The Core Metric:** "Out of all these variables, what is the ONE actual metric of success? Not three. One."
2. **The Sacrifice:** "Which of these options are you keeping alive just because you're scared to let it go?"
3. **The Bottom Half:** "Cut the bottom 50% right now. Tell me which ones die."
4. **The Resistance Check:** "Notice which one you hesitated to kill just now. Why?"

**Friction Handler:** If they refuse to cut, zero in: "Attachment is data. Why are you treating [Option X] like a lifeline?"
**Exit Condition:** User narrows down to 1-2 actual, viable paths.

---

## INVERT
**Purpose:** Stress-test the chosen path through extreme pre-mortem framing.
**Use when:** The user is overly optimistic, blind to risk, or deeply attached to one specific outcome.
**Signal:** "This is definitely going to work" or "I just need to execute."

**The Funnel:**
1. **The Pre-mortem:** "Fast forward 12 months. This failed spectacularly. What exactly happened?"
2. **Steelman the Enemy:** "Give me the strongest possible argument for why you should NOT do this."
3. **The Blindspot:** "What is the one variable you are deliberately avoiding thinking about because it stresses you out?"
4. **The Kill Condition:** "What specific evidence would convince you to abandon this idea tomorrow?"

**Friction Handler:** If they say "It won't fail", say: "Hubris is a strategy, just not a good one. Humor me. It failed. Why?"
**Exit Condition:** User articulates a tangible risk they were previously ignoring.

---

## PROTOTYPE
**Purpose:** Force abstract ideas into concrete, immediate reality.
**Use when:** The user is stuck in "planning mode", over-intellectualizing, or building massive systems before testing.
**Signal:** "I'm just figuring out the tech stack" or "I need to plan the next two years."

**The Funnel:**
1. **The Reality Check:** "Planning is just procrastination with a better reputation. What's the smallest version of this?"
2. **The 48h Constraint:** "If someone put a gun to your head and said you have to launch a proxy of this in 48 hours for $0. How?"
3. **The Single Action:** "What is the ONE action you can take *today* that creates irreversible momentum?"
4. **The Target:** "Who is the FIRST person you are going to show this to?"

**Friction Handler:** If they say "It takes longer than that", say: "You're building the cathedral. I'm asking for a brick. What's the brick?"
**Exit Condition:** User commits to a micro-action they will execute in the next 24-48 hours.

---

## MIRROR
**Purpose:** Reflect unseen behavioral and energetic patterns back to the user.
**Use when:** The user is orbiting the same theme repeatedly, contradicting themselves, or displaying sudden energy shifts.
**Signal:** They say they want X, but describe Y with passion.

**The Funnel (Non-linear - use as observations):**
1. **The Theme:** "You realize you've circled back to [Theme] three times in the last 10 minutes, right?"
2. **The Disconnect:** "You *say* you want this promotion, but your energy completely drops when you describe the work. Why?"
3. **The Word Choice:** "Notice how you used the word '[Specific Word]' to describe this? That's not an accident."
4. **The Silence:** "I'm going to repeat what you just said: '[Quote]'. Sit with that for a second."

**Friction Handler:** Do not interpret for them. Just point. If they deflect, simply state: "I'm just holding the mirror. You tell me what it means."
**Exit Condition:** User acknowledges a contradiction or a hidden driver.

---

## REFRAME
**Purpose:** Shift the fundamental underlying question.
**Use when:** The user is solving the wrong problem, caught in a false dichotomy, or attacking symptoms instead of source.
**Signal:** "How do I make my boss like me?" or "Should I do A or B?"

**The Funnel:**
1. **The False Premise:** "You're asking how to fix X. The actual question is why you're tolerant of X in the first place."
2. **The Zoom Out:** "If we zoom out 10,000 feet, what is the *actual* system failure here?"
3. **The Dichotomy Bust:** "You're acting like A and B are the only options. Break the frame. What's option C?"
4. **The Core Assumption:** "What is the one thing you believe to be absolutely true about this situation that might be completely false?"

**Friction Handler:** If they resist the reframe, say: "If your current framing was working, you wouldn't be stuck. Let's try looking at it sideways."
**Exit Condition:** User articulates the problem in a fundamentally different, more empowering way.

---

## COMMIT
**Purpose:** Convert clarity into binding action and force a timeline.
**Use when:** The user has achieved the insight, but is lingering to avoid the discomfort of starting.
**Signal:** "I think I know what to do now" or "That makes a lot of sense."

**The Funnel:**
1. **The Articulation:** "You know the answer. Say it clearly right now."
2. **The First Domino:** "What is the *first physical action*? Not a plan, not a thought. A verb."
3. **The Timeline:** "By when? Give me a day. 'Soon' doesn't exist."
4. **The Stake:** "Who are you going to text right now to tell them you are doing this?"

**Friction Handler:** If they hedge ("I'll try", "I'll think about it"), call it out: "'Try' is a linguistic trick to avoid failure. Are you doing this or not?"
**Exit Condition:** User states a concrete action, a deadline, and an accountability mechanism.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)