@mainahq/core 1.0.2 → 1.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/package.json +1 -1
- package/src/ai/__tests__/availability.test.ts +131 -0
- package/src/ai/__tests__/delegation.test.ts +55 -1
- package/src/ai/availability.ts +23 -0
- package/src/ai/delegation.ts +5 -3
- package/src/context/__tests__/budget.test.ts +29 -6
- package/src/context/__tests__/engine.test.ts +1 -0
- package/src/context/__tests__/selector.test.ts +23 -3
- package/src/context/__tests__/wiki.test.ts +349 -0
- package/src/context/budget.ts +12 -8
- package/src/context/engine.ts +37 -0
- package/src/context/selector.ts +30 -4
- package/src/context/wiki.ts +296 -0
- package/src/db/index.ts +12 -0
- package/src/feedback/__tests__/capture.test.ts +166 -0
- package/src/feedback/__tests__/signals.test.ts +144 -0
- package/src/feedback/__tests__/tmp-capture-1775575256633-lah0etnzlj/feedback.db +0 -0
- package/src/feedback/__tests__/tmp-capture-1775575256640-2xmjme4qraa/feedback.db +0 -0
- package/src/feedback/capture.ts +102 -0
- package/src/feedback/signals.ts +68 -0
- package/src/index.ts +108 -1
- package/src/init/__tests__/init.test.ts +477 -18
- package/src/init/index.ts +419 -13
- package/src/language/__tests__/__fixtures__/detect/composer.lock +1 -0
- package/src/prompts/defaults/index.ts +3 -1
- package/src/prompts/defaults/wiki-compile.md +20 -0
- package/src/prompts/defaults/wiki-query.md +18 -0
- package/src/stats/__tests__/tool-usage.test.ts +133 -0
- package/src/stats/tracker.ts +92 -0
- package/src/verify/__tests__/builtin.test.ts +270 -0
- package/src/verify/__tests__/pipeline.test.ts +11 -8
- package/src/verify/builtin.ts +350 -0
- package/src/verify/pipeline.ts +32 -2
- package/src/verify/tools/__tests__/wiki-lint.test.ts +784 -0
- package/src/verify/tools/wiki-lint-runner.ts +38 -0
- package/src/verify/tools/wiki-lint.ts +898 -0
- package/src/wiki/__tests__/compiler.test.ts +389 -0
- package/src/wiki/__tests__/extractors/code.test.ts +99 -0
- package/src/wiki/__tests__/extractors/decision.test.ts +323 -0
- package/src/wiki/__tests__/extractors/feature.test.ts +186 -0
- package/src/wiki/__tests__/extractors/workflow.test.ts +131 -0
- package/src/wiki/__tests__/graph.test.ts +344 -0
- package/src/wiki/__tests__/hooks.test.ts +119 -0
- package/src/wiki/__tests__/indexer.test.ts +285 -0
- package/src/wiki/__tests__/linker.test.ts +230 -0
- package/src/wiki/__tests__/louvain.test.ts +229 -0
- package/src/wiki/__tests__/query.test.ts +316 -0
- package/src/wiki/__tests__/schema.test.ts +114 -0
- package/src/wiki/__tests__/signals.test.ts +474 -0
- package/src/wiki/__tests__/state.test.ts +168 -0
- package/src/wiki/__tests__/tracking.test.ts +118 -0
- package/src/wiki/__tests__/types.test.ts +387 -0
- package/src/wiki/compiler.ts +1075 -0
- package/src/wiki/extractors/code.ts +90 -0
- package/src/wiki/extractors/decision.ts +217 -0
- package/src/wiki/extractors/feature.ts +206 -0
- package/src/wiki/extractors/workflow.ts +112 -0
- package/src/wiki/graph.ts +445 -0
- package/src/wiki/hooks.ts +49 -0
- package/src/wiki/indexer.ts +105 -0
- package/src/wiki/linker.ts +117 -0
- package/src/wiki/louvain.ts +190 -0
- package/src/wiki/prompts/compile-architecture.md +59 -0
- package/src/wiki/prompts/compile-decision.md +66 -0
- package/src/wiki/prompts/compile-entity.md +56 -0
- package/src/wiki/prompts/compile-feature.md +60 -0
- package/src/wiki/prompts/compile-module.md +42 -0
- package/src/wiki/prompts/wiki-query.md +25 -0
- package/src/wiki/query.ts +338 -0
- package/src/wiki/schema.ts +111 -0
- package/src/wiki/signals.ts +368 -0
- package/src/wiki/state.ts +89 -0
- package/src/wiki/tracking.ts +30 -0
- package/src/wiki/types.ts +169 -0
- package/src/workflow/context.ts +26 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP result capture — connects tool outputs to cache, feedback, and stats.
|
|
3
|
+
* This is the core of the round-trip flywheel.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { hashContent } from "../cache/keys";
|
|
7
|
+
import { createCacheManager } from "../cache/manager";
|
|
8
|
+
import { trackToolUsage } from "../stats/tracker";
|
|
9
|
+
import { recordFeedback } from "./collector";
|
|
10
|
+
import { emitRejectSignal } from "./signals";
|
|
11
|
+
|
|
12
|
+
export interface CaptureInput {
|
|
13
|
+
tool: string;
|
|
14
|
+
input: Record<string, unknown>;
|
|
15
|
+
output: string;
|
|
16
|
+
promptHash?: string;
|
|
17
|
+
durationMs: number;
|
|
18
|
+
mainaDir: string;
|
|
19
|
+
workflowId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildToolCacheKey(
|
|
23
|
+
tool: string,
|
|
24
|
+
input: Record<string, unknown>,
|
|
25
|
+
): string {
|
|
26
|
+
const inputHash = hashContent(JSON.stringify(input));
|
|
27
|
+
return hashContent(`mcp:${tool}:${inputHash}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getCachedResult(
|
|
31
|
+
tool: string,
|
|
32
|
+
input: Record<string, unknown>,
|
|
33
|
+
mainaDir: string,
|
|
34
|
+
): string | null {
|
|
35
|
+
try {
|
|
36
|
+
const cache = createCacheManager(mainaDir);
|
|
37
|
+
const key = buildToolCacheKey(tool, input);
|
|
38
|
+
const entry = cache.get(key);
|
|
39
|
+
if (entry !== null) {
|
|
40
|
+
const inputHash = hashContent(JSON.stringify(input));
|
|
41
|
+
trackToolUsage(mainaDir, {
|
|
42
|
+
tool,
|
|
43
|
+
inputHash,
|
|
44
|
+
durationMs: 0,
|
|
45
|
+
cacheHit: true,
|
|
46
|
+
});
|
|
47
|
+
return entry.value;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function captureResult(input: CaptureInput): void {
|
|
56
|
+
const inputHash = hashContent(JSON.stringify(input.input));
|
|
57
|
+
|
|
58
|
+
// 1. Cache (synchronous — fast SQLite write)
|
|
59
|
+
try {
|
|
60
|
+
const cache = createCacheManager(input.mainaDir);
|
|
61
|
+
const key = buildToolCacheKey(input.tool, input.input);
|
|
62
|
+
cache.set(key, input.output, { ttl: 0 });
|
|
63
|
+
} catch {
|
|
64
|
+
// Cache failure is non-fatal
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Reject previous result for same tool+workflow (re-run implies rejection)
|
|
68
|
+
if (input.workflowId) {
|
|
69
|
+
try {
|
|
70
|
+
emitRejectSignal(input.mainaDir, input.tool, input.workflowId);
|
|
71
|
+
} catch {
|
|
72
|
+
// Reject failure is non-fatal
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 3. Feedback (fire-and-forget via microtask)
|
|
77
|
+
queueMicrotask(() => {
|
|
78
|
+
try {
|
|
79
|
+
recordFeedback(input.mainaDir, {
|
|
80
|
+
promptHash: input.promptHash ?? `${input.tool}-mcp`,
|
|
81
|
+
task: input.tool,
|
|
82
|
+
accepted: true,
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
});
|
|
85
|
+
} catch {
|
|
86
|
+
// Never throw from background feedback
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 4. Stats (synchronous — fast SQLite write)
|
|
91
|
+
try {
|
|
92
|
+
trackToolUsage(input.mainaDir, {
|
|
93
|
+
tool: input.tool,
|
|
94
|
+
inputHash,
|
|
95
|
+
durationMs: input.durationMs,
|
|
96
|
+
cacheHit: false,
|
|
97
|
+
workflowId: input.workflowId,
|
|
98
|
+
});
|
|
99
|
+
} catch {
|
|
100
|
+
// Stats failure is non-fatal
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implicit accept/reject signals for the RL flywheel.
|
|
3
|
+
* Infers outcomes from downstream user behavior instead of requiring explicit action.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { getFeedbackDb } from "../db/index";
|
|
8
|
+
import { recordWikiUsage } from "../wiki/signals";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_ACCEPT_TOOLS = ["reviewCode", "verify", "checkSlop"];
|
|
11
|
+
|
|
12
|
+
export function emitAcceptSignal(
|
|
13
|
+
mainaDir: string,
|
|
14
|
+
workflowId: string,
|
|
15
|
+
tools?: string[],
|
|
16
|
+
wikiArticles?: string[],
|
|
17
|
+
): void {
|
|
18
|
+
try {
|
|
19
|
+
const dbResult = getFeedbackDb(mainaDir);
|
|
20
|
+
if (!dbResult.ok) return;
|
|
21
|
+
const { db } = dbResult.value;
|
|
22
|
+
const targetTools = tools ?? DEFAULT_ACCEPT_TOOLS;
|
|
23
|
+
const placeholders = targetTools.map(() => "?").join(",");
|
|
24
|
+
db.prepare(
|
|
25
|
+
`UPDATE feedback SET accepted = 1
|
|
26
|
+
WHERE workflow_id = ?
|
|
27
|
+
AND command IN (${placeholders})`,
|
|
28
|
+
).run(workflowId, ...targetTools);
|
|
29
|
+
|
|
30
|
+
// Record wiki effectiveness signal for loaded articles
|
|
31
|
+
if (wikiArticles && wikiArticles.length > 0) {
|
|
32
|
+
const wikiDir = join(mainaDir, "wiki");
|
|
33
|
+
recordWikiUsage(wikiDir, wikiArticles, "accept", true);
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// Never throw from signals
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function emitRejectSignal(
|
|
41
|
+
mainaDir: string,
|
|
42
|
+
tool: string,
|
|
43
|
+
workflowId: string,
|
|
44
|
+
wikiArticles?: string[],
|
|
45
|
+
): void {
|
|
46
|
+
try {
|
|
47
|
+
const dbResult = getFeedbackDb(mainaDir);
|
|
48
|
+
if (!dbResult.ok) return;
|
|
49
|
+
const { db } = dbResult.value;
|
|
50
|
+
db.prepare(
|
|
51
|
+
`UPDATE feedback SET accepted = 0
|
|
52
|
+
WHERE command = ? AND workflow_id = ?
|
|
53
|
+
AND id = (
|
|
54
|
+
SELECT id FROM feedback
|
|
55
|
+
WHERE command = ? AND workflow_id = ?
|
|
56
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
57
|
+
)`,
|
|
58
|
+
).run(tool, workflowId, tool, workflowId);
|
|
59
|
+
|
|
60
|
+
// Record wiki effectiveness signal for loaded articles
|
|
61
|
+
if (wikiArticles && wikiArticles.length > 0) {
|
|
62
|
+
const wikiDir = join(mainaDir, "wiki");
|
|
63
|
+
recordWikiUsage(wikiDir, wikiArticles, tool, false);
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
// Never throw from signals
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const VERSION = "0.1.0";
|
|
2
2
|
|
|
3
3
|
// AI
|
|
4
|
+
export { type AIAvailability, checkAIAvailability } from "./ai/availability";
|
|
4
5
|
export { generateCommitMessage } from "./ai/commit-msg";
|
|
5
6
|
// AI — Delegation
|
|
6
7
|
export {
|
|
@@ -99,8 +100,9 @@ export {
|
|
|
99
100
|
setVerificationResult,
|
|
100
101
|
trackFile,
|
|
101
102
|
} from "./context/working";
|
|
102
|
-
// DB
|
|
103
|
+
// DB
|
|
103
104
|
export type { Result } from "./db/index";
|
|
105
|
+
export { getFeedbackDb } from "./db/index";
|
|
104
106
|
// Design (ADR)
|
|
105
107
|
export {
|
|
106
108
|
type AdrSummary,
|
|
@@ -152,6 +154,12 @@ export type {
|
|
|
152
154
|
TraceDeps,
|
|
153
155
|
} from "./features/traceability";
|
|
154
156
|
export { traceFeature } from "./features/traceability";
|
|
157
|
+
export {
|
|
158
|
+
buildToolCacheKey,
|
|
159
|
+
type CaptureInput,
|
|
160
|
+
captureResult,
|
|
161
|
+
getCachedResult,
|
|
162
|
+
} from "./feedback/capture";
|
|
155
163
|
// Feedback
|
|
156
164
|
export {
|
|
157
165
|
type FeedbackRecord,
|
|
@@ -174,6 +182,7 @@ export {
|
|
|
174
182
|
type RulePreference,
|
|
175
183
|
savePreferences,
|
|
176
184
|
} from "./feedback/preferences";
|
|
185
|
+
export { emitAcceptSignal, emitRejectSignal } from "./feedback/signals";
|
|
177
186
|
export {
|
|
178
187
|
exportEpisodicForCloud,
|
|
179
188
|
exportFeedbackForCloud,
|
|
@@ -211,6 +220,7 @@ export {
|
|
|
211
220
|
// Init
|
|
212
221
|
export {
|
|
213
222
|
bootstrap,
|
|
223
|
+
buildMainaSection,
|
|
214
224
|
type DetectedStack,
|
|
215
225
|
type InitOptions,
|
|
216
226
|
type InitReport,
|
|
@@ -284,12 +294,16 @@ export {
|
|
|
284
294
|
getLatest,
|
|
285
295
|
getSkipRate,
|
|
286
296
|
getStats,
|
|
297
|
+
getToolUsageStats,
|
|
287
298
|
getTrends,
|
|
288
299
|
recordSnapshot,
|
|
289
300
|
type SnapshotInput,
|
|
290
301
|
type StatsReport,
|
|
302
|
+
type ToolUsageInput,
|
|
303
|
+
type ToolUsageStats,
|
|
291
304
|
type TrendDirection,
|
|
292
305
|
type TrendsReport,
|
|
306
|
+
trackToolUsage,
|
|
293
307
|
} from "./stats/tracker";
|
|
294
308
|
// Ticket
|
|
295
309
|
export {
|
|
@@ -424,8 +438,101 @@ export {
|
|
|
424
438
|
type ZapOptions,
|
|
425
439
|
type ZapResult,
|
|
426
440
|
} from "./verify/zap";
|
|
441
|
+
// Wiki — Compiler
|
|
442
|
+
export {
|
|
443
|
+
type CompilationResult as WikiCompilationResult,
|
|
444
|
+
type CompileOptions as WikiCompileOptions,
|
|
445
|
+
compile as compileWiki,
|
|
446
|
+
} from "./wiki/compiler";
|
|
447
|
+
export { type CodeEntity, extractCodeEntities } from "./wiki/extractors/code";
|
|
448
|
+
export {
|
|
449
|
+
extractDecisions,
|
|
450
|
+
extractSingleDecision,
|
|
451
|
+
} from "./wiki/extractors/decision";
|
|
452
|
+
export {
|
|
453
|
+
extractFeatures,
|
|
454
|
+
extractSingleFeature,
|
|
455
|
+
} from "./wiki/extractors/feature";
|
|
456
|
+
export { extractWorkflowTrace } from "./wiki/extractors/workflow";
|
|
457
|
+
// Wiki — Graph
|
|
458
|
+
export {
|
|
459
|
+
buildKnowledgeGraph,
|
|
460
|
+
computePageRank,
|
|
461
|
+
type GraphEdge,
|
|
462
|
+
type GraphNode,
|
|
463
|
+
type KnowledgeGraph,
|
|
464
|
+
mapToArticles,
|
|
465
|
+
} from "./wiki/graph";
|
|
466
|
+
// Wiki — Indexer
|
|
467
|
+
export { generateIndex } from "./wiki/indexer";
|
|
468
|
+
// Wiki — Linker
|
|
469
|
+
export { generateLinks, type LinkResult } from "./wiki/linker";
|
|
470
|
+
// Wiki — Louvain
|
|
471
|
+
export {
|
|
472
|
+
detectCommunities,
|
|
473
|
+
type LouvainNode,
|
|
474
|
+
type LouvainResult,
|
|
475
|
+
} from "./wiki/louvain";
|
|
476
|
+
// Wiki — Query
|
|
477
|
+
export {
|
|
478
|
+
queryWiki,
|
|
479
|
+
type WikiQueryOptions,
|
|
480
|
+
type WikiQueryResult,
|
|
481
|
+
} from "./wiki/query";
|
|
482
|
+
export {
|
|
483
|
+
DEFAULT_SCHEMA,
|
|
484
|
+
getArticleMaxLength,
|
|
485
|
+
getLinkSyntax,
|
|
486
|
+
validateArticleStructure,
|
|
487
|
+
type WikiSchema,
|
|
488
|
+
} from "./wiki/schema";
|
|
489
|
+
// Wiki — Signals
|
|
490
|
+
export {
|
|
491
|
+
type ArticleLoadSignal,
|
|
492
|
+
type CompilationPromptSignal,
|
|
493
|
+
calculateEbbinghausScore,
|
|
494
|
+
getPromptEffectiveness,
|
|
495
|
+
getWikiEffectivenessReport,
|
|
496
|
+
recordArticlesLoaded,
|
|
497
|
+
recordWikiUsage,
|
|
498
|
+
type WikiEffectivenessReport,
|
|
499
|
+
type WikiEffectivenessSignal,
|
|
500
|
+
} from "./wiki/signals";
|
|
501
|
+
export {
|
|
502
|
+
createEmptyState,
|
|
503
|
+
getChangedFiles as getWikiChangedFiles,
|
|
504
|
+
hashContent,
|
|
505
|
+
hashFile,
|
|
506
|
+
loadState as loadWikiState,
|
|
507
|
+
saveState as saveWikiState,
|
|
508
|
+
} from "./wiki/state";
|
|
509
|
+
// Wiki — Tracking
|
|
510
|
+
export {
|
|
511
|
+
trackWikiRefsRead,
|
|
512
|
+
trackWikiRefsWritten,
|
|
513
|
+
} from "./wiki/tracking";
|
|
514
|
+
// Wiki
|
|
515
|
+
export type {
|
|
516
|
+
ArticleType,
|
|
517
|
+
DecisionStatus,
|
|
518
|
+
EdgeType,
|
|
519
|
+
ExtractedDecision,
|
|
520
|
+
ExtractedFeature,
|
|
521
|
+
ExtractedWorkflowTrace,
|
|
522
|
+
RLSignal,
|
|
523
|
+
TaskItem,
|
|
524
|
+
WikiArticle,
|
|
525
|
+
WikiLink,
|
|
526
|
+
WikiLintCheck,
|
|
527
|
+
WikiLintFinding,
|
|
528
|
+
WikiLintResult,
|
|
529
|
+
WikiState,
|
|
530
|
+
WorkflowStep as WikiWorkflowStep,
|
|
531
|
+
} from "./wiki/types";
|
|
532
|
+
export { DECAY_HALF_LIVES } from "./wiki/types";
|
|
427
533
|
// Workflow
|
|
428
534
|
export {
|
|
535
|
+
appendWikiRefs,
|
|
429
536
|
appendWorkflowStep,
|
|
430
537
|
loadWorkflowContext,
|
|
431
538
|
resetWorkflowContext,
|