prizmkit 1.1.143 → 1.1.145

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 (33) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/pi/paths.js +9 -0
  3. package/bundled/adapters/pi/rules-adapter.js +13 -0
  4. package/bundled/adapters/pi/settings-adapter.js +23 -0
  5. package/bundled/adapters/pi/skill-adapter.js +18 -0
  6. package/bundled/dev-pipeline/prizmkit_runtime/config.py +15 -3
  7. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +3 -1
  8. package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +10 -2
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +1 -0
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +1 -0
  11. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +26 -2
  12. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +83 -7
  13. package/bundled/dev-pipeline/tests/test_unified_cli.py +117 -0
  14. package/bundled/skills/_metadata.json +1 -1
  15. package/bundled/skills/prizmkit-code-review/SKILL.md +3 -3
  16. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +63 -58
  17. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +8 -1
  18. package/bundled/skills/prizmkit-plan/SKILL.md +3 -3
  19. package/bundled/skills/prizmkit-plan/assets/plan-template.md +3 -0
  20. package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +55 -52
  21. package/bundled/skills/prizmkit-test/SKILL.md +3 -3
  22. package/bundled/skills/prizmkit-test/references/independent-test-review.md +68 -69
  23. package/bundled/skills/prizmkit-test/references/test-report-template.md +5 -1
  24. package/package.json +1 -1
  25. package/src/ai-cli-launch.js +1 -1
  26. package/src/clean.js +13 -4
  27. package/src/config.js +32 -5
  28. package/src/detect-platform.js +11 -7
  29. package/src/index.js +5 -0
  30. package/src/platforms.js +6 -2
  31. package/src/prompts.js +12 -4
  32. package/src/scaffold.js +72 -20
  33. package/src/upgrade.js +12 -5
package/src/scaffold.js CHANGED
@@ -96,6 +96,7 @@ async function removeEmptyDirectory(dirPath) {
96
96
  }
97
97
 
98
98
  export async function removeRetiredAgentTeamArtifacts(platform, projectRoot, dryRun) {
99
+ if (platform === 'pi') return;
99
100
  const agentDir = platform === 'claude'
100
101
  ? path.join(projectRoot, '.claude', 'agents')
101
102
  : platform === 'codex'
@@ -231,12 +232,16 @@ export async function installSkills(platform, skills, projectRoot, dryRun, runti
231
232
  // Load Claude command adapter for skill conversion
232
233
  let convertSkillToCommand;
233
234
  let convertSkillToCodex;
235
+ let convertSkillToPi;
234
236
  if (platform === 'claude') {
235
237
  const commandAdapter = await loadAdapter('claude', 'command-adapter.js');
236
238
  convertSkillToCommand = commandAdapter.convertSkillToCommand;
237
239
  } else if (platform === 'codex') {
238
240
  const codexSkillAdapter = await loadAdapter('codex', 'skill-adapter.js');
239
241
  convertSkillToCodex = codexSkillAdapter.convertSkill;
242
+ } else if (platform === 'pi') {
243
+ const piSkillAdapter = await loadAdapter('pi', 'skill-adapter.js');
244
+ convertSkillToPi = piSkillAdapter.convertSkill;
240
245
  }
241
246
 
242
247
  for (const skillName of skills) {
@@ -347,6 +352,25 @@ export async function installSkills(platform, skills, projectRoot, dryRun, runti
347
352
  }
348
353
  }
349
354
  console.log(chalk.green(` ✓ .agents/skills/${skillName}/`));
355
+ } else if (platform === 'pi') {
356
+ const content = await fs.readFile(skillMdPath, 'utf8');
357
+ const converted = convertSkillToPi(content, skillName);
358
+ const targetDir = path.join(projectRoot, '.pi', 'skills', skillName);
359
+
360
+ if (dryRun) {
361
+ console.log(chalk.gray(` [dry-run] .pi/skills/${skillName}/`));
362
+ continue;
363
+ }
364
+
365
+ await fs.remove(targetDir);
366
+ await fs.ensureDir(targetDir);
367
+ await fs.writeFile(path.join(targetDir, 'SKILL.md'), converted);
368
+ for (const entry of await fs.readdir(corePath, { withFileTypes: true })) {
369
+ if (entry.isDirectory()) {
370
+ await fs.copy(path.join(corePath, entry.name), path.join(targetDir, entry.name));
371
+ }
372
+ }
373
+ console.log(chalk.green(` ✓ .pi/skills/${skillName}/`));
350
374
  }
351
375
  }
352
376
  }
@@ -503,6 +527,18 @@ job_max_runtime_seconds = 3300
503
527
  await installCodexRules(projectRoot, ruleFiles);
504
528
  removeLegacyCodexRules(projectRoot, allRuleFileNames);
505
529
  console.log(chalk.green(` ✓ .agents/rules/ (${ruleFiles.length} 条规则)`));
530
+ } else if (platform === 'pi') {
531
+ if (dryRun) {
532
+ console.log(chalk.gray(' [dry-run] .pi/settings.json'));
533
+ console.log(chalk.gray(` [dry-run] .pi/rules/ (${ruleFiles.length} 条规则)`));
534
+ return;
535
+ }
536
+ const { generateSettings } = await loadAdapter('pi', 'settings-adapter.js');
537
+ const { installRules } = await loadAdapter('pi', 'rules-adapter.js');
538
+ await generateSettings(projectRoot);
539
+ await installRules(projectRoot, ruleFiles);
540
+ console.log(chalk.green(' ✓ .pi/settings.json'));
541
+ console.log(chalk.green(` ✓ .pi/rules/ (${ruleFiles.length} 条规则)`));
506
542
  }
507
543
  }
508
544
 
@@ -927,11 +963,11 @@ export async function installProjectMemory(platform, projectRoot, dryRun) {
927
963
  const templateName = 'project-memory-template.md';
928
964
  const mainName = projectMemoryFile(platform);
929
965
  const privateName = privateProjectMemoryFile(platform);
930
- if (!mainName || !privateName) return;
966
+ if (!privateName) return;
931
967
 
932
- const mainPath = path.join(projectRoot, mainName);
968
+ const mainPath = mainName ? path.join(projectRoot, mainName) : null;
933
969
  const privatePath = path.join(projectRoot, privateName);
934
- const importPointer = `@./${privateName}`;
970
+ const importPointer = mainName ? `@./${privateName}` : null;
935
971
 
936
972
  const templateCandidates = [
937
973
  path.join(templatesDir, templateName),
@@ -944,7 +980,18 @@ export async function installProjectMemory(platform, projectRoot, dryRun) {
944
980
  }
945
981
 
946
982
  let templateContent = (await fs.readFile(templatePath, 'utf-8')).trimEnd();
947
- if (platform === 'codex') {
983
+ if (platform === 'pi') {
984
+ templateContent = templateContent.replace(
985
+ '### Available Commands\nRun `/prizmkit` to see all available PrizmKit commands.',
986
+ `### Available PrizmKit Skills
987
+ Pi discovers project skills from \`.pi/skills/\`. Invoke them with \`/skill:<name>\` (for example \`/skill:prizmkit-plan\`).
988
+
989
+ - Start with \`/skill:prizmkit\` to see all available PrizmKit skills.
990
+ - Skill assets and references live inside each \`.pi/skills/<skill>/\` directory.
991
+ - PrizmKit behavioral rules live in \`.pi/rules/\`; read the relevant Markdown guidance before commit, documentation, or context-loading work.
992
+ `
993
+ );
994
+ } else if (platform === 'codex') {
948
995
  templateContent = templateContent.replace(
949
996
  '### Available Commands\nRun `/prizmkit` to see all available PrizmKit commands.',
950
997
  `### Available PrizmKit Skills
@@ -959,10 +1006,11 @@ Codex discovers repository skills from \`.agents/skills/\`. When a user or promp
959
1006
 
960
1007
  if (dryRun) {
961
1008
  console.log(chalk.gray(` [dry-run] ${privateName}`));
962
- console.log(chalk.gray(` [dry-run] ${mainName} import ${importPointer}`));
1009
+ if (mainName) console.log(chalk.gray(` [dry-run] ${mainName} import ${importPointer}`));
963
1010
  return;
964
1011
  }
965
1012
 
1013
+ await fs.ensureDir(path.dirname(privatePath));
966
1014
  if (!await fs.pathExists(privatePath)) {
967
1015
  await fs.writeFile(privatePath, `${templateContent}\n`, 'utf-8');
968
1016
  console.log(chalk.green(` ✓ ${privateName} (created PrizmKit section)`));
@@ -977,18 +1025,20 @@ Codex discovers repository skills from \`.agents/skills/\`. When a user or promp
977
1025
  }
978
1026
  }
979
1027
 
980
- const existingMain = await fs.pathExists(mainPath)
981
- ? await fs.readFile(mainPath, 'utf-8')
982
- : '';
983
- const mainMerge = ensureSingleImportPointer(existingMain, importPointer);
984
- logManagedProjectMemoryWarnings(mainName, mainMerge.warnings);
985
- const nextMain = mainMerge.content;
986
- if (nextMain !== existingMain) {
987
- await fs.writeFile(mainPath, nextMain, 'utf-8');
988
- const migrationSuffix = mainMerge.removedBlocks.length ? ' and migrated managed block' : '';
989
- console.log(chalk.green(` ✓ ${mainName} (imports ${privateName}${migrationSuffix})`));
990
- } else {
991
- console.log(chalk.green(` ✓ ${mainName} (import up-to-date)`));
1028
+ if (mainPath && importPointer) {
1029
+ const existingMain = await fs.pathExists(mainPath)
1030
+ ? await fs.readFile(mainPath, 'utf-8')
1031
+ : '';
1032
+ const mainMerge = ensureSingleImportPointer(existingMain, importPointer);
1033
+ logManagedProjectMemoryWarnings(mainName, mainMerge.warnings);
1034
+ const nextMain = mainMerge.content;
1035
+ if (nextMain !== existingMain) {
1036
+ await fs.writeFile(mainPath, nextMain, 'utf-8');
1037
+ const migrationSuffix = mainMerge.removedBlocks.length ? ' and migrated managed block' : '';
1038
+ console.log(chalk.green(` ✓ ${mainName} (imports ${privateName}${migrationSuffix})`));
1039
+ } else {
1040
+ console.log(chalk.green(` ✓ ${mainName} (import up-to-date)`));
1041
+ }
992
1042
  }
993
1043
  }
994
1044
 
@@ -1477,7 +1527,7 @@ export const EXTRAS_REGISTRY = {
1477
1527
  /**
1478
1528
  * 执行纯净安装
1479
1529
  * @param {Object} config
1480
- * @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'all'
1530
+ * @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'pi' | 'all'
1481
1531
  * @param {string} [config.runtime] - 'python' (legacy aliases accepted: 'unix' | 'windows')
1482
1532
  * @param {string} config.skills - 'core' | 'minimal' | 'recommended:<type>'
1483
1533
  * @param {boolean} config.pipeline - 是否安装 dev-pipeline
@@ -1499,7 +1549,7 @@ export async function scaffold(config) {
1499
1549
  });
1500
1550
  const resolvedAiCli = launchSelection.aiCli;
1501
1551
  if (!isKnownPlatform(platform)) {
1502
- throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, or all.`);
1552
+ throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, pi, or all.`);
1503
1553
  }
1504
1554
  const platforms = expandPlatforms(platform);
1505
1555
  const payloadPlatforms = resolvePayloadPlatforms(platform, resolvedAiCli);
@@ -1679,7 +1729,9 @@ export async function scaffold(config) {
1679
1729
  console.log(` 2. ${chalk.cyan(cli)} ${chalk.gray('# 启动 AI 对话')}`);
1680
1730
  const initInstruction = mainPlatform === 'codex'
1681
1731
  ? '说 "运行 prizmkit-init"'
1682
- : '说 "/prizmkit-init"';
1732
+ : mainPlatform === 'pi'
1733
+ ? '输入 "/skill:prizmkit-init"'
1734
+ : '说 "/prizmkit-init"';
1683
1735
  console.log(` 3. ${chalk.cyan(initInstruction)} ${chalk.gray('# 初始化项目上下文')}`);
1684
1736
 
1685
1737
  if (pipeline) {
package/src/upgrade.js CHANGED
@@ -73,14 +73,16 @@ export async function removeSkillFiles(platform, projectRoot, skillNames, dryRun
73
73
  console.log(chalk.red(` ✗ removed .codebuddy/skills/${skillName}/`));
74
74
  }
75
75
  }
76
- } else if (platform === 'codex') {
77
- const skillDir = path.join(projectRoot, '.agents', 'skills', skillName);
76
+ } else if (platform === 'codex' || platform === 'pi') {
77
+ const skillDir = platform === 'pi'
78
+ ? path.join(projectRoot, '.pi', 'skills', skillName)
79
+ : path.join(projectRoot, '.agents', 'skills', skillName);
78
80
  if (await fs.pathExists(skillDir)) {
79
81
  if (dryRun) {
80
- console.log(chalk.gray(` [dry-run] remove .agents/skills/${skillName}/`));
82
+ console.log(chalk.gray(` [dry-run] remove ${path.relative(projectRoot, skillDir)}/`));
81
83
  } else {
82
84
  await fs.remove(skillDir);
83
- console.log(chalk.red(` ✗ removed .agents/skills/${skillName}/`));
85
+ console.log(chalk.red(` ✗ removed ${path.relative(projectRoot, skillDir)}/`));
84
86
  }
85
87
  }
86
88
  }
@@ -91,6 +93,7 @@ export async function removeSkillFiles(platform, projectRoot, skillNames, dryRun
91
93
  * Remove agent files for a specific platform.
92
94
  */
93
95
  export async function removeAgentFiles(platform, projectRoot, agentFileNames, dryRun) {
96
+ if (platform === 'pi') return;
94
97
  for (const fileName of agentFileNames) {
95
98
  const dir = platform === 'claude'
96
99
  ? path.join(projectRoot, '.claude', 'agents')
@@ -124,7 +127,9 @@ export async function removeRuleFiles(platform, projectRoot, ruleFileNames, dryR
124
127
  ? path.join(projectRoot, '.claude', 'rules')
125
128
  : platform === 'codex'
126
129
  ? path.join(projectRoot, '.agents', 'rules')
127
- : path.join(projectRoot, '.codebuddy', 'rules');
130
+ : platform === 'pi'
131
+ ? path.join(projectRoot, '.pi', 'rules')
132
+ : path.join(projectRoot, '.codebuddy', 'rules');
128
133
  // Rules are stored as .md for claude, .mdc for codebuddy
129
134
  const targetName = platform === 'codebuddy'
130
135
  ? baseName.replace(/\.md$/, '.mdc')
@@ -243,11 +248,13 @@ export async function runUpgrade(directory, options = {}) {
243
248
  const hasClaude = await hasPlatformFiles(path.join(projectRoot, '.claude', 'commands'));
244
249
  const hasCodeBuddy = await hasPlatformFiles(path.join(projectRoot, '.codebuddy', 'skills'));
245
250
  const hasCodex = await hasPlatformFiles(path.join(projectRoot, '.agents', 'skills'));
251
+ const hasPi = await hasPlatformFiles(path.join(projectRoot, '.pi', 'skills'));
246
252
 
247
253
  const detectedPlatforms = [
248
254
  hasCodeBuddy && 'codebuddy',
249
255
  hasClaude && 'claude',
250
256
  hasCodex && 'codex',
257
+ hasPi && 'pi',
251
258
  ].filter(Boolean);
252
259
 
253
260
  if (detectedPlatforms.length > 1) platform = 'all';