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 +48 -46
- package/commands/ask.md +4 -1
- package/commands/explain.md +3 -2
- package/commands/resume.md +75 -14
- package/commands/setup.md +18 -5
- package/commands/shortcuts.md +82 -0
- package/commands/start.md +192 -0
- package/config/defaults.json +10 -0
- package/hooks/scripts/claudia-compact-tip.py +4 -22
- package/hooks/scripts/claudia-milestones.py +5 -9
- package/hooks/scripts/claudia-next-steps.py +5 -9
- package/hooks/scripts/claudia-prompt-coach.py +4 -22
- package/hooks/scripts/claudia-run-suggest.py +4 -21
- package/hooks/scripts/claudia-session-tips.py +19 -40
- package/hooks/scripts/claudia-teach.py +14 -22
- package/hooks/scripts/claudia_config.py +231 -0
- package/package.json +1 -1
- package/skills/claudia-mentor/SKILL.md +16 -2
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
**
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
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**
|
|
216
|
-
- **No network calls in hooks**
|
|
217
|
-
- **No obfuscated code**
|
|
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
|
|
package/commands/explain.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Explain
|
|
3
|
-
argument-hint: [file or
|
|
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
|
package/commands/resume.md
CHANGED
|
@@ -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
|
-
##
|
|
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
|
-
**
|
|
16
|
-
-
|
|
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. **
|
|
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**
|
|
94
|
+
**What was built** -- The main things created or changed, in 3-5 bullet points. Reference specific files.
|
|
34
95
|
|
|
35
|
-
**Decisions made**
|
|
96
|
+
**Decisions made** -- Any tech choices, architecture decisions, or tradeoffs from context/notes.
|
|
36
97
|
|
|
37
|
-
**Current state**
|
|
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**
|
|
100
|
+
**What's next** -- Open items, ordered by priority. Pull from session notes TODOs and git history gaps.
|
|
40
101
|
|
|
41
|
-
**Pick up here**
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/config/defaults.json
CHANGED
|
@@ -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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
|
38
|
+
"""Read project context, milestones, and git state to rebuild context after compaction."""
|
|
37
39
|
parts = []
|
|
38
40
|
|
|
39
|
-
# Stack and decisions from context
|
|
40
|
-
|
|
41
|
-
if
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
-
|
|
43
|
+
Claudia uses **project-scoped context** so switching projects doesn't overwrite your stack or decisions.
|
|
44
44
|
|
|
45
|
-
|
|
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
|
|