claude-prism 1.2.7 → 1.5.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.5.0",
4
+ "description": "AI agent harness implementing the EUDEC methodology",
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,73 @@ 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.5.0] — 2026-03-04
9
+
10
+ ### Changed
11
+ - **Adaptive Weight routing** — EUDEC path auto-scales by task size (Lightweight/Standard/Full)
12
+ - **Bugfix Fast Path** — 4-step lightweight path: symptom → cause → fix → verify (skips formal EUDEC)
13
+ - **Verification Fallback Ladder** simplified from 6 levels to 3 (Tests/Build/Diff)
14
+ - **Adaptive checkpoints** — frequency proportional to task weight (no pause for lightweight)
15
+ - **Rationalization Defense** compressed from 17 to 4 highest-impact items
16
+ - Section numbering corrected (4-8 moved after 4-7)
17
+ - Package description and keywords updated with "agent harness" positioning
18
+
19
+ ## [1.4.0] — 2026-03-03
20
+
21
+ ### Added
22
+ - **4 new hook events** — PreCompact, SessionEnd, SubagentStart, TaskCompleted
23
+ - `precompact-handler` — auto-generates `docs/HANDOFF.md` before context compaction
24
+ - `session-end-handler` — saves HANDOFF + appends session summary to `docs/PROJECT-MEMORY.md`
25
+ - `subagent-scope-injector` — injects current plan batch context into subagent via `additionalContext`
26
+ - `task-plan-sync` — auto-updates plan file checkboxes on task completion (fuzzy keyword match)
27
+ - **Native Claude Code plugin** — `.claude-plugin/plugin.json` + `plugin-hooks.json`
28
+ - `claude plugin install claude-prism` for plugin mode
29
+ - `prism init` (CLI mode) remains for CLAUDE.md injection
30
+ - 6 plugin script runners in `scripts/`
31
+ - **HTTP webhook dispatcher** (`lib/webhook.mjs`) — non-blocking fire-and-forget notifications
32
+ - Configure via `.prism/config.json` `webhooks` array
33
+ - Events: `compaction`, `session-end`, `batch-complete`
34
+ - **HANDOFF.md generator** (`lib/handoff.mjs`) — shared logic for auto-generating session handoff documents
35
+ - Plan progress parsing (checkbox counting, batch detection)
36
+ - Git status integration (branch, uncommitted, recent commits)
37
+ - **Checkpoint integration** in EUDEC rules — `Esc+Esc` / `/rewind` references in EXECUTE protocol
38
+ - 30 new tests covering all new handlers, utilities, plugin structure, and installer paths
39
+
40
+ ### Changed
41
+ - `templates/settings.json` — 6 events (was 2)
42
+ - `lib/installer.mjs` — installs 6 runners, 7 rules, 8 libs (was 2/3/6)
43
+ - `lib/config.mjs` — defaults include `webhooks` and 4 new hook configs
44
+ - `lib/messages.mjs` — 4 new message templates
45
+ - `package.json` `files` includes `scripts/`, `.claude-plugin/`, `plugin-hooks.json`
46
+
47
+ ## [1.3.0] — 2026-03-03
48
+
49
+ ### Added
50
+ - **`.prism/` brand directory** — unified home for all Prism project files
51
+ - `.prism/config.json` — hook configuration (committed to git, visible on GitHub)
52
+ - `.prism/.version` — installed version (gitignored via `.prism/.gitignore`)
53
+ - `.prism/plans/` — plan files (committed)
54
+ - **3-stage migration chain** in `prism update`:
55
+ - `.prism.json` → `.claude-prism.json` → `.prism/config.json`
56
+ - `.claude/.prism-version` → `.prism/.version`
57
+ - `docs/plans/` → `.prism/plans/` (moves files, cleans empty dirs)
58
+ - **Backward-compatible fallback** in plan-enforcement hook (`docs/plans/` still checked)
59
+ - Migration tests (4 new: config, version, plans, `.claude-prism.json` detection)
60
+ - `.prism/.gitignore` auto-creation (ignores `.version`)
61
+
62
+ ### Changed
63
+ - Config path: `.claude-prism.json` → `.prism/config.json`
64
+ - Version path: `.claude/.prism-version` → `.prism/.version`
65
+ - Plans path: `docs/plans/` → `.prism/plans/`
66
+ - `.claude-prism.json` removed from `.gitignore` (config is now committed via `.prism/`)
67
+ - All templates, commands, and docs updated to reference new paths
68
+ - `prism doctor` now detects legacy `.claude-prism.json` as a migration target
69
+ - `prism stats` reads plans from `.prism/plans/` with `docs/plans/` fallback
70
+ - `prism uninstall` cleans both new and legacy paths
71
+
72
+ ### Migration
73
+ Existing users: run `prism update` — all files are automatically migrated. No manual steps needed. The `docs/plans/` fallback ensures hooks work even without migration.
74
+
8
75
  ## [1.2.6] — 2026-02-28
9
76
 
10
77
  ### Fixed
@@ -56,7 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
56
123
  - session.mjs included in installed lib files
57
124
  - CHANGELOG.md (this file)
58
125
  - GitHub Actions CI workflow (test on push/PR)
59
- - Config schema versioning (`.claude-prism.json` version field)
126
+ - Config schema versioning (config version field)
60
127
  - Verification Fallback Ladder (7-level, from automated tests to manual diff)
61
128
  - Quality Gates between DECOMPOSE→EXECUTE and EXECUTE→CHECKPOINT
62
129
  - Goal Recitation mechanism at batch boundaries
package/README.md CHANGED
@@ -16,9 +16,14 @@
16
16
 
17
17
  # claude-prism
18
18
 
19
- **EUDEC methodology framework for AI coding agents.**
19
+ **AI agent harness implementing the EUDEC methodology for reliable AI-assisted coding.**
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
+ Prism is an [agent harness](https://martinfowler.com/articles/exploring-gen-ai/harness-engineering.html)
22
+ the infrastructure that channels AI coding agents toward correct, verified output.
23
+ It combines a behavioral methodology (EUDEC), deterministic hooks for enforcement,
24
+ session lifecycle automation, and adaptive process weight that scales with task complexity.
25
+
26
+ 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
27
 
23
28
  ## The Problem
24
29
 
@@ -75,27 +80,47 @@ Injected into `CLAUDE.md`, EUDEC is a behavioral framework that corrects how AI
75
80
  - **Medium risk** (new components, API integration): Build + runtime check
76
81
  - **Low risk** (imports, types, renaming): Build/lint passes
77
82
  - **No test infra** (legacy PHP, WordPress): Grep-based static check + syntax validation
78
- - Fallback: Automated Tests → Approval Testing → Build → Lint Smoke Check → Manual Diff
83
+ - Fallback Ladder: Tests → Build → Diff (use highest available)
79
84
 
80
85
  **Quality gates** between phases prevent executing on broken baselines.
81
86
 
82
- **New in v1.2.5:**
87
+ **v1.5.0:**
88
+ - **Adaptive Weight**: EUDEC auto-scales — lightweight (1-2 files), standard, or full path
89
+ - **Bugfix Fast Path**: symptom → cause → fix → verify (skips formal EUDEC cycle)
90
+ - **Streamlined verification**: 3-level fallback ladder (Tests → Build → Diff)
91
+ - **Adaptive checkpoints**: no pause for small tasks, summary for medium, full for large
92
+
93
+ **New in v1.4.0:**
94
+ - **Native Claude Code plugin** — `claude plugin install claude-prism` for zero-config setup
95
+ - **4 new hook events** — PreCompact (auto-HANDOFF), SessionEnd (session protection), SubagentStart (scope injection), TaskCompleted (plan auto-update)
96
+ - **HTTP webhooks** — fire-and-forget notifications on compaction, session-end, batch-complete
97
+ - **Checkpoint integration** — `Esc+Esc` / `/rewind` references complement Git-as-Memory
98
+
99
+ **v1.3.0:**
100
+ - `.prism/` brand directory — config, version, and plans live under `.prism/`
101
+ - Automatic 3-stage migration from legacy paths
102
+
103
+ **v1.2.5:**
83
104
  - **Analysis-only branch**: When no code change is needed, UNDERSTAND reports findings without entering DECOMPOSE/EXECUTE/CHECKPOINT
84
105
  - **Git-as-Memory**: Commits after each batch as rollback points; `git diff` summaries maintain context in long sessions
85
106
  - **Verification scoping**: Build check output filtered to changed files only — pre-existing errors are ignored
86
107
  - **Agent failure recovery**: 3-step protocol when delegated agents produce incomplete results
87
108
 
88
- ### 2. Three Focused Hooks
109
+ ### 2. Seven Focused Hooks
89
110
 
90
- Hooks enforce the methodology at critical points. All three are deterministic (no heuristics, no state accumulation issues):
111
+ Hooks enforce the methodology at critical points:
91
112
 
92
- | Hook | What It Does | Trigger |
113
+ | Hook | Event | What It Does |
93
114
  |---|---|---|
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` |
115
+ | **commit-guard** | PreToolUse | Blocks commits when tests failed or haven't run |
116
+ | **plan-enforcement** | PreToolUse | Warns when editing 6+ files without a plan |
117
+ | **test-tracker** | PostToolUse | Records test pass/fail results |
118
+ | **precompact-handler** | PreCompact | Auto-generates `docs/HANDOFF.md` before compaction |
119
+ | **session-end-handler** | SessionEnd | Saves HANDOFF + appends to `docs/PROJECT-MEMORY.md` |
120
+ | **scope-injector** | SubagentStart | Injects current plan batch context into subagent |
121
+ | **plan-sync** | TaskCompleted | Auto-updates plan file checkboxes on task completion |
97
122
 
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.
123
+ 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
124
 
100
125
  ### 3. Slash Commands
101
126
 
@@ -150,6 +175,16 @@ prism analytics --detail # Include per-session breakdown
150
175
 
151
176
  ## Installation
152
177
 
178
+ ### Option A: Plugin Mode (recommended)
179
+
180
+ ```bash
181
+ claude plugin install claude-prism
182
+ ```
183
+
184
+ Plugin mode auto-registers hooks and skills. Run `prism init` additionally if you want CLAUDE.md methodology injection (plugins cannot modify CLAUDE.md).
185
+
186
+ ### Option B: CLI Mode
187
+
153
188
  ```bash
154
189
  npx claude-prism init # Install with hooks (prompts for HUD)
155
190
  npx claude-prism init --hud # Install + auto-enable HUD
@@ -162,15 +197,19 @@ npx claude-prism init --dry-run # Preview what would be installed
162
197
 
163
198
  ```
164
199
  your-project/
165
- ├── CLAUDE.md # EUDEC methodology injected
166
- ├── .claude-prism.json # Hook configuration
200
+ ├── CLAUDE.md # EUDEC methodology injected (CLI mode only)
201
+ ├── .prism/
202
+ │ ├── config.json # Hook configuration (committed)
203
+ │ ├── .version # Installed version (gitignored)
204
+ │ ├── .gitignore # Ignores .version
205
+ │ └── plans/ # Plan files (created during work)
167
206
  ├── .claude/
168
207
  │ ├── 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)
208
+ │ ├── hooks/ # 6 runners (pre-tool, post-tool, precompact,
209
+ # session-end, subagent-start, task-completed)
210
+ │ ├── rules/ # 7 rule modules
211
+ ├── lib/ # 8 shared dependencies
212
+ └── settings.json # Hook registration (6 events)
174
213
 
175
214
  ~/.claude/ # (global install / HUD)
176
215
  ├── commands/claude-prism/ # 9 slash commands (--global)
@@ -180,7 +219,7 @@ your-project/
180
219
 
181
220
  ## Configuration
182
221
 
183
- Edit `.claude-prism.json`:
222
+ Edit `.prism/config.json`:
184
223
 
185
224
  ```json
186
225
  {
@@ -188,8 +227,19 @@ Edit `.claude-prism.json`:
188
227
  "hooks": {
189
228
  "commit-guard": { "enabled": true, "maxTestAge": 300 },
190
229
  "test-tracker": { "enabled": true },
191
- "plan-enforcement": { "enabled": true, "warnAt": 6 }
192
- }
230
+ "plan-enforcement": { "enabled": true, "warnAt": 6 },
231
+ "precompact-handler": { "enabled": true },
232
+ "session-end-handler": { "enabled": true },
233
+ "subagent-scope-injector": { "enabled": true },
234
+ "task-plan-sync": { "enabled": true, "matchThreshold": 0.3 }
235
+ },
236
+ "webhooks": [
237
+ {
238
+ "url": "https://your-server.com/webhook",
239
+ "events": ["compaction", "session-end", "batch-complete"],
240
+ "headers": { "Authorization": "Bearer token" }
241
+ }
242
+ ]
193
243
  }
194
244
  ```
195
245
 
@@ -198,6 +248,8 @@ Edit `.claude-prism.json`:
198
248
  | `version` | 1 | Config schema version (for future migrations) |
199
249
  | `commit-guard.maxTestAge` | 300 | Seconds before test run is considered stale |
200
250
  | `plan-enforcement.warnAt` | 6 | Unique source file count that triggers plan warning |
251
+ | `task-plan-sync.matchThreshold` | 0.3 | Keyword overlap ratio for fuzzy task matching |
252
+ | `webhooks` | `[]` | HTTP endpoints for event notifications |
201
253
 
202
254
  ## CLI Commands
203
255
 
@@ -235,6 +287,25 @@ prism hud disable # Deactivate HUD statusline
235
287
 
236
288
  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
289
 
290
+ ## Upgrading
291
+
292
+ ### To v1.4.0
293
+
294
+ ```bash
295
+ npx claude-prism update
296
+ ```
297
+
298
+ 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.
299
+
300
+ **Optional**: Install as a native plugin for auto-registration:
301
+ ```bash
302
+ claude plugin install claude-prism
303
+ ```
304
+
305
+ ### To v1.3.0
306
+
307
+ v1.3.0 moves project files to `.prism/` directory. Migration is automatic via `prism update`.
308
+
238
309
  ## Design Philosophy
239
310
 
240
311
  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));