supipowers 1.2.6 → 1.5.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 +118 -56
- package/bin/install.ts +48 -128
- package/package.json +11 -3
- package/skills/code-review/SKILL.md +137 -40
- package/skills/context-mode/SKILL.md +67 -56
- package/skills/creating-supi-agents/SKILL.md +204 -0
- package/skills/debugging/SKILL.md +86 -40
- package/skills/fix-pr/SKILL.md +96 -65
- package/skills/planning/SKILL.md +103 -46
- package/skills/qa-strategy/SKILL.md +68 -46
- package/skills/receiving-code-review/SKILL.md +60 -53
- package/skills/release/SKILL.md +111 -39
- package/skills/tdd/SKILL.md +118 -67
- package/skills/verification/SKILL.md +71 -37
- package/src/bootstrap.ts +27 -5
- package/src/commands/agents.ts +249 -0
- package/src/commands/ai-review.ts +1113 -0
- package/src/commands/config.ts +224 -95
- package/src/commands/doctor.ts +19 -13
- package/src/commands/fix-pr.ts +8 -11
- package/src/commands/generate.ts +200 -0
- package/src/commands/model-picker.ts +5 -15
- package/src/commands/model.ts +4 -5
- package/src/commands/optimize-context.ts +202 -0
- package/src/commands/plan.ts +148 -92
- package/src/commands/qa.ts +14 -23
- package/src/commands/release.ts +504 -275
- package/src/commands/review.ts +643 -86
- package/src/commands/status.ts +44 -17
- package/src/commands/supi.ts +69 -41
- package/src/commands/update.ts +57 -2
- package/src/config/defaults.ts +6 -39
- package/src/config/loader.ts +388 -40
- package/src/config/model-resolver.ts +26 -22
- package/src/config/schema.ts +113 -48
- package/src/context/analyzer.ts +61 -2
- package/src/context/optimizer.ts +199 -0
- package/src/context-mode/compressor.ts +14 -11
- package/src/context-mode/detector.ts +16 -54
- package/src/context-mode/event-extractor.ts +45 -16
- package/src/context-mode/event-store.ts +225 -16
- package/src/context-mode/hooks.ts +195 -22
- package/src/context-mode/knowledge/chunker.ts +235 -0
- package/src/context-mode/knowledge/store.ts +187 -0
- package/src/context-mode/routing.ts +12 -23
- package/src/context-mode/sandbox/executor.ts +183 -0
- package/src/context-mode/sandbox/runners.ts +40 -0
- package/src/context-mode/snapshot-builder.ts +243 -7
- package/src/context-mode/tools.ts +440 -0
- package/src/context-mode/web/fetcher.ts +117 -0
- package/src/context-mode/web/html-to-md.ts +293 -0
- package/src/debug/logger.ts +107 -0
- package/src/deps/registry.ts +0 -20
- package/src/docs/drift.ts +454 -0
- package/src/fix-pr/fetch-comments.ts +66 -0
- package/src/git/commit-msg.ts +2 -1
- package/src/git/commit.ts +123 -141
- package/src/git/conventions.ts +2 -2
- package/src/git/status.ts +4 -1
- package/src/lsp/bridge.ts +138 -12
- package/src/planning/approval-flow.ts +125 -19
- package/src/planning/plan-writer-prompt.ts +4 -11
- package/src/planning/planning-ask-tool.ts +81 -0
- package/src/planning/prompt-builder.ts +9 -169
- package/src/planning/system-prompt.ts +290 -0
- package/src/platform/omp.ts +50 -4
- package/src/platform/progress.ts +182 -0
- package/src/platform/test-utils.ts +4 -1
- package/src/platform/tui-colors.ts +30 -0
- package/src/platform/types.ts +1 -0
- package/src/qa/detect-app-type.ts +102 -0
- package/src/qa/discover-routes.ts +353 -0
- package/src/quality/ai-session.ts +96 -0
- package/src/quality/ai-setup.ts +86 -0
- package/src/quality/gates/ai-review.ts +129 -0
- package/src/quality/gates/build.ts +8 -0
- package/src/quality/gates/command.ts +150 -0
- package/src/quality/gates/format.ts +28 -0
- package/src/quality/gates/lint.ts +22 -0
- package/src/quality/gates/lsp-diagnostics.ts +84 -0
- package/src/quality/gates/test-suite.ts +8 -0
- package/src/quality/gates/typecheck.ts +22 -0
- package/src/quality/registry.ts +25 -0
- package/src/quality/review-gates.ts +33 -0
- package/src/quality/runner.ts +268 -0
- package/src/quality/schemas.ts +48 -0
- package/src/quality/setup.ts +227 -0
- package/src/release/changelog.ts +7 -3
- package/src/release/channels/custom.ts +43 -0
- package/src/release/channels/gitea.ts +35 -0
- package/src/release/channels/github.ts +35 -0
- package/src/release/channels/gitlab.ts +35 -0
- package/src/release/channels/registry.ts +52 -0
- package/src/release/channels/types.ts +27 -0
- package/src/release/detector.ts +10 -63
- package/src/release/executor.ts +61 -51
- package/src/release/prompt.ts +38 -38
- package/src/release/version.ts +129 -10
- package/src/review/agent-loader.ts +331 -0
- package/src/review/consolidator.ts +180 -0
- package/src/review/default-agents/correctness.md +72 -0
- package/src/review/default-agents/maintainability.md +64 -0
- package/src/review/default-agents/security.md +67 -0
- package/src/review/fixer.ts +219 -0
- package/src/review/multi-agent-runner.ts +135 -0
- package/src/review/output.ts +147 -0
- package/src/review/prompts/agent-review-wrapper.md +36 -0
- package/src/review/prompts/fix-findings.md +32 -0
- package/src/review/prompts/fix-output-schema.md +18 -0
- package/src/review/prompts/invalid-output-retry.md +22 -0
- package/src/review/prompts/output-instructions.md +14 -0
- package/src/review/prompts/review-output-schema.md +38 -0
- package/src/review/prompts/single-review.md +53 -0
- package/src/review/prompts/validation-review.md +30 -0
- package/src/review/runner.ts +128 -0
- package/src/review/scope.ts +353 -0
- package/src/review/template.ts +15 -0
- package/src/review/types.ts +296 -0
- package/src/review/validator.ts +160 -0
- package/src/storage/plans.ts +5 -3
- package/src/storage/reports.ts +50 -7
- package/src/storage/review-sessions.ts +117 -0
- package/src/text.ts +19 -0
- package/src/types.ts +336 -26
- package/src/utils/paths.ts +39 -0
- package/src/visual/companion.ts +5 -3
- package/src/visual/start-server.ts +101 -0
- package/src/visual/stop-server.ts +39 -0
- package/bin/ctx-mode-wrapper.mjs +0 -66
- package/src/config/profiles.ts +0 -64
- package/src/context-mode/installer.ts +0 -38
- package/src/quality/ai-review-gate.ts +0 -43
- package/src/quality/gate-runner.ts +0 -67
- package/src/quality/lsp-gate.ts +0 -24
- package/src/quality/test-gate.ts +0 -39
- package/src/visual/scripts/start-server.sh +0 -98
- package/src/visual/scripts/stop-server.sh +0 -21
package/src/commands/config.ts
CHANGED
|
@@ -1,78 +1,204 @@
|
|
|
1
1
|
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
3
|
+
import { formatConfigErrors, inspectConfig, updateConfig } from "../config/loader.js";
|
|
4
|
+
import type { InspectionLoadResult } from "../config/schema.js";
|
|
5
5
|
import type { SupipowersConfig } from "../types.js";
|
|
6
|
+
import { createWorkflowProgress } from "../platform/progress.js";
|
|
7
|
+
import {
|
|
8
|
+
interactivelySaveGateSetup,
|
|
9
|
+
setupGates,
|
|
10
|
+
summarizeEnabledGates,
|
|
11
|
+
type GateSetupMode,
|
|
12
|
+
type SetupGatesProgressEvent,
|
|
13
|
+
} from "../quality/setup.js";
|
|
6
14
|
|
|
7
15
|
const FRAMEWORK_OPTIONS = [
|
|
8
|
-
{ value: "", label: "not set — auto-detect on first /supi:qa run"
|
|
9
|
-
{ value: "vitest", label: "vitest — npx vitest run"
|
|
10
|
-
{ value: "jest", label: "jest — npx jest"
|
|
11
|
-
{ value: "mocha", label: "mocha — npx mocha"
|
|
12
|
-
{ value: "pytest", label: "pytest — pytest"
|
|
13
|
-
{ value: "cargo-test", label: "cargo-test — cargo test"
|
|
14
|
-
{ value: "go-test", label: "go-test — go test ./..."
|
|
15
|
-
{ value: "npm-test", label: "npm-test — npm test"
|
|
16
|
+
{ value: "", label: "not set — auto-detect on first /supi:qa run" },
|
|
17
|
+
{ value: "vitest", label: "vitest — npx vitest run" },
|
|
18
|
+
{ value: "jest", label: "jest — npx jest" },
|
|
19
|
+
{ value: "mocha", label: "mocha — npx mocha" },
|
|
20
|
+
{ value: "pytest", label: "pytest — pytest" },
|
|
21
|
+
{ value: "cargo-test", label: "cargo-test — cargo test" },
|
|
22
|
+
{ value: "go-test", label: "go-test — go test ./..." },
|
|
23
|
+
{ value: "npm-test", label: "npm-test — npm test" },
|
|
16
24
|
];
|
|
17
25
|
|
|
18
26
|
const CHANNEL_OPTIONS = [
|
|
19
27
|
{ value: [] as string[], label: "not set — auto-detect on first /supi:release run" },
|
|
20
|
-
{ value: ["github", "npm"], label: "both — GitHub Release + npm publish" },
|
|
21
28
|
{ value: ["github"], label: "github — GitHub Release with gh CLI" },
|
|
22
|
-
{ value: ["npm"], label: "npm — npm publish to registry" },
|
|
23
29
|
];
|
|
24
30
|
|
|
25
|
-
interface SettingDef {
|
|
31
|
+
export interface SettingDef {
|
|
26
32
|
label: string;
|
|
27
33
|
key: string;
|
|
28
34
|
helpText: string;
|
|
29
35
|
type: "select" | "toggle";
|
|
30
36
|
options?: string[];
|
|
31
|
-
get: (
|
|
32
|
-
set: (cwd: string, value: unknown) =>
|
|
37
|
+
get: () => string;
|
|
38
|
+
set: (cwd: string, value: unknown) => Promise<string | null>;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
export interface ConfigCommandDependencies {
|
|
42
|
+
inspectConfig: typeof inspectConfig;
|
|
43
|
+
updateConfig: typeof updateConfig;
|
|
44
|
+
setupGates: typeof setupGates;
|
|
45
|
+
interactivelySaveGateSetup: typeof interactivelySaveGateSetup;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const CONFIG_COMMAND_DEPENDENCIES: ConfigCommandDependencies = {
|
|
49
|
+
inspectConfig,
|
|
50
|
+
updateConfig,
|
|
51
|
+
setupGates,
|
|
52
|
+
interactivelySaveGateSetup,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function currentConfig(inspection: InspectionLoadResult): SupipowersConfig {
|
|
56
|
+
return inspection.effectiveConfig ?? DEFAULT_CONFIG;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function describeInspection(inspection: InspectionLoadResult, config: SupipowersConfig): string {
|
|
60
|
+
if (inspection.parseErrors.length > 0 || inspection.validationErrors.length > 0) {
|
|
61
|
+
return `config error — ${formatConfigErrors(inspection).split("\n")[0]}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return summarizeEnabledGates(config.quality.gates);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createQualityGateSetupProgress(ctx: PlatformContext, mode: GateSetupMode) {
|
|
68
|
+
if (mode !== "ai-assisted") {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const progress = createWorkflowProgress(ctx.ui, {
|
|
73
|
+
title: "quality gate setup",
|
|
74
|
+
statusKey: "supi-quality-gate-setup",
|
|
75
|
+
widgetKey: "supi-quality-gate-setup",
|
|
76
|
+
steps: [
|
|
77
|
+
{ key: "collect", label: "Inspect project" },
|
|
78
|
+
{ key: "ai", label: "AI analysis" },
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
handle(event: SetupGatesProgressEvent) {
|
|
84
|
+
switch (event.type) {
|
|
85
|
+
case "collecting-project-facts":
|
|
86
|
+
progress.activate("collect", "Reading scripts and tools");
|
|
87
|
+
return;
|
|
88
|
+
case "baseline-ready":
|
|
89
|
+
progress.complete("collect", "baseline ready");
|
|
90
|
+
return;
|
|
91
|
+
case "ai-analysis-started":
|
|
92
|
+
progress.activate("ai", "Analyzing project checks");
|
|
93
|
+
return;
|
|
94
|
+
case "ai-analysis-completed":
|
|
95
|
+
progress.complete("ai", "proposal ready");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
fail(message: string) {
|
|
100
|
+
if (progress.getStatus("ai") === "active") {
|
|
101
|
+
progress.fail("ai", message);
|
|
102
|
+
} else if (progress.getStatus("collect") === "active") {
|
|
103
|
+
progress.fail("collect", message);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
dispose() {
|
|
107
|
+
progress.dispose();
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildGateSetupDialogOptions(mode: GateSetupMode) {
|
|
113
|
+
return mode === "ai-assisted"
|
|
114
|
+
? {
|
|
115
|
+
title: "AI-assisted quality gate proposal",
|
|
116
|
+
intro: "AI analyzed your project and suggested these gates.",
|
|
117
|
+
}
|
|
118
|
+
: {
|
|
119
|
+
intro: "Detected project checks and suggested these gates.",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildSettings(
|
|
124
|
+
platform: Platform,
|
|
125
|
+
ctx: PlatformContext,
|
|
126
|
+
inspection: InspectionLoadResult,
|
|
127
|
+
deps: ConfigCommandDependencies = CONFIG_COMMAND_DEPENDENCIES,
|
|
128
|
+
): SettingDef[] {
|
|
36
129
|
const { paths } = platform;
|
|
130
|
+
const config = currentConfig(inspection);
|
|
131
|
+
|
|
37
132
|
return [
|
|
38
133
|
{
|
|
39
|
-
label: "
|
|
40
|
-
key: "
|
|
41
|
-
helpText: "
|
|
134
|
+
label: "Setup quality gates",
|
|
135
|
+
key: "quality.gates",
|
|
136
|
+
helpText: "Inspect the project and configure review gates",
|
|
42
137
|
type: "select",
|
|
43
|
-
options:
|
|
44
|
-
get: (
|
|
45
|
-
set: (
|
|
138
|
+
options: ["Run deterministic setup", "Run AI-assisted setup"],
|
|
139
|
+
get: () => describeInspection(inspection, config),
|
|
140
|
+
set: async (cwd, value) => {
|
|
141
|
+
const mode: GateSetupMode = String(value).includes("AI-assisted") ? "ai-assisted" : "deterministic";
|
|
142
|
+
const progress = createQualityGateSetupProgress(ctx, mode);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const result = await deps.setupGates(platform, cwd, deps.inspectConfig(platform.paths, cwd), {
|
|
146
|
+
mode,
|
|
147
|
+
onProgress: (event) => progress?.handle(event),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (result.status === "invalid") {
|
|
151
|
+
ctx.ui.notify(result.errors.join("\n"), "error");
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (result.status !== "proposed") {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
progress?.dispose();
|
|
160
|
+
const saveResult = await deps.interactivelySaveGateSetup(
|
|
161
|
+
ctx,
|
|
162
|
+
paths,
|
|
163
|
+
cwd,
|
|
164
|
+
result.proposal,
|
|
165
|
+
buildGateSetupDialogOptions(mode),
|
|
166
|
+
);
|
|
167
|
+
return saveResult === "saved" ? "saved" : null;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
progress?.fail((error as Error).message);
|
|
170
|
+
throw error;
|
|
171
|
+
} finally {
|
|
172
|
+
progress?.dispose();
|
|
173
|
+
}
|
|
174
|
+
},
|
|
46
175
|
},
|
|
47
176
|
{
|
|
48
177
|
label: "LSP setup guide",
|
|
49
178
|
key: "lsp.setupGuide",
|
|
50
179
|
helpText: "Show LSP setup tips when no language server is active",
|
|
51
180
|
type: "toggle",
|
|
52
|
-
get: (
|
|
53
|
-
set: (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
key: "notifications.verbosity",
|
|
58
|
-
helpText: "How much detail supipowers shows in notifications",
|
|
59
|
-
type: "select",
|
|
60
|
-
options: ["quiet", "normal", "verbose"],
|
|
61
|
-
get: (c) => c.notifications.verbosity,
|
|
62
|
-
set: (d, v) => updateConfig(paths, d, { notifications: { verbosity: v } }),
|
|
181
|
+
get: () => (config.lsp.setupGuide ? "on" : "off"),
|
|
182
|
+
set: async (cwd, value) => {
|
|
183
|
+
deps.updateConfig(paths, cwd, { lsp: { setupGuide: value === "on" } });
|
|
184
|
+
return String(value);
|
|
185
|
+
},
|
|
63
186
|
},
|
|
64
187
|
{
|
|
65
188
|
label: "QA framework",
|
|
66
189
|
key: "qa.framework",
|
|
67
190
|
helpText: "Test runner used by /supi:qa",
|
|
68
191
|
type: "select",
|
|
69
|
-
options: FRAMEWORK_OPTIONS.map((
|
|
70
|
-
get: (
|
|
71
|
-
set: (
|
|
72
|
-
const chosen = FRAMEWORK_OPTIONS.find((
|
|
73
|
-
if (chosen) {
|
|
74
|
-
|
|
192
|
+
options: FRAMEWORK_OPTIONS.map((framework) => framework.label),
|
|
193
|
+
get: () => config.qa.framework ?? "not set",
|
|
194
|
+
set: async (cwd, value) => {
|
|
195
|
+
const chosen = FRAMEWORK_OPTIONS.find((framework) => framework.label === value);
|
|
196
|
+
if (!chosen) {
|
|
197
|
+
return null;
|
|
75
198
|
}
|
|
199
|
+
|
|
200
|
+
deps.updateConfig(paths, cwd, { qa: { framework: chosen.value || null } });
|
|
201
|
+
return chosen.label.split(" — ")[0];
|
|
76
202
|
},
|
|
77
203
|
},
|
|
78
204
|
{
|
|
@@ -80,50 +206,58 @@ function buildSettings(platform: Platform, cwd: string): SettingDef[] {
|
|
|
80
206
|
key: "release.channels",
|
|
81
207
|
helpText: "Where /supi:release publishes your project",
|
|
82
208
|
type: "select",
|
|
83
|
-
options: CHANNEL_OPTIONS.map((
|
|
84
|
-
get: (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
209
|
+
options: CHANNEL_OPTIONS.map((channel) => channel.label),
|
|
210
|
+
get: () =>
|
|
211
|
+
config.release.channels.length > 0 ? config.release.channels.join(", ") : "not set",
|
|
212
|
+
set: async (cwd, value) => {
|
|
213
|
+
const chosen = CHANNEL_OPTIONS.find((channel) => channel.label === value);
|
|
214
|
+
if (!chosen) {
|
|
215
|
+
return null;
|
|
89
216
|
}
|
|
217
|
+
|
|
218
|
+
deps.updateConfig(paths, cwd, { release: { channels: chosen.value } });
|
|
219
|
+
return chosen.label.split(" — ")[0];
|
|
90
220
|
},
|
|
91
221
|
},
|
|
92
222
|
];
|
|
93
223
|
}
|
|
94
224
|
|
|
95
|
-
export function
|
|
225
|
+
export async function runConfigMenu(
|
|
226
|
+
platform: Platform,
|
|
227
|
+
ctx: PlatformContext,
|
|
228
|
+
deps: ConfigCommandDependencies = CONFIG_COMMAND_DEPENDENCIES,
|
|
229
|
+
): Promise<void> {
|
|
96
230
|
if (!ctx.hasUI) {
|
|
97
231
|
ctx.ui.notify("Config UI requires interactive mode", "warning");
|
|
98
232
|
return;
|
|
99
233
|
}
|
|
100
234
|
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const options = settings.map(
|
|
108
|
-
(s) => `${s.label}: ${s.get(config)}`
|
|
109
|
-
);
|
|
110
|
-
options.push("Done");
|
|
235
|
+
while (true) {
|
|
236
|
+
const inspection = deps.inspectConfig(platform.paths, ctx.cwd);
|
|
237
|
+
const settings = buildSettings(platform, ctx, inspection, deps);
|
|
238
|
+
const options = settings.map((setting) => `${setting.label}: ${setting.get()}`);
|
|
239
|
+
options.push("Done");
|
|
111
240
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
241
|
+
const choice = await ctx.ui.select(
|
|
242
|
+
"Supipowers Settings",
|
|
243
|
+
options,
|
|
244
|
+
{ helpText: "Select a setting to change · Esc to close" },
|
|
245
|
+
);
|
|
117
246
|
|
|
118
|
-
|
|
247
|
+
if (choice === undefined || choice === null || choice === "Done") {
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
119
250
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
251
|
+
const index = options.indexOf(choice);
|
|
252
|
+
const setting = settings[index];
|
|
253
|
+
if (!setting) {
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
123
256
|
|
|
257
|
+
try {
|
|
124
258
|
if (setting.type === "select" && setting.options) {
|
|
125
|
-
const currentValue = setting.get(
|
|
126
|
-
const currentIndex = setting.options.findIndex((
|
|
259
|
+
const currentValue = setting.get();
|
|
260
|
+
const currentIndex = setting.options.findIndex((option) => option.startsWith(currentValue));
|
|
127
261
|
const value = await ctx.ui.select(
|
|
128
262
|
setting.label,
|
|
129
263
|
setting.options,
|
|
@@ -132,39 +266,34 @@ export function handleConfig(platform: Platform, ctx: PlatformContext): void {
|
|
|
132
266
|
helpText: setting.helpText,
|
|
133
267
|
},
|
|
134
268
|
);
|
|
269
|
+
|
|
135
270
|
if (value !== undefined && value !== null) {
|
|
136
|
-
setting.set(ctx.cwd, value);
|
|
137
|
-
|
|
138
|
-
|
|
271
|
+
const display = await setting.set(ctx.cwd, value);
|
|
272
|
+
if (display) {
|
|
273
|
+
ctx.ui.notify(`${setting.label} → ${display}`, "info");
|
|
274
|
+
}
|
|
139
275
|
}
|
|
140
276
|
} else if (setting.type === "toggle") {
|
|
141
|
-
const current = setting.get(
|
|
277
|
+
const current = setting.get();
|
|
142
278
|
const newValue = current === "on" ? "off" : "on";
|
|
143
|
-
setting.set(ctx.cwd, newValue);
|
|
144
|
-
|
|
279
|
+
const display = await setting.set(ctx.cwd, newValue);
|
|
280
|
+
if (display) {
|
|
281
|
+
ctx.ui.notify(`${setting.label} → ${display}`, "info");
|
|
282
|
+
}
|
|
145
283
|
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
ctx.ui.notify((error as Error).message, "error");
|
|
146
286
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
` CLI installed: ${status.cliInstalled ? "\u2713" + (status.version ? ` v${status.version}` : "") : "\u2717"}`,
|
|
158
|
-
` MCP configured: ${status.mcpConfigured ? "\u2713" : "\u2717"}`,
|
|
159
|
-
` Tools available: ${status.toolsAvailable ? "\u2713" : "\u2717"}`,
|
|
160
|
-
];
|
|
161
|
-
if (!status.mcpConfigured && status.cliInstalled) {
|
|
162
|
-
lines.push(` \u2192 Run \`${platform.name} mcp add context-mode\` to enable`);
|
|
163
|
-
}
|
|
164
|
-
ctx.ui.notify(lines.join("\n"), "info");
|
|
165
|
-
}).catch(() => {
|
|
166
|
-
// Silently ignore — context-mode status is optional
|
|
167
|
-
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function handleConfig(platform: Platform, ctx: PlatformContext): void {
|
|
291
|
+
void runConfigMenu(platform, ctx, CONFIG_COMMAND_DEPENDENCIES);
|
|
292
|
+
|
|
293
|
+
ctx.ui.notify(
|
|
294
|
+
"\nContext Mode: ✓ built-in (native tools)",
|
|
295
|
+
"info",
|
|
296
|
+
);
|
|
168
297
|
}
|
|
169
298
|
|
|
170
299
|
export function registerConfigCommand(platform: Platform): void {
|
package/src/commands/doctor.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Platform, PlatformContext, PlatformCapabilities } from "../platform/types.js";
|
|
2
2
|
import { existsSync, writeFileSync, unlinkSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
5
|
+
import { formatConfigErrors, inspectConfig } from "../config/loader.js";
|
|
5
6
|
import { detectContextMode } from "../context-mode/detector.js";
|
|
6
7
|
import { isLspAvailable } from "../lsp/detector.js";
|
|
8
|
+
import { summarizeEnabledGates } from "../quality/setup.js";
|
|
7
9
|
import { DEPENDENCIES } from "../deps/registry.js";
|
|
8
10
|
|
|
9
11
|
export interface CheckResult {
|
|
@@ -39,7 +41,7 @@ export async function checkPlatform(platform: Platform): Promise<CheckResult> {
|
|
|
39
41
|
const name = platform.name.toUpperCase();
|
|
40
42
|
const presence = { ok: true, detail: `${name} detected` };
|
|
41
43
|
try {
|
|
42
|
-
const r = await platform.exec("
|
|
44
|
+
const r = await platform.exec("node", ["--version"]);
|
|
43
45
|
return { name: "Platform", presence, functional: { ok: r.code === 0, detail: r.code === 0 ? "exec works" : "exec failed" } };
|
|
44
46
|
} catch {
|
|
45
47
|
return { name: "Platform", presence, functional: { ok: false, detail: "exec failed" } };
|
|
@@ -51,15 +53,14 @@ export async function checkConfig(platform: Platform, cwd: string): Promise<Chec
|
|
|
51
53
|
const globalPath = platform.paths.global("config.json");
|
|
52
54
|
const projectExists = existsSync(projectPath);
|
|
53
55
|
const globalExists = existsSync(globalPath);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const config = loadConfig(platform.paths, cwd);
|
|
56
|
+
const inspection = inspectConfig(platform.paths, cwd);
|
|
57
|
+
const config = inspection.effectiveConfig ?? DEFAULT_CONFIG;
|
|
57
58
|
|
|
58
59
|
if (!projectExists && !globalExists) {
|
|
59
60
|
return {
|
|
60
61
|
name: "Config",
|
|
61
62
|
presence: { ok: false, detail: "No config.json found (using defaults)" },
|
|
62
|
-
functional: { ok: true, detail: `
|
|
63
|
+
functional: { ok: true, detail: `quality.gates: ${summarizeEnabledGates(config.quality.gates)}` },
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -67,10 +68,18 @@ export async function checkConfig(platform: Platform, cwd: string): Promise<Chec
|
|
|
67
68
|
? `${platform.paths.dotDir}/supipowers/config.json (project)`
|
|
68
69
|
: `~/${platform.paths.dotDir}/supipowers/config.json (global)`;
|
|
69
70
|
|
|
71
|
+
if (inspection.parseErrors.length > 0 || inspection.validationErrors.length > 0) {
|
|
72
|
+
return {
|
|
73
|
+
name: "Config",
|
|
74
|
+
presence: { ok: true, detail: `Found ${foundPath}` },
|
|
75
|
+
functional: { ok: false, detail: formatConfigErrors(inspection) },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
70
79
|
return {
|
|
71
80
|
name: "Config",
|
|
72
81
|
presence: { ok: true, detail: `Found ${foundPath}` },
|
|
73
|
-
functional: { ok: true, detail: `
|
|
82
|
+
functional: { ok: true, detail: `quality.gates: ${summarizeEnabledGates(config.quality.gates)}` },
|
|
74
83
|
};
|
|
75
84
|
}
|
|
76
85
|
|
|
@@ -218,11 +227,8 @@ const CTX_TOOL_NAMES: Record<string, string> = {
|
|
|
218
227
|
ctxFetchAndIndex: "ctx_fetch_and_index",
|
|
219
228
|
};
|
|
220
229
|
|
|
221
|
-
export function checkContextMode(
|
|
222
|
-
const status = detectContextMode(
|
|
223
|
-
if (!status.available) {
|
|
224
|
-
return { name: "Context Mode", presence: { ok: false, detail: "No context-mode tools detected" } };
|
|
225
|
-
}
|
|
230
|
+
export function checkContextMode(_activeTools: string[]): CheckResult {
|
|
231
|
+
const status = detectContextMode();
|
|
226
232
|
|
|
227
233
|
const foundNames = Object.entries(status.tools)
|
|
228
234
|
.filter(([, v]) => v)
|
|
@@ -230,7 +236,7 @@ export function checkContextMode(activeTools: string[]): CheckResult {
|
|
|
230
236
|
|
|
231
237
|
return {
|
|
232
238
|
name: "Context Mode",
|
|
233
|
-
presence: { ok: true, detail: "
|
|
239
|
+
presence: { ok: true, detail: "Native tools (built-in)" },
|
|
234
240
|
functional: { ok: true, detail: foundNames.join(", ") },
|
|
235
241
|
};
|
|
236
242
|
}
|
package/src/commands/fix-pr.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { moduleDir, toBashPath } from "../utils/paths.js";
|
|
4
5
|
import { loadFixPrConfig, saveFixPrConfig } from "../fix-pr/config.js";
|
|
5
6
|
import { buildFixPrOrchestratorPrompt } from "../fix-pr/prompt-builder.js";
|
|
6
7
|
import type { FixPrConfig, CommentReplyPolicy } from "../fix-pr/types.js";
|
|
@@ -15,6 +16,7 @@ import { modelRegistry } from "../config/model-registry-instance.js";
|
|
|
15
16
|
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
16
17
|
import { loadModelConfig } from "../config/model-config.js";
|
|
17
18
|
import { detectBotReviewers } from "../fix-pr/bot-detector.js";
|
|
19
|
+
import { fetchPrComments } from "../fix-pr/fetch-comments.js";
|
|
18
20
|
|
|
19
21
|
modelRegistry.register({
|
|
20
22
|
id: "fix-pr",
|
|
@@ -32,13 +34,13 @@ modelRegistry.register({
|
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
function getScriptsDir(): string {
|
|
35
|
-
return path.join(
|
|
37
|
+
return toBashPath(path.join(moduleDir(import.meta.url), "..", "fix-pr", "scripts"));
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function findSkillPath(skillName: string): string | null {
|
|
39
41
|
const candidates = [
|
|
40
42
|
path.join(process.cwd(), "skills", skillName, "SKILL.md"),
|
|
41
|
-
path.join(
|
|
43
|
+
path.join(moduleDir(import.meta.url), "..", "..", "skills", skillName, "SKILL.md"),
|
|
42
44
|
];
|
|
43
45
|
for (const p of candidates) {
|
|
44
46
|
if (fs.existsSync(p)) return p;
|
|
@@ -138,19 +140,14 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
// ── Step 4: Fetch initial comments ─────────────────────────────
|
|
141
|
-
const sessionDir = getSessionDir(platform.paths, ctx.cwd, ledger.id);
|
|
143
|
+
const sessionDir = toBashPath(getSessionDir(platform.paths, ctx.cwd, ledger.id));
|
|
142
144
|
const scriptsDir = getScriptsDir();
|
|
143
145
|
const snapshotPath = path.join(sessionDir, "snapshots", `comments-${ledger.iteration}.jsonl`);
|
|
144
146
|
|
|
145
|
-
const
|
|
146
|
-
path.join(scriptsDir, "fetch-pr-comments.sh"),
|
|
147
|
-
repo,
|
|
148
|
-
String(prNumber),
|
|
149
|
-
snapshotPath,
|
|
150
|
-
], { cwd: ctx.cwd });
|
|
147
|
+
const fetchError = await fetchPrComments(platform, repo, prNumber, snapshotPath, ctx.cwd);
|
|
151
148
|
|
|
152
|
-
if (
|
|
153
|
-
notifyError(ctx, "Failed to fetch PR comments",
|
|
149
|
+
if (fetchError) {
|
|
150
|
+
notifyError(ctx, "Failed to fetch PR comments", fetchError);
|
|
154
151
|
return;
|
|
155
152
|
}
|
|
156
153
|
|