claude-prism 1.2.7 → 1.4.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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "claude-prism",
3
+ "version": "1.4.0",
4
+ "description": "EUDEC methodology framework for AI coding agents",
5
+ "author": { "name": "lazysaturday91" },
6
+ "repository": "https://github.com/lazysaturday91/claude-prism",
7
+ "license": "MIT",
8
+ "skills": "./templates/skills/",
9
+ "hooks": "./plugin-hooks.json"
10
+ }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.0] — 2026-03-03
9
+
10
+ ### Added
11
+ - **4 new hook events** — PreCompact, SessionEnd, SubagentStart, TaskCompleted
12
+ - `precompact-handler` — auto-generates `docs/HANDOFF.md` before context compaction
13
+ - `session-end-handler` — saves HANDOFF + appends session summary to `docs/PROJECT-MEMORY.md`
14
+ - `subagent-scope-injector` — injects current plan batch context into subagent via `additionalContext`
15
+ - `task-plan-sync` — auto-updates plan file checkboxes on task completion (fuzzy keyword match)
16
+ - **Native Claude Code plugin** — `.claude-plugin/plugin.json` + `plugin-hooks.json`
17
+ - `claude plugin install claude-prism` for plugin mode
18
+ - `prism init` (CLI mode) remains for CLAUDE.md injection
19
+ - 6 plugin script runners in `scripts/`
20
+ - **HTTP webhook dispatcher** (`lib/webhook.mjs`) — non-blocking fire-and-forget notifications
21
+ - Configure via `.prism/config.json` `webhooks` array
22
+ - Events: `compaction`, `session-end`, `batch-complete`
23
+ - **HANDOFF.md generator** (`lib/handoff.mjs`) — shared logic for auto-generating session handoff documents
24
+ - Plan progress parsing (checkbox counting, batch detection)
25
+ - Git status integration (branch, uncommitted, recent commits)
26
+ - **Checkpoint integration** in EUDEC rules — `Esc+Esc` / `/rewind` references in EXECUTE protocol
27
+ - 30 new tests covering all new handlers, utilities, plugin structure, and installer paths
28
+
29
+ ### Changed
30
+ - `templates/settings.json` — 6 events (was 2)
31
+ - `lib/installer.mjs` — installs 6 runners, 7 rules, 8 libs (was 2/3/6)
32
+ - `lib/config.mjs` — defaults include `webhooks` and 4 new hook configs
33
+ - `lib/messages.mjs` — 4 new message templates
34
+ - `package.json` `files` includes `scripts/`, `.claude-plugin/`, `plugin-hooks.json`
35
+
36
+ ## [1.3.0] — 2026-03-03
37
+
38
+ ### Added
39
+ - **`.prism/` brand directory** — unified home for all Prism project files
40
+ - `.prism/config.json` — hook configuration (committed to git, visible on GitHub)
41
+ - `.prism/.version` — installed version (gitignored via `.prism/.gitignore`)
42
+ - `.prism/plans/` — plan files (committed)
43
+ - **3-stage migration chain** in `prism update`:
44
+ - `.prism.json` → `.claude-prism.json` → `.prism/config.json`
45
+ - `.claude/.prism-version` → `.prism/.version`
46
+ - `docs/plans/` → `.prism/plans/` (moves files, cleans empty dirs)
47
+ - **Backward-compatible fallback** in plan-enforcement hook (`docs/plans/` still checked)
48
+ - Migration tests (4 new: config, version, plans, `.claude-prism.json` detection)
49
+ - `.prism/.gitignore` auto-creation (ignores `.version`)
50
+
51
+ ### Changed
52
+ - Config path: `.claude-prism.json` → `.prism/config.json`
53
+ - Version path: `.claude/.prism-version` → `.prism/.version`
54
+ - Plans path: `docs/plans/` → `.prism/plans/`
55
+ - `.claude-prism.json` removed from `.gitignore` (config is now committed via `.prism/`)
56
+ - All templates, commands, and docs updated to reference new paths
57
+ - `prism doctor` now detects legacy `.claude-prism.json` as a migration target
58
+ - `prism stats` reads plans from `.prism/plans/` with `docs/plans/` fallback
59
+ - `prism uninstall` cleans both new and legacy paths
60
+
61
+ ### Migration
62
+ Existing users: run `prism update` — all files are automatically migrated. No manual steps needed. The `docs/plans/` fallback ensures hooks work even without migration.
63
+
8
64
  ## [1.2.6] — 2026-02-28
9
65
 
10
66
  ### Fixed
@@ -56,7 +112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
56
112
  - session.mjs included in installed lib files
57
113
  - CHANGELOG.md (this file)
58
114
  - GitHub Actions CI workflow (test on push/PR)
59
- - Config schema versioning (`.claude-prism.json` version field)
115
+ - Config schema versioning (config version field)
60
116
  - Verification Fallback Ladder (7-level, from automated tests to manual diff)
61
117
  - Quality Gates between DECOMPOSE→EXECUTE and EXECUTE→CHECKPOINT
62
118
  - Goal Recitation mechanism at batch boundaries
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  **EUDEC methodology framework for AI coding agents.**
20
20
 
21
- Installs the EUDEC methodology — **Essence, Understand, Decompose, Execute, Checkpoint** — directly into your project's Claude Code environment. Includes a session transition protocol (Handoff) that bookends the core cycle. Three lightweight hooks enforce the methodology where it matters most.
21
+ Installs the EUDEC methodology — **Essence, Understand, Decompose, Execute, Checkpoint** — directly into your project's Claude Code environment. Includes a session transition protocol (Handoff) that bookends the core cycle. Seven hooks enforce the methodology and automate session management.
22
22
 
23
23
  ## The Problem
24
24
 
@@ -79,23 +79,37 @@ Injected into `CLAUDE.md`, EUDEC is a behavioral framework that corrects how AI
79
79
 
80
80
  **Quality gates** between phases prevent executing on broken baselines.
81
81
 
82
- **New in v1.2.5:**
82
+ **New in v1.4.0:**
83
+ - **Native Claude Code plugin** — `claude plugin install claude-prism` for zero-config setup
84
+ - **4 new hook events** — PreCompact (auto-HANDOFF), SessionEnd (session protection), SubagentStart (scope injection), TaskCompleted (plan auto-update)
85
+ - **HTTP webhooks** — fire-and-forget notifications on compaction, session-end, batch-complete
86
+ - **Checkpoint integration** — `Esc+Esc` / `/rewind` references complement Git-as-Memory
87
+
88
+ **v1.3.0:**
89
+ - `.prism/` brand directory — config, version, and plans live under `.prism/`
90
+ - Automatic 3-stage migration from legacy paths
91
+
92
+ **v1.2.5:**
83
93
  - **Analysis-only branch**: When no code change is needed, UNDERSTAND reports findings without entering DECOMPOSE/EXECUTE/CHECKPOINT
84
94
  - **Git-as-Memory**: Commits after each batch as rollback points; `git diff` summaries maintain context in long sessions
85
95
  - **Verification scoping**: Build check output filtered to changed files only — pre-existing errors are ignored
86
96
  - **Agent failure recovery**: 3-step protocol when delegated agents produce incomplete results
87
97
 
88
- ### 2. Three Focused Hooks
98
+ ### 2. Seven Focused Hooks
89
99
 
90
- Hooks enforce the methodology at critical points. All three are deterministic (no heuristics, no state accumulation issues):
100
+ Hooks enforce the methodology at critical points:
91
101
 
92
- | Hook | What It Does | Trigger |
102
+ | Hook | Event | What It Does |
93
103
  |---|---|---|
94
- | **commit-guard** | Blocks commits when tests failed or haven't run | `git commit` |
95
- | **test-tracker** | Records test pass/fail results | Test commands (20 patterns) |
96
- | **plan-enforcement** | Warns when editing 6+ files without a plan | `Edit` / `Write` |
104
+ | **commit-guard** | PreToolUse | Blocks commits when tests failed or haven't run |
105
+ | **plan-enforcement** | PreToolUse | Warns when editing 6+ files without a plan |
106
+ | **test-tracker** | PostToolUse | Records test pass/fail results |
107
+ | **precompact-handler** | PreCompact | Auto-generates `docs/HANDOFF.md` before compaction |
108
+ | **session-end-handler** | SessionEnd | Saves HANDOFF + appends to `docs/PROJECT-MEMORY.md` |
109
+ | **scope-injector** | SubagentStart | Injects current plan batch context into subagent |
110
+ | **plan-sync** | TaskCompleted | Auto-updates plan file checkboxes on task completion |
97
111
 
98
- **Why only three?** Previous versions had 6 hooks (scope-guard, debug-loop, alignment, turn-reporter). They produced false positives that undermined the methodology they were supposed to enforce. These three survive because they're deterministic: file count + plan existence, test result parsing, commit detection. No ambiguity.
112
+ The original three hooks (commit-guard, test-tracker, plan-enforcement) are deterministic enforcers. The four new hooks (v1.4.0) are **session lifecycle automations** they don't block or warn, they auto-save context and sync plan state.
99
113
 
100
114
  ### 3. Slash Commands
101
115
 
@@ -150,6 +164,16 @@ prism analytics --detail # Include per-session breakdown
150
164
 
151
165
  ## Installation
152
166
 
167
+ ### Option A: Plugin Mode (recommended)
168
+
169
+ ```bash
170
+ claude plugin install claude-prism
171
+ ```
172
+
173
+ Plugin mode auto-registers hooks and skills. Run `prism init` additionally if you want CLAUDE.md methodology injection (plugins cannot modify CLAUDE.md).
174
+
175
+ ### Option B: CLI Mode
176
+
153
177
  ```bash
154
178
  npx claude-prism init # Install with hooks (prompts for HUD)
155
179
  npx claude-prism init --hud # Install + auto-enable HUD
@@ -162,15 +186,19 @@ npx claude-prism init --dry-run # Preview what would be installed
162
186
 
163
187
  ```
164
188
  your-project/
165
- ├── CLAUDE.md # EUDEC methodology injected
166
- ├── .claude-prism.json # Hook configuration
189
+ ├── CLAUDE.md # EUDEC methodology injected (CLI mode only)
190
+ ├── .prism/
191
+ │ ├── config.json # Hook configuration (committed)
192
+ │ ├── .version # Installed version (gitignored)
193
+ │ ├── .gitignore # Ignores .version
194
+ │ └── plans/ # Plan files (created during work)
167
195
  ├── .claude/
168
196
  │ ├── commands/claude-prism/ # 9 slash commands
169
- │ ├── hooks/ # pre-tool.mjs, post-tool.mjs
170
- ├── rules/ # commit-guard, test-tracker, plan-enforcement
171
- │ ├── lib/ # Shared dependencies
172
- └── settings.json # Hook registration
173
- └── docs/plans/ # Plan files (created during work)
197
+ │ ├── hooks/ # 6 runners (pre-tool, post-tool, precompact,
198
+ # session-end, subagent-start, task-completed)
199
+ │ ├── rules/ # 7 rule modules
200
+ ├── lib/ # 8 shared dependencies
201
+ └── settings.json # Hook registration (6 events)
174
202
 
175
203
  ~/.claude/ # (global install / HUD)
176
204
  ├── commands/claude-prism/ # 9 slash commands (--global)
@@ -180,7 +208,7 @@ your-project/
180
208
 
181
209
  ## Configuration
182
210
 
183
- Edit `.claude-prism.json`:
211
+ Edit `.prism/config.json`:
184
212
 
185
213
  ```json
186
214
  {
@@ -188,8 +216,19 @@ Edit `.claude-prism.json`:
188
216
  "hooks": {
189
217
  "commit-guard": { "enabled": true, "maxTestAge": 300 },
190
218
  "test-tracker": { "enabled": true },
191
- "plan-enforcement": { "enabled": true, "warnAt": 6 }
192
- }
219
+ "plan-enforcement": { "enabled": true, "warnAt": 6 },
220
+ "precompact-handler": { "enabled": true },
221
+ "session-end-handler": { "enabled": true },
222
+ "subagent-scope-injector": { "enabled": true },
223
+ "task-plan-sync": { "enabled": true, "matchThreshold": 0.3 }
224
+ },
225
+ "webhooks": [
226
+ {
227
+ "url": "https://your-server.com/webhook",
228
+ "events": ["compaction", "session-end", "batch-complete"],
229
+ "headers": { "Authorization": "Bearer token" }
230
+ }
231
+ ]
193
232
  }
194
233
  ```
195
234
 
@@ -198,6 +237,8 @@ Edit `.claude-prism.json`:
198
237
  | `version` | 1 | Config schema version (for future migrations) |
199
238
  | `commit-guard.maxTestAge` | 300 | Seconds before test run is considered stale |
200
239
  | `plan-enforcement.warnAt` | 6 | Unique source file count that triggers plan warning |
240
+ | `task-plan-sync.matchThreshold` | 0.3 | Keyword overlap ratio for fuzzy task matching |
241
+ | `webhooks` | `[]` | HTTP endpoints for event notifications |
201
242
 
202
243
  ## CLI Commands
203
244
 
@@ -235,6 +276,25 @@ prism hud disable # Deactivate HUD statusline
235
276
 
236
277
  Prism auto-detects [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode). When present, `prism stats` and `prism doctor` show OMC version. No configuration needed.
237
278
 
279
+ ## Upgrading
280
+
281
+ ### To v1.4.0
282
+
283
+ ```bash
284
+ npx claude-prism update
285
+ ```
286
+
287
+ v1.4.0 adds 4 new hook runners + 4 rule files + 2 new libs. `prism update` installs them automatically. Existing hooks and configuration are preserved.
288
+
289
+ **Optional**: Install as a native plugin for auto-registration:
290
+ ```bash
291
+ claude plugin install claude-prism
292
+ ```
293
+
294
+ ### To v1.3.0
295
+
296
+ v1.3.0 moves project files to `.prism/` directory. Migration is automatic via `prism update`.
297
+
238
298
  ## Design Philosophy
239
299
 
240
300
  EUDEC is the product. Everything else serves it.
package/bin/cli.mjs CHANGED
@@ -340,7 +340,7 @@ Options:
340
340
  if (/EACCES|permission/i.test(msg)) {
341
341
  process.stderr.write('💡 Check directory permissions\n');
342
342
  } else if (/JSON|parse/i.test(msg)) {
343
- process.stderr.write('💡 Config file may be corrupted. Try `prism reset` or delete .claude-prism.json\n');
343
+ process.stderr.write('💡 Config file may be corrupted. Try `prism reset` or delete .prism/config.json\n');
344
344
  } else if (/ENOENT.*package\.json/i.test(msg)) {
345
345
  process.stderr.write('💡 Not in a project directory?\n');
346
346
  }
@@ -39,13 +39,18 @@ export const planEnforcement = {
39
39
  const threshold = config.warnAt || 6;
40
40
  if (files.length < threshold) return { type: 'pass' };
41
41
 
42
- // Check for plan file in docs/plans/
42
+ // Check for plan file in .prism/plans/ (with docs/plans/ fallback)
43
43
  const projectRoot = config.projectRoot || process.cwd();
44
- const plansDir = join(projectRoot, 'docs', 'plans');
44
+ const plansDir = join(projectRoot, '.prism', 'plans');
45
45
  if (existsSync(plansDir)) {
46
46
  const plans = readdirSync(plansDir).filter(f => f.endsWith('.md'));
47
47
  if (plans.length > 0) return { type: 'pass' };
48
48
  }
49
+ const legacyPlansDir = join(projectRoot, 'docs', 'plans');
50
+ if (existsSync(legacyPlansDir)) {
51
+ const plans = readdirSync(legacyPlansDir).filter(f => f.endsWith('.md'));
52
+ if (plans.length > 0) return { type: 'pass' };
53
+ }
49
54
 
50
55
  return {
51
56
  type: 'warn',
@@ -0,0 +1,44 @@
1
+ /**
2
+ * claude-prism — PreCompact Handler
3
+ * Auto-generates HANDOFF.md before context compaction
4
+ */
5
+
6
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { generateHandoff } from '../lib/handoff.mjs';
9
+ import { dispatchWebhook } from '../lib/webhook.mjs';
10
+ import { getMessage } from '../lib/messages.mjs';
11
+
12
+ export const precompactHandler = {
13
+ name: 'precompact-handler',
14
+
15
+ /**
16
+ * @param {Object} input - { session_id, trigger, custom_instructions }
17
+ * @param {Object} config - Prism config
18
+ * @returns {Object} Hook output with additionalContext
19
+ */
20
+ evaluate(input, config) {
21
+ const projectRoot = config.projectRoot || process.cwd();
22
+
23
+ // Generate HANDOFF.md
24
+ const handoffContent = generateHandoff(projectRoot);
25
+ const docsDir = join(projectRoot, 'docs');
26
+ if (!existsSync(docsDir)) {
27
+ mkdirSync(docsDir, { recursive: true });
28
+ }
29
+ writeFileSync(join(docsDir, 'HANDOFF.md'), handoffContent);
30
+
31
+ // Webhook dispatch (fire-and-forget)
32
+ dispatchWebhook(config, 'compaction', {
33
+ session_id: input.session_id,
34
+ trigger: input.trigger || 'auto',
35
+ }).catch(() => {});
36
+
37
+ return {
38
+ hookSpecificOutput: {
39
+ hookEventName: 'PreCompact',
40
+ additionalContext: getMessage('en', 'precompact-handler.info.saved')
41
+ }
42
+ };
43
+ }
44
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * claude-prism — SessionEnd Handler
3
+ * Saves HANDOFF.md and appends session summary to PROJECT-MEMORY.md
4
+ */
5
+
6
+ import { writeFileSync, appendFileSync, mkdirSync, existsSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { generateHandoff, getActivePlanInfo } from '../lib/handoff.mjs';
9
+ import { dispatchWebhook } from '../lib/webhook.mjs';
10
+
11
+ export const sessionEndHandler = {
12
+ name: 'session-end-handler',
13
+
14
+ /**
15
+ * @param {Object} input - { session_id, reason }
16
+ * @param {Object} config - Prism config
17
+ * @returns {Object} Empty output (SessionEnd has no visible output)
18
+ */
19
+ evaluate(input, config) {
20
+ const projectRoot = config.projectRoot || process.cwd();
21
+
22
+ // 1. Generate HANDOFF.md
23
+ const handoffContent = generateHandoff(projectRoot);
24
+ const docsDir = join(projectRoot, 'docs');
25
+ if (!existsSync(docsDir)) {
26
+ mkdirSync(docsDir, { recursive: true });
27
+ }
28
+ writeFileSync(join(docsDir, 'HANDOFF.md'), handoffContent);
29
+
30
+ // 2. Append session summary to PROJECT-MEMORY.md
31
+ const planInfo = getActivePlanInfo(projectRoot);
32
+ const memoryPath = join(docsDir, 'PROJECT-MEMORY.md');
33
+ const date = new Date().toISOString().slice(0, 10);
34
+ let entry = `\n## Session ${date}\n`;
35
+ entry += `- Reason: ${input.reason || 'unknown'}\n`;
36
+ if (planInfo) {
37
+ const pct = planInfo.total > 0 ? Math.round((planInfo.done / planInfo.total) * 100) : 0;
38
+ entry += `- Plan: \`${planInfo.planName}\` — ${planInfo.done}/${planInfo.total} (${pct}%)\n`;
39
+ }
40
+ entry += `- Session ID: ${input.session_id || 'unknown'}\n`;
41
+
42
+ if (existsSync(memoryPath)) {
43
+ appendFileSync(memoryPath, entry);
44
+ } else {
45
+ writeFileSync(memoryPath, `# PROJECT-MEMORY\n\nCumulative knowledge across sessions.\n${entry}`);
46
+ }
47
+
48
+ // 3. Webhook dispatch
49
+ dispatchWebhook(config, 'session-end', {
50
+ session_id: input.session_id,
51
+ reason: input.reason,
52
+ plan_progress: planInfo ? `${planInfo.done}/${planInfo.total}` : null,
53
+ }).catch(() => {});
54
+
55
+ return {};
56
+ }
57
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * claude-prism — SubagentStart Scope Injector
3
+ * Injects current plan batch context into subagent
4
+ */
5
+
6
+ import { getMessage } from '../lib/messages.mjs';
7
+ import { getActivePlanInfo } from '../lib/handoff.mjs';
8
+
9
+ export const scopeInjector = {
10
+ name: 'subagent-scope-injector',
11
+
12
+ /**
13
+ * @param {Object} input - { session_id, agent_id, agent_type }
14
+ * @param {Object} config - Prism config
15
+ * @returns {Object|null} Hook output with additionalContext or null
16
+ */
17
+ evaluate(input, config) {
18
+ const projectRoot = config.projectRoot || process.cwd();
19
+ const planInfo = getActivePlanInfo(projectRoot);
20
+
21
+ if (!planInfo || planInfo.total === 0) {
22
+ return null;
23
+ }
24
+
25
+ const pct = Math.round((planInfo.done / planInfo.total) * 100);
26
+ const parts = [
27
+ `Plan: ${planInfo.planName} (${planInfo.done}/${planInfo.total}, ${pct}%)`
28
+ ];
29
+
30
+ if (planInfo.nextBatch) {
31
+ parts.push(`Current batch: ${planInfo.nextBatch}`);
32
+ }
33
+
34
+ if (planInfo.currentBatchFiles && planInfo.currentBatchFiles.length > 0) {
35
+ const uniqueFiles = [...new Set(planInfo.currentBatchFiles)].slice(0, 10);
36
+ parts.push(`Files in scope: ${uniqueFiles.join(', ')}`);
37
+ }
38
+
39
+ if (planInfo.nextTasks.length > 0) {
40
+ parts.push('Tasks:');
41
+ for (const task of planInfo.nextTasks.slice(0, 3)) {
42
+ parts.push(` - ${task}`);
43
+ }
44
+ }
45
+
46
+ return {
47
+ hookSpecificOutput: {
48
+ hookEventName: 'SubagentStart',
49
+ additionalContext: getMessage('en', 'subagent-scope-injector.info.scope') + '\n' + parts.join('\n')
50
+ }
51
+ };
52
+ }
53
+ };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * claude-prism — TaskCompleted Plan Sync
3
+ * Auto-updates plan file checkboxes when tasks complete
4
+ */
5
+
6
+ import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { dispatchWebhook } from '../lib/webhook.mjs';
9
+ import { getMessage } from '../lib/messages.mjs';
10
+
11
+ export const planSync = {
12
+ name: 'task-plan-sync',
13
+
14
+ /**
15
+ * @param {Object} input - { task_id, task_subject, task_description, team_name? }
16
+ * @param {Object} config - Prism config
17
+ * @returns {Object|null} Hook output with additionalContext
18
+ */
19
+ evaluate(input, config) {
20
+ const projectRoot = config.projectRoot || process.cwd();
21
+ const plansDir = join(projectRoot, '.prism', 'plans');
22
+
23
+ if (!existsSync(plansDir)) return null;
24
+
25
+ const planFiles = readdirSync(plansDir)
26
+ .filter(f => f.endsWith('.md'))
27
+ .sort()
28
+ .reverse();
29
+
30
+ if (planFiles.length === 0) return null;
31
+
32
+ const planPath = join(plansDir, planFiles[0]);
33
+ const content = readFileSync(planPath, 'utf8');
34
+ const subject = input.task_subject || '';
35
+
36
+ if (!subject) return null;
37
+
38
+ // Find matching unchecked task by keyword overlap
39
+ const lines = content.split('\n');
40
+ let matched = false;
41
+ let matchedTask = '';
42
+ let batchDone = 0;
43
+ let batchTotal = 0;
44
+ let currentBatchName = '';
45
+ let matchedBatchName = '';
46
+
47
+ for (let i = 0; i < lines.length; i++) {
48
+ // Track batch headers
49
+ const batchMatch = lines[i].match(/^#{1,3}\s+(Batch\s+\d+[:\s]*.+)/i);
50
+ if (batchMatch) {
51
+ // Check if previous batch is now complete
52
+ if (matched && batchTotal > 0) {
53
+ matchedBatchName = currentBatchName;
54
+ }
55
+ currentBatchName = batchMatch[1];
56
+ batchDone = 0;
57
+ batchTotal = 0;
58
+ }
59
+
60
+ const checkboxMatch = lines[i].match(/^([-*]\s+)\[ \]\s+(.+)/);
61
+ if (checkboxMatch) {
62
+ batchTotal++;
63
+ const taskText = checkboxMatch[2];
64
+
65
+ if (!matched && matchTask(subject, taskText, config.matchThreshold || 0.3)) {
66
+ lines[i] = lines[i].replace('[ ]', '[x]');
67
+ matched = true;
68
+ matchedTask = taskText;
69
+ matchedBatchName = currentBatchName;
70
+ batchDone++; // This one is now done
71
+ }
72
+ } else {
73
+ const doneMatch = lines[i].match(/^[-*]\s+\[x\]/);
74
+ if (doneMatch) {
75
+ batchTotal++;
76
+ batchDone++;
77
+ }
78
+ }
79
+ }
80
+
81
+ if (!matched) return null;
82
+
83
+ // Write updated plan
84
+ writeFileSync(planPath, lines.join('\n'));
85
+
86
+ // Count overall progress
87
+ let total = 0, done = 0;
88
+ for (const line of lines) {
89
+ if (line.match(/^[-*]\s+\[x\]/)) { total++; done++; }
90
+ else if (line.match(/^[-*]\s+\[ \]/)) { total++; }
91
+ }
92
+
93
+ // Check if batch is complete
94
+ const batchComplete = batchTotal > 0 && batchDone >= batchTotal;
95
+ if (batchComplete) {
96
+ dispatchWebhook(config, 'batch-complete', {
97
+ batch: matchedBatchName,
98
+ plan: planFiles[0],
99
+ progress: `${done}/${total}`,
100
+ }).catch(() => {});
101
+ }
102
+
103
+ const pct = total > 0 ? Math.round((done / total) * 100) : 0;
104
+ return {
105
+ hookSpecificOutput: {
106
+ hookEventName: 'TaskCompleted',
107
+ additionalContext: getMessage('en', 'task-plan-sync.info.updated', {
108
+ task: matchedTask.slice(0, 60),
109
+ done: String(done),
110
+ total: String(total),
111
+ pct: String(pct),
112
+ })
113
+ }
114
+ };
115
+ }
116
+ };
117
+
118
+ /**
119
+ * Match task subject against plan task text using keyword overlap
120
+ * @param {string} subject - Task subject from TaskCompleted event
121
+ * @param {string} taskText - Task text from plan file
122
+ * @param {number} threshold - Minimum overlap ratio (0-1)
123
+ * @returns {boolean}
124
+ */
125
+ function matchTask(subject, taskText, threshold) {
126
+ const normalize = (s) => s.toLowerCase()
127
+ .replace(/[^a-z0-9\s]/g, ' ')
128
+ .split(/\s+/)
129
+ .filter(w => w.length > 2);
130
+
131
+ const subjectWords = new Set(normalize(subject));
132
+ const taskWords = normalize(taskText);
133
+
134
+ if (subjectWords.size === 0 || taskWords.length === 0) return false;
135
+
136
+ let overlap = 0;
137
+ for (const word of taskWords) {
138
+ if (subjectWords.has(word)) overlap++;
139
+ }
140
+
141
+ const ratio = overlap / Math.max(subjectWords.size, taskWords.length);
142
+ return ratio >= threshold;
143
+ }
package/lib/config.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * claude-prism — Configuration Loader
3
- * Reads .claude-prism.json from project root, with defaults
3
+ * Reads .prism/config.json from project root, with defaults
4
4
  */
5
5
 
6
6
  import { readFileSync, existsSync } from 'fs';
@@ -10,15 +10,20 @@ const DEFAULTS = {
10
10
  sourceExtensions: ['ts', 'tsx', 'js', 'jsx', 'py', 'go', 'rs', 'java', 'c', 'cpp', 'h', 'svelte', 'vue', 'rb', 'kt', 'swift', 'php', 'cs', 'scala', 'ex', 'clj', 'zig', 'lua', 'dart'],
11
11
  testPatterns: ['test', 'spec', '_test'],
12
12
  customRules: [],
13
+ webhooks: [],
13
14
  hooks: {
14
15
  'commit-guard': { enabled: true, maxTestAge: 300 },
15
16
  'test-tracker': { enabled: true },
16
- 'plan-enforcement': { enabled: true, warnAt: 6 }
17
+ 'plan-enforcement': { enabled: true, warnAt: 6 },
18
+ 'precompact-handler': { enabled: true },
19
+ 'session-end-handler': { enabled: true },
20
+ 'subagent-scope-injector': { enabled: true },
21
+ 'task-plan-sync': { enabled: true, matchThreshold: 0.3 }
17
22
  }
18
23
  };
19
24
 
20
25
  export function loadConfig(projectRoot) {
21
- const configPath = join(projectRoot, '.claude-prism.json');
26
+ const configPath = join(projectRoot, '.prism', 'config.json');
22
27
 
23
28
  if (!existsSync(configPath)) {
24
29
  return JSON.parse(JSON.stringify(DEFAULTS));