prizmkit 1.1.100 → 1.1.102
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 +4 -4
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/.env.example +3 -3
- package/bundled/dev-pipeline/README.md +110 -90
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
- package/bundled/dev-pipeline/cli.py +27 -0
- package/bundled/dev-pipeline/prizmkit_runtime/__init__.py +13 -0
- package/bundled/dev-pipeline/prizmkit_runtime/__main__.py +9 -0
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +193 -0
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +313 -0
- package/bundled/dev-pipeline/prizmkit_runtime/compat.py +32 -0
- package/bundled/dev-pipeline/prizmkit_runtime/config.py +410 -0
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +478 -0
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +734 -0
- package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +256 -0
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +109 -0
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +110 -0
- package/bundled/dev-pipeline/prizmkit_runtime/processes.py +373 -0
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +389 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +51 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +179 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +355 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +118 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +187 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +153 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +425 -0
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +560 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +51 -36
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +20 -5
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +39 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +8 -2
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +6 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -6
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +14 -4
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +14 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
- package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +9 -30
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +15 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +42 -29
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +41 -39
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +8 -17
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +1 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +1 -3
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +3 -8
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +6 -2
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +5 -1
- package/bundled/dev-pipeline/tests/test_auto_skip.py +436 -14
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +115 -7
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +40 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1226 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +1045 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +29 -34
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +6 -5
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +29 -35
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +7 -6
- package/bundled/skills/feature-workflow/SKILL.md +3 -6
- package/bundled/skills/recovery-workflow/SKILL.md +23 -32
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -35
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +7 -6
- package/bundled/skills/refactor-workflow/SKILL.md +3 -6
- package/bundled/templates/hooks/commit-intent.json +2 -2
- package/bundled/templates/hooks/diff-prizm-docs.py +226 -0
- package/bundled/templates/hooks/prizm-pre-commit.py +45 -0
- package/bundled/templates/hooks/validate-prizm-docs.py +240 -0
- package/package.json +1 -1
- package/src/config.js +42 -19
- package/src/external-skills.js +14 -14
- package/src/index.js +3 -6
- package/src/manifest.js +11 -5
- package/src/metadata.js +7 -18
- package/src/platforms.js +25 -0
- package/src/prompts.js +0 -12
- package/src/runtimes.js +18 -11
- package/src/scaffold.js +144 -122
- package/src/upgrade.js +12 -3
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -500
- package/bundled/dev-pipeline/launch-feature-daemon.sh +0 -657
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -502
- package/bundled/dev-pipeline/lib/branch.sh +0 -326
- package/bundled/dev-pipeline/lib/common.sh +0 -1476
- package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
- package/bundled/dev-pipeline/reset-bug.sh +0 -425
- package/bundled/dev-pipeline/reset-feature.sh +0 -436
- package/bundled/dev-pipeline/reset-refactor.sh +0 -423
- package/bundled/dev-pipeline/run-bugfix.sh +0 -1430
- package/bundled/dev-pipeline/run-feature.sh +0 -1718
- package/bundled/dev-pipeline/run-recovery.sh +0 -648
- package/bundled/dev-pipeline/run-refactor.sh +0 -1463
- package/bundled/dev-pipeline/tests/test-path-resolution.sh +0 -100
- package/bundled/dev-pipeline-windows/.env.example +0 -36
- package/bundled/dev-pipeline-windows/README.md +0 -30
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -533
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +0 -146
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -137
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +0 -235
- package/bundled/dev-pipeline-windows/lib/common.ps1 +0 -991
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -1051
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -121
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -10
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -10
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -10
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +0 -9
- package/bundled/dev-pipeline-windows/run-feature.ps1 +0 -9
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +0 -179
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +0 -9
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +0 -301
- package/bundled/dev-pipeline-windows/scripts/continuation.py +0 -374
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2279
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -763
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -841
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +0 -316
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +0 -134
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +0 -382
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +0 -399
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1362
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +0 -191
- package/bundled/dev-pipeline-windows/scripts/prizmkit-test-gate.py +0 -446
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +0 -1121
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1789
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1327
- package/bundled/dev-pipeline-windows/scripts/utils.py +0 -542
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +0 -7
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +0 -7
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +0 -65
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +0 -5
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +0 -7
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +0 -73
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +0 -589
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +0 -710
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +0 -791
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +0 -263
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +0 -326
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +0 -237
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +0 -340
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +0 -270
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +0 -7
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +0 -91
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +0 -34
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +0 -10
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +0 -12
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +0 -8
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +0 -9
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +0 -41
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +0 -283
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +0 -112
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +0 -168
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +0 -82
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +0 -75
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +0 -23
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +0 -14
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +0 -19
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +0 -63
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +0 -42
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +0 -59
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +0 -39
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +0 -27
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +0 -27
- package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +0 -39
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +0 -35
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +0 -35
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +0 -73
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +0 -13
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +0 -5
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +0 -6
- package/bundled/dev-pipeline-windows/templates/sections/task-contract.md +0 -34
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +0 -48
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +0 -48
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +0 -83
- package/bundled/skills-windows/app-planner/SKILL.md +0 -296
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +0 -101
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +0 -52
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +0 -101
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +0 -71
- package/bundled/skills-windows/app-planner/references/infrastructure-convention-discovery.md +0 -108
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +0 -82
- package/bundled/skills-windows/app-planner/references/project-conventions-discovery.md +0 -59
- package/bundled/skills-windows/app-planner/references/project-state-detection.md +0 -90
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +0 -40
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +0 -619
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +0 -285
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +0 -249
- package/bundled/skills-windows/app-planner/references/rules/backend/question-manifest.json +0 -46
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +0 -173
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +0 -382
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +0 -211
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +0 -184
- package/bundled/skills-windows/app-planner/references/rules/database/question-manifest.json +0 -39
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +0 -158
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +0 -820
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +0 -188
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +0 -319
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-manifest.json +0 -51
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +0 -339
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +0 -649
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +0 -290
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +0 -232
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-manifest.json +0 -47
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +0 -175
- package/bundled/skills-windows/app-planner/references/rules-configuration.md +0 -46
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +0 -379
- package/bundled/skills-windows/bug-fix-workflow/references/bug-diagnosis.md +0 -41
- package/bundled/skills-windows/bug-planner/SKILL.md +0 -402
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +0 -43
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +0 -44
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +0 -73
- package/bundled/skills-windows/bug-planner/references/input-formats.md +0 -53
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +0 -25
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +0 -16
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +0 -322
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +0 -304
- package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +0 -84
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +0 -381
- package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +0 -67
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
- package/bundled/skills-windows/feature-planner/SKILL.md +0 -397
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +0 -64
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +0 -214
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +0 -59
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +0 -57
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +0 -75
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +0 -90
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +0 -112
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +0 -85
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +0 -1020
- package/bundled/skills-windows/feature-workflow/SKILL.md +0 -374
- package/bundled/skills-windows/feature-workflow/references/brainstorm-guide.md +0 -137
- package/bundled/skills-windows/prizm-kit/SKILL.md +0 -93
- package/bundled/skills-windows/prizmkit-code-review/SKILL.md +0 -161
- package/bundled/skills-windows/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
- package/bundled/skills-windows/prizmkit-code-review/references/review-report-template.md +0 -31
- package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -66
- package/bundled/skills-windows/prizmkit-code-review/scripts/check_loop.py +0 -186
- package/bundled/skills-windows/prizmkit-committer/SKILL.md +0 -89
- package/bundled/skills-windows/prizmkit-deploy/SKILL.md +0 -444
- package/bundled/skills-windows/prizmkit-deploy/references/ci-cd-workflows.md +0 -115
- package/bundled/skills-windows/prizmkit-deploy/references/cloud-platform-deploy.md +0 -93
- package/bundled/skills-windows/prizmkit-deploy/references/data-safety-examples.md +0 -120
- package/bundled/skills-windows/prizmkit-deploy/references/database-setup.md +0 -46
- package/bundled/skills-windows/prizmkit-deploy/references/deploy-config-schema.md +0 -148
- package/bundled/skills-windows/prizmkit-deploy/references/deploy-history-schema.md +0 -62
- package/bundled/skills-windows/prizmkit-deploy/references/deployment-modes.md +0 -50
- package/bundled/skills-windows/prizmkit-deploy/references/direct-upload.md +0 -26
- package/bundled/skills-windows/prizmkit-deploy/references/dns-setup.md +0 -42
- package/bundled/skills-windows/prizmkit-deploy/references/docker-deploy.md +0 -31
- package/bundled/skills-windows/prizmkit-deploy/references/firewall-setup.md +0 -37
- package/bundled/skills-windows/prizmkit-deploy/references/live-validation-notes.md +0 -21
- package/bundled/skills-windows/prizmkit-deploy/references/nginx-blue-green.md +0 -59
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-takeover.md +0 -20
- package/bundled/skills-windows/prizmkit-deploy/references/ssl-setup.md +0 -56
- package/bundled/skills-windows/prizmkit-implement/SKILL.md +0 -71
- package/bundled/skills-windows/prizmkit-init/SKILL.md +0 -356
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +0 -82
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +0 -70
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +0 -41
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +0 -13
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +0 -9
- package/bundled/skills-windows/prizmkit-plan/SKILL.md +0 -102
- package/bundled/skills-windows/prizmkit-plan/assets/plan-template.md +0 -115
- package/bundled/skills-windows/prizmkit-plan/assets/spec-template.md +0 -73
- package/bundled/skills-windows/prizmkit-plan/references/clarify-guide.md +0 -67
- package/bundled/skills-windows/prizmkit-plan/references/examples.md +0 -85
- package/bundled/skills-windows/prizmkit-plan/references/verification-checklist.md +0 -60
- package/bundled/skills-windows/prizmkit-prizm-docs/SKILL.md +0 -129
- package/bundled/skills-windows/prizmkit-prizm-docs/assets/prizm-docs-format.md +0 -613
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-init.md +0 -45
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-rebuild.md +0 -15
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-status.md +0 -14
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-update.md +0 -19
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-validate.md +0 -17
- package/bundled/skills-windows/prizmkit-retrospective/SKILL.md +0 -87
- package/bundled/skills-windows/prizmkit-retrospective/references/knowledge-injection-steps.md +0 -50
- package/bundled/skills-windows/prizmkit-retrospective/references/structural-sync-steps.md +0 -43
- package/bundled/skills-windows/prizmkit-test/SKILL.md +0 -227
- package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +0 -220
- package/bundled/skills-windows/prizmkit-test/references/examples.md +0 -143
- package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +0 -225
- package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +0 -109
- package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +0 -111
- package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +0 -347
- package/bundled/skills-windows/recovery-workflow/SKILL.md +0 -355
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +0 -46
- package/bundled/skills-windows/recovery-workflow/references/detection.md +0 -58
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +0 -544
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +0 -346
- package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +0 -72
- package/bundled/skills-windows/refactor-planner/SKILL.md +0 -398
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +0 -292
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +0 -301
- package/bundled/skills-windows/refactor-planner/references/fast-path.md +0 -59
- package/bundled/skills-windows/refactor-planner/references/planning-phases.md +0 -135
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +0 -221
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +0 -858
- package/bundled/skills-windows/refactor-workflow/SKILL.md +0 -343
- package/bundled/skills-windows/refactor-workflow/references/brainstorm-guide.md +0 -116
- package/bundled/templates/hooks/diff-prizm-docs.sh +0 -158
- package/bundled/templates/hooks/prizm-pre-commit.sh +0 -108
- package/bundled/templates/hooks/validate-prizm-docs.sh +0 -139
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"""Daemon lifecycle commands for the unified Python runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shlex
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from .commands import CommandResult # type: ignore # runtime boundary import
|
|
16
|
+
from .processes import ProcessSpec, is_pid_running, launch_process, terminate_pid_tree
|
|
17
|
+
from .runner_models import RunnerFamily, family_for
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
LOG_ROTATE_BYTES = 50 * 1024 * 1024
|
|
21
|
+
START_VERIFY_SECONDS = 2.0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class DaemonPaths:
|
|
26
|
+
"""Filesystem contract for one pipeline daemon."""
|
|
27
|
+
|
|
28
|
+
state_dir: Path
|
|
29
|
+
pid_file: Path
|
|
30
|
+
log_file: Path
|
|
31
|
+
meta_file: Path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class DaemonStartOptions:
|
|
36
|
+
"""Parsed daemon start/restart arguments."""
|
|
37
|
+
|
|
38
|
+
list_path: Path
|
|
39
|
+
env_overrides: str = ""
|
|
40
|
+
mode: str = ""
|
|
41
|
+
features_filter: str = ""
|
|
42
|
+
critic: bool | None = None
|
|
43
|
+
help_requested: bool = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class DaemonLogOptions:
|
|
48
|
+
"""Parsed daemon log arguments."""
|
|
49
|
+
|
|
50
|
+
lines: int = 50
|
|
51
|
+
follow: bool = False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
LIST_META_KEY_BY_KIND = {
|
|
55
|
+
"feature": "feature_list",
|
|
56
|
+
"bugfix": "bug_list",
|
|
57
|
+
"refactor": "refactor_list",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
ITEMS_KEY_BY_KIND = {
|
|
61
|
+
"feature": "features",
|
|
62
|
+
"bugfix": "bugs",
|
|
63
|
+
"refactor": "refactors",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ID_KEY_BY_KIND = {
|
|
67
|
+
"feature": "id",
|
|
68
|
+
"bugfix": "id",
|
|
69
|
+
"refactor": "id",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
PROGRESS_COUNTS_BY_KIND = {
|
|
73
|
+
"feature": ("completed", "in_progress", "failed", "pending", "skipped", "auto_skipped"),
|
|
74
|
+
"bugfix": ("completed", "in_progress", "failed", "pending", "needs_info", "skipped"),
|
|
75
|
+
"refactor": ("completed", "in_progress", "failed", "pending", "skipped", "auto_skipped"),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def run_daemon_command(kind: str, action: str, legacy_args: tuple[str, ...], paths) -> CommandResult:
|
|
80
|
+
"""Run a daemon lifecycle command for feature, bugfix, or refactor."""
|
|
81
|
+
family = family_for(kind, paths)
|
|
82
|
+
daemon_paths = _daemon_paths(family)
|
|
83
|
+
if action == "describe":
|
|
84
|
+
return CommandResult(0, f"Python {kind} daemon", _help_text(family))
|
|
85
|
+
if action == "start":
|
|
86
|
+
return _start(family, daemon_paths, legacy_args, paths)
|
|
87
|
+
if action == "status":
|
|
88
|
+
return _status(family, daemon_paths)
|
|
89
|
+
if action == "stop":
|
|
90
|
+
return _stop(family, daemon_paths)
|
|
91
|
+
if action in {"logs", "log"}:
|
|
92
|
+
return _logs(family, daemon_paths, legacy_args)
|
|
93
|
+
if action == "restart":
|
|
94
|
+
stop_result = _stop(family, daemon_paths)
|
|
95
|
+
start_result = _start(family, daemon_paths, legacy_args, paths)
|
|
96
|
+
stderr = "\n".join(part for part in (stop_result.stderr, start_result.stderr) if part)
|
|
97
|
+
return CommandResult(start_result.exit_code, start_result.title, start_result.message, stdout=start_result.render(), stderr=stderr)
|
|
98
|
+
return CommandResult(1, f"Python {kind} daemon", f"Unsupported daemon action: {action}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _daemon_paths(family: RunnerFamily) -> DaemonPaths:
|
|
102
|
+
return DaemonPaths(
|
|
103
|
+
state_dir=family.state_dir,
|
|
104
|
+
pid_file=family.state_dir / ".pipeline.pid",
|
|
105
|
+
log_file=family.state_dir / "pipeline-daemon.log",
|
|
106
|
+
meta_file=family.state_dir / ".pipeline-meta.json",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _start(family: RunnerFamily, daemon_paths: DaemonPaths, legacy_args: tuple[str, ...], paths) -> CommandResult:
|
|
111
|
+
options = _parse_start_args(family, legacy_args)
|
|
112
|
+
if options.help_requested:
|
|
113
|
+
return CommandResult(0, f"Python {family.kind} daemon", _help_text(family))
|
|
114
|
+
list_path = options.list_path if options.list_path.is_absolute() else (paths.project_root / options.list_path).resolve()
|
|
115
|
+
if not list_path.is_file():
|
|
116
|
+
stderr = f"{family.kind} list not found: {list_path}"
|
|
117
|
+
return CommandResult(2, f"Python {family.kind} daemon", stderr, stdout="", stderr=stderr)
|
|
118
|
+
|
|
119
|
+
daemon_paths.state_dir.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
stale_warning = _clean_stale_pid(daemon_paths)
|
|
121
|
+
stderr_lines = [stale_warning] if stale_warning else []
|
|
122
|
+
running_pid = _read_pid(daemon_paths.pid_file)
|
|
123
|
+
if is_pid_running(running_pid):
|
|
124
|
+
message = f"Pipeline is already running (PID: {running_pid})"
|
|
125
|
+
return CommandResult(1, f"Python {family.kind} daemon", message, stdout="", stderr=message)
|
|
126
|
+
|
|
127
|
+
rotated = _rotate_log_if_needed(daemon_paths.log_file)
|
|
128
|
+
if rotated:
|
|
129
|
+
stderr_lines.append(f"Log rotated: {rotated}")
|
|
130
|
+
|
|
131
|
+
started_at = _utc_iso()
|
|
132
|
+
_write_log_separator(family, daemon_paths.log_file, list_path, started_at, options)
|
|
133
|
+
command = _payload_command(family, paths, list_path, options)
|
|
134
|
+
env = _daemon_env(options)
|
|
135
|
+
spec = ProcessSpec(
|
|
136
|
+
command=tuple(command),
|
|
137
|
+
cwd=paths.project_root,
|
|
138
|
+
environment_overrides=env,
|
|
139
|
+
environment_unsets=("CLAUDECODE",),
|
|
140
|
+
pid_file=daemon_paths.pid_file,
|
|
141
|
+
)
|
|
142
|
+
log_handle = daemon_paths.log_file.open("a", encoding="utf-8")
|
|
143
|
+
try:
|
|
144
|
+
handle = launch_process(spec, stdout=log_handle, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, text=True)
|
|
145
|
+
finally:
|
|
146
|
+
log_handle.close()
|
|
147
|
+
|
|
148
|
+
pid = int(handle.pid or 0)
|
|
149
|
+
_write_meta(family, daemon_paths.meta_file, pid, started_at, list_path, options, daemon_paths.log_file)
|
|
150
|
+
time.sleep(_start_verify_seconds())
|
|
151
|
+
if not is_pid_running(pid):
|
|
152
|
+
daemon_paths.pid_file.unlink(missing_ok=True)
|
|
153
|
+
message = f"Pipeline process died immediately after launch. Check log for errors: {daemon_paths.log_file}"
|
|
154
|
+
return CommandResult(1, f"Python {family.kind} daemon", message, stdout="", stderr="\n".join([*stderr_lines, message]))
|
|
155
|
+
|
|
156
|
+
stderr_lines.extend(
|
|
157
|
+
[
|
|
158
|
+
f"Launching {family.kind} pipeline...",
|
|
159
|
+
f"{family.kind} list: {list_path}",
|
|
160
|
+
f"Log file: {daemon_paths.log_file}",
|
|
161
|
+
f"Pipeline started successfully (PID: {pid})",
|
|
162
|
+
]
|
|
163
|
+
)
|
|
164
|
+
payload = {"success": True, "pid": pid, "log_file": str(daemon_paths.log_file), "started_at": started_at}
|
|
165
|
+
return CommandResult(0, f"Python {family.kind} daemon", "started", stdout=json.dumps(payload), stderr="\n".join(stderr_lines))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _status(family: RunnerFamily, daemon_paths: DaemonPaths) -> CommandResult:
|
|
169
|
+
daemon_paths.state_dir.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
stderr_lines: list[str] = []
|
|
171
|
+
stale_warning = _clean_stale_pid(daemon_paths)
|
|
172
|
+
if stale_warning:
|
|
173
|
+
stderr_lines.append(stale_warning)
|
|
174
|
+
|
|
175
|
+
pid = _read_pid(daemon_paths.pid_file)
|
|
176
|
+
meta = _read_meta(daemon_paths.meta_file)
|
|
177
|
+
if not is_pid_running(pid):
|
|
178
|
+
stderr_lines.append("Pipeline is not running")
|
|
179
|
+
if daemon_paths.log_file.is_file():
|
|
180
|
+
stderr_lines.append(f"Last log: {daemon_paths.log_file} ({daemon_paths.log_file.stat().st_size // 1024}KB)")
|
|
181
|
+
return CommandResult(1, f"Python {family.kind} daemon status", "not running", stdout=json.dumps({"running": False}), stderr="\n".join(stderr_lines))
|
|
182
|
+
|
|
183
|
+
list_key = LIST_META_KEY_BY_KIND[family.kind]
|
|
184
|
+
list_path = str(meta.get(list_key) or "")
|
|
185
|
+
log_size_kb = daemon_paths.log_file.stat().st_size // 1024 if daemon_paths.log_file.is_file() else 0
|
|
186
|
+
started_at = str(meta.get("started_at") or "")
|
|
187
|
+
payload: dict[str, object] = {
|
|
188
|
+
"running": True,
|
|
189
|
+
"pid": pid,
|
|
190
|
+
"log_file": str(daemon_paths.log_file),
|
|
191
|
+
"log_size_kb": log_size_kb,
|
|
192
|
+
"started_at": started_at,
|
|
193
|
+
list_key: list_path,
|
|
194
|
+
}
|
|
195
|
+
if family.kind != "feature":
|
|
196
|
+
payload["pipeline_type"] = family.kind
|
|
197
|
+
progress = _progress_summary(family, Path(list_path) if list_path else None)
|
|
198
|
+
if progress:
|
|
199
|
+
payload["progress"] = progress
|
|
200
|
+
|
|
201
|
+
stderr_lines.append(f"Pipeline is running (PID: {pid})")
|
|
202
|
+
if started_at:
|
|
203
|
+
stderr_lines.append(f"Started at: {started_at}")
|
|
204
|
+
stderr_lines.append(f"Log file: {daemon_paths.log_file} ({log_size_kb}KB)")
|
|
205
|
+
tail = _tail_lines(daemon_paths.log_file, 5)
|
|
206
|
+
if tail:
|
|
207
|
+
stderr_lines.append("--- Last 5 log lines ---")
|
|
208
|
+
stderr_lines.extend(tail)
|
|
209
|
+
return CommandResult(0, f"Python {family.kind} daemon status", "running", stdout=json.dumps(payload), stderr="\n".join(stderr_lines))
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _stop(family: RunnerFamily, daemon_paths: DaemonPaths) -> CommandResult:
|
|
213
|
+
if not daemon_paths.pid_file.is_file():
|
|
214
|
+
message = "Pipeline is not running (no PID file)"
|
|
215
|
+
return CommandResult(0, f"Python {family.kind} daemon stop", message, stdout=json.dumps({"success": True, "message": "not running"}), stderr=message)
|
|
216
|
+
pid = _read_pid(daemon_paths.pid_file)
|
|
217
|
+
if pid is None:
|
|
218
|
+
daemon_paths.pid_file.unlink(missing_ok=True)
|
|
219
|
+
message = "Pipeline is not running (empty PID file)"
|
|
220
|
+
return CommandResult(0, f"Python {family.kind} daemon stop", message, stdout=json.dumps({"success": True, "message": "not running"}), stderr=message)
|
|
221
|
+
if not is_pid_running(pid):
|
|
222
|
+
daemon_paths.pid_file.unlink(missing_ok=True)
|
|
223
|
+
message = f"Pipeline is not running (process {pid} already exited)"
|
|
224
|
+
return CommandResult(0, f"Python {family.kind} daemon stop", message, stdout=json.dumps({"success": True, "message": "already exited"}), stderr=message)
|
|
225
|
+
|
|
226
|
+
termination = terminate_pid_tree(pid, "daemon_stop", grace_seconds=30.0)
|
|
227
|
+
if termination.terminated:
|
|
228
|
+
daemon_paths.pid_file.unlink(missing_ok=True)
|
|
229
|
+
payload = {"success": True, "pid": pid, "message": "stopped"}
|
|
230
|
+
return CommandResult(0, f"Python {family.kind} daemon stop", "stopped", stdout=json.dumps(payload), stderr=f"Pipeline stopped (PID: {pid})")
|
|
231
|
+
payload = {"success": False, "pid": pid, "message": "failed to stop"}
|
|
232
|
+
return CommandResult(1, f"Python {family.kind} daemon stop", "failed", stdout=json.dumps(payload), stderr=f"Failed to stop pipeline (PID: {pid})")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _logs(family: RunnerFamily, daemon_paths: DaemonPaths, legacy_args: tuple[str, ...]) -> CommandResult:
|
|
236
|
+
options = _parse_log_args(legacy_args)
|
|
237
|
+
if not daemon_paths.log_file.is_file():
|
|
238
|
+
message = f"No log file found at {daemon_paths.log_file}\nPipeline has not been started yet"
|
|
239
|
+
return CommandResult(0, f"Python {family.kind} daemon logs", message, stdout="", stderr=message)
|
|
240
|
+
if options.follow:
|
|
241
|
+
return _follow_log(family, daemon_paths.log_file)
|
|
242
|
+
return CommandResult(0, f"Python {family.kind} daemon logs", "logs", stdout="\n".join(_tail_lines(daemon_paths.log_file, options.lines)))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> DaemonStartOptions:
|
|
246
|
+
list_path = family.plan_path
|
|
247
|
+
env_overrides = ""
|
|
248
|
+
mode = ""
|
|
249
|
+
features_filter = ""
|
|
250
|
+
critic: bool | None = None
|
|
251
|
+
help_requested = False
|
|
252
|
+
positional: list[str] = []
|
|
253
|
+
index = 0
|
|
254
|
+
while index < len(legacy_args):
|
|
255
|
+
arg = legacy_args[index]
|
|
256
|
+
if arg in {"--help", "-h", "help"}:
|
|
257
|
+
help_requested = True
|
|
258
|
+
elif arg == "--env" and index + 1 < len(legacy_args):
|
|
259
|
+
index += 1
|
|
260
|
+
env_overrides = legacy_args[index]
|
|
261
|
+
elif arg == "--mode" and index + 1 < len(legacy_args):
|
|
262
|
+
index += 1
|
|
263
|
+
mode = legacy_args[index]
|
|
264
|
+
if mode not in {"lite", "standard", "full"}:
|
|
265
|
+
raise ValueError(f"Invalid mode: {mode} (must be lite, standard, or full)")
|
|
266
|
+
elif arg == "--features" and index + 1 < len(legacy_args):
|
|
267
|
+
index += 1
|
|
268
|
+
features_filter = legacy_args[index]
|
|
269
|
+
elif arg == "--critic":
|
|
270
|
+
critic = True
|
|
271
|
+
elif arg == "--no-critic":
|
|
272
|
+
critic = False
|
|
273
|
+
else:
|
|
274
|
+
positional.append(arg)
|
|
275
|
+
index += 1
|
|
276
|
+
if positional:
|
|
277
|
+
list_path = Path(positional[0]).expanduser()
|
|
278
|
+
return DaemonStartOptions(list_path=list_path, env_overrides=env_overrides, mode=mode, features_filter=features_filter, critic=critic, help_requested=help_requested)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _parse_log_args(legacy_args: tuple[str, ...]) -> DaemonLogOptions:
|
|
282
|
+
lines = 50
|
|
283
|
+
follow = False
|
|
284
|
+
index = 0
|
|
285
|
+
while index < len(legacy_args):
|
|
286
|
+
arg = legacy_args[index]
|
|
287
|
+
if arg in {"--follow", "-f"}:
|
|
288
|
+
follow = True
|
|
289
|
+
elif arg in {"--lines", "-n"} and index + 1 < len(legacy_args):
|
|
290
|
+
index += 1
|
|
291
|
+
try:
|
|
292
|
+
lines = max(0, int(legacy_args[index]))
|
|
293
|
+
except ValueError:
|
|
294
|
+
lines = 50
|
|
295
|
+
index += 1
|
|
296
|
+
return DaemonLogOptions(lines=lines, follow=follow)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _payload_command(family: RunnerFamily, paths, list_path: Path, options: DaemonStartOptions) -> list[str]:
|
|
300
|
+
command = [
|
|
301
|
+
sys.executable,
|
|
302
|
+
str(paths.pipeline_root / "cli.py"),
|
|
303
|
+
"--project-root",
|
|
304
|
+
str(paths.project_root),
|
|
305
|
+
"--pipeline-root",
|
|
306
|
+
str(paths.pipeline_root),
|
|
307
|
+
family.kind,
|
|
308
|
+
"run",
|
|
309
|
+
str(list_path),
|
|
310
|
+
]
|
|
311
|
+
if family.kind == "feature" and options.features_filter:
|
|
312
|
+
command.extend(["--features", options.features_filter])
|
|
313
|
+
if options.mode:
|
|
314
|
+
command.extend(["--mode", options.mode])
|
|
315
|
+
if options.critic is True:
|
|
316
|
+
command.append("--critic")
|
|
317
|
+
elif options.critic is False:
|
|
318
|
+
command.append("--no-critic")
|
|
319
|
+
return command
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _daemon_env(options: DaemonStartOptions) -> dict[str, str]:
|
|
323
|
+
env: dict[str, str] = {}
|
|
324
|
+
for token in shlex.split(options.env_overrides):
|
|
325
|
+
if "=" in token:
|
|
326
|
+
key, value = token.split("=", 1)
|
|
327
|
+
if key:
|
|
328
|
+
env[key] = value
|
|
329
|
+
if options.mode:
|
|
330
|
+
env["PIPELINE_MODE"] = options.mode
|
|
331
|
+
if options.critic is not None:
|
|
332
|
+
env["ENABLE_CRITIC"] = "true" if options.critic else "false"
|
|
333
|
+
return env
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _clean_stale_pid(daemon_paths: DaemonPaths) -> str:
|
|
337
|
+
if not daemon_paths.pid_file.is_file():
|
|
338
|
+
return ""
|
|
339
|
+
pid = _read_pid(daemon_paths.pid_file)
|
|
340
|
+
if pid is not None and not is_pid_running(pid):
|
|
341
|
+
daemon_paths.pid_file.unlink(missing_ok=True)
|
|
342
|
+
return f"Cleaned stale PID file (process {pid} no longer running)"
|
|
343
|
+
return ""
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _read_pid(path: Path) -> int | None:
|
|
347
|
+
if not path.is_file():
|
|
348
|
+
return None
|
|
349
|
+
try:
|
|
350
|
+
raw = path.read_text(encoding="utf-8").strip()
|
|
351
|
+
return int(raw) if raw else None
|
|
352
|
+
except (OSError, ValueError):
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _write_meta(family: RunnerFamily, path: Path, pid: int, started_at: str, list_path: Path, options: DaemonStartOptions, log_file: Path) -> None:
|
|
357
|
+
data: dict[str, object] = {
|
|
358
|
+
"pid": pid,
|
|
359
|
+
"pipeline_type": family.kind,
|
|
360
|
+
"started_at": started_at,
|
|
361
|
+
LIST_META_KEY_BY_KIND[family.kind]: str(list_path),
|
|
362
|
+
"env_overrides": options.env_overrides,
|
|
363
|
+
"mode": options.mode,
|
|
364
|
+
"log_file": str(log_file),
|
|
365
|
+
}
|
|
366
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
367
|
+
tmp.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
368
|
+
tmp.replace(path)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _read_meta(path: Path) -> dict[str, object]:
|
|
372
|
+
if not path.is_file():
|
|
373
|
+
return {}
|
|
374
|
+
try:
|
|
375
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
376
|
+
except (OSError, json.JSONDecodeError):
|
|
377
|
+
return {}
|
|
378
|
+
return data if isinstance(data, dict) else {}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _progress_summary(family: RunnerFamily, list_path: Path | None) -> dict[str, object]:
|
|
382
|
+
if list_path is None or not list_path.is_file():
|
|
383
|
+
return {}
|
|
384
|
+
try:
|
|
385
|
+
data = json.loads(list_path.read_text(encoding="utf-8"))
|
|
386
|
+
except (OSError, json.JSONDecodeError):
|
|
387
|
+
return {}
|
|
388
|
+
items = data.get(ITEMS_KEY_BY_KIND[family.kind], [])
|
|
389
|
+
if not isinstance(items, list):
|
|
390
|
+
items = []
|
|
391
|
+
counts = {key: 0 for key in PROGRESS_COUNTS_BY_KIND[family.kind]}
|
|
392
|
+
for item in items:
|
|
393
|
+
if not isinstance(item, dict):
|
|
394
|
+
continue
|
|
395
|
+
item_id = str(item.get(ID_KEY_BY_KIND[family.kind]) or "")
|
|
396
|
+
status = str(item.get("status") or "pending")
|
|
397
|
+
status_file = family.state_dir / item_id / "status.json"
|
|
398
|
+
if status_file.is_file():
|
|
399
|
+
try:
|
|
400
|
+
status_data = json.loads(status_file.read_text(encoding="utf-8"))
|
|
401
|
+
status = str(status_data.get("status") or status)
|
|
402
|
+
except (OSError, json.JSONDecodeError):
|
|
403
|
+
pass
|
|
404
|
+
counts[status if status in counts else "pending"] += 1
|
|
405
|
+
total = len(items)
|
|
406
|
+
percent = round(counts.get("completed", 0) / total * 100, 1) if total else 0
|
|
407
|
+
return {"total": total, **counts, "percent": percent}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _write_log_separator(family: RunnerFamily, log_file: Path, list_path: Path, started_at: str, options: DaemonStartOptions) -> None:
|
|
411
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
412
|
+
lines = [
|
|
413
|
+
"",
|
|
414
|
+
"================================================================",
|
|
415
|
+
f" {family.kind.title()} Pipeline Daemon Started: {started_at}",
|
|
416
|
+
f" {family.kind.title()} list: {list_path}",
|
|
417
|
+
]
|
|
418
|
+
if options.features_filter:
|
|
419
|
+
lines.append(f" Features filter: {options.features_filter}")
|
|
420
|
+
if options.mode:
|
|
421
|
+
lines.append(f" Mode: {options.mode}")
|
|
422
|
+
if options.env_overrides:
|
|
423
|
+
lines.append(f" Environment: {options.env_overrides}")
|
|
424
|
+
lines.extend(["================================================================", ""])
|
|
425
|
+
with log_file.open("a", encoding="utf-8") as handle:
|
|
426
|
+
handle.write("\n".join(lines) + "\n")
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _rotate_log_if_needed(log_file: Path) -> Path | None:
|
|
430
|
+
if not log_file.is_file() or log_file.stat().st_size <= LOG_ROTATE_BYTES:
|
|
431
|
+
return None
|
|
432
|
+
rotated = log_file.with_name(f"{log_file.name}.{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S')}.bak")
|
|
433
|
+
log_file.replace(rotated)
|
|
434
|
+
return rotated
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _tail_lines(path: Path, count: int) -> list[str]:
|
|
438
|
+
if count <= 0 or not path.is_file():
|
|
439
|
+
return []
|
|
440
|
+
try:
|
|
441
|
+
return path.read_text(encoding="utf-8", errors="replace").splitlines()[-count:]
|
|
442
|
+
except OSError:
|
|
443
|
+
return []
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _follow_log(family: RunnerFamily, log_file: Path) -> CommandResult:
|
|
447
|
+
try:
|
|
448
|
+
with log_file.open("r", encoding="utf-8", errors="replace") as handle:
|
|
449
|
+
handle.seek(0, os.SEEK_END)
|
|
450
|
+
while True:
|
|
451
|
+
line = handle.readline()
|
|
452
|
+
if line:
|
|
453
|
+
print(line, end="")
|
|
454
|
+
else:
|
|
455
|
+
time.sleep(0.5)
|
|
456
|
+
except KeyboardInterrupt:
|
|
457
|
+
return CommandResult(0, f"Python {family.kind} daemon logs", "follow stopped", stdout="")
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _utc_iso() -> str:
|
|
461
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _start_verify_seconds() -> float:
|
|
465
|
+
raw = os.environ.get("PRIZMKIT_DAEMON_VERIFY_SECONDS")
|
|
466
|
+
if raw is None:
|
|
467
|
+
return START_VERIFY_SECONDS
|
|
468
|
+
try:
|
|
469
|
+
return max(0.0, float(raw))
|
|
470
|
+
except ValueError:
|
|
471
|
+
return START_VERIFY_SECONDS
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _help_text(family: RunnerFamily) -> str:
|
|
475
|
+
return (
|
|
476
|
+
f"Usage: prizmkit-runtime daemon {family.kind} <start|stop|status|logs|restart> [options]\n"
|
|
477
|
+
"Commands preserve .pipeline.pid, pipeline-daemon.log, and .pipeline-meta.json contracts."
|
|
478
|
+
)
|