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
package/dist/lib/cost.js CHANGED
@@ -1 +1,119 @@
1
- 'use strict';const fs=require('fs'),path=require('path'),os=require('os'),session=require('./session'),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{return{};}}function computeRunCost(a,b={}){const c=a['metrics']||{},d=c['effort_log']||[],e=c['input_tokens_est']||0x0,f=c['output_tokens_est']||0x0,g=[],h=d['length']||0x1,i=Math['ceil'](e/h),j=Math['ceil'](f/h);let k=0x0;const l=(d['length']?d:[{'persona':'unknown','effort':c['mode']||'lite','model':null}])['map'](n=>{const o=n['model']?b[n['model']]:null;let p=0x0;if(o)p=i/0xf4240*(o['input_per_m']||0x0)+j/0xf4240*(o['output_per_m']||0x0);else n['model']&&g['push'](n['model']);return k+=p,{'persona':n['persona'],'effort':n['effort'],'model':n['model'],'inTok':i,'outTok':j,'cost':Number(p['toFixed'](0x6))};});return{'total':Number(k['toFixed'](0x6)),'byExpert':l,'unpriced':[...new Set(g)]};}function report({project:a,all:all=![]}={}){const b=loadConfig(),c=b['pricing']||{},d=all?session['listProjects']():[a||session['getProjectId']()];if(!d['length'])return console['log']('\x0a\x20\x20No\x20sessions\x20found.\x0a'),{'projects':[]};console['log']('\x0a💰\x20Run\x20cost\x20report\x0a');const f=[];for(const g of d){const h=session['show']({'project':g});if(!h)continue;const {total:i,byExpert:j,unpriced:k}=computeRunCost(h,c),l=h['metrics']||{};console['log']('\x20\x20'+g),console['log']('\x20\x20\x20\x20mode:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'+(l['mode']??'n/a')),console['log']('\x20\x20\x20\x20llm_calls:\x20\x20\x20\x20\x20\x20\x20\x20\x20'+(l['llm_calls']??0x0)),console['log']('\x20\x20\x20\x20input_tokens_est:\x20\x20'+(l['input_tokens_est']??0x0)),console['log']('\x20\x20\x20\x20output_tokens_est:\x20'+(l['output_tokens_est']??0x0)),console['log']('\x20\x20\x20\x20est.\x20cost:\x20\x20\x20\x20\x20\x20\x20\x20\x20$'+i['toFixed'](0x4));l['deliberation_rounds']!=null&&(console['log']('\x20\x20\x20\x20deliberation_rounds:\x20'+l['deliberation_rounds']),console['log']('\x20\x20\x20\x20objections:\x20\x20\x20\x20\x20\x20\x20\x20'+(l['objections_raised']??0x0)+'\x20raised\x20/\x20'+(l['objections_resolved']??0x0)+'\x20resolved'));if(j['length']&&(j['length']>0x1||j[0x0]['model']))for(const n of j){console['log']('\x20\x20\x20\x20\x20\x20•\x20'+n['persona']+'\x20['+n['effort']+']\x20'+(n['model']||'n/a')+'\x20→\x20$'+n['cost']['toFixed'](0x4));}k['length']&&console['log']('\x20\x20\x20\x20unpriced\x20models:\x20\x20\x20'+k['join'](',\x20')+'\x20(add\x20to\x20config.pricing)'),console['log'](''),f['push']({'project':g,'total':i,'byExpert':j,'unpriced':k});}return{'projects':f};}module['exports']={'computeRunCost':computeRunCost,'report':report,'loadConfig':loadConfig,'CONFIG_FILE':CONFIG_FILE};
1
+ 'use strict';
2
+
3
+ /**
4
+ * $-cost reporting for persona runs (v1.10).
5
+ *
6
+ * Reads the `metrics` block from session-state.json (tokens + effort_log) and
7
+ * multiplies by per-model pricing from ~/.analyzthis_design/config.json:
8
+ *
9
+ * config.pricing = {
10
+ * "<model-id>": { "input_per_m": 1.25, "output_per_m": 10 },
11
+ * ...
12
+ * }
13
+ *
14
+ * Missing pricing for a model → that call contributes $0 (best-effort: we never
15
+ * block a report because a price wasn't seeded). The CLI `cost` command prints
16
+ * the per-run breakdown; this module just computes it so it can be reused.
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const os = require('os');
22
+ const session = require('./session');
23
+
24
+ const CONFIG_FILE = path.join(os.homedir(), '.analyzthis_design', 'config.json');
25
+
26
+ function loadConfig() {
27
+ if (!fs.existsSync(CONFIG_FILE)) return {};
28
+ try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
29
+ catch { return {}; }
30
+ }
31
+
32
+ /**
33
+ * Compute the $ cost of one run from its session state.
34
+ * @param {object} state - a session-state.json object (with a `metrics` block)
35
+ * @param {object} pricing - config.pricing map: { "<model>": { input_per_m, output_per_m } }
36
+ * @returns {{ total: number, byExpert: Array<{persona, effort, model, inTok, outTok, cost}>, unpriced: string[] }}
37
+ */
38
+ function computeRunCost(state, pricing = {}) {
39
+ const m = state.metrics || {};
40
+ const log = m.effort_log || [];
41
+ const inTokTotal = m.input_tokens_est || 0;
42
+ const outTokTotal = m.output_tokens_est || 0;
43
+ const unpriced = [];
44
+
45
+ // If there's an effort_log, attribute tokens evenly across the logged calls
46
+ // (we don't store per-call token counts, only the run total). This keeps the
47
+ // breakdown useful without inflating the session-state schema further.
48
+ const calls = log.length || 1;
49
+ const inPerCall = Math.ceil(inTokTotal / calls);
50
+ const outPerCall = Math.ceil(outTokTotal / calls);
51
+
52
+ let total = 0;
53
+ const byExpert = (log.length ? log : [{ persona: 'unknown', effort: m.mode || 'lite', model: null }]).map((entry) => {
54
+ const price = entry.model ? pricing[entry.model] : null;
55
+ let cost = 0;
56
+ if (price) {
57
+ cost = (inPerCall / 1e6) * (price.input_per_m || 0) + (outPerCall / 1e6) * (price.output_per_m || 0);
58
+ } else if (entry.model) {
59
+ unpriced.push(entry.model);
60
+ }
61
+ total += cost;
62
+ return {
63
+ persona: entry.persona,
64
+ effort: entry.effort,
65
+ model: entry.model,
66
+ inTok: inPerCall,
67
+ outTok: outPerCall,
68
+ cost: Number(cost.toFixed(6)),
69
+ };
70
+ });
71
+
72
+ return { total: Number(total.toFixed(6)), byExpert, unpriced: [...new Set(unpriced)] };
73
+ }
74
+
75
+ /**
76
+ * Print the cost report for one or all projects.
77
+ * @param {{ project?: string, all?: boolean }} opts
78
+ */
79
+ function report({ project, all = false } = {}) {
80
+ const config = loadConfig();
81
+ const pricing = config.pricing || {};
82
+ const projectIds = all ? session.listProjects() : [project || session.getProjectId()];
83
+ if (!projectIds.length) {
84
+ console.log('\n No sessions found.\n');
85
+ return { projects: [] };
86
+ }
87
+
88
+ console.log('\n💰 Run cost report\n');
89
+ const projects = [];
90
+ for (const projectId of projectIds) {
91
+ const state = session.show({ project: projectId });
92
+ if (!state) continue;
93
+ const { total, byExpert, unpriced } = computeRunCost(state, pricing);
94
+ const m = state.metrics || {};
95
+ console.log(` ${projectId}`);
96
+ console.log(` mode: ${m.mode ?? 'n/a'}`);
97
+ console.log(` llm_calls: ${m.llm_calls ?? 0}`);
98
+ console.log(` input_tokens_est: ${m.input_tokens_est ?? 0}`);
99
+ console.log(` output_tokens_est: ${m.output_tokens_est ?? 0}`);
100
+ console.log(` est. cost: $${total.toFixed(4)}`);
101
+ if (m.deliberation_rounds != null) {
102
+ console.log(` deliberation_rounds: ${m.deliberation_rounds}`);
103
+ console.log(` objections: ${m.objections_raised ?? 0} raised / ${m.objections_resolved ?? 0} resolved`);
104
+ }
105
+ if (byExpert.length && (byExpert.length > 1 || byExpert[0].model)) {
106
+ for (const e of byExpert) {
107
+ console.log(` • ${e.persona} [${e.effort}] ${e.model || 'n/a'} → $${e.cost.toFixed(4)}`);
108
+ }
109
+ }
110
+ if (unpriced.length) {
111
+ console.log(` unpriced models: ${unpriced.join(', ')} (add to config.pricing)`);
112
+ }
113
+ console.log('');
114
+ projects.push({ project: projectId, total, byExpert, unpriced });
115
+ }
116
+ return { projects };
117
+ }
118
+
119
+ module.exports = { computeRunCost, report, loadConfig, CONFIG_FILE };
package/dist/lib/dedup.js CHANGED
@@ -1 +1,167 @@
1
- 'use strict';var fs=require('fs'),path=require('path'),crypto=require('crypto'),CATEGORIES=['hierarchy','contrast','spacing','accessibility','business','feasibility','delight','ia','tokens','performance'];function hashClaim(a){return crypto['createHash']('sha1')['update'](a['toLowerCase']()['trim']())['digest']('hex')['slice'](0x0,0xc);}function categorizeClaim(a){var b=a['toLowerCase']();for(var c=0x0;c<CATEGORIES['length'];c++){if(b['indexOf'](CATEGORIES[c])!==-0x1)return CATEGORIES[c];}return'other';}function extractCoveredPoints(a,b){if(!a||typeof a!=='string')return[];var c=[],d=a['match'](/Top\s*2\s*fixes:[\s\S]*?(?:\n|$)/i);if(d){var e=d[0x0]['split']('\x0a');for(var f=0x0;f<e['length'];f++){var g=e[f]['trim'](),h=g['match'](/^\d+\.\s*(.+)$/);if(h){var n=h[0x1]['trim']();c['push']({'persona':b,'claim':n,'category':categorizeClaim(n),'key':hashClaim(n)});}}}var o=a['match'](/Hierarchy\[([A-F])\]/gi);if(o)for(var q=0x0;q<o['length'];q++){var r='Visual\x20hierarchy\x20grade:\x20'+o[q];c['push']({'persona':b,'claim':r,'category':'hierarchy','key':hashClaim(r)});}var s=a['match'](/(\d+(?:\.\d+)?)\s*\/\s*5/);if(s){var t='Overall\x20score:\x20'+s[0x0];c['push']({'persona':b,'claim':t,'category':'other','key':hashClaim(t)});}var u=a['match'](/objections?:[\s\S]*?(?:\n\n|```)/gi);if(u)for(var v=0x0;v<u['length'];v++){var w=u[v]['split']('\x0a');for(var x=0x0;x<w['length'];x++){var y=w[x]['trim'](),z=y['match'](/[-*]\s*(.+)/);if(z){var A=z[0x1]['trim']();A['length']>0xa&&c['push']({'persona':b,'claim':A,'category':categorizeClaim(A),'key':hashClaim(A)});}}}var B={},C=[];for(var D=0x0;D<c['length'];D++){!B[c[D]['key']]&&(B[c[D]['key']]=!![],C['push'](c[D]));}return C;}function tagFindings(a,b){if(!a||!a['length'])return[];var c={};for(var d=0x0;d<b['length'];d++){c[b[d]['key']]=b[d]['persona'];}var e=[];for(var g=0x0;g<a['length'];g++){var h=a[g],k=h['key']||hashClaim(h['claim']),l=c[k]||null;e['push']({'persona':h['persona'],'claim':h['claim'],'category':h['category']||categorizeClaim(h['claim']),'novel':!l,'duplicate_of':l});}return e;}function buildCoveredPointsSummaryFromPoints(a){if(!a||!a['length'])return'';var b={};for(var c=0x0;c<a['length'];c++){var d=a[c];if(!b[d['persona']])b[d['persona']]=[];b[d['persona']]['push'](d['claim']);}var e=['Already\x20covered\x20by\x20prior\x20personas\x20(do\x20NOT\x20repeat\x20—\x20only\x20add\x20NEW\x20insights):'],f=Object['keys'](b);for(var g=0x0;g<f['length'];g++){var h=f[g],j=b[h]['slice'](0x0,0x3);e['push']('\x20\x20'+h['charAt'](0x0)['toUpperCase']()+h['slice'](0x1)+'\x20—\x20'+j['join'](';\x20'));}return e['join']('\x0a');}function buildCoveredPointsSummary(a){if(!a)return'';var b=[],c=Object['keys'](a);for(var d=0x0;d<c['length'];d++){var e=c[d],f=a[e];if(f&&f['text']){var g=extractCoveredPoints(f['text'],e);for(var h=0x0;h<g['length'];h++){b['push'](g[h]);}}}return buildCoveredPointsSummaryFromPoints(b);}module['exports']={'extractCoveredPoints':extractCoveredPoints,'tagFindings':tagFindings,'buildCoveredPointsSummary':buildCoveredPointsSummary,'buildCoveredPointsSummaryFromPoints':buildCoveredPointsSummaryFromPoints,'categorizeClaim':categorizeClaim,'hashClaim':hashClaim};
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var crypto = require('crypto');
6
+
7
+ var CATEGORIES = [
8
+ 'hierarchy', 'contrast', 'spacing', 'accessibility',
9
+ 'business', 'feasibility', 'delight', 'ia', 'tokens', 'performance'
10
+ ];
11
+
12
+ function hashClaim(claim) {
13
+ return crypto.createHash('sha1').update(claim.toLowerCase().trim()).digest('hex').slice(0, 12);
14
+ }
15
+
16
+ function categorizeClaim(claim) {
17
+ var lower = claim.toLowerCase();
18
+ for (var i = 0; i < CATEGORIES.length; i++) {
19
+ if (lower.indexOf(CATEGORIES[i]) !== -1) return CATEGORIES[i];
20
+ }
21
+ return 'other';
22
+ }
23
+
24
+ function extractCoveredPoints(personaText, personaId) {
25
+ if (!personaText || typeof personaText !== 'string') return [];
26
+ var points = [];
27
+
28
+ var fixesMatch = personaText.match(/Top\s*2\s*fixes:[\s\S]*?(?:\n|$)/i);
29
+ if (fixesMatch) {
30
+ var lines = fixesMatch[0].split('\n');
31
+ for (var i = 0; i < lines.length; i++) {
32
+ var line = lines[i].trim();
33
+ var m = line.match(/^\d+\.\s*(.+)$/);
34
+ if (m) {
35
+ var claim = m[1].trim();
36
+ points.push({
37
+ persona: personaId,
38
+ claim: claim,
39
+ category: categorizeClaim(claim),
40
+ key: hashClaim(claim)
41
+ });
42
+ }
43
+ }
44
+ }
45
+
46
+ var hierarchyMatch = personaText.match(/Hierarchy\[([A-F])\]/gi);
47
+ if (hierarchyMatch) {
48
+ for (var j = 0; j < hierarchyMatch.length; j++) {
49
+ var claim2 = 'Visual hierarchy grade: ' + hierarchyMatch[j];
50
+ points.push({
51
+ persona: personaId,
52
+ claim: claim2,
53
+ category: 'hierarchy',
54
+ key: hashClaim(claim2)
55
+ });
56
+ }
57
+ }
58
+
59
+ var scoreMatch = personaText.match(/(\d+(?:\.\d+)?)\s*\/\s*5/);
60
+ if (scoreMatch) {
61
+ var claim3 = 'Overall score: ' + scoreMatch[0];
62
+ points.push({
63
+ persona: personaId,
64
+ claim: claim3,
65
+ category: 'other',
66
+ key: hashClaim(claim3)
67
+ });
68
+ }
69
+
70
+ var objectionMatches = personaText.match(/objections?:[\s\S]*?(?:\n\n|```)/gi);
71
+ if (objectionMatches) {
72
+ for (var k = 0; k < objectionMatches.length; k++) {
73
+ var objLines = objectionMatches[k].split('\n');
74
+ for (var l = 0; l < objLines.length; l++) {
75
+ var objLine = objLines[l].trim();
76
+ var om = objLine.match(/[-*]\s*(.+)/);
77
+ if (om) {
78
+ var claim4 = om[1].trim();
79
+ if (claim4.length > 10) {
80
+ points.push({
81
+ persona: personaId,
82
+ claim: claim4,
83
+ category: categorizeClaim(claim4),
84
+ key: hashClaim(claim4)
85
+ });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ var seen = {};
93
+ var unique = [];
94
+ for (var p = 0; p < points.length; p++) {
95
+ if (!seen[points[p].key]) {
96
+ seen[points[p].key] = true;
97
+ unique.push(points[p]);
98
+ }
99
+ }
100
+ return unique;
101
+ }
102
+
103
+ function tagFindings(findings, priorCoveredPoints) {
104
+ if (!findings || !findings.length) return [];
105
+ var priorKeys = {};
106
+ for (var i = 0; i < priorCoveredPoints.length; i++) {
107
+ priorKeys[priorCoveredPoints[i].key] = priorCoveredPoints[i].persona;
108
+ }
109
+ var result = [];
110
+ for (var j = 0; j < findings.length; j++) {
111
+ var f = findings[j];
112
+ var key = f.key || hashClaim(f.claim);
113
+ var duplicateOf = priorKeys[key] || null;
114
+ result.push({
115
+ persona: f.persona,
116
+ claim: f.claim,
117
+ category: f.category || categorizeClaim(f.claim),
118
+ novel: !duplicateOf,
119
+ duplicate_of: duplicateOf
120
+ });
121
+ }
122
+ return result;
123
+ }
124
+
125
+ function buildCoveredPointsSummaryFromPoints(points) {
126
+ if (!points || !points.length) return '';
127
+ var byPersona = {};
128
+ for (var i = 0; i < points.length; i++) {
129
+ var pt = points[i];
130
+ if (!byPersona[pt.persona]) byPersona[pt.persona] = [];
131
+ byPersona[pt.persona].push(pt.claim);
132
+ }
133
+ var lines = ['Already covered by prior personas (do NOT repeat — only add NEW insights):'];
134
+ var personas = Object.keys(byPersona);
135
+ for (var m = 0; m < personas.length; m++) {
136
+ var pers = personas[m];
137
+ var claims = byPersona[pers].slice(0, 3);
138
+ lines.push(' ' + pers.charAt(0).toUpperCase() + pers.slice(1) + ' — ' + claims.join('; '));
139
+ }
140
+ return lines.join('\n');
141
+ }
142
+
143
+ function buildCoveredPointsSummary(personaOutputs) {
144
+ if (!personaOutputs) return '';
145
+ var allPoints = [];
146
+ var ids = Object.keys(personaOutputs);
147
+ for (var i = 0; i < ids.length; i++) {
148
+ var id = ids[i];
149
+ var entry = personaOutputs[id];
150
+ if (entry && entry.text) {
151
+ var pts = extractCoveredPoints(entry.text, id);
152
+ for (var j = 0; j < pts.length; j++) {
153
+ allPoints.push(pts[j]);
154
+ }
155
+ }
156
+ }
157
+ return buildCoveredPointsSummaryFromPoints(allPoints);
158
+ }
159
+
160
+ module.exports = {
161
+ extractCoveredPoints: extractCoveredPoints,
162
+ tagFindings: tagFindings,
163
+ buildCoveredPointsSummary: buildCoveredPointsSummary,
164
+ buildCoveredPointsSummaryFromPoints: buildCoveredPointsSummaryFromPoints,
165
+ categorizeClaim: categorizeClaim,
166
+ hashClaim: hashClaim
167
+ };