svharness 0.15.11 → 0.15.13

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 (43) hide show
  1. package/README.md +106 -2
  2. package/auto/auto-harness-detail-design.md +136 -4
  3. package/auto/skills/svharness/SKILL.md +44 -1
  4. package/auto/templates/auto-pack-req.config.example.yaml +29 -0
  5. package/auto/templates/auto.config.example.yaml +1 -1
  6. package/auto/templates/phase-prompts/S40-adversarial-review.md +31 -0
  7. package/auto/templates/phase-prompts/S40-review.md +22 -0
  8. package/auto/templates/phase-prompts/S40-sample-trace.md +26 -0
  9. package/auto/templates/requirements-review-rubric.yaml +79 -0
  10. package/dist/commands/auto-pack-req.js +70 -0
  11. package/dist/commands/auto.js +16 -1
  12. package/dist/commands/convert.js +2 -0
  13. package/dist/commands/shell-integration.js +29 -56
  14. package/dist/config/merge-options.js +2 -0
  15. package/dist/config/normalize.js +7 -0
  16. package/dist/constants/convert.js +5 -0
  17. package/dist/core/markitdown-client.js +7 -2
  18. package/dist/index.js +77 -5
  19. package/dist/lib/acp-agent-cli-resolver.js +193 -0
  20. package/dist/lib/acp-client.js +4 -2
  21. package/dist/lib/agent-launcher.js +9 -4
  22. package/dist/lib/auto-assets.js +1 -0
  23. package/dist/lib/auto-optimize.js +23 -3
  24. package/dist/lib/auto-orchestrator.js +53 -20
  25. package/dist/lib/auto-pack-req-context.js +88 -0
  26. package/dist/lib/auto-pack-req-finalize.js +28 -0
  27. package/dist/lib/auto-req-pack-review.js +161 -0
  28. package/dist/lib/auto-sample-trace.js +144 -0
  29. package/dist/lib/auto-state.js +124 -15
  30. package/dist/lib/gate-checker.js +20 -10
  31. package/dist/lib/phase-runner.js +13 -2
  32. package/dist/lib/ps-codechat-alias.js +29 -16
  33. package/dist/lib/review-parser.js +14 -0
  34. package/dist/lib/score-aggregator.js +21 -0
  35. package/dist/lib/win-registry.js +1 -1
  36. package/dist/utils/validate-args.js +2 -1
  37. package/package.json +3 -2
  38. package/templates/codechat/Start-CodeChat.ps1 +176 -135
  39. package/templates/codechat/bash.exe.stackdump +29 -0
  40. package/templates/codechat/docs/project-structure.md +59 -0
  41. package/templates/svharness.config.example.yaml +2 -0
  42. package/dist/lib/launch-script-path.js +0 -24
  43. package/templates/codechat/.claude/.env +0 -59
@@ -1,18 +1,17 @@
1
- # Standalone CodeChat launcher (no Node / svharness required).
2
- # UTF-8 BOM required for PS 5.1 when menu labels contain non-ASCII.
3
- # Compatible with both `irm | iex` and `-File` invocation (no param() block).
1
+ # Standalone CodeChat launcher.
2
+ # UTF-8 BOM is added by Install-CodeChatContextMenu for PS 5.1 -File mode.
3
+ # Compatible with both irm|iex and -File invocation.
4
4
  # Suppress Set-ExecutionPolicy errors when Group Policy overrides (common on enterprise machines).
5
5
  try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force -ErrorAction Stop | Out-Null } catch {}
6
6
  # Ensure console output uses UTF-8 so Chinese labels print correctly
7
7
  try { chcp 65001 | Out-Null } catch {}
8
8
  [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
9
9
  $OutputEncoding = [System.Text.UTF8Encoding]::new($false)
10
- $Action='Install'; $WorkDir=(Get-Location).Path; $NoSyncEnv=$NoSkipPermissions=$false; $ExtraArgs=@()
10
+ $Action='Install'; $WorkDir=(Get-Location).Path; $NoSkipPermissions=$false; $ExtraArgs=@()
11
11
  for($__i=0;$__i -lt $args.Count;$__i++){
12
12
  switch -Regex ($args[$__i]){
13
13
  '^-Action$' { $Action=$args[++$__i] }
14
14
  '^-WorkDir$' { $WorkDir=$args[++$__i] }
15
- '^-NoSyncEnv$' { $NoSyncEnv=$true }
16
15
  '^-NoSkipPermissions$' { $NoSkipPermissions=$true }
17
16
  default { $ExtraArgs += $args[$__i] }
18
17
  }
@@ -21,14 +20,12 @@ Remove-Variable __i
21
20
 
22
21
  # The URL this script was downloaded from (used when re-downloading in iex mode)
23
22
  $script:DownloadUrl = 'https://v-hub.desaysz.site/Start-CodeChat.ps1'
23
+ $script:IconDownloadUrl = 'https://v-hub.desaysz.site/codechat-agent.ico'
24
24
 
25
25
  $ErrorActionPreference = 'Stop'
26
26
 
27
- $script:MenuLabel = if ([System.Globalization.CultureInfo]::CurrentUICulture.Name -like 'zh-*') {
28
- '在此启动 CodeChat CLI'
29
- } else {
30
- 'Launch CodeChat CLI here'
31
- }
27
+ # ASCII-only label avoids mojibake in Explorer context menu (registry encoding).
28
+ $script:MenuLabel = 'Launch CodeChat CLI here'
32
29
  $script:MenuKeyBg = 'HKCU:\Software\Classes\Directory\Background\shell\CodeChatStandaloneAgent'
33
30
  $script:MenuKeyDir = 'HKCU:\Software\Classes\Directory\shell\CodeChatStandaloneAgent'
34
31
  $script:SvharnessMenuKeyBg = 'HKCU:\Software\Classes\Directory\Background\shell\SvharnessLaunchCodeChatAgent'
@@ -176,127 +173,138 @@ function Find-CodeChatRunner {
176
173
  }
177
174
  }
178
175
 
179
- function Parse-EnvFile {
180
- param([string]$Path)
181
- $result = @{}
182
- if (-not (Test-Path -LiteralPath $Path)) { return $result }
183
- Get-Content -LiteralPath $Path -Encoding UTF8 | ForEach-Object {
184
- $line = $_.Trim()
185
- if ($line -eq '' -or $line.StartsWith('#')) { return }
186
- $eq = $line.IndexOf('=')
187
- if ($eq -le 0) { return }
188
- $key = $line.Substring(0, $eq).Trim()
189
- if (-not $key) { return }
190
- $val = $line.Substring($eq + 1).Trim()
191
- # Strip surrounding quotes
192
- $wasQuoted = $false
193
- if ($val.Length -ge 2) {
194
- $fc = $val[0]; $lc = $val[-1]
195
- if (($fc -eq '"' -and $lc -eq '"') -or ($fc -eq "'" -and $lc -eq "'")) {
196
- $val = $val.Substring(1, $val.Length - 2)
197
- $wasQuoted = $true
198
- }
199
- }
200
- # Strip inline comment (# outside quotes only)
201
- if (-not $wasQuoted) {
202
- $hashIdx = $val.IndexOf('#')
203
- if ($hashIdx -ge 0) {
204
- $val = $val.Substring(0, $hashIdx).TrimEnd()
205
- }
206
- }
207
- $result[$key] = $val
176
+ function Initialize-ConsoleUtf8 {
177
+ try { chcp 65001 | Out-Null } catch {}
178
+ [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
179
+ [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
180
+ $script:OutputEncoding = [System.Text.UTF8Encoding]::new($false)
181
+ }
182
+
183
+ function Get-ExtraArgsFromJsonFragment {
184
+ param([string]$Fragment)
185
+ if ([string]::IsNullOrWhiteSpace($Fragment)) { return @() }
186
+ if ($Fragment -match '^\[\s*(.*)\s*\]$') {
187
+ $inner = $Matches[1]
188
+ if ([string]::IsNullOrWhiteSpace($inner)) { return @() }
189
+ return @([regex]::Matches($inner, '"((?:\\.|[^"\\])*)"') | ForEach-Object { $_.Groups[1].Value })
190
+ }
191
+ if ($Fragment -match '^"\s*([^"]*)\s*"$') {
192
+ return @($Matches[1].Trim() -split '\s+' | Where-Object { $_ })
208
193
  }
209
- return $result
194
+ return @()
210
195
  }
211
196
 
212
- function Merge-EnvToSettingsJson {
197
+ function Format-ExtraArgsJsonArray {
198
+ param([string[]]$ArgValues)
199
+ if (-not $ArgValues -or $ArgValues.Count -eq 0) { return '[]' }
200
+ $escaped = ($ArgValues | ForEach-Object {
201
+ '"{0}"' -f ([string]$_ -replace '\\', '\\' -replace '"', '\"')
202
+ }) -join ', '
203
+ return "[$escaped]"
204
+ }
205
+
206
+ function Update-SectionExtraArgsInConfigJson {
213
207
  param(
214
- [string]$SettingsPath,
215
- [hashtable]$EnvPairs
208
+ [string]$JsonText,
209
+ [string]$SectionName,
210
+ [string[]]$RemoveArgs = @('-c'),
211
+ [string[]]$EnsureArgs = @('--dangerously-skip-permissions'),
212
+ [switch]$SkipEnsure
216
213
  )
217
- # Read or init settings
218
- $settings = @{}
219
- if (Test-Path -LiteralPath $SettingsPath) {
220
- try {
221
- $raw = Get-Content -LiteralPath $SettingsPath -Raw -Encoding UTF8
222
- if ($raw) {
223
- # Convert PSObject to hashtable (PS5.1 compat — no -AsHashtable)
224
- $parsed = $raw | ConvertFrom-Json
225
- $settings = @{}
226
- $parsed.PSObject.Properties | ForEach-Object { $settings[$_.Name] = $_.Value }
227
- }
228
- } catch {
229
- $settings = @{}
214
+ if ([string]::IsNullOrWhiteSpace($JsonText)) { return $JsonText }
215
+
216
+ $pattern = "(`"$SectionName`"\s*:\s*\{[\s\S]*?`"extra_args`"\s*:\s*)(\[[^\]]*\]|`"[^`"]*`"|\{\s*\})"
217
+ $match = [regex]::Match($JsonText, $pattern)
218
+ if (-not $match.Success) { return $JsonText }
219
+
220
+ $current = Get-ExtraArgsFromJsonFragment -Fragment $match.Groups[2].Value
221
+ $next = @($current | Where-Object { $_ -and ($_ -notin $RemoveArgs) })
222
+ if ($SkipEnsure) {
223
+ $next = @($next | Where-Object { $_ -notin $EnsureArgs })
224
+ } else {
225
+ foreach ($arg in $EnsureArgs) {
226
+ if ($arg -and ($arg -notin $next)) { $next += $arg }
230
227
  }
231
228
  }
232
- $settings['env'] = $EnvPairs
233
- $dir = Split-Path -Parent $SettingsPath
234
- if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
235
- $json = $settings | ConvertTo-Json -Depth 10
236
- Write-Utf8NoBom -Path $SettingsPath -Value $json
229
+
230
+ $newFragment = Format-ExtraArgsJsonArray -ArgValues $next
231
+ $currentFragment = $match.Groups[2].Value
232
+ $isArrayFragment = $currentFragment -match '^\[\s*'
233
+ if ($isArrayFragment -and ($currentFragment -eq $newFragment)) { return $JsonText }
234
+
235
+ $headLen = $match.Groups[1].Index + $match.Groups[1].Length
236
+ $tailStart = $match.Groups[2].Index + $match.Groups[2].Length
237
+ return $JsonText.Substring(0, $headLen) + $newFragment + $JsonText.Substring($tailStart)
237
238
  }
238
239
 
239
- function Sync-ProjectEnv {
240
- param(
241
- [string]$ProjectRoot,
242
- [switch]$SkipSync
243
- )
244
- $projectEnv = Join-Path $ProjectRoot '.claude\.env'
245
- $userClaudeEnv = Join-Path $HOME '.claude\.env'
246
- $userCodechatEnv = Join-Path $HOME '.codechat\.env'
247
- $settingsPath = Join-Path $HOME '.claude\settings.json'
248
-
249
- if (-not $SkipSync -and (Test-Path -LiteralPath $projectEnv)) {
250
- # New: Merge project .env content into ~/.claude/settings.json["env"]
251
- $envPairs = Parse-EnvFile -Path $projectEnv
252
- if ($envPairs.Count -gt 0) {
253
- Merge-EnvToSettingsJson -SettingsPath $settingsPath -EnvPairs $envPairs
254
- Write-Host "[info] merged project env -> $settingsPath (${envPairs.Count} vars)"
255
- }
256
- # Backward compat: also sync .env file for --env-file argument
257
- $resolvedProject = (Resolve-Path -LiteralPath $projectEnv).Path
258
- $resolvedUser = if (Test-Path -LiteralPath $userClaudeEnv) { (Resolve-Path -LiteralPath $userClaudeEnv).Path } else { $null }
259
- if ($resolvedProject -ne $resolvedUser) {
260
- $userDir = Split-Path -Parent $userClaudeEnv
261
- if (-not (Test-Path -LiteralPath $userDir)) {
262
- New-Item -ItemType Directory -Force -Path $userDir | Out-Null
240
+ function Repair-CodeChatBootstrapConfigLib {
241
+ # bootstrap Config.ps1 unwraps single-element JSON arrays to strings on load, and
242
+ # ConvertTo-Json may write extra_args as strings — patch once in the local CLI install.
243
+ $configPs1 = Join-Path $env:USERPROFILE '.codechat\cli_app\win\lib\Config.ps1'
244
+ if (-not (Test-Path -LiteralPath $configPs1)) { return }
245
+
246
+ try {
247
+ $content = Get-Content -LiteralPath $configPs1 -Raw -Encoding UTF8
248
+ if (-not $content) { return }
249
+
250
+ $marker = 'Start-CodeChat: preserve single-element arrays'
251
+ if ($content -notmatch [regex]::Escape($marker)) {
252
+ $needle = " return `$list"
253
+ $replacement = " return ,@(`$list) # $marker"
254
+ if ($content.Contains($needle)) {
255
+ $content = $content.Replace($needle, $replacement)
256
+ Write-Utf8NoBom -Path $configPs1 -Value $content
263
257
  }
264
- Copy-Item -LiteralPath $projectEnv -Destination $userClaudeEnv -Force
265
258
  }
266
- return $userClaudeEnv
259
+ } catch {
260
+ # Never block startup for bootstrap patch
267
261
  }
262
+ }
268
263
 
269
- if (Test-Path -LiteralPath $userClaudeEnv) {
270
- return $userClaudeEnv
271
- }
264
+ function Ensure-BootstrapExtraArgs {
265
+ # Surgical edit only: strip legacy "-c" from extra_args; keep all other config fields/args.
266
+ param(
267
+ [switch]$SkipPermissions
268
+ )
272
269
 
273
- # Fallback: ~/.codechat/.env (from old codechat.ps1)
274
- if (Test-Path -LiteralPath $userCodechatEnv) {
275
- return $userCodechatEnv
276
- }
270
+ Repair-CodeChatBootstrapConfigLib
277
271
 
278
- Write-Host "[warn] No .env file found. Create one at $projectEnv or $userClaudeEnv"
279
- return ''
280
- }
272
+ $configPath = Join-Path $env:USERPROFILE '.codechat\bootstrap\config.json'
273
+ $ensureArgs = @('--dangerously-skip-permissions')
281
274
 
282
- function Initialize-ConsoleUtf8 {
283
- try { chcp 65001 | Out-Null } catch {}
284
- [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
285
- [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
286
- $script:OutputEncoding = [System.Text.UTF8Encoding]::new($false)
275
+ if (-not (Test-Path -LiteralPath $configPath)) { return }
276
+
277
+ try {
278
+ $raw = Get-Content -LiteralPath $configPath -Raw -Encoding UTF8
279
+ if (-not $raw) { return }
280
+
281
+ $updated = $raw
282
+ foreach ($sectionName in @('claudecode', 'codechat')) {
283
+ $updated = Update-SectionExtraArgsInConfigJson `
284
+ -JsonText $updated `
285
+ -SectionName $sectionName `
286
+ -RemoveArgs @('-c') `
287
+ -EnsureArgs $ensureArgs `
288
+ -SkipEnsure:$SkipPermissions
289
+ }
290
+
291
+ if ($updated -ne $raw) {
292
+ Write-Utf8NoBom -Path $configPath -Value $updated
293
+ }
294
+ } catch {
295
+ # Silently ignore — never block startup for config tweaks
296
+ }
287
297
  }
288
298
 
289
299
  function Invoke-CodeChatStart {
290
300
  param(
291
301
  [string]$TargetWorkDir,
292
- [switch]$SkipEnvSync,
293
302
  [switch]$SkipPermissions
294
303
  )
295
304
 
296
305
  Initialize-ConsoleUtf8
297
306
 
298
307
  $resolvedWorkDir = (Resolve-Path -LiteralPath $TargetWorkDir).Path
299
- $envFile = Sync-ProjectEnv -ProjectRoot $resolvedWorkDir -SkipSync:$SkipEnvSync
300
308
  $resolved = Find-CodeChatRunner
301
309
 
302
310
  $sourceLog = $resolved.Source
@@ -306,26 +314,25 @@ function Invoke-CodeChatStart {
306
314
 
307
315
  Write-Host "CodeChat CLI: $($resolved.Runner) ($sourceLog)"
308
316
  Write-Host "workdir: $resolvedWorkDir"
309
- if ($envFile) {
310
- Write-Host "env: $envFile"
311
- }
312
317
 
313
- $args = @(
314
- "--cwd=`"$resolvedWorkDir`""
315
- )
316
- if ($envFile) {
317
- $args += "--env-file=`"$envFile`""
318
- }
319
- if (-not $SkipPermissions) {
320
- $args += '--dangerously-skip-permissions'
321
- }
322
- # Append extra args forwarded from alias (e.g., -c for config path)
318
+ # Sync bootstrap extra_args for both codechat + claudecode before launch
319
+ Ensure-BootstrapExtraArgs -SkipPermissions:$SkipPermissions
320
+
321
+ # run.bat → bootstrap.ps1 only accepts -cwd <path> (two args).
322
+ # --cwd="path" is silently dropped when invoked via cmd.exe %*.
323
+ $args = @('-cwd', $resolvedWorkDir)
324
+ # Append extra args forwarded from alias
323
325
  if ($ExtraArgs -and $ExtraArgs.Count -gt 0) {
324
326
  $args += $ExtraArgs
325
327
  }
326
328
 
327
329
  & $resolved.Runner @args
328
- exit $LASTEXITCODE
330
+ $exitCode = $LASTEXITCODE
331
+
332
+ # bootstrap Update-CliPreference may rewrite extra_args; repair after CLI exits.
333
+ Ensure-BootstrapExtraArgs -SkipPermissions:$SkipPermissions
334
+
335
+ exit $exitCode
329
336
  }
330
337
 
331
338
  function Get-WtExecutable {
@@ -345,6 +352,9 @@ function Get-WtExecutable {
345
352
 
346
353
  function Copy-OptionalIcon {
347
354
  param([string]$SourceScriptPath)
355
+ if (Test-Path -LiteralPath $script:InstalledIcon) {
356
+ return $true
357
+ }
348
358
  $candidates = @(
349
359
  (Join-Path (Split-Path -Parent $SourceScriptPath) 'codechat-agent.ico'),
350
360
  (Join-Path (Split-Path -Parent $SourceScriptPath) '..\assets\codechat-agent.ico'),
@@ -353,13 +363,41 @@ function Copy-OptionalIcon {
353
363
  foreach ($icon in $candidates) {
354
364
  try {
355
365
  $resolved = Resolve-Path -LiteralPath $icon -ErrorAction Stop
356
- Copy-Item -LiteralPath $resolved.Path -Destination $script:InstalledIcon -Force
366
+ Copy-Item -LiteralPath $resolved.Path -Destination $script:InstalledIcon -Force -ErrorAction Stop
357
367
  return $true
358
368
  } catch {}
359
369
  }
360
370
  return $false
361
371
  }
362
372
 
373
+ function Download-MenuIcon {
374
+ if (Test-Path -LiteralPath $script:InstalledIcon) {
375
+ return $true
376
+ }
377
+ if (-not $script:IconDownloadUrl) {
378
+ return $false
379
+ }
380
+ try {
381
+ Invoke-WebRequest -Uri $script:IconDownloadUrl -OutFile $script:InstalledIcon -ErrorAction Stop
382
+ return (Test-Path -LiteralPath $script:InstalledIcon)
383
+ } catch {
384
+ try {
385
+ (New-Object System.Net.WebClient).DownloadFile($script:IconDownloadUrl, $script:InstalledIcon)
386
+ return (Test-Path -LiteralPath $script:InstalledIcon)
387
+ } catch {
388
+ return $false
389
+ }
390
+ }
391
+ }
392
+
393
+ function Ensure-MenuIcon {
394
+ param([string]$SourceScriptPath)
395
+ if (Copy-OptionalIcon -SourceScriptPath $SourceScriptPath) {
396
+ return $true
397
+ }
398
+ return (Download-MenuIcon)
399
+ }
400
+
363
401
  function Register-ExplorerMenu {
364
402
  param(
365
403
  [string]$MenuKey,
@@ -369,7 +407,8 @@ function Register-ExplorerMenu {
369
407
  New-Item -Path $MenuKey -Force | Out-Null
370
408
  Set-ItemProperty -Path $MenuKey -Name '(default)' -Value $script:MenuLabel
371
409
  if ($IconPath -and (Test-Path -LiteralPath $IconPath)) {
372
- Set-ItemProperty -Path $MenuKey -Name 'Icon' -Value "$IconPath,0"
410
+ $iconValue = "$((Resolve-Path -LiteralPath $IconPath).Path),0"
411
+ Set-ItemProperty -Path $MenuKey -Name 'Icon' -Value $iconValue
373
412
  }
374
413
  New-Item -Path "$MenuKey\command" -Force | Out-Null
375
414
  Set-ItemProperty -Path "$MenuKey\command" -Name '(default)' -Value $Command
@@ -415,7 +454,12 @@ function Install-CodeChatContextMenu {
415
454
  $null = New-Item -ItemType Directory -Force -Path $script:StandaloneDir
416
455
  Copy-Item -LiteralPath $sourceScript -Destination $script:InstalledScript -Force
417
456
  }
418
- $hasIcon = Copy-OptionalIcon -SourceScriptPath $sourceScript
457
+ # Re-add UTF-8 BOM for PS 5.1 -File mode (required for Chinese chars in menu labels)
458
+ $utf8Bom = New-Object System.Text.UTF8Encoding($true)
459
+ $savedContent = Get-Content -LiteralPath $script:InstalledScript -Raw -Encoding UTF8
460
+ [System.IO.File]::WriteAllText($script:InstalledScript, $savedContent, $utf8Bom)
461
+
462
+ $hasIcon = Ensure-MenuIcon -SourceScriptPath $sourceScript
419
463
 
420
464
  $psArgs = "-NoExit -NoProfile -ExecutionPolicy Bypass -File `"$($script:InstalledScript)`" -Action Start -WorkDir"
421
465
  $wt = Get-WtExecutable
@@ -434,6 +478,11 @@ function Install-CodeChatContextMenu {
434
478
 
435
479
  Write-Host "[ok] Context menu registered: $($script:MenuLabel)"
436
480
  Write-Host "[info] script: $($script:InstalledScript)"
481
+ if ($hasIcon) {
482
+ Write-Host "[info] icon: $($script:InstalledIcon)"
483
+ } else {
484
+ Write-Host "[warn] icon not found; menu will have no icon"
485
+ }
437
486
  if ($wt) {
438
487
  Write-Host "[info] terminal: Windows Terminal"
439
488
  }
@@ -513,16 +562,6 @@ function Install-CodeChatAlias {
513
562
  Write-Utf8NoBom -Path $PROFILE -Value $joined
514
563
  }
515
564
 
516
- # First-run bootstrap: copy ~/.codechat/.env → <cwd>/.claude/.env
517
- $claudeDir = Join-Path (Get-Location).Path '.claude'
518
- $claudeEnv = Join-Path $claudeDir '.env'
519
- $homeEnv = Join-Path $HOME '.codechat\.env'
520
- if (-not (Test-Path $claudeEnv) -and (Test-Path $homeEnv)) {
521
- if (-not (Test-Path $claudeDir)) { New-Item -ItemType Directory -Path $claudeDir -Force | Out-Null }
522
- Copy-Item -Path $homeEnv -Destination $claudeEnv -Force
523
- Write-Host "[info] copied $homeEnv -> $claudeEnv"
524
- }
525
-
526
565
  # Resolve the installed script path (this script after InstallMenu copies it)
527
566
  $aliasTarget = if (Test-Path -LiteralPath $script:InstalledScript) { $script:InstalledScript } else { $PSCommandPath }
528
567
 
@@ -546,9 +585,6 @@ function codechat {
546
585
  # Load into current session
547
586
  try { . $PROFILE } catch {}
548
587
  Write-Host "[ok] Alias 'codechat' installed to $PROFILE"
549
- if (-not (Test-Path $claudeEnv) -and -not (Test-Path $homeEnv)) {
550
- Write-Host "[warn] No .env found. Open this project in VS Code or Android Studio and run the CodeChat plugin first."
551
- }
552
588
  }
553
589
 
554
590
  function Uninstall-CodeChatAlias {
@@ -565,7 +601,7 @@ function Uninstall-CodeChatAlias {
565
601
 
566
602
  switch ($Action) {
567
603
  'Start' {
568
- Invoke-CodeChatStart -TargetWorkDir $WorkDir -SkipEnvSync:$NoSyncEnv -SkipPermissions:$NoSkipPermissions
604
+ Invoke-CodeChatStart -TargetWorkDir $WorkDir -SkipPermissions:$NoSkipPermissions
569
605
  }
570
606
  'InstallMenu' {
571
607
  Install-CodeChatContextMenu
@@ -585,5 +621,10 @@ switch ($Action) {
585
621
  'Install' {
586
622
  Install-CodeChatContextMenu
587
623
  Install-CodeChatAlias
624
+ Ensure-BootstrapExtraArgs -SkipPermissions:$NoSkipPermissions
625
+ }
626
+ 'Uninstall' {
627
+ Uninstall-CodeChatContextMenu
628
+ Uninstall-CodeChatAlias
588
629
  }
589
630
  }
@@ -0,0 +1,29 @@
1
+ Stack trace:
2
+ Frame Function Args
3
+ 0007FFFFBF80 00021005FE8E (000210285F68, 00021026AB6E, 000000000000, 0007FFFFAE80) msys-2.0.dll+0x1FE8E
4
+ 0007FFFFBF80 0002100467F9 (000000000000, 000000000000, 000000000000, 0007FFFFC258) msys-2.0.dll+0x67F9
5
+ 0007FFFFBF80 000210046832 (000210286019, 0007FFFFBE38, 000000000000, 000000000000) msys-2.0.dll+0x6832
6
+ 0007FFFFBF80 000210068CF6 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x28CF6
7
+ 0007FFFFBF80 000210068E24 (0007FFFFBF90, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x28E24
8
+ 0007FFFFC260 00021006A225 (0007FFFFBF90, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A225
9
+ End of stack trace
10
+ Loaded modules:
11
+ 000100400000 bash.exe
12
+ 7FF8D7220000 ntdll.dll
13
+ 7FF8D5CA0000 KERNEL32.DLL
14
+ 7FF8D4870000 KERNELBASE.dll
15
+ 7FF8D5080000 USER32.dll
16
+ 7FF8D4F30000 win32u.dll
17
+ 000210040000 msys-2.0.dll
18
+ 7FF8D6BC0000 GDI32.dll
19
+ 7FF8D4E10000 gdi32full.dll
20
+ 7FF8D4D70000 msvcp_win.dll
21
+ 7FF8D4750000 ucrtbase.dll
22
+ 7FF8D5E70000 advapi32.dll
23
+ 7FF8D6070000 msvcrt.dll
24
+ 7FF8D6DB0000 sechost.dll
25
+ 7FF8D5050000 bcrypt.dll
26
+ 7FF8D6980000 RPCRT4.dll
27
+ 7FF8D3E60000 CRYPTBASE.DLL
28
+ 7FF8D4FD0000 bcryptPrimitives.dll
29
+ 7FF8D56F0000 IMM32.DLL
@@ -0,0 +1,59 @@
1
+ # 前端目录(Vue3 + TypeScript + Pinia)
2
+ src/
3
+ ├── api/ # API 接口定义层(只写 URL 和 method,每个业务模块一个文件)
4
+ ├── mock/
5
+ │ ├── index.js # 路由映射表 (mockDataMap),所有 Mock 路由在此注册
6
+ │ ├── interceptor.js # axios 拦截器(Mock 核心开关)
7
+ │ └── modules/ # Mock 数据实现,与 api/ 一一对应
8
+ ├── stores/ # Pinia 状态层(可选,用于跨组件共享数据,内部仍调用 api/)
9
+ ├── views/ # 页面
10
+ ├── components/ # 公共组件(图表、顶栏、面板等)
11
+ ├── router/
12
+ │ └── index.ts # 路由配置(含权限守卫)
13
+ └── utils/
14
+ └── request.ts # axios 封装(含 Mock 拦截接入点)
15
+
16
+ # 后端目录(SpringBoot DDD 分层,包名以 [基础包名] 为前缀)
17
+ [基础包名]/
18
+ ├── application/
19
+ │ ├── adapter/
20
+ │ │ ├── api/ # REST 入口(XxxAppService)
21
+ │ │ ├── consumer/ # MQ 消费者
22
+ │ │ └── scheduler/ # 定时任务(XxxTask)
23
+ │ ├── service/
24
+ │ │ ├── XxxAppService.java # 应用服务
25
+ │ │ └── process/
26
+ │ │ └── XxxProcess.java # 编排流程
27
+ │ ├── repository/ # 仓储接口(IXxxAppRepository)
28
+ │ ├── dto/ # 请求/响应体(XxxReqDTO / XxxRespDTO)
29
+ │ ├── converter/ # 转换器(XxxAppConverter)
30
+ │ ├── external/ # 外部服务接口(IXxxExternalService)
31
+ │ └── error/ # 错误码(AppErrorEnum)
32
+ ├── domain/
33
+ │ └── [聚合根]/
34
+ │ ├── entity/ # 实体(无后缀)/ 值对象(XxxVO)
35
+ │ ├── event/ # 领域事件(完成时态,XxxEvent)
36
+ │ ├── repository/ # 仓储接口(IXxxRepository)
37
+ │ ├── constants/ # 常量(XxxConstants)
38
+ │ ├── enums/ # 枚举(XxxEnum)
39
+ │ ├── error/ # 错误码(XxxErrorEnum)
40
+ │ └── properties/
41
+ ├── infrastructure/
42
+ │ └── [聚合根]/
43
+ │ └── repository/
44
+ │ ├── dao/ # DAO(XxxDao)
45
+ │ ├── cache/ # 缓存(XxxCache)
46
+ │ ├── storage/ # 对象存储(XxxStorage)
47
+ │ ├── dataObject/ # 持久化对象(XxxDO)
48
+ │ └── converter/ # 转换器(XxxConverter)
49
+ │ └── XxxRepository.java # 仓储实现(XxxRepository)
50
+ ├── external/
51
+ │ └── [外部中心名称]/
52
+ │ ├── dto/ # 外部 DTO(XxxReqDTO / XxxRespDTO)
53
+ │ ├── converter/ # 防腐转换(XxxConverter)
54
+ │ ├── feign/ # Feign 客户端(IXxxFeignClient)
55
+ │ ├── error/ # 错误码(ExternalErrorEnum)
56
+ │ └── properties/
57
+ │ └── XxxExternalService.java # 外部服务实现
58
+ └── boot/ # 启动层(包名固定:[基础包名].boot)
59
+ └── XxxApplication.java # 启动类
@@ -26,6 +26,7 @@ build:
26
26
  repomix: false
27
27
  enableBaselineExtract: false
28
28
  strictInputConfirm: false
29
+ # convertTimeoutSec: 900 # build 自动 convert 超时(秒),默认 900(15 分钟)
29
30
  generateWiki: false
30
31
  wikiLang: zh
31
32
  force: false
@@ -55,6 +56,7 @@ convert:
55
56
  - ./docs/*.pdf
56
57
  output: ./my-app-harness
57
58
  type: requirements
59
+ # timeoutSec: 900 # 单文件转换超时(秒),默认 900(15 分钟)
58
60
  yes: true
59
61
 
60
62
  # wiki:对任意代码工程目录生成 baseline wiki(独立于 harness 生命周期)
@@ -1,24 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.resolveLaunchScriptPath = resolveLaunchScriptPath;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = __importDefault(require("path"));
9
- /**
10
- * Resolve absolute path to bin/launch-codechat-cli.js in the installed package.
11
- */
12
- function resolveLaunchScriptPath() {
13
- // dist/lib/launch-script-path.js → ../../bin/launch-codechat-cli.js
14
- const fromDist = path_1.default.resolve(__dirname, '..', '..', 'bin', 'launch-codechat-cli.js');
15
- if (fs_extra_1.default.existsSync(fromDist)) {
16
- return fromDist;
17
- }
18
- // fallback: package root from scripts/postinstall.js context
19
- const fromCwd = path_1.default.resolve(__dirname, '..', '..', '..', 'bin', 'launch-codechat-cli.js');
20
- if (fs_extra_1.default.existsSync(fromCwd)) {
21
- return fromCwd;
22
- }
23
- throw new Error(`未找到 launch-codechat-cli.js(预期:${fromDist})`);
24
- }
@@ -1,59 +0,0 @@
1
- # ============================================================
2
- # CodeChat接口
3
- # ============================================================
4
- ANTHROPIC_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVpZHEzNjQwIiwiZXhwIjoxNzg0MTAzODE1fQ.d8BSD1V2_5Kv-gB8OeS5092Jg6XoL63XR2Tocw5S2bs
5
- ANTHROPIC_BASE_URL=https://aiplus.desaysv.com/chatbot/anthropic
6
- # ANTHROPIC_BASE_URL=http://127.0.0.1:6008/chatbot/anthropic # 德赛本地调试
7
- ANTHROPIC_MODEL=deepseek # glm / deepseek
8
- ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek # glm / deepseek
9
- ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek # glm / deepseek
10
- API_TIMEOUT_MS=3000000
11
-
12
- # ============================================================
13
- # OpenAI(通过 LiteLLM 代理)
14
- # 先启动: litellm --config litellm_config.yaml --port 4000
15
- # ============================================================
16
- # ANTHROPIC_AUTH_TOKEN=sk-anything
17
- # ANTHROPIC_BASE_URL=http://localhost:4000
18
- # ANTHROPIC_MODEL=gpt-4o
19
- # ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
20
- # ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
21
- # ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
22
- # API_TIMEOUT_MS=3000000
23
-
24
- # ============================================================
25
- # DeepSeek(通过 LiteLLM 代理)
26
- # 先启动: litellm --config litellm_config.yaml --port 4000
27
- # ============================================================
28
- # ANTHROPIC_AUTH_TOKEN=sk-anything
29
- # ANTHROPIC_BASE_URL=http://localhost:4000
30
- # ANTHROPIC_MODEL=deepseek-chat
31
- # ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat
32
- # ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat
33
- # ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat
34
- # API_TIMEOUT_MS=3000000
35
-
36
- # ============================================================
37
- # OpenRouter(直连 Anthropic 兼容接口)
38
- # ============================================================
39
- # ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
40
- # ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
41
- # ANTHROPIC_MODEL=openai/gpt-4o
42
- # ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
43
- # ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
44
- # ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
45
-
46
- # ============================================================
47
- # 德赛调试专用
48
- # ============================================================
49
- CLAUDE_CODE_TRACE_ENABLED=0
50
- CLAUDE_CODE_TRACE_PIPELINE=0
51
- CLAUDE_CODE_TRACE_PIPELINE_FILE=
52
- CLAUDE_CODE_TRACE_WIRE_PARAMS=0
53
-
54
- # ============================================================
55
- # 通用设置(建议始终开启)
56
- # ============================================================
57
- DISABLE_TELEMETRY=1
58
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
59
- ENABLE_TOOL_SEARCH=0