wicked-garden 12.27.0
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/.claude-plugin/archetypes.json +166 -0
- package/.claude-plugin/components.json +75 -0
- package/.claude-plugin/marketplace.json +15 -0
- package/.claude-plugin/plugin.json +174 -0
- package/.claude-plugin/specialist.json +48 -0
- package/.claude-plugin/stack-registry.json +96 -0
- package/AGENTS.md +13 -0
- package/CHANGELOG.md +712 -0
- package/CONTRIBUTING.md +99 -0
- package/ETHOS.md +83 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/WICKED_GARDEN_BUS_EVENTS.md +149 -0
- package/agents/agentic/architect.md +449 -0
- package/agents/agentic/performance-analyst.md +702 -0
- package/agents/agentic/safety-reviewer.md +662 -0
- package/agents/crew/implementer.md +163 -0
- package/agents/crew/researcher.md +94 -0
- package/agents/crew/reviewer.md +184 -0
- package/agents/data/data-engineer.md +227 -0
- package/agents/engineering/api-documentarian.md +440 -0
- package/agents/engineering/migration-engineer.md +301 -0
- package/agents/engineering/solution-architect.md +208 -0
- package/agents/jam/brainstorm-facilitator.md +297 -0
- package/agents/jam/council.md +322 -0
- package/agents/jam/quick-facilitator.md +85 -0
- package/agents/persona/persona-agent.md +121 -0
- package/agents/platform/compliance-officer.md +266 -0
- package/agents/platform/privacy-expert.md +496 -0
- package/agents/platform/security-engineer.md +287 -0
- package/agents/product/a11y-expert.md +323 -0
- package/agents/product/requirements-analyst.md +238 -0
- package/agents/product/ui-reviewer.md +180 -0
- package/agents/product/ux-designer.md +199 -0
- package/agents/product/value-strategist.md +269 -0
- package/agents/qe/semantic-reviewer.md +78 -0
- package/commands/agentic/audit.md +24 -0
- package/commands/agentic/design.md +24 -0
- package/commands/agentic/frameworks.md +25 -0
- package/commands/agentic/review.md +27 -0
- package/commands/archetype/build.md +12 -0
- package/commands/archetype/decide.md +12 -0
- package/commands/archetype/explore.md +12 -0
- package/commands/archetype/incident.md +12 -0
- package/commands/archetype/migrate.md +12 -0
- package/commands/archetype/modernize.md +12 -0
- package/commands/archetype/review.md +14 -0
- package/commands/archetype/ship.md +12 -0
- package/commands/archetype/specify.md +12 -0
- package/commands/archetype/triage.md +12 -0
- package/commands/compile.md +37 -0
- package/commands/data/analyze.md +18 -0
- package/commands/data/data.md +21 -0
- package/commands/data/ml.md +18 -0
- package/commands/data/ontology.md +22 -0
- package/commands/data/pipeline.md +17 -0
- package/commands/deliberate.md +181 -0
- package/commands/engineering/add-field.md +77 -0
- package/commands/engineering/apply.md +58 -0
- package/commands/engineering/arch.md +26 -0
- package/commands/engineering/debug.md +27 -0
- package/commands/engineering/docs.md +27 -0
- package/commands/engineering/new-generator.md +257 -0
- package/commands/engineering/patch-plan.md +59 -0
- package/commands/engineering/plan.md +29 -0
- package/commands/engineering/remove.md +48 -0
- package/commands/engineering/rename.md +44 -0
- package/commands/engineering/review.md +32 -0
- package/commands/help.md +128 -0
- package/commands/install.md +118 -0
- package/commands/intent.md +54 -0
- package/commands/jam/brainstorm.md +37 -0
- package/commands/jam/council.md +49 -0
- package/commands/jam/quick.md +21 -0
- package/commands/jam/revisit.md +79 -0
- package/commands/persona/as.md +136 -0
- package/commands/persona/define.md +24 -0
- package/commands/persona/list.md +118 -0
- package/commands/platform/actions.md +20 -0
- package/commands/platform/assert.md +18 -0
- package/commands/platform/audit.md +23 -0
- package/commands/platform/compliance.md +20 -0
- package/commands/platform/gh.md +22 -0
- package/commands/platform/health.md +22 -0
- package/commands/platform/incident.md +23 -0
- package/commands/platform/infra.md +24 -0
- package/commands/platform/peer-health.md +103 -0
- package/commands/platform/plugin-health.md +58 -0
- package/commands/platform/security.md +38 -0
- package/commands/platform/toolchain.md +96 -0
- package/commands/platform/traces.md +20 -0
- package/commands/product/a11y.md +19 -0
- package/commands/product/acceptance.md +19 -0
- package/commands/product/align.md +20 -0
- package/commands/product/analyze.md +23 -0
- package/commands/product/elicit.md +19 -0
- package/commands/product/listen.md +24 -0
- package/commands/product/mockup.md +20 -0
- package/commands/product/screenshot.md +19 -0
- package/commands/product/strategy.md +40 -0
- package/commands/product/synthesize.md +23 -0
- package/commands/product/ux-review.md +42 -0
- package/commands/product/ux.md +20 -0
- package/commands/product/visual-direction.md +34 -0
- package/commands/prove.md +47 -0
- package/commands/report-issue.md +243 -0
- package/commands/reset.md +144 -0
- package/commands/search/blast-radius.md +58 -0
- package/commands/search/hotspots.md +55 -0
- package/commands/search/index.md +38 -0
- package/commands/search/lineage.md +47 -0
- package/commands/search/service-map.md +79 -0
- package/commands/setup.md +363 -0
- package/commands/smaht/briefing.md +200 -0
- package/commands/smaht/events-import.md +95 -0
- package/commands/smaht/state.md +19 -0
- package/commands/where-am-i.md +58 -0
- package/docs/README.md +32 -0
- package/docs/adr/0001-code-relationship-graph-engine.md +116 -0
- package/docs/adr/0002-major-cleanup-keep-cut.md +91 -0
- package/docs/adr/0003-peers-gate-required-layers-optin.md +41 -0
- package/docs/adr/0004-code-graph-moves-to-wicked-brain.md +47 -0
- package/docs/brain-chunk-format.md +135 -0
- package/docs/compiler.md +95 -0
- package/docs/daemon-api.md +70 -0
- package/docs/domains.md +197 -0
- package/docs/getting-started.md +208 -0
- package/docs/plans/2026-06-08-wicked-loom-conduct.md +1337 -0
- package/docs/plans/2026-06-08-wicked-loom-contract.md +638 -0
- package/docs/plans/2026-06-08-wicked-loom-cutover.md +1300 -0
- package/docs/plans/2026-06-08-wicked-loom-expand-compose.md +843 -0
- package/docs/plans/2026-06-08-wicked-loom-flow-cutover.md +403 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1a-graph-core.md +838 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1b-injected-edges.md +44 -0
- package/docs/required-peers.md +60 -0
- package/docs/specs/2026-06-08-wicked-loom-extraction.md +225 -0
- package/docs/specs/2026-06-10-codegraph-to-brain-migration.md +168 -0
- package/docs/v11/archetypes.md +231 -0
- package/hooks/hooks.json +275 -0
- package/hooks/scripts/bootstrap.py +1797 -0
- package/hooks/scripts/invoke.py +62 -0
- package/hooks/scripts/notification.py +166 -0
- package/hooks/scripts/permission_request.py +223 -0
- package/hooks/scripts/post_tool.py +1374 -0
- package/hooks/scripts/pre_compact.py +272 -0
- package/hooks/scripts/pre_tool.py +1278 -0
- package/hooks/scripts/prompt_submit.py +1109 -0
- package/hooks/scripts/session_end.py +95 -0
- package/hooks/scripts/stop.py +624 -0
- package/hooks/scripts/subagent_lifecycle.py +464 -0
- package/hooks/scripts/subscribers/on_gate_decided.py +84 -0
- package/hooks/scripts/task_completed.py +322 -0
- package/hooks/scripts/worktree_guard.py +148 -0
- package/hooks/subscriptions/on_gate_decided.json +9 -0
- package/install.mjs +128 -0
- package/package.json +41 -0
- package/pyproject.toml +59 -0
- package/schemas/README.md +32 -0
- package/schemas/assert_contracts.json +37 -0
- package/schemas/health_probe.json +46 -0
- package/schemas/plugin_status.json +37 -0
- package/scripts/_adapters/__init__.py +166 -0
- package/scripts/_adapters/jira_adapter.py +18 -0
- package/scripts/_adapters/linear_adapter.py +18 -0
- package/scripts/_adapters/miro_adapter.py +18 -0
- package/scripts/_adapters/notion_adapter.py +18 -0
- package/scripts/_agents.py +357 -0
- package/scripts/_brain_ingest/__init__.py +2 -0
- package/scripts/_brain_ingest/session_fact_extractor.py +341 -0
- package/scripts/_brain_port.py +163 -0
- package/scripts/_bus.py +1081 -0
- package/scripts/_bus_catalog_gen.py +93 -0
- package/scripts/_bus_consumers.json +46 -0
- package/scripts/_bus_emit.py +43 -0
- package/scripts/_capability_registry.py +334 -0
- package/scripts/_capability_resolver.py +322 -0
- package/scripts/_domain_store.py +559 -0
- package/scripts/_event_log_reader.py +154 -0
- package/scripts/_event_schema.py +305 -0
- package/scripts/_event_store.py +397 -0
- package/scripts/_heavy_cadence.py +324 -0
- package/scripts/_integration_resolver.py +321 -0
- package/scripts/_logger.py +151 -0
- package/scripts/_loom.py +482 -0
- package/scripts/_paths.py +133 -0
- package/scripts/_python.sh +22 -0
- package/scripts/_run.py +137 -0
- package/scripts/_session.py +502 -0
- package/scripts/_validate_registry.py +1042 -0
- package/scripts/_wicked_testing_probe.py +277 -0
- package/scripts/agentic/analyze_agents.py +391 -0
- package/scripts/agentic/detect_framework.py +420 -0
- package/scripts/agentic/issue_taxonomy.py +357 -0
- package/scripts/agentic/pattern_scorer.py +434 -0
- package/scripts/classify/persist.py +169 -0
- package/scripts/compiler/compile.py +552 -0
- package/scripts/compiler/phase0/VERDICT.md +60 -0
- package/scripts/compiler/phase0/detect.py +292 -0
- package/scripts/compiler/phase0/emit.py +228 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.bindings.json +30 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/command_iq.bindings.json +45 -0
- package/scripts/compiler/phase0/evidence/command_iq.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/memos.bindings.json +28 -0
- package/scripts/compiler/phase0/evidence/memos.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/verdict.json +104 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.bindings.json +29 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/wire-vault-transcript.txt +60 -0
- package/scripts/compiler/phase0/run_probe.py +234 -0
- package/scripts/compiler/phase0/wire_vault.py +310 -0
- package/scripts/crew/__init__.py +0 -0
- package/scripts/crew/_task_audit_writer.py +243 -0
- package/scripts/crew/_task_reader.py +389 -0
- package/scripts/crew/archetypes_v11.py +348 -0
- package/scripts/crew/detectors/__init__.py +12 -0
- package/scripts/crew/flow_compiler.py +121 -0
- package/scripts/crew/phase_manager.py +703 -0
- package/scripts/crew/scope_delta.py +264 -0
- package/scripts/crew/specialist_resolver.py +218 -0
- package/scripts/crew/stack_registry.py +189 -0
- package/scripts/data/data_profiler.py +271 -0
- package/scripts/data/ontology_recommender.py +231 -0
- package/scripts/data/schema_validator.py +267 -0
- package/scripts/delivery/drift.py +739 -0
- package/scripts/delivery/telemetry.py +614 -0
- package/scripts/engineering/patch/codegraph_db.py +125 -0
- package/scripts/engineering/patch/generators/__init__.py +56 -0
- package/scripts/engineering/patch/generators/base.py +373 -0
- package/scripts/engineering/patch/generators/csharp_generator.py +494 -0
- package/scripts/engineering/patch/generators/go_generator.py +430 -0
- package/scripts/engineering/patch/generators/java_generator.py +928 -0
- package/scripts/engineering/patch/generators/jsp_generator.py +454 -0
- package/scripts/engineering/patch/generators/kotlin_generator.py +561 -0
- package/scripts/engineering/patch/generators/perl_generator.py +510 -0
- package/scripts/engineering/patch/generators/php_generator.py +644 -0
- package/scripts/engineering/patch/generators/propagation_engine.py +929 -0
- package/scripts/engineering/patch/generators/python_generator.py +487 -0
- package/scripts/engineering/patch/generators/ruby_generator.py +463 -0
- package/scripts/engineering/patch/generators/rust_generator.py +400 -0
- package/scripts/engineering/patch/generators/sql_generator.py +594 -0
- package/scripts/engineering/patch/generators/typescript_generator.py +439 -0
- package/scripts/engineering/patch/patch.py +800 -0
- package/scripts/engineering/patch/safety.py +429 -0
- package/scripts/jam/__init__.py +0 -0
- package/scripts/jam/_bus_consumers.py +271 -0
- package/scripts/jam/agentic_cli_registry.py +703 -0
- package/scripts/jam/consensus.py +737 -0
- package/scripts/jam/detect_clis.py +409 -0
- package/scripts/jam/jam.py +283 -0
- package/scripts/jam/save_transcript.py +171 -0
- package/scripts/loom/__init__.py +22 -0
- package/scripts/loom/cli.py +170 -0
- package/scripts/loom/compose.py +180 -0
- package/scripts/loom/gate.py +120 -0
- package/scripts/loom/manifest.py +140 -0
- package/scripts/loom/resolve.py +59 -0
- package/scripts/persona/__init__.py +0 -0
- package/scripts/persona/registry.py +688 -0
- package/scripts/platform/guard_pipeline.py +1214 -0
- package/scripts/platform/guard_profiles.py +216 -0
- package/scripts/platform/observability/assert_contracts.py +434 -0
- package/scripts/platform/observability/health_probe.py +915 -0
- package/scripts/platform/observability/ops_log_viewer.py +209 -0
- package/scripts/platform/prereq_doctor.py +567 -0
- package/scripts/qe/conditions_manifest.py +260 -0
- package/scripts/qe/content_sanitizer.py +160 -0
- package/scripts/qe/coverage_tracker.py +535 -0
- package/scripts/qe/discover_scenarios.py +243 -0
- package/scripts/qe/evidence_tracker.py +232 -0
- package/scripts/qe/prove.py +196 -0
- package/scripts/qe/registry_lookup.py +80 -0
- package/scripts/qe/registry_store.py +176 -0
- package/scripts/qe/semantic_review.py +1005 -0
- package/scripts/qe/vault_gate.py +382 -0
- package/scripts/qe/verdict_audit.py +175 -0
- package/scripts/qe/verdict_schema.py +335 -0
- package/scripts/reset.py +220 -0
- package/scripts/resolve_path.py +22 -0
- package/scripts/sentinel/invariants.py +402 -0
- package/scripts/sentinel/pre_push.py +73 -0
- package/scripts/setup/__init__.py +1 -0
- package/scripts/setup/detect_state.py +114 -0
- package/scripts/setup/migrate_legacy.py +67 -0
- package/scripts/setup/migrate_v6_projects.py +232 -0
- package/scripts/setup/onboarding.py +114 -0
- package/scripts/smaht/_bus_consumers.py +98 -0
- package/scripts/smaht/adapters/__init__.py +83 -0
- package/scripts/smaht/adapters/brain_adapter.py +254 -0
- package/scripts/smaht/adapters/context7_adapter.py +438 -0
- package/scripts/smaht/adapters/delegation_adapter.py +104 -0
- package/scripts/smaht/adapters/domain_adapter.py +206 -0
- package/scripts/smaht/adapters/events_adapter.py +231 -0
- package/scripts/smaht/adapters/tools_adapter.py +49 -0
- package/scripts/smaht/cheatsheet_store.py +236 -0
- package/scripts/smaht/context_package.py +386 -0
- package/scripts/smaht/knowledge_graph.py +395 -0
- package/scripts/smaht/propose_skills.py +856 -0
- package/scripts/smaht/test_cheatsheet_store.py +428 -0
- package/scripts/statusline.py +151 -0
- package/scripts/where_am_i.py +236 -0
- package/skills/agentic/agentic-patterns/SKILL.md +91 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-design.md +283 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-operational.md +216 -0
- package/skills/agentic/agentic-patterns/refs/pattern-catalog.md +329 -0
- package/skills/agentic/context-engineering/SKILL.md +142 -0
- package/skills/agentic/context-engineering/refs/caching-and-optimization.md +251 -0
- package/skills/agentic/context-engineering/refs/compression-techniques.md +171 -0
- package/skills/agentic/context-engineering/refs/cost-calculation-budget.md +250 -0
- package/skills/agentic/context-engineering/refs/cost-optimization-reporting.md +300 -0
- package/skills/agentic/context-engineering/refs/selective-loading.md +179 -0
- package/skills/agentic/frameworks/SKILL.md +191 -0
- package/skills/agentic/frameworks/refs/framework-profiles-1.md +209 -0
- package/skills/agentic/frameworks/refs/framework-profiles-2.md +214 -0
- package/skills/agentic/frameworks/refs/migration-patterns-paths.md +267 -0
- package/skills/agentic/frameworks/refs/migration-patterns-testing.md +208 -0
- package/skills/agentic/refs/audit.md +134 -0
- package/skills/agentic/refs/design.md +137 -0
- package/skills/agentic/refs/frameworks.md +102 -0
- package/skills/agentic/refs/review.md +160 -0
- package/skills/agentic/review-methodology/SKILL.md +186 -0
- package/skills/agentic/review-methodology/refs/deliverable-templates.md +246 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-quality-testing.md +277 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-reliability-safety.md +253 -0
- package/skills/agentic/trust-and-safety/SKILL.md +177 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-actions.md +246 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-input-output.md +238 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-resources.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-advanced.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-core.md +215 -0
- package/skills/archetype/SKILL.md +104 -0
- package/skills/archetype/refs/build.md +142 -0
- package/skills/archetype/refs/decide.md +117 -0
- package/skills/archetype/refs/explore.md +66 -0
- package/skills/archetype/refs/incident.md +149 -0
- package/skills/archetype/refs/migrate.md +170 -0
- package/skills/archetype/refs/modernize.md +187 -0
- package/skills/archetype/refs/review.md +145 -0
- package/skills/archetype/refs/ship.md +117 -0
- package/skills/archetype/refs/specify.md +108 -0
- package/skills/archetype/refs/triage.md +62 -0
- package/skills/classify/SKILL.md +157 -0
- package/skills/data/analysis/SKILL.md +125 -0
- package/skills/data/analysis/refs/templates.md +170 -0
- package/skills/data/data/SKILL.md +118 -0
- package/skills/data/data/refs/examples.md +235 -0
- package/skills/data/ml/SKILL.md +125 -0
- package/skills/data/ml/refs/templates.md +172 -0
- package/skills/data/pipeline/SKILL.md +136 -0
- package/skills/data/pipeline/refs/templates.md +175 -0
- package/skills/data/refs/analyze.md +123 -0
- package/skills/data/refs/data.md +95 -0
- package/skills/data/refs/ml.md +118 -0
- package/skills/data/refs/pipeline.md +92 -0
- package/skills/deliberate/SKILL.md +125 -0
- package/skills/deliberate/refs/architecture-lens.md +37 -0
- package/skills/deliberate/refs/assumptions-lens.md +43 -0
- package/skills/deliberate/refs/code-lens.md +38 -0
- package/skills/deliberate/refs/content-lens.md +37 -0
- package/skills/deliberate/refs/data-lens.md +43 -0
- package/skills/deliberate/refs/design-lens.md +37 -0
- package/skills/deliberate/refs/opportunity-patterns.md +107 -0
- package/skills/deliberate/refs/ops-lens.md +43 -0
- package/skills/deliberate/refs/product-lens.md +46 -0
- package/skills/deliberate/refs/rethink-framework.md +77 -0
- package/skills/deliberate/refs/security-lens.md +47 -0
- package/skills/deliberate/refs/testing-lens.md +45 -0
- package/skills/deliberate/refs/timing-lens.md +42 -0
- package/skills/engineering/architecture/SKILL.md +177 -0
- package/skills/engineering/architecture/refs/adr-template.md +268 -0
- package/skills/engineering/architecture/refs/architecture-template-deployment.md +176 -0
- package/skills/engineering/architecture/refs/architecture-template-design.md +300 -0
- package/skills/engineering/architecture/refs/examples-ecommerce-analytics.md +299 -0
- package/skills/engineering/architecture/refs/examples-saas-trading.md +291 -0
- package/skills/engineering/architecture/refs/patterns-advanced.md +238 -0
- package/skills/engineering/architecture/refs/patterns-structural.md +303 -0
- package/skills/engineering/backend/SKILL.md +102 -0
- package/skills/engineering/backend/refs/checklists.md +146 -0
- package/skills/engineering/backend/refs/patterns.md +181 -0
- package/skills/engineering/debugging/SKILL.md +99 -0
- package/skills/engineering/debugging/refs/patterns.md +248 -0
- package/skills/engineering/debugging/refs/process.md +203 -0
- package/skills/engineering/docs/SKILL.md +97 -0
- package/skills/engineering/engineering/SKILL.md +88 -0
- package/skills/engineering/engineering/refs/checklists.md +91 -0
- package/skills/engineering/engineering/refs/templates.md +71 -0
- package/skills/engineering/frontend/SKILL.md +92 -0
- package/skills/engineering/frontend/refs/checklists.md +96 -0
- package/skills/engineering/frontend/refs/patterns.md +167 -0
- package/skills/engineering/integration/SKILL.md +183 -0
- package/skills/engineering/integration/refs/error-handling-best-practices.md +139 -0
- package/skills/engineering/integration/refs/error-handling-error-classes-typescript.md +212 -0
- package/skills/engineering/integration/refs/error-handling-error-response-format.md +301 -0
- package/skills/engineering/integration/refs/error-handling-retry-strategies.md +190 -0
- package/skills/engineering/integration/refs/event-schemas-best-practices.md +225 -0
- package/skills/engineering/integration/refs/event-schemas-event-naming-conventions.md +303 -0
- package/skills/engineering/integration/refs/event-schemas-event-structure.md +288 -0
- package/skills/engineering/integration/refs/graphql-best-practices.md +138 -0
- package/skills/engineering/integration/refs/graphql-error-handling.md +198 -0
- package/skills/engineering/integration/refs/graphql-graphql-basics.md +197 -0
- package/skills/engineering/integration/refs/graphql-query-examples.md +226 -0
- package/skills/engineering/integration/refs/graphql-schema-design-patterns.md +280 -0
- package/skills/engineering/integration/refs/grpc-grpc-overview.md +246 -0
- package/skills/engineering/integration/refs/grpc-implementation-examples.md +269 -0
- package/skills/engineering/integration/refs/grpc-metadata-headers.md +304 -0
- package/skills/engineering/integration/refs/grpc-testing.md +49 -0
- package/skills/engineering/integration/refs/openapi-template.yaml +682 -0
- package/skills/engineering/integration/refs/resilience-best-practices.md +109 -0
- package/skills/engineering/integration/refs/resilience-circuit-breaker-pattern.md +302 -0
- package/skills/engineering/integration/refs/resilience-rate-limiting.md +245 -0
- package/skills/engineering/integration/refs/resilience-timeout-pattern.md +212 -0
- package/skills/engineering/integration/refs/rest-api-fundamentals.md +299 -0
- package/skills/engineering/integration/refs/rest-api-responses-security.md +302 -0
- package/skills/engineering/integration/refs/versioning-breaking-changes.md +237 -0
- package/skills/engineering/integration/refs/versioning-migration-strategies.md +257 -0
- package/skills/engineering/integration/refs/versioning-versioning-strategies.md +296 -0
- package/skills/engineering/large-scale-migration/SKILL.md +65 -0
- package/skills/engineering/patch/SKILL.md +184 -0
- package/skills/engineering/refs/arch.md +126 -0
- package/skills/engineering/refs/debug.md +142 -0
- package/skills/engineering/refs/docs.md +190 -0
- package/skills/engineering/refs/plan.md +109 -0
- package/skills/engineering/refs/review.md +138 -0
- package/skills/engineering/system-design/SKILL.md +154 -0
- package/skills/engineering/system-design/refs/anti-patterns-big-ball-of-mud.md +254 -0
- package/skills/engineering/system-design/refs/anti-patterns-circular-dependencies.md +241 -0
- package/skills/engineering/system-design/refs/anti-patterns-feature-envy.md +168 -0
- package/skills/engineering/system-design/refs/anti-patterns-god-object.md +247 -0
- package/skills/engineering/system-design/refs/component-template-lifecycle.md +210 -0
- package/skills/engineering/system-design/refs/component-template-maintenance.md +219 -0
- package/skills/engineering/system-design/refs/component-template-structure.md +276 -0
- package/skills/engineering/system-design/refs/dependencies-best-practices.md +260 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-principles.md +295 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-tracking.md +261 -0
- package/skills/engineering/system-design/refs/interface-template-lifecycle.md +211 -0
- package/skills/engineering/system-design/refs/interface-template-maintenance.md +212 -0
- package/skills/engineering/system-design/refs/interface-template-structure.md +269 -0
- package/skills/engineering/system-design/refs/patterns-dependency-injection.md +151 -0
- package/skills/engineering/system-design/refs/patterns-facade-pattern.md +257 -0
- package/skills/engineering/system-design/refs/patterns-layered-pattern.md +204 -0
- package/skills/engineering/system-design/refs/patterns-plugin-architecture.md +229 -0
- package/skills/engineering/unit-test-quality/SKILL.md +194 -0
- package/skills/ground/SKILL.md +130 -0
- package/skills/integration-discovery/SKILL.md +168 -0
- package/skills/integration-discovery/refs/capabilities.md +282 -0
- package/skills/integration-discovery/refs/cli-detection.md +295 -0
- package/skills/integration-discovery/refs/discovery-sources.md +233 -0
- package/skills/integration-discovery/refs/task-patterns.md +206 -0
- package/skills/jam/SKILL.md +45 -0
- package/skills/jam/refs/facilitation-patterns.md +125 -0
- package/skills/jam/refs/quick.md +64 -0
- package/skills/jam/refs/synthesis-patterns.md +114 -0
- package/skills/multi-model/SKILL.md +168 -0
- package/skills/multi-model/refs/aichat.md +119 -0
- package/skills/multi-model/refs/aider.md +100 -0
- package/skills/multi-model/refs/auditability.md +238 -0
- package/skills/multi-model/refs/codex.md +169 -0
- package/skills/multi-model/refs/context.md +306 -0
- package/skills/multi-model/refs/copilot.md +161 -0
- package/skills/multi-model/refs/examples.md +224 -0
- package/skills/multi-model/refs/gemini.md +185 -0
- package/skills/multi-model/refs/goose.md +112 -0
- package/skills/multi-model/refs/llm.md +124 -0
- package/skills/multi-model/refs/opencode.md +196 -0
- package/skills/multi-model/refs/orchestration.md +188 -0
- package/skills/multi-model/refs/pi.md +208 -0
- package/skills/persona/SKILL.md +144 -0
- package/skills/persona/refs/define.md +133 -0
- package/skills/platform/actions/refs/actions.md +114 -0
- package/skills/platform/audit/SKILL.md +174 -0
- package/skills/platform/audit/refs/audit.md +145 -0
- package/skills/platform/audit/refs/checklists-evidence-operations.md +144 -0
- package/skills/platform/audit/refs/checklists-gdpr-pci-evidence.md +213 -0
- package/skills/platform/audit/refs/checklists-soc2-hipaa.md +120 -0
- package/skills/platform/audit/refs/frameworks-gdpr-pci.md +225 -0
- package/skills/platform/audit/refs/frameworks-soc2-hipaa.md +228 -0
- package/skills/platform/compliance/SKILL.md +169 -0
- package/skills/platform/compliance/refs/checklists.md +303 -0
- package/skills/platform/compliance/refs/compliance.md +117 -0
- package/skills/platform/compliance/refs/frameworks.md +282 -0
- package/skills/platform/errors/SKILL.md +183 -0
- package/skills/platform/errors/refs/patterns.md +271 -0
- package/skills/platform/errors/refs/severity.md +264 -0
- package/skills/platform/gate-benchmark-rebaseline/SKILL.md +108 -0
- package/skills/platform/gh/refs/gh.md +105 -0
- package/skills/platform/gh-cli/SKILL.md +120 -0
- package/skills/platform/gh-cli/refs/automation.md +63 -0
- package/skills/platform/gh-cli/refs/gh-ops.md +103 -0
- package/skills/platform/gh-cli/refs/patterns.md +132 -0
- package/skills/platform/gh-cli/scripts/gh_ops.py +454 -0
- package/skills/platform/github-actions/SKILL.md +159 -0
- package/skills/platform/github-actions/refs/security.md +150 -0
- package/skills/platform/github-actions/refs/templates.md +230 -0
- package/skills/platform/github-actions/refs/troubleshooting.md +174 -0
- package/skills/platform/github-actions/scripts/workflow_gen.py +215 -0
- package/skills/platform/gitlab-ci/SKILL.md +140 -0
- package/skills/platform/gitlab-ci/refs/templates.md +223 -0
- package/skills/platform/gitlab-ci/refs/troubleshooting.md +213 -0
- package/skills/platform/glab-cli/SKILL.md +133 -0
- package/skills/platform/glab-cli/refs/ci-debugging.md +95 -0
- package/skills/platform/glab-cli/refs/glab-ops.md +78 -0
- package/skills/platform/glab-cli/refs/patterns.md +131 -0
- package/skills/platform/glab-cli/scripts/glab_ops.py +313 -0
- package/skills/platform/health/SKILL.md +177 -0
- package/skills/platform/health/refs/health.md +72 -0
- package/skills/platform/health/refs/sources.md +302 -0
- package/skills/platform/incident/refs/incident.md +136 -0
- package/skills/platform/infra/refs/infra.md +104 -0
- package/skills/platform/observability/SKILL.md +128 -0
- package/skills/platform/observability/refs/toolchain-discovery.md +243 -0
- package/skills/platform/prereq-doctor/SKILL.md +57 -0
- package/skills/platform/prereq-doctor/refs/tool-registry.md +58 -0
- package/skills/platform/traces/refs/traces.md +101 -0
- package/skills/product/acceptance-criteria/SKILL.md +193 -0
- package/skills/product/acceptance-criteria/refs/ac-anti-patterns.md +291 -0
- package/skills/product/acceptance-criteria/refs/ac-examples.md +277 -0
- package/skills/product/accessibility/SKILL.md +197 -0
- package/skills/product/accessibility/refs/aria-patterns-basics.md +250 -0
- package/skills/product/accessibility/refs/aria-patterns-dynamic.md +187 -0
- package/skills/product/accessibility/refs/aria-patterns-interactive.md +246 -0
- package/skills/product/accessibility/refs/audit-report-findings.md +248 -0
- package/skills/product/accessibility/refs/audit-report-results.md +276 -0
- package/skills/product/accessibility/refs/common-violations-html-css.md +270 -0
- package/skills/product/accessibility/refs/common-violations-interactive.md +303 -0
- package/skills/product/accessibility/refs/keyboard-testing-basics.md +222 -0
- package/skills/product/accessibility/refs/keyboard-testing-patterns.md +173 -0
- package/skills/product/accessibility/refs/screen-reader-testing-advanced.md +294 -0
- package/skills/product/accessibility/refs/screen-reader-testing-commands.md +255 -0
- package/skills/product/accessibility/refs/wcag-checklist.md +262 -0
- package/skills/product/analyze/SKILL.md +164 -0
- package/skills/product/analyze/refs/algorithms.md +238 -0
- package/skills/product/analyze/refs/sentiment-patterns.md +180 -0
- package/skills/product/imagery/SKILL.md +97 -0
- package/skills/product/imagery/alter/SKILL.md +105 -0
- package/skills/product/imagery/alter/refs/editing-reference.md +146 -0
- package/skills/product/imagery/create/SKILL.md +109 -0
- package/skills/product/imagery/create/refs/prompt-engineering.md +112 -0
- package/skills/product/imagery/create/refs/provider-reference.md +219 -0
- package/skills/product/imagery/review/SKILL.md +101 -0
- package/skills/product/imagery/review/refs/analysis-lenses.md +136 -0
- package/skills/product/imagery/review/refs/quality-gates.md +153 -0
- package/skills/product/imagery/scripts/analyze.sh +23 -0
- package/skills/product/imagery/scripts/edit.sh +14 -0
- package/skills/product/imagery/scripts/generate.sh +13 -0
- package/skills/product/imagery/scripts/provider.py +787 -0
- package/skills/product/listen/SKILL.md +175 -0
- package/skills/product/listen/refs/channels.md +220 -0
- package/skills/product/mockup/SKILL.md +106 -0
- package/skills/product/mockup/refs/templates.md +95 -0
- package/skills/product/refs/a11y.md +109 -0
- package/skills/product/refs/acceptance.md +79 -0
- package/skills/product/refs/align.md +67 -0
- package/skills/product/refs/analyze.md +77 -0
- package/skills/product/refs/elicit.md +67 -0
- package/skills/product/refs/listen.md +58 -0
- package/skills/product/refs/mockup.md +77 -0
- package/skills/product/refs/screenshot.md +59 -0
- package/skills/product/refs/strategy.md +77 -0
- package/skills/product/refs/synthesize.md +54 -0
- package/skills/product/refs/ux-review.md +68 -0
- package/skills/product/refs/ux.md +101 -0
- package/skills/product/requirements-analysis/SKILL.md +136 -0
- package/skills/product/requirements-analysis/refs/requirements-example-auth.md +264 -0
- package/skills/product/requirements-analysis/refs/requirements-example-export-integration.md +88 -0
- package/skills/product/requirements-analysis/refs/requirements-output-format-template.md +247 -0
- package/skills/product/requirements-analysis/refs/user-story-guide.md +296 -0
- package/skills/product/requirements-graph/SKILL.md +155 -0
- package/skills/product/requirements-graph/refs/examples.md +223 -0
- package/skills/product/requirements-graph/refs/schema.md +162 -0
- package/skills/product/requirements-migrate/SKILL.md +90 -0
- package/skills/product/requirements-navigate/SKILL.md +141 -0
- package/skills/product/requirements-navigate/refs/meta-templates.md +141 -0
- package/skills/product/screenshot/SKILL.md +147 -0
- package/skills/product/strategy/SKILL.md +198 -0
- package/skills/product/strategy/refs/frameworks.md +295 -0
- package/skills/product/strategy/refs/templates-competitive-market.md +299 -0
- package/skills/product/strategy/refs/templates-roi-value.md +207 -0
- package/skills/product/synthesize/SKILL.md +116 -0
- package/skills/product/synthesize/refs/journey-mapping.md +212 -0
- package/skills/product/synthesize/refs/prioritization.md +193 -0
- package/skills/product/ux-review/SKILL.md +164 -0
- package/skills/product/visual-direction/SKILL.md +137 -0
- package/skills/product/visual-review/SKILL.md +131 -0
- package/skills/runtime-exec/SKILL.md +138 -0
- package/skills/search/codebase-narrator/SKILL.md +131 -0
- package/skills/search/codebase-narrator/refs/output-template.md +100 -0
- package/skills/smaht/SKILL.md +72 -0
- package/skills/smaht/discovery/SKILL.md +65 -0
- package/skills/smaht/intent/SKILL.md +89 -0
- package/skills/smaht/propose-skills/SKILL.md +142 -0
- package/skills/smaht/refs/state.md +76 -0
- package/skills/swarm/SKILL.md +132 -0
- package/skills/swarm/refs/fan-out.md +104 -0
- package/skills/swarm/refs/independent-verification.md +86 -0
- package/skills/swarm/refs/receipts-and-evidence.md +69 -0
- package/skills/swarm/refs/recon-synthesis.md +87 -0
- package/skills/swarm/refs/ship-discipline.md +90 -0
- package/skills/wickedizer/SKILL.md +147 -0
- package/skills/wickedizer/refs/artifact-types.md +238 -0
- package/skills/wickedizer/refs/complexity.md +278 -0
- package/skills/wickedizer/refs/de-ai-pass.md +140 -0
- package/skills/wickedizer/refs/failure-modes.md +249 -0
- package/skills/wickedizer/refs/patterns.md +282 -0
- package/skills/wickedizer/refs/review.md +221 -0
- package/skills/wickedizer/refs/structure.md +282 -0
- package/skills/wickedizer/refs/voice-profile.md +125 -0
- package/skills/workflow/SKILL.md +132 -0
- package/skills/workflow/refs/archetype-adjustments.md +205 -0
- package/skills/workflow/refs/evidence.md +58 -0
- package/skills/workflow/refs/integration.md +130 -0
- package/skills/workflow/refs/risk-dimension-signals.md +260 -0
- package/skills/workflow/refs/scoring-rubric.md +49 -0
- package/skills/workflow/refs/signal-keywords.md +176 -0
- package/skills/workflow/refs/specialist-routing-rules.md +159 -0
- package/skills/worktrees/SKILL.md +87 -0
- package/skills/worktrees/refs/recipes.md +96 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
telemetry.py — cross-session quality telemetry capture for wicked-garden.
|
|
4
|
+
|
|
5
|
+
Closes Issue #443: quality trends invisible across sessions.
|
|
6
|
+
|
|
7
|
+
Responsibilities
|
|
8
|
+
----------------
|
|
9
|
+
1. Capture per-session quality metrics from crew + native-task state.
|
|
10
|
+
2. Append one record per session-close to a per-project JSONL timeline.
|
|
11
|
+
3. Expose read helpers for drift.py and status commands.
|
|
12
|
+
|
|
13
|
+
Storage
|
|
14
|
+
-------
|
|
15
|
+
Path (resolve via get_local_file from _paths):
|
|
16
|
+
~/.something-wicked/wicked-garden/projects/{slug}/metrics/{project}/timeline.jsonl
|
|
17
|
+
|
|
18
|
+
One JSONL record per session-close. Append-only. Never rewrites.
|
|
19
|
+
|
|
20
|
+
Captured fields (timeline.jsonl record schema)
|
|
21
|
+
----------------------------------------------
|
|
22
|
+
version: schema version (currently "1")
|
|
23
|
+
session_id: the session that closed (sanitized)
|
|
24
|
+
project: crew project name (or "_global" if none active)
|
|
25
|
+
recorded_at: UTC ISO8601 timestamp
|
|
26
|
+
sample_window: dict with first/last observation timestamps
|
|
27
|
+
metrics:
|
|
28
|
+
gate_pass_rate: float 0..1 — APPROVE / total verdicts this session
|
|
29
|
+
gate_avg_score: float — mean gate score (APPROVE/CONDITIONAL) or None
|
|
30
|
+
gate_verdict_count: int — total verdicts observed this session
|
|
31
|
+
gate_rework_count: int — REJECT verdicts this session
|
|
32
|
+
task_count: int — native tasks observed this session
|
|
33
|
+
task_completed: int — tasks completed this session
|
|
34
|
+
complexity_delta: int — complexity_score at close minus at session open
|
|
35
|
+
cycle_time_by_phase: {phase: seconds_to_approve} (float)
|
|
36
|
+
skip_reeval_count: int — --skip-reeval invocations this session
|
|
37
|
+
rework_events: int — alias for gate_rework_count for easy querying
|
|
38
|
+
|
|
39
|
+
Design rules
|
|
40
|
+
------------
|
|
41
|
+
- Stdlib-only (importable from hook scripts).
|
|
42
|
+
- Fail-open: every public entry point has a top-level try/except that returns
|
|
43
|
+
None / False rather than raising. Hook callers must never break.
|
|
44
|
+
- Capture target: <100ms typical, <500ms worst (bounded by a single stat+read
|
|
45
|
+
pass over the session's tasks dir).
|
|
46
|
+
- No external dependencies and no wicked-bus coupling. drift.py handles bus.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from __future__ import annotations
|
|
50
|
+
|
|
51
|
+
import json
|
|
52
|
+
import os
|
|
53
|
+
import sys
|
|
54
|
+
import time
|
|
55
|
+
from datetime import datetime, timezone
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
from typing import Any, Dict, List, Optional
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Path setup — inherit from wicked-garden local root layout
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
_SCRIPTS_ROOT = Path(__file__).resolve().parents[1]
|
|
64
|
+
if str(_SCRIPTS_ROOT) not in sys.path:
|
|
65
|
+
sys.path.insert(0, str(_SCRIPTS_ROOT))
|
|
66
|
+
|
|
67
|
+
# SCHEMA_VERSION intentionally remains at "1" through v8.3.x — see #667.
|
|
68
|
+
# PR #663 renamed sample_window.task_files_scanned -> tasks_observed, but the
|
|
69
|
+
# deprecated alias (also written into timeline.jsonl) preserves wire compat for
|
|
70
|
+
# any consumer reading the old field name. Because the change is non-breaking
|
|
71
|
+
# from a consumer's perspective, the schema version is held at "1" rather than
|
|
72
|
+
# bumped to "1.1". Drop the alias (and bump the version) only when the old
|
|
73
|
+
# field name is removed entirely.
|
|
74
|
+
SCHEMA_VERSION = "1"
|
|
75
|
+
# Per-session capture worst-case budget. We bail out rather than block session-end.
|
|
76
|
+
_CAPTURE_BUDGET_SECONDS = 0.5
|
|
77
|
+
_MAX_SCENARIO_SCAN = 500 # PR #730: bound DomainStore iteration in scenario extractor
|
|
78
|
+
# Max tasks to scan in one capture. Covers any realistic session.
|
|
79
|
+
_MAX_TASKS_SCAN = 500
|
|
80
|
+
# Minimum total verdicts before gate_pass_rate is considered meaningful.
|
|
81
|
+
_MIN_VERDICTS_FOR_RATE = 1
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _utc_now_iso() -> str:
|
|
85
|
+
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _sanitize(s: str) -> str:
|
|
89
|
+
return str(s).replace("/", "_").replace("\\", "_").replace("..", "_")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _timeline_path(project: str) -> Path:
|
|
93
|
+
"""Resolve the per-project timeline.jsonl path.
|
|
94
|
+
|
|
95
|
+
Uses get_local_file so the project-scoped root from _paths.py applies.
|
|
96
|
+
Falls back to a home-relative path if _paths is unavailable.
|
|
97
|
+
"""
|
|
98
|
+
safe_project = _sanitize(project) if project else "_global"
|
|
99
|
+
try:
|
|
100
|
+
from _paths import get_local_file
|
|
101
|
+
return get_local_file("metrics", safe_project, "timeline.jsonl")
|
|
102
|
+
except Exception:
|
|
103
|
+
fallback = (
|
|
104
|
+
Path.home() / ".something-wicked" / "wicked-garden" / "local"
|
|
105
|
+
/ "metrics" / safe_project / "timeline.jsonl"
|
|
106
|
+
)
|
|
107
|
+
fallback.parent.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
return fallback
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
# Native task scanning — pure read, no mutations
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
def _native_tasks_dir(session_id: str) -> Path:
|
|
116
|
+
"""Return the native-tasks dir for the given session. Never raises."""
|
|
117
|
+
config_dir = os.environ.get("CLAUDE_CONFIG_DIR")
|
|
118
|
+
if config_dir:
|
|
119
|
+
base = Path(config_dir)
|
|
120
|
+
else:
|
|
121
|
+
base = Path.home() / ".claude"
|
|
122
|
+
return base / "tasks" / _sanitize(session_id)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _iter_task_files(session_id: str, deadline: float) -> List[Path]:
|
|
126
|
+
"""List task JSON files for the session, bounded by _MAX_TASKS_SCAN + deadline."""
|
|
127
|
+
tdir = _native_tasks_dir(session_id)
|
|
128
|
+
if not tdir.exists():
|
|
129
|
+
return []
|
|
130
|
+
out: List[Path] = []
|
|
131
|
+
try:
|
|
132
|
+
for p in sorted(tdir.glob("*.json")):
|
|
133
|
+
if time.monotonic() > deadline:
|
|
134
|
+
break
|
|
135
|
+
out.append(p)
|
|
136
|
+
if len(out) >= _MAX_TASKS_SCAN:
|
|
137
|
+
break
|
|
138
|
+
except OSError:
|
|
139
|
+
return []
|
|
140
|
+
return out
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _read_task(path: Path) -> Optional[Dict[str, Any]]:
|
|
144
|
+
try:
|
|
145
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
146
|
+
except (OSError, json.JSONDecodeError):
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _parse_iso(ts: Any) -> Optional[float]:
|
|
151
|
+
if not isinstance(ts, str) or not ts:
|
|
152
|
+
return None
|
|
153
|
+
try:
|
|
154
|
+
s = ts.replace("Z", "+00:00")
|
|
155
|
+
return datetime.fromisoformat(s).timestamp()
|
|
156
|
+
except (ValueError, TypeError):
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# ---------------------------------------------------------------------------
|
|
161
|
+
# Metric extraction
|
|
162
|
+
# ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
def _extract_metrics_from_tasks(
|
|
165
|
+
tasks: List[Dict[str, Any]],
|
|
166
|
+
) -> Dict[str, Any]:
|
|
167
|
+
"""Extract quality metrics from the session's native task records.
|
|
168
|
+
|
|
169
|
+
Looks at metadata.event_type == 'gate-finding' for verdict + score,
|
|
170
|
+
and at task status for completion counts. Phase-transition events
|
|
171
|
+
contribute cycle-time samples.
|
|
172
|
+
"""
|
|
173
|
+
approve = 0
|
|
174
|
+
conditional = 0
|
|
175
|
+
reject = 0
|
|
176
|
+
scores: List[float] = []
|
|
177
|
+
task_count = 0
|
|
178
|
+
task_completed = 0
|
|
179
|
+
phase_started: Dict[str, float] = {}
|
|
180
|
+
cycle_time_by_phase: Dict[str, float] = {}
|
|
181
|
+
|
|
182
|
+
# Per-(phase, tier) verdict + score breakdown — issue #719 ACs need
|
|
183
|
+
# `{phase}.{tier}.min_score` and verdict ratios per gate. Stored as a
|
|
184
|
+
# nested dict keyed by ``f"{phase}.{tier}"`` for stable JSON addressing.
|
|
185
|
+
gate_breakdown: Dict[str, Dict[str, Any]] = {}
|
|
186
|
+
|
|
187
|
+
for t in tasks:
|
|
188
|
+
if not isinstance(t, dict):
|
|
189
|
+
continue
|
|
190
|
+
task_count += 1
|
|
191
|
+
status = t.get("status")
|
|
192
|
+
if status == "completed":
|
|
193
|
+
task_completed += 1
|
|
194
|
+
|
|
195
|
+
meta = t.get("metadata") or {}
|
|
196
|
+
if not isinstance(meta, dict):
|
|
197
|
+
continue
|
|
198
|
+
et = meta.get("event_type")
|
|
199
|
+
|
|
200
|
+
if et == "gate-finding":
|
|
201
|
+
verdict = str(meta.get("verdict", "")).upper()
|
|
202
|
+
if verdict == "APPROVE":
|
|
203
|
+
approve += 1
|
|
204
|
+
elif verdict == "CONDITIONAL":
|
|
205
|
+
conditional += 1
|
|
206
|
+
elif verdict == "REJECT":
|
|
207
|
+
reject += 1
|
|
208
|
+
score = meta.get("score")
|
|
209
|
+
if isinstance(score, (int, float)):
|
|
210
|
+
scores.append(float(score))
|
|
211
|
+
|
|
212
|
+
# Per phase × tier rollup. Tier defaults to "standard" when the
|
|
213
|
+
# gate-finding event doesn't carry an explicit rigor tag — most
|
|
214
|
+
# phase events do, but legacy fallbacks may not.
|
|
215
|
+
phase_name = meta.get("phase") or "unknown"
|
|
216
|
+
tier = (meta.get("rigor_tier") or meta.get("tier") or "standard")
|
|
217
|
+
key = f"{phase_name}.{tier}"
|
|
218
|
+
slot = gate_breakdown.setdefault(key, {
|
|
219
|
+
"phase": phase_name,
|
|
220
|
+
"tier": tier,
|
|
221
|
+
"approve": 0,
|
|
222
|
+
"conditional": 0,
|
|
223
|
+
"reject": 0,
|
|
224
|
+
"scores": [],
|
|
225
|
+
"min_score": None,
|
|
226
|
+
})
|
|
227
|
+
if verdict == "APPROVE":
|
|
228
|
+
slot["approve"] += 1
|
|
229
|
+
elif verdict == "CONDITIONAL":
|
|
230
|
+
slot["conditional"] += 1
|
|
231
|
+
elif verdict == "REJECT":
|
|
232
|
+
slot["reject"] += 1
|
|
233
|
+
if isinstance(score, (int, float)):
|
|
234
|
+
slot["scores"].append(float(score))
|
|
235
|
+
cur_min = slot["min_score"]
|
|
236
|
+
slot["min_score"] = float(score) if cur_min is None else min(cur_min, float(score))
|
|
237
|
+
|
|
238
|
+
elif et == "phase-transition":
|
|
239
|
+
phase = meta.get("phase") or meta.get("from_phase")
|
|
240
|
+
ts_end = _parse_iso(t.get("updated_at") or t.get("created_at"))
|
|
241
|
+
if phase and ts_end is not None:
|
|
242
|
+
start = phase_started.get(phase)
|
|
243
|
+
if start is not None and ts_end >= start:
|
|
244
|
+
cycle_time_by_phase[phase] = round(ts_end - start, 3)
|
|
245
|
+
# Any transition on the phase resets its start anchor.
|
|
246
|
+
phase_started[phase] = ts_end
|
|
247
|
+
|
|
248
|
+
# Finalize per-gate avg scores (kept alongside min for SPC of mean lines).
|
|
249
|
+
for slot in gate_breakdown.values():
|
|
250
|
+
scores_list = slot.pop("scores", [])
|
|
251
|
+
slot["avg_score"] = round(sum(scores_list) / len(scores_list), 4) if scores_list else None
|
|
252
|
+
total = slot["approve"] + slot["conditional"] + slot["reject"]
|
|
253
|
+
slot["total"] = total
|
|
254
|
+
slot["reject_rate"] = round(slot["reject"] / total, 4) if total else None
|
|
255
|
+
|
|
256
|
+
total_verdicts = approve + conditional + reject
|
|
257
|
+
if total_verdicts >= _MIN_VERDICTS_FOR_RATE:
|
|
258
|
+
# CONDITIONAL counts as partial pass: weight 0.5 so CONDITIONAL-heavy
|
|
259
|
+
# sessions don't mask real degradation but also aren't full failures.
|
|
260
|
+
pass_rate = (approve + 0.5 * conditional) / total_verdicts
|
|
261
|
+
else:
|
|
262
|
+
pass_rate = None
|
|
263
|
+
|
|
264
|
+
avg_score = round(sum(scores) / len(scores), 4) if scores else None
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"gate_pass_rate": pass_rate,
|
|
268
|
+
"gate_avg_score": avg_score,
|
|
269
|
+
"gate_verdict_count": total_verdicts,
|
|
270
|
+
"gate_rework_count": reject,
|
|
271
|
+
"rework_events": reject,
|
|
272
|
+
"task_count": task_count,
|
|
273
|
+
"task_completed": task_completed,
|
|
274
|
+
"cycle_time_by_phase": cycle_time_by_phase,
|
|
275
|
+
# Issue #719: nested per-(phase, tier) rollups for SPC drift.
|
|
276
|
+
"gate_breakdown": gate_breakdown,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _extract_convergence_metrics(
|
|
281
|
+
project: Optional[str],
|
|
282
|
+
deadline: Optional[float] = None,
|
|
283
|
+
) -> Dict[str, Any]:
|
|
284
|
+
"""Read crew convergence stalls for the active project.
|
|
285
|
+
|
|
286
|
+
Returns counts of stalled artifacts (sessions_in_state >= threshold) and
|
|
287
|
+
the count of recorded transitions in the most recent log scan. Fail-open:
|
|
288
|
+
any failure → all-zero metrics. Honors capture deadline (PR #730 review):
|
|
289
|
+
if budget already exhausted on entry, returns zeros without doing any work.
|
|
290
|
+
"""
|
|
291
|
+
out = {"convergence_stalls": 0, "convergence_artifacts": 0}
|
|
292
|
+
if not project or project == "_global":
|
|
293
|
+
return out
|
|
294
|
+
if deadline is not None and time.monotonic() > deadline:
|
|
295
|
+
return out # over budget — skip rather than block the hook
|
|
296
|
+
try:
|
|
297
|
+
# `_SCRIPTS_ROOT` is already on sys.path at module-load (lines 63-65).
|
|
298
|
+
# No per-call insert needed — flagged in gemini's PR #730 review.
|
|
299
|
+
from crew import convergence # type: ignore
|
|
300
|
+
# convergence APIs take a project *directory*, not the slug. Resolve
|
|
301
|
+
# via the same helper its CLI uses so we honor _paths.
|
|
302
|
+
project_dir = convergence._resolve_project_dir(project)
|
|
303
|
+
stalls = convergence.detect_stalls(project_dir) or []
|
|
304
|
+
status = convergence.project_status(project_dir) or {}
|
|
305
|
+
out["convergence_stalls"] = len(stalls)
|
|
306
|
+
out["convergence_artifacts"] = int(status.get("total") or 0)
|
|
307
|
+
except Exception:
|
|
308
|
+
pass
|
|
309
|
+
return out
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _extract_scenario_metrics(
|
|
313
|
+
project: Optional[str],
|
|
314
|
+
deadline: Optional[float] = None,
|
|
315
|
+
since_iso: Optional[str] = None,
|
|
316
|
+
) -> Dict[str, Any]:
|
|
317
|
+
"""Read PER-SESSION scenario verdict counts for the active project.
|
|
318
|
+
|
|
319
|
+
Counts only verdicts whose recorded timestamp is later than ``since_iso``
|
|
320
|
+
(the previous session's recorded_at). This produces session-delta counts
|
|
321
|
+
suitable for ratio drift detection — Copilot flagged the prior cumulative
|
|
322
|
+
behavior in PR #730 as semantically wrong (lifetime cumulative counts can
|
|
323
|
+
only grow, so per-session ratios cannot drift).
|
|
324
|
+
|
|
325
|
+
Honors ``deadline``: if budget exhausted on entry, returns zeros. Caps the
|
|
326
|
+
iteration at _MAX_SCENARIO_SCAN to bound the worst-case scan even when no
|
|
327
|
+
deadline is provided.
|
|
328
|
+
|
|
329
|
+
Falls back to all-zero output when wicked-testing is not installed.
|
|
330
|
+
"""
|
|
331
|
+
out = {"scenario_pass": 0, "scenario_partial": 0, "scenario_fail": 0}
|
|
332
|
+
if not project or project == "_global":
|
|
333
|
+
return out
|
|
334
|
+
if deadline is not None and time.monotonic() > deadline:
|
|
335
|
+
return out
|
|
336
|
+
try:
|
|
337
|
+
from _domain_store import DomainStore # type: ignore
|
|
338
|
+
ds = DomainStore("wicked-testing", hook_mode=True)
|
|
339
|
+
scanned = 0
|
|
340
|
+
for source in ("verdicts", "runs", "scenarios"):
|
|
341
|
+
if deadline is not None and time.monotonic() > deadline:
|
|
342
|
+
break
|
|
343
|
+
try:
|
|
344
|
+
rows = ds.list(source, project=project) or []
|
|
345
|
+
except Exception:
|
|
346
|
+
rows = []
|
|
347
|
+
for row in rows:
|
|
348
|
+
scanned += 1
|
|
349
|
+
if scanned > _MAX_SCENARIO_SCAN:
|
|
350
|
+
break
|
|
351
|
+
# Only count verdicts after the previous session's capture.
|
|
352
|
+
# Records older than `since_iso` (or all rows on the first ever
|
|
353
|
+
# capture, where since_iso is None) are excluded so each
|
|
354
|
+
# session emits its own delta, not the running total.
|
|
355
|
+
if since_iso:
|
|
356
|
+
rec_ts = str(row.get("recorded_at") or row.get("created_at") or "")
|
|
357
|
+
if rec_ts and rec_ts <= since_iso:
|
|
358
|
+
continue
|
|
359
|
+
v = str(row.get("verdict") or row.get("result") or "").upper()
|
|
360
|
+
if v == "PASS":
|
|
361
|
+
out["scenario_pass"] += 1
|
|
362
|
+
elif v in ("PARTIAL", "WARN"):
|
|
363
|
+
out["scenario_partial"] += 1
|
|
364
|
+
elif v in ("FAIL", "ERROR"):
|
|
365
|
+
out["scenario_fail"] += 1
|
|
366
|
+
if any(out.values()) and scanned <= _MAX_SCENARIO_SCAN:
|
|
367
|
+
break # first source that yielded data wins
|
|
368
|
+
except Exception:
|
|
369
|
+
pass
|
|
370
|
+
return out
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _extract_session_extras() -> Dict[str, Any]:
|
|
374
|
+
"""Read session-level counters (skip_reeval, complexity delta)."""
|
|
375
|
+
skip_reeval_count = 0
|
|
376
|
+
complexity_open: Optional[int] = None
|
|
377
|
+
complexity_close: Optional[int] = None
|
|
378
|
+
active_project: Optional[str] = None
|
|
379
|
+
try:
|
|
380
|
+
from _session import SessionState
|
|
381
|
+
state = SessionState.load()
|
|
382
|
+
# These fields are optional — gracefully absent on older sessions.
|
|
383
|
+
raw_skip = getattr(state, "skip_reeval_count", None)
|
|
384
|
+
if isinstance(raw_skip, int):
|
|
385
|
+
skip_reeval_count = raw_skip
|
|
386
|
+
complexity_open = getattr(state, "complexity_at_session_open", None)
|
|
387
|
+
complexity_close = getattr(state, "complexity_score", None)
|
|
388
|
+
active_project = getattr(state, "active_project", None)
|
|
389
|
+
except Exception:
|
|
390
|
+
pass # fail open — session state unavailable is not fatal for telemetry
|
|
391
|
+
|
|
392
|
+
complexity_delta = 0
|
|
393
|
+
if isinstance(complexity_open, int) and isinstance(complexity_close, int):
|
|
394
|
+
complexity_delta = complexity_close - complexity_open
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
"skip_reeval_count": skip_reeval_count,
|
|
398
|
+
"complexity_delta": complexity_delta,
|
|
399
|
+
"active_project": active_project,
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ---------------------------------------------------------------------------
|
|
404
|
+
# Public entry points
|
|
405
|
+
# ---------------------------------------------------------------------------
|
|
406
|
+
|
|
407
|
+
def capture_session(
|
|
408
|
+
session_id: str,
|
|
409
|
+
project: Optional[str] = None,
|
|
410
|
+
) -> Optional[Dict[str, Any]]:
|
|
411
|
+
"""Capture telemetry for a session-close and append to the timeline.
|
|
412
|
+
|
|
413
|
+
Returns the appended record dict on success, or None on any failure.
|
|
414
|
+
Never raises. Honors _CAPTURE_BUDGET_SECONDS so hook callers stay fast.
|
|
415
|
+
"""
|
|
416
|
+
try:
|
|
417
|
+
t0 = time.monotonic()
|
|
418
|
+
deadline = t0 + _CAPTURE_BUDGET_SECONDS
|
|
419
|
+
|
|
420
|
+
extras = _extract_session_extras()
|
|
421
|
+
raw_project = project or extras.get("active_project") or "_global"
|
|
422
|
+
resolved_project = _sanitize(raw_project)
|
|
423
|
+
|
|
424
|
+
# Task reading routed via _task_reader (#596 v8-PR-2).
|
|
425
|
+
# WG_DAEMON_ENABLED=false → direct file scan (unchanged behaviour).
|
|
426
|
+
tasks: List[Dict[str, Any]] = []
|
|
427
|
+
# task_files: initialised before the try-block so the direct-scan fallback
|
|
428
|
+
# path populates it. The happy-path (daemon-routed read_session_tasks)
|
|
429
|
+
# leaves task_files empty but tasks populated; sample_window now uses
|
|
430
|
+
# len(tasks) (tasks_observed) rather than len(task_files) so the metric
|
|
431
|
+
# is accurate for both paths (issues #647, #660).
|
|
432
|
+
task_files: List[Path] = []
|
|
433
|
+
try:
|
|
434
|
+
_scripts_root = str(Path(__file__).resolve().parents[1])
|
|
435
|
+
import sys as _sys
|
|
436
|
+
if _scripts_root not in _sys.path:
|
|
437
|
+
_sys.path.insert(0, _scripts_root)
|
|
438
|
+
from crew._task_reader import read_session_tasks # type: ignore[import]
|
|
439
|
+
tasks = read_session_tasks(session_id, limit=_MAX_TASKS_SCAN)
|
|
440
|
+
except Exception:
|
|
441
|
+
# Fallback: direct file read (pre-PR-2 path, always available).
|
|
442
|
+
task_files = _iter_task_files(session_id, deadline)
|
|
443
|
+
for p in task_files:
|
|
444
|
+
if time.monotonic() > deadline:
|
|
445
|
+
break
|
|
446
|
+
t = _read_task(p)
|
|
447
|
+
if t is not None:
|
|
448
|
+
tasks.append(t)
|
|
449
|
+
|
|
450
|
+
metrics = _extract_metrics_from_tasks(tasks)
|
|
451
|
+
metrics["skip_reeval_count"] = extras["skip_reeval_count"]
|
|
452
|
+
metrics["complexity_delta"] = extras["complexity_delta"]
|
|
453
|
+
# Issue #719: convergence + scenario rollups join the per-session metric
|
|
454
|
+
# bag. Fail-open on both — empty zeros are better than dropped records.
|
|
455
|
+
# PR #730 review: pass `deadline` so these helpers respect the same
|
|
456
|
+
# _CAPTURE_BUDGET_SECONDS guardrail the task scanner honors.
|
|
457
|
+
# Scenario extractor also gets the previous session's recorded_at as
|
|
458
|
+
# `since_iso` so it counts session deltas, not lifetime cumulative.
|
|
459
|
+
prev_recorded_at: Optional[str] = None
|
|
460
|
+
try:
|
|
461
|
+
prior = read_timeline(resolved_project, limit=1)
|
|
462
|
+
if prior:
|
|
463
|
+
prev_recorded_at = prior[-1].get("recorded_at")
|
|
464
|
+
except Exception:
|
|
465
|
+
pass
|
|
466
|
+
metrics.update(_extract_convergence_metrics(raw_project, deadline=deadline))
|
|
467
|
+
metrics.update(_extract_scenario_metrics(raw_project, deadline=deadline, since_iso=prev_recorded_at))
|
|
468
|
+
|
|
469
|
+
# Build sample window from the first/last task timestamps observed.
|
|
470
|
+
first_ts: Optional[str] = None
|
|
471
|
+
last_ts: Optional[str] = None
|
|
472
|
+
for t in tasks:
|
|
473
|
+
c = t.get("created_at")
|
|
474
|
+
u = t.get("updated_at") or c
|
|
475
|
+
if isinstance(c, str):
|
|
476
|
+
first_ts = c if first_ts is None or c < first_ts else first_ts
|
|
477
|
+
if isinstance(u, str):
|
|
478
|
+
last_ts = u if last_ts is None or u > last_ts else last_ts
|
|
479
|
+
|
|
480
|
+
record = {
|
|
481
|
+
"version": SCHEMA_VERSION,
|
|
482
|
+
"session_id": _sanitize(session_id),
|
|
483
|
+
"project": resolved_project,
|
|
484
|
+
"recorded_at": _utc_now_iso(),
|
|
485
|
+
"sample_window": {
|
|
486
|
+
"first_observation": first_ts,
|
|
487
|
+
"last_observation": last_ts,
|
|
488
|
+
# tasks_observed counts tasks from whichever path ran (#660):
|
|
489
|
+
# daemon-routed happy path (len(tasks)) or direct file scan
|
|
490
|
+
# (also len(tasks) after tasks are populated from task_files).
|
|
491
|
+
# Previously used len(task_files) which was always 0 on the
|
|
492
|
+
# happy path, making the metric meaningless for daemon-routed
|
|
493
|
+
# sessions. Renamed from task_files_scanned for clarity.
|
|
494
|
+
"tasks_observed": len(tasks),
|
|
495
|
+
# Deprecated alias — emitted for one release (v8.3.x) so
|
|
496
|
+
# downstream consumers (dashboards, alerting rules) keyed on
|
|
497
|
+
# the old name stay green. Remove in v8.4.0. See PR #663
|
|
498
|
+
# council and CHANGELOG.
|
|
499
|
+
"task_files_scanned": len(tasks),
|
|
500
|
+
},
|
|
501
|
+
"metrics": metrics,
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
_append_record(resolved_project, record)
|
|
505
|
+
return record
|
|
506
|
+
except Exception as exc:
|
|
507
|
+
# Never break session teardown.
|
|
508
|
+
print(f"[wicked-garden] telemetry capture error: {exc}", file=sys.stderr)
|
|
509
|
+
return None
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _append_record(project: str, record: Dict[str, Any]) -> None:
|
|
513
|
+
"""Append a JSON record as a single JSONL line. Parent dirs auto-created."""
|
|
514
|
+
path = _timeline_path(project)
|
|
515
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
516
|
+
# Use a single line — no embedded newlines in JSON default encoding.
|
|
517
|
+
line = json.dumps(record, separators=(",", ":"), ensure_ascii=False)
|
|
518
|
+
with path.open("a", encoding="utf-8") as fh:
|
|
519
|
+
fh.write(line + "\n")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def read_timeline(project: str, *, limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
523
|
+
"""Return the decoded records for a project's timeline.
|
|
524
|
+
|
|
525
|
+
Most-recent-last ordering matches append order. Corrupt lines are skipped
|
|
526
|
+
rather than raising — drift analysis is best-effort.
|
|
527
|
+
"""
|
|
528
|
+
path = _timeline_path(project)
|
|
529
|
+
if not path.exists():
|
|
530
|
+
return []
|
|
531
|
+
out: List[Dict[str, Any]] = []
|
|
532
|
+
try:
|
|
533
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
534
|
+
for line in fh:
|
|
535
|
+
line = line.strip()
|
|
536
|
+
if not line:
|
|
537
|
+
continue
|
|
538
|
+
try:
|
|
539
|
+
out.append(json.loads(line))
|
|
540
|
+
except json.JSONDecodeError:
|
|
541
|
+
continue
|
|
542
|
+
except OSError:
|
|
543
|
+
return []
|
|
544
|
+
if limit is not None and limit > 0:
|
|
545
|
+
out = out[-limit:]
|
|
546
|
+
return out
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def timeline_path_for(project: str) -> str:
|
|
550
|
+
"""String path helper for commands that need to surface the location."""
|
|
551
|
+
return str(_timeline_path(project))
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
# ---------------------------------------------------------------------------
|
|
555
|
+
# CLI entry — used by hook + status command
|
|
556
|
+
# ---------------------------------------------------------------------------
|
|
557
|
+
|
|
558
|
+
def _cli_capture(args: List[str]) -> int:
|
|
559
|
+
session_id = args[0] if args else os.environ.get("CLAUDE_SESSION_ID", "default")
|
|
560
|
+
project = args[1] if len(args) > 1 else None
|
|
561
|
+
rec = capture_session(session_id, project)
|
|
562
|
+
if rec is None:
|
|
563
|
+
print(json.dumps({"ok": False, "reason": "capture returned None"}))
|
|
564
|
+
return 0 # fail-open
|
|
565
|
+
print(json.dumps({"ok": True, "record": rec}))
|
|
566
|
+
return 0
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _cli_show(args: List[str]) -> int:
|
|
570
|
+
if not args:
|
|
571
|
+
print(json.dumps({"ok": False, "reason": "project required"}))
|
|
572
|
+
return 0
|
|
573
|
+
project = args[0]
|
|
574
|
+
limit = None
|
|
575
|
+
if len(args) > 1:
|
|
576
|
+
try:
|
|
577
|
+
limit = int(args[1])
|
|
578
|
+
except ValueError:
|
|
579
|
+
limit = None
|
|
580
|
+
records = read_timeline(project, limit=limit)
|
|
581
|
+
print(json.dumps({"ok": True, "project": project, "records": records,
|
|
582
|
+
"path": timeline_path_for(project)}))
|
|
583
|
+
return 0
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
587
|
+
argv = list(argv if argv is not None else sys.argv[1:])
|
|
588
|
+
if not argv or argv[0] in ("-h", "--help", "help"):
|
|
589
|
+
print(
|
|
590
|
+
"Usage:\n"
|
|
591
|
+
" telemetry.py capture [session_id] [project]\n"
|
|
592
|
+
" telemetry.py show <project> [limit]\n"
|
|
593
|
+
" telemetry.py path <project>\n",
|
|
594
|
+
file=sys.stderr,
|
|
595
|
+
)
|
|
596
|
+
return 0
|
|
597
|
+
cmd = argv[0]
|
|
598
|
+
rest = argv[1:]
|
|
599
|
+
if cmd == "capture":
|
|
600
|
+
return _cli_capture(rest)
|
|
601
|
+
if cmd == "show":
|
|
602
|
+
return _cli_show(rest)
|
|
603
|
+
if cmd == "path":
|
|
604
|
+
if not rest:
|
|
605
|
+
print(json.dumps({"ok": False, "reason": "project required"}))
|
|
606
|
+
return 0
|
|
607
|
+
print(timeline_path_for(rest[0]))
|
|
608
|
+
return 0
|
|
609
|
+
print(json.dumps({"ok": False, "reason": f"unknown command: {cmd}"}))
|
|
610
|
+
return 0
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
if __name__ == "__main__":
|
|
614
|
+
sys.exit(main())
|