analyzthis_design 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOW-TO-USE.md +436 -0
- package/README.md +29 -13
- package/agents/cards/evolve-check.md +38 -0
- package/agents/manifests/evolve-check.json +16 -0
- package/dist/HOW-TO-USE.md +15 -3
- package/dist/README.md +29 -13
- package/dist/agents/cards/evolve-check.md +38 -0
- package/dist/agents/manifests/evolve-check.json +16 -0
- package/dist/bin/cli.js +1225 -1
- package/dist/lib/cache.js +111 -1
- package/dist/lib/chunk-executor.js +219 -1
- package/dist/lib/chunk-models.js +228 -1
- package/dist/lib/chunk-planner.js +328 -1
- package/dist/lib/chunk-router.js +66 -1
- package/dist/lib/chunk-run.js +199 -1
- package/dist/lib/chunk-synthesis.js +176 -1
- package/dist/lib/chunk-telemetry.js +88 -1
- package/dist/lib/collect.js +858 -1
- package/dist/lib/cost.js +119 -1
- package/dist/lib/dedup.js +167 -1
- package/dist/lib/deliberation.js +721 -1
- package/dist/lib/design-spec.js +236 -1
- package/dist/lib/evolution-metrics.js +197 -0
- package/dist/lib/evolve.js +361 -1
- package/dist/lib/export.js +77 -1
- package/dist/lib/feedback-submit.js +324 -1
- package/dist/lib/feedback.js +182 -1
- package/dist/lib/host-llm.js +251 -1
- package/dist/lib/install.js +301 -1
- package/dist/lib/knowledge.js +384 -1
- package/dist/lib/lessons.js +217 -1
- package/dist/lib/moodboard.js +563 -1
- package/dist/lib/orchestrator/run.js +935 -1
- package/dist/lib/outcome.js +193 -1
- package/dist/lib/platforms.js +166 -1
- package/dist/lib/provider.js +57 -1
- package/dist/lib/query-expander.js +83 -1
- package/dist/lib/ranker.js +105 -1
- package/dist/lib/reference-pack.js +221 -0
- package/dist/lib/research.js +143 -1
- package/dist/lib/retrieve.js +131 -1
- package/dist/lib/session.js +185 -1
- package/dist/lib/source-discovery.js +486 -1
- package/dist/lib/synthesis.js +155 -1
- package/dist/lib/token-gate.js +46 -1
- package/dist/skills/design-reference/google-fonts.csv +1924 -1924
- package/dist/skills/design-reference/products.csv +162 -162
- package/dist/skills/design-reference/schema.json +159 -0
- package/dist/skills/design-reference/stacks/angular.csv +1 -1
- package/dist/skills/design-reference/stacks/astro.csv +1 -1
- package/dist/skills/design-reference/stacks/laravel.csv +2 -2
- package/dist/skills/design-reference/stacks/threejs.csv +54 -54
- package/dist/skills/design-reference/styles.csv +85 -85
- package/dist/skills/design-reference/typography.csv +75 -74
- package/dist/skills/design-reference/ui-reasoning.csv +1 -1
- package/dist/skills/evolve-check/SKILL.md +106 -0
- package/package.json +8 -8
- package/scripts/validate-csvs.js +197 -0
- package/skills/design-reference/google-fonts.csv +1924 -1924
- package/skills/design-reference/products.csv +162 -162
- package/skills/design-reference/schema.json +159 -0
- package/skills/design-reference/stacks/angular.csv +1 -1
- package/skills/design-reference/stacks/astro.csv +1 -1
- package/skills/design-reference/stacks/laravel.csv +2 -2
- package/skills/design-reference/stacks/threejs.csv +54 -54
- package/skills/design-reference/styles.csv +85 -85
- package/skills/design-reference/typography.csv +75 -74
- package/skills/design-reference/ui-reasoning.csv +1 -1
- package/skills/evolve-check/SKILL.md +106 -0
package/dist/lib/evolve.js
CHANGED
|
@@ -1 +1,361 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var os = require('os');
|
|
6
|
+
var crypto = require('crypto');
|
|
7
|
+
var session = require('./session');
|
|
8
|
+
var lessons = require('./lessons');
|
|
9
|
+
var outcome = require('./outcome');
|
|
10
|
+
|
|
11
|
+
var EVOLUTION_DIR = path.join(os.homedir(), '.analyzthis_design', 'evolution');
|
|
12
|
+
|
|
13
|
+
function ensureEvolutionDir() {
|
|
14
|
+
fs.mkdirSync(EVOLUTION_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function generatePatchId() {
|
|
18
|
+
return crypto.randomBytes(6).toString('hex');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadCard(personaId) {
|
|
22
|
+
var { resolvePackageRoot } = require('./platforms');
|
|
23
|
+
var PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
24
|
+
var cardPath = path.join(PACKAGE_ROOT, 'agents', 'cards', personaId + '.md');
|
|
25
|
+
if (!fs.existsSync(cardPath)) return '';
|
|
26
|
+
return fs.readFileSync(cardPath, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function loadSkill(personaId) {
|
|
30
|
+
var { resolvePackageRoot } = require('./platforms');
|
|
31
|
+
var PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
32
|
+
var skillPath = path.join(PACKAGE_ROOT, 'skills', personaId, 'SKILL.md');
|
|
33
|
+
if (!fs.existsSync(skillPath)) return '';
|
|
34
|
+
return fs.readFileSync(skillPath, 'utf8');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function clusterLessons(lessonList) {
|
|
38
|
+
var clusters = [];
|
|
39
|
+
for (var i = 0; i < lessonList.length; i++) {
|
|
40
|
+
var l = lessonList[i];
|
|
41
|
+
var patternWords = (l.pattern || '').toLowerCase().split(/\W+/).filter(function(w) { return w.length > 3; });
|
|
42
|
+
var matched = false;
|
|
43
|
+
for (var j = 0; j < clusters.length; j++) {
|
|
44
|
+
var c = clusters[j];
|
|
45
|
+
var overlap = patternWords.filter(function(w) {
|
|
46
|
+
return (c.centerWords || []).indexOf(w) !== -1;
|
|
47
|
+
}).length;
|
|
48
|
+
if (overlap >= 2) {
|
|
49
|
+
c.items.push(l);
|
|
50
|
+
matched = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!matched) {
|
|
55
|
+
clusters.push({
|
|
56
|
+
centerWords: patternWords.slice(0, 5),
|
|
57
|
+
items: [l]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return clusters.filter(function(c) { return c.items.length >= 2; });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function proposePromptPatch(personaId, lessonList, dryRun) {
|
|
65
|
+
var clusters = clusterLessons(lessonList);
|
|
66
|
+
if (!clusters.length) return null;
|
|
67
|
+
|
|
68
|
+
var topCluster = clusters.sort(function(a, b) { return b.items.length - a.items.length; })[0];
|
|
69
|
+
var clusterPatterns = topCluster.items.map(function(l) { return l.pattern; }).slice(0, 3);
|
|
70
|
+
var clusterFixes = topCluster.items.map(function(l) { return l.fix; }).slice(0, 3);
|
|
71
|
+
|
|
72
|
+
var card = loadCard(personaId);
|
|
73
|
+
var skill = loadSkill(personaId);
|
|
74
|
+
var newFailurePattern = '- **' + clusterPatterns[0] + '** — ' + clusterFixes.join('; ') + ' (lesson cluster ' + topCluster.items.length + 'x)';
|
|
75
|
+
|
|
76
|
+
var patch = {
|
|
77
|
+
id: generatePatchId(),
|
|
78
|
+
type: 'prompt',
|
|
79
|
+
persona: personaId,
|
|
80
|
+
description: 'Add canonical failure pattern based on ' + topCluster.items.length + ' lessons',
|
|
81
|
+
target_files: ['skills/' + personaId + '/SKILL.md', 'agents/cards/' + personaId + '.md'],
|
|
82
|
+
card_original: card.slice(0, 2000),
|
|
83
|
+
card_addition: newFailurePattern,
|
|
84
|
+
skill_original: skill.slice(0, 2000),
|
|
85
|
+
skill_addition: newFailurePattern,
|
|
86
|
+
dry_run: dryRun
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
ensureEvolutionDir();
|
|
90
|
+
fs.writeFileSync(path.join(EVOLUTION_DIR, patch.id + '.json'), JSON.stringify(patch, null, 2));
|
|
91
|
+
return patch;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function proposeReferenceRow(personaId, lessonList, dryRun) {
|
|
95
|
+
if (!lessonList.length) return null;
|
|
96
|
+
|
|
97
|
+
var fileMap = {
|
|
98
|
+
arjun: 'styles.csv',
|
|
99
|
+
zara: 'colors.csv',
|
|
100
|
+
meera: 'products.csv',
|
|
101
|
+
noor: 'ux-guidelines.csv',
|
|
102
|
+
anuj: 'ux-guidelines.csv',
|
|
103
|
+
priya: 'react-performance.csv',
|
|
104
|
+
raj: 'products.csv'
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
var targetFile = fileMap[personaId];
|
|
108
|
+
if (!targetFile) return null;
|
|
109
|
+
|
|
110
|
+
var sampleLesson = lessonList[0];
|
|
111
|
+
|
|
112
|
+
// Read the actual CSV header so the proposed row matches the file's schema.
|
|
113
|
+
var header = [];
|
|
114
|
+
try {
|
|
115
|
+
var retrieve = require('./retrieve');
|
|
116
|
+
var loaded = retrieve.loadCsv(targetFile);
|
|
117
|
+
header = loaded.header;
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return null; // can't propose a row if we can't read the target file
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Build a row object keyed by the actual header columns.
|
|
123
|
+
// Fill known fields from the lesson; leave others empty.
|
|
124
|
+
var row = {};
|
|
125
|
+
for (var h = 0; h < header.length; h++) {
|
|
126
|
+
var col = header[h].trim();
|
|
127
|
+
row[col] = '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Set No to 'auto' — applyPatch will replace it with the next sequential number.
|
|
131
|
+
if (row['No'] !== undefined) row['No'] = 'auto';
|
|
132
|
+
|
|
133
|
+
// Try to fill common columns from the lesson data.
|
|
134
|
+
// Products.csv has 'Product Type' and 'Key Considerations'.
|
|
135
|
+
// Styles.csv has 'Style Category' and 'Best For'.
|
|
136
|
+
// ux-guidelines.csv and react-performance.csv have 'Category', 'Issue', 'Description', 'Do', 'Don\'t'.
|
|
137
|
+
// Colors.csv has 'Product Type' and 'Notes'.
|
|
138
|
+
var pattern = sampleLesson.pattern || '';
|
|
139
|
+
var fix = sampleLesson.fix || '';
|
|
140
|
+
var taskType = sampleLesson.task_type || 'general';
|
|
141
|
+
var outcome = sampleLesson.outcome || '';
|
|
142
|
+
|
|
143
|
+
if (row['Product Type'] !== undefined) row['Product Type'] = taskType;
|
|
144
|
+
if (row['Category'] !== undefined) row['Category'] = taskType;
|
|
145
|
+
if (row['Style Category'] !== undefined) row['Style Category'] = 'Learned: ' + taskType;
|
|
146
|
+
if (row['Issue'] !== undefined) row['Issue'] = pattern;
|
|
147
|
+
if (row['Description'] !== undefined) row['Description'] = pattern + ' (outcome: ' + outcome + ')';
|
|
148
|
+
if (row['Do'] !== undefined) row['Do'] = fix;
|
|
149
|
+
if (row["Don't"] !== undefined) row["Don't"] = pattern;
|
|
150
|
+
if (row['Key Considerations'] !== undefined) row['Key Considerations'] = fix + ' (outcome: ' + outcome + ')';
|
|
151
|
+
if (row['Best For'] !== undefined) row['Best For'] = taskType;
|
|
152
|
+
if (row['Keywords'] !== undefined) row['Keywords'] = taskType + ', ' + pattern.slice(0, 40);
|
|
153
|
+
if (row['Notes'] !== undefined) row['Notes'] = fix + ' (outcome: ' + outcome + ')';
|
|
154
|
+
if (row['Severity'] !== undefined) row['Severity'] = outcome === 'missed' ? 'High' : 'Medium';
|
|
155
|
+
|
|
156
|
+
var patch = {
|
|
157
|
+
id: generatePatchId(),
|
|
158
|
+
type: 'reference',
|
|
159
|
+
persona: personaId,
|
|
160
|
+
description: 'Propose new reference row in ' + targetFile,
|
|
161
|
+
target_file: 'skills/design-reference/' + targetFile,
|
|
162
|
+
row: row,
|
|
163
|
+
dry_run: dryRun
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
ensureEvolutionDir();
|
|
167
|
+
fs.writeFileSync(path.join(EVOLUTION_DIR, patch.id + '.json'), JSON.stringify(patch, null, 2));
|
|
168
|
+
return patch;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function proposeRouterPatch(outcomes, dryRun) {
|
|
172
|
+
if (!outcomes || !outcomes.length) return [];
|
|
173
|
+
|
|
174
|
+
var byTaskType = {};
|
|
175
|
+
for (var i = 0; i < outcomes.length; i++) {
|
|
176
|
+
var o = outcomes[i];
|
|
177
|
+
if (!byTaskType[o.task_type]) byTaskType[o.task_type] = {};
|
|
178
|
+
if (!byTaskType[o.task_type][o.persona]) byTaskType[o.task_type][o.persona] = { good: 0, bad: 0 };
|
|
179
|
+
if (o.outcome === 'shipped' || o.outcome === 'blocked_correctly' || o.outcome === 'revised') {
|
|
180
|
+
byTaskType[o.task_type][o.persona].good++;
|
|
181
|
+
} else if (o.outcome === 'missed') {
|
|
182
|
+
byTaskType[o.task_type][o.persona].bad++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var patches = [];
|
|
187
|
+
var taskTypes = Object.keys(byTaskType);
|
|
188
|
+
for (var t = 0; t < taskTypes.length; t++) {
|
|
189
|
+
var tt = taskTypes[t];
|
|
190
|
+
var personas = Object.keys(byTaskType[tt]);
|
|
191
|
+
var best = null;
|
|
192
|
+
var bestScore = -1;
|
|
193
|
+
for (var p = 0; p < personas.length; p++) {
|
|
194
|
+
var pers = personas[p];
|
|
195
|
+
var score = byTaskType[tt][pers].good - byTaskType[tt][pers].bad;
|
|
196
|
+
if (score > bestScore) {
|
|
197
|
+
bestScore = score;
|
|
198
|
+
best = pers;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (best) {
|
|
202
|
+
var patch = {
|
|
203
|
+
id: generatePatchId(),
|
|
204
|
+
type: 'router',
|
|
205
|
+
task_type: tt,
|
|
206
|
+
description: 'Route ' + tt + ' primarily to ' + best + ' based on outcomes',
|
|
207
|
+
suggested_route_to: [best],
|
|
208
|
+
dry_run: dryRun
|
|
209
|
+
};
|
|
210
|
+
ensureEvolutionDir();
|
|
211
|
+
fs.writeFileSync(path.join(EVOLUTION_DIR, patch.id + '.json'), JSON.stringify(patch, null, 2));
|
|
212
|
+
patches.push(patch);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return patches;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function collectOutcomes() {
|
|
220
|
+
var projectIds = session.listProjects();
|
|
221
|
+
var outcomes = [];
|
|
222
|
+
for (var i = 0; i < projectIds.length; i++) {
|
|
223
|
+
var pid = projectIds[i];
|
|
224
|
+
var state = session.show({ project: pid });
|
|
225
|
+
if (!state || !state.outcome) continue;
|
|
226
|
+
|
|
227
|
+
var personas = Object.keys(state.persona_outputs || {});
|
|
228
|
+
for (var p = 0; p < personas.length; p++) {
|
|
229
|
+
var pers = personas[p];
|
|
230
|
+
var val = (state.outcome.confirmed && state.outcome.confirmed[pers]) || (state.outcome.inferred && state.outcome.inferred[pers]);
|
|
231
|
+
if (val) {
|
|
232
|
+
outcomes.push({
|
|
233
|
+
project_id: pid,
|
|
234
|
+
persona: pers,
|
|
235
|
+
task_type: state.task_type || '',
|
|
236
|
+
outcome: val.value
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return outcomes;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function runEvolution(opts) {
|
|
245
|
+
opts = opts || {};
|
|
246
|
+
var windowDays = opts.windowDays || 7;
|
|
247
|
+
var dryRun = opts.dryRun !== false;
|
|
248
|
+
|
|
249
|
+
var lessonsResult = lessons.extractAllLessons({ windowDays: windowDays });
|
|
250
|
+
var outcomeResult = outcome.inferAllOutcomes({ windowDays: windowDays });
|
|
251
|
+
|
|
252
|
+
var promptPatches = [];
|
|
253
|
+
var referenceProposals = [];
|
|
254
|
+
var personas = ['arjun', 'meera', 'priya', 'zara', 'noor', 'anuj', 'raj'];
|
|
255
|
+
|
|
256
|
+
for (var i = 0; i < personas.length; i++) {
|
|
257
|
+
var pers = personas[i];
|
|
258
|
+
var list = lessons.loadLessons ? lessons.loadLessons(pers) : [];
|
|
259
|
+
if (!list.length && lessons.retrieveLessons) {
|
|
260
|
+
list = lessons.retrieveLessons({ task: '', personaId: pers, limit: 100 }).lessons;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (list.length >= 5) {
|
|
264
|
+
var promptPatch = proposePromptPatch(pers, list, dryRun);
|
|
265
|
+
if (promptPatch) promptPatches.push(promptPatch);
|
|
266
|
+
|
|
267
|
+
var refPatch = proposeReferenceRow(pers, list, dryRun);
|
|
268
|
+
if (refPatch) referenceProposals.push(refPatch);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
var allOutcomes = collectOutcomes();
|
|
273
|
+
var routerPatches = proposeRouterPatch(allOutcomes, dryRun);
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
extracted_lessons: lessonsResult.extracted,
|
|
277
|
+
inferred_outcomes: outcomeResult.inferred,
|
|
278
|
+
prompt_patches: promptPatches,
|
|
279
|
+
reference_proposals: referenceProposals,
|
|
280
|
+
router_patches: routerPatches,
|
|
281
|
+
dry_run: dryRun
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function applyPatch(opts) {
|
|
286
|
+
opts = opts || {};
|
|
287
|
+
var patchId = opts.patchId;
|
|
288
|
+
var dryRun = !!opts.dryRun;
|
|
289
|
+
if (!patchId) throw new Error('--apply requires a patch id');
|
|
290
|
+
|
|
291
|
+
var file = path.join(EVOLUTION_DIR, patchId + '.json');
|
|
292
|
+
if (!fs.existsSync(file)) throw new Error('Patch not found: ' + patchId);
|
|
293
|
+
|
|
294
|
+
var patch = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
295
|
+
var { resolvePackageRoot } = require('./platforms');
|
|
296
|
+
var PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
297
|
+
|
|
298
|
+
if (patch.type === 'prompt') {
|
|
299
|
+
var targetFile = path.join(PACKAGE_ROOT, 'skills', patch.persona, 'SKILL.md');
|
|
300
|
+
var existing = fs.existsSync(targetFile) ? fs.readFileSync(targetFile, 'utf8') : '';
|
|
301
|
+
var insertionPoint = existing.indexOf('## Failure modes to avoid');
|
|
302
|
+
if (insertionPoint === -1) insertionPoint = existing.length;
|
|
303
|
+
var updated = existing.slice(0, insertionPoint) + '\n\n## Canonical failure pattern (auto-suggested)\n\n' + patch.skill_addition + '\n\n' + existing.slice(insertionPoint);
|
|
304
|
+
if (!dryRun) fs.writeFileSync(targetFile, updated);
|
|
305
|
+
return { applied: !dryRun, targetFile, preview: dryRun ? updated : null };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (patch.type === 'reference') {
|
|
309
|
+
var refFile = path.join(PACKAGE_ROOT, patch.target_file);
|
|
310
|
+
var row = patch.row;
|
|
311
|
+
|
|
312
|
+
// Read the actual header to ensure column alignment and get the next No.
|
|
313
|
+
var header = [];
|
|
314
|
+
var existingRows = [];
|
|
315
|
+
try {
|
|
316
|
+
var retrieve = require('./retrieve');
|
|
317
|
+
var loaded = retrieve.loadCsv(path.basename(refFile));
|
|
318
|
+
header = loaded.header;
|
|
319
|
+
existingRows = loaded.rows;
|
|
320
|
+
} catch (e) {
|
|
321
|
+
return { applied: false, error: 'Cannot read target CSV: ' + e.message };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Build the CSV line in header order, with proper quoting.
|
|
325
|
+
var nextNo = existingRows.length + 1;
|
|
326
|
+
var values = [];
|
|
327
|
+
for (var h = 0; h < header.length; h++) {
|
|
328
|
+
var col = header[h].trim();
|
|
329
|
+
var val = row[col] !== undefined ? row[col] : '';
|
|
330
|
+
if (val === 'auto') val = String(nextNo);
|
|
331
|
+
values.push(val);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Quote any value containing commas, quotes, or newlines.
|
|
335
|
+
var quotedValues = values.map(function(v) {
|
|
336
|
+
if (v.indexOf(',') !== -1 || v.indexOf('"') !== -1 || v.indexOf('\n') !== -1) {
|
|
337
|
+
return '"' + v.replace(/"/g, '""') + '"';
|
|
338
|
+
}
|
|
339
|
+
return v;
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
var line = quotedValues.join(',') + '\n';
|
|
343
|
+
if (!dryRun) fs.appendFileSync(refFile, line);
|
|
344
|
+
return { applied: !dryRun, targetFile: refFile, preview: dryRun ? line : null };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (patch.type === 'router') {
|
|
348
|
+
return { applied: false, message: 'Router patches require manual review. Suggested route_to for ' + patch.task_type + ': ' + patch.suggested_route_to.join(', ') };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { applied: false, message: 'Unknown patch type' };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
runEvolution: runEvolution,
|
|
356
|
+
proposePromptPatch: proposePromptPatch,
|
|
357
|
+
proposeReferenceRow: proposeReferenceRow,
|
|
358
|
+
proposeRouterPatch: proposeRouterPatch,
|
|
359
|
+
applyPatch: applyPatch,
|
|
360
|
+
EVOLUTION_DIR: EVOLUTION_DIR
|
|
361
|
+
};
|
package/dist/lib/export.js
CHANGED
|
@@ -1 +1,77 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LoRA readiness (Phase 5 of the efficiency plan) — export hook only, no
|
|
5
|
+
* training/serving in this release. Writes JSONL training pairs from session
|
|
6
|
+
* state where a persona's output was explicitly marked accepted (see
|
|
7
|
+
* `session.markAccepted`). Schema: { system_card, digest, user, assistant }.
|
|
8
|
+
*
|
|
9
|
+
* Not run automatically — call `npx analyzthis_design export-training --persona <id>`
|
|
10
|
+
* once you have accepted runs to harvest.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
const session = require('./session');
|
|
17
|
+
|
|
18
|
+
const { resolvePackageRoot } = require('./platforms');
|
|
19
|
+
const PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
20
|
+
|
|
21
|
+
const DEFAULT_OUTPUT_DIR = path.join(os.homedir(), '.analyzthis_design', 'training');
|
|
22
|
+
|
|
23
|
+
function loadCard(personaId) {
|
|
24
|
+
const cardPath = path.join(PACKAGE_ROOT, 'agents', 'cards', `${personaId}.md`);
|
|
25
|
+
if (!fs.existsSync(cardPath)) return '';
|
|
26
|
+
return fs.readFileSync(cardPath, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {{ persona: string, project?: string, all?: boolean, output?: string }} opts
|
|
31
|
+
* @returns {{ pairs: number, filePath: string, projects: string[] }}
|
|
32
|
+
*/
|
|
33
|
+
function exportTraining({ persona, project, all = false, output } = {}) {
|
|
34
|
+
if (!persona) throw new Error('--persona is required');
|
|
35
|
+
|
|
36
|
+
const projectIds = all ? session.listProjects() : [project || session.getProjectId()];
|
|
37
|
+
const card = loadCard(persona);
|
|
38
|
+
const pairs = [];
|
|
39
|
+
const touchedProjects = [];
|
|
40
|
+
|
|
41
|
+
for (const projectId of projectIds) {
|
|
42
|
+
const state = session.show({ project: projectId });
|
|
43
|
+
if (!state) continue;
|
|
44
|
+
const entry = state.persona_outputs?.[persona];
|
|
45
|
+
if (!entry || entry.accepted !== true) continue;
|
|
46
|
+
|
|
47
|
+
touchedProjects.push(projectId);
|
|
48
|
+
const fullPrompt = (state.full_prompts && state.full_prompts[persona]) || null;
|
|
49
|
+
const structured = (state.structured_outputs && state.structured_outputs[persona]) || null;
|
|
50
|
+
let outcomeVal = 'unknown';
|
|
51
|
+
if (state.outcome && state.outcome.confirmed && state.outcome.confirmed[persona]) {
|
|
52
|
+
outcomeVal = state.outcome.confirmed[persona].value;
|
|
53
|
+
} else if (state.outcome && state.outcome.inferred && state.outcome.inferred[persona]) {
|
|
54
|
+
outcomeVal = state.outcome.inferred[persona].value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pairs.push({
|
|
58
|
+
system_card: card,
|
|
59
|
+
system_prompt_full: fullPrompt ? fullPrompt.system : card,
|
|
60
|
+
user_prompt_full: fullPrompt ? fullPrompt.user : '',
|
|
61
|
+
digest: state.digest || {},
|
|
62
|
+
user: state.digest?.task_map_summary || (state.task_map || []).map((t) => t.task).join('; '),
|
|
63
|
+
assistant: entry.text,
|
|
64
|
+
structured_output: structured,
|
|
65
|
+
outcome: outcomeVal,
|
|
66
|
+
task_type: state.task_type || '',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const filePath = path.resolve(output || path.join(DEFAULT_OUTPUT_DIR, `${persona}.jsonl`));
|
|
71
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
72
|
+
fs.writeFileSync(filePath, pairs.map((p) => JSON.stringify(p)).join('\n') + (pairs.length ? '\n' : ''));
|
|
73
|
+
|
|
74
|
+
return { pairs: pairs.length, filePath, projects: touchedProjects };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { exportTraining, DEFAULT_OUTPUT_DIR };
|