@camaradesuk/git-worktree-tools 1.8.0 → 1.10.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/README.md +48 -27
- package/dist/cli/cleanpr.js +74 -53
- package/dist/cli/cleanpr.js.map +1 -1
- package/dist/cli/cleanpr.test.js +2 -0
- package/dist/cli/cleanpr.test.js.map +1 -1
- package/dist/cli/lswt.js +32 -56
- package/dist/cli/lswt.js.map +1 -1
- package/dist/cli/lswt.test.js +17 -27
- package/dist/cli/lswt.test.js.map +1 -1
- package/dist/cli/newpr.d.ts +13 -1
- package/dist/cli/newpr.d.ts.map +1 -1
- package/dist/cli/newpr.js +350 -151
- package/dist/cli/newpr.js.map +1 -1
- package/dist/cli/newpr.test.js +314 -5
- package/dist/cli/newpr.test.js.map +1 -1
- package/dist/cli/prs.d.ts +3 -10
- package/dist/cli/prs.d.ts.map +1 -1
- package/dist/cli/prs.js +6 -168
- package/dist/cli/prs.js.map +1 -1
- package/dist/cli/prs.test.js +55 -0
- package/dist/cli/prs.test.js.map +1 -1
- package/dist/cli/wt/clean.d.ts +6 -2
- package/dist/cli/wt/clean.d.ts.map +1 -1
- package/dist/cli/wt/clean.js +401 -20
- package/dist/cli/wt/clean.js.map +1 -1
- package/dist/cli/wt/clean.test.d.ts +8 -0
- package/dist/cli/wt/clean.test.d.ts.map +1 -0
- package/dist/cli/wt/clean.test.js +624 -0
- package/dist/cli/wt/clean.test.js.map +1 -0
- package/dist/cli/wt/completion.d.ts +3 -0
- package/dist/cli/wt/completion.d.ts.map +1 -1
- package/dist/cli/wt/completion.js +80 -9
- package/dist/cli/wt/completion.js.map +1 -1
- package/dist/cli/wt/completion.test.js +102 -0
- package/dist/cli/wt/completion.test.js.map +1 -1
- package/dist/cli/wt/config.d.ts +3 -1
- package/dist/cli/wt/config.d.ts.map +1 -1
- package/dist/cli/wt/config.js +323 -32
- package/dist/cli/wt/config.js.map +1 -1
- package/dist/cli/wt/config.test.d.ts +2 -0
- package/dist/cli/wt/config.test.d.ts.map +1 -1
- package/dist/cli/wt/config.test.js +206 -26
- package/dist/cli/wt/config.test.js.map +1 -1
- package/dist/cli/wt/interactive-menu.d.ts +2 -0
- package/dist/cli/wt/interactive-menu.d.ts.map +1 -1
- package/dist/cli/wt/interactive-menu.js +346 -73
- package/dist/cli/wt/interactive-menu.js.map +1 -1
- package/dist/cli/wt/interactive-menu.test.d.ts +4 -2
- package/dist/cli/wt/interactive-menu.test.d.ts.map +1 -1
- package/dist/cli/wt/interactive-menu.test.js +383 -323
- package/dist/cli/wt/interactive-menu.test.js.map +1 -1
- package/dist/cli/wt/link.d.ts +3 -1
- package/dist/cli/wt/link.d.ts.map +1 -1
- package/dist/cli/wt/link.js +125 -38
- package/dist/cli/wt/link.js.map +1 -1
- package/dist/cli/wt/list.d.ts +4 -1
- package/dist/cli/wt/list.d.ts.map +1 -1
- package/dist/cli/wt/list.js +85 -16
- package/dist/cli/wt/list.js.map +1 -1
- package/dist/cli/wt/list.test.d.ts +10 -0
- package/dist/cli/wt/list.test.d.ts.map +1 -0
- package/dist/cli/wt/list.test.js +157 -0
- package/dist/cli/wt/list.test.js.map +1 -0
- package/dist/cli/wt/new.d.ts +8 -2
- package/dist/cli/wt/new.d.ts.map +1 -1
- package/dist/cli/wt/new.js +91 -46
- package/dist/cli/wt/new.js.map +1 -1
- package/dist/cli/wt/prs.d.ts +2 -1
- package/dist/cli/wt/prs.d.ts.map +1 -1
- package/dist/cli/wt/prs.js +3 -164
- package/dist/cli/wt/prs.js.map +1 -1
- package/dist/cli/wt/run-command.d.ts +4 -2
- package/dist/cli/wt/run-command.d.ts.map +1 -1
- package/dist/cli/wt/run-command.js +6 -4
- package/dist/cli/wt/run-command.js.map +1 -1
- package/dist/cli/wt/state.d.ts +3 -1
- package/dist/cli/wt/state.d.ts.map +1 -1
- package/dist/cli/wt/state.js +74 -10
- package/dist/cli/wt/state.js.map +1 -1
- package/dist/cli/wt/state.test.d.ts +9 -0
- package/dist/cli/wt/state.test.d.ts.map +1 -0
- package/dist/cli/wt/state.test.js +127 -0
- package/dist/cli/wt/state.test.js.map +1 -0
- package/dist/cli/wt/wt.test.d.ts +2 -2
- package/dist/cli/wt/wt.test.js +430 -212
- package/dist/cli/wt/wt.test.js.map +1 -1
- package/dist/cli/wt.d.ts.map +1 -1
- package/dist/cli/wt.js +50 -36
- package/dist/cli/wt.js.map +1 -1
- package/dist/cli/wt.unit.test.js +16 -38
- package/dist/cli/wt.unit.test.js.map +1 -1
- package/dist/cli/wtconfig.d.ts +1 -0
- package/dist/cli/wtconfig.d.ts.map +1 -1
- package/dist/cli/wtconfig.js +213 -21
- package/dist/cli/wtconfig.js.map +1 -1
- package/dist/cli/wtconfig.test.js +3 -0
- package/dist/cli/wtconfig.test.js.map +1 -1
- package/dist/cli/wtlink.js +116 -73
- package/dist/cli/wtlink.js.map +1 -1
- package/dist/cli/wtstate.js +21 -2
- package/dist/cli/wtstate.js.map +1 -1
- package/dist/e2e/wt/interactive-menu.e2e.test.js +17 -17
- package/dist/e2e/wt/interactive-menu.e2e.test.js.map +1 -1
- package/dist/lib/ai/types.d.ts +12 -0
- package/dist/lib/ai/types.d.ts.map +1 -1
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/cleanpr/args.d.ts.map +1 -1
- package/dist/lib/cleanpr/args.js +20 -0
- package/dist/lib/cleanpr/args.js.map +1 -1
- package/dist/lib/cleanpr/types.d.ts +6 -0
- package/dist/lib/cleanpr/types.d.ts.map +1 -1
- package/dist/lib/cleanpr/worktree-info.d.ts.map +1 -1
- package/dist/lib/cleanpr/worktree-info.js +1 -6
- package/dist/lib/cleanpr/worktree-info.js.map +1 -1
- package/dist/lib/cleanpr/worktree-info.test.js +10 -13
- package/dist/lib/cleanpr/worktree-info.test.js.map +1 -1
- package/dist/lib/colors.d.ts +5 -0
- package/dist/lib/colors.d.ts.map +1 -1
- package/dist/lib/colors.js +13 -6
- package/dist/lib/colors.js.map +1 -1
- package/dist/lib/config-editor.d.ts.map +1 -1
- package/dist/lib/config-editor.js.map +1 -1
- package/dist/lib/config-migration/detector.d.ts +25 -0
- package/dist/lib/config-migration/detector.d.ts.map +1 -0
- package/dist/lib/config-migration/detector.js +372 -0
- package/dist/lib/config-migration/detector.js.map +1 -0
- package/dist/lib/config-migration/detector.test.d.ts +5 -0
- package/dist/lib/config-migration/detector.test.d.ts.map +1 -0
- package/dist/lib/config-migration/detector.test.js +201 -0
- package/dist/lib/config-migration/detector.test.js.map +1 -0
- package/dist/lib/config-migration/index.d.ts +29 -0
- package/dist/lib/config-migration/index.d.ts.map +1 -0
- package/dist/lib/config-migration/index.js +33 -0
- package/dist/lib/config-migration/index.js.map +1 -0
- package/dist/lib/config-migration/reporter.d.ts +53 -0
- package/dist/lib/config-migration/reporter.d.ts.map +1 -0
- package/dist/lib/config-migration/reporter.js +257 -0
- package/dist/lib/config-migration/reporter.js.map +1 -0
- package/dist/lib/config-migration/reporter.test.d.ts +5 -0
- package/dist/lib/config-migration/reporter.test.d.ts.map +1 -0
- package/dist/lib/config-migration/reporter.test.js +305 -0
- package/dist/lib/config-migration/reporter.test.js.map +1 -0
- package/dist/lib/config-migration/runner.d.ts +46 -0
- package/dist/lib/config-migration/runner.d.ts.map +1 -0
- package/dist/lib/config-migration/runner.js +364 -0
- package/dist/lib/config-migration/runner.js.map +1 -0
- package/dist/lib/config-migration/runner.test.d.ts +5 -0
- package/dist/lib/config-migration/runner.test.d.ts.map +1 -0
- package/dist/lib/config-migration/runner.test.js +235 -0
- package/dist/lib/config-migration/runner.test.js.map +1 -0
- package/dist/lib/config-migration/types.d.ts +120 -0
- package/dist/lib/config-migration/types.d.ts.map +1 -0
- package/dist/lib/config-migration/types.js +70 -0
- package/dist/lib/config-migration/types.js.map +1 -0
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config-validation.js +6 -0
- package/dist/lib/config-validation.js.map +1 -1
- package/dist/lib/config-validation.test.js +25 -0
- package/dist/lib/config-validation.test.js.map +1 -1
- package/dist/lib/config.d.ts +31 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.js +3 -15
- package/dist/lib/config.test.js.map +1 -1
- package/dist/lib/constants.d.ts +12 -4
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +24 -5
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.js +88 -29
- package/dist/lib/constants.test.js.map +1 -1
- package/dist/lib/deprecation.d.ts +18 -0
- package/dist/lib/deprecation.d.ts.map +1 -0
- package/dist/lib/deprecation.js +28 -0
- package/dist/lib/deprecation.js.map +1 -0
- package/dist/lib/deprecation.test.d.ts +2 -0
- package/dist/lib/deprecation.test.d.ts.map +1 -0
- package/dist/lib/deprecation.test.js +71 -0
- package/dist/lib/deprecation.test.js.map +1 -0
- package/dist/lib/hooks/confirmation.d.ts +49 -0
- package/dist/lib/hooks/confirmation.d.ts.map +1 -0
- package/dist/lib/hooks/confirmation.js +147 -0
- package/dist/lib/hooks/confirmation.js.map +1 -0
- package/dist/lib/hooks/confirmation.test.d.ts +7 -0
- package/dist/lib/hooks/confirmation.test.d.ts.map +1 -0
- package/dist/lib/hooks/confirmation.test.js +300 -0
- package/dist/lib/hooks/confirmation.test.js.map +1 -0
- package/dist/lib/hooks/executor.d.ts +16 -1
- package/dist/lib/hooks/executor.d.ts.map +1 -1
- package/dist/lib/hooks/executor.js +53 -4
- package/dist/lib/hooks/executor.js.map +1 -1
- package/dist/lib/hooks/index.d.ts +4 -2
- package/dist/lib/hooks/index.d.ts.map +1 -1
- package/dist/lib/hooks/index.js +3 -2
- package/dist/lib/hooks/index.js.map +1 -1
- package/dist/lib/hooks/types.d.ts +16 -0
- package/dist/lib/hooks/types.d.ts.map +1 -1
- package/dist/lib/hooks/types.js +12 -0
- package/dist/lib/hooks/types.js.map +1 -1
- package/dist/lib/logger.d.ts +40 -155
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +349 -420
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/logger.test.d.ts +10 -1
- package/dist/lib/logger.test.d.ts.map +1 -1
- package/dist/lib/logger.test.js +658 -258
- package/dist/lib/logger.test.js.map +1 -1
- package/dist/lib/lswt/action-executors.d.ts +2 -0
- package/dist/lib/lswt/action-executors.d.ts.map +1 -1
- package/dist/lib/lswt/action-executors.js +4 -3
- package/dist/lib/lswt/action-executors.js.map +1 -1
- package/dist/lib/lswt/action-executors.test.js +7 -0
- package/dist/lib/lswt/action-executors.test.js.map +1 -1
- package/dist/lib/lswt/args.d.ts.map +1 -1
- package/dist/lib/lswt/args.js +15 -1
- package/dist/lib/lswt/args.js.map +1 -1
- package/dist/lib/lswt/environment.d.ts +21 -2
- package/dist/lib/lswt/environment.d.ts.map +1 -1
- package/dist/lib/lswt/environment.js +73 -32
- package/dist/lib/lswt/environment.js.map +1 -1
- package/dist/lib/lswt/environment.test.js +79 -1
- package/dist/lib/lswt/environment.test.js.map +1 -1
- package/dist/lib/lswt/index.d.ts +1 -0
- package/dist/lib/lswt/index.d.ts.map +1 -1
- package/dist/lib/lswt/index.js +2 -0
- package/dist/lib/lswt/index.js.map +1 -1
- package/dist/lib/lswt/table.d.ts +15 -0
- package/dist/lib/lswt/table.d.ts.map +1 -0
- package/dist/lib/lswt/table.js +61 -0
- package/dist/lib/lswt/table.js.map +1 -0
- package/dist/lib/lswt/table.test.d.ts +5 -0
- package/dist/lib/lswt/table.test.d.ts.map +1 -0
- package/dist/lib/lswt/table.test.js +262 -0
- package/dist/lib/lswt/table.test.js.map +1 -0
- package/dist/lib/lswt/types.d.ts +4 -0
- package/dist/lib/lswt/types.d.ts.map +1 -1
- package/dist/lib/lswt/worktree-info.d.ts.map +1 -1
- package/dist/lib/lswt/worktree-info.js +1 -6
- package/dist/lib/lswt/worktree-info.js.map +1 -1
- package/dist/lib/lswt/worktree-info.test.js +5 -17
- package/dist/lib/lswt/worktree-info.test.js.map +1 -1
- package/dist/lib/newpr/args.d.ts.map +1 -1
- package/dist/lib/newpr/args.js +36 -1
- package/dist/lib/newpr/args.js.map +1 -1
- package/dist/lib/newpr/hook-runner.d.ts +11 -0
- package/dist/lib/newpr/hook-runner.d.ts.map +1 -1
- package/dist/lib/newpr/hook-runner.js +49 -1
- package/dist/lib/newpr/hook-runner.js.map +1 -1
- package/dist/lib/newpr/hook-runner.test.js +121 -0
- package/dist/lib/newpr/hook-runner.test.js.map +1 -1
- package/dist/lib/newpr/plan-generator.d.ts +121 -0
- package/dist/lib/newpr/plan-generator.d.ts.map +1 -0
- package/dist/lib/newpr/plan-generator.js +185 -0
- package/dist/lib/newpr/plan-generator.js.map +1 -0
- package/dist/lib/newpr/plan-generator.test.d.ts +7 -0
- package/dist/lib/newpr/plan-generator.test.d.ts.map +1 -0
- package/dist/lib/newpr/plan-generator.test.js +387 -0
- package/dist/lib/newpr/plan-generator.test.js.map +1 -0
- package/dist/lib/newpr/types.d.ts +12 -0
- package/dist/lib/newpr/types.d.ts.map +1 -1
- package/dist/lib/prs/actions.d.ts +5 -1
- package/dist/lib/prs/actions.d.ts.map +1 -1
- package/dist/lib/prs/actions.js +12 -10
- package/dist/lib/prs/actions.js.map +1 -1
- package/dist/lib/prs/actions.test.js +48 -5
- package/dist/lib/prs/actions.test.js.map +1 -1
- package/dist/lib/prs/command.d.ts +21 -0
- package/dist/lib/prs/command.d.ts.map +1 -0
- package/dist/lib/prs/command.js +175 -0
- package/dist/lib/prs/command.js.map +1 -0
- package/dist/lib/prs/command.test.d.ts +11 -0
- package/dist/lib/prs/command.test.d.ts.map +1 -0
- package/dist/lib/prs/command.test.js +409 -0
- package/dist/lib/prs/command.test.js.map +1 -0
- package/dist/lib/prs/interactive.d.ts.map +1 -1
- package/dist/lib/prs/interactive.js +15 -2
- package/dist/lib/prs/interactive.js.map +1 -1
- package/dist/lib/prs/interactive.test.js +153 -0
- package/dist/lib/prs/interactive.test.js.map +1 -1
- package/dist/lib/prs/types.d.ts +15 -0
- package/dist/lib/prs/types.d.ts.map +1 -1
- package/dist/lib/ui/error.d.ts +31 -0
- package/dist/lib/ui/error.d.ts.map +1 -0
- package/dist/lib/ui/error.js +47 -0
- package/dist/lib/ui/error.js.map +1 -0
- package/dist/lib/ui/error.test.d.ts +2 -0
- package/dist/lib/ui/error.test.d.ts.map +1 -0
- package/dist/lib/ui/error.test.js +143 -0
- package/dist/lib/ui/error.test.js.map +1 -0
- package/dist/lib/ui/index.d.ts +15 -0
- package/dist/lib/ui/index.d.ts.map +1 -0
- package/dist/lib/ui/index.js +19 -0
- package/dist/lib/ui/index.js.map +1 -0
- package/dist/lib/ui/output.d.ts +18 -0
- package/dist/lib/ui/output.d.ts.map +1 -0
- package/dist/lib/ui/output.js +31 -0
- package/dist/lib/ui/output.js.map +1 -0
- package/dist/lib/ui/output.test.d.ts +2 -0
- package/dist/lib/ui/output.test.d.ts.map +1 -0
- package/dist/lib/ui/output.test.js +59 -0
- package/dist/lib/ui/output.test.js.map +1 -0
- package/dist/lib/ui/spinner.d.ts +10 -0
- package/dist/lib/ui/spinner.d.ts.map +1 -0
- package/dist/lib/ui/spinner.js +10 -0
- package/dist/lib/ui/spinner.js.map +1 -0
- package/dist/lib/ui/status.d.ts +65 -0
- package/dist/lib/ui/status.d.ts.map +1 -0
- package/dist/lib/ui/status.js +100 -0
- package/dist/lib/ui/status.js.map +1 -0
- package/dist/lib/ui/status.test.d.ts +2 -0
- package/dist/lib/ui/status.test.d.ts.map +1 -0
- package/dist/lib/ui/status.test.js +158 -0
- package/dist/lib/ui/status.test.js.map +1 -0
- package/dist/lib/ui/table.d.ts +39 -0
- package/dist/lib/ui/table.d.ts.map +1 -0
- package/dist/lib/ui/table.js +45 -0
- package/dist/lib/ui/table.js.map +1 -0
- package/dist/lib/ui/table.test.d.ts +2 -0
- package/dist/lib/ui/table.test.d.ts.map +1 -0
- package/dist/lib/ui/table.test.js +115 -0
- package/dist/lib/ui/table.test.js.map +1 -0
- package/dist/lib/ui/theme.d.ts +34 -0
- package/dist/lib/ui/theme.d.ts.map +1 -0
- package/dist/lib/ui/theme.js +37 -0
- package/dist/lib/ui/theme.js.map +1 -0
- package/dist/lib/ui/theme.test.d.ts +2 -0
- package/dist/lib/ui/theme.test.d.ts.map +1 -0
- package/dist/lib/ui/theme.test.js +76 -0
- package/dist/lib/ui/theme.test.js.map +1 -0
- package/dist/lib/wtconfig/environment.d.ts +18 -1
- package/dist/lib/wtconfig/environment.d.ts.map +1 -1
- package/dist/lib/wtconfig/environment.js +60 -24
- package/dist/lib/wtconfig/environment.js.map +1 -1
- package/dist/lib/wtconfig/environment.test.js +45 -1
- package/dist/lib/wtconfig/environment.test.js.map +1 -1
- package/dist/lib/wtlink/config-manifest.test.js +26 -0
- package/dist/lib/wtlink/config-manifest.test.js.map +1 -1
- package/dist/lib/wtlink/link-configs.js +7 -7
- package/dist/lib/wtlink/link-configs.js.map +1 -1
- package/dist/lib/wtlink/validate-manifest.d.ts.map +1 -1
- package/dist/lib/wtlink/validate-manifest.js +5 -5
- package/dist/lib/wtlink/validate-manifest.js.map +1 -1
- package/dist/lib/wtstate/args.d.ts.map +1 -1
- package/dist/lib/wtstate/args.js +2 -0
- package/dist/lib/wtstate/args.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +264 -44
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.test.js +111 -0
- package/dist/mcp/server.test.js.map +1 -1
- package/package.json +3 -1
- package/schemas/worktreerc.schema.json +23 -0
package/dist/cli/newpr.js
CHANGED
|
@@ -6,18 +6,20 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import fs from 'fs';
|
|
9
|
+
import { printDeprecationNotice } from '../lib/deprecation.js';
|
|
9
10
|
import * as git from '../lib/git.js';
|
|
10
11
|
import * as github from '../lib/github.js';
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
12
|
+
import { setColorEnabled } from '../lib/colors.js';
|
|
13
|
+
import { logger, initializeLogger, setAuditContext } from '../lib/logger.js';
|
|
14
|
+
import { promptChoiceIndex, promptConfirm, withSpinner } from '../lib/prompts.js';
|
|
15
|
+
import { getEnabledFiles } from '../lib/wtlink/config-manifest.js';
|
|
16
|
+
import { run as runWtlink } from '../lib/wtlink/link-configs.js';
|
|
13
17
|
import { loadConfig, generateBranchNameAsync, generateWorktreePath, generatePRContentAsync, } from '../lib/config.js';
|
|
14
18
|
import { analyzeGitState, detectScenario } from '../lib/state-detection.js';
|
|
15
19
|
import { parseArgs, getHelpText, getScenarioContext, isPrWorktreeScenario, isExistingBranchAction, executeStateAction, getBranchPoint, getScenarioMessageLevel, createHookRunner, createActionDeps, } from '../lib/newpr/index.js';
|
|
16
20
|
import { createSuccessResult, createErrorResult, formatJsonResult, ErrorCode, getErrorCodeFromError, getErrorSuggestion, } from '../lib/json-output.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
const DEBUG_ENABLED = process.env.DEBUG === 'newpr' || process.env.DEBUG === '*' || process.env.DEBUG === '1';
|
|
21
|
+
import { shouldGeneratePlan, resolvePlanPath, buildPathTemplateVars, generatePlanDocument, } from '../lib/newpr/plan-generator.js';
|
|
22
|
+
import { printStatus, printDim, printSummaryBox, printNextSteps, printError, errorToDisplay, setJsonMode, } from '../lib/ui/index.js';
|
|
21
23
|
/**
|
|
22
24
|
* Error class for non-interactive mode failures
|
|
23
25
|
*/
|
|
@@ -29,47 +31,26 @@ class NonInteractiveError extends Error {
|
|
|
29
31
|
this.name = 'NonInteractiveError';
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
function debug(message, data) {
|
|
33
|
-
if (!DEBUG_ENABLED)
|
|
34
|
-
return;
|
|
35
|
-
const timestamp = new Date().toISOString();
|
|
36
|
-
console.error(colors.dim(`[DEBUG ${timestamp}] ${message}`));
|
|
37
|
-
if (data) {
|
|
38
|
-
for (const [key, value] of Object.entries(data)) {
|
|
39
|
-
console.error(colors.dim(` ${key}: ${JSON.stringify(value)}`));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Progress logging - suppressed in JSON mode for clean output
|
|
45
|
-
*/
|
|
46
|
-
function progress(options, ...args) {
|
|
47
|
-
if (!options.json) {
|
|
48
|
-
console.log(...args);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Progress error logging - suppressed in JSON mode
|
|
53
|
-
*/
|
|
54
|
-
function progressError(options, ...args) {
|
|
55
|
-
if (!options.json) {
|
|
56
|
-
console.error(...args);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
34
|
/**
|
|
60
35
|
* Check prerequisites
|
|
61
36
|
*/
|
|
62
37
|
function checkPrerequisites() {
|
|
63
|
-
|
|
38
|
+
printStatus('info', 'Checking prerequisites...');
|
|
64
39
|
if (!github.isGhInstalled()) {
|
|
65
|
-
|
|
40
|
+
printError({
|
|
41
|
+
title: 'GitHub CLI (gh) is required.',
|
|
42
|
+
hint: 'Install: https://cli.github.com',
|
|
43
|
+
});
|
|
66
44
|
process.exit(1);
|
|
67
45
|
}
|
|
68
46
|
if (!github.isAuthenticated()) {
|
|
69
|
-
|
|
47
|
+
printError({
|
|
48
|
+
title: 'GitHub CLI not authenticated.',
|
|
49
|
+
hint: 'Run: gh auth login',
|
|
50
|
+
});
|
|
70
51
|
process.exit(1);
|
|
71
52
|
}
|
|
72
|
-
|
|
53
|
+
printStatus('success', 'Prerequisites OK');
|
|
73
54
|
}
|
|
74
55
|
/**
|
|
75
56
|
* Show local commits not in base branch
|
|
@@ -133,7 +114,7 @@ async function handleScenario(state, baseBranch, options) {
|
|
|
133
114
|
// In non-interactive mode, cannot proceed from PR worktree
|
|
134
115
|
throw new NonInteractiveError('Cannot create PR from a PR worktree in non-interactive mode. Switch to the main worktree first.', ErrorCode.INVALID_ARGUMENT);
|
|
135
116
|
}
|
|
136
|
-
|
|
117
|
+
printStatus('warning', 'You are in a PR worktree, not the main worktree.');
|
|
137
118
|
console.log();
|
|
138
119
|
console.log('Creating a new PR is best done from the main worktree.');
|
|
139
120
|
const choice = await promptChoiceIndex('How would you like to proceed?', [
|
|
@@ -178,10 +159,10 @@ async function handleScenario(state, baseBranch, options) {
|
|
|
178
159
|
// Interactive mode: display info and prompt
|
|
179
160
|
const level = getScenarioMessageLevel(scenario);
|
|
180
161
|
if (level === 'warning') {
|
|
181
|
-
|
|
162
|
+
printStatus('warning', context.message);
|
|
182
163
|
}
|
|
183
164
|
else {
|
|
184
|
-
|
|
165
|
+
printStatus('info', context.message);
|
|
185
166
|
}
|
|
186
167
|
if (context.subMessage) {
|
|
187
168
|
console.log();
|
|
@@ -230,13 +211,17 @@ async function handleScenario(state, baseBranch, options) {
|
|
|
230
211
|
}
|
|
231
212
|
/**
|
|
232
213
|
* Setup worktree (symlinks, wtlink, deps)
|
|
214
|
+
*
|
|
215
|
+
* @param worktreePath - Path to the newly created worktree
|
|
216
|
+
* @param config - Resolved configuration
|
|
217
|
+
* @param options - CLI options
|
|
218
|
+
* @param repoRoot - Repository root (required to avoid CWD issues when running from subdirectories)
|
|
233
219
|
*/
|
|
234
|
-
async function setupWorktree(worktreePath, config, options) {
|
|
235
|
-
const repoRoot = git.getRepoRoot();
|
|
220
|
+
async function setupWorktree(worktreePath, config, options, repoRoot) {
|
|
236
221
|
const parentDir = path.dirname(repoRoot);
|
|
237
222
|
// Create symlinks for shared repos
|
|
238
223
|
if (config.sharedRepos.length > 0) {
|
|
239
|
-
|
|
224
|
+
printStatus('info', 'Creating symlinks for shared repositories...');
|
|
240
225
|
for (const repo of config.sharedRepos) {
|
|
241
226
|
const target = path.join(parentDir, repo);
|
|
242
227
|
const link = path.join(worktreePath, repo);
|
|
@@ -244,18 +229,74 @@ async function setupWorktree(worktreePath, config, options) {
|
|
|
244
229
|
if (!fs.existsSync(link)) {
|
|
245
230
|
try {
|
|
246
231
|
fs.symlinkSync(target, link, 'dir');
|
|
247
|
-
|
|
232
|
+
printStatus('success', `Linked ${repo}`);
|
|
248
233
|
}
|
|
249
234
|
catch (error) {
|
|
250
|
-
|
|
235
|
+
printStatus('warning', `Failed to link ${repo}: ${error}`);
|
|
251
236
|
}
|
|
252
237
|
}
|
|
253
238
|
else {
|
|
254
|
-
|
|
239
|
+
printStatus('warning', `${repo} already exists in worktree`);
|
|
255
240
|
}
|
|
256
241
|
}
|
|
257
242
|
else {
|
|
258
|
-
|
|
243
|
+
printStatus('warning', `${repo} not found at ${target}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Auto-link config files from main worktree
|
|
248
|
+
let mainWorktreeRoot;
|
|
249
|
+
try {
|
|
250
|
+
mainWorktreeRoot = git.getMainWorktreeRoot(repoRoot);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// If we can't determine main worktree, skip linking
|
|
254
|
+
logger.debug('Could not determine main worktree root, skipping config file linking');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const enabledFiles = getEnabledFiles(mainWorktreeRoot);
|
|
258
|
+
if (enabledFiles.length > 0) {
|
|
259
|
+
let shouldLink = false;
|
|
260
|
+
if (config.linkConfigFiles === false) {
|
|
261
|
+
// Explicitly disabled - skip
|
|
262
|
+
logger.debug('linkConfigFiles is false, skipping auto-link');
|
|
263
|
+
}
|
|
264
|
+
else if (config.linkConfigFiles === true) {
|
|
265
|
+
// Explicitly enabled - auto-link
|
|
266
|
+
shouldLink = true;
|
|
267
|
+
logger.debug('linkConfigFiles is true, auto-linking');
|
|
268
|
+
}
|
|
269
|
+
else if (!options.nonInteractive && !options.json) {
|
|
270
|
+
// Not configured - prompt user
|
|
271
|
+
printStatus('info', `Found ${enabledFiles.length} config file(s) to link:`);
|
|
272
|
+
for (const file of enabledFiles.slice(0, 5)) {
|
|
273
|
+
printDim(` - ${file}`);
|
|
274
|
+
}
|
|
275
|
+
if (enabledFiles.length > 5) {
|
|
276
|
+
printDim(` ... and ${enabledFiles.length - 5} more`);
|
|
277
|
+
}
|
|
278
|
+
shouldLink = await promptConfirm('Link these config files from the main worktree?', true);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Non-interactive/JSON mode - default to linking
|
|
282
|
+
shouldLink = true;
|
|
283
|
+
logger.debug('Non-interactive mode, defaulting to auto-link');
|
|
284
|
+
}
|
|
285
|
+
if (shouldLink) {
|
|
286
|
+
try {
|
|
287
|
+
await runWtlink({
|
|
288
|
+
source: mainWorktreeRoot,
|
|
289
|
+
destination: worktreePath,
|
|
290
|
+
dryRun: false,
|
|
291
|
+
manifestFile: '.wtlinkrc',
|
|
292
|
+
type: 'hard',
|
|
293
|
+
yes: true,
|
|
294
|
+
});
|
|
295
|
+
printStatus('success', `Linked ${enabledFiles.length} config file(s)`);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
299
|
+
printStatus('warning', `Failed to link config files: ${errorMessage}`);
|
|
259
300
|
}
|
|
260
301
|
}
|
|
261
302
|
}
|
|
@@ -277,41 +318,127 @@ function printSummary(prNumber, branchName, worktreePath, prUrl, options, extra)
|
|
|
277
318
|
console.log(formatJsonResult(createSuccessResult('newpr', data)));
|
|
278
319
|
return;
|
|
279
320
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
321
|
+
printSummaryBox(`PR #${prNumber} worktree ready!`, [
|
|
322
|
+
{ label: 'Branch', value: branchName },
|
|
323
|
+
{ label: 'Worktree', value: worktreePath },
|
|
324
|
+
{ label: 'PR URL', value: prUrl },
|
|
325
|
+
]);
|
|
326
|
+
printNextSteps([
|
|
327
|
+
{ command: `cd ${worktreePath}` },
|
|
328
|
+
{ command: `gh pr view ${prNumber} --web`, description: 'Open PR in browser' },
|
|
329
|
+
{ command: 'wtlink link', description: 'Link config files from main worktree' },
|
|
330
|
+
]);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Handle plan generation based on options and config
|
|
334
|
+
*/
|
|
335
|
+
async function handlePlanGeneration(options, config, context) {
|
|
336
|
+
const aiConfig = config.ai ?? {};
|
|
337
|
+
// Check if AI provider is configured (simple check without initialization)
|
|
338
|
+
const aiAvailable = aiConfig.provider !== undefined &&
|
|
339
|
+
aiConfig.provider !== 'none' &&
|
|
340
|
+
aiConfig.planDocument === true;
|
|
341
|
+
// Determine if we should generate a plan
|
|
342
|
+
const decision = shouldGeneratePlan({
|
|
343
|
+
cliFlag: options.generatePlan,
|
|
344
|
+
noFlag: options.noPlan,
|
|
345
|
+
configEnabled: aiConfig.planDocument,
|
|
346
|
+
aiAvailable,
|
|
347
|
+
nonInteractive: options.nonInteractive ?? false,
|
|
348
|
+
});
|
|
349
|
+
// If prompting is needed and we're interactive, ask the user
|
|
350
|
+
let shouldGenerate = decision.generate;
|
|
351
|
+
if (decision.prompt && !options.json) {
|
|
352
|
+
shouldGenerate = await promptConfirm('Generate AI implementation plan document?', false);
|
|
353
|
+
}
|
|
354
|
+
if (!shouldGenerate) {
|
|
355
|
+
logger.debug('Plan generation skipped', { reason: decision.reason });
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
printStatus('info', 'Generating AI implementation plan...');
|
|
359
|
+
// Build path template variables
|
|
360
|
+
const vars = buildPathTemplateVars({
|
|
361
|
+
prNumber: context.prNumber,
|
|
362
|
+
description: context.description,
|
|
363
|
+
branchName: context.branchName,
|
|
364
|
+
});
|
|
365
|
+
// Resolve plan path
|
|
366
|
+
const planPath = resolvePlanPath(context.worktreePath, aiConfig, vars);
|
|
367
|
+
// Generate the plan
|
|
368
|
+
const result = await generatePlanDocument({
|
|
369
|
+
description: context.description,
|
|
370
|
+
branchName: context.branchName,
|
|
371
|
+
}, planPath, aiConfig, {
|
|
372
|
+
prNumber: context.prNumber,
|
|
373
|
+
baseBranch: options.baseBranch,
|
|
374
|
+
});
|
|
375
|
+
if (result.generated) {
|
|
376
|
+
printStatus('success', `Created plan: ${result.path}`);
|
|
377
|
+
}
|
|
378
|
+
else if (result.error) {
|
|
379
|
+
printStatus('warning', `Plan generation failed: ${result.error}`);
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Execute the unified post-worktree sequence:
|
|
385
|
+
* 1. Plan generation (if enabled)
|
|
386
|
+
* 2. Post-worktree hook
|
|
387
|
+
*
|
|
388
|
+
* This ensures consistent behavior across all three newpr modes.
|
|
389
|
+
*/
|
|
390
|
+
async function executePostWorktreeSequence(worktreePath, config, options, context, hookRunner) {
|
|
391
|
+
const result = {};
|
|
392
|
+
// Generate plan (if enabled) - happens BEFORE post-worktree hook
|
|
393
|
+
result.planResult = await handlePlanGeneration(options, config, {
|
|
394
|
+
...context,
|
|
395
|
+
worktreePath,
|
|
396
|
+
});
|
|
397
|
+
// Run post-worktree hook
|
|
398
|
+
if (hookRunner) {
|
|
399
|
+
hookRunner.updateContext({ worktreePath });
|
|
400
|
+
await hookRunner.runHook('post-worktree');
|
|
401
|
+
}
|
|
402
|
+
return result;
|
|
294
403
|
}
|
|
295
404
|
/**
|
|
296
405
|
* Mode: Setup worktree for existing PR
|
|
297
406
|
*/
|
|
298
407
|
async function modeExistingPr(prNumber, options) {
|
|
299
|
-
|
|
408
|
+
printStatus('info', `Setting up worktree for existing PR #${prNumber}...`);
|
|
300
409
|
const repoRoot = git.getRepoRoot();
|
|
301
410
|
const repoName = git.getRepoName(repoRoot);
|
|
302
411
|
const config = loadConfig(repoRoot);
|
|
412
|
+
// Initialize hook runner for post-worktree hook
|
|
413
|
+
const hookRunner = createHookRunner(options.noHooks ? {} : (config.hooks ?? {}), {
|
|
414
|
+
repoRoot,
|
|
415
|
+
baseBranch: options.baseBranch,
|
|
416
|
+
}, {
|
|
417
|
+
verbose: options.verbose ?? false,
|
|
418
|
+
showOutput: true,
|
|
419
|
+
defaultTimeout: config.hookDefaults?.timeout,
|
|
420
|
+
maxTimeout: config.hookDefaults?.maxTimeout,
|
|
421
|
+
confirmHooks: options.confirmHooks,
|
|
422
|
+
});
|
|
303
423
|
const pr = github.getPr(prNumber);
|
|
304
424
|
if (!pr) {
|
|
305
425
|
exitWithError(`Could not find PR #${prNumber}`, ErrorCode.PR_NOT_FOUND, options.json);
|
|
306
426
|
}
|
|
307
427
|
if (pr.state !== 'OPEN') {
|
|
308
|
-
|
|
428
|
+
printStatus('warning', `PR #${prNumber} is ${pr.state}`);
|
|
309
429
|
}
|
|
310
|
-
|
|
430
|
+
printStatus('info', `PR branch: ${pr.headBranch}`);
|
|
311
431
|
const worktreePath = generateWorktreePath(config, repoRoot, repoName, prNumber);
|
|
312
432
|
if (fs.existsSync(worktreePath)) {
|
|
313
433
|
exitWithError(`Worktree already exists: ${worktreePath}`, ErrorCode.WORKTREE_EXISTS, options.json);
|
|
314
434
|
}
|
|
435
|
+
// Update hook context
|
|
436
|
+
hookRunner.updateContext({
|
|
437
|
+
branchName: pr.headBranch,
|
|
438
|
+
prNumber,
|
|
439
|
+
prUrl: pr.url,
|
|
440
|
+
worktreePath,
|
|
441
|
+
});
|
|
315
442
|
// Use spinner for fetch (only in non-JSON mode)
|
|
316
443
|
if (options.json) {
|
|
317
444
|
git.fetch('origin');
|
|
@@ -346,18 +473,41 @@ async function modeExistingPr(prNumber, options) {
|
|
|
346
473
|
}
|
|
347
474
|
});
|
|
348
475
|
}
|
|
349
|
-
|
|
350
|
-
await setupWorktree(worktreePath, config, options);
|
|
476
|
+
printStatus('success', `Created worktree: ${worktreePath}`);
|
|
477
|
+
await setupWorktree(worktreePath, config, options, repoRoot);
|
|
478
|
+
// Generate description from branch name for plan context
|
|
479
|
+
const descriptionFromBranch = pr.headBranch
|
|
480
|
+
.replace(/^(feat|fix|chore)\//, '')
|
|
481
|
+
.replace(/-/g, ' ')
|
|
482
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
483
|
+
// Execute unified post-worktree sequence (plan generation + hook)
|
|
484
|
+
await executePostWorktreeSequence(worktreePath, config, options, {
|
|
485
|
+
prNumber,
|
|
486
|
+
branchName: pr.headBranch,
|
|
487
|
+
description: descriptionFromBranch,
|
|
488
|
+
}, hookRunner);
|
|
489
|
+
setAuditContext({ prNumber, worktreePath, gitBranch: pr.headBranch });
|
|
351
490
|
printSummary(prNumber, pr.headBranch, worktreePath, pr.url, options, { draft: pr.isDraft });
|
|
352
491
|
}
|
|
353
492
|
/**
|
|
354
493
|
* Mode: Create PR for existing branch
|
|
355
494
|
*/
|
|
356
495
|
async function modeExistingBranch(branchName, options) {
|
|
357
|
-
|
|
496
|
+
printStatus('info', `Creating PR for existing branch: ${branchName}...`);
|
|
358
497
|
const repoRoot = git.getRepoRoot();
|
|
359
498
|
const repoName = git.getRepoName(repoRoot);
|
|
360
499
|
const config = loadConfig(repoRoot);
|
|
500
|
+
// Initialize hook runner for post-worktree hook
|
|
501
|
+
const hookRunner = createHookRunner(options.noHooks ? {} : (config.hooks ?? {}), {
|
|
502
|
+
repoRoot,
|
|
503
|
+
baseBranch: options.baseBranch,
|
|
504
|
+
}, {
|
|
505
|
+
verbose: options.verbose ?? false,
|
|
506
|
+
showOutput: true,
|
|
507
|
+
defaultTimeout: config.hookDefaults?.timeout,
|
|
508
|
+
maxTimeout: config.hookDefaults?.maxTimeout,
|
|
509
|
+
confirmHooks: options.confirmHooks,
|
|
510
|
+
});
|
|
361
511
|
// Use spinner for fetch
|
|
362
512
|
if (options.json) {
|
|
363
513
|
git.fetch('origin');
|
|
@@ -385,11 +535,11 @@ async function modeExistingBranch(branchName, options) {
|
|
|
385
535
|
}
|
|
386
536
|
const existingPr = github.getPrByBranch(branchName);
|
|
387
537
|
if (existingPr) {
|
|
388
|
-
|
|
538
|
+
printStatus('info', `PR #${existingPr.number} already exists for branch ${branchName}`);
|
|
389
539
|
await modeExistingPr(existingPr.number, options);
|
|
390
540
|
return;
|
|
391
541
|
}
|
|
392
|
-
|
|
542
|
+
printStatus('info', 'Creating pull request...');
|
|
393
543
|
// Generate description from branch name for AI context
|
|
394
544
|
const descriptionFromBranch = branchName
|
|
395
545
|
.replace(/^(feat|fix|chore)\//, '')
|
|
@@ -404,7 +554,7 @@ async function modeExistingBranch(branchName, options) {
|
|
|
404
554
|
commitMessages: git.getCommitMessages(`origin/${options.baseBranch}`, branchName),
|
|
405
555
|
});
|
|
406
556
|
if (prContent.aiGenerated) {
|
|
407
|
-
|
|
557
|
+
printStatus('info', '✨ AI-generated PR content');
|
|
408
558
|
}
|
|
409
559
|
const defaultBody = `## Summary
|
|
410
560
|
|
|
@@ -427,7 +577,13 @@ PR created from existing branch: \`${branchName}\`
|
|
|
427
577
|
head: branchName,
|
|
428
578
|
draft: options.draft,
|
|
429
579
|
});
|
|
430
|
-
|
|
580
|
+
printStatus('success', `Created PR #${pr.number}: ${pr.url}`);
|
|
581
|
+
// Update hook context
|
|
582
|
+
hookRunner.updateContext({
|
|
583
|
+
branchName,
|
|
584
|
+
prNumber: pr.number,
|
|
585
|
+
prUrl: pr.url,
|
|
586
|
+
});
|
|
431
587
|
const worktreePath = generateWorktreePath(config, repoRoot, repoName, pr.number);
|
|
432
588
|
// Use spinner for worktree creation
|
|
433
589
|
if (options.json) {
|
|
@@ -454,8 +610,15 @@ PR created from existing branch: \`${branchName}\`
|
|
|
454
610
|
}
|
|
455
611
|
});
|
|
456
612
|
}
|
|
457
|
-
|
|
458
|
-
await setupWorktree(worktreePath, config, options);
|
|
613
|
+
printStatus('success', `Created worktree: ${worktreePath}`);
|
|
614
|
+
await setupWorktree(worktreePath, config, options, repoRoot);
|
|
615
|
+
// Execute unified post-worktree sequence (plan generation + hook)
|
|
616
|
+
await executePostWorktreeSequence(worktreePath, config, options, {
|
|
617
|
+
prNumber: pr.number,
|
|
618
|
+
branchName,
|
|
619
|
+
description: descriptionFromBranch,
|
|
620
|
+
}, hookRunner);
|
|
621
|
+
setAuditContext({ prNumber: pr.number, worktreePath, gitBranch: branchName });
|
|
459
622
|
printSummary(pr.number, branchName, worktreePath, pr.url, options);
|
|
460
623
|
}
|
|
461
624
|
/**
|
|
@@ -472,10 +635,11 @@ async function modeNewFeature(description, options) {
|
|
|
472
635
|
baseBranch: options.baseBranch,
|
|
473
636
|
description,
|
|
474
637
|
}, {
|
|
475
|
-
verbose:
|
|
638
|
+
verbose: options.verbose ?? false,
|
|
476
639
|
showOutput: true,
|
|
477
640
|
defaultTimeout: config.hookDefaults?.timeout,
|
|
478
641
|
maxTimeout: config.hookDefaults?.maxTimeout,
|
|
642
|
+
confirmHooks: options.confirmHooks,
|
|
479
643
|
});
|
|
480
644
|
// Run pre-analyze hook
|
|
481
645
|
if (!(await hookRunner.runHook('pre-analyze'))) {
|
|
@@ -493,7 +657,7 @@ async function modeNewFeature(description, options) {
|
|
|
493
657
|
}
|
|
494
658
|
}
|
|
495
659
|
catch {
|
|
496
|
-
|
|
660
|
+
printStatus('warning', 'Could not fetch from origin (network unavailable?)');
|
|
497
661
|
}
|
|
498
662
|
const state = analyzeGitState(options.baseBranch);
|
|
499
663
|
const scenario = detectScenario(state);
|
|
@@ -507,7 +671,7 @@ async function modeNewFeature(description, options) {
|
|
|
507
671
|
if (!(await hookRunner.runHook('post-analyze'))) {
|
|
508
672
|
exitWithError('Aborted by post-analyze hook.', ErrorCode.HOOK_FAILED, options.json);
|
|
509
673
|
}
|
|
510
|
-
debug('State analysis complete', {
|
|
674
|
+
logger.debug('State analysis complete', {
|
|
511
675
|
scenario,
|
|
512
676
|
branchType: state.branchType,
|
|
513
677
|
currentBranch: state.currentBranch,
|
|
@@ -523,10 +687,10 @@ async function modeNewFeature(description, options) {
|
|
|
523
687
|
console.log(formatJsonResult(createErrorResult('newpr', ErrorCode.USER_CANCELLED, 'User cancelled')));
|
|
524
688
|
process.exit(1);
|
|
525
689
|
}
|
|
526
|
-
|
|
690
|
+
printStatus('error', 'Aborted by user.');
|
|
527
691
|
process.exit(1);
|
|
528
692
|
}
|
|
529
|
-
debug('User selected action', {
|
|
693
|
+
logger.debug('User selected action', {
|
|
530
694
|
action: action.action,
|
|
531
695
|
branchFrom: action.branchFrom,
|
|
532
696
|
stashUnstaged: action.stashUnstaged,
|
|
@@ -545,36 +709,36 @@ async function modeNewFeature(description, options) {
|
|
|
545
709
|
const deps = createActionDeps(repoRoot);
|
|
546
710
|
executeStateAction(action, description, currentBranch, deps, repoRoot);
|
|
547
711
|
if (!git.remoteBranchExists(currentBranch)) {
|
|
548
|
-
|
|
712
|
+
printStatus('info', 'Pushing branch to origin...');
|
|
549
713
|
git.push({ setUpstream: true, remote: 'origin', branch: currentBranch });
|
|
550
714
|
}
|
|
551
715
|
await modeExistingBranch(currentBranch, options);
|
|
552
716
|
return;
|
|
553
717
|
}
|
|
554
|
-
|
|
718
|
+
printStatus('info', `Creating feature branch: ${branchName}`);
|
|
555
719
|
if (git.remoteBranchExists(branchName)) {
|
|
556
|
-
|
|
720
|
+
printStatus('warning', `Branch ${branchName} already exists on remote`);
|
|
557
721
|
const existingPr = github.getPrByBranch(branchName);
|
|
558
722
|
if (existingPr) {
|
|
559
|
-
|
|
723
|
+
printStatus('info', `PR #${existingPr.number} already exists, setting up worktree...`);
|
|
560
724
|
await modeExistingPr(existingPr.number, options);
|
|
561
725
|
}
|
|
562
726
|
else {
|
|
563
|
-
|
|
727
|
+
printStatus('info', 'No PR exists, creating one...');
|
|
564
728
|
await modeExistingBranch(branchName, options);
|
|
565
729
|
}
|
|
566
730
|
return;
|
|
567
731
|
}
|
|
568
732
|
const originalBranch = git.getCurrentBranch() || 'main';
|
|
569
733
|
const deps = createActionDeps(repoRoot);
|
|
570
|
-
debug('Before executeStateAction', {
|
|
734
|
+
logger.debug('Before executeStateAction', {
|
|
571
735
|
originalBranch,
|
|
572
736
|
branchName,
|
|
573
737
|
stagedFilesBefore: git.getStagedFiles(),
|
|
574
738
|
unstagedFilesBefore: git.getUnstagedFiles(),
|
|
575
739
|
});
|
|
576
740
|
const actionResult = executeStateAction(action, description, branchName, deps, repoRoot);
|
|
577
|
-
debug('After executeStateAction', {
|
|
741
|
+
logger.debug('After executeStateAction', {
|
|
578
742
|
success: actionResult.success,
|
|
579
743
|
stashRef: actionResult.stashRef,
|
|
580
744
|
stagedFilesAfter: git.getStagedFiles(),
|
|
@@ -586,7 +750,7 @@ async function modeNewFeature(description, options) {
|
|
|
586
750
|
// Stash unstaged changes if needed
|
|
587
751
|
let unstagedStashRef = null;
|
|
588
752
|
if (action.stashUnstaged) {
|
|
589
|
-
|
|
753
|
+
printStatus('info', 'Stashing unstaged changes (will move to worktree)...');
|
|
590
754
|
unstagedStashRef = git.stash({
|
|
591
755
|
keepIndex: true,
|
|
592
756
|
message: 'newpr: unstaged changes for worktree',
|
|
@@ -598,31 +762,34 @@ async function modeNewFeature(description, options) {
|
|
|
598
762
|
if (!(await hookRunner.runHook('pre-branch'))) {
|
|
599
763
|
exitWithError('Aborted by pre-branch hook.', ErrorCode.HOOK_FAILED, options.json);
|
|
600
764
|
}
|
|
601
|
-
|
|
602
|
-
debug('Before checkout', {
|
|
765
|
+
printStatus('info', `Creating branch from ${branchFrom}...`);
|
|
766
|
+
logger.debug('Before checkout', {
|
|
603
767
|
branchFrom,
|
|
604
768
|
branchName,
|
|
605
769
|
currentBranch: git.getCurrentBranch(),
|
|
606
770
|
stagedFilesBeforeCheckout: git.getStagedFiles(),
|
|
607
771
|
});
|
|
608
772
|
try {
|
|
609
|
-
git.exec(['checkout', '-b', branchName, branchFrom]);
|
|
773
|
+
git.exec(['checkout', '-b', branchName, branchFrom], { cwd: repoRoot });
|
|
610
774
|
}
|
|
611
775
|
catch (checkoutError) {
|
|
612
776
|
// When checkout fails (e.g., due to conflicting changes), git preserves
|
|
613
777
|
// the staged files in the index - no data is lost. Provide a helpful message.
|
|
614
778
|
const errorMessage = checkoutError instanceof Error ? checkoutError.message : String(checkoutError);
|
|
615
779
|
if (errorMessage.includes('overwritten') || errorMessage.includes('conflict')) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
780
|
+
printError({
|
|
781
|
+
title: 'Checkout failed due to conflicting changes.',
|
|
782
|
+
detail: 'Your staged changes are preserved in the index.',
|
|
783
|
+
hint: 'To resolve this, either:\n' +
|
|
784
|
+
' 1. Commit your changes first, then run newpr again\n' +
|
|
785
|
+
' 2. Stash your changes: git stash push\n' +
|
|
786
|
+
' 3. Use a different branch point (e.g., HEAD instead of origin/main)',
|
|
787
|
+
});
|
|
621
788
|
}
|
|
622
789
|
throw checkoutError;
|
|
623
790
|
}
|
|
624
791
|
const stagedFiles = git.getStagedFiles();
|
|
625
|
-
debug('After checkout', {
|
|
792
|
+
logger.debug('After checkout', {
|
|
626
793
|
newBranch: git.getCurrentBranch(),
|
|
627
794
|
stagedFilesAfterCheckout: stagedFiles,
|
|
628
795
|
stagedFilesCount: stagedFiles.length,
|
|
@@ -636,9 +803,9 @@ async function modeNewFeature(description, options) {
|
|
|
636
803
|
if (!(await hookRunner.runHook('pre-commit'))) {
|
|
637
804
|
exitWithError('Aborted by pre-commit hook.', ErrorCode.HOOK_FAILED, options.json);
|
|
638
805
|
}
|
|
639
|
-
|
|
806
|
+
printStatus('info', 'Committing staged changes...');
|
|
640
807
|
git.commit({ message: `feat: ${description}\n\n🤖 Created with newpr` });
|
|
641
|
-
debug('Committed staged changes');
|
|
808
|
+
logger.debug('Committed staged changes');
|
|
642
809
|
// Run post-commit hook
|
|
643
810
|
await hookRunner.runHook('post-commit');
|
|
644
811
|
}
|
|
@@ -647,12 +814,12 @@ async function modeNewFeature(description, options) {
|
|
|
647
814
|
if (!(await hookRunner.runHook('pre-commit'))) {
|
|
648
815
|
exitWithError('Aborted by pre-commit hook.', ErrorCode.HOOK_FAILED, options.json);
|
|
649
816
|
}
|
|
650
|
-
|
|
817
|
+
printStatus('info', 'Creating initial commit (required for PR creation)...');
|
|
651
818
|
git.commit({
|
|
652
819
|
message: `chore: initialize ${branchName}\n\nBranch created for: ${description}\n\n🤖 Created with newpr`,
|
|
653
820
|
allowEmpty: true,
|
|
654
821
|
});
|
|
655
|
-
debug('Created empty commit (no staged files found)');
|
|
822
|
+
logger.debug('Created empty commit (no staged files found)');
|
|
656
823
|
// Run post-commit hook
|
|
657
824
|
await hookRunner.runHook('post-commit');
|
|
658
825
|
}
|
|
@@ -662,21 +829,21 @@ async function modeNewFeature(description, options) {
|
|
|
662
829
|
}
|
|
663
830
|
// Use spinner for push
|
|
664
831
|
if (options.json) {
|
|
665
|
-
git.push({ setUpstream: true, remote: 'origin', branch: branchName });
|
|
832
|
+
git.push({ setUpstream: true, remote: 'origin', branch: branchName }, repoRoot);
|
|
666
833
|
}
|
|
667
834
|
else {
|
|
668
835
|
await withSpinner('Pushing branch to origin...', async () => {
|
|
669
|
-
await git.pushAsync({ setUpstream: true, remote: 'origin', branch: branchName });
|
|
836
|
+
await git.pushAsync({ setUpstream: true, remote: 'origin', branch: branchName }, repoRoot);
|
|
670
837
|
});
|
|
671
838
|
}
|
|
672
839
|
// Run post-push hook
|
|
673
840
|
await hookRunner.runHook('post-push');
|
|
674
|
-
git.checkout(originalBranch);
|
|
841
|
+
git.checkout(originalBranch, repoRoot);
|
|
675
842
|
// Run pre-pr hook
|
|
676
843
|
if (!(await hookRunner.runHook('pre-pr'))) {
|
|
677
844
|
exitWithError('Aborted by pre-pr hook.', ErrorCode.HOOK_FAILED, options.json);
|
|
678
845
|
}
|
|
679
|
-
|
|
846
|
+
printStatus('info', 'Creating pull request...');
|
|
680
847
|
// Generate AI-enhanced PR content if enabled
|
|
681
848
|
// Use origin/baseBranch to compare against remote, not potentially stale local branch
|
|
682
849
|
const prContent = await generatePRContentAsync(config, {
|
|
@@ -687,7 +854,7 @@ async function modeNewFeature(description, options) {
|
|
|
687
854
|
commitMessages: git.getCommitMessages(`origin/${options.baseBranch}`, branchName),
|
|
688
855
|
});
|
|
689
856
|
if (prContent.aiGenerated) {
|
|
690
|
-
|
|
857
|
+
printStatus('info', '✨ AI-generated PR content');
|
|
691
858
|
}
|
|
692
859
|
const defaultBody = `## Summary
|
|
693
860
|
|
|
@@ -710,7 +877,7 @@ ${description}
|
|
|
710
877
|
head: branchName,
|
|
711
878
|
draft: options.draft,
|
|
712
879
|
});
|
|
713
|
-
|
|
880
|
+
printStatus('success', `Created PR #${pr.number}: ${pr.url}`);
|
|
714
881
|
// Update context with PR info
|
|
715
882
|
hookRunner.updateContext({
|
|
716
883
|
prNumber: pr.number,
|
|
@@ -727,29 +894,34 @@ ${description}
|
|
|
727
894
|
}
|
|
728
895
|
// Use spinner for worktree creation
|
|
729
896
|
if (options.json) {
|
|
730
|
-
git.addWorktree(worktreePath, branchName);
|
|
897
|
+
git.addWorktree(worktreePath, branchName, { cwd: repoRoot });
|
|
731
898
|
}
|
|
732
899
|
else {
|
|
733
900
|
await withSpinner(`Creating worktree at ${worktreePath}...`, async () => {
|
|
734
|
-
await git.addWorktreeAsync(worktreePath, branchName);
|
|
901
|
+
await git.addWorktreeAsync(worktreePath, branchName, { cwd: repoRoot });
|
|
735
902
|
});
|
|
736
903
|
}
|
|
737
|
-
|
|
904
|
+
printStatus('success', `Created worktree: ${worktreePath}`);
|
|
738
905
|
if (unstagedStashRef) {
|
|
739
|
-
|
|
906
|
+
printStatus('info', 'Moving unstaged changes to worktree...');
|
|
740
907
|
try {
|
|
741
908
|
git.stashApply(unstagedStashRef, worktreePath);
|
|
742
|
-
|
|
909
|
+
printStatus('success', 'Unstaged changes applied to worktree');
|
|
743
910
|
git.stashDrop(unstagedStashRef);
|
|
744
911
|
}
|
|
745
912
|
catch {
|
|
746
|
-
|
|
747
|
-
|
|
913
|
+
printStatus('warning', 'Failed to apply unstaged changes to worktree.');
|
|
914
|
+
printStatus('warning', "Run 'git stash pop' in main worktree to recover them.");
|
|
748
915
|
}
|
|
749
916
|
}
|
|
750
|
-
await setupWorktree(worktreePath, config, options);
|
|
751
|
-
//
|
|
752
|
-
await
|
|
917
|
+
await setupWorktree(worktreePath, config, options, repoRoot);
|
|
918
|
+
// Execute unified post-worktree sequence (plan generation + hook)
|
|
919
|
+
await executePostWorktreeSequence(worktreePath, config, options, {
|
|
920
|
+
prNumber: pr.number,
|
|
921
|
+
branchName,
|
|
922
|
+
description,
|
|
923
|
+
}, hookRunner);
|
|
924
|
+
setAuditContext({ prNumber: pr.number, worktreePath, gitBranch: branchName });
|
|
753
925
|
printSummary(pr.number, branchName, worktreePath, pr.url, options, {
|
|
754
926
|
scenario,
|
|
755
927
|
actionTaken: action.action,
|
|
@@ -759,12 +931,12 @@ ${description}
|
|
|
759
931
|
// Run cleanup hook
|
|
760
932
|
await hookRunner.runCleanup(error instanceof Error ? error : undefined);
|
|
761
933
|
if (actionResult.stashRef) {
|
|
762
|
-
|
|
934
|
+
printStatus('info', 'Restoring stashed changes...');
|
|
763
935
|
try {
|
|
764
936
|
git.stashPop(actionResult.stashRef);
|
|
765
937
|
}
|
|
766
938
|
catch {
|
|
767
|
-
|
|
939
|
+
printStatus('warning', "Failed to restore stash. Run 'git stash pop' manually.");
|
|
768
940
|
}
|
|
769
941
|
}
|
|
770
942
|
throw error;
|
|
@@ -784,25 +956,23 @@ function exitWithError(message, code, useJson) {
|
|
|
784
956
|
console.log(formatJsonResult(createErrorResult('newpr', code, message)));
|
|
785
957
|
}
|
|
786
958
|
else {
|
|
787
|
-
|
|
959
|
+
const hint = getErrorSuggestion(code);
|
|
960
|
+
printError({ title: message, hint });
|
|
788
961
|
}
|
|
789
962
|
process.exit(1);
|
|
790
963
|
}
|
|
791
964
|
/**
|
|
792
|
-
*
|
|
965
|
+
* Run the newpr handler with already-parsed options.
|
|
966
|
+
*
|
|
967
|
+
* This is the core newpr workflow, callable both from the standalone CLI
|
|
968
|
+
* entry point and from the `wt new` handler (in-process delegation).
|
|
969
|
+
*
|
|
970
|
+
* Callers are responsible for:
|
|
971
|
+
* - Initializing the logger (initializeLogger)
|
|
972
|
+
* - Setting JSON mode (setJsonMode)
|
|
973
|
+
* - Setting color enabled state (setColorEnabled)
|
|
793
974
|
*/
|
|
794
|
-
async function
|
|
795
|
-
const rawArgs = process.argv.slice(2);
|
|
796
|
-
const useJson = hasJsonFlag(rawArgs);
|
|
797
|
-
const result = parseArgs(rawArgs);
|
|
798
|
-
if (result.kind === 'help') {
|
|
799
|
-
console.log(getHelpText());
|
|
800
|
-
process.exit(0);
|
|
801
|
-
}
|
|
802
|
-
if (result.kind === 'error') {
|
|
803
|
-
exitWithError(result.message, ErrorCode.INVALID_ARGUMENT, useJson);
|
|
804
|
-
}
|
|
805
|
-
const { options } = result;
|
|
975
|
+
export async function runNewprHandler(options) {
|
|
806
976
|
// Apply config.draftPr if user didn't explicitly set --draft or --ready
|
|
807
977
|
try {
|
|
808
978
|
const repoRoot = git.getRepoRoot();
|
|
@@ -839,26 +1009,55 @@ async function main() {
|
|
|
839
1009
|
break;
|
|
840
1010
|
}
|
|
841
1011
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
const
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1012
|
+
/**
|
|
1013
|
+
* Main entry point (standalone CLI)
|
|
1014
|
+
*/
|
|
1015
|
+
async function main() {
|
|
1016
|
+
printDeprecationNotice('newpr', 'wt new');
|
|
1017
|
+
const rawArgs = process.argv.slice(2);
|
|
1018
|
+
const useJson = hasJsonFlag(rawArgs);
|
|
1019
|
+
const result = parseArgs(rawArgs);
|
|
1020
|
+
if (result.kind === 'help') {
|
|
1021
|
+
console.log(getHelpText());
|
|
1022
|
+
process.exit(0);
|
|
853
1023
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1024
|
+
if (result.kind === 'error') {
|
|
1025
|
+
exitWithError(result.message, ErrorCode.INVALID_ARGUMENT, useJson);
|
|
1026
|
+
}
|
|
1027
|
+
const { options } = result;
|
|
1028
|
+
// Initialize logger (only when run as standalone CLI)
|
|
1029
|
+
initializeLogger({
|
|
1030
|
+
verbose: options.verbose,
|
|
1031
|
+
quiet: options.quiet,
|
|
1032
|
+
noColor: options.noColor,
|
|
1033
|
+
json: options.json,
|
|
1034
|
+
commandName: 'newpr',
|
|
1035
|
+
});
|
|
1036
|
+
setJsonMode(options.json);
|
|
1037
|
+
if (options.noColor) {
|
|
1038
|
+
setColorEnabled(false);
|
|
862
1039
|
}
|
|
863
|
-
|
|
1040
|
+
await runNewprHandler(options);
|
|
1041
|
+
}
|
|
1042
|
+
// Only run main() when this file is executed directly (not when imported)
|
|
1043
|
+
const isMain = import.meta.url.endsWith(process.argv[1]?.replace(/\\/g, '/') || '');
|
|
1044
|
+
if (isMain || process.argv[1]?.endsWith('newpr.js')) {
|
|
1045
|
+
main().catch((error) => {
|
|
1046
|
+
// Determine if JSON output is expected
|
|
1047
|
+
const useJson = hasJsonFlag(process.argv.slice(2));
|
|
1048
|
+
if (error instanceof NonInteractiveError) {
|
|
1049
|
+
exitWithError(error.message, error.code, useJson);
|
|
1050
|
+
}
|
|
1051
|
+
if (useJson) {
|
|
1052
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1053
|
+
const code = getErrorCodeFromError(error);
|
|
1054
|
+
exitWithError(message, code, useJson);
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
const display = errorToDisplay(error);
|
|
1058
|
+
printError(display);
|
|
1059
|
+
process.exit(1);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
864
1063
|
//# sourceMappingURL=newpr.js.map
|