@xdevops/issue-auto-finish 1.0.86 → 1.0.87

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.
Files changed (129) hide show
  1. package/dist/AIRunnerRegistry-II3WWSFN.js +31 -0
  2. package/dist/PtyRunner-6UGI5STW.js +22 -0
  3. package/dist/TerminalManager-RT2N7N5R.js +8 -0
  4. package/dist/ai-runner/AIRunner.d.ts +9 -1
  5. package/dist/ai-runner/AIRunner.d.ts.map +1 -1
  6. package/dist/ai-runner/AIRunnerRegistry.d.ts +37 -1
  7. package/dist/ai-runner/AIRunnerRegistry.d.ts.map +1 -1
  8. package/dist/ai-runner/PtyRunner.d.ts +114 -0
  9. package/dist/ai-runner/PtyRunner.d.ts.map +1 -0
  10. package/dist/ai-runner/index.d.ts +3 -1
  11. package/dist/ai-runner/index.d.ts.map +1 -1
  12. package/dist/{ai-runner-SVUNA3FX.js → ai-runner-HLA44WI6.js} +12 -3
  13. package/dist/{analyze-SXXPE5XL.js → analyze-ZIXNC5GN.js} +10 -8
  14. package/dist/{analyze-SXXPE5XL.js.map → analyze-ZIXNC5GN.js.map} +1 -1
  15. package/dist/{braindump-4E5SDMSZ.js → braindump-56WAY2RD.js} +10 -8
  16. package/dist/{braindump-4E5SDMSZ.js.map → braindump-56WAY2RD.js.map} +1 -1
  17. package/dist/{chunk-ICXB2WP5.js → chunk-2MESXJEZ.js} +3 -3
  18. package/dist/{chunk-P4O4ZXEC.js → chunk-2YQHKXLL.js} +40 -19
  19. package/dist/chunk-2YQHKXLL.js.map +1 -0
  20. package/dist/chunk-AVGZH64A.js +211 -0
  21. package/dist/chunk-AVGZH64A.js.map +1 -0
  22. package/dist/{chunk-OUPJMHAL.js → chunk-IP3QTP5A.js} +1026 -764
  23. package/dist/chunk-IP3QTP5A.js.map +1 -0
  24. package/dist/chunk-KC5S66OZ.js +177 -0
  25. package/dist/chunk-KC5S66OZ.js.map +1 -0
  26. package/dist/{chunk-4QV6D34Y.js → chunk-M5C2WILQ.js} +8 -6
  27. package/dist/{chunk-4QV6D34Y.js.map → chunk-M5C2WILQ.js.map} +1 -1
  28. package/dist/{chunk-FWEW5E3B.js → chunk-NZHKAPU6.js} +35 -5
  29. package/dist/chunk-NZHKAPU6.js.map +1 -0
  30. package/dist/{chunk-KTYPZTF4.js → chunk-O3WEV5W3.js} +10 -2
  31. package/dist/chunk-O3WEV5W3.js.map +1 -0
  32. package/dist/{chunk-QO5VTSMI.js → chunk-QZZGIZWC.js} +455 -202
  33. package/dist/chunk-QZZGIZWC.js.map +1 -0
  34. package/dist/{chunk-4LFNFRCL.js → chunk-SAMTXC4A.js} +91 -214
  35. package/dist/chunk-SAMTXC4A.js.map +1 -0
  36. package/dist/chunk-U237JSLB.js +1 -0
  37. package/dist/chunk-U237JSLB.js.map +1 -0
  38. package/dist/chunk-U6GWFTKA.js +657 -0
  39. package/dist/chunk-U6GWFTKA.js.map +1 -0
  40. package/dist/{chunk-HOFYJEJ4.js → chunk-UBQLXQ7I.js} +11 -11
  41. package/dist/cli/setup/env-metadata.d.ts.map +1 -1
  42. package/dist/cli.js +8 -7
  43. package/dist/cli.js.map +1 -1
  44. package/dist/{config-QLINHCHD.js → config-WTRSZLOC.js} +4 -3
  45. package/dist/config-WTRSZLOC.js.map +1 -0
  46. package/dist/config-schema.d.ts +17 -1
  47. package/dist/config-schema.d.ts.map +1 -1
  48. package/dist/config.d.ts +20 -0
  49. package/dist/config.d.ts.map +1 -1
  50. package/dist/errors/PhaseAbortedError.d.ts +3 -3
  51. package/dist/errors/PhaseAbortedError.d.ts.map +1 -1
  52. package/dist/errors-S3BWYA4I.js +43 -0
  53. package/dist/errors-S3BWYA4I.js.map +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +14 -11
  56. package/dist/{init-TDKDC6YP.js → init-QQDXGTPB.js} +7 -6
  57. package/dist/{init-TDKDC6YP.js.map → init-QQDXGTPB.js.map} +1 -1
  58. package/dist/lib.js +9 -7
  59. package/dist/lib.js.map +1 -1
  60. package/dist/orchestrator/IssueProcessingContext.d.ts +39 -21
  61. package/dist/orchestrator/IssueProcessingContext.d.ts.map +1 -1
  62. package/dist/orchestrator/PipelineOrchestrator.d.ts +10 -1
  63. package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
  64. package/dist/orchestrator/steps/PhaseLoopStep.d.ts +1 -1
  65. package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
  66. package/dist/orchestrator/steps/SetupStep.d.ts.map +1 -1
  67. package/dist/persistence/PlanPersistence.d.ts +7 -1
  68. package/dist/persistence/PlanPersistence.d.ts.map +1 -1
  69. package/dist/phases/BasePhase.d.ts +31 -42
  70. package/dist/phases/BasePhase.d.ts.map +1 -1
  71. package/dist/phases/BuildPhase.d.ts.map +1 -1
  72. package/dist/phases/PhaseFactory.d.ts +2 -3
  73. package/dist/phases/PhaseFactory.d.ts.map +1 -1
  74. package/dist/phases/PhaseOutcome.d.ts +42 -0
  75. package/dist/phases/PhaseOutcome.d.ts.map +1 -0
  76. package/dist/phases/PlanPhase.d.ts +1 -1
  77. package/dist/phases/PlanPhase.d.ts.map +1 -1
  78. package/dist/phases/ReleasePhase.d.ts +8 -18
  79. package/dist/phases/ReleasePhase.d.ts.map +1 -1
  80. package/dist/phases/UatPhase.d.ts +7 -24
  81. package/dist/phases/UatPhase.d.ts.map +1 -1
  82. package/dist/phases/VerifyPhase.d.ts +4 -4
  83. package/dist/phases/VerifyPhase.d.ts.map +1 -1
  84. package/dist/poller/IssuePoller.d.ts.map +1 -1
  85. package/dist/prompts/release-templates.d.ts.map +1 -1
  86. package/dist/prompts/templates.d.ts.map +1 -1
  87. package/dist/{restart-4NSHDOX3.js → restart-BMILTP5X.js} +6 -5
  88. package/dist/{restart-4NSHDOX3.js.map → restart-BMILTP5X.js.map} +1 -1
  89. package/dist/run.js +14 -11
  90. package/dist/run.js.map +1 -1
  91. package/dist/settings/ExperimentalSettings.d.ts +1 -1
  92. package/dist/settings/ExperimentalSettings.d.ts.map +1 -1
  93. package/dist/start-6QRW6IJI.js +15 -0
  94. package/dist/start-6QRW6IJI.js.map +1 -0
  95. package/dist/terminal/TerminalManager.d.ts +62 -0
  96. package/dist/terminal/TerminalManager.d.ts.map +1 -0
  97. package/dist/terminal/TerminalWebSocket.d.ts +9 -0
  98. package/dist/terminal/TerminalWebSocket.d.ts.map +1 -0
  99. package/dist/tracker/ExecutableTask.d.ts +4 -2
  100. package/dist/tracker/ExecutableTask.d.ts.map +1 -1
  101. package/dist/tracker/IssueState.d.ts +11 -1
  102. package/dist/tracker/IssueState.d.ts.map +1 -1
  103. package/dist/tracker/IssueTracker.d.ts +19 -1
  104. package/dist/tracker/IssueTracker.d.ts.map +1 -1
  105. package/dist/web/WebServer.d.ts +4 -0
  106. package/dist/web/WebServer.d.ts.map +1 -1
  107. package/dist/web/routes/terminal.d.ts +11 -0
  108. package/dist/web/routes/terminal.d.ts.map +1 -0
  109. package/dist/webhook/CommandExecutor.d.ts.map +1 -1
  110. package/package.json +7 -1
  111. package/src/web/frontend/dist/assets/index-COYziOhv.css +1 -0
  112. package/src/web/frontend/dist/assets/index-D_oTMuJU.js +151 -0
  113. package/src/web/frontend/dist/index.html +2 -2
  114. package/dist/chunk-4LFNFRCL.js.map +0 -1
  115. package/dist/chunk-DADQSKPL.js +0 -1
  116. package/dist/chunk-FWEW5E3B.js.map +0 -1
  117. package/dist/chunk-KTYPZTF4.js.map +0 -1
  118. package/dist/chunk-OUPJMHAL.js.map +0 -1
  119. package/dist/chunk-P4O4ZXEC.js.map +0 -1
  120. package/dist/chunk-QO5VTSMI.js.map +0 -1
  121. package/dist/start-XZIBPLC2.js +0 -14
  122. package/src/web/frontend/dist/assets/index-BWVpNmFm.js +0 -133
  123. package/src/web/frontend/dist/assets/index-C7lorIa0.css +0 -1
  124. /package/dist/{ai-runner-SVUNA3FX.js.map → AIRunnerRegistry-II3WWSFN.js.map} +0 -0
  125. /package/dist/{chunk-DADQSKPL.js.map → PtyRunner-6UGI5STW.js.map} +0 -0
  126. /package/dist/{config-QLINHCHD.js.map → TerminalManager-RT2N7N5R.js.map} +0 -0
  127. /package/dist/{start-XZIBPLC2.js.map → ai-runner-HLA44WI6.js.map} +0 -0
  128. /package/dist/{chunk-ICXB2WP5.js.map → chunk-2MESXJEZ.js.map} +0 -0
  129. /package/dist/{chunk-HOFYJEJ4.js.map → chunk-UBQLXQ7I.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/git/GitOperations.ts","../src/events/EventBus.ts","../src/utils/AsyncMutex.ts","../src/tracker/BaseTracker.ts","../src/tracker/IssueRecordHelper.ts","../src/tracker/ExecutableTask.ts","../src/prompts/templates.ts","../src/git/ConflictResolver.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { logger as rootLogger } from '../logger.js';\n\nconst execFileAsync = promisify(execFile);\nconst logger = rootLogger.child('GitOperations');\n\nexport class GitOperations {\n private workDir: string;\n\n constructor(workDir: string) {\n this.workDir = workDir;\n }\n\n private async exec(args: string[]): Promise<string> {\n logger.debug('git exec', { args });\n const { stdout } = await execFileAsync('git', args, {\n cwd: this.workDir,\n maxBuffer: 10 * 1024 * 1024,\n env: { ...process.env, HUSKY: '0' },\n });\n return stdout.trim();\n }\n\n async fetchAndPull(branch: string): Promise<void> {\n await this.exec(['fetch', 'origin']);\n await this.exec(['checkout', '-f', branch]);\n await this.exec(['pull', 'origin', branch]);\n logger.info('Fetched and pulled', { branch });\n }\n\n async fetch(): Promise<void> {\n await this.exec(['fetch', 'origin']);\n logger.info('Fetched from origin');\n }\n\n async createBranch(name: string, from: string): Promise<void> {\n // Create branch from origin/<from> without needing to checkout <from> first\n // Use -f to force checkout when untracked files conflict with the target\n await this.exec(['checkout', '-f', '-b', name, `origin/${from}`]);\n logger.info('Branch created', { name, from: `origin/${from}` });\n }\n\n async checkout(branch: string): Promise<void> {\n await this.exec(['checkout', '-f', branch]);\n logger.info('Checked out', { branch });\n }\n\n async add(files: string[]): Promise<void> {\n await this.exec(['add', ...files]);\n }\n\n async commit(message: string): Promise<void> {\n await this.exec(['commit', '--no-verify', '-m', message]);\n logger.info('Committed', { message: message.slice(0, 80) });\n }\n\n async push(branch: string): Promise<void> {\n await this.exec(['push', '--no-verify', '-u', 'origin', branch]);\n logger.info('Pushed', { branch });\n }\n\n async branchExists(name: string): Promise<boolean> {\n try {\n await this.exec(['rev-parse', '--verify', name]);\n return true;\n } catch {\n return false;\n }\n }\n\n async remoteBranchExists(name: string): Promise<boolean> {\n try {\n await this.exec(['ls-remote', '--exit-code', '--heads', 'origin', name]);\n return true;\n } catch {\n return false;\n }\n }\n\n async getCurrentBranch(): Promise<string> {\n return this.exec(['rev-parse', '--abbrev-ref', 'HEAD']);\n }\n\n async stash(): Promise<void> {\n await this.exec(['stash']);\n }\n\n async stashPop(): Promise<void> {\n await this.exec(['stash', 'pop']);\n }\n\n async hasChanges(): Promise<boolean> {\n const status = await this.exec(['status', '--porcelain']);\n return status.length > 0;\n }\n\n async addAndCommit(files: string[], message: string): Promise<void> {\n await this.add(files);\n await this.commit(message);\n }\n\n async fetchBranch(branch: string): Promise<void> {\n await this.exec(['fetch', 'origin', branch]);\n logger.info('Fetched specific branch', { branch });\n }\n\n async checkoutTrack(remoteBranch: string): Promise<void> {\n await this.fetchBranch(remoteBranch);\n await this.exec(['checkout', '-f', '-B', remoteBranch, 'FETCH_HEAD']);\n logger.info('Checked out remote branch via FETCH_HEAD', { remoteBranch });\n }\n\n async addCommitAndPush(files: string[], message: string, branch: string): Promise<void> {\n await this.add(files);\n await this.commit(message);\n await this.push(branch);\n }\n\n async deleteBranch(name: string): Promise<void> {\n await this.exec(['branch', '-D', name]);\n logger.info('Branch deleted', { name });\n }\n\n async deleteRemoteBranch(name: string): Promise<void> {\n await this.exec(['push', 'origin', '--delete', name]);\n logger.info('Remote branch deleted', { name });\n }\n\n async worktreeAdd(dir: string, newBranch: string, startPoint: string): Promise<void> {\n await this.exec(['worktree', 'add', '-b', newBranch, dir, startPoint]);\n logger.info('Worktree added (new branch)', { dir, newBranch, startPoint });\n }\n\n async worktreeAddExisting(dir: string, branch: string): Promise<void> {\n await this.exec(['worktree', 'add', dir, branch]);\n logger.info('Worktree added (existing branch)', { dir, branch });\n }\n\n async worktreeAddTracking(dir: string, remoteBranch: string): Promise<void> {\n await this.exec(['worktree', 'add', '--track', '-b', remoteBranch, dir, `origin/${remoteBranch}`]);\n logger.info('Worktree added (tracking remote)', { dir, remoteBranch });\n }\n\n async worktreeRemove(dir: string, force = false): Promise<void> {\n const args = ['worktree', 'remove', dir];\n if (force) args.push('--force');\n await this.exec(args);\n logger.info('Worktree removed', { dir, force });\n }\n\n async worktreePrune(): Promise<void> {\n await this.exec(['worktree', 'prune']);\n logger.info('Worktree pruned stale entries');\n }\n\n async worktreeList(): Promise<string[]> {\n const output = await this.exec(['worktree', 'list', '--porcelain']);\n return output\n .split('\\n')\n .filter((line) => line.startsWith('worktree '))\n .map((line) => line.replace('worktree ', ''));\n }\n\n async showFile(ref: string, filePath: string): Promise<string | null> {\n try {\n return await this.exec(['show', `${ref}:${filePath}`]);\n } catch {\n return null;\n }\n }\n\n async getConflictFiles(): Promise<string[]> {\n const output = await this.exec(['diff', '--name-only', '--diff-filter=U']);\n if (!output) return [];\n return output.split('\\n').filter(Boolean);\n }\n\n async rebase(targetRef: string): Promise<{ success: boolean; conflictFiles: string[] }> {\n try {\n await this.exec(['rebase', targetRef]);\n return { success: true, conflictFiles: [] };\n } catch (err) {\n const msg = (err as Error).message || '';\n if (msg.includes('CONFLICT') || msg.includes('could not apply')) {\n const conflictFiles = await this.getConflictFiles();\n return { success: false, conflictFiles };\n }\n throw err;\n }\n }\n\n async rebaseContinue(): Promise<{ done: boolean; conflictFiles: string[] }> {\n try {\n await this.exec(['-c', 'core.editor=true', 'rebase', '--continue']);\n return { done: true, conflictFiles: [] };\n } catch (err) {\n const msg = (err as Error).message || '';\n if (msg.includes('CONFLICT') || msg.includes('could not apply')) {\n const conflictFiles = await this.getConflictFiles();\n return { done: false, conflictFiles };\n }\n throw err;\n }\n }\n\n async rebaseAbort(): Promise<void> {\n await this.exec(['rebase', '--abort']);\n logger.info('Rebase aborted');\n }\n\n async isRebaseInProgress(): Promise<boolean> {\n const status = await this.exec(['status']);\n return status.includes('rebase in progress');\n }\n\n async forcePush(branch: string): Promise<void> {\n await this.exec(['push', '--no-verify', '--force-with-lease', '-u', 'origin', branch]);\n logger.info('Force pushed', { branch });\n }\n\n async mergeFF(branch: string): Promise<void> {\n await this.exec(['merge', '--ff-only', branch]);\n logger.info('Fast-forward merged', { branch });\n }\n\n async merge(branch: string, message: string): Promise<void> {\n await this.exec(['merge', '--no-ff', '--no-verify', '-m', message, branch]);\n logger.info('Merged', { branch, message: message.slice(0, 80) });\n }\n}\n","import { EventEmitter } from 'node:events';\n\nexport type EventType =\n | 'issue:created'\n | 'issue:stateChanged'\n | 'issue:failed'\n | 'issue:deleted'\n | 'issue:resetForRetry'\n | 'issue:restarted'\n | 'issue:retryFromPhase'\n | 'poll:tick'\n | 'heartbeat'\n | 'agent:output'\n | 'pipeline:progress'\n | 'review:requested'\n | 'review:approved'\n | 'review:rejected'\n | 'conflict:started'\n | 'conflict:resolved'\n | 'conflict:failed'\n | 'update:checking'\n | 'update:available'\n | 'update:downloading'\n | 'update:completed'\n | 'update:failed'\n // Braindump events\n | 'braindump:created'\n | 'braindump:split:done'\n | 'braindump:confirmed'\n | 'braindump:task:started'\n | 'braindump:task:completed'\n | 'braindump:task:merging'\n | 'braindump:task:merged'\n | 'braindump:task:failed'\n | 'braindump:completed'\n | 'braindump:failed'\n // Distill events\n | 'distill:diary:created'\n | 'distill:started'\n | 'distill:memory:updated'\n | 'distill:rule:generated'\n | 'distill:completed'\n | 'distill:failed'\n // Verify-fix loop events\n | 'verify:loopStarted'\n | 'verify:iterationComplete'\n | 'verify:loopExhausted'\n // Release events\n | 'release:gateRequested'\n // UAT async events\n | 'uat:gateRequested'\n | 'uat:completed'\n | 'uat:failed'\n // Phase abort/continue/redo events\n | 'issue:paused'\n | 'issue:continued'\n | 'issue:redone'\n // Preview reaper events\n | 'preview:reaped';\n\nexport interface EventPayload {\n type: EventType;\n data: unknown;\n timestamp: string;\n}\n\n/**\n * Typed event bus based on EventEmitter.\n *\n * Exported as a class so consumers can receive an instance via dependency\n * injection instead of relying on the global singleton.\n */\nexport class EventBus extends EventEmitter {\n emit(event: string | symbol, ...args: unknown[]): boolean {\n super.emit('*', event, ...args);\n return super.emit(event, ...args);\n }\n\n emitTyped(type: EventType, data: unknown): void {\n const payload: EventPayload = {\n type,\n data,\n timestamp: new Date().toISOString(),\n };\n this.emit(type, payload);\n }\n}\n\n/**\n * Global singleton — preserved for backward compatibility.\n *\n * New code should prefer receiving an `EventBus` instance via constructor\n * injection. The global singleton can be passed as the default value.\n */\nexport const eventBus = new EventBus();\n","/**\n * Promise-based async mutex for serializing access to shared resources\n * in a single Node.js process (e.g. mainGit operations, tracker file writes).\n */\nexport class AsyncMutex {\n private queue: Array<() => void> = [];\n private locked = false;\n\n async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire();\n try {\n return await fn();\n } finally {\n this.release();\n }\n }\n\n private acquire(): Promise<void> {\n if (!this.locked) {\n this.locked = true;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this.queue.push(resolve);\n });\n }\n\n private release(): void {\n const next = this.queue.shift();\n if (next) {\n next();\n } else {\n this.locked = false;\n }\n }\n\n get isLocked(): boolean {\n return this.locked;\n }\n\n get queueLength(): number {\n return this.queue.length;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger as rootLogger } from '../logger.js';\n\n/**\n * BaseTracker — JSON 文件持久化的泛型 Tracker 基类。\n *\n * 集中公共的 load/save/get/getAll/delete 逻辑。\n * 子类通过构造函数参数注入 collectionKey 和 trackerName。\n */\nexport abstract class BaseTracker<TRecord> {\n protected readonly filePath: string;\n protected data: Record<string, Record<string, TRecord>>;\n\n /** JSON 根字段名(如 'issues' 或 'batches')*/\n protected readonly collectionKey: string;\n /** 临时文件前缀(如 'tracker' 或 'braindump-tracker')*/\n protected readonly trackerName: string;\n\n constructor(dataDir: string, filename: string, collectionKey: string, trackerName: string) {\n this.collectionKey = collectionKey;\n this.trackerName = trackerName;\n this.filePath = path.join(dataDir, filename);\n this.data = this.load();\n }\n\n protected load(): Record<string, Record<string, TRecord>> {\n try {\n if (fs.existsSync(this.filePath)) {\n const raw = fs.readFileSync(this.filePath, 'utf-8');\n return JSON.parse(raw);\n }\n } catch (err) {\n rootLogger.child(this.trackerName).error(\n 'Failed to load tracker data',\n { error: (err as Error).message },\n );\n }\n return { [this.collectionKey]: {} };\n }\n\n protected save(): void {\n const dir = path.dirname(this.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const tmpPath = path.join(dir,\n `.${this.trackerName}-${process.pid}-${Date.now()}.tmp`);\n fs.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), 'utf-8');\n fs.renameSync(tmpPath, this.filePath);\n }\n\n /** 获取记录集合的引用 */\n protected get collection(): Record<string, TRecord> {\n return this.data[this.collectionKey] as Record<string, TRecord>;\n }\n\n protected getByKey(key: string): TRecord | undefined {\n return this.collection[key];\n }\n\n protected getAllRecords(): TRecord[] {\n return Object.values(this.collection);\n }\n\n protected setRecord(key: string, record: TRecord): void {\n this.collection[key] = record;\n }\n\n protected deleteByKey(key: string): boolean {\n if (!this.collection[key]) return false;\n delete this.collection[key];\n this.save();\n return true;\n }\n}\n","import type { IssueRecord } from './IssueState.js';\n\n/**\n * IssueRecord 身份字段辅助函数。\n *\n * 从 demandSpec 读取身份信息。\n */\n\n/** 获取显示 IID(工蜂 Issue IID) */\nexport function getIid(record: IssueRecord): number {\n return Number(record.demandSpec!.sourceRef.displayId);\n}\n\n/** 获取外部 ID(工蜂 Issue ID) */\nexport function getExternalId(record: IssueRecord): number {\n return Number(record.demandSpec!.sourceRef.externalId);\n}\n\n/** 获取标题 */\nexport function getTitle(record: IssueRecord): string {\n return record.demandSpec!.title;\n}\n","import type { IssueRecord } from './IssueState.js';\nimport type { BraindumpTask } from '../braindump/BraindumpState.js';\nimport type { ActionLifecycleManager } from '../lifecycle/ActionLifecycleManager.js';\nimport { getIid, getTitle } from './IssueRecordHelper.js';\n\n/**\n * UnifiedTaskStatus — 所有任务类型共享的通用状态枚举。\n *\n * 与 IssueState/BatchStatus/TaskStatus 共存,不替代它们。\n * 用于跨任务类型的通用逻辑(如统一 dashboard、统一恢复)。\n */\nexport type UnifiedTaskStatus =\n | 'idle' // 尚未开始\n | 'preparing' // 准备中(创建分支、安装依赖等)\n | 'running' // 执行中\n | 'waiting' // 等待外部输入(审核等)\n | 'merging' // 合并中\n | 'completed' // 完成\n | 'failed'; // 失败\n\n/**\n * ExecutableTask — 所有可执行任务的统一接口。\n *\n * IssueRecord 和 BraindumpTask 都可以投影为此接口,\n * 用于跨任务类型的通用操作。\n */\nexport interface ExecutableTask {\n /** 任务类型标识 */\n readonly kind: 'issue' | 'braindump-task';\n /** 唯一标识(issue: string(issueIid), braindump: taskId) */\n readonly taskId: string;\n /** 显示标题 */\n readonly title: string;\n /** 统一状态 */\n readonly status: UnifiedTaskStatus;\n /** 重试次数 */\n readonly attempts: number;\n /** 最后错误 */\n readonly lastError?: string;\n /** 创建时间 */\n readonly createdAt: string;\n /** 最后更新时间 */\n readonly updatedAt: string;\n /** 特性分支名 */\n readonly branchName?: string;\n /** 原始状态值(IssueState 或 TaskStatus) */\n readonly sourceState?: string;\n /** 过滤分类:active/completed/failed/blocked/idle/skipped */\n readonly stateCategory?: string;\n /** 预计算的状态展示标签(由后端投影时通过 ActionLifecycleManager.resolveLabel 生成) */\n readonly displayLabel?: string;\n\n /** 阶段进度快照(由后端投影时预计算)。\n * 各阶段按定义顺序排列,包含真实事件时间戳。\n * 未提供时前端不渲染阶段进度列。 */\n readonly phaseProgress?: {\n name: string;\n label: string;\n status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'gate_waiting';\n startedAt?: string;\n completedAt?: string;\n }[];\n}\n\n/**\n * ActionStatus → UnifiedTaskStatus 映射。\n *\n * 使用 ActionLifecycleManager 的 resolve() 返回的 ActionStatus 做语义映射:\n * - idle/skipped → 'idle'\n * - ready → 'preparing'\n * - running → 'running'\n * - waiting → 'waiting'\n * - done → 'completed'\n * - failed → 'failed'\n */\nexport function issueStateToUnified(actionStatus: string): UnifiedTaskStatus {\n switch (actionStatus) {\n case 'idle':\n case 'skipped':\n return 'idle';\n case 'ready':\n return 'preparing';\n case 'running':\n return 'running';\n case 'waiting':\n return 'waiting';\n case 'done':\n return 'completed';\n case 'failed':\n return 'failed';\n default:\n return 'idle';\n }\n}\n\n/**\n * TaskStatus → UnifiedTaskStatus 映射。\n */\nexport function taskStatusToUnified(status: string): UnifiedTaskStatus {\n switch (status) {\n case 'pending':\n case 'blocked':\n return 'idle';\n case 'running':\n return 'running';\n case 'done':\n return 'preparing'; // done but not yet merged\n case 'merging':\n case 'conflict_resolving':\n return 'merging';\n case 'merged':\n return 'completed';\n case 'failed':\n return 'failed';\n default:\n return 'idle';\n }\n}\n\n/** 从 UnifiedTaskStatus 派生 stateCategory(用于前端过滤) */\nexport function unifiedStatusToCategory(status: UnifiedTaskStatus): string {\n switch (status) {\n case 'running':\n case 'preparing':\n case 'merging':\n return 'active';\n case 'waiting':\n return 'blocked';\n case 'completed':\n return 'completed';\n case 'failed':\n return 'failed';\n case 'idle':\n default:\n return 'idle';\n }\n}\n\n/**\n * 为 Issue 计算 stateCategory(精确版,使用 ActionLifecycleManager)。\n * 比 unifiedStatusToCategory 更精准,能区分 skipped 等状态。\n */\nexport function issueStateCategory(record: IssueRecord, lm: ActionLifecycleManager): string {\n if (lm.isTerminal(record.state)) {\n if (record.state === 'failed') return 'failed';\n if (record.state === 'deployed') return 'deployed';\n if (record.state === 'completed') return 'completed';\n return 'skipped';\n }\n if (lm.isBlocked(record.state)) return 'blocked';\n return 'active';\n}\n\n// ── Adapters ──\n\n/** 将 IssueRecord 投影为 ExecutableTask */\nexport function issueToExecutableTask(\n record: IssueRecord,\n lm: ActionLifecycleManager,\n): ExecutableTask {\n const actionState = lm.resolve(record.state, record.currentPhase);\n\n // 阶段进度快照:优先使用 tracker 中的真实进度,旧记录降级为推导\n const phaseDefs = lm.getPhaseDefs();\n let phaseProgress: ExecutableTask['phaseProgress'];\n if (record.phaseProgress) {\n phaseProgress = phaseDefs.map(p => ({\n name: p.name,\n label: p.label,\n status: record.phaseProgress![p.name]?.status ?? 'pending' as const,\n startedAt: record.phaseProgress![p.name]?.startedAt,\n completedAt: record.phaseProgress![p.name]?.completedAt,\n }));\n } else {\n const phaseStatusMap = lm.derivePhaseStatuses(record.state, record.currentPhase);\n phaseProgress = phaseDefs.map(p => ({\n name: p.name,\n label: p.label,\n status: phaseStatusMap[p.name] ?? 'pending' as const,\n }));\n }\n\n return {\n kind: 'issue',\n taskId: String(getIid(record)),\n title: getTitle(record),\n status: issueStateToUnified(actionState.status),\n attempts: record.attempts,\n lastError: record.lastError,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n branchName: record.branchName,\n sourceState: record.state,\n stateCategory: issueStateCategory(record, lm),\n displayLabel: lm.resolveLabel(record.state, record.currentPhase),\n phaseProgress,\n };\n}\n\n/** 将 BraindumpTask 投影为 ExecutableTask */\nexport function braindumpTaskToExecutableTask(\n task: BraindumpTask,\n batch: { createdAt: string; updatedAt: string },\n): ExecutableTask {\n const status = taskStatusToUnified(task.status);\n return {\n kind: 'braindump-task',\n taskId: task.id,\n title: task.title,\n status,\n attempts: task.attempts,\n lastError: task.lastError,\n createdAt: task.startedAt ?? batch.createdAt,\n updatedAt: task.completedAt ?? task.startedAt ?? batch.updatedAt,\n branchName: task.branchName,\n sourceState: task.status,\n stateCategory: unifiedStatusToCategory(status),\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { t } from '../i18n/index.js';\nimport { getProjectKnowledge } from '../knowledge/index.js';\nimport { KNOWLEDGE_DEFAULTS } from '../knowledge/KnowledgeDefaults.js';\nimport type { ProjectKnowledge } from '../knowledge/ProjectKnowledge.js';\nimport type { DemandSpec } from '../demand/DemandSpec.js';\nimport type { RepoContext } from '../workspace/index.js';\n\nexport interface WorkspaceLayout {\n repos: RepoContext[];\n workspaceRoot: string;\n}\n\nexport interface PromptContext {\n issueTitle: string;\n issueDescription: string;\n issueIid: number;\n supplementText?: string;\n workspace?: WorkspaceLayout;\n}\n\nfunction planDir(iid: number): string {\n return `.claude-plan/issue-${iid}`;\n}\n\n/**\n * Build template variable map from knowledge config (or defaults).\n */\nexport function getKnowledgeForPrompt(): Record<string, string> {\n const k: ProjectKnowledge = getProjectKnowledge() ?? KNOWLEDGE_DEFAULTS;\n\n const codeStyleParts: string[] = [];\n if (k.codeStyle.indentStyle === 'spaces') {\n codeStyleParts.push(`${k.codeStyle.indentSize}空格缩进`);\n } else {\n codeStyleParts.push('Tab缩进');\n }\n codeStyleParts.push(`${k.codeStyle.lineWidth}字符行宽`);\n codeStyleParts.push('命名规范等');\n if (k.codeStyle.additionalRules?.length) {\n codeStyleParts.push(...k.codeStyle.additionalRules);\n }\n\n const knownIssueLines = k.knownIssues.map(issue => `- ${issue.description}${issue.advice ? `,${issue.advice}` : ''}`);\n\n return {\n dependencyCheckPath: k.toolchain.dependencyCheckPath ?? 'node_modules/.bin/eslint',\n installCommand: k.toolchain.installCommand,\n installFallbackCommand: k.toolchain.installFallbackCommand ?? `${k.toolchain.installCommand} --ignore-scripts`,\n lintCommand: k.toolchain.lintCommand ?? 'npm run lint',\n buildCommand: k.toolchain.buildCommand ?? 'npm run build',\n testCommand: k.toolchain.testCommand ?? 'npm test',\n testFilesCommand: k.toolchain.testFilesCommand\n ?? `${k.toolchain.testCommand ?? 'npm test'} -- <涉及变更的测试文件>`,\n knownIssuesSection: knownIssueLines.length > 0\n ? knownIssueLines.join('\\n')\n : '- 无已知预存问题',\n codeStyleDescription: codeStyleParts.join('、'),\n // E2E related\n e2eDir: k.structure.e2eDir ?? 'e2e',\n e2eTool: k.structure.e2eTool ?? 'E2E',\n frontendDir: k.structure.frontendDir ?? 'frontend',\n };\n}\n\n/**\n * 从 DemandSpec 构建 prompt 上下文,统一补充信息的格式化。\n */\nexport function demandToPromptContext(demand: DemandSpec): {\n title: string;\n description: string;\n displayId: string;\n supplementText: string;\n} {\n const parts: string[] = [];\n const s = demand.supplement;\n if (s?.requirements) parts.push(`### 补充需求说明\\n${s.requirements}`);\n if (s?.acceptanceCriteria) parts.push(`### 验收标准\\n${s.acceptanceCriteria}`);\n if (s?.scope) parts.push(`### 变更范围\\n${s.scope}`);\n if (s?.constraints) parts.push(`### 约束条件\\n${s.constraints}`);\n if (s?.references) parts.push(`### 参考链接\\n${s.references}`);\n if (s?.freeText) parts.push(`### 其他补充\\n${s.freeText}`);\n return {\n title: demand.title,\n description: demand.description,\n displayId: demand.sourceRef.displayId ?? demand.demandId,\n supplementText: parts.length ? `## 补充信息\\n\\n${parts.join('\\n\\n')}` : '',\n };\n}\n\n/**\n * Build a workspace layout section for multi-repo prompts.\n * Returns empty string when workspace has only one repo.\n */\nexport function buildWorkspaceSection(workspace?: WorkspaceLayout): string {\n if (!workspace || workspace.repos.length <= 1) return '';\n\n const lines: string[] = [\n '## 多仓库工作区',\n '',\n '当前工作区包含多个关联仓库,你可以跨仓库读写文件。各仓库相对于工作区根目录的布局如下:',\n '',\n '| 仓库 | 相对路径 | 角色 |',\n '|------|---------|------|',\n ];\n\n for (const repo of workspace.repos) {\n const relPath = repo.name + (repo.workDir !== repo.gitRootDir\n ? ` (项目目录: ${repo.name}/${repo.workDir.slice(repo.gitRootDir.length + 1)})`\n : '');\n const roleText = repo.role || (repo.isPrimary ? '主仓库' : '关联仓库');\n lines.push(`| ${repo.name} | \\`${relPath}\\` | ${roleText} |`);\n }\n\n lines.push('');\n lines.push('修改任一仓库的代码时,使用对应仓库的相对路径即可。请先阅读各仓库根目录下的 CLAUDE.md(如有)了解项目约定。');\n\n return lines.join('\\n');\n}\n\nexport function analysisPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const pd = planDir(ctx.issueIid);\n return t('prompt.analysis', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n planDir: pd,\n });\n}\n\nexport function designPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const pd = planDir(ctx.issueIid);\n return t('prompt.design', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n supplement: supplementSection,\n planDir: pd,\n });\n}\n\nexport function implementPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n return t('prompt.implement', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n}\n\nexport function verifyPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n return t('prompt.verify', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n}\n\nexport function planModeVerifyPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const base = t('prompt.planModeVerify', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport function planPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const pd = planDir(ctx.issueIid);\n const base = t('prompt.plan', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n planDir: pd,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport function buildPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const base = t('prompt.build', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport interface ReviewRoundForPrompt {\n round: number;\n feedback: string;\n timestamp: string;\n}\n\nexport function rePlanPrompt(ctx: PromptContext, history: ReviewRoundForPrompt[]): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const pd = planDir(ctx.issueIid);\n const feedbackLines = history.map(\n r => t('prompt.rePlanRound', { round: r.round, timestamp: r.timestamp, feedback: r.feedback })\n ).join('\\n\\n');\n const base = t('prompt.rePlan', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n historyCount: history.length,\n feedbackLines,\n planDir: pd,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport interface E2ePromptPorts {\n backendPort: number;\n frontendPort: number;\n host: string;\n}\n\nexport interface UatToolConfig {\n vendorDir: string;\n configFile: string;\n}\n\n/**\n * Load all SKILL.md files from the vendor .cursor/skills/ directory.\n * Scans every sub-directory for SKILL.md, concatenates them in sorted order.\n * Falls back to a top-level SKILL.md if .cursor/skills/ doesn't exist.\n */\nfunction loadUatSkill(vendorDir: string): string | null {\n const skillsDir = path.join(vendorDir, '.cursor/skills');\n\n if (fs.existsSync(skillsDir)) {\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .sort((a, b) => a.name.localeCompare(b.name));\n\n const parts: string[] = [];\n for (const entry of entries) {\n const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');\n if (fs.existsSync(skillFile)) {\n try {\n parts.push(fs.readFileSync(skillFile, 'utf-8'));\n } catch { /* skip unreadable */ }\n }\n }\n if (parts.length > 0) return parts.join('\\n\\n---\\n\\n');\n } catch { /* fall through */ }\n }\n\n const fallback = path.join(vendorDir, 'SKILL.md');\n if (fs.existsSync(fallback)) {\n try { return fs.readFileSync(fallback, 'utf-8'); } catch { /* skip */ }\n }\n return null;\n}\n\nexport function e2eVerifyPromptSuffix(\n ctx: PromptContext,\n ports?: E2ePromptPorts,\n uatTool?: UatToolConfig,\n): string {\n if (uatTool?.vendorDir) {\n return buildOaUatPrompt(ctx, uatTool, ports);\n }\n return buildGenericE2ePrompt(ctx, ports);\n}\n\n/**\n * Load the \"e2e_prompt\" field from the oa_pc_uat config.json.\n * Returns the custom prompt string or null if not found / not set.\n */\nfunction loadUatConfigPrompt(configFile: string): string | null {\n if (!configFile || !fs.existsSync(configFile)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(configFile, 'utf-8'));\n if (typeof raw.e2e_prompt === 'string' && raw.e2e_prompt.trim()) {\n return raw.e2e_prompt.trim();\n }\n } catch { /* ignore parse errors */ }\n return null;\n}\n\nfunction buildOaUatPrompt(\n ctx: PromptContext,\n uatTool: UatToolConfig,\n ports?: E2ePromptPorts,\n): string {\n const vendorDir = uatTool.vendorDir;\n const configFile = uatTool.configFile\n || (fs.existsSync(path.join(vendorDir, 'config.json'))\n ? path.join(vendorDir, 'config.json')\n : '');\n\n const skillContent = loadUatSkill(vendorDir);\n const skillSection = skillContent\n ? `\\n\\n<uat-skills>\\n${skillContent}\\n</uat-skills>`\n : '';\n\n const configArg = configFile ? ` --config ${configFile}` : '';\n const outputDirArg = ` --output-dir ${vendorDir}/outputs/issue-${ctx.issueIid}`;\n const cfgLabel = configFile || 'config.json';\n const portHint = ports\n ? `\n\n> **⚠️ 端口覆盖(必须遵守)**\n> 系统已自动在动态端口启动前后端 Preview 服务,无需手动启动:\n> - 后端: http://${ports.host}:${ports.backendPort}\n> - 前端: https://${ports.host}:${ports.frontendPort}\n>\n> **在执行任何 E2E 脚本之前**,你必须先更新 \\`${cfgLabel}\\` 中的地址配置,\n> 将前端地址(base_url 等)改为 \\`https://${ports.host}:${ports.frontendPort}\\`,\n> 后端地址(api_url 等)改为 \\`http://${ports.host}:${ports.backendPort}\\`。\n> config.json 中原有的端口是开发环境默认值,不适用于当前 Preview 环境。`\n : `\n\n> **注意**:Preview 环境未启动。在执行 E2E 测试前,请确认前后端服务已运行。\n> 如果 \\`${cfgLabel}\\` 中配置的服务端口没有响应,请先手动启动前后端服务再执行脚本。`;\n\n const customPrompt = loadUatConfigPrompt(configFile);\n\n const cmdOverride = `\n\n> **⚠️ 命令参数覆盖**:项目指引中出现的所有 \\`python3 .../main.py\\` 命令,都必须替换为以下标准格式:\n> - 执行脚本:\\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\\`\n> - REPL 排障:\\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless\\`\n`;\n\n const instructionSection = customPrompt\n ? `\\n\\n### 项目 E2E 指引\\n\\n${customPrompt}\\n${cmdOverride}`\n : '';\n\n const defaultSteps = `\n### 执行要点\n\n1. 影响面分析:基于 \\`git diff\\` 分析本次变更影响的功能点\n2. 增量剧本生成:扫描 \\`uat-scripts/\\` 目录,仅为未覆盖场景生成新剧本\n3. 执行验证脚本:\n \\`\\`\\`bash\n python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\n \\`\\`\\`\n4. 失败时使用 REPL 排障:\n \\`\\`\\`bash\n python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless\n \\`\\`\\`\n5. 输出结构化验证总览(包含截图路径、执行结果、耗时)`;\n\n return `\n\n## E2E UI 验证(已启用 - OA Web UAT)\n\n本次变更已开启 E2E UI 自动验收,使用 oa_pc_uat 增量验证流水线。${portHint}\n\n**工具位置**: \\`${vendorDir}\\`\n${configFile ? `**配置文件**: \\`${configFile}\\`` : ''}\n**产物输出目录**: \\`${vendorDir}/outputs/issue-${ctx.issueIid}\\`(所有 --script 和 -i 命令必须携带 \\`${outputDirArg.trim()}\\` 参数)\n\n请严格按照以下 oa-web-uat skill 的流程执行端对端验证:\n${skillSection}\n${instructionSection}\n${customPrompt ? '' : defaultSteps}\n\n### ⛔ 阶段 Gate 约束(必须严格遵守)\n\nE2E 验证必须**严格按顺序**经过三个阶段,**禁止跳过或合并**:\n\n**Gate 1 — 影响面分析完成检查点**\n- 必须先通过 \\`git diff\\` 分析出具体的功能点清单\n- 产出物:功能点清单(列出每个受影响的功能名称、影响模块、需验证路径)\n- ❌ 如果没有产出功能点清单,禁止进入阶段 2\n\n**Gate 2 — 剧本生成完成检查点**\n- 必须先扫描 \\`uat-scripts/\\` 已有脚本,判断增量需求\n- 对于需要新增的场景,必须**严格按顺序**完成以下三步,**不允许跳过任何一步**:\n 1. **编写 Markdown 用例** → 生成 \\`uc{ID}_{描述}.md\\` 文件\n 2. **执行 scenario-gen** → 生成 \\`result.json\\`(包含主路径/行为验证/规则验证剧本)\n \\`\\`\\`bash\n cd ${vendorDir}/scenario-gen && npx tsx src/cli.ts --from-md <用例.md> -o result.json\n \\`\\`\\`\n 3. **基于 result.json 编写 Python 脚本** → 每个剧本对应一个 \\`.py\\` 脚本\n- ⛔ **禁止跳过剧本生成步骤直接手写脚本**——即使场景简单也必须走完 Markdown 用例 → result.json → 脚本的完整流程\n- 产出物检查(**三者缺一不可**):\n - ✅ \\`.md\\` 用例文件存在\n - ✅ \\`result.json\\` 剧本文件存在(包含 test_scenarios 和 coverage_report)\n - ✅ \\`.py\\` 脚本文件存在\n- ✅ 验证方式:\\`ls -la\\` 确认上述三类文件全部存在\n- ❌ 如果缺少 .md 用例或 result.json 剧本,**即使 .py 脚本存在也不允许通过 Gate 2**\n- 📦 **中间产物归档**(Gate 2 通过后、进入 Gate 3 前执行):\n \\`\\`\\`bash\n mkdir -p ${vendorDir}/outputs/issue-${ctx.issueIid}/scenarios\n # 只复制以下三类文件,不要复制 config*.json 等配置文件\n cp <用例.md> ${vendorDir}/outputs/issue-${ctx.issueIid}/scenarios/ # Markdown 用例文档\n cp <result.json> ${vendorDir}/outputs/issue-${ctx.issueIid}/scenarios/ # scenario-gen 生成的剧本\n cp <生成的脚本.py> ${vendorDir}/outputs/issue-${ctx.issueIid}/scenarios/ # 验证脚本\n \\`\\`\\`\n ⚠️ **只归档上述三类文件**(.md 用例、result.json 剧本、.py 脚本),**不要**将 config.json / config.storytool.json 等配置文件复制到 scenarios 目录。\n\n**Gate 3 — 执行阶段**\n- 只有 Gate 2 通过后才可以执行 \\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\\`\n- 每个脚本单独执行,逐一记录结果\n- 失败时进入 REPL 排障(\\`-i\\` 模式),排查并修复脚本\n- ⚠️ **REPL 排障后必须重跑脚本**:如果使用了 REPL 交互式探索或修改了脚本,**必须退出 REPL 后重新执行 \\`--script\\` 模式运行该脚本**。REPL 过程中产生的截图是调试中间状态,不能作为最终验证结果。只有 \\`--script\\` 模式产出的截图才是有效的验证截图\n- 最终验证标准:所有脚本都以 \\`--script\\` 模式成功执行,产出的截图反映最终正确状态\n\n将所有 E2E 测试结果汇总写入 \\`.claude-plan/issue-${ctx.issueIid}/03-uat-report.md\\`,包含:\n- 测试场景清单\n- 每个场景的执行结果(通过/失败)\n- 截图路径(如有)\n- 失败原因分析(如有失败)`;\n}\n\nfunction buildGenericE2ePrompt(ctx: PromptContext, ports?: E2ePromptPorts): string {\n const kv = getKnowledgeForPrompt();\n const frontendDir = kv.frontendDir;\n const e2eDir = kv.e2eDir;\n const e2eTool = kv.e2eTool;\n\n const serverSection = ports\n ? `\n**Preview 环境已启动(由系统管理,无需手动启动):**\n- 后端: http://${ports.host}:${ports.backendPort}\n- 前端: https://${ports.host}:${ports.frontendPort}\n\n执行 E2E 测试时请使用以下环境变量来连接已启动的服务:\n\\`\\`\\`bash\nE2E_PORT=${ports.frontendPort} E2E_HOST=${ports.host} E2E_BASE_URL=https://${ports.host}:${ports.frontendPort} \\\\\n cd ${frontendDir} && npx ${e2eTool.toLowerCase()} test\n\\`\\`\\`\n\n**注意**: 不要使用 pnpm test:e2e(它会尝试自行启动 webServer),直接用 npx ${e2eTool.toLowerCase()} test 即可复用已启动的前端。`\n : `\n执行 E2E 测试:\n\\`\\`\\`bash\ncd ${frontendDir} && pnpm test:e2e\n\\`\\`\\``;\n\n return `\n\n## E2E UI 验证(已启用)\n\n本次变更已开启 E2E UI 自动验收,请额外执行以下步骤:\n\n6. 如果本次变更涉及前端页面(${frontendDir}/ 目录有改动),请执行 UI E2E 验证:\n a. 在 ${e2eDir}/ 目录下编写针对本次变更的 ${e2eTool} 测试\n b. ${serverSection.trim()}\n c. 如果测试失败,分析失败原因并尝试修复\n7. 将 E2E 测试结果写入 \\`.claude-plan/issue-${ctx.issueIid}/03-uat-report.md\\`,包括:\n - 冒烟测试通过数 / 总数\n - 专项测试结果列表\n - 截图路径(如有)\n - 失败原因分析(如有失败)`;\n}\n\nexport interface ConflictResolveContext {\n issueIid: number;\n branchName: string;\n baseBranch: string;\n conflictFiles: string[];\n}\n\nexport function conflictResolvePrompt(ctx: ConflictResolveContext): string {\n const conflictFilesList = ctx.conflictFiles.map(f => `- \\`${f}\\``).join('\\n');\n return t('prompt.conflictResolve', {\n iid: ctx.issueIid,\n branch: ctx.branchName,\n baseBranch: ctx.baseBranch,\n conflictFilesList,\n });\n}\n\nexport function issueProgressComment(phase: string, status: string, detail?: string): string {\n const emoji: Record<string, string> = {\n analysis: '🔍', design: '📐', implement: '💻', verify: '✅',\n plan: '📋', review: '👀', build: '🔨', uat: '🧪',\n };\n const icon = emoji[phase] || '📋';\n const statusKey = status === 'completed' ? 'progress.completed' : status === 'failed' ? 'progress.failed' : 'progress.inProgress';\n const statusText = t(statusKey);\n let msg = t('progress.comment', { icon, phase, status: statusText });\n if (detail) {\n msg += `\\n\\n${detail}`;\n }\n return msg;\n}\n","import { GitOperations } from './GitOperations.js';\nimport type { AIRunner } from '../ai-runner/index.js';\nimport { conflictResolvePrompt } from '../prompts/templates.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('ConflictResolver');\n\nexport interface ConflictResolveOptions {\n wtGit: GitOperations;\n targetRef: string;\n workDir: string;\n branchName: string;\n /** Identifier for logging and events (e.g. issueIid or taskId) */\n contextId: string | number;\n maxAttempts?: number;\n phaseTimeoutMs: number;\n onEvent?: (event: unknown) => void;\n}\n\n/**\n * Shared conflict resolution logic: rebase onto target, resolve conflicts with AI.\n * Extracted from PipelineOrchestrator.resolveConflict() for reuse by BraindumpOrchestrator.\n */\nexport class ConflictResolver {\n constructor(private aiRunner: AIRunner) {}\n\n /**\n * Rebase the current branch onto `targetRef` and resolve any conflicts using AI.\n * After successful resolution, the caller is responsible for force-pushing.\n *\n * @throws if conflicts cannot be resolved within maxAttempts\n */\n async resolve(opts: ConflictResolveOptions): Promise<void> {\n const { wtGit, targetRef, workDir, branchName, contextId, phaseTimeoutMs, onEvent } = opts;\n const maxAttempts = opts.maxAttempts ?? 20;\n\n // Abort residual rebase if any\n if (await wtGit.isRebaseInProgress()) {\n logger.warn('Found residual rebase in progress, aborting', { contextId });\n await wtGit.rebaseAbort();\n }\n\n // Attempt rebase\n const rebaseResult = await wtGit.rebase(targetRef);\n\n if (rebaseResult.success) {\n logger.info('Rebase succeeded without conflicts', { contextId });\n return;\n }\n\n // Has conflicts — resolve with AI\n let conflictFiles = rebaseResult.conflictFiles;\n let attempt = 0;\n\n while (conflictFiles.length > 0 && attempt < maxAttempts) {\n attempt++;\n logger.info('Resolving conflicts with AI', { contextId, attempt, conflictFiles });\n\n const prompt = conflictResolvePrompt({\n issueIid: typeof contextId === 'number' ? contextId : 0,\n branchName,\n baseBranch: targetRef.replace(/^origin\\//, ''),\n conflictFiles,\n });\n\n await this.aiRunner.run({\n prompt,\n workDir,\n timeoutMs: phaseTimeoutMs,\n onStreamEvent: onEvent ? (event: unknown) => onEvent(event) : undefined,\n });\n\n // Stage resolved files\n await wtGit.add(conflictFiles);\n\n // Continue rebase\n const continueResult = await wtGit.rebaseContinue();\n if (continueResult.done) {\n conflictFiles = [];\n } else {\n conflictFiles = continueResult.conflictFiles;\n }\n }\n\n if (conflictFiles.length > 0) {\n await wtGit.rebaseAbort();\n throw new Error(\n `Failed to resolve all conflicts after ${maxAttempts} attempts. Remaining: ${conflictFiles.join(', ')}`,\n );\n }\n\n logger.info('All conflicts resolved', { contextId, totalAttempts: attempt });\n }\n}\n"],"mappings":"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","names":["logger","fs","path","path","fs","logger"]}
@@ -0,0 +1,211 @@
1
+ // src/errors/BaseError.ts
2
+ var AppError = class extends Error {
3
+ /** Unique error code for programmatic matching (e.g. 'GONGFENG_API_ERROR'). */
4
+ code;
5
+ /** Original cause, if any. */
6
+ cause;
7
+ constructor(code, message, cause) {
8
+ super(message, { cause });
9
+ this.name = this.constructor.name;
10
+ this.code = code;
11
+ this.cause = cause;
12
+ }
13
+ toJSON() {
14
+ return {
15
+ name: this.name,
16
+ code: this.code,
17
+ message: this.message,
18
+ ...this.cause ? { cause: this.cause.message } : {}
19
+ };
20
+ }
21
+ };
22
+
23
+ // src/errors/ApiError.ts
24
+ var GongfengApiError = class extends AppError {
25
+ statusCode;
26
+ responseBody;
27
+ constructor(statusCode, message, responseBody) {
28
+ super("GONGFENG_API_ERROR", message);
29
+ this.statusCode = statusCode;
30
+ this.responseBody = responseBody;
31
+ }
32
+ /** Whether this error is potentially retryable (5xx or network). */
33
+ get isRetryable() {
34
+ return this.statusCode >= 500 || this.statusCode === 0;
35
+ }
36
+ };
37
+ var GongfengUploadError = class extends GongfengApiError {
38
+ constructor(statusCode, message, responseBody) {
39
+ super(statusCode, message, responseBody);
40
+ this.name = "GongfengUploadError";
41
+ Object.defineProperty(this, "code", { value: "GONGFENG_UPLOAD_ERROR" });
42
+ }
43
+ };
44
+
45
+ // src/errors/AIExecutionError.ts
46
+ var AIExecutionError = class extends AppError {
47
+ phase;
48
+ output;
49
+ exitCode;
50
+ isRetryable;
51
+ constructor(phase, message, opts) {
52
+ super("AI_EXECUTION_ERROR", message, opts?.cause);
53
+ this.phase = phase;
54
+ this.output = opts?.output;
55
+ this.exitCode = opts?.exitCode;
56
+ this.isRetryable = opts?.isRetryable ?? true;
57
+ }
58
+ };
59
+
60
+ // src/errors/NotFoundError.ts
61
+ var IssueNotFoundError = class extends AppError {
62
+ issueIid;
63
+ constructor(issueIid) {
64
+ super("ISSUE_NOT_FOUND", `Issue ${issueIid} not found in tracker`);
65
+ this.issueIid = issueIid;
66
+ }
67
+ };
68
+ var BatchNotFoundError = class extends AppError {
69
+ batchId;
70
+ constructor(batchId) {
71
+ super("BATCH_NOT_FOUND", `Braindump batch not found: ${batchId}`);
72
+ this.batchId = batchId;
73
+ }
74
+ };
75
+ var SessionNotFoundError = class extends AppError {
76
+ sessionId;
77
+ constructor(sessionId) {
78
+ super("SESSION_NOT_FOUND", `Chat session not found: ${sessionId}`);
79
+ this.sessionId = sessionId;
80
+ }
81
+ };
82
+ var TaskNotFoundError = class extends AppError {
83
+ taskId;
84
+ constructor(taskId) {
85
+ super("TASK_NOT_FOUND", `Braindump task not found: ${taskId}`);
86
+ this.taskId = taskId;
87
+ }
88
+ };
89
+
90
+ // src/errors/InvalidOperationError.ts
91
+ var InvalidPhaseError = class extends AppError {
92
+ phase;
93
+ constructor(phase, message) {
94
+ super("INVALID_PHASE", message ?? `Invalid phase for retry: ${phase}`);
95
+ this.phase = phase;
96
+ }
97
+ };
98
+ var InvalidStateError = class extends AppError {
99
+ state;
100
+ constructor(state, message) {
101
+ super("INVALID_STATE", message ?? `Invalid state: ${state}`);
102
+ this.state = state;
103
+ }
104
+ };
105
+ var PortExhaustionError = class extends AppError {
106
+ constructor(message) {
107
+ super("PORT_EXHAUSTION", message ?? "No available ports in the configured range");
108
+ }
109
+ };
110
+ var SessionLimitError = class extends AppError {
111
+ limit;
112
+ constructor(limit) {
113
+ super("SESSION_LIMIT", `Session message limit reached (${limit})`);
114
+ this.limit = limit;
115
+ }
116
+ };
117
+
118
+ // src/errors/ShutdownError.ts
119
+ var ServiceShutdownError = class extends AppError {
120
+ constructor() {
121
+ super("SERVICE_SHUTDOWN", "Service shutting down");
122
+ }
123
+ };
124
+
125
+ // src/errors/PhaseAbortedError.ts
126
+ var PhaseAbortedError = class extends AppError {
127
+ phaseName;
128
+ action;
129
+ constructor(phaseName, action) {
130
+ super("PHASE_ABORTED", `Phase '${phaseName}' interrupted by user action '${action}'`);
131
+ this.phaseName = phaseName;
132
+ this.action = action;
133
+ }
134
+ };
135
+
136
+ // src/errors/ParseError.ts
137
+ var AIOutputParseError = class extends AppError {
138
+ rawOutput;
139
+ constructor(message, rawOutput) {
140
+ super("AI_OUTPUT_PARSE_ERROR", message);
141
+ this.rawOutput = rawOutput;
142
+ }
143
+ };
144
+
145
+ // src/errors/RegistryError.ts
146
+ var PhaseNotRegisteredError = class extends AppError {
147
+ phaseName;
148
+ registeredPhases;
149
+ constructor(phaseName, registeredPhases) {
150
+ super(
151
+ "PHASE_NOT_REGISTERED",
152
+ `Unknown phase: ${phaseName}. Registered phases: ${registeredPhases.join(", ")}`
153
+ );
154
+ this.phaseName = phaseName;
155
+ this.registeredPhases = registeredPhases;
156
+ }
157
+ };
158
+ var RunnerNotRegisteredError = class extends AppError {
159
+ mode;
160
+ registeredModes;
161
+ constructor(mode, registeredModes) {
162
+ super(
163
+ "RUNNER_NOT_REGISTERED",
164
+ `Unknown AI runner mode: ${mode}. Registered modes: ${registeredModes.join(", ")}`
165
+ );
166
+ this.mode = mode;
167
+ this.registeredModes = registeredModes;
168
+ }
169
+ };
170
+ var PipelineNotFoundError = class extends AppError {
171
+ pipelineMode;
172
+ constructor(pipelineMode) {
173
+ super("PIPELINE_NOT_FOUND", `Unknown pipeline mode: ${pipelineMode}`);
174
+ this.pipelineMode = pipelineMode;
175
+ }
176
+ };
177
+ var UnregisteredPhasesError = class extends AppError {
178
+ missingPhases;
179
+ registeredPhases;
180
+ constructor(missingPhases, registeredPhases) {
181
+ super(
182
+ "UNREGISTERED_PHASES",
183
+ `Pipeline defines unregistered phases: ${missingPhases.join(", ")}. Registered: ${registeredPhases.join(", ")}`
184
+ );
185
+ this.missingPhases = missingPhases;
186
+ this.registeredPhases = registeredPhases;
187
+ }
188
+ };
189
+
190
+ export {
191
+ AppError,
192
+ GongfengApiError,
193
+ GongfengUploadError,
194
+ AIExecutionError,
195
+ IssueNotFoundError,
196
+ BatchNotFoundError,
197
+ SessionNotFoundError,
198
+ TaskNotFoundError,
199
+ InvalidPhaseError,
200
+ InvalidStateError,
201
+ PortExhaustionError,
202
+ SessionLimitError,
203
+ ServiceShutdownError,
204
+ PhaseAbortedError,
205
+ AIOutputParseError,
206
+ PhaseNotRegisteredError,
207
+ RunnerNotRegisteredError,
208
+ PipelineNotFoundError,
209
+ UnregisteredPhasesError
210
+ };
211
+ //# sourceMappingURL=chunk-AVGZH64A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/BaseError.ts","../src/errors/ApiError.ts","../src/errors/AIExecutionError.ts","../src/errors/NotFoundError.ts","../src/errors/InvalidOperationError.ts","../src/errors/ShutdownError.ts","../src/errors/PhaseAbortedError.ts","../src/errors/ParseError.ts","../src/errors/RegistryError.ts"],"sourcesContent":["/**\n * Base application error with structured error code and cause chain.\n */\nexport class AppError extends Error {\n /** Unique error code for programmatic matching (e.g. 'GONGFENG_API_ERROR'). */\n public readonly code: string;\n /** Original cause, if any. */\n public override readonly cause?: Error;\n\n constructor(code: string, message: string, cause?: Error) {\n super(message, { cause });\n this.name = this.constructor.name;\n this.code = code;\n this.cause = cause;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n ...(this.cause ? { cause: this.cause.message } : {}),\n };\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class GongfengApiError extends AppError {\n public readonly statusCode: number;\n public readonly responseBody?: string;\n\n constructor(statusCode: number, message: string, responseBody?: string) {\n super('GONGFENG_API_ERROR', message);\n this.statusCode = statusCode;\n this.responseBody = responseBody;\n }\n\n /** Whether this error is potentially retryable (5xx or network). */\n get isRetryable(): boolean {\n return this.statusCode >= 500 || this.statusCode === 0;\n }\n}\n\nexport class GongfengUploadError extends GongfengApiError {\n constructor(statusCode: number, message: string, responseBody?: string) {\n super(statusCode, message, responseBody);\n this.name = 'GongfengUploadError';\n // Override code\n Object.defineProperty(this, 'code', { value: 'GONGFENG_UPLOAD_ERROR' });\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class AIExecutionError extends AppError {\n public readonly phase: string;\n public readonly output?: string;\n public readonly exitCode?: number | null;\n public readonly isRetryable: boolean;\n\n constructor(phase: string, message: string, opts?: { output?: string; exitCode?: number | null; cause?: Error; isRetryable?: boolean }) {\n super('AI_EXECUTION_ERROR', message, opts?.cause);\n this.phase = phase;\n this.output = opts?.output;\n this.exitCode = opts?.exitCode;\n this.isRetryable = opts?.isRetryable ?? true;\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class IssueNotFoundError extends AppError {\n public readonly issueIid: number;\n constructor(issueIid: number) {\n super('ISSUE_NOT_FOUND', `Issue ${issueIid} not found in tracker`);\n this.issueIid = issueIid;\n }\n}\n\nexport class BatchNotFoundError extends AppError {\n public readonly batchId: string;\n constructor(batchId: string) {\n super('BATCH_NOT_FOUND', `Braindump batch not found: ${batchId}`);\n this.batchId = batchId;\n }\n}\n\nexport class SessionNotFoundError extends AppError {\n public readonly sessionId: string;\n constructor(sessionId: string) {\n super('SESSION_NOT_FOUND', `Chat session not found: ${sessionId}`);\n this.sessionId = sessionId;\n }\n}\n\nexport class TaskNotFoundError extends AppError {\n public readonly taskId: string;\n constructor(taskId: string) {\n super('TASK_NOT_FOUND', `Braindump task not found: ${taskId}`);\n this.taskId = taskId;\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class InvalidPhaseError extends AppError {\n public readonly phase: string;\n constructor(phase: string, message?: string) {\n super('INVALID_PHASE', message ?? `Invalid phase for retry: ${phase}`);\n this.phase = phase;\n }\n}\n\nexport class InvalidStateError extends AppError {\n public readonly state: string;\n constructor(state: string, message?: string) {\n super('INVALID_STATE', message ?? `Invalid state: ${state}`);\n this.state = state;\n }\n}\n\nexport class PortExhaustionError extends AppError {\n constructor(message?: string) {\n super('PORT_EXHAUSTION', message ?? 'No available ports in the configured range');\n }\n}\n\nexport class SessionLimitError extends AppError {\n public readonly limit: number;\n constructor(limit: number) {\n super('SESSION_LIMIT', `Session message limit reached (${limit})`);\n this.limit = limit;\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class ServiceShutdownError extends AppError {\n constructor() {\n super('SERVICE_SHUTDOWN', 'Service shutting down');\n }\n}\n","import { AppError } from './BaseError.js';\n\n/**\n * 用户主动中止/重做/重启阶段时抛出,中断 PhaseLoopStep 循环。\n *\n * 在 PipelineOrchestrator 的 catch 块中被特殊处理,\n * 不会进入 handleFailure 路径。\n */\nexport class PhaseAbortedError extends AppError {\n public readonly phaseName: string;\n public readonly action: 'abort' | 'redo' | 'restart';\n\n constructor(phaseName: string, action: 'abort' | 'redo' | 'restart') {\n super('PHASE_ABORTED', `Phase '${phaseName}' interrupted by user action '${action}'`);\n this.phaseName = phaseName;\n this.action = action;\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class AIOutputParseError extends AppError {\n public readonly rawOutput?: string;\n constructor(message: string, rawOutput?: string) {\n super('AI_OUTPUT_PARSE_ERROR', message);\n this.rawOutput = rawOutput;\n }\n}\n","import { AppError } from './BaseError.js';\n\nexport class PhaseNotRegisteredError extends AppError {\n public readonly phaseName: string;\n public readonly registeredPhases: string[];\n constructor(phaseName: string, registeredPhases: string[]) {\n super(\n 'PHASE_NOT_REGISTERED',\n `Unknown phase: ${phaseName}. Registered phases: ${registeredPhases.join(', ')}`,\n );\n this.phaseName = phaseName;\n this.registeredPhases = registeredPhases;\n }\n}\n\nexport class RunnerNotRegisteredError extends AppError {\n public readonly mode: string;\n public readonly registeredModes: string[];\n constructor(mode: string, registeredModes: string[]) {\n super(\n 'RUNNER_NOT_REGISTERED',\n `Unknown AI runner mode: ${mode}. Registered modes: ${registeredModes.join(', ')}`,\n );\n this.mode = mode;\n this.registeredModes = registeredModes;\n }\n}\n\nexport class PipelineNotFoundError extends AppError {\n public readonly pipelineMode: string;\n constructor(pipelineMode: string) {\n super('PIPELINE_NOT_FOUND', `Unknown pipeline mode: ${pipelineMode}`);\n this.pipelineMode = pipelineMode;\n }\n}\n\nexport class UnregisteredPhasesError extends AppError {\n public readonly missingPhases: string[];\n public readonly registeredPhases: string[];\n constructor(missingPhases: string[], registeredPhases: string[]) {\n super(\n 'UNREGISTERED_PHASES',\n `Pipeline defines unregistered phases: ${missingPhases.join(', ')}. Registered: ${registeredPhases.join(', ')}`,\n );\n this.missingPhases = missingPhases;\n this.registeredPhases = registeredPhases;\n }\n}\n"],"mappings":"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","names":[]}