supipowers 1.5.3 → 2.0.1

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 +131 -42
  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 +18 -8
  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 +135 -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 +268 -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 +221 -109
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +114 -13
  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 +1 -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 +1399 -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,108 @@
1
+ // src/lsp/capabilities.ts
2
+ //
3
+ // Probe the active LSP server's capabilities once per gate run.
4
+ // Used to short-circuit diagnostic collection (and any future capability-
5
+ // gated workflow) when the server has no `textDocument/diagnostic` support,
6
+ // instead of failing the gate on a vacuous error.
7
+ //
8
+ // The probe asks an agent to invoke the lsp tool with action "capabilities"
9
+ // (introduced in OMP 14.5.13) and emit a tiny JSON record summarizing which
10
+ // methods at least one active server advertises. Failure to probe is
11
+ // treated as fail-closed: NO_LSP_SUPPORT (gate skips rather than pretending
12
+ // it ran).
13
+
14
+ import { Type, type Static } from "@sinclair/typebox";
15
+ import {
16
+ parseStructuredOutput,
17
+ runWithOutputValidation,
18
+ type ReliabilityReporter,
19
+ } from "../ai/structured-output.js";
20
+ import { renderSchemaText } from "../ai/schema-text.js";
21
+ import type { GateExecutionContext } from "../types.js";
22
+
23
+ export const LspCapabilitiesSchema = Type.Object(
24
+ {
25
+ diagnostics: Type.Boolean(),
26
+ references: Type.Boolean(),
27
+ definition: Type.Boolean(),
28
+ hover: Type.Boolean(),
29
+ rename: Type.Boolean(),
30
+ },
31
+ { additionalProperties: false },
32
+ );
33
+
34
+ export type LspCapabilities = Static<typeof LspCapabilitiesSchema>;
35
+
36
+ const SCHEMA_TEXT = renderSchemaText(LspCapabilitiesSchema);
37
+
38
+ const PROBE_PROMPT = [
39
+ "You are probing LSP server capabilities for a quality gate.",
40
+ 'Run the lsp tool with action: "capabilities" and file: "*".',
41
+ "Examine the returned capabilities and emit JSON only matching this schema:",
42
+ SCHEMA_TEXT,
43
+ "",
44
+ "Rules:",
45
+ "- `diagnostics`: true when at least one server advertises `textDocument/diagnostic` or `textDocument/publishDiagnostics` support.",
46
+ "- `references`: true when at least one server advertises `textDocument/references`.",
47
+ "- `definition`, `hover`, `rename`: same pattern for their LSP method names.",
48
+ "- Use `false` when the capability is missing, not when you are unsure.",
49
+ "- Do not wrap the JSON in markdown fences.",
50
+ ].join("\n");
51
+
52
+ /** Pessimistic default: every capability is missing. Used as fail-closed
53
+ * fallback when the probe itself fails or returns blocked. */
54
+ export const NO_LSP_SUPPORT: LspCapabilities = {
55
+ diagnostics: false,
56
+ references: false,
57
+ definition: false,
58
+ hover: false,
59
+ rename: false,
60
+ };
61
+
62
+ /** Optimistic default: every capability advertised. Used by tests and as a
63
+ * reference shape for the probe contract. */
64
+ export const FULL_LSP_SUPPORT: LspCapabilities = {
65
+ diagnostics: true,
66
+ references: true,
67
+ definition: true,
68
+ hover: true,
69
+ rename: true,
70
+ };
71
+
72
+ export interface ProbeLspCapabilitiesOptions {
73
+ cwd: string;
74
+ createAgentSession: GateExecutionContext["createAgentSession"];
75
+ reviewModel?: GateExecutionContext["reviewModel"];
76
+ reliability?: ReliabilityReporter;
77
+ }
78
+
79
+ /**
80
+ * Probe LSP capabilities. Returns NO_LSP_SUPPORT when the probe blocks or
81
+ * throws — callers must treat unknown as unsupported and skip cleanly
82
+ * rather than running a workflow that depends on the missing capability.
83
+ */
84
+ export async function probeLspCapabilities(
85
+ options: ProbeLspCapabilitiesOptions,
86
+ ): Promise<LspCapabilities> {
87
+ try {
88
+ const result = await runWithOutputValidation<LspCapabilities>(
89
+ options.createAgentSession,
90
+ {
91
+ cwd: options.cwd,
92
+ prompt: PROBE_PROMPT,
93
+ schema: SCHEMA_TEXT,
94
+ parse: (raw) => parseStructuredOutput<LspCapabilities>(raw, LspCapabilitiesSchema),
95
+ model: options.reviewModel?.model,
96
+ thinkingLevel: options.reviewModel?.thinkingLevel ?? null,
97
+ timeoutMs: 60_000,
98
+ reliability: options.reliability,
99
+ },
100
+ );
101
+ if (result.status === "blocked") {
102
+ return NO_LSP_SUPPORT;
103
+ }
104
+ return result.output;
105
+ } catch {
106
+ return NO_LSP_SUPPORT;
107
+ }
108
+ }
@@ -0,0 +1,35 @@
1
+ // src/lsp/contracts.ts
2
+ //
3
+ // TypeBox contract for the LSP diagnostics agent flow. Consumed by
4
+ // ai/structured-output.ts to schema-check model output and by
5
+ // ai/schema-text.ts to render the shape into the prompt.
6
+
7
+ import { Type, type Static } from "@sinclair/typebox";
8
+
9
+ const DIAGNOSTIC_SEVERITIES = ["error", "warning", "info", "hint"] as const;
10
+
11
+ export const LspDiagnosticSchema = Type.Object(
12
+ {
13
+ severity: Type.Union(
14
+ DIAGNOSTIC_SEVERITIES.map((value) => Type.Literal(value)),
15
+ ),
16
+ message: Type.String(),
17
+ line: Type.Number(),
18
+ column: Type.Number(),
19
+ },
20
+ { additionalProperties: false },
21
+ );
22
+
23
+ export const LspDiagnosticsResultSchema = Type.Object(
24
+ {
25
+ file: Type.String(),
26
+ diagnostics: Type.Array(LspDiagnosticSchema),
27
+ },
28
+ { additionalProperties: false },
29
+ );
30
+
31
+ export const LspDiagnosticsResultsSchema = Type.Array(LspDiagnosticsResultSchema);
32
+
33
+ export type LspDiagnostic = Static<typeof LspDiagnosticSchema>;
34
+ export type LspDiagnosticsResult = Static<typeof LspDiagnosticsResultSchema>;
35
+ export type LspDiagnosticsResults = Static<typeof LspDiagnosticsResultsSchema>;
@@ -1,4 +1,5 @@
1
1
  // src/lsp/detector.ts
2
+ import { findExecutable } from "../utils/executable.js";
2
3
  import { LSP_SERVERS, type LspServerEntry } from "./setup-guide.js";
3
4
 
4
5
  export interface LspServerStatus {
@@ -7,21 +8,16 @@ export interface LspServerStatus {
7
8
  }
8
9
 
9
10
  /**
10
- * Check which LSP servers are installed by looking for their binaries.
11
+ * Check which LSP servers are installed by looking for their binaries on PATH.
11
12
  */
12
13
  export async function checkInstalledServers(
13
- exec: (cmd: string, args: string[]) => Promise<{ stdout: string; code: number }>
14
+ _exec: (cmd: string, args: string[]) => Promise<{ stdout: string; code: number }>,
15
+ resolveExecutable: (command: string) => string | null = (command) => findExecutable(command),
14
16
  ): Promise<LspServerStatus[]> {
15
- const results: LspServerStatus[] = [];
16
- for (const server of LSP_SERVERS) {
17
- try {
18
- const result = await exec("which", [server.server]);
19
- results.push({ server, installed: result.code === 0 });
20
- } catch {
21
- results.push({ server, installed: false });
22
- }
23
- }
24
- return results;
17
+ return LSP_SERVERS.map((server) => ({
18
+ server,
19
+ installed: resolveExecutable(server.server) !== null,
20
+ }));
25
21
  }
26
22
 
27
23
  /**
@@ -0,0 +1,68 @@
1
+ import { parse as parseYaml } from "yaml";
2
+ import { normalizeLineEndings } from "./text.js";
3
+
4
+ export type MarkdownFrontmatterErrorCode =
5
+ | "missing-frontmatter"
6
+ | "invalid-frontmatter"
7
+ | "empty-body";
8
+
9
+ export class MarkdownFrontmatterError extends Error {
10
+ constructor(
11
+ public readonly code: MarkdownFrontmatterErrorCode,
12
+ public readonly filePath: string,
13
+ message: string,
14
+ ) {
15
+ super(message);
16
+ this.name = "MarkdownFrontmatterError";
17
+ }
18
+ }
19
+
20
+ export interface ParsedMarkdownFrontmatter {
21
+ frontmatter: Record<string, unknown>;
22
+ body: string;
23
+ }
24
+
25
+ export function parseMarkdownFrontmatter(content: string, filePath: string): ParsedMarkdownFrontmatter {
26
+ const normalized = normalizeLineEndings(content);
27
+ const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
28
+ if (!match) {
29
+ throw new MarkdownFrontmatterError(
30
+ "missing-frontmatter",
31
+ filePath,
32
+ `${filePath} is missing YAML frontmatter.`,
33
+ );
34
+ }
35
+
36
+ let frontmatter: unknown;
37
+ try {
38
+ frontmatter = parseYaml(match[1]);
39
+ } catch (error) {
40
+ throw new MarkdownFrontmatterError(
41
+ "invalid-frontmatter",
42
+ filePath,
43
+ `Invalid YAML frontmatter in ${filePath}: ${(error as Error).message}`,
44
+ );
45
+ }
46
+
47
+ if (!frontmatter || typeof frontmatter !== "object" || Array.isArray(frontmatter)) {
48
+ throw new MarkdownFrontmatterError(
49
+ "invalid-frontmatter",
50
+ filePath,
51
+ `Invalid YAML frontmatter in ${filePath}: frontmatter must be a mapping.`,
52
+ );
53
+ }
54
+
55
+ const body = match[2]?.trim() ?? "";
56
+ if (!body) {
57
+ throw new MarkdownFrontmatterError(
58
+ "empty-body",
59
+ filePath,
60
+ `${filePath} has an empty prompt body.`,
61
+ );
62
+ }
63
+
64
+ return {
65
+ frontmatter: frontmatter as Record<string, unknown>,
66
+ body,
67
+ };
68
+ }
@@ -0,0 +1,135 @@
1
+ import type { ResolvedMempalaceConfig } from "./config.js";
2
+ import type { MempalaceParams } from "./schema.js";
3
+ import {
4
+ resolveBridgeScriptPath,
5
+ resolveManagedVenvPaths,
6
+ runBridgeRequest,
7
+ type BridgePathResult,
8
+ type MempalaceRuntimeError,
9
+ type RunBridgeRequestOptions,
10
+ type RunBridgeRequestResult,
11
+ } from "./runtime.js";
12
+
13
+ export type MempalaceBridgeCallResult =
14
+ | {
15
+ ok: true;
16
+ action: MempalaceParams["action"];
17
+ result: unknown;
18
+ diagnostics: Record<string, unknown>;
19
+ }
20
+ | {
21
+ ok: false;
22
+ action: MempalaceParams["action"];
23
+ error: MempalaceRuntimeError;
24
+ diagnostics: Record<string, unknown>;
25
+ };
26
+
27
+ export interface MempalaceBridgeRuntimeDeps {
28
+ resolveBridgeScriptPath?: () => BridgePathResult;
29
+ runBridgeRequest?: (options: RunBridgeRequestOptions) => Promise<RunBridgeRequestResult>;
30
+ }
31
+
32
+ export interface CreateMempalaceBridgeOptions {
33
+ cwd: string;
34
+ config: ResolvedMempalaceConfig;
35
+ runtime?: MempalaceBridgeRuntimeDeps;
36
+ }
37
+
38
+ export interface MempalaceBridgeFacade {
39
+ execute(params: MempalaceParams): Promise<MempalaceBridgeCallResult>;
40
+ }
41
+
42
+ function withSetupRemediation(error: MempalaceRuntimeError): MempalaceRuntimeError {
43
+ if (error.remediation) return error;
44
+ if (error.code === "mempalace_missing" || error.code === "bridge_process_failed") {
45
+ return {
46
+ ...error,
47
+ remediation: "Call mempalace(action=\"setup\") first, then retry the requested MemPalace action.",
48
+ };
49
+ }
50
+ return error;
51
+ }
52
+
53
+ function mergeDiagnostics(...parts: Array<Record<string, unknown> | undefined>): Record<string, unknown> {
54
+ return Object.assign({}, ...parts.filter(Boolean));
55
+ }
56
+
57
+ function resolveToolTimeoutMs(params: MempalaceParams, bridgeTimeoutMs: number): number {
58
+ if (typeof params.timeout !== "number") return bridgeTimeoutMs;
59
+
60
+ // Public tool schemas express timeouts in seconds, matching the rest of the
61
+ // OMP tool surface. The bridge runner uses milliseconds internally.
62
+ return Math.min(params.timeout * 1000, bridgeTimeoutMs);
63
+ }
64
+
65
+ export function createMempalaceBridge(options: CreateMempalaceBridgeOptions): MempalaceBridgeFacade {
66
+ const resolveBridge = options.runtime?.resolveBridgeScriptPath ?? (() => resolveBridgeScriptPath());
67
+ const runBridge = options.runtime?.runBridgeRequest ?? runBridgeRequest;
68
+
69
+ return {
70
+ async execute(params: MempalaceParams): Promise<MempalaceBridgeCallResult> {
71
+ const bridgePath = resolveBridge();
72
+ if (!bridgePath.ok) {
73
+ return {
74
+ ok: false,
75
+ action: params.action,
76
+ error: bridgePath.error,
77
+ diagnostics: { bridgeScriptPath: bridgePath.path },
78
+ };
79
+ }
80
+
81
+ const venv = resolveManagedVenvPaths(options.config.managedVenvPath);
82
+ const timeoutMs = resolveToolTimeoutMs(params, options.config.timeouts.bridgeMs);
83
+ const palacePath = params.palace ?? options.config.palacePath;
84
+ const runResult = await runBridge({
85
+ pythonPath: venv.python,
86
+ bridgeScriptPath: bridgePath.path,
87
+ timeoutMs,
88
+ request: {
89
+ action: params.action,
90
+ params: { ...params },
91
+ options: {
92
+ cwd: options.cwd,
93
+ palacePath,
94
+ agentName: options.config.defaultAgentName,
95
+ },
96
+ },
97
+ });
98
+
99
+ if (!runResult.ok) {
100
+ return {
101
+ ok: false,
102
+ action: params.action,
103
+ error: withSetupRemediation(runResult.error),
104
+ diagnostics: {
105
+ durationMs: runResult.durationMs,
106
+ stdoutPreview: runResult.stdoutPreview,
107
+ stderrTail: runResult.stderrTail,
108
+ },
109
+ };
110
+ }
111
+
112
+ if (!runResult.response.ok) {
113
+ return {
114
+ ok: false,
115
+ action: params.action,
116
+ error: withSetupRemediation(runResult.response.error),
117
+ diagnostics: mergeDiagnostics(runResult.response.diagnostics, {
118
+ durationMs: runResult.durationMs,
119
+ stderr: runResult.stderr,
120
+ }),
121
+ };
122
+ }
123
+
124
+ return {
125
+ ok: true,
126
+ action: params.action,
127
+ result: runResult.response.result ?? {},
128
+ diagnostics: mergeDiagnostics(runResult.response.diagnostics, {
129
+ durationMs: runResult.durationMs,
130
+ stderr: runResult.stderr,
131
+ }),
132
+ };
133
+ },
134
+ };
135
+ }
@@ -0,0 +1,75 @@
1
+ import { homedir } from "node:os";
2
+ import path from "node:path";
3
+ import type { PlatformPaths } from "../platform/types.js";
4
+ import type { MempalaceConfig, SupipowersConfig } from "../types.js";
5
+ import { getProjectStateDir } from "../workspace/state-paths.js";
6
+ import { resolveRepoIdentityRootFromFs } from "../workspace/repo-root.js";
7
+
8
+ export interface ResolvedMempalaceConfig extends MempalaceConfig {
9
+ managedVenvPath: string;
10
+ palacePath: string;
11
+ managedVenvPathDisplay: string;
12
+ palacePathDisplay: string;
13
+ }
14
+
15
+ function expandUserPath(input: string, cwd: string): string {
16
+ const trimmed = input.trim();
17
+ if (trimmed === "~") {
18
+ return homedir();
19
+ }
20
+
21
+ if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
22
+ return path.join(homedir(), trimmed.slice(2));
23
+ }
24
+
25
+ return path.isAbsolute(trimmed) ? path.normalize(trimmed) : path.resolve(cwd, trimmed);
26
+ }
27
+
28
+ function sanitizedWing(value: string): string {
29
+ return value
30
+ .trim()
31
+ .toLowerCase()
32
+ .replace(/[\s/\\]+/g, "-")
33
+ .replace(/[^a-z0-9_-]+/g, "-")
34
+ .replace(/[-_]+/g, "-")
35
+ .replace(/^-+|-+$/g, "");
36
+ }
37
+
38
+ export function normalizeMempalaceWing(value: string): string {
39
+ return sanitizedWing(value) || "project";
40
+ }
41
+
42
+ export function resolveMempalaceConfig(
43
+ config: SupipowersConfig,
44
+ cwd: string,
45
+ _paths: PlatformPaths,
46
+ ): ResolvedMempalaceConfig {
47
+ const raw = config.mempalace;
48
+ return {
49
+ ...raw,
50
+ managedVenvPath: expandUserPath(raw.managedVenvPath, cwd),
51
+ palacePath: expandUserPath(raw.palacePath, cwd),
52
+ managedVenvPathDisplay: raw.managedVenvPath,
53
+ palacePathDisplay: raw.palacePath,
54
+ };
55
+ }
56
+
57
+ export function resolveDefaultWing(
58
+ config: Pick<ResolvedMempalaceConfig, "defaultWingStrategy" | "explicitWing">,
59
+ cwd: string,
60
+ paths: PlatformPaths,
61
+ ): string {
62
+ if (config.defaultWingStrategy === "explicit") {
63
+ const explicit = sanitizedWing(config.explicitWing ?? "");
64
+ if (!explicit) {
65
+ throw new Error("mempalace.defaultWingStrategy is explicit but explicitWing is empty");
66
+ }
67
+ return explicit;
68
+ }
69
+
70
+ if (config.defaultWingStrategy === "project-slug") {
71
+ return normalizeMempalaceWing(path.basename(getProjectStateDir(paths, cwd)));
72
+ }
73
+
74
+ return normalizeMempalaceWing(path.basename(resolveRepoIdentityRootFromFs(cwd)));
75
+ }
@@ -0,0 +1,163 @@
1
+ import type { MempalaceAction } from "./schema.js";
2
+ import type { MempalaceConfig } from "../types.js";
3
+
4
+ export interface MempalaceFormattedResult {
5
+ text: string;
6
+ details: unknown;
7
+ }
8
+
9
+ export interface MempalaceBridgeError {
10
+ code: string;
11
+ message: string;
12
+ remediation?: string;
13
+ [key: string]: unknown;
14
+ }
15
+
16
+ type ResultBudgets = MempalaceConfig["budgets"];
17
+
18
+ type RecordValue = Record<string, unknown>;
19
+
20
+ const TRUNCATED_SEARCH_GUIDANCE = "\n\nOutput truncated. Use a narrower query/limit or call get_drawer with a result id for full text.";
21
+ const TRUNCATED_LIST_GUIDANCE = "\n\nOutput truncated. Re-run with a smaller limit or a more specific wing/room.";
22
+
23
+ function asRecord(value: unknown): RecordValue {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value as RecordValue : {};
25
+ }
26
+
27
+ function asArray(value: unknown): RecordValue[] {
28
+ return Array.isArray(value) ? value.map(asRecord) : [];
29
+ }
30
+
31
+ function stringValue(value: unknown): string {
32
+ if (value === null || value === undefined) return "";
33
+ if (typeof value === "string") return value;
34
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
35
+ return JSON.stringify(value);
36
+ }
37
+
38
+ function truncateText(text: string, maxChars: number, guidance: string): string {
39
+ if (text.length <= maxChars) return text;
40
+ if (maxChars <= guidance.length + 1) {
41
+ return guidance.trim().slice(0, maxChars);
42
+ }
43
+ const headChars = maxChars - guidance.length - 1;
44
+ return `${text.slice(0, headChars).trimEnd()}…${guidance}`;
45
+ }
46
+
47
+ function formatSimilarity(value: unknown): string {
48
+ return typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "n/a";
49
+ }
50
+
51
+ function formatSearch(result: RecordValue, budgets: ResultBudgets): string {
52
+ const results = asArray(result.results ?? result.items);
53
+ const query = stringValue(result.query) || "(unspecified query)";
54
+ const count = typeof result.count === "number" ? result.count : results.length;
55
+ const lines = [`MemPalace search`, `Search results for ${query} (${count})`];
56
+
57
+ for (const [index, item] of results.entries()) {
58
+ const id = stringValue(item.id ?? item.drawer_id) || `#${index + 1}`;
59
+ const wing = stringValue(item.wing);
60
+ const room = stringValue(item.room);
61
+ const location = [wing, room].filter(Boolean).join("/") || "unscoped";
62
+ const excerpt = stringValue(item.content ?? item.text ?? item.entry ?? item.summary).replace(/\s+/g, " ").trim();
63
+ lines.push(`${index + 1}. ${id} · ${location} · similarity ${formatSimilarity(item.similarity ?? item.score)}`);
64
+ if (excerpt) lines.push(` ${excerpt}`);
65
+ }
66
+
67
+ return truncateText(lines.join("\n"), budgets.searchResultChars, TRUNCATED_SEARCH_GUIDANCE);
68
+ }
69
+
70
+ function formatDrawerList(result: RecordValue, budgets: ResultBudgets): string {
71
+ const drawers = asArray(result.drawers ?? result.results ?? result.items);
72
+ const lines = [`Drawers (${drawers.length})`];
73
+
74
+ for (const drawer of drawers) {
75
+ const id = stringValue(drawer.id ?? drawer.drawer_id) || "unknown";
76
+ const location = [stringValue(drawer.wing), stringValue(drawer.room)].filter(Boolean).join("/") || "unscoped";
77
+ const updated = stringValue(drawer.updated_at ?? drawer.created_at ?? drawer.timestamp);
78
+ lines.push(`- ${id} · ${location}${updated ? ` · ${updated}` : ""}`);
79
+ }
80
+
81
+ return truncateText(lines.join("\n"), budgets.listResultChars, TRUNCATED_LIST_GUIDANCE);
82
+ }
83
+
84
+ function formatDiary(result: RecordValue, budgets: ResultBudgets): string {
85
+ const entries = asArray(result.entries ?? result.results ?? result.items);
86
+ const lines = [`Diary entries (${entries.length})`];
87
+
88
+ for (const entry of entries) {
89
+ const timestamp = stringValue(entry.timestamp ?? entry.created_at);
90
+ const agent = stringValue(entry.agent_name ?? entry.agent) || "unknown-agent";
91
+ const text = stringValue(entry.entry ?? entry.content ?? entry.text).replace(/\s+/g, " ").trim();
92
+ lines.push(`- ${timestamp ? `${timestamp} · ` : ""}${agent}: ${text}`);
93
+ }
94
+
95
+ return truncateText(lines.join("\n"), budgets.diaryChars, TRUNCATED_LIST_GUIDANCE);
96
+ }
97
+
98
+ function formatStatus(result: RecordValue): string {
99
+ const wings = Array.isArray(result.wings) ? result.wings.length : result.wingCount ?? result.wings_count ?? "unknown";
100
+ const lines = ["MemPalace status"];
101
+ const palacePath = stringValue(result.palacePath ?? result.palace_path ?? result.palace);
102
+ if (palacePath) lines.push(`palace: ${palacePath}`);
103
+ if ("ready" in result) lines.push(`ready: ${String(result.ready)}`);
104
+ if ("version" in result) lines.push(`version: ${stringValue(result.version)}`);
105
+ lines.push(`wings: ${String(wings)}`);
106
+ return lines.join("\n");
107
+ }
108
+
109
+ function formatGeneric(action: MempalaceAction, result: RecordValue, budgets: ResultBudgets): string {
110
+ const summary = stringValue(result.message ?? result.summary ?? result.status);
111
+ if (summary) return truncateText(`MemPalace ${action}: ${summary}`, budgets.listResultChars, TRUNCATED_LIST_GUIDANCE);
112
+ return truncateText(`MemPalace ${action} result\n${JSON.stringify(result, null, 2)}`, budgets.listResultChars, TRUNCATED_LIST_GUIDANCE);
113
+ }
114
+
115
+ export function formatMempalaceResult(
116
+ action: MempalaceAction,
117
+ result: unknown,
118
+ budgets: ResultBudgets,
119
+ ): MempalaceFormattedResult {
120
+ const record = asRecord(result);
121
+ let text: string;
122
+
123
+ if (action === "status" || action === "version") {
124
+ text = formatStatus(record);
125
+ } else if (action === "search" || action === "wake_up") {
126
+ text = formatSearch(record, budgets);
127
+ } else if (action === "list_drawers" || action === "list_wings" || action === "list_rooms") {
128
+ text = formatDrawerList(record, budgets);
129
+ } else if (action === "diary_read") {
130
+ text = formatDiary(record, budgets);
131
+ } else {
132
+ text = formatGeneric(action, record, budgets);
133
+ }
134
+
135
+ return { text, details: result };
136
+ }
137
+
138
+ export function formatMempalaceError(error: MempalaceBridgeError, details: Record<string, unknown> = {}): MempalaceFormattedResult {
139
+ const lines = [`MemPalace error (${error.code})`, error.message];
140
+ if (error.remediation) lines.push(`Remediation: ${error.remediation}`);
141
+
142
+ // Surface bridge subprocess output. When the bridge fails (e.g. malformed
143
+ // JSON, non-zero exit) the model and the user need to see what actually
144
+ // happened, not just the abstract error code.
145
+ const diagnostics = (details.diagnostics ?? {}) as Record<string, unknown>;
146
+ const stderrTail = pickString(diagnostics.stderrTail) ?? pickString(details.stderrTail);
147
+ const stdoutPreview = pickString(diagnostics.stdoutPreview) ?? pickString(details.stdoutPreview);
148
+ if (stderrTail && stderrTail.trim().length > 0) {
149
+ lines.push("", "Bridge stderr:", stderrTail.trim());
150
+ }
151
+ if (stdoutPreview && stdoutPreview.trim().length > 0) {
152
+ lines.push("", "Bridge stdout (preview):", stdoutPreview.trim());
153
+ }
154
+
155
+ return {
156
+ text: lines.join("\n"),
157
+ details: { ...details, error },
158
+ };
159
+ }
160
+
161
+ function pickString(value: unknown): string | undefined {
162
+ return typeof value === "string" ? value : undefined;
163
+ }