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.
Files changed (108) hide show
  1. package/.claude-plugin/marketplace.json +27 -0
  2. package/.claude-plugin/plugin.json +17 -0
  3. package/LICENSE +21 -0
  4. package/README.md +477 -0
  5. package/bin/cli.js +1027 -0
  6. package/bin/copilot.js +228 -0
  7. package/hooks/README.md +3 -0
  8. package/hooks/hooks.json +40 -0
  9. package/package.json +41 -0
  10. package/templates/CLAUDE.md +51 -0
  11. package/templates/agents/cc-cli-integrator.md +104 -0
  12. package/templates/agents/cc-template-author.md +109 -0
  13. package/templates/agents/cc-test-maintainer.md +101 -0
  14. package/templates/agents/code-reviewer.md +136 -0
  15. package/templates/agents/loop-controller.md +118 -0
  16. package/templates/agents/orchestrator-init.md +196 -0
  17. package/templates/agents/test-writer.md +129 -0
  18. package/templates/capabilities/evolution/cycle2-knowledge.md +87 -0
  19. package/templates/capabilities/evolution/cycle3-topology.md +128 -0
  20. package/templates/capabilities/evolution/detect.md +103 -0
  21. package/templates/capabilities/evolution/evaluate.md +90 -0
  22. package/templates/capabilities/evolution/generate.md +123 -0
  23. package/templates/capabilities/evolution/re-derivation.md +77 -0
  24. package/templates/capabilities/intelligence/debate.md +104 -0
  25. package/templates/capabilities/intelligence/elo.md +122 -0
  26. package/templates/capabilities/intelligence/experiment.md +86 -0
  27. package/templates/capabilities/intelligence/opro.md +84 -0
  28. package/templates/capabilities/intelligence/pipeline.md +149 -0
  29. package/templates/capabilities/level-builders/level1-claudemd.md +52 -0
  30. package/templates/capabilities/level-builders/level2-mcp.md +58 -0
  31. package/templates/capabilities/level-builders/level3-skills.md +276 -0
  32. package/templates/capabilities/level-builders/level4-memory.md +72 -0
  33. package/templates/capabilities/level-builders/level5-agents.md +123 -0
  34. package/templates/capabilities/level-builders/level6-hooks.md +119 -0
  35. package/templates/capabilities/level-builders/level7-extmcp.md +60 -0
  36. package/templates/capabilities/level-builders/level8-orchestrated.md +98 -0
  37. package/templates/capabilities/manifest.md +58 -0
  38. package/templates/capabilities/shared/5-layer-agent.md +206 -0
  39. package/templates/capabilities/shared/completion-rule.md +44 -0
  40. package/templates/capabilities/shared/context-artifacts.md +96 -0
  41. package/templates/capabilities/shared/domain-advisor-generator.md +205 -0
  42. package/templates/capabilities/shared/friction-log.md +43 -0
  43. package/templates/capabilities/shared/multi-cli-paths.md +56 -0
  44. package/templates/capabilities/shared/native-tools.md +199 -0
  45. package/templates/capabilities/shared/plan-tracker.md +69 -0
  46. package/templates/capabilities/shared/pressure-test.md +88 -0
  47. package/templates/capabilities/shared/quality-check.md +83 -0
  48. package/templates/capabilities/shared/reflexes.md +159 -0
  49. package/templates/capabilities/shared/review-reception.md +70 -0
  50. package/templates/capabilities/shared/security.md +174 -0
  51. package/templates/capabilities/shared/semantic-boundary-check.md +140 -0
  52. package/templates/capabilities/shared/session-rhythm.md +42 -0
  53. package/templates/capabilities/shared/tdd.md +54 -0
  54. package/templates/capabilities/shared/vocabulary-transform.md +63 -0
  55. package/templates/commands/add.md +152 -0
  56. package/templates/commands/audit.md +123 -0
  57. package/templates/commands/blueprint.md +115 -0
  58. package/templates/commands/copilot.md +157 -0
  59. package/templates/commands/create.md +156 -0
  60. package/templates/commands/debate.md +75 -0
  61. package/templates/commands/deps.md +112 -0
  62. package/templates/commands/doc.md +100 -0
  63. package/templates/commands/dream.md +120 -0
  64. package/templates/commands/evolve.md +170 -0
  65. package/templates/commands/explain.md +25 -0
  66. package/templates/commands/find.md +100 -0
  67. package/templates/commands/fix.md +122 -0
  68. package/templates/commands/hookify.md +100 -0
  69. package/templates/commands/level-up.md +48 -0
  70. package/templates/commands/loop.md +62 -0
  71. package/templates/commands/migrate.md +119 -0
  72. package/templates/commands/persist.md +73 -0
  73. package/templates/commands/pulse.md +87 -0
  74. package/templates/commands/refactor.md +97 -0
  75. package/templates/commands/reflect.md +107 -0
  76. package/templates/commands/reflexes.md +141 -0
  77. package/templates/commands/setup.md +97 -0
  78. package/templates/commands/ship.md +131 -0
  79. package/templates/commands/snapshot.md +70 -0
  80. package/templates/commands/test.md +86 -0
  81. package/templates/hooks/post-tool-use.js +175 -0
  82. package/templates/hooks/stop.js +85 -0
  83. package/templates/hooks/user-prompt.js +96 -0
  84. package/templates/scripts/env-scan.sh +46 -0
  85. package/templates/scripts/import-graph.sh +88 -0
  86. package/templates/scripts/validate-boundaries.sh +180 -0
  87. package/templates/skills/agent-creator/SKILL.md +91 -0
  88. package/templates/skills/agent-creator/examples/sample-agent.md +80 -0
  89. package/templates/skills/agent-creator/references/agent-engineering-guide.md +596 -0
  90. package/templates/skills/agent-creator/references/quality-checklist.md +42 -0
  91. package/templates/skills/agent-creator/scripts/scaffold.sh +144 -0
  92. package/templates/skills/architecture-advisor/SKILL.md +92 -0
  93. package/templates/skills/architecture-advisor/references/database-decisions.md +61 -0
  94. package/templates/skills/architecture-advisor/references/decision-matrices.md +122 -0
  95. package/templates/skills/architecture-advisor/references/rendering-decisions.md +39 -0
  96. package/templates/skills/architecture-advisor/scripts/detect-scale.sh +67 -0
  97. package/templates/skills/debate/SKILL.md +36 -0
  98. package/templates/skills/debate/references/acemad-protocol.md +72 -0
  99. package/templates/skills/env-scanner/SKILL.md +41 -0
  100. package/templates/skills/security/SKILL.md +44 -0
  101. package/templates/skills/security/references/security-details.md +48 -0
  102. package/templates/skills/session-guard/SKILL.md +33 -0
  103. package/templates/skills/skill-creator/SKILL.md +82 -0
  104. package/templates/skills/skill-creator/examples/sample-skill.md +74 -0
  105. package/templates/skills/skill-creator/references/quality-checklist.md +36 -0
  106. package/templates/skills/skill-creator/references/skill-engineering-guide.md +365 -0
  107. package/templates/skills/skill-creator/scripts/scaffold.sh +75 -0
  108. 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
+ }