prizmkit 1.0.136 → 1.0.138

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.136",
3
- "bundledAt": "2026-03-28T13:29:14.147Z",
4
- "bundledFrom": "0e6a37f"
2
+ "frameworkVersion": "1.0.138",
3
+ "bundledAt": "2026-03-28T14:47:53.378Z",
4
+ "bundledFrom": "cc919b1"
5
5
  }
@@ -90,22 +90,27 @@ except: pass
90
90
 
91
91
  # prizm_detect_subagents <session_log>
92
92
  #
93
- # Scan session log for subagent spawns and log the result.
94
- # Uses grep -q for early exit on first match.
93
+ # Scan session log for subagent spawns, count them, and log the result.
94
+ # Sets _SUBAGENT_COUNT to the number of Agent tool calls detected.
95
95
  # Requires: USE_STREAM_JSON (from detect_stream_json_support)
96
+ _SUBAGENT_COUNT=0
96
97
  prizm_detect_subagents() {
97
98
  local session_log="$1"
99
+ _SUBAGENT_COUNT=0
98
100
  [[ -f "$session_log" ]] || return 0
99
101
 
100
- local has_subagent=false
102
+ local count=0
101
103
  if [[ "$USE_STREAM_JSON" == "true" ]]; then
102
- grep -q '"name"[[:space:]]*:[[:space:]]*"Agent"' "$session_log" 2>/dev/null && has_subagent=true
104
+ count=$(grep -c '"name"[[:space:]]*:[[:space:]]*"Agent"' "$session_log" 2>/dev/null || echo "0")
103
105
  else
104
- grep -qE '(Tool: Agent|"tool":\s*"Agent"|tool_use.*Agent|subagent_type)' "$session_log" 2>/dev/null && has_subagent=true
106
+ count=$(grep -cE '(Tool: Agent|"tool":\s*"Agent"|tool_use.*Agent|subagent_type)' "$session_log" 2>/dev/null || echo "0")
105
107
  fi
106
108
 
107
- if [[ "$has_subagent" == true ]]; then
108
- log_info "Subagent calls detected in session"
109
+ _SUBAGENT_COUNT=$count
110
+ if [[ "$count" -gt 0 ]]; then
111
+ log_info "Subagent calls detected in session: $count"
112
+ else
113
+ log_info "Subagent calls detected in session: 0 (single-agent mode)"
109
114
  fi
110
115
  }
111
116
 
@@ -599,6 +599,7 @@ main() {
599
599
  fi
600
600
 
601
601
  local session_count=0
602
+ local total_subagent_calls=0
602
603
 
603
604
  while true; do
604
605
  # Find next bug to process
@@ -614,6 +615,7 @@ main() {
614
615
  log_success "════════════════════════════════════════════════════"
615
616
  log_success " All bugs processed! Bug fix pipeline finished."
616
617
  log_success " Total sessions: $session_count"
618
+ log_success " Total subagent calls: $total_subagent_calls"
617
619
  log_success "════════════════════════════════════════════════════"
618
620
 
619
621
  # Merge dev branch back to original
@@ -672,6 +674,10 @@ main() {
672
674
  --state-dir "$STATE_DIR" \
673
675
  --output "$bootstrap_prompt" >/dev/null 2>&1
674
676
 
677
+ # Log agent configuration (bugfix always uses dual-agent: Orchestrator + Dev + Reviewer)
678
+ log_info "Pipeline mode: ${BOLD}standard${NC} (Dual Agent — Orchestrator + Dev + Reviewer)"
679
+ log_info "Agents: 3 (critic: disabled)"
680
+
675
681
  # Spawn session
676
682
  log_info "Spawning AI CLI session: $session_id"
677
683
  _SPAWN_RESULT=""
@@ -681,6 +687,7 @@ main() {
681
687
  "$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$_ORIGINAL_BRANCH"
682
688
 
683
689
  session_count=$((session_count + 1))
690
+ total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
684
691
 
685
692
  log_info "Pausing 5s before next bug..."
686
693
  sleep 5
@@ -647,8 +647,11 @@ sys.exit(1)
647
647
  log_error "Failed to generate bootstrap prompt for $feature_id"
648
648
  return 1
649
649
  }
650
- local feature_model
650
+ local feature_model pipeline_mode agent_count critic_enabled
651
651
  feature_model=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('model',''))" 2>/dev/null || echo "")
652
+ pipeline_mode=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('pipeline_mode','lite'))" 2>/dev/null || echo "lite")
653
+ agent_count=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('agent_count',1))" 2>/dev/null || echo "1")
654
+ critic_enabled=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('critic_enabled','false'))" 2>/dev/null || echo "false")
652
655
 
653
656
  # Dry-Run: Print info and exit
654
657
  if [[ "$dry_run" == true ]]; then
@@ -664,6 +667,8 @@ sys.exit(1)
664
667
  else
665
668
  log_info "Mode: auto-detect (from complexity)"
666
669
  fi
670
+ log_info "Pipeline mode: $pipeline_mode"
671
+ log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
667
672
  if [[ -n "$feature_model" ]]; then
668
673
  log_info "Feature Model: $feature_model"
669
674
  elif [[ -n "${MODEL:-}" ]]; then
@@ -707,6 +712,15 @@ sys.exit(1)
707
712
  if [[ -n "$mode_override" ]]; then
708
713
  log_info "Mode Override: $mode_override"
709
714
  fi
715
+ local _run_one_mode_desc
716
+ case "$pipeline_mode" in
717
+ lite) _run_one_mode_desc="Tier 1 — Single Agent" ;;
718
+ standard) _run_one_mode_desc="Tier 2 — Orchestrator + Dev + Reviewer" ;;
719
+ full) _run_one_mode_desc="Tier 3 — Full Team (+ Multi-Critic)" ;;
720
+ *) _run_one_mode_desc="$pipeline_mode" ;;
721
+ esac
722
+ log_info "Pipeline mode: ${BOLD}$pipeline_mode${NC} ($_run_one_mode_desc)"
723
+ log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
710
724
  if [[ $SESSION_TIMEOUT -gt 0 ]]; then
711
725
  log_info "Session timeout: ${SESSION_TIMEOUT}s"
712
726
  else
@@ -881,6 +895,7 @@ main() {
881
895
 
882
896
  # Main processing loop
883
897
  local session_count=0
898
+ local total_subagent_calls=0
884
899
 
885
900
  while true; do
886
901
  # Check for stuck features
@@ -923,6 +938,7 @@ for f in data.get('stuck_features', []):
923
938
  log_success "════════════════════════════════════════════════════"
924
939
  log_success " All features completed! Pipeline finished."
925
940
  log_success " Total sessions: $session_count"
941
+ log_success " Total subagent calls: $total_subagent_calls"
926
942
  log_success "════════════════════════════════════════════════════"
927
943
  break
928
944
  fi
@@ -999,8 +1015,22 @@ for f in data.get('stuck_features', []):
999
1015
  log_error "Failed to generate bootstrap prompt for $feature_id"
1000
1016
  continue
1001
1017
  }
1002
- local feature_model
1018
+ local feature_model pipeline_mode agent_count critic_enabled
1003
1019
  feature_model=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('model',''))" 2>/dev/null || echo "")
1020
+ pipeline_mode=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('pipeline_mode','lite'))" 2>/dev/null || echo "lite")
1021
+ agent_count=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('agent_count',1))" 2>/dev/null || echo "1")
1022
+ critic_enabled=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('critic_enabled','false'))" 2>/dev/null || echo "false")
1023
+
1024
+ # Log pipeline mode and agent configuration
1025
+ local _mode_desc
1026
+ case "$pipeline_mode" in
1027
+ lite) _mode_desc="Tier 1 — Single Agent" ;;
1028
+ standard) _mode_desc="Tier 2 — Orchestrator + Dev + Reviewer" ;;
1029
+ full) _mode_desc="Tier 3 — Full Team (+ Multi-Critic)" ;;
1030
+ *) _mode_desc="$pipeline_mode" ;;
1031
+ esac
1032
+ log_info "Pipeline mode: ${BOLD}$pipeline_mode${NC} ($_mode_desc)"
1033
+ log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
1004
1034
 
1005
1035
  # Mark feature as in-progress before spawning session
1006
1036
  python3 "$SCRIPTS_DIR/update-feature-status.py" \
@@ -1042,6 +1072,7 @@ for f in data.get('stuck_features', []):
1042
1072
  fi
1043
1073
 
1044
1074
  session_count=$((session_count + 1))
1075
+ total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
1045
1076
 
1046
1077
  # Brief pause before next iteration
1047
1078
  log_info "Pausing 5s before next feature..."
@@ -723,10 +723,22 @@ def main():
723
723
 
724
724
  # Success
725
725
  feature_model = feature.get("model", "")
726
+ pipeline_mode = replacements.get("{{PIPELINE_MODE}}", "lite")
727
+ critic_enabled = replacements.get("{{CRITIC_ENABLED}}", "false") == "true"
728
+ # Map mode to agent count for logging
729
+ # lite=1 (single agent), standard/full=3 (orchestrator + dev + reviewer)
730
+ mode_agent_counts = {"lite": 1, "standard": 3, "full": 3}
731
+ agent_count = mode_agent_counts.get(pipeline_mode, 1)
732
+ critic_count_val = int(replacements.get("{{CRITIC_COUNT}}", "1"))
733
+ if critic_enabled:
734
+ agent_count += critic_count_val
726
735
  output = {
727
736
  "success": True,
728
737
  "output_path": os.path.abspath(args.output),
729
738
  "model": feature_model,
739
+ "pipeline_mode": pipeline_mode,
740
+ "agent_count": agent_count,
741
+ "critic_enabled": "true" if critic_enabled else "false",
730
742
  }
731
743
  print(json.dumps(output, indent=2, ensure_ascii=False))
732
744
  sys.exit(0)
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.136",
2
+ "version": "1.0.138",
3
3
  "skills": {
4
4
  "prizm-kit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.136",
3
+ "version": "1.0.138",
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/manifest.js CHANGED
@@ -55,6 +55,7 @@ export async function writeManifest(projectRoot, data) {
55
55
  * @param {boolean} params.team - whether team config was installed
56
56
  * @param {string} [params.aiCli] - AI CLI command
57
57
  * @param {string} [params.rulesPreset] - rules preset name
58
+ * @param {string[]} [params.extras] - installed extras (e.g. ['playwright-cli'])
58
59
  * @returns {Object} manifest
59
60
  */
60
61
  export function buildManifest({
@@ -68,6 +69,7 @@ export function buildManifest({
68
69
  team,
69
70
  aiCli,
70
71
  rulesPreset,
72
+ extras,
71
73
  }) {
72
74
  const now = new Date().toISOString();
73
75
  return {
@@ -87,6 +89,7 @@ export function buildManifest({
87
89
  agents: [...agents],
88
90
  rules: [...rules],
89
91
  pipeline: pipeline ? [...pipeline] : [],
92
+ extras: extras ? [...extras] : [],
90
93
  other: [platform === 'claude' ? 'CLAUDE.md' : platform === 'codebuddy' ? 'CODEBUDDY.md' : 'CLAUDE.md'],
91
94
  },
92
95
  };
@@ -96,7 +99,7 @@ export function buildManifest({
96
99
  * Diff two manifests and return added/removed items.
97
100
  * @param {Object} oldManifest - previous manifest
98
101
  * @param {Object} newManifest - new manifest to compare against
99
- * @returns {Object} { skills: {added, removed}, agents: {added, removed}, rules: {added, removed} }
102
+ * @returns {Object} { skills: {added, removed}, agents: {added, removed}, rules: {added, removed}, pipeline: {added, removed}, extras: {added, removed} }
100
103
  */
101
104
  export function diffManifest(oldManifest, newManifest) {
102
105
  function diffArrays(oldArr, newArr) {
@@ -116,5 +119,6 @@ export function diffManifest(oldManifest, newManifest) {
116
119
  Array.isArray(oldManifest?.files?.pipeline) ? oldManifest.files.pipeline : [],
117
120
  Array.isArray(newManifest?.files?.pipeline) ? newManifest.files.pipeline : [],
118
121
  ),
122
+ extras: diffArrays(oldManifest?.files?.extras, newManifest?.files?.extras),
119
123
  };
120
124
  }
package/src/scaffold.js CHANGED
@@ -676,6 +676,7 @@ export async function installPipeline(projectRoot, dryRun, { forceOverwrite = fa
676
676
  */
677
677
  export async function installGitignore(projectRoot, options, dryRun) {
678
678
  const targetPath = path.join(projectRoot, '.gitignore');
679
+ const templateContent = generateGitignore({ pipeline: options.pipeline });
679
680
 
680
681
  if (dryRun) {
681
682
  console.log(chalk.gray(` [dry-run] .gitignore`));
@@ -683,12 +684,23 @@ export async function installGitignore(projectRoot, options, dryRun) {
683
684
  }
684
685
 
685
686
  if (await fs.pathExists(targetPath)) {
686
- console.log(chalk.yellow(` ⚠ .gitignore 已存在,跳过`));
687
- return;
687
+ // 合并模式:追加缺失条目
688
+ const existing = await fs.readFile(targetPath, 'utf-8');
689
+ const existingLines = new Set(existing.split('\n').map(l => l.trim()));
690
+ const templateLines = templateContent.split('\n').map(l => l.trim()).filter(Boolean);
691
+ const missing = templateLines.filter(line => !existingLines.has(line) && !line.startsWith('#'));
692
+
693
+ if (missing.length > 0) {
694
+ const separator = existing.endsWith('\n') ? '' : '\n';
695
+ const header = existingLines.has('# PrizmKit') ? '' : '# PrizmKit\n';
696
+ await fs.appendFile(targetPath, separator + '\n' + header + missing.join('\n') + '\n');
697
+ console.log(chalk.green(` ✓ .gitignore (added ${missing.length} entries)`));
698
+ } else {
699
+ console.log(chalk.green(' ✓ .gitignore (up-to-date)'));
700
+ }
688
701
  } else {
689
- const content = generateGitignore({ pipeline: options.pipeline });
690
- await fs.writeFile(targetPath, content);
691
- console.log(chalk.green(` ✓ .gitignore`));
702
+ await fs.writeFile(targetPath, templateContent);
703
+ console.log(chalk.green(' ✓ .gitignore'));
692
704
  }
693
705
  }
694
706
 
@@ -733,6 +745,21 @@ export async function installPlaywrightCli(projectRoot, dryRun) {
733
745
  }
734
746
  }
735
747
 
748
+ // ============================================================
749
+ // Extras 注册表
750
+ // ============================================================
751
+
752
+ /**
753
+ * 外部工具注册表。upgrade.js 遍历此表来安装/更新外部工具。
754
+ * 新增工具只需:1) 写 installXxx 函数 2) 加一行到此表
755
+ */
756
+ export const EXTRAS_REGISTRY = {
757
+ 'playwright-cli': {
758
+ label: '浏览器交互工具',
759
+ install: installPlaywrightCli,
760
+ },
761
+ };
762
+
736
763
  // ============================================================
737
764
  // 主安装函数
738
765
  // ============================================================
@@ -846,9 +873,11 @@ export async function scaffold(config) {
846
873
  }
847
874
  }
848
875
 
849
- // 10. Playwright CLI (optional)
876
+ // 10. Extras (external tools via registry)
877
+ const activeExtras = [];
850
878
  if (playwrightCli) {
851
- console.log(chalk.blue(' 浏览器交互工具:'));
879
+ activeExtras.push('playwright-cli');
880
+ console.log(chalk.blue(` ${EXTRAS_REGISTRY['playwright-cli'].label}:`));
852
881
  await installPlaywrightCli(projectRoot, dryRun);
853
882
  console.log('');
854
883
  }
@@ -866,15 +895,15 @@ export async function scaffold(config) {
866
895
  }
867
896
  }
868
897
 
869
- // 11. Git pre-commit hook
898
+ // 12. Git pre-commit hook
870
899
  console.log(chalk.blue(' Git Hook:'));
871
900
  await installGitHook(projectRoot, dryRun);
872
901
 
873
- // 12. PrizmKit scripts (always installed)
902
+ // 13. PrizmKit scripts (always installed)
874
903
  console.log(chalk.blue(' PrizmKit 脚本:'));
875
904
  await installPrizmkitScripts(projectRoot, dryRun);
876
905
 
877
- // 13. Write installation manifest
906
+ // 14. Write installation manifest
878
907
  if (!dryRun) {
879
908
  const agentsDir = getAgentsDir();
880
909
  const agentFileNames = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
@@ -894,6 +923,7 @@ export async function scaffold(config) {
894
923
  team,
895
924
  aiCli,
896
925
  rulesPreset: rulesPresetName,
926
+ extras: activeExtras,
897
927
  });
898
928
  await writeManifest(projectRoot, manifest);
899
929
  console.log(chalk.green(' ✓ .prizmkit/manifest.json'));
package/src/upgrade.js CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  installGitignore,
31
31
  installProjectMemory,
32
32
  resolvePipelineFileList,
33
+ EXTRAS_REGISTRY,
33
34
  } from './scaffold.js';
34
35
 
35
36
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -244,6 +245,13 @@ export async function runUpgrade(directory, options = {}) {
244
245
  // Resolve pipeline file list from source (for manifest diff)
245
246
  const newPipelineFiles = pipeline ? resolvePipelineFileList() : [];
246
247
 
248
+ // Resolve extras: merge old manifest extras + default extras for old manifests without the field
249
+ const extrasToInstall = [...new Set([
250
+ ...(oldManifest?.files?.extras || []),
251
+ // Old manifests without extras field: default to all registered extras
252
+ ...(!oldManifest?.files?.extras ? Object.keys(EXTRAS_REGISTRY) : []),
253
+ ])].filter(name => EXTRAS_REGISTRY[name]); // only keep extras that still exist in registry
254
+
247
255
  // 4. Build new manifest and compute diff
248
256
  const newManifest = buildManifest({
249
257
  version: pkg.version,
@@ -256,6 +264,7 @@ export async function runUpgrade(directory, options = {}) {
256
264
  team,
257
265
  aiCli,
258
266
  rulesPreset,
267
+ extras: extrasToInstall,
259
268
  });
260
269
 
261
270
  // Preserve original installedAt
@@ -263,7 +272,7 @@ export async function runUpgrade(directory, options = {}) {
263
272
  newManifest.installedAt = oldManifest.installedAt;
264
273
  }
265
274
 
266
- const diff = oldManifest ? diffManifest(oldManifest, newManifest) : { skills: { added: [], removed: [] }, agents: { added: [], removed: [] }, rules: { added: [], removed: [] }, pipeline: { added: [], removed: [] } };
275
+ const diff = oldManifest ? diffManifest(oldManifest, newManifest) : { skills: { added: [], removed: [] }, agents: { added: [], removed: [] }, rules: { added: [], removed: [] }, pipeline: { added: [], removed: [] }, extras: { added: [], removed: [] } };
267
276
 
268
277
  // 5. Display upgrade summary
269
278
  const oldVersion = oldManifest?.version || 'unknown';
@@ -273,8 +282,8 @@ export async function runUpgrade(directory, options = {}) {
273
282
  console.log(` Suite: ${suite}`);
274
283
  console.log('');
275
284
 
276
- const totalAdded = diff.skills.added.length + diff.agents.added.length + diff.rules.added.length + diff.pipeline.added.length;
277
- const totalRemoved = diff.skills.removed.length + diff.agents.removed.length + diff.rules.removed.length + diff.pipeline.removed.length;
285
+ const totalAdded = diff.skills.added.length + diff.agents.added.length + diff.rules.added.length + diff.pipeline.added.length + diff.extras.added.length;
286
+ const totalRemoved = diff.skills.removed.length + diff.agents.removed.length + diff.rules.removed.length + diff.pipeline.removed.length + diff.extras.removed.length;
278
287
  const totalUpdated = newSkillList.length + newAgentFiles.length + newRuleFiles.length;
279
288
 
280
289
  if (diff.skills.added.length) console.log(chalk.green(` + Skills added: ${diff.skills.added.join(', ')}`));
@@ -285,6 +294,8 @@ export async function runUpgrade(directory, options = {}) {
285
294
  if (diff.rules.removed.length) console.log(chalk.red(` - Rules removed: ${diff.rules.removed.join(', ')}`));
286
295
  if (diff.pipeline.added.length) console.log(chalk.green(` + Pipeline files added: ${diff.pipeline.added.length} file(s)`));
287
296
  if (diff.pipeline.removed.length) console.log(chalk.red(` - Pipeline files removed: ${diff.pipeline.removed.length} file(s)`));
297
+ if (diff.extras.added.length) console.log(chalk.green(` + Extras added: ${diff.extras.added.join(', ')}`));
298
+ if (diff.extras.removed.length) console.log(chalk.red(` - Extras removed: ${diff.extras.removed.join(', ')}`));
288
299
 
289
300
  if (totalAdded === 0 && totalRemoved === 0 && oldVersion === pkg.version) {
290
301
  console.log(chalk.gray(' No changes detected. Re-installing all files to ensure consistency.'));
@@ -360,6 +371,17 @@ export async function runUpgrade(directory, options = {}) {
360
371
  console.log('');
361
372
  }
362
373
 
374
+ // Extras (external tools via registry)
375
+ if (extrasToInstall.length > 0) {
376
+ for (const extraName of extrasToInstall) {
377
+ const entry = EXTRAS_REGISTRY[extraName];
378
+ if (!entry) continue;
379
+ console.log(chalk.blue(` ${entry.label}:`));
380
+ await entry.install(projectRoot, dryRun);
381
+ console.log('');
382
+ }
383
+ }
384
+
363
385
  // Git hook
364
386
  console.log(chalk.blue(' Git Hook:'));
365
387
  await installGitHook(projectRoot, dryRun);