@dezycro-ai/agent-plugin 2.1.1 → 2.2.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 (52) hide show
  1. package/README.md +14 -98
  2. package/dist/cli/dezycro.js +50 -0
  3. package/dist/cli/install.js +7 -0
  4. package/dist/dezycro-config.d.ts +52 -0
  5. package/dist/dezycro-config.js +167 -0
  6. package/dist/gateway/compact-cli.js +8 -0
  7. package/dist/gateway/proxy.js +35 -0
  8. package/dist/hooks/lib/router.js +14 -782
  9. package/dist/schemas/anthropic.d.ts +119 -0
  10. package/dist/schemas/anthropic.js +43 -0
  11. package/dist/schemas/dezycro.d.ts +19 -0
  12. package/dist/schemas/dezycro.js +22 -0
  13. package/dist/schemas/gateway.d.ts +40 -0
  14. package/dist/schemas/gateway.js +30 -0
  15. package/dist/schemas/gating.d.ts +32 -0
  16. package/dist/schemas/gating.js +15 -0
  17. package/dist/schemas/index.d.ts +14 -0
  18. package/dist/schemas/index.js +29 -0
  19. package/package.json +21 -13
  20. package/LICENSE +0 -18
  21. package/bin/resolve-auth.js +0 -112
  22. package/dist/hooks/lib/authoring.d.ts +0 -118
  23. package/dist/hooks/lib/authoring.js +0 -277
  24. package/dist/hooks/lib/command-bus.d.ts +0 -53
  25. package/dist/hooks/lib/command-bus.js +0 -318
  26. package/dist/hooks/lib/config.d.ts +0 -28
  27. package/dist/hooks/lib/config.js +0 -175
  28. package/dist/hooks/lib/controller-match.d.ts +0 -16
  29. package/dist/hooks/lib/controller-match.js +0 -96
  30. package/dist/hooks/lib/coverage.d.ts +0 -17
  31. package/dist/hooks/lib/coverage.js +0 -66
  32. package/dist/hooks/lib/hashing.d.ts +0 -8
  33. package/dist/hooks/lib/hashing.js +0 -49
  34. package/dist/hooks/lib/logging.d.ts +0 -13
  35. package/dist/hooks/lib/logging.js +0 -72
  36. package/dist/hooks/lib/publish-spec.d.ts +0 -17
  37. package/dist/hooks/lib/publish-spec.js +0 -64
  38. package/dist/hooks/lib/quality-gate.d.ts +0 -67
  39. package/dist/hooks/lib/quality-gate.js +0 -187
  40. package/dist/hooks/lib/router.d.ts +0 -32
  41. package/dist/hooks/lib/state.d.ts +0 -34
  42. package/dist/hooks/lib/state.js +0 -244
  43. package/dist/hooks/lib/undo.d.ts +0 -11
  44. package/dist/hooks/lib/undo.js +0 -71
  45. package/dist/hooks/lib/verify.d.ts +0 -28
  46. package/dist/hooks/lib/verify.js +0 -94
  47. package/dist/hooks/lib/workbook.d.ts +0 -21
  48. package/dist/hooks/lib/workbook.js +0 -77
  49. package/dist/hooks/types.d.ts +0 -278
  50. package/dist/hooks/types.js +0 -14
  51. package/install.js +0 -84
  52. package/setup.js +0 -53
@@ -1,187 +0,0 @@
1
- /**
2
- * quality-gate.ts — deterministic TRD-approval checks + code-side enforcement
3
- * of the P-9 LLM result. TRD §5.5.3 QUALITY_GATE and §6.9 enforcement notes.
4
- *
5
- * No LLM calls live here. The skill markdown is responsible for invoking P-9;
6
- * `enforceP9Result` is the tamper-proof code-side guard run against P-9's
7
- * structured output.
8
- */
9
- 'use strict';
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.MIN_P9_SCORE = exports.MIN_CONTEXT_EVIDENCE_CHARS = exports.MIN_CONTEXT_CATEGORIES = exports.PILLAR_HEADING_REGEX = exports.MIN_TRD_LENGTH = exports.PLACEHOLDER_REGEX = exports.REQUIRED_TRD_HEADINGS = exports.INVENTION_KEYWORDS = void 0;
12
- exports.parsePrdPillarNames = parsePrdPillarNames;
13
- exports.deterministicTrdGate = deterministicTrdGate;
14
- exports.minContextGate = minContextGate;
15
- exports.enforceP9Result = enforceP9Result;
16
- exports.INVENTION_KEYWORDS = Object.freeze([
17
- 'architecture',
18
- 'data model',
19
- 'data-model',
20
- 'datamodel',
21
- 'api contract',
22
- 'api-contract',
23
- 'apicontract',
24
- 'migration',
25
- 'auth',
26
- 'queue',
27
- 'verifier-behavior',
28
- 'verifier behavior',
29
- ]);
30
- exports.REQUIRED_TRD_HEADINGS = Object.freeze([
31
- { id: 'architecture', pattern: /^##\s+architecture\b/im },
32
- { id: 'data_model', pattern: /^##\s+data\s+model\b/im },
33
- { id: 'api_contracts', pattern: /^##\s+api\s+contracts?\b/im },
34
- { id: 'failure_modes', pattern: /^##\s+failure\s+modes?/im },
35
- { id: 'migration', pattern: /^##\s+migration\b/im },
36
- { id: 'open_questions', pattern: /^##\s+open\s+questions?\b/im },
37
- ]);
38
- exports.PLACEHOLDER_REGEX = /\b(TBD|TODO|FIXME|fill in later|to be determined)\b/i;
39
- exports.MIN_TRD_LENGTH = 1000;
40
- exports.PILLAR_HEADING_REGEX = /^##\s+Pillar\s+\d+\s*:\s*(.+?)\s*$/gim;
41
- exports.MIN_CONTEXT_CATEGORIES = Object.freeze([
42
- 'data_model',
43
- 'api_contracts',
44
- 'state_or_persistence',
45
- 'failure_modes',
46
- 'migration_or_rollout',
47
- ]);
48
- exports.MIN_CONTEXT_EVIDENCE_CHARS = 80;
49
- exports.MIN_P9_SCORE = 0.85;
50
- function normalizeWhitespace(s) {
51
- return (s || '').toString().replace(/\s+/g, ' ').trim();
52
- }
53
- function parsePrdPillarNames(prdContent) {
54
- if (!prdContent || typeof prdContent !== 'string')
55
- return [];
56
- const names = [];
57
- exports.PILLAR_HEADING_REGEX.lastIndex = 0;
58
- let match;
59
- while ((match = exports.PILLAR_HEADING_REGEX.exec(prdContent)) !== null) {
60
- const name = match[1].trim();
61
- if (name)
62
- names.push(name);
63
- }
64
- return names;
65
- }
66
- function deterministicTrdGate(opts) {
67
- const { trdContent, prdContent } = opts;
68
- const issues = [];
69
- const trd = (trdContent || '').toString();
70
- const trdLen = trd.trim().length;
71
- if (trdLen < exports.MIN_TRD_LENGTH) {
72
- issues.push({
73
- section: 'document',
74
- issue: `TRD is too short (${trdLen} chars; minimum ${exports.MIN_TRD_LENGTH}).`,
75
- required_fix: 'Expand the draft with concrete technical detail.',
76
- });
77
- }
78
- for (const required of exports.REQUIRED_TRD_HEADINGS) {
79
- if (!required.pattern.test(trd)) {
80
- issues.push({
81
- section: required.id,
82
- issue: `Required H2 section is missing.`,
83
- required_fix: `Add a "## ${required.id.replace(/_/g, ' ')}" section grounded in the PRD + discovery.`,
84
- });
85
- }
86
- }
87
- const placeholderMatch = trd.match(exports.PLACEHOLDER_REGEX);
88
- if (placeholderMatch) {
89
- issues.push({
90
- section: 'placeholders',
91
- issue: `Placeholder token "${placeholderMatch[0]}" present — TRDs must not ship with unresolved placeholders.`,
92
- required_fix: 'Resolve or move the open item into the ## Open Questions section.',
93
- });
94
- }
95
- const pillarNames = parsePrdPillarNames(prdContent);
96
- if (pillarNames.length > 0) {
97
- const missing = [];
98
- const haystack = trd.toLowerCase();
99
- for (const name of pillarNames) {
100
- if (!haystack.includes(name.toLowerCase()))
101
- missing.push(name);
102
- }
103
- if (missing.length > 0) {
104
- issues.push({
105
- section: 'pillar_coverage',
106
- issue: `TRD does not mention PRD pillar(s): ${missing.join(', ')}.`,
107
- required_fix: 'Add a per-pillar technical-design section, or reference each pillar by name in the relevant TRD section.',
108
- });
109
- }
110
- }
111
- return {
112
- passed: issues.length === 0,
113
- blockingIssues: issues,
114
- deterministic: true,
115
- };
116
- }
117
- function minContextGate(opts) {
118
- const summary = opts.discoverySummary || {};
119
- const confirmed = summary.confirmed_context || {};
120
- const likely = summary.likely_conventions || {};
121
- const answers = opts.answeredCategories || {};
122
- const missing = {};
123
- for (const cat of exports.MIN_CONTEXT_CATEGORIES) {
124
- const items = []
125
- .concat(confirmed[cat] || [])
126
- .concat(likely[cat] || [])
127
- .concat(answers[cat] || []);
128
- const combined = normalizeWhitespace(items.join(' '));
129
- const reasons = [];
130
- if (items.length === 0) {
131
- reasons.push('no evidence in confirmed_context, likely_conventions, or QA answers');
132
- }
133
- else if (combined.length < exports.MIN_CONTEXT_EVIDENCE_CHARS) {
134
- reasons.push(`evidence too thin (${combined.length} chars; need \u2265 ${exports.MIN_CONTEXT_EVIDENCE_CHARS})`);
135
- }
136
- if (reasons.length > 0)
137
- missing[cat] = reasons;
138
- }
139
- return {
140
- ready: Object.keys(missing).length === 0,
141
- missing,
142
- };
143
- }
144
- function enforceP9Result(p9Output) {
145
- const out = p9Output || {};
146
- const score = typeof out.score === 'number' ? out.score : 0;
147
- const suspected = Array.isArray(out.suspected_inventions) ? out.suspected_inventions : [];
148
- if (score < exports.MIN_P9_SCORE) {
149
- return {
150
- passed: false,
151
- reason: `P-9 score ${score.toFixed(2)} is below the minimum ${exports.MIN_P9_SCORE.toFixed(2)} — TRD is not approved.`,
152
- score,
153
- suspectedInventions: suspected,
154
- };
155
- }
156
- const flagged = [];
157
- for (const inv of suspected) {
158
- const claim = (inv && inv.claim ? inv.claim : '').toString().toLowerCase();
159
- if (!claim)
160
- continue;
161
- const hit = exports.INVENTION_KEYWORDS.find((kw) => claim.includes(kw));
162
- if (hit)
163
- flagged.push({ claim: String(inv.claim), keyword: hit });
164
- }
165
- if (flagged.length > 0) {
166
- const summary = flagged
167
- .map((f) => `"${f.claim}" (keyword: ${f.keyword})`)
168
- .join('; ');
169
- return {
170
- passed: false,
171
- reason: `P-9 flagged invented technical claim(s): ${summary}. Move to Open Questions or ask the user.`,
172
- score,
173
- suspectedInventions: suspected,
174
- };
175
- }
176
- if (out.approved !== true) {
177
- return {
178
- passed: false,
179
- reason: out.blocking_issues && out.blocking_issues.length > 0
180
- ? `P-9 reviewer rejected: ${String(out.blocking_issues[0].issue || 'blocking issue')}`
181
- : 'P-9 reviewer did not approve the TRD.',
182
- score,
183
- suspectedInventions: suspected,
184
- };
185
- }
186
- return { passed: true, reason: null, score, suspectedInventions: suspected };
187
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * router.ts — dispatches hook invocations to the right handler.
3
- *
4
- * The companion-runner shim parses argv and stdin payloads into a
5
- * RouterInvocation and calls dispatch(). Per-handler boundaries try/catch
6
- * via the runner entry point: any exception inside this module is logged
7
- * by the runner and converts to silent exit so a hook never breaks a
8
- * user session.
9
- */
10
- import type { PostToolUsePayload, PreToolUsePayload, RouterInvocation, StopPayload, UserPromptSubmitPayload, SessionStartPayload } from '../types';
11
- export declare const QUALITY_GATE_REASON_PREFIX = "TRD quality gate (deterministic layer) blocked APPROVED transition: ";
12
- export declare function dispatch(invocation: RouterInvocation): Promise<number>;
13
- declare function handleUpdateTaskGate(payload: PreToolUsePayload): number;
14
- declare function handlePostEdit(payload: PostToolUsePayload): Promise<number>;
15
- declare function extractEditedPaths(toolInput: Record<string, unknown> | null | undefined): string[];
16
- declare function handleSessionStart(_payload: SessionStartPayload): Promise<number>;
17
- declare function handleUserPromptSubmit(payload: UserPromptSubmitPayload): Promise<number>;
18
- declare function handleStop(payload: StopPayload): Promise<number>;
19
- declare function transcriptCharsSinceLastStop(payload: StopPayload): number | null;
20
- declare function readActiveFeatureId(repoRoot: string): string | null;
21
- declare function handleWorkbookMutation(payload: PostToolUsePayload): number;
22
- export declare const _extractEditedPaths: typeof extractEditedPaths;
23
- export declare const _handlePostEdit: typeof handlePostEdit;
24
- export declare const _handleUpdateTaskGate: typeof handleUpdateTaskGate;
25
- export declare const _handleStop: typeof handleStop;
26
- export declare const _handleUserPromptSubmit: typeof handleUserPromptSubmit;
27
- export declare const _handleWorkbookMutation: typeof handleWorkbookMutation;
28
- export declare const _handleSessionStart: typeof handleSessionStart;
29
- export declare const _readActiveFeatureId: typeof readActiveFeatureId;
30
- export declare const _transcriptCharsSinceLastStop: typeof transcriptCharsSinceLastStop;
31
- export declare const TRIVIAL_TURN_CHAR_DEFAULT_EXPORT = 600;
32
- export {};
@@ -1,34 +0,0 @@
1
- /**
2
- * state.ts — atomic read/write of `.dezycro/companion-state.json` per TRD §3.3.
3
- *
4
- * Lock acquisition: O_EXCL create of `.dezycro/.companion-state.lock`, retrying
5
- * with 10ms backoff for up to 500ms. On timeout, the caller no-ops — every
6
- * write site MUST be idempotent. Stale-lock cleanup is out of scope for V2.0.
7
- *
8
- * Hot-path readers (PostToolUse, UserPromptSubmit) call `read()` without the
9
- * lock — they accept a slightly stale view (TRD §3.3).
10
- */
11
- import type { CompanionState, CompanionMetrics, StatePatch } from '../types';
12
- export declare const HASH_RING_BUFFER_CAP = 32;
13
- export declare const CONTROLLER_EDITS_CAP = 64;
14
- export declare const DEFAULT_COMPANION_METRICS: Readonly<CompanionMetrics>;
15
- export declare const DEFAULT_COMPANION_STATE: Readonly<CompanionState>;
16
- export declare function read(repoRoot: string): CompanionState;
17
- declare function tryAcquireLock(repoRoot: string): boolean;
18
- declare function releaseLock(repoRoot: string): void;
19
- /**
20
- * Atomic patch under O_EXCL lock. Special keys:
21
- * - metrics: shallow-merged inside (caller can patch one counter).
22
- * - pushDecisionHash / pushIssueHash / pushAssumptionHash: appended to
23
- * the corresponding ring buffer (FIFO-capped at 32) instead of replacing.
24
- * - pushControllerEdit: same idea for controllerEditsSinceLastPublish
25
- * (FIFO-capped at 64), with replace-by-path semantics.
26
- *
27
- * Returns the new state, or null on lock-contention timeout.
28
- */
29
- export declare function patch(repoRoot: string, _patch: StatePatch): CompanionState | null;
30
- /** Bump a metric by 1 atomically. Returns null on lock contention. */
31
- export declare function incrementMetric(repoRoot: string, metricKey: keyof CompanionMetrics, delta?: number): CompanionState | null;
32
- export declare const _tryAcquireLock: typeof tryAcquireLock;
33
- export declare const _releaseLock: typeof releaseLock;
34
- export {};
@@ -1,244 +0,0 @@
1
- /**
2
- * state.ts — atomic read/write of `.dezycro/companion-state.json` per TRD §3.3.
3
- *
4
- * Lock acquisition: O_EXCL create of `.dezycro/.companion-state.lock`, retrying
5
- * with 10ms backoff for up to 500ms. On timeout, the caller no-ops — every
6
- * write site MUST be idempotent. Stale-lock cleanup is out of scope for V2.0.
7
- *
8
- * Hot-path readers (PostToolUse, UserPromptSubmit) call `read()` without the
9
- * lock — they accept a slightly stale view (TRD §3.3).
10
- */
11
- 'use strict';
12
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- var desc = Object.getOwnPropertyDescriptor(m, k);
15
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
- desc = { enumerable: true, get: function() { return m[k]; } };
17
- }
18
- Object.defineProperty(o, k2, desc);
19
- }) : (function(o, m, k, k2) {
20
- if (k2 === undefined) k2 = k;
21
- o[k2] = m[k];
22
- }));
23
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
- Object.defineProperty(o, "default", { enumerable: true, value: v });
25
- }) : function(o, v) {
26
- o["default"] = v;
27
- });
28
- var __importStar = (this && this.__importStar) || (function () {
29
- var ownKeys = function(o) {
30
- ownKeys = Object.getOwnPropertyNames || function (o) {
31
- var ar = [];
32
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
- return ar;
34
- };
35
- return ownKeys(o);
36
- };
37
- return function (mod) {
38
- if (mod && mod.__esModule) return mod;
39
- var result = {};
40
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
- __setModuleDefault(result, mod);
42
- return result;
43
- };
44
- })();
45
- Object.defineProperty(exports, "__esModule", { value: true });
46
- exports._releaseLock = exports._tryAcquireLock = exports.DEFAULT_COMPANION_STATE = exports.DEFAULT_COMPANION_METRICS = exports.CONTROLLER_EDITS_CAP = exports.HASH_RING_BUFFER_CAP = void 0;
47
- exports.read = read;
48
- exports.patch = patch;
49
- exports.incrementMetric = incrementMetric;
50
- const fs = __importStar(require("fs"));
51
- const path = __importStar(require("path"));
52
- const LOCK_TIMEOUT_MS = 500;
53
- const LOCK_BACKOFF_MS = 10;
54
- exports.HASH_RING_BUFFER_CAP = 32;
55
- exports.CONTROLLER_EDITS_CAP = 64;
56
- exports.DEFAULT_COMPANION_METRICS = Object.freeze({
57
- autoDecisionLogs: 0,
58
- autoIssueLogs: 0,
59
- autoAssumptionLogs: 0,
60
- autoTaskStatusLogs: 0,
61
- undoCount: 0,
62
- verifyAutoRuns: 0,
63
- verifyAutoFailures: 0,
64
- qualityGateFailures: 0,
65
- contextGateFailures: 0,
66
- lockContention: 0,
67
- forceApprovals: 0,
68
- });
69
- exports.DEFAULT_COMPANION_STATE = Object.freeze({
70
- schemaVersion: 1,
71
- currentSessionId: null,
72
- authoringMode: false,
73
- authoringDocId: null,
74
- authoringDocType: null,
75
- authoringTranscriptPath: null,
76
- authoringStartedTs: null,
77
- pulseCount: 0,
78
- lastEditTs: null,
79
- needsSpecPublish: false,
80
- controllerEditsSinceLastPublish: [],
81
- coverageSnapshot: null,
82
- coverageSnapshotTs: null,
83
- coverageFetchPending: false,
84
- lastVerifyCompletionTs: null,
85
- recentDecisionHashes: [],
86
- recentIssueHashes: [],
87
- recentAssumptionHashes: [],
88
- lastAutoWorkbookMutation: null,
89
- sessionAutoLogCount: 0,
90
- turnAutoLogCount: 0,
91
- currentTurnId: 0,
92
- suppressWorkbookUpdatesUntil: null,
93
- pendingWorkbookSweep: false,
94
- pendingWorkbookSweepReason: null,
95
- lastSeenVerifyCompletionTs: null,
96
- commandBus: {
97
- cursor: null,
98
- lastPollTs: null,
99
- consecutiveFailures: 0,
100
- recentEventIds: [],
101
- },
102
- metrics: exports.DEFAULT_COMPANION_METRICS,
103
- });
104
- function statePath(repoRoot) {
105
- return path.join(repoRoot, '.dezycro', 'companion-state.json');
106
- }
107
- function lockPath(repoRoot) {
108
- return path.join(repoRoot, '.dezycro', '.companion-state.lock');
109
- }
110
- function tmpPath(repoRoot) {
111
- return path.join(repoRoot, '.dezycro', '.companion-state.json.tmp');
112
- }
113
- function ensureDir(repoRoot) {
114
- fs.mkdirSync(path.join(repoRoot, '.dezycro'), { recursive: true });
115
- }
116
- function cloneDefault() {
117
- return JSON.parse(JSON.stringify(exports.DEFAULT_COMPANION_STATE));
118
- }
119
- function read(repoRoot) {
120
- const file = statePath(repoRoot);
121
- try {
122
- const raw = fs.readFileSync(file, 'utf8');
123
- const parsed = JSON.parse(raw);
124
- return {
125
- ...cloneDefault(),
126
- ...parsed,
127
- metrics: { ...exports.DEFAULT_COMPANION_METRICS, ...(parsed.metrics || {}) },
128
- };
129
- }
130
- catch (err) {
131
- const e = err;
132
- if (e && e.code === 'ENOENT')
133
- return cloneDefault();
134
- // Corrupt JSON — fall back. Caller (Stop hook etc) should still make
135
- // forward progress; the next successful patch will rewrite.
136
- return cloneDefault();
137
- }
138
- }
139
- function tryAcquireLock(repoRoot) {
140
- const lp = lockPath(repoRoot);
141
- const start = Date.now();
142
- while (Date.now() - start < LOCK_TIMEOUT_MS) {
143
- try {
144
- const fd = fs.openSync(lp, 'wx');
145
- fs.writeSync(fd, String(process.pid));
146
- fs.closeSync(fd);
147
- return true;
148
- }
149
- catch (err) {
150
- const e = err;
151
- if (e.code !== 'EEXIST')
152
- throw err;
153
- }
154
- const wait = Date.now() + LOCK_BACKOFF_MS;
155
- while (Date.now() < wait) { /* spin */ }
156
- }
157
- return false;
158
- }
159
- function releaseLock(repoRoot) {
160
- try {
161
- fs.unlinkSync(lockPath(repoRoot));
162
- }
163
- catch { /* ignore */ }
164
- }
165
- function mergeMetrics(current, incoming) {
166
- if (!incoming)
167
- return current;
168
- return { ...current, ...incoming };
169
- }
170
- function appendRing(existing, value, cap) {
171
- if (!value)
172
- return existing;
173
- if (existing.includes(value))
174
- return existing;
175
- const next = existing.concat([value]);
176
- return next.length > cap ? next.slice(next.length - cap) : next;
177
- }
178
- /**
179
- * Atomic patch under O_EXCL lock. Special keys:
180
- * - metrics: shallow-merged inside (caller can patch one counter).
181
- * - pushDecisionHash / pushIssueHash / pushAssumptionHash: appended to
182
- * the corresponding ring buffer (FIFO-capped at 32) instead of replacing.
183
- * - pushControllerEdit: same idea for controllerEditsSinceLastPublish
184
- * (FIFO-capped at 64), with replace-by-path semantics.
185
- *
186
- * Returns the new state, or null on lock-contention timeout.
187
- */
188
- function patch(repoRoot, _patch) {
189
- ensureDir(repoRoot);
190
- if (!tryAcquireLock(repoRoot)) {
191
- return null;
192
- }
193
- try {
194
- const current = read(repoRoot);
195
- const incoming = _patch || {};
196
- const next = { ...current };
197
- for (const key of Object.keys(incoming)) {
198
- const value = incoming[key];
199
- if (key === 'metrics') {
200
- next.metrics = mergeMetrics(current.metrics, value);
201
- }
202
- else if (key === 'pushDecisionHash') {
203
- next.recentDecisionHashes = appendRing(current.recentDecisionHashes, value, exports.HASH_RING_BUFFER_CAP);
204
- }
205
- else if (key === 'pushIssueHash') {
206
- next.recentIssueHashes = appendRing(current.recentIssueHashes, value, exports.HASH_RING_BUFFER_CAP);
207
- }
208
- else if (key === 'pushAssumptionHash') {
209
- next.recentAssumptionHashes = appendRing(current.recentAssumptionHashes, value, exports.HASH_RING_BUFFER_CAP);
210
- }
211
- else if (key === 'pushControllerEdit') {
212
- const edit = value;
213
- const existing = current.controllerEditsSinceLastPublish.filter((e) => e.path !== edit.path);
214
- const appended = existing.concat([edit]);
215
- next.controllerEditsSinceLastPublish =
216
- appended.length > exports.CONTROLLER_EDITS_CAP
217
- ? appended.slice(appended.length - exports.CONTROLLER_EDITS_CAP)
218
- : appended;
219
- }
220
- else {
221
- // Plain top-level field override.
222
- next[key] = value;
223
- }
224
- }
225
- const tmp = tmpPath(repoRoot);
226
- const fd = fs.openSync(tmp, 'w');
227
- fs.writeSync(fd, JSON.stringify(next, null, 2));
228
- fs.fsyncSync(fd);
229
- fs.closeSync(fd);
230
- fs.renameSync(tmp, statePath(repoRoot));
231
- return next;
232
- }
233
- finally {
234
- releaseLock(repoRoot);
235
- }
236
- }
237
- /** Bump a metric by 1 atomically. Returns null on lock contention. */
238
- function incrementMetric(repoRoot, metricKey, delta = 1) {
239
- const current = read(repoRoot).metrics[metricKey] || 0;
240
- return patch(repoRoot, { metrics: { [metricKey]: current + delta } });
241
- }
242
- // Exposed for tests:
243
- exports._tryAcquireLock = tryAcquireLock;
244
- exports._releaseLock = releaseLock;
@@ -1,11 +0,0 @@
1
- /**
2
- * undo.ts — detects undo phrases in user messages and builds the directive
3
- * that asks the agent to reverse the last auto-workbook mutation
4
- * (TRD §5.3.3).
5
- */
6
- import type { LastAutoWorkbookMutation, ReversalDirective } from '../types';
7
- export declare const UNDO_PHRASES: readonly string[];
8
- export declare function isUndoMessage(userMessage: unknown): boolean;
9
- export declare function buildReversalDirective(mutation: LastAutoWorkbookMutation | null): ReversalDirective | null;
10
- /** TRD §5.3.3: undo window is currentTurnId == createdInTurnId + 1. */
11
- export declare function isInUndoWindow(mutation: LastAutoWorkbookMutation | null, currentTurnId: number): boolean;
@@ -1,71 +0,0 @@
1
- /**
2
- * undo.ts — detects undo phrases in user messages and builds the directive
3
- * that asks the agent to reverse the last auto-workbook mutation
4
- * (TRD §5.3.3).
5
- */
6
- 'use strict';
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.UNDO_PHRASES = void 0;
9
- exports.isUndoMessage = isUndoMessage;
10
- exports.buildReversalDirective = buildReversalDirective;
11
- exports.isInUndoWindow = isInUndoWindow;
12
- exports.UNDO_PHRASES = Object.freeze([
13
- 'undo that',
14
- 'no, wrong',
15
- 'revert that',
16
- 'undo the last log',
17
- 'wrong, undo',
18
- ]);
19
- function isUndoMessage(userMessage) {
20
- if (typeof userMessage !== 'string')
21
- return false;
22
- const m = userMessage.toLowerCase().trim();
23
- if (!m)
24
- return false;
25
- for (let i = 0; i < exports.UNDO_PHRASES.length; i += 1) {
26
- if (m.includes(exports.UNDO_PHRASES[i]))
27
- return true;
28
- }
29
- return false;
30
- }
31
- function buildReversalDirective(mutation) {
32
- if (!mutation || !mutation.tool || !mutation.entityId)
33
- return null;
34
- const id = mutation.entityId;
35
- switch (mutation.tool) {
36
- case 'add_decision':
37
- return {
38
- tool: 'update_decision',
39
- args: { id, status: 'INVALIDATED', reason: 'undo by user' },
40
- humanLine: `Reverting the last auto-logged decision (${id.slice(0, 8)}\u2026) — marking INVALIDATED.`,
41
- };
42
- case 'add_issue':
43
- return {
44
- tool: 'update_issue',
45
- args: { id, status: 'CANCELLED', reason: 'undo by user' },
46
- humanLine: `Reverting the last auto-logged issue (${id.slice(0, 8)}\u2026) — marking CANCELLED.`,
47
- };
48
- case 'add_assumption':
49
- return {
50
- tool: 'update_issue',
51
- args: { id, status: 'INVALIDATED', kind: 'ASSUMPTION', reason: 'undo by user' },
52
- humanLine: `Reverting the last auto-logged assumption (${id.slice(0, 8)}\u2026) — marking INVALIDATED.`,
53
- };
54
- case 'update_task':
55
- if (!mutation.priorStatus)
56
- return null;
57
- return {
58
- tool: 'update_task',
59
- args: { id, status: mutation.priorStatus },
60
- humanLine: `Reverting the last auto-task-status change — restoring ${id.slice(0, 8)}\u2026 to ${mutation.priorStatus}.`,
61
- };
62
- default:
63
- return null;
64
- }
65
- }
66
- /** TRD §5.3.3: undo window is currentTurnId == createdInTurnId + 1. */
67
- function isInUndoWindow(mutation, currentTurnId) {
68
- if (!mutation || typeof mutation.createdInTurnId !== 'number')
69
- return false;
70
- return currentTurnId === mutation.createdInTurnId + 1;
71
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * verify.ts — verify-evidence helpers for the pillar-1 pre-DONE gate
3
- * (TRD §5.1).
4
- *
5
- * The PreToolUse hook does NOT shell out to the verifier binary — that lives
6
- * in the `/dezycro:verify` skill. The hook's job is to detect "this path-linked
7
- * task is transitioning to DONE/PUSHED without recent passing verifier
8
- * evidence" and block with a structured message telling the agent to run
9
- * `/dezycro:verify --path <p>` first.
10
- *
11
- * Evidence source: `.dezycro/state.json` (the pre-Companion local state file
12
- * written by the V1 verify skill). Per-path coverage isn't recorded there in
13
- * V1 — any recent passing run counts as covering all paths. V2.1 should
14
- * record covered-paths explicitly.
15
- */
16
- export interface HasRecentPassingVerifyOpts {
17
- repoRoot?: string;
18
- paths?: string[];
19
- maxAgeMinutes?: number;
20
- }
21
- export declare function hasRecentPassingVerify(opts: HasRecentPassingVerifyOpts): boolean;
22
- export interface BuildBlockReasonOpts {
23
- paths?: string[];
24
- status?: string;
25
- }
26
- export declare function buildBlockReason(opts: BuildBlockReasonOpts): {
27
- reason: string;
28
- };