worclaude 2.7.1 → 2.9.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/CHANGELOG.md +71 -0
- package/README.md +72 -56
- package/package.json +1 -1
- package/src/commands/doc-lint.js +37 -0
- package/src/commands/doctor.js +145 -0
- package/src/commands/init.js +144 -44
- package/src/commands/observability.js +24 -0
- package/src/commands/regenerate-routing.js +70 -0
- package/src/commands/status.js +14 -0
- package/src/commands/upgrade.js +87 -1
- package/src/commands/worktrees.js +90 -0
- package/src/core/config.js +10 -1
- package/src/core/file-categorizer.js +16 -0
- package/src/core/merger.js +42 -20
- package/src/core/scaffolder.js +26 -0
- package/src/data/agents.js +14 -28
- package/src/data/optional-features.js +46 -0
- package/src/generators/agent-routing.js +189 -34
- package/src/index.js +37 -0
- package/src/prompts/agent-selection.js +11 -3
- package/src/utils/agent-frontmatter.js +109 -0
- package/src/utils/doc-lint.js +196 -0
- package/src/utils/observability.js +300 -0
- package/templates/agents/optional/backend/api-designer.md +7 -1
- package/templates/agents/optional/backend/auth-auditor.md +7 -1
- package/templates/agents/optional/backend/database-analyst.md +7 -1
- package/templates/agents/optional/data/data-pipeline-reviewer.md +7 -1
- package/templates/agents/optional/data/ml-experiment-tracker.md +7 -1
- package/templates/agents/optional/data/prompt-engineer.md +7 -1
- package/templates/agents/optional/devops/ci-fixer.md +7 -1
- package/templates/agents/optional/devops/dependency-manager.md +7 -1
- package/templates/agents/optional/devops/deploy-validator.md +7 -1
- package/templates/agents/optional/devops/docker-helper.md +7 -1
- package/templates/agents/optional/docs/changelog-generator.md +9 -1
- package/templates/agents/optional/docs/doc-writer.md +7 -1
- package/templates/agents/optional/frontend/style-enforcer.md +7 -1
- package/templates/agents/optional/frontend/ui-reviewer.md +7 -1
- package/templates/agents/optional/quality/bug-fixer.md +19 -1
- package/templates/agents/optional/quality/build-fixer.md +7 -1
- package/templates/agents/optional/quality/performance-auditor.md +8 -1
- package/templates/agents/optional/quality/refactorer.md +7 -1
- package/templates/agents/optional/quality/security-reviewer.md +7 -1
- package/templates/agents/universal/build-validator.md +7 -1
- package/templates/agents/universal/code-simplifier.md +8 -1
- package/templates/agents/universal/plan-reviewer.md +8 -1
- package/templates/agents/universal/test-writer.md +19 -1
- package/templates/agents/universal/upstream-watcher.md +8 -1
- package/templates/agents/universal/verify-app.md +45 -3
- package/templates/commands/build-fix.md +30 -11
- package/templates/commands/commit-push-pr.md +47 -24
- package/templates/commands/compact-safe.md +79 -7
- package/templates/commands/conflict-resolver.md +7 -3
- package/templates/commands/end.md +63 -17
- package/templates/commands/learn.md +72 -8
- package/templates/commands/observability.md +59 -0
- package/templates/commands/refactor-clean.md +44 -2
- package/templates/commands/review-changes.md +40 -11
- package/templates/commands/review-plan.md +83 -10
- package/templates/commands/start.md +61 -30
- package/templates/commands/sync.md +86 -6
- package/templates/commands/test-coverage.md +78 -12
- package/templates/commands/update-claude-md.md +96 -7
- package/templates/commands/verify.md +32 -8
- package/templates/core/claude-md.md +9 -0
- package/templates/hooks/correction-detect.cjs +1 -1
- package/templates/hooks/learn-capture.cjs +0 -2
- package/templates/hooks/obs-agent-events.cjs +55 -0
- package/templates/hooks/obs-command-invocations.cjs +53 -0
- package/templates/hooks/obs-skill-loads.cjs +54 -0
- package/templates/hooks/skill-hint.cjs +22 -2
- package/templates/scripts/start-drift.sh +29 -0
- package/templates/scripts/sync-release-scope.sh +17 -0
- package/templates/scripts/test-coverage-changed-files.sh +14 -0
- package/templates/settings/base.json +73 -0
- package/templates/skills/universal/claude-md-maintenance.md +50 -14
- package/templates/skills/universal/git-conventions.md +11 -1
- package/templates/skills/universal/memory-architecture.md +115 -0
- package/templates/skills/universal/subagent-usage.md +15 -2
- package/src/data/agent-registry.js +0 -365
- package/templates/agents/optional/quality/e2e-runner.md +0 -98
- package/templates/commands/status.md +0 -15
- package/templates/commands/techdebt.md +0 -18
- package/templates/commands/upstream-check.md +0 -85
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
{tech_stack_filled_during_init}
|
|
11
11
|
|
|
12
12
|
## Commands
|
|
13
|
+
|
|
14
|
+
<!-- references package.json (or equivalent for non-Node stacks) -->
|
|
15
|
+
The verification commands below are filled during init from the project's
|
|
16
|
+
package manager scripts. Reference them by script name; do not restate the
|
|
17
|
+
underlying tool invocations.
|
|
18
|
+
|
|
13
19
|
{commands_filled_during_init}
|
|
14
20
|
|
|
15
21
|
## Skills (read on demand, not upfront)
|
|
@@ -35,6 +41,7 @@ See `.claude/skills/` — load only what's relevant:
|
|
|
35
41
|
**Feature branch:** /start → work → /verify → /commit-push-pr
|
|
36
42
|
**After merging PRs:** git checkout develop → git pull → /conflict-resolver (if needed) → /sync
|
|
37
43
|
**Mid-task stop:** /end (writes handoff file)
|
|
44
|
+
**Trigger discipline:** /commit-push-pr and /sync execute only when the human types them. They do not run autonomously after work "feels done."
|
|
38
45
|
|
|
39
46
|
## Critical Rules
|
|
40
47
|
1. SPEC.md is source of truth. Do not invent features.
|
|
@@ -49,6 +56,7 @@ See `.claude/skills/` — load only what's relevant:
|
|
|
49
56
|
10. Surgical changes only — every changed line must trace to the request. Don't "improve" adjacent code, comments, or formatting.
|
|
50
57
|
11. Push back when simpler approaches exist. Present alternatives, don't pick silently.
|
|
51
58
|
12. Transform tasks to success criteria. "Fix the bug" → "Write a failing test, then make it pass."
|
|
59
|
+
13. Commit, push, and PR only when the human explicitly invokes /commit-push-pr or /sync. Never run git commit, git push, or gh pr create on your own initiative, never invoke those slash commands without an explicit human trigger, and never auto-answer the Version bump: question — refuse to proceed without a human-selected option.
|
|
52
60
|
|
|
53
61
|
## Memory Architecture
|
|
54
62
|
|
|
@@ -58,6 +66,7 @@ See `.claude/skills/` — load only what's relevant:
|
|
|
58
66
|
- Path-scoped rules: `.claude/rules/` with YAML frontmatter.
|
|
59
67
|
- Session state: `.claude/sessions/` (gitignored).{memory_architecture_extras}
|
|
60
68
|
- Do NOT write session learnings or auto-captured patterns here.
|
|
69
|
+
- If your repository has the Claude Code GitHub Action installed (run `/install-github-action`), `@claude` mentions in PR comments will automatically propose CLAUDE.md updates.
|
|
61
70
|
|
|
62
71
|
## Learnings
|
|
63
72
|
|
|
@@ -37,7 +37,7 @@ try {
|
|
|
37
37
|
);
|
|
38
38
|
} else if (correctionPatterns.some((p) => p.test(prompt))) {
|
|
39
39
|
process.stdout.write(
|
|
40
|
-
'[Correction detected]
|
|
40
|
+
'[Correction detected — semi-auto] Draft a one-line generalizable rule, then prompt via AskUserQuestion: "Capture as team learning? yes / yes, let me edit / no". On yes or yes-edit, emit a [LEARN] block; the Stop hook will persist it.\n'
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -139,7 +139,6 @@ try {
|
|
|
139
139
|
`created: ${today}`,
|
|
140
140
|
`category: ${learning.category}`,
|
|
141
141
|
`project: ${projectName}`,
|
|
142
|
-
'times_applied: 0',
|
|
143
142
|
'---',
|
|
144
143
|
'',
|
|
145
144
|
`**Rule:** ${learning.rule}`,
|
|
@@ -163,7 +162,6 @@ try {
|
|
|
163
162
|
file: filename,
|
|
164
163
|
category: learning.category,
|
|
165
164
|
created: today,
|
|
166
|
-
times_applied: 0,
|
|
167
165
|
});
|
|
168
166
|
}
|
|
169
167
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// SubagentStart and SubagentStop hook: records each agent event to
|
|
5
|
+
// .claude/observability/agent-events.jsonl. The aggregator
|
|
6
|
+
// (worclaude observability) pairs start+stop on session+agent to
|
|
7
|
+
// compute durations — keeping the hook stateless.
|
|
8
|
+
//
|
|
9
|
+
// Single hook serves both events; the input.event field tells which
|
|
10
|
+
// fired. Always exits 0.
|
|
11
|
+
|
|
12
|
+
const { readFileSync, appendFileSync, mkdirSync, existsSync } = require('fs');
|
|
13
|
+
const { join } = require('path');
|
|
14
|
+
|
|
15
|
+
function main() {
|
|
16
|
+
let input;
|
|
17
|
+
try {
|
|
18
|
+
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const cwd = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
|
24
|
+
const obsDir = join(cwd, '.claude', 'observability');
|
|
25
|
+
if (!existsSync(obsDir)) {
|
|
26
|
+
try {
|
|
27
|
+
mkdirSync(obsDir, { recursive: true });
|
|
28
|
+
} catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const event = input.event || input.hook_event_name || 'unknown';
|
|
34
|
+
const phase = /stop$/i.test(event) ? 'stop' : 'start';
|
|
35
|
+
|
|
36
|
+
const entry = {
|
|
37
|
+
ts: new Date().toISOString(),
|
|
38
|
+
event: phase,
|
|
39
|
+
agent: input.agent_name || input.subagent_type || input.agent || 'unknown',
|
|
40
|
+
};
|
|
41
|
+
if (input.session_id) entry.session = input.session_id;
|
|
42
|
+
if (phase === 'stop' && input.exit_status) entry.exit = input.exit_status;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
appendFileSync(join(obsDir, 'agent-events.jsonl'), JSON.stringify(entry) + '\n');
|
|
46
|
+
} catch {
|
|
47
|
+
// Non-critical
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
main();
|
|
53
|
+
} catch {
|
|
54
|
+
// Never block session
|
|
55
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// UserPromptSubmit hook: records slash-command invocations to
|
|
5
|
+
// .claude/observability/command-invocations.jsonl. Reads JSON from stdin
|
|
6
|
+
// per Claude Code hook contract. Skips non-slash prompts (filter is
|
|
7
|
+
// `/^/`). Always exits 0.
|
|
8
|
+
|
|
9
|
+
const { readFileSync, appendFileSync, mkdirSync, existsSync } = require('fs');
|
|
10
|
+
const { join } = require('path');
|
|
11
|
+
|
|
12
|
+
const SLASH_RE = /^\s*\/([\w-]+)/;
|
|
13
|
+
|
|
14
|
+
function main() {
|
|
15
|
+
let input;
|
|
16
|
+
try {
|
|
17
|
+
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const prompt = input.prompt || input.user_prompt || input.text || '';
|
|
23
|
+
const match = SLASH_RE.exec(prompt);
|
|
24
|
+
if (!match) return;
|
|
25
|
+
|
|
26
|
+
const cwd = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
|
27
|
+
const obsDir = join(cwd, '.claude', 'observability');
|
|
28
|
+
if (!existsSync(obsDir)) {
|
|
29
|
+
try {
|
|
30
|
+
mkdirSync(obsDir, { recursive: true });
|
|
31
|
+
} catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const entry = {
|
|
37
|
+
ts: new Date().toISOString(),
|
|
38
|
+
command: '/' + match[1],
|
|
39
|
+
};
|
|
40
|
+
if (input.session_id) entry.session = input.session_id;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
appendFileSync(join(obsDir, 'command-invocations.jsonl'), JSON.stringify(entry) + '\n');
|
|
44
|
+
} catch {
|
|
45
|
+
// Non-critical
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
main();
|
|
51
|
+
} catch {
|
|
52
|
+
// Never block session
|
|
53
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// InstructionsLoaded hook: records each skill load to
|
|
5
|
+
// .claude/observability/skill-loads.jsonl. Reads JSON from stdin per Claude
|
|
6
|
+
// Code hook contract. Always exits 0 — never blocks the session.
|
|
7
|
+
|
|
8
|
+
const { readFileSync, appendFileSync, mkdirSync, existsSync } = require('fs');
|
|
9
|
+
const { join } = require('path');
|
|
10
|
+
|
|
11
|
+
function main() {
|
|
12
|
+
let input;
|
|
13
|
+
try {
|
|
14
|
+
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cwd = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
|
20
|
+
const obsDir = join(cwd, '.claude', 'observability');
|
|
21
|
+
if (!existsSync(obsDir)) {
|
|
22
|
+
try {
|
|
23
|
+
mkdirSync(obsDir, { recursive: true });
|
|
24
|
+
} catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const skill =
|
|
30
|
+
input.skill_name ||
|
|
31
|
+
input.skill ||
|
|
32
|
+
input.instructions_name ||
|
|
33
|
+
(input.path && String(input.path).split('/').filter(Boolean).slice(-2, -1)[0]) ||
|
|
34
|
+
'unknown';
|
|
35
|
+
const trigger = input.trigger || input.reason || 'unknown';
|
|
36
|
+
|
|
37
|
+
const entry = {
|
|
38
|
+
ts: new Date().toISOString(),
|
|
39
|
+
skill,
|
|
40
|
+
trigger,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
appendFileSync(join(obsDir, 'skill-loads.jsonl'), JSON.stringify(entry) + '\n');
|
|
45
|
+
} catch {
|
|
46
|
+
// Non-critical
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
main();
|
|
52
|
+
} catch {
|
|
53
|
+
// Never block session
|
|
54
|
+
}
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
// UserPromptSubmit hook: hints at relevant skills based on user prompt keywords.
|
|
5
|
-
// Reads .claude/skills/ directory
|
|
5
|
+
// Reads .claude/skills/ directory and matches prompt tokens against skill
|
|
6
|
+
// directory names AND each skill's `description:` frontmatter line. The
|
|
7
|
+
// description fallback lets renames stay in sync with intent (e.g. a skill
|
|
8
|
+
// named "compact-safe" still matches the prompt "session context budget"
|
|
9
|
+
// because its description mentions "context" and "session").
|
|
6
10
|
// Outputs at most one hint to stdout if a match is found; empty output otherwise.
|
|
7
11
|
// Always exits 0.
|
|
8
12
|
|
|
@@ -24,6 +28,18 @@ function tokenize(text) {
|
|
|
24
28
|
.filter((w) => w.length >= 4 && !STOPWORDS.has(w));
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
function readSkillDescription(skillsDir, slug) {
|
|
32
|
+
try {
|
|
33
|
+
const content = readFileSync(path.join(skillsDir, slug, 'SKILL.md'), 'utf8');
|
|
34
|
+
const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
35
|
+
if (!fm) return '';
|
|
36
|
+
const desc = fm[1].match(/^description:\s*["']?(.+?)["']?\s*$/m);
|
|
37
|
+
return desc ? desc[1] : '';
|
|
38
|
+
} catch {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
try {
|
|
28
44
|
const data = JSON.parse(readFileSync(0, 'utf8'));
|
|
29
45
|
const prompt = data.input?.prompt || '';
|
|
@@ -50,7 +66,11 @@ try {
|
|
|
50
66
|
|
|
51
67
|
for (const slug of skills) {
|
|
52
68
|
const slugTokens = tokenize(slug);
|
|
53
|
-
|
|
69
|
+
let hit = slugTokens.some((t) => promptTokens.has(t));
|
|
70
|
+
if (!hit) {
|
|
71
|
+
const descTokens = tokenize(readSkillDescription(skillsDir, slug));
|
|
72
|
+
hit = descTokens.some((t) => promptTokens.has(t));
|
|
73
|
+
}
|
|
54
74
|
if (hit) {
|
|
55
75
|
process.stdout.write(
|
|
56
76
|
`[Skill hint] Consider loading skill: ${slug}/SKILL.md\n`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Drift detection for /start. Invoke as a single command:
|
|
3
|
+
# bash .claude/scripts/start-drift.sh
|
|
4
|
+
# Bundling this in a script avoids per-line permission prompts that fire on
|
|
5
|
+
# multi-line bash with X=$(...) patterns (env-var-prefixed substitution
|
|
6
|
+
# is not covered by Bash(cmd:*) allow rules).
|
|
7
|
+
set -eu
|
|
8
|
+
|
|
9
|
+
LAST_SESSION=$(ls -t .claude/sessions/*.md 2>/dev/null | head -1 || true)
|
|
10
|
+
LAST_SHA=""
|
|
11
|
+
if [ -n "$LAST_SESSION" ]; then
|
|
12
|
+
LAST_SHA=$(awk '/^sha:/ {print $2; exit}' "$LAST_SESSION" 2>/dev/null || true)
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
if [ -n "$LAST_SHA" ] && git rev-parse --verify --quiet "$LAST_SHA" >/dev/null 2>&1; then
|
|
16
|
+
echo "Commits since last session SHA ($LAST_SHA):"
|
|
17
|
+
git log --oneline "$LAST_SHA"..HEAD 2>/dev/null | head -15
|
|
18
|
+
elif [ -n "$LAST_SESSION" ]; then
|
|
19
|
+
SESSION_DATE=$(echo "$LAST_SESSION" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1)
|
|
20
|
+
echo "Commits since last session ($SESSION_DATE):"
|
|
21
|
+
git log --oneline --since="$SESSION_DATE" 2>/dev/null | head -15
|
|
22
|
+
else
|
|
23
|
+
echo "No previous session found. Recent commits:"
|
|
24
|
+
git log --oneline -10 2>/dev/null
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Current branch:"
|
|
29
|
+
git branch --show-current 2>/dev/null
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolve the last release tag and its commit date (YYYY-MM-DD) for /sync.
|
|
3
|
+
# Invoke as a single command:
|
|
4
|
+
# bash .claude/scripts/sync-release-scope.sh
|
|
5
|
+
# Output: two key=value lines suitable for downstream parsing.
|
|
6
|
+
# last_tag=v1.2.3
|
|
7
|
+
# since=2026-04-01
|
|
8
|
+
# When no tag exists, both values are empty and /sync should bootstrap one.
|
|
9
|
+
set -eu
|
|
10
|
+
|
|
11
|
+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
12
|
+
SINCE=""
|
|
13
|
+
if [ -n "$LAST_TAG" ]; then
|
|
14
|
+
SINCE=$(git log -1 --format=%as "$LAST_TAG" 2>/dev/null || echo "")
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
printf 'last_tag=%s\nsince=%s\n' "$LAST_TAG" "$SINCE"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# List files changed since the last release tag (or last 10 commits if no tag).
|
|
3
|
+
# Invoke as a single command:
|
|
4
|
+
# bash .claude/scripts/test-coverage-changed-files.sh
|
|
5
|
+
# Output is one filename per line, sorted and de-duplicated.
|
|
6
|
+
set -eu
|
|
7
|
+
|
|
8
|
+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
9
|
+
|
|
10
|
+
if [ -n "$LAST_TAG" ]; then
|
|
11
|
+
git log "$LAST_TAG"..HEAD --name-only --pretty=format: 2>/dev/null | sort -u | grep -v '^$' || true
|
|
12
|
+
else
|
|
13
|
+
git diff --name-only HEAD~10 2>/dev/null || true
|
|
14
|
+
fi
|
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
"Bash(worclaude scan:*)",
|
|
20
20
|
"Bash(worclaude setup-state:*)",
|
|
21
21
|
|
|
22
|
+
"// -- Shell builtins for slash-command flow --",
|
|
23
|
+
"Bash(test:*)",
|
|
24
|
+
"Bash([:*)",
|
|
25
|
+
"Bash(bash:*)",
|
|
26
|
+
|
|
27
|
+
"// -- WebFetch (research targets) + built-in WebSearch --",
|
|
28
|
+
"WebFetch(domain:docs.anthropic.com)",
|
|
29
|
+
"WebFetch(domain:docs.claude.com)",
|
|
30
|
+
"WebFetch(domain:github.com)",
|
|
31
|
+
"WebFetch(domain:api.github.com)",
|
|
32
|
+
"WebSearch",
|
|
33
|
+
|
|
34
|
+
"// -- Built-in skills used by the workflow --",
|
|
35
|
+
"Skill(update-config)",
|
|
36
|
+
"Skill(fewer-permission-prompts)",
|
|
37
|
+
|
|
22
38
|
"// -- Common Dev Tools --",
|
|
23
39
|
"Bash(echo:*)", "Bash(mkdir:*)", "Bash(touch:*)",
|
|
24
40
|
"Bash(cp:*)", "Bash(mv:*)",
|
|
@@ -40,6 +56,29 @@
|
|
|
40
56
|
"Edit(README*)", "Edit(*.md)",
|
|
41
57
|
"Edit(package.json)", "Edit(pyproject.toml)",
|
|
42
58
|
"Edit(.github/**)"
|
|
59
|
+
],
|
|
60
|
+
"deny": [
|
|
61
|
+
"// -- Secret files (Read/Edit — Claude's own tools) --",
|
|
62
|
+
"Read(./.env)", "Read(./.env.*)",
|
|
63
|
+
"Read(./secrets/**)",
|
|
64
|
+
"Edit(./.env)", "Edit(./.env.*)", "Edit(./secrets/**)",
|
|
65
|
+
"Read(./*.pem)", "Read(./*.key)",
|
|
66
|
+
"Read(./id_rsa)", "Read(./id_ed25519)",
|
|
67
|
+
|
|
68
|
+
"// -- SSH and cloud creds (home-relative) --",
|
|
69
|
+
"Read(~/.ssh/**)",
|
|
70
|
+
"Read(~/.aws/credentials)",
|
|
71
|
+
"Read(~/.config/gh/hosts.yml)",
|
|
72
|
+
|
|
73
|
+
"// -- Bash file-view commands targeting .env --",
|
|
74
|
+
"Bash(cat *.env*)", "Bash(head *.env*)", "Bash(tail *.env*)",
|
|
75
|
+
"Bash(less *.env*)", "Bash(more *.env*)", "Bash(grep *.env*)",
|
|
76
|
+
|
|
77
|
+
"// -- Catastrophic rm patterns (literal-match defense-in-depth) --",
|
|
78
|
+
"Bash(rm -rf /)", "Bash(rm -rf /*)",
|
|
79
|
+
"Bash(rm -fr /)", "Bash(rm -fr /*)",
|
|
80
|
+
"Bash(rm -rf ~)", "Bash(rm -rf ~/*)",
|
|
81
|
+
"Bash(rm -rf $HOME)", "Bash(rm -rf $HOME/*)"
|
|
43
82
|
]
|
|
44
83
|
},
|
|
45
84
|
"hooks": {
|
|
@@ -120,6 +159,40 @@
|
|
|
120
159
|
"type": "command",
|
|
121
160
|
"command": "p=${WORCLAUDE_HOOK_PROFILE:-standard}; case \"$p\" in minimal) exit 0;; esac; test -f .claude/hooks/skill-hint.cjs && node .claude/hooks/skill-hint.cjs || true"
|
|
122
161
|
}]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"matcher": "",
|
|
165
|
+
"hooks": [{
|
|
166
|
+
"type": "command",
|
|
167
|
+
"command": "p=${WORCLAUDE_HOOK_PROFILE:-standard}; case \"$p\" in minimal) exit 0;; esac; test -f .claude/hooks/obs-command-invocations.cjs && node .claude/hooks/obs-command-invocations.cjs || true"
|
|
168
|
+
}]
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"InstructionsLoaded": [
|
|
172
|
+
{
|
|
173
|
+
"matcher": "",
|
|
174
|
+
"hooks": [{
|
|
175
|
+
"type": "command",
|
|
176
|
+
"command": "p=${WORCLAUDE_HOOK_PROFILE:-standard}; case \"$p\" in minimal) exit 0;; esac; test -f .claude/hooks/obs-skill-loads.cjs && node .claude/hooks/obs-skill-loads.cjs || true"
|
|
177
|
+
}]
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"SubagentStart": [
|
|
181
|
+
{
|
|
182
|
+
"matcher": "",
|
|
183
|
+
"hooks": [{
|
|
184
|
+
"type": "command",
|
|
185
|
+
"command": "p=${WORCLAUDE_HOOK_PROFILE:-standard}; case \"$p\" in minimal) exit 0;; esac; test -f .claude/hooks/obs-agent-events.cjs && node .claude/hooks/obs-agent-events.cjs || true"
|
|
186
|
+
}]
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"SubagentStop": [
|
|
190
|
+
{
|
|
191
|
+
"matcher": "",
|
|
192
|
+
"hooks": [{
|
|
193
|
+
"type": "command",
|
|
194
|
+
"command": "p=${WORCLAUDE_HOOK_PROFILE:-standard}; case \"$p\" in minimal) exit 0;; esac; test -f .claude/hooks/obs-agent-events.cjs && node .claude/hooks/obs-agent-events.cjs || true"
|
|
195
|
+
}]
|
|
123
196
|
}
|
|
124
197
|
],
|
|
125
198
|
"Notification": [
|
|
@@ -21,11 +21,12 @@ The cycle:
|
|
|
21
21
|
This is why the Gotchas section exists. It grows organically from real problems
|
|
22
22
|
encountered during development.
|
|
23
23
|
|
|
24
|
-
## The
|
|
24
|
+
## The 200-Line Target
|
|
25
25
|
|
|
26
|
-
CLAUDE.md should stay under
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
CLAUDE.md should stay under 200 lines of actual content. This is the official
|
|
27
|
+
Claude Code guidance and matches `worclaude doctor`'s WARN threshold (150 lines /
|
|
28
|
+
30,000 chars) and ERROR threshold (200 lines). It is a target, not a hard limit,
|
|
29
|
+
but exceeding it significantly means CLAUDE.md is trying to do too much.
|
|
29
30
|
|
|
30
31
|
Claude reads CLAUDE.md at the start of every session and after every /compact.
|
|
31
32
|
Long CLAUDE.md files waste context on every single interaction.
|
|
@@ -78,7 +79,7 @@ Each gotcha should be:
|
|
|
78
79
|
## When to Prune
|
|
79
80
|
|
|
80
81
|
Review CLAUDE.md when:
|
|
81
|
-
- It exceeds
|
|
82
|
+
- It exceeds the 200-line target
|
|
82
83
|
- You notice rules that no longer apply
|
|
83
84
|
- A rule has been absorbed into a skill
|
|
84
85
|
- Two rules say the same thing differently
|
|
@@ -102,25 +103,60 @@ Only add rules for recurring problems.
|
|
|
102
103
|
|
|
103
104
|
## The @include Directive
|
|
104
105
|
|
|
105
|
-
When CLAUDE.md
|
|
106
|
-
into separate files
|
|
106
|
+
When CLAUDE.md outgrows what one file can hold cleanly, use `@path/to/import`
|
|
107
|
+
to split content into separate files that still load with CLAUDE.md:
|
|
107
108
|
|
|
108
109
|
```markdown
|
|
109
110
|
# CLAUDE.md
|
|
110
111
|
## Key Files
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
@README
|
|
113
|
+
@docs/git-instructions.md
|
|
114
|
+
@~/.claude/my-project-instructions.md
|
|
113
115
|
```
|
|
114
116
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
-
|
|
117
|
+
Syntax (per official Claude Code docs):
|
|
118
|
+
|
|
119
|
+
- `@path/to/import` — relative to the file containing the directive
|
|
120
|
+
(e.g., `@README`, `@docs/git-instructions.md`)
|
|
121
|
+
- `@~/path` — home-directory relative
|
|
122
|
+
(e.g., `@~/.claude/my-project-instructions.md`)
|
|
118
123
|
- Works in CLAUDE.md, .claude/CLAUDE.md, .claude/rules/*.md, and CLAUDE.local.md
|
|
119
124
|
- Does NOT work inside code blocks (only in leaf text nodes)
|
|
120
125
|
- Non-existent files are silently ignored; circular references are prevented
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
Behavior:
|
|
128
|
+
|
|
129
|
+
- **Imports load at launch and consume context.** They help organization, not
|
|
130
|
+
context budget. Splitting CLAUDE.md into 5 files of 50 lines each produces
|
|
131
|
+
the same context cost as one 250-line file.
|
|
132
|
+
- **Recursion is capped at 5 hops.** A imports B imports C... — Claude stops
|
|
133
|
+
resolving after 5 levels.
|
|
134
|
+
- **External imports trigger an approval dialog on first use.** Importing a
|
|
135
|
+
path outside the project root (e.g., a system-wide config) prompts the user
|
|
136
|
+
to confirm before the import resolves. This is a one-time approval.
|
|
137
|
+
|
|
138
|
+
### The `@~/.claude/...` worktree-share pattern
|
|
139
|
+
|
|
140
|
+
Worktree agents (`isolation: worktree`) operate in a freshly-checked-out copy
|
|
141
|
+
of the repo. Anything in `.claude/CLAUDE.local.md` or other gitignored files
|
|
142
|
+
is not present in the worktree, so the agent loses your local rules.
|
|
143
|
+
|
|
144
|
+
Workaround: store shared local rules in a home-directory file and import it
|
|
145
|
+
from the project's CLAUDE.md:
|
|
146
|
+
|
|
147
|
+
```markdown
|
|
148
|
+
# CLAUDE.md
|
|
149
|
+
@~/.claude/my-team-rules.md
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The worktree agent re-reads `~/.claude/my-team-rules.md` at launch (it lives
|
|
153
|
+
outside the repo, so no checkout is needed) and gets the same rules as your
|
|
154
|
+
main session. The first run triggers an external-import approval; after that
|
|
155
|
+
it loads silently.
|
|
156
|
+
|
|
157
|
+
Reserve this pattern for rules that belong to *you* across all projects
|
|
158
|
+
(coding style preferences, personal shortcuts). Team-shared rules should
|
|
159
|
+
still live inside the repo.
|
|
124
160
|
|
|
125
161
|
## Gotchas
|
|
126
162
|
|
|
@@ -6,6 +6,16 @@ version: "1.0.0"
|
|
|
6
6
|
|
|
7
7
|
# Git Conventions
|
|
8
8
|
|
|
9
|
+
## Invocation Boundary
|
|
10
|
+
|
|
11
|
+
`git commit`, `git push`, and `gh pr create` are invoked only when the human
|
|
12
|
+
explicitly types `/commit-push-pr` or `/sync` (or one of their listed Trigger
|
|
13
|
+
Phrases). The slash commands themselves are not invoked autonomously — wait for
|
|
14
|
+
the human trigger. The `Version bump:` AskUserQuestion in `/commit-push-pr` is
|
|
15
|
+
non-skippable; refuse to proceed without an explicit human selection.
|
|
16
|
+
|
|
17
|
+
See CLAUDE.md Critical Rule 13.
|
|
18
|
+
|
|
9
19
|
## Branch Naming
|
|
10
20
|
|
|
11
21
|
Pattern: `{type}/{short-description}`
|
|
@@ -98,7 +108,7 @@ When using `git worktree` for parallel work:
|
|
|
98
108
|
- Always clean up worktrees when done: `git worktree remove {path}`
|
|
99
109
|
- Don't leave stale worktrees — they hold refs and can cause confusion
|
|
100
110
|
|
|
101
|
-
Agents that use worktree isolation (code-simplifier, test-writer,
|
|
111
|
+
Agents that use worktree isolation (code-simplifier, test-writer, bug-fixer, etc.)
|
|
102
112
|
create and clean up their own worktrees automatically.
|
|
103
113
|
|
|
104
114
|
## Shared-State Files
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Five-layer memory architecture: where each fact lives, how layers interact, when to promote learnings'
|
|
3
|
+
when_to_use: 'When deciding where a new fact, rule, or preference belongs. When triaging a [LEARN] capture. When promoting from learnings to CLAUDE.md.'
|
|
4
|
+
version: '1.0.0'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Memory Architecture
|
|
8
|
+
|
|
9
|
+
Worclaude projects use **five distinct memory layers**. Each has a different
|
|
10
|
+
scope, owner, and lifecycle. Routing a fact to the correct layer is a
|
|
11
|
+
load-bearing decision — the wrong layer means the fact is invisible when
|
|
12
|
+
needed, or noisy when not.
|
|
13
|
+
|
|
14
|
+
## The Five Layers
|
|
15
|
+
|
|
16
|
+
| Layer | Scope | Owner | Lifecycle |
|
|
17
|
+
| ------------------------- | -------- | ----------------------- | -------------------------------- |
|
|
18
|
+
| `CLAUDE.md` | Team | Manual (humans + Claude via `/update-claude-md`) | Stable, lean (target ~200 lines) |
|
|
19
|
+
| `.claude/rules/` | Team | Manual, topic-organized | Stable; optionally path-scoped (deferred — see BACKLOG) |
|
|
20
|
+
| `.claude/learnings/` | Team | Hook-captured (`learn-capture.cjs`) | Append-only; transient inputs to promotion |
|
|
21
|
+
| `CLAUDE.local.md` | Personal | Manual; gitignored | Per-machine sandbox; never shared |
|
|
22
|
+
| Claude Code auto memory | Personal | Autonomous (Claude) | Active, self-pruning |
|
|
23
|
+
|
|
24
|
+
The line between team and personal is the most important boundary. Team
|
|
25
|
+
layers are committed and shared with collaborators; personal layers stay
|
|
26
|
+
on the local machine.
|
|
27
|
+
|
|
28
|
+
## Routing Contract
|
|
29
|
+
|
|
30
|
+
When a fact, rule, or pattern surfaces during a session, route it like this:
|
|
31
|
+
|
|
32
|
+
| Source | Destination |
|
|
33
|
+
| --------------------------------------------------- | --------------------------------- |
|
|
34
|
+
| A team-relevant rule the user wants enforced — typed `[LEARN]` block or `/learn` invocation | `.claude/learnings/<category>.md` (via hook) |
|
|
35
|
+
| A personal preference (workflow, tone, naming whim) | Claude Code auto memory (Claude does this autonomously when noticed) |
|
|
36
|
+
| A machine-local sandbox value (paths, secrets, dev URLs) | `CLAUDE.local.md` (manual; gitignored) |
|
|
37
|
+
| A topic that has accreted multiple learnings AND is stable | Promote to `CLAUDE.md` via `/update-claude-md` |
|
|
38
|
+
|
|
39
|
+
**Default rule:** if you cannot point to a specific reason a fact belongs in
|
|
40
|
+
a different layer, it does not belong in `CLAUDE.md`. `CLAUDE.md` is the
|
|
41
|
+
last layer to grow, not the first.
|
|
42
|
+
|
|
43
|
+
## Layer Interactions
|
|
44
|
+
|
|
45
|
+
- **`CLAUDE.md` is the read-on-every-session layer.** It is loaded into
|
|
46
|
+
context at session start and after every `/compact`. Long files waste
|
|
47
|
+
context on every interaction. Stay under ~200 lines of actual content.
|
|
48
|
+
- **`.claude/learnings/` is the staging area.** Hooks write here on every
|
|
49
|
+
`[LEARN]` block. Same category = same file = appended block, so a file
|
|
50
|
+
that grows multiple `**Rule:**` entries signals recurrence. The
|
|
51
|
+
`index.json` `created` field is updated to the latest capture date —
|
|
52
|
+
use it as a "last touched" timestamp, not a fixed creation date.
|
|
53
|
+
- **Auto memory runs in parallel.** Claude Code maintains
|
|
54
|
+
`~/.claude/projects/<project-slug>/memory/` autonomously. It is
|
|
55
|
+
per-machine and personal. Worclaude does not write to it and does not
|
|
56
|
+
read from it during `/update-claude-md` promotion (deliberate scope
|
|
57
|
+
boundary — see BACKLOG for the discussion).
|
|
58
|
+
- **`CLAUDE.local.md` overrides `CLAUDE.md`** for the local machine. Use
|
|
59
|
+
it for facts that are genuinely user-specific within an otherwise shared
|
|
60
|
+
project. Do not commit it.
|
|
61
|
+
- **`.claude/rules/` is reserved.** Claude Code's official docs recommend
|
|
62
|
+
it for topic-organized, optionally path-scoped team rules. Worclaude
|
|
63
|
+
defers scaffolding it until a usage signal exists; users can still
|
|
64
|
+
create the folder manually. Do not duplicate `CLAUDE.md` content into
|
|
65
|
+
`.claude/rules/` ad-hoc.
|
|
66
|
+
|
|
67
|
+
## Promotion Path: Learnings → CLAUDE.md
|
|
68
|
+
|
|
69
|
+
Promotion is the bridge from `.claude/learnings/` to `CLAUDE.md`. It is
|
|
70
|
+
deliberately gated by `/update-claude-md` rather than automatic — promotion
|
|
71
|
+
is a content decision, not a mechanical one.
|
|
72
|
+
|
|
73
|
+
A learning is a **promotion candidate** when at least one of these holds:
|
|
74
|
+
|
|
75
|
+
1. **Recurrence:** the learning's file in `.claude/learnings/` has 3 or
|
|
76
|
+
more `**Rule:**` blocks (i.e., the same category was captured at least
|
|
77
|
+
three times). Counted by scanning the file, not the index.
|
|
78
|
+
2. **Recency cluster:** the index entry's `created` date is within the
|
|
79
|
+
last 14 days AND the same theme has shown up in another recent
|
|
80
|
+
learning. Recent + repeated > recent alone.
|
|
81
|
+
3. **Drift:** the learning's content is structurally relevant to an
|
|
82
|
+
existing `CLAUDE.md` section (e.g., a new "always do X" pattern that
|
|
83
|
+
would naturally live in `## Critical Rules` or `## Gotchas`) but the
|
|
84
|
+
pattern is missing from the file.
|
|
85
|
+
|
|
86
|
+
Even when a candidate qualifies, `/update-claude-md` confirms each
|
|
87
|
+
proposed addition with the user via `AskUserQuestion`. No silent writes.
|
|
88
|
+
|
|
89
|
+
## Don't / Do
|
|
90
|
+
|
|
91
|
+
- **Don't** edit `.claude/learnings/` files by hand to "fix" them. They
|
|
92
|
+
are the raw capture surface. If a learning is wrong, fix the rule in
|
|
93
|
+
`CLAUDE.md` or remove the learning file.
|
|
94
|
+
- **Don't** scaffold `.claude/rules/` content from old `CLAUDE.md`
|
|
95
|
+
sections "to make `CLAUDE.md` smaller." Splitting into sub-files just
|
|
96
|
+
fragments the single source of truth without saving context.
|
|
97
|
+
- **Don't** mix personal preferences into team layers. If something
|
|
98
|
+
applies only to your local workflow, it belongs in `CLAUDE.local.md`
|
|
99
|
+
or Claude Code's auto memory — not in `CLAUDE.md`.
|
|
100
|
+
- **Do** delete stale learnings. If a category was captured once eight
|
|
101
|
+
months ago and never recurred, it is noise.
|
|
102
|
+
- **Do** prune `CLAUDE.md` when it crosses 200 lines. `worclaude doctor`
|
|
103
|
+
warns at 150 and errors at 200. Pruning is part of maintenance.
|
|
104
|
+
- **Do** read the file before recommending an update. Memory across
|
|
105
|
+
sessions is not authoritative — current file content is.
|
|
106
|
+
|
|
107
|
+
## Cross-References
|
|
108
|
+
|
|
109
|
+
- `/learn` — captures a `[LEARN]` block to `.claude/learnings/`.
|
|
110
|
+
- `/update-claude-md` — proposes promotions from learnings to
|
|
111
|
+
`CLAUDE.md`, with size + dedup gates.
|
|
112
|
+
- `claude-md-maintenance` skill — what belongs in `CLAUDE.md`, format
|
|
113
|
+
discipline, the 200-line target.
|
|
114
|
+
- `worclaude doctor` — surfaces drift between `CLAUDE.md` claims and
|
|
115
|
+
`package.json` reality (see Phase 3 T3.8).
|