@polymorphism-tech/morph-spec 4.5.0 → 4.7.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 +77 -56
- package/README.md +394 -700
- package/docs/ARCHITECTURE.md +331 -0
- package/docs/CHEATSHEET.md +221 -0
- package/docs/COMMAND-FLOWS.md +368 -0
- package/docs/QUICKSTART.md +212 -0
- package/docs/examples/order-management/contracts.cs +84 -0
- package/docs/examples/order-management/proposal.md +24 -0
- package/docs/examples/order-management/spec.md +162 -0
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +1153 -0
- package/docs/plans/2026-02-23-ddd-nextsteps.md +682 -0
- package/docs/plans/2026-02-23-infra-architect-refactor.md +437 -0
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +156 -0
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +1254 -0
- package/docs/plans/2026-02-23-nextjs-standards-design.md +149 -0
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +1846 -0
- package/framework/{skills/level-2-domains → agents}/README.md +14 -14
- package/framework/{skills/level-2-domains → agents}/ai-agents/ai-system-architect.md +1 -4
- package/framework/{skills/level-2-domains → agents}/architecture/po-pm-advisor.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/prompt-engineer.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/seo-growth-hacker.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/standards-architect.md +159 -162
- package/framework/agents/backend/api-designer.md +103 -0
- package/framework/{skills/level-2-domains → agents}/backend/dotnet-senior.md +1 -2
- package/framework/agents/backend/ef-modeler.md +119 -0
- package/framework/{skills/level-2-domains → agents}/backend/hangfire-orchestrator.md +1 -4
- package/framework/{skills/level-2-domains → agents}/backend/ms-agent-expert.md +1 -4
- package/framework/{skills/level-2-domains → agents}/frontend/blazor-builder.md +1 -4
- package/framework/agents/frontend/nextjs-expert.md +118 -0
- package/framework/{skills/level-2-domains → agents}/frontend/ui-ux-designer.md +1 -2
- package/framework/{skills/level-2-domains → agents}/infrastructure/azure-architect.md +147 -148
- package/framework/{skills/level-2-domains → agents}/infrastructure/azure-deploy-specialist.md +1 -2
- package/framework/{skills/level-2-domains → agents}/infrastructure/bicep-architect.md +1 -4
- package/framework/{skills/level-2-domains → agents}/infrastructure/container-specialist.md +1 -4
- package/framework/{skills/level-2-domains → agents}/infrastructure/devops-engineer.md +1 -4
- package/framework/agents/infrastructure/infra-architect.md +45 -0
- package/framework/{skills/level-2-domains → agents}/integrations/asaas-financial.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/azure-identity.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/clerk-auth.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/hangfire-integration.md +1 -2
- package/framework/{skills/level-2-domains → agents}/integrations/resend-email.md +1 -4
- package/framework/{skills/level-2-domains → agents}/quality/code-analyzer.md +1 -4
- package/framework/{skills/level-2-domains → agents}/quality/testing-specialist.md +1 -4
- package/framework/agents.json +1145 -278
- package/framework/hooks/claude-code/statusline.py +384 -85
- package/framework/hooks/shared/phase-utils.js +129 -129
- package/framework/rules/frontend-standards.md +0 -3
- package/framework/rules/nextjs-standards.md +17 -0
- package/framework/skills/README.md +66 -0
- package/framework/skills/level-0-meta/{brainstorming.md → brainstorming/SKILL.md} +3 -1
- package/framework/skills/level-0-meta/brainstorming/references/proposal-example.md +138 -0
- package/framework/skills/level-0-meta/{code-review.md → code-review/SKILL.md} +3 -2
- package/framework/skills/level-0-meta/code-review/references/review-example.md +164 -0
- package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +121 -0
- package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +147 -0
- package/framework/skills/level-0-meta/code-review-nextjs/references/review-example-nextjs.md +254 -0
- package/framework/skills/level-0-meta/{morph-checklist.md → morph-checklist/SKILL.md} +2 -5
- package/framework/skills/{level-1-workflows/morph-replicate.md → level-0-meta/morph-replicate/SKILL.md} +6 -7
- package/framework/skills/level-0-meta/{simulation-checklist.md → simulation-checklist/SKILL.md} +3 -6
- package/framework/skills/level-0-meta/{tool-usage-guide.md → tool-usage-guide/SKILL.md} +4 -5
- package/framework/skills/level-0-meta/{verification-before-completion.md → verification-before-completion/SKILL.md} +3 -1
- package/framework/skills/level-0-meta/verification-before-completion/scripts/check-phase-outputs.mjs +110 -0
- package/framework/skills/level-1-workflows/{phase-clarify.md → phase-clarify/SKILL.md} +3 -3
- package/framework/skills/level-1-workflows/phase-clarify/references/clarifications-example.md +117 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis.md → phase-codebase-analysis/SKILL.md} +2 -3
- package/framework/skills/level-1-workflows/{phase-design.md → phase-design/SKILL.md} +46 -182
- package/framework/skills/level-1-workflows/phase-design/references/spec-example.md +253 -0
- package/framework/skills/level-1-workflows/{phase-implement.md → phase-implement/SKILL.md} +3 -3
- package/framework/skills/level-1-workflows/phase-implement/references/recap-example.md +132 -0
- package/framework/skills/level-1-workflows/{phase-setup.md → phase-setup/SKILL.md} +2 -3
- package/framework/skills/level-1-workflows/{phase-tasks.md → phase-tasks/SKILL.md} +42 -3
- package/framework/skills/level-1-workflows/phase-tasks/references/tasks-example.md +231 -0
- package/framework/skills/level-1-workflows/phase-tasks/scripts/validate-tasks.mjs +112 -0
- package/framework/skills/level-1-workflows/{phase-uiux.md → phase-uiux/SKILL.md} +2 -3
- package/framework/standards/STANDARDS.json +121 -0
- package/framework/standards/architecture/ddd/bounded-contexts.md +105 -0
- package/framework/standards/architecture/ddd/complexity-levels.md +108 -0
- package/framework/standards/architecture/ddd/ubiquitous-language.md +58 -0
- package/framework/standards/frontend/nextjs/app-router.md +123 -0
- package/framework/standards/frontend/nextjs/components.md +132 -0
- package/framework/standards/frontend/nextjs/data-fetching.md +126 -0
- package/framework/standards/frontend/nextjs/forms.md +128 -0
- package/framework/standards/frontend/nextjs/naming-conventions.md +67 -0
- package/framework/standards/frontend/nextjs/project-structure.md +102 -0
- package/framework/standards/frontend/nextjs/state-management.md +72 -0
- package/framework/standards/frontend/nextjs/testing.md +111 -0
- package/framework/templates/REGISTRY.json +538 -142
- package/framework/templates/code/dotnet/contracts/contracts-level1.cs +69 -0
- package/framework/templates/code/dotnet/contracts/contracts-level2.cs +86 -0
- package/framework/templates/code/dotnet/contracts/contracts-level3.cs +41 -0
- package/framework/templates/docs/spec.md +49 -0
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -0
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -0
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -0
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -0
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -0
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -0
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -0
- package/framework/templates/project-structure/dotnet-ddd.md +70 -0
- package/framework/workflows/docs/enforcement-pipeline.md +2 -1
- package/package.json +1 -1
- package/scripts/scan-nextjs.mjs +169 -0
- package/src/commands/project/doctor.js +52 -1
- package/src/commands/project/init.js +19 -65
- package/src/commands/project/update.js +7 -63
- package/src/lib/detectors/claude-config-detector.js +1 -3
- package/src/lib/standards/standards-context-injector.js +5 -0
- package/src/lib/validators/nextjs/index.js +6 -0
- package/src/lib/validators/nextjs/next-component-validator.js +181 -0
- package/src/lib/validators/validation-runner.js +5 -0
- package/src/utils/agents-installer.js +16 -4
- package/src/utils/skills-installer.js +59 -15
- package/.morph/.morphversion +0 -5
- package/.morph/analytics/threads-log.jsonl +0 -44
- package/.morph/config/config.json +0 -8
- package/.morph/context/README.md +0 -17
- package/.morph/framework/agents.json +0 -948
- package/.morph/framework/standards/STANDARDS.json +0 -812
- package/.morph/framework/standards/ai-agents/blazor-ui.md +0 -364
- package/.morph/framework/standards/ai-agents/production.md +0 -415
- package/.morph/framework/standards/ai-agents/setup.md +0 -418
- package/.morph/framework/standards/ai-agents/team-orchestration.md +0 -479
- package/.morph/framework/standards/ai-agents/workflows.md +0 -354
- package/.morph/framework/standards/architecture/ddd/aggregates.md +0 -120
- package/.morph/framework/standards/architecture/ddd/entities.md +0 -99
- package/.morph/framework/standards/architecture/ddd/value-objects.md +0 -124
- package/.morph/framework/standards/backend/api/minimal-api.md +0 -494
- package/.morph/framework/standards/backend/api/rest.md +0 -492
- package/.morph/framework/standards/backend/api/validation.md +0 -88
- package/.morph/framework/standards/backend/authentication/passkeys.md +0 -428
- package/.morph/framework/standards/backend/database/ef-core.md +0 -199
- package/.morph/framework/standards/backend/database/migrations.md +0 -393
- package/.morph/framework/standards/backend/database/postgresql/database.md +0 -352
- package/.morph/framework/standards/backend/database/repository-patterns.md +0 -528
- package/.morph/framework/standards/backend/database/vector-search-rag.md +0 -541
- package/.morph/framework/standards/backend/dotnet/async.md +0 -366
- package/.morph/framework/standards/backend/dotnet/core.md +0 -117
- package/.morph/framework/standards/backend/dotnet/di.md +0 -439
- package/.morph/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
- package/.morph/framework/standards/backend/integrations/asaas/asaas-api.md +0 -216
- package/.morph/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
- package/.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +0 -350
- package/.morph/framework/standards/backend/integrations/resend/resend-email.md +0 -385
- package/.morph/framework/standards/context/analytics.md +0 -96
- package/.morph/framework/standards/context/bundles.md +0 -110
- package/.morph/framework/standards/context/priming.md +0 -78
- package/.morph/framework/standards/core/architecture.md +0 -185
- package/.morph/framework/standards/core/coding.md +0 -214
- package/.morph/framework/standards/core/git-branching-strategy.md +0 -403
- package/.morph/framework/standards/core/git.md +0 -185
- package/.morph/framework/standards/core/testing.md +0 -295
- package/.morph/framework/standards/data/nosql/blob-storage.md +0 -102
- package/.morph/framework/standards/data/nosql/cache/redis.md +0 -97
- package/.morph/framework/standards/data/nosql/cosmos-db.md +0 -118
- package/.morph/framework/standards/data/vector-search/azure-ai-search.md +0 -121
- package/.morph/framework/standards/data/vector-search/rag-chunking.md +0 -104
- package/.morph/framework/standards/frontend/blazor/design-checklist.md +0 -222
- package/.morph/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
- package/.morph/framework/standards/frontend/blazor/fluent-ui.md +0 -137
- package/.morph/framework/standards/frontend/blazor/html-conversion.md +0 -184
- package/.morph/framework/standards/frontend/blazor/lifecycle.md +0 -195
- package/.morph/framework/standards/frontend/blazor/pitfalls.md +0 -198
- package/.morph/framework/standards/frontend/blazor/state.md +0 -191
- package/.morph/framework/standards/frontend/design-system/animations.md +0 -151
- package/.morph/framework/standards/frontend/design-system/naming.md +0 -64
- package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +0 -198
- package/.morph/framework/standards/infrastructure/azure/azure.md +0 -624
- package/.morph/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
- package/.morph/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
- package/.morph/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
- package/.morph/framework/standards/infrastructure/azure/services/functions.md +0 -486
- package/.morph/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
- package/.morph/framework/standards/infrastructure/azure/services/storage.md +0 -407
- package/.morph/framework/standards/infrastructure/docker/easypanel-deploy.md +0 -196
- package/.morph/framework/standards/infrastructure/supabase/mcp-setup.md +0 -252
- package/.morph/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
- package/.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
- package/.morph/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
- package/.morph/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
- package/.morph/framework/standards/integration/api/graphql.md +0 -91
- package/.morph/framework/standards/integration/api/grpc.md +0 -114
- package/.morph/framework/standards/integration/api/rest-design.md +0 -95
- package/.morph/framework/standards/integration/event-driven/cqrs.md +0 -101
- package/.morph/framework/standards/integration/event-driven/event-sourcing.md +0 -124
- package/.morph/framework/standards/integration/event-driven/service-bus.md +0 -95
- package/.morph/framework/standards/integration/mcp/mcp-tools.md +0 -384
- package/.morph/framework/standards/observability/logging.md +0 -131
- package/.morph/framework/standards/observability/metrics.md +0 -121
- package/.morph/framework/standards/observability/monitoring.md +0 -114
- package/.morph/framework/standards/observability/tracing.md +0 -132
- package/.morph/framework/standards/workflows/parallel-execution.md +0 -112
- package/.morph/framework/standards/workflows/thread-management.md +0 -113
- package/.morph/framework/templates/.idea/morph-templates.xml +0 -92
- package/.morph/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/.morph/framework/templates/IDE-SNIPPETS.md +0 -266
- package/.morph/framework/templates/README.md +0 -814
- package/.morph/framework/templates/REGISTRY.json +0 -1492
- package/.morph/framework/templates/code/dotnet/backend/repository.cs +0 -141
- package/.morph/framework/templates/code/dotnet/backend/service.cs +0 -139
- package/.morph/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
- package/.morph/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/README.md +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
- package/.morph/framework/templates/code/dotnet/contracts/contracts.cs +0 -217
- package/.morph/framework/templates/code/dotnet/contracts/contracts.cs.hbs +0 -172
- package/.morph/framework/templates/code/dotnet/database/migration.cs +0 -83
- package/.morph/framework/templates/code/dotnet/frontend/component.razor +0 -239
- package/.morph/framework/templates/code/dotnet/jobs/agent.cs +0 -163
- package/.morph/framework/templates/code/dotnet/jobs/job.cs +0 -171
- package/.morph/framework/templates/code/dotnet/test.cs +0 -239
- package/.morph/framework/templates/code/sql/rls-policy.sql +0 -57
- package/.morph/framework/templates/code/sql/supabase-migration.sql +0 -100
- package/.morph/framework/templates/code/sql/supabase-migration.template.sql +0 -113
- package/.morph/framework/templates/code/typescript/contracts.ts +0 -168
- package/.morph/framework/templates/context/CONTEXT-FEATURE.md +0 -276
- package/.morph/framework/templates/context/CONTEXT.md +0 -181
- package/.morph/framework/templates/docs/clarifications.md +0 -253
- package/.morph/framework/templates/docs/onboarding.md +0 -123
- package/.morph/framework/templates/docs/proposal.md +0 -182
- package/.morph/framework/templates/docs/schema-analysis.md +0 -119
- package/.morph/framework/templates/docs/spec.md +0 -149
- package/.morph/framework/templates/docs/ui-components.md +0 -124
- package/.morph/framework/templates/docs/ui-design-system.md +0 -76
- package/.morph/framework/templates/docs/ui-flows.md +0 -167
- package/.morph/framework/templates/docs/ui-mockups.md +0 -98
- package/.morph/framework/templates/examples/design-system-examples.md +0 -357
- package/.morph/framework/templates/examples/spec-examples.md +0 -90
- package/.morph/framework/templates/feature/decisions.md +0 -187
- package/.morph/framework/templates/feature/recap.md +0 -146
- package/.morph/framework/templates/feature/tasks.md +0 -199
- package/.morph/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
- package/.morph/framework/templates/infrastructure/azure/README.md +0 -286
- package/.morph/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
- package/.morph/framework/templates/infrastructure/azure/app-service.bicep +0 -164
- package/.morph/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
- package/.morph/framework/templates/infrastructure/azure/container-app.bicep +0 -156
- package/.morph/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
- package/.morph/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
- package/.morph/framework/templates/infrastructure/azure/deploy.sh +0 -208
- package/.morph/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
- package/.morph/framework/templates/infrastructure/azure/main.bicep +0 -189
- package/.morph/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
- package/.morph/framework/templates/infrastructure/azure/storage.bicep +0 -106
- package/.morph/framework/templates/infrastructure/docker/Dockerfile.template +0 -58
- package/.morph/framework/templates/infrastructure/docker/docker-compose.template.yml +0 -67
- package/.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +0 -38
- package/.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +0 -48
- package/.morph/framework/templates/infrastructure/docker/easypanel.template.json +0 -54
- package/.morph/framework/templates/infrastructure/github/README.md +0 -593
- package/.morph/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
- package/.morph/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +0 -45
- package/.morph/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +0 -27
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
- package/.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +0 -59
- package/.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +0 -39
- package/.morph/framework/templates/integrations/asaas-client.cs +0 -387
- package/.morph/framework/templates/integrations/asaas-webhook.cs +0 -351
- package/.morph/framework/templates/integrations/azure-identity-config.cs +0 -288
- package/.morph/framework/templates/integrations/clerk-config.cs +0 -258
- package/.morph/framework/templates/meta-prompts/fusion/fusion-agent.md +0 -76
- package/.morph/framework/templates/meta-prompts/fusion/fusion-aggregator.md +0 -100
- package/.morph/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/.morph/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/.morph/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
- package/.morph/framework/templates/meta-prompts/squad-leaders/backend-squad.md +0 -90
- package/.morph/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +0 -126
- package/.morph/framework/templates/meta-prompts/squad-leaders/squad-leader.md +0 -43
- package/.morph/framework/templates/meta-prompts/validators/checkpoint-validator.md +0 -107
- package/.morph/framework/templates/meta-prompts/validators/pre-commit-validator.md +0 -95
- package/.morph/framework/templates/saas/subscription.cs +0 -347
- package/.morph/framework/templates/saas/tenant.cs +0 -338
- package/.morph/framework/templates/state.template.json +0 -17
- package/.morph/framework/templates/ui/FluentDesignTheme.cs +0 -149
- package/.morph/framework/templates/ui/MudTheme.cs +0 -281
- package/.morph/framework/templates/ui/design-system.css +0 -226
- package/.morph/logs/tool-failures.log +0 -51
- package/.morph/memory/pre-compact-2026-02-22T17-01-01-658Z.json +0 -16
- package/.morph/state.json +0 -48
- package/framework/skills/level-2-domains/backend/api-designer.md +0 -66
- package/framework/skills/level-2-domains/backend/ef-modeler.md +0 -65
- package/framework/skills/level-2-domains/frontend/nextjs-expert.md +0 -161
- package/framework/skills/level-3-technologies/README.md +0 -7
- package/framework/skills/level-4-patterns/README.md +0 -7
- package/framework/templates/code/dotnet/contracts/contracts.cs +0 -217
- package/framework/templates/code/dotnet/contracts/contracts.cs.hbs +0 -172
|
@@ -8,7 +8,10 @@ import json
|
|
|
8
8
|
import os
|
|
9
9
|
import subprocess
|
|
10
10
|
import time
|
|
11
|
+
import re
|
|
12
|
+
import hashlib
|
|
11
13
|
from pathlib import Path
|
|
14
|
+
from datetime import datetime, timezone
|
|
12
15
|
|
|
13
16
|
# Ensure UTF-8 output on Windows (stdout defaults to CP1252 otherwise)
|
|
14
17
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -27,10 +30,52 @@ GRAY = '\033[90m'
|
|
|
27
30
|
WHITE = '\033[97m'
|
|
28
31
|
|
|
29
32
|
|
|
33
|
+
# ── MORPH framework constants (derived from phases.json / trust-manager.js) ──
|
|
34
|
+
|
|
35
|
+
# Ordered core phases for pipeline mini-map (5 positions, optional phases mapped)
|
|
36
|
+
# uiux maps to position 2 (same slot as design — they're mutually exclusive in practice)
|
|
37
|
+
PHASE_POSITIONS = {
|
|
38
|
+
'proposal': 1, 'setup': 1,
|
|
39
|
+
'uiux': 2, 'design': 2,
|
|
40
|
+
'clarify': 3,
|
|
41
|
+
'tasks': 4,
|
|
42
|
+
'implement': 5, 'sync': 5,
|
|
43
|
+
}
|
|
44
|
+
PHASE_ABBREV = {
|
|
45
|
+
'proposal': 'prop', 'setup': 'setup',
|
|
46
|
+
'uiux': 'ui', 'design': 'design',
|
|
47
|
+
'clarify': 'clarify', 'tasks': 'tasks',
|
|
48
|
+
'implement': 'impl', 'sync': 'sync',
|
|
49
|
+
}
|
|
50
|
+
PIPELINE_TOTAL = 5
|
|
51
|
+
|
|
52
|
+
# Approval gates per phase (from phases.json pausePoints)
|
|
53
|
+
PHASE_GATES = {
|
|
54
|
+
'proposal': 'proposal',
|
|
55
|
+
'uiux': 'uiux',
|
|
56
|
+
'design': 'design',
|
|
57
|
+
'tasks': 'tasks',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Trust level thresholds and badges (from trust-manager.js)
|
|
61
|
+
# passRate >= threshold → level
|
|
62
|
+
TRUST_LEVELS = [
|
|
63
|
+
(0.95, 'maximum', GREEN + BOLD, '◆◆◆◆'),
|
|
64
|
+
(0.90, 'high', GREEN, '◆◆◆○'),
|
|
65
|
+
(0.80, 'medium', YELLOW, '◆◆○○'),
|
|
66
|
+
(0.00, 'low', RED, '◆○○○'),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
CHECKPOINT_FREQUENCY = 3 # matches llm-interaction.json default
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ── General helpers ──────────────────────────────────────────────────────────
|
|
73
|
+
|
|
30
74
|
def ctx_color(pct):
|
|
31
|
-
|
|
75
|
+
"""Color based on context usage. 80% = Claude's auto-compact threshold."""
|
|
76
|
+
if pct < 60:
|
|
32
77
|
return GREEN
|
|
33
|
-
if pct <
|
|
78
|
+
if pct < 80:
|
|
34
79
|
return YELLOW
|
|
35
80
|
return RED
|
|
36
81
|
|
|
@@ -42,13 +87,113 @@ def progress_bar(pct, width=8):
|
|
|
42
87
|
|
|
43
88
|
|
|
44
89
|
def format_tokens(n):
|
|
90
|
+
if n >= 1_000_000:
|
|
91
|
+
return f"{n / 1_000_000:.1f}m"
|
|
45
92
|
if n >= 1000:
|
|
46
93
|
return f"{n // 1000}k"
|
|
47
94
|
return str(n)
|
|
48
95
|
|
|
49
96
|
|
|
97
|
+
# ── MORPH feature helpers ────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
def calculate_trust(checkpoints):
|
|
100
|
+
"""Return (level_str, color, badge, pass_rate) from checkpoint array."""
|
|
101
|
+
if not checkpoints:
|
|
102
|
+
return 'low', RED, '○○○○', 0.0
|
|
103
|
+
total = len(checkpoints)
|
|
104
|
+
passed = sum(1 for c in checkpoints if c.get('passed'))
|
|
105
|
+
rate = passed / total
|
|
106
|
+
for threshold, level, color, badge in TRUST_LEVELS:
|
|
107
|
+
if rate >= threshold:
|
|
108
|
+
return level, color, badge, rate
|
|
109
|
+
return 'low', RED, '○○○○', rate
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_phase_minimap(phase):
|
|
113
|
+
"""Return colored dot strip + phase abbrev, e.g. '●►○○○ design'."""
|
|
114
|
+
pos = PHASE_POSITIONS.get(phase)
|
|
115
|
+
if pos is None:
|
|
116
|
+
return None
|
|
117
|
+
dots = ''
|
|
118
|
+
for i in range(1, PIPELINE_TOTAL + 1):
|
|
119
|
+
if i < pos:
|
|
120
|
+
dots += f"{GREEN}●{R}"
|
|
121
|
+
elif i == pos:
|
|
122
|
+
dots += f"{CYAN}►{R}"
|
|
123
|
+
else:
|
|
124
|
+
dots += f"{GRAY}○{R}"
|
|
125
|
+
abbrev = PHASE_ABBREV.get(phase, phase)
|
|
126
|
+
return f"{dots} {CYAN}{abbrev}{R}"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_checkpoint_countdown(tasks_done):
|
|
130
|
+
"""Tasks remaining until next checkpoint fires (frequency=3)."""
|
|
131
|
+
if tasks_done <= 0:
|
|
132
|
+
return None
|
|
133
|
+
remaining = CHECKPOINT_FREQUENCY - (tasks_done % CHECKPOINT_FREQUENCY)
|
|
134
|
+
return 0 if remaining == CHECKPOINT_FREQUENCY else remaining
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_next_gate(phase, approval_gates):
|
|
138
|
+
"""Return the upcoming gate for this phase if not yet triggered in state."""
|
|
139
|
+
gate_id = PHASE_GATES.get(phase)
|
|
140
|
+
if not gate_id:
|
|
141
|
+
return None
|
|
142
|
+
# If the gate already appears in approvalGates (approved or pending),
|
|
143
|
+
# it's either done or already shown as pending — don't duplicate.
|
|
144
|
+
if gate_id in (approval_gates or {}):
|
|
145
|
+
return None
|
|
146
|
+
return gate_id
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def get_all_active_features(cwd):
|
|
150
|
+
"""Return list of all in_progress features with enriched MORPH metadata."""
|
|
151
|
+
state_path = Path(cwd) / '.morph' / 'state.json'
|
|
152
|
+
if not state_path.exists():
|
|
153
|
+
return []
|
|
154
|
+
try:
|
|
155
|
+
state = json.loads(state_path.read_text())
|
|
156
|
+
features = state.get('features', {})
|
|
157
|
+
result = []
|
|
158
|
+
for name, feat in features.items():
|
|
159
|
+
if feat.get('status') != 'in_progress':
|
|
160
|
+
continue
|
|
161
|
+
phase = feat.get('phase', '?')
|
|
162
|
+
tasks = feat.get('tasks', {})
|
|
163
|
+
done = tasks.get('completed', 0)
|
|
164
|
+
total = tasks.get('total', 0)
|
|
165
|
+
gates = feat.get('approvalGates', {})
|
|
166
|
+
checkpts = feat.get('checkpoints', [])
|
|
167
|
+
|
|
168
|
+
pending = [g for g, v in gates.items() if not v.get('approved')]
|
|
169
|
+
trust_lvl, trust_color, trust_badge, trust_rate = calculate_trust(checkpts)
|
|
170
|
+
countdown = get_checkpoint_countdown(done)
|
|
171
|
+
next_gate = get_next_gate(phase, gates)
|
|
172
|
+
minimap = get_phase_minimap(phase)
|
|
173
|
+
|
|
174
|
+
result.append({
|
|
175
|
+
'name': name,
|
|
176
|
+
'phase': phase,
|
|
177
|
+
'tasks_done': done,
|
|
178
|
+
'tasks_total': total,
|
|
179
|
+
'pending': pending[0] if pending else None,
|
|
180
|
+
'trust_level': trust_lvl,
|
|
181
|
+
'trust_color': trust_color,
|
|
182
|
+
'trust_badge': trust_badge,
|
|
183
|
+
'trust_rate': trust_rate,
|
|
184
|
+
'countdown': countdown,
|
|
185
|
+
'next_gate': next_gate,
|
|
186
|
+
'minimap': minimap,
|
|
187
|
+
})
|
|
188
|
+
return result
|
|
189
|
+
except Exception:
|
|
190
|
+
return []
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ── Git helpers ───────────────────────────────────────────────────────────────
|
|
194
|
+
|
|
50
195
|
def get_git_info(cwd):
|
|
51
|
-
"""Get git branch and
|
|
196
|
+
"""Get git branch and diff stats. Uses a 5s file cache to avoid lag."""
|
|
52
197
|
try:
|
|
53
198
|
cache_file = Path(cwd) / '.morph' / '.git-cache'
|
|
54
199
|
try:
|
|
@@ -64,31 +209,32 @@ def get_git_info(cwd):
|
|
|
64
209
|
cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
|
|
65
210
|
).decode().strip()
|
|
66
211
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
212
|
+
# Diff stats: insertions/deletions from staged + unstaged changes
|
|
213
|
+
ins, dels = 0, 0
|
|
214
|
+
for cmd in [['diff', '--shortstat'], ['diff', '--cached', '--shortstat']]:
|
|
215
|
+
try:
|
|
216
|
+
out = subprocess.check_output(
|
|
217
|
+
['git'] + cmd, cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
|
|
218
|
+
).decode()
|
|
219
|
+
m = re.search(r'(\d+) insertion', out)
|
|
220
|
+
if m:
|
|
221
|
+
ins += int(m.group(1))
|
|
222
|
+
m = re.search(r'(\d+) deletion', out)
|
|
223
|
+
if m:
|
|
224
|
+
dels += int(m.group(1))
|
|
225
|
+
except Exception:
|
|
226
|
+
pass
|
|
75
227
|
|
|
76
228
|
parts = [f"{BLUE} {branch}{R}"]
|
|
77
|
-
if
|
|
78
|
-
parts.append(f"{GREEN}+{
|
|
79
|
-
if modified:
|
|
80
|
-
parts.append(f"{YELLOW}~{modified}{R}")
|
|
81
|
-
if untracked:
|
|
82
|
-
parts.append(f"{GRAY}?{untracked}{R}")
|
|
229
|
+
if ins or dels:
|
|
230
|
+
parts.append(f"{GREEN}+{ins}{R}{GRAY},{R}{RED}-{dels}{R}")
|
|
83
231
|
|
|
84
232
|
result = ' '.join(parts)
|
|
85
|
-
|
|
86
233
|
try:
|
|
87
234
|
cache_file.parent.mkdir(parents=True, exist_ok=True)
|
|
88
235
|
cache_file.write_text(result)
|
|
89
236
|
except Exception:
|
|
90
237
|
pass
|
|
91
|
-
|
|
92
238
|
return result
|
|
93
239
|
except Exception:
|
|
94
240
|
return ""
|
|
@@ -97,71 +243,164 @@ def get_git_info(cwd):
|
|
|
97
243
|
def get_worktree_info(cwd):
|
|
98
244
|
"""Detect if running in a git worktree (not the main worktree)."""
|
|
99
245
|
try:
|
|
100
|
-
|
|
246
|
+
out = subprocess.check_output(
|
|
101
247
|
['git', 'worktree', 'list', '--porcelain'],
|
|
102
248
|
cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
|
|
103
249
|
).decode()
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
current = {}
|
|
107
|
-
for line in worktrees_out.splitlines():
|
|
250
|
+
entries, current = [], {}
|
|
251
|
+
for line in out.splitlines():
|
|
108
252
|
if line.startswith('worktree '):
|
|
109
253
|
if current:
|
|
110
254
|
entries.append(current)
|
|
111
255
|
current = {'path': line.split(' ', 1)[1]}
|
|
112
256
|
elif line.startswith('branch '):
|
|
113
257
|
current['branch'] = line.split(' ', 1)[1]
|
|
114
|
-
|
|
115
258
|
if current:
|
|
116
259
|
entries.append(current)
|
|
117
|
-
|
|
118
260
|
if len(entries) > 1:
|
|
119
|
-
|
|
261
|
+
cwd_r = str(Path(cwd).resolve())
|
|
120
262
|
for entry in entries[1:]:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
branch_ref = entry.get('branch', '')
|
|
124
|
-
branch = branch_ref.replace('refs/heads/', '')
|
|
263
|
+
if str(Path(entry.get('path', '')).resolve()) == cwd_r:
|
|
264
|
+
branch = entry.get('branch', '').replace('refs/heads/', '')
|
|
125
265
|
return f"{MAGENTA}worktree:{branch}{R}"
|
|
126
266
|
except Exception:
|
|
127
267
|
pass
|
|
128
268
|
return ""
|
|
129
269
|
|
|
130
270
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
271
|
+
# ── Transcript / JSONL helpers ────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
def read_transcript_jsonl(path):
|
|
274
|
+
"""Read and parse JSONL transcript file. Returns list of parsed entries."""
|
|
275
|
+
entries = []
|
|
276
|
+
try:
|
|
277
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
278
|
+
for line in f:
|
|
279
|
+
line = line.strip()
|
|
280
|
+
if not line:
|
|
281
|
+
continue
|
|
282
|
+
try:
|
|
283
|
+
entries.append(json.loads(line))
|
|
284
|
+
except Exception:
|
|
285
|
+
pass
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
return entries
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def get_token_metrics(entries):
|
|
292
|
+
"""Sum token usage from all non-sidechain JSONL entries."""
|
|
293
|
+
total_input, total_output, total_cached = 0, 0, 0
|
|
294
|
+
for entry in entries:
|
|
295
|
+
if entry.get('isSidechain') or entry.get('isApiErrorMessage'):
|
|
296
|
+
continue
|
|
297
|
+
usage = (entry.get('message') or {}).get('usage') or {}
|
|
298
|
+
if not usage:
|
|
299
|
+
continue
|
|
300
|
+
total_input += usage.get('input_tokens', 0)
|
|
301
|
+
total_output += usage.get('output_tokens', 0)
|
|
302
|
+
total_cached += (
|
|
303
|
+
usage.get('cache_creation_input_tokens', 0) +
|
|
304
|
+
usage.get('cache_read_input_tokens', 0)
|
|
305
|
+
)
|
|
306
|
+
return {'input': total_input, 'output': total_output, 'cached': total_cached}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def parse_timestamp(ts):
|
|
310
|
+
"""Parse ISO 8601 timestamp to Unix float. Returns None on error."""
|
|
311
|
+
try:
|
|
312
|
+
return datetime.fromisoformat(ts.replace('Z', '+00:00')).timestamp()
|
|
313
|
+
except Exception:
|
|
135
314
|
return None
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def get_session_duration(entries):
|
|
318
|
+
"""Elapsed time since first message. Returns 'Xhr Ym' or None."""
|
|
319
|
+
now = time.time()
|
|
320
|
+
for entry in entries:
|
|
321
|
+
ts = entry.get('timestamp')
|
|
322
|
+
if not ts:
|
|
323
|
+
continue
|
|
324
|
+
t = parse_timestamp(ts)
|
|
325
|
+
if t is None:
|
|
326
|
+
continue
|
|
327
|
+
elapsed_s = now - t
|
|
328
|
+
hours = int(elapsed_s // 3600)
|
|
329
|
+
minutes = int((elapsed_s % 3600) // 60)
|
|
330
|
+
if hours == 0:
|
|
331
|
+
return f"{minutes}m"
|
|
332
|
+
elif minutes == 0:
|
|
333
|
+
return f"{hours}hr"
|
|
334
|
+
else:
|
|
335
|
+
return f"{hours}hr {minutes}m"
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def get_session_name(entries):
|
|
340
|
+
"""Find the most recent /rename title. Returns string or None."""
|
|
341
|
+
for entry in reversed(entries):
|
|
342
|
+
if entry.get('type') == 'custom-title' and entry.get('customTitle'):
|
|
343
|
+
return entry['customTitle']
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def get_block_start(transcript_path, entries):
|
|
348
|
+
"""Find start of current 5-hour billing block. Cached per transcript."""
|
|
349
|
+
h = hashlib.sha256(transcript_path.encode()).hexdigest()[:16]
|
|
350
|
+
cache_dir = Path.home() / '.cache' / 'morph-spec'
|
|
351
|
+
cache_file = cache_dir / f'block-{h}.json'
|
|
352
|
+
now = time.time()
|
|
353
|
+
block_s = 5 * 3600
|
|
354
|
+
|
|
136
355
|
try:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
(
|
|
141
|
-
|
|
142
|
-
if feat.get('status') == 'in_progress'
|
|
143
|
-
]
|
|
144
|
-
if not active:
|
|
145
|
-
return None
|
|
146
|
-
|
|
147
|
-
name, feat = active[0]
|
|
148
|
-
phase = feat.get('phase', '?')
|
|
149
|
-
tasks = feat.get('tasks', {})
|
|
150
|
-
done = tasks.get('completed', 0)
|
|
151
|
-
total = tasks.get('total', 0)
|
|
152
|
-
gates = feat.get('approvalGates', {})
|
|
153
|
-
pending = [g for g, v in gates.items() if not v.get('approved')]
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
'name': name,
|
|
157
|
-
'phase': phase,
|
|
158
|
-
'tasks_done': done,
|
|
159
|
-
'tasks_total': total,
|
|
160
|
-
'pending_approval': pending[0] if pending else None,
|
|
161
|
-
}
|
|
356
|
+
if cache_file.exists():
|
|
357
|
+
cached = json.loads(cache_file.read_text())
|
|
358
|
+
start = cached.get('block_start')
|
|
359
|
+
if start and (now - start) < block_s:
|
|
360
|
+
return start
|
|
162
361
|
except Exception:
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
start = None
|
|
365
|
+
for entry in entries:
|
|
366
|
+
ts = entry.get('timestamp')
|
|
367
|
+
if not ts:
|
|
368
|
+
continue
|
|
369
|
+
t = parse_timestamp(ts)
|
|
370
|
+
if t is None:
|
|
371
|
+
continue
|
|
372
|
+
if (now - t) <= block_s:
|
|
373
|
+
start = t
|
|
374
|
+
break
|
|
375
|
+
|
|
376
|
+
if start is None:
|
|
163
377
|
return None
|
|
164
378
|
|
|
379
|
+
try:
|
|
380
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
381
|
+
cache_file.write_text(json.dumps({'block_start': start}))
|
|
382
|
+
except Exception:
|
|
383
|
+
pass
|
|
384
|
+
return start
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def format_block_timer(block_start):
|
|
388
|
+
"""Format block timer as 'bar Xhr Ym' relative to 5-hour window."""
|
|
389
|
+
now = time.time()
|
|
390
|
+
elapsed_s = max(0.0, now - block_start)
|
|
391
|
+
pct = min(elapsed_s / (5 * 3600) * 100, 100)
|
|
392
|
+
hours = int(elapsed_s // 3600)
|
|
393
|
+
minutes = int((elapsed_s % 3600) // 60)
|
|
394
|
+
if hours == 0:
|
|
395
|
+
time_str = f"{minutes}m"
|
|
396
|
+
elif minutes == 0:
|
|
397
|
+
time_str = f"{hours}hr"
|
|
398
|
+
else:
|
|
399
|
+
time_str = f"{hours}hr {minutes}m"
|
|
400
|
+
return f"{progress_bar(pct, 6)} {time_str}"
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ── Main ──────────────────────────────────────────────────────────────────────
|
|
165
404
|
|
|
166
405
|
def main():
|
|
167
406
|
try:
|
|
@@ -172,43 +411,103 @@ def main():
|
|
|
172
411
|
except Exception:
|
|
173
412
|
sys.exit(0)
|
|
174
413
|
|
|
175
|
-
cwd
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
414
|
+
cwd = data.get('cwd', os.getcwd())
|
|
415
|
+
transcript_path = data.get('transcript_path')
|
|
416
|
+
|
|
417
|
+
# Read JSONL transcript once — shared by session clock, block timer,
|
|
418
|
+
# token metrics, and session name.
|
|
419
|
+
entries = read_transcript_jsonl(transcript_path) if transcript_path else []
|
|
420
|
+
|
|
421
|
+
# ── MORPH feature lines (one line per active feature) ────────────────────
|
|
422
|
+
features = get_all_active_features(cwd)
|
|
423
|
+
for feat in features:
|
|
424
|
+
parts = [f"{CYAN}{BOLD}{feat['name']}{R}"]
|
|
425
|
+
|
|
426
|
+
# Phase pipeline mini-map: ●●►○○ design
|
|
427
|
+
if feat['minimap']:
|
|
428
|
+
parts.append(feat['minimap'])
|
|
429
|
+
|
|
430
|
+
# Task progress bar
|
|
431
|
+
if feat['tasks_total'] > 0:
|
|
432
|
+
pct = feat['tasks_done'] / feat['tasks_total'] * 100
|
|
186
433
|
bar = progress_bar(pct, 6)
|
|
187
|
-
|
|
188
|
-
if morph['pending_approval']:
|
|
189
|
-
parts1.append(f"{YELLOW}⏳ {morph['pending_approval']} pending{R}")
|
|
190
|
-
print(' | '.join(parts1))
|
|
434
|
+
parts.append(f"{GREEN}{bar} {feat['tasks_done']}/{feat['tasks_total']}{R}")
|
|
191
435
|
|
|
192
|
-
|
|
436
|
+
# Checkpoint countdown: how many tasks until next validation fires
|
|
437
|
+
if feat['countdown'] is not None:
|
|
438
|
+
if feat['countdown'] == 0:
|
|
439
|
+
parts.append(f"{GREEN}ckpt!{R}") # just hit checkpoint
|
|
440
|
+
elif feat['countdown'] == 1:
|
|
441
|
+
parts.append(f"{YELLOW}ckpt:1{R}") # 1 task away — heads up
|
|
442
|
+
else:
|
|
443
|
+
parts.append(f"{GRAY}ckpt:{feat['countdown']}{R}")
|
|
444
|
+
|
|
445
|
+
# Trust level badge: ◆◆◆○ high
|
|
446
|
+
tc = feat['trust_color']
|
|
447
|
+
parts.append(f"{tc}{feat['trust_badge']}{R}")
|
|
448
|
+
|
|
449
|
+
# Pending approval gate (blocking — already triggered, not yet approved)
|
|
450
|
+
if feat['pending']:
|
|
451
|
+
parts.append(f"{YELLOW}⏳ {feat['pending']} pending{R}")
|
|
452
|
+
|
|
453
|
+
# Upcoming gate (not yet triggered — reminds what comes at end of phase)
|
|
454
|
+
if feat['next_gate']:
|
|
455
|
+
parts.append(f"{GRAY}→gate:{feat['next_gate']}{R}")
|
|
456
|
+
|
|
457
|
+
print(' | '.join(parts))
|
|
458
|
+
|
|
459
|
+
# ── Session info line (always shown) ─────────────────────────────────────
|
|
193
460
|
parts2 = []
|
|
194
461
|
|
|
462
|
+
# Session name (set via /rename)
|
|
463
|
+
if entries:
|
|
464
|
+
session_name = get_session_name(entries)
|
|
465
|
+
if session_name:
|
|
466
|
+
parts2.append(f"{CYAN}{BOLD}📌 {session_name}{R}")
|
|
467
|
+
|
|
195
468
|
# Model
|
|
196
|
-
model
|
|
469
|
+
model = data.get('model', {})
|
|
197
470
|
model_name = model.get('display_name', model.get('id', ''))
|
|
198
471
|
if model_name:
|
|
199
472
|
short = model_name.replace('Claude ', '').replace(' (claude.ai)', '')
|
|
200
473
|
parts2.append(f"{WHITE}{BOLD}🤖 {short}{R}")
|
|
201
474
|
|
|
202
|
-
#
|
|
475
|
+
# Session clock (elapsed time since first message)
|
|
476
|
+
if entries:
|
|
477
|
+
duration = get_session_duration(entries)
|
|
478
|
+
if duration:
|
|
479
|
+
parts2.append(f"{YELLOW}⏱ {duration}{R}")
|
|
480
|
+
|
|
481
|
+
# Block timer (progress through current 5-hour billing window)
|
|
482
|
+
if entries and transcript_path:
|
|
483
|
+
block_start = get_block_start(transcript_path, entries)
|
|
484
|
+
if block_start is not None:
|
|
485
|
+
parts2.append(f"{YELLOW}blk:{format_block_timer(block_start)}{R}")
|
|
486
|
+
|
|
487
|
+
# Context window (60% = yellow, 80% = red/auto-compact threshold)
|
|
203
488
|
ctx = data.get('context_window', {})
|
|
204
489
|
if ctx:
|
|
205
|
-
used_pct
|
|
206
|
-
cur
|
|
490
|
+
used_pct = ctx.get('used_percentage', 0)
|
|
491
|
+
cur = ctx.get('current_usage', 0)
|
|
207
492
|
total_ctx = ctx.get('context_window_size', 0)
|
|
208
|
-
color
|
|
209
|
-
bar
|
|
210
|
-
toks
|
|
211
|
-
|
|
493
|
+
color = ctx_color(used_pct)
|
|
494
|
+
bar = progress_bar(used_pct, 8)
|
|
495
|
+
toks = f"{format_tokens(cur)}/{format_tokens(total_ctx)}"
|
|
496
|
+
suffix = f" {RED}~cmpct{R}" if used_pct >= 80 else ""
|
|
497
|
+
parts2.append(f"{color}{bar} {used_pct:.0f}%{R} ({toks}){suffix}")
|
|
498
|
+
|
|
499
|
+
# Token breakdown from JSONL (session totals: input / output / cached)
|
|
500
|
+
if entries:
|
|
501
|
+
tok = get_token_metrics(entries)
|
|
502
|
+
tok_parts = []
|
|
503
|
+
if tok['input']:
|
|
504
|
+
tok_parts.append(f"in:{format_tokens(tok['input'])}")
|
|
505
|
+
if tok['output']:
|
|
506
|
+
tok_parts.append(f"out:{format_tokens(tok['output'])}")
|
|
507
|
+
if tok['cached']:
|
|
508
|
+
tok_parts.append(f"↩{format_tokens(tok['cached'])}")
|
|
509
|
+
if tok_parts:
|
|
510
|
+
parts2.append(f"{GRAY}{' '.join(tok_parts)}{R}")
|
|
212
511
|
|
|
213
512
|
# Cost
|
|
214
513
|
cost = data.get('cost', {})
|
|
@@ -221,7 +520,7 @@ def main():
|
|
|
221
520
|
if agent.get('name'):
|
|
222
521
|
parts2.append(f"{BLUE}agent:{agent['name']}{R}")
|
|
223
522
|
|
|
224
|
-
# Git info (
|
|
523
|
+
# Git info (branch + diff stats, 5s cached)
|
|
225
524
|
git = get_git_info(cwd)
|
|
226
525
|
if git:
|
|
227
526
|
parts2.append(git)
|