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.
- 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 +135 -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 +268 -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/config.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1
2
|
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
2
3
|
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
3
|
-
import { formatConfigErrors, inspectConfig, updateConfig } from "../config/loader.js";
|
|
4
|
+
import { formatConfigErrors, inspectConfig, inspectConfigAtScope, updateConfig } from "../config/loader.js";
|
|
4
5
|
import type { InspectionLoadResult } from "../config/schema.js";
|
|
5
|
-
import
|
|
6
|
+
import { validateQualityGates } from "../config/schema.js";
|
|
6
7
|
import { createWorkflowProgress } from "../platform/progress.js";
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
+
formatGateProposal,
|
|
9
10
|
setupGates,
|
|
10
11
|
summarizeEnabledGates,
|
|
11
12
|
type GateSetupMode,
|
|
12
13
|
type SetupGatesProgressEvent,
|
|
13
14
|
} from "../quality/setup.js";
|
|
15
|
+
import type { ConfigScope, QualityGatesConfig, SetupProposal, SupipowersConfig } from "../types.js";
|
|
16
|
+
import { resolvePackageManager } from "../workspace/package-manager.js";
|
|
17
|
+
import { resolveRepoRoot } from "../workspace/repo-root.js";
|
|
18
|
+
import { discoverWorkspaceTargets } from "../workspace/targets.js";
|
|
14
19
|
|
|
15
20
|
const FRAMEWORK_OPTIONS = [
|
|
16
21
|
{ value: "", label: "not set — auto-detect on first /supi:qa run" },
|
|
@@ -38,18 +43,29 @@ export interface SettingDef {
|
|
|
38
43
|
set: (cwd: string, value: unknown) => Promise<string | null>;
|
|
39
44
|
}
|
|
40
45
|
|
|
46
|
+
export interface ConfigScopeSelection {
|
|
47
|
+
scope: ConfigScope;
|
|
48
|
+
repoRoot: string;
|
|
49
|
+
isMonorepo: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ConfigScopeView {
|
|
53
|
+
selection: ConfigScopeSelection;
|
|
54
|
+
inspection: InspectionLoadResult;
|
|
55
|
+
globalInspection: InspectionLoadResult;
|
|
56
|
+
rootInspection: InspectionLoadResult;
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
export interface ConfigCommandDependencies {
|
|
42
60
|
inspectConfig: typeof inspectConfig;
|
|
43
61
|
updateConfig: typeof updateConfig;
|
|
44
62
|
setupGates: typeof setupGates;
|
|
45
|
-
interactivelySaveGateSetup: typeof interactivelySaveGateSetup;
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
const CONFIG_COMMAND_DEPENDENCIES: ConfigCommandDependencies = {
|
|
49
66
|
inspectConfig,
|
|
50
67
|
updateConfig,
|
|
51
68
|
setupGates,
|
|
52
|
-
interactivelySaveGateSetup,
|
|
53
69
|
};
|
|
54
70
|
|
|
55
71
|
function currentConfig(inspection: InspectionLoadResult): SupipowersConfig {
|
|
@@ -64,6 +80,159 @@ function describeInspection(inspection: InspectionLoadResult, config: Supipowers
|
|
|
64
80
|
return summarizeEnabledGates(config.quality.gates);
|
|
65
81
|
}
|
|
66
82
|
|
|
83
|
+
function getConfigResolutionOptions(selection: ConfigScopeSelection): {
|
|
84
|
+
repoRoot: string;
|
|
85
|
+
} {
|
|
86
|
+
return { repoRoot: selection.repoRoot };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getConfigMutationOptions(selection: ConfigScopeSelection): {
|
|
90
|
+
scope: ConfigScope;
|
|
91
|
+
repoRoot: string;
|
|
92
|
+
} {
|
|
93
|
+
return {
|
|
94
|
+
scope: selection.scope,
|
|
95
|
+
...getConfigResolutionOptions(selection),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getSelectionCwd(selection: ConfigScopeSelection): string {
|
|
100
|
+
return selection.repoRoot;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function configPathForSelection(selection: ConfigScopeSelection): string {
|
|
104
|
+
switch (selection.scope) {
|
|
105
|
+
case "global":
|
|
106
|
+
return "~/.omp/supipowers/config.json";
|
|
107
|
+
case "root":
|
|
108
|
+
return ".omp/supipowers/config.json";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function repositoryScopeLabel(selection: ConfigScopeSelection): string {
|
|
113
|
+
return selection.isMonorepo ? "monorepo repository" : "repository";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function selectionSummary(selection: ConfigScopeSelection): string {
|
|
117
|
+
return `${selection.scope === "global" ? "global" : repositoryScopeLabel(selection)} — ${configPathForSelection(selection)}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getNestedValue(value: unknown, key: string): unknown {
|
|
121
|
+
return key
|
|
122
|
+
.split(".")
|
|
123
|
+
.reduce<unknown>((current, segment) => {
|
|
124
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return (current as Record<string, unknown>)[segment];
|
|
128
|
+
}, value);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getSelectedConfig(view: ConfigScopeView): SupipowersConfig {
|
|
132
|
+
return currentConfig(view.inspection);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function describeSettingProvenance(view: ConfigScopeView, key: string): string {
|
|
136
|
+
const defaultValue = getNestedValue(DEFAULT_CONFIG, key);
|
|
137
|
+
const globalValue = getNestedValue(currentConfig(view.globalInspection), key);
|
|
138
|
+
const rootValue = getNestedValue(currentConfig(view.rootInspection), key);
|
|
139
|
+
|
|
140
|
+
switch (view.selection.scope) {
|
|
141
|
+
case "global":
|
|
142
|
+
return isDeepStrictEqual(globalValue, defaultValue) ? "default" : "overridden in global";
|
|
143
|
+
case "root":
|
|
144
|
+
if (!isDeepStrictEqual(rootValue, globalValue)) {
|
|
145
|
+
return "overridden in repository";
|
|
146
|
+
}
|
|
147
|
+
if (!isDeepStrictEqual(globalValue, defaultValue)) {
|
|
148
|
+
return "inherited from global";
|
|
149
|
+
}
|
|
150
|
+
return "default";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function describeSettingValue(display: string, view: ConfigScopeView, key: string): string {
|
|
155
|
+
return `${display} — ${describeSettingProvenance(view, key)}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseRevisedQualityGates(raw: string): QualityGatesConfig {
|
|
159
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
160
|
+
const validation = validateQualityGates(parsed);
|
|
161
|
+
if (!validation.valid) {
|
|
162
|
+
throw new Error(validation.errors.join("\n"));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return parsed as QualityGatesConfig;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildQualityGateSetupHelpText(
|
|
169
|
+
proposal: SetupProposal,
|
|
170
|
+
scopeSelection: ConfigScopeSelection,
|
|
171
|
+
mode: GateSetupMode,
|
|
172
|
+
): string {
|
|
173
|
+
const intro = mode === "ai-assisted"
|
|
174
|
+
? "AI analyzed your project and suggested these gates."
|
|
175
|
+
: "Detected project checks and suggested these gates.";
|
|
176
|
+
|
|
177
|
+
return [
|
|
178
|
+
intro,
|
|
179
|
+
"",
|
|
180
|
+
formatGateProposal(proposal),
|
|
181
|
+
"",
|
|
182
|
+
`Will write to ${configPathForSelection(scopeSelection)}`,
|
|
183
|
+
].join("\n");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function saveQualityGateProposal(
|
|
187
|
+
ctx: PlatformContext,
|
|
188
|
+
paths: Platform["paths"],
|
|
189
|
+
deps: ConfigCommandDependencies,
|
|
190
|
+
selection: ConfigScopeSelection,
|
|
191
|
+
proposal: SetupProposal,
|
|
192
|
+
mode: GateSetupMode,
|
|
193
|
+
): Promise<"saved" | "cancelled"> {
|
|
194
|
+
let nextProposal = proposal;
|
|
195
|
+
const selectionCwd = getSelectionCwd(selection);
|
|
196
|
+
const mutationOptions = getConfigMutationOptions(selection);
|
|
197
|
+
|
|
198
|
+
while (true) {
|
|
199
|
+
const choice = await ctx.ui.select(
|
|
200
|
+
mode === "ai-assisted" ? "AI-assisted quality gate proposal" : "Quality gate setup",
|
|
201
|
+
["Accept", "Revise", "Cancel"],
|
|
202
|
+
{
|
|
203
|
+
helpText: buildQualityGateSetupHelpText(nextProposal, selection, mode),
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
if (!choice || choice === "Cancel") {
|
|
208
|
+
return "cancelled";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (choice === "Revise") {
|
|
212
|
+
const revised = await ctx.ui.input(
|
|
213
|
+
"Edit quality.gates JSON",
|
|
214
|
+
{ value: JSON.stringify(nextProposal.gates, null, 2) },
|
|
215
|
+
);
|
|
216
|
+
if (!revised) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
nextProposal = {
|
|
222
|
+
...nextProposal,
|
|
223
|
+
gates: parseRevisedQualityGates(revised),
|
|
224
|
+
};
|
|
225
|
+
} catch (error) {
|
|
226
|
+
ctx.ui.notify((error as Error).message, "error");
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
deps.updateConfig(paths, selectionCwd, { quality: { gates: nextProposal.gates } }, mutationOptions);
|
|
232
|
+
return "saved";
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
67
236
|
function createQualityGateSetupProgress(ctx: PlatformContext, mode: GateSetupMode) {
|
|
68
237
|
if (mode !== "ai-assisted") {
|
|
69
238
|
return null;
|
|
@@ -109,40 +278,51 @@ function createQualityGateSetupProgress(ctx: PlatformContext, mode: GateSetupMod
|
|
|
109
278
|
};
|
|
110
279
|
}
|
|
111
280
|
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
281
|
+
export function buildConfigScopeView(
|
|
282
|
+
platform: Platform,
|
|
283
|
+
cwd: string,
|
|
284
|
+
selection: ConfigScopeSelection,
|
|
285
|
+
): ConfigScopeView {
|
|
286
|
+
const resolutionOptions = getConfigResolutionOptions(selection);
|
|
287
|
+
const globalInspection = inspectConfigAtScope(platform.paths, cwd, "global", resolutionOptions);
|
|
288
|
+
const rootInspection = inspectConfigAtScope(platform.paths, cwd, "root", resolutionOptions);
|
|
289
|
+
const inspection = selection.scope === "global" ? globalInspection : rootInspection;
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
selection,
|
|
293
|
+
inspection,
|
|
294
|
+
globalInspection,
|
|
295
|
+
rootInspection,
|
|
296
|
+
};
|
|
121
297
|
}
|
|
122
298
|
|
|
123
299
|
export function buildSettings(
|
|
124
300
|
platform: Platform,
|
|
125
301
|
ctx: PlatformContext,
|
|
126
|
-
|
|
302
|
+
view: ConfigScopeView,
|
|
127
303
|
deps: ConfigCommandDependencies = CONFIG_COMMAND_DEPENDENCIES,
|
|
128
304
|
): SettingDef[] {
|
|
129
305
|
const { paths } = platform;
|
|
130
|
-
const config =
|
|
306
|
+
const config = getSelectedConfig(view);
|
|
307
|
+
const selectionCwd = getSelectionCwd(view.selection);
|
|
308
|
+
const resolutionOptions = getConfigResolutionOptions(view.selection);
|
|
309
|
+
const mutationOptions = getConfigMutationOptions(view.selection);
|
|
131
310
|
|
|
132
311
|
return [
|
|
133
312
|
{
|
|
134
313
|
label: "Setup quality gates",
|
|
135
314
|
key: "quality.gates",
|
|
136
|
-
helpText:
|
|
315
|
+
helpText: `Inspect the project and configure review gates. Changes write to ${configPathForSelection(view.selection)}.`,
|
|
137
316
|
type: "select",
|
|
138
317
|
options: ["Run deterministic setup", "Run AI-assisted setup"],
|
|
139
|
-
get: () => describeInspection(inspection, config),
|
|
140
|
-
set: async (
|
|
318
|
+
get: () => describeSettingValue(describeInspection(view.inspection, config), view, "quality.gates"),
|
|
319
|
+
set: async (_cwd, value) => {
|
|
141
320
|
const mode: GateSetupMode = String(value).includes("AI-assisted") ? "ai-assisted" : "deterministic";
|
|
142
321
|
const progress = createQualityGateSetupProgress(ctx, mode);
|
|
143
322
|
|
|
144
323
|
try {
|
|
145
|
-
const
|
|
324
|
+
const selectedInspection = deps.inspectConfig(paths, selectionCwd, resolutionOptions);
|
|
325
|
+
const result = await deps.setupGates(platform, selectionCwd, selectedInspection, {
|
|
146
326
|
mode,
|
|
147
327
|
onProgress: (event) => progress?.handle(event),
|
|
148
328
|
});
|
|
@@ -157,12 +337,13 @@ export function buildSettings(
|
|
|
157
337
|
}
|
|
158
338
|
|
|
159
339
|
progress?.dispose();
|
|
160
|
-
const saveResult = await
|
|
340
|
+
const saveResult = await saveQualityGateProposal(
|
|
161
341
|
ctx,
|
|
162
342
|
paths,
|
|
163
|
-
|
|
343
|
+
deps,
|
|
344
|
+
view.selection,
|
|
164
345
|
result.proposal,
|
|
165
|
-
|
|
346
|
+
mode,
|
|
166
347
|
);
|
|
167
348
|
return saveResult === "saved" ? "saved" : null;
|
|
168
349
|
} catch (error) {
|
|
@@ -176,52 +357,57 @@ export function buildSettings(
|
|
|
176
357
|
{
|
|
177
358
|
label: "LSP setup guide",
|
|
178
359
|
key: "lsp.setupGuide",
|
|
179
|
-
helpText:
|
|
360
|
+
helpText: `Show LSP setup tips when no language server is active. Changes write to ${configPathForSelection(view.selection)}.`,
|
|
180
361
|
type: "toggle",
|
|
181
|
-
get: () => (config.lsp.setupGuide ? "on" : "off"),
|
|
182
|
-
set: async (
|
|
183
|
-
deps.updateConfig(paths,
|
|
362
|
+
get: () => describeSettingValue(config.lsp.setupGuide ? "on" : "off", view, "lsp.setupGuide"),
|
|
363
|
+
set: async (_cwd, value) => {
|
|
364
|
+
deps.updateConfig(paths, selectionCwd, { lsp: { setupGuide: value === "on" } }, mutationOptions);
|
|
184
365
|
return String(value);
|
|
185
366
|
},
|
|
186
367
|
},
|
|
187
368
|
{
|
|
188
369
|
label: "QA framework",
|
|
189
370
|
key: "qa.framework",
|
|
190
|
-
helpText:
|
|
371
|
+
helpText: `Test runner used by /supi:qa. Changes write to ${configPathForSelection(view.selection)}.`,
|
|
191
372
|
type: "select",
|
|
192
373
|
options: FRAMEWORK_OPTIONS.map((framework) => framework.label),
|
|
193
|
-
get: () => config.qa.framework ?? "not set",
|
|
194
|
-
set: async (
|
|
374
|
+
get: () => describeSettingValue(config.qa.framework ?? "not set", view, "qa.framework"),
|
|
375
|
+
set: async (_cwd, value) => {
|
|
195
376
|
const chosen = FRAMEWORK_OPTIONS.find((framework) => framework.label === value);
|
|
196
377
|
if (!chosen) {
|
|
197
378
|
return null;
|
|
198
379
|
}
|
|
199
380
|
|
|
200
|
-
deps.updateConfig(paths,
|
|
381
|
+
deps.updateConfig(paths, selectionCwd, { qa: { framework: chosen.value || null } }, mutationOptions);
|
|
201
382
|
return chosen.label.split(" — ")[0];
|
|
202
383
|
},
|
|
203
384
|
},
|
|
204
385
|
{
|
|
205
386
|
label: "Release channels",
|
|
206
387
|
key: "release.channels",
|
|
207
|
-
helpText:
|
|
388
|
+
helpText: `Where /supi:release publishes your project. Changes write to ${configPathForSelection(view.selection)}.`,
|
|
208
389
|
type: "select",
|
|
209
390
|
options: CHANNEL_OPTIONS.map((channel) => channel.label),
|
|
210
|
-
get: () =>
|
|
391
|
+
get: () => describeSettingValue(
|
|
211
392
|
config.release.channels.length > 0 ? config.release.channels.join(", ") : "not set",
|
|
212
|
-
|
|
393
|
+
view,
|
|
394
|
+
"release.channels",
|
|
395
|
+
),
|
|
396
|
+
set: async (_cwd, value) => {
|
|
213
397
|
const chosen = CHANNEL_OPTIONS.find((channel) => channel.label === value);
|
|
214
398
|
if (!chosen) {
|
|
215
399
|
return null;
|
|
216
400
|
}
|
|
217
401
|
|
|
218
|
-
deps.updateConfig(paths,
|
|
402
|
+
deps.updateConfig(paths, selectionCwd, { release: { channels: chosen.value } }, mutationOptions);
|
|
219
403
|
return chosen.label.split(" — ")[0];
|
|
220
404
|
},
|
|
221
405
|
},
|
|
222
406
|
];
|
|
223
407
|
}
|
|
224
408
|
|
|
409
|
+
|
|
410
|
+
|
|
225
411
|
export async function runConfigMenu(
|
|
226
412
|
platform: Platform,
|
|
227
413
|
ctx: PlatformContext,
|
|
@@ -232,16 +418,28 @@ export async function runConfigMenu(
|
|
|
232
418
|
return;
|
|
233
419
|
}
|
|
234
420
|
|
|
421
|
+
const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
|
|
422
|
+
const isMonorepo = discoverWorkspaceTargets(repoRoot, resolvePackageManager(repoRoot).id)
|
|
423
|
+
.some((target) => target.kind === "workspace");
|
|
424
|
+
let selection: ConfigScopeSelection = {
|
|
425
|
+
scope: "root",
|
|
426
|
+
repoRoot,
|
|
427
|
+
isMonorepo,
|
|
428
|
+
};
|
|
429
|
+
|
|
235
430
|
while (true) {
|
|
236
|
-
const
|
|
237
|
-
const settings = buildSettings(platform, ctx,
|
|
238
|
-
const options = settings.map((setting) => `${setting.label}: ${setting.get()}`);
|
|
239
|
-
options.push("Done");
|
|
431
|
+
const view = buildConfigScopeView(platform, ctx.cwd, selection);
|
|
432
|
+
const settings = buildSettings(platform, ctx, view, deps);
|
|
433
|
+
const options = [...settings.map((setting) => `${setting.label}: ${setting.get()}`), "Done"];
|
|
240
434
|
|
|
241
435
|
const choice = await ctx.ui.select(
|
|
242
436
|
"Supipowers Settings",
|
|
243
437
|
options,
|
|
244
|
-
{
|
|
438
|
+
{
|
|
439
|
+
helpText: selection.scope === "root" && selection.isMonorepo
|
|
440
|
+
? `Editing ${selectionSummary(selection)}. Changes apply to every workspace in this monorepo. Esc to close.`
|
|
441
|
+
: `Editing ${selectionSummary(selection)}. Changes write to ${configPathForSelection(selection)}. Esc to close.`,
|
|
442
|
+
},
|
|
245
443
|
);
|
|
246
444
|
|
|
247
445
|
if (choice === undefined || choice === null || choice === "Done") {
|
|
@@ -257,7 +455,7 @@ export async function runConfigMenu(
|
|
|
257
455
|
try {
|
|
258
456
|
if (setting.type === "select" && setting.options) {
|
|
259
457
|
const currentValue = setting.get();
|
|
260
|
-
const currentIndex = setting.options.findIndex((option) =>
|
|
458
|
+
const currentIndex = setting.options.findIndex((option) => currentValue.startsWith(option.split(" — ")[0]));
|
|
261
459
|
const value = await ctx.ui.select(
|
|
262
460
|
setting.label,
|
|
263
461
|
setting.options,
|
|
@@ -268,15 +466,15 @@ export async function runConfigMenu(
|
|
|
268
466
|
);
|
|
269
467
|
|
|
270
468
|
if (value !== undefined && value !== null) {
|
|
271
|
-
const display = await setting.set(
|
|
469
|
+
const display = await setting.set(getSelectionCwd(selection), value);
|
|
272
470
|
if (display) {
|
|
273
471
|
ctx.ui.notify(`${setting.label} → ${display}`, "info");
|
|
274
472
|
}
|
|
275
473
|
}
|
|
276
474
|
} else if (setting.type === "toggle") {
|
|
277
475
|
const current = setting.get();
|
|
278
|
-
const newValue = current
|
|
279
|
-
const display = await setting.set(
|
|
476
|
+
const newValue = current.startsWith("on") ? "off" : "on";
|
|
477
|
+
const display = await setting.set(getSelectionCwd(selection), newValue);
|
|
280
478
|
if (display) {
|
|
281
479
|
ctx.ui.notify(`${setting.label} → ${display}`, "info");
|
|
282
480
|
}
|
package/src/commands/context.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
5
|
+
import { systemPromptText } from "../platform/system-prompt.js";
|
|
5
6
|
import {
|
|
6
7
|
parseSystemPrompt,
|
|
7
8
|
buildBreakdownItems,
|
|
@@ -9,6 +10,14 @@ import {
|
|
|
9
10
|
formatToolsReport,
|
|
10
11
|
} from "../context/analyzer.js";
|
|
11
12
|
import type { ContextUsage } from "../context/analyzer.js";
|
|
13
|
+
import {
|
|
14
|
+
buildSavingsLinesFromStore,
|
|
15
|
+
formatSavingsReportFromStore,
|
|
16
|
+
getFirstRunNotice,
|
|
17
|
+
} from "../context/savings.js";
|
|
18
|
+
import { getMetricsStore, getSessionId } from "../context-mode/hooks.js";
|
|
19
|
+
import { getProjectStateDir, getProjectStatePath } from "../workspace/state-paths.js";
|
|
20
|
+
import { openInEditor } from "../utils/editor.js";
|
|
12
21
|
|
|
13
22
|
const REPORT_FILE = ".omp-context-breakdown.md";
|
|
14
23
|
|
|
@@ -33,13 +42,26 @@ export function handleContext(platform: Platform, ctx: PlatformContext): void {
|
|
|
33
42
|
|
|
34
43
|
let systemPrompt = "";
|
|
35
44
|
try {
|
|
36
|
-
systemPrompt = (ctx as any).getSystemPrompt?.()
|
|
45
|
+
systemPrompt = systemPromptText((ctx as any).getSystemPrompt?.());
|
|
37
46
|
} catch {
|
|
38
47
|
// getSystemPrompt not available — continue without
|
|
39
48
|
}
|
|
40
49
|
|
|
41
|
-
//
|
|
42
|
-
|
|
50
|
+
// L1 metrics surfaces
|
|
51
|
+
const store = getMetricsStore();
|
|
52
|
+
const sessionId = getSessionId();
|
|
53
|
+
const projectSlug = basename(getProjectStateDir(platform.paths, ctx.cwd));
|
|
54
|
+
const dbAbsPath = getProjectStatePath(platform.paths, ctx.cwd, "sessions", "metrics.db");
|
|
55
|
+
const sessionStartedAtMs = (() => {
|
|
56
|
+
try {
|
|
57
|
+
return store?.getSessionMeta(sessionId)?.started_at ?? null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
// Bail only when *nothing* is available: no usage, no system prompt, no metrics store.
|
|
64
|
+
if (!usage && !systemPrompt && !store) {
|
|
43
65
|
ctx.ui.notify("Context data unavailable", "warning");
|
|
44
66
|
return;
|
|
45
67
|
}
|
|
@@ -47,7 +69,25 @@ export function handleContext(platform: Platform, ctx: PlatformContext): void {
|
|
|
47
69
|
// Parse system prompt (may be empty)
|
|
48
70
|
const sections = systemPrompt ? parseSystemPrompt(systemPrompt) : [];
|
|
49
71
|
const activeTools = platform.getActiveTools();
|
|
50
|
-
const
|
|
72
|
+
const baseItems = buildBreakdownItems(usage, sections, activeTools, !systemPrompt);
|
|
73
|
+
|
|
74
|
+
// Build the savings panel + first-run notice + footer
|
|
75
|
+
const noticeLine = getFirstRunNotice(store, projectSlug, dbAbsPath);
|
|
76
|
+
const savingsLines = buildSavingsLinesFromStore(
|
|
77
|
+
store,
|
|
78
|
+
sessionId,
|
|
79
|
+
sessionStartedAtMs,
|
|
80
|
+
dbAbsPath,
|
|
81
|
+
);
|
|
82
|
+
const footerLine = `Metrics DB: ${dbAbsPath}`;
|
|
83
|
+
const drillableSavings = new Set(savingsLines);
|
|
84
|
+
|
|
85
|
+
const items: typeof baseItems = [];
|
|
86
|
+
if (noticeLine) items.push({ line: noticeLine });
|
|
87
|
+
for (const line of savingsLines) items.push({ line });
|
|
88
|
+
items.push({ line: footerLine });
|
|
89
|
+
items.push(...baseItems);
|
|
90
|
+
|
|
51
91
|
const lines = items.map(i => i.line);
|
|
52
92
|
|
|
53
93
|
while (true) {
|
|
@@ -56,14 +96,17 @@ export function handleContext(platform: Platform, ctx: PlatformContext): void {
|
|
|
56
96
|
});
|
|
57
97
|
if (!choice || choice.trim() === "Close") break;
|
|
58
98
|
|
|
59
|
-
const item = items.find(i => i.line === choice);
|
|
60
|
-
if (!item || (!item.section && !item.toolNames)) continue;
|
|
61
|
-
|
|
62
99
|
let report: string | null = null;
|
|
63
|
-
if (
|
|
64
|
-
report =
|
|
65
|
-
} else
|
|
66
|
-
|
|
100
|
+
if (drillableSavings.has(choice)) {
|
|
101
|
+
report = formatSavingsReportFromStore(store, sessionId, sessionStartedAtMs);
|
|
102
|
+
} else {
|
|
103
|
+
const item = items.find(i => i.line === choice);
|
|
104
|
+
if (!item || (!item.section && !item.toolNames)) continue;
|
|
105
|
+
if (item.section) {
|
|
106
|
+
report = formatSectionReport(item.section);
|
|
107
|
+
} else if (item.toolNames) {
|
|
108
|
+
report = formatToolsReport(item.toolNames);
|
|
109
|
+
}
|
|
67
110
|
}
|
|
68
111
|
|
|
69
112
|
if (report) {
|
|
@@ -98,20 +141,4 @@ function writeReport(cwd: string, content: string): string {
|
|
|
98
141
|
writeFileSync(fallback, content, "utf-8");
|
|
99
142
|
return fallback;
|
|
100
143
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Open a file in the user's preferred editor */
|
|
104
|
-
async function openInEditor(platform: Platform, filePath: string): Promise<void> {
|
|
105
|
-
const editor = process.env.VISUAL || process.env.EDITOR;
|
|
106
|
-
try {
|
|
107
|
-
if (editor) {
|
|
108
|
-
await platform.exec(editor, [filePath]);
|
|
109
|
-
} else {
|
|
110
|
-
const cmd = process.platform === "darwin" ? "open"
|
|
111
|
-
: process.platform === "win32" ? "start" : "xdg-open";
|
|
112
|
-
await platform.exec(cmd, [filePath]);
|
|
113
|
-
}
|
|
114
|
-
} catch {
|
|
115
|
-
// Editor open failed — non-fatal, file was still written
|
|
116
|
-
}
|
|
117
144
|
}
|