supipowers 1.5.3 → 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.
- package/README.md +14 -8
- package/bin/install.mjs +20 -5
- package/bin/install.ts +95 -0
- package/package.json +8 -4
- package/skills/context-mode/SKILL.md +17 -10
- package/skills/harness/SKILL.md +94 -0
- package/skills/ui-design/SKILL.md +63 -0
- package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
- package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
- package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
- package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
- package/skills/ultraplan-discover/SKILL.md +96 -0
- package/skills/ultraplan-intake/SKILL.md +89 -0
- package/skills/ultraplan-research/SKILL.md +129 -0
- package/skills/ultraplan-review/SKILL.md +86 -0
- package/skills/ultraplan-review-scope/SKILL.md +111 -0
- package/skills/ultraplan-review-structure/SKILL.md +120 -0
- package/skills/ultraplan-review-tdd/SKILL.md +142 -0
- package/skills/ultraplan-scout/SKILL.md +110 -0
- package/skills/ultraplan-synthesize/SKILL.md +124 -0
- package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
- package/src/ai/schema-text.ts +129 -0
- package/src/ai/structured-output.ts +274 -0
- package/src/ai/template.ts +27 -0
- package/src/bootstrap.ts +63 -28
- package/src/commands/agents.ts +131 -42
- package/src/commands/ai-review.ts +251 -30
- package/src/commands/clear.ts +434 -0
- package/src/commands/commit.ts +1 -0
- package/src/commands/config.ts +242 -44
- package/src/commands/context.ts +55 -28
- package/src/commands/doctor.ts +234 -6
- package/src/commands/fix-pr.ts +306 -131
- package/src/commands/generate.ts +111 -21
- package/src/commands/memory.ts +192 -0
- package/src/commands/model-picker.ts +28 -21
- package/src/commands/model.ts +18 -8
- package/src/commands/optimize-context.ts +408 -29
- package/src/commands/plan.ts +2 -0
- package/src/commands/qa.ts +312 -137
- package/src/commands/release.ts +259 -76
- package/src/commands/review.ts +293 -59
- package/src/commands/status.ts +200 -13
- package/src/commands/supi.ts +3 -35
- package/src/commands/ui-design.ts +394 -0
- package/src/commands/ultraplan.ts +1518 -0
- package/src/commands/update.ts +86 -0
- package/src/config/defaults.ts +62 -0
- package/src/config/loader.ts +448 -60
- package/src/config/schema.ts +108 -2
- package/src/context/optimizer.ts +25 -33
- package/src/context/rule-renderer.ts +223 -0
- package/src/context/savings.ts +258 -0
- package/src/context/startup-check.ts +380 -0
- package/src/context/startup-optimizer.ts +355 -0
- package/src/context/tokenignore.ts +146 -0
- package/src/context-mode/cache-handle.ts +49 -0
- package/src/context-mode/cache-preview.ts +71 -0
- package/src/context-mode/cache-store.ts +738 -0
- package/src/context-mode/compressor.ts +131 -26
- package/src/context-mode/dedup.ts +108 -0
- package/src/context-mode/detector.ts +35 -4
- package/src/context-mode/event-extractor.ts +14 -12
- package/src/context-mode/event-store.ts +91 -36
- package/src/context-mode/hooks.ts +798 -56
- package/src/context-mode/knowledge/store.ts +255 -11
- package/src/context-mode/memory-store.ts +325 -0
- package/src/context-mode/metrics-recorder.ts +158 -0
- package/src/context-mode/metrics-store.ts +765 -0
- package/src/context-mode/model.ts +24 -0
- package/src/context-mode/processor-keys.ts +29 -0
- package/src/context-mode/processors/build.ts +66 -0
- package/src/context-mode/processors/docker.ts +57 -0
- package/src/context-mode/processors/git.ts +111 -0
- package/src/context-mode/processors/json.ts +112 -0
- package/src/context-mode/processors/k8s.ts +67 -0
- package/src/context-mode/processors/lint.ts +67 -0
- package/src/context-mode/processors/log.ts +86 -0
- package/src/context-mode/processors/registry.ts +116 -0
- package/src/context-mode/processors/test-runner.ts +102 -0
- package/src/context-mode/processors/types.ts +20 -0
- package/src/context-mode/repomap.ts +400 -0
- package/src/context-mode/routing.ts +97 -24
- package/src/context-mode/sandbox/runners.ts +5 -1
- package/src/context-mode/snapshot-builder.ts +106 -11
- package/src/context-mode/source-hash.ts +173 -0
- package/src/context-mode/tool-name.ts +11 -0
- package/src/context-mode/tools.ts +654 -22
- package/src/context-mode/web/fetcher.ts +31 -12
- package/src/debug/logger.ts +2 -1
- package/src/deps/registry.ts +1 -1
- package/src/discipline/failure-summarizer.ts +170 -0
- package/src/discipline/failure-taxonomy.ts +131 -0
- package/src/discipline/workflow-invariants.ts +125 -0
- package/src/discovery/index.ts +31 -0
- package/src/discovery/lsp.ts +87 -0
- package/src/discovery/rank.ts +144 -0
- package/src/discovery/sources.ts +89 -0
- package/src/discovery/workflow.ts +87 -0
- package/src/docs/contracts.ts +39 -0
- package/src/docs/drift.ts +117 -87
- package/src/fix-pr/assessment.ts +200 -0
- package/src/fix-pr/contracts.ts +47 -0
- package/src/fix-pr/fetch-comments.ts +80 -0
- package/src/fix-pr/prompt-builder.ts +58 -40
- package/src/fix-pr/scripts/exec.ts +34 -0
- package/src/fix-pr/scripts/trigger-review.ts +106 -0
- package/src/fix-pr/scripts/wait-and-check.ts +108 -0
- package/src/fix-pr/types.ts +4 -0
- package/src/git/branch-finish.ts +5 -0
- package/src/git/commit-contract.ts +83 -0
- package/src/git/commit.ts +121 -184
- package/src/git/status.ts +62 -8
- package/src/harness/anti_slop/architecture-parser.ts +210 -0
- package/src/harness/anti_slop/backend-factory.ts +30 -0
- package/src/harness/anti_slop/backend.ts +140 -0
- package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
- package/src/harness/anti_slop/fallow-adapter.ts +305 -0
- package/src/harness/anti_slop/installer.ts +227 -0
- package/src/harness/anti_slop/queue.ts +216 -0
- package/src/harness/anti_slop/recommend.ts +84 -0
- package/src/harness/anti_slop/score.ts +180 -0
- package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
- package/src/harness/artifacts/agents-md.ts +88 -0
- package/src/harness/artifacts/checks-wiring.ts +57 -0
- package/src/harness/artifacts/docs-tree.ts +79 -0
- package/src/harness/artifacts/lint-configs.ts +136 -0
- package/src/harness/artifacts/review-agents.ts +67 -0
- package/src/harness/bare-entry.ts +108 -0
- package/src/harness/command.ts +1010 -0
- package/src/harness/default-agents/design.md +23 -0
- package/src/harness/default-agents/discover.md +18 -0
- package/src/harness/default-agents/implement.md +24 -0
- package/src/harness/default-agents/plan.md +19 -0
- package/src/harness/default-agents/research.md +21 -0
- package/src/harness/default-agents/validate.md +22 -0
- package/src/harness/gc/reporter.ts +28 -0
- package/src/harness/gc/runner.ts +136 -0
- package/src/harness/hooks/layer-context-inject.ts +155 -0
- package/src/harness/hooks/post-session-sweep.ts +130 -0
- package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
- package/src/harness/hooks/register.ts +118 -0
- package/src/harness/model.ts +117 -0
- package/src/harness/pipeline.ts +348 -0
- package/src/harness/project-paths.ts +235 -0
- package/src/harness/stage-runner.ts +107 -0
- package/src/harness/stages/design.ts +386 -0
- package/src/harness/stages/discover.ts +454 -0
- package/src/harness/stages/implement.ts +162 -0
- package/src/harness/stages/plan.ts +335 -0
- package/src/harness/stages/research.ts +263 -0
- package/src/harness/stages/validate.ts +684 -0
- package/src/harness/storage.ts +467 -0
- package/src/harness/tools.ts +426 -0
- package/src/lsp/bridge.ts +56 -95
- package/src/lsp/capabilities.ts +108 -0
- package/src/lsp/contracts.ts +35 -0
- package/src/lsp/detector.ts +8 -12
- package/src/markdown-frontmatter.ts +68 -0
- package/src/mempalace/bridge.ts +129 -0
- package/src/mempalace/config.ts +75 -0
- package/src/mempalace/format.ts +163 -0
- package/src/mempalace/hooks.ts +370 -0
- package/src/mempalace/installer-helper.ts +194 -0
- package/src/mempalace/python/mempalace_bridge.py +440 -0
- package/src/mempalace/runtime.ts +565 -0
- package/src/mempalace/schema.ts +264 -0
- package/src/mempalace/session-summary.ts +198 -0
- package/src/mempalace/tool.ts +186 -0
- package/src/mempalace/uv.ts +256 -0
- package/src/migrate/runner.ts +354 -0
- package/src/planning/approval-flow.ts +206 -9
- package/src/planning/plan-writer-prompt.ts +4 -3
- package/src/planning/planning-ask-tool.ts +39 -0
- package/src/planning/render-markdown.ts +74 -0
- package/src/planning/spec.ts +42 -0
- package/src/planning/system-prompt.ts +11 -8
- package/src/planning/validate.ts +84 -0
- package/src/platform/omp.ts +15 -2
- package/src/platform/system-prompt.ts +37 -0
- package/src/platform/test-utils.ts +3 -0
- package/src/platform/types.ts +6 -1
- package/src/qa/config.ts +12 -6
- package/src/qa/detect-app-type.ts +13 -6
- package/src/qa/matrix.ts +12 -6
- package/src/qa/prompt-builder.ts +28 -30
- package/src/qa/scripts/dev-server-utils.ts +72 -0
- package/src/qa/scripts/run-e2e-tests.ts +226 -0
- package/src/qa/scripts/start-dev-server.ts +138 -0
- package/src/qa/scripts/stop-dev-server.ts +77 -0
- package/src/qa/session.ts +13 -7
- package/src/quality/ai-setup.ts +27 -25
- package/src/quality/contracts.ts +34 -0
- package/src/quality/gates/ai-review.ts +20 -58
- package/src/quality/gates/command.ts +249 -46
- package/src/quality/review-gates.ts +18 -2
- package/src/quality/runner.ts +63 -22
- package/src/quality/schemas.ts +37 -2
- package/src/quality/setup.ts +96 -16
- package/src/release/changelog.ts +1 -1
- package/src/release/channels/custom.ts +13 -3
- package/src/release/channels/types.ts +5 -0
- package/src/release/contracts.ts +90 -0
- package/src/release/executor.ts +122 -45
- package/src/release/prompt.ts +18 -2
- package/src/release/targets.ts +86 -0
- package/src/release/version.ts +96 -71
- package/src/review/agent-loader.ts +221 -109
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +114 -13
- package/src/review/output.ts +12 -139
- package/src/review/runner.ts +12 -6
- package/src/review/scope.ts +144 -24
- package/src/review/types.ts +1 -20
- package/src/review/validator.ts +12 -6
- package/src/storage/fix-pr-sessions.ts +21 -14
- package/src/storage/plans.ts +14 -5
- package/src/storage/qa-sessions.ts +25 -19
- package/src/storage/reliability-metrics.ts +180 -0
- package/src/storage/reports.ts +8 -7
- package/src/storage/review-sessions.ts +55 -20
- package/src/tool-catalog/active-tool-controller.ts +164 -0
- package/src/tool-catalog/active-tool-planner.ts +212 -0
- package/src/tool-catalog/tool-groups.ts +102 -0
- package/src/types.ts +1399 -5
- package/src/ui-design/backend-adapter.ts +78 -0
- package/src/ui-design/backends/local-html.ts +82 -0
- package/src/ui-design/backends/pencil-mcp.ts +111 -0
- package/src/ui-design/components-scanner.ts +124 -0
- package/src/ui-design/config.ts +55 -0
- package/src/ui-design/pen-scanner.ts +95 -0
- package/src/ui-design/pen-selector.ts +72 -0
- package/src/ui-design/prompt-builder.ts +73 -0
- package/src/ui-design/scanner.ts +136 -0
- package/src/ui-design/session.ts +974 -0
- package/src/ui-design/system-prompt.ts +312 -0
- package/src/ui-design/tokens-scanner.ts +181 -0
- package/src/ui-design/types.ts +96 -0
- package/src/ultraplan/agent-catalog.ts +522 -0
- package/src/ultraplan/authoring/agent-catalog.ts +310 -0
- package/src/ultraplan/authoring/authoring-tools.ts +552 -0
- package/src/ultraplan/authoring/command-handlers.ts +339 -0
- package/src/ultraplan/authoring/markdown.ts +510 -0
- package/src/ultraplan/authoring/model.ts +162 -0
- package/src/ultraplan/authoring/pipeline.ts +319 -0
- package/src/ultraplan/authoring/stage-runner.ts +141 -0
- package/src/ultraplan/authoring/stages/approve.ts +249 -0
- package/src/ultraplan/authoring/stages/discover.ts +289 -0
- package/src/ultraplan/authoring/stages/intake.ts +203 -0
- package/src/ultraplan/authoring/stages/research.ts +399 -0
- package/src/ultraplan/authoring/stages/review.ts +333 -0
- package/src/ultraplan/authoring/stages/scout.ts +188 -0
- package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
- package/src/ultraplan/authoring/storage.ts +594 -0
- package/src/ultraplan/authoring/synth-gate.ts +165 -0
- package/src/ultraplan/authoring-draft.ts +653 -0
- package/src/ultraplan/authoring-persist.ts +180 -0
- package/src/ultraplan/authoring-tool.ts +608 -0
- package/src/ultraplan/authoring-wizard.ts +587 -0
- package/src/ultraplan/batch/merge.ts +98 -0
- package/src/ultraplan/batch/planner.ts +150 -0
- package/src/ultraplan/batch/presenter.ts +97 -0
- package/src/ultraplan/batch/storage.ts +420 -0
- package/src/ultraplan/batch/supervisor.ts +317 -0
- package/src/ultraplan/batch/worker.ts +26 -0
- package/src/ultraplan/batch/worktree.ts +110 -0
- package/src/ultraplan/contracts.ts +1593 -0
- package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
- package/src/ultraplan/default-agents/authoring/intake.md +12 -0
- package/src/ultraplan/default-agents/authoring/planner.md +12 -0
- package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
- package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/scout.md +12 -0
- package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
- package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-executor.md +10 -0
- package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-tester.md +10 -0
- package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-executor.md +10 -0
- package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-tester.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
- package/src/ultraplan/execution/contract.ts +71 -0
- package/src/ultraplan/execution/policy.ts +217 -0
- package/src/ultraplan/execution/runtime-tools.ts +107 -0
- package/src/ultraplan/execution/session-runner.ts +281 -0
- package/src/ultraplan/next-router.ts +85 -0
- package/src/ultraplan/presenter.ts +359 -0
- package/src/ultraplan/project-paths.ts +342 -0
- package/src/ultraplan/runtime/active-execution.ts +72 -0
- package/src/ultraplan/runtime/apply-mutation.ts +416 -0
- package/src/ultraplan/runtime/blockers.ts +243 -0
- package/src/ultraplan/runtime/hook-bridge.ts +486 -0
- package/src/ultraplan/runtime/launch-context.ts +207 -0
- package/src/ultraplan/runtime/migration.ts +524 -0
- package/src/ultraplan/runtime/normalize.ts +281 -0
- package/src/ultraplan/runtime/proof.ts +260 -0
- package/src/ultraplan/runtime/reducer.ts +416 -0
- package/src/ultraplan/runtime/repair.ts +251 -0
- package/src/ultraplan/runtime/tracker-storage.ts +368 -0
- package/src/ultraplan/session-selection.ts +291 -0
- package/src/ultraplan/storage.ts +374 -0
- package/src/utils/editor.ts +38 -0
- package/src/utils/executable.ts +80 -0
- package/src/utils/paths.ts +1 -20
- package/src/utils/shell.ts +31 -0
- package/src/visual/companion.ts +2 -1
- package/src/visual/scripts/frame-template.html +60 -0
- package/src/visual/scripts/index.js +59 -13
- package/src/visual/scripts/package.json +3 -0
- package/src/visual/start-server.ts +2 -1
- package/src/workspace/git-scope.ts +64 -0
- package/src/workspace/locks.ts +23 -0
- package/src/workspace/package-manager.ts +117 -0
- package/src/workspace/path-mapping.ts +75 -0
- package/src/workspace/project-slug.ts +92 -0
- package/src/workspace/repo-root.ts +137 -0
- package/src/workspace/selector.ts +115 -0
- package/src/workspace/state-paths.ts +118 -0
- package/src/workspace/targets.ts +313 -0
- package/src/fix-pr/scripts/diff-comments.sh +0 -33
- package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
- package/src/fix-pr/scripts/trigger-review.sh +0 -36
- package/src/fix-pr/scripts/wait-and-check.sh +0 -37
- package/src/qa/scripts/detect-app-type.sh +0 -68
- package/src/qa/scripts/discover-routes.sh +0 -143
- package/src/qa/scripts/run-e2e-tests.sh +0 -131
- package/src/qa/scripts/start-dev-server.sh +0 -46
- package/src/qa/scripts/stop-dev-server.sh +0 -36
- package/src/review/prompts/fix-output-schema.md +0 -18
- package/src/review/prompts/review-output-schema.md +0 -38
- package/src/review/template.ts +0 -15
- /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())
|