@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.
- package/dist/agents/agent-registry.d.ts +206 -0
- package/dist/agents/agent-registry.d.ts.map +1 -0
- package/dist/agents/agent-schema.d.ts.map +1 -1
- package/dist/agents/execution-learning.d.ts +223 -0
- package/dist/agents/execution-learning.d.ts.map +1 -0
- package/dist/agents/health-monitor.d.ts +161 -0
- package/dist/agents/health-monitor.d.ts.map +1 -0
- package/dist/agents/index.d.ts +4 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/retry.d.ts +57 -4
- package/dist/agents/retry.d.ts.map +1 -1
- package/dist/backfill/index.d.ts +83 -0
- package/dist/backfill/index.d.ts.map +1 -0
- package/dist/bootstrap.d.ts +1 -1
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6985 -5068
- package/dist/index.js.map +4 -4
- package/dist/intelligence/adaptive-validation.d.ts +151 -0
- package/dist/intelligence/adaptive-validation.d.ts.map +1 -0
- package/dist/intelligence/impact.d.ts +34 -1
- package/dist/intelligence/impact.d.ts.map +1 -1
- package/dist/intelligence/index.d.ts +7 -2
- package/dist/intelligence/index.d.ts.map +1 -1
- package/dist/intelligence/types.d.ts +60 -0
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/internal.d.ts +8 -4
- package/dist/internal.d.ts.map +1 -1
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/retry.d.ts +128 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/nexus/sharing/index.d.ts +48 -2
- package/dist/nexus/sharing/index.d.ts.map +1 -1
- package/dist/sessions/session-enforcement.d.ts.map +1 -1
- package/dist/stats/index.d.ts +1 -0
- package/dist/stats/index.d.ts.map +1 -1
- package/dist/stats/workflow-telemetry.d.ts +89 -0
- package/dist/stats/workflow-telemetry.d.ts.map +1 -0
- package/dist/store/brain-schema.d.ts.map +1 -1
- package/dist/store/converters.d.ts.map +1 -1
- package/dist/store/cross-db-cleanup.d.ts +93 -0
- package/dist/store/cross-db-cleanup.d.ts.map +1 -0
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/migration-sqlite.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/task-store.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +18 -3
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/validation-schemas.d.ts +32 -0
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/tasks/add.d.ts +10 -1
- package/dist/tasks/add.d.ts.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/enforcement.d.ts +22 -0
- package/dist/tasks/enforcement.d.ts.map +1 -0
- package/dist/tasks/epic-enforcement.d.ts +199 -0
- package/dist/tasks/epic-enforcement.d.ts.map +1 -0
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/pipeline-stage.d.ts +181 -0
- package/dist/tasks/pipeline-stage.d.ts.map +1 -0
- package/dist/tasks/update.d.ts +2 -0
- package/dist/tasks/update.d.ts.map +1 -1
- package/migrations/drizzle-brain/20260321000001_t033-brain-indexes/migration.sql +12 -0
- package/migrations/drizzle-brain/20260321000001_t033-brain-indexes/snapshot.json +1232 -0
- package/migrations/drizzle-tasks/20260321000000_t033-connection-health/migration.sql +518 -0
- package/migrations/drizzle-tasks/20260321000000_t033-connection-health/snapshot.json +4312 -0
- package/migrations/drizzle-tasks/20260321000002_t060-pipeline-stage-binding/migration.sql +82 -0
- package/migrations/drizzle-tasks/20260321000002_t060-pipeline-stage-binding/snapshot.json +9 -0
- package/package.json +5 -5
- package/schemas/config.schema.json +37 -1547
- package/src/__tests__/sharing.test.ts +24 -0
- package/src/agents/__tests__/agent-registry.test.ts +351 -0
- package/src/agents/__tests__/execution-learning.test.ts +684 -0
- package/src/agents/__tests__/health-monitor.test.ts +332 -0
- package/src/agents/__tests__/registry.test.ts +30 -2
- package/src/agents/agent-registry.ts +394 -0
- package/src/agents/agent-schema.ts +5 -0
- package/src/agents/execution-learning.ts +675 -0
- package/src/agents/health-monitor.ts +279 -0
- package/src/agents/index.ts +37 -1
- package/src/agents/retry.ts +57 -4
- package/src/backfill/index.ts +309 -0
- package/src/bootstrap.ts +1 -1
- package/src/config.ts +126 -0
- package/src/index.ts +8 -1
- package/src/intelligence/__tests__/adaptive-validation.test.ts +694 -0
- package/src/intelligence/__tests__/impact.test.ts +165 -1
- package/src/intelligence/adaptive-validation.ts +764 -0
- package/src/intelligence/impact.ts +203 -0
- package/src/intelligence/index.ts +19 -0
- package/src/intelligence/types.ts +76 -0
- package/src/internal.ts +39 -0
- package/src/lib/__tests__/retry.test.ts +321 -0
- package/src/lib/index.ts +16 -0
- package/src/lib/retry.ts +224 -0
- package/src/lifecycle/__tests__/chain-store.test.ts +7 -0
- package/src/lifecycle/__tests__/tessera-engine.test.ts +52 -0
- package/src/nexus/sharing/index.ts +142 -2
- package/src/sessions/__tests__/session-edge-cases.test.ts +24 -1
- package/src/sessions/session-enforcement.ts +13 -2
- package/src/stats/index.ts +7 -0
- package/src/stats/workflow-telemetry.ts +502 -0
- package/src/store/__tests__/migration-safety.test.ts +3 -0
- package/src/store/__tests__/session-store.test.ts +132 -1
- package/src/store/__tests__/task-store.test.ts +22 -1
- package/src/store/__tests__/test-db-helper.ts +29 -2
- package/src/store/brain-schema.ts +4 -1
- package/src/store/converters.ts +2 -0
- package/src/store/cross-db-cleanup.ts +192 -0
- package/src/store/db-helpers.ts +2 -0
- package/src/store/migration-sqlite.ts +6 -0
- package/src/store/sqlite-data-accessor.ts +20 -28
- package/src/store/sqlite.ts +14 -2
- package/src/store/task-store.ts +6 -0
- package/src/store/tasks-schema.ts +59 -20
- package/src/tasks/__tests__/add.test.ts +16 -0
- package/src/tasks/__tests__/complete-unblocks.test.ts +10 -1
- package/src/tasks/__tests__/complete.test.ts +11 -2
- package/src/tasks/__tests__/epic-enforcement.test.ts +909 -0
- package/src/tasks/__tests__/minimal-test.test.ts +28 -0
- package/src/tasks/__tests__/pipeline-stage.test.ts +403 -0
- package/src/tasks/__tests__/update.test.ts +40 -6
- package/src/tasks/add.ts +128 -2
- package/src/tasks/complete.ts +29 -17
- package/src/tasks/enforcement.ts +127 -0
- package/src/tasks/epic-enforcement.ts +364 -0
- package/src/tasks/index.ts +1 -0
- package/src/tasks/pipeline-stage.ts +293 -0
- package/src/tasks/update.ts +62 -0
- package/templates/config.template.json +34 -111
- 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
|
-
*
|
|
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
|
|
249
|
+
// Bootstrap — used by postinstall and self-update
|
|
243
250
|
export { bootstrapGlobalCleo } from './bootstrap.js';
|
|
244
251
|
export type {
|
|
245
252
|
AdminAPI,
|