prizmkit 1.1.58 → 1.1.61
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/bin/create-prizmkit.js +8 -6
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/agent-adapter.js +38 -0
- package/bundled/adapters/codex/paths.js +27 -0
- package/bundled/adapters/codex/rules-adapter.js +30 -0
- package/bundled/adapters/codex/settings-adapter.js +27 -0
- package/bundled/adapters/codex/skill-adapter.js +65 -0
- package/bundled/adapters/codex/team-adapter.js +37 -0
- package/bundled/agents/prizm-dev-team-dev.md +6 -5
- package/bundled/dev-pipeline/.env.example +2 -1
- package/bundled/dev-pipeline/README.md +10 -7
- package/bundled/dev-pipeline/lib/common.sh +278 -37
- package/bundled/dev-pipeline/run-bugfix.sh +10 -61
- package/bundled/dev-pipeline/run-feature.sh +10 -78
- package/bundled/dev-pipeline/run-recovery.sh +10 -46
- package/bundled/dev-pipeline/run-refactor.sh +10 -61
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/utils.py +6 -4
- package/bundled/dev-pipeline-windows/.env.example +28 -0
- package/bundled/dev-pipeline-windows/README.md +30 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
- package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
- package/bundled/rules/prizm/prizm-documentation.md +2 -4
- package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +24 -16
- package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/feature-planner/SKILL.md +9 -2
- package/bundled/skills/prizmkit-implement/SKILL.md +1 -0
- package/bundled/skills/prizmkit-init/SKILL.md +8 -7
- package/bundled/skills/prizmkit-init/references/config-schema.md +3 -1
- package/bundled/skills/prizmkit-plan/SKILL.md +2 -0
- package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +6 -1
- package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +2 -2
- package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +2 -1
- package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +2 -2
- package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -0
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +2 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +2 -0
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
- package/bundled/skills-windows/app-planner/SKILL.md +639 -0
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
- package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
- package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
- package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
- package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +70 -0
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
- package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
- package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
- package/bundled/templates/hooks/diff-prizm-docs.sh +1 -1
- package/bundled/templates/hooks/prizm-pre-commit.sh +53 -4
- package/bundled/templates/hooks/validate-prizm-docs.sh +62 -7
- package/bundled/templates/project-memory-template.md +2 -1
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +167 -50
- package/src/detect-platform.js +15 -9
- package/src/external-skills.js +26 -19
- package/src/index.js +28 -10
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +38 -0
- package/src/prompts.js +50 -9
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +447 -114
- package/src/upgrade.js +87 -44
package/src/config.js
CHANGED
|
@@ -31,8 +31,11 @@ import {
|
|
|
31
31
|
installProjectMemory,
|
|
32
32
|
installPipeline,
|
|
33
33
|
installGitignore,
|
|
34
|
+
installGitHook,
|
|
35
|
+
installPrizmkitScripts,
|
|
34
36
|
resolveSkillList,
|
|
35
37
|
resolvePipelineFileList,
|
|
38
|
+
resolveRuleNamesForRuntime,
|
|
36
39
|
} from './scaffold.js';
|
|
37
40
|
import {
|
|
38
41
|
removeSkillFiles,
|
|
@@ -44,11 +47,14 @@ import { detectPlatform } from './detect-platform.js';
|
|
|
44
47
|
import {
|
|
45
48
|
selectPlatform,
|
|
46
49
|
selectAiCli,
|
|
50
|
+
selectRuntime,
|
|
47
51
|
selectSkillSuite,
|
|
48
52
|
selectRulesPreset,
|
|
49
53
|
confirmTeamMode,
|
|
50
54
|
confirmPipeline,
|
|
51
55
|
} from './prompts.js';
|
|
56
|
+
import { expandPlatforms, isKnownPlatform, platformLabel, projectMemoryFile } from './platforms.js';
|
|
57
|
+
import { normalizeRuntime, runtimeLabel } from './runtimes.js';
|
|
52
58
|
|
|
53
59
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
54
60
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -57,36 +63,53 @@ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-
|
|
|
57
63
|
// Helpers
|
|
58
64
|
// ============================================================
|
|
59
65
|
|
|
66
|
+
async function removeGeneratedCodexConfig(projectRoot, dryRun) {
|
|
67
|
+
const configPath = path.join(projectRoot, '.codex', 'config.toml');
|
|
68
|
+
if (!await fs.pathExists(configPath)) return;
|
|
69
|
+
|
|
70
|
+
const content = await fs.readFile(configPath, 'utf8');
|
|
71
|
+
if (!content.includes('Generated by PrizmKit for Codex CLI project configuration')) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (dryRun) {
|
|
76
|
+
console.log(chalk.gray(' [dry-run] remove .codex/config.toml'));
|
|
77
|
+
} else {
|
|
78
|
+
await fs.remove(configPath);
|
|
79
|
+
console.log(chalk.red(' ✗ removed .codex/config.toml'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
60
83
|
/**
|
|
61
84
|
* Detect which platforms are actually installed on filesystem.
|
|
62
85
|
*/
|
|
63
86
|
async function detectInstalledPlatforms(projectRoot) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
const hasPlatformFiles = async (dir) => {
|
|
88
|
+
if (!await fs.pathExists(dir)) return false;
|
|
89
|
+
try {
|
|
90
|
+
return (await fs.readdir(dir)).length > 0;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
68
95
|
|
|
96
|
+
const hasClaude = await hasPlatformFiles(path.join(projectRoot, '.claude', 'commands'))
|
|
97
|
+
|| await hasPlatformFiles(path.join(projectRoot, '.claude', 'agents'));
|
|
98
|
+
const hasCodeBuddy = await hasPlatformFiles(path.join(projectRoot, '.codebuddy', 'skills'))
|
|
99
|
+
|| await hasPlatformFiles(path.join(projectRoot, '.codebuddy', 'agents'));
|
|
100
|
+
const hasCodex = await hasPlatformFiles(path.join(projectRoot, '.agents', 'skills'))
|
|
101
|
+
|| await hasPlatformFiles(path.join(projectRoot, '.codex', 'agents'));
|
|
102
|
+
|
|
103
|
+
if (hasClaude && hasCodeBuddy && hasCodex) return 'all';
|
|
69
104
|
if (hasClaude && hasCodeBuddy) return 'both';
|
|
105
|
+
if (hasCodex) return 'codex';
|
|
70
106
|
if (hasCodeBuddy) return 'codebuddy';
|
|
71
107
|
if (hasClaude) return 'claude';
|
|
72
108
|
return null;
|
|
73
109
|
}
|
|
74
110
|
|
|
75
|
-
/**
|
|
76
|
-
* Expand a platform string to an array of individual platforms.
|
|
77
|
-
*/
|
|
78
|
-
function expandPlatforms(platform) {
|
|
79
|
-
return platform === 'both' ? ['codebuddy', 'claude'] : [platform];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
111
|
function getProjectMemoryFile(platform) {
|
|
83
|
-
|
|
84
|
-
if (platform === 'codebuddy') return 'CODEBUDDY.md';
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getCounterpartPlatform(platform) {
|
|
89
|
-
return platform === 'claude' ? 'codebuddy' : platform === 'codebuddy' ? 'claude' : null;
|
|
112
|
+
return projectMemoryFile(platform);
|
|
90
113
|
}
|
|
91
114
|
|
|
92
115
|
async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms, projectRoot, dryRun) {
|
|
@@ -96,10 +119,7 @@ async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms,
|
|
|
96
119
|
const sourcePath = path.join(projectRoot, sourceFile);
|
|
97
120
|
if (!await fs.pathExists(sourcePath)) return;
|
|
98
121
|
|
|
99
|
-
const
|
|
100
|
-
const targetPlatform = (counterpart && newPlatforms.includes(counterpart))
|
|
101
|
-
? counterpart
|
|
102
|
-
: newPlatforms.find(p => p !== droppedPlatform);
|
|
122
|
+
const targetPlatform = newPlatforms.find(p => p !== droppedPlatform) || newPlatforms[0];
|
|
103
123
|
|
|
104
124
|
if (!targetPlatform || targetPlatform === droppedPlatform) return;
|
|
105
125
|
|
|
@@ -138,9 +158,6 @@ async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms,
|
|
|
138
158
|
console.log(chalk.green(` ✓ merged ${sourceFile} content into ${targetFile}`));
|
|
139
159
|
}
|
|
140
160
|
|
|
141
|
-
const platformLabel = (p) => p === 'both' ? 'CodeBuddy + Claude Code'
|
|
142
|
-
: p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
|
|
143
|
-
|
|
144
161
|
/**
|
|
145
162
|
* Remove all PrizmKit-owned platform-specific files for a single platform.
|
|
146
163
|
* Selective removal — only removes known PrizmKit files, leaves user-created files intact.
|
|
@@ -166,15 +183,28 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
|
|
|
166
183
|
}
|
|
167
184
|
|
|
168
185
|
// Settings
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
if (platform === 'codex') {
|
|
187
|
+
await removeGeneratedCodexConfig(projectRoot, dryRun);
|
|
188
|
+
const legacySettingsFile = path.join(projectRoot, '.codex', 'prizmkit-settings.json');
|
|
189
|
+
if (await fs.pathExists(legacySettingsFile)) {
|
|
190
|
+
if (dryRun) {
|
|
191
|
+
console.log(chalk.gray(' [dry-run] remove .codex/prizmkit-settings.json (legacy)'));
|
|
192
|
+
} else {
|
|
193
|
+
await fs.remove(legacySettingsFile);
|
|
194
|
+
console.log(chalk.red(' ✗ removed .codex/prizmkit-settings.json (legacy)'));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
const settingsFile = platform === 'claude'
|
|
199
|
+
? path.join(projectRoot, '.claude', 'settings.json')
|
|
200
|
+
: path.join(projectRoot, '.codebuddy', 'settings.json');
|
|
201
|
+
if (await fs.pathExists(settingsFile)) {
|
|
202
|
+
if (dryRun) {
|
|
203
|
+
console.log(chalk.gray(` [dry-run] remove ${path.relative(projectRoot, settingsFile)}`));
|
|
204
|
+
} else {
|
|
205
|
+
await fs.remove(settingsFile);
|
|
206
|
+
console.log(chalk.red(` ✗ removed ${path.relative(projectRoot, settingsFile)}`));
|
|
207
|
+
}
|
|
178
208
|
}
|
|
179
209
|
}
|
|
180
210
|
|
|
@@ -200,10 +230,21 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
|
|
|
200
230
|
console.log(chalk.red(' ✗ removed ~/.codebuddy/teams/prizm-dev-team/'));
|
|
201
231
|
}
|
|
202
232
|
}
|
|
233
|
+
} else if (platform === 'codex') {
|
|
234
|
+
const teamInfo = path.join(projectRoot, '.codex', 'team-info.json');
|
|
235
|
+
if (await fs.pathExists(teamInfo)) {
|
|
236
|
+
if (dryRun) {
|
|
237
|
+
console.log(chalk.gray(' [dry-run] remove .codex/team-info.json'));
|
|
238
|
+
} else {
|
|
239
|
+
await fs.remove(teamInfo);
|
|
240
|
+
console.log(chalk.red(' ✗ removed .codex/team-info.json'));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
203
243
|
}
|
|
204
244
|
|
|
205
245
|
// Project memory file
|
|
206
|
-
const memoryFile = platform
|
|
246
|
+
const memoryFile = projectMemoryFile(platform);
|
|
247
|
+
if (!memoryFile) return;
|
|
207
248
|
const memoryPath = path.join(projectRoot, memoryFile);
|
|
208
249
|
if (await fs.pathExists(memoryPath)) {
|
|
209
250
|
if (dryRun) {
|
|
@@ -225,6 +266,48 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
|
|
|
225
266
|
console.log(chalk.red(' ✗ removed .claude/command-assets/'));
|
|
226
267
|
}
|
|
227
268
|
}
|
|
269
|
+
} else if (platform === 'codex') {
|
|
270
|
+
const agentsSkillsDir = path.join(projectRoot, '.agents', 'skills');
|
|
271
|
+
if (await fs.pathExists(agentsSkillsDir)) {
|
|
272
|
+
if (dryRun) {
|
|
273
|
+
console.log(chalk.gray(' [dry-run] remove .agents/skills/ if empty'));
|
|
274
|
+
} else if ((await fs.readdir(agentsSkillsDir)).length === 0) {
|
|
275
|
+
await fs.remove(agentsSkillsDir);
|
|
276
|
+
console.log(chalk.red(' ✗ removed .agents/skills/'));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const agentsDir = path.join(projectRoot, '.agents');
|
|
280
|
+
if (await fs.pathExists(agentsDir)) {
|
|
281
|
+
if (dryRun) {
|
|
282
|
+
console.log(chalk.gray(' [dry-run] remove .agents/ if empty'));
|
|
283
|
+
} else if ((await fs.readdir(agentsDir)).length === 0) {
|
|
284
|
+
await fs.remove(agentsDir);
|
|
285
|
+
console.log(chalk.red(' ✗ removed .agents/'));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const codexAgentsDir = path.join(projectRoot, '.codex', 'agents');
|
|
290
|
+
if (await fs.pathExists(codexAgentsDir)) {
|
|
291
|
+
if (dryRun) {
|
|
292
|
+
console.log(chalk.gray(' [dry-run] remove .codex/agents/ if empty'));
|
|
293
|
+
} else if ((await fs.readdir(codexAgentsDir)).length === 0) {
|
|
294
|
+
await fs.remove(codexAgentsDir);
|
|
295
|
+
console.log(chalk.red(' ✗ removed .codex/agents/'));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const codexDir = path.join(projectRoot, '.codex');
|
|
300
|
+
if (await fs.pathExists(codexDir)) {
|
|
301
|
+
if (dryRun) {
|
|
302
|
+
console.log(chalk.gray(' [dry-run] remove .codex/ if empty'));
|
|
303
|
+
} else {
|
|
304
|
+
const remaining = await fs.readdir(codexDir);
|
|
305
|
+
if (remaining.length === 0) {
|
|
306
|
+
await fs.remove(codexDir);
|
|
307
|
+
console.log(chalk.red(' ✗ removed .codex/'));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
228
311
|
}
|
|
229
312
|
}
|
|
230
313
|
|
|
@@ -265,11 +348,13 @@ export async function runConfig(directory, options = {}) {
|
|
|
265
348
|
try { userConfig = await fs.readJSON(configPath); } catch { /* ignore */ }
|
|
266
349
|
}
|
|
267
350
|
|
|
268
|
-
// Filesystem
|
|
269
|
-
|
|
351
|
+
// Existing manifests are the source of truth. Filesystem detection is only a
|
|
352
|
+
// legacy fallback for manifests created before platform was recorded.
|
|
353
|
+
const detectedPlatform = oldManifest.platform || await detectInstalledPlatforms(projectRoot) || 'claude';
|
|
270
354
|
|
|
271
355
|
const currentConfig = {
|
|
272
356
|
platform: detectedPlatform,
|
|
357
|
+
runtime: normalizeRuntime(oldManifest.runtime || 'unix'),
|
|
273
358
|
suite: oldManifest.suite || 'core',
|
|
274
359
|
rules: oldManifest.options?.rules || 'recommended',
|
|
275
360
|
team: oldManifest.options?.team ?? true,
|
|
@@ -277,9 +362,14 @@ export async function runConfig(directory, options = {}) {
|
|
|
277
362
|
aiCli: userConfig.ai_cli || oldManifest.options?.aiCli || '',
|
|
278
363
|
};
|
|
279
364
|
|
|
365
|
+
if (options.platform !== undefined && !isKnownPlatform(options.platform)) {
|
|
366
|
+
throw new Error(`Unknown platform "${options.platform}". Expected codebuddy, claude, codex, or all.`);
|
|
367
|
+
}
|
|
368
|
+
|
|
280
369
|
// Display current config
|
|
281
370
|
console.log(chalk.bold(' 当前配置:'));
|
|
282
371
|
console.log(` 平台: ${chalk.cyan(platformLabel(currentConfig.platform))}`);
|
|
372
|
+
console.log(` 运行系统: ${chalk.cyan(runtimeLabel(currentConfig.runtime))}`);
|
|
283
373
|
console.log(` AI CLI: ${currentConfig.aiCli ? chalk.cyan(currentConfig.aiCli) : chalk.gray('(未设置)')}`);
|
|
284
374
|
console.log(` 技能套件: ${chalk.cyan(currentConfig.suite)}`);
|
|
285
375
|
console.log(` 规则: ${chalk.cyan(currentConfig.rules)}`);
|
|
@@ -290,18 +380,20 @@ export async function runConfig(directory, options = {}) {
|
|
|
290
380
|
// ── Phase 2: PROMPT for new config ───────────────────────────────────────
|
|
291
381
|
|
|
292
382
|
let newConfig;
|
|
383
|
+
const optionRuntime = options.runtime !== undefined ? normalizeRuntime(options.runtime) : undefined;
|
|
293
384
|
|
|
294
385
|
if (nonInteractive) {
|
|
295
386
|
// Non-interactive: only change what's explicitly specified via CLI options
|
|
296
|
-
if (!options.platform && !options.skills && !options.rules
|
|
387
|
+
if (!options.platform && !options.skills && !options.rules && options.runtime === undefined
|
|
297
388
|
&& options.aiCli === undefined && options.team === undefined && options.pipeline === undefined) {
|
|
298
389
|
console.log(chalk.yellow(' ⚠ 非交互式模式下未指定任何变更。'));
|
|
299
|
-
console.log(chalk.gray(' 使用 --platform, --skills, --rules, --ai-cli, --team/--no-team, --pipeline/--no-pipeline'));
|
|
390
|
+
console.log(chalk.gray(' 使用 --platform, --runtime, --skills, --rules, --ai-cli, --team/--no-team, --pipeline/--no-pipeline'));
|
|
300
391
|
return;
|
|
301
392
|
}
|
|
302
393
|
|
|
303
394
|
newConfig = {
|
|
304
395
|
platform: options.platform || currentConfig.platform,
|
|
396
|
+
runtime: optionRuntime || currentConfig.runtime,
|
|
305
397
|
suite: options.skills || currentConfig.suite,
|
|
306
398
|
rules: options.rules || currentConfig.rules,
|
|
307
399
|
team: options.team !== undefined ? options.team : currentConfig.team,
|
|
@@ -315,6 +407,7 @@ export async function runConfig(directory, options = {}) {
|
|
|
315
407
|
try {
|
|
316
408
|
console.log(chalk.bold(' 选择新配置(按 Enter 保持当前值):\n'));
|
|
317
409
|
|
|
410
|
+
const newRuntime = optionRuntime || await selectRuntime(currentConfig.runtime);
|
|
318
411
|
const newPlatform = await selectPlatform(currentConfig.platform, detected);
|
|
319
412
|
const newAiCli = await selectAiCli(currentConfig.aiCli, detected);
|
|
320
413
|
|
|
@@ -326,6 +419,7 @@ export async function runConfig(directory, options = {}) {
|
|
|
326
419
|
|
|
327
420
|
newConfig = {
|
|
328
421
|
platform: newPlatform,
|
|
422
|
+
runtime: newRuntime,
|
|
329
423
|
suite: newSuite,
|
|
330
424
|
rules: newRules,
|
|
331
425
|
team: newTeam,
|
|
@@ -341,12 +435,19 @@ export async function runConfig(directory, options = {}) {
|
|
|
341
435
|
}
|
|
342
436
|
}
|
|
343
437
|
|
|
438
|
+
if (!isKnownPlatform(newConfig.platform)) {
|
|
439
|
+
throw new Error(`Unknown platform "${newConfig.platform}". Expected codebuddy, claude, codex, or all.`);
|
|
440
|
+
}
|
|
441
|
+
|
|
344
442
|
// ── Phase 3: COMPUTE DIFF ────────────────────────────────────────────────
|
|
345
443
|
|
|
346
444
|
const changes = [];
|
|
347
445
|
if (newConfig.platform !== currentConfig.platform) {
|
|
348
446
|
changes.push(`平台: ${platformLabel(currentConfig.platform)} → ${platformLabel(newConfig.platform)}`);
|
|
349
447
|
}
|
|
448
|
+
if (newConfig.runtime !== currentConfig.runtime) {
|
|
449
|
+
changes.push(`运行系统: ${runtimeLabel(currentConfig.runtime)} → ${runtimeLabel(newConfig.runtime)}`);
|
|
450
|
+
}
|
|
350
451
|
if (newConfig.aiCli !== currentConfig.aiCli) {
|
|
351
452
|
changes.push(`AI CLI: ${currentConfig.aiCli || '(未设置)'} → ${newConfig.aiCli || '(未设置)'}`);
|
|
352
453
|
}
|
|
@@ -419,9 +520,8 @@ export async function runConfig(directory, options = {}) {
|
|
|
419
520
|
const agentsDir = getAgentsDir();
|
|
420
521
|
const newAgentFiles = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
|
|
421
522
|
const rulesMeta = await loadRulesMetadata();
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
const newPipelineFiles = newConfig.pipeline ? resolvePipelineFileList() : [];
|
|
523
|
+
const newRuleFiles = resolveRuleNamesForRuntime(rulesMeta, newConfig.rules, newConfig.runtime).map(name => `${name}.md`);
|
|
524
|
+
const newPipelineFiles = newConfig.pipeline ? resolvePipelineFileList(newConfig.runtime) : [];
|
|
425
525
|
|
|
426
526
|
// 5a. Migrate project memory before removing dropped platforms
|
|
427
527
|
for (const p of platformsToRemove) {
|
|
@@ -434,13 +534,15 @@ export async function runConfig(directory, options = {}) {
|
|
|
434
534
|
await removePlatformFiles(p, projectRoot, oldManifest, false);
|
|
435
535
|
}
|
|
436
536
|
|
|
437
|
-
// 5c. Handle skill/rule diff for existing platforms (suite or
|
|
438
|
-
if (newConfig.suite !== currentConfig.suite
|
|
537
|
+
// 5c. Handle skill/rule diff for existing platforms (suite, rules, or runtime changed)
|
|
538
|
+
if (newConfig.suite !== currentConfig.suite
|
|
539
|
+
|| newConfig.rules !== currentConfig.rules
|
|
540
|
+
|| newConfig.runtime !== currentConfig.runtime) {
|
|
439
541
|
const newTempManifest = buildManifest({
|
|
440
542
|
version: pkg.version, platform: newConfig.platform, suite: newConfig.suite,
|
|
441
543
|
skills: newSkillList, agents: newAgentFiles, rules: newRuleFiles,
|
|
442
544
|
pipeline: newPipelineFiles, team: newConfig.team, aiCli: newConfig.aiCli,
|
|
443
|
-
rulesPreset: newConfig.rules, extras: oldManifest?.files?.extras || [],
|
|
545
|
+
runtime: newConfig.runtime, rulesPreset: newConfig.rules, extras: oldManifest?.files?.extras || [],
|
|
444
546
|
});
|
|
445
547
|
const diff = diffManifest(oldManifest, newTempManifest);
|
|
446
548
|
|
|
@@ -460,6 +562,7 @@ export async function runConfig(directory, options = {}) {
|
|
|
460
562
|
// 5d. Install/update files for new + existing platforms
|
|
461
563
|
const allTargetPlatforms = [...platformsToAdd, ...platformsToUpdate];
|
|
462
564
|
const needsReinstall = newConfig.platform !== currentConfig.platform
|
|
565
|
+
|| newConfig.runtime !== currentConfig.runtime
|
|
463
566
|
|| newConfig.suite !== currentConfig.suite
|
|
464
567
|
|| newConfig.rules !== currentConfig.rules
|
|
465
568
|
|| newConfig.team !== currentConfig.team;
|
|
@@ -471,13 +574,13 @@ export async function runConfig(directory, options = {}) {
|
|
|
471
574
|
console.log(chalk.bold(`\n ${isNew ? '安装' : '更新'} ${platformLabel(p)} 环境...\n`));
|
|
472
575
|
|
|
473
576
|
console.log(chalk.blue(' Skills:'));
|
|
474
|
-
await installSkills(p, newSkillList, projectRoot, false);
|
|
577
|
+
await installSkills(p, newSkillList, projectRoot, false, newConfig.runtime);
|
|
475
578
|
|
|
476
579
|
console.log(chalk.blue('\n Agents:'));
|
|
477
580
|
await installAgents(p, projectRoot, false);
|
|
478
581
|
|
|
479
582
|
console.log(chalk.blue('\n Settings & Rules:'));
|
|
480
|
-
await installSettings(p, projectRoot, { pipeline: newConfig.pipeline, rules: newConfig.rules }, false);
|
|
583
|
+
await installSettings(p, projectRoot, { pipeline: newConfig.pipeline, rules: newConfig.rules }, false, newConfig.runtime);
|
|
481
584
|
|
|
482
585
|
if (isNew) {
|
|
483
586
|
console.log(chalk.blue('\n Project Memory:'));
|
|
@@ -492,10 +595,13 @@ export async function runConfig(directory, options = {}) {
|
|
|
492
595
|
}
|
|
493
596
|
|
|
494
597
|
// 5e. Pipeline changes
|
|
495
|
-
if (newConfig.pipeline !== currentConfig.pipeline) {
|
|
598
|
+
if (newConfig.pipeline !== currentConfig.pipeline || (newConfig.pipeline && newConfig.runtime !== currentConfig.runtime)) {
|
|
496
599
|
if (newConfig.pipeline) {
|
|
497
600
|
console.log(chalk.blue('\n 安装 Pipeline:'));
|
|
498
|
-
await installPipeline(projectRoot, false
|
|
601
|
+
await installPipeline(projectRoot, false, {
|
|
602
|
+
runtime: newConfig.runtime,
|
|
603
|
+
previousRuntime: currentConfig.runtime,
|
|
604
|
+
});
|
|
499
605
|
} else {
|
|
500
606
|
console.log(chalk.blue('\n 移除 Pipeline 框架文件(保留 state/):'));
|
|
501
607
|
const oldPipelineFiles = oldManifest?.files?.pipeline || [];
|
|
@@ -509,7 +615,16 @@ export async function runConfig(directory, options = {}) {
|
|
|
509
615
|
console.log(chalk.blue('\n Gitignore:'));
|
|
510
616
|
await installGitignore(projectRoot, { pipeline: newConfig.pipeline }, false);
|
|
511
617
|
|
|
512
|
-
// 5g.
|
|
618
|
+
// 5g. Runtime-aware git hook and PrizmKit helper scripts
|
|
619
|
+
if (newConfig.runtime !== currentConfig.runtime) {
|
|
620
|
+
console.log(chalk.blue('\n Git Hook:'));
|
|
621
|
+
await installGitHook(projectRoot, false, newConfig.runtime);
|
|
622
|
+
|
|
623
|
+
console.log(chalk.blue('\n PrizmKit Scripts:'));
|
|
624
|
+
await installPrizmkitScripts(projectRoot, false, newConfig.runtime);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// 5h. Update .prizmkit/config.json (ai_cli)
|
|
513
628
|
if (newConfig.aiCli !== currentConfig.aiCli) {
|
|
514
629
|
const prizmkitDir = path.join(projectRoot, '.prizmkit');
|
|
515
630
|
await fs.ensureDir(prizmkitDir);
|
|
@@ -530,6 +645,7 @@ export async function runConfig(directory, options = {}) {
|
|
|
530
645
|
const newManifest = buildManifest({
|
|
531
646
|
version: pkg.version,
|
|
532
647
|
platform: newConfig.platform,
|
|
648
|
+
runtime: newConfig.runtime,
|
|
533
649
|
suite: newConfig.suite,
|
|
534
650
|
skills: newSkillList,
|
|
535
651
|
agents: newAgentFiles,
|
|
@@ -559,6 +675,7 @@ export async function runConfig(directory, options = {}) {
|
|
|
559
675
|
|
|
560
676
|
console.log(chalk.gray(' 新配置:'));
|
|
561
677
|
console.log(chalk.gray(` 平台: ${platformLabel(newConfig.platform)}`));
|
|
678
|
+
console.log(chalk.gray(` 运行系统: ${runtimeLabel(newConfig.runtime)}`));
|
|
562
679
|
console.log(chalk.gray(` AI CLI: ${newConfig.aiCli || '(未设置)'}`));
|
|
563
680
|
console.log(chalk.gray(` 技能套件: ${newConfig.suite}`));
|
|
564
681
|
console.log(chalk.gray(` 规则: ${newConfig.rules}`));
|
package/src/detect-platform.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { execFileSync } from 'child_process';
|
|
7
|
+
import os from 'os';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 允许检测的命令白名单(仅用于 which 检测)
|
|
10
11
|
*/
|
|
11
|
-
const ALLOWED_COMMANDS = ['cbc', 'claude'];
|
|
12
|
+
const ALLOWED_COMMANDS = ['cbc', 'claude', 'codex'];
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* 检查命令是否存在于 PATH 中
|
|
@@ -18,7 +19,8 @@ function commandExists(cmd) {
|
|
|
18
19
|
throw new Error(`Unknown command: ${cmd}`);
|
|
19
20
|
}
|
|
20
21
|
try {
|
|
21
|
-
|
|
22
|
+
const isWindows = os.platform() === 'win32';
|
|
23
|
+
execFileSync(isWindows ? 'where.exe' : 'which', [cmd], { stdio: 'ignore' });
|
|
22
24
|
return true;
|
|
23
25
|
} catch {
|
|
24
26
|
return false;
|
|
@@ -31,26 +33,30 @@ function commandExists(cmd) {
|
|
|
31
33
|
* 注意:platform 控制目录结构(.codebuddy/ 或 .claude/),
|
|
32
34
|
* suggestedCli 是实际运行的可执行命令,二者相互独立。
|
|
33
35
|
*
|
|
34
|
-
* @returns {{ cbc: boolean, claude: boolean,
|
|
36
|
+
* @returns {{ cbc: boolean, claude: boolean, codex: boolean, suggested: string, suggestedCli: string }}
|
|
35
37
|
*/
|
|
36
38
|
export function detectPlatform() {
|
|
37
39
|
const cbc = commandExists('cbc');
|
|
38
40
|
const claude = commandExists('claude');
|
|
41
|
+
const codex = commandExists('codex');
|
|
39
42
|
|
|
40
|
-
// platform
|
|
43
|
+
// platform 建议:基于目录结构
|
|
41
44
|
let suggested = 'codebuddy';
|
|
42
|
-
if (
|
|
43
|
-
suggested = '
|
|
45
|
+
if (cbc && claude && codex) {
|
|
46
|
+
suggested = 'all';
|
|
47
|
+
} else if (codex && !cbc && !claude) {
|
|
48
|
+
suggested = 'codex';
|
|
44
49
|
} else if ((claude) && !cbc) {
|
|
45
50
|
suggested = 'claude';
|
|
46
51
|
} else if (cbc && !claude) {
|
|
47
52
|
suggested = 'codebuddy';
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
// CLI 命令建议:优先 cbc,其次 claude
|
|
55
|
+
// CLI 命令建议:优先 cbc,其次 claude,再次 codex
|
|
51
56
|
let suggestedCli = '';
|
|
52
57
|
if (cbc) suggestedCli = 'cbc';
|
|
53
58
|
else if (claude) suggestedCli = 'claude';
|
|
59
|
+
else if (codex) suggestedCli = 'codex';
|
|
54
60
|
|
|
55
|
-
return { cbc, claude, suggested, suggestedCli };
|
|
56
|
-
}
|
|
61
|
+
return { cbc, claude, codex, suggested, suggestedCli };
|
|
62
|
+
}
|
package/src/external-skills.js
CHANGED
|
@@ -7,20 +7,21 @@
|
|
|
7
7
|
* - Install all: installs all skills from a repo (e.g. pbakaus/impeccable)
|
|
8
8
|
*
|
|
9
9
|
* When run non-interactively (stdio: 'pipe'), npx skills writes canonical
|
|
10
|
-
* .agents/skills/<name>/SKILL.md files.
|
|
11
|
-
*
|
|
10
|
+
* .agents/skills/<name>/SKILL.md files. Codex uses that location directly;
|
|
11
|
+
* other platforms get copied into their platform-specific skill directories.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { execSync } from 'node:child_process';
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import fs from 'fs-extra';
|
|
17
|
+
import { expandPlatforms } from './platforms.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* @param {Object} skill - Skill definition from external_skills.known
|
|
20
21
|
* skill.repo {string} - GitHub short ref (e.g. "pbakaus/impeccable") or full URL
|
|
21
22
|
* skill.name {string} - Skill name (used for display / single-skill mode)
|
|
22
23
|
* skill.installAll {boolean} - If true, install all skills from the repo
|
|
23
|
-
* @param {string} platform - 'claude' | 'codebuddy' | 'both'
|
|
24
|
+
* @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
|
|
24
25
|
* @param {string} projectRoot - Target project root
|
|
25
26
|
* @param {boolean} dryRun
|
|
26
27
|
*/
|
|
@@ -64,6 +65,9 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
|
|
|
64
65
|
for (const skillName of skillDirs) {
|
|
65
66
|
const srcDir = path.join(agentsSkillsDir, skillName);
|
|
66
67
|
const destDir = path.join(targetDir, skillName);
|
|
68
|
+
if (path.resolve(srcDir) === path.resolve(destDir)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
67
71
|
await fs.remove(destDir).catch(() => {});
|
|
68
72
|
await fs.copy(srcDir, destDir);
|
|
69
73
|
}
|
|
@@ -79,14 +83,20 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
|
|
|
79
83
|
|
|
80
84
|
for (const targetDir of getTargetDirs(platform, projectRoot)) {
|
|
81
85
|
const skillDir = path.join(targetDir, skill.name);
|
|
86
|
+
if (path.resolve(skillDir) === path.resolve(path.dirname(canonicalSkillMd))) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
82
89
|
await fs.remove(skillDir).catch(() => {});
|
|
83
90
|
await fs.ensureDir(skillDir);
|
|
84
91
|
await fs.writeFile(path.join(skillDir, 'SKILL.md'), content);
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
// Clean up npx skills artifacts after copying
|
|
89
|
-
|
|
95
|
+
// Clean up npx skills artifacts after copying. Preserve .agents/skills when
|
|
96
|
+
// Codex is selected because it is the official repository skill location.
|
|
97
|
+
if (!expandPlatforms(platform).includes('codex')) {
|
|
98
|
+
await fs.remove(path.join(projectRoot, '.agents')).catch(() => {});
|
|
99
|
+
}
|
|
90
100
|
await fs.remove(path.join(projectRoot, 'skills-lock.json')).catch(() => {});
|
|
91
101
|
}
|
|
92
102
|
|
|
@@ -95,21 +105,23 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
|
|
|
95
105
|
* Call this once after all external skills have been installed.
|
|
96
106
|
*
|
|
97
107
|
* @param {string} projectRoot
|
|
98
|
-
* @param {string} platform - 'claude' | 'codebuddy' | 'both'
|
|
108
|
+
* @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
|
|
99
109
|
*/
|
|
100
110
|
export async function cleanExternalSkillArtifacts(projectRoot, platform) {
|
|
101
111
|
const keepDirs = new Set(
|
|
102
112
|
getTargetDirs(platform, projectRoot).map(d => path.resolve(d))
|
|
103
113
|
);
|
|
104
114
|
|
|
115
|
+
const selectedPlatforms = expandPlatforms(platform);
|
|
105
116
|
const dirsToCheck = [
|
|
106
|
-
'.agents',
|
|
117
|
+
selectedPlatforms.includes('codex') ? null : '.agents',
|
|
107
118
|
'.agent',
|
|
108
119
|
'.trae',
|
|
109
120
|
'skills',
|
|
110
121
|
'.claude/skills',
|
|
111
122
|
'.codebuddy/skills',
|
|
112
|
-
|
|
123
|
+
'.codex/skills',
|
|
124
|
+
].filter(Boolean);
|
|
113
125
|
|
|
114
126
|
for (const dir of dirsToCheck) {
|
|
115
127
|
const abs = path.join(projectRoot, dir);
|
|
@@ -135,7 +147,7 @@ export async function cleanExternalSkillArtifacts(projectRoot, platform) {
|
|
|
135
147
|
}
|
|
136
148
|
|
|
137
149
|
// Remove empty parent dirs left behind (e.g. .codebuddy/ after removing .codebuddy/skills)
|
|
138
|
-
const parentDirs = ['.claude', '.codebuddy', '.trae', '.agent'];
|
|
150
|
+
const parentDirs = ['.claude', '.codebuddy', '.codex', '.trae', '.agent'];
|
|
139
151
|
for (const dir of parentDirs) {
|
|
140
152
|
const abs = path.join(projectRoot, dir);
|
|
141
153
|
if (!await fs.pathExists(abs)) continue;
|
|
@@ -155,14 +167,9 @@ export async function cleanExternalSkillArtifacts(projectRoot, platform) {
|
|
|
155
167
|
}
|
|
156
168
|
|
|
157
169
|
function getTargetDirs(platform, projectRoot) {
|
|
158
|
-
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
if (platform === 'codebuddy') {
|
|
165
|
-
return [path.join(projectRoot, '.codebuddy', 'skills')];
|
|
166
|
-
}
|
|
167
|
-
return [path.join(projectRoot, '.claude', 'skills')];
|
|
170
|
+
return expandPlatforms(platform).map(p => {
|
|
171
|
+
if (p === 'codebuddy') return path.join(projectRoot, '.codebuddy', 'skills');
|
|
172
|
+
if (p === 'codex') return path.join(projectRoot, '.agents', 'skills');
|
|
173
|
+
return path.join(projectRoot, '.claude', 'skills');
|
|
174
|
+
});
|
|
168
175
|
}
|