autosnippet 3.3.2 → 3.3.4
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 +8 -4
- package/dist/bin/cli.js +27 -1
- package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
- package/dist/lib/cli/KnowledgeSyncService.js +33 -1
- package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/browse.js +2 -1
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/panorama.d.ts +11 -11
- package/dist/lib/external/mcp/handlers/panorama.js +20 -20
- package/dist/lib/external/mcp/handlers/system.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/task.js +38 -15
- package/dist/lib/external/mcp/tools.d.ts +12 -12
- package/dist/lib/external/mcp/tools.js +120 -118
- package/dist/lib/http/middleware/validate.js +7 -3
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +100 -0
- package/dist/lib/infrastructure/database/drizzle/schema.js +10 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
- package/dist/lib/injection/ServiceContainer.js +2 -0
- package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +80 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +9 -9
- package/dist/lib/service/evolution/ContradictionDetector.js +2 -2
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +2 -2
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +18 -1
- package/dist/lib/service/panorama/PanoramaService.js +148 -5
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
- package/dist/lib/service/search/CoarseRanker.js +11 -10
- package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
- package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +2 -2
- package/dist/lib/service/search/MultiSignalRanker.js +1 -1
- package/dist/lib/service/search/SearchEngine.d.ts +8 -7
- package/dist/lib/service/search/SearchEngine.js +59 -10
- package/dist/lib/service/search/SearchTypes.d.ts +23 -3
- package/dist/lib/service/search/SearchTypes.js +6 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +11 -1
- package/dist/lib/service/task/IntentExtractor.js +137 -3
- package/dist/lib/service/task/PrimeSearchPipeline.js +95 -25
- package/dist/lib/service/vector/VectorService.d.ts +3 -0
- package/dist/lib/service/vector/VectorService.js +38 -4
- package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -0
- package/dist/lib/shared/schemas/mcp-tools.js +5 -1
- package/package.json +1 -1
- package/skills/autosnippet-create/SKILL.md +98 -89
- package/skills/autosnippet-devdocs/SKILL.md +55 -60
- package/templates/guard-ci.yml +2 -2
- package/templates/instructions/conventions.md +4 -2
- package/templates/recipes-setup/_template.md +39 -39
package/README.md
CHANGED
|
@@ -93,13 +93,17 @@ Want to know the blast radius before refactoring a function? Static call graph a
|
|
|
93
93
|
|
|
94
94
|
Keyword search only finds literal matches. With an LLM API Key, search upgrades to vector + BM25 hybrid retrieval — asking "how to manage memory" finds Recipes about garbage collection, semantically similar results rank first.
|
|
95
95
|
|
|
96
|
-
###
|
|
96
|
+
### Intent-Aware Search (Prime)
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
At the start of every conversation, the Agent auto-triggers prime to intelligently inject knowledge based on the user query and current file. IntentExtractor extracts tech terms, infers language and module, performs cross-language (EN↔CJK) synonym expansion; PrimeSearchPipeline executes multi-query parallel search (raw query + term query + file context + focused synonyms), returning precise results after 3-layer quality filtering. Supports long natural-language sentences, short exact matches, and mixed-language queries.
|
|
99
|
+
|
|
100
|
+
### Recipe Source Evidence (sourceRefs)
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
Recipes carry the project file paths analyzed during creation as evidence. The 📍 sourceRefs in search results point to real project files — the Agent can trust and reference them without self-verification. Path validity is monitored automatically, with git rename auto-repair.
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
### Knowledge Graph
|
|
105
|
+
|
|
106
|
+
Recipes have relationships. Query impact paths, dependency depth, and related Recipes for any module — once you've accumulated enough knowledge, it helps you see the structure behind it.
|
|
103
107
|
|
|
104
108
|
### Self-Cycling Signal Mechanism
|
|
105
109
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -742,6 +742,17 @@ program
|
|
|
742
742
|
httpServer = new HttpServer({ port, host });
|
|
743
743
|
await httpServer.initialize();
|
|
744
744
|
await httpServer.start();
|
|
745
|
+
// ── UiStartupTasks: 后台异步刷新(不阻塞 UI) ──
|
|
746
|
+
import('../lib/service/bootstrap/UiStartupTasks.js')
|
|
747
|
+
.then(({ runUiStartupTasks }) => runUiStartupTasks({ projectRoot, container }))
|
|
748
|
+
.then((report) => {
|
|
749
|
+
if (report.errors.length > 0) {
|
|
750
|
+
cli.warn(`⚠️ UiStartupTasks completed with ${report.errors.length} error(s)`);
|
|
751
|
+
}
|
|
752
|
+
})
|
|
753
|
+
.catch((err) => {
|
|
754
|
+
cli.debug(`UiStartupTasks failed: ${err.message}`);
|
|
755
|
+
});
|
|
745
756
|
// ── MCP 配置检测 ──
|
|
746
757
|
const cursorMcpPath = join(projectRoot, '.cursor', 'mcp.json');
|
|
747
758
|
const vscodeMcpPath = join(projectRoot, '.vscode', 'mcp.json');
|
|
@@ -1337,7 +1348,7 @@ program
|
|
|
1337
1348
|
process.exit(1);
|
|
1338
1349
|
}
|
|
1339
1350
|
try {
|
|
1340
|
-
const report = syncService.
|
|
1351
|
+
const report = await syncService.syncAll(db, {
|
|
1341
1352
|
dryRun: opts.dryRun,
|
|
1342
1353
|
force: opts.force,
|
|
1343
1354
|
});
|
|
@@ -1347,6 +1358,21 @@ program
|
|
|
1347
1358
|
cli.log(` Updated: ${report.updated ?? 0}`);
|
|
1348
1359
|
cli.log(` Unchanged: ${report.unchanged ?? 0}`);
|
|
1349
1360
|
cli.log(` Deleted: ${report.deleted ?? 0}`);
|
|
1361
|
+
if (report.reconcileReport) {
|
|
1362
|
+
cli.log(`\n 📍 Source Refs`);
|
|
1363
|
+
cli.log(` ${'─'.repeat(40)}`);
|
|
1364
|
+
cli.log(` Inserted: ${report.reconcileReport.inserted ?? 0}`);
|
|
1365
|
+
cli.log(` Active: ${report.reconcileReport.active ?? 0}`);
|
|
1366
|
+
cli.log(` Stale: ${report.reconcileReport.stale ?? 0}`);
|
|
1367
|
+
cli.log(` Skipped: ${report.reconcileReport.skipped ?? 0}`);
|
|
1368
|
+
}
|
|
1369
|
+
if (report.repairReport &&
|
|
1370
|
+
(report.repairReport.renamed > 0 || report.repairReport.stillStale > 0)) {
|
|
1371
|
+
cli.log(`\n 🔧 Rename Repairs`);
|
|
1372
|
+
cli.log(` ${'─'.repeat(40)}`);
|
|
1373
|
+
cli.log(` Renamed: ${report.repairReport.renamed ?? 0}`);
|
|
1374
|
+
cli.log(` Still Stale: ${report.repairReport.stillStale ?? 0}`);
|
|
1375
|
+
}
|
|
1350
1376
|
if (report.violations.length > 0) {
|
|
1351
1377
|
cli.log(`\n ⚠️ Violations (${report.violations.length}):`);
|
|
1352
1378
|
for (const v of report.violations) {
|
|
@@ -14,12 +14,38 @@
|
|
|
14
14
|
* - 内部: SetupService.stepDatabase() 委托调用(skipViolations = true)
|
|
15
15
|
*/
|
|
16
16
|
import Logger from '../infrastructure/logging/Logger.js';
|
|
17
|
+
import type { ApplyReport, ReconcileReport, RepairReport } from '../service/knowledge/SourceRefReconciler.js';
|
|
18
|
+
export interface SyncAllReport {
|
|
19
|
+
synced: number;
|
|
20
|
+
created: number;
|
|
21
|
+
updated: number;
|
|
22
|
+
violations: string[];
|
|
23
|
+
orphaned: string[];
|
|
24
|
+
skipped: number;
|
|
25
|
+
reconcileReport?: ReconcileReport;
|
|
26
|
+
repairReport?: RepairReport;
|
|
27
|
+
applyReport?: ApplyReport;
|
|
28
|
+
}
|
|
17
29
|
export declare class KnowledgeSyncService {
|
|
18
30
|
candidatesDir: string;
|
|
19
31
|
logger: ReturnType<typeof Logger.getInstance>;
|
|
20
32
|
projectRoot: string;
|
|
21
33
|
recipesDir: string;
|
|
22
34
|
constructor(projectRoot: string);
|
|
35
|
+
/**
|
|
36
|
+
* 完整同步入口 — sync + reconcile + repair
|
|
37
|
+
*
|
|
38
|
+
* asd sync CLI 和 asd ui 启动都调用此方法。
|
|
39
|
+
*
|
|
40
|
+
* @param db better-sqlite3 原始句柄
|
|
41
|
+
* @param opts 同步选项
|
|
42
|
+
* @returns 包含 sync + reconcile + repair 报告的综合结果
|
|
43
|
+
*/
|
|
44
|
+
syncAll(db: Parameters<KnowledgeSyncService['sync']>[0], opts?: {
|
|
45
|
+
dryRun?: boolean;
|
|
46
|
+
force?: boolean;
|
|
47
|
+
skipViolations?: boolean;
|
|
48
|
+
}): Promise<SyncAllReport>;
|
|
23
49
|
/**
|
|
24
50
|
* 执行增量同步:.md → DB(knowledge_entries 表)
|
|
25
51
|
*
|
|
@@ -19,6 +19,7 @@ import path from 'node:path';
|
|
|
19
19
|
import { CANDIDATES_DIR, RECIPES_DIR } from '../infrastructure/config/Defaults.js';
|
|
20
20
|
import Logger from '../infrastructure/logging/Logger.js';
|
|
21
21
|
import { computeKnowledgeHash, parseKnowledgeMarkdown, } from '../service/knowledge/KnowledgeFileWriter.js';
|
|
22
|
+
import { SourceRefReconciler } from '../service/knowledge/SourceRefReconciler.js';
|
|
22
23
|
export class KnowledgeSyncService {
|
|
23
24
|
candidatesDir;
|
|
24
25
|
logger;
|
|
@@ -30,6 +31,37 @@ export class KnowledgeSyncService {
|
|
|
30
31
|
this.candidatesDir = path.join(projectRoot, CANDIDATES_DIR);
|
|
31
32
|
this.logger = Logger.getInstance();
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* 完整同步入口 — sync + reconcile + repair
|
|
36
|
+
*
|
|
37
|
+
* asd sync CLI 和 asd ui 启动都调用此方法。
|
|
38
|
+
*
|
|
39
|
+
* @param db better-sqlite3 原始句柄
|
|
40
|
+
* @param opts 同步选项
|
|
41
|
+
* @returns 包含 sync + reconcile + repair 报告的综合结果
|
|
42
|
+
*/
|
|
43
|
+
async syncAll(db, opts = {}) {
|
|
44
|
+
// 1. .md → DB 同步
|
|
45
|
+
const syncReport = this.sync(db, opts);
|
|
46
|
+
const report = { ...syncReport };
|
|
47
|
+
// 2. 填充/验证 recipe_source_refs 桥接表
|
|
48
|
+
try {
|
|
49
|
+
const reconciler = new SourceRefReconciler(this.projectRoot, db);
|
|
50
|
+
report.reconcileReport = reconciler.reconcile({ force: opts.force });
|
|
51
|
+
// 3. git rename 修复
|
|
52
|
+
report.repairReport = await reconciler.repairRenames();
|
|
53
|
+
// 4. 写回修复
|
|
54
|
+
if (report.repairReport.renamed > 0) {
|
|
55
|
+
report.applyReport = reconciler.applyRepairs();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.logger.warn('KnowledgeSyncService: sourceRef reconcile failed (non-blocking)', {
|
|
60
|
+
error: err.message,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return report;
|
|
64
|
+
}
|
|
33
65
|
/**
|
|
34
66
|
* 执行增量同步:.md → DB(knowledge_entries 表)
|
|
35
67
|
*
|
|
@@ -89,7 +121,7 @@ export class KnowledgeSyncService {
|
|
|
89
121
|
if (!dryRun) {
|
|
90
122
|
const existed = this._entryExists(db, parsed.id);
|
|
91
123
|
const row = this._buildDbRow(parsed, relPath, content);
|
|
92
|
-
upsertStmt
|
|
124
|
+
upsertStmt?.run(...Object.values(row));
|
|
93
125
|
if (existed) {
|
|
94
126
|
report.updated++;
|
|
95
127
|
}
|
|
@@ -67,6 +67,7 @@ export declare function getRecipe(ctx: McpContext, args: BrowseGetArgs): Promise
|
|
|
67
67
|
} | undefined;
|
|
68
68
|
headers: string[] | undefined;
|
|
69
69
|
reasoning: {
|
|
70
|
+
sources?: unknown[] | undefined;
|
|
70
71
|
confidence?: number | undefined;
|
|
71
72
|
whyStandard?: string | undefined;
|
|
72
73
|
} | undefined;
|
|
@@ -74,11 +74,12 @@ function _projectForAgent(json) {
|
|
|
74
74
|
...((json.content.steps?.length ?? 0) > 0 ? { steps: json.content.steps } : {}),
|
|
75
75
|
}
|
|
76
76
|
: undefined;
|
|
77
|
-
// reasoning
|
|
77
|
+
// reasoning 精简:保留 whyStandard + confidence + sources(可信度证据链)
|
|
78
78
|
const reasoning = json.reasoning
|
|
79
79
|
? {
|
|
80
80
|
...(json.reasoning.whyStandard ? { whyStandard: json.reasoning.whyStandard } : {}),
|
|
81
81
|
...(json.reasoning.confidence != null ? { confidence: json.reasoning.confidence } : {}),
|
|
82
|
+
...((json.reasoning.sources?.length ?? 0) > 0 ? { sources: json.reasoning.sources } : {}),
|
|
82
83
|
}
|
|
83
84
|
: undefined;
|
|
84
85
|
// constraints 精简:仅保留 guards 和 sideEffects
|
|
@@ -112,6 +112,7 @@ export declare function consolidatedKnowledge(ctx: McpContext, args: Consolidate
|
|
|
112
112
|
} | undefined;
|
|
113
113
|
headers: string[] | undefined;
|
|
114
114
|
reasoning: {
|
|
115
|
+
sources?: unknown[] | undefined;
|
|
115
116
|
confidence?: number | undefined;
|
|
116
117
|
whyStandard?: string | undefined;
|
|
117
118
|
} | undefined;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Handler — autosnippet_panorama
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* overview —
|
|
6
|
-
* module —
|
|
7
|
-
* gaps —
|
|
8
|
-
* health —
|
|
9
|
-
* governance_cycle —
|
|
10
|
-
* decay_report —
|
|
11
|
-
* staging_check — staging
|
|
12
|
-
* enhancement_suggestions —
|
|
4
|
+
* Project panorama query tool with 8 operations:
|
|
5
|
+
* overview — project skeleton + layers + module roles
|
|
6
|
+
* module — single module detail + neighbors + recipes + file groups
|
|
7
|
+
* gaps — knowledge gaps (code without Recipes)
|
|
8
|
+
* health — panorama health (coverage + coupling + cycles)
|
|
9
|
+
* governance_cycle — full metabolism cycle (contradiction + redundancy + decay)
|
|
10
|
+
* decay_report — decay assessment report
|
|
11
|
+
* staging_check — staging entry check + auto-publish
|
|
12
|
+
* enhancement_suggestions — usage-data-based enhancement suggestions
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* All read-only except governance_cycle and staging_check (which perform state transitions).
|
|
15
15
|
*/
|
|
16
16
|
import type { McpContext } from './types.js';
|
|
17
17
|
interface PanoramaArgs {
|
|
@@ -19,7 +19,7 @@ interface PanoramaArgs {
|
|
|
19
19
|
module?: string;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* autosnippet_panorama —
|
|
22
|
+
* autosnippet_panorama — unified panorama query
|
|
23
23
|
*/
|
|
24
24
|
export declare function panoramaHandler(ctx: McpContext, args: PanoramaArgs): Promise<{
|
|
25
25
|
success: boolean;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Handler — autosnippet_panorama
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* overview —
|
|
6
|
-
* module —
|
|
7
|
-
* gaps —
|
|
8
|
-
* health —
|
|
9
|
-
* governance_cycle —
|
|
10
|
-
* decay_report —
|
|
11
|
-
* staging_check — staging
|
|
12
|
-
* enhancement_suggestions —
|
|
4
|
+
* Project panorama query tool with 8 operations:
|
|
5
|
+
* overview — project skeleton + layers + module roles
|
|
6
|
+
* module — single module detail + neighbors + recipes + file groups
|
|
7
|
+
* gaps — knowledge gaps (code without Recipes)
|
|
8
|
+
* health — panorama health (coverage + coupling + cycles)
|
|
9
|
+
* governance_cycle — full metabolism cycle (contradiction + redundancy + decay)
|
|
10
|
+
* decay_report — decay assessment report
|
|
11
|
+
* staging_check — staging entry check + auto-publish
|
|
12
|
+
* enhancement_suggestions — usage-data-based enhancement suggestions
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* All read-only except governance_cycle and staging_check (which perform state transitions).
|
|
15
15
|
*/
|
|
16
16
|
import { envelope } from '../envelope.js';
|
|
17
17
|
/**
|
|
18
|
-
* autosnippet_panorama —
|
|
18
|
+
* autosnippet_panorama — unified panorama query
|
|
19
19
|
*/
|
|
20
20
|
export async function panoramaHandler(ctx, args) {
|
|
21
21
|
const op = args.operation || 'overview';
|
|
@@ -23,11 +23,11 @@ export async function panoramaHandler(ctx, args) {
|
|
|
23
23
|
if (!panoramaService) {
|
|
24
24
|
return envelope({
|
|
25
25
|
success: false,
|
|
26
|
-
message: '
|
|
26
|
+
message: 'Panorama service not initialized',
|
|
27
27
|
meta: { tool: 'autosnippet_panorama' },
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
//
|
|
30
|
+
// Auto-ensure data is ready (triggers built-in scan when no data exists)
|
|
31
31
|
await panoramaService.ensureData();
|
|
32
32
|
switch (op) {
|
|
33
33
|
case 'overview': {
|
|
@@ -43,7 +43,7 @@ export async function panoramaHandler(ctx, args) {
|
|
|
43
43
|
if (!moduleName) {
|
|
44
44
|
return envelope({
|
|
45
45
|
success: false,
|
|
46
|
-
message: 'operation=module
|
|
46
|
+
message: 'operation=module requires the "module" parameter (module name)',
|
|
47
47
|
meta: { tool: 'autosnippet_panorama' },
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -51,7 +51,7 @@ export async function panoramaHandler(ctx, args) {
|
|
|
51
51
|
if (!detail) {
|
|
52
52
|
return envelope({
|
|
53
53
|
success: false,
|
|
54
|
-
message:
|
|
54
|
+
message: `Module not found: ${moduleName}`,
|
|
55
55
|
meta: { tool: 'autosnippet_panorama' },
|
|
56
56
|
});
|
|
57
57
|
}
|
|
@@ -78,7 +78,7 @@ export async function panoramaHandler(ctx, args) {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
default:
|
|
81
|
-
// ── Governance operations (
|
|
81
|
+
// ── Governance operations (independent of panoramaService) ──
|
|
82
82
|
return handleGovernanceOps(ctx, op);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -90,7 +90,7 @@ async function handleGovernanceOps(ctx, op) {
|
|
|
90
90
|
if (!metabolism) {
|
|
91
91
|
return envelope({
|
|
92
92
|
success: false,
|
|
93
|
-
message: '
|
|
93
|
+
message: 'Governance service not initialized (knowledgeMetabolism not registered)',
|
|
94
94
|
meta: { tool: 'autosnippet_panorama' },
|
|
95
95
|
});
|
|
96
96
|
}
|
|
@@ -106,7 +106,7 @@ async function handleGovernanceOps(ctx, op) {
|
|
|
106
106
|
if (!decayDetector) {
|
|
107
107
|
return envelope({
|
|
108
108
|
success: false,
|
|
109
|
-
message: '
|
|
109
|
+
message: 'Decay detector not initialized (decayDetector not registered)',
|
|
110
110
|
meta: { tool: 'autosnippet_panorama' },
|
|
111
111
|
});
|
|
112
112
|
}
|
|
@@ -122,7 +122,7 @@ async function handleGovernanceOps(ctx, op) {
|
|
|
122
122
|
if (!stagingManager) {
|
|
123
123
|
return envelope({
|
|
124
124
|
success: false,
|
|
125
|
-
message: '
|
|
125
|
+
message: 'Staging manager not initialized (stagingManager not registered)',
|
|
126
126
|
meta: { tool: 'autosnippet_panorama' },
|
|
127
127
|
});
|
|
128
128
|
}
|
|
@@ -139,7 +139,7 @@ async function handleGovernanceOps(ctx, op) {
|
|
|
139
139
|
if (!suggester) {
|
|
140
140
|
return envelope({
|
|
141
141
|
success: false,
|
|
142
|
-
message: '
|
|
142
|
+
message: 'Enhancement suggester not initialized (enhancementSuggester not registered)',
|
|
143
143
|
meta: { tool: 'autosnippet_panorama' },
|
|
144
144
|
});
|
|
145
145
|
}
|
|
@@ -12,7 +12,7 @@ export declare function health(ctx: McpContext): Promise<{
|
|
|
12
12
|
issues?: string[] | undefined;
|
|
13
13
|
session?: {
|
|
14
14
|
id: string;
|
|
15
|
-
intentPhase: "
|
|
15
|
+
intentPhase: "active" | "idle" | "ended";
|
|
16
16
|
toolCallCount: number;
|
|
17
17
|
toolsUsed: string[];
|
|
18
18
|
durationMs: number;
|
|
@@ -44,6 +44,10 @@ const _taskRules = {
|
|
|
44
44
|
* Unified entry point
|
|
45
45
|
*/
|
|
46
46
|
export async function taskHandler(ctx, args) {
|
|
47
|
+
// Normalize taskId → id (schema accepts both for convenience)
|
|
48
|
+
if (!args.id && typeof args.taskId === 'string') {
|
|
49
|
+
args.id = args.taskId;
|
|
50
|
+
}
|
|
47
51
|
let result;
|
|
48
52
|
switch (args.operation) {
|
|
49
53
|
case 'prime':
|
|
@@ -88,11 +92,20 @@ async function _prime(ctx, args) {
|
|
|
88
92
|
if (pipeline && extracted.queries[0]?.trim()) {
|
|
89
93
|
try {
|
|
90
94
|
searchResult = await pipeline.search(extracted);
|
|
95
|
+
if (!searchResult) {
|
|
96
|
+
process.stderr.write('[MCP/Task] prime: pipeline.search returned null (all filtered)\n');
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
|
-
catch {
|
|
93
|
-
|
|
99
|
+
catch (err) {
|
|
100
|
+
process.stderr.write(`[MCP/Task] prime search error: ${err instanceof Error ? err.stack || err.message : String(err)}\n`);
|
|
94
101
|
}
|
|
95
102
|
}
|
|
103
|
+
else if (!pipeline) {
|
|
104
|
+
process.stderr.write('[MCP/Task] prime: pipeline is null, skipping search\n');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
process.stderr.write(`[MCP/Task] prime: queries empty, skipping search. queries=${JSON.stringify(extracted.queries)}\n`);
|
|
108
|
+
}
|
|
96
109
|
// ─── Lifecycle: initialize IntentState ───
|
|
97
110
|
const freshIntent = createIdleIntent();
|
|
98
111
|
freshIntent.phase = 'active';
|
|
@@ -124,7 +137,8 @@ async function _prime(ctx, args) {
|
|
|
124
137
|
lines.push(`📋 Found ${relatedCount} recipe(s), ${ruleCount} guard rule(s).`);
|
|
125
138
|
for (const r of searchResult.relatedKnowledge) {
|
|
126
139
|
const hint = r.actionHint ? ` — ${r.actionHint}` : '';
|
|
127
|
-
|
|
140
|
+
const refs = r.sourceRefs?.length ? `\n 📍 ${r.sourceRefs.join(', ')}` : '';
|
|
141
|
+
lines.push(` • ${r.trigger || r.title}${hint}${refs}`);
|
|
128
142
|
}
|
|
129
143
|
for (const r of searchResult.guardRules) {
|
|
130
144
|
lines.push(` • [rule] ${r.trigger || r.title}`);
|
|
@@ -174,14 +188,16 @@ async function _create(ctx, args) {
|
|
|
174
188
|
}
|
|
175
189
|
// ═══ close ══════════════════════════════════════════════
|
|
176
190
|
async function _close(ctx, args) {
|
|
177
|
-
|
|
191
|
+
const intent = ctx.session?.intent;
|
|
192
|
+
// Resolve id: explicit arg > session intent > fail
|
|
193
|
+
const id = args.id || (intent?.taskId ?? '');
|
|
194
|
+
if (!id) {
|
|
178
195
|
return envelope({
|
|
179
196
|
success: false,
|
|
180
|
-
message: 'id is required',
|
|
197
|
+
message: 'id is required (pass id or ensure a task was created in this session)',
|
|
181
198
|
meta: { tool: 'autosnippet_task' },
|
|
182
199
|
});
|
|
183
200
|
}
|
|
184
|
-
const intent = ctx.session?.intent;
|
|
185
201
|
const reason = args.reason || 'Completed';
|
|
186
202
|
// Persist intent chain via SignalBus
|
|
187
203
|
if (intent && intent.phase === 'active') {
|
|
@@ -191,13 +207,13 @@ async function _close(ctx, args) {
|
|
|
191
207
|
if (ctx.session) {
|
|
192
208
|
ctx.session.intent = createIdleIntent();
|
|
193
209
|
}
|
|
194
|
-
const lines = [`✅ Closed: ${
|
|
210
|
+
const lines = [`✅ Closed: ${id} — ${reason}`];
|
|
195
211
|
lines.push('');
|
|
196
212
|
lines.push('⚠️ REQUIRED: You MUST call autosnippet_guard (no args) NOW to review changed files for compliance violations.');
|
|
197
213
|
return envelope({
|
|
198
214
|
success: true,
|
|
199
215
|
data: {
|
|
200
|
-
closed: { id
|
|
216
|
+
closed: { id, reason, closedAt: Date.now() },
|
|
201
217
|
nextAction: {
|
|
202
218
|
tool: 'autosnippet_guard',
|
|
203
219
|
args: {},
|
|
@@ -211,14 +227,16 @@ async function _close(ctx, args) {
|
|
|
211
227
|
}
|
|
212
228
|
// ═══ fail ═══════════════════════════════════════════════
|
|
213
229
|
async function _fail(ctx, args) {
|
|
214
|
-
|
|
230
|
+
const intent = ctx.session?.intent;
|
|
231
|
+
// Resolve id: explicit arg > session intent > fail
|
|
232
|
+
const id = args.id || (intent?.taskId ?? '');
|
|
233
|
+
if (!id) {
|
|
215
234
|
return envelope({
|
|
216
235
|
success: false,
|
|
217
|
-
message: 'id is required',
|
|
236
|
+
message: 'id is required (pass id or ensure a task was created in this session)',
|
|
218
237
|
meta: { tool: 'autosnippet_task' },
|
|
219
238
|
});
|
|
220
239
|
}
|
|
221
|
-
const intent = ctx.session?.intent;
|
|
222
240
|
const reason = args.reason || 'Agent execution failed';
|
|
223
241
|
// Persist intent chain via SignalBus
|
|
224
242
|
if (intent && intent.phase === 'active') {
|
|
@@ -231,9 +249,9 @@ async function _fail(ctx, args) {
|
|
|
231
249
|
return envelope({
|
|
232
250
|
success: true,
|
|
233
251
|
data: {
|
|
234
|
-
failed: { id
|
|
252
|
+
failed: { id, reason, failedAt: Date.now() },
|
|
235
253
|
},
|
|
236
|
-
message: `❌ Failed: ${
|
|
254
|
+
message: `❌ Failed: ${id} — ${reason}`,
|
|
237
255
|
meta: { tool: 'autosnippet_task' },
|
|
238
256
|
});
|
|
239
257
|
}
|
|
@@ -322,9 +340,14 @@ function _computeDriftScore(intent) {
|
|
|
322
340
|
}
|
|
323
341
|
function _getPipeline(container) {
|
|
324
342
|
try {
|
|
325
|
-
|
|
343
|
+
const p = container.get('primeSearchPipeline');
|
|
344
|
+
if (!p) {
|
|
345
|
+
process.stderr.write('[MCP/Task] _getPipeline: container returned null/undefined\n');
|
|
346
|
+
}
|
|
347
|
+
return p;
|
|
326
348
|
}
|
|
327
|
-
catch {
|
|
349
|
+
catch (err) {
|
|
350
|
+
process.stderr.write(`[MCP/Task] _getPipeline failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
328
351
|
return null;
|
|
329
352
|
}
|
|
330
353
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP
|
|
2
|
+
* MCP Tool Definitions — V3 Consolidated (14 agent + 2 admin = 16 tools)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* description
|
|
6
|
-
* inputSchema
|
|
4
|
+
* Each tool declaration contains name, tier (agent/admin), description, and inputSchema.
|
|
5
|
+
* description is the key for Agent tool selection — use bullet list to enumerate all operations and their purposes.
|
|
6
|
+
* inputSchema is auto-generated from Zod Schema (zodToMcpSchema); parameter .describe() translates to JSON Schema description.
|
|
7
7
|
*
|
|
8
|
-
* Agent
|
|
9
|
-
* 1-7:
|
|
10
|
-
* 8:
|
|
11
|
-
* 9: Skill
|
|
12
|
-
* 10-12:
|
|
13
|
-
* 13:
|
|
14
|
-
* 14:
|
|
8
|
+
* Agent tools (14):
|
|
9
|
+
* 1-7: Query tools (health/search/knowledge/structure/graph/call_context/guard)
|
|
10
|
+
* 8: Write tool (submit_knowledge — unified pipeline, single/batch)
|
|
11
|
+
* 9: Skill management (skill)
|
|
12
|
+
* 10-12: Cold-start (bootstrap/dimension_complete/wiki)
|
|
13
|
+
* 13: Project panorama (panorama)
|
|
14
|
+
* 14: Task management (task — 5 ops: prime/create/close/fail/record_decision)
|
|
15
15
|
*
|
|
16
|
-
* Admin
|
|
16
|
+
* Admin tools (2):
|
|
17
17
|
* 15-16: enrich_candidates/knowledge_lifecycle
|
|
18
18
|
*/
|
|
19
19
|
export declare const TIER_ORDER: {
|