@mainahq/core 1.0.3 → 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 (71) hide show
  1. package/package.json +1 -1
  2. package/src/ai/__tests__/delegation.test.ts +55 -1
  3. package/src/ai/delegation.ts +5 -3
  4. package/src/context/__tests__/budget.test.ts +29 -6
  5. package/src/context/__tests__/engine.test.ts +1 -0
  6. package/src/context/__tests__/selector.test.ts +23 -3
  7. package/src/context/__tests__/wiki.test.ts +349 -0
  8. package/src/context/budget.ts +12 -8
  9. package/src/context/engine.ts +37 -0
  10. package/src/context/selector.ts +30 -4
  11. package/src/context/wiki.ts +296 -0
  12. package/src/db/index.ts +12 -0
  13. package/src/feedback/__tests__/capture.test.ts +166 -0
  14. package/src/feedback/__tests__/signals.test.ts +144 -0
  15. package/src/feedback/__tests__/tmp-capture-1775575256633-lah0etnzlj/feedback.db +0 -0
  16. package/src/feedback/__tests__/tmp-capture-1775575256640-2xmjme4qraa/feedback.db +0 -0
  17. package/src/feedback/capture.ts +102 -0
  18. package/src/feedback/signals.ts +68 -0
  19. package/src/index.ts +104 -0
  20. package/src/init/__tests__/init.test.ts +400 -3
  21. package/src/init/index.ts +368 -12
  22. package/src/language/__tests__/__fixtures__/detect/composer.lock +1 -0
  23. package/src/prompts/defaults/index.ts +3 -1
  24. package/src/prompts/defaults/wiki-compile.md +20 -0
  25. package/src/prompts/defaults/wiki-query.md +18 -0
  26. package/src/stats/__tests__/tool-usage.test.ts +133 -0
  27. package/src/stats/tracker.ts +92 -0
  28. package/src/verify/__tests__/pipeline.test.ts +11 -8
  29. package/src/verify/pipeline.ts +13 -1
  30. package/src/verify/tools/__tests__/wiki-lint.test.ts +784 -0
  31. package/src/verify/tools/wiki-lint-runner.ts +38 -0
  32. package/src/verify/tools/wiki-lint.ts +898 -0
  33. package/src/wiki/__tests__/compiler.test.ts +389 -0
  34. package/src/wiki/__tests__/extractors/code.test.ts +99 -0
  35. package/src/wiki/__tests__/extractors/decision.test.ts +323 -0
  36. package/src/wiki/__tests__/extractors/feature.test.ts +186 -0
  37. package/src/wiki/__tests__/extractors/workflow.test.ts +131 -0
  38. package/src/wiki/__tests__/graph.test.ts +344 -0
  39. package/src/wiki/__tests__/hooks.test.ts +119 -0
  40. package/src/wiki/__tests__/indexer.test.ts +285 -0
  41. package/src/wiki/__tests__/linker.test.ts +230 -0
  42. package/src/wiki/__tests__/louvain.test.ts +229 -0
  43. package/src/wiki/__tests__/query.test.ts +316 -0
  44. package/src/wiki/__tests__/schema.test.ts +114 -0
  45. package/src/wiki/__tests__/signals.test.ts +474 -0
  46. package/src/wiki/__tests__/state.test.ts +168 -0
  47. package/src/wiki/__tests__/tracking.test.ts +118 -0
  48. package/src/wiki/__tests__/types.test.ts +387 -0
  49. package/src/wiki/compiler.ts +1075 -0
  50. package/src/wiki/extractors/code.ts +90 -0
  51. package/src/wiki/extractors/decision.ts +217 -0
  52. package/src/wiki/extractors/feature.ts +206 -0
  53. package/src/wiki/extractors/workflow.ts +112 -0
  54. package/src/wiki/graph.ts +445 -0
  55. package/src/wiki/hooks.ts +49 -0
  56. package/src/wiki/indexer.ts +105 -0
  57. package/src/wiki/linker.ts +117 -0
  58. package/src/wiki/louvain.ts +190 -0
  59. package/src/wiki/prompts/compile-architecture.md +59 -0
  60. package/src/wiki/prompts/compile-decision.md +66 -0
  61. package/src/wiki/prompts/compile-entity.md +56 -0
  62. package/src/wiki/prompts/compile-feature.md +60 -0
  63. package/src/wiki/prompts/compile-module.md +42 -0
  64. package/src/wiki/prompts/wiki-query.md +25 -0
  65. package/src/wiki/query.ts +338 -0
  66. package/src/wiki/schema.ts +111 -0
  67. package/src/wiki/signals.ts +368 -0
  68. package/src/wiki/state.ts +89 -0
  69. package/src/wiki/tracking.ts +30 -0
  70. package/src/wiki/types.ts +169 -0
  71. package/src/workflow/context.ts +26 -0
package/src/init/index.ts CHANGED
@@ -332,7 +332,9 @@ const MCP_TOOLS_TABLE = `| Tool | When to use |
332
332
  | \`suggestTests\` | When implementing — generate TDD test stubs |
333
333
  | \`getConventions\` | Understand project coding conventions |
334
334
  | \`explainModule\` | Understand a module's purpose and dependencies |
335
- | \`analyzeFeature\` | Analyze a feature directory for consistency |`;
335
+ | \`analyzeFeature\` | Analyze a feature directory for consistency |
336
+ | \`wikiQuery\` | Search wiki for codebase knowledge — "how does auth work?" |
337
+ | \`wikiStatus\` | Wiki health check — article counts, staleness, coverage |`;
336
338
 
337
339
  // ── Templates ────────────────────────────────────────────────────────────────
338
340
 
@@ -465,6 +467,13 @@ maina commit # verify + commit
465
467
 
466
468
  ${MCP_TOOLS_TABLE}
467
469
 
470
+ ## Wiki
471
+
472
+ If \`.maina/wiki/\` exists, use wiki tools for context:
473
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
474
+ - \`wikiStatus\` to check health
475
+ - Wiki articles are loaded automatically as Context Engine Layer 5
476
+
468
477
  ## Config Files
469
478
  | File | Purpose | Who Edits |
470
479
  |------|---------|-----------|
@@ -474,6 +483,13 @@ ${MCP_TOOLS_TABLE}
474
483
  | \`CLAUDE.md\` | Claude Code specific instructions | Optional, Claude Code users |
475
484
  | \`GEMINI.md\` | Gemini CLI specific instructions | Optional, Gemini CLI users |
476
485
  | \`.cursorrules\` | Cursor specific instructions | Optional, Cursor users |
486
+ | \`.windsurfrules\` | Windsurf specific instructions | Optional, Windsurf users |
487
+ | \`.clinerules\` | Cline specific instructions | Optional, Cline users |
488
+ | \`.continue/\` | Continue.dev config + MCP | Optional, Continue.dev users |
489
+ | \`.roo/\` | Roo Code MCP config + rules | Optional, Roo Code users |
490
+ | \`.amazonq/mcp.json\` | Amazon Q MCP config | Optional, Amazon Q users |
491
+ | \`.aider.conf.yml\` | Aider config | Optional, Aider users |
492
+ | \`CONVENTIONS.md\` | Project conventions (Aider, generic) | Team |
477
493
  | \`.mcp.json\` | MCP server configuration | Team |
478
494
  | \`.maina/prompts/*.md\` | Prompt overrides for review/commit/etc | Maina (via \`maina learn\`) |
479
495
 
@@ -506,6 +522,13 @@ Follow this order for every feature:
506
522
 
507
523
  ${MCP_TOOLS_TABLE}
508
524
 
525
+ ## Wiki
526
+
527
+ If \`.maina/wiki/\` exists, use wiki tools for context:
528
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
529
+ - \`wikiStatus\` to check health
530
+ - Wiki articles are loaded automatically as Context Engine Layer 5
531
+
509
532
  ## Conventions
510
533
 
511
534
  - Runtime: ${stack.runtime}
@@ -529,6 +552,10 @@ const MERGEABLE_AGENT_FILES = [
529
552
  "GEMINI.md",
530
553
  ".cursorrules",
531
554
  ".github/copilot-instructions.md",
555
+ ".windsurfrules",
556
+ ".clinerules",
557
+ ".roo/rules/maina.md",
558
+ "CONVENTIONS.md",
532
559
  ];
533
560
 
534
561
  /**
@@ -551,13 +578,32 @@ ${MCP_TOOLS_TABLE}
551
578
 
552
579
  // ── .mcp.json ───────────────────────────────────────────────────────────────
553
580
 
554
- function buildMcpJson(): string {
581
+ function buildMcpJson(stack: DetectedStack): string {
582
+ const command = stack.runtime === "bun" ? "bunx" : "npx";
555
583
  return JSON.stringify(
556
584
  {
557
585
  mcpServers: {
558
586
  maina: {
559
- command: "maina",
560
- args: ["--mcp"],
587
+ command,
588
+ args: ["@mainahq/cli", "--mcp"],
589
+ },
590
+ },
591
+ },
592
+ null,
593
+ 2,
594
+ );
595
+ }
596
+
597
+ // ── .claude/settings.json (Claude Code MCP config) ─────────────────────────
598
+
599
+ function buildClaudeSettings(stack: DetectedStack): string {
600
+ const command = stack.runtime === "bun" ? "bunx" : "npx";
601
+ return JSON.stringify(
602
+ {
603
+ mcpServers: {
604
+ maina: {
605
+ command,
606
+ args: ["@mainahq/cli", "--mcp"],
561
607
  },
562
608
  },
563
609
  },
@@ -586,16 +632,42 @@ Maina exposes MCP tools — use them in every session:
586
632
 
587
633
  ${MCP_TOOLS_TABLE}
588
634
 
635
+ ## Wiki
636
+
637
+ If \`.maina/wiki/\` exists, use wiki tools for context:
638
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
639
+ - \`wikiStatus\` to check health
640
+ - Wiki articles are loaded automatically as Context Engine Layer 5
641
+
589
642
  ## Commands
590
643
 
591
644
  \`\`\`bash
592
- maina verify # run full verification pipeline
593
- maina commit # verify + commit
594
- maina review # two-stage code review
595
- maina context # generate focused codebase context
596
- maina doctor # check tool health
597
- maina plan # create feature with spec/plan/tasks
598
- maina stats # show verification metrics
645
+ # Workflow
646
+ maina brainstorm # explore ideas interactively
647
+ maina ticket # create GitHub issue with module tagging
648
+ maina plan <name> # scaffold feature branch + directory
649
+ maina design # create ADR (architecture decision record)
650
+ maina spec # generate TDD test stubs from plan
651
+
652
+ # Verify & Review
653
+ maina verify # run full verification pipeline (12+ tools)
654
+ maina review # two-stage code review
655
+ maina slop # detect AI-generated slop patterns
656
+ maina commit # verify + commit staged changes
657
+
658
+ # Wiki (codebase knowledge)
659
+ maina wiki init # compile codebase knowledge wiki
660
+ maina wiki query # ask questions about the codebase
661
+ maina wiki compile # recompile wiki (incremental)
662
+ maina wiki status # wiki health dashboard
663
+ maina wiki lint # check wiki for issues
664
+
665
+ # Context & Info
666
+ maina context # generate focused codebase context
667
+ maina explain # explain a module with wiki context
668
+ maina doctor # check tool health
669
+ maina stats # verification metrics
670
+ maina status # branch health overview
599
671
  \`\`\`
600
672
 
601
673
  ## Conventions
@@ -629,6 +701,13 @@ Maina exposes MCP tools via \`.mcp.json\`. Use them:
629
701
 
630
702
  ${MCP_TOOLS_TABLE}
631
703
 
704
+ ## Wiki
705
+
706
+ If \`.maina/wiki/\` exists, use wiki tools for context:
707
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
708
+ - \`wikiStatus\` to check health
709
+ - Wiki articles are loaded automatically as Context Engine Layer 5
710
+
632
711
  ## Key Commands
633
712
 
634
713
  - \`maina verify\` — run full verification pipeline
@@ -661,6 +740,89 @@ ${WORKFLOW_ORDER}
661
740
  ## MCP Tools (via .mcp.json)
662
741
  ${MCP_TOOLS_TABLE}
663
742
 
743
+ ## Wiki
744
+
745
+ If \`.maina/wiki/\` exists, use wiki tools for context:
746
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
747
+ - \`wikiStatus\` to check health
748
+ - Wiki articles are loaded automatically as Context Engine Layer 5
749
+
750
+ ## Commands
751
+ - maina verify — run full verification pipeline
752
+ - maina commit — verify + commit
753
+ - maina review — two-stage code review
754
+ - maina context — generate focused codebase context
755
+
756
+ ## Conventions
757
+ - Runtime: ${stack.runtime}
758
+ - Test: ${runCmd} test
759
+ - Conventional commits
760
+ - No console.log in production
761
+ - Diff-only: only report findings on changed lines
762
+ - TDD: write tests first, then implement
763
+ `;
764
+ }
765
+
766
+ // ── Windsurf ────────────────────────────────────────────────────────────────
767
+
768
+ function buildWindsurfRules(stack: DetectedStack): string {
769
+ const runCmd = stack.runtime === "bun" ? "bun" : "npm";
770
+ return `# Windsurf Rules
771
+
772
+ This repo uses Maina for verification-first development.
773
+ Read \`.maina/constitution.md\` for project DNA.
774
+
775
+ ## Workflow Order
776
+ ${WORKFLOW_ORDER}
777
+
778
+ ## MCP Tools (via .mcp.json)
779
+ ${MCP_TOOLS_TABLE}
780
+
781
+ ## Wiki
782
+
783
+ If \`.maina/wiki/\` exists, use wiki tools for context:
784
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
785
+ - \`wikiStatus\` to check health
786
+ - Wiki articles are loaded automatically as Context Engine Layer 5
787
+
788
+ ## Commands
789
+ - maina verify — run full verification pipeline
790
+ - maina commit — verify + commit
791
+ - maina review — two-stage code review
792
+ - maina context — generate focused codebase context
793
+
794
+ ## Conventions
795
+ - Runtime: ${stack.runtime}
796
+ - Test: ${runCmd} test
797
+ - Conventional commits
798
+ - No console.log in production
799
+ - Diff-only: only report findings on changed lines
800
+ - TDD: write tests first, then implement
801
+ `;
802
+ }
803
+
804
+ // ── Cline ───────────────────────────────────────────────────────────────────
805
+
806
+ function buildClineRules(stack: DetectedStack): string {
807
+ const runCmd = stack.runtime === "bun" ? "bun" : "npm";
808
+ return `# Cline Rules
809
+
810
+ This repo uses Maina for verification-first development.
811
+ Read \`.maina/constitution.md\` for project DNA.
812
+
813
+ ## Workflow Order
814
+ ${WORKFLOW_ORDER}
815
+
816
+ ## MCP Tools (via .mcp.json)
817
+ ${MCP_TOOLS_TABLE}
818
+
819
+ ## Wiki
820
+
821
+ If \`.maina/wiki/\` exists, use wiki tools for context:
822
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
823
+ - \`wikiStatus\` to check health
824
+ - Wiki articles are loaded automatically as Context Engine Layer 5
825
+
664
826
  ## Commands
665
827
  - maina verify — run full verification pipeline
666
828
  - maina commit — verify + commit
@@ -677,6 +839,160 @@ ${MCP_TOOLS_TABLE}
677
839
  `;
678
840
  }
679
841
 
842
+ // ── Continue.dev ─────────────────────────────────────────────────────────────
843
+
844
+ function buildContinueConfig(_stack: DetectedStack): string {
845
+ return `# Continue.dev configuration — auto-generated by maina init
846
+ # See https://docs.continue.dev/reference/config
847
+
848
+ customInstructions: |
849
+ This repo uses Maina for verification-first development.
850
+ Read .maina/constitution.md for project DNA.
851
+ Workflow: ${WORKFLOW_ORDER}
852
+ Always run maina verify before committing.
853
+ Use MCP tools: getContext, verify, checkSlop, reviewCode, suggestTests.
854
+ `;
855
+ }
856
+
857
+ function buildContinueMcpJson(stack: DetectedStack): string {
858
+ const command = stack.runtime === "bun" ? "bunx" : "npx";
859
+ return JSON.stringify(
860
+ {
861
+ maina: {
862
+ command,
863
+ args: ["@mainahq/cli", "--mcp"],
864
+ },
865
+ },
866
+ null,
867
+ 2,
868
+ );
869
+ }
870
+
871
+ // ── Roo Code ────────────────────────────────────────────────────────────────
872
+
873
+ function buildRooMcpJson(stack: DetectedStack): string {
874
+ const command = stack.runtime === "bun" ? "bunx" : "npx";
875
+ return JSON.stringify(
876
+ {
877
+ mcpServers: {
878
+ maina: {
879
+ command,
880
+ args: ["@mainahq/cli", "--mcp"],
881
+ },
882
+ },
883
+ },
884
+ null,
885
+ 2,
886
+ );
887
+ }
888
+
889
+ function buildRooRules(stack: DetectedStack): string {
890
+ const runCmd = stack.runtime === "bun" ? "bun" : "npm";
891
+ return `# Maina
892
+
893
+ This repo uses [Maina](https://mainahq.com) for verification-first development.
894
+ Read \`.maina/constitution.md\` for project DNA.
895
+
896
+ ## Workflow Order
897
+ ${WORKFLOW_ORDER}
898
+
899
+ ## MCP Tools
900
+ ${MCP_TOOLS_TABLE}
901
+
902
+ ## Wiki
903
+
904
+ If \`.maina/wiki/\` exists, use wiki tools for context:
905
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
906
+ - \`wikiStatus\` to check health
907
+ - Wiki articles are loaded automatically as Context Engine Layer 5
908
+
909
+ ## Commands
910
+ - maina verify — run full verification pipeline
911
+ - maina commit — verify + commit
912
+ - maina review — two-stage code review
913
+ - maina context — generate focused codebase context
914
+
915
+ ## Conventions
916
+ - Runtime: ${stack.runtime}
917
+ - Test: ${runCmd} test
918
+ - Conventional commits
919
+ - No console.log in production
920
+ - Diff-only: only report findings on changed lines
921
+ - TDD: write tests first, then implement
922
+ `;
923
+ }
924
+
925
+ // ── Amazon Q ────────────────────────────────────────────────────────────────
926
+
927
+ function buildAmazonQMcpJson(stack: DetectedStack): string {
928
+ const command = stack.runtime === "bun" ? "bunx" : "npx";
929
+ return JSON.stringify(
930
+ {
931
+ mcpServers: {
932
+ maina: {
933
+ command,
934
+ args: ["@mainahq/cli", "--mcp"],
935
+ },
936
+ },
937
+ },
938
+ null,
939
+ 2,
940
+ );
941
+ }
942
+
943
+ // ── Aider ───────────────────────────────────────────────────────────────────
944
+
945
+ function buildAiderConfig(_stack: DetectedStack): string {
946
+ return `# Maina conventions — auto-generated by maina init
947
+ read: [CONVENTIONS.md, .maina/constitution.md]
948
+ auto-commits: false
949
+ `;
950
+ }
951
+
952
+ function buildConventionsMd(stack: DetectedStack): string {
953
+ const runCmd = stack.runtime === "bun" ? "bun" : "npm";
954
+ return `# Conventions
955
+
956
+ This repo uses [Maina](https://mainahq.com) for verification-first development.
957
+ Read \`.maina/constitution.md\` for project DNA — stack rules, conventions, and gates.
958
+
959
+ ## Maina Workflow
960
+
961
+ Follow this order for every feature:
962
+ \`${WORKFLOW_ORDER}\`
963
+
964
+ ## MCP Tools
965
+
966
+ Maina exposes MCP tools — use them in every session:
967
+
968
+ ${MCP_TOOLS_TABLE}
969
+
970
+ ## Wiki
971
+
972
+ If \`.maina/wiki/\` exists, use wiki tools for context:
973
+ - \`wikiQuery\` before coding — understand existing patterns and decisions
974
+ - \`wikiStatus\` to check health
975
+ - Wiki articles are loaded automatically as Context Engine Layer 5
976
+
977
+ ## Key Commands
978
+
979
+ - \`maina verify\` — run full verification pipeline
980
+ - \`maina commit\` — verify + commit
981
+ - \`maina review\` — two-stage code review
982
+ - \`maina context\` — generate focused codebase context
983
+ - \`maina doctor\` — check tool health
984
+
985
+ ## Conventions
986
+
987
+ - Runtime: ${stack.runtime}
988
+ - Test: \`${runCmd} test\`
989
+ - Conventional commits (feat, fix, refactor, test, docs, chore)
990
+ - No \`console.log\` in production code
991
+ - Diff-only: only report findings on changed lines
992
+ - TDD always — write tests first
993
+ `;
994
+ }
995
+
680
996
  // ── AI-Generated Constitution ───────────────────────────────────────────────
681
997
 
682
998
  function buildProjectSummary(repoRoot: string, stack: DetectedStack): string {
@@ -869,7 +1185,11 @@ function getFileManifest(
869
1185
  },
870
1186
  {
871
1187
  relativePath: ".mcp.json",
872
- content: buildMcpJson(),
1188
+ content: buildMcpJson(stack),
1189
+ },
1190
+ {
1191
+ relativePath: ".claude/settings.json",
1192
+ content: buildClaudeSettings(stack),
873
1193
  },
874
1194
  {
875
1195
  relativePath: "CLAUDE.md",
@@ -883,6 +1203,42 @@ function getFileManifest(
883
1203
  relativePath: ".cursorrules",
884
1204
  content: buildCursorRules(stack),
885
1205
  },
1206
+ {
1207
+ relativePath: ".windsurfrules",
1208
+ content: buildWindsurfRules(stack),
1209
+ },
1210
+ {
1211
+ relativePath: ".clinerules",
1212
+ content: buildClineRules(stack),
1213
+ },
1214
+ {
1215
+ relativePath: ".continue/config.yaml",
1216
+ content: buildContinueConfig(stack),
1217
+ },
1218
+ {
1219
+ relativePath: ".continue/mcpServers/maina.json",
1220
+ content: buildContinueMcpJson(stack),
1221
+ },
1222
+ {
1223
+ relativePath: ".roo/mcp.json",
1224
+ content: buildRooMcpJson(stack),
1225
+ },
1226
+ {
1227
+ relativePath: ".roo/rules/maina.md",
1228
+ content: buildRooRules(stack),
1229
+ },
1230
+ {
1231
+ relativePath: ".amazonq/mcp.json",
1232
+ content: buildAmazonQMcpJson(stack),
1233
+ },
1234
+ {
1235
+ relativePath: ".aider.conf.yml",
1236
+ content: buildAiderConfig(stack),
1237
+ },
1238
+ {
1239
+ relativePath: "CONVENTIONS.md",
1240
+ content: buildConventionsMd(stack),
1241
+ },
886
1242
  ];
887
1243
  }
888
1244
 
@@ -0,0 +1 @@
1
+ {"packages": []}
@@ -11,7 +11,9 @@ export type PromptTask =
11
11
  | "spec-questions"
12
12
  | "design-approaches"
13
13
  | "ai-review"
14
- | "design-hld-lld";
14
+ | "design-hld-lld"
15
+ | "wiki-query"
16
+ | "wiki-compile";
15
17
 
16
18
  const FALLBACK_TEMPLATE = `You are a helpful AI assistant completing the "{{task}}" task.
17
19
 
@@ -0,0 +1,20 @@
1
+ You are enhancing a wiki article with natural language descriptions.
2
+
3
+ ## Constitution (non-negotiable)
4
+ {{constitution}}
5
+
6
+ ## Instructions
7
+ Enhance this wiki article with natural language descriptions. Keep all existing structured data.
8
+ Add a brief overview paragraph and usage context. Do not remove any existing content.
9
+
10
+ Rules:
11
+ 1. Preserve ALL existing markdown structure, headings, lists, and wikilinks
12
+ 2. Add a concise overview paragraph after the title heading
13
+ 3. Add natural language descriptions to sections that are purely data-driven
14
+ 4. Infer relationships not visible from code structure alone where possible
15
+ 5. Add brief usage examples if the context provides enough information
16
+ 6. Keep the tone technical and factual — do not add filler or marketing language
17
+ 7. If anything is ambiguous, use [NEEDS CLARIFICATION: specific question]
18
+
19
+ ## Input
20
+ {{input}}
@@ -0,0 +1,18 @@
1
+ You are answering a question about a codebase using wiki documentation.
2
+
3
+ ## Constitution (non-negotiable)
4
+ {{constitution}}
5
+
6
+ ## Instructions
7
+ Answer the user's question based on the provided wiki articles.
8
+
9
+ Rules:
10
+ 1. Provide a clear, concise answer that directly addresses the question
11
+ 2. Cite relevant articles using [[article_path]] notation (e.g., [[modules/core.md]])
12
+ 3. If the articles don't contain enough information, say so explicitly
13
+ 4. Do NOT guess or hallucinate information not present in the articles
14
+ 5. If anything is ambiguous, use [NEEDS CLARIFICATION: specific question]
15
+ 6. Prioritize accuracy over completeness
16
+
17
+ ## Input
18
+ {{input}}
@@ -0,0 +1,133 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { getStatsDb } from "../../db/index";
5
+ import { getToolUsageStats, trackToolUsage } from "../tracker";
6
+
7
+ let tmpDir: string;
8
+
9
+ beforeEach(() => {
10
+ tmpDir = join(
11
+ import.meta.dir,
12
+ `tmp-tool-usage-${Date.now()}-${Math.random().toString(36).slice(2)}`,
13
+ );
14
+ mkdirSync(tmpDir, { recursive: true });
15
+ });
16
+
17
+ afterEach(() => {
18
+ try {
19
+ rmSync(tmpDir, { recursive: true, force: true });
20
+ } catch {
21
+ /* ignore */
22
+ }
23
+ });
24
+
25
+ describe("tool_usage table", () => {
26
+ test("getStatsDb creates tool_usage table", () => {
27
+ const result = getStatsDb(tmpDir);
28
+ expect(result.ok).toBe(true);
29
+ if (!result.ok) return;
30
+ const { db } = result.value;
31
+ const tables = db
32
+ .query(
33
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='tool_usage'",
34
+ )
35
+ .all();
36
+ expect(tables).toHaveLength(1);
37
+ });
38
+ });
39
+
40
+ describe("trackToolUsage", () => {
41
+ test("inserts a tool usage row", () => {
42
+ trackToolUsage(tmpDir, {
43
+ tool: "reviewCode",
44
+ inputHash: "abc123",
45
+ durationMs: 150,
46
+ cacheHit: false,
47
+ });
48
+ const result = getStatsDb(tmpDir);
49
+ expect(result.ok).toBe(true);
50
+ if (!result.ok) return;
51
+ const { db } = result.value;
52
+ const rows = db.query("SELECT * FROM tool_usage").all() as Array<{
53
+ tool: string;
54
+ duration_ms: number;
55
+ cache_hit: number;
56
+ }>;
57
+ expect(rows).toHaveLength(1);
58
+ const row = rows[0];
59
+ expect(row).toBeDefined();
60
+ expect(row?.tool).toBe("reviewCode");
61
+ expect(row?.duration_ms).toBe(150);
62
+ expect(row?.cache_hit).toBe(0);
63
+ });
64
+
65
+ test("tracks cache hit", () => {
66
+ trackToolUsage(tmpDir, {
67
+ tool: "reviewCode",
68
+ inputHash: "abc123",
69
+ durationMs: 0,
70
+ cacheHit: true,
71
+ });
72
+ const result = getStatsDb(tmpDir);
73
+ if (!result.ok) return;
74
+ const { db } = result.value;
75
+ const rows = db.query("SELECT * FROM tool_usage WHERE cache_hit = 1").all();
76
+ expect(rows).toHaveLength(1);
77
+ });
78
+
79
+ test("stores workflow_id when provided", () => {
80
+ trackToolUsage(tmpDir, {
81
+ tool: "verify",
82
+ inputHash: "def456",
83
+ durationMs: 200,
84
+ cacheHit: false,
85
+ workflowId: "wf-123",
86
+ });
87
+ const result = getStatsDb(tmpDir);
88
+ if (!result.ok) return;
89
+ const { db } = result.value;
90
+ const row = db.query("SELECT workflow_id FROM tool_usage").get() as {
91
+ workflow_id: string;
92
+ } | null;
93
+ expect(row?.workflow_id).toBe("wf-123");
94
+ });
95
+ });
96
+
97
+ describe("getToolUsageStats", () => {
98
+ test("returns stats across multiple tool calls", () => {
99
+ trackToolUsage(tmpDir, {
100
+ tool: "reviewCode",
101
+ inputHash: "a",
102
+ durationMs: 100,
103
+ cacheHit: false,
104
+ });
105
+ trackToolUsage(tmpDir, {
106
+ tool: "reviewCode",
107
+ inputHash: "b",
108
+ durationMs: 200,
109
+ cacheHit: true,
110
+ });
111
+ trackToolUsage(tmpDir, {
112
+ tool: "verify",
113
+ inputHash: "c",
114
+ durationMs: 50,
115
+ cacheHit: false,
116
+ });
117
+ const stats = getToolUsageStats(tmpDir);
118
+ expect(stats.totalCalls).toBe(3);
119
+ expect(stats.cacheHits).toBe(1);
120
+ expect(stats.cacheHitRate).toBeCloseTo(1 / 3, 2);
121
+ expect(stats.byTool.reviewCode).toBeDefined();
122
+ expect(stats.byTool.reviewCode?.calls).toBe(2);
123
+ expect(stats.byTool.reviewCode?.cacheHits).toBe(1);
124
+ expect(stats.byTool.verify).toBeDefined();
125
+ expect(stats.byTool.verify?.calls).toBe(1);
126
+ });
127
+
128
+ test("returns empty stats when no data", () => {
129
+ const stats = getToolUsageStats(tmpDir);
130
+ expect(stats.totalCalls).toBe(0);
131
+ expect(stats.cacheHitRate).toBe(0);
132
+ });
133
+ });