prjct-cli 1.22.0 → 1.23.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/CHANGELOG.md +147 -0
- package/bin/prjct +30 -13
- package/dist/bin/prjct.mjs +917 -35845
- package/dist/bin/prjct.mjs.map +7 -0
- package/dist/cli/linear.mjs +16 -0
- package/dist/cli/linear.mjs.map +7 -0
- package/dist/templates.json +1 -0
- package/package.json +4 -5
- package/bin/prjct.ts +0 -342
- package/core/__tests__/agentic/analysis-injection.test.ts +0 -377
- package/core/__tests__/agentic/cache-eviction.test.ts +0 -294
- package/core/__tests__/agentic/command-context.test.ts +0 -281
- package/core/__tests__/agentic/command-executor.test.ts +0 -659
- package/core/__tests__/agentic/domain-classifier.test.ts +0 -330
- package/core/__tests__/agentic/injection-validator.test.ts +0 -255
- package/core/__tests__/agentic/memory-system.test.ts +0 -281
- package/core/__tests__/agentic/plan-mode.test.ts +0 -386
- package/core/__tests__/agentic/prompt-assembly.test.ts +0 -298
- package/core/__tests__/agentic/prompt-builder.test.ts +0 -243
- package/core/__tests__/agentic/response-validator.test.ts +0 -263
- package/core/__tests__/agentic/semantic-matching.test.ts +0 -131
- package/core/__tests__/agentic/smart-context.test.ts +0 -372
- package/core/__tests__/agentic/tech-normalizer.test.ts +0 -136
- package/core/__tests__/agentic/token-budget.test.ts +0 -294
- package/core/__tests__/ai-tools/formatters.test.ts +0 -476
- package/core/__tests__/domain/bm25.test.ts +0 -225
- package/core/__tests__/domain/change-propagator.test.ts +0 -100
- package/core/__tests__/domain/fibonacci.test.ts +0 -113
- package/core/__tests__/domain/file-hasher.test.ts +0 -146
- package/core/__tests__/domain/file-ranker.test.ts +0 -169
- package/core/__tests__/domain/git-cochange.test.ts +0 -121
- package/core/__tests__/domain/import-graph.test.ts +0 -156
- package/core/__tests__/domain/velocity.test.ts +0 -623
- package/core/__tests__/infrastructure/performance-tracker.test.ts +0 -328
- package/core/__tests__/schemas/model.test.ts +0 -272
- package/core/__tests__/services/dependency-validator.test.ts +0 -175
- package/core/__tests__/services/hierarchical-agent-resolver.test.ts +0 -359
- package/core/__tests__/services/nested-context-resolver.test.ts +0 -443
- package/core/__tests__/services/project-index.test.ts +0 -355
- package/core/__tests__/services/staleness-checker.test.ts +0 -204
- package/core/__tests__/storage/analysis-storage.test.ts +0 -641
- package/core/__tests__/storage/archive-storage.test.ts +0 -455
- package/core/__tests__/storage/safe-reader.test.ts +0 -262
- package/core/__tests__/storage/sqlite-migration.test.ts +0 -1016
- package/core/__tests__/storage/state-storage-feedback.test.ts +0 -463
- package/core/__tests__/storage/state-storage-history.test.ts +0 -469
- package/core/__tests__/storage/storage-manager.test.ts +0 -383
- package/core/__tests__/storage/subtask-handoff.test.ts +0 -237
- package/core/__tests__/types/fs.test.ts +0 -125
- package/core/__tests__/utils/date-helper.test.ts +0 -449
- package/core/__tests__/utils/output.test.ts +0 -278
- package/core/__tests__/utils/preserve-sections.test.ts +0 -216
- package/core/__tests__/utils/project-commands.test.ts +0 -71
- package/core/__tests__/utils/retry.test.ts +0 -381
- package/core/__tests__/workflow/state-machine.test.ts +0 -216
- package/core/agentic/agent-router.ts +0 -150
- package/core/agentic/anti-hallucination.ts +0 -141
- package/core/agentic/chain-of-thought.ts +0 -234
- package/core/agentic/command-classifier.ts +0 -141
- package/core/agentic/command-context.ts +0 -168
- package/core/agentic/command-executor.ts +0 -471
- package/core/agentic/context-builder.ts +0 -285
- package/core/agentic/domain-classifier.ts +0 -525
- package/core/agentic/environment-block.ts +0 -102
- package/core/agentic/ground-truth.ts +0 -706
- package/core/agentic/index.ts +0 -193
- package/core/agentic/injection-validator.ts +0 -208
- package/core/agentic/loop-detector.ts +0 -451
- package/core/agentic/memory-system.ts +0 -1547
- package/core/agentic/orchestrator-executor.ts +0 -579
- package/core/agentic/plan-mode.ts +0 -525
- package/core/agentic/prompt-builder.ts +0 -1069
- package/core/agentic/response-validator.ts +0 -98
- package/core/agentic/services.ts +0 -167
- package/core/agentic/skill-loader.ts +0 -106
- package/core/agentic/smart-context.ts +0 -393
- package/core/agentic/tech-normalizer.ts +0 -167
- package/core/agentic/template-executor.ts +0 -272
- package/core/agentic/template-loader.ts +0 -109
- package/core/agentic/token-budget.ts +0 -226
- package/core/agentic/tool-registry.ts +0 -146
- package/core/agents/index.ts +0 -28
- package/core/agents/performance.ts +0 -429
- package/core/ai-tools/formatters.ts +0 -341
- package/core/ai-tools/generator.ts +0 -144
- package/core/ai-tools/index.ts +0 -15
- package/core/ai-tools/registry.ts +0 -201
- package/core/bus/bus.ts +0 -314
- package/core/bus/index.ts +0 -8
- package/core/cli/linear.ts +0 -500
- package/core/cli/lint-meta-commentary.ts +0 -177
- package/core/cli/start.ts +0 -386
- package/core/commands/analysis.ts +0 -1274
- package/core/commands/analytics.ts +0 -342
- package/core/commands/base.ts +0 -118
- package/core/commands/cleanup.ts +0 -157
- package/core/commands/command-data.ts +0 -463
- package/core/commands/commands.ts +0 -306
- package/core/commands/context.ts +0 -238
- package/core/commands/design.ts +0 -77
- package/core/commands/index.ts +0 -19
- package/core/commands/maintenance.ts +0 -77
- package/core/commands/performance.ts +0 -114
- package/core/commands/planning.ts +0 -662
- package/core/commands/register.ts +0 -127
- package/core/commands/registry.ts +0 -444
- package/core/commands/setup.ts +0 -280
- package/core/commands/shipping.ts +0 -267
- package/core/commands/snapshots.ts +0 -297
- package/core/commands/uninstall.ts +0 -542
- package/core/commands/velocity.ts +0 -149
- package/core/commands/workflow.ts +0 -505
- package/core/config/command-context.config.json +0 -66
- package/core/constants/index.ts +0 -379
- package/core/context/generator.ts +0 -368
- package/core/context-tools/files-tool.ts +0 -577
- package/core/context-tools/imports-tool.ts +0 -400
- package/core/context-tools/index.ts +0 -434
- package/core/context-tools/recent-tool.ts +0 -301
- package/core/context-tools/signatures-tool.ts +0 -495
- package/core/context-tools/summary-tool.ts +0 -301
- package/core/context-tools/token-counter.ts +0 -273
- package/core/context-tools/types.ts +0 -253
- package/core/domain/agent-generator.ts +0 -186
- package/core/domain/agent-loader.ts +0 -419
- package/core/domain/analyzer.ts +0 -387
- package/core/domain/architecture-generator.ts +0 -108
- package/core/domain/bm25.ts +0 -525
- package/core/domain/change-propagator.ts +0 -162
- package/core/domain/context-estimator.ts +0 -175
- package/core/domain/fibonacci.ts +0 -128
- package/core/domain/file-hasher.ts +0 -296
- package/core/domain/file-ranker.ts +0 -151
- package/core/domain/git-cochange.ts +0 -250
- package/core/domain/import-graph.ts +0 -315
- package/core/domain/snapshot-manager.ts +0 -415
- package/core/domain/task-stack.ts +0 -578
- package/core/domain/velocity.ts +0 -470
- package/core/errors.ts +0 -335
- package/core/events/events.ts +0 -85
- package/core/events/index.ts +0 -8
- package/core/index.ts +0 -481
- package/core/infrastructure/agent-detector.ts +0 -135
- package/core/infrastructure/ai-provider.ts +0 -578
- package/core/infrastructure/author-detector.ts +0 -133
- package/core/infrastructure/capability-installer.ts +0 -76
- package/core/infrastructure/claude-agent.ts +0 -297
- package/core/infrastructure/command-installer.ts +0 -752
- package/core/infrastructure/config-manager.ts +0 -364
- package/core/infrastructure/editors-config.ts +0 -172
- package/core/infrastructure/path-manager.ts +0 -571
- package/core/infrastructure/performance-tracker.ts +0 -326
- package/core/infrastructure/permission-manager.ts +0 -289
- package/core/infrastructure/setup.ts +0 -1061
- package/core/infrastructure/update-checker.ts +0 -246
- package/core/integrations/issue-tracker/enricher.ts +0 -271
- package/core/integrations/issue-tracker/index.ts +0 -8
- package/core/integrations/issue-tracker/manager.ts +0 -286
- package/core/integrations/issue-tracker/types.ts +0 -310
- package/core/integrations/jira/cache.ts +0 -57
- package/core/integrations/jira/client.ts +0 -688
- package/core/integrations/jira/index.ts +0 -23
- package/core/integrations/jira/service.ts +0 -244
- package/core/integrations/linear/cache.ts +0 -68
- package/core/integrations/linear/client.ts +0 -436
- package/core/integrations/linear/index.ts +0 -20
- package/core/integrations/linear/service.ts +0 -260
- package/core/integrations/linear/sync.ts +0 -314
- package/core/outcomes/analyzer.ts +0 -286
- package/core/outcomes/index.ts +0 -34
- package/core/outcomes/recorder.ts +0 -195
- package/core/plugin/builtin/webhook.ts +0 -148
- package/core/plugin/hooks.ts +0 -315
- package/core/plugin/index.ts +0 -50
- package/core/plugin/loader.ts +0 -354
- package/core/plugin/registry.ts +0 -326
- package/core/schemas/agents.ts +0 -27
- package/core/schemas/analysis.ts +0 -530
- package/core/schemas/classification.ts +0 -91
- package/core/schemas/command-context.ts +0 -29
- package/core/schemas/enriched-task.ts +0 -291
- package/core/schemas/ideas.ts +0 -114
- package/core/schemas/index.ts +0 -53
- package/core/schemas/issues.ts +0 -159
- package/core/schemas/llm-output.ts +0 -170
- package/core/schemas/metrics.ts +0 -143
- package/core/schemas/model.ts +0 -153
- package/core/schemas/outcomes.ts +0 -487
- package/core/schemas/performance.ts +0 -128
- package/core/schemas/permissions.ts +0 -180
- package/core/schemas/prd.ts +0 -450
- package/core/schemas/project.ts +0 -57
- package/core/schemas/roadmap.ts +0 -322
- package/core/schemas/schemas.ts +0 -38
- package/core/schemas/shipped.ts +0 -109
- package/core/schemas/state.ts +0 -284
- package/core/schemas/velocity.ts +0 -103
- package/core/server/index.ts +0 -21
- package/core/server/routes-extended.ts +0 -566
- package/core/server/routes.ts +0 -176
- package/core/server/server.ts +0 -149
- package/core/server/sse.ts +0 -192
- package/core/services/agent-generator.ts +0 -385
- package/core/services/agent-service.ts +0 -168
- package/core/services/breakdown-service.ts +0 -124
- package/core/services/context-generator.ts +0 -445
- package/core/services/context-selector.ts +0 -429
- package/core/services/dependency-validator.ts +0 -318
- package/core/services/diff-generator.ts +0 -313
- package/core/services/doctor-service.ts +0 -423
- package/core/services/file-categorizer.ts +0 -448
- package/core/services/file-scorer.ts +0 -270
- package/core/services/git-analyzer.ts +0 -293
- package/core/services/hierarchical-agent-resolver.ts +0 -236
- package/core/services/hooks-service.ts +0 -685
- package/core/services/index.ts +0 -46
- package/core/services/local-state-generator.ts +0 -158
- package/core/services/memory-service.ts +0 -181
- package/core/services/nested-context-resolver.ts +0 -842
- package/core/services/project-index.ts +0 -911
- package/core/services/project-service.ts +0 -155
- package/core/services/session-tracker.ts +0 -287
- package/core/services/skill-installer.ts +0 -447
- package/core/services/skill-lock.ts +0 -132
- package/core/services/skill-service.ts +0 -306
- package/core/services/stack-detector.ts +0 -229
- package/core/services/staleness-checker.ts +0 -327
- package/core/services/sync-service.ts +0 -1515
- package/core/services/sync-verifier.ts +0 -253
- package/core/services/watch-service.ts +0 -312
- package/core/session/compaction.ts +0 -248
- package/core/session/index.ts +0 -35
- package/core/session/log-migration.ts +0 -88
- package/core/session/metrics.ts +0 -323
- package/core/session/session-log-manager.ts +0 -307
- package/core/session/task-session-manager.ts +0 -404
- package/core/session/utils.ts +0 -51
- package/core/storage/analysis-storage.ts +0 -373
- package/core/storage/archive-storage.ts +0 -205
- package/core/storage/database.ts +0 -575
- package/core/storage/ideas-storage.ts +0 -298
- package/core/storage/index-storage.ts +0 -523
- package/core/storage/index.ts +0 -79
- package/core/storage/metrics-storage.ts +0 -321
- package/core/storage/migrate-json.ts +0 -720
- package/core/storage/queue-storage.ts +0 -336
- package/core/storage/safe-reader.ts +0 -105
- package/core/storage/shipped-storage.ts +0 -253
- package/core/storage/state-storage.ts +0 -1035
- package/core/storage/storage-manager.ts +0 -205
- package/core/storage/storage.ts +0 -177
- package/core/storage/velocity-storage.ts +0 -149
- package/core/sync/auth-config.ts +0 -138
- package/core/sync/index.ts +0 -31
- package/core/sync/oauth-handler.ts +0 -143
- package/core/sync/sync-client.ts +0 -251
- package/core/sync/sync-manager.ts +0 -327
- package/core/tsconfig.json +0 -22
- package/core/types/agentic.ts +0 -760
- package/core/types/agents.ts +0 -150
- package/core/types/bus.ts +0 -193
- package/core/types/citations.ts +0 -22
- package/core/types/commands.ts +0 -399
- package/core/types/config.ts +0 -92
- package/core/types/core.ts +0 -96
- package/core/types/diff.ts +0 -41
- package/core/types/domain.ts +0 -71
- package/core/types/errors.ts +0 -111
- package/core/types/events.ts +0 -42
- package/core/types/fs.ts +0 -72
- package/core/types/index.ts +0 -510
- package/core/types/infrastructure.ts +0 -210
- package/core/types/integrations.ts +0 -31
- package/core/types/jira.ts +0 -51
- package/core/types/logger.ts +0 -17
- package/core/types/memory.ts +0 -313
- package/core/types/outcomes.ts +0 -190
- package/core/types/output.ts +0 -47
- package/core/types/plugin.ts +0 -25
- package/core/types/project-sync.ts +0 -129
- package/core/types/provider.ts +0 -163
- package/core/types/server.ts +0 -71
- package/core/types/services.ts +0 -84
- package/core/types/session.ts +0 -135
- package/core/types/stack.ts +0 -19
- package/core/types/storage.ts +0 -318
- package/core/types/sync-verifier.ts +0 -33
- package/core/types/sync.ts +0 -121
- package/core/types/task.ts +0 -72
- package/core/types/template.ts +0 -24
- package/core/types/utils.ts +0 -92
- package/core/types/workflow.ts +0 -23
- package/core/utils/agent-stream.ts +0 -140
- package/core/utils/animations.ts +0 -251
- package/core/utils/branding.ts +0 -88
- package/core/utils/cache.ts +0 -187
- package/core/utils/citations.ts +0 -39
- package/core/utils/collection-filters.ts +0 -209
- package/core/utils/date-helper.ts +0 -176
- package/core/utils/error-messages.ts +0 -38
- package/core/utils/file-helper.ts +0 -277
- package/core/utils/fs-helpers.ts +0 -14
- package/core/utils/help.ts +0 -314
- package/core/utils/jsonl-helper.ts +0 -290
- package/core/utils/keychain.ts +0 -127
- package/core/utils/logger.ts +0 -77
- package/core/utils/markdown-builder.ts +0 -280
- package/core/utils/next-steps.ts +0 -95
- package/core/utils/output.ts +0 -403
- package/core/utils/preserve-sections.ts +0 -218
- package/core/utils/project-commands.ts +0 -126
- package/core/utils/project-credentials.ts +0 -143
- package/core/utils/provider-cache.ts +0 -49
- package/core/utils/retry.ts +0 -318
- package/core/utils/runtime.ts +0 -108
- package/core/utils/session-helper.ts +0 -278
- package/core/utils/subtask-table.ts +0 -227
- package/core/utils/version.ts +0 -128
- package/core/wizard/index.ts +0 -13
- package/core/wizard/onboarding.ts +0 -633
- package/core/workflow/index.ts +0 -7
- package/core/workflow/state-machine.ts +0 -198
- package/core/workflow/workflow-preferences.ts +0 -294
- package/dist/core/infrastructure/command-installer.js +0 -1141
- package/dist/core/infrastructure/editors-config.js +0 -177
- package/dist/core/infrastructure/setup.js +0 -2244
- package/dist/core/utils/version.js +0 -141
- package/templates/agentic/agent-routing.md +0 -45
- package/templates/agentic/agents/uxui.md +0 -63
- package/templates/agentic/checklist-routing.md +0 -98
- package/templates/agentic/orchestrator.md +0 -68
- package/templates/agentic/task-fragmentation.md +0 -89
- package/templates/agents/AGENTS.md +0 -68
- package/templates/analysis/analyze.md +0 -84
- package/templates/analysis/patterns.md +0 -60
- package/templates/antigravity/SKILL.md +0 -39
- package/templates/architect/discovery.md +0 -67
- package/templates/architect/phases.md +0 -59
- package/templates/checklists/architecture.md +0 -28
- package/templates/checklists/code-quality.md +0 -28
- package/templates/checklists/data.md +0 -33
- package/templates/checklists/documentation.md +0 -33
- package/templates/checklists/infrastructure.md +0 -33
- package/templates/checklists/performance.md +0 -33
- package/templates/checklists/security.md +0 -33
- package/templates/checklists/testing.md +0 -33
- package/templates/checklists/ux-ui.md +0 -37
- package/templates/commands/analyze.md +0 -56
- package/templates/commands/auth.md +0 -234
- package/templates/commands/bug.md +0 -163
- package/templates/commands/cleanup.md +0 -19
- package/templates/commands/dash.md +0 -99
- package/templates/commands/design.md +0 -15
- package/templates/commands/done.md +0 -291
- package/templates/commands/enrich.md +0 -174
- package/templates/commands/git.md +0 -295
- package/templates/commands/history.md +0 -389
- package/templates/commands/idea.md +0 -88
- package/templates/commands/impact.md +0 -864
- package/templates/commands/init.md +0 -54
- package/templates/commands/jira.md +0 -278
- package/templates/commands/linear.md +0 -288
- package/templates/commands/merge.md +0 -206
- package/templates/commands/next.md +0 -80
- package/templates/commands/p.md +0 -67
- package/templates/commands/p.toml +0 -37
- package/templates/commands/pause.md +0 -136
- package/templates/commands/plan.md +0 -696
- package/templates/commands/prd.md +0 -356
- package/templates/commands/resume.md +0 -171
- package/templates/commands/review.md +0 -276
- package/templates/commands/serve.md +0 -118
- package/templates/commands/setup.md +0 -91
- package/templates/commands/ship.md +0 -475
- package/templates/commands/skill.md +0 -259
- package/templates/commands/spec.md +0 -218
- package/templates/commands/status.md +0 -207
- package/templates/commands/sync.md +0 -104
- package/templates/commands/task.md +0 -312
- package/templates/commands/test.md +0 -93
- package/templates/commands/update.md +0 -63
- package/templates/commands/verify.md +0 -204
- package/templates/commands/workflow.md +0 -150
- package/templates/config/skill-mappings.json +0 -82
- package/templates/context/dashboard.md +0 -256
- package/templates/context/roadmap.md +0 -221
- package/templates/cursor/commands/bug.md +0 -8
- package/templates/cursor/commands/done.md +0 -4
- package/templates/cursor/commands/pause.md +0 -6
- package/templates/cursor/commands/resume.md +0 -4
- package/templates/cursor/commands/ship.md +0 -8
- package/templates/cursor/commands/sync.md +0 -4
- package/templates/cursor/commands/task.md +0 -8
- package/templates/cursor/p.md +0 -29
- package/templates/cursor/router.mdc +0 -28
- package/templates/design/api.md +0 -95
- package/templates/design/architecture.md +0 -77
- package/templates/design/component.md +0 -89
- package/templates/design/database.md +0 -78
- package/templates/design/flow.md +0 -94
- package/templates/global/ANTIGRAVITY.md +0 -254
- package/templates/global/CLAUDE.md +0 -497
- package/templates/global/CURSOR.mdc +0 -266
- package/templates/global/GEMINI.md +0 -293
- package/templates/global/STORAGE-SPEC.md +0 -391
- package/templates/global/WINDSURF.md +0 -266
- package/templates/global/modules/CLAUDE-commands.md +0 -70
- package/templates/global/modules/CLAUDE-core.md +0 -105
- package/templates/global/modules/CLAUDE-git.md +0 -50
- package/templates/global/modules/CLAUDE-intelligence.md +0 -92
- package/templates/global/modules/CLAUDE-storage.md +0 -50
- package/templates/global/modules/module-config.json +0 -36
- package/templates/mcp-config.json +0 -19
- package/templates/permissions/default.jsonc +0 -60
- package/templates/permissions/permissive.jsonc +0 -49
- package/templates/permissions/strict.jsonc +0 -58
- package/templates/planning-methodology.md +0 -195
- package/templates/skills/code-review.md +0 -47
- package/templates/skills/debug.md +0 -61
- package/templates/skills/refactor.md +0 -47
- package/templates/subagents/agent-base.md +0 -20
- package/templates/subagents/domain/backend.md +0 -109
- package/templates/subagents/domain/database.md +0 -121
- package/templates/subagents/domain/devops.md +0 -152
- package/templates/subagents/domain/frontend.md +0 -103
- package/templates/subagents/domain/testing.md +0 -169
- package/templates/subagents/pm-expert.md +0 -366
- package/templates/subagents/workflow/chief-architect.md +0 -657
- package/templates/subagents/workflow/prjct-planner.md +0 -159
- package/templates/subagents/workflow/prjct-shipper.md +0 -188
- package/templates/subagents/workflow/prjct-workflow.md +0 -98
- package/templates/tools/bash.txt +0 -22
- package/templates/tools/edit.txt +0 -18
- package/templates/tools/glob.txt +0 -19
- package/templates/tools/grep.txt +0 -21
- package/templates/tools/read.txt +0 -14
- package/templates/tools/task.txt +0 -20
- package/templates/tools/webfetch.txt +0 -16
- package/templates/tools/websearch.txt +0 -18
- package/templates/tools/write.txt +0 -17
- package/templates/windsurf/router.md +0 -28
- package/templates/windsurf/workflows/bug.md +0 -8
- package/templates/windsurf/workflows/done.md +0 -4
- package/templates/windsurf/workflows/pause.md +0 -4
- package/templates/windsurf/workflows/resume.md +0 -4
- package/templates/windsurf/workflows/ship.md +0 -8
- package/templates/windsurf/workflows/sync.md +0 -4
- package/templates/windsurf/workflows/task.md +0 -8
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency Validator
|
|
3
|
-
*
|
|
4
|
-
* Provides graceful degradation for missing system dependencies.
|
|
5
|
-
* Checks tool availability before operations and provides helpful
|
|
6
|
-
* recovery hints when tools are missing.
|
|
7
|
-
*
|
|
8
|
-
* Pattern from Google Gemini CLI: "Never assumes library availability.
|
|
9
|
-
* Checks --help before using unknown flags."
|
|
10
|
-
*
|
|
11
|
-
* @see PRJ-114
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { execSync } from 'node:child_process'
|
|
15
|
-
import { createError, type ErrorWithHint } from '../utils/error-messages'
|
|
16
|
-
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// TYPES
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
export interface ToolDefinition {
|
|
22
|
-
name: string
|
|
23
|
-
command: string // Command to check availability (e.g., 'git --version')
|
|
24
|
-
versionRegex?: RegExp // Regex to extract version from output
|
|
25
|
-
required: boolean // If false, missing tool is a warning, not error
|
|
26
|
-
installHint: string // How to install if missing
|
|
27
|
-
docs?: string // Documentation URL
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ToolStatus {
|
|
31
|
-
available: boolean
|
|
32
|
-
version?: string
|
|
33
|
-
error?: ErrorWithHint
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// TOOL DEFINITIONS
|
|
38
|
-
// ============================================================================
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Known tools that prjct depends on
|
|
42
|
-
*/
|
|
43
|
-
export const TOOLS: Record<string, ToolDefinition> = {
|
|
44
|
-
git: {
|
|
45
|
-
name: 'git',
|
|
46
|
-
command: 'git --version',
|
|
47
|
-
versionRegex: /git version ([\d.]+)/,
|
|
48
|
-
required: true,
|
|
49
|
-
installHint: 'Install Git: https://git-scm.com/downloads',
|
|
50
|
-
docs: 'https://git-scm.com/doc',
|
|
51
|
-
},
|
|
52
|
-
node: {
|
|
53
|
-
name: 'node',
|
|
54
|
-
command: 'node --version',
|
|
55
|
-
versionRegex: /v([\d.]+)/,
|
|
56
|
-
required: true,
|
|
57
|
-
installHint: 'Install Node.js: https://nodejs.org',
|
|
58
|
-
docs: 'https://nodejs.org/docs',
|
|
59
|
-
},
|
|
60
|
-
bun: {
|
|
61
|
-
name: 'bun',
|
|
62
|
-
command: 'bun --version',
|
|
63
|
-
versionRegex: /([\d.]+)/,
|
|
64
|
-
required: false,
|
|
65
|
-
installHint: 'Install Bun: curl -fsSL https://bun.sh/install | bash',
|
|
66
|
-
docs: 'https://bun.sh/docs',
|
|
67
|
-
},
|
|
68
|
-
gh: {
|
|
69
|
-
name: 'gh',
|
|
70
|
-
command: 'gh --version',
|
|
71
|
-
versionRegex: /gh version ([\d.]+)/,
|
|
72
|
-
required: false,
|
|
73
|
-
installHint: 'Install GitHub CLI: https://cli.github.com',
|
|
74
|
-
docs: 'https://cli.github.com/manual',
|
|
75
|
-
},
|
|
76
|
-
npm: {
|
|
77
|
-
name: 'npm',
|
|
78
|
-
command: 'npm --version',
|
|
79
|
-
versionRegex: /([\d.]+)/,
|
|
80
|
-
required: false,
|
|
81
|
-
installHint: 'npm comes with Node.js: https://nodejs.org',
|
|
82
|
-
},
|
|
83
|
-
claude: {
|
|
84
|
-
name: 'claude',
|
|
85
|
-
command: 'claude --version',
|
|
86
|
-
versionRegex: /claude ([\d.]+)/,
|
|
87
|
-
required: false,
|
|
88
|
-
installHint: 'Install Claude Code: npm install -g @anthropic-ai/claude-code',
|
|
89
|
-
docs: 'https://docs.anthropic.com/claude-code',
|
|
90
|
-
},
|
|
91
|
-
gemini: {
|
|
92
|
-
name: 'gemini',
|
|
93
|
-
command: 'gemini --version',
|
|
94
|
-
versionRegex: /gemini ([\d.]+)/,
|
|
95
|
-
required: false,
|
|
96
|
-
installHint: 'Install Gemini CLI: npm install -g @google/gemini-cli',
|
|
97
|
-
docs: 'https://ai.google.dev/gemini-api/docs',
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ============================================================================
|
|
102
|
-
// DEPENDENCY VALIDATOR
|
|
103
|
-
// ============================================================================
|
|
104
|
-
|
|
105
|
-
class DependencyValidator {
|
|
106
|
-
private cache = new Map<string, ToolStatus>()
|
|
107
|
-
private cacheTimeout = 60_000 // 1 minute cache
|
|
108
|
-
private cacheTimestamps = new Map<string, number>()
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Check if a tool is available
|
|
112
|
-
* Uses caching to avoid repeated execSync calls
|
|
113
|
-
*/
|
|
114
|
-
checkTool(toolName: string): ToolStatus {
|
|
115
|
-
// Check cache first
|
|
116
|
-
const cached = this.getCached(toolName)
|
|
117
|
-
if (cached) return cached
|
|
118
|
-
|
|
119
|
-
const definition = TOOLS[toolName]
|
|
120
|
-
if (!definition) {
|
|
121
|
-
// Unknown tool - try to check directly
|
|
122
|
-
return this.checkUnknownTool(toolName)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const status = this.executeCheck(definition)
|
|
126
|
-
this.setCache(toolName, status)
|
|
127
|
-
return status
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Ensure a tool is available, throw helpful error if not
|
|
132
|
-
* Use this before operations that require a specific tool
|
|
133
|
-
*/
|
|
134
|
-
ensureTool(toolName: string): void {
|
|
135
|
-
const status = this.checkTool(toolName)
|
|
136
|
-
|
|
137
|
-
if (!status.available) {
|
|
138
|
-
const definition = TOOLS[toolName]
|
|
139
|
-
const error = status.error || {
|
|
140
|
-
message: `${toolName} is not available`,
|
|
141
|
-
hint: definition?.installHint || `Install ${toolName} and try again`,
|
|
142
|
-
docs: definition?.docs,
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
throw new DependencyError(error)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Ensure multiple tools are available
|
|
151
|
-
*/
|
|
152
|
-
ensureTools(toolNames: string[]): void {
|
|
153
|
-
const missing: string[] = []
|
|
154
|
-
|
|
155
|
-
for (const name of toolNames) {
|
|
156
|
-
const status = this.checkTool(name)
|
|
157
|
-
if (!status.available) {
|
|
158
|
-
missing.push(name)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (missing.length > 0) {
|
|
163
|
-
const hints = missing
|
|
164
|
-
.map((name) => {
|
|
165
|
-
const def = TOOLS[name]
|
|
166
|
-
return def ? ` ${name}: ${def.installHint}` : ` ${name}: Install and try again`
|
|
167
|
-
})
|
|
168
|
-
.join('\n')
|
|
169
|
-
|
|
170
|
-
throw new DependencyError({
|
|
171
|
-
message: `Missing required tools: ${missing.join(', ')}`,
|
|
172
|
-
hint: `Install the following:\n${hints}`,
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Check if tool is available (boolean convenience method)
|
|
179
|
-
*/
|
|
180
|
-
isAvailable(toolName: string): boolean {
|
|
181
|
-
return this.checkTool(toolName).available
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get tool version if available
|
|
186
|
-
*/
|
|
187
|
-
getVersion(toolName: string): string | undefined {
|
|
188
|
-
return this.checkTool(toolName).version
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Check multiple tools and return summary
|
|
193
|
-
*/
|
|
194
|
-
checkAll(toolNames?: string[]): Map<string, ToolStatus> {
|
|
195
|
-
const names = toolNames || Object.keys(TOOLS)
|
|
196
|
-
const results = new Map<string, ToolStatus>()
|
|
197
|
-
|
|
198
|
-
for (const name of names) {
|
|
199
|
-
results.set(name, this.checkTool(name))
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return results
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Clear the cache (useful for tests or after installations)
|
|
207
|
-
*/
|
|
208
|
-
clearCache(): void {
|
|
209
|
-
this.cache.clear()
|
|
210
|
-
this.cacheTimestamps.clear()
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ==========================================================================
|
|
214
|
-
// PRIVATE METHODS
|
|
215
|
-
// ==========================================================================
|
|
216
|
-
|
|
217
|
-
private executeCheck(definition: ToolDefinition): ToolStatus {
|
|
218
|
-
try {
|
|
219
|
-
const output = execSync(definition.command, {
|
|
220
|
-
encoding: 'utf-8',
|
|
221
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
222
|
-
timeout: 5000, // 5 second timeout
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
let version: string | undefined
|
|
226
|
-
if (definition.versionRegex) {
|
|
227
|
-
const match = output.match(definition.versionRegex)
|
|
228
|
-
version = match ? match[1] : undefined
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return { available: true, version }
|
|
232
|
-
} catch {
|
|
233
|
-
return {
|
|
234
|
-
available: false,
|
|
235
|
-
error: createError(
|
|
236
|
-
`${definition.name} is not installed or not in PATH`,
|
|
237
|
-
definition.installHint,
|
|
238
|
-
{ docs: definition.docs }
|
|
239
|
-
),
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
private checkUnknownTool(toolName: string): ToolStatus {
|
|
245
|
-
try {
|
|
246
|
-
// Try running with --version (common convention)
|
|
247
|
-
execSync(`${toolName} --version`, {
|
|
248
|
-
encoding: 'utf-8',
|
|
249
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
250
|
-
timeout: 5000,
|
|
251
|
-
})
|
|
252
|
-
return { available: true }
|
|
253
|
-
} catch {
|
|
254
|
-
// Try running with -v
|
|
255
|
-
try {
|
|
256
|
-
execSync(`${toolName} -v`, {
|
|
257
|
-
encoding: 'utf-8',
|
|
258
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
259
|
-
timeout: 5000,
|
|
260
|
-
})
|
|
261
|
-
return { available: true }
|
|
262
|
-
} catch {
|
|
263
|
-
return {
|
|
264
|
-
available: false,
|
|
265
|
-
error: createError(
|
|
266
|
-
`${toolName} is not installed or not in PATH`,
|
|
267
|
-
`Install ${toolName} and try again`
|
|
268
|
-
),
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private getCached(toolName: string): ToolStatus | null {
|
|
275
|
-
const timestamp = this.cacheTimestamps.get(toolName)
|
|
276
|
-
if (!timestamp) return null
|
|
277
|
-
|
|
278
|
-
// Check if cache is expired
|
|
279
|
-
if (Date.now() - timestamp > this.cacheTimeout) {
|
|
280
|
-
this.cache.delete(toolName)
|
|
281
|
-
this.cacheTimestamps.delete(toolName)
|
|
282
|
-
return null
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return this.cache.get(toolName) || null
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
private setCache(toolName: string, status: ToolStatus): void {
|
|
289
|
-
this.cache.set(toolName, status)
|
|
290
|
-
this.cacheTimestamps.set(toolName, Date.now())
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// ============================================================================
|
|
295
|
-
// CUSTOM ERROR
|
|
296
|
-
// ============================================================================
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Error thrown when a required dependency is missing
|
|
300
|
-
*/
|
|
301
|
-
export class DependencyError extends Error {
|
|
302
|
-
readonly hint?: string
|
|
303
|
-
readonly docs?: string
|
|
304
|
-
|
|
305
|
-
constructor(error: ErrorWithHint) {
|
|
306
|
-
super(error.message)
|
|
307
|
-
this.name = 'DependencyError'
|
|
308
|
-
this.hint = error.hint
|
|
309
|
-
this.docs = error.docs
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// ============================================================================
|
|
314
|
-
// EXPORTS
|
|
315
|
-
// ============================================================================
|
|
316
|
-
|
|
317
|
-
export const dependencyValidator = new DependencyValidator()
|
|
318
|
-
export default dependencyValidator
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diff Generator for Sync Preview
|
|
3
|
-
*
|
|
4
|
-
* Generates human-readable diffs between old and new context files.
|
|
5
|
-
* Shows what will change before sync applies.
|
|
6
|
-
*
|
|
7
|
-
* @see PRJ-125
|
|
8
|
-
* @module services/diff-generator
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import chalk from 'chalk'
|
|
12
|
-
import type { DiffOptions, ParsedMarkdownSection, SyncDiff } from '../types'
|
|
13
|
-
|
|
14
|
-
export type {
|
|
15
|
-
DiffOptions,
|
|
16
|
-
DiffSection,
|
|
17
|
-
ParsedMarkdownSection,
|
|
18
|
-
PreservedInfo,
|
|
19
|
-
SyncDiff,
|
|
20
|
-
} from '../types'
|
|
21
|
-
|
|
22
|
-
// =============================================================================
|
|
23
|
-
// Constants
|
|
24
|
-
// =============================================================================
|
|
25
|
-
|
|
26
|
-
const CHARS_PER_TOKEN = 4
|
|
27
|
-
|
|
28
|
-
// =============================================================================
|
|
29
|
-
// Token Estimation
|
|
30
|
-
// =============================================================================
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Estimate token count for content
|
|
34
|
-
*/
|
|
35
|
-
export function estimateTokens(content: string): number {
|
|
36
|
-
return Math.ceil(content.length / CHARS_PER_TOKEN)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// =============================================================================
|
|
40
|
-
// Section Parsing
|
|
41
|
-
// =============================================================================
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Parse markdown into sections based on headers
|
|
45
|
-
*/
|
|
46
|
-
export function parseMarkdownSections(content: string): ParsedMarkdownSection[] {
|
|
47
|
-
const lines = content.split('\n')
|
|
48
|
-
const sections: ParsedMarkdownSection[] = []
|
|
49
|
-
let currentSection: ParsedMarkdownSection | null = null
|
|
50
|
-
|
|
51
|
-
for (let i = 0; i < lines.length; i++) {
|
|
52
|
-
const line = lines[i]
|
|
53
|
-
const headerMatch = line.match(/^(#{1,3})\s+(.+)$/)
|
|
54
|
-
|
|
55
|
-
if (headerMatch) {
|
|
56
|
-
// Save previous section
|
|
57
|
-
if (currentSection) {
|
|
58
|
-
currentSection.endLine = i - 1
|
|
59
|
-
sections.push(currentSection)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Start new section
|
|
63
|
-
currentSection = {
|
|
64
|
-
name: headerMatch[2].trim(),
|
|
65
|
-
content: line,
|
|
66
|
-
startLine: i,
|
|
67
|
-
endLine: i,
|
|
68
|
-
}
|
|
69
|
-
} else if (currentSection) {
|
|
70
|
-
currentSection.content += `\n${line}`
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Save last section
|
|
75
|
-
if (currentSection) {
|
|
76
|
-
currentSection.endLine = lines.length - 1
|
|
77
|
-
sections.push(currentSection)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return sections
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Check if content is within a preserve block
|
|
85
|
-
*/
|
|
86
|
-
function isPreservedSection(content: string): boolean {
|
|
87
|
-
return content.includes('<!-- prjct:preserve')
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// =============================================================================
|
|
91
|
-
// Diff Generation
|
|
92
|
-
// =============================================================================
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Generate diff between old and new content
|
|
96
|
-
*/
|
|
97
|
-
export function generateSyncDiff(oldContent: string, newContent: string): SyncDiff {
|
|
98
|
-
const oldSections = parseMarkdownSections(oldContent)
|
|
99
|
-
const newSections = parseMarkdownSections(newContent)
|
|
100
|
-
|
|
101
|
-
const diff: SyncDiff = {
|
|
102
|
-
hasChanges: false,
|
|
103
|
-
added: [],
|
|
104
|
-
modified: [],
|
|
105
|
-
removed: [],
|
|
106
|
-
preserved: [],
|
|
107
|
-
tokensBefore: estimateTokens(oldContent),
|
|
108
|
-
tokensAfter: estimateTokens(newContent),
|
|
109
|
-
tokenDelta: 0,
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
diff.tokenDelta = diff.tokensAfter - diff.tokensBefore
|
|
113
|
-
|
|
114
|
-
// Create maps for quick lookup
|
|
115
|
-
const oldMap = new Map(oldSections.map((s) => [s.name.toLowerCase(), s]))
|
|
116
|
-
const newMap = new Map(newSections.map((s) => [s.name.toLowerCase(), s]))
|
|
117
|
-
|
|
118
|
-
// Find preserved sections in old content
|
|
119
|
-
for (const section of oldSections) {
|
|
120
|
-
if (isPreservedSection(section.content)) {
|
|
121
|
-
diff.preserved.push({
|
|
122
|
-
name: section.name,
|
|
123
|
-
lineCount: section.content.split('\n').length,
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Find added and modified sections
|
|
129
|
-
for (const newSection of newSections) {
|
|
130
|
-
const key = newSection.name.toLowerCase()
|
|
131
|
-
const oldSection = oldMap.get(key)
|
|
132
|
-
|
|
133
|
-
if (!oldSection) {
|
|
134
|
-
// New section
|
|
135
|
-
diff.added.push({
|
|
136
|
-
name: newSection.name,
|
|
137
|
-
type: 'added',
|
|
138
|
-
after: newSection.content,
|
|
139
|
-
lineCount: newSection.content.split('\n').length,
|
|
140
|
-
})
|
|
141
|
-
diff.hasChanges = true
|
|
142
|
-
} else if (oldSection.content.trim() !== newSection.content.trim()) {
|
|
143
|
-
// Modified section (skip if preserved)
|
|
144
|
-
if (!isPreservedSection(oldSection.content)) {
|
|
145
|
-
diff.modified.push({
|
|
146
|
-
name: newSection.name,
|
|
147
|
-
type: 'modified',
|
|
148
|
-
before: oldSection.content,
|
|
149
|
-
after: newSection.content,
|
|
150
|
-
lineCount: newSection.content.split('\n').length,
|
|
151
|
-
})
|
|
152
|
-
diff.hasChanges = true
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Find removed sections
|
|
158
|
-
for (const oldSection of oldSections) {
|
|
159
|
-
const key = oldSection.name.toLowerCase()
|
|
160
|
-
if (!newMap.has(key) && !isPreservedSection(oldSection.content)) {
|
|
161
|
-
diff.removed.push({
|
|
162
|
-
name: oldSection.name,
|
|
163
|
-
type: 'removed',
|
|
164
|
-
before: oldSection.content,
|
|
165
|
-
lineCount: oldSection.content.split('\n').length,
|
|
166
|
-
})
|
|
167
|
-
diff.hasChanges = true
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return diff
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// =============================================================================
|
|
175
|
-
// Diff Formatting
|
|
176
|
-
// =============================================================================
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Format diff for terminal display
|
|
180
|
-
*/
|
|
181
|
-
export function formatDiffPreview(diff: SyncDiff, options: DiffOptions = {}): string {
|
|
182
|
-
const { colorize = true } = options
|
|
183
|
-
const lines: string[] = []
|
|
184
|
-
|
|
185
|
-
const green = colorize ? chalk.green : (s: string) => s
|
|
186
|
-
const red = colorize ? chalk.red : (s: string) => s
|
|
187
|
-
const yellow = colorize ? chalk.yellow : (s: string) => s
|
|
188
|
-
const dim = colorize ? chalk.dim : (s: string) => s
|
|
189
|
-
const bold = colorize ? chalk.bold : (s: string) => s
|
|
190
|
-
|
|
191
|
-
if (!diff.hasChanges) {
|
|
192
|
-
lines.push(dim('No changes detected (context is up to date)'))
|
|
193
|
-
return lines.join('\n')
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
lines.push('')
|
|
197
|
-
lines.push(bold('📋 Changes to context files:'))
|
|
198
|
-
lines.push('')
|
|
199
|
-
|
|
200
|
-
// Added sections
|
|
201
|
-
if (diff.added.length > 0) {
|
|
202
|
-
for (const section of diff.added) {
|
|
203
|
-
lines.push(green(`+ │ + ${section.name} (new)`))
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Modified sections
|
|
208
|
-
if (diff.modified.length > 0) {
|
|
209
|
-
for (const section of diff.modified) {
|
|
210
|
-
lines.push(yellow(`~ │ ${section.name} (modified)`))
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Removed sections
|
|
215
|
-
if (diff.removed.length > 0) {
|
|
216
|
-
for (const section of diff.removed) {
|
|
217
|
-
lines.push(red(`- │ - ${section.name} (removed)`))
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Preserved sections
|
|
222
|
-
if (diff.preserved.length > 0) {
|
|
223
|
-
lines.push('')
|
|
224
|
-
lines.push(dim(' ## Your Customizations'))
|
|
225
|
-
for (const section of diff.preserved) {
|
|
226
|
-
lines.push(dim(` │ ✓ ${section.name} (${section.lineCount} lines preserved)`))
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Summary
|
|
231
|
-
lines.push('')
|
|
232
|
-
lines.push(dim('────────────────────────────────'))
|
|
233
|
-
|
|
234
|
-
const summaryParts: string[] = []
|
|
235
|
-
if (diff.added.length > 0) summaryParts.push(green(`+${diff.added.length} added`))
|
|
236
|
-
if (diff.modified.length > 0) summaryParts.push(yellow(`~${diff.modified.length} modified`))
|
|
237
|
-
if (diff.removed.length > 0) summaryParts.push(red(`-${diff.removed.length} removed`))
|
|
238
|
-
|
|
239
|
-
lines.push(`Summary: ${summaryParts.join(', ') || 'no changes'}`)
|
|
240
|
-
|
|
241
|
-
// Token delta
|
|
242
|
-
const tokenSign = diff.tokenDelta >= 0 ? '+' : ''
|
|
243
|
-
const tokenColor = diff.tokenDelta >= 0 ? green : red
|
|
244
|
-
lines.push(
|
|
245
|
-
`Tokens: ${diff.tokensBefore.toLocaleString()} → ${diff.tokensAfter.toLocaleString()} (${tokenColor(tokenSign + diff.tokenDelta.toLocaleString())})`
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
lines.push('')
|
|
249
|
-
|
|
250
|
-
return lines.join('\n')
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Format full git-style diff
|
|
255
|
-
*/
|
|
256
|
-
export function formatFullDiff(diff: SyncDiff, options: DiffOptions = {}): string {
|
|
257
|
-
const { colorize = true } = options
|
|
258
|
-
const lines: string[] = []
|
|
259
|
-
|
|
260
|
-
const green = colorize ? chalk.green : (s: string) => s
|
|
261
|
-
const red = colorize ? chalk.red : (s: string) => s
|
|
262
|
-
const cyan = colorize ? chalk.cyan : (s: string) => s
|
|
263
|
-
const dim = colorize ? chalk.dim : (s: string) => s
|
|
264
|
-
|
|
265
|
-
// Added sections
|
|
266
|
-
for (const section of diff.added) {
|
|
267
|
-
lines.push(cyan(`@@ +${section.name} @@`))
|
|
268
|
-
if (section.after) {
|
|
269
|
-
for (const line of section.after.split('\n')) {
|
|
270
|
-
lines.push(green(`+ ${line}`))
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
lines.push('')
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Modified sections
|
|
277
|
-
for (const section of diff.modified) {
|
|
278
|
-
lines.push(cyan(`@@ ${section.name} @@`))
|
|
279
|
-
if (section.before) {
|
|
280
|
-
for (const line of section.before.split('\n').slice(0, 5)) {
|
|
281
|
-
lines.push(red(`- ${line}`))
|
|
282
|
-
}
|
|
283
|
-
if (section.before.split('\n').length > 5) {
|
|
284
|
-
lines.push(dim(` ... ${section.before.split('\n').length - 5} more lines`))
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (section.after) {
|
|
288
|
-
for (const line of section.after.split('\n').slice(0, 5)) {
|
|
289
|
-
lines.push(green(`+ ${line}`))
|
|
290
|
-
}
|
|
291
|
-
if (section.after.split('\n').length > 5) {
|
|
292
|
-
lines.push(dim(` ... ${section.after.split('\n').length - 5} more lines`))
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
lines.push('')
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Removed sections
|
|
299
|
-
for (const section of diff.removed) {
|
|
300
|
-
lines.push(cyan(`@@ -${section.name} @@`))
|
|
301
|
-
if (section.before) {
|
|
302
|
-
for (const line of section.before.split('\n').slice(0, 5)) {
|
|
303
|
-
lines.push(red(`- ${line}`))
|
|
304
|
-
}
|
|
305
|
-
if (section.before.split('\n').length > 5) {
|
|
306
|
-
lines.push(dim(` ... ${section.before.split('\n').length - 5} more lines`))
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
lines.push('')
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return lines.join('\n')
|
|
313
|
-
}
|