@dezycro-ai/agent-plugin 2.0.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 (55) hide show
  1. package/.claude-plugin/plugin.json +7 -0
  2. package/LICENSE +18 -0
  3. package/PERSONAS.md +309 -0
  4. package/README.md +125 -0
  5. package/bin/resolve-auth.js +112 -0
  6. package/dist/hooks/lib/authoring.d.ts +118 -0
  7. package/dist/hooks/lib/authoring.js +277 -0
  8. package/dist/hooks/lib/config.d.ts +28 -0
  9. package/dist/hooks/lib/config.js +175 -0
  10. package/dist/hooks/lib/controller-match.d.ts +16 -0
  11. package/dist/hooks/lib/controller-match.js +96 -0
  12. package/dist/hooks/lib/coverage.d.ts +17 -0
  13. package/dist/hooks/lib/coverage.js +66 -0
  14. package/dist/hooks/lib/hashing.d.ts +8 -0
  15. package/dist/hooks/lib/hashing.js +49 -0
  16. package/dist/hooks/lib/logging.d.ts +13 -0
  17. package/dist/hooks/lib/logging.js +72 -0
  18. package/dist/hooks/lib/publish-spec.d.ts +17 -0
  19. package/dist/hooks/lib/publish-spec.js +64 -0
  20. package/dist/hooks/lib/quality-gate.d.ts +67 -0
  21. package/dist/hooks/lib/quality-gate.js +187 -0
  22. package/dist/hooks/lib/router.d.ts +32 -0
  23. package/dist/hooks/lib/router.js +717 -0
  24. package/dist/hooks/lib/state.d.ts +34 -0
  25. package/dist/hooks/lib/state.js +238 -0
  26. package/dist/hooks/lib/undo.d.ts +11 -0
  27. package/dist/hooks/lib/undo.js +71 -0
  28. package/dist/hooks/lib/verify.d.ts +28 -0
  29. package/dist/hooks/lib/verify.js +94 -0
  30. package/dist/hooks/lib/workbook.d.ts +21 -0
  31. package/dist/hooks/lib/workbook.js +77 -0
  32. package/dist/hooks/types.d.ts +249 -0
  33. package/dist/hooks/types.js +14 -0
  34. package/hooks/companion-runner +108 -0
  35. package/hooks/hooks.json +74 -0
  36. package/install.js +84 -0
  37. package/package.json +50 -0
  38. package/prompts/existing-work-ranker.md +47 -0
  39. package/prompts/prd-drafter.md +53 -0
  40. package/prompts/prd-question.md +66 -0
  41. package/prompts/trd-context-gate.md +57 -0
  42. package/prompts/trd-discovery.md +71 -0
  43. package/prompts/trd-drafter.md +63 -0
  44. package/prompts/trd-question.md +61 -0
  45. package/prompts/trd-reviewer.md +74 -0
  46. package/prompts/workbook-sweep.md +89 -0
  47. package/setup.js +35 -0
  48. package/skills/author/SKILL.md +285 -0
  49. package/skills/import-features/SKILL.md +428 -0
  50. package/skills/init/SKILL.md +133 -0
  51. package/skills/publish-spec/SKILL.md +101 -0
  52. package/skills/status/SKILL.md +76 -0
  53. package/skills/sync/SKILL.md +76 -0
  54. package/skills/verify/SKILL.md +248 -0
  55. package/skills/work/SKILL.md +201 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * authoring.ts — state machine helpers for /dezycro:author (TRD §5.5.1).
3
+ *
4
+ * The skill markdown drives the agent turn-by-turn; this module owns the
5
+ * persisted side effects (authoring mode in companion-state.json, the
6
+ * transcript at .dezycro/authoring-transcript.json, and the most-recent-draft
7
+ * sidecar at .dezycro/authoring-current-draft.json).
8
+ *
9
+ * No LLM calls live here.
10
+ */
11
+ export declare const STATES: Readonly<{
12
+ readonly IDLE: "IDLE";
13
+ readonly DETECT_INTENT: "DETECT_INTENT";
14
+ readonly SCAN_EXISTING_WORK: "SCAN_EXISTING_WORK";
15
+ readonly CLARIFY: "CLARIFY";
16
+ readonly PROPOSE_PLACEMENT: "PROPOSE_PLACEMENT";
17
+ readonly CREATE_FEATURE: "CREATE_FEATURE";
18
+ readonly TRD_DISCOVERY: "TRD_DISCOVERY";
19
+ readonly MIN_CONTEXT_GATE: "MIN_CONTEXT_GATE";
20
+ readonly DRAFT: "DRAFT";
21
+ readonly ITERATE: "ITERATE";
22
+ readonly QUALITY_GATE: "QUALITY_GATE";
23
+ readonly FINALIZE: "FINALIZE";
24
+ }>;
25
+ export type AuthoringStateName = typeof STATES[keyof typeof STATES];
26
+ export declare const DOC_TYPES: Readonly<{
27
+ readonly PRD: "PRD";
28
+ readonly TRD: "TRD";
29
+ }>;
30
+ export type DocType = typeof DOC_TYPES[keyof typeof DOC_TYPES];
31
+ export declare const SKIP_PHRASES: readonly string[];
32
+ export declare const NON_SKIPPABLE_STATES: readonly AuthoringStateName[];
33
+ export declare const TRANSCRIPT_FILENAME = "authoring-transcript.json";
34
+ export declare const DRAFT_SIDECAR_FILENAME = "authoring-current-draft.json";
35
+ export interface AuthoringTranscript {
36
+ docId: string;
37
+ docType: DocType;
38
+ featureId: string | null;
39
+ seed: string | null;
40
+ startedTs: string;
41
+ discoverySummary: string | null;
42
+ qaTurns: Array<{
43
+ ts: string;
44
+ question: string;
45
+ answer: string;
46
+ category: string | null;
47
+ }>;
48
+ draftHistory: Array<{
49
+ ts: string;
50
+ contentHash: string;
51
+ }>;
52
+ qualityGateHistory: Array<{
53
+ ts: string;
54
+ passed: boolean;
55
+ blockingIssues: unknown[];
56
+ score: number | null;
57
+ deterministic: boolean;
58
+ }>;
59
+ }
60
+ export interface DraftSidecar {
61
+ docId: string;
62
+ docType: DocType;
63
+ content: string;
64
+ prdDocId: string | null;
65
+ prdContent: string | null;
66
+ updatedTs: string;
67
+ }
68
+ declare function transcriptPath(repoRoot: string): string;
69
+ declare function draftSidecarPath(repoRoot: string): string;
70
+ export interface EnterOpts {
71
+ docId: string;
72
+ docType: DocType;
73
+ featureId?: string | null;
74
+ seed?: string | null;
75
+ }
76
+ export declare function enter(repoRoot: string, opts: EnterOpts): AuthoringTranscript;
77
+ export declare function exit(repoRoot: string): void;
78
+ export declare function isActive(repoRoot: string): boolean;
79
+ export interface AuthoringStateSnapshot {
80
+ active: boolean;
81
+ docId: string | null;
82
+ docType: DocType | null;
83
+ transcriptPath: string | null;
84
+ startedTs: string | null;
85
+ }
86
+ export declare function getState(repoRoot: string): AuthoringStateSnapshot;
87
+ export interface RecordAnswerOpts {
88
+ question: string;
89
+ answer: string;
90
+ category?: string | null;
91
+ }
92
+ export declare function recordAnswer(repoRoot: string, opts: RecordAnswerOpts): AuthoringTranscript | null;
93
+ export declare function recordDiscoverySummary(repoRoot: string, discoverySummary: string | null): AuthoringTranscript | null;
94
+ export declare function recordDraft(repoRoot: string, opts: {
95
+ contentHash: string;
96
+ }): AuthoringTranscript | null;
97
+ export interface RecordGateResultOpts {
98
+ passed: boolean;
99
+ blockingIssues?: unknown[];
100
+ score?: number;
101
+ deterministic?: boolean;
102
+ }
103
+ export declare function recordGateResult(repoRoot: string, opts: RecordGateResultOpts): AuthoringTranscript | null;
104
+ export declare function readTranscript(repoRoot: string): AuthoringTranscript | null;
105
+ export interface WriteDraftSidecarOpts {
106
+ docId: string;
107
+ docType: DocType;
108
+ content: string;
109
+ prdDocId?: string | null;
110
+ prdContent?: string | null;
111
+ }
112
+ export declare function writeDraftSidecar(repoRoot: string, opts: WriteDraftSidecarOpts): void;
113
+ export declare function readDraftSidecar(repoRoot: string): DraftSidecar | null;
114
+ export declare function isSkipPhrase(userMessage: unknown): boolean;
115
+ export declare function isNonSkippableState(stateName: AuthoringStateName): boolean;
116
+ export declare const _transcriptPath: typeof transcriptPath;
117
+ export declare const _draftSidecarPath: typeof draftSidecarPath;
118
+ export {};
@@ -0,0 +1,277 @@
1
+ /**
2
+ * authoring.ts — state machine helpers for /dezycro:author (TRD §5.5.1).
3
+ *
4
+ * The skill markdown drives the agent turn-by-turn; this module owns the
5
+ * persisted side effects (authoring mode in companion-state.json, the
6
+ * transcript at .dezycro/authoring-transcript.json, and the most-recent-draft
7
+ * sidecar at .dezycro/authoring-current-draft.json).
8
+ *
9
+ * No LLM calls live here.
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._draftSidecarPath = exports._transcriptPath = exports.DRAFT_SIDECAR_FILENAME = exports.TRANSCRIPT_FILENAME = exports.NON_SKIPPABLE_STATES = exports.SKIP_PHRASES = exports.DOC_TYPES = exports.STATES = void 0;
47
+ exports.enter = enter;
48
+ exports.exit = exit;
49
+ exports.isActive = isActive;
50
+ exports.getState = getState;
51
+ exports.recordAnswer = recordAnswer;
52
+ exports.recordDiscoverySummary = recordDiscoverySummary;
53
+ exports.recordDraft = recordDraft;
54
+ exports.recordGateResult = recordGateResult;
55
+ exports.readTranscript = readTranscript;
56
+ exports.writeDraftSidecar = writeDraftSidecar;
57
+ exports.readDraftSidecar = readDraftSidecar;
58
+ exports.isSkipPhrase = isSkipPhrase;
59
+ exports.isNonSkippableState = isNonSkippableState;
60
+ const fs = __importStar(require("fs"));
61
+ const path = __importStar(require("path"));
62
+ const state = __importStar(require("./state"));
63
+ exports.STATES = Object.freeze({
64
+ IDLE: 'IDLE',
65
+ DETECT_INTENT: 'DETECT_INTENT',
66
+ SCAN_EXISTING_WORK: 'SCAN_EXISTING_WORK',
67
+ CLARIFY: 'CLARIFY',
68
+ PROPOSE_PLACEMENT: 'PROPOSE_PLACEMENT',
69
+ CREATE_FEATURE: 'CREATE_FEATURE',
70
+ TRD_DISCOVERY: 'TRD_DISCOVERY',
71
+ MIN_CONTEXT_GATE: 'MIN_CONTEXT_GATE',
72
+ DRAFT: 'DRAFT',
73
+ ITERATE: 'ITERATE',
74
+ QUALITY_GATE: 'QUALITY_GATE',
75
+ FINALIZE: 'FINALIZE',
76
+ });
77
+ exports.DOC_TYPES = Object.freeze({ PRD: 'PRD', TRD: 'TRD' });
78
+ exports.SKIP_PHRASES = Object.freeze(['skip', 'draft it', 'just write it', 'next']);
79
+ exports.NON_SKIPPABLE_STATES = Object.freeze([
80
+ exports.STATES.MIN_CONTEXT_GATE,
81
+ exports.STATES.QUALITY_GATE,
82
+ ]);
83
+ exports.TRANSCRIPT_FILENAME = 'authoring-transcript.json';
84
+ exports.DRAFT_SIDECAR_FILENAME = 'authoring-current-draft.json';
85
+ function transcriptPath(repoRoot) {
86
+ return path.join(repoRoot, '.dezycro', exports.TRANSCRIPT_FILENAME);
87
+ }
88
+ function draftSidecarPath(repoRoot) {
89
+ return path.join(repoRoot, '.dezycro', exports.DRAFT_SIDECAR_FILENAME);
90
+ }
91
+ function ensureDezycroDir(repoRoot) {
92
+ fs.mkdirSync(path.join(repoRoot, '.dezycro'), { recursive: true });
93
+ }
94
+ function writeJsonAtomic(filePath, data) {
95
+ const tmp = `${filePath}.tmp`;
96
+ const fd = fs.openSync(tmp, 'w');
97
+ try {
98
+ fs.writeSync(fd, JSON.stringify(data, null, 2));
99
+ fs.fsyncSync(fd);
100
+ }
101
+ finally {
102
+ fs.closeSync(fd);
103
+ }
104
+ fs.renameSync(tmp, filePath);
105
+ }
106
+ function readJson(filePath) {
107
+ try {
108
+ const raw = fs.readFileSync(filePath, 'utf8');
109
+ return JSON.parse(raw);
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ }
115
+ function enter(repoRoot, opts) {
116
+ if (!opts || !opts.docId || !opts.docType) {
117
+ throw new Error('authoring.enter: docId and docType required');
118
+ }
119
+ if (opts.docType !== exports.DOC_TYPES.PRD && opts.docType !== exports.DOC_TYPES.TRD) {
120
+ throw new Error(`authoring.enter: invalid docType ${opts.docType}`);
121
+ }
122
+ ensureDezycroDir(repoRoot);
123
+ const tp = transcriptPath(repoRoot);
124
+ const now = new Date().toISOString();
125
+ const existing = readJson(tp);
126
+ const transcript = existing && existing.docId === opts.docId
127
+ ? existing
128
+ : {
129
+ docId: opts.docId,
130
+ docType: opts.docType,
131
+ featureId: opts.featureId || null,
132
+ seed: opts.seed || null,
133
+ startedTs: now,
134
+ discoverySummary: null,
135
+ qaTurns: [],
136
+ draftHistory: [],
137
+ qualityGateHistory: [],
138
+ };
139
+ writeJsonAtomic(tp, transcript);
140
+ state.patch(repoRoot, {
141
+ authoringMode: true,
142
+ authoringDocId: opts.docId,
143
+ authoringDocType: opts.docType,
144
+ authoringTranscriptPath: tp,
145
+ authoringStartedTs: now,
146
+ });
147
+ return transcript;
148
+ }
149
+ function exit(repoRoot) {
150
+ state.patch(repoRoot, {
151
+ authoringMode: false,
152
+ authoringDocId: null,
153
+ authoringDocType: null,
154
+ authoringTranscriptPath: null,
155
+ authoringStartedTs: null,
156
+ });
157
+ for (const p of [transcriptPath(repoRoot), draftSidecarPath(repoRoot)]) {
158
+ try {
159
+ fs.unlinkSync(p);
160
+ }
161
+ catch { /* ignore */ }
162
+ }
163
+ }
164
+ function isActive(repoRoot) {
165
+ const s = state.read(repoRoot);
166
+ return Boolean(s && s.authoringMode);
167
+ }
168
+ function getState(repoRoot) {
169
+ const s = state.read(repoRoot);
170
+ return {
171
+ active: Boolean(s && s.authoringMode),
172
+ docId: s ? s.authoringDocId : null,
173
+ docType: s ? s.authoringDocType : null,
174
+ transcriptPath: s ? s.authoringTranscriptPath : null,
175
+ startedTs: s ? s.authoringStartedTs : null,
176
+ };
177
+ }
178
+ function recordAnswer(repoRoot, opts) {
179
+ if (!opts || typeof opts.question !== 'string' || typeof opts.answer !== 'string') {
180
+ throw new Error('authoring.recordAnswer: question and answer required');
181
+ }
182
+ const tp = transcriptPath(repoRoot);
183
+ const t = readJson(tp);
184
+ if (!t)
185
+ return null;
186
+ t.qaTurns = (t.qaTurns || []).concat([{
187
+ ts: new Date().toISOString(),
188
+ question: opts.question,
189
+ answer: opts.answer,
190
+ category: opts.category || null,
191
+ }]);
192
+ writeJsonAtomic(tp, t);
193
+ return t;
194
+ }
195
+ function recordDiscoverySummary(repoRoot, discoverySummary) {
196
+ const tp = transcriptPath(repoRoot);
197
+ const t = readJson(tp);
198
+ if (!t)
199
+ return null;
200
+ t.discoverySummary = discoverySummary || null;
201
+ writeJsonAtomic(tp, t);
202
+ return t;
203
+ }
204
+ function recordDraft(repoRoot, opts) {
205
+ if (!opts || typeof opts.contentHash !== 'string') {
206
+ throw new Error('authoring.recordDraft: contentHash required');
207
+ }
208
+ const tp = transcriptPath(repoRoot);
209
+ const t = readJson(tp);
210
+ if (!t)
211
+ return null;
212
+ t.draftHistory = (t.draftHistory || []).concat([{
213
+ ts: new Date().toISOString(),
214
+ contentHash: opts.contentHash,
215
+ }]);
216
+ writeJsonAtomic(tp, t);
217
+ return t;
218
+ }
219
+ function recordGateResult(repoRoot, opts) {
220
+ if (!opts || typeof opts.passed !== 'boolean') {
221
+ throw new Error('authoring.recordGateResult: passed required');
222
+ }
223
+ const tp = transcriptPath(repoRoot);
224
+ const t = readJson(tp);
225
+ if (!t)
226
+ return null;
227
+ t.qualityGateHistory = (t.qualityGateHistory || []).concat([{
228
+ ts: new Date().toISOString(),
229
+ passed: opts.passed,
230
+ blockingIssues: opts.blockingIssues || [],
231
+ score: typeof opts.score === 'number' ? opts.score : null,
232
+ deterministic: opts.deterministic === true,
233
+ }]);
234
+ writeJsonAtomic(tp, t);
235
+ return t;
236
+ }
237
+ function readTranscript(repoRoot) {
238
+ return readJson(transcriptPath(repoRoot));
239
+ }
240
+ function writeDraftSidecar(repoRoot, opts) {
241
+ if (!opts || !opts.docId || !opts.docType || typeof opts.content !== 'string') {
242
+ throw new Error('authoring.writeDraftSidecar: docId, docType, content required');
243
+ }
244
+ ensureDezycroDir(repoRoot);
245
+ writeJsonAtomic(draftSidecarPath(repoRoot), {
246
+ docId: opts.docId,
247
+ docType: opts.docType,
248
+ content: opts.content,
249
+ prdDocId: opts.prdDocId || null,
250
+ prdContent: opts.prdContent || null,
251
+ updatedTs: new Date().toISOString(),
252
+ });
253
+ }
254
+ function readDraftSidecar(repoRoot) {
255
+ return readJson(draftSidecarPath(repoRoot));
256
+ }
257
+ function isSkipPhrase(userMessage) {
258
+ if (typeof userMessage !== 'string')
259
+ return false;
260
+ const trimmed = userMessage.trim().toLowerCase();
261
+ if (!trimmed)
262
+ return false;
263
+ const stripped = trimmed.replace(/[.!?,;:]+$/g, '').trim();
264
+ for (const phrase of exports.SKIP_PHRASES) {
265
+ if (stripped === phrase)
266
+ return true;
267
+ if (stripped.startsWith(`${phrase} `) || stripped.startsWith(`${phrase},`))
268
+ return true;
269
+ }
270
+ return false;
271
+ }
272
+ function isNonSkippableState(stateName) {
273
+ return exports.NON_SKIPPABLE_STATES.includes(stateName);
274
+ }
275
+ // Path helpers exposed for tests and the router.
276
+ exports._transcriptPath = transcriptPath;
277
+ exports._draftSidecarPath = draftSidecarPath;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * config.ts — reads `.dezycro/config.json` and resolves Companion settings.
3
+ *
4
+ * Owns the named-preset constant table (TRD §7.2) for the workbook sweep
5
+ * confidence-threshold logic.
6
+ */
7
+ import type { CompanionConfig, PresetName, Thresholds } from '../types';
8
+ export declare const PRESETS: Readonly<Record<PresetName, Thresholds>>;
9
+ export declare const DEFAULT_CONFIG: Readonly<CompanionConfig>;
10
+ export declare function deepMerge<T>(base: T, override: unknown): T;
11
+ /**
12
+ * Load `.dezycro/config.json` from the given repo root, deep-merged with
13
+ * DEFAULT_CONFIG. Returns DEFAULT_CONFIG if the file is missing or unparseable.
14
+ */
15
+ export declare function load(repoRoot: string): CompanionConfig;
16
+ /**
17
+ * Resolve per-type confidence thresholds for the workbook sweep, honoring
18
+ * `companion.autoWorkbookUpdates.preset` and any numeric overrides under
19
+ * `companion.autoWorkbookUpdates.thresholds.*`.
20
+ */
21
+ export declare function resolveThresholds(config: CompanionConfig): Thresholds;
22
+ /**
23
+ * Find the nearest ancestor of `startDir` that contains a `.dezycro/`
24
+ * directory, returning that ancestor as the repo root. Returns null if not
25
+ * found before reaching the filesystem root.
26
+ */
27
+ export declare function findRepoRoot(startDir: string | null | undefined): string | null;
28
+ export declare const _deepMerge: typeof deepMerge;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * config.ts — reads `.dezycro/config.json` and resolves Companion settings.
3
+ *
4
+ * Owns the named-preset constant table (TRD §7.2) for the workbook sweep
5
+ * confidence-threshold logic.
6
+ */
7
+ 'use strict';
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports._deepMerge = exports.DEFAULT_CONFIG = exports.PRESETS = void 0;
43
+ exports.deepMerge = deepMerge;
44
+ exports.load = load;
45
+ exports.resolveThresholds = resolveThresholds;
46
+ exports.findRepoRoot = findRepoRoot;
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ exports.PRESETS = Object.freeze({
50
+ conservative: { decision: 0.85, issue: 0.80, assumption: 0.90, taskStatus: 0.90 },
51
+ balanced: { decision: 0.75, issue: 0.70, assumption: 0.80, taskStatus: 0.85 },
52
+ aggressive: { decision: 0.60, issue: 0.55, assumption: 0.70, taskStatus: 0.75 },
53
+ });
54
+ exports.DEFAULT_CONFIG = Object.freeze({
55
+ companion: {
56
+ autoVerify: {
57
+ preTaskDone: true,
58
+ prePush: true,
59
+ postEditPulse: { enabled: true, threshold: 5 },
60
+ autoPublishSpecOnControllerChange: true,
61
+ controllerPaths: [
62
+ '**/controllers/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}',
63
+ '**/api/**/*.{kt,java,py,go,ts,tsx,js,rs,rb,cs}',
64
+ ],
65
+ verifyTimeoutSeconds: 300,
66
+ publishSpecTimeoutSeconds: 300,
67
+ },
68
+ autoWorkbookUpdates: {
69
+ enabled: true,
70
+ preset: 'conservative',
71
+ maxAutoLogsPerTurn: 2,
72
+ maxAutoLogsPerSession: 10,
73
+ undoWindow: 'next-turn',
74
+ thresholds: {
75
+ decision: null,
76
+ issue: null,
77
+ assumption: null,
78
+ taskStatus: null,
79
+ },
80
+ trivialTurnCharCutoff: 600,
81
+ hashRingBufferSize: 32,
82
+ },
83
+ coverageNudges: {
84
+ enabled: true,
85
+ staleBaselineDays: 14,
86
+ fetchMode: 'async',
87
+ },
88
+ authoring: {
89
+ defaultDepth: 'adaptive',
90
+ existingWorkScan: true,
91
+ qualityGate: { enabled: true },
92
+ minViableContextGate: { enabled: true },
93
+ },
94
+ safeMode: false,
95
+ },
96
+ });
97
+ function isPlainObject(v) {
98
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
99
+ }
100
+ function deepMerge(base, override) {
101
+ if (!isPlainObject(override))
102
+ return override === undefined ? base : override;
103
+ const baseObj = (Array.isArray(base) ? [...base] : { ...base });
104
+ for (const key of Object.keys(override)) {
105
+ const b = baseObj[key];
106
+ const o = override[key];
107
+ if (isPlainObject(b) && isPlainObject(o)) {
108
+ baseObj[key] = deepMerge(b, o);
109
+ }
110
+ else {
111
+ baseObj[key] = o;
112
+ }
113
+ }
114
+ return baseObj;
115
+ }
116
+ /**
117
+ * Load `.dezycro/config.json` from the given repo root, deep-merged with
118
+ * DEFAULT_CONFIG. Returns DEFAULT_CONFIG if the file is missing or unparseable.
119
+ */
120
+ function load(repoRoot) {
121
+ const file = path.join(repoRoot, '.dezycro', 'config.json');
122
+ let raw;
123
+ try {
124
+ raw = fs.readFileSync(file, 'utf8');
125
+ }
126
+ catch (err) {
127
+ const e = err;
128
+ if (e.code === 'ENOENT')
129
+ return JSON.parse(JSON.stringify(exports.DEFAULT_CONFIG));
130
+ throw err;
131
+ }
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(raw);
135
+ }
136
+ catch {
137
+ return JSON.parse(JSON.stringify(exports.DEFAULT_CONFIG));
138
+ }
139
+ return deepMerge(JSON.parse(JSON.stringify(exports.DEFAULT_CONFIG)), parsed);
140
+ }
141
+ /**
142
+ * Resolve per-type confidence thresholds for the workbook sweep, honoring
143
+ * `companion.autoWorkbookUpdates.preset` and any numeric overrides under
144
+ * `companion.autoWorkbookUpdates.thresholds.*`.
145
+ */
146
+ function resolveThresholds(config) {
147
+ const ws = config?.companion?.autoWorkbookUpdates || {};
148
+ const presetName = (ws.preset || 'conservative');
149
+ const base = exports.PRESETS[presetName] || exports.PRESETS.conservative;
150
+ const overrides = ws.thresholds || {};
151
+ return {
152
+ decision: typeof overrides.decision === 'number' ? overrides.decision : base.decision,
153
+ issue: typeof overrides.issue === 'number' ? overrides.issue : base.issue,
154
+ assumption: typeof overrides.assumption === 'number' ? overrides.assumption : base.assumption,
155
+ taskStatus: typeof overrides.taskStatus === 'number' ? overrides.taskStatus : base.taskStatus,
156
+ };
157
+ }
158
+ /**
159
+ * Find the nearest ancestor of `startDir` that contains a `.dezycro/`
160
+ * directory, returning that ancestor as the repo root. Returns null if not
161
+ * found before reaching the filesystem root.
162
+ */
163
+ function findRepoRoot(startDir) {
164
+ let dir = path.resolve(startDir || process.cwd());
165
+ for (let i = 0; i < 64; i += 1) {
166
+ if (fs.existsSync(path.join(dir, '.dezycro')))
167
+ return dir;
168
+ const parent = path.dirname(dir);
169
+ if (parent === dir)
170
+ return null;
171
+ dir = parent;
172
+ }
173
+ return null;
174
+ }
175
+ exports._deepMerge = deepMerge;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * controller-match.ts — match file paths against `controllerPaths` globs from
3
+ * `.dezycro/config.json` (TRD §5.2).
4
+ *
5
+ * Minimal glob subset (stdlib only):
6
+ * - `**` zero or more path segments
7
+ * - `*` any chars within a single segment (no slashes)
8
+ * - `?` any single char within a single segment
9
+ * - `{a,b}` alternation (`*.{kt,java}`)
10
+ *
11
+ * Limitations: no character classes, no escaping, forward-slash only.
12
+ */
13
+ export declare function expandAlternation(glob: string): string[];
14
+ export declare function globToRegex(glob: string): RegExp;
15
+ export declare function matchesControllerPath(filePath: string, globs: string[]): boolean;
16
+ export declare function selectControllers(filePaths: string[], globs: string[]): string[];