supipowers 1.5.2 → 2.0.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 (340) hide show
  1. package/README.md +14 -8
  2. package/bin/install.mjs +20 -5
  3. package/bin/install.ts +95 -0
  4. package/package.json +8 -4
  5. package/skills/context-mode/SKILL.md +17 -10
  6. package/skills/harness/SKILL.md +94 -0
  7. package/skills/ui-design/SKILL.md +63 -0
  8. package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
  9. package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
  10. package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
  11. package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
  12. package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
  13. package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
  14. package/skills/ultraplan-discover/SKILL.md +96 -0
  15. package/skills/ultraplan-intake/SKILL.md +89 -0
  16. package/skills/ultraplan-research/SKILL.md +129 -0
  17. package/skills/ultraplan-review/SKILL.md +86 -0
  18. package/skills/ultraplan-review-scope/SKILL.md +111 -0
  19. package/skills/ultraplan-review-structure/SKILL.md +120 -0
  20. package/skills/ultraplan-review-tdd/SKILL.md +142 -0
  21. package/skills/ultraplan-scout/SKILL.md +110 -0
  22. package/skills/ultraplan-synthesize/SKILL.md +124 -0
  23. package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
  24. package/src/ai/schema-text.ts +129 -0
  25. package/src/ai/structured-output.ts +274 -0
  26. package/src/ai/template.ts +27 -0
  27. package/src/bootstrap.ts +63 -28
  28. package/src/commands/agents.ts +149 -45
  29. package/src/commands/ai-review.ts +251 -30
  30. package/src/commands/clear.ts +434 -0
  31. package/src/commands/commit.ts +1 -0
  32. package/src/commands/config.ts +242 -44
  33. package/src/commands/context.ts +55 -28
  34. package/src/commands/doctor.ts +234 -6
  35. package/src/commands/fix-pr.ts +306 -131
  36. package/src/commands/generate.ts +111 -21
  37. package/src/commands/memory.ts +192 -0
  38. package/src/commands/model-picker.ts +28 -21
  39. package/src/commands/model.ts +19 -9
  40. package/src/commands/optimize-context.ts +408 -29
  41. package/src/commands/plan.ts +2 -0
  42. package/src/commands/qa.ts +312 -137
  43. package/src/commands/release.ts +259 -76
  44. package/src/commands/review.ts +293 -59
  45. package/src/commands/status.ts +200 -13
  46. package/src/commands/supi.ts +3 -35
  47. package/src/commands/ui-design.ts +394 -0
  48. package/src/commands/ultraplan.ts +1518 -0
  49. package/src/commands/update.ts +86 -0
  50. package/src/config/defaults.ts +62 -0
  51. package/src/config/loader.ts +448 -60
  52. package/src/config/schema.ts +108 -2
  53. package/src/context/optimizer.ts +25 -33
  54. package/src/context/rule-renderer.ts +223 -0
  55. package/src/context/savings.ts +258 -0
  56. package/src/context/startup-check.ts +380 -0
  57. package/src/context/startup-optimizer.ts +355 -0
  58. package/src/context/tokenignore.ts +146 -0
  59. package/src/context-mode/cache-handle.ts +49 -0
  60. package/src/context-mode/cache-preview.ts +71 -0
  61. package/src/context-mode/cache-store.ts +738 -0
  62. package/src/context-mode/compressor.ts +131 -26
  63. package/src/context-mode/dedup.ts +108 -0
  64. package/src/context-mode/detector.ts +35 -4
  65. package/src/context-mode/event-extractor.ts +14 -12
  66. package/src/context-mode/event-store.ts +91 -36
  67. package/src/context-mode/hooks.ts +798 -56
  68. package/src/context-mode/knowledge/store.ts +255 -11
  69. package/src/context-mode/memory-store.ts +325 -0
  70. package/src/context-mode/metrics-recorder.ts +158 -0
  71. package/src/context-mode/metrics-store.ts +765 -0
  72. package/src/context-mode/model.ts +24 -0
  73. package/src/context-mode/processor-keys.ts +29 -0
  74. package/src/context-mode/processors/build.ts +66 -0
  75. package/src/context-mode/processors/docker.ts +57 -0
  76. package/src/context-mode/processors/git.ts +111 -0
  77. package/src/context-mode/processors/json.ts +112 -0
  78. package/src/context-mode/processors/k8s.ts +67 -0
  79. package/src/context-mode/processors/lint.ts +67 -0
  80. package/src/context-mode/processors/log.ts +86 -0
  81. package/src/context-mode/processors/registry.ts +116 -0
  82. package/src/context-mode/processors/test-runner.ts +102 -0
  83. package/src/context-mode/processors/types.ts +20 -0
  84. package/src/context-mode/repomap.ts +400 -0
  85. package/src/context-mode/routing.ts +97 -24
  86. package/src/context-mode/sandbox/runners.ts +5 -1
  87. package/src/context-mode/snapshot-builder.ts +106 -11
  88. package/src/context-mode/source-hash.ts +173 -0
  89. package/src/context-mode/tool-name.ts +11 -0
  90. package/src/context-mode/tools.ts +654 -22
  91. package/src/context-mode/web/fetcher.ts +31 -12
  92. package/src/debug/logger.ts +2 -1
  93. package/src/deps/registry.ts +1 -1
  94. package/src/discipline/failure-summarizer.ts +170 -0
  95. package/src/discipline/failure-taxonomy.ts +131 -0
  96. package/src/discipline/workflow-invariants.ts +125 -0
  97. package/src/discovery/index.ts +31 -0
  98. package/src/discovery/lsp.ts +87 -0
  99. package/src/discovery/rank.ts +144 -0
  100. package/src/discovery/sources.ts +89 -0
  101. package/src/discovery/workflow.ts +87 -0
  102. package/src/docs/contracts.ts +39 -0
  103. package/src/docs/drift.ts +117 -87
  104. package/src/fix-pr/assessment.ts +200 -0
  105. package/src/fix-pr/contracts.ts +47 -0
  106. package/src/fix-pr/fetch-comments.ts +80 -0
  107. package/src/fix-pr/prompt-builder.ts +58 -40
  108. package/src/fix-pr/scripts/exec.ts +34 -0
  109. package/src/fix-pr/scripts/trigger-review.ts +106 -0
  110. package/src/fix-pr/scripts/wait-and-check.ts +108 -0
  111. package/src/fix-pr/types.ts +4 -0
  112. package/src/git/branch-finish.ts +5 -0
  113. package/src/git/commit-contract.ts +83 -0
  114. package/src/git/commit.ts +121 -184
  115. package/src/git/status.ts +62 -8
  116. package/src/harness/anti_slop/architecture-parser.ts +210 -0
  117. package/src/harness/anti_slop/backend-factory.ts +30 -0
  118. package/src/harness/anti_slop/backend.ts +140 -0
  119. package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
  120. package/src/harness/anti_slop/fallow-adapter.ts +305 -0
  121. package/src/harness/anti_slop/installer.ts +227 -0
  122. package/src/harness/anti_slop/queue.ts +216 -0
  123. package/src/harness/anti_slop/recommend.ts +84 -0
  124. package/src/harness/anti_slop/score.ts +180 -0
  125. package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
  126. package/src/harness/artifacts/agents-md.ts +88 -0
  127. package/src/harness/artifacts/checks-wiring.ts +57 -0
  128. package/src/harness/artifacts/docs-tree.ts +79 -0
  129. package/src/harness/artifacts/lint-configs.ts +136 -0
  130. package/src/harness/artifacts/review-agents.ts +67 -0
  131. package/src/harness/bare-entry.ts +108 -0
  132. package/src/harness/command.ts +1010 -0
  133. package/src/harness/default-agents/design.md +23 -0
  134. package/src/harness/default-agents/discover.md +18 -0
  135. package/src/harness/default-agents/implement.md +24 -0
  136. package/src/harness/default-agents/plan.md +19 -0
  137. package/src/harness/default-agents/research.md +21 -0
  138. package/src/harness/default-agents/validate.md +22 -0
  139. package/src/harness/gc/reporter.ts +28 -0
  140. package/src/harness/gc/runner.ts +136 -0
  141. package/src/harness/hooks/layer-context-inject.ts +155 -0
  142. package/src/harness/hooks/post-session-sweep.ts +130 -0
  143. package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
  144. package/src/harness/hooks/register.ts +118 -0
  145. package/src/harness/model.ts +117 -0
  146. package/src/harness/pipeline.ts +348 -0
  147. package/src/harness/project-paths.ts +235 -0
  148. package/src/harness/stage-runner.ts +107 -0
  149. package/src/harness/stages/design.ts +386 -0
  150. package/src/harness/stages/discover.ts +454 -0
  151. package/src/harness/stages/implement.ts +162 -0
  152. package/src/harness/stages/plan.ts +335 -0
  153. package/src/harness/stages/research.ts +263 -0
  154. package/src/harness/stages/validate.ts +684 -0
  155. package/src/harness/storage.ts +467 -0
  156. package/src/harness/tools.ts +426 -0
  157. package/src/lsp/bridge.ts +56 -95
  158. package/src/lsp/capabilities.ts +108 -0
  159. package/src/lsp/contracts.ts +35 -0
  160. package/src/lsp/detector.ts +8 -12
  161. package/src/markdown-frontmatter.ts +68 -0
  162. package/src/mempalace/bridge.ts +129 -0
  163. package/src/mempalace/config.ts +75 -0
  164. package/src/mempalace/format.ts +163 -0
  165. package/src/mempalace/hooks.ts +370 -0
  166. package/src/mempalace/installer-helper.ts +194 -0
  167. package/src/mempalace/python/mempalace_bridge.py +440 -0
  168. package/src/mempalace/runtime.ts +565 -0
  169. package/src/mempalace/schema.ts +264 -0
  170. package/src/mempalace/session-summary.ts +198 -0
  171. package/src/mempalace/tool.ts +186 -0
  172. package/src/mempalace/uv.ts +256 -0
  173. package/src/migrate/runner.ts +354 -0
  174. package/src/planning/approval-flow.ts +206 -9
  175. package/src/planning/plan-writer-prompt.ts +4 -3
  176. package/src/planning/planning-ask-tool.ts +39 -0
  177. package/src/planning/render-markdown.ts +74 -0
  178. package/src/planning/spec.ts +42 -0
  179. package/src/planning/system-prompt.ts +11 -8
  180. package/src/planning/validate.ts +84 -0
  181. package/src/platform/omp.ts +15 -2
  182. package/src/platform/system-prompt.ts +37 -0
  183. package/src/platform/test-utils.ts +3 -0
  184. package/src/platform/types.ts +6 -1
  185. package/src/qa/config.ts +12 -6
  186. package/src/qa/detect-app-type.ts +13 -6
  187. package/src/qa/matrix.ts +12 -6
  188. package/src/qa/prompt-builder.ts +28 -30
  189. package/src/qa/scripts/dev-server-utils.ts +72 -0
  190. package/src/qa/scripts/run-e2e-tests.ts +226 -0
  191. package/src/qa/scripts/start-dev-server.ts +138 -0
  192. package/src/qa/scripts/stop-dev-server.ts +77 -0
  193. package/src/qa/session.ts +13 -7
  194. package/src/quality/ai-setup.ts +27 -25
  195. package/src/quality/contracts.ts +34 -0
  196. package/src/quality/gates/ai-review.ts +20 -58
  197. package/src/quality/gates/command.ts +249 -46
  198. package/src/quality/review-gates.ts +18 -2
  199. package/src/quality/runner.ts +63 -22
  200. package/src/quality/schemas.ts +37 -2
  201. package/src/quality/setup.ts +96 -16
  202. package/src/release/changelog.ts +1 -1
  203. package/src/release/channels/custom.ts +13 -3
  204. package/src/release/channels/types.ts +5 -0
  205. package/src/release/contracts.ts +90 -0
  206. package/src/release/executor.ts +122 -45
  207. package/src/release/prompt.ts +18 -2
  208. package/src/release/targets.ts +86 -0
  209. package/src/release/version.ts +96 -71
  210. package/src/review/agent-loader.ts +298 -127
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +115 -14
  213. package/src/review/output.ts +12 -139
  214. package/src/review/runner.ts +12 -6
  215. package/src/review/scope.ts +144 -24
  216. package/src/review/types.ts +11 -20
  217. package/src/review/validator.ts +12 -6
  218. package/src/storage/fix-pr-sessions.ts +21 -14
  219. package/src/storage/plans.ts +14 -5
  220. package/src/storage/qa-sessions.ts +25 -19
  221. package/src/storage/reliability-metrics.ts +180 -0
  222. package/src/storage/reports.ts +8 -7
  223. package/src/storage/review-sessions.ts +55 -20
  224. package/src/tool-catalog/active-tool-controller.ts +164 -0
  225. package/src/tool-catalog/active-tool-planner.ts +212 -0
  226. package/src/tool-catalog/tool-groups.ts +102 -0
  227. package/src/types.ts +1401 -5
  228. package/src/ui-design/backend-adapter.ts +78 -0
  229. package/src/ui-design/backends/local-html.ts +82 -0
  230. package/src/ui-design/backends/pencil-mcp.ts +111 -0
  231. package/src/ui-design/components-scanner.ts +124 -0
  232. package/src/ui-design/config.ts +55 -0
  233. package/src/ui-design/pen-scanner.ts +95 -0
  234. package/src/ui-design/pen-selector.ts +72 -0
  235. package/src/ui-design/prompt-builder.ts +73 -0
  236. package/src/ui-design/scanner.ts +136 -0
  237. package/src/ui-design/session.ts +974 -0
  238. package/src/ui-design/system-prompt.ts +312 -0
  239. package/src/ui-design/tokens-scanner.ts +181 -0
  240. package/src/ui-design/types.ts +96 -0
  241. package/src/ultraplan/agent-catalog.ts +522 -0
  242. package/src/ultraplan/authoring/agent-catalog.ts +310 -0
  243. package/src/ultraplan/authoring/authoring-tools.ts +552 -0
  244. package/src/ultraplan/authoring/command-handlers.ts +339 -0
  245. package/src/ultraplan/authoring/markdown.ts +510 -0
  246. package/src/ultraplan/authoring/model.ts +162 -0
  247. package/src/ultraplan/authoring/pipeline.ts +319 -0
  248. package/src/ultraplan/authoring/stage-runner.ts +141 -0
  249. package/src/ultraplan/authoring/stages/approve.ts +249 -0
  250. package/src/ultraplan/authoring/stages/discover.ts +289 -0
  251. package/src/ultraplan/authoring/stages/intake.ts +203 -0
  252. package/src/ultraplan/authoring/stages/research.ts +399 -0
  253. package/src/ultraplan/authoring/stages/review.ts +333 -0
  254. package/src/ultraplan/authoring/stages/scout.ts +188 -0
  255. package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
  256. package/src/ultraplan/authoring/storage.ts +594 -0
  257. package/src/ultraplan/authoring/synth-gate.ts +165 -0
  258. package/src/ultraplan/authoring-draft.ts +653 -0
  259. package/src/ultraplan/authoring-persist.ts +180 -0
  260. package/src/ultraplan/authoring-tool.ts +608 -0
  261. package/src/ultraplan/authoring-wizard.ts +587 -0
  262. package/src/ultraplan/batch/merge.ts +98 -0
  263. package/src/ultraplan/batch/planner.ts +150 -0
  264. package/src/ultraplan/batch/presenter.ts +97 -0
  265. package/src/ultraplan/batch/storage.ts +420 -0
  266. package/src/ultraplan/batch/supervisor.ts +317 -0
  267. package/src/ultraplan/batch/worker.ts +26 -0
  268. package/src/ultraplan/batch/worktree.ts +110 -0
  269. package/src/ultraplan/contracts.ts +1593 -0
  270. package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
  271. package/src/ultraplan/default-agents/authoring/intake.md +12 -0
  272. package/src/ultraplan/default-agents/authoring/planner.md +12 -0
  273. package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
  274. package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
  275. package/src/ultraplan/default-agents/authoring/scout.md +12 -0
  276. package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
  277. package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
  278. package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
  279. package/src/ultraplan/default-agents/backend-executor.md +10 -0
  280. package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
  281. package/src/ultraplan/default-agents/backend-tester.md +10 -0
  282. package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
  283. package/src/ultraplan/default-agents/frontend-executor.md +10 -0
  284. package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
  285. package/src/ultraplan/default-agents/frontend-tester.md +10 -0
  286. package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
  287. package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
  288. package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
  289. package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
  290. package/src/ultraplan/execution/contract.ts +71 -0
  291. package/src/ultraplan/execution/policy.ts +217 -0
  292. package/src/ultraplan/execution/runtime-tools.ts +107 -0
  293. package/src/ultraplan/execution/session-runner.ts +281 -0
  294. package/src/ultraplan/next-router.ts +85 -0
  295. package/src/ultraplan/presenter.ts +359 -0
  296. package/src/ultraplan/project-paths.ts +342 -0
  297. package/src/ultraplan/runtime/active-execution.ts +72 -0
  298. package/src/ultraplan/runtime/apply-mutation.ts +416 -0
  299. package/src/ultraplan/runtime/blockers.ts +243 -0
  300. package/src/ultraplan/runtime/hook-bridge.ts +486 -0
  301. package/src/ultraplan/runtime/launch-context.ts +207 -0
  302. package/src/ultraplan/runtime/migration.ts +524 -0
  303. package/src/ultraplan/runtime/normalize.ts +281 -0
  304. package/src/ultraplan/runtime/proof.ts +260 -0
  305. package/src/ultraplan/runtime/reducer.ts +416 -0
  306. package/src/ultraplan/runtime/repair.ts +251 -0
  307. package/src/ultraplan/runtime/tracker-storage.ts +368 -0
  308. package/src/ultraplan/session-selection.ts +291 -0
  309. package/src/ultraplan/storage.ts +374 -0
  310. package/src/utils/editor.ts +38 -0
  311. package/src/utils/executable.ts +80 -0
  312. package/src/utils/paths.ts +1 -20
  313. package/src/utils/shell.ts +31 -0
  314. package/src/visual/companion.ts +2 -1
  315. package/src/visual/scripts/frame-template.html +60 -0
  316. package/src/visual/scripts/index.js +59 -13
  317. package/src/visual/scripts/package.json +3 -0
  318. package/src/visual/start-server.ts +2 -1
  319. package/src/workspace/git-scope.ts +64 -0
  320. package/src/workspace/locks.ts +23 -0
  321. package/src/workspace/package-manager.ts +117 -0
  322. package/src/workspace/path-mapping.ts +75 -0
  323. package/src/workspace/project-slug.ts +92 -0
  324. package/src/workspace/repo-root.ts +137 -0
  325. package/src/workspace/selector.ts +115 -0
  326. package/src/workspace/state-paths.ts +118 -0
  327. package/src/workspace/targets.ts +313 -0
  328. package/src/fix-pr/scripts/diff-comments.sh +0 -33
  329. package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
  330. package/src/fix-pr/scripts/trigger-review.sh +0 -36
  331. package/src/fix-pr/scripts/wait-and-check.sh +0 -37
  332. package/src/qa/scripts/detect-app-type.sh +0 -68
  333. package/src/qa/scripts/discover-routes.sh +0 -143
  334. package/src/qa/scripts/run-e2e-tests.sh +0 -131
  335. package/src/qa/scripts/start-dev-server.sh +0 -46
  336. package/src/qa/scripts/stop-dev-server.sh +0 -36
  337. package/src/review/prompts/fix-output-schema.md +0 -18
  338. package/src/review/prompts/review-output-schema.md +0 -38
  339. package/src/review/template.ts +0 -15
  340. /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
@@ -0,0 +1,440 @@
1
+ #!/usr/bin/env python3
2
+ """One-shot JSON bridge for the native supipowers MemPalace tool.
3
+
4
+ Action mapping (auditable API surface) — wired against MemPalace 3.3.4:
5
+ - version: stdlib importlib.metadata only; does not import MemPalace runtime modules.
6
+ - 29 MCP-equivalent actions dispatch to ``mempalace.mcp_server.tool_<action>``
7
+ (with the ``traverse → tool_traverse_graph`` rename). The mcp_server
8
+ module is imported as a Python library — supipowers does NOT register it as
9
+ an MCP server, spawn an MCP child process, or hand its tools to mcpc.
10
+ The bridge calls the underlying tool functions directly with kwargs.
11
+ - wake_up: ``mempalace.layers.MemoryStack().wake_up(wing=...)`` — the
12
+ documented Python API for L0 + L1 context (no tool_wake_up exists).
13
+ - native CLI actions (init, mine, split, repair): invoked via
14
+ ``python -m mempalace.cli <subcommand>`` as a subprocess inside the same
15
+ managed venv. Output is captured and returned as text.
16
+ - unknown actions: deny-by-default.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import importlib
22
+ import importlib.metadata
23
+ import json
24
+ import os
25
+ import platform
26
+ import sys
27
+ import traceback
28
+ from typing import Any, Callable, Dict
29
+
30
+ # Capture the real stdout file descriptor before any import. MemPalace
31
+ # (specifically `mempalace.mcp_server`) calls `os.dup2(2, 1)` during its
32
+ # search/HNSW path, which silently redirects fd 1 to the stderr pipe.
33
+ # A plain `_REAL_STDOUT = sys.stdout` reference keeps fd 1 internally but
34
+ # its writes still travel through the redirected fd, corrupting our JSON
35
+ # protocol. Duplicating fd 1 to a fresh fd preserves a path to the
36
+ # original stdout that survives mempalace's fd redirection. We then
37
+ # rebind sys.stdout to sys.stderr so any print() during import goes to
38
+ # stderr, and reserve the duped fd exclusively for the final JSON
39
+ # response.
40
+ _REAL_STDOUT_FD = os.dup(1)
41
+ _REAL_STDOUT = os.fdopen(_REAL_STDOUT_FD, "w", encoding="utf-8")
42
+ sys.stdout = sys.stderr
43
+
44
+ # Force UTF-8 across the bridge so JSON-encoded text round-trips identically on
45
+ # Windows shells with non-UTF-8 default encodings.
46
+ os.environ.setdefault("PYTHONIOENCODING", "utf-8")
47
+
48
+ # mempalace.mcp_server runs `argparse.parse_known_args(sys.argv[1:])` at import
49
+ # time. Our bridge's argv is `[bridge.py]`, so parse_known_args returns empty
50
+ # results and the import is benign — but we defensively scrub argv to the
51
+ # program name so the import is independent of how the bridge was invoked.
52
+ _REAL_ARGV = sys.argv[:]
53
+ sys.argv = [sys.argv[0]] if sys.argv else ["mempalace_bridge.py"]
54
+
55
+ BRIDGE_VERSION = "0.1.0"
56
+
57
+
58
+ class BridgeDomainError(Exception):
59
+ def __init__(self, code: str, message: str, remediation: str | None = None):
60
+ super().__init__(message)
61
+ self.code = code
62
+ self.message = message
63
+ self.remediation = remediation
64
+
65
+
66
+ def _json_response(payload: Dict[str, Any]) -> None:
67
+ serialized = json.dumps(payload, separators=(",", ":"), sort_keys=True)
68
+ _REAL_STDOUT.write(serialized)
69
+ _REAL_STDOUT.write("\n")
70
+ _REAL_STDOUT.flush()
71
+
72
+
73
+ def _error_payload(error: BridgeDomainError) -> Dict[str, Any]:
74
+ payload: Dict[str, Any] = {
75
+ "ok": False,
76
+ "error": {
77
+ "code": error.code,
78
+ "message": error.message,
79
+ },
80
+ "diagnostics": _base_diagnostics({}),
81
+ }
82
+ if error.remediation:
83
+ payload["error"]["remediation"] = error.remediation
84
+ return payload
85
+
86
+
87
+ def _read_request() -> Dict[str, Any]:
88
+ raw = sys.stdin.read()
89
+ try:
90
+ request = json.loads(raw)
91
+ except json.JSONDecodeError as exc:
92
+ raise BridgeDomainError("invalid_json", f"Bridge request is not valid JSON: {exc.msg}") from exc
93
+ if not isinstance(request, dict):
94
+ raise BridgeDomainError("invalid_request", "Bridge request must be a JSON object.")
95
+ return request
96
+
97
+
98
+ def _base_diagnostics(options: Dict[str, Any]) -> Dict[str, Any]:
99
+ diagnostics: Dict[str, Any] = {
100
+ "bridgeVersion": BRIDGE_VERSION,
101
+ "python": platform.python_version(),
102
+ }
103
+ palace_path = options.get("palacePath") or options.get("palace")
104
+ if palace_path:
105
+ diagnostics["palacePath"] = palace_path
106
+ try:
107
+ diagnostics["mempalaceVersion"] = importlib.metadata.version("mempalace")
108
+ except importlib.metadata.PackageNotFoundError:
109
+ diagnostics["mempalaceVersion"] = None
110
+ return diagnostics
111
+
112
+
113
+ def _handle_version(params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
114
+ diagnostics = _base_diagnostics(options)
115
+ return {
116
+ "ok": True,
117
+ "result": {
118
+ "bridgeVersion": BRIDGE_VERSION,
119
+ "python": diagnostics["python"],
120
+ "mempalaceVersion": diagnostics["mempalaceVersion"],
121
+ "palacePath": diagnostics.get("palacePath"),
122
+ },
123
+ "diagnostics": diagnostics,
124
+ }
125
+
126
+
127
+ # ── Helpers ───────────────────────────────────────────────────────────────
128
+
129
+
130
+ def _apply_palace_path(options: Dict[str, Any]) -> None:
131
+ """Set MEMPALACE_PALACE_PATH so MemPalace's MempalaceConfig picks it up."""
132
+ palace = options.get("palacePath") or options.get("palace")
133
+ if palace and isinstance(palace, str) and palace:
134
+ os.environ["MEMPALACE_PALACE_PATH"] = os.path.expanduser(palace)
135
+
136
+
137
+ def _to_jsonable(value: Any) -> Any:
138
+ if value is None or isinstance(value, (str, int, float, bool)):
139
+ return value
140
+ if isinstance(value, list):
141
+ return [_to_jsonable(item) for item in value]
142
+ if isinstance(value, tuple):
143
+ return [_to_jsonable(item) for item in value]
144
+ if isinstance(value, dict):
145
+ return {str(key): _to_jsonable(item) for key, item in value.items()}
146
+ if hasattr(value, "model_dump"):
147
+ return _to_jsonable(value.model_dump())
148
+ if hasattr(value, "__dict__"):
149
+ return _to_jsonable(vars(value))
150
+ return str(value)
151
+
152
+
153
+ def _ok(result: Any, options: Dict[str, Any]) -> Dict[str, Any]:
154
+ return {
155
+ "ok": True,
156
+ "result": _to_jsonable(result) if result is not None else {},
157
+ "diagnostics": _base_diagnostics(options),
158
+ }
159
+
160
+
161
+ def _import_or_raise(module_name: str) -> Any:
162
+ try:
163
+ return importlib.import_module(module_name)
164
+ except Exception as exc:
165
+ raise BridgeDomainError(
166
+ "mempalace_missing",
167
+ f"Failed to import {module_name}: {exc}",
168
+ "Run `/supi:memory setup` to (re)install the managed MemPalace runtime.",
169
+ ) from exc
170
+
171
+
172
+ def _wrap_runtime_errors(label: str, fn: Callable[[], Any]) -> Any:
173
+ try:
174
+ return fn()
175
+ except BridgeDomainError:
176
+ raise
177
+ except Exception as exc:
178
+ raise BridgeDomainError(
179
+ "mempalace_runtime_error",
180
+ f"{label} raised: {exc}",
181
+ ) from exc
182
+
183
+
184
+ def _select(*keys: str) -> Callable[[Dict[str, Any]], Dict[str, Any]]:
185
+ """Build kwargs from `params` containing only the listed keys (skipping None/missing)."""
186
+ def extract(params: Dict[str, Any]) -> Dict[str, Any]:
187
+ out: Dict[str, Any] = {}
188
+ for key in keys:
189
+ if key in params and params[key] is not None:
190
+ out[key] = params[key]
191
+ return out
192
+ return extract
193
+
194
+
195
+ def _rename(mapping: Dict[str, str]) -> Callable[[Dict[str, Any]], Dict[str, Any]]:
196
+ """Build kwargs from `params`, renaming each `<src>` key to `<dst>`."""
197
+ def extract(params: Dict[str, Any]) -> Dict[str, Any]:
198
+ out: Dict[str, Any] = {}
199
+ for src, dst in mapping.items():
200
+ if src in params and params[src] is not None:
201
+ out[dst] = params[src]
202
+ return out
203
+ return extract
204
+
205
+
206
+ # ── MCP-equivalent action dispatch ───────────────────────────────────────
207
+ #
208
+ # Each entry maps our action -> (function name in mempalace.mcp_server,
209
+ # extractor that builds **kwargs from our params). MemPalace's tool_* functions
210
+ # return native Python dicts/lists; we JSON-normalize the result.
211
+
212
+ MCP_TOOL_DISPATCH: Dict[str, "tuple[str, Callable[[Dict[str, Any]], Dict[str, Any]]]"] = {
213
+ "status": ("tool_status", lambda p: {}),
214
+ "list_wings": ("tool_list_wings", lambda p: {}),
215
+ "list_rooms": ("tool_list_rooms", _select("wing")),
216
+ "get_taxonomy": ("tool_get_taxonomy", lambda p: {}),
217
+ "search": ("tool_search", _select("query", "limit", "wing", "room")),
218
+ "check_duplicate": ("tool_check_duplicate", _select("content")),
219
+ "get_aaak_spec": ("tool_get_aaak_spec", lambda p: {}),
220
+ "get_drawer": ("tool_get_drawer", _select("drawer_id")),
221
+ "list_drawers": ("tool_list_drawers", _select("wing", "room", "limit", "offset")),
222
+ "add_drawer": ("tool_add_drawer", _select("wing", "room", "content", "source_file", "added_by")),
223
+ "update_drawer": ("tool_update_drawer", _select("drawer_id", "content", "wing", "room")),
224
+ "delete_drawer": ("tool_delete_drawer", _select("drawer_id")),
225
+ # Knowledge graph: MemPalace uses `entity` for the subject in queries.
226
+ "kg_query": ("tool_kg_query", _rename({"subject": "entity", "as_of": "as_of", "direction": "direction"})),
227
+ "kg_add": ("tool_kg_add", _select("subject", "predicate", "object", "valid_from", "source_closet")),
228
+ "kg_invalidate": ("tool_kg_invalidate", _select("subject", "predicate", "object", "ended")),
229
+ "kg_timeline": ("tool_kg_timeline", _rename({"subject": "entity"})),
230
+ "kg_stats": ("tool_kg_stats", lambda p: {}),
231
+ # Palace graph: MemPalace's function is `tool_traverse_graph`, not `tool_traverse`.
232
+ "traverse": ("tool_traverse_graph", _select("start_room", "max_hops")),
233
+ # find_tunnels: MemPalace uses wing_a/wing_b. Our schema names them
234
+ # source_wing/target_wing for symmetry with create_tunnel; we rename here.
235
+ "find_tunnels": ("tool_find_tunnels", _rename({"source_wing": "wing_a", "target_wing": "wing_b"})),
236
+ "graph_stats": ("tool_graph_stats", lambda p: {}),
237
+ "create_tunnel": ("tool_create_tunnel", _select("source_wing", "source_room", "target_wing", "target_room", "label")),
238
+ "list_tunnels": ("tool_list_tunnels", _select("wing")),
239
+ "delete_tunnel": ("tool_delete_tunnel", _select("tunnel_id")),
240
+ # follow_tunnels: MemPalace uses wing/room (not source_wing/source_room).
241
+ "follow_tunnels": ("tool_follow_tunnels", _rename({"source_wing": "wing", "source_room": "room"})),
242
+ "diary_write": ("tool_diary_write", _select("agent_name", "entry", "topic", "wing")),
243
+ "diary_read": ("tool_diary_read", _select("agent_name", "wing")),
244
+ "hook_settings": ("tool_hook_settings", lambda p: {}),
245
+ "memories_filed_away": ("tool_memories_filed_away", lambda p: {}),
246
+ "reconnect": ("tool_reconnect", lambda p: {}),
247
+ }
248
+
249
+
250
+ def _handle_mcp_tool(action: str, params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
251
+ func_name, extractor = MCP_TOOL_DISPATCH[action]
252
+ _apply_palace_path(options)
253
+ module = _import_or_raise("mempalace.mcp_server")
254
+ func = getattr(module, func_name, None)
255
+ if not callable(func):
256
+ raise BridgeDomainError(
257
+ "mempalace_missing",
258
+ f"mempalace.mcp_server.{func_name} is not callable in the installed package.",
259
+ "Upgrade the managed MemPalace runtime via `/supi:memory setup`.",
260
+ )
261
+ kwargs = extractor(params)
262
+ raw = _wrap_runtime_errors(f"mempalace.mcp_server.{func_name}", lambda: func(**kwargs))
263
+ result = _to_jsonable(raw)
264
+ if isinstance(result, dict) and result.get("ok") is False and isinstance(result.get("error"), dict):
265
+ return {"ok": False, "error": result["error"], "diagnostics": _base_diagnostics(options)}
266
+ return _ok(result, options)
267
+
268
+
269
+ def _make_mcp_handler(action: str) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]:
270
+ def _handler(params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
271
+ return _handle_mcp_tool(action, params, options)
272
+ return _handler
273
+
274
+
275
+ # ── wake_up handler (documented Python API, no tool_* equivalent) ────────
276
+
277
+
278
+ def _handle_wake_up(params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
279
+ _apply_palace_path(options)
280
+ layers = _import_or_raise("mempalace.layers")
281
+ palace = options.get("palacePath")
282
+ stack = layers.MemoryStack(palace_path=palace) if palace else layers.MemoryStack()
283
+ wake_kwargs: Dict[str, Any] = {}
284
+ if params.get("wing"):
285
+ wake_kwargs["wing"] = params["wing"]
286
+ text = _wrap_runtime_errors("MemoryStack.wake_up", lambda: stack.wake_up(**wake_kwargs))
287
+ return _ok({"text": text}, options)
288
+
289
+
290
+ # ── Native CLI args builders ──────────────────────────────────────────────
291
+
292
+
293
+ def _make_cli_args_init(params: Dict[str, Any]) -> "list[str]":
294
+ args = ["init", str(params.get("dir") or ".")]
295
+ if params.get("yes"):
296
+ args.append("--yes")
297
+ return args
298
+
299
+
300
+ def _make_cli_args_mine(params: Dict[str, Any]) -> "list[str]":
301
+ args = ["mine", str(params.get("dir") or ".")]
302
+ if params.get("mode"):
303
+ args.extend(["--mode", str(params["mode"])])
304
+ if isinstance(params.get("limit"), int):
305
+ args.extend(["--limit", str(params["limit"])])
306
+ if params.get("include_ignored"):
307
+ args.append("--include-ignored")
308
+ if params.get("no_gitignore"):
309
+ args.append("--no-gitignore")
310
+ if params.get("extract"):
311
+ args.append("--extract")
312
+ return args
313
+
314
+
315
+ def _make_cli_args_split(params: Dict[str, Any]) -> "list[str]":
316
+ source = params.get("source_file") or params.get("dir")
317
+ if not source:
318
+ raise BridgeDomainError("invalid_params", "split requires source_file or dir.")
319
+ args = ["split", str(source)]
320
+ if params.get("mode"):
321
+ args.extend(["--mode", str(params["mode"])])
322
+ return args
323
+
324
+
325
+ def _make_cli_args_repair(params: Dict[str, Any]) -> "list[str]":
326
+ # `mempalace repair` is a global palace operation — it does NOT accept a
327
+ # positional directory argument (unlike init/mine/split). Passing one
328
+ # makes argparse exit with code 2 ("unrecognized arguments").
329
+ args = ["repair"]
330
+ if params.get("yes"):
331
+ args.append("--yes")
332
+ if params.get("mode"):
333
+ args.extend(["--mode", str(params["mode"])])
334
+ if params.get("dry_run"):
335
+ args.append("--dry-run")
336
+ return args
337
+
338
+
339
+ CLI_DISPATCH: Dict[str, Callable[[Dict[str, Any]], "list[str]"]] = {
340
+ "init": _make_cli_args_init,
341
+ "mine": _make_cli_args_mine,
342
+ "split": _make_cli_args_split,
343
+ "repair": _make_cli_args_repair,
344
+ }
345
+
346
+
347
+ def _handle_cli_action(action: str, params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
348
+ builder = CLI_DISPATCH[action]
349
+ cli_args = builder(params)
350
+ _apply_palace_path(options)
351
+ cmd = [sys.executable, "-m", "mempalace.cli", *cli_args]
352
+ cwd = options.get("cwd") if isinstance(options.get("cwd"), str) else None
353
+ import subprocess
354
+ proc = subprocess.run(
355
+ cmd,
356
+ cwd=cwd,
357
+ env=os.environ.copy(),
358
+ capture_output=True,
359
+ text=True,
360
+ timeout=600,
361
+ )
362
+ diagnostics = _base_diagnostics(options)
363
+ diagnostics["argv"] = cli_args
364
+ if proc.returncode != 0:
365
+ return {
366
+ "ok": False,
367
+ "error": {
368
+ "code": "mempalace_cli_failed",
369
+ "message": f"`mempalace {' '.join(cli_args)}` exited {proc.returncode}.",
370
+ "stderr": proc.stderr.strip(),
371
+ "stdout": proc.stdout.strip(),
372
+ },
373
+ "diagnostics": diagnostics,
374
+ }
375
+ return {
376
+ "ok": True,
377
+ "result": {
378
+ "argv": cli_args,
379
+ "stdout": proc.stdout,
380
+ "stderr": proc.stderr,
381
+ "returncode": proc.returncode,
382
+ },
383
+ "diagnostics": diagnostics,
384
+ }
385
+
386
+
387
+ def _make_cli_handler(action: str) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]:
388
+ def _handler(params: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
389
+ return _handle_cli_action(action, params, options)
390
+ return _handler
391
+
392
+
393
+ # ── Final dispatch table ──────────────────────────────────────────────────
394
+
395
+
396
+ DISPATCH: Dict[str, Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]] = {
397
+ "version": _handle_version,
398
+ "wake_up": _handle_wake_up,
399
+ }
400
+ for _action in MCP_TOOL_DISPATCH:
401
+ DISPATCH[_action] = _make_mcp_handler(_action)
402
+ for _action in CLI_DISPATCH:
403
+ DISPATCH[_action] = _make_cli_handler(_action)
404
+
405
+
406
+ def main() -> int:
407
+ try:
408
+ request = _read_request()
409
+ action = request.get("action")
410
+ params = request.get("params") or {}
411
+ options = request.get("options") or {}
412
+ if not isinstance(action, str) or not action:
413
+ raise BridgeDomainError("missing_action", "Bridge request requires a non-empty action string.")
414
+ if not isinstance(params, dict):
415
+ raise BridgeDomainError("invalid_params", "Bridge request params must be an object.")
416
+ if not isinstance(options, dict):
417
+ raise BridgeDomainError("invalid_options", "Bridge request options must be an object.")
418
+ handler = DISPATCH.get(action)
419
+ if handler is None:
420
+ raise BridgeDomainError("unknown_action", f"Unsupported MemPalace bridge action: {action}")
421
+ _json_response(handler(params, options))
422
+ return 0
423
+ except BridgeDomainError as exc:
424
+ _json_response(_error_payload(exc))
425
+ return 0
426
+ except Exception as exc: # pragma: no cover - defensive bridge boundary
427
+ traceback.print_exc(file=sys.stderr)
428
+ _json_response({
429
+ "ok": False,
430
+ "error": {
431
+ "code": "bridge_runtime_error",
432
+ "message": str(exc),
433
+ },
434
+ "diagnostics": _base_diagnostics({}),
435
+ })
436
+ return 1
437
+
438
+
439
+ if __name__ == "__main__":
440
+ raise SystemExit(main())