analyzthis_design 2.3.0 → 2.4.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 +2 -1
- package/README.md +39 -7
- package/dist/HOW-TO-USE.md +2 -1
- package/dist/README.md +39 -7
- package/dist/bin/cli.js +22 -4
- package/dist/lib/chunk-run.js +2 -0
- package/dist/lib/install.js +53 -0
- package/dist/lib/knowledge.js +49 -18
- package/dist/lib/mcp-server.js +206 -75
- package/dist/lib/orchestrator/run.js +5 -0
- package/dist/lib/session.js +33 -2
- package/dist/skills/anuj/SKILL.md +7 -0
- package/dist/skills/arjun/SKILL.md +5 -263
- package/dist/skills/arjun/references/lens.md +265 -0
- package/dist/skills/kavi/SKILL.md +7 -0
- package/dist/skills/knowledge-bank/SKILL.md +14 -116770
- package/dist/skills/meera/SKILL.md +7 -0
- package/dist/skills/noor/SKILL.md +7 -0
- package/dist/skills/persona-orchestrator/SKILL.md +5 -195
- package/dist/skills/persona-orchestrator/references/lens.md +197 -0
- package/dist/skills/priya/SKILL.md +7 -0
- package/dist/skills/raj/SKILL.md +7 -0
- package/dist/skills/receipt/SKILL.md +17 -0
- package/dist/skills/ux-story-gate/SKILL.md +5 -338
- package/dist/skills/ux-story-gate/references/lens.md +340 -0
- package/dist/skills/zara/SKILL.md +7 -0
- package/package.json +1 -1
- package/skills/anuj/SKILL.md +7 -0
- package/skills/arjun/SKILL.md +5 -263
- package/skills/arjun/references/lens.md +265 -0
- package/skills/kavi/SKILL.md +7 -0
- package/skills/knowledge-bank/SKILL.md +14 -116770
- package/skills/meera/SKILL.md +7 -0
- package/skills/noor/SKILL.md +7 -0
- package/skills/persona-orchestrator/SKILL.md +5 -195
- package/skills/persona-orchestrator/references/lens.md +197 -0
- package/skills/priya/SKILL.md +7 -0
- package/skills/raj/SKILL.md +7 -0
- package/skills/receipt/SKILL.md +17 -0
- package/skills/ux-story-gate/SKILL.md +5 -338
- package/skills/ux-story-gate/references/lens.md +340 -0
- package/skills/zara/SKILL.md +7 -0
package/dist/lib/mcp-server.js
CHANGED
|
@@ -248,16 +248,18 @@ var TOOLS = [
|
|
|
248
248
|
},
|
|
249
249
|
{
|
|
250
250
|
name: 'analyzthis_retrieve',
|
|
251
|
-
description: 'Retrieve
|
|
251
|
+
description: 'Retrieve on demand: CSV rows, a skill reference file, or one knowledge-bank persona slice. Do not load the full bank.',
|
|
252
252
|
inputSchema: {
|
|
253
253
|
type: 'object',
|
|
254
254
|
properties: {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
kind: { type: 'string', enum: ['csv', 'skill', 'knowledge'], description: 'csv (default), skill (references/*.md), or knowledge (one persona slice)' },
|
|
256
|
+
file: { type: 'string', description: 'CSV filename, or skill id (e.g. arjun), or knowledge/<persona>' },
|
|
257
|
+
column: { type: 'string', description: 'CSV column to filter on' },
|
|
258
|
+
keywords: { type: 'string', description: 'Comma-separated CSV keywords' },
|
|
259
|
+
limit: { type: 'number', description: 'Max CSV rows (default: 3)' },
|
|
260
|
+
persona: { type: 'string', description: 'Persona id for knowledge slice (arjun, zara, …)' },
|
|
261
|
+
ref: { type: 'string', description: 'Skill reference filename without path (e.g. lens.md)' },
|
|
259
262
|
},
|
|
260
|
-
required: ['file'],
|
|
261
263
|
},
|
|
262
264
|
},
|
|
263
265
|
{
|
|
@@ -285,6 +287,43 @@ var TOOLS = [
|
|
|
285
287
|
},
|
|
286
288
|
];
|
|
287
289
|
|
|
290
|
+
var ROUTER_TOOL = {
|
|
291
|
+
name: 'analyzthis_design',
|
|
292
|
+
description: 'Design team router. Set who to zara, arjun, noor, anuj, meera, priya, raj, kavi, orchestrator, ux-ideator, design-critic, ux-story-gate, design-director, or mood-board. Returns a short skill front — call analyzthis_retrieve for the rest.',
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {
|
|
296
|
+
who: { type: 'string', description: 'Persona or pass name (e.g. zara, arjun, orchestrator)' },
|
|
297
|
+
task: { type: 'string', description: 'What to design or critique' },
|
|
298
|
+
context: { type: 'string', description: 'Optional extra context' },
|
|
299
|
+
},
|
|
300
|
+
required: ['who', 'task'],
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
var RECEIPT_TOOL = {
|
|
305
|
+
name: 'analyzthis_receipt',
|
|
306
|
+
description: 'Show inferred token receipt for this project. Not a bill. Do not invent a dollar figure.',
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
project: { type: 'string', description: 'Optional project id override' },
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
function resolveCatalog(explicit) {
|
|
316
|
+
var raw = explicit || process.env.ANALYZTHIS_MCP_CATALOG || 'lite';
|
|
317
|
+
return String(raw).toLowerCase() === 'full' ? 'full' : 'lite';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function listedTools(catalog) {
|
|
321
|
+
if (resolveCatalog(catalog) === 'full') {
|
|
322
|
+
return TOOLS.concat([ROUTER_TOOL, RECEIPT_TOOL]);
|
|
323
|
+
}
|
|
324
|
+
return [ROUTER_TOOL, TOOLS.find(function (t) { return t.name === 'analyzthis_retrieve'; }), TOOLS.find(function (t) { return t.name === 'analyzthis_session'; }), RECEIPT_TOOL].filter(Boolean);
|
|
325
|
+
}
|
|
326
|
+
|
|
288
327
|
// ── Tool handlers ────────────────────────────────────────────────────────
|
|
289
328
|
|
|
290
329
|
function handleToolCall(name, args) {
|
|
@@ -326,6 +365,10 @@ function handleToolCall(name, args) {
|
|
|
326
365
|
return handleSession(args);
|
|
327
366
|
case 'analyzthis_system_prompt':
|
|
328
367
|
return handleSystemPrompt(args);
|
|
368
|
+
case 'analyzthis_design':
|
|
369
|
+
return handleRouter(args);
|
|
370
|
+
case 'analyzthis_receipt':
|
|
371
|
+
return handleReceipt(args);
|
|
329
372
|
|
|
330
373
|
default:
|
|
331
374
|
return { error: 'Unknown tool: ' + name };
|
|
@@ -333,7 +376,7 @@ function handleToolCall(name, args) {
|
|
|
333
376
|
}
|
|
334
377
|
|
|
335
378
|
function handleSinglePersona(personaId, args) {
|
|
336
|
-
var card =
|
|
379
|
+
var card = readSkillFront(personaId);
|
|
337
380
|
if (!card) return { error: 'Persona not found: ' + personaId };
|
|
338
381
|
|
|
339
382
|
var task = args.task || '';
|
|
@@ -351,11 +394,10 @@ function handleSinglePersona(personaId, args) {
|
|
|
351
394
|
'## Task\n' + task + '\n' +
|
|
352
395
|
(context ? '\n## Context\n' + context : '') +
|
|
353
396
|
extra + '\n\n' +
|
|
354
|
-
'
|
|
355
|
-
'Be specific — cite components, zones, and fixes. '
|
|
356
|
-
'Do not give generic advice.';
|
|
397
|
+
'Use the lite output contract. Call analyzthis_retrieve (kind=skill, file=' + personaId + ') only if you need the full lens. ' +
|
|
398
|
+
'Be specific — cite components, zones, and fixes. Do not give generic advice.';
|
|
357
399
|
|
|
358
|
-
return { prompt: prompt, persona: personaId, task: task };
|
|
400
|
+
return { prompt: prompt, persona: personaId, task: task, verdicts: 1 };
|
|
359
401
|
}
|
|
360
402
|
|
|
361
403
|
function handleCombinationPass(skillId, args) {
|
|
@@ -381,11 +423,11 @@ function handleCombinationPass(skillId, args) {
|
|
|
381
423
|
skill + '\n\n' +
|
|
382
424
|
'## Task\n' + task + '\n' +
|
|
383
425
|
(context ? '\n## Context\n' + context : '') + '\n\n' +
|
|
384
|
-
'Run
|
|
385
|
-
'
|
|
386
|
-
'
|
|
426
|
+
'Run the smallest useful team. Challenge the premise first. ' +
|
|
427
|
+
'Call analyzthis_retrieve (kind=skill, file=' + skillId + ') if the front is not enough. ' +
|
|
428
|
+
'Lite default: verdict + top 3 + one evidence line. Expand only if the user says expand.';
|
|
387
429
|
|
|
388
|
-
return { prompt: prompt, skill: skillId, task: task };
|
|
430
|
+
return { prompt: prompt, skill: skillId, task: task, persona: skillId, verdicts: 1 };
|
|
389
431
|
}
|
|
390
432
|
|
|
391
433
|
function handleMoodBoard(args) {
|
|
@@ -439,10 +481,13 @@ function handleCollect(args) {
|
|
|
439
481
|
}
|
|
440
482
|
|
|
441
483
|
function handleRetrieve(args) {
|
|
484
|
+
var kind = args.kind || 'csv';
|
|
485
|
+
if (kind === 'skill') return handleRetrieveSkill(args);
|
|
486
|
+
if (kind === 'knowledge') return handleRetrieveKnowledge(args);
|
|
442
487
|
try {
|
|
443
488
|
var retrieve = require('./retrieve');
|
|
444
489
|
var file = args.file;
|
|
445
|
-
if (!file) return { error: 'file is required' };
|
|
490
|
+
if (!file) return { error: 'file is required for CSV retrieve' };
|
|
446
491
|
var filters = [];
|
|
447
492
|
if (args.column && args.keywords) {
|
|
448
493
|
filters.push({ column: args.column, anyOf: args.keywords.split(',').map(function(s) { return s.trim(); }) });
|
|
@@ -464,6 +509,70 @@ function handleRetrieve(args) {
|
|
|
464
509
|
}
|
|
465
510
|
}
|
|
466
511
|
|
|
512
|
+
function handleRetrieveSkill(args) {
|
|
513
|
+
var id = String(args.file || args.persona || '').replace(/^skill\//, '');
|
|
514
|
+
if (!id) return { error: 'file (skill id) is required' };
|
|
515
|
+
var refName = args.ref || 'lens.md';
|
|
516
|
+
var refPath = path.join(PACKAGE_ROOT, 'skills', id, 'references', refName);
|
|
517
|
+
if (!fs.existsSync(refPath)) {
|
|
518
|
+
var dir = path.join(PACKAGE_ROOT, 'skills', id, 'references');
|
|
519
|
+
if (fs.existsSync(dir)) {
|
|
520
|
+
var names = fs.readdirSync(dir).filter(function (n) { return n.endsWith('.md'); });
|
|
521
|
+
if (names.length) refPath = path.join(dir, names[0]);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (!fs.existsSync(refPath)) {
|
|
525
|
+
var full = readSkill(id);
|
|
526
|
+
if (!full) return { error: 'No skill references for ' + id };
|
|
527
|
+
return { result: full, persona: id };
|
|
528
|
+
}
|
|
529
|
+
return { result: fs.readFileSync(refPath, 'utf8'), persona: id };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function handleRetrieveKnowledge(args) {
|
|
533
|
+
var persona = args.persona || String(args.file || '').replace(/^knowledge\//, '');
|
|
534
|
+
if (!persona) return { error: 'persona is required for knowledge retrieve' };
|
|
535
|
+
try {
|
|
536
|
+
var knowledge = require('./knowledge');
|
|
537
|
+
var sessionMod = require('./session');
|
|
538
|
+
var projectId = sessionMod.getProjectId();
|
|
539
|
+
var slicePath = path.join(knowledge.KNOWLEDGE_SLICE_ROOT, projectId, persona + '.md');
|
|
540
|
+
if (!fs.existsSync(slicePath)) {
|
|
541
|
+
return { result: 'No knowledge slice for ' + persona + '. Run collect/sync first. Do not load a full bank.' };
|
|
542
|
+
}
|
|
543
|
+
var text = fs.readFileSync(slicePath, 'utf8');
|
|
544
|
+
var cap = 6000;
|
|
545
|
+
if (text.length > cap) text = text.slice(0, cap) + '\n…[truncated — this is one slice, not the full bank]';
|
|
546
|
+
return { result: text, persona: persona };
|
|
547
|
+
} catch (e) {
|
|
548
|
+
return { error: e.message };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
var ROUTER_WHO = {
|
|
553
|
+
zara: 'persona', arjun: 'persona', meera: 'persona', priya: 'persona',
|
|
554
|
+
noor: 'persona', anuj: 'persona', raj: 'persona', kavi: 'persona',
|
|
555
|
+
orchestrator: 'persona-orchestrator', 'persona-orchestrator': 'persona-orchestrator',
|
|
556
|
+
'ux-ideator': 'ux-ideator', 'design-critic': 'design-critic',
|
|
557
|
+
'ux-story-gate': 'ux-story-gate', 'design-director': 'design-director',
|
|
558
|
+
'mood-board': 'mood-board',
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
function handleRouter(args) {
|
|
562
|
+
var who = String(args.who || '').toLowerCase().replace(/^analyzthis_/, '').replace(/_/g, '-');
|
|
563
|
+
if (!who) return { error: 'who is required (e.g. zara, arjun, orchestrator)' };
|
|
564
|
+
var mapped = ROUTER_WHO[who];
|
|
565
|
+
if (!mapped) return { error: 'Unknown who: ' + who + '. Use a persona or orchestrator / ux-ideator / design-critic / ux-story-gate / design-director / mood-board.' };
|
|
566
|
+
if (mapped === 'persona') return handleSinglePersona(who, args);
|
|
567
|
+
if (mapped === 'mood-board') return handleMoodBoard(args);
|
|
568
|
+
return handleCombinationPass(mapped, args);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function handleReceipt(args) {
|
|
572
|
+
var receipt = require('./receipt');
|
|
573
|
+
return { result: receipt.report({ project: args.project }) };
|
|
574
|
+
}
|
|
575
|
+
|
|
467
576
|
function handleSession(args) {
|
|
468
577
|
try {
|
|
469
578
|
var session = require('./session');
|
|
@@ -506,84 +615,86 @@ function handleSystemPrompt(args) {
|
|
|
506
615
|
|
|
507
616
|
// ── Helpers ──
|
|
508
617
|
|
|
509
|
-
function readCard(id) {
|
|
510
|
-
var p = path.join(PACKAGE_ROOT, 'agents', 'cards', id + '.md');
|
|
511
|
-
if (!fs.existsSync(p)) return null;
|
|
512
|
-
return fs.readFileSync(p, 'utf8');
|
|
513
|
-
}
|
|
514
|
-
|
|
515
618
|
function readSkill(id) {
|
|
516
619
|
var p = path.join(PACKAGE_ROOT, 'skills', id, 'SKILL.md');
|
|
517
620
|
if (!fs.existsSync(p)) return null;
|
|
518
621
|
return fs.readFileSync(p, 'utf8');
|
|
519
622
|
}
|
|
520
623
|
|
|
624
|
+
function readSkillFront(id) {
|
|
625
|
+
return readSkill(id);
|
|
626
|
+
}
|
|
627
|
+
|
|
521
628
|
// ── MCP config writer ──
|
|
522
629
|
|
|
523
|
-
function
|
|
524
|
-
var
|
|
525
|
-
|
|
630
|
+
function mergeMcpServer(configPath) {
|
|
631
|
+
var dir = path.dirname(configPath);
|
|
632
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
633
|
+
var config = {};
|
|
634
|
+
if (fs.existsSync(configPath)) {
|
|
635
|
+
try { config = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch (e) {}
|
|
636
|
+
}
|
|
637
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) config = {};
|
|
638
|
+
config.mcpServers = config.mcpServers || {};
|
|
639
|
+
config.mcpServers['analyzthis_design'] = {
|
|
640
|
+
command: 'npx',
|
|
641
|
+
args: ['analyzthis_design', 'mcp'],
|
|
642
|
+
env: { ANALYZTHIS_MCP_CATALOG: process.env.ANALYZTHIS_MCP_CATALOG || 'lite' },
|
|
643
|
+
};
|
|
644
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
645
|
+
return configPath;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function looksLikeProjectCwd() {
|
|
649
|
+
var cwd = process.cwd();
|
|
650
|
+
return fs.existsSync(path.join(cwd, 'package.json')) || fs.existsSync(path.join(cwd, '.git'));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function writeMcpConfig(target, opts) {
|
|
654
|
+
opts = opts || {};
|
|
655
|
+
if (opts.catalog) process.env.ANALYZTHIS_MCP_CATALOG = opts.catalog;
|
|
526
656
|
var home = os.homedir();
|
|
527
657
|
|
|
528
658
|
if (target === 'cursor') {
|
|
529
659
|
var cursorConfigPath = path.join(home, '.cursor', 'mcp.json');
|
|
530
|
-
|
|
531
|
-
if (!fs.existsSync(cursorDir)) fs.mkdirSync(cursorDir, { recursive: true });
|
|
532
|
-
var cursorConfig = {};
|
|
533
|
-
if (fs.existsSync(cursorConfigPath)) {
|
|
534
|
-
try { cursorConfig = JSON.parse(fs.readFileSync(cursorConfigPath, 'utf8')); } catch (e) {}
|
|
535
|
-
}
|
|
536
|
-
cursorConfig.mcpServers = cursorConfig.mcpServers || {};
|
|
537
|
-
cursorConfig.mcpServers['analyzthis_design'] = {
|
|
538
|
-
command: serverCmd,
|
|
539
|
-
args: serverArgs,
|
|
540
|
-
};
|
|
541
|
-
fs.writeFileSync(cursorConfigPath, JSON.stringify(cursorConfig, null, 2));
|
|
660
|
+
mergeMcpServer(cursorConfigPath);
|
|
542
661
|
console.log('Cursor MCP config written to ' + cursorConfigPath);
|
|
662
|
+
console.log('Default MCP catalog is lite (router + retrieve + session + receipt).');
|
|
663
|
+
console.log('Full 19-tool list: ANALYZTHIS_MCP_CATALOG=full or mcp --catalog full');
|
|
543
664
|
console.log('Restart Cursor to activate.\n');
|
|
544
665
|
return true;
|
|
545
666
|
}
|
|
546
667
|
|
|
547
668
|
if (target === 'claude') {
|
|
548
|
-
var
|
|
669
|
+
var claudeDesktopPath;
|
|
549
670
|
if (process.platform === 'darwin') {
|
|
550
|
-
|
|
671
|
+
claudeDesktopPath = path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
551
672
|
} else if (process.platform === 'win32') {
|
|
552
|
-
|
|
673
|
+
claudeDesktopPath = path.join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
553
674
|
} else {
|
|
554
|
-
|
|
675
|
+
claudeDesktopPath = path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
555
676
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
677
|
+
mergeMcpServer(claudeDesktopPath);
|
|
678
|
+
console.log('Claude Desktop MCP config written to ' + claudeDesktopPath);
|
|
679
|
+
|
|
680
|
+
var claudeCodePath = path.join(home, '.claude.json');
|
|
681
|
+
mergeMcpServer(claudeCodePath);
|
|
682
|
+
console.log('Claude Code MCP config written to ' + claudeCodePath);
|
|
683
|
+
|
|
684
|
+
if (looksLikeProjectCwd()) {
|
|
685
|
+
var projectMcp = path.join(process.cwd(), '.mcp.json');
|
|
686
|
+
mergeMcpServer(projectMcp);
|
|
687
|
+
console.log('Project MCP config written to ' + projectMcp);
|
|
561
688
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
args: serverArgs,
|
|
566
|
-
};
|
|
567
|
-
fs.writeFileSync(claudeConfigPath, JSON.stringify(claudeConfig, null, 2));
|
|
568
|
-
console.log('Claude Desktop MCP config written to ' + claudeConfigPath);
|
|
569
|
-
console.log('Restart Claude Desktop to activate.\n');
|
|
689
|
+
|
|
690
|
+
console.log('Default MCP catalog is lite. Full list: ANALYZTHIS_MCP_CATALOG=full');
|
|
691
|
+
console.log('Restart Claude Code and Claude Desktop to activate.\n');
|
|
570
692
|
return true;
|
|
571
693
|
}
|
|
572
694
|
|
|
573
695
|
if (target === 'windsurf') {
|
|
574
696
|
var windsurfConfigPath = path.join(home, '.codeium', 'windsurf', 'mcp_config.json');
|
|
575
|
-
|
|
576
|
-
if (!fs.existsSync(windsurfDir)) fs.mkdirSync(windsurfDir, { recursive: true });
|
|
577
|
-
var windsurfConfig = {};
|
|
578
|
-
if (fs.existsSync(windsurfConfigPath)) {
|
|
579
|
-
try { windsurfConfig = JSON.parse(fs.readFileSync(windsurfConfigPath, 'utf8')); } catch (e) {}
|
|
580
|
-
}
|
|
581
|
-
windsurfConfig.mcpServers = windsurfConfig.mcpServers || {};
|
|
582
|
-
windsurfConfig.mcpServers['analyzthis_design'] = {
|
|
583
|
-
command: serverCmd,
|
|
584
|
-
args: serverArgs,
|
|
585
|
-
};
|
|
586
|
-
fs.writeFileSync(windsurfConfigPath, JSON.stringify(windsurfConfig, null, 2));
|
|
697
|
+
mergeMcpServer(windsurfConfigPath);
|
|
587
698
|
console.log('Windsurf MCP config written to ' + windsurfConfigPath);
|
|
588
699
|
console.log('Restart Windsurf to activate.\n');
|
|
589
700
|
return true;
|
|
@@ -593,8 +704,8 @@ function writeMcpConfig(target) {
|
|
|
593
704
|
console.log(JSON.stringify({
|
|
594
705
|
mcpServers: {
|
|
595
706
|
'analyzthis_design': {
|
|
596
|
-
command:
|
|
597
|
-
args:
|
|
707
|
+
command: 'npx',
|
|
708
|
+
args: ['analyzthis_design', 'mcp'],
|
|
598
709
|
}
|
|
599
710
|
}
|
|
600
711
|
}, null, 2));
|
|
@@ -604,7 +715,9 @@ function writeMcpConfig(target) {
|
|
|
604
715
|
|
|
605
716
|
// ── MCP protocol (JSON-RPC 2.0 over stdio) ────────────────────────────────
|
|
606
717
|
|
|
607
|
-
function startStdioServer() {
|
|
718
|
+
function startStdioServer(catalog) {
|
|
719
|
+
var catalogMode = resolveCatalog(catalog);
|
|
720
|
+
var activeTools = listedTools(catalogMode);
|
|
608
721
|
var rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
609
722
|
var initialized = false;
|
|
610
723
|
|
|
@@ -649,7 +762,7 @@ function startStdioServer() {
|
|
|
649
762
|
jsonrpc: '2.0',
|
|
650
763
|
id: id,
|
|
651
764
|
result: {
|
|
652
|
-
tools:
|
|
765
|
+
tools: activeTools.map(function(t) {
|
|
653
766
|
return {
|
|
654
767
|
name: t.name,
|
|
655
768
|
description: t.description,
|
|
@@ -675,11 +788,17 @@ function startStdioServer() {
|
|
|
675
788
|
});
|
|
676
789
|
} else {
|
|
677
790
|
var text = result.prompt || result.result || result.command || JSON.stringify(result, null, 2);
|
|
791
|
+
var receipt = require('./receipt');
|
|
792
|
+
var wrapped = receipt.wrapPayload(text, {
|
|
793
|
+
path: 'mcp',
|
|
794
|
+
persona: result.persona || result.skill || toolName,
|
|
795
|
+
verdicts: result.verdicts || 0,
|
|
796
|
+
});
|
|
678
797
|
send({
|
|
679
798
|
jsonrpc: '2.0',
|
|
680
799
|
id: id,
|
|
681
800
|
result: {
|
|
682
|
-
content: [{ type: 'text', text:
|
|
801
|
+
content: [{ type: 'text', text: wrapped }],
|
|
683
802
|
},
|
|
684
803
|
});
|
|
685
804
|
}
|
|
@@ -713,24 +832,32 @@ function startStdioServer() {
|
|
|
713
832
|
|
|
714
833
|
if (process.env.MCP_DEBUG) {
|
|
715
834
|
process.stderr.write('analyzthis_design MCP server v' + VERSION + ' running on stdio\n');
|
|
716
|
-
process.stderr.write('Tools: ' +
|
|
835
|
+
process.stderr.write('Tools: ' + activeTools.length + ' (' + catalogMode + ' catalog)\n');
|
|
717
836
|
}
|
|
718
837
|
}
|
|
719
838
|
|
|
720
839
|
// ── Entry point ──
|
|
721
840
|
|
|
841
|
+
function parseCatalogFlag(args) {
|
|
842
|
+
var eq = args.filter(function (a) { return a.indexOf('--catalog=') === 0; })[0];
|
|
843
|
+
if (eq) return eq.split('=').slice(1).join('=');
|
|
844
|
+
var idx = args.indexOf('--catalog');
|
|
845
|
+
if (idx !== -1 && args[idx + 1] && args[idx + 1].indexOf('--') !== 0) return args[idx + 1];
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
|
|
722
849
|
function main() {
|
|
723
850
|
var args = process.argv.slice(2);
|
|
851
|
+
var catalog = parseCatalogFlag(args);
|
|
724
852
|
|
|
725
853
|
// --configure <target>
|
|
726
854
|
var configureIdx = args.indexOf('--configure');
|
|
727
855
|
if (configureIdx !== -1 && args[configureIdx + 1]) {
|
|
728
|
-
writeMcpConfig(args[configureIdx + 1]);
|
|
856
|
+
writeMcpConfig(args[configureIdx + 1], { catalog: catalog });
|
|
729
857
|
return;
|
|
730
858
|
}
|
|
731
859
|
|
|
732
|
-
|
|
733
|
-
startStdioServer();
|
|
860
|
+
startStdioServer(catalog);
|
|
734
861
|
}
|
|
735
862
|
|
|
736
863
|
if (require.main === module) {
|
|
@@ -739,6 +866,10 @@ if (require.main === module) {
|
|
|
739
866
|
|
|
740
867
|
module.exports = {
|
|
741
868
|
TOOLS: TOOLS,
|
|
869
|
+
ROUTER_TOOL: ROUTER_TOOL,
|
|
870
|
+
RECEIPT_TOOL: RECEIPT_TOOL,
|
|
871
|
+
listedTools: listedTools,
|
|
872
|
+
resolveCatalog: resolveCatalog,
|
|
742
873
|
handleToolCall: handleToolCall,
|
|
743
874
|
startStdioServer: startStdioServer,
|
|
744
875
|
writeMcpConfig: writeMcpConfig,
|
|
@@ -819,6 +819,8 @@ async function runUnchunked(opts = {}) {
|
|
|
819
819
|
objections_raised: delibResult.metrics.objections_raised,
|
|
820
820
|
objections_resolved: delibResult.metrics.objections_resolved,
|
|
821
821
|
raj_escalations: delibResult.metrics.raj_escalations,
|
|
822
|
+
verdicts_this_run: 1,
|
|
823
|
+
tokens_per_verdict: (delibResult.metrics.input_tokens_est || 0) || null,
|
|
822
824
|
},
|
|
823
825
|
},
|
|
824
826
|
});
|
|
@@ -834,6 +836,8 @@ async function runUnchunked(opts = {}) {
|
|
|
834
836
|
if (delibResult.deliberation.raj_escalated) console.log(' Raj escalated: yes');
|
|
835
837
|
console.log(` Verdict: ${syn.composite.verdict} (${syn.composite.total}/${syn.composite.max_total})`);
|
|
836
838
|
console.log(` Est. tokens: ${delibResult.metrics.input_tokens_est} in / ${delibResult.metrics.output_tokens_est} out`);
|
|
839
|
+
const tpvUn = delibResult.metrics.input_tokens_est || 0;
|
|
840
|
+
if (tpvUn) console.log(` Tokens per verdict: ${tpvUn} (CLI est. input / 1)`);
|
|
837
841
|
if (costUsd) console.log(` Est. cost: $${costUsd.toFixed(4)}`);
|
|
838
842
|
console.log(` Session: ${session.sessionPath(projectId)}`);
|
|
839
843
|
console.log('\n── Synthesis ──────────────────────────────────────');
|
|
@@ -878,6 +882,7 @@ async function run(opts = {}) {
|
|
|
878
882
|
console.log(` Cost: $${(chunkRunState.total_cost || 0).toFixed(4)}`);
|
|
879
883
|
const tokens = chunkRunState.total_tokens || { input: 0, output: 0 };
|
|
880
884
|
console.log(` Tokens: ${tokens.input} in / ${tokens.output} out`);
|
|
885
|
+
if (tokens.input) console.log(` Tokens per verdict: ${tokens.input} (CLI est. input / 1)`);
|
|
881
886
|
if (syn) {
|
|
882
887
|
console.log(` Verdict: ${syn.verdict || 'n/a'} (${syn.total || 0}/${syn.max_total || 0})`);
|
|
883
888
|
if (syn.premise_challenge) {
|
package/dist/lib/session.js
CHANGED
|
@@ -95,6 +95,9 @@ function defaultState(projectId) {
|
|
|
95
95
|
objections_raised: 0,
|
|
96
96
|
objections_resolved: 0,
|
|
97
97
|
raj_escalations: 0,
|
|
98
|
+
host_turns: [],
|
|
99
|
+
verdicts_this_run: 0,
|
|
100
|
+
tokens_per_verdict: null,
|
|
98
101
|
},
|
|
99
102
|
};
|
|
100
103
|
}
|
|
@@ -147,7 +150,11 @@ function reset({ project, all = false } = {}) {
|
|
|
147
150
|
function update({ project, patch = {} } = {}) {
|
|
148
151
|
const projectId = project || getProjectId();
|
|
149
152
|
const existing = show({ project: projectId }) || defaultState(projectId);
|
|
150
|
-
const
|
|
153
|
+
const patchCopy = { ...patch };
|
|
154
|
+
if (patchCopy.metrics && existing.metrics) {
|
|
155
|
+
patchCopy.metrics = { ...existing.metrics, ...patchCopy.metrics };
|
|
156
|
+
}
|
|
157
|
+
const merged = { ...existing, ...patchCopy, updated_at: new Date().toISOString() };
|
|
151
158
|
const dir = sessionDir(projectId);
|
|
152
159
|
fs.mkdirSync(dir, { recursive: true });
|
|
153
160
|
fs.writeFileSync(sessionPath(projectId), JSON.stringify(merged, null, 2));
|
|
@@ -181,6 +188,30 @@ function markAccepted({ project, persona, accepted = true } = {}) {
|
|
|
181
188
|
return { updated: true, state: merged };
|
|
182
189
|
}
|
|
183
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Append one inferred host (slash/MCP) turn. Caps the log. Recomputes tokens_per_verdict.
|
|
193
|
+
*/
|
|
194
|
+
function logHostTurn({ project, path: turnPath, persona, inferred_in, verdicts = 0 } = {}) {
|
|
195
|
+
const projectId = project || getProjectId();
|
|
196
|
+
const existing = show({ project: projectId }) || defaultState(projectId);
|
|
197
|
+
const metrics = { ...(existing.metrics || {}) };
|
|
198
|
+
const turns = Array.isArray(metrics.host_turns) ? metrics.host_turns.slice() : [];
|
|
199
|
+
turns.push({
|
|
200
|
+
at: new Date().toISOString(),
|
|
201
|
+
path: turnPath || 'mcp',
|
|
202
|
+
persona: persona || '',
|
|
203
|
+
inferred_in: Number(inferred_in) || 0,
|
|
204
|
+
});
|
|
205
|
+
const cap = 40;
|
|
206
|
+
metrics.host_turns = turns.length > cap ? turns.slice(turns.length - cap) : turns;
|
|
207
|
+
if (verdicts) metrics.verdicts_this_run = (metrics.verdicts_this_run || 0) + verdicts;
|
|
208
|
+
const host = metrics.host_turns.reduce((sum, t) => sum + (t.inferred_in || 0), 0);
|
|
209
|
+
const cliIn = metrics.input_tokens_est || 0;
|
|
210
|
+
const v = metrics.verdicts_this_run || (metrics.experts_run && metrics.experts_run.length ? 1 : 0) || (metrics.host_turns.length ? 1 : 0);
|
|
211
|
+
metrics.tokens_per_verdict = v && (host + cliIn) ? Math.round((host + cliIn) / v) : null;
|
|
212
|
+
return update({ project: projectId, patch: { metrics } });
|
|
213
|
+
}
|
|
214
|
+
|
|
184
215
|
module.exports = {
|
|
185
|
-
getProjectId, sessionPath, sessionDir, init, show, reset, update, listProjects, markAccepted, SESSIONS_ROOT,
|
|
216
|
+
getProjectId, sessionPath, sessionDir, init, show, reset, update, listProjects, markAccepted, logHostTurn, SESSIONS_ROOT,
|
|
186
217
|
};
|
|
@@ -21,6 +21,13 @@ You are Anuj (alias: Dev). 6 years as a domain analyst in high-volume operations
|
|
|
21
21
|
|
|
22
22
|
**Assess-only:** if the user asked to assess/propose/critique rather than build/implement/ship, stop at the concept — do not edit code.
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
## Lite output (default)
|
|
26
|
+
|
|
27
|
+
Verdict or one moment. Top three fixes (or one delight). One evidence line.
|
|
28
|
+
Use the full output schema below only if the user says expand.
|
|
29
|
+
You cannot know host tokens. Do not invent a dollar figure.
|
|
30
|
+
|
|
24
31
|
## Non-negotiables
|
|
25
32
|
|
|
26
33
|
- **Density never flattens the information hierarchy.** Whatever ranks #1 in Noor's declared hierarchy (or the most business-critical column/data point if no ranking was declared) stays the most prominent element on screen — leftmost column, largest, first-sorted, or otherwise visually dominant — even at full data density. "Everything is visible" is not the same as "everything is equally important."
|