@devrik-tools/claude-gates 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +17 -0
- package/README.es.md +219 -0
- package/README.md +222 -0
- package/cli/__tests__/config.test.mjs +101 -0
- package/cli/__tests__/init-flags.test.mjs +111 -0
- package/cli/__tests__/registry-gates-consistency.test.mjs +44 -0
- package/cli/__tests__/selection.test.mjs +200 -0
- package/cli/config.mjs +113 -0
- package/cli/constants.mjs +36 -0
- package/cli/index.mjs +97 -0
- package/cli/init.mjs +256 -0
- package/cli/install.mjs +80 -0
- package/cli/materialize.mjs +51 -0
- package/cli/registry.mjs +127 -0
- package/cli/selection.mjs +83 -0
- package/package.json +73 -0
- package/plugins/gates/.claude-plugin/plugin.json +9 -0
- package/plugins/gates/hooks/gates/audit-before-build/index.mjs +88 -0
- package/plugins/gates/hooks/gates/audit-before-build/test.mjs +92 -0
- package/plugins/gates/hooks/gates/bash-commands/index.mjs +233 -0
- package/plugins/gates/hooks/gates/bash-commands/test.mjs +113 -0
- package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +177 -0
- package/plugins/gates/hooks/gates/brief-before-delegate/test.mjs +111 -0
- package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +456 -0
- package/plugins/gates/hooks/gates/circuit-breaker/test.mjs +143 -0
- package/plugins/gates/hooks/gates/dependency-skills/index.mjs +118 -0
- package/plugins/gates/hooks/gates/dependency-skills/test.mjs +109 -0
- package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +49 -0
- package/plugins/gates/hooks/gates/diagnosis-before-patch/test.mjs +68 -0
- package/plugins/gates/hooks/gates/feature-catalog/index.mjs +100 -0
- package/plugins/gates/hooks/gates/feature-catalog/test.mjs +97 -0
- package/plugins/gates/hooks/gates/forge-flow/index.mjs +112 -0
- package/plugins/gates/hooks/gates/forge-flow/test.mjs +135 -0
- package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +186 -0
- package/plugins/gates/hooks/gates/implementation-pipeline/test.mjs +86 -0
- package/plugins/gates/hooks/gates/intent-flow/index.mjs +238 -0
- package/plugins/gates/hooks/gates/intent-flow/test.mjs +136 -0
- package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +166 -0
- package/plugins/gates/hooks/gates/mandatory-flow/test.mjs +119 -0
- package/plugins/gates/hooks/gates/neutral-spanish/index.mjs +85 -0
- package/plugins/gates/hooks/gates/neutral-spanish/test.mjs +65 -0
- package/plugins/gates/hooks/gates/never-assume/index.mjs +55 -0
- package/plugins/gates/hooks/gates/never-assume/test.mjs +78 -0
- package/plugins/gates/hooks/gates/no-blocking/index.mjs +142 -0
- package/plugins/gates/hooks/gates/no-blocking/test.mjs +108 -0
- package/plugins/gates/hooks/gates/no-memory-dependency/index.mjs +120 -0
- package/plugins/gates/hooks/gates/no-memory-dependency/test.mjs +106 -0
- package/plugins/gates/hooks/gates/no-reconfirm/index.mjs +202 -0
- package/plugins/gates/hooks/gates/no-reconfirm/test.mjs +131 -0
- package/plugins/gates/hooks/gates/protected-paths/index.mjs +147 -0
- package/plugins/gates/hooks/gates/protected-paths/test.mjs +75 -0
- package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +64 -0
- package/plugins/gates/hooks/gates/recurrence-lock/test.mjs +99 -0
- package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +150 -0
- package/plugins/gates/hooks/gates/reuse-before-build/test.mjs +101 -0
- package/plugins/gates/hooks/gates/risk-level/index.mjs +203 -0
- package/plugins/gates/hooks/gates/risk-level/test.mjs +125 -0
- package/plugins/gates/hooks/gates/root-cause-first/index.mjs +50 -0
- package/plugins/gates/hooks/gates/root-cause-first/test.mjs +73 -0
- package/plugins/gates/hooks/gates/root-whitelist/index.mjs +111 -0
- package/plugins/gates/hooks/gates/root-whitelist/test.mjs +80 -0
- package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +99 -0
- package/plugins/gates/hooks/gates/rule-skill-autodiscovery/test.mjs +78 -0
- package/plugins/gates/hooks/gates/sdd-specs/index.mjs +251 -0
- package/plugins/gates/hooks/gates/sdd-specs/test.mjs +163 -0
- package/plugins/gates/hooks/gates/test-after-implementation/index.mjs +101 -0
- package/plugins/gates/hooks/gates/test-after-implementation/test.mjs +81 -0
- package/plugins/gates/hooks/gates/test-matrix/index.mjs +181 -0
- package/plugins/gates/hooks/gates/test-matrix/test.mjs +87 -0
- package/plugins/gates/hooks/gates/tool-map/index.mjs +140 -0
- package/plugins/gates/hooks/gates/tool-map/test.mjs +87 -0
- package/plugins/gates/hooks/hooks.json +266 -0
- package/plugins/gates/hooks/lib/__tests__/config.test.mjs +154 -0
- package/plugins/gates/hooks/lib/__tests__/hook-io.test.mjs +154 -0
- package/plugins/gates/hooks/lib/config.mjs +165 -0
- package/plugins/gates/hooks/lib/hook-io.mjs +208 -0
- package/plugins/tasks/hooks/lib/__tests__/task-store.test.mjs +132 -0
- package/plugins/tasks/hooks/lib/task-store.mjs +159 -0
- package/plugins/tasks/hooks/register-requests.mjs +108 -0
- package/registry.json +668 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// register-requests — UserPromptSubmit hook. Runs on every user message, BEFORE the
|
|
2
|
+
// assistant responds. It is the deterministic half of the task system: it never decides
|
|
3
|
+
// what is a task (that judgment is the assistant's), it only makes sure open tasks are not
|
|
4
|
+
// forgotten. Two jobs, both cheap and side-effect-light:
|
|
5
|
+
// 1. Count messages. Every `remindEvery` messages, surface a stronger reminder.
|
|
6
|
+
// 2. Inject the open tasks (title + status) into the assistant's context so a long or
|
|
7
|
+
// compacted conversation never loses the thread of what is still pending.
|
|
8
|
+
//
|
|
9
|
+
// A UserPromptSubmit hook's stdout is added to the assistant's context as plain text, so
|
|
10
|
+
// the reminder is simply written to stdout. Self-contained: Node built-ins + the task-store
|
|
11
|
+
// lib only, no npm at runtime.
|
|
12
|
+
//
|
|
13
|
+
// justification: no existing tool covers this. The task store and the audit were done when
|
|
14
|
+
// task-store.mjs was written (see its header); this file is the hook wiring on top.
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
import { openTaskStore } from './lib/task-store.mjs';
|
|
20
|
+
|
|
21
|
+
const STDIN_FILE_DESCRIPTOR = 0;
|
|
22
|
+
const CONFIG_KEY = 'runTaskReminders';
|
|
23
|
+
const DEFAULT_REMIND_EVERY = 10;
|
|
24
|
+
const MAX_TASKS_SHOWN = 12;
|
|
25
|
+
|
|
26
|
+
// Config lookup mirrors the gates' config.mjs (project → global), kept local so this plugin
|
|
27
|
+
// stays independent of the gates plugin.
|
|
28
|
+
const PROJECT_ROOT_MARKERS = ['.git', '.ai'];
|
|
29
|
+
const PROJECT_CONFIG = join('.ai', 'config.json');
|
|
30
|
+
const GLOBAL_CONFIG = join('.claude', 'claude-gates', 'config.json');
|
|
31
|
+
|
|
32
|
+
function readJson(path) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function projectRootOf(startDirectory) {
|
|
41
|
+
let current = startDirectory;
|
|
42
|
+
while (true) {
|
|
43
|
+
if (
|
|
44
|
+
PROJECT_ROOT_MARKERS.some((marker) => existsSync(join(current, marker)))
|
|
45
|
+
) {
|
|
46
|
+
return current;
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(current);
|
|
49
|
+
if (parent === current) return null;
|
|
50
|
+
current = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The tasks gate config entry (project overrides global), or null. */
|
|
55
|
+
function taskConfig(startDirectory) {
|
|
56
|
+
const root = projectRootOf(startDirectory);
|
|
57
|
+
const projectData = root ? readJson(join(root, PROJECT_CONFIG)) : null;
|
|
58
|
+
const globalData = readJson(join(homedir(), GLOBAL_CONFIG));
|
|
59
|
+
const layer = projectData?.gates ?? globalData?.gates ?? {};
|
|
60
|
+
const entry = layer[CONFIG_KEY];
|
|
61
|
+
if (typeof entry === 'boolean') return { enabled: entry };
|
|
62
|
+
if (entry && typeof entry === 'object') return entry;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readPayload() {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(readFileSync(STDIN_FILE_DESCRIPTOR, 'utf8'));
|
|
69
|
+
} catch {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatReminder(tasks, forced) {
|
|
75
|
+
const shown = tasks.slice(0, MAX_TASKS_SHOWN);
|
|
76
|
+
const lines = shown.map(
|
|
77
|
+
(task) => ` - [${task.status}] ${task.id}: ${task.title}`,
|
|
78
|
+
);
|
|
79
|
+
const extra = tasks.length - shown.length;
|
|
80
|
+
if (extra > 0) lines.push(` - …and ${extra} more`);
|
|
81
|
+
const header = forced
|
|
82
|
+
? 'OPEN TASKS — recite these pending tasks to the user and confirm whether any should be dropped:'
|
|
83
|
+
: 'Open tasks (do not lose track of these):';
|
|
84
|
+
return `[tasks] ${header}\n${lines.join('\n')}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const payload = readPayload();
|
|
89
|
+
const cwd = payload.cwd || process.cwd();
|
|
90
|
+
|
|
91
|
+
const config = taskConfig(cwd);
|
|
92
|
+
if (config && config.enabled === false) return; // gate turned off for this project
|
|
93
|
+
|
|
94
|
+
const store = openTaskStore(cwd);
|
|
95
|
+
if (!store) return; // no project: nowhere to track
|
|
96
|
+
|
|
97
|
+
const tasks = store.active();
|
|
98
|
+
if (tasks.length === 0) return; // nothing pending: stay silent
|
|
99
|
+
|
|
100
|
+
const remindEvery = Number(config?.remindEvery) || DEFAULT_REMIND_EVERY;
|
|
101
|
+
const next = store.counter() + 1;
|
|
102
|
+
const forced = next >= remindEvery;
|
|
103
|
+
store.setCounter(forced ? 0 : next);
|
|
104
|
+
|
|
105
|
+
process.stdout.write(`${formatReminder(tasks, forced)}\n`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main();
|