prjct-cli 1.22.0 → 1.24.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 +230 -0
- package/bin/prjct +30 -13
- package/dist/bin/prjct-core.mjs +1748 -0
- package/dist/bin/prjct.mjs +17 -36672
- package/dist/cli/linear.mjs +14 -0
- package/dist/daemon/entry.mjs +1429 -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,752 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Installer
|
|
3
|
-
* Installs prjct commands in Claude Code and other AI CLI agents.
|
|
4
|
-
*
|
|
5
|
-
* Architecture:
|
|
6
|
-
* - Claude: Full command sync to ~/.claude/commands/p/ (workaround for bug #2422)
|
|
7
|
-
* - Gemini: Simple router (p.toml) to ~/.gemini/commands/ (handled by setup.ts)
|
|
8
|
-
*
|
|
9
|
-
* This module handles the more complex Claude installation.
|
|
10
|
-
* For Gemini, see setup.ts::installGeminiRouter()
|
|
11
|
-
*
|
|
12
|
-
* @version 0.6.0 - Multi-provider support
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import fs from 'node:fs/promises'
|
|
16
|
-
import os from 'node:os'
|
|
17
|
-
import path from 'node:path'
|
|
18
|
-
import type {
|
|
19
|
-
CheckResult,
|
|
20
|
-
GlobalConfigResult,
|
|
21
|
-
InstallResult,
|
|
22
|
-
SyncResult,
|
|
23
|
-
UninstallResult,
|
|
24
|
-
} from '../types'
|
|
25
|
-
import { getErrorMessage, isNotFoundError } from '../types/fs'
|
|
26
|
-
import { PACKAGE_ROOT } from '../utils/version'
|
|
27
|
-
|
|
28
|
-
// =============================================================================
|
|
29
|
-
// Module Types
|
|
30
|
-
// =============================================================================
|
|
31
|
-
|
|
32
|
-
interface ModuleProfile {
|
|
33
|
-
description: string
|
|
34
|
-
modules: string[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
interface ModuleConfig {
|
|
38
|
-
description: string
|
|
39
|
-
version: string
|
|
40
|
-
profiles: Record<string, ModuleProfile>
|
|
41
|
-
default: string
|
|
42
|
-
commandProfiles: Record<string, string>
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// =============================================================================
|
|
46
|
-
// Modular Template Composition (PRJ-94)
|
|
47
|
-
// =============================================================================
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Load module configuration
|
|
51
|
-
*/
|
|
52
|
-
async function loadModuleConfig(): Promise<ModuleConfig | null> {
|
|
53
|
-
try {
|
|
54
|
-
const configPath = path.join(PACKAGE_ROOT, 'templates/global/modules/module-config.json')
|
|
55
|
-
const content = await fs.readFile(configPath, 'utf-8')
|
|
56
|
-
return JSON.parse(content) as ModuleConfig
|
|
57
|
-
} catch {
|
|
58
|
-
return null
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Compose global template from modules based on profile
|
|
64
|
-
* @param profile - Profile name ('full', 'standard', 'minimal') or null for default
|
|
65
|
-
* @returns Composed template content with markers
|
|
66
|
-
*/
|
|
67
|
-
export async function composeGlobalTemplate(profile?: string): Promise<string> {
|
|
68
|
-
const config = await loadModuleConfig()
|
|
69
|
-
const modulesDir = path.join(PACKAGE_ROOT, 'templates/global/modules')
|
|
70
|
-
|
|
71
|
-
// Fallback to legacy template if config not found
|
|
72
|
-
if (!config) {
|
|
73
|
-
const legacyPath = path.join(PACKAGE_ROOT, 'templates/global/CLAUDE.md')
|
|
74
|
-
return fs.readFile(legacyPath, 'utf-8')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const profileName = profile || config.default
|
|
78
|
-
const selectedProfile = config.profiles[profileName]
|
|
79
|
-
|
|
80
|
-
if (!selectedProfile) {
|
|
81
|
-
// Fallback to default profile
|
|
82
|
-
const defaultProfile = config.profiles[config.default]
|
|
83
|
-
if (!defaultProfile) {
|
|
84
|
-
const legacyPath = path.join(PACKAGE_ROOT, 'templates/global/CLAUDE.md')
|
|
85
|
-
return fs.readFile(legacyPath, 'utf-8')
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const modules = (selectedProfile || config.profiles[config.default]).modules
|
|
90
|
-
|
|
91
|
-
// Load and compose modules
|
|
92
|
-
const parts: string[] = []
|
|
93
|
-
parts.push('<!-- prjct:start - DO NOT REMOVE THIS MARKER -->')
|
|
94
|
-
|
|
95
|
-
for (const moduleName of modules) {
|
|
96
|
-
try {
|
|
97
|
-
const modulePath = path.join(modulesDir, moduleName)
|
|
98
|
-
const content = await fs.readFile(modulePath, 'utf-8')
|
|
99
|
-
parts.push('')
|
|
100
|
-
parts.push(content)
|
|
101
|
-
} catch {
|
|
102
|
-
// Module not found, skip
|
|
103
|
-
console.warn(`Module not found: ${moduleName}`)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
parts.push('')
|
|
108
|
-
parts.push('<!-- prjct:end - DO NOT REMOVE THIS MARKER -->')
|
|
109
|
-
parts.push('')
|
|
110
|
-
|
|
111
|
-
return parts.join('\n')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Get recommended profile for a command
|
|
116
|
-
*/
|
|
117
|
-
export async function getProfileForCommand(command: string): Promise<string> {
|
|
118
|
-
const config = await loadModuleConfig()
|
|
119
|
-
if (!config) return 'standard'
|
|
120
|
-
return config.commandProfiles[command] || config.default
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// =============================================================================
|
|
124
|
-
// Global Config
|
|
125
|
-
// =============================================================================
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Install documentation files to ~/.prjct-cli/docs/
|
|
129
|
-
*/
|
|
130
|
-
export async function installDocs(): Promise<{ success: boolean; error?: string }> {
|
|
131
|
-
try {
|
|
132
|
-
const docsDir = path.join(os.homedir(), '.prjct-cli', 'docs')
|
|
133
|
-
const templateDocsDir = path.join(PACKAGE_ROOT, 'templates/global/docs')
|
|
134
|
-
|
|
135
|
-
// Ensure docs directory exists
|
|
136
|
-
await fs.mkdir(docsDir, { recursive: true })
|
|
137
|
-
|
|
138
|
-
// Read all doc files from template
|
|
139
|
-
const docFiles = await fs.readdir(templateDocsDir)
|
|
140
|
-
|
|
141
|
-
// Copy each doc file
|
|
142
|
-
for (const file of docFiles) {
|
|
143
|
-
if (file.endsWith('.md')) {
|
|
144
|
-
const srcPath = path.join(templateDocsDir, file)
|
|
145
|
-
const destPath = path.join(docsDir, file)
|
|
146
|
-
const content = await fs.readFile(srcPath, 'utf-8')
|
|
147
|
-
await fs.writeFile(destPath, content, 'utf-8')
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return { success: true }
|
|
152
|
-
} catch (error) {
|
|
153
|
-
return { success: false, error: getErrorMessage(error) }
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Install or update global AI agent configuration (CLAUDE.md / GEMINI.md)
|
|
159
|
-
*/
|
|
160
|
-
export async function installGlobalConfig(): Promise<GlobalConfigResult> {
|
|
161
|
-
const aiProvider = require('./ai-provider')
|
|
162
|
-
const activeProvider = await aiProvider.getActiveProvider()
|
|
163
|
-
const providerName = activeProvider.name
|
|
164
|
-
|
|
165
|
-
// Check if provider is installed
|
|
166
|
-
const detection = await aiProvider.detectProvider(providerName)
|
|
167
|
-
if (!detection.installed && !activeProvider.configDir) {
|
|
168
|
-
return {
|
|
169
|
-
success: false,
|
|
170
|
-
error: `${activeProvider.displayName} not detected`,
|
|
171
|
-
action: 'skipped',
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
// Ensure config directory exists
|
|
177
|
-
await fs.mkdir(activeProvider.configDir, { recursive: true })
|
|
178
|
-
|
|
179
|
-
const globalConfigPath = path.join(activeProvider.configDir, activeProvider.contextFile)
|
|
180
|
-
const templatePath = path.join(PACKAGE_ROOT, 'templates', 'global', activeProvider.contextFile)
|
|
181
|
-
|
|
182
|
-
// Read template content - use modular composition (PRJ-94)
|
|
183
|
-
let templateContent = ''
|
|
184
|
-
try {
|
|
185
|
-
// First try provider-specific template
|
|
186
|
-
templateContent = await fs.readFile(templatePath, 'utf-8')
|
|
187
|
-
} catch (_error) {
|
|
188
|
-
// Use modular composition for Claude (PRJ-94)
|
|
189
|
-
if (providerName === 'claude') {
|
|
190
|
-
try {
|
|
191
|
-
templateContent = await composeGlobalTemplate('standard')
|
|
192
|
-
} catch {
|
|
193
|
-
// Final fallback to legacy template
|
|
194
|
-
const fallbackTemplatePath = path.join(PACKAGE_ROOT, 'templates/global/CLAUDE.md')
|
|
195
|
-
templateContent = await fs.readFile(fallbackTemplatePath, 'utf-8')
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
// Fallback for other providers
|
|
199
|
-
const fallbackTemplatePath = path.join(PACKAGE_ROOT, 'templates/global/CLAUDE.md')
|
|
200
|
-
templateContent = await fs.readFile(fallbackTemplatePath, 'utf-8')
|
|
201
|
-
// If it is Gemini, we should rename Claude to Gemini in the fallback content
|
|
202
|
-
if (providerName === 'gemini') {
|
|
203
|
-
templateContent = templateContent.replace(/Claude/g, 'Gemini')
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check if global config already exists
|
|
209
|
-
let existingContent = ''
|
|
210
|
-
let fileExists = false
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
existingContent = await fs.readFile(globalConfigPath, 'utf-8')
|
|
214
|
-
fileExists = true
|
|
215
|
-
} catch (error) {
|
|
216
|
-
if (isNotFoundError(error)) {
|
|
217
|
-
// File doesn't exist, will create new
|
|
218
|
-
fileExists = false
|
|
219
|
-
} else {
|
|
220
|
-
throw error
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (!fileExists) {
|
|
225
|
-
// Create new file with full template
|
|
226
|
-
await fs.writeFile(globalConfigPath, templateContent, 'utf-8')
|
|
227
|
-
return {
|
|
228
|
-
success: true,
|
|
229
|
-
action: 'created',
|
|
230
|
-
path: globalConfigPath,
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
// File exists - perform intelligent merge
|
|
234
|
-
const startMarker = '<!-- prjct:start - DO NOT REMOVE THIS MARKER -->'
|
|
235
|
-
const endMarker = '<!-- prjct:end - DO NOT REMOVE THIS MARKER -->'
|
|
236
|
-
|
|
237
|
-
// Check if markers exist in existing file
|
|
238
|
-
const hasMarkers =
|
|
239
|
-
existingContent.includes(startMarker) && existingContent.includes(endMarker)
|
|
240
|
-
|
|
241
|
-
if (!hasMarkers) {
|
|
242
|
-
// No markers - append prjct section at the end
|
|
243
|
-
const updatedContent = `${existingContent}\n\n${templateContent}`
|
|
244
|
-
await fs.writeFile(globalConfigPath, updatedContent, 'utf-8')
|
|
245
|
-
return {
|
|
246
|
-
success: true,
|
|
247
|
-
action: 'appended',
|
|
248
|
-
path: globalConfigPath,
|
|
249
|
-
}
|
|
250
|
-
} else {
|
|
251
|
-
// Markers exist - replace content between markers
|
|
252
|
-
const beforeMarker = existingContent.substring(0, existingContent.indexOf(startMarker))
|
|
253
|
-
const afterMarker = existingContent.substring(
|
|
254
|
-
existingContent.indexOf(endMarker) + endMarker.length
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
// Extract prjct section from template
|
|
258
|
-
const prjctSection = templateContent.substring(
|
|
259
|
-
templateContent.indexOf(startMarker),
|
|
260
|
-
templateContent.indexOf(endMarker) + endMarker.length
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
const updatedContent = beforeMarker + prjctSection + afterMarker
|
|
264
|
-
await fs.writeFile(globalConfigPath, updatedContent, 'utf-8')
|
|
265
|
-
return {
|
|
266
|
-
success: true,
|
|
267
|
-
action: 'updated',
|
|
268
|
-
path: globalConfigPath,
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
} catch (error) {
|
|
273
|
-
return {
|
|
274
|
-
success: false,
|
|
275
|
-
error: getErrorMessage(error),
|
|
276
|
-
action: 'failed',
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// =============================================================================
|
|
282
|
-
// Command Installer
|
|
283
|
-
// =============================================================================
|
|
284
|
-
|
|
285
|
-
export class CommandInstaller {
|
|
286
|
-
homeDir: string
|
|
287
|
-
claudeCommandsPath = ''
|
|
288
|
-
claudeConfigPath = ''
|
|
289
|
-
templatesDir: string
|
|
290
|
-
private _initialized = false
|
|
291
|
-
|
|
292
|
-
constructor() {
|
|
293
|
-
this.homeDir = os.homedir()
|
|
294
|
-
this.templatesDir = path.join(PACKAGE_ROOT, 'templates', 'commands')
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
private async ensureInit(): Promise<void> {
|
|
298
|
-
if (this._initialized) return
|
|
299
|
-
|
|
300
|
-
const aiProvider = require('./ai-provider')
|
|
301
|
-
const activeProvider = await aiProvider.getActiveProvider()
|
|
302
|
-
|
|
303
|
-
// Command paths are provider-specific
|
|
304
|
-
if (activeProvider.name === 'gemini') {
|
|
305
|
-
this.claudeCommandsPath = path.join(activeProvider.configDir, 'commands')
|
|
306
|
-
} else {
|
|
307
|
-
// Claude: Commands are in p/ subdirectory to avoid cluttering commands/
|
|
308
|
-
this.claudeCommandsPath = path.join(activeProvider.configDir, 'commands', 'p')
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
this.claudeConfigPath = activeProvider.configDir
|
|
312
|
-
this._initialized = true
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Detect if active provider is installed
|
|
317
|
-
*/
|
|
318
|
-
async detectActiveProvider(): Promise<boolean> {
|
|
319
|
-
await this.ensureInit()
|
|
320
|
-
try {
|
|
321
|
-
await fs.access(this.claudeConfigPath)
|
|
322
|
-
return true
|
|
323
|
-
} catch (error) {
|
|
324
|
-
if (isNotFoundError(error)) {
|
|
325
|
-
return false
|
|
326
|
-
}
|
|
327
|
-
throw error
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Detect if Claude is installed (legacy support)
|
|
333
|
-
*/
|
|
334
|
-
async detectClaude(): Promise<boolean> {
|
|
335
|
-
return this.detectActiveProvider()
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Get list of command files to install
|
|
340
|
-
*/
|
|
341
|
-
async getCommandFiles(): Promise<string[]> {
|
|
342
|
-
try {
|
|
343
|
-
const files = await fs.readdir(this.templatesDir)
|
|
344
|
-
return files.filter((f) => f.endsWith('.md'))
|
|
345
|
-
} catch (_error) {
|
|
346
|
-
// Fallback to core commands if template directory not accessible (ENOENT or other)
|
|
347
|
-
return [
|
|
348
|
-
'init.md',
|
|
349
|
-
'now.md',
|
|
350
|
-
'done.md',
|
|
351
|
-
'ship.md',
|
|
352
|
-
'next.md',
|
|
353
|
-
'idea.md',
|
|
354
|
-
'recap.md',
|
|
355
|
-
'progress.md',
|
|
356
|
-
'stuck.md',
|
|
357
|
-
'context.md',
|
|
358
|
-
'analyze.md',
|
|
359
|
-
'sync.md',
|
|
360
|
-
'roadmap.md',
|
|
361
|
-
'task.md',
|
|
362
|
-
'git.md',
|
|
363
|
-
'fix.md',
|
|
364
|
-
'test.md',
|
|
365
|
-
'cleanup.md',
|
|
366
|
-
'design.md',
|
|
367
|
-
]
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Install commands to active AI agent
|
|
373
|
-
*/
|
|
374
|
-
async installCommands(): Promise<InstallResult> {
|
|
375
|
-
const providerDetected = await this.detectActiveProvider()
|
|
376
|
-
const aiProvider = require('./ai-provider')
|
|
377
|
-
const activeProvider = await aiProvider.getActiveProvider()
|
|
378
|
-
|
|
379
|
-
if (!providerDetected) {
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
error: `${activeProvider.displayName} not detected. Please install it first.`,
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
try {
|
|
387
|
-
// Install the router to enable "p. task" trigger
|
|
388
|
-
await this.installRouter()
|
|
389
|
-
|
|
390
|
-
// Ensure commands directory exists
|
|
391
|
-
await fs.mkdir(this.claudeCommandsPath, { recursive: true })
|
|
392
|
-
|
|
393
|
-
const commandFiles = await this.getCommandFiles()
|
|
394
|
-
const installed: string[] = []
|
|
395
|
-
const errors: Array<{ file: string; error: string }> = []
|
|
396
|
-
|
|
397
|
-
for (const file of commandFiles) {
|
|
398
|
-
try {
|
|
399
|
-
const sourcePath = path.join(this.templatesDir, file)
|
|
400
|
-
const destPath = path.join(this.claudeCommandsPath, file)
|
|
401
|
-
|
|
402
|
-
const content = await fs.readFile(sourcePath, 'utf-8')
|
|
403
|
-
await fs.writeFile(destPath, content, 'utf-8')
|
|
404
|
-
|
|
405
|
-
installed.push(file.replace('.md', ''))
|
|
406
|
-
} catch (error) {
|
|
407
|
-
errors.push({ file, error: getErrorMessage(error) })
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return {
|
|
412
|
-
success: true,
|
|
413
|
-
installed,
|
|
414
|
-
errors,
|
|
415
|
-
path: this.claudeCommandsPath,
|
|
416
|
-
}
|
|
417
|
-
} catch (error) {
|
|
418
|
-
return {
|
|
419
|
-
success: false,
|
|
420
|
-
error: getErrorMessage(error),
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Uninstall commands from Claude
|
|
427
|
-
*/
|
|
428
|
-
async uninstallCommands(): Promise<UninstallResult> {
|
|
429
|
-
try {
|
|
430
|
-
const commandFiles = await this.getCommandFiles()
|
|
431
|
-
const uninstalled: string[] = []
|
|
432
|
-
const errors: Array<{ file: string; error: string }> = []
|
|
433
|
-
|
|
434
|
-
for (const file of commandFiles) {
|
|
435
|
-
try {
|
|
436
|
-
const filePath = path.join(this.claudeCommandsPath, file)
|
|
437
|
-
await fs.unlink(filePath)
|
|
438
|
-
uninstalled.push(file.replace('.md', ''))
|
|
439
|
-
} catch (error) {
|
|
440
|
-
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
441
|
-
errors.push({ file, error: getErrorMessage(error) })
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Try to remove the /p directory if empty
|
|
447
|
-
try {
|
|
448
|
-
await fs.rmdir(this.claudeCommandsPath)
|
|
449
|
-
} catch (_error) {
|
|
450
|
-
// Directory not empty or doesn't exist - that's fine (ENOTEMPTY or ENOENT)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return {
|
|
454
|
-
success: true,
|
|
455
|
-
uninstalled,
|
|
456
|
-
errors,
|
|
457
|
-
}
|
|
458
|
-
} catch (error) {
|
|
459
|
-
return {
|
|
460
|
-
success: false,
|
|
461
|
-
error: getErrorMessage(error),
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Check if commands are already installed
|
|
468
|
-
*/
|
|
469
|
-
async checkInstallation(): Promise<CheckResult> {
|
|
470
|
-
const claudeDetected = await this.detectClaude()
|
|
471
|
-
|
|
472
|
-
if (!claudeDetected) {
|
|
473
|
-
return {
|
|
474
|
-
installed: false,
|
|
475
|
-
claudeDetected: false,
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
try {
|
|
480
|
-
await fs.access(this.claudeCommandsPath)
|
|
481
|
-
const files = await fs.readdir(this.claudeCommandsPath)
|
|
482
|
-
const installedCommands = files
|
|
483
|
-
.filter((f) => f.endsWith('.md'))
|
|
484
|
-
.map((f) => f.replace('.md', ''))
|
|
485
|
-
|
|
486
|
-
return {
|
|
487
|
-
installed: installedCommands.length > 0,
|
|
488
|
-
claudeDetected: true,
|
|
489
|
-
commands: installedCommands,
|
|
490
|
-
path: this.claudeCommandsPath,
|
|
491
|
-
}
|
|
492
|
-
} catch (error) {
|
|
493
|
-
if (isNotFoundError(error)) {
|
|
494
|
-
return {
|
|
495
|
-
installed: false,
|
|
496
|
-
claudeDetected: true,
|
|
497
|
-
commands: [],
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
throw error
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Update commands (reinstall with latest templates)
|
|
506
|
-
*/
|
|
507
|
-
async updateCommands(): Promise<InstallResult> {
|
|
508
|
-
// Simply reinstall - will overwrite with latest templates
|
|
509
|
-
console.log('Updating commands with latest templates...')
|
|
510
|
-
const result = await this.installCommands()
|
|
511
|
-
if (result.success && result.installed) {
|
|
512
|
-
console.log(`Updated ${result.installed.length} commands`)
|
|
513
|
-
}
|
|
514
|
-
return result
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Install to all detected editors (alias for installCommands)
|
|
519
|
-
*/
|
|
520
|
-
async installToAll(): Promise<InstallResult> {
|
|
521
|
-
return await this.installCommands()
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Get installation path for Claude commands
|
|
526
|
-
*/
|
|
527
|
-
async getInstallPath(): Promise<string> {
|
|
528
|
-
await this.ensureInit()
|
|
529
|
-
return this.claudeCommandsPath
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Verify command template exists
|
|
534
|
-
*/
|
|
535
|
-
async verifyTemplate(commandName: string): Promise<boolean> {
|
|
536
|
-
try {
|
|
537
|
-
const templatePath = path.join(this.templatesDir, `${commandName}.md`)
|
|
538
|
-
await fs.access(templatePath)
|
|
539
|
-
return true
|
|
540
|
-
} catch (error) {
|
|
541
|
-
if (isNotFoundError(error)) {
|
|
542
|
-
return false
|
|
543
|
-
}
|
|
544
|
-
throw error
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Install the router (p.md for Claude, p.toml for Gemini) to commands directory
|
|
550
|
-
* This enables the "p. task" natural language trigger
|
|
551
|
-
*/
|
|
552
|
-
async installRouter(): Promise<boolean> {
|
|
553
|
-
const aiProvider = require('./ai-provider')
|
|
554
|
-
const activeProvider = await aiProvider.getActiveProvider()
|
|
555
|
-
const routerFile = activeProvider.name === 'gemini' ? 'p.toml' : 'p.md'
|
|
556
|
-
|
|
557
|
-
try {
|
|
558
|
-
const routerSource = path.join(this.templatesDir, routerFile)
|
|
559
|
-
const routerDest = path.join(activeProvider.configDir, 'commands', routerFile)
|
|
560
|
-
|
|
561
|
-
// Ensure commands directory exists
|
|
562
|
-
await fs.mkdir(path.dirname(routerDest), { recursive: true })
|
|
563
|
-
|
|
564
|
-
const content = await fs.readFile(routerSource, 'utf-8')
|
|
565
|
-
await fs.writeFile(routerDest, content, 'utf-8')
|
|
566
|
-
return true
|
|
567
|
-
} catch (error) {
|
|
568
|
-
if (isNotFoundError(error)) {
|
|
569
|
-
return false
|
|
570
|
-
}
|
|
571
|
-
throw error
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Remove legacy p.*.md files from commands root directory
|
|
577
|
-
* These were replaced by the p/ subdirectory structure in v0.50+
|
|
578
|
-
*/
|
|
579
|
-
async removeLegacyCommands(): Promise<number> {
|
|
580
|
-
const aiProvider = require('./ai-provider')
|
|
581
|
-
const activeProvider = await aiProvider.getActiveProvider()
|
|
582
|
-
const commandsRoot = path.join(activeProvider.configDir, 'commands')
|
|
583
|
-
|
|
584
|
-
let removed = 0
|
|
585
|
-
|
|
586
|
-
try {
|
|
587
|
-
const files = await fs.readdir(commandsRoot)
|
|
588
|
-
const legacyFiles = files.filter((f) => f.startsWith('p.') && f.endsWith('.md'))
|
|
589
|
-
|
|
590
|
-
for (const file of legacyFiles) {
|
|
591
|
-
try {
|
|
592
|
-
await fs.unlink(path.join(commandsRoot, file))
|
|
593
|
-
removed++
|
|
594
|
-
} catch {
|
|
595
|
-
// Ignore errors removing individual files
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
} catch {
|
|
599
|
-
// Ignore errors if directory doesn't exist
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
return removed
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Sync commands - intelligent update that detects and removes orphans
|
|
607
|
-
*/
|
|
608
|
-
async syncCommands(): Promise<SyncResult> {
|
|
609
|
-
const providerDetected = await this.detectActiveProvider()
|
|
610
|
-
|
|
611
|
-
if (!providerDetected) {
|
|
612
|
-
return {
|
|
613
|
-
success: false,
|
|
614
|
-
error: 'AI agent not detected',
|
|
615
|
-
added: 0,
|
|
616
|
-
updated: 0,
|
|
617
|
-
removed: 0,
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
try {
|
|
622
|
-
// Install the p.md router to enable "p. task" trigger
|
|
623
|
-
await this.installRouter()
|
|
624
|
-
|
|
625
|
-
// Ensure commands directory exists
|
|
626
|
-
await fs.mkdir(this.claudeCommandsPath, { recursive: true })
|
|
627
|
-
|
|
628
|
-
// Get current state
|
|
629
|
-
const templateFiles = await this.getCommandFiles()
|
|
630
|
-
let installedFiles: string[] = []
|
|
631
|
-
|
|
632
|
-
try {
|
|
633
|
-
installedFiles = await fs.readdir(this.claudeCommandsPath)
|
|
634
|
-
installedFiles = installedFiles.filter((f) => f.endsWith('.md'))
|
|
635
|
-
} catch (error) {
|
|
636
|
-
if (isNotFoundError(error)) {
|
|
637
|
-
// Directory doesn't exist yet
|
|
638
|
-
installedFiles = []
|
|
639
|
-
} else {
|
|
640
|
-
throw error
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const results: SyncResult = {
|
|
645
|
-
success: true,
|
|
646
|
-
added: 0,
|
|
647
|
-
updated: 0,
|
|
648
|
-
removed: 0,
|
|
649
|
-
errors: [],
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Install/update all template files (always overwrite)
|
|
653
|
-
for (const file of templateFiles) {
|
|
654
|
-
try {
|
|
655
|
-
const sourcePath = path.join(this.templatesDir, file)
|
|
656
|
-
const destPath = path.join(this.claudeCommandsPath, file)
|
|
657
|
-
|
|
658
|
-
// Check if file exists in installed location
|
|
659
|
-
const exists = installedFiles.includes(file)
|
|
660
|
-
|
|
661
|
-
// Read and write (always overwrite to ensure latest version)
|
|
662
|
-
const content = await fs.readFile(sourcePath, 'utf-8')
|
|
663
|
-
await fs.writeFile(destPath, content, 'utf-8')
|
|
664
|
-
|
|
665
|
-
if (!exists) {
|
|
666
|
-
results.added++
|
|
667
|
-
} else {
|
|
668
|
-
results.updated++
|
|
669
|
-
}
|
|
670
|
-
} catch (error) {
|
|
671
|
-
results.errors!.push({ file, error: getErrorMessage(error) })
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Remove legacy p.*.md files from commands root (old naming convention)
|
|
676
|
-
// These were replaced by p/ subdirectory structure
|
|
677
|
-
await this.removeLegacyCommands()
|
|
678
|
-
|
|
679
|
-
return results
|
|
680
|
-
} catch (error) {
|
|
681
|
-
return {
|
|
682
|
-
success: false,
|
|
683
|
-
error: getErrorMessage(error),
|
|
684
|
-
added: 0,
|
|
685
|
-
updated: 0,
|
|
686
|
-
removed: 0,
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Install or update global AI agent configuration (CLAUDE.md / GEMINI.md)
|
|
693
|
-
*/
|
|
694
|
-
async installGlobalConfig(): Promise<GlobalConfigResult> {
|
|
695
|
-
return installGlobalConfig()
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Install documentation files to ~/.prjct-cli/docs/
|
|
700
|
-
*/
|
|
701
|
-
async installDocs(): Promise<{ success: boolean; error?: string }> {
|
|
702
|
-
return installDocs()
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// =============================================================================
|
|
707
|
-
// Multi-Provider Support
|
|
708
|
-
// =============================================================================
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Get installation paths for all providers
|
|
712
|
-
*/
|
|
713
|
-
export function getProviderPaths(): {
|
|
714
|
-
claude: { commands: string; config: string; router: string }
|
|
715
|
-
gemini: { commands: string; config: string; router: string }
|
|
716
|
-
} {
|
|
717
|
-
const homeDir = os.homedir()
|
|
718
|
-
return {
|
|
719
|
-
claude: {
|
|
720
|
-
commands: path.join(homeDir, '.claude', 'commands', 'p'),
|
|
721
|
-
config: path.join(homeDir, '.claude'),
|
|
722
|
-
router: path.join(homeDir, '.claude', 'commands', 'p.md'),
|
|
723
|
-
},
|
|
724
|
-
gemini: {
|
|
725
|
-
commands: path.join(homeDir, '.gemini', 'commands'),
|
|
726
|
-
config: path.join(homeDir, '.gemini'),
|
|
727
|
-
router: path.join(homeDir, '.gemini', 'commands', 'p.toml'),
|
|
728
|
-
},
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/**
|
|
733
|
-
* Check if provider router is installed
|
|
734
|
-
*/
|
|
735
|
-
export async function isRouterInstalled(provider: 'claude' | 'gemini'): Promise<boolean> {
|
|
736
|
-
const paths = getProviderPaths()
|
|
737
|
-
const routerPath = paths[provider].router
|
|
738
|
-
|
|
739
|
-
try {
|
|
740
|
-
await fs.access(routerPath)
|
|
741
|
-
return true
|
|
742
|
-
} catch {
|
|
743
|
-
return false
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
// =============================================================================
|
|
748
|
-
// Exports
|
|
749
|
-
// =============================================================================
|
|
750
|
-
|
|
751
|
-
const commandInstaller = new CommandInstaller()
|
|
752
|
-
export default commandInstaller
|