azclaude-copilot 0.1.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/.claude-plugin/marketplace.json +27 -0
- package/.claude-plugin/plugin.json +17 -0
- package/LICENSE +21 -0
- package/README.md +477 -0
- package/bin/cli.js +1027 -0
- package/bin/copilot.js +228 -0
- package/hooks/README.md +3 -0
- package/hooks/hooks.json +40 -0
- package/package.json +41 -0
- package/templates/CLAUDE.md +51 -0
- package/templates/agents/cc-cli-integrator.md +104 -0
- package/templates/agents/cc-template-author.md +109 -0
- package/templates/agents/cc-test-maintainer.md +101 -0
- package/templates/agents/code-reviewer.md +136 -0
- package/templates/agents/loop-controller.md +118 -0
- package/templates/agents/orchestrator-init.md +196 -0
- package/templates/agents/test-writer.md +129 -0
- package/templates/capabilities/evolution/cycle2-knowledge.md +87 -0
- package/templates/capabilities/evolution/cycle3-topology.md +128 -0
- package/templates/capabilities/evolution/detect.md +103 -0
- package/templates/capabilities/evolution/evaluate.md +90 -0
- package/templates/capabilities/evolution/generate.md +123 -0
- package/templates/capabilities/evolution/re-derivation.md +77 -0
- package/templates/capabilities/intelligence/debate.md +104 -0
- package/templates/capabilities/intelligence/elo.md +122 -0
- package/templates/capabilities/intelligence/experiment.md +86 -0
- package/templates/capabilities/intelligence/opro.md +84 -0
- package/templates/capabilities/intelligence/pipeline.md +149 -0
- package/templates/capabilities/level-builders/level1-claudemd.md +52 -0
- package/templates/capabilities/level-builders/level2-mcp.md +58 -0
- package/templates/capabilities/level-builders/level3-skills.md +276 -0
- package/templates/capabilities/level-builders/level4-memory.md +72 -0
- package/templates/capabilities/level-builders/level5-agents.md +123 -0
- package/templates/capabilities/level-builders/level6-hooks.md +119 -0
- package/templates/capabilities/level-builders/level7-extmcp.md +60 -0
- package/templates/capabilities/level-builders/level8-orchestrated.md +98 -0
- package/templates/capabilities/manifest.md +58 -0
- package/templates/capabilities/shared/5-layer-agent.md +206 -0
- package/templates/capabilities/shared/completion-rule.md +44 -0
- package/templates/capabilities/shared/context-artifacts.md +96 -0
- package/templates/capabilities/shared/domain-advisor-generator.md +205 -0
- package/templates/capabilities/shared/friction-log.md +43 -0
- package/templates/capabilities/shared/multi-cli-paths.md +56 -0
- package/templates/capabilities/shared/native-tools.md +199 -0
- package/templates/capabilities/shared/plan-tracker.md +69 -0
- package/templates/capabilities/shared/pressure-test.md +88 -0
- package/templates/capabilities/shared/quality-check.md +83 -0
- package/templates/capabilities/shared/reflexes.md +159 -0
- package/templates/capabilities/shared/review-reception.md +70 -0
- package/templates/capabilities/shared/security.md +174 -0
- package/templates/capabilities/shared/semantic-boundary-check.md +140 -0
- package/templates/capabilities/shared/session-rhythm.md +42 -0
- package/templates/capabilities/shared/tdd.md +54 -0
- package/templates/capabilities/shared/vocabulary-transform.md +63 -0
- package/templates/commands/add.md +152 -0
- package/templates/commands/audit.md +123 -0
- package/templates/commands/blueprint.md +115 -0
- package/templates/commands/copilot.md +157 -0
- package/templates/commands/create.md +156 -0
- package/templates/commands/debate.md +75 -0
- package/templates/commands/deps.md +112 -0
- package/templates/commands/doc.md +100 -0
- package/templates/commands/dream.md +120 -0
- package/templates/commands/evolve.md +170 -0
- package/templates/commands/explain.md +25 -0
- package/templates/commands/find.md +100 -0
- package/templates/commands/fix.md +122 -0
- package/templates/commands/hookify.md +100 -0
- package/templates/commands/level-up.md +48 -0
- package/templates/commands/loop.md +62 -0
- package/templates/commands/migrate.md +119 -0
- package/templates/commands/persist.md +73 -0
- package/templates/commands/pulse.md +87 -0
- package/templates/commands/refactor.md +97 -0
- package/templates/commands/reflect.md +107 -0
- package/templates/commands/reflexes.md +141 -0
- package/templates/commands/setup.md +97 -0
- package/templates/commands/ship.md +131 -0
- package/templates/commands/snapshot.md +70 -0
- package/templates/commands/test.md +86 -0
- package/templates/hooks/post-tool-use.js +175 -0
- package/templates/hooks/stop.js +85 -0
- package/templates/hooks/user-prompt.js +96 -0
- package/templates/scripts/env-scan.sh +46 -0
- package/templates/scripts/import-graph.sh +88 -0
- package/templates/scripts/validate-boundaries.sh +180 -0
- package/templates/skills/agent-creator/SKILL.md +91 -0
- package/templates/skills/agent-creator/examples/sample-agent.md +80 -0
- package/templates/skills/agent-creator/references/agent-engineering-guide.md +596 -0
- package/templates/skills/agent-creator/references/quality-checklist.md +42 -0
- package/templates/skills/agent-creator/scripts/scaffold.sh +144 -0
- package/templates/skills/architecture-advisor/SKILL.md +92 -0
- package/templates/skills/architecture-advisor/references/database-decisions.md +61 -0
- package/templates/skills/architecture-advisor/references/decision-matrices.md +122 -0
- package/templates/skills/architecture-advisor/references/rendering-decisions.md +39 -0
- package/templates/skills/architecture-advisor/scripts/detect-scale.sh +67 -0
- package/templates/skills/debate/SKILL.md +36 -0
- package/templates/skills/debate/references/acemad-protocol.md +72 -0
- package/templates/skills/env-scanner/SKILL.md +41 -0
- package/templates/skills/security/SKILL.md +44 -0
- package/templates/skills/security/references/security-details.md +48 -0
- package/templates/skills/session-guard/SKILL.md +33 -0
- package/templates/skills/skill-creator/SKILL.md +82 -0
- package/templates/skills/skill-creator/examples/sample-skill.md +74 -0
- package/templates/skills/skill-creator/references/quality-checklist.md +36 -0
- package/templates/skills/skill-creator/references/skill-engineering-guide.md +365 -0
- package/templates/skills/skill-creator/scripts/scaffold.sh +75 -0
- package/templates/skills/test-first/SKILL.md +41 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
description: Analyze the project, fill CLAUDE.md, create memory structure. Run once at project start or to fill unfilled placeholders.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /setup — Project Environment Setup
|
|
9
|
+
|
|
10
|
+
Current environment:
|
|
11
|
+
!`bash .claude/scripts/env-scan.sh 2>/dev/null || echo '{"note": "run npx azclaude first to install scripts"}'`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Step 1: Scan
|
|
16
|
+
|
|
17
|
+
Check for project signals:
|
|
18
|
+
```bash
|
|
19
|
+
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null
|
|
20
|
+
find . -name "*.ts" -o -name "*.py" -o -name "*.rs" | head -5 2>/dev/null
|
|
21
|
+
ls knowledge/ docs/ 2>/dev/null
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Read existing CLAUDE.md if present — note which fields are unfilled placeholders.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 2: Clarify (if domain is ambiguous)
|
|
29
|
+
|
|
30
|
+
If the scan cannot determine domain clearly (e.g. mixed signals, no code files, empty project):
|
|
31
|
+
Use **AskUserQuestion** to collect:
|
|
32
|
+
- **Domain**: developer / writer / researcher / compliance / data-ML / other
|
|
33
|
+
- **Primary language / stack** (if developer)
|
|
34
|
+
- **Team size**: solo / small team / org
|
|
35
|
+
- **What's the main goal of this project?**
|
|
36
|
+
|
|
37
|
+
Skip this step if the scan already answers these clearly.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 3: Track Setup Progress
|
|
42
|
+
|
|
43
|
+
**TaskCreate** for each step before starting:
|
|
44
|
+
- `Scan project` (mark completed after Step 1)
|
|
45
|
+
- `Fill CLAUDE.md`
|
|
46
|
+
- `Create memory structure`
|
|
47
|
+
- `Create skills`
|
|
48
|
+
- `Run quality check`
|
|
49
|
+
|
|
50
|
+
**TaskUpdate → in_progress** as each step begins.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Step 4: Spawn Initialization Specialist
|
|
55
|
+
|
|
56
|
+
Spawn `agents/orchestrator-init.md` as a subagent with:
|
|
57
|
+
- Current working directory
|
|
58
|
+
- Domain and stack (from scan or AskUserQuestion answers)
|
|
59
|
+
- Project scale (file count)
|
|
60
|
+
- Existing CLAUDE.md content (to avoid overwriting intentional config)
|
|
61
|
+
|
|
62
|
+
The orchestrator-init agent:
|
|
63
|
+
- Detects scale, domain, and signals
|
|
64
|
+
- Fills CLAUDE.md
|
|
65
|
+
- Creates `.claude/memory/goals.md`
|
|
66
|
+
- Creates `knowledge-index.md` if `knowledge/` directory exists
|
|
67
|
+
- Runs environment scan as a single script (not 15 separate calls)
|
|
68
|
+
|
|
69
|
+
**TaskUpdate → completed** for Fill CLAUDE.md and Create memory structure steps.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Step 5: Quality Gate
|
|
74
|
+
|
|
75
|
+
Load `capabilities/shared/quality-check.md` and run the full environment check.
|
|
76
|
+
|
|
77
|
+
**TaskUpdate → completed** for Run quality check.
|
|
78
|
+
|
|
79
|
+
All ✓ required before printing "Setup complete."
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## If Running Again on an Existing Project
|
|
84
|
+
|
|
85
|
+
Do not overwrite CLAUDE.md — read it first and update only the placeholders that are still unfilled.
|
|
86
|
+
Do not overwrite goals.md — read it and preserve existing threads.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Completion Rule
|
|
91
|
+
|
|
92
|
+
Show:
|
|
93
|
+
1. Filled CLAUDE.md (full content)
|
|
94
|
+
2. Created goals.md (full content)
|
|
95
|
+
3. All TaskUpdate items marked completed
|
|
96
|
+
|
|
97
|
+
Do not say "setup complete" without showing these outputs.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ship
|
|
3
|
+
description: >
|
|
4
|
+
Stage changes, commit, and push to GitHub. Pre-ship gate: IDE diagnostics + tests must pass.
|
|
5
|
+
Triggers on: "ship", "push", "commit and push", "deploy", "send to GitHub", "save and push".
|
|
6
|
+
Skips .env and secrets automatically. Never ships with failing tests or IDE errors.
|
|
7
|
+
argument-hint: "[optional: commit message hint]"
|
|
8
|
+
disable-model-invocation: true
|
|
9
|
+
allowed-tools: Bash, Read
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /ship — Save and Push to GitHub
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Pre-Ship Gate (runs before any commit)
|
|
19
|
+
|
|
20
|
+
**1. IDE diagnostics** — use `mcp__ide__getDiagnostics` if available.
|
|
21
|
+
If unavailable or empty: skip this check.
|
|
22
|
+
If errors exist: STOP.
|
|
23
|
+
```
|
|
24
|
+
✗ Pre-ship blocked: {N} IDE errors. Fix with /fix before shipping.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**2. Tests**
|
|
28
|
+
```bash
|
|
29
|
+
{test command}; EXIT=$?
|
|
30
|
+
echo "Exit: $EXIT"
|
|
31
|
+
```
|
|
32
|
+
If EXIT ≠ 0: STOP.
|
|
33
|
+
```
|
|
34
|
+
✗ Pre-ship blocked: tests failing. Run /test to fix.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If both pass: `✓ Pre-ship gate passed`
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 0.5: Docs Sync (runs before commit)
|
|
42
|
+
|
|
43
|
+
Check for stale documentation — 3 quick greps, no file reads required:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 1. Version in README matches package.json?
|
|
47
|
+
README_VER=$(grep -oE '[0-9]+\.[0-9]+\.[0-9]+' README.md 2>/dev/null | head -1)
|
|
48
|
+
PKG_VER=$(node -p "require('./package.json').version" 2>/dev/null)
|
|
49
|
+
[ "$README_VER" != "$PKG_VER" ] && echo "⚠ README version ($README_VER) ≠ package.json ($PKG_VER) — update README"
|
|
50
|
+
|
|
51
|
+
# 2. Command count in README matches installed commands?
|
|
52
|
+
README_CMDS=$(grep -oE '[0-9]+ commands?' README.md 2>/dev/null | grep -oE '[0-9]+' | head -1)
|
|
53
|
+
ACTUAL_CMDS=$(ls .claude/commands/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
54
|
+
[ -n "$README_CMDS" ] && [ "$README_CMDS" != "$ACTUAL_CMDS" ] && echo "⚠ README says $README_CMDS commands, found $ACTUAL_CMDS"
|
|
55
|
+
|
|
56
|
+
# 3. Known stale risk section?
|
|
57
|
+
grep -q "bash.*Windows\|Git Bash.*fail" README.md 2>/dev/null && echo "⚠ README still has stale bash/Windows risk — update to Node.js hooks"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If any warnings fire: update README before committing.
|
|
61
|
+
If README does not exist or all checks pass: skip.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Step 1: Show What Will Ship
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git status --short
|
|
69
|
+
git diff --stat HEAD
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Step 2: Secret Scan
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git status --short | grep -iE "\.env|secret|credential|\.key|token|password"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If found: warn and skip those files. Never stage secrets.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Step 3: Stage and Commit
|
|
85
|
+
|
|
86
|
+
Stage changed files — never `.env`, secrets, `node_modules`.
|
|
87
|
+
|
|
88
|
+
Generate commit message:
|
|
89
|
+
- Format: `{type}: {what changed} — {why}`
|
|
90
|
+
- Types: `feat` / `fix` / `refactor` / `docs` / `chore`
|
|
91
|
+
- Lead with impact ("add user auth" not "update auth.ts")
|
|
92
|
+
- Use $ARGUMENTS hint if provided
|
|
93
|
+
|
|
94
|
+
If not a git repo: run `git init` first.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Step 4: Push
|
|
99
|
+
|
|
100
|
+
If remote exists: `git push`
|
|
101
|
+
|
|
102
|
+
If no remote:
|
|
103
|
+
```
|
|
104
|
+
git remote add origin https://github.com/{username}/{repo}.git
|
|
105
|
+
git push -u origin main
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Step 5: Deploy (Copilot Mode Only)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
[ -f .claude/copilot-intent.md ] && echo "COPILOT_MODE" || echo "INTERACTIVE_MODE"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If `COPILOT_MODE` and the intent mentions deploy targets (Vercel, Railway, Netlify, etc.):
|
|
117
|
+
1. Check if deploy config exists (vercel.json, railway.json, netlify.toml, Dockerfile)
|
|
118
|
+
2. If config exists → run the deploy command (`vercel --prod`, `railway up`, etc.)
|
|
119
|
+
3. If deploy succeeds → record URL in `.claude/copilot-report.md`
|
|
120
|
+
4. If deploy fails → log to `.claude/memory/blockers.md`, continue (product is still built)
|
|
121
|
+
5. If no deploy target mentioned → skip deployment, push is sufficient
|
|
122
|
+
|
|
123
|
+
If `INTERACTIVE_MODE`: skip Step 5.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Completion Rule
|
|
128
|
+
|
|
129
|
+
Show: files changed, commit hash, branch, push status.
|
|
130
|
+
If no changes: "Nothing to ship — working tree is clean."
|
|
131
|
+
Do not say "shipped" without showing the push output.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: snapshot
|
|
3
|
+
description: Mid-session snapshot — captures current reasoning, decisions, and state to survive context compaction. Run every 15-20 turns on complex work. Auto-injected on next session start.
|
|
4
|
+
argument-hint: "[optional: topic label]"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: Read, Write, Edit
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /snapshot — Mid-Session Snapshot
|
|
10
|
+
|
|
11
|
+
**Purpose**: Write your current mental model to disk NOW, before context compaction loses it.
|
|
12
|
+
This is different from `/persist` (end-of-session). Checkpoint = mid-flight snapshot.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Step 1: Read Current State
|
|
17
|
+
|
|
18
|
+
Read `.claude/memory/goals.md` — scan the "In progress" entries and "Current threads."
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Step 2: Write Checkpoint File
|
|
23
|
+
|
|
24
|
+
Write `.claude/memory/checkpoints/{YYYY-MM-DD}-{HH:MM}.md`:
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
---
|
|
28
|
+
date: {ISO datetime}
|
|
29
|
+
label: {$ARGUMENTS or "mid-session"}
|
|
30
|
+
files_in_progress: [{list from goals.md ## In progress}]
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## What I'm doing right now
|
|
34
|
+
{1-2 sentences: the specific task in flight, not the project description}
|
|
35
|
+
|
|
36
|
+
## Why — key decisions made this session
|
|
37
|
+
{Bullet list: each decision + the reason. Example:}
|
|
38
|
+
- Used spawnSync over exec: timeout needed, exec doesn't block
|
|
39
|
+
- Skipped STRUCTURE-ONLY on SKIM projects: blueprint.json already has scale field
|
|
40
|
+
|
|
41
|
+
## What I know that isn't written down yet
|
|
42
|
+
{Anything in working memory that hasn't been committed to a file or comment}
|
|
43
|
+
|
|
44
|
+
## What's next (top 3)
|
|
45
|
+
1. {next concrete action}
|
|
46
|
+
2.
|
|
47
|
+
3.
|
|
48
|
+
|
|
49
|
+
## Risk / open question
|
|
50
|
+
{Anything uncertain that needs resolution — or "None"}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Create the directory if needed: `.claude/memory/checkpoints/`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Step 3: Update goals.md
|
|
58
|
+
|
|
59
|
+
Add one line to the top of `## Current threads`:
|
|
60
|
+
```
|
|
61
|
+
- [checkpoint] {HH:MM} — {label} → .claude/memory/checkpoints/{date}-{HH:MM}.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Step 4: Confirm
|
|
67
|
+
|
|
68
|
+
Print the checkpoint file content.
|
|
69
|
+
Print: `Checkpoint saved. Inject on next session start: UserPromptSubmit will load the latest checkpoint automatically.`
|
|
70
|
+
Do NOT say "checkpoint saved" without showing the content.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test
|
|
3
|
+
description: >
|
|
4
|
+
Run the test suite, interpret results, and fix failures.
|
|
5
|
+
Triggers on: "run tests", "test this", "check tests", "tests failing", "are tests passing",
|
|
6
|
+
"run the suite", "test file X", "why is X test failing", "test before ship".
|
|
7
|
+
Checks IDE diagnostics first — type errors cause test failures.
|
|
8
|
+
argument-hint: "[test file, test name, or blank for full suite]"
|
|
9
|
+
disable-model-invocation: true
|
|
10
|
+
allowed-tools: Read, Bash, Grep
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /test — Run and Interpret Tests
|
|
14
|
+
|
|
15
|
+
$ARGUMENTS
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Step 1: IDE Diagnostics First
|
|
20
|
+
|
|
21
|
+
Use `mcp__ide__getDiagnostics` if available.
|
|
22
|
+
|
|
23
|
+
If it returns errors:
|
|
24
|
+
```
|
|
25
|
+
IDE errors found — these will cause test failures. Fix first:
|
|
26
|
+
file:line — message
|
|
27
|
+
```
|
|
28
|
+
If unavailable or returns empty: skip this step and proceed to Step 2.
|
|
29
|
+
|
|
30
|
+
Type errors and import failures cause misleading test output — clear them before running.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Step 2: Detect Test Framework
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
grep -E '"test":|"jest"|"vitest"|"mocha"|"playwright"' package.json 2>/dev/null | head -3
|
|
38
|
+
grep -E "pytest|unittest" requirements.txt pyproject.toml 2>/dev/null | head -3
|
|
39
|
+
ls Cargo.toml go.mod 2>/dev/null
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the framework already in the project. Never introduce a new one.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Step 3: Run Tests
|
|
47
|
+
|
|
48
|
+
Scope:
|
|
49
|
+
- $ARGUMENTS = specific file → run that file only
|
|
50
|
+
- $ARGUMENTS = test name → run matching test
|
|
51
|
+
- blank → run the full suite
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
{test command} {scope}; EXIT=$?
|
|
55
|
+
echo "Exit: $EXIT"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Paste the **full output** — never summarize it.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Step 4: Interpret Results
|
|
63
|
+
|
|
64
|
+
**If EXIT = 0**: Report `Tests: {N} passed, 0 failed` — done.
|
|
65
|
+
|
|
66
|
+
**If EXIT ≠ 0**: For each failing test:
|
|
67
|
+
1. Name the test exactly
|
|
68
|
+
2. Map to `file:line`
|
|
69
|
+
3. Classify:
|
|
70
|
+
- **Compile/type error** → fix IDE errors first (Step 1)
|
|
71
|
+
- **Assertion failure** → go to /fix Phase 2 at `file:line`
|
|
72
|
+
- **Timeout** → check for async issues, missing mocks
|
|
73
|
+
- **Not found / import error** → check module paths
|
|
74
|
+
|
|
75
|
+
Fix one failing test at a time — not all at once.
|
|
76
|
+
Apply /fix self-correction loop for each: 2 attempts, then escalate.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Completion Rule
|
|
81
|
+
|
|
82
|
+
Show the full test output.
|
|
83
|
+
State: `Tests: {N} passed, {M} failed`.
|
|
84
|
+
|
|
85
|
+
If failures remain: list each with `file:line` — stay in progress.
|
|
86
|
+
Do not report "tests done" with failures open.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* AZCLAUDE — PostToolUse hook
|
|
5
|
+
* Tracks edits to goals.md after every Write/Edit.
|
|
6
|
+
* Captures: timestamp, file path, git diff stat (+N/-N), and change summary.
|
|
7
|
+
* Survives Claude Code context compaction — goals.md is the external memory.
|
|
8
|
+
* No user action required. Silent. Works on Windows/macOS/Linux.
|
|
9
|
+
*/
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { spawnSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
// ── Hook profile gate ───────────────────────────────────────────────────────
|
|
16
|
+
// AZCLAUDE_HOOK_PROFILE=minimal|standard|strict (default: standard)
|
|
17
|
+
// minimal = goals.md tracking only (no observations, no cost tracking)
|
|
18
|
+
// standard = all features (default)
|
|
19
|
+
// strict = all features + extra validation
|
|
20
|
+
const HOOK_PROFILE = process.env.AZCLAUDE_HOOK_PROFILE || 'standard';
|
|
21
|
+
|
|
22
|
+
// Read tool input + response from stdin — Claude Code sends JSON and closes stdin
|
|
23
|
+
let filePath = '';
|
|
24
|
+
let changeSummary = '';
|
|
25
|
+
try {
|
|
26
|
+
const raw = fs.readFileSync(0, 'utf8'); // fd 0 = stdin, cross-platform
|
|
27
|
+
const data = JSON.parse(raw);
|
|
28
|
+
filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
29
|
+
// Extract change summary from old_string/new_string diff hint (Edit tool)
|
|
30
|
+
const oldStr = data.tool_input?.old_string || '';
|
|
31
|
+
const newStr = data.tool_input?.new_string || '';
|
|
32
|
+
if (oldStr && newStr) {
|
|
33
|
+
// Summarize: first non-empty line of new content (what was added)
|
|
34
|
+
const firstNew = newStr.split('\n').find(l => l.trim().length > 0) || '';
|
|
35
|
+
if (firstNew.length > 0 && firstNew.length < 80) {
|
|
36
|
+
changeSummary = ' — ' + firstNew.trim().replace(/^[-*#`]+\s*/, '').slice(0, 60);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch (_) {}
|
|
40
|
+
|
|
41
|
+
// Also accept env var fallback (older Claude Code versions)
|
|
42
|
+
if (!filePath) filePath = process.env.CLAUDE_FILE_PATH || '';
|
|
43
|
+
if (!filePath) process.exit(0);
|
|
44
|
+
|
|
45
|
+
// Guard: skip memory files (prevent write loop), skip non-project paths
|
|
46
|
+
const rel = path.relative(process.cwd(), path.resolve(filePath));
|
|
47
|
+
if (rel.startsWith('..')) process.exit(0); // outside project
|
|
48
|
+
if (/goals\.md$/.test(rel)) process.exit(0); // prevent loop
|
|
49
|
+
if (/node_modules[\\/]|\.git[\\/]/.test(rel)) process.exit(0); // noise
|
|
50
|
+
|
|
51
|
+
const cfg = process.env.AZCLAUDE_CFG || '.claude';
|
|
52
|
+
// Guard: cfg must resolve inside the project root
|
|
53
|
+
if (path.resolve(cfg).indexOf(process.cwd()) !== 0) process.exit(0);
|
|
54
|
+
const goalsPath = path.join(cfg, 'memory', 'goals.md');
|
|
55
|
+
if (!fs.existsSync(goalsPath)) process.exit(0); // not an AZCLAUDE project
|
|
56
|
+
|
|
57
|
+
// Timestamp HH:MM
|
|
58
|
+
const now = new Date();
|
|
59
|
+
const ts = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
60
|
+
|
|
61
|
+
// Git diff stat: "+N/-M" — cached for 5s to avoid repeated git calls on consecutive edits
|
|
62
|
+
let diffStat = '';
|
|
63
|
+
const diffCachePath = path.join(os.tmpdir(), `.azclaude-diff-${process.ppid || process.pid}`);
|
|
64
|
+
let diffCache = {};
|
|
65
|
+
try { diffCache = JSON.parse(fs.readFileSync(diffCachePath, 'utf8')); } catch (_) {}
|
|
66
|
+
const cacheAge = Date.now() - (diffCache._ts || 0);
|
|
67
|
+
const cached = diffCache[rel];
|
|
68
|
+
if (cached && cacheAge < 5000) {
|
|
69
|
+
diffStat = cached;
|
|
70
|
+
} else {
|
|
71
|
+
try {
|
|
72
|
+
const r = spawnSync('git', ['diff', 'HEAD', '--numstat', '--', rel],
|
|
73
|
+
{ encoding: 'utf8', cwd: process.cwd(), timeout: 3000 });
|
|
74
|
+
if (r.status === 0 && r.stdout.trim()) {
|
|
75
|
+
const [added, deleted] = r.stdout.trim().split('\t');
|
|
76
|
+
const a = parseInt(added, 10);
|
|
77
|
+
const d = parseInt(deleted, 10);
|
|
78
|
+
if (!isNaN(a) && !isNaN(d) && (a > 0 || d > 0)) {
|
|
79
|
+
diffStat = ` (+${a}/-${d})`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (_) {}
|
|
83
|
+
// Update cache
|
|
84
|
+
if (cacheAge >= 5000) diffCache = {};
|
|
85
|
+
diffCache[rel] = diffStat;
|
|
86
|
+
diffCache._ts = Date.now();
|
|
87
|
+
try { fs.writeFileSync(diffCachePath, JSON.stringify(diffCache)); } catch (_) {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const entry = `- ${ts} — ${rel}${diffStat}${changeSummary}`;
|
|
91
|
+
|
|
92
|
+
let content = fs.readFileSync(goalsPath, 'utf8');
|
|
93
|
+
|
|
94
|
+
const HEADING = '## In progress';
|
|
95
|
+
|
|
96
|
+
if (!content.includes(HEADING)) {
|
|
97
|
+
// Add section at end
|
|
98
|
+
content = content.trimEnd() + `\n\n${HEADING}\n${entry}\n`;
|
|
99
|
+
} else {
|
|
100
|
+
const lines = content.split('\n');
|
|
101
|
+
const hIdx = lines.findIndex(l => l.trim() === HEADING);
|
|
102
|
+
|
|
103
|
+
// Remove any existing entry for the same file (dedup — keep latest timestamp)
|
|
104
|
+
const cleaned = lines.filter((l, i) => {
|
|
105
|
+
if (i <= hIdx) return true; // keep heading and everything before
|
|
106
|
+
if (!l.startsWith('- ')) return true; // keep non-entry lines
|
|
107
|
+
return !l.includes(rel); // remove old entry for this file
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Insert new entry right after heading
|
|
111
|
+
cleaned.splice(hIdx + 1, 0, entry);
|
|
112
|
+
content = cleaned.join('\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try { fs.writeFileSync(goalsPath, content); } catch (_) {}
|
|
116
|
+
|
|
117
|
+
// ── Reflex observation capture (standard/strict only) ───────────────────────
|
|
118
|
+
// Append tool-use observation to observations.jsonl for pattern detection.
|
|
119
|
+
// Lightweight: one JSON line per tool call. Secret patterns scrubbed.
|
|
120
|
+
if (HOOK_PROFILE !== 'minimal') {
|
|
121
|
+
const reflexDir = path.join(cfg, 'memory', 'reflexes');
|
|
122
|
+
try {
|
|
123
|
+
fs.mkdirSync(reflexDir, { recursive: true });
|
|
124
|
+
const obsPath = path.join(reflexDir, 'observations.jsonl');
|
|
125
|
+
const obsTs = now.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
126
|
+
const tool = 'Edit'; // PostToolUse matcher is Write|Edit
|
|
127
|
+
// Scrub secrets: strip API keys, tokens, passwords from file paths
|
|
128
|
+
const safeRel = rel.replace(/\.(env|key|pem|secret|credential)/gi, '.[REDACTED]');
|
|
129
|
+
const obs = JSON.stringify({
|
|
130
|
+
ts: obsTs, tool, file: safeRel, session: process.ppid || process.pid, event: 'complete'
|
|
131
|
+
});
|
|
132
|
+
fs.appendFileSync(obsPath, obs + '\n');
|
|
133
|
+
// Auto-truncate: keep last 2000 lines max (prevent unbounded growth)
|
|
134
|
+
try {
|
|
135
|
+
const obsContent = fs.readFileSync(obsPath, 'utf8');
|
|
136
|
+
const obsLines = obsContent.split('\n').filter(Boolean);
|
|
137
|
+
if (obsLines.length > 2000) {
|
|
138
|
+
fs.writeFileSync(obsPath, obsLines.slice(-500).join('\n') + '\n');
|
|
139
|
+
}
|
|
140
|
+
} catch (_) {}
|
|
141
|
+
} catch (_) {}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Cost tracking (standard/strict only) ────────────────────────────────────
|
|
145
|
+
// Append estimated cost per tool call to costs.jsonl for budget awareness.
|
|
146
|
+
if (HOOK_PROFILE !== 'minimal') {
|
|
147
|
+
try {
|
|
148
|
+
const costsDir = path.join(cfg, 'memory', 'metrics');
|
|
149
|
+
fs.mkdirSync(costsDir, { recursive: true });
|
|
150
|
+
const costsPath = path.join(costsDir, 'costs.jsonl');
|
|
151
|
+
const costEntry = JSON.stringify({
|
|
152
|
+
ts: now.toISOString().replace(/\.\d{3}Z$/, 'Z'),
|
|
153
|
+
tool: 'Edit',
|
|
154
|
+
file: rel,
|
|
155
|
+
session: process.ppid || process.pid
|
|
156
|
+
});
|
|
157
|
+
fs.appendFileSync(costsPath, costEntry + '\n');
|
|
158
|
+
// Auto-truncate: keep last 1000 entries
|
|
159
|
+
try {
|
|
160
|
+
const costLines = fs.readFileSync(costsPath, 'utf8').split('\n').filter(Boolean);
|
|
161
|
+
if (costLines.length > 1000) {
|
|
162
|
+
fs.writeFileSync(costsPath, costLines.slice(-500).join('\n') + '\n');
|
|
163
|
+
}
|
|
164
|
+
} catch (_) {}
|
|
165
|
+
} catch (_) {}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Checkpoint reminder every 15 edits ──────────────────────────────────────
|
|
169
|
+
const counterPath = path.join(os.tmpdir(), `.azclaude-edit-count-${process.ppid || process.pid}`);
|
|
170
|
+
let editCount = 1;
|
|
171
|
+
try { editCount = parseInt(fs.readFileSync(counterPath, 'utf8'), 10) + 1; } catch (_) {}
|
|
172
|
+
try { fs.writeFileSync(counterPath, String(editCount)); } catch (_) {}
|
|
173
|
+
if (editCount > 0 && editCount % 15 === 0) {
|
|
174
|
+
process.stdout.write(`\n⚠ ${editCount} edits this session — run /snapshot before context compaction loses your reasoning\n`);
|
|
175
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* AZCLAUDE — Stop hook
|
|
5
|
+
* Runs at end of every session.
|
|
6
|
+
* 1. Migrates "In progress" entries → "Done this session"
|
|
7
|
+
* 2. Stamps goals.md with today's date
|
|
8
|
+
* 3. Writes friction stub if /persist was not run
|
|
9
|
+
* Works on: Windows (PowerShell/CMD/Git Bash), macOS, Linux.
|
|
10
|
+
*/
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// ── Hook profile gate ───────────────────────────────────────────────────────
|
|
15
|
+
// AZCLAUDE_HOOK_PROFILE=minimal|standard|strict (default: standard)
|
|
16
|
+
const HOOK_PROFILE = process.env.AZCLAUDE_HOOK_PROFILE || 'standard';
|
|
17
|
+
|
|
18
|
+
const cfg = process.env.AZCLAUDE_CFG || '.claude';
|
|
19
|
+
// Guard: cfg must resolve inside the project root
|
|
20
|
+
if (path.resolve(cfg).indexOf(process.cwd()) !== 0) process.exit(0);
|
|
21
|
+
const goalsPath = path.join(cfg, 'memory', 'goals.md');
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(goalsPath)) process.exit(0);
|
|
24
|
+
|
|
25
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
26
|
+
let content = fs.readFileSync(goalsPath, 'utf8');
|
|
27
|
+
|
|
28
|
+
// ── Migrate "In progress" → "Done this session" ──────────────────────────────
|
|
29
|
+
const IN_PROGRESS = '## In progress';
|
|
30
|
+
const DONE = '## Done this session';
|
|
31
|
+
|
|
32
|
+
if (content.includes(IN_PROGRESS)) {
|
|
33
|
+
const lines = content.split('\n');
|
|
34
|
+
const ipIdx = lines.findIndex(l => l.trim() === IN_PROGRESS);
|
|
35
|
+
const doneIdx = lines.findIndex(l => l.trim() === DONE);
|
|
36
|
+
|
|
37
|
+
// Collect entries under ## In progress (lines starting with "- " until next ##)
|
|
38
|
+
const ipEntries = [];
|
|
39
|
+
for (let i = ipIdx + 1; i < lines.length; i++) {
|
|
40
|
+
if (lines[i].startsWith('## ')) break;
|
|
41
|
+
if (lines[i].startsWith('- ')) ipEntries.push(lines[i]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (ipEntries.length > 0) {
|
|
45
|
+
// Remove ## In progress section entirely
|
|
46
|
+
const withoutIP = [];
|
|
47
|
+
let skip = false;
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (line.trim() === IN_PROGRESS) { skip = true; continue; }
|
|
50
|
+
if (skip && line.startsWith('## ')) skip = false;
|
|
51
|
+
if (!skip) withoutIP.push(line);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add entries to ## Done this session
|
|
55
|
+
const dIdx = withoutIP.findIndex(l => l.trim() === DONE);
|
|
56
|
+
if (dIdx !== -1) {
|
|
57
|
+
withoutIP.splice(dIdx + 1, 0, ...ipEntries);
|
|
58
|
+
} else {
|
|
59
|
+
// No Done section — create one
|
|
60
|
+
withoutIP.push('', DONE, ...ipEntries);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
content = withoutIP.join('\n');
|
|
64
|
+
} else {
|
|
65
|
+
// Empty In progress — just remove the heading
|
|
66
|
+
content = content.replace(`\n${IN_PROGRESS}\n`, '\n');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Stamp today's date ────────────────────────────────────────────────────────
|
|
71
|
+
content = content.replace(/^Updated: .*/m, `Updated: ${today}`);
|
|
72
|
+
try { fs.writeFileSync(goalsPath, content); } catch (_) {}
|
|
73
|
+
|
|
74
|
+
// ── Warn if /persist was not run (only in AZCLAUDE projects with obs dir) ──
|
|
75
|
+
const obsDir = path.join('ops', 'observations');
|
|
76
|
+
if (!fs.existsSync(obsDir)) process.exit(0);
|
|
77
|
+
const todayStamp = today.replace(/-/g, '');
|
|
78
|
+
try {
|
|
79
|
+
const existing = fs.readdirSync(obsDir).filter(f => f.startsWith(todayStamp) && f.endsWith('-friction.md'));
|
|
80
|
+
if (existing.length === 0) {
|
|
81
|
+
process.stdout.write('⚠ session state not persisted — run /persist before closing\n');
|
|
82
|
+
}
|
|
83
|
+
} catch (_) {
|
|
84
|
+
// obs dir doesn't exist yet — that's fine
|
|
85
|
+
}
|