azclaude-copilot 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <p align="center"><strong>AI coding environment that learns, evolves, and builds autonomously.</strong></p>
4
4
  <p align="center">
5
5
  <a href="https://www.npmjs.com/package/azclaude-copilot"><img src="https://img.shields.io/npm/v/azclaude-copilot.svg" alt="npm version"></a>
6
- <a href="https://github.com/haytamAroui/AZ-CLAUDE-COPILOT/actions"><img src="https://img.shields.io/badge/tests-1048%20passing-brightgreen" alt="tests"></a>
6
+ <a href="https://github.com/haytamAroui/AZ-CLAUDE-COPILOT/actions/workflows/tests.yml"><img src="https://github.com/haytamAroui/AZ-CLAUDE-COPILOT/actions/workflows/tests.yml/badge.svg" alt="tests"></a>
7
7
  <a href="https://github.com/haytamAroui/AZ-CLAUDE-COPILOT/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
8
8
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D16-brightgreen" alt="node version"></a>
9
9
  </p>
package/bin/cli.js CHANGED
@@ -142,7 +142,7 @@ function buildHookEntries(scriptsDir) {
142
142
  return {
143
143
  UserPromptSubmit: [{ matcher: '', hooks: [{ type: 'command', command: `"${nodeExe}" "${userPromptScript}"` }] }],
144
144
  Stop: [{ matcher: '', hooks: [{ type: 'command', command: `"${nodeExe}" "${stopScript}"` }] }],
145
- PostToolUse: [{ matcher: 'Write|Edit', hooks: [{ type: 'command', command: `"${nodeExe}" "${postToolUseScript}"` }] }],
145
+ PostToolUse: [{ matcher: 'Write|Edit|Read|Bash|Grep', hooks: [{ type: 'command', command: `"${nodeExe}" "${postToolUseScript}"` }] }],
146
146
  };
147
147
  }
148
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azclaude-copilot",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "AI coding environment — 26 commands, 8 skills, 7 agents, memory, reflexes, evolution. Install once, works on any stack.",
5
5
  "bin": {
6
6
  "azclaude": "./bin/cli.js",
@@ -22,10 +22,12 @@ const HOOK_PROFILE = process.env.AZCLAUDE_HOOK_PROFILE || 'standard';
22
22
  // Read tool input + response from stdin — Claude Code sends JSON and closes stdin
23
23
  let filePath = '';
24
24
  let changeSummary = '';
25
+ let toolName = '';
25
26
  try {
26
27
  const raw = fs.readFileSync(0, 'utf8'); // fd 0 = stdin, cross-platform
27
28
  const data = JSON.parse(raw);
28
- filePath = data.tool_input?.file_path || data.tool_input?.path || '';
29
+ toolName = data.tool_name || '';
30
+ filePath = data.tool_input?.file_path || data.tool_input?.path || data.tool_input?.command || '';
29
31
  // Extract change summary from old_string/new_string diff hint (Edit tool)
30
32
  const oldStr = data.tool_input?.old_string || '';
31
33
  const newStr = data.tool_input?.new_string || '';
@@ -40,13 +42,6 @@ try {
40
42
 
41
43
  // Also accept env var fallback (older Claude Code versions)
42
44
  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
45
 
51
46
  const cfg = process.env.AZCLAUDE_CFG || '.claude';
52
47
  // Guard: cfg must resolve inside the project root
@@ -54,10 +49,24 @@ if (path.resolve(cfg).indexOf(process.cwd()) !== 0) process.exit(0);
54
49
  const goalsPath = path.join(cfg, 'memory', 'goals.md');
55
50
  if (!fs.existsSync(goalsPath)) process.exit(0); // not an AZCLAUDE project
56
51
 
52
+ // For non-file tools (Bash, Grep without file_path), still capture observations but skip goals tracking
53
+ const isFileTool = toolName === 'Write' || toolName === 'Edit' || (!toolName && filePath);
54
+ const rel = filePath ? path.relative(process.cwd(), path.resolve(filePath)) : toolName || 'unknown';
55
+
56
+ if (isFileTool) {
57
+ if (!filePath) process.exit(0);
58
+ if (rel.startsWith('..')) process.exit(0); // outside project
59
+ if (/goals\.md$/.test(rel)) process.exit(0); // prevent loop
60
+ if (/node_modules[\\/]|\.git[\\/]/.test(rel)) process.exit(0); // noise
61
+ }
62
+
57
63
  // Timestamp HH:MM
58
64
  const now = new Date();
59
65
  const ts = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
60
66
 
67
+ // ── Goals.md tracking (Write/Edit only — file modifications) ────────────────
68
+ if (isFileTool) {
69
+
61
70
  // Git diff stat: "+N/-M" — cached for 5s to avoid repeated git calls on consecutive edits
62
71
  let diffStat = '';
63
72
  const diffCachePath = path.join(os.tmpdir(), `.azclaude-diff-${process.ppid || process.pid}`);
@@ -114,20 +123,32 @@ if (!content.includes(HEADING)) {
114
123
 
115
124
  try { fs.writeFileSync(goalsPath, content); } catch (_) {}
116
125
 
126
+ } // end isFileTool goals tracking
127
+
117
128
  // ── Reflex observation capture (standard/strict only) ───────────────────────
118
129
  // Append tool-use observation to observations.jsonl for pattern detection.
119
- // Lightweight: one JSON line per tool call. Secret patterns scrubbed.
130
+ // Tracks actual tool name + tool sequences (last 3 tools) for pattern detection.
120
131
  if (HOOK_PROFILE !== 'minimal') {
121
132
  const reflexDir = path.join(cfg, 'memory', 'reflexes');
122
133
  try {
123
134
  fs.mkdirSync(reflexDir, { recursive: true });
124
135
  const obsPath = path.join(reflexDir, 'observations.jsonl');
125
136
  const obsTs = now.toISOString().replace(/\.\d{3}Z$/, 'Z');
126
- const tool = 'Edit'; // PostToolUse matcher is Write|Edit
137
+ const tool = toolName || 'Edit';
127
138
  // Scrub secrets: strip API keys, tokens, passwords from file paths
128
139
  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'
140
+
141
+ // Track tool sequence: last 3 tools for pattern detection (Read→Edit→Bash)
142
+ const seqPath = path.join(os.tmpdir(), `.azclaude-seq-${process.ppid || process.pid}`);
143
+ let seq = [];
144
+ try { seq = JSON.parse(fs.readFileSync(seqPath, 'utf8')); } catch (_) {}
145
+ seq.push(tool);
146
+ if (seq.length > 3) seq = seq.slice(-3);
147
+ try { fs.writeFileSync(seqPath, JSON.stringify(seq)); } catch (_) {}
148
+
149
+ const obs = JSON.stringify({
150
+ ts: obsTs, tool, file: safeRel, session: process.ppid || process.pid,
151
+ event: 'complete', seq: seq.join('→')
131
152
  });
132
153
  fs.appendFileSync(obsPath, obs + '\n');
133
154
  // Auto-truncate: keep last 2000 lines max (prevent unbounded growth)
@@ -150,7 +171,7 @@ if (HOOK_PROFILE !== 'minimal') {
150
171
  const costsPath = path.join(costsDir, 'costs.jsonl');
151
172
  const costEntry = JSON.stringify({
152
173
  ts: now.toISOString().replace(/\.\d{3}Z$/, 'Z'),
153
- tool: 'Edit',
174
+ tool: toolName || 'Edit',
154
175
  file: rel,
155
176
  session: process.ppid || process.pid
156
177
  });