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.
Files changed (69) hide show
  1. package/HOW-TO-USE.md +436 -0
  2. package/README.md +29 -13
  3. package/agents/cards/evolve-check.md +38 -0
  4. package/agents/manifests/evolve-check.json +16 -0
  5. package/dist/HOW-TO-USE.md +15 -3
  6. package/dist/README.md +29 -13
  7. package/dist/agents/cards/evolve-check.md +38 -0
  8. package/dist/agents/manifests/evolve-check.json +16 -0
  9. package/dist/bin/cli.js +1225 -1
  10. package/dist/lib/cache.js +111 -1
  11. package/dist/lib/chunk-executor.js +219 -1
  12. package/dist/lib/chunk-models.js +228 -1
  13. package/dist/lib/chunk-planner.js +328 -1
  14. package/dist/lib/chunk-router.js +66 -1
  15. package/dist/lib/chunk-run.js +199 -1
  16. package/dist/lib/chunk-synthesis.js +176 -1
  17. package/dist/lib/chunk-telemetry.js +88 -1
  18. package/dist/lib/collect.js +858 -1
  19. package/dist/lib/cost.js +119 -1
  20. package/dist/lib/dedup.js +167 -1
  21. package/dist/lib/deliberation.js +721 -1
  22. package/dist/lib/design-spec.js +236 -1
  23. package/dist/lib/evolution-metrics.js +197 -0
  24. package/dist/lib/evolve.js +361 -1
  25. package/dist/lib/export.js +77 -1
  26. package/dist/lib/feedback-submit.js +324 -1
  27. package/dist/lib/feedback.js +182 -1
  28. package/dist/lib/host-llm.js +251 -1
  29. package/dist/lib/install.js +301 -1
  30. package/dist/lib/knowledge.js +384 -1
  31. package/dist/lib/lessons.js +217 -1
  32. package/dist/lib/moodboard.js +563 -1
  33. package/dist/lib/orchestrator/run.js +935 -1
  34. package/dist/lib/outcome.js +193 -1
  35. package/dist/lib/platforms.js +166 -1
  36. package/dist/lib/provider.js +57 -1
  37. package/dist/lib/query-expander.js +83 -1
  38. package/dist/lib/ranker.js +105 -1
  39. package/dist/lib/reference-pack.js +221 -0
  40. package/dist/lib/research.js +143 -1
  41. package/dist/lib/retrieve.js +131 -1
  42. package/dist/lib/session.js +185 -1
  43. package/dist/lib/source-discovery.js +486 -1
  44. package/dist/lib/synthesis.js +155 -1
  45. package/dist/lib/token-gate.js +46 -1
  46. package/dist/skills/design-reference/google-fonts.csv +1924 -1924
  47. package/dist/skills/design-reference/products.csv +162 -162
  48. package/dist/skills/design-reference/schema.json +159 -0
  49. package/dist/skills/design-reference/stacks/angular.csv +1 -1
  50. package/dist/skills/design-reference/stacks/astro.csv +1 -1
  51. package/dist/skills/design-reference/stacks/laravel.csv +2 -2
  52. package/dist/skills/design-reference/stacks/threejs.csv +54 -54
  53. package/dist/skills/design-reference/styles.csv +85 -85
  54. package/dist/skills/design-reference/typography.csv +75 -74
  55. package/dist/skills/design-reference/ui-reasoning.csv +1 -1
  56. package/dist/skills/evolve-check/SKILL.md +106 -0
  57. package/package.json +8 -8
  58. package/scripts/validate-csvs.js +197 -0
  59. package/skills/design-reference/google-fonts.csv +1924 -1924
  60. package/skills/design-reference/products.csv +162 -162
  61. package/skills/design-reference/schema.json +159 -0
  62. package/skills/design-reference/stacks/angular.csv +1 -1
  63. package/skills/design-reference/stacks/astro.csv +1 -1
  64. package/skills/design-reference/stacks/laravel.csv +2 -2
  65. package/skills/design-reference/stacks/threejs.csv +54 -54
  66. package/skills/design-reference/styles.csv +85 -85
  67. package/skills/design-reference/typography.csv +75 -74
  68. package/skills/design-reference/ui-reasoning.csv +1 -1
  69. package/skills/evolve-check/SKILL.md +106 -0
@@ -1 +1,328 @@
1
- 'use strict';var fs=require('fs'),path=require('path'),os=require('os'),session=require('./session'),knowledge=require('./knowledge'),chunkModels=require('./chunk-models'),{resolvePackageRoot}=require('./platforms'),PACKAGE_ROOT=resolvePackageRoot(__dirname),AGENTS_DIR=path['join'](PACKAGE_ROOT,'agents');function loadJson(a){if(!fs['existsSync'](a))return null;try{return JSON['parse'](fs['readFileSync'](a,'utf8'));}catch(b){return null;}}var CONFIG_FILE=path['join'](os['homedir'](),'.analyzthis_design','config.json');function loadConfig(){if(!fs['existsSync'](CONFIG_FILE))return{};try{return JSON['parse'](fs['readFileSync'](CONFIG_FILE,'utf8'));}catch(a){return{};}}function loadManifest(a){return loadJson(path['join'](AGENTS_DIR,'manifests',a+'.json'));}function loadSkillOrCard(a){var b=loadManifest(a);if(!b)return'';var c=path['join'](PACKAGE_ROOT,b['system_card']||'');if(c&&fs['existsSync'](c))return fs['readFileSync'](c,'utf8');var d=path['join'](PACKAGE_ROOT,b['system_skill']||'');if(d&&fs['existsSync'](d))return fs['readFileSync'](d,'utf8')['slice'](0x0,0x1770);return'';}function buildContextPack(a,b,c){var d=(a['task_map']||[])['slice'](0x0,0x8),f=[];try{f=knowledge['getPersonaSliceForPrompt'](a,'arjun');}catch(g){}return{'task':b,'figma':c||a['figma_node']&&a['figma_node']['url']||'','task_map':d,'hierarchy_top3':a['digest']&&a['digest']['hierarchy_top3']||[],'ds_at_risk':a['digest']&&a['digest']['ds_at_risk']||[],'knowledge_bank_excerpt':f['slice'](0x0,0x3)['map'](function(h){return h['title']+':\x20'+h['content']['slice'](0x0,0x190);})['join']('\x0a\x0a'),'mode':a['mode']||'assess_only'};}function plannerSystemPrompt(){return['You\x20are\x20the\x20Task\x20Planner\x20for\x20a\x20team\x20of\x20design\x20personas.','Your\x20job\x20is\x20to\x20break\x20a\x20design\x20task\x20into\x20small,\x20sequential,\x20independently\x20executable\x20chunks.','Each\x20chunk\x20targets\x20one\x20persona\x20and\x20has\x20a\x20clear\x20goal,\x20prompts,\x20effort\x20level,\x20and\x20dependencies.','You\x20must\x20output\x20ONLY\x20a\x20JSON\x20object\x20inside\x20a\x20```json\x20code\x20fence.','','Allowed\x20personas:\x20arjun\x20(UX/visual),\x20meera\x20(business),\x20priya\x20(feasibility),\x20zara\x20(delight),\x20noor\x20(minimalist\x20IA),\x20anuj\x20(dense\x20power-user),\x20raj\x20(strategy/arbitration).','','Effort\x20levels:','-\x20trivial:\x20simple\x20extraction\x20or\x20structured\x20scoring','-\x20standard:\x20a\x20full\x20persona\x20output\x20block','-\x20hard:\x20synthesis,\x20arbitration,\x20or\x20ambiguous\x20multi-constraint\x20tasks','','Output\x20schema:','{','\x20\x20\x22plan_id\x22:\x20\x22short-unique-id\x22,','\x20\x20\x22task_summary\x22:\x20\x22one-line\x20summary\x22,','\x20\x20\x22chunks\x22:\x20[','\x20\x20\x20\x20{','\x20\x20\x20\x20\x20\x20\x22id\x22:\x20\x22ia\x22,','\x20\x20\x20\x20\x20\x20\x22persona\x22:\x20\x22noor\x22,','\x20\x20\x20\x20\x20\x20\x22goal\x22:\x20\x22define\x20information\x20hierarchy\x22,','\x20\x20\x20\x20\x20\x20\x22system_prompt\x22:\x20\x22...\x22,','\x20\x20\x20\x20\x20\x20\x22user_prompt\x22:\x20\x22...\x22,','\x20\x20\x20\x20\x20\x20\x22effort\x22:\x20\x22standard\x22,','\x20\x20\x20\x20\x20\x20\x22depends_on\x22:\x20[],','\x20\x20\x20\x20\x20\x20\x22output_schema\x22:\x20\x22hierarchy_list\x22','\x20\x20\x20\x20}','\x20\x20],','\x20\x20\x22synthesis\x22:\x20{','\x20\x20\x20\x20\x22persona\x22:\x20\x22raj\x22,','\x20\x20\x20\x20\x22goal\x22:\x20\x22resolve\x20conflicts\x20and\x20produce\x20final\x20verdict\x22,','\x20\x20\x20\x20\x22system_prompt\x22:\x20\x22...\x22,','\x20\x20\x20\x20\x22user_prompt\x22:\x20\x22...\x22','\x20\x20}','}']['join']('\x0a');}function plannerUserPrompt(a,b,c,d){return['Task:\x20'+a,b?'Figma:\x20'+b:'','','Context\x20pack:',JSON['stringify'](c,null,0x2)['slice'](0x0,0xfa0),'','Design\x20a\x20chunk\x20plan\x20with\x20at\x20most\x20'+d+'\x20chunks.','Prefer\x20sequential\x20dependencies.\x20Use\x20depends_on\x20to\x20declare\x20which\x20chunk\x20outputs\x20are\x20inputs.','Keep\x20each\x20chunk\x20focused\x20on\x20one\x20persona\x20lens.','','The\x20final\x20synthesis\x20chunk\x20should\x20merge\x20all\x20outputs\x20into\x20a\x20verdict\x20(SHIP\x20/\x20REVISE\x20/\x20BLOCK),\x20composite\x20score,\x20and\x20Top\x203\x20actionable\x20changes.']['join']('\x0a');}function parsePlan(a){var b={'plan_id':'','task_summary':'','chunks':[],'synthesis':{'persona':'raj','goal':'','system_prompt':'','user_prompt':''}};if(!a)return{'plan':b,'parse_error':'empty'};var c=a['match'](/```json\s*([\s\S]*?)```/i),d=c?c[0x1]['trim']():a['trim']();try{var f=JSON['parse'](d);if(!f['chunks']||!Array['isArray'](f['chunks']))return{'plan':b,'parse_error':'missing\x20chunks\x20array'};return{'plan':f,'parse_error':null};}catch(g){return{'plan':b,'parse_error':g['message']};}}function topologicalSort(a){var b={};for(var c=0x0;c<a['length'];c++)b[a[c]['id']]=a[c];var d={},e=[];function f(h,l){if(l['indexOf'](h)!==-0x1)throw new Error('Circular\x20dependency\x20in\x20chunk\x20plan:\x20'+h);if(d[h])return;d[h]=!![];var m=b[h];if(!m)throw new Error('Unknown\x20chunk\x20dependency:\x20'+h);var n=m['depends_on']||[];for(var o=0x0;o<n['length'];o++)f(n[o],l['concat']([h]));e['push'](m);}for(var g=0x0;g<a['length'];g++)f(a[g]['id'],[]);return e;}async function createPlan(a){a=a||{};var b=a['task']||'',c=a['figma']||'',d=a['project']||session['getProjectId'](),f=a['config']||loadConfig(),g=a['maxChunks']||f['chunk_models']&&f['chunk_models']['max_chunks']||0x6,h=session['show']({'project':d})||session['init']({'project':d}),i=buildContextPack(h,b,c),j=chunkModels['resolvePlannerModel'](f),k=null;if(j['provider']==='host'){var l=h&&h['host_run']||{};if(a['continueRun']&&l['run_dir']&&fs['existsSync'](l['run_dir']))k={'runId':l['run_id'],'runDir':l['run_dir'],'manifest':require('./host-llm')['loadManifest'](l['run_dir'])};else{k=require('./host-llm')['createRun']({'projectId':d,'task':b});try{session['update']({'project':d,'patch':{'host_run':{'run_id':k['runId'],'run_dir':k['runDir'],'task':b,'status':'in_progress','last_pending':null,'checkpoint':null,'started_at':new Date()['toISOString']()}}});}catch(y){}}}else{if(j['provider']==='ollama'){}}var m=null;if(j['provider']==='host'&&k&&k['manifest']&&a['continueRun']){var n=require('./host-llm'),o=(k['manifest']['completed_steps']||[])['slice']()['reverse']();for(var p=0x0;p<o['length'];p++){if(o[p]['indexOf']('chunk-planner')===-0x1)continue;try{m=n['readResponse'](k['runDir'],o[p]);if(m)break;}catch(z){m=null;}}if(!m){var q=String(k['manifest']['step_counter'])['padStart'](0x3,'0')+'-chunk-planner';try{m=n['readResponse'](k['runDir'],q),m&&!k['manifest']['completed_steps']['includes'](q)&&(k['manifest']['completed_steps']['push'](q),n['saveManifest'](k['runDir'],k['manifest']));}catch(A){m=null;}}}var r=plannerSystemPrompt(),s=plannerUserPrompt(b,c,i,g),t=m||'',u=a['callLlmFn'];if(!u){var v=require('./orchestrator/run');u=v['callLlm'];}if(!t)try{t=await u({'provider':j['provider'],'model':j['model'],'system':r,'user':s,'maxTokens':0x9c4,'personaId':'chunk-planner','hostContext':j['provider']==='host'?k:undefined});}catch(B){if(B&&B['name']==='HostLlmPendingError')return{'plan':null,'planner_model':j,'context_pack':i,'planner_warning':j['fallback_warning']?'No\x20frontier\x20API\x20key\x20found.\x20Planner\x20fell\x20back\x20to\x20host/Devi;\x20chunk\x20quality\x20depends\x20on\x20the\x20host\x20model.':null,'host_pending':{'run_dir':B['runDir'],'step_id':B['stepId'],'message':'The\x20chunk\x20planner\x20prompt\x20has\x20been\x20written\x20to\x20the\x20host\x20queue.\x20Respond\x20via\x20/devi,\x20then\x20rerun\x20with\x20--continue.'},'planner_model':j,'planner_warning':j['fallback_warning']?'No\x20frontier\x20API\x20key\x20found.\x20Planner\x20fell\x20back\x20to\x20host/Devi;\x20chunk\x20quality\x20depends\x20on\x20the\x20host\x20model.':null};throw new Error('Planner\x20failed:\x20'+(B&&B['message']?B['message']:String(B)));}var w=parsePlan(t);if(w['parse_error'])throw new Error('Planner\x20produced\x20invalid\x20JSON:\x20'+w['parse_error']);var x=w['plan'];return x['chunks']=topologicalSort(x['chunks']),{'plan':x,'planner_model':j,'context_pack':i,'planner_warning':j['fallback_warning']?'No\x20frontier\x20API\x20key\x20found.\x20Planner\x20fell\x20back\x20to\x20host/Devi;\x20chunk\x20quality\x20depends\x20on\x20the\x20host\x20model.':null};}module['exports']={'createPlan':createPlan,'parsePlan':parsePlan,'topologicalSort':topologicalSort,'plannerSystemPrompt':plannerSystemPrompt,'plannerUserPrompt':plannerUserPrompt};
1
+ 'use strict';
2
+
3
+ /**
4
+ * Chunk planner (v2.0).
5
+ *
6
+ * Uses a frontier/strong model to produce a chunked execution plan:
7
+ * - persona per chunk
8
+ * - chunk goal
9
+ * - system/user prompts for the chunk
10
+ * - effort level
11
+ * - dependencies
12
+ * - output schema
13
+ *
14
+ * If no frontier API key is available, falls back to the host/Devi model
15
+ * with a warning that planner quality may degrade.
16
+ *
17
+ * CommonJS, 'use strict', var, try/catch around JSON.
18
+ */
19
+
20
+ var fs = require('fs');
21
+ var path = require('path');
22
+ var os = require('os');
23
+ var session = require('./session');
24
+ var knowledge = require('./knowledge');
25
+ var chunkModels = require('./chunk-models');
26
+ var { resolvePackageRoot } = require('./platforms');
27
+
28
+ var PACKAGE_ROOT = resolvePackageRoot(__dirname);
29
+ var AGENTS_DIR = path.join(PACKAGE_ROOT, 'agents');
30
+
31
+ function loadJson(filePath) {
32
+ if (!fs.existsSync(filePath)) return null;
33
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
34
+ catch (e) { return null; }
35
+ }
36
+
37
+ var CONFIG_FILE = path.join(os.homedir(), '.analyzthis_design', 'config.json');
38
+ function loadConfig() {
39
+ if (!fs.existsSync(CONFIG_FILE)) return {};
40
+ try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
41
+ catch (e) { return {}; }
42
+ }
43
+
44
+ function loadManifest(id) {
45
+ return loadJson(path.join(AGENTS_DIR, 'manifests', id + '.json'));
46
+ }
47
+
48
+ function loadSkillOrCard(id) {
49
+ var manifest = loadManifest(id);
50
+ if (!manifest) return '';
51
+ var cardPath = path.join(PACKAGE_ROOT, manifest.system_card || '');
52
+ if (cardPath && fs.existsSync(cardPath)) return fs.readFileSync(cardPath, 'utf8');
53
+ var skillPath = path.join(PACKAGE_ROOT, manifest.system_skill || '');
54
+ if (skillPath && fs.existsSync(skillPath)) return fs.readFileSync(skillPath, 'utf8').slice(0, 6000);
55
+ return '';
56
+ }
57
+
58
+ function buildContextPack(state, task, figma) {
59
+ var taskMap = (state.task_map || []).slice(0, 8);
60
+ var kb = [];
61
+ try {
62
+ kb = knowledge.getPersonaSliceForPrompt(state, 'arjun');
63
+ } catch (e) { /* ignore */ }
64
+
65
+ // Gather available reference data for the planner so it can scope chunks
66
+ // to personas whose CSV references are strongest for this task.
67
+ var refHints = [];
68
+ try {
69
+ var refPack = require('./reference-pack');
70
+ var keywords = refPack.extractKeywords(task);
71
+ if (keywords.length) {
72
+ var personas = Object.keys(refPack.REFERENCE_MAP);
73
+ for (var i = 0; i < personas.length; i++) {
74
+ var spec = refPack.getReferenceSpec(personas[i], state);
75
+ if (!spec) continue;
76
+ try {
77
+ var result = require('./retrieve').retrieve({
78
+ file: spec.file,
79
+ filters: [{ column: spec.column, anyOf: keywords }],
80
+ limit: 3,
81
+ });
82
+ if (result.rows.length) {
83
+ refHints.push(personas[i] + ': ' + result.rows.length + ' rows in ' + spec.file);
84
+ }
85
+ } catch (e) { /* file missing */ }
86
+ }
87
+ }
88
+ } catch (e) { /* reference-pack unavailable */ }
89
+
90
+ return {
91
+ task: task,
92
+ figma: figma || (state.figma_node && state.figma_node.url) || '',
93
+ task_map: taskMap,
94
+ hierarchy_top3: (state.digest && state.digest.hierarchy_top3) || [],
95
+ ds_at_risk: (state.digest && state.digest.ds_at_risk) || [],
96
+ knowledge_bank_excerpt: kb.slice(0, 3).map(function(n) {
97
+ return n.title + ': ' + n.content.slice(0, 400);
98
+ }).join('\n\n'),
99
+ reference_hints: refHints,
100
+ mode: state.mode || 'assess_only',
101
+ };
102
+ }
103
+
104
+ function plannerSystemPrompt() {
105
+ return [
106
+ 'You are the Task Planner for a team of design personas.',
107
+ 'Your job is to break a design task into small, sequential, independently executable chunks.',
108
+ 'Each chunk targets one persona and has a clear goal, prompts, effort level, and dependencies.',
109
+ 'You must output ONLY a JSON object inside a ```json code fence.',
110
+ '',
111
+ 'Allowed personas: arjun (UX/visual), meera (business), priya (feasibility), zara (delight), noor (minimalist IA), anuj (dense power-user), raj (strategy/arbitration).',
112
+ '',
113
+ 'Effort levels:',
114
+ '- trivial: simple extraction or structured scoring',
115
+ '- standard: a full persona output block',
116
+ '- hard: synthesis, arbitration, or ambiguous multi-constraint tasks',
117
+ '',
118
+ 'Output schema:',
119
+ '{',
120
+ ' "plan_id": "short-unique-id",',
121
+ ' "task_summary": "one-line summary",',
122
+ ' "chunks": [',
123
+ ' {',
124
+ ' "id": "ia",',
125
+ ' "persona": "noor",',
126
+ ' "goal": "define information hierarchy",',
127
+ ' "system_prompt": "...",',
128
+ ' "user_prompt": "...",',
129
+ ' "effort": "standard",',
130
+ ' "depends_on": [],',
131
+ ' "output_schema": "hierarchy_list"',
132
+ ' }',
133
+ ' ],',
134
+ ' "synthesis": {',
135
+ ' "persona": "raj",',
136
+ ' "goal": "resolve conflicts and produce final verdict",',
137
+ ' "system_prompt": "...",',
138
+ ' "user_prompt": "..."',
139
+ ' }',
140
+ '}',
141
+ ].join('\n');
142
+ }
143
+
144
+ function plannerUserPrompt(task, figma, contextPack, maxChunks) {
145
+ return [
146
+ 'Task: ' + task,
147
+ figma ? 'Figma: ' + figma : '',
148
+ '',
149
+ 'Context pack:',
150
+ JSON.stringify(contextPack, null, 2).slice(0, 4000),
151
+ '',
152
+ 'Design a chunk plan with at most ' + maxChunks + ' chunks.',
153
+ 'Prefer sequential dependencies. Use depends_on to declare which chunk outputs are inputs.',
154
+ 'Keep each chunk focused on one persona lens.',
155
+ '',
156
+ 'The final synthesis chunk should merge all outputs into a verdict (SHIP / REVISE / BLOCK), composite score, and Top 3 actionable changes.',
157
+ ].join('\n');
158
+ }
159
+
160
+ function parsePlan(text) {
161
+ var empty = {
162
+ plan_id: '',
163
+ task_summary: '',
164
+ chunks: [],
165
+ synthesis: { persona: 'raj', goal: '', system_prompt: '', user_prompt: '' }
166
+ };
167
+ if (!text) return { plan: empty, parse_error: 'empty' };
168
+
169
+ var fence = text.match(/```json\s*([\s\S]*?)```/i);
170
+ var raw = fence ? fence[1].trim() : text.trim();
171
+ try {
172
+ var parsed = JSON.parse(raw);
173
+ if (!parsed.chunks || !Array.isArray(parsed.chunks)) {
174
+ return { plan: empty, parse_error: 'missing chunks array' };
175
+ }
176
+ return { plan: parsed, parse_error: null };
177
+ } catch (e) {
178
+ return { plan: empty, parse_error: e.message };
179
+ }
180
+ }
181
+
182
+ function topologicalSort(chunks) {
183
+ var byId = {};
184
+ for (var i = 0; i < chunks.length; i++) byId[chunks[i].id] = chunks[i];
185
+
186
+ var visited = {};
187
+ var result = [];
188
+
189
+ function visit(id, stack) {
190
+ if (stack.indexOf(id) !== -1) throw new Error('Circular dependency in chunk plan: ' + id);
191
+ if (visited[id]) return;
192
+ visited[id] = true;
193
+ var chunk = byId[id];
194
+ if (!chunk) throw new Error('Unknown chunk dependency: ' + id);
195
+ var deps = chunk.depends_on || [];
196
+ for (var j = 0; j < deps.length; j++) visit(deps[j], stack.concat([id]));
197
+ result.push(chunk);
198
+ }
199
+
200
+ for (var k = 0; k < chunks.length; k++) visit(chunks[k].id, []);
201
+ return result;
202
+ }
203
+
204
+ async function createPlan(opts) {
205
+ opts = opts || {};
206
+ var task = opts.task || '';
207
+ var figma = opts.figma || '';
208
+ var projectId = opts.project || session.getProjectId();
209
+ var config = opts.config || loadConfig();
210
+ var maxChunks = opts.maxChunks || (config.chunk_models && config.chunk_models.max_chunks) || 6;
211
+
212
+ var state = session.show({ project: projectId }) || session.init({ project: projectId });
213
+ var contextPack = buildContextPack(state, task, figma);
214
+
215
+ var plannerModel = chunkModels.resolvePlannerModel(config);
216
+
217
+ var hostRun = null;
218
+ if (plannerModel.provider === 'host') {
219
+ var existingHost = (state && state.host_run) || {};
220
+ if (opts.continueRun && existingHost.run_dir && fs.existsSync(existingHost.run_dir)) {
221
+ hostRun = {
222
+ runId: existingHost.run_id,
223
+ runDir: existingHost.run_dir,
224
+ manifest: require('./host-llm').loadManifest(existingHost.run_dir),
225
+ };
226
+ } else {
227
+ hostRun = require('./host-llm').createRun({ projectId: projectId, task: task });
228
+ try {
229
+ session.update({ project: projectId, patch: { host_run: { run_id: hostRun.runId, run_dir: hostRun.runDir, task: task, status: 'in_progress', last_pending: null, checkpoint: null, started_at: new Date().toISOString() } } });
230
+ } catch (e) { /* ignore */ }
231
+ }
232
+ } else if (plannerModel.provider === 'ollama') {
233
+ // Ollama needs no host run context; callLlm handles it directly.
234
+ }
235
+
236
+ // Reuse the most recent completed chunk-planner response when --continue is set.
237
+ var reusedText = null;
238
+ if (plannerModel.provider === 'host' && hostRun && hostRun.manifest && opts.continueRun) {
239
+ var hostModule = require('./host-llm');
240
+ var completed = (hostRun.manifest.completed_steps || []).slice().reverse();
241
+ for (var ci = 0; ci < completed.length; ci++) {
242
+ if (completed[ci].indexOf('chunk-planner') === -1) continue;
243
+ try {
244
+ reusedText = hostModule.readResponse(hostRun.runDir, completed[ci]);
245
+ if (reusedText) break;
246
+ } catch (e) { reusedText = null; }
247
+ }
248
+ // Also try the exact step the manifest points at, in case the response was
249
+ // submitted by an external tool but the manifest was not updated.
250
+ if (!reusedText) {
251
+ var latestPendingId = String(hostRun.manifest.step_counter).padStart(3, '0') + '-chunk-planner';
252
+ try {
253
+ reusedText = hostModule.readResponse(hostRun.runDir, latestPendingId);
254
+ if (reusedText && !hostRun.manifest.completed_steps.includes(latestPendingId)) {
255
+ hostRun.manifest.completed_steps.push(latestPendingId);
256
+ hostModule.saveManifest(hostRun.runDir, hostRun.manifest);
257
+ }
258
+ } catch (e) { reusedText = null; }
259
+ }
260
+ }
261
+
262
+ var system = plannerSystemPrompt();
263
+ var user = plannerUserPrompt(task, figma, contextPack, maxChunks);
264
+
265
+ var text = reusedText || '';
266
+ var callLlmFn = opts.callLlmFn;
267
+ if (!callLlmFn) {
268
+ var orchestrator = require('./orchestrator/run');
269
+ callLlmFn = orchestrator.callLlm;
270
+ }
271
+
272
+ if (!text) {
273
+ try {
274
+ text = await callLlmFn({
275
+ provider: plannerModel.provider,
276
+ model: plannerModel.model,
277
+ system: system,
278
+ user: user,
279
+ maxTokens: 2500,
280
+ personaId: 'chunk-planner',
281
+ hostContext: plannerModel.provider === 'host' ? hostRun : undefined,
282
+ });
283
+ } catch (e) {
284
+ if (e && e.name === 'HostLlmPendingError') {
285
+ return {
286
+ plan: null,
287
+ planner_model: plannerModel,
288
+ context_pack: contextPack,
289
+ planner_warning: plannerModel.fallback_warning ?
290
+ 'No frontier API key found. Planner fell back to host/Devi; chunk quality depends on the host model.' : null,
291
+ host_pending: {
292
+ run_dir: e.runDir,
293
+ step_id: e.stepId,
294
+ message: 'The chunk planner prompt has been written to the host queue. Respond via /devi, then rerun with --continue.',
295
+ },
296
+ planner_model: plannerModel,
297
+ planner_warning: plannerModel.fallback_warning ?
298
+ 'No frontier API key found. Planner fell back to host/Devi; chunk quality depends on the host model.' : null,
299
+ };
300
+ }
301
+ throw new Error('Planner failed: ' + (e && e.message ? e.message : String(e)));
302
+ }
303
+ }
304
+
305
+ var parsed = parsePlan(text);
306
+ if (parsed.parse_error) {
307
+ throw new Error('Planner produced invalid JSON: ' + parsed.parse_error);
308
+ }
309
+
310
+ var plan = parsed.plan;
311
+ plan.chunks = topologicalSort(plan.chunks);
312
+
313
+ return {
314
+ plan: plan,
315
+ planner_model: plannerModel,
316
+ context_pack: contextPack,
317
+ planner_warning: plannerModel.fallback_warning ?
318
+ 'No frontier API key found. Planner fell back to host/Devi; chunk quality depends on the host model.' : null,
319
+ };
320
+ }
321
+
322
+ module.exports = {
323
+ createPlan: createPlan,
324
+ parsePlan: parsePlan,
325
+ topologicalSort: topologicalSort,
326
+ plannerSystemPrompt: plannerSystemPrompt,
327
+ plannerUserPrompt: plannerUserPrompt,
328
+ };
@@ -1 +1,66 @@
1
- 'use strict';var chunkModels=require('./chunk-models'),telemetry=require('./chunk-telemetry');function applyTelemetryPenalty(a,b){var c=telemetry['getStatsForChunkType'](b);if(!c||!c['length'])return a;var d=[];for(var e=0x0;e<a['length'];e++){var f=a[e],g=chunkModels['formatModelName'](f),h=c['find'](function(l){return l['model']===g;}),j=0x0;if(h&&h['attempts']>=0x3){var k=h['successful']/h['attempts'];if(k<0.5)j+=0.01;if(k<0.25)j+=0.02;}d['push'](Object['assign']({},f,{'effective_cost':(f['cost']||0x0)+j}));}return d['sort'](function(l,m){return l['effective_cost']-m['effective_cost'];}),d;}async function resolveChunkModel(a){a=a||{};var b=a['effort']||'standard',c=a['chunkType']||a['persona']||'general',d=a['budget']||'auto',e=a['config']||chunkModels['loadConfig'](),f=await chunkModels['discoverPool']({'config':e}),g=chunkModels['filterPool'](f,{'effort':b,'budget':d}),h=applyTelemetryPenalty(g,c);if(!h['length'])throw new Error('No\x20chunk\x20model\x20available\x20for\x20effort='+b+'\x20budget='+d);return{'ranked':h,'selected':h[0x0]};}module['exports']={'resolveChunkModel':resolveChunkModel,'applyTelemetryPenalty':applyTelemetryPenalty};
1
+ 'use strict';
2
+
3
+ /**
4
+ * Chunk router (v2.0).
5
+ *
6
+ * Picks the cheapest capable model for a chunk from the discovered pool.
7
+ * Applies telemetry-based penalties so bad model+chunk combinations are
8
+ * deprioritized over time.
9
+ *
10
+ * CommonJS, 'use strict', var.
11
+ */
12
+
13
+ var chunkModels = require('./chunk-models');
14
+ var telemetry = require('./chunk-telemetry');
15
+
16
+ function applyTelemetryPenalty(pool, chunkType) {
17
+ var stats = telemetry.getStatsForChunkType(chunkType);
18
+ if (!stats || !stats.length) return pool;
19
+
20
+ var penalized = [];
21
+ for (var i = 0; i < pool.length; i++) {
22
+ var spec = pool[i];
23
+ var name = chunkModels.formatModelName(spec);
24
+ var record = stats.find(function(s) { return s.model === name; });
25
+ var penalty = 0;
26
+ if (record && record.attempts >= 3) {
27
+ // Deprioritize if success rate is low.
28
+ var successRate = record.successful / record.attempts;
29
+ if (successRate < 0.5) penalty += 0.01;
30
+ if (successRate < 0.25) penalty += 0.02;
31
+ }
32
+ penalized.push(Object.assign({}, spec, { effective_cost: (spec.cost || 0) + penalty }));
33
+ }
34
+
35
+ penalized.sort(function(a, b) {
36
+ return a.effective_cost - b.effective_cost;
37
+ });
38
+
39
+ return penalized;
40
+ }
41
+
42
+ async function resolveChunkModel(opts) {
43
+ opts = opts || {};
44
+ var effort = opts.effort || 'standard';
45
+ var chunkType = opts.chunkType || opts.persona || 'general';
46
+ var budget = opts.budget || 'auto';
47
+ var config = opts.config || chunkModels.loadConfig();
48
+
49
+ var pool = await chunkModels.discoverPool({ config: config });
50
+ var filtered = chunkModels.filterPool(pool, { effort: effort, budget: budget });
51
+ var ranked = applyTelemetryPenalty(filtered, chunkType);
52
+
53
+ if (!ranked.length) {
54
+ throw new Error('No chunk model available for effort=' + effort + ' budget=' + budget);
55
+ }
56
+
57
+ return {
58
+ ranked: ranked,
59
+ selected: ranked[0],
60
+ };
61
+ }
62
+
63
+ module.exports = {
64
+ resolveChunkModel: resolveChunkModel,
65
+ applyTelemetryPenalty: applyTelemetryPenalty,
66
+ };
@@ -1 +1,199 @@
1
- 'use strict';var fs=require('fs'),os=require('os'),path=require('path'),session=require('./session'),planner=require('./chunk-planner'),executor=require('./chunk-executor'),synthesis=require('./chunk-synthesis'),chunkModels=require('./chunk-models'),CONFIG_FILE=path['join'](os['homedir'](),'.analyzthis_design','config.json');function loadConfig(){if(!fs['existsSync'](CONFIG_FILE))return{};try{return JSON['parse'](fs['readFileSync'](CONFIG_FILE,'utf8'));}catch(a){return{};}}async function runChunked(a){a=a||{};var b=a['task']||'',c=a['figma']||'',d=a['project']||session['getProjectId'](),f=a['config']||loadConfig(),g=a['budget']||f['chunk_models']&&f['chunk_models']['budget']||'auto',h=a['sequential']!==![],j=a['maxChunks']||f['chunk_models']&&f['chunk_models']['max_chunks']||0x6,k=!!a['dryRun'],l=a['callLlmFn'];if(!b)throw new Error('--task\x20is\x20required');var m=session['show']({'project':d})||session['init']({'project':d}),n;try{n=await planner['createPlan']({'task':b,'figma':c,'project':d,'config':f,'maxChunks':j,'callLlmFn':l,'continueRun':!!a['continueRun']});}catch(D){throw new Error('Chunk\x20planner\x20failed:\x20'+D['message']);}if(n['host_pending'])return{'project_id':d,'task':b,'host_pending':n['host_pending'],'planner_model':n['planner_model'],'planner_warning':n['planner_warning'],'mode':'host_pending'};if(k)return{'project_id':d,'task':b,'plan':n['plan'],'planner_model':n['planner_model'],'planner_warning':n['planner_warning'],'mode':'chunked_dry_run'};var o=[],p=0x0,q=0x0,r=0x0,s=[],t=[],u=n['plan']['chunks']||[];for(var v=0x0;v<u['length'];v++){var w=u[v],x=await executor['executeChunk']({'chunk':w,'priorResults':o,'contextPack':n['context_pack'],'config':f,'budget':g,'callLlmFn':l});o['push'](x);if(x['success']){var y=chunkModels['formatModelName'](x['model_used']);if(s['indexOf'](y)===-0x1)s['push'](y);p+=x['cost']||0x0,q+=x['tokens']['input'],r+=x['tokens']['output'];}else t['push']({'id':w['id'],'error':x['error']});}var z=await synthesis['synthesize']({'plan':n['plan'],'results':o,'contextPack':n['context_pack'],'config':f,'budget':g,'callLlmFn':l});p+=z['cost']||0x0,q+=z['tokens']?z['tokens']['input']:0x0,r+=z['tokens']?z['tokens']['output']:0x0;if(z['model_used']){var A=chunkModels['formatModelName'](z['model_used']);if(s['indexOf'](A)===-0x1)s['push'](A);}var B={'plan':n['plan'],'planner_model':n['planner_model'],'planner_warning':n['planner_warning'],'results':o,'synthesis':z,'total_cost':Number(p['toFixed'](0x6)),'total_tokens':{'input':q,'output':r},'models_used':s,'failed_chunks':t,'budget':g,'mode':h?'sequential':'parallel'},C=session['update']({'project':d,'patch':{'chunk_run':B,'task_type':m['task_type']||'chunked_run','mode':'assess_only','digest':Object['assign']({},m['digest']||{},{'task_map_summary':b['slice'](0x0,0xf0),'experts':u['map'](function(E){return E['persona'];}),'mode':'assess_only'}),'metrics':{'llm_calls':o['length']+0x1,'experts_run':u['map'](function(E){return E['persona'];}),'input_tokens_est':q,'output_tokens_est':r,'cost_usd':Number(p['toFixed'](0x6)),'mode':'chunked','cache_hits':0x0,'effort_log':o['map'](function(E){return{'persona':E['persona'],'effort':E['chunk']?E['chunk']['effort']:'standard','model':E['model_used']?chunkModels['formatModelName'](E['model_used']):null,'scoped_mode':null};})}}});return{'project_id':d,'task':b,'plan':n['plan'],'chunk_run':B,'session':C};}module['exports']={'runChunked':runChunked};
1
+ 'use strict';
2
+
3
+ /**
4
+ * Chunk run coordinator (v2.0).
5
+ *
6
+ * Top-level orchestrator for chunked execution. Default behavior for
7
+ * `npx analyzthis_design run --task "..."`.
8
+ *
9
+ * 1. Calls the planner (frontier/host) to produce a chunk graph.
10
+ * 2. Executes chunks sequentially by default using the cheapest model.
11
+ * 3. Synthesizes outputs.
12
+ * 4. Persists chunk_run to session.
13
+ *
14
+ * CommonJS, 'use strict', var.
15
+ */
16
+
17
+ var fs = require('fs');
18
+ var os = require('os');
19
+ var path = require('path');
20
+
21
+ var session = require('./session');
22
+ var planner = require('./chunk-planner');
23
+ var executor = require('./chunk-executor');
24
+ var synthesis = require('./chunk-synthesis');
25
+ var chunkModels = require('./chunk-models');
26
+
27
+ var CONFIG_FILE = path.join(os.homedir(), '.analyzthis_design', 'config.json');
28
+
29
+ function loadConfig() {
30
+ if (!fs.existsSync(CONFIG_FILE)) return {};
31
+ try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
32
+ catch (e) { return {}; }
33
+ }
34
+
35
+ async function runChunked(opts) {
36
+ opts = opts || {};
37
+ var task = opts.task || '';
38
+ var figma = opts.figma || '';
39
+ var projectId = opts.project || session.getProjectId();
40
+ var config = opts.config || loadConfig();
41
+ var budget = opts.budget || (config.chunk_models && config.chunk_models.budget) || 'auto';
42
+ var sequential = opts.sequential !== false; // default true
43
+ var maxChunks = opts.maxChunks || (config.chunk_models && config.chunk_models.max_chunks) || 6;
44
+ var dryRun = !!opts.dryRun;
45
+ var callLlmFn = opts.callLlmFn;
46
+
47
+ if (!task) throw new Error('--task is required');
48
+
49
+ var state = session.show({ project: projectId }) || session.init({ project: projectId });
50
+
51
+ var planResult;
52
+ try {
53
+ planResult = await planner.createPlan({
54
+ task: task,
55
+ figma: figma,
56
+ project: projectId,
57
+ config: config,
58
+ maxChunks: maxChunks,
59
+ callLlmFn: callLlmFn,
60
+ continueRun: !!opts.continueRun,
61
+ });
62
+ } catch (e) {
63
+ throw new Error('Chunk planner failed: ' + e.message);
64
+ }
65
+
66
+ if (planResult.host_pending) {
67
+ return {
68
+ project_id: projectId,
69
+ task: task,
70
+ host_pending: planResult.host_pending,
71
+ planner_model: planResult.planner_model,
72
+ planner_warning: planResult.planner_warning,
73
+ mode: 'host_pending',
74
+ };
75
+ }
76
+
77
+ if (dryRun) {
78
+ return {
79
+ project_id: projectId,
80
+ task: task,
81
+ plan: planResult.plan,
82
+ planner_model: planResult.planner_model,
83
+ planner_warning: planResult.planner_warning,
84
+ mode: 'chunked_dry_run',
85
+ };
86
+ }
87
+
88
+ var results = [];
89
+ var totalCost = 0;
90
+ var totalInput = 0;
91
+ var totalOutput = 0;
92
+ var modelsUsed = [];
93
+ var failedChunks = [];
94
+ var cacheHits = 0;
95
+
96
+ var chunks = planResult.plan.chunks || [];
97
+
98
+ for (var i = 0; i < chunks.length; i++) {
99
+ var chunk = chunks[i];
100
+ var result = await executor.executeChunk({
101
+ chunk: chunk,
102
+ priorResults: results,
103
+ contextPack: planResult.context_pack,
104
+ state: state,
105
+ task: task,
106
+ config: config,
107
+ budget: budget,
108
+ callLlmFn: callLlmFn,
109
+ });
110
+
111
+ results.push(result);
112
+
113
+ if (result.success) {
114
+ var name = chunkModels.formatModelName(result.model_used);
115
+ if (modelsUsed.indexOf(name) === -1) modelsUsed.push(name);
116
+ totalCost += result.cost || 0;
117
+ totalInput += result.tokens.input;
118
+ totalOutput += result.tokens.output;
119
+ if (result.cache_hit) cacheHits++;
120
+ } else {
121
+ failedChunks.push({ id: chunk.id, error: result.error });
122
+ }
123
+ }
124
+
125
+ var syn = await synthesis.synthesize({
126
+ plan: planResult.plan,
127
+ results: results,
128
+ contextPack: planResult.context_pack,
129
+ config: config,
130
+ budget: budget,
131
+ callLlmFn: callLlmFn,
132
+ });
133
+
134
+ totalCost += syn.cost || 0;
135
+ totalInput += syn.tokens ? syn.tokens.input : 0;
136
+ totalOutput += syn.tokens ? syn.tokens.output : 0;
137
+ if (syn.model_used) {
138
+ var synName = chunkModels.formatModelName(syn.model_used);
139
+ if (modelsUsed.indexOf(synName) === -1) modelsUsed.push(synName);
140
+ }
141
+
142
+ var chunkRun = {
143
+ plan: planResult.plan,
144
+ planner_model: planResult.planner_model,
145
+ planner_warning: planResult.planner_warning,
146
+ results: results,
147
+ synthesis: syn,
148
+ total_cost: Number(totalCost.toFixed(6)),
149
+ total_tokens: { input: totalInput, output: totalOutput },
150
+ models_used: modelsUsed,
151
+ failed_chunks: failedChunks,
152
+ cache_hits: cacheHits,
153
+ budget: budget,
154
+ mode: sequential ? 'sequential' : 'parallel',
155
+ };
156
+
157
+ var final = session.update({
158
+ project: projectId,
159
+ patch: {
160
+ chunk_run: chunkRun,
161
+ task_type: state.task_type || 'chunked_run',
162
+ mode: 'assess_only',
163
+ digest: Object.assign({}, state.digest || {}, {
164
+ task_map_summary: task.slice(0, 240),
165
+ experts: chunks.map(function(c) { return c.persona; }),
166
+ mode: 'assess_only',
167
+ }),
168
+ metrics: {
169
+ llm_calls: results.length + 1, // chunks + synthesis
170
+ experts_run: chunks.map(function(c) { return c.persona; }),
171
+ input_tokens_est: totalInput,
172
+ output_tokens_est: totalOutput,
173
+ cost_usd: Number(totalCost.toFixed(6)),
174
+ mode: 'chunked',
175
+ cache_hits: cacheHits,
176
+ effort_log: results.map(function(r) {
177
+ return {
178
+ persona: r.persona,
179
+ effort: r.chunk ? r.chunk.effort : 'standard',
180
+ model: r.model_used ? chunkModels.formatModelName(r.model_used) : null,
181
+ scoped_mode: null,
182
+ };
183
+ }),
184
+ },
185
+ },
186
+ });
187
+
188
+ return {
189
+ project_id: projectId,
190
+ task: task,
191
+ plan: planResult.plan,
192
+ chunk_run: chunkRun,
193
+ session: final,
194
+ };
195
+ }
196
+
197
+ module.exports = {
198
+ runChunked: runChunked,
199
+ };