claudia-mentor 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A Claude Code plugin that acts as your technology mentor, security advisor, and prompt coach. Claudia fills the gaps between writing code and making good technology decisions.
4
4
 
5
- **10 knowledge domains. 7 automated hooks. Context-aware advice.**
5
+ **10 knowledge domains. 14 hooks. 11 commands. Beginner-friendly.**
6
6
 
7
7
  ## What Claudia Does
8
8
 
@@ -11,7 +11,8 @@ A Claude Code plugin that acts as your technology mentor, security advisor, and
11
11
  - **Anti-pattern detection** -- Warns about common mistakes across code, Docker, dependencies, accessibility, and git hygiene
12
12
  - **Prompt coaching** -- Detects vague prompts and suggests improvements to get better results from Claude
13
13
  - **Context-aware** -- Reads your package.json, configs, and stack to give specific advice, not generic recommendations
14
- - **Project health audits** -- `/claudia-health` scans your project for security, testing, dependency, and architecture issues
14
+ - **Project health audits** -- `/claudia:health` scans your project for security, testing, dependency, and architecture issues
15
+ - **Beginner mode** -- Simplified greeting, stuck detection, run suggestions, milestone celebrations, progressive command reveal
15
16
 
16
17
  ## Install
17
18
 
@@ -27,17 +28,23 @@ To verify, type `/claudia:ask are you there?` inside Claude Code.
27
28
 
28
29
  ### Slash Commands
29
30
 
30
- Commands are namespaced under `claudia-mentor:`. Type `/claudia-mentor:` and tab to see all options.
31
+ Type `/claudia:` and tab to see all options.
31
32
 
32
33
  ```
33
34
  /claudia:ask what database should I use for time-series data?
34
- /claudia:explain # Explain the code that was just written
35
+ /claudia:explain # Explain code, a technology, or a concept
35
36
  /claudia:explain src/auth.ts # Explain a specific file
36
37
  /claudia:review # Review recent changes for bugs
37
38
  /claudia:review feature-branch # Review a specific branch
38
39
  /claudia:why # Explain why your project uses this stack
39
40
  /claudia:why prisma # Explain a specific technology choice
40
41
  /claudia:health # Run a full project health audit
42
+ /claudia:wtf # Break down an error: What / Why / Fix
43
+ /claudia:where # Guided tour of your project structure
44
+ /claudia:resume # Pick up where you left off
45
+ /claudia:shortcuts # Keyboard shortcut reference
46
+ /claudia:setup # First-time onboarding
47
+ /claudia:start # Create a new project from scratch
41
48
  ```
42
49
 
43
50
  ### Automatic (Model-Invoked)
@@ -51,45 +58,40 @@ Claudia automatically activates when you:
51
58
 
52
59
  ### Hooks (Always Active)
53
60
 
54
- **Secret detection** (blocks):
55
- - AWS access keys, OpenAI/Stripe keys, GitHub tokens, GitLab PATs, Slack tokens
56
- - Hardcoded passwords, secrets, and API keys
57
- - Database connection strings with credentials
58
- - Private keys
59
-
60
- **Anti-pattern warnings** (advisory):
61
- - `eval()` usage, `document.write()`, `innerHTML`
62
- - `console.log` in production code
63
- - Empty catch blocks
64
- - HTTP URLs (non-HTTPS), disabled SSL verification
65
- - SQL string concatenation
66
- - `chmod 777`
67
- - TODO/FIXME markers in new code
68
-
69
- **Dependency audit** (advisory):
70
- - Deprecated packages (moment, request, gulp)
71
- - Compromised packages (colors, faker, event-stream)
72
- - Trivial packages (is-odd, is-even, left-pad)
73
-
74
- **Dockerfile lint** (advisory):
75
- - Running as root, large base images, `:latest` tag
76
- - Missing multi-stage builds, secrets in ENV
77
- - npm install without --production
78
-
79
- **Git hygiene** (blocks/advisory):
80
- - Writing to .env files (blocks)
81
- - Merge conflict markers in code (blocks)
82
- - Large binary files (advisory)
83
-
84
- **Accessibility** (advisory):
85
- - Images without alt text
86
- - Inputs without labels
87
- - Icon-only buttons without aria-label
88
- - Click handlers on non-interactive elements
89
- - Positive tabIndex values
90
-
91
- **License compliance** (advisory):
92
- - Copyleft dependencies (GPL, AGPL, SSPL) in permissive-licensed projects
61
+ **7 file-check hooks** (run on every file write):
62
+
63
+ | Hook | Type | What it catches |
64
+ |------|------|-----------------|
65
+ | Secret detection | blocks | AWS keys, API tokens, passwords, private keys, connection strings |
66
+ | Bad practices | warns | `eval()`, empty catch, `console.log` in prod, SQL concat, `chmod 777` |
67
+ | Dependency audit | warns | Deprecated, compromised, or trivial packages |
68
+ | Dockerfile lint | warns | Running as root, large images, secrets in ENV, missing multi-stage |
69
+ | Git hygiene | blocks | .env writes, merge conflict markers. Warns on large binaries |
70
+ | Accessibility | warns | Missing alt text, unlabeled inputs, icon-only buttons, div click handlers |
71
+ | License compliance | warns | GPL/AGPL dependencies in permissive-licensed projects |
72
+
73
+ **7 proactive hooks** (watch your conversation):
74
+
75
+ | Hook | Event | What it does |
76
+ |------|-------|--------------|
77
+ | Teach | Stop | Explains tech keywords, reveals commands contextually |
78
+ | Compact tip | PreCompact | Tips on context compaction |
79
+ | Session tips | SessionStart | Rotating tips, beginner-simplified greeting |
80
+ | Prompt coach | UserPromptSubmit | Stuck detection, vague prompt coaching |
81
+ | Run suggest | Stop | Tells beginners how to run created files |
82
+ | Next steps | Stop | Suggests 2-3 contextual next actions |
83
+ | Milestones | Stop | Celebrates achievements (persistent across sessions) |
84
+
85
+ ## Beginner Mode
86
+
87
+ Set `"experience": "beginner"` in `~/.claude/claudia-context.json` (or run `/claudia:setup`) and Claudia adapts:
88
+
89
+ - **Simplified greeting** -- No command list on startup. Just "Claudia is here. Just build. I'm watching."
90
+ - **Stuck detection** -- Type "I'm stuck" or "help" and she asks one clarifying question, then suggests one small next step
91
+ - **Run suggestions** -- After a file is created, she tells you how to run it
92
+ - **Next-step suggestions** -- When Claude finishes a task, she suggests what to try next
93
+ - **Milestones** -- Celebrates first file, first bug fix, first commit. Persists across sessions
94
+ - **Progressive command reveal** -- Commands appear when relevant, not all at once
93
95
 
94
96
  ## Knowledge Domains
95
97
 
@@ -212,9 +214,9 @@ This creates the directory structure and template files. Fill in the SKILL.md, a
212
214
 
213
215
  Hooks run locally with the same permissions as Claude Code. Malicious hooks could read files, exfiltrate data, or modify code silently.
214
216
 
215
- - **All hook/script PRs require maintainer review** markdown-only reference PRs are lower risk
216
- - **No network calls in hooks** no fetching URLs, no phoning home, local filesystem only
217
- - **No obfuscated code** every line must be readable and understandable
217
+ - **All hook/script PRs require maintainer review** -- markdown-only reference PRs are lower risk
218
+ - **No network calls in hooks** -- no fetching URLs, no phoning home, local filesystem only
219
+ - **No obfuscated code** -- every line must be readable and understandable
218
220
  - Hook PRs get a dedicated security review with line-by-line diff and sandboxed testing
219
221
 
220
222
  ### Writing Guidelines
package/commands/ask.md CHANGED
@@ -13,10 +13,13 @@ You are Claudia, a proactive technology mentor. The user is asking you a direct
13
13
  ## Other Commands
14
14
 
15
15
  If the user seems to want one of these instead, tell them:
16
- - **Explain code**: `/claudia:explain` + file or topic
16
+ - **Explain code or concept**: `/claudia:explain` + file or topic
17
17
  - **Review changes**: `/claudia:review` + file or branch
18
18
  - **Why this stack**: `/claudia:why` + technology name
19
19
  - **Project health**: `/claudia:health`
20
+ - **Error explainer**: `/claudia:wtf` + paste the error
21
+ - **Project tour**: `/claudia:where`
22
+ - **Keyboard shortcuts**: `/claudia:shortcuts`
20
23
 
21
24
  ## How to Respond
22
25
 
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: Explain the code Claude just wrote like you're new to this
3
- argument-hint: [file or topic to explain]
2
+ description: Explain code, a technology, or a concept in plain language
3
+ argument-hint: [file, topic, or technology to explain]
4
4
  allowed-tools: [Read, Glob, Grep, Bash, WebSearch]
5
5
  ---
6
6
 
@@ -16,6 +16,7 @@ You are Claudia, a technology mentor. The user wants you to explain code they're
16
16
 
17
17
  2. Figure out what to explain:
18
18
  - If the user named a specific file → read that file
19
+ - If the user named a technology, concept, or tool (e.g. "astro", "websockets", "docker") → explain that thing directly. Do NOT tie it to the current project or Claudia. Just explain the concept.
19
20
  - If the user said "the last thing you wrote" or similar → run `git diff HEAD~1` to see recent changes
20
21
  - If the user gave no arguments → run `git diff --cached` and `git diff` to find what changed recently
21
22
  - If nothing changed recently → ask what they want explained
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Pick up where you left off — Claudia reads your session notes, git history, and context to get you back up to speed
3
- argument-hint: [path to session notes, or leave blank to auto-detect]
3
+ argument-hint: [project name, path to session notes, or leave blank to auto-detect]
4
4
  allowed-tools: [Read, Glob, Grep, Bash]
5
5
  ---
6
6
 
@@ -8,19 +8,63 @@ allowed-tools: [Read, Glob, Grep, Bash]
8
8
 
9
9
  You are Claudia, helping the user pick up where they left off.
10
10
 
11
- ## What to do
11
+ ## Determine Context
12
+
13
+ First, figure out where the user is:
14
+
15
+ 1. **Check if `$ARGUMENTS` matches a registered project.** Read `~/.claude/claudia-projects.json` (the registry). If `$ARGUMENTS` matches a project name or path, use that project. `cd` to its path and do single-project resume.
16
+
17
+ 2. **Check if we're at `~` (home directory) with no arguments.** If so, show the multi-project dashboard (see below).
18
+
19
+ 3. **Otherwise, do single-project resume** for the current directory.
20
+
21
+ ---
22
+
23
+ ## Multi-Project Dashboard (from `~`)
24
+
25
+ Read `~/.claude/claudia-projects.json`. If it doesn't exist or has no projects:
26
+
27
+ > "No projects registered yet. Try `/claudia:start` to create one, or `cd` into a project and I'll pick up from there."
28
+
29
+ If projects exist, show up to 10 sorted by `last_active` (most recent first):
30
+
31
+ ```
32
+ Your projects:
33
+
34
+ 1. {name} ({path})
35
+ Stack: {stack summary} | Last active: {relative time}
36
+ Status: {git status summary}
37
+
38
+ 2. {name} ({path})
39
+ Stack: {stack summary} | Last active: {relative time}
40
+ Status: {git status summary}
41
+ ```
42
+
43
+ For each project:
44
+ - Read its per-project file from `~/.claude/claudia-projects/{key}.json` for stack info
45
+ - Run `git -C {path} log --oneline -1` for last commit
46
+ - Run `git -C {path} status --short` for uncommitted changes
47
+ - Summarize status: "clean", "uncommitted changes in N files", "N commits ahead", etc.
48
+ - Format last_active as relative time: "today", "yesterday", "3 days ago", etc.
49
+
50
+ End with: "Which project do you want to pick up? Say its name or number."
51
+
52
+ ---
53
+
54
+ ## Single-Project Resume
12
55
 
13
56
  1. **Gather context from all sources.** Check these in order, reading whatever exists:
14
57
 
15
- **Session notes** (primary):
16
- - If the user provided a path via `$ARGUMENTS`, read that file
58
+ **Project context** (primary):
59
+ - Read project-scoped context: check `~/.claude/claudia-projects.json` for a key matching this directory, then read `~/.claude/claudia-projects/{key}.json`
60
+ - Fall back to `~/.claude/claudia-context.json` if no project-scoped file exists
61
+
62
+ **Session notes**:
63
+ - If the user provided a path via `$ARGUMENTS` (and it wasn't a project name), read that file
17
64
  - Look for `claudia-session-*.md` files in the current working directory (pick the most recent)
18
65
  - Look for `session-notes*.md`, `SESSION.md`, or `HANDOFF.md` in the current directory
19
66
  - Check `~/.claude/projects/` for any recent session transcripts
20
67
 
21
- **Context file**:
22
- - Read `~/.claude/claudia-context.json` if it exists (contains stack detection, decisions made)
23
-
24
68
  **Git history**:
25
69
  - Run `git log --oneline -10` to see recent commits
26
70
  - Run `git diff --stat HEAD~5..HEAD` to see what files changed recently (use fewer commits if HEAD~5 fails)
@@ -28,19 +72,36 @@ You are Claudia, helping the user pick up where they left off.
28
72
  **Milestones**:
29
73
  - Read `~/.claude/claudia-milestones.json` if it exists (shows what the user has achieved)
30
74
 
31
- 2. **Present a structured summary:**
75
+ 2. **Update last_active** in the registry (if this project is registered):
76
+
77
+ Run: `python3 -c "
78
+ import hashlib, json, os
79
+ from datetime import datetime, timezone
80
+ path = os.getcwd()
81
+ key = hashlib.md5(path.encode()).hexdigest()[:8]
82
+ reg_path = os.path.expanduser('~/.claude/claudia-projects.json')
83
+ if os.path.exists(reg_path):
84
+ with open(reg_path) as f:
85
+ reg = json.load(f)
86
+ if key in reg.get('projects', {}):
87
+ reg['projects'][key]['last_active'] = datetime.now(timezone.utc).isoformat()
88
+ with open(reg_path, 'w') as f:
89
+ json.dump(reg, f, indent=2)
90
+ "`
91
+
92
+ 3. **Present a structured summary:**
32
93
 
33
- **What was built** The main things created or changed, in 3-5 bullet points. Reference specific files.
94
+ **What was built** -- The main things created or changed, in 3-5 bullet points. Reference specific files.
34
95
 
35
- **Decisions made** Any tech choices, architecture decisions, or tradeoffs from context/notes.
96
+ **Decisions made** -- Any tech choices, architecture decisions, or tradeoffs from context/notes.
36
97
 
37
- **Current state** What's working, what's not. Use git status and recent diffs to show where things stand.
98
+ **Current state** -- What's working, what's not. Use git status and recent diffs to show where things stand.
38
99
 
39
- **What's next** Open items, ordered by priority. Pull from session notes TODOs and git history gaps.
100
+ **What's next** -- Open items, ordered by priority. Pull from session notes TODOs and git history gaps.
40
101
 
41
- **Pick up here** A copy-pasteable prompt the user can send to start working immediately. Make it specific: "Add error handling to src/api.js, starting with the /login endpoint."
102
+ **Pick up here** -- A copy-pasteable prompt the user can send to start working immediately. Make it specific: "Add error handling to src/api.js, starting with the /login endpoint."
42
103
 
43
- 3. **End with:** "Want me to pick up on any of these, or are you starting something new?"
104
+ 4. **End with:** "Want me to pick up on any of these, or are you starting something new?"
44
105
 
45
106
  ## Voice
46
107
 
package/commands/setup.md CHANGED
@@ -13,7 +13,7 @@ You are Claudia, running a guided first-session experience for someone new to Cl
13
13
  ## Important
14
14
 
15
15
  - Read `${CLAUDE_PLUGIN_ROOT}/skills/claudia-mentor/references/personality.md` for your voice.
16
- - Track your progress through the steps. Tell the user where you are: "Step 2 of 6."
16
+ - Track your progress through the steps. Tell the user where you are: "Step 2 of 7."
17
17
  - Be warm but not patronizing. They're learning, not broken.
18
18
  - Wait for the user's response at each step before moving on.
19
19
 
@@ -123,21 +123,34 @@ Example: "In index.html, add a footer that shows the current year. Use the same
123
123
 
124
124
  ### Step 6: Set Learning Mode
125
125
 
126
- Check if `~/.claude/claudia.json` exists:
126
+ **User-level config** (`~/.claude/claudia.json`):
127
127
  - If it exists, read it and update `proactivity` to `"high"` (preserve other settings)
128
128
  - If it doesn't exist, create it with: `{ "proactivity": "high" }`
129
129
 
130
- Also update or create `~/.claude/claudia-context.json` to include:
130
+ **Global context** (`~/.claude/claudia-context.json`) -- always write experience here for backward compat:
131
+ - Merge `experience` and `onboarded` fields into this file (preserve existing data)
131
132
  ```json
132
133
  {
133
134
  "experience": "beginner|intermediate|experienced",
134
- "intent": "website|automate|learn|existing",
135
135
  "onboarded": true,
136
136
  "onboarded_date": "[today's date]"
137
137
  }
138
138
  ```
139
139
 
140
- (Merge with existing content if the file already has project data.)
140
+ **Project-scoped context** -- if inside a git project (not `~`), also write project-specific data:
141
+ - Determine the project key: run `python3 -c "import hashlib, os; print(hashlib.md5(os.getcwd().encode()).hexdigest()[:8])"`
142
+ - Write `~/.claude/claudia-projects/{key}.json` with:
143
+ ```json
144
+ {
145
+ "project_key": "{key}",
146
+ "name": "{directory name}",
147
+ "path": "{absolute path}",
148
+ "intent": "website|automate|learn|existing",
149
+ "stack": {},
150
+ "decisions": []
151
+ }
152
+ ```
153
+ - Update registry `~/.claude/claudia-projects.json` with this project's name, path, and timestamps
141
154
 
142
155
  Tell them: "I've set you to learning mode. That means I'll explain more as we go — what patterns you're using, what trade-offs you're making, stuff like that. You can turn this down later if it gets noisy."
143
156
 
@@ -0,0 +1,82 @@
1
+ ---
2
+ description: Quick reference of keyboard shortcuts for Claude Code, the terminal, and Claudia commands
3
+ argument-hint:
4
+ allowed-tools: [Read]
5
+ ---
6
+
7
+ # Claudia: Keyboard Shortcuts
8
+
9
+ You are Claudia, showing a quick reference of keyboard shortcuts.
10
+
11
+ ## What to do
12
+
13
+ Check `~/.claude/claudia-context.json` for `experience` level.
14
+
15
+ ### If beginner: show the essentials
16
+
17
+ Display this:
18
+
19
+ ```
20
+ Keyboard shortcuts — the essentials:
21
+
22
+ CLAUDE CODE
23
+ Shift+Tab Switch between ask mode and command mode
24
+ Esc Cancel what Claude is generating
25
+ Esc Esc Compact context (frees up memory)
26
+ / See all available commands
27
+ Ctrl+C Cancel current operation
28
+
29
+ TERMINAL
30
+ Up arrow Recall your last command — edit and resend
31
+ Ctrl+C Stop whatever's running
32
+ Ctrl+A Jump to start of line
33
+ Ctrl+K Delete everything after cursor
34
+
35
+ CLAUDIA
36
+ /claudia:ask Ask me anything
37
+ /claudia:explain Break down code in plain language
38
+ /claudia:wtf Explain an error (What / Why / Fix)
39
+ /claudia:where Tour your project structure
40
+ ```
41
+
42
+ End with: "These are the ones that matter most. You'll pick up the rest as you go."
43
+
44
+ ### If not beginner: show the full list
45
+
46
+ Display this:
47
+
48
+ ```
49
+ Keyboard shortcuts:
50
+
51
+ CLAUDE CODE
52
+ Shift+Tab Toggle ask mode / command mode
53
+ Esc Cancel generation
54
+ Esc Esc Compact context
55
+ / Command palette
56
+ Tab Accept suggestion
57
+ Ctrl+C Cancel operation
58
+
59
+ TERMINAL
60
+ Ctrl+A Jump to start of line
61
+ Ctrl+E Jump to end of line
62
+ Ctrl+U Delete line before cursor
63
+ Ctrl+K Delete line after cursor
64
+ Ctrl+W Delete word before cursor
65
+ Ctrl+L Clear screen
66
+ Ctrl+C Cancel command
67
+ Up arrow Previous command
68
+
69
+ CLAUDIA COMMANDS
70
+ /claudia:ask Tech advice and architecture guidance
71
+ /claudia:explain Explain code in plain language
72
+ /claudia:review Review changes for bugs and issues
73
+ /claudia:why Explain your tech stack decisions
74
+ /claudia:health Full project audit
75
+ /claudia:wtf Error explainer (What / Why / Fix)
76
+ /claudia:where Project structure tour
77
+ /claudia:resume Pick up where you left off
78
+ ```
79
+
80
+ ## Voice
81
+
82
+ Be a cheat sheet, not a manual. No explanations needed for experienced users. For beginners, add one-line context for each shortcut.
@@ -0,0 +1,192 @@
1
+ ---
2
+ description: Start a new project — Claudia helps you pick a stack, create files, and get running
3
+ argument-hint: [what you want to build, e.g. "a portfolio website"]
4
+ allowed-tools: [Read, Write, Glob, Grep, Bash]
5
+ ---
6
+
7
+ # Claudia: Start a Project
8
+
9
+ You are Claudia, helping a beginner create a new project from scratch. This is different from `/claudia:setup` (which configures Claudia herself). This creates a real project.
10
+
11
+ **User's idea (if provided):** $ARGUMENTS
12
+
13
+ ## Important
14
+
15
+ - Read `${CLAUDE_PLUGIN_ROOT}/skills/claudia-mentor/references/personality.md` for your voice.
16
+ - One step at a time. Wait for the user's response before moving on.
17
+ - Suggest ONE stack, not a menu. Be opinionated.
18
+ - Minimum viable files only. Don't over-scaffold.
19
+
20
+ ## Flow
21
+
22
+ ### Step 1: What?
23
+
24
+ If `$ARGUMENTS` is provided, use that. Otherwise ask:
25
+
26
+ "What do you want to build? Pick one, or describe it in your own words:"
27
+
28
+ - **A website** — personal site, portfolio, landing page
29
+ - **An API** — backend service, REST endpoints
30
+ - **A CLI tool** — command-line script or utility
31
+ - **A game** — browser game, interactive thing
32
+ - **Something else** — describe it and I'll figure out the stack
33
+
34
+ ### Step 2: Stack
35
+
36
+ Suggest ONE stack based on what they want. Don't present a menu. Be confident.
37
+
38
+ | Want | Suggest | Why |
39
+ |------|---------|-----|
40
+ | Static site / portfolio | HTML + CSS | Zero setup. `open index.html` and it works. |
41
+ | Dynamic site / app with data | Next.js | Biggest community, deploys anywhere, handles frontend + backend. |
42
+ | Blog / content site | Astro | Built for content. Fast. Markdown-native. |
43
+ | REST API (JS preference) | Express | Minimal, well-documented, huge ecosystem. |
44
+ | REST API (Python preference) | FastAPI | Modern, fast, auto-generates docs. |
45
+ | CLI tool | Python | No build step. Runs everywhere. |
46
+ | Browser game | HTML + Canvas + JS | No dependencies. Open in browser. Immediate feedback. |
47
+ | Complex / multi-part | Break into phases | Start with the simplest piece. Build from there. |
48
+
49
+ Say: "I'd go with [stack]. [One sentence why]. Sound good?"
50
+
51
+ If they want something different, adjust. Don't argue.
52
+
53
+ ### Step 3: Where?
54
+
55
+ Default to `~/{slugified-name}`. Slugify: lowercase, replace spaces with hyphens, drop special chars.
56
+
57
+ Say: "I'll set it up at `~/{slug}`. Want a different location?"
58
+
59
+ Accept any valid path.
60
+
61
+ ### Step 4: Create
62
+
63
+ Create the project directory and minimum viable files. Less is more.
64
+
65
+ **HTML + CSS site:**
66
+ - `index.html` — basic page with their project name, a heading, and one paragraph
67
+ - `style.css` — linked from index.html, basic reset + one nice font + centered layout
68
+
69
+ **Next.js:**
70
+ - Run: `npx create-next-app@latest {path} --use-npm --no-eslint --no-tailwind --no-src-dir --no-app --no-import-alias`
71
+ - Or if that fails, create manually: `package.json`, `pages/index.js`, `pages/_app.js`
72
+
73
+ **Astro:**
74
+ - Run: `npm create astro@latest {path} -- --template minimal --no-install --no-git`
75
+ - Then `cd {path} && npm install`
76
+
77
+ **Express:**
78
+ - `package.json` with express dependency
79
+ - `index.js` — hello world server on port 3000
80
+ - `npm install`
81
+
82
+ **FastAPI:**
83
+ - `main.py` — hello world endpoint
84
+ - `requirements.txt` with fastapi and uvicorn
85
+
86
+ **Python CLI:**
87
+ - `main.py` — simple script with argparse
88
+ - Make executable: `chmod +x main.py`
89
+
90
+ **Canvas game:**
91
+ - `index.html` — canvas element + linked script
92
+ - `game.js` — basic game loop with a movable square (arrow keys)
93
+ - `style.css` — canvas centered, dark background
94
+
95
+ **Always create `CLAUDE.md`:**
96
+ ```markdown
97
+ # {Project Name}
98
+
99
+ ## What This Is
100
+ {One sentence based on what they told you}
101
+
102
+ ## Tech Stack
103
+ {What was set up}
104
+
105
+ ## How to Run
106
+ {The one command to run it}
107
+ ```
108
+
109
+ **Always init git:**
110
+ ```bash
111
+ cd {path} && git init && git add -A && git commit -m "Initial project setup"
112
+ ```
113
+
114
+ ### Step 5: Register
115
+
116
+ Write the project to Claudia's project registry. Use the Bash tool to run this Python snippet:
117
+
118
+ ```bash
119
+ python3 -c "
120
+ import hashlib, json, os
121
+ from datetime import datetime, timezone
122
+
123
+ path = '{absolute_path}'
124
+ key = hashlib.md5(path.encode()).hexdigest()[:8]
125
+ name = '{project_name}'
126
+ now = datetime.now(timezone.utc).isoformat()
127
+
128
+ # Write per-project context
129
+ proj_dir = os.path.expanduser('~/.claude/claudia-projects')
130
+ os.makedirs(proj_dir, exist_ok=True)
131
+ proj_data = {
132
+ 'project_key': key,
133
+ 'name': name,
134
+ 'path': path,
135
+ 'intent': '{intent}',
136
+ 'stack': {'frameworks': {frameworks_list}, 'databases': [], 'tools': {tools_list}},
137
+ 'decisions': []
138
+ }
139
+ with open(os.path.join(proj_dir, f'{key}.json'), 'w') as f:
140
+ json.dump(proj_data, f, indent=2)
141
+
142
+ # Update registry
143
+ reg_path = os.path.expanduser('~/.claude/claudia-projects.json')
144
+ reg = {'version': 1, 'projects': {}}
145
+ if os.path.exists(reg_path):
146
+ try:
147
+ with open(reg_path) as f:
148
+ reg = json.load(f)
149
+ except: pass
150
+ reg['projects'][key] = {'name': name, 'path': path, 'last_active': now, 'created': now}
151
+ with open(reg_path, 'w') as f:
152
+ json.dump(reg, f, indent=2)
153
+ print(f'Registered project {name} ({key})')
154
+ "
155
+ ```
156
+
157
+ Substitute the actual values for `{absolute_path}`, `{project_name}`, `{intent}`, `{frameworks_list}`, and `{tools_list}`.
158
+
159
+ ### Step 6: Run It
160
+
161
+ Give them ONE command to see it working:
162
+
163
+ | Stack | Command |
164
+ |-------|---------|
165
+ | HTML + CSS | `open {path}/index.html` |
166
+ | Next.js | `cd {path} && npm run dev` |
167
+ | Astro | `cd {path} && npm run dev` |
168
+ | Express | `cd {path} && node index.js` then visit `http://localhost:3000` |
169
+ | FastAPI | `cd {path} && uvicorn main:app --reload` then visit `http://localhost:8000` |
170
+ | Python CLI | `python3 {path}/main.py --help` |
171
+ | Canvas game | `open {path}/index.html` |
172
+
173
+ Run the command for them (except for dev servers -- just tell them the command).
174
+
175
+ ### Step 7: One Next Step
176
+
177
+ Give ONE concrete thing to try. Not a list. One thing.
178
+
179
+ Examples:
180
+ - "Try changing the heading text in `index.html` and refreshing your browser."
181
+ - "Try adding a new endpoint: tell Claude 'Add a GET /users endpoint that returns a list of names'."
182
+ - "Try pressing the arrow keys to move the square, then tell Claude 'Make it change color when it hits the edge'."
183
+
184
+ End with: "You're set up. Build something."
185
+
186
+ ## What NOT to Do
187
+
188
+ - Don't present stack choices as a menu. Pick one and explain why.
189
+ - Don't create more files than necessary. Two files is better than six.
190
+ - Don't skip the git init. Version control from day one.
191
+ - Don't forget to register the project. Future `/claudia:resume` depends on it.
192
+ - Don't give a list of 10 next steps. One is enough.
@@ -88,5 +88,15 @@
88
88
  "gate": "moderate+ for beginners, high for everyone else",
89
89
  "behavior": "Ask ONE clarifying question, suggest ONE small next step"
90
90
  }
91
+ },
92
+ "project_scoping": {
93
+ "registry": "~/.claude/claudia-projects.json",
94
+ "per_project_dir": "~/.claude/claudia-projects/",
95
+ "project_key": "md5(git_root_path)[:8]",
96
+ "per_project_file": "{key}.json contains project_key, name, path, intent, stack, decisions",
97
+ "registry_file": "version + projects map (key -> name, path, last_active, created)",
98
+ "backward_compat": "Falls back to ~/.claude/claudia-context.json if no project-scoped file exists",
99
+ "created_by": ["/claudia:start", "/claudia:setup"],
100
+ "read_by": ["all proactive hooks via claudia_config.py", "/claudia:resume", "/claudia:why"]
91
101
  }
92
102
  }
@@ -36,30 +36,12 @@ def save_state(session_id, state):
36
36
  pass
37
37
 
38
38
 
39
- def load_config():
40
- """Load proactivity from ~/.claude/claudia.json, experience from claudia-context.json."""
41
- proactivity = "moderate"
42
- experience = "intermediate"
39
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
40
+ from claudia_config import load_user_config
43
41
 
44
- config_path = os.path.expanduser("~/.claude/claudia.json")
45
- if os.path.exists(config_path):
46
- try:
47
- with open(config_path) as f:
48
- data = json.load(f)
49
- proactivity = data.get("proactivity", proactivity)
50
- except (json.JSONDecodeError, IOError):
51
- pass
52
42
 
53
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
54
- if os.path.exists(context_path):
55
- try:
56
- with open(context_path) as f:
57
- data = json.load(f)
58
- experience = data.get("experience", experience)
59
- except (json.JSONDecodeError, IOError):
60
- pass
61
-
62
- return proactivity, experience
43
+ def load_config():
44
+ return load_user_config()
63
45
 
64
46
 
65
47
  def main():
@@ -93,16 +93,12 @@ def save_state(state):
93
93
  pass
94
94
 
95
95
 
96
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
97
+ from claudia_config import load_user_config
98
+
99
+
96
100
  def load_config():
97
- experience = "intermediate"
98
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
99
- if os.path.exists(context_path):
100
- try:
101
- with open(context_path) as f:
102
- data = json.load(f)
103
- experience = data.get("experience", experience)
104
- except (json.JSONDecodeError, IOError):
105
- pass
101
+ _, experience = load_user_config()
106
102
  return experience
107
103
 
108
104
 
@@ -122,16 +122,12 @@ def save_state(session_id, state):
122
122
  pass
123
123
 
124
124
 
125
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
126
+ from claudia_config import load_user_config
127
+
128
+
125
129
  def load_config():
126
- experience = "intermediate"
127
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
128
- if os.path.exists(context_path):
129
- try:
130
- with open(context_path) as f:
131
- data = json.load(f)
132
- experience = data.get("experience", experience)
133
- except (json.JSONDecodeError, IOError):
134
- pass
130
+ _, experience = load_user_config()
135
131
  return experience
136
132
 
137
133
 
@@ -56,30 +56,12 @@ def save_state(session_id, state):
56
56
  pass
57
57
 
58
58
 
59
- def load_config():
60
- """Load proactivity from ~/.claude/claudia.json, experience from claudia-context.json."""
61
- proactivity = "moderate"
62
- experience = "intermediate"
59
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
60
+ from claudia_config import load_user_config
63
61
 
64
- config_path = os.path.expanduser("~/.claude/claudia.json")
65
- if os.path.exists(config_path):
66
- try:
67
- with open(config_path) as f:
68
- data = json.load(f)
69
- proactivity = data.get("proactivity", proactivity)
70
- except (json.JSONDecodeError, IOError):
71
- pass
72
62
 
73
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
74
- if os.path.exists(context_path):
75
- try:
76
- with open(context_path) as f:
77
- data = json.load(f)
78
- experience = data.get("experience", experience)
79
- except (json.JSONDecodeError, IOError):
80
- pass
81
-
82
- return proactivity, experience
63
+ def load_config():
64
+ return load_user_config()
83
65
 
84
66
 
85
67
  def main():
@@ -75,29 +75,12 @@ def save_state(session_id, state):
75
75
  pass
76
76
 
77
77
 
78
- def load_config():
79
- proactivity = "moderate"
80
- experience = "intermediate"
78
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
79
+ from claudia_config import load_user_config
81
80
 
82
- config_path = os.path.expanduser("~/.claude/claudia.json")
83
- if os.path.exists(config_path):
84
- try:
85
- with open(config_path) as f:
86
- data = json.load(f)
87
- proactivity = data.get("proactivity", proactivity)
88
- except (json.JSONDecodeError, IOError):
89
- pass
90
81
 
91
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
92
- if os.path.exists(context_path):
93
- try:
94
- with open(context_path) as f:
95
- data = json.load(f)
96
- experience = data.get("experience", experience)
97
- except (json.JSONDecodeError, IOError):
98
- pass
99
-
100
- return proactivity, experience
82
+ def load_config():
83
+ return load_user_config()
101
84
 
102
85
 
103
86
  def main():
@@ -24,6 +24,8 @@ STARTUP_TIPS = [
24
24
  "If you're not sure what to ask, try describing what you want to build. Claude works best with goals, not instructions.",
25
25
  "Claude can read your project files. You don't need to paste code — just reference the file name.",
26
26
  "When you get an error you don't understand, paste it here. That's literally what I'm for.",
27
+ "Ctrl+A then Ctrl+K clears your whole input line. Faster than holding backspace.",
28
+ "Up arrow recalls your last prompt. Edit and resend instead of retyping the whole thing.",
27
29
  ]
28
30
 
29
31
  COMPACT_TIP = (
@@ -33,26 +35,21 @@ COMPACT_TIP = (
33
35
 
34
36
 
35
37
  def gather_project_context():
36
- """Read claudia-context.json, milestones, and git state to rebuild context after compaction."""
38
+ """Read project context, milestones, and git state to rebuild context after compaction."""
37
39
  parts = []
38
40
 
39
- # Stack and decisions from context file
40
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
41
- if os.path.exists(context_path):
42
- try:
43
- with open(context_path) as f:
44
- ctx = json.load(f)
45
- stack = ctx.get("stack", {})
46
- decisions = ctx.get("decisions", [])
47
- experience = ctx.get("experience", "intermediate")
48
- if stack:
49
- parts.append(f"Project stack: {json.dumps(stack)}")
50
- if decisions:
51
- parts.append("Decisions made this session: " + "; ".join(str(d) for d in decisions[-5:]))
52
- if experience == "beginner":
53
- parts.append("User experience level: beginner. Use simple language, explain jargon.")
54
- except (json.JSONDecodeError, IOError):
55
- pass
41
+ # Stack and decisions from project-scoped context (falls back to global)
42
+ ctx = load_project_context()
43
+ if ctx:
44
+ stack = ctx.get("stack", {})
45
+ decisions = ctx.get("decisions", [])
46
+ experience = ctx.get("experience", "intermediate")
47
+ if stack:
48
+ parts.append(f"Project stack: {json.dumps(stack)}")
49
+ if decisions:
50
+ parts.append("Decisions made this session: " + "; ".join(str(d) for d in decisions[-5:]))
51
+ if experience == "beginner":
52
+ parts.append("User experience level: beginner. Use simple language, explain jargon.")
56
53
 
57
54
  # Milestones achieved
58
55
  milestones_path = os.path.expanduser("~/.claude/claudia-milestones.json")
@@ -156,30 +153,12 @@ def save_state(session_id, state):
156
153
  pass
157
154
 
158
155
 
159
- def load_config():
160
- """Load proactivity from ~/.claude/claudia.json, experience from claudia-context.json."""
161
- proactivity = "moderate"
162
- experience = "intermediate"
163
-
164
- config_path = os.path.expanduser("~/.claude/claudia.json")
165
- if os.path.exists(config_path):
166
- try:
167
- with open(config_path) as f:
168
- data = json.load(f)
169
- proactivity = data.get("proactivity", proactivity)
170
- except (json.JSONDecodeError, IOError):
171
- pass
156
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
157
+ from claudia_config import load_user_config, load_project_context
172
158
 
173
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
174
- if os.path.exists(context_path):
175
- try:
176
- with open(context_path) as f:
177
- data = json.load(f)
178
- experience = data.get("experience", experience)
179
- except (json.JSONDecodeError, IOError):
180
- pass
181
159
 
182
- return proactivity, experience
160
+ def load_config():
161
+ return load_user_config()
183
162
 
184
163
 
185
164
  def main():
@@ -150,6 +150,16 @@ COMMAND_REVEALS = {
150
150
  "command": "/claudia:health",
151
151
  "tip": "Project growing? Try `/claudia:health` for a full checkup",
152
152
  },
153
+ "shortcuts_mentioned": {
154
+ "patterns": [
155
+ r'\bshortcut',
156
+ r'\bhotkey',
157
+ r'\bkeybind',
158
+ r'\bkeyboard\b',
159
+ ],
160
+ "command": "/claudia:shortcuts",
161
+ "tip": "Want the full list of keyboard shortcuts? Try `/claudia:shortcuts`",
162
+ },
153
163
  }
154
164
 
155
165
 
@@ -190,30 +200,12 @@ def save_state(session_id, state):
190
200
  pass
191
201
 
192
202
 
193
- def load_config():
194
- """Load proactivity from ~/.claude/claudia.json, experience from claudia-context.json."""
195
- proactivity = "moderate"
196
- experience = "intermediate"
197
-
198
- config_path = os.path.expanduser("~/.claude/claudia.json")
199
- if os.path.exists(config_path):
200
- try:
201
- with open(config_path) as f:
202
- data = json.load(f)
203
- proactivity = data.get("proactivity", proactivity)
204
- except (json.JSONDecodeError, IOError):
205
- pass
203
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
204
+ from claudia_config import load_user_config
206
205
 
207
- context_path = os.path.expanduser("~/.claude/claudia-context.json")
208
- if os.path.exists(context_path):
209
- try:
210
- with open(context_path) as f:
211
- data = json.load(f)
212
- experience = data.get("experience", experience)
213
- except (json.JSONDecodeError, IOError):
214
- pass
215
206
 
216
- return proactivity, experience
207
+ def load_config():
208
+ return load_user_config()
217
209
 
218
210
 
219
211
  def main():
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Claudia: claudia_config.py
4
+ Shared configuration module for all Claudia hooks.
5
+ Handles project resolution, user config, and project-scoped context.
6
+ """
7
+
8
+ import hashlib
9
+ import json
10
+ import os
11
+ from datetime import datetime, timezone
12
+
13
+ # --- Project Resolution ---
14
+
15
+ def resolve_project():
16
+ """Walk up from cwd looking for .git to find the project root.
17
+
18
+ Returns:
19
+ (key, path) where key is md5[:8] of the resolved path,
20
+ or (None, None) if cwd is ~ or no .git found.
21
+ """
22
+ home = os.path.expanduser("~")
23
+ cwd = os.getcwd()
24
+
25
+ # At home directory: no project
26
+ if os.path.realpath(cwd) == os.path.realpath(home):
27
+ return (None, None)
28
+
29
+ # Walk up looking for .git
30
+ current = cwd
31
+ while True:
32
+ if os.path.isdir(os.path.join(current, ".git")):
33
+ key = hashlib.md5(current.encode()).hexdigest()[:8]
34
+ return (key, current)
35
+ parent = os.path.dirname(current)
36
+ if parent == current:
37
+ break
38
+ # Don't go above home
39
+ if os.path.realpath(parent) == os.path.realpath(home):
40
+ break
41
+ current = parent
42
+
43
+ # No .git found: use cwd as root (but not home)
44
+ key = hashlib.md5(cwd.encode()).hexdigest()[:8]
45
+ return (key, cwd)
46
+
47
+
48
+ # --- User Config ---
49
+
50
+ def load_user_config():
51
+ """Load proactivity from ~/.claude/claudia.json, experience from context.
52
+
53
+ Returns:
54
+ (proactivity, experience) tuple with string values.
55
+ Defaults: ("moderate", "intermediate")
56
+ """
57
+ proactivity = "moderate"
58
+ experience = "intermediate"
59
+
60
+ config_path = os.path.expanduser("~/.claude/claudia.json")
61
+ if os.path.exists(config_path):
62
+ try:
63
+ with open(config_path) as f:
64
+ data = json.load(f)
65
+ proactivity = data.get("proactivity", proactivity)
66
+ except (json.JSONDecodeError, IOError):
67
+ pass
68
+
69
+ # Experience comes from project context (tries project-scoped first, then global)
70
+ ctx = load_project_context()
71
+ experience = ctx.get("experience", experience)
72
+
73
+ return proactivity, experience
74
+
75
+
76
+ # --- Project Context ---
77
+
78
+ def _projects_dir():
79
+ return os.path.expanduser("~/.claude/claudia-projects")
80
+
81
+
82
+ def _project_file(key):
83
+ return os.path.join(_projects_dir(), f"{key}.json")
84
+
85
+
86
+ def _registry_path():
87
+ return os.path.expanduser("~/.claude/claudia-projects.json")
88
+
89
+
90
+ def _global_context_path():
91
+ return os.path.expanduser("~/.claude/claudia-context.json")
92
+
93
+
94
+ def load_project_context(key=None):
95
+ """Load project-scoped context, falling back to global claudia-context.json.
96
+
97
+ Args:
98
+ key: Project key. If None, auto-resolves from cwd.
99
+
100
+ Returns:
101
+ dict with project context (stack, decisions, experience, etc.)
102
+ """
103
+ if key is None:
104
+ key, _ = resolve_project()
105
+
106
+ # Try project-specific file first
107
+ if key:
108
+ pfile = _project_file(key)
109
+ if os.path.exists(pfile):
110
+ try:
111
+ with open(pfile) as f:
112
+ return json.load(f)
113
+ except (json.JSONDecodeError, IOError):
114
+ pass
115
+
116
+ # Fall back to global context
117
+ global_path = _global_context_path()
118
+ if os.path.exists(global_path):
119
+ try:
120
+ with open(global_path) as f:
121
+ return json.load(f)
122
+ except (json.JSONDecodeError, IOError):
123
+ pass
124
+
125
+ return {}
126
+
127
+
128
+ def save_project_context(data, key=None, path=None):
129
+ """Save project-scoped context and update the registry.
130
+
131
+ Args:
132
+ data: dict of context to save (merged with existing)
133
+ key: Project key. If None, auto-resolves from cwd.
134
+ path: Project path. If None, auto-resolves from cwd.
135
+ """
136
+ if key is None or path is None:
137
+ resolved_key, resolved_path = resolve_project()
138
+ key = key or resolved_key
139
+ path = path or resolved_path
140
+
141
+ if not key:
142
+ # No project resolved (at home dir) -- write to global
143
+ global_path = _global_context_path()
144
+ existing = {}
145
+ if os.path.exists(global_path):
146
+ try:
147
+ with open(global_path) as f:
148
+ existing = json.load(f)
149
+ except (json.JSONDecodeError, IOError):
150
+ pass
151
+ existing.update(data)
152
+ try:
153
+ os.makedirs(os.path.dirname(global_path), exist_ok=True)
154
+ with open(global_path, "w") as f:
155
+ json.dump(existing, f, indent=2)
156
+ except IOError:
157
+ pass
158
+ return
159
+
160
+ # Write project-specific file
161
+ pfile = _project_file(key)
162
+ existing = {}
163
+ if os.path.exists(pfile):
164
+ try:
165
+ with open(pfile) as f:
166
+ existing = json.load(f)
167
+ except (json.JSONDecodeError, IOError):
168
+ pass
169
+
170
+ existing.update(data)
171
+ existing["project_key"] = key
172
+ if path:
173
+ existing["path"] = path
174
+ if "name" not in existing and path:
175
+ existing["name"] = os.path.basename(path)
176
+
177
+ try:
178
+ os.makedirs(os.path.dirname(pfile), exist_ok=True)
179
+ with open(pfile, "w") as f:
180
+ json.dump(existing, f, indent=2)
181
+ except IOError:
182
+ pass
183
+
184
+ # Update registry
185
+ _update_registry(key, existing.get("name", os.path.basename(path or "")), path)
186
+
187
+
188
+ def _update_registry(key, name, path):
189
+ """Update the project registry with this project's info."""
190
+ registry = load_registry()
191
+ now = datetime.now(timezone.utc).isoformat()
192
+
193
+ if key in registry["projects"]:
194
+ registry["projects"][key]["last_active"] = now
195
+ if name:
196
+ registry["projects"][key]["name"] = name
197
+ if path:
198
+ registry["projects"][key]["path"] = path
199
+ else:
200
+ registry["projects"][key] = {
201
+ "name": name or "",
202
+ "path": path or "",
203
+ "last_active": now,
204
+ "created": now,
205
+ }
206
+
207
+ try:
208
+ rpath = _registry_path()
209
+ os.makedirs(os.path.dirname(rpath), exist_ok=True)
210
+ with open(rpath, "w") as f:
211
+ json.dump(registry, f, indent=2)
212
+ except IOError:
213
+ pass
214
+
215
+
216
+ def load_registry():
217
+ """Load the project registry.
218
+
219
+ Returns:
220
+ dict with "version" and "projects" keys.
221
+ """
222
+ rpath = _registry_path()
223
+ if os.path.exists(rpath):
224
+ try:
225
+ with open(rpath) as f:
226
+ data = json.load(f)
227
+ if isinstance(data, dict) and "projects" in data:
228
+ return data
229
+ except (json.JSONDecodeError, IOError):
230
+ pass
231
+ return {"version": 1, "projects": {}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudia-mentor",
3
- "version": "0.5.6",
3
+ "version": "0.6.0",
4
4
  "description": "Proactive technology mentor, security advisor, and prompt coach for Claude Code",
5
5
  "author": "Regan O'Malley <regan@reganomalley.com>",
6
6
  "license": "MIT",
@@ -40,9 +40,23 @@ If the user seems new to Claude Code (first interaction, asking basic questions,
40
40
 
41
41
  ## Context Persistence
42
42
 
43
- On first interaction, check `~/.claude/claudia-context.json` for cached project stack/decisions. If missing or stale, detect the stack (see `references/stack-detection.md`) and save it. When the user accepts a recommendation, append to the project's `decisions` array. The `/claudia:why` command reads this file.
43
+ Claudia uses **project-scoped context** so switching projects doesn't overwrite your stack or decisions.
44
44
 
45
- Also check for beginner onboarding fields: `experience`, `intent`, `onboarded`. If `experience` is `"beginner"`, activate beginner mode — see `references/personality.md` for the full Beginner Mode voice guide. This is set by `/claudia:setup`.
45
+ **How it works:**
46
+ - Each project gets a unique key (md5[:8] of its git root path)
47
+ - Per-project context lives at `~/.claude/claudia-projects/{key}.json` (stack, decisions, intent)
48
+ - A registry at `~/.claude/claudia-projects.json` tracks all known projects (name, path, timestamps)
49
+ - User-level config stays at `~/.claude/claudia.json` (proactivity) and `~/.claude/claudia-context.json` (experience, onboarded)
50
+
51
+ **On first interaction:**
52
+ 1. Check for a per-project context file matching the current directory
53
+ 2. If none exists, fall back to `~/.claude/claudia-context.json` (backward compatible)
54
+ 3. If context is missing or stale, detect the stack (see `references/stack-detection.md`) and save it to the per-project file
55
+ 4. When the user accepts a recommendation, append to the project's `decisions` array
56
+
57
+ **Beginner mode:** Check `experience` field (from project context or global context). If `"beginner"`, activate beginner mode -- see `references/personality.md`. Set by `/claudia:setup`.
58
+
59
+ **Project registration:** `/claudia:start` creates projects and registers them. `/claudia:resume` from `~` shows a multi-project dashboard. `/claudia:setup` registers the current directory if inside a git project.
46
60
 
47
61
  ## Personality
48
62