@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.
Files changed (75) hide show
  1. package/package.json +1 -1
  2. package/src/ai/__tests__/availability.test.ts +131 -0
  3. package/src/ai/__tests__/delegation.test.ts +55 -1
  4. package/src/ai/availability.ts +23 -0
  5. package/src/ai/delegation.ts +5 -3
  6. package/src/context/__tests__/budget.test.ts +29 -6
  7. package/src/context/__tests__/engine.test.ts +1 -0
  8. package/src/context/__tests__/selector.test.ts +23 -3
  9. package/src/context/__tests__/wiki.test.ts +349 -0
  10. package/src/context/budget.ts +12 -8
  11. package/src/context/engine.ts +37 -0
  12. package/src/context/selector.ts +30 -4
  13. package/src/context/wiki.ts +296 -0
  14. package/src/db/index.ts +12 -0
  15. package/src/feedback/__tests__/capture.test.ts +166 -0
  16. package/src/feedback/__tests__/signals.test.ts +144 -0
  17. package/src/feedback/__tests__/tmp-capture-1775575256633-lah0etnzlj/feedback.db +0 -0
  18. package/src/feedback/__tests__/tmp-capture-1775575256640-2xmjme4qraa/feedback.db +0 -0
  19. package/src/feedback/capture.ts +102 -0
  20. package/src/feedback/signals.ts +68 -0
  21. package/src/index.ts +108 -1
  22. package/src/init/__tests__/init.test.ts +477 -18
  23. package/src/init/index.ts +419 -13
  24. package/src/language/__tests__/__fixtures__/detect/composer.lock +1 -0
  25. package/src/prompts/defaults/index.ts +3 -1
  26. package/src/prompts/defaults/wiki-compile.md +20 -0
  27. package/src/prompts/defaults/wiki-query.md +18 -0
  28. package/src/stats/__tests__/tool-usage.test.ts +133 -0
  29. package/src/stats/tracker.ts +92 -0
  30. package/src/verify/__tests__/builtin.test.ts +270 -0
  31. package/src/verify/__tests__/pipeline.test.ts +11 -8
  32. package/src/verify/builtin.ts +350 -0
  33. package/src/verify/pipeline.ts +32 -2
  34. package/src/verify/tools/__tests__/wiki-lint.test.ts +784 -0
  35. package/src/verify/tools/wiki-lint-runner.ts +38 -0
  36. package/src/verify/tools/wiki-lint.ts +898 -0
  37. package/src/wiki/__tests__/compiler.test.ts +389 -0
  38. package/src/wiki/__tests__/extractors/code.test.ts +99 -0
  39. package/src/wiki/__tests__/extractors/decision.test.ts +323 -0
  40. package/src/wiki/__tests__/extractors/feature.test.ts +186 -0
  41. package/src/wiki/__tests__/extractors/workflow.test.ts +131 -0
  42. package/src/wiki/__tests__/graph.test.ts +344 -0
  43. package/src/wiki/__tests__/hooks.test.ts +119 -0
  44. package/src/wiki/__tests__/indexer.test.ts +285 -0
  45. package/src/wiki/__tests__/linker.test.ts +230 -0
  46. package/src/wiki/__tests__/louvain.test.ts +229 -0
  47. package/src/wiki/__tests__/query.test.ts +316 -0
  48. package/src/wiki/__tests__/schema.test.ts +114 -0
  49. package/src/wiki/__tests__/signals.test.ts +474 -0
  50. package/src/wiki/__tests__/state.test.ts +168 -0
  51. package/src/wiki/__tests__/tracking.test.ts +118 -0
  52. package/src/wiki/__tests__/types.test.ts +387 -0
  53. package/src/wiki/compiler.ts +1075 -0
  54. package/src/wiki/extractors/code.ts +90 -0
  55. package/src/wiki/extractors/decision.ts +217 -0
  56. package/src/wiki/extractors/feature.ts +206 -0
  57. package/src/wiki/extractors/workflow.ts +112 -0
  58. package/src/wiki/graph.ts +445 -0
  59. package/src/wiki/hooks.ts +49 -0
  60. package/src/wiki/indexer.ts +105 -0
  61. package/src/wiki/linker.ts +117 -0
  62. package/src/wiki/louvain.ts +190 -0
  63. package/src/wiki/prompts/compile-architecture.md +59 -0
  64. package/src/wiki/prompts/compile-decision.md +66 -0
  65. package/src/wiki/prompts/compile-entity.md +56 -0
  66. package/src/wiki/prompts/compile-feature.md +60 -0
  67. package/src/wiki/prompts/compile-module.md +42 -0
  68. package/src/wiki/prompts/wiki-query.md +25 -0
  69. package/src/wiki/query.ts +338 -0
  70. package/src/wiki/schema.ts +111 -0
  71. package/src/wiki/signals.ts +368 -0
  72. package/src/wiki/state.ts +89 -0
  73. package/src/wiki/tracking.ts +30 -0
  74. package/src/wiki/types.ts +169 -0
  75. 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 (Result type)
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,