superpowers-spec 1.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/LICENSE +22 -0
- package/README.md +141 -0
- package/agents/code-reviewer.md +48 -0
- package/bin/openspec.js +3 -0
- package/bin/superpowers.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +482 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +277 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +257 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +552 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +294 -0
- package/dist/commands/workflow/index.d.ts +17 -0
- package/dist/commands/workflow/index.js +12 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +381 -0
- package/dist/commands/workflow/new-change.d.ts +11 -0
- package/dist/commands/workflow/new-change.js +44 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +34 -0
- package/dist/commands/workflow/shared.d.ts +57 -0
- package/dist/commands/workflow/shared.js +116 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +75 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +68 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +318 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/index.d.ts +7 -0
- package/dist/core/artifact-graph/index.js +13 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
- package/dist/core/artifact-graph/instruction-loader.js +214 -0
- package/dist/core/artifact-graph/resolver.d.ts +81 -0
- package/dist/core/artifact-graph/resolver.js +257 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +54 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/available-tools.d.ts +16 -0
- package/dist/core/available-tools.js +30 -0
- package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
- package/dist/core/command-generation/adapters/amazon-q.js +26 -0
- package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
- package/dist/core/command-generation/adapters/antigravity.js +26 -0
- package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
- package/dist/core/command-generation/adapters/auggie.js +27 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +50 -0
- package/dist/core/command-generation/adapters/cline.d.ts +14 -0
- package/dist/core/command-generation/adapters/cline.js +27 -0
- package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
- package/dist/core/command-generation/adapters/codebuddy.js +28 -0
- package/dist/core/command-generation/adapters/codex.d.ts +16 -0
- package/dist/core/command-generation/adapters/codex.js +39 -0
- package/dist/core/command-generation/adapters/continue.d.ts +13 -0
- package/dist/core/command-generation/adapters/continue.js +28 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
- package/dist/core/command-generation/adapters/costrict.js +27 -0
- package/dist/core/command-generation/adapters/crush.d.ts +13 -0
- package/dist/core/command-generation/adapters/crush.js +30 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +44 -0
- package/dist/core/command-generation/adapters/factory.d.ts +13 -0
- package/dist/core/command-generation/adapters/factory.js +27 -0
- package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
- package/dist/core/command-generation/adapters/gemini.js +26 -0
- package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
- package/dist/core/command-generation/adapters/github-copilot.js +26 -0
- package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
- package/dist/core/command-generation/adapters/iflow.js +29 -0
- package/dist/core/command-generation/adapters/index.d.ts +29 -0
- package/dist/core/command-generation/adapters/index.js +29 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/kilocode.js +23 -0
- package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
- package/dist/core/command-generation/adapters/kiro.js +26 -0
- package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
- package/dist/core/command-generation/adapters/opencode.js +29 -0
- package/dist/core/command-generation/adapters/pi.d.ts +14 -0
- package/dist/core/command-generation/adapters/pi.js +41 -0
- package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
- package/dist/core/command-generation/adapters/qoder.js +30 -0
- package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
- package/dist/core/command-generation/adapters/qwen.js +26 -0
- package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/roocode.js +27 -0
- package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
- package/dist/core/command-generation/adapters/windsurf.js +51 -0
- package/dist/core/command-generation/generator.d.ts +21 -0
- package/dist/core/command-generation/generator.js +27 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/registry.d.ts +36 -0
- package/dist/core/command-generation/registry.js +92 -0
- package/dist/core/command-generation/types.d.ts +56 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.js +461 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.js +102 -0
- package/dist/core/completions/factory.d.ts +64 -0
- package/dist/core/completions/factory.js +75 -0
- package/dist/core/completions/generators/bash-generator.d.ts +32 -0
- package/dist/core/completions/generators/bash-generator.js +174 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +157 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
- package/dist/core/completions/generators/powershell-generator.js +207 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
- package/dist/core/completions/generators/zsh-generator.js +250 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +318 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +143 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +88 -0
- package/dist/core/completions/installers/powershell-installer.js +327 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
- package/dist/core/completions/installers/zsh-installer.js +449 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +24 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +39 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +25 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +36 -0
- package/dist/core/completions/types.d.ts +79 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-schema.d.ts +86 -0
- package/dist/core/config-schema.js +213 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +33 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +44 -0
- package/dist/core/global-config.js +125 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +39 -0
- package/dist/core/init.js +634 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +512 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +108 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +193 -0
- package/dist/core/parsers/markdown-parser.d.ts +22 -0
- package/dist/core/parsers/markdown-parser.js +187 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/profile-sync-drift.d.ts +38 -0
- package/dist/core/profile-sync-drift.js +200 -0
- package/dist/core/profiles.d.ts +26 -0
- package/dist/core/profiles.js +40 -0
- package/dist/core/project-config.d.ts +64 -0
- package/dist/core/project-config.js +223 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/shared/index.d.ts +8 -0
- package/dist/core/shared/index.js +8 -0
- package/dist/core/shared/skill-generation.d.ts +49 -0
- package/dist/core/shared/skill-generation.js +96 -0
- package/dist/core/shared/tool-detection.d.ts +71 -0
- package/dist/core/shared/tool-detection.js +158 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +384 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/index.d.ts +8 -0
- package/dist/core/templates/index.js +9 -0
- package/dist/core/templates/skill-templates.d.ts +19 -0
- package/dist/core/templates/skill-templates.js +18 -0
- package/dist/core/templates/types.d.ts +19 -0
- package/dist/core/templates/types.js +5 -0
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +313 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +271 -0
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/bulk-archive-change.js +488 -0
- package/dist/core/templates/workflows/continue-change.d.ts +10 -0
- package/dist/core/templates/workflows/continue-change.js +232 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +469 -0
- package/dist/core/templates/workflows/feedback.d.ts +9 -0
- package/dist/core/templates/workflows/feedback.js +108 -0
- package/dist/core/templates/workflows/ff-change.d.ts +10 -0
- package/dist/core/templates/workflows/ff-change.js +198 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +143 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +565 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +223 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +272 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +332 -0
- package/dist/core/update.d.ts +77 -0
- package/dist/core/update.js +537 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.js +409 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.js +159 -0
- package/dist/telemetry/config.d.ts +32 -0
- package/dist/telemetry/config.js +68 -0
- package/dist/telemetry/index.d.ts +31 -0
- package/dist/telemetry/index.js +180 -0
- package/dist/ui/ascii-patterns.d.ts +16 -0
- package/dist/ui/ascii-patterns.js +133 -0
- package/dist/ui/welcome-screen.d.ts +10 -0
- package/dist/ui/welcome-screen.js +146 -0
- package/dist/utils/change-metadata.d.ts +51 -0
- package/dist/utils/change-metadata.js +147 -0
- package/dist/utils/change-utils.d.ts +62 -0
- package/dist/utils/change-utils.js +121 -0
- package/dist/utils/command-references.d.ts +18 -0
- package/dist/utils/command-references.js +20 -0
- package/dist/utils/file-system.d.ts +36 -0
- package/dist/utils/file-system.js +281 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.js +36 -0
- package/extension-manifest.json +9 -0
- package/hooks/hooks.json +16 -0
- package/hooks/run-hook.cmd +46 -0
- package/hooks/session-start +51 -0
- package/package.json +87 -0
- package/schemas/spec-driven/schema.yaml +153 -0
- package/schemas/spec-driven/templates/design.md +19 -0
- package/schemas/spec-driven/templates/proposal.md +23 -0
- package/schemas/spec-driven/templates/spec.md +8 -0
- package/schemas/spec-driven/templates/tasks.md +9 -0
- package/scripts/postinstall.js +147 -0
- package/skills/dispatching-parallel-agents/SKILL.md +113 -0
- package/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/skills/receiving-code-review/SKILL.md +213 -0
- package/skills/requesting-code-review/SKILL.md +73 -0
- package/skills/requesting-code-review/code-reviewer.md +146 -0
- package/skills/subagent-driven-development/SKILL.md +98 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +20 -0
- package/skills/subagent-driven-development/implementer-prompt.md +78 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/test-driven-development/SKILL.md +356 -0
- package/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/using-git-worktrees/SKILL.md +212 -0
- package/skills/using-superpowers/SKILL.md +95 -0
- package/skills/verification-before-completion/SKILL.md +126 -0
- package/skills/writing-skills/SKILL.md +148 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { spawn, execSync } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { getGlobalConfigPath, getGlobalConfig, saveGlobalConfig, } from '../core/global-config.js';
|
|
5
|
+
import { getNestedValue, setNestedValue, deleteNestedValue, coerceValue, formatValueYaml, validateConfigKeyPath, validateConfig, DEFAULT_CONFIG, } from '../core/config-schema.js';
|
|
6
|
+
import { CORE_WORKFLOWS, ALL_WORKFLOWS, getProfileWorkflows } from '../core/profiles.js';
|
|
7
|
+
import { SUPERPOWERS_DIR_NAME } from '../core/config.js';
|
|
8
|
+
import { hasProjectConfigDrift } from '../core/profile-sync-drift.js';
|
|
9
|
+
const WORKFLOW_PROMPT_META = {
|
|
10
|
+
propose: {
|
|
11
|
+
name: 'Propose change',
|
|
12
|
+
description: 'Create proposal, design, and tasks from a request',
|
|
13
|
+
},
|
|
14
|
+
explore: {
|
|
15
|
+
name: 'Explore ideas',
|
|
16
|
+
description: 'Investigate a problem before implementation',
|
|
17
|
+
},
|
|
18
|
+
new: {
|
|
19
|
+
name: 'New change',
|
|
20
|
+
description: 'Create a new change scaffold quickly',
|
|
21
|
+
},
|
|
22
|
+
continue: {
|
|
23
|
+
name: 'Continue change',
|
|
24
|
+
description: 'Resume work on an existing change',
|
|
25
|
+
},
|
|
26
|
+
apply: {
|
|
27
|
+
name: 'Apply tasks',
|
|
28
|
+
description: 'Implement tasks from the current change',
|
|
29
|
+
},
|
|
30
|
+
ff: {
|
|
31
|
+
name: 'Fast-forward',
|
|
32
|
+
description: 'Run a faster implementation workflow',
|
|
33
|
+
},
|
|
34
|
+
sync: {
|
|
35
|
+
name: 'Sync specs',
|
|
36
|
+
description: 'Sync change artifacts with specs',
|
|
37
|
+
},
|
|
38
|
+
archive: {
|
|
39
|
+
name: 'Archive change',
|
|
40
|
+
description: 'Finalize and archive a completed change',
|
|
41
|
+
},
|
|
42
|
+
'bulk-archive': {
|
|
43
|
+
name: 'Bulk archive',
|
|
44
|
+
description: 'Archive multiple completed changes together',
|
|
45
|
+
},
|
|
46
|
+
verify: {
|
|
47
|
+
name: 'Verify change',
|
|
48
|
+
description: 'Run verification checks against a change',
|
|
49
|
+
},
|
|
50
|
+
onboard: {
|
|
51
|
+
name: 'Onboard',
|
|
52
|
+
description: 'Guided onboarding flow for Superpowers',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
function isPromptCancellationError(error) {
|
|
56
|
+
return (error instanceof Error &&
|
|
57
|
+
(error.name === 'ExitPromptError' || error.message.includes('force closed the prompt with SIGINT')));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the effective current profile state from global config defaults.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveCurrentProfileState(config) {
|
|
63
|
+
const profile = config.profile || 'core';
|
|
64
|
+
const delivery = config.delivery || 'both';
|
|
65
|
+
const workflows = [
|
|
66
|
+
...getProfileWorkflows(profile, config.workflows ? [...config.workflows] : undefined),
|
|
67
|
+
];
|
|
68
|
+
return { profile, delivery, workflows };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Derive profile type from selected workflows.
|
|
72
|
+
*/
|
|
73
|
+
export function deriveProfileFromWorkflowSelection(selectedWorkflows) {
|
|
74
|
+
const isCoreMatch = selectedWorkflows.length === CORE_WORKFLOWS.length &&
|
|
75
|
+
CORE_WORKFLOWS.every((w) => selectedWorkflows.includes(w));
|
|
76
|
+
return isCoreMatch ? 'core' : 'custom';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Format a compact workflow summary for the profile header.
|
|
80
|
+
*/
|
|
81
|
+
export function formatWorkflowSummary(workflows, profile) {
|
|
82
|
+
return `${workflows.length} selected (${profile})`;
|
|
83
|
+
}
|
|
84
|
+
function stableWorkflowOrder(workflows) {
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const ordered = [];
|
|
87
|
+
for (const workflow of ALL_WORKFLOWS) {
|
|
88
|
+
if (workflows.includes(workflow) && !seen.has(workflow)) {
|
|
89
|
+
ordered.push(workflow);
|
|
90
|
+
seen.add(workflow);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const extras = workflows.filter((w) => !ALL_WORKFLOWS.includes(w));
|
|
94
|
+
extras.sort();
|
|
95
|
+
for (const extra of extras) {
|
|
96
|
+
if (!seen.has(extra)) {
|
|
97
|
+
ordered.push(extra);
|
|
98
|
+
seen.add(extra);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return ordered;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build a user-facing diff summary between two profile states.
|
|
105
|
+
*/
|
|
106
|
+
export function diffProfileState(before, after) {
|
|
107
|
+
const lines = [];
|
|
108
|
+
if (before.delivery !== after.delivery) {
|
|
109
|
+
lines.push(`delivery: ${before.delivery} -> ${after.delivery}`);
|
|
110
|
+
}
|
|
111
|
+
if (before.profile !== after.profile) {
|
|
112
|
+
lines.push(`profile: ${before.profile} -> ${after.profile}`);
|
|
113
|
+
}
|
|
114
|
+
const beforeOrdered = stableWorkflowOrder(before.workflows);
|
|
115
|
+
const afterOrdered = stableWorkflowOrder(after.workflows);
|
|
116
|
+
const beforeSet = new Set(beforeOrdered);
|
|
117
|
+
const afterSet = new Set(afterOrdered);
|
|
118
|
+
const added = afterOrdered.filter((w) => !beforeSet.has(w));
|
|
119
|
+
const removed = beforeOrdered.filter((w) => !afterSet.has(w));
|
|
120
|
+
if (added.length > 0 || removed.length > 0) {
|
|
121
|
+
const tokens = [];
|
|
122
|
+
if (added.length > 0) {
|
|
123
|
+
tokens.push(`added ${added.join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
if (removed.length > 0) {
|
|
126
|
+
tokens.push(`removed ${removed.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
lines.push(`workflows: ${tokens.join('; ')}`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
hasChanges: lines.length > 0,
|
|
132
|
+
lines,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function maybeWarnConfigDrift(projectDir, state, colorize) {
|
|
136
|
+
const superpowersDir = path.join(projectDir, SUPERPOWERS_DIR_NAME);
|
|
137
|
+
if (!fs.existsSync(superpowersDir)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!hasProjectConfigDrift(projectDir, state.workflows, state.delivery)) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
console.log(colorize('Warning: Global config is not applied to this project. Run `superpowers update` to sync.'));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Register the config command and all its subcommands.
|
|
147
|
+
*
|
|
148
|
+
* @param program - The Commander program instance
|
|
149
|
+
*/
|
|
150
|
+
export function registerConfigCommand(program) {
|
|
151
|
+
const configCmd = program
|
|
152
|
+
.command('config')
|
|
153
|
+
.description('View and modify global Superpowers configuration')
|
|
154
|
+
.option('--scope <scope>', 'Config scope (only "global" supported currently)')
|
|
155
|
+
.hook('preAction', (thisCommand) => {
|
|
156
|
+
const opts = thisCommand.opts();
|
|
157
|
+
if (opts.scope && opts.scope !== 'global') {
|
|
158
|
+
console.error('Error: Project-local config is not yet implemented');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// config path
|
|
163
|
+
configCmd
|
|
164
|
+
.command('path')
|
|
165
|
+
.description('Show config file location')
|
|
166
|
+
.action(() => {
|
|
167
|
+
console.log(getGlobalConfigPath());
|
|
168
|
+
});
|
|
169
|
+
// config list
|
|
170
|
+
configCmd
|
|
171
|
+
.command('list')
|
|
172
|
+
.description('Show all current settings')
|
|
173
|
+
.option('--json', 'Output as JSON')
|
|
174
|
+
.action((options) => {
|
|
175
|
+
const config = getGlobalConfig();
|
|
176
|
+
if (options.json) {
|
|
177
|
+
console.log(JSON.stringify(config, null, 2));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Read raw config to determine which values are explicit vs defaults
|
|
181
|
+
const configPath = getGlobalConfigPath();
|
|
182
|
+
let rawConfig = {};
|
|
183
|
+
try {
|
|
184
|
+
if (fs.existsSync(configPath)) {
|
|
185
|
+
rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// If reading fails, treat all as defaults
|
|
190
|
+
}
|
|
191
|
+
console.log(formatValueYaml(config));
|
|
192
|
+
// Annotate profile settings
|
|
193
|
+
const profileSource = rawConfig.profile !== undefined ? '(explicit)' : '(default)';
|
|
194
|
+
const deliverySource = rawConfig.delivery !== undefined ? '(explicit)' : '(default)';
|
|
195
|
+
console.log(`\nProfile settings:`);
|
|
196
|
+
console.log(` profile: ${config.profile} ${profileSource}`);
|
|
197
|
+
console.log(` delivery: ${config.delivery} ${deliverySource}`);
|
|
198
|
+
if (config.profile === 'core') {
|
|
199
|
+
console.log(` workflows: ${CORE_WORKFLOWS.join(', ')} (from core profile)`);
|
|
200
|
+
}
|
|
201
|
+
else if (config.workflows && config.workflows.length > 0) {
|
|
202
|
+
console.log(` workflows: ${config.workflows.join(', ')} (explicit)`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log(` workflows: (none)`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// config get
|
|
210
|
+
configCmd
|
|
211
|
+
.command('get <key>')
|
|
212
|
+
.description('Get a specific value (raw, scriptable)')
|
|
213
|
+
.action((key) => {
|
|
214
|
+
const config = getGlobalConfig();
|
|
215
|
+
const value = getNestedValue(config, key);
|
|
216
|
+
if (value === undefined) {
|
|
217
|
+
process.exitCode = 1;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (typeof value === 'object' && value !== null) {
|
|
221
|
+
console.log(JSON.stringify(value));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(String(value));
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
// config set
|
|
228
|
+
configCmd
|
|
229
|
+
.command('set <key> <value>')
|
|
230
|
+
.description('Set a value (auto-coerce types)')
|
|
231
|
+
.option('--string', 'Force value to be stored as string')
|
|
232
|
+
.option('--allow-unknown', 'Allow setting unknown keys')
|
|
233
|
+
.action((key, value, options) => {
|
|
234
|
+
const allowUnknown = Boolean(options.allowUnknown);
|
|
235
|
+
const keyValidation = validateConfigKeyPath(key);
|
|
236
|
+
if (!keyValidation.valid && !allowUnknown) {
|
|
237
|
+
const reason = keyValidation.reason ? ` ${keyValidation.reason}.` : '';
|
|
238
|
+
console.error(`Error: Invalid configuration key "${key}".${reason}`);
|
|
239
|
+
console.error('Use "superpowers config list" to see available keys.');
|
|
240
|
+
console.error('Pass --allow-unknown to bypass this check.');
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const config = getGlobalConfig();
|
|
245
|
+
const coercedValue = coerceValue(value, options.string || false);
|
|
246
|
+
// Create a copy to validate before saving
|
|
247
|
+
const newConfig = JSON.parse(JSON.stringify(config));
|
|
248
|
+
setNestedValue(newConfig, key, coercedValue);
|
|
249
|
+
// Validate the new config
|
|
250
|
+
const validation = validateConfig(newConfig);
|
|
251
|
+
if (!validation.success) {
|
|
252
|
+
console.error(`Error: Invalid configuration - ${validation.error}`);
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// Apply changes and save
|
|
257
|
+
setNestedValue(config, key, coercedValue);
|
|
258
|
+
saveGlobalConfig(config);
|
|
259
|
+
const displayValue = typeof coercedValue === 'string' ? `"${coercedValue}"` : String(coercedValue);
|
|
260
|
+
console.log(`Set ${key} = ${displayValue}`);
|
|
261
|
+
});
|
|
262
|
+
// config unset
|
|
263
|
+
configCmd
|
|
264
|
+
.command('unset <key>')
|
|
265
|
+
.description('Remove a key (revert to default)')
|
|
266
|
+
.action((key) => {
|
|
267
|
+
const config = getGlobalConfig();
|
|
268
|
+
const existed = deleteNestedValue(config, key);
|
|
269
|
+
if (existed) {
|
|
270
|
+
saveGlobalConfig(config);
|
|
271
|
+
console.log(`Unset ${key} (reverted to default)`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
console.log(`Key "${key}" was not set`);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
// config reset
|
|
278
|
+
configCmd
|
|
279
|
+
.command('reset')
|
|
280
|
+
.description('Reset configuration to defaults')
|
|
281
|
+
.option('--all', 'Reset all configuration (required)')
|
|
282
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
283
|
+
.action(async (options) => {
|
|
284
|
+
if (!options.all) {
|
|
285
|
+
console.error('Error: --all flag is required for reset');
|
|
286
|
+
console.error('Usage: superpowers config reset --all [-y]');
|
|
287
|
+
process.exitCode = 1;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (!options.yes) {
|
|
291
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
292
|
+
let confirmed;
|
|
293
|
+
try {
|
|
294
|
+
confirmed = await confirm({
|
|
295
|
+
message: 'Reset all configuration to defaults?',
|
|
296
|
+
default: false,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
if (isPromptCancellationError(error)) {
|
|
301
|
+
console.log('Reset cancelled.');
|
|
302
|
+
process.exitCode = 130;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
if (!confirmed) {
|
|
308
|
+
console.log('Reset cancelled.');
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
saveGlobalConfig({ ...DEFAULT_CONFIG });
|
|
313
|
+
console.log('Configuration reset to defaults');
|
|
314
|
+
});
|
|
315
|
+
// config edit
|
|
316
|
+
configCmd
|
|
317
|
+
.command('edit')
|
|
318
|
+
.description('Open config in $EDITOR')
|
|
319
|
+
.action(async () => {
|
|
320
|
+
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
321
|
+
if (!editor) {
|
|
322
|
+
console.error('Error: No editor configured');
|
|
323
|
+
console.error('Set the EDITOR or VISUAL environment variable to your preferred editor');
|
|
324
|
+
console.error('Example: export EDITOR=vim');
|
|
325
|
+
process.exitCode = 1;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const configPath = getGlobalConfigPath();
|
|
329
|
+
// Ensure config file exists with defaults
|
|
330
|
+
if (!fs.existsSync(configPath)) {
|
|
331
|
+
saveGlobalConfig({ ...DEFAULT_CONFIG });
|
|
332
|
+
}
|
|
333
|
+
// Spawn editor and wait for it to close
|
|
334
|
+
// Avoid shell parsing to correctly handle paths with spaces in both
|
|
335
|
+
// the editor path and config path
|
|
336
|
+
const child = spawn(editor, [configPath], {
|
|
337
|
+
stdio: 'inherit',
|
|
338
|
+
shell: false,
|
|
339
|
+
});
|
|
340
|
+
await new Promise((resolve, reject) => {
|
|
341
|
+
child.on('close', (code) => {
|
|
342
|
+
if (code === 0) {
|
|
343
|
+
resolve();
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
reject(new Error(`Editor exited with code ${code}`));
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
child.on('error', reject);
|
|
350
|
+
});
|
|
351
|
+
try {
|
|
352
|
+
const rawConfig = fs.readFileSync(configPath, 'utf-8');
|
|
353
|
+
const parsedConfig = JSON.parse(rawConfig);
|
|
354
|
+
const validation = validateConfig(parsedConfig);
|
|
355
|
+
if (!validation.success) {
|
|
356
|
+
console.error(`Error: Invalid configuration - ${validation.error}`);
|
|
357
|
+
process.exitCode = 1;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
if (error.code === 'ENOENT') {
|
|
362
|
+
console.error(`Error: Config file not found at ${configPath}`);
|
|
363
|
+
}
|
|
364
|
+
else if (error instanceof SyntaxError) {
|
|
365
|
+
console.error(`Error: Invalid JSON in ${configPath}`);
|
|
366
|
+
console.error(error.message);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
console.error(`Error: Unable to validate configuration - ${error instanceof Error ? error.message : String(error)}`);
|
|
370
|
+
}
|
|
371
|
+
process.exitCode = 1;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
// config profile [preset]
|
|
375
|
+
configCmd
|
|
376
|
+
.command('profile [preset]')
|
|
377
|
+
.description('Configure workflow profile (interactive picker or preset shortcut)')
|
|
378
|
+
.action(async (preset) => {
|
|
379
|
+
// Preset shortcut: `superpowers config profile core`
|
|
380
|
+
if (preset === 'core') {
|
|
381
|
+
const config = getGlobalConfig();
|
|
382
|
+
config.profile = 'core';
|
|
383
|
+
config.workflows = [...CORE_WORKFLOWS];
|
|
384
|
+
// Preserve delivery setting
|
|
385
|
+
saveGlobalConfig(config);
|
|
386
|
+
console.log('Config updated. Run `superpowers update` in your projects to apply.');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (preset) {
|
|
390
|
+
console.error(`Error: Unknown profile preset "${preset}". Available presets: core`);
|
|
391
|
+
process.exitCode = 1;
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
// Non-interactive check
|
|
395
|
+
if (!process.stdout.isTTY) {
|
|
396
|
+
console.error('Interactive mode required. Use `superpowers config profile core` or set config via environment/flags.');
|
|
397
|
+
process.exitCode = 1;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// Interactive picker
|
|
401
|
+
const { select, checkbox, confirm } = await import('@inquirer/prompts');
|
|
402
|
+
const chalk = (await import('chalk')).default;
|
|
403
|
+
try {
|
|
404
|
+
const config = getGlobalConfig();
|
|
405
|
+
const currentState = resolveCurrentProfileState(config);
|
|
406
|
+
console.log(chalk.bold('\nCurrent profile settings'));
|
|
407
|
+
console.log(` Delivery: ${currentState.delivery}`);
|
|
408
|
+
console.log(` Workflows: ${formatWorkflowSummary(currentState.workflows, currentState.profile)}`);
|
|
409
|
+
console.log(chalk.dim(' Delivery = where workflows are installed (skills, commands, or both)'));
|
|
410
|
+
console.log(chalk.dim(' Workflows = which actions are available (propose, explore, apply, etc.)'));
|
|
411
|
+
console.log();
|
|
412
|
+
const action = await select({
|
|
413
|
+
message: 'What do you want to configure?',
|
|
414
|
+
choices: [
|
|
415
|
+
{
|
|
416
|
+
value: 'both',
|
|
417
|
+
name: 'Delivery and workflows',
|
|
418
|
+
description: 'Update install mode and available actions together',
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
value: 'delivery',
|
|
422
|
+
name: 'Delivery only',
|
|
423
|
+
description: 'Change where workflows are installed',
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
value: 'workflows',
|
|
427
|
+
name: 'Workflows only',
|
|
428
|
+
description: 'Change which workflow actions are available',
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
value: 'keep',
|
|
432
|
+
name: 'Keep current settings (exit)',
|
|
433
|
+
description: 'Leave configuration unchanged and exit',
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
});
|
|
437
|
+
if (action === 'keep') {
|
|
438
|
+
console.log('No config changes.');
|
|
439
|
+
maybeWarnConfigDrift(process.cwd(), currentState, chalk.yellow);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const nextState = {
|
|
443
|
+
profile: currentState.profile,
|
|
444
|
+
delivery: currentState.delivery,
|
|
445
|
+
workflows: [...currentState.workflows],
|
|
446
|
+
};
|
|
447
|
+
if (action === 'both' || action === 'delivery') {
|
|
448
|
+
const deliveryChoices = [
|
|
449
|
+
{
|
|
450
|
+
value: 'both',
|
|
451
|
+
name: 'Both (skills + commands)',
|
|
452
|
+
description: 'Install workflows as both skills and slash commands',
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
value: 'skills',
|
|
456
|
+
name: 'Skills only',
|
|
457
|
+
description: 'Install workflows only as skills',
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
value: 'commands',
|
|
461
|
+
name: 'Commands only',
|
|
462
|
+
description: 'Install workflows only as slash commands',
|
|
463
|
+
},
|
|
464
|
+
];
|
|
465
|
+
for (const choice of deliveryChoices) {
|
|
466
|
+
if (choice.value === currentState.delivery) {
|
|
467
|
+
choice.name += ' [current]';
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
nextState.delivery = await select({
|
|
471
|
+
message: 'Delivery mode (how workflows are installed):',
|
|
472
|
+
choices: deliveryChoices,
|
|
473
|
+
default: currentState.delivery,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
if (action === 'both' || action === 'workflows') {
|
|
477
|
+
const formatWorkflowChoice = (workflow) => {
|
|
478
|
+
const metadata = WORKFLOW_PROMPT_META[workflow] ?? {
|
|
479
|
+
name: workflow,
|
|
480
|
+
description: `Workflow: ${workflow}`,
|
|
481
|
+
};
|
|
482
|
+
return {
|
|
483
|
+
value: workflow,
|
|
484
|
+
name: metadata.name,
|
|
485
|
+
description: metadata.description,
|
|
486
|
+
short: metadata.name,
|
|
487
|
+
checked: currentState.workflows.includes(workflow),
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
const selectedWorkflows = await checkbox({
|
|
491
|
+
message: 'Select workflows to make available:',
|
|
492
|
+
instructions: 'Space to toggle, Enter to confirm',
|
|
493
|
+
pageSize: ALL_WORKFLOWS.length,
|
|
494
|
+
theme: {
|
|
495
|
+
icon: {
|
|
496
|
+
checked: '[x]',
|
|
497
|
+
unchecked: '[ ]',
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
choices: ALL_WORKFLOWS.map(formatWorkflowChoice),
|
|
501
|
+
});
|
|
502
|
+
nextState.workflows = selectedWorkflows;
|
|
503
|
+
nextState.profile = deriveProfileFromWorkflowSelection(selectedWorkflows);
|
|
504
|
+
}
|
|
505
|
+
const diff = diffProfileState(currentState, nextState);
|
|
506
|
+
if (!diff.hasChanges) {
|
|
507
|
+
console.log('No config changes.');
|
|
508
|
+
maybeWarnConfigDrift(process.cwd(), nextState, chalk.yellow);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
console.log(chalk.bold('\nConfig changes:'));
|
|
512
|
+
for (const line of diff.lines) {
|
|
513
|
+
console.log(` ${line}`);
|
|
514
|
+
}
|
|
515
|
+
console.log();
|
|
516
|
+
config.profile = nextState.profile;
|
|
517
|
+
config.delivery = nextState.delivery;
|
|
518
|
+
config.workflows = nextState.workflows;
|
|
519
|
+
saveGlobalConfig(config);
|
|
520
|
+
// Check if inside an Superpowers project
|
|
521
|
+
const projectDir = process.cwd();
|
|
522
|
+
const superpowersDir = path.join(projectDir, SUPERPOWERS_DIR_NAME);
|
|
523
|
+
if (fs.existsSync(superpowersDir)) {
|
|
524
|
+
const applyNow = await confirm({
|
|
525
|
+
message: 'Apply changes to this project now?',
|
|
526
|
+
default: true,
|
|
527
|
+
});
|
|
528
|
+
if (applyNow) {
|
|
529
|
+
try {
|
|
530
|
+
execSync('npx superpowers update', { stdio: 'inherit', cwd: projectDir });
|
|
531
|
+
console.log('Run `superpowers update` in your other projects to apply.');
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
console.error('`superpowers update` failed. Please run it manually to apply the profile changes.');
|
|
535
|
+
process.exitCode = 1;
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
console.log('Config updated. Run `superpowers update` in your projects to apply.');
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
if (isPromptCancellationError(error)) {
|
|
544
|
+
console.log('Config profile cancelled.');
|
|
545
|
+
process.exitCode = 130;
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
//# sourceMappingURL=config.js.map
|