analyzthis_design 2.0.1 → 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/README.md +10 -5
- package/agents/cards/evolve-check.md +38 -0
- package/agents/manifests/evolve-check.json +16 -0
- package/dist/README.md +10 -5
- 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 -1
- 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/evolve-check/SKILL.md +106 -0
- package/package.json +3 -6
- package/skills/evolve-check/SKILL.md +106 -0
package/dist/lib/outcome.js
CHANGED
|
@@ -1 +1,193 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var os = require('os');
|
|
6
|
+
var session = require('./session');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
|
+
|
|
9
|
+
function inferOutcome(opts) {
|
|
10
|
+
var project = opts.project || session.getProjectId();
|
|
11
|
+
var persona = opts.persona;
|
|
12
|
+
|
|
13
|
+
var state = session.show({ project: project });
|
|
14
|
+
if (!state) {
|
|
15
|
+
return { inferred: 'unknown', confidence: 0, reason: 'No session found' };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
var entry = state.persona_outputs && state.persona_outputs[persona];
|
|
19
|
+
if (!entry) {
|
|
20
|
+
return { inferred: 'unknown', confidence: 0, reason: 'No output for persona' };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var allProjects = session.listProjects();
|
|
24
|
+
var currentIdx = allProjects.indexOf(project);
|
|
25
|
+
if (currentIdx === -1 || currentIdx === allProjects.length - 1) {
|
|
26
|
+
return { inferred: 'unknown', confidence: 0, reason: 'No subsequent session to compare' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var nextProject = allProjects[currentIdx + 1];
|
|
30
|
+
var nextState = session.show({ project: nextProject });
|
|
31
|
+
if (!nextState) {
|
|
32
|
+
return { inferred: 'unknown', confidence: 0, reason: 'Next session not found' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var currentDsAtRisk = (state.digest && state.digest.ds_at_risk) || [];
|
|
36
|
+
var nextDsAtRisk = (nextState.digest && nextState.digest.ds_at_risk) || [];
|
|
37
|
+
|
|
38
|
+
var sameDsIssues = currentDsAtRisk.filter(function(item) {
|
|
39
|
+
return nextDsAtRisk.indexOf(item) !== -1;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
var nextVerdict = nextState.synthesis && nextState.synthesis.verdict;
|
|
43
|
+
var nextFeedback = nextState.feedback_log || [];
|
|
44
|
+
var negativeFeedback = nextFeedback.filter(function(f) {
|
|
45
|
+
return f.persona === persona && f.satisfied === false;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (nextVerdict === 'SHIP' && sameDsIssues.length === 0 && negativeFeedback.length === 0) {
|
|
49
|
+
return {
|
|
50
|
+
inferred: 'shipped',
|
|
51
|
+
confidence: 0.7,
|
|
52
|
+
reason: 'Next session SHIP with no recurring DS issues or negative feedback'
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (nextVerdict === 'REVISE' && sameDsIssues.length > 0) {
|
|
57
|
+
return {
|
|
58
|
+
inferred: 'missed',
|
|
59
|
+
confidence: 0.6,
|
|
60
|
+
reason: 'Next session REVISE with same DS issues persisting: ' + sameDsIssues.join(', ')
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (nextVerdict === 'BLOCK' && sameDsIssues.length > 0) {
|
|
65
|
+
return {
|
|
66
|
+
inferred: 'blocked_correctly',
|
|
67
|
+
confidence: 0.6,
|
|
68
|
+
reason: 'Next session BLOCK on same issues'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (negativeFeedback.length > 0) {
|
|
73
|
+
return {
|
|
74
|
+
inferred: 'missed',
|
|
75
|
+
confidence: 0.5,
|
|
76
|
+
reason: 'Negative feedback in next session'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { inferred: 'unknown', confidence: 0, reason: 'Could not determine outcome' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function inferAllOutcomes(opts) {
|
|
84
|
+
var windowDays = opts.windowDays || 7;
|
|
85
|
+
var cutoff = Date.now() - (windowDays * 24 * 60 * 60 * 1000);
|
|
86
|
+
var projectIds = session.listProjects();
|
|
87
|
+
var inferred = 0;
|
|
88
|
+
var confirmed = 0;
|
|
89
|
+
var unknown = 0;
|
|
90
|
+
var personas = ['arjun', 'meera', 'priya', 'zara', 'noor', 'anuj', 'raj'];
|
|
91
|
+
|
|
92
|
+
for (var i = 0; i < projectIds.length; i++) {
|
|
93
|
+
var pid = projectIds[i];
|
|
94
|
+
var state = session.show({ project: pid });
|
|
95
|
+
if (!state) continue;
|
|
96
|
+
if (state.updated_at && new Date(state.updated_at).getTime() < cutoff) continue;
|
|
97
|
+
|
|
98
|
+
for (var p = 0; p < personas.length; p++) {
|
|
99
|
+
var pers = personas[p];
|
|
100
|
+
var entry = state.persona_outputs && state.persona_outputs[pers];
|
|
101
|
+
if (!entry || entry.accepted !== true) continue;
|
|
102
|
+
|
|
103
|
+
var hasConfirmed = state.outcome && state.outcome.confirmed && state.outcome.confirmed[pers];
|
|
104
|
+
var hasInferred = state.outcome && state.outcome.inferred && state.outcome.inferred[pers];
|
|
105
|
+
|
|
106
|
+
if (hasConfirmed) {
|
|
107
|
+
confirmed++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var result = inferOutcome({ project: pid, persona: pers });
|
|
112
|
+
if (result.inferred !== 'unknown') {
|
|
113
|
+
var outcomePatch = {
|
|
114
|
+
inferred: { ...(state.outcome && state.outcome.inferred ? state.outcome.inferred : {}) },
|
|
115
|
+
confirmed: { ...(state.outcome && state.outcome.confirmed ? state.outcome.confirmed : {}) },
|
|
116
|
+
};
|
|
117
|
+
outcomePatch.inferred[pers] = {
|
|
118
|
+
value: result.inferred,
|
|
119
|
+
confidence: result.confidence,
|
|
120
|
+
reason: result.reason,
|
|
121
|
+
at: new Date().toISOString()
|
|
122
|
+
};
|
|
123
|
+
session.update({ project: pid, patch: { outcome: outcomePatch } });
|
|
124
|
+
inferred++;
|
|
125
|
+
} else {
|
|
126
|
+
unknown++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { inferred: inferred, confirmed: confirmed, unknown: unknown };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function confirmOutcome(opts) {
|
|
135
|
+
var project = opts.project || session.getProjectId();
|
|
136
|
+
var persona = opts.persona;
|
|
137
|
+
var outcome = opts.outcome;
|
|
138
|
+
|
|
139
|
+
var validOutcomes = ['shipped', 'revised', 'blocked_correctly', 'missed'];
|
|
140
|
+
if (validOutcomes.indexOf(outcome) === -1) {
|
|
141
|
+
throw new Error('Invalid outcome. Must be one of: ' + validOutcomes.join(', '));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
var state = session.show({ project: project });
|
|
145
|
+
if (!state) throw new Error('No session found');
|
|
146
|
+
|
|
147
|
+
var outcomePatch = {
|
|
148
|
+
inferred: { ...(state.outcome && state.outcome.inferred ? state.outcome.inferred : {}) },
|
|
149
|
+
confirmed: { ...(state.outcome && state.outcome.confirmed ? state.outcome.confirmed : {}) },
|
|
150
|
+
};
|
|
151
|
+
outcomePatch.confirmed[persona] = {
|
|
152
|
+
value: outcome,
|
|
153
|
+
at: new Date().toISOString()
|
|
154
|
+
};
|
|
155
|
+
delete outcomePatch.inferred[persona];
|
|
156
|
+
|
|
157
|
+
session.update({ project: project, patch: { outcome: outcomePatch } });
|
|
158
|
+
return { updated: true };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function listPendingConfirmations() {
|
|
162
|
+
var projectIds = session.listProjects();
|
|
163
|
+
var pending = [];
|
|
164
|
+
|
|
165
|
+
for (var i = 0; i < projectIds.length; i++) {
|
|
166
|
+
var pid = projectIds[i];
|
|
167
|
+
var state = session.show({ project: pid });
|
|
168
|
+
if (!state || !state.outcome || !state.outcome.inferred) continue;
|
|
169
|
+
|
|
170
|
+
var inferred = state.outcome.inferred;
|
|
171
|
+
var confirmed = state.outcome.confirmed || {};
|
|
172
|
+
|
|
173
|
+
for (var persona in inferred) {
|
|
174
|
+
if (inferred[persona] && !confirmed[persona]) {
|
|
175
|
+
pending.push({
|
|
176
|
+
project: pid,
|
|
177
|
+
persona: persona,
|
|
178
|
+
inferred: inferred[persona].value,
|
|
179
|
+
reason: inferred[persona].reason
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return pending;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = {
|
|
189
|
+
inferOutcome: inferOutcome,
|
|
190
|
+
inferAllOutcomes: inferAllOutcomes,
|
|
191
|
+
confirmOutcome: confirmOutcome,
|
|
192
|
+
listPendingConfirmations: listPendingConfirmations
|
|
193
|
+
};
|
package/dist/lib/platforms.js
CHANGED
|
@@ -1 +1,166 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-platform skill install targets for vibe-coding tools.
|
|
5
|
+
* SKILL.md (Agent Skills open standard) is the shared format; layout differs:
|
|
6
|
+
* - dir: <root>/<skill>/SKILL.md (Cursor, Claude skills, Codex, Grok, Windsurf, agents)
|
|
7
|
+
* - flat: <root>/<skill>.md (legacy Claude Code commands/)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
const HOME = os.homedir();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the analyzthis_design package root from a file under lib/ or dist/lib/.
|
|
18
|
+
*/
|
|
19
|
+
function resolvePackageRoot(fromDirname) {
|
|
20
|
+
const candidates = [
|
|
21
|
+
path.join(fromDirname, '..', '..', '..'),
|
|
22
|
+
path.join(fromDirname, '..', '..'),
|
|
23
|
+
path.join(fromDirname, '..'),
|
|
24
|
+
];
|
|
25
|
+
for (const dir of candidates) {
|
|
26
|
+
try {
|
|
27
|
+
if (!fs.existsSync(path.join(dir, 'skills', 'arjun', 'SKILL.md'))) continue;
|
|
28
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
29
|
+
if (pkg.name === 'analyzthis_design') return dir;
|
|
30
|
+
} catch { /* try next */ }
|
|
31
|
+
}
|
|
32
|
+
return path.join(fromDirname, '..');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @type {Record<string, PlatformTarget>} */
|
|
36
|
+
const TARGETS = {
|
|
37
|
+
cursor: {
|
|
38
|
+
id: 'cursor',
|
|
39
|
+
label: 'Cursor',
|
|
40
|
+
root: path.join(HOME, '.cursor', 'skills'),
|
|
41
|
+
layout: 'dir',
|
|
42
|
+
invokePrefix: '/',
|
|
43
|
+
usageHint: 'Wireframes: /ux-ideator or /noor — Critique: /persona-orchestrator',
|
|
44
|
+
},
|
|
45
|
+
claude: {
|
|
46
|
+
id: 'claude',
|
|
47
|
+
label: 'Claude Code',
|
|
48
|
+
root: path.join(HOME, '.claude', 'skills'),
|
|
49
|
+
layout: 'dir',
|
|
50
|
+
also: { root: path.join(HOME, '.claude', 'commands'), layout: 'flat' },
|
|
51
|
+
invokePrefix: '/',
|
|
52
|
+
usageHint: 'Wireframes: /ux-ideator or /noor — Critique: /persona-orchestrator',
|
|
53
|
+
},
|
|
54
|
+
codex: {
|
|
55
|
+
id: 'codex',
|
|
56
|
+
label: 'Codex CLI',
|
|
57
|
+
root: path.join(HOME, '.codex', 'skills'),
|
|
58
|
+
layout: 'dir',
|
|
59
|
+
invokePrefix: '',
|
|
60
|
+
usageHint: 'Reference skills in AGENTS.md — wireframes: ux-ideator/noor; critique: persona-orchestrator',
|
|
61
|
+
},
|
|
62
|
+
grok: {
|
|
63
|
+
id: 'grok',
|
|
64
|
+
label: 'Grok Build (xAI)',
|
|
65
|
+
root: path.join(HOME, '.grok', 'skills'),
|
|
66
|
+
layout: 'dir',
|
|
67
|
+
invokePrefix: '/',
|
|
68
|
+
usageHint: 'Wireframes: /ux-ideator or /noor — Critique: /persona-orchestrator',
|
|
69
|
+
},
|
|
70
|
+
windsurf: {
|
|
71
|
+
id: 'windsurf',
|
|
72
|
+
label: 'Windsurf Cascade',
|
|
73
|
+
root: path.join(HOME, '.codeium', 'windsurf', 'skills'),
|
|
74
|
+
layout: 'dir',
|
|
75
|
+
invokePrefix: '@',
|
|
76
|
+
usageHint: 'Wireframes: @ux-ideator or @noor — Critique: @persona-orchestrator',
|
|
77
|
+
},
|
|
78
|
+
agents: {
|
|
79
|
+
id: 'agents',
|
|
80
|
+
label: 'Cross-agent (~/.agents/skills)',
|
|
81
|
+
root: path.join(HOME, '.agents', 'skills'),
|
|
82
|
+
layout: 'dir',
|
|
83
|
+
invokePrefix: '/',
|
|
84
|
+
usageHint: 'Discovered by Cursor, Grok, Windsurf, and other Agent Skills–compatible hosts',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const ALL_TARGET_IDS = ['cursor', 'claude', 'codex', 'grok', 'windsurf', 'agents'];
|
|
89
|
+
|
|
90
|
+
const TARGET_DIRS = Object.fromEntries(
|
|
91
|
+
Object.entries(TARGETS).map(([id, t]) => [id, t.root])
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
function resolveTargets(target) {
|
|
95
|
+
if (!target || target === 'cursor') return ['cursor'];
|
|
96
|
+
if (target === 'all') return ALL_TARGET_IDS.slice();
|
|
97
|
+
if (TARGETS[target]) return [target];
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function knowledgeBankPaths(targetId) {
|
|
102
|
+
const t = TARGETS[targetId];
|
|
103
|
+
if (!t) return [];
|
|
104
|
+
const paths = [];
|
|
105
|
+
if (t.layout === 'dir') {
|
|
106
|
+
paths.push(path.join(t.root, 'knowledge-bank', 'SKILL.md'));
|
|
107
|
+
} else {
|
|
108
|
+
paths.push(path.join(t.root, 'knowledge-bank.md'));
|
|
109
|
+
}
|
|
110
|
+
if (t.also) {
|
|
111
|
+
if (t.also.layout === 'dir') {
|
|
112
|
+
paths.push(path.join(t.also.root, 'knowledge-bank', 'SKILL.md'));
|
|
113
|
+
} else {
|
|
114
|
+
paths.push(path.join(t.also.root, 'knowledge-bank.md'));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return paths;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const PLATFORM_SKILL_LOOKUP = `
|
|
121
|
+
Look for sibling skills (knowledge-bank, arjun, …) relative to this skill's install location first.
|
|
122
|
+
Otherwise try, in order:
|
|
123
|
+
- ~/.cursor/skills/<name>/SKILL.md
|
|
124
|
+
- ~/.claude/skills/<name>/SKILL.md
|
|
125
|
+
- ~/.claude/commands/<name>.md
|
|
126
|
+
- ~/.codex/skills/<name>/SKILL.md
|
|
127
|
+
- ~/.grok/skills/<name>/SKILL.md
|
|
128
|
+
- ~/.codeium/windsurf/skills/<name>/SKILL.md
|
|
129
|
+
- ~/.agents/skills/<name>/SKILL.md
|
|
130
|
+
`.trim();
|
|
131
|
+
|
|
132
|
+
/** Host-aware read hint for SKILL.md references in chat workflows */
|
|
133
|
+
function skillReadHint(skillName) {
|
|
134
|
+
return [
|
|
135
|
+
`Read \`${skillName}\` from your host skills dir (sibling preferred), e.g.`,
|
|
136
|
+
`\`~/.cursor/skills/${skillName}/SKILL.md\`,`,
|
|
137
|
+
`\`~/.claude/skills/${skillName}/SKILL.md\`,`,
|
|
138
|
+
`\`~/.grok/skills/${skillName}/SKILL.md\`,`,
|
|
139
|
+
`\`~/.agents/skills/${skillName}/SKILL.md\`,`,
|
|
140
|
+
`or legacy \`~/.claude/commands/${skillName}.md\`.`,
|
|
141
|
+
].join(' ');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const DELIBERATION_PROTOCOL_READ = skillReadHint('deliberation-protocol');
|
|
145
|
+
|
|
146
|
+
function listInstalledTargets() {
|
|
147
|
+
return ALL_TARGET_IDS.filter((id) => {
|
|
148
|
+
const t = TARGETS[id];
|
|
149
|
+
try {
|
|
150
|
+
return fs.existsSync(t.root) || (t.also && fs.existsSync(t.also.root));
|
|
151
|
+
} catch { return false; }
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
TARGETS,
|
|
157
|
+
TARGET_DIRS,
|
|
158
|
+
ALL_TARGET_IDS,
|
|
159
|
+
resolveTargets,
|
|
160
|
+
knowledgeBankPaths,
|
|
161
|
+
PLATFORM_SKILL_LOOKUP,
|
|
162
|
+
skillReadHint,
|
|
163
|
+
DELIBERATION_PROTOCOL_READ,
|
|
164
|
+
listInstalledTargets,
|
|
165
|
+
resolvePackageRoot,
|
|
166
|
+
};
|
package/dist/lib/provider.js
CHANGED
|
@@ -1 +1,57 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve LLM provider — prefers explicit choice, then API keys, then host (Devi).
|
|
5
|
+
*
|
|
6
|
+
* v2.0 adds chunk-oriented providers: ollama, groq, together, openrouter, deepseek.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const PROVIDERS = ['anthropic', 'openai', 'google', 'zai', 'ollama', 'groq', 'together', 'openrouter', 'deepseek', 'host'];
|
|
10
|
+
|
|
11
|
+
function hasKey(provider) {
|
|
12
|
+
switch (provider) {
|
|
13
|
+
case 'anthropic': return !!process.env.ANTHROPIC_API_KEY;
|
|
14
|
+
case 'openai': return !!process.env.OPENAI_API_KEY;
|
|
15
|
+
case 'google': return !!(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY);
|
|
16
|
+
case 'zai': return !!(process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY);
|
|
17
|
+
case 'ollama': return true; // availability is checked at runtime via localhost
|
|
18
|
+
case 'groq': return !!process.env.GROQ_API_KEY;
|
|
19
|
+
case 'together': return !!process.env.TOGETHER_API_KEY;
|
|
20
|
+
case 'openrouter': return !!process.env.OPENROUTER_API_KEY;
|
|
21
|
+
case 'deepseek': return !!process.env.DEEPSEEK_API_KEY;
|
|
22
|
+
case 'host': return true;
|
|
23
|
+
default: return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pick default provider for orchestrator runs.
|
|
29
|
+
* @param {object} [config]
|
|
30
|
+
* @param {string|null} [explicit]
|
|
31
|
+
*/
|
|
32
|
+
function resolveDefaultProvider(config, explicit) {
|
|
33
|
+
if (explicit && explicit !== 'auto') return explicit;
|
|
34
|
+
if (process.env.ANALYZTHIS_PROVIDER) return process.env.ANALYZTHIS_PROVIDER;
|
|
35
|
+
|
|
36
|
+
const fromConfig = config?.orchestrator?.provider;
|
|
37
|
+
if (fromConfig && fromConfig !== 'auto' && hasKey(fromConfig)) return fromConfig;
|
|
38
|
+
|
|
39
|
+
for (const p of ['anthropic', 'openai', 'google', 'zai']) {
|
|
40
|
+
if (hasKey(p)) return p;
|
|
41
|
+
}
|
|
42
|
+
return 'host';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Map effort tier provider — fall back to host when API key missing.
|
|
47
|
+
*/
|
|
48
|
+
function resolveTierProvider(tierProvider, defaultProvider) {
|
|
49
|
+
if (tierProvider === 'host') return 'host';
|
|
50
|
+
if (tierProvider === 'ollama') return 'ollama';
|
|
51
|
+
if (tierProvider && hasKey(tierProvider)) return tierProvider;
|
|
52
|
+
if (defaultProvider === 'host') return 'host';
|
|
53
|
+
if (hasKey(defaultProvider)) return defaultProvider;
|
|
54
|
+
return 'host';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { PROVIDERS, hasKey, resolveDefaultProvider, resolveTierProvider };
|
|
@@ -1 +1,83 @@
|
|
|
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 cache = require('./cache');
|
|
8
|
+
var { resolvePackageRoot } = require('./platforms');
|
|
9
|
+
|
|
10
|
+
var PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
11
|
+
var AGENTS_DIR = path.join(PACKAGE_ROOT, 'agents');
|
|
12
|
+
var CONFIG_FILE = path.join(os.homedir(), '.analyzthis_design', 'config.json');
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
16
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
|
|
17
|
+
catch { return {}; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadJson(filePath) {
|
|
21
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadManifest(id) {
|
|
25
|
+
var p = path.join(AGENTS_DIR, 'manifests', id + '.json');
|
|
26
|
+
if (!fs.existsSync(p)) return null;
|
|
27
|
+
return loadJson(p);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function loadExpanderCard() {
|
|
31
|
+
var cardPath = path.join(AGENTS_DIR, 'cards', 'query-expander.md');
|
|
32
|
+
if (!fs.existsSync(cardPath)) return '';
|
|
33
|
+
return fs.readFileSync(cardPath, 'utf8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function expandQuery(opts) {
|
|
37
|
+
var task = opts.task || '';
|
|
38
|
+
var personaId = opts.personaId;
|
|
39
|
+
var callLlmFn = opts.callLlmFn;
|
|
40
|
+
var provider = opts.provider || 'host';
|
|
41
|
+
var model = opts.model || 'devi';
|
|
42
|
+
|
|
43
|
+
if (!personaId || !callLlmFn) {
|
|
44
|
+
return Promise.resolve({ terms: [], cacheHit: false });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var manifest = loadManifest(personaId);
|
|
48
|
+
var lens = manifest && manifest.allowed_jobs ? manifest.allowed_jobs.join('; ') : '';
|
|
49
|
+
|
|
50
|
+
var cacheKey = 'queryexpand:' + personaId + ':' + crypto.createHash('sha1').update(task).digest('hex').slice(0, 12);
|
|
51
|
+
var cached = cache.get(cacheKey);
|
|
52
|
+
if (cached) return Promise.resolve({ terms: cached, cacheHit: true });
|
|
53
|
+
|
|
54
|
+
var system = loadExpanderCard();
|
|
55
|
+
var user = 'Task: ' + task + '\nPersona: ' + personaId + '\nPersona lens: ' + lens + '\n\nReturn JSON array of 5-10 search terms only.';
|
|
56
|
+
|
|
57
|
+
return callLlmFn({
|
|
58
|
+
provider: provider,
|
|
59
|
+
model: model,
|
|
60
|
+
system: system,
|
|
61
|
+
user: user,
|
|
62
|
+
maxTokens: 200,
|
|
63
|
+
personaId: 'query-expander',
|
|
64
|
+
hostContext: null
|
|
65
|
+
}).then(function(text) {
|
|
66
|
+
try {
|
|
67
|
+
var terms = JSON.parse(text.trim());
|
|
68
|
+
if (!Array.isArray(terms)) terms = [];
|
|
69
|
+
terms = terms.filter(function(t) { return typeof t === 'string' && t.length > 0; }).slice(0, 10);
|
|
70
|
+
cache.set(cacheKey, terms);
|
|
71
|
+
return { terms: terms, cacheHit: false };
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return { terms: [], cacheHit: false };
|
|
74
|
+
}
|
|
75
|
+
}).catch(function(err) {
|
|
76
|
+
return { terms: [], cacheHit: false };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
expandQuery: expandQuery,
|
|
82
|
+
loadExpanderCard: loadExpanderCard
|
|
83
|
+
};
|
package/dist/lib/ranker.js
CHANGED
|
@@ -1 +1,105 @@
|
|
|
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 cache = require('./cache');
|
|
8
|
+
var { resolvePackageRoot } = require('./platforms');
|
|
9
|
+
|
|
10
|
+
var PACKAGE_ROOT = resolvePackageRoot(__dirname);
|
|
11
|
+
var AGENTS_DIR = path.join(PACKAGE_ROOT, 'agents');
|
|
12
|
+
var CONFIG_FILE = path.join(os.homedir(), '.analyzthis_design', 'config.json');
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
16
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
|
|
17
|
+
catch { return {}; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadJson(filePath) {
|
|
21
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadManifest(id) {
|
|
25
|
+
var p = path.join(AGENTS_DIR, 'manifests', id + '.json');
|
|
26
|
+
if (!fs.existsSync(p)) return null;
|
|
27
|
+
return loadJson(p);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function loadRankerCard() {
|
|
31
|
+
var cardPath = path.join(AGENTS_DIR, 'cards', 'ranker.md');
|
|
32
|
+
if (!fs.existsSync(cardPath)) return '';
|
|
33
|
+
return fs.readFileSync(cardPath, 'utf8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function rankCandidates(opts) {
|
|
37
|
+
var task = opts.task || '';
|
|
38
|
+
var personaId = opts.personaId;
|
|
39
|
+
var candidates = opts.candidates || [];
|
|
40
|
+
var callLlmFn = opts.callLlmFn;
|
|
41
|
+
var provider = opts.provider || 'host';
|
|
42
|
+
var model = opts.model || 'devi';
|
|
43
|
+
|
|
44
|
+
if (!personaId || !callLlmFn || !candidates.length) {
|
|
45
|
+
return Promise.resolve({ ranked: candidates.slice(0, 5), cacheHit: false });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var manifest = loadManifest(personaId);
|
|
49
|
+
var lens = manifest && manifest.allowed_jobs ? manifest.allowed_jobs.join('; ') : '';
|
|
50
|
+
|
|
51
|
+
var cacheInput = { task: task, candidates: candidates.map(function(c) { return c.content; }) };
|
|
52
|
+
var cacheKey = 'rank:' + personaId + ':' + crypto.createHash('sha1').update(JSON.stringify(cacheInput)).digest('hex').slice(0, 12);
|
|
53
|
+
var cached = cache.get(cacheKey);
|
|
54
|
+
if (cached) return Promise.resolve({ ranked: cached, cacheHit: true });
|
|
55
|
+
|
|
56
|
+
var candidateList = '';
|
|
57
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
58
|
+
var c = candidates[i];
|
|
59
|
+
candidateList += (i + 1) + '. [' + c.source + (c.row ? ' row ' + c.row : '') + ': "' + c.content.slice(0, 200) + '"]\n';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var system = loadRankerCard();
|
|
63
|
+
var user = 'Task: ' + task + '\nPersona: ' + personaId + '\nPersona lens: ' + lens + '\n\nCandidates:\n' + candidateList + '\nReturn JSON array of candidate numbers (1-indexed) in priority order (most relevant first), max 5.';
|
|
64
|
+
|
|
65
|
+
return callLlmFn({
|
|
66
|
+
provider: provider,
|
|
67
|
+
model: model,
|
|
68
|
+
system: system,
|
|
69
|
+
user: user,
|
|
70
|
+
maxTokens: 200,
|
|
71
|
+
personaId: 'ranker',
|
|
72
|
+
hostContext: null
|
|
73
|
+
}).then(function(text) {
|
|
74
|
+
try {
|
|
75
|
+
var ranks = JSON.parse(text.trim());
|
|
76
|
+
if (!Array.isArray(ranks)) ranks = [];
|
|
77
|
+
var ranked = [];
|
|
78
|
+
for (var j = 0; j < ranks.length && j < 5; j++) {
|
|
79
|
+
var idx = ranks[j] - 1;
|
|
80
|
+
if (idx >= 0 && idx < candidates.length) {
|
|
81
|
+
var rc = candidates[idx];
|
|
82
|
+
ranked.push({
|
|
83
|
+
type: rc.type,
|
|
84
|
+
source: rc.source,
|
|
85
|
+
row: rc.row,
|
|
86
|
+
content: rc.content,
|
|
87
|
+
rank: j + 1,
|
|
88
|
+
reason: ''
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
cache.set(cacheKey, ranked);
|
|
93
|
+
return { ranked: ranked, cacheHit: false };
|
|
94
|
+
} catch (e) {
|
|
95
|
+
return { ranked: candidates.slice(0, 5), cacheHit: false };
|
|
96
|
+
}
|
|
97
|
+
}).catch(function(err) {
|
|
98
|
+
return { ranked: candidates.slice(0, 5), cacheHit: false };
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
rankCandidates: rankCandidates,
|
|
104
|
+
loadRankerCard: loadRankerCard
|
|
105
|
+
};
|