@cleocode/core 2026.3.57 → 2026.3.59

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 (136) hide show
  1. package/dist/agents/agent-registry.d.ts +206 -0
  2. package/dist/agents/agent-registry.d.ts.map +1 -0
  3. package/dist/agents/agent-schema.d.ts.map +1 -1
  4. package/dist/agents/execution-learning.d.ts +223 -0
  5. package/dist/agents/execution-learning.d.ts.map +1 -0
  6. package/dist/agents/health-monitor.d.ts +161 -0
  7. package/dist/agents/health-monitor.d.ts.map +1 -0
  8. package/dist/agents/index.d.ts +4 -1
  9. package/dist/agents/index.d.ts.map +1 -1
  10. package/dist/agents/retry.d.ts +57 -4
  11. package/dist/agents/retry.d.ts.map +1 -1
  12. package/dist/backfill/index.d.ts +83 -0
  13. package/dist/backfill/index.d.ts.map +1 -0
  14. package/dist/bootstrap.d.ts +1 -1
  15. package/dist/config.d.ts +47 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6985 -5068
  20. package/dist/index.js.map +4 -4
  21. package/dist/intelligence/adaptive-validation.d.ts +151 -0
  22. package/dist/intelligence/adaptive-validation.d.ts.map +1 -0
  23. package/dist/intelligence/impact.d.ts +34 -1
  24. package/dist/intelligence/impact.d.ts.map +1 -1
  25. package/dist/intelligence/index.d.ts +7 -2
  26. package/dist/intelligence/index.d.ts.map +1 -1
  27. package/dist/intelligence/types.d.ts +60 -0
  28. package/dist/intelligence/types.d.ts.map +1 -1
  29. package/dist/internal.d.ts +8 -4
  30. package/dist/internal.d.ts.map +1 -1
  31. package/dist/lib/index.d.ts +10 -0
  32. package/dist/lib/index.d.ts.map +1 -0
  33. package/dist/lib/retry.d.ts +128 -0
  34. package/dist/lib/retry.d.ts.map +1 -0
  35. package/dist/nexus/sharing/index.d.ts +48 -2
  36. package/dist/nexus/sharing/index.d.ts.map +1 -1
  37. package/dist/sessions/session-enforcement.d.ts.map +1 -1
  38. package/dist/stats/index.d.ts +1 -0
  39. package/dist/stats/index.d.ts.map +1 -1
  40. package/dist/stats/workflow-telemetry.d.ts +89 -0
  41. package/dist/stats/workflow-telemetry.d.ts.map +1 -0
  42. package/dist/store/brain-schema.d.ts.map +1 -1
  43. package/dist/store/converters.d.ts.map +1 -1
  44. package/dist/store/cross-db-cleanup.d.ts +93 -0
  45. package/dist/store/cross-db-cleanup.d.ts.map +1 -0
  46. package/dist/store/db-helpers.d.ts.map +1 -1
  47. package/dist/store/migration-sqlite.d.ts.map +1 -1
  48. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  49. package/dist/store/sqlite.d.ts.map +1 -1
  50. package/dist/store/task-store.d.ts.map +1 -1
  51. package/dist/store/tasks-schema.d.ts +18 -3
  52. package/dist/store/tasks-schema.d.ts.map +1 -1
  53. package/dist/store/validation-schemas.d.ts +32 -0
  54. package/dist/store/validation-schemas.d.ts.map +1 -1
  55. package/dist/tasks/add.d.ts +10 -1
  56. package/dist/tasks/add.d.ts.map +1 -1
  57. package/dist/tasks/complete.d.ts.map +1 -1
  58. package/dist/tasks/enforcement.d.ts +22 -0
  59. package/dist/tasks/enforcement.d.ts.map +1 -0
  60. package/dist/tasks/epic-enforcement.d.ts +199 -0
  61. package/dist/tasks/epic-enforcement.d.ts.map +1 -0
  62. package/dist/tasks/index.d.ts +1 -1
  63. package/dist/tasks/index.d.ts.map +1 -1
  64. package/dist/tasks/pipeline-stage.d.ts +181 -0
  65. package/dist/tasks/pipeline-stage.d.ts.map +1 -0
  66. package/dist/tasks/update.d.ts +2 -0
  67. package/dist/tasks/update.d.ts.map +1 -1
  68. package/migrations/drizzle-brain/20260321000001_t033-brain-indexes/migration.sql +12 -0
  69. package/migrations/drizzle-brain/20260321000001_t033-brain-indexes/snapshot.json +1232 -0
  70. package/migrations/drizzle-tasks/20260321000000_t033-connection-health/migration.sql +518 -0
  71. package/migrations/drizzle-tasks/20260321000000_t033-connection-health/snapshot.json +4312 -0
  72. package/migrations/drizzle-tasks/20260321000002_t060-pipeline-stage-binding/migration.sql +82 -0
  73. package/migrations/drizzle-tasks/20260321000002_t060-pipeline-stage-binding/snapshot.json +9 -0
  74. package/package.json +5 -5
  75. package/schemas/config.schema.json +37 -1547
  76. package/src/__tests__/sharing.test.ts +24 -0
  77. package/src/agents/__tests__/agent-registry.test.ts +351 -0
  78. package/src/agents/__tests__/execution-learning.test.ts +684 -0
  79. package/src/agents/__tests__/health-monitor.test.ts +332 -0
  80. package/src/agents/__tests__/registry.test.ts +30 -2
  81. package/src/agents/agent-registry.ts +394 -0
  82. package/src/agents/agent-schema.ts +5 -0
  83. package/src/agents/execution-learning.ts +675 -0
  84. package/src/agents/health-monitor.ts +279 -0
  85. package/src/agents/index.ts +37 -1
  86. package/src/agents/retry.ts +57 -4
  87. package/src/backfill/index.ts +309 -0
  88. package/src/bootstrap.ts +1 -1
  89. package/src/config.ts +126 -0
  90. package/src/index.ts +8 -1
  91. package/src/intelligence/__tests__/adaptive-validation.test.ts +694 -0
  92. package/src/intelligence/__tests__/impact.test.ts +165 -1
  93. package/src/intelligence/adaptive-validation.ts +764 -0
  94. package/src/intelligence/impact.ts +203 -0
  95. package/src/intelligence/index.ts +19 -0
  96. package/src/intelligence/types.ts +76 -0
  97. package/src/internal.ts +39 -0
  98. package/src/lib/__tests__/retry.test.ts +321 -0
  99. package/src/lib/index.ts +16 -0
  100. package/src/lib/retry.ts +224 -0
  101. package/src/lifecycle/__tests__/chain-store.test.ts +7 -0
  102. package/src/lifecycle/__tests__/tessera-engine.test.ts +52 -0
  103. package/src/nexus/sharing/index.ts +142 -2
  104. package/src/sessions/__tests__/session-edge-cases.test.ts +24 -1
  105. package/src/sessions/session-enforcement.ts +13 -2
  106. package/src/stats/index.ts +7 -0
  107. package/src/stats/workflow-telemetry.ts +502 -0
  108. package/src/store/__tests__/migration-safety.test.ts +3 -0
  109. package/src/store/__tests__/session-store.test.ts +132 -1
  110. package/src/store/__tests__/task-store.test.ts +22 -1
  111. package/src/store/__tests__/test-db-helper.ts +29 -2
  112. package/src/store/brain-schema.ts +4 -1
  113. package/src/store/converters.ts +2 -0
  114. package/src/store/cross-db-cleanup.ts +192 -0
  115. package/src/store/db-helpers.ts +2 -0
  116. package/src/store/migration-sqlite.ts +6 -0
  117. package/src/store/sqlite-data-accessor.ts +20 -28
  118. package/src/store/sqlite.ts +14 -2
  119. package/src/store/task-store.ts +6 -0
  120. package/src/store/tasks-schema.ts +59 -20
  121. package/src/tasks/__tests__/add.test.ts +16 -0
  122. package/src/tasks/__tests__/complete-unblocks.test.ts +10 -1
  123. package/src/tasks/__tests__/complete.test.ts +11 -2
  124. package/src/tasks/__tests__/epic-enforcement.test.ts +909 -0
  125. package/src/tasks/__tests__/minimal-test.test.ts +28 -0
  126. package/src/tasks/__tests__/pipeline-stage.test.ts +403 -0
  127. package/src/tasks/__tests__/update.test.ts +40 -6
  128. package/src/tasks/add.ts +128 -2
  129. package/src/tasks/complete.ts +29 -17
  130. package/src/tasks/enforcement.ts +127 -0
  131. package/src/tasks/epic-enforcement.ts +364 -0
  132. package/src/tasks/index.ts +1 -0
  133. package/src/tasks/pipeline-stage.ts +293 -0
  134. package/src/tasks/update.ts +62 -0
  135. package/templates/config.template.json +34 -111
  136. package/templates/global-config.template.json +24 -40
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Backfill module: retroactively add AC and verification metadata to
3
+ * existing tasks that were created before T058 (AC enforcement) and
4
+ * T061 (verification gate auto-init).
5
+ *
6
+ * Usage:
7
+ * backfillTasks(root, { dryRun: true }) -- preview only
8
+ * backfillTasks(root, {}) -- apply changes
9
+ * backfillTasks(root, { rollback: true }) -- revert backfill
10
+ *
11
+ * @packageDocumentation
12
+ * @epic T056
13
+ * @task T066
14
+ */
15
+
16
+ import type { Task, TaskVerification } from '@cleocode/contracts';
17
+ import { getAccessor } from '../store/data-accessor.js';
18
+ import { buildDefaultVerification } from '../tasks/add.js';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Types
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** Options for backfillTasks(). */
25
+ export interface BackfillOptions {
26
+ /** Preview changes only — do not modify the database. Default: false. */
27
+ dryRun?: boolean;
28
+ /** Revert a previous backfill (remove auto-generated AC + verification). Default: false. */
29
+ rollback?: boolean;
30
+ /** Restrict backfill to specific task IDs. Default: all tasks. */
31
+ taskIds?: string[];
32
+ }
33
+
34
+ /** Summary of what was (or would be) changed for a single task. */
35
+ export interface BackfillTaskChange {
36
+ taskId: string;
37
+ title: string;
38
+ addedAc: boolean;
39
+ generatedAc: string[];
40
+ addedVerification: boolean;
41
+ addedNote: boolean;
42
+ /** Populated during rollback: fields that were cleared. */
43
+ rolledBack?: string[];
44
+ }
45
+
46
+ /** Overall result returned by backfillTasks(). */
47
+ export interface BackfillResult {
48
+ dryRun: boolean;
49
+ rollback: boolean;
50
+ tasksScanned: number;
51
+ tasksChanged: number;
52
+ acAdded: number;
53
+ verificationAdded: number;
54
+ changes: BackfillTaskChange[];
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // AC generation (heuristic — no LLM)
59
+ // ---------------------------------------------------------------------------
60
+
61
+ /**
62
+ * Generate 3 baseline acceptance criteria from a task description.
63
+ * Uses simple text analysis — no LLM required.
64
+ *
65
+ * @remarks
66
+ * Extracts action verbs from the title + description to produce contextually
67
+ * relevant criteria. Falls back to generic criteria when no verbs match.
68
+ *
69
+ * @param title - The task title
70
+ * @param description - The task description
71
+ * @returns Array of 3 acceptance criteria strings
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * generateAcFromDescription('Fix login bug', 'Users cannot log in');
76
+ * // => ['The defect is resolved...', 'No breaking changes...', 'Changes verified...']
77
+ * ```
78
+ */
79
+ export function generateAcFromDescription(title: string, description: string): string[] {
80
+ const text = `${title} ${description}`.toLowerCase();
81
+
82
+ // Extract action verbs from the task description to build specific criteria
83
+ const actionPatterns: Array<{ pattern: RegExp; criterion: string }> = [
84
+ {
85
+ pattern: /\b(implement|create|build|add|write)\b/,
86
+ criterion: 'Implementation is complete and matches the described requirements',
87
+ },
88
+ {
89
+ pattern: /\b(test|spec|verify|check|validate)\b/,
90
+ criterion: 'All tests pass and edge cases are covered',
91
+ },
92
+ {
93
+ pattern: /\b(fix|repair|resolve|patch|correct)\b/,
94
+ criterion: 'The defect is resolved and does not regress',
95
+ },
96
+ {
97
+ pattern: /\b(refactor|clean|reorganize|restructure)\b/,
98
+ criterion: 'Refactoring does not change observable behaviour',
99
+ },
100
+ {
101
+ pattern: /\b(document|doc|spec|describe)\b/,
102
+ criterion: 'Documentation is accurate and complete',
103
+ },
104
+ {
105
+ pattern: /\b(migrate|move|transfer|convert)\b/,
106
+ criterion: 'Migration is complete and data integrity is preserved',
107
+ },
108
+ {
109
+ pattern: /\b(update|upgrade|bump|change)\b/,
110
+ criterion: 'Update is applied correctly with no breaking changes',
111
+ },
112
+ {
113
+ pattern: /\b(delete|remove|drop|clean)\b/,
114
+ criterion: 'Removal is complete and no orphaned references remain',
115
+ },
116
+ ];
117
+
118
+ const specific: string[] = [];
119
+ for (const { pattern, criterion } of actionPatterns) {
120
+ if (pattern.test(text) && !specific.includes(criterion)) {
121
+ specific.push(criterion);
122
+ if (specific.length >= 1) break; // use the first match for a specific criterion
123
+ }
124
+ }
125
+
126
+ // Always include two generic safety criteria
127
+ const generic = [
128
+ 'Implementation matches the task description with no unintended side effects',
129
+ 'No breaking changes introduced to dependent code or workflows',
130
+ 'Changes verified manually or via automated tests',
131
+ ];
132
+
133
+ // Merge: one specific (if found) + two generic, total 3
134
+ if (specific.length > 0) {
135
+ return [specific[0]!, generic[1]!, generic[2]!];
136
+ }
137
+
138
+ return generic;
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Rollback marker detection
143
+ // ---------------------------------------------------------------------------
144
+
145
+ const BACKFILL_NOTE_MARKER = '[T066-backfill]';
146
+
147
+ function isBackfilledNote(note: string): boolean {
148
+ return note.includes(BACKFILL_NOTE_MARKER);
149
+ }
150
+
151
+ function isBackfilledTask(task: Task): boolean {
152
+ return (task.notes ?? []).some(isBackfilledNote);
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Core backfill logic
157
+ // ---------------------------------------------------------------------------
158
+
159
+ /**
160
+ * Retroactively populate AC and verification metadata for tasks that lack them.
161
+ *
162
+ * @remarks
163
+ * In dry-run mode, computes changes without writing to the database.
164
+ * Backfilled tasks are tagged with a note so they can be identified and
165
+ * optionally rolled back later.
166
+ *
167
+ * @param projectRoot - Project root directory (cwd for CLEO operations)
168
+ * @param options - Backfill options (dryRun, rollback, taskIds)
169
+ * @returns Summary of changes applied (or previewed in dry-run mode)
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * const result = await backfillTasks('/my/project', { dryRun: true });
174
+ * console.log(result.changed); // number of tasks that would be modified
175
+ * ```
176
+ */
177
+ export async function backfillTasks(
178
+ projectRoot: string,
179
+ options: BackfillOptions = {},
180
+ ): Promise<BackfillResult> {
181
+ const { dryRun = false, rollback = false, taskIds } = options;
182
+ const now = new Date().toISOString();
183
+
184
+ const accessor = await getAccessor(projectRoot);
185
+ const { tasks } = await accessor.queryTasks({});
186
+
187
+ // Filter to requested task IDs (if supplied)
188
+ const candidates = taskIds ? tasks.filter((t) => taskIds.includes(t.id)) : tasks;
189
+
190
+ const changes: BackfillTaskChange[] = [];
191
+ let acAdded = 0;
192
+ let verificationAdded = 0;
193
+
194
+ for (const task of candidates) {
195
+ if (rollback) {
196
+ // --- Rollback mode: undo what backfill added ---
197
+ if (!isBackfilledTask(task)) continue;
198
+
199
+ const rolledBack: string[] = [];
200
+ const updates: import('@cleocode/contracts').TaskFieldUpdates & {
201
+ notesJson?: string;
202
+ acceptanceJson?: string;
203
+ verificationJson?: string | null;
204
+ } = { updatedAt: now };
205
+
206
+ // Remove backfill notes
207
+ const cleanedNotes = (task.notes ?? []).filter((n) => !isBackfilledNote(n));
208
+ if (cleanedNotes.length !== (task.notes ?? []).length) {
209
+ updates.notesJson = JSON.stringify(cleanedNotes);
210
+ rolledBack.push('note');
211
+ }
212
+
213
+ // If task has the backfill-generated AC marker in notes, we can infer the
214
+ // AC was auto-generated. We clear it back to empty to revert.
215
+ // NOTE: We only clear AC if it was added by backfill (the note marker is present).
216
+ if ((task.acceptance ?? []).length > 0) {
217
+ updates.acceptanceJson = JSON.stringify([]);
218
+ rolledBack.push('ac');
219
+ }
220
+
221
+ // Clear verification if it was set by backfill
222
+ if (task.verification) {
223
+ updates.verificationJson = null;
224
+ rolledBack.push('verification');
225
+ }
226
+
227
+ if (rolledBack.length > 0) {
228
+ changes.push({
229
+ taskId: task.id,
230
+ title: task.title,
231
+ addedAc: false,
232
+ generatedAc: [],
233
+ addedVerification: false,
234
+ addedNote: false,
235
+ rolledBack,
236
+ });
237
+
238
+ if (!dryRun) {
239
+ await accessor.updateTaskFields(task.id, updates);
240
+ }
241
+ }
242
+
243
+ continue;
244
+ }
245
+
246
+ // --- Forward mode: fill missing AC and verification ---
247
+ const needsAc = !task.acceptance || task.acceptance.length === 0;
248
+ const needsVerification = !task.verification;
249
+
250
+ if (!needsAc && !needsVerification) continue;
251
+
252
+ const change: BackfillTaskChange = {
253
+ taskId: task.id,
254
+ title: task.title,
255
+ addedAc: false,
256
+ generatedAc: [],
257
+ addedVerification: false,
258
+ addedNote: false,
259
+ };
260
+
261
+ const updates: import('@cleocode/contracts').TaskFieldUpdates & {
262
+ notesJson?: string;
263
+ acceptanceJson?: string;
264
+ verificationJson?: string | null;
265
+ } = { updatedAt: now };
266
+
267
+ if (needsAc) {
268
+ const generated = generateAcFromDescription(task.title, task.description ?? '');
269
+ updates.acceptanceJson = JSON.stringify(generated);
270
+ change.addedAc = true;
271
+ change.generatedAc = generated;
272
+ acAdded++;
273
+ }
274
+
275
+ if (needsVerification) {
276
+ const verification: TaskVerification = buildDefaultVerification(now);
277
+ updates.verificationJson = JSON.stringify(verification);
278
+ change.addedVerification = true;
279
+ verificationAdded++;
280
+ }
281
+
282
+ // Add a backfill note so rollback can identify what was changed
283
+ const existingNotes = task.notes ?? [];
284
+ const backfillNote = `${BACKFILL_NOTE_MARKER} auto-backfilled at ${now}: ${[
285
+ needsAc ? 'ac' : null,
286
+ needsVerification ? 'verification' : null,
287
+ ]
288
+ .filter(Boolean)
289
+ .join(', ')}`;
290
+ updates.notesJson = JSON.stringify([...existingNotes, backfillNote]);
291
+ change.addedNote = true;
292
+
293
+ changes.push(change);
294
+
295
+ if (!dryRun) {
296
+ await accessor.updateTaskFields(task.id, updates);
297
+ }
298
+ }
299
+
300
+ return {
301
+ dryRun,
302
+ rollback,
303
+ tasksScanned: candidates.length,
304
+ tasksChanged: changes.length,
305
+ acAdded: rollback ? 0 : acAdded,
306
+ verificationAdded: rollback ? 0 : verificationAdded,
307
+ changes,
308
+ };
309
+ }
package/src/bootstrap.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * - `cleo install-global` CLI command
7
7
  *
8
8
  * This is the single source of truth for global setup operations.
9
- * Neither postinstall nor install-global should duplicate this logic.
9
+ * This is the SSoT — postinstall and self-update both delegate here.
10
10
  *
11
11
  * @task T5267
12
12
  */
package/src/config.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * @epic T4454
7
7
  * @task T4458
8
+ * @task T067
8
9
  */
9
10
 
10
11
  import { existsSync } from 'node:fs';
@@ -316,3 +317,128 @@ export async function setConfigValue(
316
317
 
317
318
  return { key, value: parsedValue, scope: opts?.global ? 'global' : 'project' };
318
319
  }
320
+
321
+ // ---------------------------------------------------------------------------
322
+ // Strictness Presets (@task T067)
323
+ // ---------------------------------------------------------------------------
324
+
325
+ /** Valid preset names. */
326
+ export type StrictnessPreset = 'strict' | 'standard' | 'minimal';
327
+
328
+ /** A preset definition: the config keys it sets and a human description. */
329
+ export interface PresetDefinition {
330
+ /** Short summary of what this preset enforces. */
331
+ description: string;
332
+ /** Flat dot-notation key/value pairs applied to project config. */
333
+ values: Record<string, unknown>;
334
+ }
335
+
336
+ /**
337
+ * All three strictness presets.
338
+ *
339
+ * strict — block on missing AC, require sessions, enforce lifecycle pipeline
340
+ * standard — warn on missing AC, optional sessions, advisory pipeline
341
+ * minimal — no AC checking, no session requirement, lifecycle off
342
+ */
343
+ export const STRICTNESS_PRESETS: Record<StrictnessPreset, PresetDefinition> = {
344
+ strict: {
345
+ description:
346
+ 'Block on missing acceptance criteria, require sessions, enforce lifecycle pipeline.',
347
+ values: {
348
+ 'session.autoStart': false,
349
+ 'session.requireNotes': true,
350
+ 'session.multiSession': false,
351
+ 'enforcement.acceptance.mode': 'block',
352
+ 'lifecycle.mode': 'strict',
353
+ },
354
+ },
355
+ standard: {
356
+ description:
357
+ 'Warn on missing acceptance criteria, optional sessions, advisory lifecycle pipeline.',
358
+ values: {
359
+ 'session.autoStart': false,
360
+ 'session.requireNotes': false,
361
+ 'session.multiSession': true,
362
+ 'enforcement.acceptance.mode': 'warn',
363
+ 'lifecycle.mode': 'advisory',
364
+ },
365
+ },
366
+ minimal: {
367
+ description: 'No acceptance criteria checking, no session requirement, lifecycle pipeline off.',
368
+ values: {
369
+ 'session.autoStart': false,
370
+ 'session.requireNotes': false,
371
+ 'session.multiSession': true,
372
+ 'enforcement.acceptance.mode': 'off',
373
+ 'lifecycle.mode': 'off',
374
+ },
375
+ },
376
+ };
377
+
378
+ /** Result of applying a preset. */
379
+ export interface ApplyPresetResult {
380
+ preset: StrictnessPreset;
381
+ description: string;
382
+ applied: Record<string, unknown>;
383
+ scope: 'project' | 'global';
384
+ }
385
+
386
+ /**
387
+ * Apply a strictness preset to the project (or global) config.
388
+ * Merges preset values over existing config — keys not covered by the preset
389
+ * are preserved unchanged.
390
+ * Idempotent: applying the same preset twice yields the same config.
391
+ *
392
+ * @task T067
393
+ */
394
+ export async function applyStrictnessPreset(
395
+ preset: StrictnessPreset,
396
+ cwd?: string,
397
+ opts?: { global?: boolean },
398
+ ): Promise<ApplyPresetResult> {
399
+ const definition = STRICTNESS_PRESETS[preset];
400
+ const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
401
+
402
+ // Ensure config file exists
403
+ if (!existsSync(configPath)) {
404
+ const dir = dirname(configPath);
405
+ await mkdir(dir, { recursive: true });
406
+ await writeFile(configPath, '{}', 'utf-8');
407
+ }
408
+
409
+ const config = (await readJson<Record<string, unknown>>(configPath)) ?? {};
410
+
411
+ // Apply each preset key via dot-notation setter
412
+ for (const [key, value] of Object.entries(definition.values)) {
413
+ setNestedValue(config, key, value);
414
+ }
415
+
416
+ await saveJson(configPath, config);
417
+
418
+ return {
419
+ preset,
420
+ description: definition.description,
421
+ applied: definition.values,
422
+ scope: opts?.global ? 'global' : 'project',
423
+ };
424
+ }
425
+
426
+ /**
427
+ * List all available presets with their descriptions and values.
428
+ * Used by the CLI help output.
429
+ *
430
+ * @task T067
431
+ */
432
+ export function listStrictnessPresets(): Array<{
433
+ name: StrictnessPreset;
434
+ description: string;
435
+ values: Record<string, unknown>;
436
+ }> {
437
+ return (Object.entries(STRICTNESS_PRESETS) as Array<[StrictnessPreset, PresetDefinition]>).map(
438
+ ([name, def]) => ({
439
+ name,
440
+ description: def.description,
441
+ values: def.values,
442
+ }),
443
+ );
444
+ }
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export * as coreHooks from './hooks/index.js';
40
40
  export * as inject from './inject/index.js';
41
41
  export * as intelligence from './intelligence/index.js';
42
42
  export * as issue from './issue/index.js';
43
+ export * as lib from './lib/index.js';
43
44
  export * as lifecycle from './lifecycle/index.js';
44
45
  export * as coreMcp from './mcp/index.js';
45
46
  export * as memory from './memory/index.js';
@@ -155,11 +156,17 @@ export { queryAudit } from './audit.js';
155
156
  export { pruneAuditLog } from './audit-prune.js';
156
157
  // Config
157
158
  export {
159
+ type ApplyPresetResult,
160
+ applyStrictnessPreset,
158
161
  getConfigValue,
159
162
  getRawConfig,
160
163
  getRawConfigValue,
164
+ listStrictnessPresets,
161
165
  loadConfig,
166
+ type PresetDefinition,
162
167
  parseConfigValue,
168
+ STRICTNESS_PRESETS,
169
+ type StrictnessPreset,
163
170
  setConfigValue,
164
171
  } from './config.js';
165
172
  // Constants
@@ -239,7 +246,7 @@ export {
239
246
  // Adapter manager
240
247
  export { AdapterManager } from './adapters/index.js';
241
248
  export type { BootstrapContext, BootstrapOptions } from './bootstrap.js';
242
- // Bootstrap — used by postinstall and install-global
249
+ // Bootstrap — used by postinstall and self-update
243
250
  export { bootstrapGlobalCleo } from './bootstrap.js';
244
251
  export type {
245
252
  AdminAPI,