@vpxa/aikit 0.1.359 → 0.1.361

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 (67) hide show
  1. package/README.md +442 -442
  2. package/package.json +3 -1
  3. package/packages/analyzers/dist/index.d.ts +1 -1
  4. package/packages/claude-desktop/dist/manifest.json +31 -0
  5. package/packages/claude-desktop/dist/mcp-source.json +16 -0
  6. package/packages/cli/dist/index.js +14 -14
  7. package/packages/cli/dist/init-JfM6gnAO.js +7 -0
  8. package/packages/cli/dist/mcp-entry.json +8 -0
  9. package/packages/cli/dist/{templates-d2UQHjpi.js → templates-CNDa9H4e.js} +25 -25
  10. package/packages/core/dist/index.d.ts +46 -3
  11. package/packages/core/dist/index.js +1 -1
  12. package/packages/flows/dist/index.d.ts +1 -1
  13. package/packages/flows/dist/index.js +1 -1
  14. package/packages/present/dist/annotation-layer.js +10 -10
  15. package/packages/server/dist/bin.js +1 -1
  16. package/packages/server/dist/config-B4klhHVp.js +2 -0
  17. package/packages/server/dist/config-D3rRPB9X.js +1 -0
  18. package/packages/server/dist/index.js +1 -1
  19. package/packages/server/dist/observations-CfglxBMB.js +6 -0
  20. package/packages/server/dist/observations-CrQU_HVm.js +5 -0
  21. package/packages/server/dist/prelude-BIJWSWyX.js +2 -0
  22. package/packages/server/dist/prelude-Dxc3G0Q2.js +1 -0
  23. package/packages/server/dist/routes-Ct7q5jrL.js +6 -0
  24. package/packages/server/dist/routes-DsSm9ea0.js +5 -0
  25. package/packages/server/dist/sampling-BX1qQuXx.js +2 -0
  26. package/packages/server/dist/sampling-QHtj6UuO.js +1 -0
  27. package/packages/server/dist/{server-CICqmfQ_.js → server-CiPwrtSl.js} +171 -171
  28. package/packages/server/dist/{server-BZVPmRGV.js → server-e0mFUKvp.js} +172 -170
  29. package/packages/server/dist/{server-http-CPU9_eVT.js → server-http-B3dZd6bx.js} +1 -1
  30. package/packages/server/dist/{server-http-D3DK1H19.js → server-http-CzScS-EZ.js} +1 -1
  31. package/packages/server/dist/{server-stdio-BRKxFWyO.js → server-stdio-COkJ2pX9.js} +1 -1
  32. package/packages/server/dist/{server-stdio-l8I0RW4l.js → server-stdio-DmsOjBSR.js} +1 -1
  33. package/packages/server/viewers/architecture-static.html +794 -794
  34. package/packages/server/viewers/report-template.html +11 -11
  35. package/packages/server/viewers/tour-viewer.html +26 -26
  36. package/packages/settings-ui/dist/index.html +12 -12
  37. package/packages/tools/dist/index.d.ts +109 -14
  38. package/packages/tools/dist/index.js +118 -98
  39. package/scaffold/definitions/mcp-entry.json +1 -1
  40. package/scaffold/dist/adapters/_shared.mjs +7 -6
  41. package/scaffold/dist/adapters/claude-code.mjs +7 -7
  42. package/scaffold/dist/adapters/claude-desktop.mjs +7 -0
  43. package/scaffold/dist/adapters/copilot.mjs +1 -1
  44. package/scaffold/dist/adapters/gemini.mjs +6 -6
  45. package/scaffold/dist/adapters/hermes-agent.mjs +1 -1
  46. package/scaffold/dist/adapters/hermes.mjs +5 -5
  47. package/scaffold/dist/adapters/hooks.mjs +1 -1
  48. package/scaffold/dist/adapters/opencode.mjs +7 -7
  49. package/scaffold/dist/adapters/zed.mjs +1 -1
  50. package/scaffold/dist/definitions/exec-hooks.mjs +1 -1
  51. package/scaffold/dist/definitions/flows.mjs +40 -162
  52. package/scaffold/dist/definitions/mcp-entry.json +1 -1
  53. package/scaffold/dist/definitions/platform-capabilities.mjs +1 -0
  54. package/scaffold/dist/definitions/protocols.mjs +22 -19
  55. package/scaffold/dist/definitions/skills/adr-skill.mjs +1 -1
  56. package/scaffold/dist/definitions/skills/docs.mjs +51 -1
  57. package/scaffold/dist/definitions/skills/lesson-learned.mjs +21 -0
  58. package/scaffold/vitest.config.mjs +1 -0
  59. package/packages/cli/dist/init-Cvys5TD4.js +0 -7
  60. package/packages/server/dist/config-4M54ZAT8.js +0 -2
  61. package/packages/server/dist/config-BXd38nVn.js +0 -1
  62. package/packages/server/dist/prelude-CfXBHLpw.js +0 -1
  63. package/packages/server/dist/prelude-D33q42vQ.js +0 -2
  64. package/packages/server/dist/routes-9KnBpwfe.js +0 -6
  65. package/packages/server/dist/routes-B8hTXXR8.js +0 -5
  66. package/packages/server/dist/sampling-Cs9MoNYX.js +0 -2
  67. package/packages/server/dist/sampling-DFogBKrL.js +0 -1
@@ -1,4 +1,4 @@
1
- import{generateForbiddenTable as e}from"./policies.mjs";function t(e){return`
1
+ import{SERVER_NAME as e}from"./mcp.mjs";import{generateForbiddenTable as t}from"./policies.mjs";function n(e){return`
2
2
  ## Flow Context Bootstrap
3
3
 
4
4
  When dispatched as a subagent within an active flow:
@@ -17,10 +17,11 @@ When dispatched as a subagent within an active flow:
17
17
 
18
18
  ${e===`<PROFILE>`?`**Profile:** Check your role → implementer | documenter | reviewer | researcher | debugger`:`**Profile:** \`${e}\``}
19
19
 
20
- ---`}function n(){return"\n## Evidence Citation Protocol (tier-aware)\n\nNo FORGE `task_id` → skip `evidence_map`; use `file:line` citations only.\nDo not create your own `task_id` or run the gate.\n\n| Tier | Your responsibility |\n|------|---------------------|\n| Floor | Findings with `file.ts#Lxx` citations. No `evidence_map`. |\n| Standard | Add 2-4 CRITICAL/HIGH findings with receipts. |\n| Critical | Add all CRITICAL/HIGH findings; tag contract/security claims with `safety_gate`. |\n\n**Every response MUST include:**\n- `**FORGE Task ID:** <task_id>` (passed in by Orchestrator, or state \"not provided\")\n- `**Tier applied:** Floor | Standard | Critical`\n- `**Findings:** <list>` with `file:line` receipts\n- Verdict: `APPROVED` | `CHANGES_REQUESTED` | `BLOCKED`\n\nDo NOT create a new `evidence_map`, run `evidence_map({action:'gate'})`, or add non-critical noise."}function r(...e){return e.filter(Boolean).join(`
20
+ ---`}function r(){return"\n## Evidence Citation Protocol (tier-aware)\n\nNo FORGE `task_id` → skip `evidence_map`; use `file:line` citations only.\nDo not create your own `task_id` or run the gate.\n\n| Tier | Your responsibility |\n|------|---------------------|\n| Floor | Findings with `file.ts#Lxx` citations. No `evidence_map`. |\n| Standard | Add 2-4 CRITICAL/HIGH findings with receipts. |\n| Critical | Add all CRITICAL/HIGH findings; tag contract/security claims with `safety_gate`. |\n\n**Every response MUST include:**\n- `**FORGE Task ID:** <task_id>` (passed in by Orchestrator, or state \"not provided\")\n- `**Tier applied:** Floor | Standard | Critical`\n- `**Findings:** <list>` with `file:line` receipts\n- Verdict: `APPROVED` | `CHANGES_REQUESTED` | `BLOCKED`\n\nDo NOT create a new `evidence_map`, run `evidence_map({action:'gate'})`, or add non-critical noise."}function i(...e){return e.filter(Boolean).join(`
21
21
 
22
- `)}function i({title:e=`Knowledge Recall`,intro:t,commands:n,followUp:i}={}){return r(`## Pre-Task: ${e} (MANDATORY)`,t,["```",...(Array.isArray(n)?n:[n]).filter(Boolean),"```"].join(`
23
- `),i)}function a(){return r(`## Post-Task: Capture Lesson`,`**HARD RULE:** Before DONE, capture 1-2 lessons unless change is pure config/formatting.`,'Quick capture:\n```\nknowledge({ action: "lesson", subAction: "create", context: "<what situation you faced>", insight: "<what principle the solution demonstrates>", evidence: "<file:line or commit that proves it>", confidence: 65 })\n```',"If recalled lesson was confirmed/invalid, use `confirm` or `contradict`.")}const o=1,s=Object.freeze({version:1,l0MemoriesDir:`memories`,sessionStart:{requiredCards:[`workspace-core`],maxAdditionalCards:1,activeFlowPreference:`workflow-card`,totalTokenBudget:2100},retrievalOrder:[`l0`,`l1`,`l2-on-demand`,`l3-evidence-only`],noFlowFallback:`l0-plus-l2-with-turn-local-context`,degradedFallback:`prelude-manifest-canonical-markdown`,lifecycleAuthority:`opaque-owner-capability`,autoKnowledgeMode:`observer-only`,l0Budgets:Object.freeze({"workspace-core":{min:800,max:1200},architecture:{min:600,max:900},"testing-release":{min:600,max:900},"known-issues":{min:400,max:700},"workflow:*":{min:500,max:900}})});function c(e={}){let{audience:t=`agent`}=e,n=s,r=`## Layered Knowledge Protocol (v${n.version})
22
+ `)}function a({title:e=`Knowledge Recall`,intro:t,commands:n,followUp:r}={}){let a=[``,`### Retrieval-First Knowledge Access`,``,`**Always follow progressive discovery: L0 → exact-topic search/refs → broad scans.**`,``,"1. **Start with L0** — `status({ includePrelude: true })` or `aikit://l0/workspace-core` resource",'2. **Search exact topic** — `search({ query: "<specific-topic>" })` — results include `ctxc_` refs','3. **Reuse refs** — `compact({ ref: "ctxc_<hash>" })` restores context without re-running search',"4. **Use `enrich: true`** on search/find/symbol for graph context without extra round-trip",`5. **Broad scan last resort** — only use find/glob/raw reads when L0 + exact search + refs fail`,``,`> **Note:** L1 (flow working-context withdrawal) is handled by your Orchestrator before dispatch —`,'> do not re-withdraw `knowledge({ scope: "flow" })` yourself. L2 (curated) is covered by step 2 above.',``].join(`
23
+ `);return i(`## Pre-Task: ${e} (MANDATORY)`,t,["```",...(Array.isArray(n)?n:[n]).filter(Boolean),"```"].join(`
24
+ `),r,a)}function o(){return i(`## Post-Task: Capture Lesson`,`**HARD RULE:** Before DONE, capture 1-2 lessons unless change is pure config/formatting.`,'Quick capture:\n```\nknowledge({ action: "lesson", subAction: "create", context: "<what situation you faced>", insight: "<what principle the solution demonstrates>", evidence: "<file:line or commit that proves it>", confidence: 65 })\n```',"If recalled lesson was confirmed/invalid, use `confirm` or `contradict`.")}const s=1,c=Object.freeze({version:1,l0MemoriesDir:`memories`,sessionStart:{requiredCards:[`workspace-core`],maxAdditionalCards:1,activeFlowPreference:`workflow-card`,totalTokenBudget:2100},retrievalOrder:[`l0`,`l1`,`l2-on-demand`,`l3-evidence-only`],noFlowFallback:`l0-plus-l2-with-turn-local-context`,degradedFallback:`prelude-manifest-canonical-markdown`,lifecycleAuthority:`opaque-owner-capability`,autoKnowledgeMode:`observer-only`,l0Budgets:Object.freeze({"workspace-core":{min:800,max:1200},architecture:{min:600,max:900},"testing-release":{min:600,max:900},"known-issues":{min:400,max:700},"workflow:*":{min:500,max:900}})});function l(e={}){let{audience:t=`agent`}=e,n=c,r=`## Layered Knowledge Protocol (v${n.version})
24
25
 
25
26
  AI Kit delivers knowledge through four layers:
26
27
 
@@ -51,7 +52,7 @@ AI Kit delivers knowledge through four layers:
51
52
  - Route L1 withdrawal by configured profile; never promote.
52
53
  - Emit L3 evidence as observer-only; never navigate flow state.`:t===`flow`?r+`
53
54
  ### Step Contracts
54
- Each built-in flow step declares knowledge inputs (L0 card types, L1 fields, L2 refs, L3 evidence), context updates, candidate learnings, and handoff artifacts. The step knowledge contract drives role-aware L1 projection.`:r}const l=r(`## Output Rules (HARD RULE)`,`**Plain text is allowed only when ALL are true:**`,` - Response is 1-2 short sentences.`,` - No table, list, checklist, plan, report, verdict, review, summary, progress, evidence map, or batch result is being returned.`,` - No user approval, mandatory stop, or choice is needed.`,"Follow the **Presentation Priority** (1st Inline Visual - `present({ schemaVersion: 1, title, blocks })` → 2nd Interactive - `present({ schemaVersion: 1, title, blocks, actions })` → 3rd Plain Text). Only tiny status/questions that pass the gate above → plain text (Priority 3). Prefer `present` for all others, especially summaries, comparisons, reports, task plans, verdicts, and progress.");function u(){return'## Tool Preference — AI Kit First (HARD RULE)\n\n**Never start with `read_file`, `grep_search`, `semantic_search`, or `run_in_terminal`. AI Kit tools replace all of these.**\n\n| Need | Use | NEVER |\n|------|-----|-------|\n| Structure of a file | `file_summary({ path })` | `read_file` to "understand" it |\n| Find specific code | `file_summary({ files, tier: \'T2\', query })` | `read_file` to search |\n| Multiple files | `digest({ sources, query })` | Multiple `read_file` calls |\n| Reuse previous output | `file_summary({ ref, query })` with `enrich:true` | Re-reading same file |\n| Search codebase | `search({ query })` | `grep_search` / `semantic_search` |\n| Find a symbol | `symbol({ name })` | `grep_search` |\n| Trace data flow | `trace({ items: [{start, direction}] })` | Manual code tracing |\n| Module relationships | `graph({action:\'neighbors\', node_id})` | Manual import tracing |\n| Typecheck + lint | `check({})` | `run_in_terminal tsc` / biome |\n| Run tests | `test_run({})` | `run_in_terminal vitest` |\n| Impact analysis | `blast_radius({ changed_files })` | Manual caller tracing |\n| Complexity metrics | `measure({ path })` | Line counting / wc |\n| L0 briefing card (workspace-core) | `aikit://l0/workspace-core` resource (full card), `file_summary` | Raw file discovery |\n| L0 briefing card (known issues) | `search({ query: "known issues" })` + `knowledge({ action: "lesson" })` | Manual history review |\n| Web research | `web_search({ queries })` + `web_fetch({ urls })` | `fetch_webpage` |\n\n`read_file` is ONLY acceptable when you need exact line content FOR EDITING (before `replace_string_in_file`).\n\nEven then, use `file_summary` first to identify which lines to read.'}const d=r(`## AI Kit Recovery`,"If AI Kit tools are unavailable or missing, run `npx -y @vpxa/aikit@latest init --user`, then reload the client so the `aikit` MCP server and user instructions become available."),f={"code-agent-base":`# Code Agent - Shared Kernel
55
+ Each built-in flow step declares knowledge inputs (L0 card types, L1 fields, L2 refs, L3 evidence), context updates, candidate learnings, and handoff artifacts. The step knowledge contract drives role-aware L1 projection.`:r}const u=i(`## Output Rules (HARD RULE)`,`**Plain text is allowed only when ALL are true:**`,` - Response is 1-2 short sentences.`,` - No table, list, checklist, plan, report, verdict, review, summary, progress, evidence map, or batch result is being returned.`,` - No user approval, mandatory stop, or choice is needed.`,"Follow the **Presentation Priority** (1st Inline Visual - `present({ schemaVersion: 1, title, blocks })` → 2nd Interactive - `present({ schemaVersion: 1, title, blocks, actions })` → 3rd Plain Text). Only tiny status/questions that pass the gate above → plain text (Priority 3). Prefer `present` for all others, especially summaries, comparisons, reports, task plans, verdicts, and progress.");function d(){return'## Tool Preference — AI Kit First (HARD RULE)\n\n**Never start with `read_file`, `grep_search`, `semantic_search`, or `run_in_terminal`. AI Kit tools replace all of these.**\n\n| Need | Use | NEVER |\n|------|-----|-------|\n| Structure of a file | `file_summary({ path })` | `read_file` to "understand" it |\n| Find specific code | `file_summary({ files, tier: \'T2\', query })` | `read_file` to search |\n| Multiple files | `digest({ sources, query })` | Multiple `read_file` calls |\n| Reuse previous output | `file_summary({ ref, query })` with `enrich:true` | Re-reading same file |\n| Search codebase | `search({ query })` | `grep_search` / `semantic_search` |\n| Find a symbol | `symbol({ name })` | `grep_search` |\n| Trace data flow | `trace({ items: [{start, direction}] })` | Manual code tracing |\n| Module relationships | `graph({action:\'neighbors\', node_id})` | Manual import tracing |\n| Typecheck + lint | `check({})` | `run_in_terminal tsc` / biome |\n| Run tests | `test_run({})` | `run_in_terminal vitest` |\n| Impact analysis | `blast_radius({ changed_files })` | Manual caller tracing |\n| Complexity metrics | `measure({ path })` | Line counting / wc |\n| L0 briefing card (workspace-core) | `aikit://l0/workspace-core` resource (full card), `file_summary` | Raw file discovery |\n| L0 briefing card (known issues) | `search({ query: "known issues" })` + `knowledge({ action: "lesson" })` | Manual history review |\n| Web research | `web_search({ queries })` + `web_fetch({ urls })` | `fetch_webpage` |\n\n`read_file` is ONLY acceptable when you need exact line content FOR EDITING (before `replace_string_in_file`).\n\nEven then, use `file_summary` first to identify which lines to read.'}const f=i(`## AI Kit Recovery`,`If AI Kit tools are unavailable or missing, run \`npx -y @vpxa/aikit@latest init --user\`, then reload the client so the \`${e}\` MCP server and user instructions become available.`),p={"code-agent-base":`# Code Agent - Shared Kernel
55
56
 
56
57
  > Shared protocol for code-writing agents. Role files add only role-specific behavior.
57
58
 
@@ -71,7 +72,7 @@ Each built-in flow step declares knowledge inputs (L0 card types, L1 fields, L2
71
72
  - Use terse style if requested by Orchestrator.
72
73
  - End with one status: \`DONE\` | \`DONE_WITH_CONCERNS\` | \`NEEDS_CONTEXT\` | \`BLOCKED\`.
73
74
 
74
- ${u()}
75
+ ${d()}
75
76
 
76
77
  ## Protocol Coverage
77
78
  - conversation-compression: prefer provided compressed context; stash/remember concise findings before context changes; never paste raw long output.
@@ -81,9 +82,9 @@ ${u()}
81
82
  - access protocol: \`401\`/\`403\`/\`404\`/\`407\`, login HTML, CAPTCHA, SSO, or repo auth failure -> \`NEEDS_CONTEXT\` with receipt.
82
83
  - context-cache protocol: search/reuse \`ctxc_...\`, stash, or flow withdrawal before rereading same path/topic.
83
84
 
84
- ${e()}
85
+ ${t()}
85
86
 
86
- ${n()}
87
+ ${r()}
87
88
 
88
89
  ## Work Loop
89
90
  1. Restate scope and success criteria.
@@ -118,9 +119,9 @@ Instructions use short names such as \`status\`, \`file_summary\`, \`search\`. R
118
119
 
119
120
  > Researchers are read-only subagents for discovery, trade-offs, and decision input.
120
121
 
121
- ${t(`researcher`)}
122
+ ${n(`researcher`)}
122
123
 
123
- ${u()}
124
+ ${d()}
124
125
 
125
126
  ## Method
126
127
  1. Start from Orchestrator question + provided context.
@@ -148,9 +149,9 @@ Status: DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED
148
149
 
149
150
  > Review changed behavior, tests, correctness, security, maintainability, patterns, and type safety.
150
151
 
151
- ${t(`reviewer`)}
152
+ ${n(`reviewer`)}
152
153
 
153
- ${u()}
154
+ ${d()}
154
155
 
155
156
  ## Method
156
157
  1. Use provided diff/context first; run \`blast_radius\` when scope is unclear.
@@ -180,9 +181,9 @@ Approve only with zero CRITICAL/HIGH findings.
180
181
 
181
182
  > Review structure: dependency direction, boundaries, public contracts, pattern fit, testability.
182
183
 
183
- ${t(`reviewer`)}
184
+ ${n(`reviewer`)}
184
185
 
185
- ${u()}
186
+ ${d()}
186
187
 
187
188
  ## Method
188
189
  1. Use provided design/diff/context first.
@@ -241,9 +242,11 @@ Each perspective was produced independently — they have NOT seen each other's
241
242
  [Perspective B]
242
243
  {Beta's full response}
243
244
 
244
- [Perspective C]Gamma's full response}
245
+ [Perspective C]
246
+ {Gamma's full response}
245
247
 
246
- [Perspective D]Delta's full response}
248
+ [Perspective D]
249
+ {Delta's full response}
247
250
 
248
251
  Evaluate ALL perspectives. Your review MUST include:
249
252
  1. **Strongest argument** — which perspective and why (cite specific evidence)
@@ -427,7 +430,7 @@ evidence_map({ action: "gate", task_id: "add-user-api" }) → YIELD ✅
427
430
  - **Surface conflicts.** If two patterns contradict, pick one (more recent / more tested). Explain why. Flag the other.
428
431
  - **Checkpoint.** After every significant step, summarize what was done, what’s verified, what’s left.
429
432
  - **Fail loud.** “Completed” is wrong if anything was skipped. Default to surfacing uncertainty.
430
- `,"layered-knowledge-operating-policy":c({audience:`agent`}),"engineering-principles":`# Engineering Principles
433
+ `,"layered-knowledge-operating-policy":l({audience:`agent`}),"engineering-principles":`# Engineering Principles
431
434
 
432
435
  > Operating constraints for code-writing agents. Violating these is a defect.
433
436
 
@@ -516,7 +519,7 @@ Reduces per-request token consumption by 40-70% (from ~85K to ~25-50K). Cache hi
516
519
  - ALWAYS deposit findings BEFORE compressing
517
520
  - Keep each step-summary under 2000 characters
518
521
  - If a step produced errors or blockers, include those verbatim (do not compress)
519
- - On completion: knowledge({action: flush, scope: flow:<run-id>})`},p={"execution-state":`# Execution State: {Task Title}
522
+ - On completion: knowledge({action: flush, scope: flow:<run-id>})`},m={"execution-state":`# Execution State: {Task Title}
520
523
 
521
524
  **Status:** PLANNING | IN_PROGRESS | REVIEW | COMPLETED | BLOCKED
522
525
  **Started:** {timestamp}
@@ -568,4 +571,4 @@ Reduces per-request token consumption by 40-70% (from ~85K to ~25-50K). Cache hi
568
571
 
569
572
  ## Alternatives Considered
570
573
  {Other approaches evaluated and why they were rejected - keeps the "why not" alongside the "why"}
571
- `};export{d as AIKIT_INSTALL_HINT_SECTION,s as LAYERED_KNOWLEDGE_OPERATING_POLICY,o as LAYERED_KNOWLEDGE_OPERATING_POLICY_VERSION,l as PLATFORM_OUTPUT_RULES_SECTION,f as PROTOCOLS,p as TEMPLATES,u as aiKitToolPreference,a as postTaskLesson,i as preTaskKnowledgeRecall,c as renderLayeredKnowledgeOperatingPolicyV1};
574
+ `};export{f as AIKIT_INSTALL_HINT_SECTION,c as LAYERED_KNOWLEDGE_OPERATING_POLICY,s as LAYERED_KNOWLEDGE_OPERATING_POLICY_VERSION,u as PLATFORM_OUTPUT_RULES_SECTION,p as PROTOCOLS,m as TEMPLATES,d as aiKitToolPreference,o as postTaskLesson,a as preTaskKnowledgeRecall,l as renderLayeredKnowledgeOperatingPolicyV1};
@@ -334,7 +334,7 @@ Spin up a fresh PostgreSQL container for each CI job.
334
334
 
335
335
  ### In-memory PostgreSQL (pg-mem)
336
336
 
337
- [pg-mem](https://github.com/oguimbal/pg-mem) — in-memory PostgreSQL emulator for testing.
337
+ pg-mem — in-memory PostgreSQL emulator for testing.
338
338
 
339
339
  - Good, because better SQL compatibility than SQLite
340
340
  - Good, because no infrastructure needed