@accelerationguy/accel 1.0.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.md +19 -0
- package/LICENSE +33 -0
- package/README.md +275 -0
- package/bin/install.js +661 -0
- package/docs/getting-started.md +164 -0
- package/docs/module-guide.md +139 -0
- package/modules/drive/LICENSE +21 -0
- package/modules/drive/PAUL-VS-GSD.md +171 -0
- package/modules/drive/README.md +555 -0
- package/modules/drive/assets/terminal.svg +67 -0
- package/modules/drive/bin/install.js +210 -0
- package/modules/drive/integration.js +76 -0
- package/modules/drive/package.json +38 -0
- package/modules/drive/src/commands/add-phase.md +36 -0
- package/modules/drive/src/commands/apply.md +83 -0
- package/modules/drive/src/commands/assumptions.md +37 -0
- package/modules/drive/src/commands/audit.md +57 -0
- package/modules/drive/src/commands/complete-milestone.md +36 -0
- package/modules/drive/src/commands/config.md +175 -0
- package/modules/drive/src/commands/consider-issues.md +41 -0
- package/modules/drive/src/commands/discover.md +48 -0
- package/modules/drive/src/commands/discuss-milestone.md +33 -0
- package/modules/drive/src/commands/discuss.md +34 -0
- package/modules/drive/src/commands/flows.md +73 -0
- package/modules/drive/src/commands/handoff.md +201 -0
- package/modules/drive/src/commands/help.md +525 -0
- package/modules/drive/src/commands/init.md +54 -0
- package/modules/drive/src/commands/map-codebase.md +34 -0
- package/modules/drive/src/commands/milestone.md +34 -0
- package/modules/drive/src/commands/pause.md +44 -0
- package/modules/drive/src/commands/plan-fix.md +216 -0
- package/modules/drive/src/commands/plan.md +36 -0
- package/modules/drive/src/commands/progress.md +138 -0
- package/modules/drive/src/commands/register.md +29 -0
- package/modules/drive/src/commands/remove-phase.md +37 -0
- package/modules/drive/src/commands/research-phase.md +209 -0
- package/modules/drive/src/commands/research.md +47 -0
- package/modules/drive/src/commands/resume.md +49 -0
- package/modules/drive/src/commands/status.md +78 -0
- package/modules/drive/src/commands/unify.md +87 -0
- package/modules/drive/src/commands/verify.md +60 -0
- package/modules/drive/src/references/checkpoints.md +234 -0
- package/modules/drive/src/references/context-management.md +219 -0
- package/modules/drive/src/references/git-strategy.md +206 -0
- package/modules/drive/src/references/loop-phases.md +254 -0
- package/modules/drive/src/references/plan-format.md +263 -0
- package/modules/drive/src/references/quality-principles.md +152 -0
- package/modules/drive/src/references/research-quality-control.md +247 -0
- package/modules/drive/src/references/sonarqube-integration.md +244 -0
- package/modules/drive/src/references/specialized-workflow-integration.md +186 -0
- package/modules/drive/src/references/subagent-criteria.md +179 -0
- package/modules/drive/src/references/tdd.md +219 -0
- package/modules/drive/src/references/work-units.md +161 -0
- package/modules/drive/src/rules/commands.md +108 -0
- package/modules/drive/src/rules/references.md +107 -0
- package/modules/drive/src/rules/style.md +123 -0
- package/modules/drive/src/rules/templates.md +51 -0
- package/modules/drive/src/rules/workflows.md +133 -0
- package/modules/drive/src/templates/CONTEXT.md +88 -0
- package/modules/drive/src/templates/DEBUG.md +164 -0
- package/modules/drive/src/templates/DISCOVERY.md +148 -0
- package/modules/drive/src/templates/HANDOFF.md +77 -0
- package/modules/drive/src/templates/ISSUES.md +93 -0
- package/modules/drive/src/templates/MILESTONES.md +167 -0
- package/modules/drive/src/templates/PLAN.md +328 -0
- package/modules/drive/src/templates/PROJECT.md +219 -0
- package/modules/drive/src/templates/RESEARCH.md +130 -0
- package/modules/drive/src/templates/ROADMAP.md +328 -0
- package/modules/drive/src/templates/SPECIAL-FLOWS.md +70 -0
- package/modules/drive/src/templates/STATE.md +210 -0
- package/modules/drive/src/templates/SUMMARY.md +221 -0
- package/modules/drive/src/templates/UAT-ISSUES.md +139 -0
- package/modules/drive/src/templates/codebase/architecture.md +259 -0
- package/modules/drive/src/templates/codebase/concerns.md +329 -0
- package/modules/drive/src/templates/codebase/conventions.md +311 -0
- package/modules/drive/src/templates/codebase/integrations.md +284 -0
- package/modules/drive/src/templates/codebase/stack.md +190 -0
- package/modules/drive/src/templates/codebase/structure.md +287 -0
- package/modules/drive/src/templates/codebase/testing.md +484 -0
- package/modules/drive/src/templates/config.md +181 -0
- package/modules/drive/src/templates/milestone-archive.md +236 -0
- package/modules/drive/src/templates/milestone-context.md +190 -0
- package/modules/drive/src/templates/paul-json.md +147 -0
- package/modules/drive/src/vector-config/PAUL +26 -0
- package/modules/drive/src/vector-config/PAUL.manifest +11 -0
- package/modules/drive/src/workflows/apply-phase.md +393 -0
- package/modules/drive/src/workflows/audit-plan.md +344 -0
- package/modules/drive/src/workflows/complete-milestone.md +479 -0
- package/modules/drive/src/workflows/configure-special-flows.md +283 -0
- package/modules/drive/src/workflows/consider-issues.md +172 -0
- package/modules/drive/src/workflows/create-milestone.md +268 -0
- package/modules/drive/src/workflows/debug.md +292 -0
- package/modules/drive/src/workflows/discovery.md +187 -0
- package/modules/drive/src/workflows/discuss-milestone.md +245 -0
- package/modules/drive/src/workflows/discuss-phase.md +231 -0
- package/modules/drive/src/workflows/init-project.md +698 -0
- package/modules/drive/src/workflows/map-codebase.md +459 -0
- package/modules/drive/src/workflows/pause-work.md +259 -0
- package/modules/drive/src/workflows/phase-assumptions.md +181 -0
- package/modules/drive/src/workflows/plan-phase.md +385 -0
- package/modules/drive/src/workflows/quality-gate.md +263 -0
- package/modules/drive/src/workflows/register-manifest.md +107 -0
- package/modules/drive/src/workflows/research.md +241 -0
- package/modules/drive/src/workflows/resume-project.md +200 -0
- package/modules/drive/src/workflows/roadmap-management.md +334 -0
- package/modules/drive/src/workflows/transition-phase.md +368 -0
- package/modules/drive/src/workflows/unify-phase.md +290 -0
- package/modules/drive/src/workflows/verify-work.md +241 -0
- package/modules/forge/README.md +281 -0
- package/modules/forge/bin/install.js +200 -0
- package/modules/forge/package.json +32 -0
- package/modules/forge/skillsmith/rules/checklists-rules.md +42 -0
- package/modules/forge/skillsmith/rules/context-rules.md +43 -0
- package/modules/forge/skillsmith/rules/entry-point-rules.md +44 -0
- package/modules/forge/skillsmith/rules/frameworks-rules.md +43 -0
- package/modules/forge/skillsmith/rules/tasks-rules.md +52 -0
- package/modules/forge/skillsmith/rules/templates-rules.md +43 -0
- package/modules/forge/skillsmith/skillsmith.md +82 -0
- package/modules/forge/skillsmith/tasks/audit.md +277 -0
- package/modules/forge/skillsmith/tasks/discover.md +145 -0
- package/modules/forge/skillsmith/tasks/distill.md +276 -0
- package/modules/forge/skillsmith/tasks/scaffold.md +349 -0
- package/modules/forge/specs/checklists.md +193 -0
- package/modules/forge/specs/context.md +223 -0
- package/modules/forge/specs/entry-point.md +320 -0
- package/modules/forge/specs/frameworks.md +228 -0
- package/modules/forge/specs/rules.md +245 -0
- package/modules/forge/specs/tasks.md +344 -0
- package/modules/forge/specs/templates.md +335 -0
- package/modules/forge/terminal.svg +70 -0
- package/modules/ignition/README.md +245 -0
- package/modules/ignition/bin/install.js +184 -0
- package/modules/ignition/checklists/planning-quality.md +55 -0
- package/modules/ignition/data/application/config.md +21 -0
- package/modules/ignition/data/application/guide.md +51 -0
- package/modules/ignition/data/application/skill-loadout.md +11 -0
- package/modules/ignition/data/campaign/config.md +18 -0
- package/modules/ignition/data/campaign/guide.md +36 -0
- package/modules/ignition/data/campaign/skill-loadout.md +10 -0
- package/modules/ignition/data/client/config.md +18 -0
- package/modules/ignition/data/client/guide.md +36 -0
- package/modules/ignition/data/client/skill-loadout.md +11 -0
- package/modules/ignition/data/utility/config.md +18 -0
- package/modules/ignition/data/utility/guide.md +31 -0
- package/modules/ignition/data/utility/skill-loadout.md +8 -0
- package/modules/ignition/data/workflow/config.md +19 -0
- package/modules/ignition/data/workflow/guide.md +41 -0
- package/modules/ignition/data/workflow/skill-loadout.md +10 -0
- package/modules/ignition/integration.js +54 -0
- package/modules/ignition/package.json +35 -0
- package/modules/ignition/seed.md +81 -0
- package/modules/ignition/tasks/add-type.md +164 -0
- package/modules/ignition/tasks/graduate.md +182 -0
- package/modules/ignition/tasks/ideate.md +221 -0
- package/modules/ignition/tasks/launch.md +137 -0
- package/modules/ignition/tasks/status.md +71 -0
- package/modules/ignition/templates/planning-application.md +193 -0
- package/modules/ignition/templates/planning-campaign.md +138 -0
- package/modules/ignition/templates/planning-client.md +149 -0
- package/modules/ignition/templates/planning-utility.md +112 -0
- package/modules/ignition/templates/planning-workflow.md +125 -0
- package/modules/ignition/terminal.svg +74 -0
- package/modules/mission-control/CONTEXT-CONTINUITY-SPEC.md +293 -0
- package/modules/mission-control/CONTEXT-ENGINEERING-GUIDE.md +282 -0
- package/modules/mission-control/README.md +91 -0
- package/modules/mission-control/assets/terminal.svg +80 -0
- package/modules/mission-control/examples/entities.example.json +133 -0
- package/modules/mission-control/examples/projects.example.json +318 -0
- package/modules/mission-control/examples/state.example.json +183 -0
- package/modules/mission-control/examples/vector.example.json +245 -0
- package/modules/mission-control/mission-control/checklists/install-verification.md +46 -0
- package/modules/mission-control/mission-control/frameworks/framework-registry.md +83 -0
- package/modules/mission-control/mission-control/mission-control.md +83 -0
- package/modules/mission-control/mission-control/tasks/insights.md +73 -0
- package/modules/mission-control/mission-control/tasks/install.md +194 -0
- package/modules/mission-control/mission-control/tasks/status.md +125 -0
- package/modules/mission-control/schemas/entities.schema.json +89 -0
- package/modules/mission-control/schemas/projects.schema.json +221 -0
- package/modules/mission-control/schemas/state.schema.json +108 -0
- package/modules/mission-control/schemas/vector.schema.json +200 -0
- package/modules/momentum/README.md +678 -0
- package/modules/momentum/bin/install.js +563 -0
- package/modules/momentum/integration.js +131 -0
- package/modules/momentum/package.json +42 -0
- package/modules/momentum/schemas/entities.schema.json +89 -0
- package/modules/momentum/schemas/projects.schema.json +221 -0
- package/modules/momentum/schemas/state.schema.json +108 -0
- package/modules/momentum/src/commands/audit-claude-md.md +31 -0
- package/modules/momentum/src/commands/audit.md +33 -0
- package/modules/momentum/src/commands/groom.md +35 -0
- package/modules/momentum/src/commands/history.md +27 -0
- package/modules/momentum/src/commands/pulse.md +33 -0
- package/modules/momentum/src/commands/scaffold.md +33 -0
- package/modules/momentum/src/commands/status.md +28 -0
- package/modules/momentum/src/commands/surface-convert.md +35 -0
- package/modules/momentum/src/commands/surface-create.md +34 -0
- package/modules/momentum/src/commands/surface-list.md +27 -0
- package/modules/momentum/src/commands/vector-hygiene.md +33 -0
- package/modules/momentum/src/framework/context/momentum-principles.md +71 -0
- package/modules/momentum/src/framework/frameworks/audit-strategies.md +53 -0
- package/modules/momentum/src/framework/frameworks/satellite-registration.md +44 -0
- package/modules/momentum/src/framework/tasks/audit-claude-md.md +68 -0
- package/modules/momentum/src/framework/tasks/audit.md +64 -0
- package/modules/momentum/src/framework/tasks/groom.md +164 -0
- package/modules/momentum/src/framework/tasks/history.md +34 -0
- package/modules/momentum/src/framework/tasks/pulse.md +83 -0
- package/modules/momentum/src/framework/tasks/scaffold.md +202 -0
- package/modules/momentum/src/framework/tasks/status.md +35 -0
- package/modules/momentum/src/framework/tasks/surface-convert.md +143 -0
- package/modules/momentum/src/framework/tasks/surface-create.md +184 -0
- package/modules/momentum/src/framework/tasks/surface-list.md +42 -0
- package/modules/momentum/src/framework/tasks/vector-hygiene.md +160 -0
- package/modules/momentum/src/framework/templates/workspace-json.md +96 -0
- package/modules/momentum/src/hooks/_template.py +129 -0
- package/modules/momentum/src/hooks/active-hook.py +178 -0
- package/modules/momentum/src/hooks/backlog-hook.py +115 -0
- package/modules/momentum/src/hooks/mission-control-insights.py +169 -0
- package/modules/momentum/src/hooks/momentum-pulse-check.py +351 -0
- package/modules/momentum/src/hooks/operator.py +53 -0
- package/modules/momentum/src/hooks/psmm-injector.py +67 -0
- package/modules/momentum/src/hooks/satellite-detection.py +248 -0
- package/modules/momentum/src/packages/momentum-mcp/index.js +119 -0
- package/modules/momentum/src/packages/momentum-mcp/package.json +10 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/entities.js +226 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/operator.js +106 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/projects.js +322 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/psmm.js +206 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/state.js +199 -0
- package/modules/momentum/src/packages/momentum-mcp/tools/surfaces.js +404 -0
- package/modules/momentum/src/skill/momentum.md +111 -0
- package/modules/momentum/src/tasks/groom.md +164 -0
- package/modules/momentum/src/templates/operator.json +66 -0
- package/modules/momentum/src/templates/workspace.json +111 -0
- package/modules/momentum/terminal.svg +77 -0
- package/modules/radar/README.md +1552 -0
- package/modules/radar/commands/audit.md +233 -0
- package/modules/radar/commands/guardrails.md +194 -0
- package/modules/radar/commands/init.md +207 -0
- package/modules/radar/commands/playbook.md +176 -0
- package/modules/radar/commands/remediate.md +156 -0
- package/modules/radar/commands/report.md +172 -0
- package/modules/radar/commands/resume.md +176 -0
- package/modules/radar/commands/status.md +148 -0
- package/modules/radar/commands/transform.md +205 -0
- package/modules/radar/commands/validate.md +177 -0
- package/modules/radar/docs/ARCHITECTURE.md +336 -0
- package/modules/radar/docs/GETTING-STARTED.md +287 -0
- package/modules/radar/docs/standards/agents.md +197 -0
- package/modules/radar/docs/standards/commands.md +250 -0
- package/modules/radar/docs/standards/domains.md +191 -0
- package/modules/radar/docs/standards/personas.md +211 -0
- package/modules/radar/docs/standards/rules.md +218 -0
- package/modules/radar/docs/standards/runtime.md +445 -0
- package/modules/radar/docs/standards/schemas.md +269 -0
- package/modules/radar/docs/standards/tools.md +273 -0
- package/modules/radar/docs/standards/workflows.md +254 -0
- package/modules/radar/docs/terminal.svg +72 -0
- package/modules/radar/docs/validation/convention-compliance-report.md +183 -0
- package/modules/radar/docs/validation/cross-reference-report.md +195 -0
- package/modules/radar/docs/validation/validation-summary.md +118 -0
- package/modules/radar/docs/validation/version-manifest.yaml +363 -0
- package/modules/radar/install.sh +711 -0
- package/modules/radar/integration.js +53 -0
- package/modules/radar/src/core/agents/architect.md +25 -0
- package/modules/radar/src/core/agents/compliance-officer.md +25 -0
- package/modules/radar/src/core/agents/data-engineer.md +25 -0
- package/modules/radar/src/core/agents/devils-advocate.md +22 -0
- package/modules/radar/src/core/agents/performance-engineer.md +25 -0
- package/modules/radar/src/core/agents/principal-engineer.md +23 -0
- package/modules/radar/src/core/agents/reality-gap-analyst.md +22 -0
- package/modules/radar/src/core/agents/security-engineer.md +25 -0
- package/modules/radar/src/core/agents/senior-app-engineer.md +25 -0
- package/modules/radar/src/core/agents/sre.md +25 -0
- package/modules/radar/src/core/agents/staff-engineer.md +23 -0
- package/modules/radar/src/core/agents/test-engineer.md +25 -0
- package/modules/radar/src/core/personas/architect.md +111 -0
- package/modules/radar/src/core/personas/compliance-officer.md +104 -0
- package/modules/radar/src/core/personas/data-engineer.md +113 -0
- package/modules/radar/src/core/personas/devils-advocate.md +105 -0
- package/modules/radar/src/core/personas/performance-engineer.md +119 -0
- package/modules/radar/src/core/personas/principal-engineer.md +119 -0
- package/modules/radar/src/core/personas/reality-gap-analyst.md +111 -0
- package/modules/radar/src/core/personas/security-engineer.md +108 -0
- package/modules/radar/src/core/personas/senior-app-engineer.md +111 -0
- package/modules/radar/src/core/personas/sre.md +117 -0
- package/modules/radar/src/core/personas/staff-engineer.md +109 -0
- package/modules/radar/src/core/personas/test-engineer.md +109 -0
- package/modules/radar/src/core/workflows/disagreement-resolution.md +183 -0
- package/modules/radar/src/core/workflows/phase-0-context.md +148 -0
- package/modules/radar/src/core/workflows/phase-1-reconnaissance.md +169 -0
- package/modules/radar/src/core/workflows/phase-2-domain-audits.md +190 -0
- package/modules/radar/src/core/workflows/phase-3-cross-domain.md +177 -0
- package/modules/radar/src/core/workflows/phase-4-adversarial-review.md +165 -0
- package/modules/radar/src/core/workflows/phase-5-report.md +189 -0
- package/modules/radar/src/core/workflows/phase-checkpoint.md +222 -0
- package/modules/radar/src/core/workflows/session-handoff.md +152 -0
- package/modules/radar/src/domains/00-context.md +201 -0
- package/modules/radar/src/domains/01-architecture.md +248 -0
- package/modules/radar/src/domains/02-data.md +224 -0
- package/modules/radar/src/domains/03-correctness.md +230 -0
- package/modules/radar/src/domains/04-security.md +274 -0
- package/modules/radar/src/domains/05-compliance.md +228 -0
- package/modules/radar/src/domains/06-testing.md +228 -0
- package/modules/radar/src/domains/07-reliability.md +246 -0
- package/modules/radar/src/domains/08-performance.md +247 -0
- package/modules/radar/src/domains/09-maintainability.md +271 -0
- package/modules/radar/src/domains/10-operability.md +250 -0
- package/modules/radar/src/domains/11-change-risk.md +246 -0
- package/modules/radar/src/domains/12-team-risk.md +221 -0
- package/modules/radar/src/domains/13-risk-synthesis.md +202 -0
- package/modules/radar/src/rules/agent-boundaries.md +78 -0
- package/modules/radar/src/rules/disagreement-protocol.md +76 -0
- package/modules/radar/src/rules/epistemic-hygiene.md +78 -0
- package/modules/radar/src/schemas/confidence.md +185 -0
- package/modules/radar/src/schemas/disagreement.md +238 -0
- package/modules/radar/src/schemas/finding.md +287 -0
- package/modules/radar/src/schemas/report-section.md +150 -0
- package/modules/radar/src/schemas/signal.md +108 -0
- package/modules/radar/src/tools/checkov.md +463 -0
- package/modules/radar/src/tools/git-history.md +581 -0
- package/modules/radar/src/tools/gitleaks.md +447 -0
- package/modules/radar/src/tools/grype.md +611 -0
- package/modules/radar/src/tools/semgrep.md +378 -0
- package/modules/radar/src/tools/sonarqube.md +550 -0
- package/modules/radar/src/tools/syft.md +539 -0
- package/modules/radar/src/tools/trivy.md +439 -0
- package/modules/radar/src/transform/agents/change-risk-modeler.md +24 -0
- package/modules/radar/src/transform/agents/execution-validator.md +24 -0
- package/modules/radar/src/transform/agents/guardrail-generator.md +24 -0
- package/modules/radar/src/transform/agents/pedagogy-agent.md +24 -0
- package/modules/radar/src/transform/agents/remediation-architect.md +24 -0
- package/modules/radar/src/transform/personas/change-risk-modeler.md +95 -0
- package/modules/radar/src/transform/personas/execution-validator.md +95 -0
- package/modules/radar/src/transform/personas/guardrail-generator.md +103 -0
- package/modules/radar/src/transform/personas/pedagogy-agent.md +105 -0
- package/modules/radar/src/transform/personas/remediation-architect.md +95 -0
- package/modules/radar/src/transform/rules/change-risk-rules.md +87 -0
- package/modules/radar/src/transform/rules/safety-governance.md +87 -0
- package/modules/radar/src/transform/schemas/change-risk.md +139 -0
- package/modules/radar/src/transform/schemas/intervention-level.md +207 -0
- package/modules/radar/src/transform/schemas/playbook.md +205 -0
- package/modules/radar/src/transform/schemas/verification-plan.md +134 -0
- package/modules/radar/src/transform/workflows/phase-6-remediation.md +148 -0
- package/modules/radar/src/transform/workflows/phase-7-risk-validation.md +161 -0
- package/modules/radar/src/transform/workflows/phase-8-execution-planning.md +159 -0
- package/modules/radar/src/transform/workflows/transform-safety.md +158 -0
- package/modules/vector/.vector-template/sessions/.gitkeep +0 -0
- package/modules/vector/.vector-template/vector.json +72 -0
- package/modules/vector/AUDIT-CLAUDEMD.md +154 -0
- package/modules/vector/INSTALL.md +185 -0
- package/modules/vector/LICENSE +21 -0
- package/modules/vector/README.md +409 -0
- package/modules/vector/VECTOR-BLOCK.md +57 -0
- package/modules/vector/assets/terminal.svg +68 -0
- package/modules/vector/bin/install.js +455 -0
- package/modules/vector/bin/migrate-v1-to-v2.sh +492 -0
- package/modules/vector/commands/help.md +46 -0
- package/modules/vector/hooks/vector-hook.py +775 -0
- package/modules/vector/mcp/index.js +118 -0
- package/modules/vector/mcp/package.json +10 -0
- package/modules/vector/mcp/tools/decisions.js +269 -0
- package/modules/vector/mcp/tools/domains.js +361 -0
- package/modules/vector/mcp/tools/staging.js +252 -0
- package/modules/vector/mcp/tools/vector-json.js +647 -0
- package/modules/vector/package.json +38 -0
- package/modules/vector/schemas/vector.schema.json +237 -0
- package/package.json +39 -0
- package/shared/branding/branding.js +70 -0
- package/shared/config/defaults.json +59 -0
- package/shared/events/README.md +175 -0
- package/shared/events/event-bus.js +134 -0
- package/shared/events/event_bus.py +255 -0
- package/shared/events/integrations.js +161 -0
- package/shared/events/schemas/audit-complete.schema.json +21 -0
- package/shared/events/schemas/phase-progress.schema.json +23 -0
- package/shared/events/schemas/plan-created.schema.json +21 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Mission Control Insights — On-demand workspace analytics
|
|
4
|
+
Computes velocity, stall detection, blocking analysis, workload, and dependency chains.
|
|
5
|
+
Invoked by /accel:insights slash command via !command injection.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
from datetime import datetime, date
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from collections import defaultdict
|
|
13
|
+
|
|
14
|
+
WORKSPACE = Path(__file__).resolve().parent.parent.parent
|
|
15
|
+
PROJECTS_FILE = WORKSPACE / ".accel/momentum" / "data" / "projects.json"
|
|
16
|
+
WORKSPACE_JSON = WORKSPACE / ".accel/momentum" / "workspace.json"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_json(path):
|
|
20
|
+
try:
|
|
21
|
+
with open(path) as f:
|
|
22
|
+
return json.load(f)
|
|
23
|
+
except (json.JSONDecodeError, OSError):
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def days_ago(iso_str):
|
|
28
|
+
if not iso_str:
|
|
29
|
+
return None
|
|
30
|
+
try:
|
|
31
|
+
s = iso_str.replace("Z", "+00:00")
|
|
32
|
+
if "T" in s:
|
|
33
|
+
d = datetime.fromisoformat(s).date()
|
|
34
|
+
else:
|
|
35
|
+
d = date.fromisoformat(s[:10])
|
|
36
|
+
return (date.today() - d).days
|
|
37
|
+
except (ValueError, TypeError):
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
projects = load_json(PROJECTS_FILE)
|
|
43
|
+
workspace = load_json(WORKSPACE_JSON)
|
|
44
|
+
|
|
45
|
+
if not projects:
|
|
46
|
+
print("ERROR: Cannot read projects.json")
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
|
|
49
|
+
items = projects.get("items", [])
|
|
50
|
+
satellites = (workspace or {}).get("satellites", {})
|
|
51
|
+
|
|
52
|
+
# --- VELOCITY ---
|
|
53
|
+
print("## VELOCITY (Drive Projects)")
|
|
54
|
+
drive_projects = []
|
|
55
|
+
for item in items:
|
|
56
|
+
drive = item.get("drive")
|
|
57
|
+
if drive and drive.get("is_drive_project") and drive.get("phase"):
|
|
58
|
+
lp_age = days_ago(drive.get("last_plan_completed_at") or drive.get("last_update"))
|
|
59
|
+
drive_projects.append({
|
|
60
|
+
"id": item["id"],
|
|
61
|
+
"title": item["title"][:35],
|
|
62
|
+
"phase": f"{drive.get('completed_phases', '?')}/{drive.get('total_phases', '?')}",
|
|
63
|
+
"loop": drive.get("loop_position", "?"),
|
|
64
|
+
"last_plan_age": lp_age,
|
|
65
|
+
"handoff": drive.get("handoff", False),
|
|
66
|
+
"status": item.get("status"),
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
if drive_projects:
|
|
70
|
+
for p in sorted(drive_projects, key=lambda x: (x["last_plan_age"] or 0), reverse=True):
|
|
71
|
+
age_str = f"{p['last_plan_age']}d ago" if p["last_plan_age"] is not None else "never"
|
|
72
|
+
hf = " [HANDOFF]" if (isinstance(p["handoff"], dict) and p["handoff"].get("present")) or p["handoff"] is True else ""
|
|
73
|
+
print(f" {p['id']} {p['title']:35s} Phase {p['phase']:8s} {p['loop']:5s} plan: {age_str}{hf}")
|
|
74
|
+
else:
|
|
75
|
+
print(" No Drive projects found")
|
|
76
|
+
print()
|
|
77
|
+
|
|
78
|
+
# --- STALLS (active projects with plan age > 14d) ---
|
|
79
|
+
print("## STALLS (plan age > 14 days, not completed/deferred)")
|
|
80
|
+
stalls = [p for p in drive_projects
|
|
81
|
+
if p["last_plan_age"] is not None
|
|
82
|
+
and p["last_plan_age"] > 14
|
|
83
|
+
and p["status"] not in ("completed", "deferred", "archived")]
|
|
84
|
+
if stalls:
|
|
85
|
+
for s in sorted(stalls, key=lambda x: x["last_plan_age"], reverse=True):
|
|
86
|
+
print(f" {s['id']} {s['title']:35s} STALLED {s['last_plan_age']}d")
|
|
87
|
+
else:
|
|
88
|
+
print(" No stalls detected")
|
|
89
|
+
print()
|
|
90
|
+
|
|
91
|
+
# --- BLOCKING ANALYSIS ---
|
|
92
|
+
print("## BLOCKING ANALYSIS")
|
|
93
|
+
blocked = [i for i in items if i.get("blocked_by") and i.get("status") not in ("completed", "archived")]
|
|
94
|
+
if blocked:
|
|
95
|
+
# Group by blocker
|
|
96
|
+
blockers = defaultdict(list)
|
|
97
|
+
for item in blocked:
|
|
98
|
+
blockers[item["blocked_by"]].append(item)
|
|
99
|
+
|
|
100
|
+
for blocker, items_blocked in blockers.items():
|
|
101
|
+
rev_items = [i for i in items_blocked if i.get("revenue")]
|
|
102
|
+
rev_str = ""
|
|
103
|
+
if rev_items:
|
|
104
|
+
rev_str = f" | Revenue at risk: {', '.join(i['revenue']['amount'] for i in rev_items)}"
|
|
105
|
+
print(f" Blocker: {blocker}")
|
|
106
|
+
for i in items_blocked:
|
|
107
|
+
print(f" {i['id']} {i['title'][:40]}")
|
|
108
|
+
if rev_str:
|
|
109
|
+
print(f" {rev_str}")
|
|
110
|
+
print()
|
|
111
|
+
else:
|
|
112
|
+
print(" No blocked projects")
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
# --- DEPENDENCIES ---
|
|
116
|
+
print("## CROSS-PROJECT DEPENDENCIES")
|
|
117
|
+
has_deps = [i for i in items if i.get("dependencies")]
|
|
118
|
+
if has_deps:
|
|
119
|
+
for item in has_deps:
|
|
120
|
+
for dep in item["dependencies"]:
|
|
121
|
+
dep_project = next((i for i in items if i["id"] == dep["project_id"]), None)
|
|
122
|
+
dep_title = dep_project["title"][:30] if dep_project else dep["project_id"]
|
|
123
|
+
print(f" {item['id']} {item['title'][:30]} --{dep['type']}--> {dep_title}")
|
|
124
|
+
if dep.get("notes"):
|
|
125
|
+
print(f" Note: {dep['notes']}")
|
|
126
|
+
else:
|
|
127
|
+
print(" No cross-project dependencies defined")
|
|
128
|
+
print()
|
|
129
|
+
|
|
130
|
+
# --- WORKLOAD BY CATEGORY ---
|
|
131
|
+
print("## WORKLOAD BY CATEGORY")
|
|
132
|
+
active = [i for i in items if i.get("status") not in ("backlog", "archived", "completed") and i.get("type") != "initiative"]
|
|
133
|
+
cats = defaultdict(int)
|
|
134
|
+
for item in active:
|
|
135
|
+
cats[item.get("category", "uncategorized")] += 1
|
|
136
|
+
for cat, count in sorted(cats.items(), key=lambda x: -x[1]):
|
|
137
|
+
print(f" {cat}: {count} projects")
|
|
138
|
+
print()
|
|
139
|
+
|
|
140
|
+
# --- REVENUE SUMMARY ---
|
|
141
|
+
print("## REVENUE EXPOSURE")
|
|
142
|
+
rev_projects = [i for i in items if i.get("revenue") and i.get("status") not in ("completed", "archived")]
|
|
143
|
+
if rev_projects:
|
|
144
|
+
for item in rev_projects:
|
|
145
|
+
rev = item["revenue"]
|
|
146
|
+
status = item.get("status", "?")
|
|
147
|
+
blocked_flag = " [BLOCKED]" if item.get("blocked_by") else ""
|
|
148
|
+
print(f" {item['id']} {item['title'][:35]} | {rev['amount']} ({rev['type']}){blocked_flag}")
|
|
149
|
+
else:
|
|
150
|
+
print(" No revenue projects active")
|
|
151
|
+
print()
|
|
152
|
+
|
|
153
|
+
# --- HANDOFFS ---
|
|
154
|
+
print("## PENDING HANDOFFS")
|
|
155
|
+
handoff_sats = [(name, sat) for name, sat in satellites.items() if sat.get("handoff")]
|
|
156
|
+
if handoff_sats:
|
|
157
|
+
for name, sat in handoff_sats:
|
|
158
|
+
phase = sat.get("phase_name", "?")
|
|
159
|
+
print(f" {name}: Phase {phase} — has HANDOFF waiting")
|
|
160
|
+
else:
|
|
161
|
+
print(" No pending handoffs")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
try:
|
|
166
|
+
main()
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print(f"ERROR: {e}")
|
|
169
|
+
sys.exit(0)
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Momentum Hook v2: momentum-pulse-check.py
|
|
4
|
+
Purpose: Workspace health check on session start.
|
|
5
|
+
Reads .accel/momentum/data/state.json (pre-calculated drift, areas, groom config).
|
|
6
|
+
Much simpler than v1 which parsed STATE.md text + computed drift from file mtimes.
|
|
7
|
+
Triggers: UserPromptSubmit (session context)
|
|
8
|
+
Output: <momentum-pulse> workspace health status or groom reminder
|
|
9
|
+
|
|
10
|
+
Drop-in replacement for momentum-pulse-check.py. Swap in settings.json when ready.
|
|
11
|
+
Legacy momentum-pulse-check.py reads STATE.md + workspace.json (unchanged).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
import json
|
|
16
|
+
from datetime import datetime, date
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
HOOK_DIR = Path(__file__).resolve().parent
|
|
20
|
+
WORKSPACE_ROOT = HOOK_DIR.parent.parent
|
|
21
|
+
BASE_DIR = WORKSPACE_ROOT / ".accel/momentum"
|
|
22
|
+
STATE_FILE = BASE_DIR / "data" / "state.json"
|
|
23
|
+
PROJECTS_FILE = BASE_DIR / "data" / "projects.json"
|
|
24
|
+
STAGING_FILE = BASE_DIR / "data" / "staging.json"
|
|
25
|
+
EVENTS_DIR = Path.home() / ".accel" / "events"
|
|
26
|
+
ARCHIVE_DIR = EVENTS_DIR / "archive"
|
|
27
|
+
BACKLOG_DIR = BASE_DIR / "data" / "backlog"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def recalculate_drift(state):
|
|
31
|
+
"""Recalculate drift indicators from live data and update state.json.
|
|
32
|
+
|
|
33
|
+
This ensures drift score is always fresh on session start, not stale
|
|
34
|
+
from the last time momentum_update_drift was manually called.
|
|
35
|
+
"""
|
|
36
|
+
now = date.today()
|
|
37
|
+
|
|
38
|
+
# Calculate indicators from projects.json
|
|
39
|
+
indicators = {
|
|
40
|
+
"active_age_days": 0,
|
|
41
|
+
"backlog_age_days": 0,
|
|
42
|
+
"backlog_past_review": 0,
|
|
43
|
+
"orphaned_sessions": 0,
|
|
44
|
+
"untracked_root_files": 0,
|
|
45
|
+
"stale_satellites": 0,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if PROJECTS_FILE.exists():
|
|
49
|
+
try:
|
|
50
|
+
projects = json.loads(PROJECTS_FILE.read_text())
|
|
51
|
+
items = projects.get("items", [])
|
|
52
|
+
|
|
53
|
+
# Active staleness: max days since update for active/in_progress/blocked/in_review projects
|
|
54
|
+
active_statuses = {"in_progress", "blocked", "in_review", "todo"}
|
|
55
|
+
active_ages = []
|
|
56
|
+
backlog_ages = []
|
|
57
|
+
past_review = 0
|
|
58
|
+
|
|
59
|
+
for item in items:
|
|
60
|
+
if item.get("type") != "project":
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
updated = item.get("updated_at")
|
|
64
|
+
if updated:
|
|
65
|
+
try:
|
|
66
|
+
updated_date = datetime.fromisoformat(updated).date()
|
|
67
|
+
age = (now - updated_date).days
|
|
68
|
+
except (ValueError, TypeError):
|
|
69
|
+
age = 0
|
|
70
|
+
else:
|
|
71
|
+
age = 0
|
|
72
|
+
|
|
73
|
+
status = item.get("status", "")
|
|
74
|
+
if status in active_statuses:
|
|
75
|
+
active_ages.append(age)
|
|
76
|
+
elif status == "backlog":
|
|
77
|
+
backlog_ages.append(age)
|
|
78
|
+
|
|
79
|
+
# Check review_by dates
|
|
80
|
+
review_by = item.get("review_by")
|
|
81
|
+
if review_by:
|
|
82
|
+
try:
|
|
83
|
+
review_date = date.fromisoformat(review_by)
|
|
84
|
+
if now > review_date:
|
|
85
|
+
past_review += 1
|
|
86
|
+
except (ValueError, TypeError):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
indicators["active_age_days"] = max(active_ages) if active_ages else 0
|
|
90
|
+
indicators["backlog_age_days"] = max(backlog_ages) if backlog_ages else 0
|
|
91
|
+
indicators["backlog_past_review"] = past_review
|
|
92
|
+
|
|
93
|
+
except (json.JSONDecodeError, OSError):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
# Stale satellites: check drive.json timestamps
|
|
97
|
+
satellites = state.get("satellites", {})
|
|
98
|
+
stale_sats = 0
|
|
99
|
+
for name, sat in satellites.items():
|
|
100
|
+
sat_path = WORKSPACE_ROOT / sat.get("path", "") / ".drive" / "drive.json"
|
|
101
|
+
if sat_path.exists():
|
|
102
|
+
try:
|
|
103
|
+
drive = json.loads(sat_path.read_text())
|
|
104
|
+
ts = drive.get("timestamps", {}).get("updated_at")
|
|
105
|
+
if ts:
|
|
106
|
+
updated_date = datetime.fromisoformat(ts).date()
|
|
107
|
+
if (now - updated_date).days > 14:
|
|
108
|
+
stale_sats += 1
|
|
109
|
+
except (json.JSONDecodeError, OSError, ValueError):
|
|
110
|
+
pass
|
|
111
|
+
indicators["stale_satellites"] = stale_sats
|
|
112
|
+
|
|
113
|
+
# Compute score as sum of indicators
|
|
114
|
+
score = sum(v for v in indicators.values() if isinstance(v, (int, float)))
|
|
115
|
+
|
|
116
|
+
# Write back to state
|
|
117
|
+
if "drift" not in state:
|
|
118
|
+
state["drift"] = {}
|
|
119
|
+
state["drift"]["score"] = score
|
|
120
|
+
state["drift"]["indicators"] = indicators
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
state["last_modified"] = datetime.now().isoformat()
|
|
124
|
+
STATE_FILE.write_text(json.dumps(state, indent=2))
|
|
125
|
+
except OSError:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
return state
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _consume_events(event_types=None):
|
|
132
|
+
"""
|
|
133
|
+
Read and archive matching event files from ~/.accel/events/.
|
|
134
|
+
|
|
135
|
+
Same logic as shared/events/event_bus.py consume() — inlined here so
|
|
136
|
+
this hook has zero import dependencies outside the stdlib.
|
|
137
|
+
"""
|
|
138
|
+
consumed = []
|
|
139
|
+
if not EVENTS_DIR.is_dir():
|
|
140
|
+
return consumed
|
|
141
|
+
|
|
142
|
+
ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
|
|
144
|
+
files = sorted(
|
|
145
|
+
f for f in EVENTS_DIR.iterdir()
|
|
146
|
+
if f.is_file() and f.suffix == ".json"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
for filepath in files:
|
|
150
|
+
try:
|
|
151
|
+
event = json.loads(filepath.read_text())
|
|
152
|
+
if event_types and event.get("type") not in event_types:
|
|
153
|
+
continue
|
|
154
|
+
consumed.append({"file": filepath.name, "event": event})
|
|
155
|
+
# Move to archive
|
|
156
|
+
filepath.rename(ARCHIVE_DIR / filepath.name)
|
|
157
|
+
except (json.JSONDecodeError, OSError):
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
return consumed
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _process_pending_events():
|
|
164
|
+
"""
|
|
165
|
+
Consume pending events from the event bus and translate them into
|
|
166
|
+
Momentum state changes and backlog items.
|
|
167
|
+
|
|
168
|
+
Processes:
|
|
169
|
+
- audit-complete -> create backlog item under .accel/momentum/data/backlog/
|
|
170
|
+
- phase-progress -> update state.json currentPhase / phases
|
|
171
|
+
- plan-created -> informational notice in pulse output
|
|
172
|
+
|
|
173
|
+
Returns list of human-readable summary strings for pulse output.
|
|
174
|
+
"""
|
|
175
|
+
summaries = []
|
|
176
|
+
|
|
177
|
+
# ── audit-complete events (Radar -> Momentum) ──
|
|
178
|
+
audit_events = _consume_events(["audit-complete"])
|
|
179
|
+
for item in audit_events:
|
|
180
|
+
ev = item["event"]
|
|
181
|
+
payload = ev.get("payload", {})
|
|
182
|
+
findings = payload.get("findings", 0)
|
|
183
|
+
critical = payload.get("critical", 0)
|
|
184
|
+
report = payload.get("report", "")
|
|
185
|
+
|
|
186
|
+
BACKLOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
187
|
+
backlog_item = {
|
|
188
|
+
"id": f"radar-{int(datetime.now().timestamp() * 1000)}",
|
|
189
|
+
"type": "audit-finding",
|
|
190
|
+
"source": "radar",
|
|
191
|
+
"title": f"Radar found {findings} issue{'s' if findings != 1 else ''} ({critical} critical)",
|
|
192
|
+
"priority": "high" if critical > 0 else "medium",
|
|
193
|
+
"report": report,
|
|
194
|
+
"createdAt": datetime.now().isoformat(),
|
|
195
|
+
"status": "open",
|
|
196
|
+
}
|
|
197
|
+
item_path = BACKLOG_DIR / f"{backlog_item['id']}.json"
|
|
198
|
+
try:
|
|
199
|
+
item_path.write_text(json.dumps(backlog_item, indent=2))
|
|
200
|
+
except OSError:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
summaries.append(
|
|
204
|
+
f"Event: Radar audit — {findings} findings, {critical} critical → backlog item created"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# ── phase-progress events (Drive -> Momentum) ──
|
|
208
|
+
phase_events = _consume_events(["phase-progress"])
|
|
209
|
+
for item in phase_events:
|
|
210
|
+
ev = item["event"]
|
|
211
|
+
payload = ev.get("payload", {})
|
|
212
|
+
phase = payload.get("phase", "unknown")
|
|
213
|
+
status = payload.get("status", "unknown")
|
|
214
|
+
|
|
215
|
+
# Update state.json if it exists
|
|
216
|
+
if STATE_FILE.exists():
|
|
217
|
+
try:
|
|
218
|
+
state = json.loads(STATE_FILE.read_text())
|
|
219
|
+
state["currentPhase"] = phase
|
|
220
|
+
state["lastUpdated"] = datetime.now().isoformat()
|
|
221
|
+
if status == "completed":
|
|
222
|
+
if "phases" not in state:
|
|
223
|
+
state["phases"] = []
|
|
224
|
+
state["phases"].append({
|
|
225
|
+
"phase": phase,
|
|
226
|
+
"completedAt": datetime.now().isoformat(),
|
|
227
|
+
})
|
|
228
|
+
STATE_FILE.write_text(json.dumps(state, indent=2))
|
|
229
|
+
except (json.JSONDecodeError, OSError):
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
summaries.append(f"Event: Drive phase \"{phase}\" → {status}")
|
|
233
|
+
|
|
234
|
+
# ── plan-created events (Ignition -> Drive/Momentum) ──
|
|
235
|
+
plan_events = _consume_events(["plan-created"])
|
|
236
|
+
for item in plan_events:
|
|
237
|
+
ev = item["event"]
|
|
238
|
+
payload = ev.get("payload", {})
|
|
239
|
+
title = payload.get("title", "untitled")
|
|
240
|
+
project_type = payload.get("projectType", "unknown")
|
|
241
|
+
summaries.append(
|
|
242
|
+
f"Event: Ignition plan created — \"{title}\" ({project_type})"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return summaries
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def main():
|
|
249
|
+
# ── Process pending cross-module events ──
|
|
250
|
+
event_summaries = _process_pending_events()
|
|
251
|
+
|
|
252
|
+
if not STATE_FILE.exists():
|
|
253
|
+
# Even without state, report any events that were processed
|
|
254
|
+
if event_summaries:
|
|
255
|
+
print(f"""<momentum-pulse>
|
|
256
|
+
{chr(10).join(event_summaries)}
|
|
257
|
+
</momentum-pulse>""")
|
|
258
|
+
sys.exit(0)
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
state = json.loads(STATE_FILE.read_text())
|
|
262
|
+
except (json.JSONDecodeError, OSError):
|
|
263
|
+
if event_summaries:
|
|
264
|
+
print(f"""<momentum-pulse>
|
|
265
|
+
{chr(10).join(event_summaries)}
|
|
266
|
+
</momentum-pulse>""")
|
|
267
|
+
sys.exit(0)
|
|
268
|
+
|
|
269
|
+
# Self-heal: recalculate drift from live data every session start
|
|
270
|
+
state = recalculate_drift(state)
|
|
271
|
+
|
|
272
|
+
output_parts = []
|
|
273
|
+
|
|
274
|
+
# Include event summaries at the top of pulse output
|
|
275
|
+
output_parts.extend(event_summaries)
|
|
276
|
+
|
|
277
|
+
now = date.today()
|
|
278
|
+
|
|
279
|
+
# Check groom overdue
|
|
280
|
+
groom = state.get("groom", {})
|
|
281
|
+
next_due = groom.get("next_groom_due")
|
|
282
|
+
if next_due:
|
|
283
|
+
try:
|
|
284
|
+
due_date = date.fromisoformat(next_due)
|
|
285
|
+
if now > due_date:
|
|
286
|
+
last_groom = groom.get("last_groom", "unknown")
|
|
287
|
+
overdue_days = (now - due_date).days
|
|
288
|
+
output_parts.append(
|
|
289
|
+
f"Momentum: Workspace groom overdue by {overdue_days} days "
|
|
290
|
+
f"(last groom: {last_groom}). "
|
|
291
|
+
f"Run /momentum:groom to maintain workspace health."
|
|
292
|
+
)
|
|
293
|
+
except ValueError:
|
|
294
|
+
pass
|
|
295
|
+
|
|
296
|
+
# Drift score and stale areas
|
|
297
|
+
drift = state.get("drift", {})
|
|
298
|
+
drift_score = drift.get("score", 0)
|
|
299
|
+
areas = state.get("areas", {})
|
|
300
|
+
stale_areas = [name for name, area in areas.items() if area.get("status") in ("stale", "critical")]
|
|
301
|
+
|
|
302
|
+
if stale_areas:
|
|
303
|
+
output_parts.append(
|
|
304
|
+
f"Momentum drift score: {drift_score} | Stale areas: {', '.join(stale_areas)}"
|
|
305
|
+
)
|
|
306
|
+
elif drift_score == 0:
|
|
307
|
+
last_groom = groom.get("last_groom", "unknown")
|
|
308
|
+
output_parts.append(
|
|
309
|
+
f"Momentum: Drift 0 | Last groom: {last_groom} | All areas current"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Vector hygiene reminder
|
|
313
|
+
vector_hygiene = state.get("vector_hygiene", state.get("carl_hygiene", {}))
|
|
314
|
+
if vector_hygiene.get("proactive", False):
|
|
315
|
+
hygiene_cadence = {"weekly": 7, "bi-weekly": 14, "monthly": 30}.get(
|
|
316
|
+
vector_hygiene.get("cadence", "monthly"), 30
|
|
317
|
+
)
|
|
318
|
+
last_run = vector_hygiene.get("last_run")
|
|
319
|
+
if last_run:
|
|
320
|
+
try:
|
|
321
|
+
last_run_date = date.fromisoformat(last_run)
|
|
322
|
+
days_since = (now - last_run_date).days
|
|
323
|
+
if days_since > hygiene_cadence:
|
|
324
|
+
output_parts.append(
|
|
325
|
+
f"Vector hygiene overdue ({days_since}d since last run). Run /momentum:vector-hygiene"
|
|
326
|
+
)
|
|
327
|
+
except ValueError:
|
|
328
|
+
output_parts.append("Vector hygiene: last_run date invalid. Run /momentum:vector-hygiene")
|
|
329
|
+
else:
|
|
330
|
+
output_parts.append("Vector hygiene never run. Run /momentum:vector-hygiene when ready")
|
|
331
|
+
|
|
332
|
+
# Check staging proposals
|
|
333
|
+
if STAGING_FILE.exists():
|
|
334
|
+
try:
|
|
335
|
+
staging = json.loads(STAGING_FILE.read_text())
|
|
336
|
+
pending = [p for p in staging.get("proposals", []) if p.get("status") == "pending"]
|
|
337
|
+
if pending:
|
|
338
|
+
output_parts[-1] += f" | {len(pending)} staged proposals pending"
|
|
339
|
+
except (json.JSONDecodeError, OSError):
|
|
340
|
+
pass
|
|
341
|
+
|
|
342
|
+
if output_parts:
|
|
343
|
+
print(f"""<momentum-pulse>
|
|
344
|
+
{chr(10).join(output_parts)}
|
|
345
|
+
</momentum-pulse>""")
|
|
346
|
+
|
|
347
|
+
sys.exit(0)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
if __name__ == "__main__":
|
|
351
|
+
main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Momentum Hook: operator.py
|
|
4
|
+
Source: .accel/momentum/operator.json
|
|
5
|
+
Output: <operator> compact identity summary for alignment context
|
|
6
|
+
Controlled by: hook_active field in operator.json (true/false)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
HOOK_DIR = Path(__file__).resolve().parent
|
|
13
|
+
WORKSPACE_ROOT = HOOK_DIR.parent.parent
|
|
14
|
+
DATA_FILE = WORKSPACE_ROOT / ".accel/momentum" / "operator.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
if not DATA_FILE.exists():
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
data = json.loads(DATA_FILE.read_text())
|
|
23
|
+
except (json.JSONDecodeError, IOError):
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
# Check activation flag
|
|
27
|
+
if not data.get("hook_active", False):
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
# Extract high-signal fields
|
|
31
|
+
north_star = data.get("north_star", {}).get("metric", "Not set")
|
|
32
|
+
timeframe = data.get("north_star", {}).get("timeframe", "")
|
|
33
|
+
deep_why = data.get("deep_why", {}).get("statement", "Not set")
|
|
34
|
+
values = [v.get("value", "") for v in data.get("key_values", {}).get("values", [])]
|
|
35
|
+
vision = data.get("surface_vision", {}).get("summary", "Not set")
|
|
36
|
+
pitch = data.get("elevator_pitch", {}).get("pitch", "Not set")
|
|
37
|
+
|
|
38
|
+
values_str = ", ".join(values) if values else "Not set"
|
|
39
|
+
star_str = f"{north_star} ({timeframe})" if timeframe else north_star
|
|
40
|
+
|
|
41
|
+
output = f"""<operator>
|
|
42
|
+
North Star: {star_str}
|
|
43
|
+
Deep Why: {deep_why}
|
|
44
|
+
Values: {values_str}
|
|
45
|
+
Vision: {vision}
|
|
46
|
+
Pitch: {pitch}
|
|
47
|
+
</operator>"""
|
|
48
|
+
|
|
49
|
+
print(output)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: psmm-injector.py
|
|
4
|
+
Purpose: Per-Session Meta Memory — inject ephemeral session observations
|
|
5
|
+
into every prompt so they stay hot in long sessions (1M window).
|
|
6
|
+
|
|
7
|
+
Uses a single psmm.json file with session-keyed entries.
|
|
8
|
+
Each session gets its own array keyed by Claude Code session UUID.
|
|
9
|
+
Stale sessions are NOT auto-cleaned — that's the operator's job
|
|
10
|
+
via Vector hygiene / Momentum drift detection.
|
|
11
|
+
|
|
12
|
+
Triggers: UserPromptSubmit
|
|
13
|
+
Output: Current session's PSMM entries as system context, or silent if empty.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
import json
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
HOOK_DIR = Path(__file__).resolve().parent
|
|
21
|
+
WORKSPACE_ROOT = HOOK_DIR.parent.parent
|
|
22
|
+
PSMM_FILE = WORKSPACE_ROOT / ".accel/momentum" / "data" / "psmm.json"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
# Get session_id from hook input
|
|
27
|
+
try:
|
|
28
|
+
input_data = json.loads(sys.stdin.read())
|
|
29
|
+
session_id = input_data.get("session_id", "")
|
|
30
|
+
except (json.JSONDecodeError, OSError):
|
|
31
|
+
session_id = ""
|
|
32
|
+
|
|
33
|
+
if not session_id or not PSMM_FILE.exists():
|
|
34
|
+
sys.exit(0)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
data = json.loads(PSMM_FILE.read_text())
|
|
38
|
+
except (json.JSONDecodeError, OSError):
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
sessions = data.get("sessions", {})
|
|
42
|
+
session = sessions.get(session_id)
|
|
43
|
+
|
|
44
|
+
if not session or not session.get("entries"):
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
47
|
+
# Build output from this session's entries
|
|
48
|
+
entries = session["entries"]
|
|
49
|
+
lines = []
|
|
50
|
+
for entry in entries:
|
|
51
|
+
entry_type = entry.get("type", "NOTE")
|
|
52
|
+
text = entry.get("text", "")
|
|
53
|
+
timestamp = entry.get("timestamp", "")
|
|
54
|
+
lines.append(f"- [{timestamp}] {entry_type}: {text}")
|
|
55
|
+
|
|
56
|
+
if lines:
|
|
57
|
+
created = session.get("created", "unknown")
|
|
58
|
+
count = len(entries)
|
|
59
|
+
print(f"""<psmm session="{session_id[:8]}" entries="{count}" created="{created}">
|
|
60
|
+
{chr(10).join(lines)}
|
|
61
|
+
</psmm>""")
|
|
62
|
+
|
|
63
|
+
sys.exit(0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
main()
|