tarsk 0.4.4 → 0.4.6
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/dist/index.js +447375 -37
- package/package.json +2 -2
- package/dist/agent/agent.error-utils.d.ts +0 -14
- package/dist/agent/agent.error-utils.js +0 -52
- package/dist/agent/agent.event-transformer.d.ts +0 -55
- package/dist/agent/agent.event-transformer.js +0 -175
- package/dist/agent/agent.executor.d.ts +0 -26
- package/dist/agent/agent.executor.js +0 -286
- package/dist/agent/agent.model-resolver.d.ts +0 -22
- package/dist/agent/agent.model-resolver.js +0 -67
- package/dist/agent/agent.process-manager.d.ts +0 -57
- package/dist/agent/agent.process-manager.js +0 -262
- package/dist/agent/agent.processing-state-manager.d.ts +0 -74
- package/dist/agent/agent.processing-state-manager.js +0 -87
- package/dist/agent/agent.prompt-loader.d.ts +0 -16
- package/dist/agent/agent.prompt-loader.js +0 -227
- package/dist/agent/agent.subagent-executor.d.ts +0 -35
- package/dist/agent/agent.subagent-executor.js +0 -135
- package/dist/bun/index.d.ts +0 -2
- package/dist/bun/index.js +0 -165
- package/dist/cli.d.ts +0 -3
- package/dist/cli.js +0 -22
- package/dist/core/crypto.d.ts +0 -29
- package/dist/core/crypto.js +0 -166
- package/dist/core/dev-server-cache.d.ts +0 -46
- package/dist/core/dev-server-cache.js +0 -59
- package/dist/core/env-manager.d.ts +0 -3
- package/dist/core/env-manager.js +0 -60
- package/dist/core/error-responses.d.ts +0 -61
- package/dist/core/error-responses.js +0 -64
- package/dist/core/logger.d.ts +0 -10
- package/dist/core/logger.js +0 -47
- package/dist/core/paths.d.ts +0 -22
- package/dist/core/paths.js +0 -26
- package/dist/core/random-words.d.ts +0 -18
- package/dist/core/random-words.js +0 -135
- package/dist/core/response-builder.d.ts +0 -50
- package/dist/core/response-builder.js +0 -56
- package/dist/core/route-helpers.d.ts +0 -47
- package/dist/core/route-helpers.js +0 -54
- package/dist/core/run-command-detector.d.ts +0 -26
- package/dist/core/run-command-detector.js +0 -98
- package/dist/core/stream-helper.d.ts +0 -44
- package/dist/core/stream-helper.js +0 -50
- package/dist/core/utils.d.ts +0 -43
- package/dist/core/utils.js +0 -113
- package/dist/core/validation.d.ts +0 -10
- package/dist/core/validation.js +0 -20
- package/dist/database/database.d.ts +0 -40
- package/dist/database/database.encryption.d.ts +0 -33
- package/dist/database/database.encryption.js +0 -62
- package/dist/database/database.js +0 -480
- package/dist/database/database.state.d.ts +0 -52
- package/dist/database/database.state.js +0 -119
- package/dist/database/database.test-utils.d.ts +0 -22
- package/dist/database/database.test-utils.js +0 -39
- package/dist/database/database.types.d.ts +0 -3
- package/dist/database/database.types.js +0 -2
- package/dist/features/agents/agents.manager.d.ts +0 -24
- package/dist/features/agents/agents.manager.js +0 -200
- package/dist/features/ask-user/ask-user.routes.d.ts +0 -9
- package/dist/features/ask-user/ask-user.routes.js +0 -35
- package/dist/features/chat/chat-delete.route.d.ts +0 -15
- package/dist/features/chat/chat-delete.route.js +0 -36
- package/dist/features/chat/chat-post.route.d.ts +0 -11
- package/dist/features/chat/chat-post.route.js +0 -270
- package/dist/features/chat/chat-stop.route.d.ts +0 -21
- package/dist/features/chat/chat-stop.route.js +0 -22
- package/dist/features/chat/chat-subscribe.route.d.ts +0 -9
- package/dist/features/chat/chat-subscribe.route.js +0 -71
- package/dist/features/chat/chat.routes.d.ts +0 -22
- package/dist/features/chat/chat.routes.js +0 -29
- package/dist/features/conversations/conversations-delete.route.d.ts +0 -16
- package/dist/features/conversations/conversations-delete.route.js +0 -34
- package/dist/features/conversations/conversations-get-all.route.d.ts +0 -16
- package/dist/features/conversations/conversations-get-all.route.js +0 -39
- package/dist/features/conversations/conversations-get-by-id.route.d.ts +0 -16
- package/dist/features/conversations/conversations-get-by-id.route.js +0 -38
- package/dist/features/conversations/conversations-get-deleted.route.d.ts +0 -15
- package/dist/features/conversations/conversations-get-deleted.route.js +0 -34
- package/dist/features/conversations/conversations-get-messages.route.d.ts +0 -16
- package/dist/features/conversations/conversations-get-messages.route.js +0 -49
- package/dist/features/conversations/conversations.content.d.ts +0 -28
- package/dist/features/conversations/conversations.content.js +0 -142
- package/dist/features/conversations/conversations.database.d.ts +0 -108
- package/dist/features/conversations/conversations.database.js +0 -373
- package/dist/features/conversations/conversations.manager.d.ts +0 -130
- package/dist/features/conversations/conversations.manager.js +0 -162
- package/dist/features/conversations/conversations.routes.d.ts +0 -21
- package/dist/features/conversations/conversations.routes.js +0 -38
- package/dist/features/conversations/token-usage.route.d.ts +0 -41
- package/dist/features/conversations/token-usage.route.js +0 -419
- package/dist/features/git/git-commit.route.d.ts +0 -12
- package/dist/features/git/git-commit.route.js +0 -39
- package/dist/features/git/git-create-branch.route.d.ts +0 -28
- package/dist/features/git/git-create-branch.route.js +0 -119
- package/dist/features/git/git-create-pr.route.d.ts +0 -13
- package/dist/features/git/git-create-pr.route.js +0 -50
- package/dist/features/git/git-create-repo.route.d.ts +0 -14
- package/dist/features/git/git-create-repo.route.js +0 -108
- package/dist/features/git/git-diff.route.d.ts +0 -30
- package/dist/features/git/git-diff.route.js +0 -189
- package/dist/features/git/git-fetch.route.d.ts +0 -12
- package/dist/features/git/git-fetch.route.js +0 -31
- package/dist/features/git/git-generate-commit-message.route.d.ts +0 -12
- package/dist/features/git/git-generate-commit-message.route.js +0 -76
- package/dist/features/git/git-generate-pr-info.route.d.ts +0 -13
- package/dist/features/git/git-generate-pr-info.route.js +0 -147
- package/dist/features/git/git-github-status.route.d.ts +0 -16
- package/dist/features/git/git-github-status.route.js +0 -68
- package/dist/features/git/git-log.route.d.ts +0 -17
- package/dist/features/git/git-log.route.js +0 -33
- package/dist/features/git/git-pr-status.route.d.ts +0 -15
- package/dist/features/git/git-pr-status.route.js +0 -33
- package/dist/features/git/git-pull.route.d.ts +0 -12
- package/dist/features/git/git-pull.route.js +0 -35
- package/dist/features/git/git-push.route.d.ts +0 -12
- package/dist/features/git/git-push.route.js +0 -46
- package/dist/features/git/git-status-cache.database.d.ts +0 -7
- package/dist/features/git/git-status-cache.database.js +0 -53
- package/dist/features/git/git-status.route.d.ts +0 -15
- package/dist/features/git/git-status.route.js +0 -62
- package/dist/features/git/git-sync-branch.route.d.ts +0 -4
- package/dist/features/git/git-sync-branch.route.js +0 -208
- package/dist/features/git/git-unified-status.route.d.ts +0 -30
- package/dist/features/git/git-unified-status.route.js +0 -165
- package/dist/features/git/git-username.route.d.ts +0 -3
- package/dist/features/git/git-username.route.js +0 -24
- package/dist/features/git/git.manager.d.ts +0 -139
- package/dist/features/git/git.manager.js +0 -352
- package/dist/features/git/git.routes.d.ts +0 -4
- package/dist/features/git/git.routes.js +0 -116
- package/dist/features/git/git.utils.d.ts +0 -82
- package/dist/features/git/git.utils.js +0 -1040
- package/dist/features/mcp/mcp.config.d.ts +0 -27
- package/dist/features/mcp/mcp.config.js +0 -148
- package/dist/features/mcp/mcp.manager.d.ts +0 -61
- package/dist/features/mcp/mcp.manager.js +0 -254
- package/dist/features/mcp/mcp.popular.json +0 -103
- package/dist/features/mcp/mcp.routes.d.ts +0 -13
- package/dist/features/mcp/mcp.routes.js +0 -159
- package/dist/features/mcp/mcp.types.d.ts +0 -80
- package/dist/features/mcp/mcp.types.js +0 -8
- package/dist/features/metadata/metadata.manager.d.ts +0 -126
- package/dist/features/metadata/metadata.manager.js +0 -423
- package/dist/features/models/model-info-aihubmix.d.ts +0 -25
- package/dist/features/models/model-info-aihubmix.js +0 -117
- package/dist/features/models/model-info-openrouter.d.ts +0 -25
- package/dist/features/models/model-info-openrouter.js +0 -104
- package/dist/features/models/model-info.d.ts +0 -37
- package/dist/features/models/model-info.js +0 -39
- package/dist/features/models/models-catalog.d.ts +0 -49
- package/dist/features/models/models-catalog.js +0 -80
- package/dist/features/models/models-catalog.route.d.ts +0 -43
- package/dist/features/models/models-catalog.route.js +0 -15
- package/dist/features/models/models-get-available.route.d.ts +0 -36
- package/dist/features/models/models-get-available.route.js +0 -66
- package/dist/features/models/models-get-enabled.route.d.ts +0 -33
- package/dist/features/models/models-get-enabled.route.js +0 -45
- package/dist/features/models/models-get-model-info.route.d.ts +0 -31
- package/dist/features/models/models-get-model-info.route.js +0 -84
- package/dist/features/models/models-model-disable.route.d.ts +0 -15
- package/dist/features/models/models-model-disable.route.js +0 -20
- package/dist/features/models/models-model-enable.route.d.ts +0 -13
- package/dist/features/models/models-model-enable.route.js +0 -20
- package/dist/features/models/models-provider-refresh.route.d.ts +0 -17
- package/dist/features/models/models-provider-refresh.route.js +0 -20
- package/dist/features/models/models.manager.d.ts +0 -58
- package/dist/features/models/models.manager.js +0 -138
- package/dist/features/models/models.routes.d.ts +0 -18
- package/dist/features/models/models.routes.js +0 -83
- package/dist/features/models/open-router-models.d.ts +0 -38
- package/dist/features/models/open-router-models.js +0 -73
- package/dist/features/models/openai-models.d.ts +0 -63
- package/dist/features/models/openai-models.js +0 -150
- package/dist/features/onboarding/onboarding-get-git-check.route.d.ts +0 -11
- package/dist/features/onboarding/onboarding-get-git-check.route.js +0 -28
- package/dist/features/onboarding/onboarding-get-status.route.d.ts +0 -12
- package/dist/features/onboarding/onboarding-get-status.route.js +0 -15
- package/dist/features/onboarding/onboarding-post-complete.route.d.ts +0 -12
- package/dist/features/onboarding/onboarding-post-complete.route.js +0 -15
- package/dist/features/onboarding/onboarding-post-reset.route.d.ts +0 -12
- package/dist/features/onboarding/onboarding-post-reset.route.js +0 -15
- package/dist/features/onboarding/onboarding.routes.d.ts +0 -18
- package/dist/features/onboarding/onboarding.routes.js +0 -28
- package/dist/features/project-todos/project-todos.database.d.ts +0 -38
- package/dist/features/project-todos/project-todos.database.js +0 -91
- package/dist/features/project-todos/project-todos.routes.d.ts +0 -4
- package/dist/features/project-todos/project-todos.routes.js +0 -94
- package/dist/features/projects/projects-ai-files.route.d.ts +0 -148
- package/dist/features/projects/projects-ai-files.route.js +0 -425
- package/dist/features/projects/projects-commands.route.d.ts +0 -27
- package/dist/features/projects/projects-commands.route.js +0 -39
- package/dist/features/projects/projects-create.route.d.ts +0 -19
- package/dist/features/projects/projects-create.route.js +0 -37
- package/dist/features/projects/projects-delete.route.d.ts +0 -24
- package/dist/features/projects/projects-delete.route.js +0 -34
- package/dist/features/projects/projects-get.route.d.ts +0 -47
- package/dist/features/projects/projects-get.route.js +0 -36
- package/dist/features/projects/projects-list.route.d.ts +0 -58
- package/dist/features/projects/projects-list.route.js +0 -59
- package/dist/features/projects/projects-open-folder.route.d.ts +0 -10
- package/dist/features/projects/projects-open-folder.route.js +0 -11
- package/dist/features/projects/projects-open.route.d.ts +0 -26
- package/dist/features/projects/projects-open.route.js +0 -49
- package/dist/features/projects/projects-package-scripts.route.d.ts +0 -15
- package/dist/features/projects/projects-package-scripts.route.js +0 -96
- package/dist/features/projects/projects-run-command.route.d.ts +0 -8
- package/dist/features/projects/projects-run-command.route.js +0 -21
- package/dist/features/projects/projects-run.route.d.ts +0 -51
- package/dist/features/projects/projects-run.route.js +0 -74
- package/dist/features/projects/projects-update.route.d.ts +0 -24
- package/dist/features/projects/projects-update.route.js +0 -81
- package/dist/features/projects/projects.creator.d.ts +0 -33
- package/dist/features/projects/projects.creator.js +0 -555
- package/dist/features/projects/projects.database.d.ts +0 -61
- package/dist/features/projects/projects.database.js +0 -212
- package/dist/features/projects/projects.manager.d.ts +0 -291
- package/dist/features/projects/projects.manager.js +0 -426
- package/dist/features/projects/projects.open-with.d.ts +0 -27
- package/dist/features/projects/projects.open-with.js +0 -156
- package/dist/features/projects/projects.routes.d.ts +0 -20
- package/dist/features/projects/projects.routes.js +0 -255
- package/dist/features/projects/terminal-session-manager.d.ts +0 -55
- package/dist/features/projects/terminal-session-manager.js +0 -90
- package/dist/features/providers/provider-resolver.d.ts +0 -13
- package/dist/features/providers/provider-resolver.js +0 -22
- package/dist/features/providers/providers-get-credits.route.d.ts +0 -15
- package/dist/features/providers/providers-get-credits.route.js +0 -51
- package/dist/features/providers/providers-get.route.d.ts +0 -16
- package/dist/features/providers/providers-get.route.js +0 -32
- package/dist/features/providers/providers-open-external.route.d.ts +0 -15
- package/dist/features/providers/providers-open-external.route.js +0 -49
- package/dist/features/providers/providers-post-bulk-keys.route.d.ts +0 -14
- package/dist/features/providers/providers-post-bulk-keys.route.js +0 -31
- package/dist/features/providers/providers-post-keys.route.d.ts +0 -14
- package/dist/features/providers/providers-post-keys.route.js +0 -25
- package/dist/features/providers/providers.routes.d.ts +0 -19
- package/dist/features/providers/providers.routes.js +0 -31
- package/dist/features/rules/rules-post.route.d.ts +0 -43
- package/dist/features/rules/rules-post.route.js +0 -89
- package/dist/features/rules/rules.manager.d.ts +0 -36
- package/dist/features/rules/rules.manager.js +0 -203
- package/dist/features/rules/rules.routes.d.ts +0 -12
- package/dist/features/rules/rules.routes.js +0 -13
- package/dist/features/run/run-get-running.route.d.ts +0 -15
- package/dist/features/run/run-get-running.route.js +0 -21
- package/dist/features/run/run-post-start.route.d.ts +0 -8
- package/dist/features/run/run-post-start.route.js +0 -21
- package/dist/features/run/run-post-stop.route.d.ts +0 -15
- package/dist/features/run/run-post-stop.route.js +0 -24
- package/dist/features/run/run-post-suggest.route.d.ts +0 -15
- package/dist/features/run/run-post-suggest.route.js +0 -21
- package/dist/features/run/run-put-command.route.d.ts +0 -15
- package/dist/features/run/run-put-command.route.js +0 -24
- package/dist/features/run/run.routes.d.ts +0 -19
- package/dist/features/run/run.routes.js +0 -31
- package/dist/features/scaffold/index.d.ts +0 -7
- package/dist/features/scaffold/index.js +0 -5
- package/dist/features/scaffold/scaffold-get-templates.route.d.ts +0 -27
- package/dist/features/scaffold/scaffold-get-templates.route.js +0 -17
- package/dist/features/scaffold/scaffold-post.route.d.ts +0 -8
- package/dist/features/scaffold/scaffold-post.route.js +0 -30
- package/dist/features/scaffold/scaffold.routes.d.ts +0 -10
- package/dist/features/scaffold/scaffold.routes.js +0 -16
- package/dist/features/scaffold/scaffold.runner.d.ts +0 -48
- package/dist/features/scaffold/scaffold.runner.js +0 -475
- package/dist/features/scaffold/scaffold.types.d.ts +0 -26
- package/dist/features/scaffold/scaffold.types.js +0 -5
- package/dist/features/skills/skills.activation.d.ts +0 -31
- package/dist/features/skills/skills.activation.js +0 -155
- package/dist/features/skills/skills.manager.d.ts +0 -35
- package/dist/features/skills/skills.manager.js +0 -251
- package/dist/features/slash-commands/slash-commands-delete.route.d.ts +0 -23
- package/dist/features/slash-commands/slash-commands-delete.route.js +0 -36
- package/dist/features/slash-commands/slash-commands-get.route.d.ts +0 -53
- package/dist/features/slash-commands/slash-commands-get.route.js +0 -54
- package/dist/features/slash-commands/slash-commands-post.route.d.ts +0 -39
- package/dist/features/slash-commands/slash-commands-post.route.js +0 -70
- package/dist/features/slash-commands/slash-commands-put.route.d.ts +0 -23
- package/dist/features/slash-commands/slash-commands-put.route.js +0 -36
- package/dist/features/slash-commands/slash-commands.manager.d.ts +0 -46
- package/dist/features/slash-commands/slash-commands.manager.js +0 -265
- package/dist/features/slash-commands/slash-commands.routes.d.ts +0 -13
- package/dist/features/slash-commands/slash-commands.routes.js +0 -20
- package/dist/features/threads/threads-ai-files.route.d.ts +0 -153
- package/dist/features/threads/threads-ai-files.route.js +0 -287
- package/dist/features/threads/threads-conversation-folder-path.route.d.ts +0 -14
- package/dist/features/threads/threads-conversation-folder-path.route.js +0 -23
- package/dist/features/threads/threads-create.route.d.ts +0 -22
- package/dist/features/threads/threads-create.route.js +0 -60
- package/dist/features/threads/threads-delete.route.d.ts +0 -25
- package/dist/features/threads/threads-delete.route.js +0 -35
- package/dist/features/threads/threads-files.route.d.ts +0 -15
- package/dist/features/threads/threads-files.route.js +0 -20
- package/dist/features/threads/threads-fix-comments.route.d.ts +0 -26
- package/dist/features/threads/threads-fix-comments.route.js +0 -45
- package/dist/features/threads/threads-get.route.d.ts +0 -30
- package/dist/features/threads/threads-get.route.js +0 -38
- package/dist/features/threads/threads-list.route.d.ts +0 -56
- package/dist/features/threads/threads-list.route.js +0 -58
- package/dist/features/threads/threads-messages.route.d.ts +0 -28
- package/dist/features/threads/threads-messages.route.js +0 -110
- package/dist/features/threads/threads-open.route.d.ts +0 -26
- package/dist/features/threads/threads-open.route.js +0 -62
- package/dist/features/threads/threads-select.route.d.ts +0 -25
- package/dist/features/threads/threads-select.route.js +0 -35
- package/dist/features/threads/threads-update.route.d.ts +0 -15
- package/dist/features/threads/threads-update.route.js +0 -30
- package/dist/features/threads/threads.database.d.ts +0 -68
- package/dist/features/threads/threads.database.js +0 -215
- package/dist/features/threads/threads.manager.d.ts +0 -204
- package/dist/features/threads/threads.manager.js +0 -505
- package/dist/features/threads/threads.routes.d.ts +0 -20
- package/dist/features/threads/threads.routes.js +0 -230
- package/dist/features/todos/todos.database.d.ts +0 -14
- package/dist/features/todos/todos.database.js +0 -31
- package/dist/features/updates/updates.routes.d.ts +0 -13
- package/dist/features/updates/updates.routes.js +0 -40
- package/dist/index.d.ts +0 -3
- package/dist/project-analyzer.d.ts +0 -26
- package/dist/project-analyzer.js +0 -307
- package/dist/server.d.ts +0 -12
- package/dist/server.js +0 -142
- package/dist/tools/agent-tool.d.ts +0 -49
- package/dist/tools/agent-tool.js +0 -131
- package/dist/tools/ask-user.d.ts +0 -25
- package/dist/tools/ask-user.js +0 -74
- package/dist/tools/ast-grep.d.ts +0 -28
- package/dist/tools/ast-grep.js +0 -273
- package/dist/tools/bash.d.ts +0 -33
- package/dist/tools/bash.js +0 -186
- package/dist/tools/edit-diff.d.ts +0 -24
- package/dist/tools/edit-diff.js +0 -136
- package/dist/tools/edit.d.ts +0 -28
- package/dist/tools/edit.js +0 -78
- package/dist/tools/find.d.ts +0 -31
- package/dist/tools/find.js +0 -117
- package/dist/tools/grep.d.ts +0 -37
- package/dist/tools/grep.js +0 -231
- package/dist/tools/index.d.ts +0 -93
- package/dist/tools/index.js +0 -110
- package/dist/tools/ls.d.ts +0 -31
- package/dist/tools/ls.js +0 -108
- package/dist/tools/mcp-tools.d.ts +0 -31
- package/dist/tools/mcp-tools.js +0 -59
- package/dist/tools/path-utils.d.ts +0 -14
- package/dist/tools/path-utils.js +0 -87
- package/dist/tools/read.d.ts +0 -27
- package/dist/tools/read.js +0 -86
- package/dist/tools/resolve-bin.d.ts +0 -5
- package/dist/tools/resolve-bin.js +0 -28
- package/dist/tools/shell.d.ts +0 -7
- package/dist/tools/shell.js +0 -143
- package/dist/tools/skill-reference-tool.d.ts +0 -30
- package/dist/tools/skill-reference-tool.js +0 -171
- package/dist/tools/skill-tool.d.ts +0 -33
- package/dist/tools/skill-tool.js +0 -213
- package/dist/tools/todo.d.ts +0 -20
- package/dist/tools/todo.js +0 -168
- package/dist/tools/tool-helpers.d.ts +0 -78
- package/dist/tools/tool-helpers.js +0 -109
- package/dist/tools/truncate.d.ts +0 -31
- package/dist/tools/truncate.js +0 -164
- package/dist/tools/write.d.ts +0 -21
- package/dist/tools/write.js +0 -65
|
@@ -1,1040 +0,0 @@
|
|
|
1
|
-
import { spawnProcess } from "../../core/utils.js";
|
|
2
|
-
import { existsSync, readFileSync, statSync } from "fs";
|
|
3
|
-
import { isAbsolute, normalize, resolve, join } from "path";
|
|
4
|
-
import { getDataDir } from "../../core/paths.js";
|
|
5
|
-
import { getModelsCatalog } from "../models/models-catalog.js";
|
|
6
|
-
import { completeSimple, getModel, } from "@mariozechner/pi-ai";
|
|
7
|
-
const PROVIDER_NAME_TO_PI = {
|
|
8
|
-
anthropic: "anthropic",
|
|
9
|
-
openai: "openai",
|
|
10
|
-
google: "google",
|
|
11
|
-
groq: "groq",
|
|
12
|
-
cerebras: "cerebras",
|
|
13
|
-
xai: "xai",
|
|
14
|
-
openrouter: "openrouter",
|
|
15
|
-
"github copilot": "github-copilot",
|
|
16
|
-
minimax: "minimax",
|
|
17
|
-
"kimi coding plan": "kimi-coding",
|
|
18
|
-
"hugging face": "huggingface",
|
|
19
|
-
codex: "openai-codex",
|
|
20
|
-
mistral: "mistral",
|
|
21
|
-
};
|
|
22
|
-
export const DEFAULT_MODEL_MAP = {
|
|
23
|
-
Anthropic: "claude-3-5-sonnet-20241022",
|
|
24
|
-
OpenAI: "gpt-4o-mini",
|
|
25
|
-
OpenRouter: "anthropic/claude-3.5-sonnet",
|
|
26
|
-
Groq: "llama-3.3-70b-versatile",
|
|
27
|
-
DeepSeek: "deepseek-chat",
|
|
28
|
-
};
|
|
29
|
-
export function resolveModelForGit(providerName, modelId, apiUrl) {
|
|
30
|
-
const piProvider = PROVIDER_NAME_TO_PI[providerName.toLowerCase()];
|
|
31
|
-
if (piProvider) {
|
|
32
|
-
try {
|
|
33
|
-
return getModel(piProvider, modelId);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// fall through to custom model
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (!apiUrl)
|
|
40
|
-
throw new Error(`No API URL configured for provider: ${providerName}`);
|
|
41
|
-
const apiType = apiUrl.includes("/anthropic/") || apiUrl.includes("api.anthropic.com")
|
|
42
|
-
? "anthropic-messages"
|
|
43
|
-
: "openai-completions";
|
|
44
|
-
return {
|
|
45
|
-
id: modelId,
|
|
46
|
-
name: modelId,
|
|
47
|
-
api: apiType,
|
|
48
|
-
provider: providerName.toLowerCase(),
|
|
49
|
-
baseUrl: apiUrl,
|
|
50
|
-
reasoning: false,
|
|
51
|
-
input: ["text"],
|
|
52
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
53
|
-
contextWindow: 128000,
|
|
54
|
-
maxTokens: 16384,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
async function getAllProviders() {
|
|
58
|
-
const catalog = await getModelsCatalog();
|
|
59
|
-
if (!catalog)
|
|
60
|
-
return [];
|
|
61
|
-
return Object.values(catalog);
|
|
62
|
-
}
|
|
63
|
-
async function findProviderByName(name) {
|
|
64
|
-
const providers = await getAllProviders();
|
|
65
|
-
return providers.find((p) => p.name.toLowerCase() === name.toLowerCase()) ?? null;
|
|
66
|
-
}
|
|
67
|
-
async function overlayCatalogApi(prov) {
|
|
68
|
-
// The catalog already has the API info, so just return as-is
|
|
69
|
-
return prov;
|
|
70
|
-
}
|
|
71
|
-
export async function resolveProviderAndKey(metadataProviderKeys, preferredProvider) {
|
|
72
|
-
if (preferredProvider) {
|
|
73
|
-
const prov = await findProviderByName(preferredProvider);
|
|
74
|
-
if (prov) {
|
|
75
|
-
const envKey = prov.env.length > 0 ? prov.env[0] : undefined;
|
|
76
|
-
// Try both exact name match and case-insensitive match for metadata keys
|
|
77
|
-
const key = envKey && process.env[envKey]
|
|
78
|
-
? process.env[envKey]
|
|
79
|
-
: (metadataProviderKeys[prov.name] ??
|
|
80
|
-
(() => {
|
|
81
|
-
const foundKey = Object.keys(metadataProviderKeys).find((k) => k.toLowerCase() === prov.name.toLowerCase());
|
|
82
|
-
return foundKey ? metadataProviderKeys[foundKey] : undefined;
|
|
83
|
-
})());
|
|
84
|
-
if (key)
|
|
85
|
-
return { provider: await overlayCatalogApi(prov), apiKey: key };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const providerPriority = ["Anthropic", "OpenAI", "OpenRouter", "Groq", "DeepSeek"];
|
|
89
|
-
for (const name of providerPriority) {
|
|
90
|
-
const prov = await findProviderByName(name);
|
|
91
|
-
if (prov && prov.env.length > 0) {
|
|
92
|
-
const envKey = process.env[prov.env[0]];
|
|
93
|
-
if (envKey)
|
|
94
|
-
return { provider: await overlayCatalogApi(prov), apiKey: envKey };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
for (const name of providerPriority) {
|
|
98
|
-
const prov = await findProviderByName(name);
|
|
99
|
-
if (prov && metadataProviderKeys[prov.name]) {
|
|
100
|
-
return { provider: await overlayCatalogApi(prov), apiKey: metadataProviderKeys[prov.name] };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const allProviders = await getAllProviders();
|
|
104
|
-
for (const prov of allProviders) {
|
|
105
|
-
if (prov.api && prov.env.length > 0) {
|
|
106
|
-
const envKey = process.env[prov.env[0]];
|
|
107
|
-
if (envKey)
|
|
108
|
-
return { provider: await overlayCatalogApi(prov), apiKey: envKey };
|
|
109
|
-
const metadataKey = Object.keys(metadataProviderKeys).find((k) => k.toLowerCase() === prov.name.toLowerCase());
|
|
110
|
-
if (metadataKey)
|
|
111
|
-
return {
|
|
112
|
-
provider: await overlayCatalogApi(prov),
|
|
113
|
-
apiKey: metadataProviderKeys[metadataKey],
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
export async function generatePRInfoWithAI(commitLog, detailedCommits, diff, currentBranch, baseBranch, metadataManager, model, provider) {
|
|
120
|
-
// Build the prompt content - prefer commits for PR generation, fall back to diff
|
|
121
|
-
let promptContent;
|
|
122
|
-
if (commitLog.trim()) {
|
|
123
|
-
// We have commits between branches - use those for PR info
|
|
124
|
-
promptContent = `Based on the following commits in branch "${currentBranch}" compared to "${baseBranch}", generate a pull request title and description.
|
|
125
|
-
|
|
126
|
-
Commit log:
|
|
127
|
-
${commitLog}
|
|
128
|
-
|
|
129
|
-
Detailed commit messages:
|
|
130
|
-
${detailedCommits || "(no details available)"}
|
|
131
|
-
|
|
132
|
-
${diff.trim() ? `\nAdditionally, there are uncommitted changes:\n${diff.length > 1500 ? diff.substring(0, 1500) + "\n...(truncated)" : diff}` : ""}`;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// No commits between branches - use just the diff
|
|
136
|
-
const truncatedDiff = diff.length > 3000 ? diff.substring(0, 3000) + "\n...(truncated)" : diff;
|
|
137
|
-
promptContent = `Based on the following git diff, generate a pull request title and description.
|
|
138
|
-
|
|
139
|
-
Git diff:
|
|
140
|
-
${truncatedDiff}`;
|
|
141
|
-
}
|
|
142
|
-
const prompt = `${promptContent}
|
|
143
|
-
|
|
144
|
-
Follow these guidelines:
|
|
145
|
-
- Title: Concise, descriptive, under 72 characters
|
|
146
|
-
- Description: Clear explanation of changes, why they were made, and any relevant context
|
|
147
|
-
- Use markdown formatting for the description (bullet points, headers, etc.)
|
|
148
|
-
- Be professional and clear
|
|
149
|
-
- If multiple changes, group them logically in the description
|
|
150
|
-
|
|
151
|
-
Generate the response in this exact format:
|
|
152
|
-
TITLE: <title here>
|
|
153
|
-
DESCRIPTION: <description here>`;
|
|
154
|
-
try {
|
|
155
|
-
const providerKeys = await metadataManager.getProviderKeys();
|
|
156
|
-
const resolved = await resolveProviderAndKey(providerKeys, provider);
|
|
157
|
-
if (!resolved?.provider?.api) {
|
|
158
|
-
const errorMsg = provider
|
|
159
|
-
? `AI provider "${provider}" is specified but no API key is configured. Please add the API key in settings.`
|
|
160
|
-
: `No AI provider configured. Please configure a provider in settings.`;
|
|
161
|
-
console.log(`[pr-generation] ${errorMsg}`);
|
|
162
|
-
return { title: "Update", description: "" };
|
|
163
|
-
}
|
|
164
|
-
let selectedModel = model;
|
|
165
|
-
if (selectedModel) {
|
|
166
|
-
selectedModel = selectedModel.replace(`${resolved.provider.name.toLowerCase()}/`, "");
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
selectedModel = DEFAULT_MODEL_MAP[resolved.provider.name] || "default";
|
|
170
|
-
}
|
|
171
|
-
const resolvedModel = resolveModelForGit(resolved.provider.name, selectedModel, resolved.provider.api);
|
|
172
|
-
console.log(`[pr-generation] Using model: ${resolvedModel.name} provider: ${resolved.provider.name}. key: ${resolved.apiKey ? "exists" : "undefined"} prompt length: ${prompt.length}`);
|
|
173
|
-
const response = await completeSimple(resolvedModel, {
|
|
174
|
-
messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
|
|
175
|
-
}, { apiKey: resolved.apiKey });
|
|
176
|
-
const prInfo = response.content
|
|
177
|
-
.filter((b) => b.type === "text")
|
|
178
|
-
.map((b) => b.text)
|
|
179
|
-
.join("")
|
|
180
|
-
.trim();
|
|
181
|
-
const titleMatch = prInfo.match(/TITLE:\s*(.+?)(?:\nDESCRIPTION:|$)/s);
|
|
182
|
-
const descriptionMatch = prInfo.match(/DESCRIPTION:\s*(.+?)$/s);
|
|
183
|
-
const title = titleMatch ? titleMatch[1].trim() : "Update";
|
|
184
|
-
const description = descriptionMatch ? descriptionMatch[1].trim() : "";
|
|
185
|
-
return { title, description };
|
|
186
|
-
}
|
|
187
|
-
catch (error) {
|
|
188
|
-
console.log(`[pr-generation] Error in generatePRInfoWithAI: ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
-
return { title: "Update", description: "" };
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
export async function generateCommitMessageWithAI(diff, metadataManager, model, provider) {
|
|
193
|
-
const truncatedDiff = diff.length > 3000 ? diff.substring(0, 3000) + "\n...(truncated)" : diff;
|
|
194
|
-
const prompt = `Based on the following git diff, generate a concise, conventional commit message. Follow these guidelines:
|
|
195
|
-
- Use conventional commit format: type(scope): description
|
|
196
|
-
- Types: feat, fix, docs, style, refactor, test, chore
|
|
197
|
-
- Keep it under 72 characters
|
|
198
|
-
- Be specific and descriptive
|
|
199
|
-
- Focus on what changed and why
|
|
200
|
-
|
|
201
|
-
Git diff:
|
|
202
|
-
${truncatedDiff}
|
|
203
|
-
|
|
204
|
-
Generate only the commit message, nothing else:`;
|
|
205
|
-
try {
|
|
206
|
-
const providerKeys = await metadataManager.getProviderKeys();
|
|
207
|
-
const resolved = await resolveProviderAndKey(providerKeys, provider);
|
|
208
|
-
if (!resolved?.provider?.api) {
|
|
209
|
-
const errorMsg = provider
|
|
210
|
-
? `AI provider "${provider}" is specified but no API key is configured. Please add the API key in settings.`
|
|
211
|
-
: `No AI provider configured. Please configure a provider in settings.`;
|
|
212
|
-
process.stdout.write(`[generate-commit-message] ${errorMsg}\n`);
|
|
213
|
-
return `AI Error: ${errorMsg}`;
|
|
214
|
-
}
|
|
215
|
-
let selectedModel = model;
|
|
216
|
-
if (selectedModel) {
|
|
217
|
-
selectedModel = selectedModel.replace(`${resolved.provider.name.toLowerCase()}/`, "");
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
selectedModel = DEFAULT_MODEL_MAP[resolved.provider.name] || "default";
|
|
221
|
-
}
|
|
222
|
-
const resolvedModel = resolveModelForGit(resolved.provider.name, selectedModel, resolved.provider.api);
|
|
223
|
-
console.log(`[generate-commit-message] Using model: ${resolvedModel.name} provider: ${resolved.provider.name}. key: ${resolved.apiKey ? "exists" : "undefined"} prompt: ${prompt}`);
|
|
224
|
-
const response = await completeSimple(resolvedModel, {
|
|
225
|
-
messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
|
|
226
|
-
}, { apiKey: resolved.apiKey });
|
|
227
|
-
let commitMessage = response.content
|
|
228
|
-
.filter((b) => b.type === "text")
|
|
229
|
-
.map((b) => b.text)
|
|
230
|
-
.join("")
|
|
231
|
-
.trim();
|
|
232
|
-
commitMessage = commitMessage.replace(/^["']|["']$/g, "").trim();
|
|
233
|
-
if (!commitMessage) {
|
|
234
|
-
console.log(`[generate-commit-message] AI returned empty commit message, using fallback`, response);
|
|
235
|
-
process.stdout.write("[generate-commit-message] AI returned empty commit message, using fallback\n");
|
|
236
|
-
return "Update files";
|
|
237
|
-
}
|
|
238
|
-
return commitMessage;
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
242
|
-
process.stdout.write(`[generate-commit-message] AI generation failed: ${errorMsg}\n`);
|
|
243
|
-
return `AI Error: Failed to generate commit message: ${errorMsg}`;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
/** Resolve thread path: if absolute use as-is (normalized), else resolve against data dir. */
|
|
247
|
-
export function resolveThreadPath(repoPath) {
|
|
248
|
-
const base = isAbsolute(repoPath) ? repoPath : resolve(getDataDir(), repoPath);
|
|
249
|
-
return normalize(base);
|
|
250
|
-
}
|
|
251
|
-
/** Get git repo root for a path (directory or subdir of repo). Throws if not a git repo. */
|
|
252
|
-
export function getGitRoot(cwd) {
|
|
253
|
-
return new Promise((resolveRoot, reject) => {
|
|
254
|
-
const proc = spawnProcess("git", ["rev-parse", "--show-toplevel"], { cwd });
|
|
255
|
-
let out = "";
|
|
256
|
-
let err = "";
|
|
257
|
-
if (proc.stdout) {
|
|
258
|
-
proc.stdout.on("data", (d) => {
|
|
259
|
-
out += d.toString();
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (proc.stderr) {
|
|
263
|
-
proc.stderr.on("data", (d) => {
|
|
264
|
-
err += d.toString();
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
proc.on("close", (code) => {
|
|
268
|
-
if (code === 0) {
|
|
269
|
-
resolveRoot(out.trim());
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
reject(new Error(err.trim() || "Not a git repository"));
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
proc.on("error", reject);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
/** Get current git branch */
|
|
279
|
-
export function getCurrentBranch(gitRoot) {
|
|
280
|
-
return new Promise((resolve) => {
|
|
281
|
-
const proc = spawnProcess("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
|
|
282
|
-
let out = "";
|
|
283
|
-
if (proc.stdout) {
|
|
284
|
-
proc.stdout.on("data", (d) => {
|
|
285
|
-
out += d.toString();
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
proc.on("close", () => {
|
|
289
|
-
resolve(out.trim());
|
|
290
|
-
});
|
|
291
|
-
proc.on("error", () => resolve(""));
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
/** Get git diff for staged and unstaged changes */
|
|
295
|
-
export async function getGitDiff(gitRoot) {
|
|
296
|
-
return new Promise((resolveDiff, reject) => {
|
|
297
|
-
const runUnstagedDiff = () => {
|
|
298
|
-
const proc2 = spawnProcess("git", ["diff"], { cwd: gitRoot });
|
|
299
|
-
let out2 = "";
|
|
300
|
-
if (proc2.stdout) {
|
|
301
|
-
proc2.stdout.on("data", (d) => {
|
|
302
|
-
out2 += d.toString();
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
proc2.on("close", (code) => {
|
|
306
|
-
resolveDiff(code === 0 ? out2 : "");
|
|
307
|
-
});
|
|
308
|
-
proc2.on("error", reject);
|
|
309
|
-
};
|
|
310
|
-
const proc = spawnProcess("git", ["diff", "--cached"], { cwd: gitRoot });
|
|
311
|
-
let out = "";
|
|
312
|
-
let err = "";
|
|
313
|
-
if (proc.stdout) {
|
|
314
|
-
proc.stdout.on("data", (d) => {
|
|
315
|
-
out += d.toString();
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
if (proc.stderr) {
|
|
319
|
-
proc.stderr.on("data", (d) => {
|
|
320
|
-
err += d.toString();
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
proc.on("close", (code) => {
|
|
324
|
-
if (code === 0) {
|
|
325
|
-
if (!out.trim()) {
|
|
326
|
-
runUnstagedDiff();
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
resolveDiff(out);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
// No commits yet (HEAD missing) or similar: use working tree diff
|
|
334
|
-
const noHead = /HEAD|revision|unknown revision/i.test(err);
|
|
335
|
-
if (noHead) {
|
|
336
|
-
runUnstagedDiff();
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
reject(new Error(err || "Failed to get git diff"));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
proc.on("error", reject);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
/** Get diff for untracked files */
|
|
347
|
-
export async function getUntrackedFilesDiff(gitRoot) {
|
|
348
|
-
const statusOut = await new Promise((resolveStatus) => {
|
|
349
|
-
const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
350
|
-
cwd: gitRoot,
|
|
351
|
-
});
|
|
352
|
-
let out = "";
|
|
353
|
-
if (proc.stdout) {
|
|
354
|
-
proc.stdout.on("data", (d) => {
|
|
355
|
-
out += d.toString();
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
proc.on("close", () => resolveStatus(out));
|
|
359
|
-
proc.on("error", () => resolveStatus(""));
|
|
360
|
-
});
|
|
361
|
-
const lines = statusOut
|
|
362
|
-
.trim()
|
|
363
|
-
.split("\n")
|
|
364
|
-
.filter((l) => l.length > 0);
|
|
365
|
-
const untrackedPaths = [];
|
|
366
|
-
for (const line of lines) {
|
|
367
|
-
const code = line.slice(0, 2);
|
|
368
|
-
const pathPart = line.slice(3).trim();
|
|
369
|
-
if (code === "??") {
|
|
370
|
-
untrackedPaths.push(pathPart);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (untrackedPaths.length === 0)
|
|
374
|
-
return "";
|
|
375
|
-
const parts = [];
|
|
376
|
-
const maxFileSize = 100_000;
|
|
377
|
-
for (const relPath of untrackedPaths) {
|
|
378
|
-
const fullPath = join(gitRoot, relPath);
|
|
379
|
-
if (!existsSync(fullPath))
|
|
380
|
-
continue;
|
|
381
|
-
try {
|
|
382
|
-
if (statSync(fullPath).isDirectory())
|
|
383
|
-
continue;
|
|
384
|
-
const content = readFileSync(fullPath, "utf-8");
|
|
385
|
-
const safeContent = content.length > maxFileSize ? content.slice(0, maxFileSize) + "\n...(truncated)" : content;
|
|
386
|
-
const linesForDiff = safeContent
|
|
387
|
-
.split(/\r?\n/)
|
|
388
|
-
.map((l) => `+${l}`)
|
|
389
|
-
.join("\n");
|
|
390
|
-
parts.push(`diff --git a/${relPath} b/${relPath}\nnew file mode 100644\n--- /dev/null\n+++ b/${relPath}\n${linesForDiff}`);
|
|
391
|
-
}
|
|
392
|
-
catch {
|
|
393
|
-
parts.push(`diff --git a/${relPath} b/${relPath}\nnew file mode 100644\n(Binary or unreadable file: ${relPath})`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return parts.join("\n");
|
|
397
|
-
}
|
|
398
|
-
/** Get git status output */
|
|
399
|
-
export function getGitStatus(gitRoot) {
|
|
400
|
-
return new Promise((resolve) => {
|
|
401
|
-
const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
402
|
-
cwd: gitRoot,
|
|
403
|
-
});
|
|
404
|
-
let out = "";
|
|
405
|
-
if (proc.stdout) {
|
|
406
|
-
proc.stdout.on("data", (d) => {
|
|
407
|
-
out += d.toString();
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
proc.on("close", () => resolve(out));
|
|
411
|
-
proc.on("error", () => resolve(""));
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
/** Check if there are unpushed commits */
|
|
415
|
-
export function hasUnpushedCommits(gitRoot, currentBranch) {
|
|
416
|
-
return new Promise((resolve) => {
|
|
417
|
-
const proc = spawnProcess("git", ["log", `origin/${currentBranch}..HEAD`, "--oneline"], {
|
|
418
|
-
cwd: gitRoot,
|
|
419
|
-
});
|
|
420
|
-
let out = "";
|
|
421
|
-
if (proc.stdout) {
|
|
422
|
-
proc.stdout.on("data", (d) => {
|
|
423
|
-
out += d.toString();
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
proc.on("close", () => {
|
|
427
|
-
resolve(out.trim().length > 0);
|
|
428
|
-
});
|
|
429
|
-
proc.on("error", () => resolve(false));
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
/** Check if current branch has commits not yet in the default branch (pushed or not) */
|
|
433
|
-
export function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
|
|
434
|
-
return new Promise((resolve) => {
|
|
435
|
-
const proc = spawnProcess("git", ["rev-list", "--count", `origin/${defaultBranch}..HEAD`], {
|
|
436
|
-
cwd: gitRoot,
|
|
437
|
-
});
|
|
438
|
-
let out = "";
|
|
439
|
-
if (proc.stdout) {
|
|
440
|
-
proc.stdout.on("data", (d) => {
|
|
441
|
-
out += d.toString();
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
proc.on("close", (code) => {
|
|
445
|
-
if (code === 0) {
|
|
446
|
-
resolve(parseInt(out.trim(), 10) > 0);
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
resolve(false);
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
proc.on("error", () => resolve(false));
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
/** Get remote origin URL */
|
|
456
|
-
export function getRemoteOrigin(gitRoot) {
|
|
457
|
-
return new Promise((resolve) => {
|
|
458
|
-
const proc = spawnProcess("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
|
|
459
|
-
let out = "";
|
|
460
|
-
if (proc.stdout) {
|
|
461
|
-
proc.stdout.on("data", (d) => {
|
|
462
|
-
out += d.toString();
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
proc.on("close", (code) => {
|
|
466
|
-
if (code === 0) {
|
|
467
|
-
resolve(out.trim());
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
resolve(""); // No origin remote
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
proc.on("error", () => resolve(""));
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
/** Get the hash of the origin branch head */
|
|
477
|
-
export function getOriginBranchHash(gitRoot, branch) {
|
|
478
|
-
return new Promise((resolve) => {
|
|
479
|
-
const proc = spawnProcess("git", ["show-ref", "--heads", "-s", `origin/${branch}`], {
|
|
480
|
-
cwd: gitRoot,
|
|
481
|
-
});
|
|
482
|
-
let out = "";
|
|
483
|
-
if (proc.stdout) {
|
|
484
|
-
proc.stdout.on("data", (d) => {
|
|
485
|
-
out += d.toString();
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
if (proc.stderr) {
|
|
489
|
-
proc.stderr.on("data", () => {
|
|
490
|
-
// Ignore stderr
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
proc.on("close", (code) => {
|
|
494
|
-
if (code === 0 && out.trim()) {
|
|
495
|
-
resolve(out.trim());
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
resolve(null);
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
proc.on("error", () => resolve(null));
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
/** Get the merge-base hash between origin branch and current branch */
|
|
505
|
-
export function getMergeBaseHash(gitRoot, originBranch, currentBranch) {
|
|
506
|
-
return new Promise((resolve) => {
|
|
507
|
-
const proc = spawnProcess("git", ["merge-base", `origin/${originBranch}`, currentBranch], {
|
|
508
|
-
cwd: gitRoot,
|
|
509
|
-
});
|
|
510
|
-
let out = "";
|
|
511
|
-
if (proc.stdout) {
|
|
512
|
-
proc.stdout.on("data", (d) => {
|
|
513
|
-
out += d.toString();
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
if (proc.stderr) {
|
|
517
|
-
proc.stderr.on("data", () => {
|
|
518
|
-
// Ignore stderr
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
proc.on("close", (code) => {
|
|
522
|
-
if (code === 0 && out.trim()) {
|
|
523
|
-
resolve(out.trim());
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
526
|
-
resolve(null);
|
|
527
|
-
}
|
|
528
|
-
});
|
|
529
|
-
proc.on("error", () => resolve(null));
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
/** Check if current branch is behind origin branch */
|
|
533
|
-
export async function isBranchBehind(gitRoot, branch) {
|
|
534
|
-
const originHash = await getOriginBranchHash(gitRoot, branch);
|
|
535
|
-
const mergeBaseHash = await getMergeBaseHash(gitRoot, branch, branch);
|
|
536
|
-
// If either hash is null, we can't determine the status
|
|
537
|
-
if (!originHash || !mergeBaseHash) {
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
// If hashes are different, the branch is behind
|
|
541
|
-
return originHash !== mergeBaseHash;
|
|
542
|
-
}
|
|
543
|
-
/** Get detailed git sync status (Behind, Ahead, Diverged, Up to date) */
|
|
544
|
-
export async function getGitSyncStatus(gitRoot, branch) {
|
|
545
|
-
console.log(`[git-status] Checking sync status for branch: ${branch}`);
|
|
546
|
-
// Fetch to update remote tracking information
|
|
547
|
-
console.log(`[git-status] Fetching from origin...`);
|
|
548
|
-
await new Promise((resolve) => {
|
|
549
|
-
const fetchProc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
|
|
550
|
-
fetchProc.on("close", () => {
|
|
551
|
-
console.log(`[git-status] ✓ Fetch completed`);
|
|
552
|
-
resolve();
|
|
553
|
-
});
|
|
554
|
-
fetchProc.on("error", () => {
|
|
555
|
-
console.log(`[git-status] Fetch error (continuing anyway)`);
|
|
556
|
-
resolve();
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
// Check if branch is behind the default branch
|
|
560
|
-
console.log(`[git-status] Finding default branch...`);
|
|
561
|
-
const defaultBranch = await findDefaultBranch(gitRoot);
|
|
562
|
-
console.log(`[git-status] Default branch: ${defaultBranch}`);
|
|
563
|
-
if (defaultBranch && defaultBranch !== branch) {
|
|
564
|
-
console.log(`[git-status] Checking if ${branch} is behind ${defaultBranch}...`);
|
|
565
|
-
// Count commits in default branch that are not in current branch
|
|
566
|
-
const isBehindDefault = await new Promise((resolve) => {
|
|
567
|
-
const proc = spawnProcess("git", ["rev-list", "--count", `${branch}..origin/${defaultBranch}`], { cwd: gitRoot });
|
|
568
|
-
let out = "";
|
|
569
|
-
if (proc.stdout) {
|
|
570
|
-
proc.stdout.on("data", (d) => {
|
|
571
|
-
out += d.toString();
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
proc.on("close", () => {
|
|
575
|
-
const count = parseInt(out.trim(), 10);
|
|
576
|
-
console.log(`[git-status] Commits in ${defaultBranch} but not in ${branch}: ${count}`);
|
|
577
|
-
resolve(count > 0);
|
|
578
|
-
});
|
|
579
|
-
proc.on("error", () => {
|
|
580
|
-
console.log(`[git-status] Error checking commits (assuming not behind)`);
|
|
581
|
-
resolve(false);
|
|
582
|
-
});
|
|
583
|
-
});
|
|
584
|
-
if (isBehindDefault) {
|
|
585
|
-
console.log(`[git-status] ✓ Branch IS behind default branch`);
|
|
586
|
-
return "Behind";
|
|
587
|
-
}
|
|
588
|
-
console.log(`[git-status] Branch is NOT behind default branch`);
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
console.log(`[git-status] Skipping default branch check (same branch or no default found)`);
|
|
592
|
-
}
|
|
593
|
-
// Check git status against remote tracking branch
|
|
594
|
-
console.log(`[git-status] Checking status against remote tracking branch...`);
|
|
595
|
-
return new Promise((resolve) => {
|
|
596
|
-
const statusProc = spawnProcess("git", ["status", "-sb"], { cwd: gitRoot });
|
|
597
|
-
let statusOut = "";
|
|
598
|
-
let statusErr = "";
|
|
599
|
-
if (statusProc.stdout) {
|
|
600
|
-
statusProc.stdout.on("data", (d) => {
|
|
601
|
-
statusOut += d.toString();
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
if (statusProc.stderr) {
|
|
605
|
-
statusProc.stderr.on("data", (d) => {
|
|
606
|
-
statusErr += d.toString();
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
statusProc.on("close", () => {
|
|
610
|
-
const output = statusOut + statusErr;
|
|
611
|
-
console.log(`[git-status] git status -sb output: ${output.trim()}`);
|
|
612
|
-
// Validate that we're checking the right branch
|
|
613
|
-
if (!output.includes(branch)) {
|
|
614
|
-
console.log(`[git-status] Branch name not found in status output, returning 'Up to date'`);
|
|
615
|
-
resolve("Up to date");
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
// Check for each status pattern based on git status output
|
|
619
|
-
if (output.includes("ahead") && output.includes("behind")) {
|
|
620
|
-
console.log(`[git-status] ✓ Status: Diverged`);
|
|
621
|
-
resolve("Diverged");
|
|
622
|
-
}
|
|
623
|
-
else if (output.includes("behind")) {
|
|
624
|
-
console.log(`[git-status] ✓ Status: Behind`);
|
|
625
|
-
resolve("Behind");
|
|
626
|
-
}
|
|
627
|
-
else if (output.includes("ahead")) {
|
|
628
|
-
console.log(`[git-status] ✓ Status: Ahead`);
|
|
629
|
-
resolve("Ahead");
|
|
630
|
-
}
|
|
631
|
-
else if (output.includes("#") && !output.includes("ahead") && !output.includes("behind")) {
|
|
632
|
-
console.log(`[git-status] ✓ Status: Up to date`);
|
|
633
|
-
resolve("Up to date");
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
console.log(`[git-status] ✓ Status: Up to date (default)`);
|
|
637
|
-
resolve("Up to date");
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
statusProc.on("error", () => {
|
|
641
|
-
console.log(`[git-status] Error getting status, defaulting to 'Up to date'`);
|
|
642
|
-
resolve("Up to date");
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
/** Check if remote branch exists */
|
|
647
|
-
export async function hasRemoteBranch(gitRoot, branch) {
|
|
648
|
-
return new Promise((resolve) => {
|
|
649
|
-
const proc = spawnProcess("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
650
|
-
cwd: gitRoot,
|
|
651
|
-
});
|
|
652
|
-
proc.on("close", (code) => {
|
|
653
|
-
resolve(code === 0);
|
|
654
|
-
});
|
|
655
|
-
proc.on("error", () => {
|
|
656
|
-
resolve(false);
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
/** Get default branch from origin/HEAD */
|
|
661
|
-
export function getDefaultBranch(gitRoot) {
|
|
662
|
-
return new Promise((resolve) => {
|
|
663
|
-
const proc = spawnProcess("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
664
|
-
cwd: gitRoot,
|
|
665
|
-
});
|
|
666
|
-
let out = "";
|
|
667
|
-
if (proc.stdout) {
|
|
668
|
-
proc.stdout.on("data", (d) => {
|
|
669
|
-
out += d.toString();
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
proc.on("close", (code) => {
|
|
673
|
-
if (code === 0 && out.trim()) {
|
|
674
|
-
// Output is like: refs/remotes/origin/main
|
|
675
|
-
const match = out.trim().match(/refs\/remotes\/origin\/(.+)/);
|
|
676
|
-
resolve(match ? match[1] : null);
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
resolve(null);
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
proc.on("error", () => resolve(null));
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
/** Find default branch by checking common candidates */
|
|
686
|
-
export async function findDefaultBranch(gitRoot) {
|
|
687
|
-
const candidates = ["main", "master", "develop", "staging"];
|
|
688
|
-
for (const branch of candidates) {
|
|
689
|
-
const exists = await new Promise((resolve) => {
|
|
690
|
-
const proc = spawnProcess("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
691
|
-
cwd: gitRoot,
|
|
692
|
-
});
|
|
693
|
-
proc.on("close", (code) => resolve(code === 0));
|
|
694
|
-
proc.on("error", () => resolve(false));
|
|
695
|
-
});
|
|
696
|
-
if (exists)
|
|
697
|
-
return branch;
|
|
698
|
-
}
|
|
699
|
-
return null;
|
|
700
|
-
}
|
|
701
|
-
/** Get commit log between branches */
|
|
702
|
-
export function getCommitLog(gitRoot, baseBranch, currentBranch) {
|
|
703
|
-
return new Promise((resolve) => {
|
|
704
|
-
const proc = spawnProcess("git", ["log", "--oneline", `${baseBranch}..${currentBranch}`], {
|
|
705
|
-
cwd: gitRoot,
|
|
706
|
-
});
|
|
707
|
-
let out = "";
|
|
708
|
-
if (proc.stdout) {
|
|
709
|
-
proc.stdout.on("data", (d) => {
|
|
710
|
-
out += d.toString();
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
proc.on("close", () => resolve(out.trim()));
|
|
714
|
-
proc.on("error", () => resolve(""));
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
/** Get detailed commit information */
|
|
718
|
-
export async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
|
|
719
|
-
return new Promise((resolve) => {
|
|
720
|
-
// Get commit messages with body (subject + body, limited)
|
|
721
|
-
const proc = spawnProcess("git", ["log", `${baseBranch}..${currentBranch}`, "--format=%h %s%n%b%n---"], { cwd: gitRoot });
|
|
722
|
-
let out = "";
|
|
723
|
-
if (proc.stdout) {
|
|
724
|
-
proc.stdout.on("data", (d) => {
|
|
725
|
-
out += d.toString();
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
proc.on("close", () => {
|
|
729
|
-
// Truncate to avoid huge prompts
|
|
730
|
-
resolve(out.length > 2000 ? out.substring(0, 2000) + "\n...(more commits)" : out);
|
|
731
|
-
});
|
|
732
|
-
proc.on("error", () => resolve(""));
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
/** Get git log with limit */
|
|
736
|
-
export function getGitLog(gitRoot, limit) {
|
|
737
|
-
return new Promise((resolve, reject) => {
|
|
738
|
-
const proc = spawnProcess("git", ["log", "--oneline", "-n", limit.toString(), "--format=%H|%s|%an|%ai"], { cwd: gitRoot });
|
|
739
|
-
let out = "";
|
|
740
|
-
let err = "";
|
|
741
|
-
if (proc.stdout) {
|
|
742
|
-
proc.stdout.on("data", (d) => {
|
|
743
|
-
out += d.toString();
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
if (proc.stderr) {
|
|
747
|
-
proc.stderr.on("data", (d) => {
|
|
748
|
-
err += d.toString();
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
proc.on("close", (code) => {
|
|
752
|
-
if (code === 0) {
|
|
753
|
-
const lines = out
|
|
754
|
-
.trim()
|
|
755
|
-
.split("\n")
|
|
756
|
-
.filter((line) => line.length > 0);
|
|
757
|
-
const parsed = lines.map((line) => {
|
|
758
|
-
const parts = line.split("|");
|
|
759
|
-
return {
|
|
760
|
-
hash: parts[0] || "",
|
|
761
|
-
message: parts[1] || "",
|
|
762
|
-
author: parts[2] || "",
|
|
763
|
-
date: parts[3] || "",
|
|
764
|
-
};
|
|
765
|
-
});
|
|
766
|
-
resolve(parsed);
|
|
767
|
-
}
|
|
768
|
-
else {
|
|
769
|
-
reject(new Error(err || "Failed to get git log"));
|
|
770
|
-
}
|
|
771
|
-
});
|
|
772
|
-
proc.on("error", reject);
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
/** Stage all changes */
|
|
776
|
-
export function stageAllChanges(gitRoot) {
|
|
777
|
-
return new Promise((resolve, reject) => {
|
|
778
|
-
const proc = spawnProcess("git", ["add", "-A"], { cwd: gitRoot });
|
|
779
|
-
proc.on("close", (code) => {
|
|
780
|
-
if (code === 0)
|
|
781
|
-
resolve();
|
|
782
|
-
else
|
|
783
|
-
reject(new Error("Failed to stage changes"));
|
|
784
|
-
});
|
|
785
|
-
proc.on("error", reject);
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
/** Commit changes */
|
|
789
|
-
export function commitChanges(gitRoot, message) {
|
|
790
|
-
return new Promise((resolve, reject) => {
|
|
791
|
-
const proc = spawnProcess("git", ["commit", "-m", message], { cwd: gitRoot });
|
|
792
|
-
proc.on("close", (code) => {
|
|
793
|
-
if (code === 0)
|
|
794
|
-
resolve();
|
|
795
|
-
else
|
|
796
|
-
reject(new Error("Failed to commit changes"));
|
|
797
|
-
});
|
|
798
|
-
proc.on("error", reject);
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
/** Push changes to origin */
|
|
802
|
-
export function pushToOrigin(gitRoot, currentBranch) {
|
|
803
|
-
return new Promise((resolve, reject) => {
|
|
804
|
-
console.log(`[git-push] Executing: git push -u origin ${currentBranch}`);
|
|
805
|
-
const proc = spawnProcess("git", ["push", "-u", "origin", currentBranch], { cwd: gitRoot });
|
|
806
|
-
let err = "";
|
|
807
|
-
let _out = "";
|
|
808
|
-
if (proc.stdout) {
|
|
809
|
-
proc.stdout.on("data", (d) => {
|
|
810
|
-
_out += d.toString();
|
|
811
|
-
console.log(`[git-push] stdout: ${d.toString().trim()}`);
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
if (proc.stderr) {
|
|
815
|
-
proc.stderr.on("data", (d) => {
|
|
816
|
-
err += d.toString();
|
|
817
|
-
console.log(`[git-push] stderr: ${d.toString().trim()}`);
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
proc.on("close", (code) => {
|
|
821
|
-
console.log(`[git-push] git push exited with code: ${code}`);
|
|
822
|
-
if (code === 0) {
|
|
823
|
-
console.log(`[git-push] ✓ Push completed successfully`);
|
|
824
|
-
resolve();
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
console.log(`[git-push] ✗ Push failed with error: ${err || "Unknown error"}`);
|
|
828
|
-
reject(new Error(err || "Failed to push changes"));
|
|
829
|
-
}
|
|
830
|
-
});
|
|
831
|
-
proc.on("error", (error) => {
|
|
832
|
-
console.log(`[git-push] ✗ Process error: ${error instanceof Error ? error.message : String(error)}`);
|
|
833
|
-
reject(error);
|
|
834
|
-
});
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
/** Fetch from origin */
|
|
838
|
-
export function fetchFromOrigin(gitRoot) {
|
|
839
|
-
return new Promise((resolve, reject) => {
|
|
840
|
-
const proc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
|
|
841
|
-
let err = "";
|
|
842
|
-
if (proc.stderr) {
|
|
843
|
-
proc.stderr.on("data", (d) => {
|
|
844
|
-
err += d.toString();
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
proc.on("close", (code) => {
|
|
848
|
-
if (code === 0)
|
|
849
|
-
resolve();
|
|
850
|
-
else
|
|
851
|
-
reject(new Error(err || "Failed to fetch from origin"));
|
|
852
|
-
});
|
|
853
|
-
proc.on("error", reject);
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
/** Pull from origin */
|
|
857
|
-
export function pullFromOrigin(gitRoot) {
|
|
858
|
-
return new Promise((resolve, reject) => {
|
|
859
|
-
const proc = spawnProcess("git", ["pull"], { cwd: gitRoot });
|
|
860
|
-
let err = "";
|
|
861
|
-
if (proc.stderr) {
|
|
862
|
-
proc.stderr.on("data", (d) => {
|
|
863
|
-
err += d.toString();
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
proc.on("close", (code) => {
|
|
867
|
-
if (code === 0)
|
|
868
|
-
resolve();
|
|
869
|
-
else
|
|
870
|
-
reject(new Error(err || "Failed to pull from origin"));
|
|
871
|
-
});
|
|
872
|
-
proc.on("error", reject);
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
/** Stash changes */
|
|
876
|
-
export function stashChanges(gitRoot) {
|
|
877
|
-
return new Promise((resolve, reject) => {
|
|
878
|
-
const proc = spawnProcess("git", ["stash"], { cwd: gitRoot });
|
|
879
|
-
let err = "";
|
|
880
|
-
if (proc.stderr) {
|
|
881
|
-
proc.stderr.on("data", (d) => {
|
|
882
|
-
err += d.toString();
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
proc.on("close", (code) => {
|
|
886
|
-
if (code === 0)
|
|
887
|
-
resolve();
|
|
888
|
-
else
|
|
889
|
-
reject(new Error(err || "Failed to stash changes"));
|
|
890
|
-
});
|
|
891
|
-
proc.on("error", reject);
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
/** Pop stashed changes */
|
|
895
|
-
export function popStashedChanges(gitRoot) {
|
|
896
|
-
return new Promise((resolve, reject) => {
|
|
897
|
-
const proc = spawnProcess("git", ["stash", "pop"], { cwd: gitRoot });
|
|
898
|
-
let err = "";
|
|
899
|
-
if (proc.stderr) {
|
|
900
|
-
proc.stderr.on("data", (d) => {
|
|
901
|
-
err += d.toString();
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
proc.on("close", (code) => {
|
|
905
|
-
if (code === 0)
|
|
906
|
-
resolve();
|
|
907
|
-
else
|
|
908
|
-
reject(new Error(err || "Failed to pop stashed changes"));
|
|
909
|
-
});
|
|
910
|
-
proc.on("error", reject);
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
/** Create GitHub repository using gh CLI */
|
|
914
|
-
export function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
|
|
915
|
-
return new Promise((resolve, reject) => {
|
|
916
|
-
const args = ["repo", "create"];
|
|
917
|
-
if (repoName) {
|
|
918
|
-
args.push(repoName);
|
|
919
|
-
}
|
|
920
|
-
if (description) {
|
|
921
|
-
args.push("--description", description);
|
|
922
|
-
}
|
|
923
|
-
if (isPrivate) {
|
|
924
|
-
args.push("--private");
|
|
925
|
-
}
|
|
926
|
-
else {
|
|
927
|
-
args.push("--public");
|
|
928
|
-
}
|
|
929
|
-
args.push("--source", ".", "--push");
|
|
930
|
-
const proc = spawnProcess("gh", args, { cwd: gitRoot });
|
|
931
|
-
let out = "";
|
|
932
|
-
let err = "";
|
|
933
|
-
if (proc.stdout) {
|
|
934
|
-
proc.stdout.on("data", (d) => {
|
|
935
|
-
out += d.toString();
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
if (proc.stderr) {
|
|
939
|
-
proc.stderr.on("data", (d) => {
|
|
940
|
-
err += d.toString();
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
proc.on("close", (code) => {
|
|
944
|
-
if (code === 0) {
|
|
945
|
-
// Extract URL from output
|
|
946
|
-
const urlMatch = out.match(/https:\/\/[^\s]+/);
|
|
947
|
-
resolve(urlMatch ? urlMatch[0] : out.trim());
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
reject(new Error(err || "Failed to create repository"));
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
proc.on("error", () => reject(new Error("GitHub CLI (gh) not found. Please install it to create repositories.")));
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
/** Create pull request using gh CLI */
|
|
957
|
-
export function createPullRequest(gitRoot, title, description) {
|
|
958
|
-
return new Promise((resolve, reject) => {
|
|
959
|
-
const args = ["pr", "create", "--title", title ?? "", "--body", description ?? ""];
|
|
960
|
-
console.log(`[git-create-pr] Executing: gh ${args.join(" ")}`);
|
|
961
|
-
const proc = spawnProcess("gh", args, { cwd: gitRoot });
|
|
962
|
-
let out = "";
|
|
963
|
-
let err = "";
|
|
964
|
-
if (proc.stdout) {
|
|
965
|
-
proc.stdout.on("data", (d) => {
|
|
966
|
-
out += d.toString();
|
|
967
|
-
console.log(`[git-create-pr] stdout: ${d.toString().trim()}`);
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
if (proc.stderr) {
|
|
971
|
-
proc.stderr.on("data", (d) => {
|
|
972
|
-
err += d.toString();
|
|
973
|
-
console.log(`[git-create-pr] stderr: ${d.toString().trim()}`);
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
proc.on("close", (code) => {
|
|
977
|
-
console.log(`[git-create-pr] gh pr create exited with code: ${code}`);
|
|
978
|
-
if (code === 0) {
|
|
979
|
-
// Extract URL from output
|
|
980
|
-
const urlMatch = out.match(/https:\/\/[^\s]+/);
|
|
981
|
-
const prUrl = urlMatch ? urlMatch[0] : out.trim();
|
|
982
|
-
console.log(`[git-create-pr] ✓ PR URL extracted: ${prUrl}`);
|
|
983
|
-
resolve(prUrl);
|
|
984
|
-
}
|
|
985
|
-
else {
|
|
986
|
-
console.log(`[git-create-pr] ✗ gh pr create failed with error: ${err || "Unknown error"}`);
|
|
987
|
-
reject(new Error(err || "Failed to create PR"));
|
|
988
|
-
}
|
|
989
|
-
});
|
|
990
|
-
proc.on("error", (error) => {
|
|
991
|
-
console.log(`[git-create-pr] ✗ Process error: ${error instanceof Error ? error.message : String(error)}`);
|
|
992
|
-
reject(new Error("GitHub CLI (gh) not found. Please install it to create PRs."));
|
|
993
|
-
});
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
/** Check if there's an existing PR for the current branch */
|
|
997
|
-
export function getPullRequestStatus(gitRoot) {
|
|
998
|
-
return new Promise((resolve, reject) => {
|
|
999
|
-
const args = ["pr", "view", "--json", "url,state"];
|
|
1000
|
-
const proc = spawnProcess("gh", args, { cwd: gitRoot });
|
|
1001
|
-
let out = "";
|
|
1002
|
-
let err = "";
|
|
1003
|
-
if (proc.stdout) {
|
|
1004
|
-
proc.stdout.on("data", (d) => {
|
|
1005
|
-
out += d.toString();
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1008
|
-
if (proc.stderr) {
|
|
1009
|
-
proc.stderr.on("data", (d) => {
|
|
1010
|
-
err += d.toString();
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
proc.on("close", (code) => {
|
|
1014
|
-
if (code === 0) {
|
|
1015
|
-
try {
|
|
1016
|
-
const prData = JSON.parse(out.trim());
|
|
1017
|
-
resolve({
|
|
1018
|
-
exists: true,
|
|
1019
|
-
url: prData.url,
|
|
1020
|
-
state: prData.state,
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
catch {
|
|
1024
|
-
reject(new Error("Failed to parse PR data"));
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
else {
|
|
1028
|
-
// Check if error indicates no PR exists
|
|
1029
|
-
if (err.includes("no pull requests") || err.includes("not found")) {
|
|
1030
|
-
resolve({ exists: false });
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
reject(new Error(err || "Failed to check PR status"));
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
proc.on("error", () => reject(new Error("GitHub CLI (gh) not found. Please install it to check PR status.")));
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
//# sourceMappingURL=git.utils.js.map
|