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
@@ -1,12 +1,13 @@
1
- // src/config/schema.ts
2
1
  import type { TSchema } from "@sinclair/typebox";
3
2
  import { Type } from "@sinclair/typebox";
4
3
  import { Value } from "@sinclair/typebox/value";
5
4
  import type { SupipowersConfig } from "../types.js";
6
5
  import { QualityGatesSchema } from "../quality/schemas.js";
6
+ import { UltraPlanConfigSchema } from "../ultraplan/contracts.js";
7
7
 
8
8
  const TAG_FORMAT_PATTERN = "^(?:(?!\\$\\{version\\}).)*\\$\\{version\\}(?:(?!\\$\\{version\\}).)*$";
9
9
 
10
+
10
11
  export const ConfigSchema = Type.Object(
11
12
  {
12
13
  version: Type.String(),
@@ -46,6 +47,7 @@ export const ConfigSchema = Type.Object(
46
47
  },
47
48
  { additionalProperties: false },
48
49
  ),
50
+ ultraplan: UltraPlanConfigSchema,
49
51
  contextMode: Type.Object(
50
52
  {
51
53
  enabled: Type.Boolean(),
@@ -57,6 +59,64 @@ export const ConfigSchema = Type.Object(
57
59
  llmSummarization: Type.Boolean(),
58
60
  llmThreshold: Type.Number({ minimum: 4096 }),
59
61
  enforceRouting: Type.Boolean(),
62
+ lazyTools: Type.Object(
63
+ {
64
+ enabled: Type.Boolean(),
65
+ mode: Type.Union([
66
+ Type.Literal("conservative"),
67
+ Type.Literal("balanced"),
68
+ Type.Literal("aggressive"),
69
+ ]),
70
+ alwaysKeep: Type.Array(Type.String()),
71
+ commandAllowlist: Type.Record(Type.String(), Type.Array(Type.String())),
72
+ keywordTools: Type.Record(Type.String(), Type.Array(Type.String())),
73
+ },
74
+ { additionalProperties: false },
75
+ ),
76
+ processors: Type.Object(
77
+ {
78
+ enabled: Type.Boolean(),
79
+ disable: Type.Array(
80
+ Type.Union([
81
+ Type.Literal("git"),
82
+ Type.Literal("test"),
83
+ Type.Literal("lint"),
84
+ Type.Literal("build"),
85
+ Type.Literal("k8s"),
86
+ Type.Literal("docker"),
87
+ Type.Literal("log"),
88
+ Type.Literal("json"),
89
+ ]),
90
+ ),
91
+ },
92
+ { additionalProperties: false },
93
+ ),
94
+ cacheHandles: Type.Object(
95
+ {
96
+ enabled: Type.Boolean(),
97
+ spillThresholdBytes: Type.Number({ minimum: 1024 }),
98
+ previewBytes: Type.Number({ minimum: 256 }),
99
+ },
100
+ { additionalProperties: false },
101
+ ),
102
+ repomap: Type.Object(
103
+ {
104
+ enabled: Type.Boolean(),
105
+ tokenBudget: Type.Number({ minimum: 100 }),
106
+ maxFiles: Type.Number({ minimum: 1 }),
107
+ },
108
+ { additionalProperties: false },
109
+ ),
110
+ memory: Type.Object(
111
+ {
112
+ enabled: Type.Boolean(),
113
+ byteBudget: Type.Number({ minimum: 256 }),
114
+ maxRows: Type.Number({ minimum: 1 }),
115
+ retentionDays: Type.Number({ minimum: 1 }),
116
+ focusChainCadence: Type.Integer({ minimum: 1 }),
117
+ },
118
+ { additionalProperties: false },
119
+ ),
60
120
  },
61
121
  { additionalProperties: false },
62
122
  ),
@@ -66,12 +126,58 @@ export const ConfigSchema = Type.Object(
66
126
  },
67
127
  { additionalProperties: false },
68
128
  ),
129
+ mempalace: Type.Object(
130
+ {
131
+ enabled: Type.Boolean(),
132
+ packageVersion: Type.String({ minLength: 1 }),
133
+ managedVenvPath: Type.String({ minLength: 1 }),
134
+ palacePath: Type.String({ minLength: 1 }),
135
+ defaultWingStrategy: Type.Union([
136
+ Type.Literal("repo-name"),
137
+ Type.Literal("project-slug"),
138
+ Type.Literal("explicit"),
139
+ ]),
140
+ explicitWing: Type.Union([Type.String(), Type.Null()]),
141
+ defaultAgentName: Type.String({ minLength: 1 }),
142
+ autoSetup: Type.Boolean(),
143
+ hooks: Type.Object(
144
+ {
145
+ wakeUp: Type.Boolean(),
146
+ searchGuidance: Type.Boolean(),
147
+ autoSearchOnPrompt: Type.Boolean(),
148
+ compactionCheckpoint: Type.Boolean(),
149
+ shutdownDiary: Type.Boolean(),
150
+ },
151
+ { additionalProperties: false },
152
+ ),
153
+ budgets: Type.Object(
154
+ {
155
+ wakeUpTokens: Type.Integer({ minimum: 1 }),
156
+ searchResultChars: Type.Integer({ minimum: 1 }),
157
+ listResultChars: Type.Integer({ minimum: 1 }),
158
+ diaryChars: Type.Integer({ minimum: 1 }),
159
+ autoSearchTokens: Type.Integer({ minimum: 1 }),
160
+ wakeUpInjectionEvery: Type.Integer({ minimum: 1 }),
161
+ },
162
+ { additionalProperties: false },
163
+ ),
164
+ timeouts: Type.Object(
165
+ {
166
+ setupMs: Type.Integer({ minimum: 1 }),
167
+ bridgeMs: Type.Integer({ minimum: 1 }),
168
+ hookMs: Type.Integer({ minimum: 1 }),
169
+ },
170
+ { additionalProperties: false },
171
+ ),
172
+ },
173
+ { additionalProperties: false },
174
+ ),
69
175
  },
70
176
  { additionalProperties: false },
71
177
  );
72
178
 
73
179
  export interface ConfigParseError {
74
- source: "global" | "project";
180
+ source: "global" | "root";
75
181
  path: string;
76
182
  message: string;
77
183
  }
@@ -1,3 +1,5 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
1
3
  import type { Platform } from "../platform/types.js";
2
4
  import type { ParsedSkill, PromptSection } from "./analyzer.js";
3
5
  import { estimateTokens } from "./analyzer.js";
@@ -56,24 +58,20 @@ const DEP_TO_TOOL: Record<string, string> = {
56
58
 
57
59
  // ── Tech Stack Detection ────────────────────────────────────
58
60
 
59
- /** Read a file via platform.exec; returns content or null on failure. */
60
- async function tryRead(
61
- platform: Platform,
62
- cwd: string,
63
- filename: string,
64
- ): Promise<string | null> {
65
- const r = await platform.exec("cat", [filename], { cwd });
66
- return r.code === 0 ? r.stdout : null;
61
+ function tryReadFile(cwd: string, filename: string): string | null {
62
+ try {
63
+ return fs.readFileSync(path.join(cwd, filename), "utf8");
64
+ } catch {
65
+ return null;
66
+ }
67
67
  }
68
68
 
69
- /** Check if a file exists at `cwd/filename` via exec. */
70
- async function fileExists(
71
- platform: Platform,
72
- cwd: string,
73
- filename: string,
74
- ): Promise<boolean> {
75
- const r = await platform.exec("test", ["-f", filename], { cwd });
76
- return r.code === 0;
69
+ function fileExists(cwd: string, filename: string): boolean {
70
+ try {
71
+ return fs.statSync(path.join(cwd, filename)).isFile();
72
+ } catch {
73
+ return false;
74
+ }
77
75
  }
78
76
 
79
77
  /**
@@ -84,13 +82,14 @@ export async function detectTechStack(
84
82
  platform: Platform,
85
83
  cwd: string,
86
84
  ): Promise<TechStack> {
85
+ void platform;
86
+
87
87
  const languages = new Set<string>();
88
88
  const frameworks = new Set<string>();
89
89
  const tools = new Set<string>();
90
90
  let runtime: string | null = null;
91
91
 
92
- // 1. package.json — deps & devDeps
93
- const pkgRaw = await tryRead(platform, cwd, "package.json");
92
+ const pkgRaw = tryReadFile(cwd, "package.json");
94
93
  if (pkgRaw) {
95
94
  try {
96
95
  const pkg = JSON.parse(pkgRaw);
@@ -110,18 +109,16 @@ export async function detectTechStack(
110
109
  }
111
110
  }
112
111
 
113
- // 2. Lockfiles → runtime (first match wins, most-specific first)
114
- if (await fileExists(platform, cwd, "bun.lock")) {
112
+ if (fileExists(cwd, "bun.lock")) {
115
113
  runtime = "bun";
116
- } else if (await fileExists(platform, cwd, "package-lock.json")) {
114
+ } else if (fileExists(cwd, "package-lock.json")) {
117
115
  runtime = "node";
118
- } else if (await fileExists(platform, cwd, "pnpm-lock.yaml")) {
116
+ } else if (fileExists(cwd, "pnpm-lock.yaml")) {
119
117
  runtime = "node";
120
- } else if (await fileExists(platform, cwd, "yarn.lock")) {
118
+ } else if (fileExists(cwd, "yarn.lock")) {
121
119
  runtime = "node";
122
120
  }
123
121
 
124
- // 3. Config files → languages
125
122
  const configChecks: [string, string][] = [
126
123
  ["tsconfig.json", "typescript"],
127
124
  ["Cargo.toml", "rust"],
@@ -130,15 +127,10 @@ export async function detectTechStack(
130
127
  ["go.mod", "go"],
131
128
  ["Gemfile", "ruby"],
132
129
  ];
133
- const results = await Promise.all(
134
- configChecks.map(([file, lang]) =>
135
- fileExists(platform, cwd, file).then((exists) =>
136
- exists ? lang : null,
137
- ),
138
- ),
139
- );
140
- for (const lang of results) {
141
- if (lang) languages.add(lang);
130
+ for (const [file, language] of configChecks) {
131
+ if (fileExists(cwd, file)) {
132
+ languages.add(language);
133
+ }
142
134
  }
143
135
 
144
136
  return {
@@ -0,0 +1,223 @@
1
+ import type { RuleMode, WriteRuleAction } from "./startup-optimizer.js";
2
+
3
+ export const MANAGED_RULE_HEADER = "<!-- supipowers:managed-rule";
4
+ export const MANAGED_RULE_END = "-->";
5
+
6
+ export interface ManagedRuleMetadata {
7
+ version: number;
8
+ mode: RuleMode;
9
+ sourceId: string;
10
+ sourceName: string;
11
+ sourceHash: string;
12
+ slug: string;
13
+ sourceBytes: number;
14
+ }
15
+
16
+ export type ParsedManagedRule =
17
+ | { status: "unmanaged"; managed: false }
18
+ | {
19
+ status: "managed";
20
+ managed: true;
21
+ metadata: ManagedRuleMetadata;
22
+ frontmatter: Record<string, string>;
23
+ /**
24
+ * Canonical source body — exactly what the optimizer hashed/sized to produce
25
+ * `metadata.sourceHash` / `metadata.sourceBytes`. Trailing newline added by the
26
+ * renderer (for POSIX-friendly EOF) is stripped during parsing.
27
+ */
28
+ body: string;
29
+ }
30
+ | { status: "malformed"; managed: true; error: string };
31
+
32
+ /**
33
+ * Render a managed rule file.
34
+ *
35
+ * The body is written exactly as `action.sourceContent`, plus a single trailing newline
36
+ * if missing (POSIX-friendly EOF). The parser strips that trailing newline so
37
+ * `parsed.body` equals the canonical source content used to compute the manifest hash.
38
+ */
39
+ export function renderManagedRule(action: WriteRuleAction): string {
40
+ const frontmatter = renderFrontmatter(action);
41
+ const metadata = [
42
+ MANAGED_RULE_HEADER,
43
+ "version: 1",
44
+ `mode: ${action.mode}`,
45
+ `sourceId: ${action.sourceId}`,
46
+ `sourceName: ${action.sourceName}`,
47
+ `sourceHash: ${action.sourceHash}`,
48
+ `slug: ${action.slug}`,
49
+ `sourceBytes: ${action.sourceBytes}`,
50
+ MANAGED_RULE_END,
51
+ ].join("\n");
52
+ const body = action.sourceContent.endsWith("\n")
53
+ ? action.sourceContent
54
+ : `${action.sourceContent}\n`;
55
+
56
+ return `${metadata}\n---\n${frontmatter}\n---\n${body}`;
57
+ }
58
+
59
+ export function parseManagedRule(text: string): ParsedManagedRule {
60
+ if (!text.startsWith(MANAGED_RULE_HEADER)) {
61
+ return { status: "unmanaged", managed: false };
62
+ }
63
+
64
+ const headerEnd = text.indexOf(MANAGED_RULE_END, MANAGED_RULE_HEADER.length);
65
+ if (headerEnd === -1) {
66
+ return { status: "malformed", managed: true, error: "managed header is not closed" };
67
+ }
68
+
69
+ const headerText = text.slice(0, headerEnd).trimEnd();
70
+ const metadataResult = parseMetadata(headerText);
71
+ if (typeof metadataResult === "string") {
72
+ return { status: "malformed", managed: true, error: metadataResult };
73
+ }
74
+
75
+ const afterHeader = text.slice(headerEnd + MANAGED_RULE_END.length).replace(/^\r?\n/, "");
76
+ const frontmatterResult = parseFrontmatter(afterHeader);
77
+ if (typeof frontmatterResult === "string") {
78
+ return { status: "malformed", managed: true, error: frontmatterResult };
79
+ }
80
+
81
+ return {
82
+ status: "managed",
83
+ managed: true,
84
+ metadata: metadataResult,
85
+ frontmatter: frontmatterResult.frontmatter,
86
+ body: stripTrailingNewline(frontmatterResult.body),
87
+ };
88
+ }
89
+
90
+ function renderFrontmatter(action: WriteRuleAction): string {
91
+ if (action.mode === "ttsr") {
92
+ if (!action.condition) {
93
+ throw new Error(`TTSR rule ${action.slug} is missing a condition`);
94
+ }
95
+ return `condition: ${frontmatterScalarLiteral(action.condition)}`;
96
+ }
97
+
98
+ const description = action.description ?? deriveRuleDescription(action.sourceContent) ?? `Use ${action.sourceName} when relevant.`;
99
+ return `description: ${frontmatterScalarLiteral(description)}`;
100
+ }
101
+
102
+ export function deriveRuleDescription(sourceContent: string): string | null {
103
+ for (const rawLine of sourceContent.split(/\r?\n/)) {
104
+ const line = rawLine.trim();
105
+ if (!line) continue;
106
+ if (line.startsWith("#")) continue;
107
+ if (line.startsWith("<!--")) continue;
108
+ return line;
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * Encode a frontmatter scalar value.
115
+ *
116
+ * The managed rule file uses YAML-shaped frontmatter (`---`-delimited blocks) but the
117
+ * scalar values are intentionally restricted to JSON string literals: `JSON.stringify`
118
+ * produces a deterministic, dependency-free encoding that handles quotes, backslashes,
119
+ * and newlines unambiguously. Parsing uses `JSON.parse` to round-trip the same subset.
120
+ *
121
+ * This is a deliberate, narrow subset of YAML — single-quoted strings, multi-line
122
+ * scalars, anchors, and other YAML constructs are NOT supported. If broader YAML
123
+ * support becomes necessary, replace both this helper and `parseFrontmatterScalar`
124
+ * with a real YAML library and update callers accordingly.
125
+ */
126
+ function frontmatterScalarLiteral(value: string): string {
127
+ return JSON.stringify(value);
128
+ }
129
+
130
+ function parseMetadata(headerText: string): ManagedRuleMetadata | string {
131
+ const lines = headerText.split(/\r?\n/);
132
+ if (lines[0] !== MANAGED_RULE_HEADER) {
133
+ return "managed header marker is invalid";
134
+ }
135
+
136
+ const raw: Record<string, string> = {};
137
+ for (const line of lines.slice(1)) {
138
+ const idx = line.indexOf(":");
139
+ if (idx === -1) continue;
140
+ raw[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
141
+ }
142
+
143
+ const required = ["version", "mode", "sourceId", "sourceName", "sourceHash", "slug", "sourceBytes"];
144
+ for (const key of required) {
145
+ if (!raw[key]) return `managed metadata missing ${key}`;
146
+ }
147
+
148
+ if (raw.mode !== "ttsr" && raw.mode !== "rulebook") {
149
+ return `managed metadata has invalid mode ${raw.mode}`;
150
+ }
151
+
152
+ const version = Number(raw.version);
153
+ const sourceBytes = Number(raw.sourceBytes);
154
+ if (!Number.isInteger(version) || version <= 0) return "managed metadata has invalid version";
155
+ if (!Number.isInteger(sourceBytes) || sourceBytes < 0) return "managed metadata has invalid sourceBytes";
156
+
157
+ return {
158
+ version,
159
+ mode: raw.mode,
160
+ sourceId: raw.sourceId,
161
+ sourceName: raw.sourceName,
162
+ sourceHash: raw.sourceHash,
163
+ slug: raw.slug,
164
+ sourceBytes,
165
+ };
166
+ }
167
+
168
+ function parseFrontmatter(text: string): { frontmatter: Record<string, string>; body: string } | string {
169
+ if (!text.startsWith("---\n") && !text.startsWith("---\r\n")) {
170
+ return "managed frontmatter is missing opening delimiter";
171
+ }
172
+
173
+ const normalizedStartLength = text.startsWith("---\r\n") ? 5 : 4;
174
+ const rest = text.slice(normalizedStartLength);
175
+ const closeMatch = rest.match(/\r?\n---\r?\n/);
176
+ if (!closeMatch || closeMatch.index === undefined) {
177
+ return "managed frontmatter is missing closing delimiter";
178
+ }
179
+
180
+ const frontmatterText = rest.slice(0, closeMatch.index);
181
+ const body = rest.slice(closeMatch.index + closeMatch[0].length);
182
+ const frontmatter: Record<string, string> = {};
183
+
184
+ for (const rawLine of frontmatterText.split(/\r?\n/)) {
185
+ const line = rawLine.trim();
186
+ if (!line) continue;
187
+ const idx = line.indexOf(":");
188
+ if (idx === -1) return `managed frontmatter has invalid line: ${line}`;
189
+ const key = line.slice(0, idx).trim();
190
+ const rawValue = line.slice(idx + 1).trim();
191
+ const parsedValue = parseFrontmatterScalar(rawValue);
192
+ if (typeof parsedValue !== "string") {
193
+ return `managed frontmatter has invalid ${key}`;
194
+ }
195
+ frontmatter[key] = parsedValue;
196
+ }
197
+
198
+ return { frontmatter, body };
199
+ }
200
+
201
+ /**
202
+ * Decode a frontmatter scalar value.
203
+ *
204
+ * See `frontmatterScalarLiteral`: managed rule frontmatter encodes scalars as JSON
205
+ * string literals. Double-quoted values must round-trip through `JSON.parse`; bare
206
+ * values (no leading quote) are returned verbatim for forward compatibility with
207
+ * unquoted single-token scalars.
208
+ */
209
+ function parseFrontmatterScalar(rawValue: string): string | null {
210
+ if (!rawValue.startsWith('"')) return rawValue;
211
+ try {
212
+ const parsed = JSON.parse(rawValue);
213
+ return typeof parsed === "string" ? parsed : null;
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+
219
+ function stripTrailingNewline(value: string): string {
220
+ if (value.endsWith("\r\n")) return value.slice(0, -2);
221
+ if (value.endsWith("\n")) return value.slice(0, -1);
222
+ return value;
223
+ }