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
package/src/commands/status.ts
CHANGED
|
@@ -1,21 +1,79 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
3
|
+
import type { ConfigResolutionOptions } from "../config/loader.js";
|
|
4
|
+
import type { PackageManagerId, ReviewReport, WorkspaceTarget } from "../types.js";
|
|
2
5
|
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
3
6
|
import { formatConfigErrors, inspectConfig } from "../config/loader.js";
|
|
4
|
-
import {
|
|
7
|
+
import { listTargetPlans } from "../storage/plans.js";
|
|
8
|
+
import { loadLatestReport } from "../storage/reports.js";
|
|
9
|
+
import { formatReliabilitySection, loadReliabilitySummaries } from "../storage/reliability-metrics.js";
|
|
5
10
|
import { summarizeEnabledGates } from "../quality/setup.js";
|
|
11
|
+
import { detectPackageManager } from "../workspace/package-manager.js";
|
|
12
|
+
import { resolveRepoRoot } from "../workspace/repo-root.js";
|
|
13
|
+
import { discoverWorkspaceTargets } from "../workspace/targets.js";
|
|
6
14
|
|
|
7
15
|
export interface StatusCommandDependencies {
|
|
8
16
|
inspectConfig: typeof inspectConfig;
|
|
9
|
-
|
|
17
|
+
listTargetPlans: typeof listTargetPlans;
|
|
18
|
+
loadLatestReport: typeof loadLatestReport;
|
|
19
|
+
detectPackageManager: typeof detectPackageManager;
|
|
20
|
+
discoverWorkspaceTargets: typeof discoverWorkspaceTargets;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TargetStatusSnapshot {
|
|
24
|
+
target: WorkspaceTarget;
|
|
25
|
+
label: string;
|
|
26
|
+
shortLabel: string;
|
|
27
|
+
configSummary: string;
|
|
28
|
+
plans: string[];
|
|
29
|
+
latestReport: ReviewReport | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface StatusSnapshot {
|
|
33
|
+
targets: TargetStatusSnapshot[];
|
|
34
|
+
isMonorepo: boolean;
|
|
35
|
+
workspaceCount: number;
|
|
36
|
+
targetsWithPlans: number;
|
|
37
|
+
targetsWithReports: number;
|
|
38
|
+
reliabilityLines: string[];
|
|
10
39
|
}
|
|
11
40
|
|
|
12
41
|
const STATUS_COMMAND_DEPENDENCIES: StatusCommandDependencies = {
|
|
13
42
|
inspectConfig,
|
|
14
|
-
|
|
43
|
+
listTargetPlans,
|
|
44
|
+
loadLatestReport,
|
|
45
|
+
detectPackageManager,
|
|
46
|
+
discoverWorkspaceTargets,
|
|
15
47
|
};
|
|
16
48
|
|
|
17
|
-
function
|
|
18
|
-
const
|
|
49
|
+
function createFallbackRootTarget(repoRoot: string, packageManager: PackageManagerId): WorkspaceTarget {
|
|
50
|
+
const repoName = path.basename(repoRoot) || "repo-root";
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
id: repoName,
|
|
54
|
+
name: repoName,
|
|
55
|
+
kind: "root",
|
|
56
|
+
repoRoot,
|
|
57
|
+
packageDir: repoRoot,
|
|
58
|
+
manifestPath: path.join(repoRoot, "package.json"),
|
|
59
|
+
relativeDir: ".",
|
|
60
|
+
version: "0.0.0",
|
|
61
|
+
private: false,
|
|
62
|
+
packageManager,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getTargetConfigOptions(target: WorkspaceTarget): ConfigResolutionOptions {
|
|
67
|
+
return { repoRoot: target.repoRoot };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function summarizeTargetConfig(
|
|
71
|
+
platform: Platform,
|
|
72
|
+
ctx: PlatformContext,
|
|
73
|
+
deps: StatusCommandDependencies,
|
|
74
|
+
target: WorkspaceTarget,
|
|
75
|
+
): string {
|
|
76
|
+
const inspection = deps.inspectConfig(platform.paths, ctx.cwd, getTargetConfigOptions(target));
|
|
19
77
|
const config = inspection.effectiveConfig ?? DEFAULT_CONFIG;
|
|
20
78
|
|
|
21
79
|
if (inspection.parseErrors.length > 0 || inspection.validationErrors.length > 0) {
|
|
@@ -25,21 +83,150 @@ function formatStatusSummary(platform: Platform, ctx: PlatformContext, deps: Sta
|
|
|
25
83
|
return `Gates: ${summarizeEnabledGates(config.quality.gates)}`;
|
|
26
84
|
}
|
|
27
85
|
|
|
28
|
-
|
|
86
|
+
function createTargetLabel(target: WorkspaceTarget): string {
|
|
87
|
+
return target.kind === "root"
|
|
88
|
+
? `${target.name} (root)`
|
|
89
|
+
: `${target.name} (${target.relativeDir})`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function createTargetShortLabel(target: WorkspaceTarget): string {
|
|
93
|
+
return target.kind === "root" ? "root" : target.name;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function collectStatusSnapshot(
|
|
97
|
+
platform: Platform,
|
|
98
|
+
ctx: PlatformContext,
|
|
99
|
+
deps: StatusCommandDependencies,
|
|
100
|
+
): Promise<StatusSnapshot> {
|
|
101
|
+
const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
|
|
102
|
+
const packageManager = deps.detectPackageManager(repoRoot);
|
|
103
|
+
const discoveredTargets = deps.discoverWorkspaceTargets(repoRoot, packageManager);
|
|
104
|
+
const targets = discoveredTargets.length > 0
|
|
105
|
+
? discoveredTargets
|
|
106
|
+
: [createFallbackRootTarget(repoRoot, packageManager)];
|
|
107
|
+
|
|
108
|
+
const snapshots = targets.map((target) => {
|
|
109
|
+
const latestReport = deps.loadLatestReport(platform.paths, target);
|
|
110
|
+
const plans = deps.listTargetPlans(platform.paths, target);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
target,
|
|
114
|
+
label: createTargetLabel(target),
|
|
115
|
+
shortLabel: createTargetShortLabel(target),
|
|
116
|
+
configSummary: summarizeTargetConfig(platform, ctx, deps, target),
|
|
117
|
+
plans,
|
|
118
|
+
latestReport,
|
|
119
|
+
} satisfies TargetStatusSnapshot;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const reliabilityLines = formatReliabilitySection(loadReliabilitySummaries(platform.paths, ctx.cwd));
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
targets: snapshots,
|
|
126
|
+
isMonorepo: snapshots.length > 1,
|
|
127
|
+
workspaceCount: snapshots.filter((snapshot) => snapshot.target.kind === "workspace").length,
|
|
128
|
+
targetsWithPlans: snapshots.filter((snapshot) => snapshot.plans.length > 0).length,
|
|
129
|
+
targetsWithReports: snapshots.filter((snapshot) => snapshot.latestReport !== null).length,
|
|
130
|
+
reliabilityLines,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function formatLatestReport(report: ReviewReport | null): string {
|
|
135
|
+
return report ? `${report.timestamp.slice(0, 10)} (${report.overallStatus})` : "none";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function summarizePlanTargets(snapshot: StatusSnapshot): string {
|
|
139
|
+
const entries = snapshot.targets
|
|
140
|
+
.filter((target) => target.plans.length > 0)
|
|
141
|
+
.map((target) => `${target.shortLabel}: ${target.plans.length}`);
|
|
142
|
+
|
|
143
|
+
return entries.join(" · ") || "none";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function summarizeReportTargets(snapshot: StatusSnapshot): string {
|
|
147
|
+
const entries = snapshot.targets
|
|
148
|
+
.filter((target) => target.latestReport)
|
|
149
|
+
.map((target) => `${target.shortLabel}: ${formatLatestReport(target.latestReport)}`);
|
|
150
|
+
|
|
151
|
+
return entries.join(" · ") || "none";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function summarizeConfigProblems(snapshot: StatusSnapshot): string {
|
|
155
|
+
const entries = snapshot.targets
|
|
156
|
+
.filter((target) => target.configSummary.startsWith("Config error:"))
|
|
157
|
+
.map((target) => `${target.shortLabel}: ${target.configSummary.slice("Config error: ".length)}`);
|
|
158
|
+
|
|
159
|
+
return entries.join(" · ") || "none";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function formatOverviewStatus(
|
|
29
163
|
platform: Platform,
|
|
30
164
|
ctx: PlatformContext,
|
|
31
165
|
deps: StatusCommandDependencies = STATUS_COMMAND_DEPENDENCIES,
|
|
32
|
-
): Promise<
|
|
33
|
-
const
|
|
166
|
+
): Promise<string[]> {
|
|
167
|
+
const snapshot = await collectStatusSnapshot(platform, ctx, deps);
|
|
168
|
+
|
|
169
|
+
if (!snapshot.isMonorepo) {
|
|
170
|
+
const [target] = snapshot.targets;
|
|
171
|
+
return [
|
|
172
|
+
target.configSummary,
|
|
173
|
+
`Plans: ${target.plans.length}`,
|
|
174
|
+
`Last checks: ${formatLatestReport(target.latestReport)}`,
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return [
|
|
179
|
+
`Packages: ${snapshot.targets.length} targets · ${snapshot.workspaceCount} workspaces`,
|
|
180
|
+
`Config issues: ${summarizeConfigProblems(snapshot)}`,
|
|
181
|
+
`Plans: ${summarizePlanTargets(snapshot)}`,
|
|
182
|
+
`Last checks: ${summarizeReportTargets(snapshot)}`,
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function formatStatusOptions(snapshot: StatusSnapshot): string[] {
|
|
187
|
+
if (!snapshot.isMonorepo) {
|
|
188
|
+
const [target] = snapshot.targets;
|
|
189
|
+
return [
|
|
190
|
+
target.configSummary,
|
|
191
|
+
`Plans: ${target.plans.length === 0 ? "none" : target.plans.length}`,
|
|
192
|
+
...target.plans.map((plan) => ` · ${plan}`),
|
|
193
|
+
`Last checks: ${formatLatestReport(target.latestReport)}`,
|
|
194
|
+
"",
|
|
195
|
+
...snapshot.reliabilityLines,
|
|
196
|
+
"",
|
|
197
|
+
"Close",
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
34
201
|
const options = [
|
|
35
|
-
|
|
36
|
-
`
|
|
37
|
-
...plans.map((plan) => ` · ${plan}`),
|
|
202
|
+
`Packages: ${snapshot.targets.length} targets · ${snapshot.workspaceCount} workspaces`,
|
|
203
|
+
`Artifacts: ${snapshot.targetsWithPlans} with plans · ${snapshot.targetsWithReports} with reports`,
|
|
38
204
|
"",
|
|
39
|
-
"Close",
|
|
40
205
|
];
|
|
41
206
|
|
|
42
|
-
|
|
207
|
+
for (const target of snapshot.targets) {
|
|
208
|
+
options.push(
|
|
209
|
+
target.label,
|
|
210
|
+
` ${target.configSummary}`,
|
|
211
|
+
` Plans: ${target.plans.length === 0 ? "none" : target.plans.length}`,
|
|
212
|
+
...target.plans.map((plan) => ` · ${plan}`),
|
|
213
|
+
` Last checks: ${formatLatestReport(target.latestReport)}`,
|
|
214
|
+
"",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
options.push(...snapshot.reliabilityLines, "", "Close");
|
|
219
|
+
return options;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function showStatusDialog(
|
|
223
|
+
platform: Platform,
|
|
224
|
+
ctx: PlatformContext,
|
|
225
|
+
deps: StatusCommandDependencies = STATUS_COMMAND_DEPENDENCIES,
|
|
226
|
+
): Promise<void> {
|
|
227
|
+
const snapshot = await collectStatusSnapshot(platform, ctx, deps);
|
|
228
|
+
|
|
229
|
+
await ctx.ui.select("Supipowers Status", formatStatusOptions(snapshot), {
|
|
43
230
|
helpText: "Esc to close",
|
|
44
231
|
});
|
|
45
232
|
}
|
package/src/commands/supi.ts
CHANGED
|
@@ -1,41 +1,9 @@
|
|
|
1
1
|
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
2
|
-
import {
|
|
3
|
-
import { formatConfigErrors, inspectConfig } from "../config/loader.js";
|
|
4
|
-
import { loadLatestReport } from "../storage/reports.js";
|
|
5
|
-
import { listPlans } from "../storage/plans.js";
|
|
6
|
-
import { summarizeEnabledGates } from "../quality/setup.js";
|
|
7
|
-
|
|
8
|
-
export interface SupiCommandDependencies {
|
|
9
|
-
inspectConfig: typeof inspectConfig;
|
|
10
|
-
loadLatestReport: typeof loadLatestReport;
|
|
11
|
-
listPlans: typeof listPlans;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const SUPI_COMMAND_DEPENDENCIES: SupiCommandDependencies = {
|
|
15
|
-
inspectConfig,
|
|
16
|
-
loadLatestReport,
|
|
17
|
-
listPlans,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function formatOverviewStatus(platform: Platform, ctx: PlatformContext, deps: SupiCommandDependencies): string[] {
|
|
21
|
-
const inspection = deps.inspectConfig(platform.paths, ctx.cwd);
|
|
22
|
-
const config = inspection.effectiveConfig ?? DEFAULT_CONFIG;
|
|
23
|
-
const latestReport = deps.loadLatestReport(platform.paths, ctx.cwd);
|
|
24
|
-
const plans = deps.listPlans(platform.paths, ctx.cwd);
|
|
25
|
-
|
|
26
|
-
return [
|
|
27
|
-
inspection.parseErrors.length > 0 || inspection.validationErrors.length > 0
|
|
28
|
-
? `Config error: ${formatConfigErrors(inspection).split("\n")[0]}`
|
|
29
|
-
: `Gates: ${summarizeEnabledGates(config.quality.gates)}`,
|
|
30
|
-
`Plans: ${plans.length}`,
|
|
31
|
-
`Last checks: ${latestReport ? `${latestReport.timestamp.slice(0, 10)} (${latestReport.overallStatus})` : "none"}`,
|
|
32
|
-
];
|
|
33
|
-
}
|
|
2
|
+
import { formatOverviewStatus } from "./status.js";
|
|
34
3
|
|
|
35
4
|
export async function showSupiDialog(
|
|
36
5
|
platform: Platform,
|
|
37
6
|
ctx: PlatformContext,
|
|
38
|
-
deps: SupiCommandDependencies = SUPI_COMMAND_DEPENDENCIES,
|
|
39
7
|
): Promise<void> {
|
|
40
8
|
const commands = [
|
|
41
9
|
"/supi:plan — Start collaborative planning",
|
|
@@ -52,7 +20,7 @@ export async function showSupiDialog(
|
|
|
52
20
|
"/supi:context — Show context breakdown",
|
|
53
21
|
"/supi:optimize-context — Optimize context to save tokens",
|
|
54
22
|
];
|
|
55
|
-
const status = formatOverviewStatus(platform, ctx
|
|
23
|
+
const status = await formatOverviewStatus(platform, ctx);
|
|
56
24
|
|
|
57
25
|
const choice = await ctx.ui.select(
|
|
58
26
|
"Supipowers",
|
|
@@ -70,7 +38,7 @@ export async function showSupiDialog(
|
|
|
70
38
|
}
|
|
71
39
|
|
|
72
40
|
export function handleSupi(platform: Platform, ctx: PlatformContext): void {
|
|
73
|
-
void showSupiDialog(platform, ctx
|
|
41
|
+
void showSupiDialog(platform, ctx).catch((error) => {
|
|
74
42
|
ctx.ui.notify(`Supipowers error: ${(error as Error).message}`, "error");
|
|
75
43
|
});
|
|
76
44
|
}
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { Platform } from "../platform/types.js";
|
|
4
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
5
|
+
import { applyModelOverride, createModelBridge, resolveModelForAction } from "../config/model-resolver.js";
|
|
6
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
7
|
+
import { notifyError, notifyInfo } from "../notifications/renderer.js";
|
|
8
|
+
import { cancelPlanTracking } from "../planning/approval-flow.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_UI_DESIGN_CONFIG,
|
|
11
|
+
loadUiDesignConfig,
|
|
12
|
+
saveUiDesignConfig,
|
|
13
|
+
type UiDesignConfig,
|
|
14
|
+
} from "../ui-design/config.js";
|
|
15
|
+
import { scanDesignContext } from "../ui-design/scanner.js";
|
|
16
|
+
import {
|
|
17
|
+
BackendUnavailableError,
|
|
18
|
+
getBackend,
|
|
19
|
+
} from "../ui-design/backend-adapter.js";
|
|
20
|
+
import type { UiDesignBackend } from "../ui-design/backend-adapter.js";
|
|
21
|
+
import {
|
|
22
|
+
cancelUiDesignTracking,
|
|
23
|
+
createSessionDir,
|
|
24
|
+
generateUiDesignSessionId,
|
|
25
|
+
startUiDesignTracking,
|
|
26
|
+
} from "../ui-design/session.js";
|
|
27
|
+
import type { Manifest, UiDesignBackendId, UiDesignSession } from "../ui-design/types.js";
|
|
28
|
+
import { buildUiDesignKickoffPrompt, renderContextScanSummary } from "../ui-design/prompt-builder.js";
|
|
29
|
+
import { setUiDesignPromptOptions } from "../ui-design/system-prompt.js";
|
|
30
|
+
import type { UiDesignSystemPromptOptions } from "../ui-design/system-prompt.js";
|
|
31
|
+
import { detectPencilMcp } from "../ui-design/backends/pencil-mcp.js";
|
|
32
|
+
import { selectPenFile } from "../ui-design/pen-selector.js";
|
|
33
|
+
import { moduleDir } from "../utils/paths.js";
|
|
34
|
+
import { resolveRepoRoot } from "../workspace/repo-root.js";
|
|
35
|
+
|
|
36
|
+
modelRegistry.register({
|
|
37
|
+
id: "ui-design",
|
|
38
|
+
category: "command",
|
|
39
|
+
label: "UI Design",
|
|
40
|
+
harnessRoleHint: "plan",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
interface UiDesignPromptAssets {
|
|
44
|
+
skillContent?: string;
|
|
45
|
+
subAgentTemplates?: { name: string; content: string }[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface UiDesignBackendOption {
|
|
49
|
+
id: UiDesignBackendId | "figma-mcp" | "paper-mcp";
|
|
50
|
+
label: string;
|
|
51
|
+
available: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface UiDesignCommandDependencies {
|
|
55
|
+
loadUiDesignConfig: typeof loadUiDesignConfig;
|
|
56
|
+
saveUiDesignConfig: typeof saveUiDesignConfig;
|
|
57
|
+
scanDesignContext: typeof scanDesignContext;
|
|
58
|
+
getBackend: typeof getBackend;
|
|
59
|
+
generateUiDesignSessionId: typeof generateUiDesignSessionId;
|
|
60
|
+
createSessionDir: typeof createSessionDir;
|
|
61
|
+
startUiDesignTracking: typeof startUiDesignTracking;
|
|
62
|
+
notifyInfo: typeof notifyInfo;
|
|
63
|
+
notifyError: typeof notifyError;
|
|
64
|
+
applyModelOverride: typeof applyModelOverride;
|
|
65
|
+
setUiDesignPromptOptions: typeof setUiDesignPromptOptions;
|
|
66
|
+
loadUiDesignPromptAssets: typeof loadUiDesignPromptAssets;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULT_DEPS: UiDesignCommandDependencies = {
|
|
70
|
+
loadUiDesignConfig,
|
|
71
|
+
saveUiDesignConfig,
|
|
72
|
+
scanDesignContext,
|
|
73
|
+
getBackend,
|
|
74
|
+
generateUiDesignSessionId,
|
|
75
|
+
createSessionDir,
|
|
76
|
+
startUiDesignTracking,
|
|
77
|
+
notifyInfo,
|
|
78
|
+
notifyError,
|
|
79
|
+
applyModelOverride,
|
|
80
|
+
setUiDesignPromptOptions,
|
|
81
|
+
loadUiDesignPromptAssets,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
function buildBackendOptions(platform: Platform): UiDesignBackendOption[] {
|
|
85
|
+
// Tool introspection is best-effort — if the harness throws, degrade pencil
|
|
86
|
+
// to unavailable rather than killing the whole wizard.
|
|
87
|
+
let activeTools: string[] = [];
|
|
88
|
+
try {
|
|
89
|
+
activeTools = platform.getActiveTools();
|
|
90
|
+
} catch {
|
|
91
|
+
activeTools = [];
|
|
92
|
+
}
|
|
93
|
+
const pencilAvailable = detectPencilMcp(activeTools);
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
id: "local-html",
|
|
97
|
+
label: "local-html — Local HTML mockups in browser companion (recommended for v1)",
|
|
98
|
+
available: true,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "pencil-mcp",
|
|
102
|
+
label: pencilAvailable
|
|
103
|
+
? "pencil-mcp — Drive a .pen file via the Pencil MCP server"
|
|
104
|
+
: "pencil-mcp — not connected (start Pencil MCP server and rerun)",
|
|
105
|
+
available: pencilAvailable,
|
|
106
|
+
},
|
|
107
|
+
{ id: "figma-mcp", label: "figma-mcp — coming soon", available: false },
|
|
108
|
+
{ id: "paper-mcp", label: "paper-mcp — coming soon", available: false },
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function findUiDesignSkillDir(): string | null {
|
|
113
|
+
const candidates = [
|
|
114
|
+
path.join(process.cwd(), "skills", "ui-design"),
|
|
115
|
+
path.join(moduleDir(import.meta.url), "..", "..", "skills", "ui-design"),
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
for (const candidate of candidates) {
|
|
119
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
120
|
+
return candidate;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function loadUiDesignPromptAssets(
|
|
128
|
+
options: { backend?: UiDesignBackendId } = {},
|
|
129
|
+
): UiDesignPromptAssets {
|
|
130
|
+
const skillDir = findUiDesignSkillDir();
|
|
131
|
+
if (!skillDir) return {};
|
|
132
|
+
|
|
133
|
+
let skillContent: string | undefined;
|
|
134
|
+
const skillPath = path.join(skillDir, "SKILL.md");
|
|
135
|
+
try {
|
|
136
|
+
if (fs.existsSync(skillPath) && fs.statSync(skillPath).isFile()) {
|
|
137
|
+
skillContent = fs.readFileSync(skillPath, "utf-8");
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
skillContent = undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Backend-specific sub-agent templates live in a subdir; fall back to the
|
|
144
|
+
// flat (HTML-assuming) template set when the subdir is missing.
|
|
145
|
+
const backendSubdir = options.backend === "pencil-mcp" ? "pencil" : null;
|
|
146
|
+
const candidates = backendSubdir
|
|
147
|
+
? [path.join(skillDir, "sub-agent-templates", backendSubdir), path.join(skillDir, "sub-agent-templates")]
|
|
148
|
+
: [path.join(skillDir, "sub-agent-templates")];
|
|
149
|
+
|
|
150
|
+
let subAgentTemplates: { name: string; content: string }[] | undefined;
|
|
151
|
+
for (const templatesDir of candidates) {
|
|
152
|
+
try {
|
|
153
|
+
if (!fs.existsSync(templatesDir) || !fs.statSync(templatesDir).isDirectory()) continue;
|
|
154
|
+
const templates = fs
|
|
155
|
+
.readdirSync(templatesDir)
|
|
156
|
+
.filter((entry) => entry.endsWith(".md"))
|
|
157
|
+
.sort()
|
|
158
|
+
.map((entry) => ({
|
|
159
|
+
name: path.basename(entry, ".md"),
|
|
160
|
+
content: fs.readFileSync(path.join(templatesDir, entry), "utf-8"),
|
|
161
|
+
}));
|
|
162
|
+
if (templates.length > 0) {
|
|
163
|
+
subAgentTemplates = templates;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
// Try next candidate
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { skillContent, subAgentTemplates };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function runSetupWizard(platform: Platform, ctx: any): Promise<UiDesignConfig | null> {
|
|
175
|
+
const options = buildBackendOptions(platform);
|
|
176
|
+
const choiceLabel = await ctx.ui.select(
|
|
177
|
+
"Design backend",
|
|
178
|
+
options.map((option) => option.label),
|
|
179
|
+
{
|
|
180
|
+
helpText: "Pick a design backend (local-html is always available; pencil-mcp requires the Pencil MCP server)",
|
|
181
|
+
},
|
|
182
|
+
);
|
|
183
|
+
if (!choiceLabel) return null;
|
|
184
|
+
|
|
185
|
+
const selected = options.find((option) => option.label === choiceLabel);
|
|
186
|
+
if (!selected) return null;
|
|
187
|
+
if (!selected.available) {
|
|
188
|
+
ctx.ui.notify(`Backend '${selected.id}' is not available right now.`, "warning");
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (selected.id !== "local-html" && selected.id !== "pencil-mcp") {
|
|
192
|
+
ctx.ui.notify(`Backend '${selected.id}' is not wired up yet.`, "warning");
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const cfg: UiDesignConfig = { ...DEFAULT_UI_DESIGN_CONFIG, backend: selected.id };
|
|
197
|
+
|
|
198
|
+
if (selected.id === "local-html") {
|
|
199
|
+
const portStr = await ctx.ui.input(
|
|
200
|
+
"HTTP port for companion (blank = auto)",
|
|
201
|
+
"",
|
|
202
|
+
{ helpText: "Leave blank to let the server pick a free port." },
|
|
203
|
+
);
|
|
204
|
+
if (portStr == null) return null;
|
|
205
|
+
const trimmed = String(portStr).trim();
|
|
206
|
+
if (trimmed) {
|
|
207
|
+
const port = parseInt(trimmed, 10);
|
|
208
|
+
if (!Number.isNaN(port) && port > 0) {
|
|
209
|
+
cfg.port = port;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return cfg;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function writeInitialManifest(sessionDir: string, manifest: Manifest): void {
|
|
218
|
+
fs.writeFileSync(path.join(sessionDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function handleUiDesign(
|
|
222
|
+
platform: Platform,
|
|
223
|
+
ctx: any,
|
|
224
|
+
args: string | undefined,
|
|
225
|
+
deps: UiDesignCommandDependencies = DEFAULT_DEPS,
|
|
226
|
+
): Promise<void> {
|
|
227
|
+
let modelCleanup: () => Promise<void> = async () => {};
|
|
228
|
+
let handoffStarted = false;
|
|
229
|
+
let startResult: { url: string; cleanup: () => Promise<void> } | null = null;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
// Resolve + apply the ui-design model override up front.
|
|
233
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
234
|
+
const bridge = createModelBridge(platform);
|
|
235
|
+
const resolved = resolveModelForAction("ui-design", modelRegistry, modelCfg, bridge);
|
|
236
|
+
modelCleanup = await deps.applyModelOverride(platform, ctx, "ui-design", resolved);
|
|
237
|
+
|
|
238
|
+
// ── Config ────────────────────────────────────────────────────────
|
|
239
|
+
let config = deps.loadUiDesignConfig(platform.paths, ctx.cwd);
|
|
240
|
+
if (!config) {
|
|
241
|
+
if (!ctx.hasUI) {
|
|
242
|
+
deps.notifyError(
|
|
243
|
+
ctx,
|
|
244
|
+
"ui-design first-run setup required",
|
|
245
|
+
"Run /supi:ui-design in interactive mode for first-time setup.",
|
|
246
|
+
);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
config = await runSetupWizard(platform, ctx);
|
|
250
|
+
if (!config) return;
|
|
251
|
+
deps.saveUiDesignConfig(platform.paths, ctx.cwd, config);
|
|
252
|
+
deps.notifyInfo(ctx, "ui-design config saved", `Backend: ${config.backend}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ── Resolve target directory + scan ───────────────────────────────
|
|
256
|
+
const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
|
|
257
|
+
const contextScan = await deps.scanDesignContext(
|
|
258
|
+
repoRoot,
|
|
259
|
+
config.componentsGlobs ? { components: { globs: config.componentsGlobs } } : {},
|
|
260
|
+
);
|
|
261
|
+
const contextScanSummary = renderContextScanSummary(contextScan);
|
|
262
|
+
|
|
263
|
+
// ── Session + backend ────────────────────────────────────────────
|
|
264
|
+
const sessionId = deps.generateUiDesignSessionId();
|
|
265
|
+
const sessionDir = deps.createSessionDir(platform.paths, ctx.cwd, sessionId);
|
|
266
|
+
|
|
267
|
+
// For pencil-mcp: pick the .pen file BEFORE startSession. The selector
|
|
268
|
+
// accepts the not-yet-created session dir as the parent for the "new"
|
|
269
|
+
// fallback (the directory itself was just created above for artifacts).
|
|
270
|
+
let penFilePath: string | undefined;
|
|
271
|
+
if (config.backend === "pencil-mcp") {
|
|
272
|
+
const selection = await selectPenFile({ ctx, repoRoot, sessionDir });
|
|
273
|
+
if (!selection) {
|
|
274
|
+
// User cancelled before any session artifacts were produced. Remove
|
|
275
|
+
// the empty session directory we just created so we don't leave
|
|
276
|
+
// dead `.omp/supipowers/ui-design/<id>` folders behind.
|
|
277
|
+
try {
|
|
278
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
279
|
+
} catch {
|
|
280
|
+
// non-fatal; user can clean up manually
|
|
281
|
+
}
|
|
282
|
+
deps.notifyInfo(ctx, "ui-design cancelled", "No .pen file selected");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
penFilePath = selection.penFilePath;
|
|
286
|
+
if (selection.kind === "existing") {
|
|
287
|
+
deps.notifyInfo(ctx, "ui-design pen file selected", penFilePath);
|
|
288
|
+
} else {
|
|
289
|
+
deps.notifyInfo(ctx, "ui-design will create a new .pen", penFilePath);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let backend: UiDesignBackend;
|
|
294
|
+
try {
|
|
295
|
+
backend = deps.getBackend(config.backend as UiDesignBackendId, {
|
|
296
|
+
getActiveTools: () => platform.getActiveTools(),
|
|
297
|
+
});
|
|
298
|
+
startResult = await backend.startSession({
|
|
299
|
+
sessionDir,
|
|
300
|
+
port: config.port,
|
|
301
|
+
penFilePath,
|
|
302
|
+
});
|
|
303
|
+
} catch (err) {
|
|
304
|
+
const message = err instanceof BackendUnavailableError ? err.message : (err as Error).message;
|
|
305
|
+
deps.notifyError(ctx, "ui-design backend unavailable", message);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Initial manifest ──────────────────────────────────────────────
|
|
310
|
+
const topic = args?.trim() || undefined;
|
|
311
|
+
const manifest: Manifest = {
|
|
312
|
+
id: sessionId,
|
|
313
|
+
topic,
|
|
314
|
+
backend: config.backend,
|
|
315
|
+
status: "in-progress",
|
|
316
|
+
acknowledged: false,
|
|
317
|
+
createdAt: new Date().toISOString(),
|
|
318
|
+
components: [],
|
|
319
|
+
sections: [],
|
|
320
|
+
page: "page.html",
|
|
321
|
+
...(penFilePath ? { penFilePath } : {}),
|
|
322
|
+
};
|
|
323
|
+
writeInitialManifest(sessionDir, manifest);
|
|
324
|
+
|
|
325
|
+
// ── Capture prompt options for the before_agent_start hook ───────
|
|
326
|
+
const promptAssets = deps.loadUiDesignPromptAssets({ backend: config.backend });
|
|
327
|
+
const promptOptions: UiDesignSystemPromptOptions = {
|
|
328
|
+
dotDirDisplay: platform.paths.dotDirDisplay,
|
|
329
|
+
sessionDir,
|
|
330
|
+
companionUrl: startResult.url,
|
|
331
|
+
backend: config.backend,
|
|
332
|
+
contextScanSummary,
|
|
333
|
+
topic,
|
|
334
|
+
skillContent: promptAssets.skillContent,
|
|
335
|
+
subAgentTemplates: promptAssets.subAgentTemplates,
|
|
336
|
+
...(penFilePath ? { penFilePath } : {}),
|
|
337
|
+
};
|
|
338
|
+
deps.setUiDesignPromptOptions(promptOptions);
|
|
339
|
+
|
|
340
|
+
// ── Track session (before sending steer) ──────────────────────────
|
|
341
|
+
const session: UiDesignSession = {
|
|
342
|
+
id: sessionId,
|
|
343
|
+
dir: sessionDir,
|
|
344
|
+
backend: config.backend,
|
|
345
|
+
companionUrl: startResult.url,
|
|
346
|
+
topic,
|
|
347
|
+
resolvedModel: resolved,
|
|
348
|
+
...(penFilePath ? { penFilePath } : {}),
|
|
349
|
+
};
|
|
350
|
+
deps.startUiDesignTracking(session, startResult.cleanup);
|
|
351
|
+
|
|
352
|
+
// ── Kickoff prompt ────────────────────────────────────────────────
|
|
353
|
+
const kickoff = buildUiDesignKickoffPrompt({
|
|
354
|
+
topic,
|
|
355
|
+
sessionDir,
|
|
356
|
+
companionUrl: startResult.url,
|
|
357
|
+
contextScanSummary,
|
|
358
|
+
...(penFilePath ? { penFilePath } : {}),
|
|
359
|
+
});
|
|
360
|
+
platform.sendUserMessage(kickoff);
|
|
361
|
+
cancelPlanTracking();
|
|
362
|
+
handoffStarted = true;
|
|
363
|
+
|
|
364
|
+
deps.notifyInfo(
|
|
365
|
+
ctx,
|
|
366
|
+
"ui-design session started",
|
|
367
|
+
`${startResult.url} · session ${sessionId}`,
|
|
368
|
+
);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
deps.notifyError(ctx, "ui-design failed", (err as Error).message);
|
|
371
|
+
} finally {
|
|
372
|
+
if (!handoffStarted) {
|
|
373
|
+
if (startResult) {
|
|
374
|
+
try {
|
|
375
|
+
await startResult.cleanup();
|
|
376
|
+
} catch {
|
|
377
|
+
// preserve the original startup error
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
cancelUiDesignTracking("startup_failed");
|
|
381
|
+
deps.setUiDesignPromptOptions(null);
|
|
382
|
+
await modelCleanup();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function registerUiDesignCommand(platform: Platform): void {
|
|
388
|
+
platform.registerCommand("supi:ui-design", {
|
|
389
|
+
description: "Drive the Design Director pipeline: scan context, decompose, build + critique HTML mockups",
|
|
390
|
+
async handler(args: string | undefined, ctx: any) {
|
|
391
|
+
await handleUiDesign(platform, ctx, args, DEFAULT_DEPS);
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
}
|