prizmkit 1.1.1 → 1.1.3

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.
Files changed (99) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/agent-adapter.js +18 -0
  3. package/bundled/adapters/claude/command-adapter.js +1 -27
  4. package/bundled/agents/prizm-dev-team-critic.md +2 -0
  5. package/bundled/agents/prizm-dev-team-dev.md +2 -0
  6. package/bundled/agents/prizm-dev-team-reviewer.md +2 -0
  7. package/bundled/dev-pipeline/README.md +63 -63
  8. package/bundled/dev-pipeline/assets/feature-list-example.json +1 -1
  9. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
  10. package/bundled/dev-pipeline/{launch-daemon.sh → launch-feature-daemon.sh} +33 -33
  11. package/bundled/dev-pipeline/launch-refactor-daemon.sh +454 -0
  12. package/bundled/dev-pipeline/lib/branch.sh +1 -1
  13. package/bundled/dev-pipeline/reset-feature.sh +3 -3
  14. package/bundled/dev-pipeline/reset-refactor.sh +312 -0
  15. package/bundled/dev-pipeline/{retry-bug.sh → retry-bugfix.sh} +47 -59
  16. package/bundled/dev-pipeline/retry-feature.sh +41 -54
  17. package/bundled/dev-pipeline/retry-refactor.sh +358 -0
  18. package/bundled/dev-pipeline/run-bugfix.sh +6 -0
  19. package/bundled/dev-pipeline/{run.sh → run-feature.sh} +31 -31
  20. package/bundled/dev-pipeline/run-refactor.sh +787 -0
  21. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +177 -10
  22. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +419 -0
  23. package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +393 -0
  24. package/bundled/dev-pipeline/scripts/update-refactor-status.py +726 -0
  25. package/bundled/dev-pipeline/templates/agent-prompts/critic-code-challenge.md +13 -0
  26. package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +7 -0
  27. package/bundled/dev-pipeline/templates/agent-prompts/dev-fix.md +7 -0
  28. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +26 -0
  29. package/bundled/dev-pipeline/templates/agent-prompts/dev-resume.md +5 -0
  30. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-analyze.md +5 -0
  31. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +12 -0
  32. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +29 -2
  33. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +8 -7
  34. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +11 -10
  35. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +2 -3
  36. package/bundled/dev-pipeline/templates/feature-list-schema.json +1 -1
  37. package/bundled/dev-pipeline/templates/refactor-list-schema.json +159 -0
  38. package/bundled/dev-pipeline/templates/sections/ac-verification-checklist.md +13 -0
  39. package/bundled/dev-pipeline/templates/sections/feature-context.md +1 -1
  40. package/bundled/dev-pipeline/templates/sections/phase-analyze-agent.md +9 -8
  41. package/bundled/dev-pipeline/templates/sections/phase-analyze-full.md +9 -8
  42. package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +2 -1
  43. package/bundled/dev-pipeline/templates/sections/phase-critic-code.md +8 -10
  44. package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +9 -10
  45. package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +8 -9
  46. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +7 -10
  47. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +8 -15
  48. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +7 -12
  49. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +8 -19
  50. package/bundled/dev-pipeline/templates/sections/test-failure-recovery.md +75 -0
  51. package/bundled/skills/_metadata.json +33 -6
  52. package/bundled/skills/app-planner/SKILL.md +105 -320
  53. package/bundled/skills/app-planner/assets/app-design-guide.md +101 -0
  54. package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
  55. package/bundled/skills/app-planner/references/project-brief-guide.md +49 -80
  56. package/bundled/skills/bug-fix-workflow/SKILL.md +2 -2
  57. package/bundled/skills/bug-planner/SKILL.md +68 -5
  58. package/bundled/skills/bug-planner/scripts/validate-bug-list.py +3 -2
  59. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +19 -5
  60. package/bundled/skills/{dev-pipeline-launcher → feature-pipeline-launcher}/SKILL.md +32 -32
  61. package/bundled/skills/feature-planner/SKILL.md +337 -0
  62. package/bundled/skills/{app-planner → feature-planner}/assets/evaluation-guide.md +4 -4
  63. package/bundled/skills/{app-planner → feature-planner}/assets/planning-guide.md +3 -171
  64. package/bundled/skills/{app-planner → feature-planner}/references/browser-interaction.md +6 -5
  65. package/bundled/skills/feature-planner/references/decomposition-patterns.md +75 -0
  66. package/bundled/skills/{app-planner → feature-planner}/references/error-recovery.md +8 -8
  67. package/bundled/skills/{app-planner → feature-planner}/references/incremental-feature-planning.md +1 -1
  68. package/bundled/skills/{app-planner/references/new-app-planning.md → feature-planner/references/new-project-planning.md} +1 -1
  69. package/bundled/skills/{app-planner → feature-planner}/scripts/validate-and-generate.py +4 -4
  70. package/bundled/skills/feature-workflow/SKILL.md +23 -23
  71. package/bundled/skills/prizm-kit/SKILL.md +1 -3
  72. package/bundled/skills/prizmkit-analyze/SKILL.md +2 -5
  73. package/bundled/skills/prizmkit-code-review/SKILL.md +2 -2
  74. package/bundled/skills/prizmkit-committer/SKILL.md +4 -8
  75. package/bundled/skills/prizmkit-deploy/SKILL.md +1 -5
  76. package/bundled/skills/prizmkit-implement/SKILL.md +3 -50
  77. package/bundled/skills/prizmkit-init/SKILL.md +5 -77
  78. package/bundled/skills/prizmkit-plan/SKILL.md +1 -12
  79. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +6 -24
  80. package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +21 -0
  81. package/bundled/skills/prizmkit-retrospective/SKILL.md +12 -117
  82. package/bundled/skills/recovery-workflow/SKILL.md +166 -316
  83. package/bundled/skills/recovery-workflow/evals/evals.json +29 -13
  84. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +232 -274
  85. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +352 -0
  86. package/bundled/skills/refactor-planner/SKILL.md +436 -0
  87. package/bundled/skills/refactor-planner/assets/planning-guide.md +292 -0
  88. package/bundled/skills/refactor-planner/references/behavior-preservation.md +301 -0
  89. package/bundled/skills/refactor-planner/references/refactor-scoping-guide.md +221 -0
  90. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +786 -0
  91. package/bundled/skills/refactor-workflow/SKILL.md +299 -319
  92. package/package.json +1 -1
  93. package/src/clean.js +3 -3
  94. package/src/scaffold.js +6 -6
  95. package/bundled/skills/prizmkit-plan/assets/spec-template.md +0 -56
  96. package/bundled/skills/prizmkit-plan/references/clarify-guide.md +0 -67
  97. package/src/config.js +0 -504
  98. package/src/prompts.js +0 -210
  99. /package/bundled/skills/{dev-pipeline-launcher → feature-pipeline-launcher}/scripts/preflight-check.py +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/clean.js CHANGED
@@ -119,10 +119,10 @@ export async function runClean(directory, options = {}) {
119
119
  '.prizm-docs', // AI-generated project context docs
120
120
  'CODEBUDDY.md',
121
121
  'CLAUDE.md',
122
- 'feature-list.json', // app-planner output
122
+ 'feature-list.json', // feature-planner output
123
123
  'bug-fix-list.json', // bug-planner output
124
- 'project-brief.md', // app-planner project brief
125
- 'project-conventions.json', // app-planner coding conventions
124
+ 'project-brief.md', // feature-planner project brief
125
+ 'project-conventions.json', // feature-planner coding conventions
126
126
  path.join('.codebuddy', 'settings.json'),
127
127
  path.join('.claude', 'settings.json'),
128
128
  path.join('.claude', 'team-info.json'),
package/src/scaffold.js CHANGED
@@ -109,7 +109,7 @@ export async function installSkills(platform, skills, projectRoot, dryRun) {
109
109
  }
110
110
 
111
111
  for (const skillName of skills) {
112
- // Determine the category subdirectory (prizmkit-skill, prizmkit-tool-skill, orchestration-skill)
112
+ // Determine the category subdirectory (prizmkit-skill, orchestration-skill)
113
113
  const category = skillCategories[skillName];
114
114
  if (!category) {
115
115
  console.warn(` ⚠ Skill ${skillName} has no category mapping in metadata, skipping`);
@@ -414,7 +414,7 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
414
414
  'Bash(jq *)',
415
415
  ];
416
416
  if (options.pipeline) {
417
- permissions.push('Bash(./dev-pipeline/run.sh *)', 'Bash(./dev-pipeline/launch-daemon.sh *)');
417
+ permissions.push('Bash(./dev-pipeline/run-feature.sh *)', 'Bash(./dev-pipeline/launch-feature-daemon.sh *)');
418
418
  }
419
419
 
420
420
  const settings = {
@@ -601,8 +601,8 @@ export function resolvePipelineFileList() {
601
601
  if (!pipelineSource || !fs.pathExistsSync(pipelineSource)) return [];
602
602
 
603
603
  const items = [
604
- 'run.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-daemon.sh',
605
- 'run-bugfix.sh', 'retry-bug.sh', 'launch-bugfix-daemon.sh',
604
+ 'run-feature.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-feature-daemon.sh',
605
+ 'run-bugfix.sh', 'retry-bugfix.sh', 'launch-bugfix-daemon.sh',
606
606
  'lib', 'scripts', 'templates', 'assets', 'README.md', '.gitignore',
607
607
  ];
608
608
 
@@ -638,8 +638,8 @@ export async function installPipeline(projectRoot, dryRun, { forceOverwrite = fa
638
638
 
639
639
  // 需要安装的 Pipeline 文件和目录
640
640
  const items = [
641
- 'run.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-daemon.sh',
642
- 'run-bugfix.sh', 'retry-bug.sh', 'launch-bugfix-daemon.sh',
641
+ 'run-feature.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-feature-daemon.sh',
642
+ 'run-bugfix.sh', 'retry-bugfix.sh', 'launch-bugfix-daemon.sh',
643
643
  'lib', 'scripts', 'templates', 'assets', 'README.md', '.gitignore',
644
644
  ];
645
645
 
@@ -1,56 +0,0 @@
1
- # Feature: [FEATURE_TITLE]
2
-
3
- ## Overview
4
- [One paragraph describing the feature purpose and business value]
5
-
6
- ## User Stories
7
-
8
- ### US1: [Story Title]
9
- **As a** [role]
10
- **I want** [capability]
11
- **So that** [benefit]
12
-
13
- **Acceptance Criteria:**
14
- - [ ] Given [context], When [action], Then [expected result]
15
-
16
- ## Scope
17
-
18
- ### In Scope
19
- - [Item 1]
20
-
21
- ### Out of Scope
22
- - [Item 1]
23
-
24
- ## Dependencies
25
- - [Dependency 1]: [Why needed]
26
-
27
- ## Data Model (if feature involves database changes)
28
-
29
- ### Existing Schema Reference
30
- <!-- Read existing schema/model files BEFORE designing new tables. Document observed conventions here. -->
31
- - Tables reviewed: [list existing tables related to this feature]
32
- - Naming convention: [snake_case/camelCase — match existing]
33
- - ID strategy: [UUID/auto-increment — match existing]
34
- - Timestamp pattern: [created_at/updated_at — match existing]
35
- - Soft delete: [yes/no, field name — match existing]
36
-
37
- ### New/Modified Entities
38
- | Entity | Type (new/modify) | Key Fields | Relationships | Constraints |
39
- |--------|-------------------|------------|---------------|-------------|
40
- | [entity_name] | new | [field: type] | [FK to existing_table] | [NOT NULL, UNIQUE, etc.] |
41
-
42
- ### Open Data Model Questions
43
- <!-- All questions here MUST be resolved before /prizmkit-plan generates Tasks -->
44
- - [NEEDS CLARIFICATION] [Any uncertain data model decisions — field types, nullability, relationships]
45
-
46
- ## Constraints
47
- - [Constraint 1]
48
-
49
- ## Clarifications
50
- [NEEDS CLARIFICATION]: [Ambiguous item]
51
-
52
- ## Review Checklist
53
- - [ ] All user stories have acceptance criteria
54
- - [ ] Scope boundaries are clearly defined
55
- - [ ] Dependencies are identified
56
- - [ ] No implementation details (WHAT not HOW)
@@ -1,67 +0,0 @@
1
- # Clarification Phase — Execution Guide
2
-
3
- Invoked automatically during `/prizmkit-plan` Phase 0 (specify) when `[NEEDS CLARIFICATION]` markers exist, or during Phase 1 (design) when data model ambiguities arise.
4
-
5
- ---
6
-
7
- ## Question Strategy
8
-
9
- **Ask one question at a time.** Batching questions produces lower-quality answers.
10
-
11
- For each question:
12
- 1. Cite the exact location in spec.md (e.g., "§3.2 says...")
13
- 2. State what is ambiguous and why it matters for implementation
14
- 3. Provide a **recommended answer** with rationale — gives the user a concrete starting point to accept, modify, or reject
15
-
16
- **Prioritize by implementation impact** (highest first):
17
- 1. Data model (entities, relationships, field types, constraints, naming) — **hardest to change after code is written**
18
- 2. Functional scope boundaries
19
- 3. UX flow and error handling
20
- 4. Non-functional requirements (performance, security)
21
- 5. Edge cases and integration points
22
-
23
- ## Update Discipline
24
-
25
- After **each** user answer:
26
- - Immediately update `spec.md` (do not batch at the end)
27
- - Remove the resolved `[NEEDS CLARIFICATION]` marker
28
- - Re-evaluate for new ambiguities exposed by the answer
29
-
30
- ## No Limits Policy
31
-
32
- **There is no cap on rounds or questions.** Keep asking until:
33
- - All `[NEEDS CLARIFICATION]` markers are removed, AND
34
- - No vague or underspecified areas remain, AND
35
- - You are fully confident about the requirements
36
-
37
- If the user's answer raises new questions — ask those too. If a previously-resolved item needs revisiting due to new context — go back.
38
-
39
- ## Early Termination
40
-
41
- If the user says "done", "stop", or "enough" — end immediately and output a summary of:
42
- - What was resolved
43
- - What remains unclear (list any open `[NEEDS CLARIFICATION]` items)
44
-
45
- ## Example Exchange
46
-
47
- **Question:**
48
- > §3.2 says "User can upload files" but doesn't specify file types or size limits.
49
- >
50
- > **Recommended:** Accept JPEG, PNG, PDF up to 10MB — covers common use cases without straining storage.
51
- >
52
- > Do you agree, or different constraints?
53
-
54
- **User:** "Also allow SVG, make it 25MB"
55
-
56
- **Action:** Update spec.md §3.2 → `File upload: JPEG, PNG, PDF, SVG. Max 25MB per file.` Remove marker.
57
-
58
- **Follow-up question triggered by answer:**
59
- > SVGs can contain embedded scripts (XSS risk). Should uploaded SVGs be sanitized (strip `<script>` + event handlers), or restricted to trusted users only?
60
- >
61
- > **Recommended:** Sanitize all SVG uploads — allows all users to upload safely.
62
-
63
- ## Guidelines
64
-
65
- - Stay at WHAT/WHY level — no implementation details (HOW) in spec
66
- - If an answer contradicts an existing requirement, surface the conflict and ask which takes precedence
67
- - If the user seems fatigued: "I have N more questions — continue now or later?" — but never silently stop with unresolved ambiguities
package/src/config.js DELETED
@@ -1,504 +0,0 @@
1
- /**
2
- * PrizmKit Config Command
3
- *
4
- * Reconfigure an existing PrizmKit installation without a full reinstall.
5
- * Supports changing platform, AI CLI, skills suite, rules, team, and pipeline.
6
- *
7
- * Flow: READ manifest → PROMPT user → COMPUTE DIFF → DISPLAY SUMMARY → EXECUTE
8
- *
9
- * Preserves runtime files (.prizm-docs/, dev-pipeline/state/, .prizmkit/specs/).
10
- */
11
-
12
- import chalk from 'chalk';
13
- import fs from 'fs-extra';
14
- import path from 'path';
15
- import { readFileSync } from 'fs';
16
- import { dirname, join } from 'path';
17
- import { fileURLToPath } from 'url';
18
- import { confirm } from '@inquirer/prompts';
19
-
20
- import { readManifest, writeManifest, buildManifest, diffManifest } from './manifest.js';
21
- import {
22
- loadMetadata,
23
- loadRulesMetadata,
24
- getAgentsDir,
25
- } from './metadata.js';
26
- import {
27
- installSkills,
28
- installAgents,
29
- installSettings,
30
- installTeamConfig,
31
- installProjectMemory,
32
- installPipeline,
33
- installGitignore,
34
- resolveSkillList,
35
- resolvePipelineFileList,
36
- } from './scaffold.js';
37
- import {
38
- removeSkillFiles,
39
- removeAgentFiles,
40
- removeRuleFiles,
41
- removePipelineFiles,
42
- } from './upgrade.js';
43
- import { detectPlatform } from './detect-platform.js';
44
- import {
45
- selectPlatform,
46
- selectAiCli,
47
- selectSkillSuite,
48
- selectRulesPreset,
49
- confirmTeamMode,
50
- confirmPipeline,
51
- } from './prompts.js';
52
-
53
- const __dirname = dirname(fileURLToPath(import.meta.url));
54
- const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
55
-
56
- // ============================================================
57
- // Helpers
58
- // ============================================================
59
-
60
- /**
61
- * Detect which platforms are actually installed on filesystem.
62
- */
63
- async function detectInstalledPlatforms(projectRoot) {
64
- const hasClaude = await fs.pathExists(path.join(projectRoot, '.claude', 'commands'))
65
- || await fs.pathExists(path.join(projectRoot, '.claude', 'agents'));
66
- const hasCodeBuddy = await fs.pathExists(path.join(projectRoot, '.codebuddy', 'skills'))
67
- || await fs.pathExists(path.join(projectRoot, '.codebuddy', 'agents'));
68
-
69
- if (hasClaude && hasCodeBuddy) return 'both';
70
- if (hasCodeBuddy) return 'codebuddy';
71
- if (hasClaude) return 'claude';
72
- return null;
73
- }
74
-
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
- const platformLabel = (p) => p === 'both' ? 'CodeBuddy + Claude Code'
83
- : p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
84
-
85
- /**
86
- * Remove all PrizmKit-owned platform-specific files for a single platform.
87
- * Selective removal — only removes known PrizmKit files, leaves user-created files intact.
88
- */
89
- async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
90
- const skillNames = manifest?.files?.skills || [];
91
- const agentFileNames = manifest?.files?.agents || [];
92
- const ruleFileNames = manifest?.files?.rules || [];
93
-
94
- if (skillNames.length) {
95
- console.log(chalk.blue(`\n 删除 ${platformLabel(platform)} skills:`));
96
- await removeSkillFiles(platform, projectRoot, skillNames, dryRun);
97
- }
98
-
99
- if (agentFileNames.length) {
100
- console.log(chalk.blue(`\n 删除 ${platformLabel(platform)} agents:`));
101
- await removeAgentFiles(platform, projectRoot, agentFileNames, dryRun);
102
- }
103
-
104
- if (ruleFileNames.length) {
105
- console.log(chalk.blue(`\n 删除 ${platformLabel(platform)} rules:`));
106
- await removeRuleFiles(platform, projectRoot, ruleFileNames, dryRun);
107
- }
108
-
109
- // Settings
110
- const settingsFile = platform === 'claude'
111
- ? path.join(projectRoot, '.claude', 'settings.json')
112
- : path.join(projectRoot, '.codebuddy', 'settings.json');
113
- if (await fs.pathExists(settingsFile)) {
114
- if (dryRun) {
115
- console.log(chalk.gray(` [dry-run] remove ${path.relative(projectRoot, settingsFile)}`));
116
- } else {
117
- await fs.remove(settingsFile);
118
- console.log(chalk.red(` ✗ removed ${path.relative(projectRoot, settingsFile)}`));
119
- }
120
- }
121
-
122
- // Team info
123
- if (platform === 'claude') {
124
- const teamInfo = path.join(projectRoot, '.claude', 'team-info.json');
125
- if (await fs.pathExists(teamInfo)) {
126
- if (dryRun) {
127
- console.log(chalk.gray(' [dry-run] remove .claude/team-info.json'));
128
- } else {
129
- await fs.remove(teamInfo);
130
- console.log(chalk.red(' ✗ removed .claude/team-info.json'));
131
- }
132
- }
133
- } else if (platform === 'codebuddy') {
134
- const homeDir = process.env.HOME || process.env.USERPROFILE;
135
- const teamDir = path.join(homeDir, '.codebuddy', 'teams', 'prizm-dev-team');
136
- if (await fs.pathExists(teamDir)) {
137
- if (dryRun) {
138
- console.log(chalk.gray(' [dry-run] remove ~/.codebuddy/teams/prizm-dev-team/'));
139
- } else {
140
- await fs.remove(teamDir);
141
- console.log(chalk.red(' ✗ removed ~/.codebuddy/teams/prizm-dev-team/'));
142
- }
143
- }
144
- }
145
-
146
- // Project memory file
147
- const memoryFile = platform === 'claude' ? 'CLAUDE.md' : 'CODEBUDDY.md';
148
- const memoryPath = path.join(projectRoot, memoryFile);
149
- if (await fs.pathExists(memoryPath)) {
150
- if (dryRun) {
151
- console.log(chalk.gray(` [dry-run] remove ${memoryFile}`));
152
- } else {
153
- await fs.remove(memoryPath);
154
- console.log(chalk.red(` ✗ removed ${memoryFile}`));
155
- }
156
- }
157
-
158
- // Command assets (Claude only)
159
- if (platform === 'claude') {
160
- const assetsDir = path.join(projectRoot, '.claude', 'command-assets');
161
- if (await fs.pathExists(assetsDir)) {
162
- if (dryRun) {
163
- console.log(chalk.gray(' [dry-run] remove .claude/command-assets/'));
164
- } else {
165
- await fs.remove(assetsDir);
166
- console.log(chalk.red(' ✗ removed .claude/command-assets/'));
167
- }
168
- }
169
- }
170
- }
171
-
172
- // ============================================================
173
- // Main
174
- // ============================================================
175
-
176
- /**
177
- * Run the config reconfiguration command.
178
- * @param {string} directory - target project directory
179
- * @param {Object} options - CLI options from commander
180
- */
181
- export async function runConfig(directory, options = {}) {
182
- const projectRoot = path.resolve(directory || '.');
183
- const dryRun = Boolean(options.dryRun);
184
- const nonInteractive = Boolean(options.nonInteractive);
185
-
186
- console.log('');
187
- console.log(chalk.bold(' PrizmKit Config'));
188
- console.log(` Target: ${projectRoot}`);
189
- if (dryRun) console.log(chalk.yellow(' Mode: dry-run'));
190
- console.log('');
191
-
192
- // ── Phase 1: READ current state ──────────────────────────────────────────
193
-
194
- const oldManifest = await readManifest(projectRoot);
195
- if (!oldManifest) {
196
- console.log(chalk.red(' ✗ 未找到 .prizmkit/manifest.json'));
197
- console.log(chalk.gray(' 请先运行 `npx prizmkit install .` 安装 PrizmKit。'));
198
- console.log('');
199
- return;
200
- }
201
-
202
- // Read user config
203
- const configPath = path.join(projectRoot, '.prizmkit', 'config.json');
204
- let userConfig = {};
205
- if (await fs.pathExists(configPath)) {
206
- try { userConfig = await fs.readJSON(configPath); } catch { /* ignore */ }
207
- }
208
-
209
- // Filesystem-based platform detection (overrides manifest)
210
- const detectedPlatform = await detectInstalledPlatforms(projectRoot) || oldManifest.platform || 'claude';
211
-
212
- const currentConfig = {
213
- platform: detectedPlatform,
214
- suite: oldManifest.suite || 'core',
215
- rules: oldManifest.options?.rules || 'recommended',
216
- team: oldManifest.options?.team ?? true,
217
- pipeline: oldManifest.options?.pipeline ?? true,
218
- aiCli: userConfig.ai_cli || oldManifest.options?.aiCli || '',
219
- };
220
-
221
- // Display current config
222
- console.log(chalk.bold(' 当前配置:'));
223
- console.log(` 平台: ${chalk.cyan(platformLabel(currentConfig.platform))}`);
224
- console.log(` AI CLI: ${currentConfig.aiCli ? chalk.cyan(currentConfig.aiCli) : chalk.gray('(未设置)')}`);
225
- console.log(` 技能套件: ${chalk.cyan(currentConfig.suite)}`);
226
- console.log(` 规则: ${chalk.cyan(currentConfig.rules)}`);
227
- console.log(` 团队模式: ${currentConfig.team ? chalk.green('启用') : chalk.gray('禁用')}`);
228
- console.log(` 流水线: ${currentConfig.pipeline ? chalk.green('启用') : chalk.gray('禁用')}`);
229
- console.log('');
230
-
231
- // ── Phase 2: PROMPT for new config ───────────────────────────────────────
232
-
233
- let newConfig;
234
-
235
- if (nonInteractive) {
236
- // Non-interactive: only change what's explicitly specified via CLI options
237
- if (!options.platform && !options.skills && !options.rules
238
- && options.aiCli === undefined && options.team === undefined && options.pipeline === undefined) {
239
- console.log(chalk.yellow(' ⚠ 非交互式模式下未指定任何变更。'));
240
- console.log(chalk.gray(' 使用 --platform, --skills, --rules, --ai-cli, --team/--no-team, --pipeline/--no-pipeline'));
241
- return;
242
- }
243
-
244
- newConfig = {
245
- platform: options.platform || currentConfig.platform,
246
- suite: options.skills || currentConfig.suite,
247
- rules: options.rules || currentConfig.rules,
248
- team: options.team !== undefined ? options.team : currentConfig.team,
249
- pipeline: options.pipeline !== undefined ? options.pipeline : currentConfig.pipeline,
250
- aiCli: options.aiCli !== undefined ? options.aiCli : currentConfig.aiCli,
251
- };
252
- } else {
253
- // Interactive: let user select each setting
254
- const detected = detectPlatform();
255
-
256
- try {
257
- console.log(chalk.bold(' 选择新配置(按 Enter 保持当前值):\n'));
258
-
259
- const newPlatform = await selectPlatform(currentConfig.platform, detected);
260
- const newAiCli = await selectAiCli(currentConfig.aiCli, detected);
261
-
262
- const metadata = await loadMetadata();
263
- const newSuite = await selectSkillSuite(currentConfig.suite, metadata);
264
- const newRules = await selectRulesPreset(currentConfig.rules);
265
- const newTeam = await confirmTeamMode(currentConfig.team);
266
- const newPipeline = await confirmPipeline(currentConfig.pipeline);
267
-
268
- newConfig = {
269
- platform: newPlatform,
270
- suite: newSuite,
271
- rules: newRules,
272
- team: newTeam,
273
- pipeline: newPipeline,
274
- aiCli: newAiCli,
275
- };
276
- } catch (err) {
277
- if (err.message?.includes('User force closed')) {
278
- console.log(chalk.yellow('\n 配置已取消。'));
279
- return;
280
- }
281
- throw err;
282
- }
283
- }
284
-
285
- // ── Phase 3: COMPUTE DIFF ────────────────────────────────────────────────
286
-
287
- const changes = [];
288
- if (newConfig.platform !== currentConfig.platform) {
289
- changes.push(`平台: ${platformLabel(currentConfig.platform)} → ${platformLabel(newConfig.platform)}`);
290
- }
291
- if (newConfig.aiCli !== currentConfig.aiCli) {
292
- changes.push(`AI CLI: ${currentConfig.aiCli || '(未设置)'} → ${newConfig.aiCli || '(未设置)'}`);
293
- }
294
- if (newConfig.suite !== currentConfig.suite) {
295
- changes.push(`技能套件: ${currentConfig.suite} → ${newConfig.suite}`);
296
- }
297
- if (newConfig.rules !== currentConfig.rules) {
298
- changes.push(`规则: ${currentConfig.rules} → ${newConfig.rules}`);
299
- }
300
- if (newConfig.team !== currentConfig.team) {
301
- changes.push(`团队模式: ${currentConfig.team ? '启用' : '禁用'} → ${newConfig.team ? '启用' : '禁用'}`);
302
- }
303
- if (newConfig.pipeline !== currentConfig.pipeline) {
304
- changes.push(`流水线: ${currentConfig.pipeline ? '启用' : '禁用'} → ${newConfig.pipeline ? '启用' : '禁用'}`);
305
- }
306
-
307
- // ── Phase 4: DISPLAY SUMMARY + CONFIRM ───────────────────────────────────
308
-
309
- if (changes.length === 0) {
310
- console.log(chalk.green('\n ✓ 配置未变更,无需操作。'));
311
- console.log('');
312
- return;
313
- }
314
-
315
- console.log(chalk.bold('\n 变更摘要:'));
316
- for (const change of changes) {
317
- console.log(` ${chalk.yellow('→')} ${change}`);
318
- }
319
-
320
- // Warn about destructive changes
321
- if (newConfig.platform !== currentConfig.platform) {
322
- const oldPlatforms = expandPlatforms(currentConfig.platform);
323
- const newPlatforms = expandPlatforms(newConfig.platform);
324
- const dropping = oldPlatforms.filter(p => !newPlatforms.includes(p));
325
- if (dropping.length) {
326
- console.log(chalk.yellow(`\n ⚠ 将移除 ${dropping.map(platformLabel).join(', ')} 的 PrizmKit 文件`));
327
- }
328
- }
329
- console.log('');
330
-
331
- if (dryRun) {
332
- console.log(chalk.yellow(' [dry-run] 预览完成,未修改任何文件。'));
333
- console.log('');
334
- return;
335
- }
336
-
337
- if (!nonInteractive) {
338
- const proceed = await confirm({
339
- message: '确认应用配置变更?',
340
- default: true,
341
- });
342
- if (!proceed) {
343
- console.log(chalk.yellow(' 配置已取消。'));
344
- return;
345
- }
346
- }
347
-
348
- // ── Phase 5: EXECUTE ─────────────────────────────────────────────────────
349
-
350
- console.log(chalk.bold('\n 应用配置变更...\n'));
351
-
352
- const oldPlatforms = expandPlatforms(currentConfig.platform);
353
- const newPlatforms = expandPlatforms(newConfig.platform);
354
- const platformsToRemove = oldPlatforms.filter(p => !newPlatforms.includes(p));
355
- const platformsToAdd = newPlatforms.filter(p => !oldPlatforms.includes(p));
356
- const platformsToUpdate = newPlatforms.filter(p => oldPlatforms.includes(p));
357
-
358
- // Resolve new file lists
359
- const newSkillList = await resolveSkillList(newConfig.suite);
360
- const agentsDir = getAgentsDir();
361
- const newAgentFiles = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
362
- const rulesMeta = await loadRulesMetadata();
363
- const rulesPresetDef = rulesMeta.presets[newConfig.rules] || rulesMeta.presets.recommended;
364
- const newRuleFiles = (rulesPresetDef?.rules || []).map(name => `${name}.md`);
365
- const newPipelineFiles = newConfig.pipeline ? resolvePipelineFileList() : [];
366
-
367
- // 5a. Remove files for platforms being dropped
368
- for (const p of platformsToRemove) {
369
- console.log(chalk.bold(` 移除 ${platformLabel(p)} 平台文件...`));
370
- await removePlatformFiles(p, projectRoot, oldManifest, false);
371
- }
372
-
373
- // 5b. Handle skill/rule diff for existing platforms (suite or rules changed)
374
- if (newConfig.suite !== currentConfig.suite || newConfig.rules !== currentConfig.rules) {
375
- const newTempManifest = buildManifest({
376
- version: pkg.version, platform: newConfig.platform, suite: newConfig.suite,
377
- skills: newSkillList, agents: newAgentFiles, rules: newRuleFiles,
378
- pipeline: newPipelineFiles, team: newConfig.team, aiCli: newConfig.aiCli,
379
- rulesPreset: newConfig.rules, extras: oldManifest?.files?.extras || [],
380
- });
381
- const diff = diffManifest(oldManifest, newTempManifest);
382
-
383
- // Remove orphaned skills/rules from existing platforms
384
- for (const p of platformsToUpdate) {
385
- if (diff.skills.removed.length) {
386
- console.log(chalk.blue(`\n 删除多余 skills (${platformLabel(p)}):`));
387
- await removeSkillFiles(p, projectRoot, diff.skills.removed, false);
388
- }
389
- if (diff.rules.removed.length) {
390
- console.log(chalk.blue(`\n 删除多余 rules (${platformLabel(p)}):`));
391
- await removeRuleFiles(p, projectRoot, diff.rules.removed, false);
392
- }
393
- }
394
- }
395
-
396
- // 5c. Install/update files for new + existing platforms
397
- const allTargetPlatforms = [...platformsToAdd, ...platformsToUpdate];
398
- const needsReinstall = newConfig.platform !== currentConfig.platform
399
- || newConfig.suite !== currentConfig.suite
400
- || newConfig.rules !== currentConfig.rules
401
- || newConfig.team !== currentConfig.team;
402
-
403
- for (const p of allTargetPlatforms) {
404
- const isNew = platformsToAdd.includes(p);
405
-
406
- if (isNew || needsReinstall) {
407
- console.log(chalk.bold(`\n ${isNew ? '安装' : '更新'} ${platformLabel(p)} 环境...\n`));
408
-
409
- console.log(chalk.blue(' Skills:'));
410
- await installSkills(p, newSkillList, projectRoot, false);
411
-
412
- console.log(chalk.blue('\n Agents:'));
413
- await installAgents(p, projectRoot, false);
414
-
415
- console.log(chalk.blue('\n Settings & Rules:'));
416
- await installSettings(p, projectRoot, { pipeline: newConfig.pipeline, rules: newConfig.rules }, false);
417
-
418
- if (isNew) {
419
- console.log(chalk.blue('\n Project Memory:'));
420
- await installProjectMemory(p, projectRoot, false);
421
- }
422
-
423
- if (newConfig.team) {
424
- console.log(chalk.blue('\n Team Config:'));
425
- await installTeamConfig(p, projectRoot, false);
426
- }
427
- }
428
- }
429
-
430
- // 5d. Pipeline changes
431
- if (newConfig.pipeline !== currentConfig.pipeline) {
432
- if (newConfig.pipeline) {
433
- console.log(chalk.blue('\n 安装 Pipeline:'));
434
- await installPipeline(projectRoot, false);
435
- } else {
436
- console.log(chalk.blue('\n 移除 Pipeline 框架文件(保留 state/):'));
437
- const oldPipelineFiles = oldManifest?.files?.pipeline || [];
438
- if (oldPipelineFiles.length) {
439
- await removePipelineFiles(projectRoot, oldPipelineFiles, false);
440
- }
441
- }
442
- }
443
-
444
- // 5e. Update .gitignore
445
- console.log(chalk.blue('\n Gitignore:'));
446
- await installGitignore(projectRoot, { pipeline: newConfig.pipeline }, false);
447
-
448
- // 5f. Update .prizmkit/config.json (ai_cli)
449
- if (newConfig.aiCli !== currentConfig.aiCli) {
450
- const prizmkitDir = path.join(projectRoot, '.prizmkit');
451
- await fs.ensureDir(prizmkitDir);
452
- let existingConfig = {};
453
- if (await fs.pathExists(configPath)) {
454
- try { existingConfig = await fs.readJSON(configPath); } catch { /* ignore */ }
455
- }
456
- if (newConfig.aiCli) {
457
- existingConfig.ai_cli = newConfig.aiCli;
458
- } else {
459
- delete existingConfig.ai_cli;
460
- }
461
- await fs.writeJSON(configPath, existingConfig, { spaces: 2 });
462
- console.log(chalk.green(`\n ✓ .prizmkit/config.json (ai_cli: ${newConfig.aiCli || '(cleared)'})`));
463
- }
464
-
465
- // 5g. Write new manifest
466
- const newManifest = buildManifest({
467
- version: pkg.version,
468
- platform: newConfig.platform,
469
- suite: newConfig.suite,
470
- skills: newSkillList,
471
- agents: newAgentFiles,
472
- rules: newRuleFiles,
473
- pipeline: newPipelineFiles,
474
- team: newConfig.team,
475
- aiCli: newConfig.aiCli,
476
- rulesPreset: newConfig.rules,
477
- extras: oldManifest?.files?.extras || [],
478
- });
479
-
480
- // Preserve original installedAt
481
- if (oldManifest?.installedAt) {
482
- newManifest.installedAt = oldManifest.installedAt;
483
- }
484
-
485
- await writeManifest(projectRoot, newManifest);
486
- console.log(chalk.green(' ✓ .prizmkit/manifest.json'));
487
-
488
- // ── Done ─────────────────────────────────────────────────────────────────
489
-
490
- console.log('');
491
- console.log(chalk.bold(' ════════════════════════════════════════════════'));
492
- console.log(chalk.green.bold(' ✅ 配置变更完成!'));
493
- console.log(chalk.bold(' ════════════════════════════════════════════════'));
494
- console.log('');
495
-
496
- console.log(chalk.gray(' 新配置:'));
497
- console.log(chalk.gray(` 平台: ${platformLabel(newConfig.platform)}`));
498
- console.log(chalk.gray(` AI CLI: ${newConfig.aiCli || '(未设置)'}`));
499
- console.log(chalk.gray(` 技能套件: ${newConfig.suite}`));
500
- console.log(chalk.gray(` 规则: ${newConfig.rules}`));
501
- console.log(chalk.gray(` 团队模式: ${newConfig.team ? '启用' : '禁用'}`));
502
- console.log(chalk.gray(` 流水线: ${newConfig.pipeline ? '启用' : '禁用'}`));
503
- console.log('');
504
- }