From e2f5b92b7a8e42830788ae16ae7f8b293a577486 Mon Sep 17 00:00:00 2001 From: Gabriel Mireles Date: Sun, 3 May 2026 17:11:50 -0400 Subject: [PATCH] fix: use forward slashes in Join-Path literals for cross-platform paths (#343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PowerShell on Linux/macOS treats `\` as a literal character in path strings, so `Join-Path $x "workspace\tasks"` produced nonexistent paths like `/home/user/workspace\tasks` on Unix. Test-Path returned false against those paths and surrounding code silently took the "directory doesn't exist" branch — runtime was broken in ways the cross-platform test suite couldn't catch. Replace backslash literals with forward slashes (Windows accepts both; Linux/macOS only accept `/`) in Join-Path second arguments across runtime, UI, MCP tools, scripts, server, stacks, and workflow code (~100 files, sweeping the remaining sites flagged in the audit). Add tests/Test-NoBackslashPaths.ps1 as a Layer 1 hard-fail lint that greps for `Join-Path[^"]*"[^"]*\` and the single-quoted variant, with tests/ allowlisted (fixtures intentionally use Windows-style strings). Wire into Run-Tests.ps1 so new violations are caught at PR time. Closes #343 --- core/go.ps1 | 8 +- core/hooks/scripts/steering.ps1 | 2 +- core/init.ps1 | 6 +- core/mcp/dotbot-mcp.ps1 | 8 +- core/mcp/modules/NotificationClient.psm1 | 2 +- core/mcp/modules/TaskIndexCache.psm1 | 2 +- core/mcp/modules/TaskMutation.psm1 | 2 +- core/mcp/modules/TaskStore.psm1 | 2 +- core/mcp/tools/decision-create/script.ps1 | 2 +- core/mcp/tools/decision-get/script.ps1 | 2 +- core/mcp/tools/decision-list/script.ps1 | 2 +- .../tools/decision-mark-accepted/script.ps1 | 2 +- .../tools/decision-mark-deprecated/script.ps1 | 2 +- .../tools/decision-mark-superseded/script.ps1 | 2 +- core/mcp/tools/decision-update/script.ps1 | 2 +- core/mcp/tools/dev-start/script.ps1 | 2 +- core/mcp/tools/dev-stop/script.ps1 | 2 +- core/mcp/tools/plan-create/script.ps1 | 4 +- core/mcp/tools/plan-update/script.ps1 | 2 +- core/mcp/tools/session-get-state/script.ps1 | 2 +- core/mcp/tools/session-get-stats/script.ps1 | 2 +- .../session-increment-completed/script.ps1 | 2 +- core/mcp/tools/session-initialize/script.ps1 | 2 +- core/mcp/tools/session-update/script.ps1 | 2 +- core/mcp/tools/steering-heartbeat/script.ps1 | 4 +- core/mcp/tools/steering-heartbeat/test.ps1 | 2 +- .../mcp/tools/task-answer-question/script.ps1 | 4 +- core/mcp/tools/task-answer-question/test.ps1 | 6 +- core/mcp/tools/task-approve-split/script.ps1 | 2 +- core/mcp/tools/task-create-bulk/script.ps1 | 4 +- core/mcp/tools/task-create/script.ps1 | 4 +- core/mcp/tools/task-get-context/script.ps1 | 2 +- core/mcp/tools/task-get-next/script.ps1 | 2 +- core/mcp/tools/task-get-stats/script.ps1 | 2 +- core/mcp/tools/task-list/script.ps1 | 2 +- core/mcp/tools/task-mark-analysed/script.ps1 | 2 +- core/mcp/tools/task-mark-done/script.ps1 | 6 +- core/mcp/tools/task-mark-done/test.ps1 | 4 +- core/mcp/tools/task-mark-skipped/test.ps1 | 2 +- core/runtime/ProviderCLI/ProviderCLI.psm1 | 2 +- core/runtime/expand-task-groups.ps1 | 8 +- core/runtime/launch-process.ps1 | 6 +- core/runtime/modules/DotBotTheme.psm1 | 4 +- core/runtime/modules/InterviewLoop.ps1 | 4 +- .../ProcessTypes/Invoke-PromptProcess.ps1 | 6 +- .../ProcessTypes/Invoke-WorkflowProcess.ps1 | 12 +- core/runtime/modules/SettingsLoader.psm1 | 4 +- core/runtime/modules/WorktreeManager.psm1 | 56 ++++----- core/runtime/modules/cleanup.ps1 | 2 +- core/runtime/modules/task-reset.ps1 | 2 +- core/runtime/modules/test-task-completion.ps1 | 4 +- core/runtime/modules/workflow-manifest.ps1 | 2 +- core/ui/modules/ControlAPI.psm1 | 8 +- core/ui/modules/DecisionAPI.psm1 | 2 +- core/ui/modules/FileWatcher.psm1 | 10 +- core/ui/modules/InboxWatcher.psm1 | 2 +- core/ui/modules/NotificationPoller.psm1 | 10 +- core/ui/modules/ProcessAPI.psm1 | 4 +- core/ui/modules/ProductAPI.psm1 | 22 ++-- core/ui/modules/ReferenceCache.psm1 | 8 +- core/ui/modules/SettingsAPI.psm1 | 10 +- core/ui/modules/StateBuilder.psm1 | 12 +- core/ui/modules/TaskAPI.psm1 | 14 +-- core/ui/server.ps1 | 54 ++++----- scripts/doctor.ps1 | 6 +- scripts/init-project.ps1 | 24 ++-- scripts/registry-add.ps1 | 2 +- scripts/registry-list.ps1 | 2 +- scripts/registry-update.ps1 | 2 +- scripts/tasks-run.ps1 | 2 +- scripts/tasks-stop.ps1 | 4 +- scripts/workflow-add.ps1 | 8 +- scripts/workflow-list.ps1 | 2 +- scripts/workflow-remove.ps1 | 8 +- scripts/workflow-run.ps1 | 6 +- server/Send-DotbotQuestion.ps1 | 2 +- server/scripts/Deploy.ps1 | 8 +- server/scripts/Load-Env.ps1 | 2 +- server/scripts/Send-QuestionInstance.ps1 | 2 +- server/scripts/Test-EndToEnd.ps1 | 2 +- server/scripts/create-icons.ps1 | 2 +- server/scripts/publish-teams-app.ps1 | 2 +- server/scripts/resize-icon.ps1 | 4 +- stacks/dotnet/hooks/dev/Common.ps1 | 2 +- stacks/dotnet/hooks/dev/Start-Dev.ps1 | 2 +- stacks/dotnet/hooks/dev/Stop-Dev.ps1 | 2 +- stacks/dotnet/hooks/scripts/migrate.ps1 | 4 +- .../systems/mcp/tools/dev-db/script.ps1 | 4 +- .../systems/mcp/tools/dev-deploy/script.ps1 | 4 +- .../systems/mcp/tools/dev-logs/script.ps1 | 4 +- .../systems/mcp/tools/dev-release/script.ps1 | 4 +- .../systems/mcp/tools/prod-start/script.ps1 | 4 +- .../systems/mcp/tools/prod-stop/script.ps1 | 4 +- tests/Run-Tests.ps1 | 3 +- tests/Test-NoBackslashPaths.ps1 | 109 ++++++++++++++++++ tests/Test-TaskActions.ps1 | 2 +- tests/Test-WorkflowIntegration.ps1 | 2 +- .../hooks/verify/03-research-completeness.ps1 | 4 +- .../systems/mcp/tools/repo-clone/script.ps1 | 6 +- .../systems/mcp/tools/repo-list/script.ps1 | 8 +- .../systems/mcp/tools/repo-list/test.ps1 | 2 +- .../mcp/tools/research-status/script.ps1 | 4 +- .../mcp/tools/research-status/test.ps1 | 4 +- 103 files changed, 380 insertions(+), 270 deletions(-) create mode 100644 tests/Test-NoBackslashPaths.ps1 diff --git a/core/go.ps1 b/core/go.ps1 index 443eb002..d0ad7a4f 100644 --- a/core/go.ps1 +++ b/core/go.ps1 @@ -35,8 +35,8 @@ $ServerScript = Join-Path $UIDir "server.ps1" $oldDefaults = Join-Path $BotDir "defaults" $newSettings = Join-Path $BotDir "settings" if ((Test-Path $oldDefaults) -and -not (Test-Path $newSettings)) { Rename-Item $oldDefaults $newSettings } -$oldInner = Join-Path $BotDir "prompts\workflows" -if (Test-Path $oldInner) { Rename-Item $oldInner (Join-Path $BotDir "prompts\_prompts_tmp") } +$oldInner = Join-Path $BotDir "prompts/workflows" +if (Test-Path $oldInner) { Rename-Item $oldInner (Join-Path $BotDir "prompts/_prompts_tmp") } $oldPrompts = Join-Path $BotDir "prompts" $newRecipes = Join-Path $BotDir "recipes" if ((Test-Path $oldPrompts) -and -not (Test-Path $newRecipes)) { @@ -44,8 +44,8 @@ if ((Test-Path $oldPrompts) -and -not (Test-Path $newRecipes)) { $tmp = Join-Path $newRecipes "_prompts_tmp" if (Test-Path $tmp) { Rename-Item $tmp (Join-Path $newRecipes "prompts") } } -$oldAdrs = Join-Path $BotDir "workspace\adrs" -$newDec = Join-Path $BotDir "workspace\decisions" +$oldAdrs = Join-Path $BotDir "workspace/adrs" +$newDec = Join-Path $BotDir "workspace/decisions" if ((Test-Path $oldAdrs) -and -not (Test-Path $newDec)) { Rename-Item $oldAdrs $newDec } # Initialize structured logging diff --git a/core/hooks/scripts/steering.ps1 b/core/hooks/scripts/steering.ps1 index e095f602..ae093543 100644 --- a/core/hooks/scripts/steering.ps1 +++ b/core/hooks/scripts/steering.ps1 @@ -52,7 +52,7 @@ if (Test-Path $themePath) { } } -$controlDir = Join-Path $PSScriptRoot "..\..\..\.control" +$controlDir = Join-Path $PSScriptRoot "../../../.control" $controlDir = [System.IO.Path]::GetFullPath($controlDir) $processesDir = Join-Path $controlDir "processes" $statusFile = Join-Path $controlDir "steering-status.json" diff --git a/core/init.ps1 b/core/init.ps1 index a10fcd76..001f57c6 100644 --- a/core/init.ps1 +++ b/core/init.ps1 @@ -26,7 +26,7 @@ $ErrorActionPreference = "Stop" # Get script and project directories $BotDir = $PSScriptRoot $ProjectRoot = Split-Path -Parent $BotDir -$ProvidersDir = Join-Path $BotDir "settings\providers" +$ProvidersDir = Join-Path $BotDir "settings/providers" Write-Status "Initializing IDE integrations..." Write-BlankLine @@ -51,8 +51,8 @@ if (Test-Path $ProvidersDir) { } } -$SourceAgentsDir = Join-Path $BotDir "recipes\agents" -$SourceSkillsDir = Join-Path $BotDir "recipes\skills" +$SourceAgentsDir = Join-Path $BotDir "recipes/agents" +$SourceSkillsDir = Join-Path $BotDir "recipes/skills" foreach ($provider in $providerDirs) { $providerName = $provider.Name diff --git a/core/mcp/dotbot-mcp.ps1 b/core/mcp/dotbot-mcp.ps1 index ea0ebd6d..a3157075 100644 --- a/core/mcp/dotbot-mcp.ps1 +++ b/core/mcp/dotbot-mcp.ps1 @@ -40,10 +40,10 @@ if (-not $script:ProjectRoot) { $global:DotbotProjectRoot = $script:ProjectRoot # Initialize structured logging (console disabled — stdout is MCP protocol) -$mcpControlDir = Join-Path $script:ProjectRoot ".bot\.control" +$mcpControlDir = Join-Path $script:ProjectRoot ".bot/.control" $mcpLogsDir = Join-Path $mcpControlDir "logs" if (-not (Test-Path $mcpLogsDir)) { New-Item -Path $mcpLogsDir -ItemType Directory -Force | Out-Null } -$dotBotLogPath = Join-Path $PSScriptRoot "..\runtime\modules\DotBotLog.psm1" +$dotBotLogPath = Join-Path $PSScriptRoot "../runtime/modules/DotBotLog.psm1" if (Test-Path $dotBotLogPath) { Import-Module $dotBotLogPath -Force -DisableNameChecking Initialize-DotBotLog -LogDir $mcpLogsDir -ControlDir $mcpControlDir -ProjectRoot $script:ProjectRoot -ConsoleEnabled $false @@ -51,7 +51,7 @@ if (Test-Path $dotBotLogPath) { # Diagnostic logging (stderr, separate from MCP protocol on stdout) [Console]::Error.WriteLine("Project root: $($script:ProjectRoot)") -$tasksCheck = Join-Path $script:ProjectRoot ".bot\workspace\tasks" +$tasksCheck = Join-Path $script:ProjectRoot ".bot/workspace/tasks" if (Test-Path $tasksCheck) { [Console]::Error.WriteLine("Tasks directory: OK ($tasksCheck)") } else { @@ -104,7 +104,7 @@ foreach ($toolDirItem in $toolDirs) { } # Discover workflow tools: .bot/workflows/*/tools/ -$workflowsDir = Join-Path (Split-Path $PSScriptRoot -Parent) "..\workflows" +$workflowsDir = Join-Path (Split-Path $PSScriptRoot -Parent) "../workflows" if (Test-Path $workflowsDir) { Get-ChildItem -Path $workflowsDir -Directory | Where-Object { Test-ValidWorkflowDir -Dir $_.FullName diff --git a/core/mcp/modules/NotificationClient.psm1 b/core/mcp/modules/NotificationClient.psm1 index 012ff257..b675b7f1 100644 --- a/core/mcp/modules/NotificationClient.psm1 +++ b/core/mcp/modules/NotificationClient.psm1 @@ -10,7 +10,7 @@ to collect external responses. #> if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } function Get-NotificationSettings { diff --git a/core/mcp/modules/TaskIndexCache.psm1 b/core/mcp/modules/TaskIndexCache.psm1 index 7ee3ce77..7a6ca3dc 100644 --- a/core/mcp/modules/TaskIndexCache.psm1 +++ b/core/mcp/modules/TaskIndexCache.psm1 @@ -194,7 +194,7 @@ function Get-IgnoreRoadmapDependencyMap { ) $workspaceDir = Split-Path -Parent $TasksBaseDir - $overviewPath = Join-Path $workspaceDir "product\roadmap-overview.md" + $overviewPath = Join-Path $workspaceDir "product/roadmap-overview.md" $dependencyMap = @{} if (-not (Test-Path $overviewPath)) { return $dependencyMap diff --git a/core/mcp/modules/TaskMutation.psm1 b/core/mcp/modules/TaskMutation.psm1 index 40a71c39..68a13477 100644 --- a/core/mcp/modules/TaskMutation.psm1 +++ b/core/mcp/modules/TaskMutation.psm1 @@ -209,7 +209,7 @@ function Get-RoadmapOverviewDependencyMap { $resolvedBaseDir = Get-TasksBaseDir -TasksBaseDir $TasksBaseDir $workspaceDir = Split-Path -Parent $resolvedBaseDir - $overviewPath = Join-Path $workspaceDir "product\roadmap-overview.md" + $overviewPath = Join-Path $workspaceDir "product/roadmap-overview.md" $dependencyMap = @{} if (-not (Test-Path $overviewPath)) { return $dependencyMap diff --git a/core/mcp/modules/TaskStore.psm1 b/core/mcp/modules/TaskStore.psm1 index 630e8ec2..843c1181 100644 --- a/core/mcp/modules/TaskStore.psm1 +++ b/core/mcp/modules/TaskStore.psm1 @@ -54,7 +54,7 @@ function Get-TasksBaseDir { } $projectRoot = Get-DotbotProjectRoot - return (Join-Path $projectRoot ".bot\workspace\tasks") + return (Join-Path $projectRoot ".bot/workspace/tasks") } function Get-TodoDirectories { diff --git a/core/mcp/tools/decision-create/script.ps1 b/core/mcp/tools/decision-create/script.ps1 index 08d5eece..865aa727 100644 --- a/core/mcp/tools/decision-create/script.ps1 +++ b/core/mcp/tools/decision-create/script.ps1 @@ -44,7 +44,7 @@ function Invoke-DecisionCreate { $slug = ($title -replace '[^\w\s-]', '' -replace '\s+', '-').ToLowerInvariant() if ($slug.Length -gt 60) { $slug = $slug.Substring(0, 60).TrimEnd('-') } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $targetDir = Join-Path $decisionsBaseDir $status if (-not (Test-Path $targetDir)) { New-Item -ItemType Directory -Force -Path $targetDir | Out-Null } diff --git a/core/mcp/tools/decision-get/script.ps1 b/core/mcp/tools/decision-get/script.ps1 index 3c12ed9e..0ef9a2d9 100644 --- a/core/mcp/tools/decision-get/script.ps1 +++ b/core/mcp/tools/decision-get/script.ps1 @@ -5,7 +5,7 @@ function Invoke-DecisionGet { if (-not $decId) { throw "decision_id is required" } if ($decId -notmatch '^dec-[a-f0-9]{8}$') { throw "Invalid decision_id format '$decId'. Expected: dec-XXXXXXXX" } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $allStatuses = @('proposed', 'accepted', 'deprecated', 'superseded') $found = $null diff --git a/core/mcp/tools/decision-list/script.ps1 b/core/mcp/tools/decision-list/script.ps1 index 0702c204..3d03292c 100644 --- a/core/mcp/tools/decision-list/script.ps1 +++ b/core/mcp/tools/decision-list/script.ps1 @@ -2,7 +2,7 @@ function Invoke-DecisionList { param([hashtable]$Arguments) $filterStatus = $Arguments['status'] - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $allStatuses = @('proposed', 'accepted', 'deprecated', 'superseded') if ($filterStatus -and $filterStatus -notin $allStatuses) { diff --git a/core/mcp/tools/decision-mark-accepted/script.ps1 b/core/mcp/tools/decision-mark-accepted/script.ps1 index 6af9dda2..bcb5f8cd 100644 --- a/core/mcp/tools/decision-mark-accepted/script.ps1 +++ b/core/mcp/tools/decision-mark-accepted/script.ps1 @@ -5,7 +5,7 @@ function Invoke-DecisionMarkAccepted { if (-not $decId) { throw "decision_id is required" } if ($decId -notmatch '^dec-[a-f0-9]{8}$') { throw "Invalid decision_id format '$decId'. Expected: dec-XXXXXXXX" } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $sourceDir = Join-Path $decisionsBaseDir "proposed" if (-not (Test-Path $sourceDir)) { throw "No proposed decisions directory found" } diff --git a/core/mcp/tools/decision-mark-deprecated/script.ps1 b/core/mcp/tools/decision-mark-deprecated/script.ps1 index 6a441354..8b83ed81 100644 --- a/core/mcp/tools/decision-mark-deprecated/script.ps1 +++ b/core/mcp/tools/decision-mark-deprecated/script.ps1 @@ -6,7 +6,7 @@ function Invoke-DecisionMarkDeprecated { if (-not $decId) { throw "decision_id is required" } if ($decId -notmatch '^dec-[a-f0-9]{8}$') { throw "Invalid decision_id format '$decId'. Expected: dec-XXXXXXXX" } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $allStatuses = @('proposed', 'accepted') $found = $null diff --git a/core/mcp/tools/decision-mark-superseded/script.ps1 b/core/mcp/tools/decision-mark-superseded/script.ps1 index 3e0d7cd5..7a7eee86 100644 --- a/core/mcp/tools/decision-mark-superseded/script.ps1 +++ b/core/mcp/tools/decision-mark-superseded/script.ps1 @@ -8,7 +8,7 @@ function Invoke-DecisionMarkSuperseded { if (-not $supersededBy) { throw "superseded_by is required" } if ($supersededBy -notmatch '^dec-[a-f0-9]{8}$') { throw "Invalid superseded_by format '$supersededBy'. Expected: dec-XXXXXXXX" } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $allStatuses = @('proposed', 'accepted') $found = $null diff --git a/core/mcp/tools/decision-update/script.ps1 b/core/mcp/tools/decision-update/script.ps1 index 42f5cb07..ac11e67f 100644 --- a/core/mcp/tools/decision-update/script.ps1 +++ b/core/mcp/tools/decision-update/script.ps1 @@ -5,7 +5,7 @@ function Invoke-DecisionUpdate { if (-not $decId) { throw "decision_id is required" } if ($decId -notmatch '^dec-[a-f0-9]{8}$') { throw "Invalid decision_id format '$decId'. Expected: dec-XXXXXXXX" } - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $allStatuses = @('proposed', 'accepted', 'deprecated', 'superseded') $found = $null diff --git a/core/mcp/tools/dev-start/script.ps1 b/core/mcp/tools/dev-start/script.ps1 index e3a18566..e940e982 100644 --- a/core/mcp/tools/dev-start/script.ps1 +++ b/core/mcp/tools/dev-start/script.ps1 @@ -26,7 +26,7 @@ function Invoke-DevStart { } # Check for dev script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Start-Dev.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Start-Dev.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/core/mcp/tools/dev-stop/script.ps1 b/core/mcp/tools/dev-stop/script.ps1 index f060ffe2..db4adcdd 100644 --- a/core/mcp/tools/dev-stop/script.ps1 +++ b/core/mcp/tools/dev-stop/script.ps1 @@ -26,7 +26,7 @@ function Invoke-DevStop { } # Check for dev script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Stop-Dev.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Stop-Dev.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/core/mcp/tools/plan-create/script.ps1 b/core/mcp/tools/plan-create/script.ps1 index e5076b7c..82e74ef3 100644 --- a/core/mcp/tools/plan-create/script.ps1 +++ b/core/mcp/tools/plan-create/script.ps1 @@ -17,7 +17,7 @@ function Invoke-PlanCreate { } # Find task file by ID (search all status directories) - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" $statusDirs = @('todo', 'in-progress', 'done', 'skipped', 'cancelled') $taskFile = $null $taskFilename = $null @@ -44,7 +44,7 @@ function Invoke-PlanCreate { # Derive plan filename from task filename (replace .json with -plan.md) $planFilename = $taskFilename -replace '\.json$', '-plan.md' - $plansDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\plans" + $plansDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/plans" # Ensure plans directory exists if (-not (Test-Path $plansDir)) { diff --git a/core/mcp/tools/plan-update/script.ps1 b/core/mcp/tools/plan-update/script.ps1 index 30a81f0c..69f79dfa 100644 --- a/core/mcp/tools/plan-update/script.ps1 +++ b/core/mcp/tools/plan-update/script.ps1 @@ -17,7 +17,7 @@ function Invoke-PlanUpdate { } # Find task file by ID (search all status directories) - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" $statusDirs = @('todo', 'in-progress', 'done', 'skipped', 'cancelled') $taskFile = $null $task = $null diff --git a/core/mcp/tools/session-get-state/script.ps1 b/core/mcp/tools/session-get-state/script.ps1 index 6e04ac34..fb45f5eb 100644 --- a/core/mcp/tools/session-get-state/script.ps1 +++ b/core/mcp/tools/session-get-state/script.ps1 @@ -4,7 +4,7 @@ function Invoke-SessionGetState { ) # Define paths - $stateFile = Join-Path $global:DotbotProjectRoot ".bot\workspace\sessions\runs\session-state.json" + $stateFile = Join-Path $global:DotbotProjectRoot ".bot/workspace/sessions/runs/session-state.json" # Check if state file exists if (-not (Test-Path $stateFile)) { diff --git a/core/mcp/tools/session-get-stats/script.ps1 b/core/mcp/tools/session-get-stats/script.ps1 index 7ade52c5..2ce7c486 100644 --- a/core/mcp/tools/session-get-stats/script.ps1 +++ b/core/mcp/tools/session-get-stats/script.ps1 @@ -4,7 +4,7 @@ function Invoke-SessionGetStats { ) # Define paths - $stateFile = Join-Path $global:DotbotProjectRoot ".bot\workspace\sessions\runs\session-state.json" + $stateFile = Join-Path $global:DotbotProjectRoot ".bot/workspace/sessions/runs/session-state.json" # Check if state file exists if (-not (Test-Path $stateFile)) { diff --git a/core/mcp/tools/session-increment-completed/script.ps1 b/core/mcp/tools/session-increment-completed/script.ps1 index a87c0abe..4893a482 100644 --- a/core/mcp/tools/session-increment-completed/script.ps1 +++ b/core/mcp/tools/session-increment-completed/script.ps1 @@ -4,7 +4,7 @@ function Invoke-SessionIncrementCompleted { ) # Define paths - $stateFile = Join-Path $global:DotbotProjectRoot ".bot\workspace\sessions\runs\session-state.json" + $stateFile = Join-Path $global:DotbotProjectRoot ".bot/workspace/sessions/runs/session-state.json" # Check if state file exists if (-not (Test-Path $stateFile)) { diff --git a/core/mcp/tools/session-initialize/script.ps1 b/core/mcp/tools/session-initialize/script.ps1 index 11d89a2e..6ea656d4 100644 --- a/core/mcp/tools/session-initialize/script.ps1 +++ b/core/mcp/tools/session-initialize/script.ps1 @@ -6,7 +6,7 @@ function Invoke-SessionInitialize { $sessionType = $Arguments.session_type # Define paths - $autonomousDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\sessions\runs" + $autonomousDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/sessions/runs" $stateFile = Join-Path $autonomousDir "session-state.json" $lockFile = Join-Path $autonomousDir "session.lock" diff --git a/core/mcp/tools/session-update/script.ps1 b/core/mcp/tools/session-update/script.ps1 index afc26bef..09bd710c 100644 --- a/core/mcp/tools/session-update/script.ps1 +++ b/core/mcp/tools/session-update/script.ps1 @@ -4,7 +4,7 @@ function Invoke-SessionUpdate { ) # Define paths - $stateFile = Join-Path $global:DotbotProjectRoot ".bot\workspace\sessions\runs\session-state.json" + $stateFile = Join-Path $global:DotbotProjectRoot ".bot/workspace/sessions/runs/session-state.json" # Check if state file exists if (-not (Test-Path $stateFile)) { diff --git a/core/mcp/tools/steering-heartbeat/script.ps1 b/core/mcp/tools/steering-heartbeat/script.ps1 index 6b674d5f..d41a42b0 100644 --- a/core/mcp/tools/steering-heartbeat/script.ps1 +++ b/core/mcp/tools/steering-heartbeat/script.ps1 @@ -1,4 +1,4 @@ -Import-Module (Join-Path $PSScriptRoot "..\..\..\runtime\modules\ConsoleSequenceSanitizer.psm1") +Import-Module (Join-Path $PSScriptRoot "../../../runtime/modules/ConsoleSequenceSanitizer.psm1") function Invoke-SteeringHeartbeat { <# @@ -43,7 +43,7 @@ function Invoke-SteeringHeartbeat { } } - $controlDir = Join-Path $global:DotbotProjectRoot ".bot\.control" + $controlDir = Join-Path $global:DotbotProjectRoot ".bot/.control" $controlDir = [System.IO.Path]::GetFullPath($controlDir) # Ensure control directory exists diff --git a/core/mcp/tools/steering-heartbeat/test.ps1 b/core/mcp/tools/steering-heartbeat/test.ps1 index 39d80456..6cf8931b 100644 --- a/core/mcp/tools/steering-heartbeat/test.ps1 +++ b/core/mcp/tools/steering-heartbeat/test.ps1 @@ -5,7 +5,7 @@ Import-Module $env:DOTBOT_TEST_HELPERS -Force Reset-TestResults -$controlDir = Join-Path $global:DotbotProjectRoot ".bot\.control" +$controlDir = Join-Path $global:DotbotProjectRoot ".bot/.control" $processesDir = Join-Path $controlDir "processes" if (-not (Test-Path $processesDir)) { New-Item -ItemType Directory -Path $processesDir -Force | Out-Null diff --git a/core/mcp/tools/task-answer-question/script.ps1 b/core/mcp/tools/task-answer-question/script.ps1 index 0bb3fddc..c2ed6254 100644 --- a/core/mcp/tools/task-answer-question/script.ps1 +++ b/core/mcp/tools/task-answer-question/script.ps1 @@ -5,7 +5,7 @@ function Write-InterviewAnswer { [string]$BotRoot, [hashtable]$Entry # { question_id, question, answer_key, answer_label, answer, context, answered_at } ) - $productDir = Join-Path $BotRoot "workspace\product" + $productDir = Join-Path $BotRoot "workspace/product" if (-not (Test-Path $productDir)) { return } $answersPath = Join-Path $productDir "interview-answers.json" @@ -42,7 +42,7 @@ function Invoke-TaskAnswerQuestion { } # Define tasks directories - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" $needsInputDir = Join-Path $tasksBaseDir "needs-input" $analysingDir = Join-Path $tasksBaseDir "analysing" diff --git a/core/mcp/tools/task-answer-question/test.ps1 b/core/mcp/tools/task-answer-question/test.ps1 index ae243efe..9d412b76 100644 --- a/core/mcp/tools/task-answer-question/test.ps1 +++ b/core/mcp/tools/task-answer-question/test.ps1 @@ -5,7 +5,7 @@ Import-Module $env:DOTBOT_TEST_HELPERS -Force Reset-TestResults -$needsInputDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\needs-input" +$needsInputDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/needs-input" if (-not (Test-Path $needsInputDir)) { New-Item -ItemType Directory -Force -Path $needsInputDir | Out-Null } @@ -54,7 +54,7 @@ try { -Actual $result.attachments_count # Verify task moved to analysing/ - $analysingDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\analysing" + $analysingDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/analysing" $movedFile = Get-ChildItem -Path $analysingDir -Filter "$testTaskId.json" -ErrorAction SilentlyContinue | Select-Object -First 1 Assert-True -Name "task-answer-question: file moved to analysing/" ` @@ -122,7 +122,7 @@ try { -Message "Expected throw for missing task_id" } finally { - $analysingDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\analysing" + $analysingDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/analysing" Remove-Item (Join-Path $analysingDir "$testTaskId.json") -Force -ErrorAction SilentlyContinue Remove-Item (Join-Path $analysingDir "$testTaskId2.json") -Force -ErrorAction SilentlyContinue Remove-Item (Join-Path $needsInputDir "$testTaskId.json") -Force -ErrorAction SilentlyContinue diff --git a/core/mcp/tools/task-approve-split/script.ps1 b/core/mcp/tools/task-approve-split/script.ps1 index f044e076..420ab519 100644 --- a/core/mcp/tools/task-approve-split/script.ps1 +++ b/core/mcp/tools/task-approve-split/script.ps1 @@ -17,7 +17,7 @@ function Invoke-TaskApproveSplit { } # Define tasks directories - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" $needsInputDir = Join-Path $tasksBaseDir "needs-input" $analysingDir = Join-Path $tasksBaseDir "analysing" $splitDir = Join-Path $tasksBaseDir "split" diff --git a/core/mcp/tools/task-create-bulk/script.ps1 b/core/mcp/tools/task-create-bulk/script.ps1 index aaaa5437..b2f0eec1 100644 --- a/core/mcp/tools/task-create-bulk/script.ps1 +++ b/core/mcp/tools/task-create-bulk/script.ps1 @@ -37,12 +37,12 @@ function Invoke-TaskCreateBulk { } # Initialize task index - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir $index = Get-TaskIndex # Define tasks directory - $tasksDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\todo" + $tasksDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/todo" # Ensure directory exists if (-not (Test-Path $tasksDir)) { diff --git a/core/mcp/tools/task-create/script.ps1 b/core/mcp/tools/task-create/script.ps1 index ac85c8ff..309aa3d0 100644 --- a/core/mcp/tools/task-create/script.ps1 +++ b/core/mcp/tools/task-create/script.ps1 @@ -96,7 +96,7 @@ function Invoke-TaskCreate { } # Initialize index - $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" + $tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir $index = Get-TaskIndex @@ -189,7 +189,7 @@ function Invoke-TaskCreate { } # Define file path - $tasksDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\todo" + $tasksDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/todo" # Ensure directory exists if (-not (Test-Path $tasksDir)) { diff --git a/core/mcp/tools/task-get-context/script.ps1 b/core/mcp/tools/task-get-context/script.ps1 index 7f8dd55c..9bc62d61 100644 --- a/core/mcp/tools/task-get-context/script.ps1 +++ b/core/mcp/tools/task-get-context/script.ps1 @@ -66,7 +66,7 @@ function Invoke-TaskGetContext { $decisionContent = @() $decisionIds = @($taskContent.applicable_decisions | Where-Object { $_ -match '^dec-[a-f0-9]{8}$' }) if (-not $hasEmbeddedDecisions -and $decisionIds.Count -gt 0) { - $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\decisions" + $decisionsBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/decisions" $decisionStatuses = @('accepted', 'proposed', 'deprecated', 'superseded') foreach ($decId in $decisionIds) { $decFound = $false diff --git a/core/mcp/tools/task-get-next/script.ps1 b/core/mcp/tools/task-get-next/script.ps1 index 7149bea1..9f795e6f 100644 --- a/core/mcp/tools/task-get-next/script.ps1 +++ b/core/mcp/tools/task-get-next/script.ps1 @@ -23,7 +23,7 @@ if (-not (Get-Command Test-ManifestCondition -ErrorAction SilentlyContinue)) { } # Initialize index on first use -$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" +$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir function Invoke-TaskGetNext { diff --git a/core/mcp/tools/task-get-stats/script.ps1 b/core/mcp/tools/task-get-stats/script.ps1 index 90526099..41d2ba1d 100644 --- a/core/mcp/tools/task-get-stats/script.ps1 +++ b/core/mcp/tools/task-get-stats/script.ps1 @@ -5,7 +5,7 @@ if (-not (Get-Module TaskIndexCache)) { } # Initialize index on first use -$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" +$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir function Invoke-TaskGetStats { diff --git a/core/mcp/tools/task-list/script.ps1 b/core/mcp/tools/task-list/script.ps1 index c4764105..74240365 100644 --- a/core/mcp/tools/task-list/script.ps1 +++ b/core/mcp/tools/task-list/script.ps1 @@ -5,7 +5,7 @@ if (-not (Get-Module TaskIndexCache)) { } # Initialize index on first use -$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" +$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir function Invoke-TaskList { diff --git a/core/mcp/tools/task-mark-analysed/script.ps1 b/core/mcp/tools/task-mark-analysed/script.ps1 index 84fabfd4..6614ebe2 100644 --- a/core/mcp/tools/task-mark-analysed/script.ps1 +++ b/core/mcp/tools/task-mark-analysed/script.ps1 @@ -9,7 +9,7 @@ function Get-AnalysisActivityLog { [string]$TaskId ) - $controlDir = Join-Path $global:DotbotProjectRoot ".bot\.control" + $controlDir = Join-Path $global:DotbotProjectRoot ".bot/.control" $activityFile = Join-Path $controlDir "activity.jsonl" if (-not (Test-Path $activityFile)) { return @() } diff --git a/core/mcp/tools/task-mark-done/script.ps1 b/core/mcp/tools/task-mark-done/script.ps1 index 6865a37b..4d914882 100644 --- a/core/mcp/tools/task-mark-done/script.ps1 +++ b/core/mcp/tools/task-mark-done/script.ps1 @@ -13,7 +13,7 @@ function Write-TaskMarkDoneFailure { ) try { - $controlDir = Join-Path $global:DotbotProjectRoot ".bot\.control" + $controlDir = Join-Path $global:DotbotProjectRoot ".bot/.control" $activityFile = Join-Path $controlDir "activity.jsonl" if (-not (Test-Path $controlDir)) { return } @@ -49,7 +49,7 @@ function Get-ExecutionActivityLog { [string]$ProjectRoot ) - $controlDir = Join-Path $global:DotbotProjectRoot ".bot\.control" + $controlDir = Join-Path $global:DotbotProjectRoot ".bot/.control" $activityFile = Join-Path $controlDir "activity.jsonl" if (-not (Test-Path $activityFile)) { return @() } @@ -77,7 +77,7 @@ function Invoke-VerificationScripts { [string]$ProjectRoot ) - $scriptsDir = Join-Path $global:DotbotProjectRoot ".bot\hooks\verify" + $scriptsDir = Join-Path $global:DotbotProjectRoot ".bot/hooks/verify" $configPath = Join-Path $scriptsDir "config.json" if (-not (Test-Path $configPath)) { diff --git a/core/mcp/tools/task-mark-done/test.ps1 b/core/mcp/tools/task-mark-done/test.ps1 index 5a7510a5..d71430cf 100644 --- a/core/mcp/tools/task-mark-done/test.ps1 +++ b/core/mcp/tools/task-mark-done/test.ps1 @@ -10,7 +10,7 @@ Reset-TestResults $cleanupFiles = @() # Disable verification hooks (they require a git remote which test projects lack) -$verifyConfigPath = Join-Path $global:DotbotProjectRoot ".bot\hooks\verify\config.json" +$verifyConfigPath = Join-Path $global:DotbotProjectRoot ".bot/hooks/verify/config.json" $verifyBackup = $null if (Test-Path $verifyConfigPath) { $verifyBackup = Get-Content $verifyConfigPath -Raw @@ -41,7 +41,7 @@ try { -Expected 'done' ` -Actual $result.new_status - $doneDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\done" + $doneDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/done" $doneFile = Get-ChildItem -Path $doneDir -Filter "*.json" -ErrorAction SilentlyContinue | Where-Object { (Get-Content $_.FullName -Raw | ConvertFrom-Json).id -eq $created.task_id } diff --git a/core/mcp/tools/task-mark-skipped/test.ps1 b/core/mcp/tools/task-mark-skipped/test.ps1 index 43210df1..d7fa2127 100644 --- a/core/mcp/tools/task-mark-skipped/test.ps1 +++ b/core/mcp/tools/task-mark-skipped/test.ps1 @@ -36,7 +36,7 @@ try { -Actual $result.skip_count # Verify file in skipped/ - $skippedDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks\skipped" + $skippedDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks/skipped" $skippedFile = Get-ChildItem -Path $skippedDir -Filter "*.json" -ErrorAction SilentlyContinue | Where-Object { (Get-Content $_.FullName -Raw | ConvertFrom-Json).id -eq $created.task_id } diff --git a/core/runtime/ProviderCLI/ProviderCLI.psm1 b/core/runtime/ProviderCLI/ProviderCLI.psm1 index 223552d3..dbb3a087 100644 --- a/core/runtime/ProviderCLI/ProviderCLI.psm1 +++ b/core/runtime/ProviderCLI/ProviderCLI.psm1 @@ -22,7 +22,7 @@ if (-not (Get-Module ClaudeCLI)) { } if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../modules/SettingsLoader.psm1") -DisableNameChecking -Global } #region Provider Config diff --git a/core/runtime/expand-task-groups.ps1 b/core/runtime/expand-task-groups.ps1 index f47509fb..f9450498 100644 --- a/core/runtime/expand-task-groups.ps1 +++ b/core/runtime/expand-task-groups.ps1 @@ -48,16 +48,16 @@ Import-Module "$BotRoot/core/runtime/ClaudeCLI/ClaudeCLI.psm1" -Force Import-Module "$BotRoot/core/runtime/ProviderCLI/ProviderCLI.psm1" -Force Import-Module "$BotRoot/core/runtime/modules/DotBotTheme.psm1" -Force -$productDir = Join-Path $BotRoot "workspace\product" -$todoDir = Join-Path $BotRoot "workspace\tasks\todo" +$productDir = Join-Path $BotRoot "workspace/product" +$todoDir = Join-Path $BotRoot "workspace/tasks/todo" # Resolve template: workflow-scoped install takes priority, fall back to global prompts dir $templatePath = $null if ($WorkflowDir) { - $candidate = Join-Path $WorkflowDir "recipes\prompts\03b-expand-task-group.md" + $candidate = Join-Path $WorkflowDir "recipes/prompts/03b-expand-task-group.md" if (Test-Path $candidate) { $templatePath = $candidate } } if (-not $templatePath) { - $templatePath = Join-Path $BotRoot "recipes\prompts\03b-expand-task-group.md" + $templatePath = Join-Path $BotRoot "recipes/prompts/03b-expand-task-group.md" } $groupsPath = Join-Path $productDir "task-groups.json" diff --git a/core/runtime/launch-process.ps1 b/core/runtime/launch-process.ps1 index 8752960c..d81fd8b9 100644 --- a/core/runtime/launch-process.ps1 +++ b/core/runtime/launch-process.ps1 @@ -146,7 +146,7 @@ if ($Type -eq 'task-runner') { . "$PSScriptRoot\modules\test-task-completion.ps1" # MCP tool functions — load ALL tools dynamically (includes workflow-specific ones) - $mcpToolsDir = Join-Path $PSScriptRoot "..\mcp\tools" + $mcpToolsDir = Join-Path $PSScriptRoot "../mcp/tools" Get-ChildItem -Path $mcpToolsDir -Directory | ForEach-Object { $toolScript = Join-Path $_.FullName "script.ps1" if (Test-Path $toolScript) { . $toolScript } @@ -157,7 +157,7 @@ if ($Type -eq 'task-runner') { if (-not (Get-Module SettingsLoader)) { Import-Module "$PSScriptRoot\modules\SettingsLoader.psm1" -DisableNameChecking -Global } -$settingsPath = Join-Path $botRoot "settings\settings.default.json" +$settingsPath = Join-Path $botRoot "settings/settings.default.json" $settings = Get-MergedSettings -BotRoot $botRoot if (-not $settings.PSObject.Properties['execution']) { $settings | Add-Member -NotePropertyName execution -NotePropertyValue ([pscustomobject]@{ model = 'Opus' }) -Force @@ -187,7 +187,7 @@ if (-not $instanceId) { # Override model selections from UI settings (ui-settings.json) $uiSettings = $null -$uiSettingsPath = Join-Path $botRoot ".control\ui-settings.json" +$uiSettingsPath = Join-Path $botRoot ".control/ui-settings.json" if (Test-Path $uiSettingsPath) { try { $uiSettings = Get-Content $uiSettingsPath -Raw | ConvertFrom-Json diff --git a/core/runtime/modules/DotBotTheme.psm1 b/core/runtime/modules/DotBotTheme.psm1 index 961b89bb..8e39c786 100644 --- a/core/runtime/modules/DotBotTheme.psm1 +++ b/core/runtime/modules/DotBotTheme.psm1 @@ -10,7 +10,7 @@ $script:UiSettingsPath = $null # Helper function to get the selected theme name from ui-settings.json function Get-SelectedThemeName { if (-not $script:UiSettingsPath) { - $script:UiSettingsPath = Join-Path $PSScriptRoot "..\..\..\.control\ui-settings.json" + $script:UiSettingsPath = Join-Path $PSScriptRoot "../../../.control/ui-settings.json" # Normalize path (handle relative traversal) $script:UiSettingsPath = [System.IO.Path]::GetFullPath($script:UiSettingsPath) } @@ -248,7 +248,7 @@ function Update-DotBotTheme { # Ensure settings path is initialized if (-not $script:UiSettingsPath) { - $script:UiSettingsPath = Join-Path $PSScriptRoot "..\..\..\.control\ui-settings.json" + $script:UiSettingsPath = Join-Path $PSScriptRoot "../../../.control/ui-settings.json" $script:UiSettingsPath = [System.IO.Path]::GetFullPath($script:UiSettingsPath) } diff --git a/core/runtime/modules/InterviewLoop.ps1 b/core/runtime/modules/InterviewLoop.ps1 index fc85dc79..9cb2fa3c 100644 --- a/core/runtime/modules/InterviewLoop.ps1 +++ b/core/runtime/modules/InterviewLoop.ps1 @@ -24,7 +24,7 @@ function Invoke-InterviewLoop { $processData = $ProcessData # Load interview prompt template - $interviewWorkflowPath = Join-Path $BotRoot "recipes\prompts\00-interview.md" + $interviewWorkflowPath = Join-Path $BotRoot "recipes/prompts/00-interview.md" $interviewWorkflow = "" if (Test-Path $interviewWorkflowPath) { $interviewWorkflow = Get-Content $interviewWorkflowPath -Raw @@ -202,7 +202,7 @@ Review all context above. Decide whether to write clarification-questions.json ( $notif = $interviewNotifications[$qId] $resp = Get-TaskNotificationResponse -Notification $notif -Settings $interviewNotifSettings if ($resp) { - $attachDir = Join-Path $ProductDir "attachments\$qId" + $attachDir = Join-Path $ProductDir "attachments/$qId" $resolved = Resolve-NotificationAnswer -Response $resp -Settings $interviewNotifSettings -AttachDir $attachDir if ($resolved) { $teamsAnswers[$qId] = $resolved diff --git a/core/runtime/modules/ProcessTypes/Invoke-PromptProcess.ps1 b/core/runtime/modules/ProcessTypes/Invoke-PromptProcess.ps1 index e2a93421..4d97e3ea 100644 --- a/core/runtime/modules/ProcessTypes/Invoke-PromptProcess.ps1 +++ b/core/runtime/modules/ProcessTypes/Invoke-PromptProcess.ps1 @@ -26,9 +26,9 @@ $permissionMode = $Context.PermissionMode # Determine workflow template $workflowFile = switch ($Type) { - 'planning' { Join-Path $botRoot "recipes\prompts\03-plan-roadmap.md" } - 'commit' { Join-Path $botRoot "recipes\prompts\90-commit-and-push.md" } - 'task-creation' { Join-Path $botRoot "recipes\prompts\91-new-tasks.md" } + 'planning' { Join-Path $botRoot "recipes/prompts/03-plan-roadmap.md" } + 'commit' { Join-Path $botRoot "recipes/prompts/90-commit-and-push.md" } + 'task-creation' { Join-Path $botRoot "recipes/prompts/91-new-tasks.md" } } $processData.workflow = switch ($Type) { diff --git a/core/runtime/modules/ProcessTypes/Invoke-WorkflowProcess.ps1 b/core/runtime/modules/ProcessTypes/Invoke-WorkflowProcess.ps1 index 70346a73..d5ae04d3 100644 --- a/core/runtime/modules/ProcessTypes/Invoke-WorkflowProcess.ps1 +++ b/core/runtime/modules/ProcessTypes/Invoke-WorkflowProcess.ps1 @@ -54,7 +54,7 @@ function Resolve-TaskScriptArgument { if ($params.ContainsKey('Settings')) { $built['Settings'] = $Settings } if ($params.ContainsKey('Model') -and $ClaudeModelName) { $built['Model'] = $ClaudeModelName } if ($params.ContainsKey('WorkflowDir') -and $WorkflowName) { - $wfDir = Join-Path $BotRoot "workflows\$WorkflowName" + $wfDir = Join-Path $BotRoot "workflows/$WorkflowName" if (Test-Path $wfDir) { $built['WorkflowDir'] = $wfDir } } } catch { @@ -63,7 +63,7 @@ function Resolve-TaskScriptArgument { # unconditionally, skip Settings so unprepared scripts don't fail. if ($ClaudeModelName) { $built['Model'] = $ClaudeModelName } if ($WorkflowName) { - $wfDir = Join-Path $BotRoot "workflows\$WorkflowName" + $wfDir = Join-Path $BotRoot "workflows/$WorkflowName" if (Test-Path $wfDir) { $built['WorkflowDir'] = $wfDir } } } @@ -492,7 +492,7 @@ $processData.workflow = "workflow (analyse + execute)" $standardsList = "" $productMission = "" $entityModel = "" -$standardsDir = Join-Path $botRoot "recipes\standards\global" +$standardsDir = Join-Path $botRoot "recipes/standards/global" if (Test-Path $standardsDir) { $standardsFiles = Get-ChildItem -Path $standardsDir -Filter "*.md" -File | ForEach-Object { ".bot/recipes/standards/global/$($_.Name)" } @@ -771,7 +771,7 @@ try { # Resolve prompt template from workflow dir or .bot/ $promptBase = $botRoot if ($task.workflow) { - $wfPromptBase = Join-Path $botRoot "workflows\$($task.workflow)" + $wfPromptBase = Join-Path $botRoot "workflows/$($task.workflow)" if (Test-Path $wfPromptBase) { $promptBase = $wfPromptBase } } $templatePath = Join-Path $promptBase $task.prompt @@ -792,7 +792,7 @@ try { if (-not (Get-Command Read-WorkflowManifest -ErrorAction SilentlyContinue)) { . (Join-Path $botRoot "core/runtime/modules/workflow-manifest.ps1") } - $wfTaskDir = Join-Path $botRoot "workflows\$($task.workflow)" + $wfTaskDir = Join-Path $botRoot "workflows/$($task.workflow)" if (Test-ValidWorkflowDir -Dir $wfTaskDir) { $wfManifest = Read-WorkflowManifest -WorkflowDir $wfTaskDir $matchingPhase = $wfManifest.tasks | Where-Object { $_['name'] -eq $task.name } | Select-Object -First 1 @@ -824,7 +824,7 @@ try { # Resolve script base: workflow dir → core/runtime/ → .bot/ $scriptBase = $botRoot if ($task.workflow) { - $wfScriptBase = Join-Path $botRoot "workflows\$($task.workflow)" + $wfScriptBase = Join-Path $botRoot "workflows/$($task.workflow)" if (Test-Path $wfScriptBase) { $scriptBase = $wfScriptBase } } diff --git a/core/runtime/modules/SettingsLoader.psm1 b/core/runtime/modules/SettingsLoader.psm1 index 4fd81273..4b3d2509 100644 --- a/core/runtime/modules/SettingsLoader.psm1 +++ b/core/runtime/modules/SettingsLoader.psm1 @@ -87,9 +87,9 @@ function Get-MergedSettings { ) $layerFiles = @( - (Join-Path $BotRoot "settings\settings.default.json"), + (Join-Path $BotRoot "settings/settings.default.json"), (Join-Path $HOME "dotbot" "user-settings.json"), - (Join-Path $BotRoot ".control\settings.json") + (Join-Path $BotRoot ".control/settings.json") ) $merged = $null diff --git a/core/runtime/modules/WorktreeManager.psm1 b/core/runtime/modules/WorktreeManager.psm1 index a661d073..beb1f72b 100644 --- a/core/runtime/modules/WorktreeManager.psm1 +++ b/core/runtime/modules/WorktreeManager.psm1 @@ -23,7 +23,7 @@ Shared infrastructure via directory links (junctions on Windows, symlinks on mac .bot/settings/ -> settings defaults #> -Import-Module (Join-Path $PSScriptRoot "..\..\mcp\modules\TaskStore.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "../../mcp/modules/TaskStore.psm1") -Force # --- Internal State --- $script:WorktreeMapPath = $null @@ -385,13 +385,13 @@ function Remove-Junctions { ) $junctionPaths = @( - (Join-Path $WorktreePath ".bot\.control"), - (Join-Path $WorktreePath ".bot\workspace\tasks"), - (Join-Path $WorktreePath ".bot\workspace\product"), - (Join-Path $WorktreePath ".bot\hooks"), - (Join-Path $WorktreePath ".bot\systems"), - (Join-Path $WorktreePath ".bot\recipes"), - (Join-Path $WorktreePath ".bot\settings") + (Join-Path $WorktreePath ".bot/.control"), + (Join-Path $WorktreePath ".bot/workspace/tasks"), + (Join-Path $WorktreePath ".bot/workspace/product"), + (Join-Path $WorktreePath ".bot/hooks"), + (Join-Path $WorktreePath ".bot/systems"), + (Join-Path $WorktreePath ".bot/recipes"), + (Join-Path $WorktreePath ".bot/settings") ) $failures = @() foreach ($jp in $junctionPaths) { @@ -455,7 +455,7 @@ function New-TaskWorktree { # Worktree path: {repo-parent}/worktrees/{repo-name}/task-{shortId}-{slug}/ $repoParent = Split-Path $ProjectRoot -Parent $repoName = Split-Path $ProjectRoot -Leaf - $worktreeDir = Join-Path $repoParent "worktrees\$repoName" + $worktreeDir = Join-Path $repoParent "worktrees/$repoName" $worktreePath = Join-Path $worktreeDir "task-$shortId-$slug" if (-not (Test-Path $worktreeDir)) { @@ -522,7 +522,7 @@ function New-TaskWorktree { # macOS/Linux: symbolic links # 1. .bot/.control/ — gitignored, won't exist in worktree - $worktreeControlDir = Join-Path $worktreePath ".bot\.control" + $worktreeControlDir = Join-Path $worktreePath ".bot/.control" $mainControlDir = Join-Path $BotRoot ".control" if (-not (Test-Path $worktreeControlDir)) { $controlParent = Split-Path $worktreeControlDir -Parent @@ -533,8 +533,8 @@ function New-TaskWorktree { } # 2. .bot/workspace/tasks/ — has tracked .gitkeep files, replace with junction - $worktreeTasksDir = Join-Path $worktreePath ".bot\workspace\tasks" - $mainTasksDir = Join-Path $BotRoot "workspace\tasks" + $worktreeTasksDir = Join-Path $worktreePath ".bot/workspace/tasks" + $mainTasksDir = Join-Path $BotRoot "workspace/tasks" if (Test-Path $worktreeTasksDir) { Assert-PathWithinBounds -Path $worktreeTasksDir -ExpectedRoot $worktreePath Remove-Item -Path $worktreeTasksDir -Recurse -Force @@ -546,36 +546,36 @@ function New-TaskWorktree { New-DirectoryLink -Path $worktreeTasksDir -Target $mainTasksDir # 3. .bot/hooks/ — verify scripts, commit-bot-state, dev lifecycle - $worktreeHooksDir = Join-Path $worktreePath ".bot\hooks" + $worktreeHooksDir = Join-Path $worktreePath ".bot/hooks" $mainHooksDir = Join-Path $BotRoot "hooks" if ((Test-Path $mainHooksDir) -and -not (Test-Path $worktreeHooksDir)) { New-DirectoryLink -Path $worktreeHooksDir -Target $mainHooksDir } # 4. .bot/systems/ — MCP server, runtime, UI - $worktreeSystemsDir = Join-Path $worktreePath ".bot\systems" + $worktreeSystemsDir = Join-Path $worktreePath ".bot/systems" $mainSystemsDir = Join-Path $BotRoot "systems" if ((Test-Path $mainSystemsDir) -and -not (Test-Path $worktreeSystemsDir)) { New-DirectoryLink -Path $worktreeSystemsDir -Target $mainSystemsDir } # 5. .bot/recipes/ — recipes, research methodologies, standards - $worktreeRecipesDir = Join-Path $worktreePath ".bot\recipes" + $worktreeRecipesDir = Join-Path $worktreePath ".bot/recipes" $mainRecipesDir = Join-Path $BotRoot "recipes" if ((Test-Path $mainRecipesDir) -and -not (Test-Path $worktreeRecipesDir)) { New-DirectoryLink -Path $worktreeRecipesDir -Target $mainRecipesDir } # 6. .bot/settings/ — settings defaults - $worktreeSettingsDir = Join-Path $worktreePath ".bot\settings" + $worktreeSettingsDir = Join-Path $worktreePath ".bot/settings" $mainSettingsDir = Join-Path $BotRoot "settings" if ((Test-Path $mainSettingsDir) -and -not (Test-Path $worktreeSettingsDir)) { New-DirectoryLink -Path $worktreeSettingsDir -Target $mainSettingsDir } # 7. .bot/workspace/product/ — shared research outputs and briefing - $worktreeProductDir = Join-Path $worktreePath ".bot\workspace\product" - $mainProductDir = Join-Path $BotRoot "workspace\product" + $worktreeProductDir = Join-Path $worktreePath ".bot/workspace/product" + $mainProductDir = Join-Path $BotRoot "workspace/product" if (Test-Path $mainProductDir) { if (Test-Path $worktreeProductDir) { Assert-PathWithinBounds -Path $worktreeProductDir -ExpectedRoot $worktreePath @@ -704,7 +704,7 @@ function Complete-TaskWorktree { # Backup live task state before merge (concurrent processes may have written via junctions) $taskBackup = @{} foreach ($subDir in @('todo','analysing','analysed','needs-input','in-progress','done','skipped','split','cancelled')) { - $backupDir = Join-Path $ProjectRoot ".bot\workspace\tasks\$subDir" + $backupDir = Join-Path $ProjectRoot ".bot/workspace/tasks/$subDir" $backupFiles = Get-ChildItem $backupDir -Filter "*.json" -File -ErrorAction SilentlyContinue foreach ($bf in $backupFiles) { try { @@ -728,7 +728,7 @@ function Complete-TaskWorktree { if ($LASTEXITCODE -ne 0) { if ($wasStashed) { git -C $ProjectRoot stash pop 2>$null } foreach ($key in $taskBackup.Keys) { - $restorePath = Join-Path $ProjectRoot ".bot\workspace\tasks\$key" + $restorePath = Join-Path $ProjectRoot ".bot/workspace/tasks/$key" $restoreDir = Split-Path $restorePath -Parent if (-not (Test-Path $restoreDir)) { New-Item $restoreDir -ItemType Directory -Force | Out-Null } $taskBackup[$key] | Set-Content $restorePath -Encoding UTF8 @@ -752,7 +752,7 @@ function Complete-TaskWorktree { } # Restore backed-up task state after failed merge foreach ($key in $taskBackup.Keys) { - $restorePath = Join-Path $ProjectRoot ".bot\workspace\tasks\$key" + $restorePath = Join-Path $ProjectRoot ".bot/workspace/tasks/$key" $restoreDir = Split-Path $restorePath -Parent if (-not (Test-Path $restoreDir)) { New-Item $restoreDir -ItemType Directory -Force | Out-Null } $taskBackup[$key] | Set-Content $restorePath -Encoding UTF8 @@ -768,7 +768,7 @@ function Complete-TaskWorktree { # Discard branch's task state, restore live state from backup git -C $ProjectRoot checkout HEAD -- .bot/workspace/tasks/ 2>$null foreach ($key in $taskBackup.Keys) { - $restorePath = Join-Path $ProjectRoot ".bot\workspace\tasks\$key" + $restorePath = Join-Path $ProjectRoot ".bot/workspace/tasks/$key" $restoreDir = Split-Path $restorePath -Parent if (-not (Test-Path $restoreDir)) { New-Item $restoreDir -ItemType Directory -Force | Out-Null } $taskBackup[$key] | Set-Content $restorePath -Encoding UTF8 @@ -778,7 +778,7 @@ function Complete-TaskWorktree { # The branch may carry stale copies of tasks that moved while the branch was alive # (e.g., a task split from todo→split while this branch still had the todo copy). foreach ($subDir in @('todo','analysing','analysed','needs-input','in-progress','done','skipped','split','cancelled')) { - $dir = Join-Path $ProjectRoot ".bot\workspace\tasks\$subDir" + $dir = Join-Path $ProjectRoot ".bot/workspace/tasks/$subDir" Get-ChildItem $dir -Filter "*.json" -File -ErrorAction SilentlyContinue | ForEach-Object { $key = "$subDir/$($_.Name)" if (-not $taskBackup.ContainsKey($key)) { @@ -797,7 +797,7 @@ function Complete-TaskWorktree { Assert-OnBaseBranch -ProjectRoot $ProjectRoot -BranchName $baseBranch | Out-Null if ($wasStashed) { git -C $ProjectRoot stash pop 2>$null } foreach ($key in $taskBackup.Keys) { - $restorePath = Join-Path $ProjectRoot ".bot\workspace\tasks\$key" + $restorePath = Join-Path $ProjectRoot ".bot/workspace/tasks/$key" $restoreDir = Split-Path $restorePath -Parent if (-not (Test-Path $restoreDir)) { New-Item $restoreDir -ItemType Directory -Force | Out-Null } $taskBackup[$key] | Set-Content $restorePath -Encoding UTF8 @@ -817,8 +817,8 @@ function Complete-TaskWorktree { # and done/, the non-terminal copy is stale and must be removed before committing. # This is a defensive measure against any mechanism that reintroduces stale files # (stash pop, junction race conditions, Reset function edge cases). - $doneDir = Join-Path $ProjectRoot ".bot\workspace\tasks\done" - $todoDir = Join-Path $ProjectRoot ".bot\workspace\tasks\todo" + $doneDir = Join-Path $ProjectRoot ".bot/workspace/tasks/done" + $todoDir = Join-Path $ProjectRoot ".bot/workspace/tasks/todo" if ((Test-Path $doneDir) -and (Test-Path $todoDir)) { $doneFileNames = @{} Get-ChildItem $doneDir -Filter "*.json" -File -ErrorAction SilentlyContinue | ForEach-Object { @@ -830,7 +830,7 @@ function Complete-TaskWorktree { } } foreach ($intermediateDir in @('analysing', 'analysed', 'in-progress', 'needs-input')) { - $dirPath = Join-Path $ProjectRoot ".bot\workspace\tasks\$intermediateDir" + $dirPath = Join-Path $ProjectRoot ".bot/workspace/tasks/$intermediateDir" if (Test-Path $dirPath) { Get-ChildItem $dirPath -Filter "*.json" -File -ErrorAction SilentlyContinue | ForEach-Object { if ($doneFileNames.ContainsKey($_.Name)) { @@ -1040,7 +1040,7 @@ function Remove-OrphanWorktrees { $map = Read-WorktreeMap if ($map.Count -eq 0) { return } - $tasksBaseDir = Join-Path $BotRoot "workspace\tasks" + $tasksBaseDir = Join-Path $BotRoot "workspace/tasks" # 'done' is included: tasks that just completed execution may still have a live worktree # pending squash-merge by Complete-TaskWorktree. Removing them here would race with that. $activeDirs = @('todo', 'analysing', 'needs-input', 'analysed', 'in-progress', 'done') diff --git a/core/runtime/modules/cleanup.ps1 b/core/runtime/modules/cleanup.ps1 index 4bfdd0d5..8207a018 100644 --- a/core/runtime/modules/cleanup.ps1 +++ b/core/runtime/modules/cleanup.ps1 @@ -70,7 +70,7 @@ function Remove-ProviderSession { $providerName = 'claude' try { if (-not (Get-Module ProviderCLI)) { - Import-Module (Join-Path $PSScriptRoot '..\ProviderCLI\ProviderCLI.psm1') -Force + Import-Module (Join-Path $PSScriptRoot '../ProviderCLI/ProviderCLI.psm1') -Force } $config = Get-ProviderConfig $providerName = $config.name diff --git a/core/runtime/modules/task-reset.ps1 b/core/runtime/modules/task-reset.ps1 index b493ee08..6654582f 100644 --- a/core/runtime/modules/task-reset.ps1 +++ b/core/runtime/modules/task-reset.ps1 @@ -136,7 +136,7 @@ function Reset-SkippedTasks { # dot-sourcing this script, so the function is in scope. Defensive import # guard for direct/test callers that load task-reset.ps1 in isolation. if (-not (Get-Command Test-IsFrameworkErrorSkip -ErrorAction SilentlyContinue)) { - $taskIndexModule = Join-Path $PSScriptRoot "..\..\mcp\modules\TaskIndexCache.psm1" + $taskIndexModule = Join-Path $PSScriptRoot "../../mcp/modules/TaskIndexCache.psm1" if (Test-Path $taskIndexModule) { Import-Module $taskIndexModule -DisableNameChecking } diff --git a/core/runtime/modules/test-task-completion.ps1 b/core/runtime/modules/test-task-completion.ps1 index 28098dc3..4f67510b 100644 --- a/core/runtime/modules/test-task-completion.ps1 +++ b/core/runtime/modules/test-task-completion.ps1 @@ -1,11 +1,11 @@ # Import task index module -$indexModule = Join-Path $PSScriptRoot "..\..\mcp\modules\TaskIndexCache.psm1" +$indexModule = Join-Path $PSScriptRoot "../../mcp/modules/TaskIndexCache.psm1" if (-not (Get-Module TaskIndexCache)) { Import-Module $indexModule -Force } # Initialize index on first use -$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks" +$tasksBaseDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/tasks" Initialize-TaskIndex -TasksBaseDir $tasksBaseDir function Test-TaskCompletion { diff --git a/core/runtime/modules/workflow-manifest.ps1 b/core/runtime/modules/workflow-manifest.ps1 index 591f1104..4c77ba01 100644 --- a/core/runtime/modules/workflow-manifest.ps1 +++ b/core/runtime/modules/workflow-manifest.ps1 @@ -339,7 +339,7 @@ function New-WorkflowTask { [string]$Effort = "XS" ) - $tasksDir = Join-Path $ProjectBotDir "workspace\tasks\todo" + $tasksDir = Join-Path $ProjectBotDir "workspace/tasks/todo" if (-not (Test-Path $tasksDir)) { New-Item -Path $tasksDir -ItemType Directory -Force | Out-Null } $id = [System.Guid]::NewGuid().ToString() diff --git a/core/ui/modules/ControlAPI.psm1 b/core/ui/modules/ControlAPI.psm1 index ac7824da..d2454c59 100644 --- a/core/ui/modules/ControlAPI.psm1 +++ b/core/ui/modules/ControlAPI.psm1 @@ -8,7 +8,7 @@ operator whisper channel, and activity log tail streaming. Extracted from server.ps1 for modularity. #> -Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\ConsoleSequenceSanitizer.psm1") +Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/ConsoleSequenceSanitizer.psm1") function Update-ActivityEventFields { param( @@ -196,11 +196,11 @@ function Set-ControlSignal { if (Test-Path $pauseSignal) { Remove-Item $pauseSignal -Force } # Clear session lock - $lockFile = Join-Path $botRoot "workspace\sessions\runs\session.lock" + $lockFile = Join-Path $botRoot "workspace/sessions/runs/session.lock" if (Test-Path $lockFile) { Remove-Item $lockFile -Force } # Update session state to stopped - $stateFile = Join-Path $botRoot "workspace\sessions\runs\session-state.json" + $stateFile = Join-Path $botRoot "workspace/sessions/runs/session-state.json" if (Test-Path $stateFile) { $state = Get-Content $stateFile -Raw | ConvertFrom-Json $state.status = "stopped" @@ -274,7 +274,7 @@ function Get-ActivityTail { [int]$TailLines = 0 ) $botRoot = $script:Config.BotRoot - $logPath = Join-Path $botRoot ".control\activity.jsonl" + $logPath = Join-Path $botRoot ".control/activity.jsonl" if (-not (Test-Path $logPath)) { return @{ events = @(); position = 0 } diff --git a/core/ui/modules/DecisionAPI.psm1 b/core/ui/modules/DecisionAPI.psm1 index 73f139fe..002726e7 100644 --- a/core/ui/modules/DecisionAPI.psm1 +++ b/core/ui/modules/DecisionAPI.psm1 @@ -19,7 +19,7 @@ function Initialize-DecisionAPI { } function Get-DecisionsBaseDir { - return (Join-Path $script:Config.BotRoot "workspace\decisions") + return (Join-Path $script:Config.BotRoot "workspace/decisions") } function Test-DecisionIdFormat([string]$Id) { diff --git a/core/ui/modules/FileWatcher.psm1 b/core/ui/modules/FileWatcher.psm1 index eb948665..2cfaabf7 100644 --- a/core/ui/modules/FileWatcher.psm1 +++ b/core/ui/modules/FileWatcher.psm1 @@ -31,9 +31,9 @@ function Initialize-FileWatchers { # Watch tasks directories $tasksDirs = @( - (Join-Path $BotRoot "workspace\tasks\todo"), - (Join-Path $BotRoot "workspace\tasks\in-progress"), - (Join-Path $BotRoot "workspace\tasks\done") + (Join-Path $BotRoot "workspace/tasks/todo"), + (Join-Path $BotRoot "workspace/tasks/in-progress"), + (Join-Path $BotRoot "workspace/tasks/done") ) foreach ($dir in $tasksDirs) { @@ -76,7 +76,7 @@ function Initialize-FileWatchers { } # Watch product docs directory - $productDir = Join-Path $BotRoot "workspace\product" + $productDir = Join-Path $BotRoot "workspace/product" if (-not (Test-Path $productDir)) { New-Item -Path $productDir -ItemType Directory -Force | Out-Null } @@ -113,7 +113,7 @@ function Initialize-FileWatchers { } # Watch session state file - $sessionsDir = Join-Path $BotRoot "workspace\sessions\runs" + $sessionsDir = Join-Path $BotRoot "workspace/sessions/runs" if (-not (Test-Path $sessionsDir)) { New-Item -Path $sessionsDir -ItemType Directory -Force | Out-Null } diff --git a/core/ui/modules/InboxWatcher.psm1 b/core/ui/modules/InboxWatcher.psm1 index a955487c..8282e5ce 100644 --- a/core/ui/modules/InboxWatcher.psm1 +++ b/core/ui/modules/InboxWatcher.psm1 @@ -14,7 +14,7 @@ entirely — no PS event system, no $script: scope issues, no silent failures. #> if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } # Module-scope state diff --git a/core/ui/modules/NotificationPoller.psm1 b/core/ui/modules/NotificationPoller.psm1 index 428d23be..b292ae23 100644 --- a/core/ui/modules/NotificationPoller.psm1 +++ b/core/ui/modules/NotificationPoller.psm1 @@ -86,7 +86,7 @@ function Invoke-NotificationPollTick { $botRoot = if ($BotRoot) { $BotRoot } else { $script:pollerBotRoot } if (-not $botRoot) { return } - $needsInputDir = Join-Path $botRoot "workspace\tasks\needs-input" + $needsInputDir = Join-Path $botRoot "workspace/tasks/needs-input" if (-not (Test-Path $needsInputDir)) { return } # Ensure notification client is loaded @@ -149,7 +149,7 @@ function Invoke-NotificationPollTick { # Question response: resolve answer and transition $taskId = $taskContent.id $questionId = $taskContent.pending_question.id - $attachDir = Join-Path $botRoot "workspace\attachments\$taskId\$questionId" + $attachDir = Join-Path $botRoot "workspace/attachments/$taskId/$questionId" $resolved = Resolve-NotificationAnswer -Response $response -Settings $settings -AttachDir $attachDir if ($resolved) { @@ -179,7 +179,7 @@ function Invoke-NotificationPollTick { $response = Get-TaskNotificationResponse -Notification $notifEntry -Settings $settings if (-not $response) { continue } - $attachDir = Join-Path $botRoot "workspace\attachments\$taskId\$($pq.id)" + $attachDir = Join-Path $botRoot "workspace/attachments/$taskId/$($pq.id)" $resolved = Resolve-NotificationAnswer -Response $response -Settings $settings -AttachDir $attachDir if (-not $resolved) { continue } @@ -226,7 +226,7 @@ function Invoke-TaskTransitionFromNotification { [array]$Attachments = @() ) - $tasksBaseDir = Join-Path $BotRoot "workspace\tasks" + $tasksBaseDir = Join-Path $BotRoot "workspace/tasks" $analysingDir = Join-Path $tasksBaseDir "analysing" $pendingQuestion = $TaskContent.pending_question @@ -427,7 +427,7 @@ function Invoke-BatchQuestionTransitionFromNotification { [array]$Attachments = @() ) - $tasksBaseDir = Join-Path $BotRoot "workspace\tasks" + $tasksBaseDir = Join-Path $BotRoot "workspace/tasks" $now = (Get-Date).ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'") # Resolve option key to label (same logic as Invoke-TaskTransitionFromNotification) diff --git a/core/ui/modules/ProcessAPI.psm1 b/core/ui/modules/ProcessAPI.psm1 index 6542a4e0..a9a0fcfd 100644 --- a/core/ui/modules/ProcessAPI.psm1 +++ b/core/ui/modules/ProcessAPI.psm1 @@ -14,9 +14,9 @@ $script:Config = @{ ControlDir = $null } -Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\ConsoleSequenceSanitizer.psm1") +Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/ConsoleSequenceSanitizer.psm1") if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } function Update-ActivityEventFields { diff --git a/core/ui/modules/ProductAPI.psm1 b/core/ui/modules/ProductAPI.psm1 index 379a9dfd..0181e483 100644 --- a/core/ui/modules/ProductAPI.psm1 +++ b/core/ui/modules/ProductAPI.psm1 @@ -9,7 +9,7 @@ Extracted from server.ps1 for modularity. #> if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } $script:Config = @{ @@ -172,7 +172,7 @@ function Resolve-ProductDocumentPath { function Get-ProductList { $botRoot = $script:Config.BotRoot - $productDir = Join-Path $botRoot "workspace\product" + $productDir = Join-Path $botRoot "workspace/product" $docs = @() if (Test-Path $productDir) { @@ -261,7 +261,7 @@ function Get-ProductDocument { [Parameter(Mandatory)] [string]$Name ) $botRoot = $script:Config.BotRoot - $productDir = Join-Path $botRoot "workspace\product" + $productDir = Join-Path $botRoot "workspace/product" $resolvedDoc = Resolve-ProductDocumentPath -Name $Name -ProductDir $productDir if ($resolvedDoc -and (Test-Path -LiteralPath $resolvedDoc.FullPath)) { @@ -285,7 +285,7 @@ function Get-ProductDocumentRaw { [Parameter(Mandatory)] [string]$Name ) $botRoot = $script:Config.BotRoot - $productDir = Join-Path $botRoot "workspace\product" + $productDir = Join-Path $botRoot "workspace/product" $resolvedDoc = Resolve-ProductDocumentPath -Name $Name -ProductDir $productDir if (-not $resolvedDoc -or -not (Test-Path -LiteralPath $resolvedDoc.FullPath)) { @@ -419,7 +419,7 @@ function Start-RoadmapPlanning { $botRoot = $script:Config.BotRoot # Validate product docs exist - $productDir = Join-Path $botRoot "workspace\product" + $productDir = Join-Path $botRoot "workspace/product" $requiredDocs = @("mission.md", "tech-stack.md", "entity-model.md") $missingDocs = @() foreach ($doc in $requiredDocs) { @@ -456,7 +456,7 @@ function Resolve-PhaseStatusFromOutputs { [Parameter(Mandatory)] [object]$Phase, [Parameter(Mandatory)] [string]$BotRoot ) - $productDir = Join-Path $BotRoot "workspace\product" + $productDir = Join-Path $BotRoot "workspace/product" $phaseType = if ($Phase.type) { $Phase.type } else { "llm" } # If the phase has a condition, check it first — unmet means it can't have run @@ -491,13 +491,13 @@ function Resolve-PhaseStatusFromOutputs { } if ($Phase.required_outputs_dir) { - $dirPath = Join-Path $BotRoot "workspace\$($Phase.required_outputs_dir)" + $dirPath = Join-Path $BotRoot "workspace/$($Phase.required_outputs_dir)" $minCount = if ($Phase.min_output_count) { [int]$Phase.min_output_count } else { 1 } $fileCount = if (Test-Path $dirPath) { @(Get-ChildItem $dirPath -Filter "*.json" -File).Count } else { 0 } if ($fileCount -ge $minCount) { return "completed" } # Tasks may have moved through the pipeline (todo → done) if ($Phase.required_outputs_dir -match '^tasks/') { - $taskBaseDir = Join-Path $BotRoot "workspace\tasks" + $taskBaseDir = Join-Path $BotRoot "workspace/tasks" $totalTasks = 0 foreach ($td in @("todo","analysing","analysed","in-progress","done","skipped","cancelled")) { $tdPath = Join-Path $taskBaseDir $td @@ -526,12 +526,12 @@ function Resolve-PhaseStatusFromOutputs { # Check outputs_dir (manifest-style field name) if ($Phase.outputs_dir) { - $dirPath = Join-Path $BotRoot "workspace\$($Phase.outputs_dir)" + $dirPath = Join-Path $BotRoot "workspace/$($Phase.outputs_dir)" $minCount = if ($Phase.min_output_count) { [int]$Phase.min_output_count } else { 1 } $fileCount = if (Test-Path $dirPath) { @(Get-ChildItem $dirPath -Filter "*.json" -File).Count } else { 0 } if ($fileCount -ge $minCount) { return "completed" } if ($Phase.outputs_dir -match '^tasks/') { - $taskBaseDir = Join-Path $BotRoot "workspace\tasks" + $taskBaseDir = Join-Path $BotRoot "workspace/tasks" $totalTasks = 0 # Canonical task-pipeline status dirs. Keep in sync with the list # in the script-phase probe below and with workflow-manifest.ps1 @@ -614,7 +614,7 @@ function Resolve-TaskGenChildTasks { if (-not $hasTaskGen) { return $Phases } # Collect all tasks from every status directory - $taskBaseDir = Join-Path $BotRoot "workspace\tasks" + $taskBaseDir = Join-Path $BotRoot "workspace/tasks" $statusDirs = @('todo', 'analysing', 'needs-input', 'analysed', 'in-progress', 'done', 'skipped', 'cancelled') $statusMap = @{ 'todo' = 'todo'; 'analysing' = 'analysing'; 'needs-input' = 'needs-input' diff --git a/core/ui/modules/ReferenceCache.psm1 b/core/ui/modules/ReferenceCache.psm1 index 00c3cb7b..9cdab0d4 100644 --- a/core/ui/modules/ReferenceCache.psm1 +++ b/core/ui/modules/ReferenceCache.psm1 @@ -194,7 +194,7 @@ function Build-ReferenceCache { # First pass: collect all files foreach ($dir in $dirs) { - $dirPath = Join-Path $botRoot "recipes\$dir" + $dirPath = Join-Path $botRoot "recipes/$dir" if (Test-Path $dirPath) { $mdFiles = Get-ChildItem -Path $dirPath -Filter "*.md" -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '\\archived\\' } @@ -338,7 +338,7 @@ function Get-FileWithReferences { $lastUnderscore = $Type.LastIndexOf('_') $wfName = $Type.Substring(0, $lastUnderscore) $subType = $Type.Substring($lastUnderscore + 1) - $wfPromptsDir = Join-Path $botRoot "workflows\$wfName\recipes" + $wfPromptsDir = Join-Path $botRoot "workflows/$wfName/recipes" if (Test-Path $wfPromptsDir) { $wfDirs = Get-ChildItem -Path $wfPromptsDir -Directory foreach ($dir in $wfDirs) { @@ -360,9 +360,9 @@ function Get-FileWithReferences { # Resolve the actual filesystem path if ($matchingDir -match '^__wf__(.+)/(.+)$') { - $targetDir = Join-Path $botRoot "workflows\$($Matches[1])\recipes\$($Matches[2])" + $targetDir = Join-Path $botRoot "workflows/$($Matches[1])/recipes/$($Matches[2])" } else { - $targetDir = Join-Path $botRoot "recipes\$matchingDir" + $targetDir = Join-Path $botRoot "recipes/$matchingDir" } $filePath = Join-Path $targetDir $Filename diff --git a/core/ui/modules/SettingsAPI.psm1 b/core/ui/modules/SettingsAPI.psm1 index 323a3707..d3eb993a 100644 --- a/core/ui/modules/SettingsAPI.psm1 +++ b/core/ui/modules/SettingsAPI.psm1 @@ -8,7 +8,7 @@ Extracted from server.ps1 for modularity. #> if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } $script:Config = @{ @@ -333,7 +333,7 @@ function Set-AnalysisConfig { } function Get-VerificationConfig { - $verifyConfigFile = Join-Path $script:Config.BotRoot "hooks\verify\config.json" + $verifyConfigFile = Join-Path $script:Config.BotRoot "hooks/verify/config.json" try { return Get-Content $verifyConfigFile -Raw | ConvertFrom-Json @@ -346,7 +346,7 @@ function Set-VerificationConfig { param( [Parameter(Mandatory)] $Body ) - $verifyConfigFile = Join-Path $script:Config.BotRoot "hooks\verify\config.json" + $verifyConfigFile = Join-Path $script:Config.BotRoot "hooks/verify/config.json" $verifyData = Get-Content $verifyConfigFile -Raw | ConvertFrom-Json $scriptName = $Body.name @@ -629,7 +629,7 @@ function Get-ProviderProbe { } function Get-ProviderList { - $providersDir = Join-Path $script:Config.BotRoot "settings\providers" + $providersDir = Join-Path $script:Config.BotRoot "settings/providers" try { # Read active provider and permission mode from the merged settings chain @@ -732,7 +732,7 @@ function Set-ActiveProvider { param( [Parameter(Mandatory)] $Body ) - $providersDir = Join-Path $script:Config.BotRoot "settings\providers" + $providersDir = Join-Path $script:Config.BotRoot "settings/providers" $providerName = $Body.provider if (-not $providerName) { diff --git a/core/ui/modules/StateBuilder.psm1 b/core/ui/modules/StateBuilder.psm1 index 5bd26bd1..c4c56b6b 100644 --- a/core/ui/modules/StateBuilder.psm1 +++ b/core/ui/modules/StateBuilder.psm1 @@ -15,8 +15,8 @@ $script:Config = @{ ProcessesDir = $null } -Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\ConsoleSequenceSanitizer.psm1") -Import-Module (Join-Path $PSScriptRoot "..\..\mcp\modules\TaskMutation.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/ConsoleSequenceSanitizer.psm1") +Import-Module (Join-Path $PSScriptRoot "../../mcp/modules/TaskMutation.psm1") -Force function Initialize-StateBuilder { param( @@ -81,7 +81,7 @@ function Get-BotState { } # Build fresh state - $tasksDir = Join-Path $botRoot "workspace\tasks" + $tasksDir = Join-Path $botRoot "workspace/tasks" $roadmapDependencyMap = Get-RoadmapOverviewDependencyMap -TasksBaseDir $tasksDir # Count tasks (including new analysis statuses) @@ -618,7 +618,7 @@ function Get-BotState { # Read workspace instance ID from settings.default.json $workspaceInstanceId = $null - $settingsPath = Join-Path $botRoot "settings\settings.default.json" + $settingsPath = Join-Path $botRoot "settings/settings.default.json" if (Test-Path $settingsPath) { try { $settingsJson = Get-Content $settingsPath -Raw | ConvertFrom-Json @@ -639,7 +639,7 @@ function Get-BotState { } # Count decisions by status - $decisionsBaseDir = Join-Path $botRoot "workspace\decisions" + $decisionsBaseDir = Join-Path $botRoot "workspace/decisions" $decisionCounts = @{ proposed = 0; accepted = 0; deprecated = 0; superseded = 0; total = 0 } foreach ($decStatus in @('proposed', 'accepted', 'deprecated', 'superseded')) { $decDir = Join-Path $decisionsBaseDir $decStatus @@ -688,7 +688,7 @@ function Get-BotState { } instances = $instances steering = $steeringStatus - product_docs = @(Get-ChildItem -Path (Join-Path $botRoot "workspace\product") -Filter "*.md" -File -Recurse -ErrorAction SilentlyContinue).Count + product_docs = @(Get-ChildItem -Path (Join-Path $botRoot "workspace/product") -Filter "*.md" -File -Recurse -ErrorAction SilentlyContinue).Count workflows = $workflowCounts } diff --git a/core/ui/modules/TaskAPI.psm1 b/core/ui/modules/TaskAPI.psm1 index 72b95d48..c9dec5cd 100644 --- a/core/ui/modules/TaskAPI.psm1 +++ b/core/ui/modules/TaskAPI.psm1 @@ -9,7 +9,7 @@ Extracted from server.ps1 for modularity. #> if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $PSScriptRoot "..\..\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $PSScriptRoot "../../runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } $script:Config = @{ @@ -33,7 +33,7 @@ function Initialize-TaskAPI { } function Get-TasksBaseDir { - return (Join-Path $script:Config.BotRoot "workspace\tasks") + return (Join-Path $script:Config.BotRoot "workspace/tasks") } function Import-TaskMutationModule { @@ -225,7 +225,7 @@ function Get-TaskPlan { $projectRoot = $script:Config.ProjectRoot # Search for task file by ID - $tasksDir = Join-Path $botRoot "workspace\tasks" + $tasksDir = Join-Path $botRoot "workspace/tasks" $statusDirs = @('todo', 'in-progress', 'done', 'skipped', 'cancelled') $task = $null @@ -286,7 +286,7 @@ function Get-TaskPlan { function Get-ActionRequired { $botRoot = $script:Config.BotRoot - $tasksDir = Join-Path $botRoot "workspace\tasks" + $tasksDir = Join-Path $botRoot "workspace/tasks" $actionItems = @() # Get needs-input tasks (questions) @@ -327,7 +327,7 @@ function Get-ActionRequired { } # Scan processes for workflow-launch interview questions (needs-input status) - $processesDir = Join-Path $botRoot ".control\processes" + $processesDir = Join-Path $botRoot ".control/processes" if (Test-Path $processesDir) { $procFiles = Get-ChildItem -Path $processesDir -Filter "proc-*.json" -File -ErrorAction SilentlyContinue foreach ($pf in $procFiles) { @@ -372,7 +372,7 @@ function Submit-TaskAnswer { # placement and the answer submission — not only when attachments are present. $resolvedQuestionId = $QuestionId if (-not $resolvedQuestionId) { - $needsInputDir = Join-Path $script:Config.BotRoot "workspace\tasks\needs-input" + $needsInputDir = Join-Path $script:Config.BotRoot "workspace/tasks/needs-input" $taskFilePath = Get-ChildItem -Path $needsInputDir -Filter "*.json" -ErrorAction SilentlyContinue | Where-Object { (Get-Content $_.FullName -Raw | ConvertFrom-Json).id -eq $TaskId } | Select-Object -First 1 -ExpandProperty FullName @@ -394,7 +394,7 @@ function Submit-TaskAnswer { if (-not $resolvedQuestionId) { Write-DotbotWarning "Skipping attachments for task '$TaskId': no pending question could be resolved" } else { - $attachDir = Join-Path $script:Config.BotRoot "workspace\attachments\$TaskId\$resolvedQuestionId" + $attachDir = Join-Path $script:Config.BotRoot "workspace/attachments/$TaskId/$resolvedQuestionId" if (-not (Test-Path $attachDir)) { New-Item -ItemType Directory -Force -Path $attachDir | Out-Null } diff --git a/core/ui/server.ps1 b/core/ui/server.ps1 index 68942e27..11465428 100644 --- a/core/ui/server.ps1 +++ b/core/ui/server.ps1 @@ -121,7 +121,7 @@ $processesDir = Join-Path $controlDir "processes" if (-not (Test-Path $processesDir)) { New-Item -Path $processesDir -ItemType Directory -Force | Out-Null } # Import FileWatcher module for event-driven state updates -Import-Module (Join-Path $PSScriptRoot "modules\FileWatcher.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/FileWatcher.psm1") -Force $settingsLoaderModule = Join-Path $botRoot "core/runtime/modules/SettingsLoader.psm1" Import-Module $settingsLoaderModule -Force -DisableNameChecking -Global @@ -130,19 +130,19 @@ if (-not (Get-Command Get-MergedSettings -ErrorAction SilentlyContinue)) { } # Import domain modules -Import-Module (Join-Path $PSScriptRoot "modules\GitAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\AetherAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\ReferenceCache.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\SettingsAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\ControlAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\ProductAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/GitAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/AetherAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/ReferenceCache.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/SettingsAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/ControlAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/ProductAPI.psm1") -Force # TaskAPI intentionally exports Delete-RoadmapTask for UI/back-compat, so disable verb-name warnings here. -Import-Module (Join-Path $PSScriptRoot "modules\TaskAPI.psm1") -Force -DisableNameChecking -Import-Module (Join-Path $PSScriptRoot "modules\ProcessAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\StateBuilder.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\NotificationPoller.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\DecisionAPI.psm1") -Force -Import-Module (Join-Path $PSScriptRoot "modules\InboxWatcher.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/TaskAPI.psm1") -Force -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "modules/ProcessAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/StateBuilder.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/NotificationPoller.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/DecisionAPI.psm1") -Force +Import-Module (Join-Path $PSScriptRoot "modules/InboxWatcher.psm1") -Force # Import workflow manifest utilities (for installed workflows API) . (Join-Path $botRoot "core/runtime/modules/workflow-manifest.ps1") @@ -235,7 +235,7 @@ try { function Get-BotDirectoryList { param([string]$Directory) - $dirPath = Join-Path $botRoot "recipes\$Directory" + $dirPath = Join-Path $botRoot "recipes/$Directory" $groups = [System.Collections.Generic.Dictionary[string, System.Collections.ArrayList]]::new() if (Test-Path $dirPath) { @@ -449,7 +449,7 @@ function Get-ProjectInfoPayload { # Executive summary — scan priority product docs first, then any remaining $executiveSummary = $null - $productDir = Join-Path $BotRoot "workspace\product" + $productDir = Join-Path $BotRoot "workspace/product" if (Test-Path $productDir) { $priorityFiles = @('overview.md', 'mission.md', 'roadmap.md', 'roadmap-overview.md') $allFiles = @(Get-ChildItem -Path $productDir -Filter "*.md" -ErrorAction SilentlyContinue) @@ -1696,7 +1696,7 @@ $docContext } else { # Save any per-question attachment files and replace base64 with paths $allowedAttachExtensions = @('.md', '.docx', '.xlsx', '.pdf', '.txt', '.png', '.jpg', '.jpeg') - $productDir = Join-Path $botRoot "workspace\product" + $productDir = Join-Path $botRoot "workspace/product" $processedAnswers = @() foreach ($ans in @($body.answers)) { $ansObj = @{ @@ -1706,7 +1706,7 @@ $docContext } if ($ans.attachments -and @($ans.attachments).Count -gt 0) { $attachMeta = @() - $attachDir = Join-Path $productDir "attachments\$($ans.question_id)" + $attachDir = Join-Path $productDir "attachments/$($ans.question_id)" if (-not (Test-Path $attachDir)) { New-Item -ItemType Directory -Force -Path $attachDir | Out-Null } @@ -1834,7 +1834,7 @@ $docContext $workflowsDir = Join-Path $botRoot "workflows" $installedList = @() - $tasksDir = Join-Path $botRoot "workspace\tasks" + $tasksDir = Join-Path $botRoot "workspace/tasks" # --- Helper: cached manifest read (mtime-based) --- function Get-CachedManifest { @@ -1932,11 +1932,11 @@ $docContext homepage = if ($manifest['homepage']) { "$($manifest['homepage'])" } else { '' } agents = if ($manifest['agents'] -and $manifest['agents'].Count -gt 0) { @($manifest['agents'] | Where-Object { $_ }) } else { # Fallback: discover from prompts directory - $wfAgentsDir = Join-Path $wfDir "recipes\agents" + $wfAgentsDir = Join-Path $wfDir "recipes/agents" if (Test-Path $wfAgentsDir) { @(Get-ChildItem $wfAgentsDir -Directory -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }) } else { @() } } skills = if ($manifest['skills'] -and $manifest['skills'].Count -gt 0) { @($manifest['skills'] | Where-Object { $_ }) } else { - $wfSkillsDir = Join-Path $wfDir "recipes\skills" + $wfSkillsDir = Join-Path $wfDir "recipes/skills" if (Test-Path $wfSkillsDir) { @(Get-ChildItem $wfSkillsDir -Directory -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }) } else { @() } } tools = if ($manifest['tools'] -and $manifest['tools'].Count -gt 0) { @($manifest['tools'] | Where-Object { $_ }) } else { @() } @@ -1998,7 +1998,7 @@ $docContext } # Installed workflows live at .bot/workflows/{name}/. - $wfDir = Join-Path $botRoot "workflows\$wfName" + $wfDir = Join-Path $botRoot "workflows/$wfName" if (-not (Test-ValidWorkflowDir -Dir $wfDir)) { $statusCode = 404 @@ -2037,7 +2037,7 @@ $docContext break } # Installed workflows live at .bot/workflows/{name}/. - $wfDir = Join-Path $botRoot "workflows\$wfName" + $wfDir = Join-Path $botRoot "workflows/$wfName" if (-not (Test-ValidWorkflowDir -Dir $wfDir)) { $statusCode = 404 @@ -2062,7 +2062,7 @@ $docContext # Save briefing files if provided $failedFiles = 0 if ($body -and $body.files) { - $briefingDir = Join-Path $botRoot "workspace\product\briefing" + $briefingDir = Join-Path $botRoot "workspace/product/briefing" if (-not (Test-Path $briefingDir)) { New-Item -Path $briefingDir -ItemType Directory -Force | Out-Null } @@ -2088,7 +2088,7 @@ $docContext # Save user prompt for task prompt injection (canonical path used by ProductAPI/runtime) if ($body -and $body.prompt) { - $launchersDir = Join-Path $botRoot ".control\launchers" + $launchersDir = Join-Path $botRoot ".control/launchers" if (-not (Test-Path $launchersDir)) { New-Item -Path $launchersDir -ItemType Directory -Force | Out-Null } @@ -2099,7 +2099,7 @@ $docContext # Clear tasks if rerun: fresh if ($manifest.rerun -eq 'fresh') { - $tasksBaseDir = Join-Path $botRoot "workspace\tasks" + $tasksBaseDir = Join-Path $botRoot "workspace/tasks" if (Test-Path $tasksBaseDir) { Clear-WorkflowTasks -TasksBaseDir $tasksBaseDir -WorkflowName $wfName } @@ -2332,7 +2332,7 @@ $docContext } else { $wfName = "unknown"; $subDir = "unknown" } - $wfPromptDir = Join-Path $botRoot "workflows\$wfName\recipes\$subDir" + $wfPromptDir = Join-Path $botRoot "workflows/$wfName/recipes/$subDir" if (Test-Path $wfPromptDir) { # Reuse same grouping logic as Get-BotDirectoryList but from workflow path $groups = [System.Collections.Generic.Dictionary[string, System.Collections.ArrayList]]::new() @@ -2366,7 +2366,7 @@ $docContext } else { $dirName = "unknown" } - $dirPath = Join-Path $botRoot "recipes\$dirName" + $dirPath = Join-Path $botRoot "recipes/$dirName" if (Test-Path $dirPath) { $content = Get-BotDirectoryList -Directory $dirName diff --git a/scripts/doctor.ps1 b/scripts/doctor.ps1 index 0011e982..2cad5c58 100644 --- a/scripts/doctor.ps1 +++ b/scripts/doctor.ps1 @@ -102,7 +102,7 @@ Write-BlankLine Write-DotbotSection -Title "SETTINGS" -$settingsPath = Join-Path $BotRoot "settings\settings.default.json" +$settingsPath = Join-Path $BotRoot "settings/settings.default.json" if (Test-Path $settingsPath) { try { $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json @@ -119,7 +119,7 @@ if (Test-Path $settingsPath) { } # Theme config -$themeDefault = Join-Path $BotRoot "settings\theme.default.json" +$themeDefault = Join-Path $BotRoot "settings/theme.default.json" if (Test-Path $themeDefault) { try { Get-Content $themeDefault -Raw | ConvertFrom-Json | Out-Null @@ -205,7 +205,7 @@ Write-BlankLine Write-DotbotSection -Title "TASK QUEUE" -$tasksDir = Join-Path $BotRoot "workspace\tasks" +$tasksDir = Join-Path $BotRoot "workspace/tasks" if (Test-Path $tasksDir) { $badJson = 0 $missingId = 0 diff --git a/scripts/init-project.ps1 b/scripts/init-project.ps1 index 5d029193..2dd737a0 100644 --- a/scripts/init-project.ps1 +++ b/scripts/init-project.ps1 @@ -196,8 +196,8 @@ function Invoke-BotFolderMigration { if ((Test-Path $old) -and -not (Test-Path $new)) { Rename-Item $old $new } # prompts/workflows/ → prompts/_prompts_tmp, then prompts/ → recipes/, then rename inner - $oldInner = Join-Path $Dir "prompts\workflows" - $newInner = Join-Path $Dir "prompts\_prompts_tmp" + $oldInner = Join-Path $Dir "prompts/workflows" + $newInner = Join-Path $Dir "prompts/_prompts_tmp" if ((Test-Path $oldInner) -and -not (Test-Path $newInner)) { Rename-Item $oldInner $newInner } $oldOuter = Join-Path $Dir "prompts" $newOuter = Join-Path $Dir "recipes" @@ -209,8 +209,8 @@ function Invoke-BotFolderMigration { } # workspace/adrs/ → workspace/decisions/ - $oldAdrs = Join-Path $Dir "workspace\adrs" - $newDec = Join-Path $Dir "workspace\decisions" + $oldAdrs = Join-Path $Dir "workspace/adrs" + $newDec = Join-Path $Dir "workspace/decisions" if ((Test-Path $oldAdrs) -and -not (Test-Path $newDec)) { Rename-Item $oldAdrs $newDec } # PR-5: legacy workflows/default residue at .bot/ root. Pre-PR-5 installs @@ -244,7 +244,7 @@ if (Test-Path $BotDir) { $existingInstanceId = $null if ((Test-Path $BotDir) -and $Force) { # Preserve instance_id before replacing settings/ - $existingSettingsPath = Join-Path $BotDir "settings\settings.default.json" + $existingSettingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $existingSettingsPath) { try { $existingSettings = Get-Content $existingSettingsPath -Raw | ConvertFrom-Json @@ -411,7 +411,7 @@ if ($Workflow) { if ($wfName -match '^([^:]+):(.+)$') { $namespace = $Matches[1] $wfShortName = $Matches[2] - $candidate = Join-Path $RegistriesDir "$namespace\workflows\$wfShortName" + $candidate = Join-Path $RegistriesDir "$namespace/workflows/$wfShortName" if (Test-Path $candidate) { $wfSourceDir = $candidate } $displayName = $wfShortName } else { @@ -531,7 +531,7 @@ if ($Workflow) { } # Record installed workflows in core settings - $settingsPath = Join-Path $BotDir "settings\settings.default.json" + $settingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $settingsPath) { $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json $settings | Add-Member -NotePropertyName "installed_workflows" -NotePropertyValue $installedWorkflows -Force @@ -598,7 +598,7 @@ function Resolve-StackDir { $namespace = $Matches[1] $stackName = $Matches[2] $RegistriesDir = Join-Path $DotbotBase "registries" - $candidate = Join-Path $RegistriesDir "$namespace\stacks\$stackName" + $candidate = Join-Path $RegistriesDir "$namespace/stacks/$stackName" if (Test-Path $candidate) { return $candidate } return $null } @@ -620,7 +620,7 @@ if ($Workflow) { $wfDir = $null if ($Workflow -match '^([^:]+):(.+)$') { $ns = $Matches[1]; $wfShort = $Matches[2] - $candidate = Join-Path (Join-Path $DotbotBase "registries") "$ns\workflows\$wfShort" + $candidate = Join-Path (Join-Path $DotbotBase "registries") "$ns/workflows/$wfShort" if (Test-Path $candidate) { $wfDir = $candidate } } else { $candidate = Join-Path $WorkflowsDir $Workflow @@ -815,7 +815,7 @@ foreach ($entryName in $resolvedOrder) { } if ($wfManifest -and $wfManifest.domain -and $wfManifest.domain['task_categories']) { $wfCategories = @($wfManifest.domain['task_categories']) - $settingsFile = Join-Path $BotDir "settings\settings.default.json" + $settingsFile = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $settingsFile) { $sObj = Get-Content $settingsFile -Raw | ConvertFrom-Json $currentCategories = @() @@ -840,7 +840,7 @@ foreach ($entryName in $resolvedOrder) { # --- Record workflow + stacks in settings --- if ($resolvedOrder.Count -gt 0) { - $settingsPath = Join-Path $BotDir "settings\settings.default.json" + $settingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $settingsPath) { $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json if ($activeWorkflow) { @@ -854,7 +854,7 @@ if ($resolvedOrder.Count -gt 0) { } # Ensure workspace instance GUID exists (preserve on -Force re-init) -$workspaceSettingsPath = Join-Path $BotDir "settings\settings.default.json" +$workspaceSettingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $workspaceSettingsPath) { try { $settings = Get-Content $workspaceSettingsPath -Raw | ConvertFrom-Json diff --git a/scripts/registry-add.ps1 b/scripts/registry-add.ps1 index a4409fcb..d3e748f0 100644 --- a/scripts/registry-add.ps1 +++ b/scripts/registry-add.ps1 @@ -204,7 +204,7 @@ foreach ($type in $contentMap.Keys) { $dirPrefix = $contentTypeMap[$type] if (-not $dirPrefix) { $dirPrefix = $type } foreach ($item in $contentMap[$type]) { - $itemDir = Join-Path $RegistryPath "$dirPrefix\$item" + $itemDir = Join-Path $RegistryPath "$dirPrefix/$item" if (-not (Test-Path $itemDir)) { $missingDirs += "$dirPrefix/$item" } diff --git a/scripts/registry-list.ps1 b/scripts/registry-list.ps1 index cf595945..c8df5af2 100644 --- a/scripts/registry-list.ps1 +++ b/scripts/registry-list.ps1 @@ -132,7 +132,7 @@ foreach ($entry in $config.registries) { $items = $meta['content'][$type] if ($items -and $items.Count -gt 0) { foreach ($item in $items) { - $itemPath = Join-Path $registryPath "$type\$item" + $itemPath = Join-Path $registryPath "$type/$item" $exists = Test-Path $itemPath $icon = if ($exists) { "✓" } else { "?" } Write-Status "$icon ${name}:${item} ($type)" diff --git a/scripts/registry-update.ps1 b/scripts/registry-update.ps1 index 5ae53717..9165d60a 100644 --- a/scripts/registry-update.ps1 +++ b/scripts/registry-update.ps1 @@ -145,7 +145,7 @@ function Invoke-RegistryValidation { $missingDirs = @() foreach ($type in $contentMap.Keys) { foreach ($item in $contentMap[$type]) { - $itemDir = Join-Path $RegistryPath "$type\$item" + $itemDir = Join-Path $RegistryPath "$type/$item" if (-not (Test-Path $itemDir)) { $missingDirs += "$type/$item" } } } diff --git a/scripts/tasks-run.ps1 b/scripts/tasks-run.ps1 index 04d928a5..d2db4e84 100644 --- a/scripts/tasks-run.ps1 +++ b/scripts/tasks-run.ps1 @@ -16,7 +16,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force Import-Module (Join-Path $DotbotBase "core/runtime/modules/DotBotTheme.psm1") -Force -DisableNameChecking if (-not (Test-Path $BotDir)) { diff --git a/scripts/tasks-stop.ps1 b/scripts/tasks-stop.ps1 index 72b277cf..f99cc2a4 100644 --- a/scripts/tasks-stop.ps1 +++ b/scripts/tasks-stop.ps1 @@ -16,7 +16,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force Import-Module (Join-Path $DotbotBase "core/runtime/modules/DotBotTheme.psm1") -Force -DisableNameChecking if (-not (Test-Path $BotDir)) { @@ -24,7 +24,7 @@ if (-not (Test-Path $BotDir)) { exit 1 } -$processesDir = Join-Path $BotDir ".control\processes" +$processesDir = Join-Path $BotDir ".control/processes" if (-not (Test-Path $processesDir)) { Write-DotbotWarning "No process directory at $processesDir — nothing to stop." exit 0 diff --git a/scripts/workflow-add.ps1 b/scripts/workflow-add.ps1 index 462ea309..a08a24b5 100644 --- a/scripts/workflow-add.ps1 +++ b/scripts/workflow-add.ps1 @@ -21,7 +21,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force Import-Module (Join-Path $DotbotBase "core/runtime/modules/DotBotTheme.psm1") -Force -DisableNameChecking if (-not (Test-Path $BotDir)) { @@ -48,11 +48,11 @@ $wfSourceDir = $null if ($Name -match '^([^:]+):(.+)$') { $namespace = $Matches[1] $wfShortName = $Matches[2] - $candidate = Join-Path $DotbotBase "registries\$namespace\workflows\$wfShortName" + $candidate = Join-Path $DotbotBase "registries/$namespace/workflows/$wfShortName" if (Test-Path $candidate) { $wfSourceDir = $candidate } $displayName = $wfShortName } else { - $candidate = Join-Path $DotbotBase "workflows\$Name" + $candidate = Join-Path $DotbotBase "workflows/$Name" if (Test-Path $candidate) { $wfSourceDir = $candidate } $displayName = $Name } @@ -130,7 +130,7 @@ if ($manifest.mcp_servers) { } # Update installed_workflows list + merge domain.task_categories from manifest -$settingsPath = Join-Path $BotDir "settings\settings.default.json" +$settingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $settingsPath) { $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json $existing = @() diff --git a/scripts/workflow-list.ps1 b/scripts/workflow-list.ps1 index a52a3360..839adbe7 100644 --- a/scripts/workflow-list.ps1 +++ b/scripts/workflow-list.ps1 @@ -11,7 +11,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force if (-not (Test-Path $BotDir)) { Write-DotbotError "No .bot directory found. Run 'dotbot init' first." diff --git a/scripts/workflow-remove.ps1 b/scripts/workflow-remove.ps1 index 9f0138a4..124e7dbe 100644 --- a/scripts/workflow-remove.ps1 +++ b/scripts/workflow-remove.ps1 @@ -17,7 +17,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force Import-Module (Join-Path $DotbotBase "core/runtime/modules/DotBotTheme.psm1") -Force -DisableNameChecking if (-not (Test-Path $BotDir)) { @@ -33,7 +33,7 @@ if (-not $Name) { # Import manifest utilities . (Join-Path $BotDir "core/runtime/modules/workflow-manifest.ps1") -$wfDir = Join-Path $BotDir "workflows\$Name" +$wfDir = Join-Path $BotDir "workflows/$Name" if (-not (Test-Path $wfDir)) { Write-DotbotError "Workflow '$Name' is not installed." exit 1 @@ -42,7 +42,7 @@ if (-not (Test-Path $wfDir)) { Write-Status "Removing workflow: $Name" # Clear tasks belonging to this workflow -$tasksDir = Join-Path $BotDir "workspace\tasks" +$tasksDir = Join-Path $BotDir "workspace/tasks" $removed = Clear-WorkflowTasks -TasksBaseDir $tasksDir -WorkflowName $Name if ($removed -gt 0) { Write-DotbotCommand "Removed $removed task(s)" @@ -61,7 +61,7 @@ if ($orphansRemoved -gt 0) { } # Update installed_workflows list -$settingsPath = Join-Path $BotDir "settings\settings.default.json" +$settingsPath = Join-Path $BotDir "settings/settings.default.json" if (Test-Path $settingsPath) { $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json if ($settings.PSObject.Properties['installed_workflows']) { diff --git a/scripts/workflow-run.ps1 b/scripts/workflow-run.ps1 index e1261aab..38ed3c57 100644 --- a/scripts/workflow-run.ps1 +++ b/scripts/workflow-run.ps1 @@ -22,7 +22,7 @@ $DotbotBase = Join-Path $HOME "dotbot" $ProjectDir = Get-Location $BotDir = Join-Path $ProjectDir ".bot" -Import-Module (Join-Path $DotbotBase "scripts\Platform-Functions.psm1") -Force +Import-Module (Join-Path $DotbotBase "scripts/Platform-Functions.psm1") -Force Import-Module (Join-Path $DotbotBase "core/runtime/modules/DotBotTheme.psm1") -Force -DisableNameChecking if (-not (Test-Path $BotDir)) { @@ -33,7 +33,7 @@ if (-not (Test-Path $BotDir)) { # Import manifest utilities . (Join-Path $BotDir "core/runtime/modules/workflow-manifest.ps1") -$wfDir = Join-Path $BotDir "workflows\$WorkflowName" +$wfDir = Join-Path $BotDir "workflows/$WorkflowName" if (-not (Test-ValidWorkflowDir -Dir $wfDir)) { Write-DotbotError "Workflow '$WorkflowName' is not installed." Write-DotbotWarning "Installed workflows:" @@ -81,7 +81,7 @@ if ($manifest.requires -and $manifest.requires.env_vars) { } # --- Handle rerun --- -$tasksDir = Join-Path $BotDir "workspace\tasks" +$tasksDir = Join-Path $BotDir "workspace/tasks" $rerunMode = if ($manifest.rerun) { $manifest.rerun } else { "fresh" } # Check for existing tasks diff --git a/server/Send-DotbotQuestion.ps1 b/server/Send-DotbotQuestion.ps1 index a1ebeb18..ea0a7899 100644 --- a/server/Send-DotbotQuestion.ps1 +++ b/server/Send-DotbotQuestion.ps1 @@ -129,7 +129,7 @@ param( $ErrorActionPreference = 'Stop' # ── Load environment ───────────────────────────────────────────────────────── -. (Join-Path $PSScriptRoot 'scripts\Load-Env.ps1') +. (Join-Path $PSScriptRoot 'scripts/Load-Env.ps1') $headers = $dotbotHeaders if (-not $BotUrl) { diff --git a/server/scripts/Deploy.ps1 b/server/scripts/Deploy.ps1 index 26cdc099..445bf1be 100644 --- a/server/scripts/Deploy.ps1 +++ b/server/scripts/Deploy.ps1 @@ -35,7 +35,7 @@ param( [string]$ResourceGroup = "RG_WE_APPS_DOTBOT_TEST", [string]$AppName = "we-dotbot-bot-test-01", [switch]$WithTerraform, - [string]$TerraformDir = (Join-Path $PSScriptRoot "..\terraform"), + [string]$TerraformDir = (Join-Path $PSScriptRoot "../terraform"), [switch]$AutoApprove ) @@ -83,9 +83,9 @@ if ($WithTerraform) { } # ── Build & Deploy ────────────────────────────────────────────────────────── -$projectDir = Join-Path $PSScriptRoot "..\src\Dotbot.Server" -$publishDir = Join-Path $PSScriptRoot "..\publish" -$zipPath = Join-Path $PSScriptRoot "..\publish.zip" +$projectDir = Join-Path $PSScriptRoot "../src/Dotbot.Server" +$publishDir = Join-Path $PSScriptRoot "../publish" +$zipPath = Join-Path $PSScriptRoot "../publish.zip" Write-Host "Building Dotbot.Server..." -ForegroundColor Cyan dotnet publish $projectDir -c Release -o $publishDir --nologo diff --git a/server/scripts/Load-Env.ps1 b/server/scripts/Load-Env.ps1 index 40f61e2b..4f850999 100644 --- a/server/scripts/Load-Env.ps1 +++ b/server/scripts/Load-Env.ps1 @@ -6,7 +6,7 @@ and $dotbotHeaders (X-Api-Key header) ready to use. #> -$_envFile = Join-Path $PSScriptRoot '..\.env.local' +$_envFile = Join-Path $PSScriptRoot '../.env.local' if (-not (Test-Path $_envFile)) { Write-Host "Missing .env.local — copy .env.example to .env.local and set values" -ForegroundColor Red throw "File not found: $_envFile" diff --git a/server/scripts/Send-QuestionInstance.ps1 b/server/scripts/Send-QuestionInstance.ps1 index e0346fd8..a7bab477 100644 --- a/server/scripts/Send-QuestionInstance.ps1 +++ b/server/scripts/Send-QuestionInstance.ps1 @@ -23,7 +23,7 @@ if (-not $BotUrl) { } # ── Load question from SampleQuestions.json ─────────────────────────────── -$sampleFile = Join-Path $PSScriptRoot '..\SampleQuestions.json' +$sampleFile = Join-Path $PSScriptRoot '../SampleQuestions.json' $samples = Get-Content $sampleFile -Raw | ConvertFrom-Json if ($Question -lt 1 -or $Question -gt $samples.Count) { throw "Question must be between 1 and $($samples.Count). Got: $Question" diff --git a/server/scripts/Test-EndToEnd.ps1 b/server/scripts/Test-EndToEnd.ps1 index 804050e7..2adce506 100644 --- a/server/scripts/Test-EndToEnd.ps1 +++ b/server/scripts/Test-EndToEnd.ps1 @@ -75,7 +75,7 @@ catch { # ── 2. Publish template ───────────────────────────────────────────────────── Write-Host "[2/5] Publish template..." -ForegroundColor Cyan try { - $sampleFile = Join-Path $PSScriptRoot "..\SampleQuestions.json" + $sampleFile = Join-Path $PSScriptRoot "../SampleQuestions.json" $templates = Get-Content $sampleFile -Raw | ConvertFrom-Json $template = $templates[0] $projectId = $template.project.projectId diff --git a/server/scripts/create-icons.ps1 b/server/scripts/create-icons.ps1 index b08c8690..9c3b1aaf 100644 --- a/server/scripts/create-icons.ps1 +++ b/server/scripts/create-icons.ps1 @@ -1,6 +1,6 @@ Add-Type -AssemblyName System.Drawing -$teamsDir = Join-Path $PSScriptRoot "..\teams-app" +$teamsDir = Join-Path $PSScriptRoot "../teams-app" New-Item -ItemType Directory -Path $teamsDir -Force | Out-Null # Color icon (192x192) - blue background with white "D" diff --git a/server/scripts/publish-teams-app.ps1 b/server/scripts/publish-teams-app.ps1 index 2ee0900f..bea5fe67 100644 --- a/server/scripts/publish-teams-app.ps1 +++ b/server/scripts/publish-teams-app.ps1 @@ -1,6 +1,6 @@ $ErrorActionPreference = 'Stop' -$teamsAppDir = Join-Path $PSScriptRoot '..\teams-app' +$teamsAppDir = Join-Path $PSScriptRoot '../teams-app' $manifestFile = Join-Path $teamsAppDir 'manifest.json' $colorIcon = Join-Path $teamsAppDir 'color.png' $outlineIcon = Join-Path $teamsAppDir 'outline.png' diff --git a/server/scripts/resize-icon.ps1 b/server/scripts/resize-icon.ps1 index 091599da..6ae48f39 100644 --- a/server/scripts/resize-icon.ps1 +++ b/server/scripts/resize-icon.ps1 @@ -10,8 +10,8 @@ if (-not (Test-Path $SourceImage)) { Add-Type -AssemblyName System.Drawing -$colorPath = Join-Path $PSScriptRoot '..\teams-app\color.png' -$outlinePath = Join-Path $PSScriptRoot '..\teams-app\outline.png' +$colorPath = Join-Path $PSScriptRoot '../teams-app/color.png' +$outlinePath = Join-Path $PSScriptRoot '../teams-app/outline.png' $src = [System.Drawing.Image]::FromFile($SourceImage) diff --git a/stacks/dotnet/hooks/dev/Common.ps1 b/stacks/dotnet/hooks/dev/Common.ps1 index e560a71d..faf8a7a3 100644 --- a/stacks/dotnet/hooks/dev/Common.ps1 +++ b/stacks/dotnet/hooks/dev/Common.ps1 @@ -2,7 +2,7 @@ # Shared utilities for dev scripts # Import DotBotTheme for Write-Status and other theme helpers (deployed path) -$_dotBotTheme = Join-Path $PSScriptRoot "..\..\systems\runtime\modules\DotBotTheme.psm1" +$_dotBotTheme = Join-Path $PSScriptRoot "../../systems/runtime/modules/DotBotTheme.psm1" if (Test-Path $_dotBotTheme) { Import-Module $_dotBotTheme -Force -DisableNameChecking } diff --git a/stacks/dotnet/hooks/dev/Start-Dev.ps1 b/stacks/dotnet/hooks/dev/Start-Dev.ps1 index 8ce0fe9a..fc419732 100644 --- a/stacks/dotnet/hooks/dev/Start-Dev.ps1 +++ b/stacks/dotnet/hooks/dev/Start-Dev.ps1 @@ -133,7 +133,7 @@ dotnet watch --project $apiProjectRelPath Write-Status "API window opened (PID: $($apiProcess.Id))" -Type Success # Save PID for cleanup - $pidFile = Join-Path $repoRoot ".bot\.dev-pids.json" + $pidFile = Join-Path $repoRoot ".bot/.dev-pids.json" $pids = @{ api_pid = $apiProcess.Id started_at = (Get-Date).ToString('o') diff --git a/stacks/dotnet/hooks/dev/Stop-Dev.ps1 b/stacks/dotnet/hooks/dev/Stop-Dev.ps1 index 10a00f5e..4da76560 100644 --- a/stacks/dotnet/hooks/dev/Stop-Dev.ps1 +++ b/stacks/dotnet/hooks/dev/Stop-Dev.ps1 @@ -32,7 +32,7 @@ if (Test-Path $layoutConfigPath) { } # Read saved PIDs from Start-Dev.ps1 -$pidFile = Join-Path $repoRoot ".bot\.dev-pids.json" +$pidFile = Join-Path $repoRoot ".bot/.dev-pids.json" $savedPids = $null if (Test-Path $pidFile) { try { diff --git a/stacks/dotnet/hooks/scripts/migrate.ps1 b/stacks/dotnet/hooks/scripts/migrate.ps1 index 4d178eb6..76f68853 100644 --- a/stacks/dotnet/hooks/scripts/migrate.ps1 +++ b/stacks/dotnet/hooks/scripts/migrate.ps1 @@ -50,8 +50,8 @@ try { Write-Host "=== Flux Database Migration Tool ===" -ForegroundColor Cyan Write-Host "" - $apiProject = Join-Path $projectRoot "src\Flux.Api\Flux.Api.csproj" - $infraProject = Join-Path $projectRoot "src\Flux.Infrastructure\Flux.Infrastructure.csproj" + $apiProject = Join-Path $projectRoot "src/Flux.Api/Flux.Api.csproj" + $infraProject = Join-Path $projectRoot "src/Flux.Infrastructure/Flux.Infrastructure.csproj" if (-not (Test-Path $apiProject)) { throw "API project not found at: $apiProject" diff --git a/stacks/dotnet/systems/mcp/tools/dev-db/script.ps1 b/stacks/dotnet/systems/mcp/tools/dev-db/script.ps1 index 23382dd2..1608efeb 100644 --- a/stacks/dotnet/systems/mcp/tools/dev-db/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/dev-db/script.ps1 @@ -4,7 +4,7 @@ function Invoke-DevDb { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-DevDb { } # Check for Query-Db script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Query-Db.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Query-Db.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/stacks/dotnet/systems/mcp/tools/dev-deploy/script.ps1 b/stacks/dotnet/systems/mcp/tools/dev-deploy/script.ps1 index 0461a919..33cd2b51 100644 --- a/stacks/dotnet/systems/mcp/tools/dev-deploy/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/dev-deploy/script.ps1 @@ -4,7 +4,7 @@ function Invoke-DevDeploy { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-DevDeploy { } # Check for deploy script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Start-Deploy.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Start-Deploy.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/stacks/dotnet/systems/mcp/tools/dev-logs/script.ps1 b/stacks/dotnet/systems/mcp/tools/dev-logs/script.ps1 index 418d752e..85a3ee47 100644 --- a/stacks/dotnet/systems/mcp/tools/dev-logs/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/dev-logs/script.ps1 @@ -4,7 +4,7 @@ function Invoke-DevLogs { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-DevLogs { } # Check for View-Logs script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\View-Logs.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/View-Logs.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/stacks/dotnet/systems/mcp/tools/dev-release/script.ps1 b/stacks/dotnet/systems/mcp/tools/dev-release/script.ps1 index 5d7c11fd..595f451b 100644 --- a/stacks/dotnet/systems/mcp/tools/dev-release/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/dev-release/script.ps1 @@ -4,7 +4,7 @@ function Invoke-DevRelease { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-DevRelease { } # Check for release script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Release-DevToProd.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Release-DevToProd.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/stacks/dotnet/systems/mcp/tools/prod-start/script.ps1 b/stacks/dotnet/systems/mcp/tools/prod-start/script.ps1 index 94adf796..8b490ea7 100644 --- a/stacks/dotnet/systems/mcp/tools/prod-start/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/prod-start/script.ps1 @@ -4,7 +4,7 @@ function Invoke-ProdStart { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-ProdStart { } # Check for prod start script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Start-Prod.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Start-Prod.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/stacks/dotnet/systems/mcp/tools/prod-stop/script.ps1 b/stacks/dotnet/systems/mcp/tools/prod-stop/script.ps1 index 0c13eff5..229de933 100644 --- a/stacks/dotnet/systems/mcp/tools/prod-stop/script.ps1 +++ b/stacks/dotnet/systems/mcp/tools/prod-stop/script.ps1 @@ -4,7 +4,7 @@ function Invoke-ProdStop { ) # Import helpers - $coreHelpersPath = Join-Path $PSScriptRoot '..\..\core-helpers.psm1' + $coreHelpersPath = Join-Path $PSScriptRoot '../../core-helpers.psm1' Import-Module $coreHelpersPath -Force -DisableNameChecking -WarningAction SilentlyContinue $timer = Start-ToolTimer @@ -26,7 +26,7 @@ function Invoke-ProdStop { } # Check for prod stop script - $scriptPath = Join-Path $solutionRoot '.bot\hooks\dev\Stop-Prod.ps1' + $scriptPath = Join-Path $solutionRoot '.bot/hooks/dev/Stop-Prod.ps1' if (-not (Test-Path $scriptPath)) { $duration = Get-ToolDuration -Stopwatch $timer return New-EnvelopeResponse ` diff --git a/tests/Run-Tests.ps1 b/tests/Run-Tests.ps1 index b6b24bbc..73dbf62d 100644 --- a/tests/Run-Tests.ps1 +++ b/tests/Run-Tests.ps1 @@ -151,11 +151,12 @@ if (1 -in $layersToRun) { $workflowManifestCode = Invoke-TestFile -Layer '1' -FileName 'Test-WorkflowManifest.ps1' $mdRefsCode = Invoke-TestFile -Layer '1' -FileName 'Test-MdRefs.ps1' $legacyVocabularyCode = Invoke-TestFile -Layer '1' -FileName 'Test-NoLegacyVocabulary.ps1' + $backslashPathsCode = Invoke-TestFile -Layer '1' -FileName 'Test-NoBackslashPaths.ps1' $clarificationCode = Invoke-TestFile -Layer '1' -FileName 'Test-StartFromPromptClarification.ps1' $activityLogCode = Invoke-TestFile -Layer '1' -FileName 'Test-ActivityLogHygiene.ps1' $privacyScanCode = Invoke-TestFile -Layer '1' -FileName 'Test-PrivacyScan.ps1' - $exitCode = if ($structureCode -ne 0 -or $compilationCode -ne 0 -or $workflowManifestCode -ne 0 -or $mdRefsCode -ne 0 -or $legacyVocabularyCode -ne 0 -or $clarificationCode -ne 0 -or $activityLogCode -ne 0 -or $privacyScanCode -ne 0) { 1 } else { 0 } + $exitCode = if ($structureCode -ne 0 -or $compilationCode -ne 0 -or $workflowManifestCode -ne 0 -or $mdRefsCode -ne 0 -or $legacyVocabularyCode -ne 0 -or $backslashPathsCode -ne 0 -or $clarificationCode -ne 0 -or $activityLogCode -ne 0 -or $privacyScanCode -ne 0) { 1 } else { 0 } $layerResults["1"] = ($exitCode -eq 0) if ($exitCode -ne 0) { $overallFailed = $true } } diff --git a/tests/Test-NoBackslashPaths.ps1 b/tests/Test-NoBackslashPaths.ps1 new file mode 100644 index 00000000..972cffa6 --- /dev/null +++ b/tests/Test-NoBackslashPaths.ps1 @@ -0,0 +1,109 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Layer 1: Hard gate — fail if `Join-Path X "...\..."` patterns appear outside the allowlist. +.DESCRIPTION + PowerShell on Linux/macOS treats `\` as a literal character in path + strings, so `Join-Path $x "workspace\tasks"` produces nonexistent paths + like `/home/user/workspace\tasks` on Unix. Test-Path returns false against + those paths and surrounding code silently takes the "directory doesn't + exist" branch — runtime is broken in ways the cross-platform test suite + cannot catch. + + This test fails the build on any new violation. The fix pattern is a + forward-slash literal (`"workspace/tasks"`); for interpolated variables + that may contain `\`, normalise first via `-replace '\\', '/'` (canonical + example: `core/runtime/modules/post-script-runner.ps1`). + + Allowlist (paths matched against forward-slash-normalised relative paths + from the repo root): + + tests/ test fixtures intentionally use Windows-style strings +#> + +[CmdletBinding()] +param() + +$ErrorActionPreference = "Stop" + +Import-Module "$PSScriptRoot\Test-Helpers.psm1" -Force + +$repoRoot = Get-RepoRoot + +Write-Host "" +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Blue +Write-Host " Layer 1: No Backslash Paths in Join-Path (hard fail)" -ForegroundColor Blue +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Blue +Write-Host "" + +Reset-TestResults + +$allowlist = @( + 'tests/' +) + +# Two regexes catch both double- and single-quoted second-arg literals +# containing a backslash. `git grep -nIE` is fast and ignores binaries. +$patterns = @( + 'Join-Path[^"]*"[^"]*\\', + "Join-Path[^']*'[^']*\\" +) + +Push-Location $repoRoot +try { + $matches = New-Object System.Collections.Generic.List[string] + foreach ($pattern in $patterns) { + $hits = & git grep -nIE -e $pattern -- '*.ps1' '*.psm1' 2>$null + if ($hits) { foreach ($h in $hits) { [void]$matches.Add($h) } } + } +} finally { + Pop-Location +} + +if ($matches.Count -eq 0) { + Write-TestResult -Name "No Join-Path backslash literals found anywhere" -Status Pass + [void](Write-TestSummary -LayerName "Layer 1: No Backslash Paths") + exit 0 +} + +$unexpected = New-Object System.Collections.Generic.List[string] +$allowedHits = 0 + +foreach ($line in $matches) { + if ($line -notmatch '^([^:]+):') { continue } + $file = ($Matches[1] -replace '\\', '/') + $allowed = $false + foreach ($prefix in $allowlist) { + if ($file -eq $prefix -or $file.StartsWith($prefix)) { $allowed = $true; break } + } + if ($allowed) { + $allowedHits++ + } else { + $unexpected.Add($line) + } +} + +if ($unexpected.Count -gt 0) { + Write-Host " ✗ Join-Path backslash literals outside allowlist: $($unexpected.Count)" -ForegroundColor Red + foreach ($line in ($unexpected | Select-Object -First 30)) { + Write-Host " $line" -ForegroundColor DarkRed + } + if ($unexpected.Count -gt 30) { + Write-Host " ... and $($unexpected.Count - 30) more" -ForegroundColor DarkRed + } + Write-Host "" + Write-Host " Fix: replace `\` with `/` inside the second argument of Join-Path." -ForegroundColor Yellow + Write-Host " Windows accepts both; Linux/macOS only accept forward slashes." -ForegroundColor Yellow + Write-Host " For interpolated variables that may contain \, normalise first:" -ForegroundColor Yellow + Write-Host " `$normalized = `$x -replace '\\', '/'" -ForegroundColor DarkGray + Write-Host " Join-Path `$BotRoot `"systems/runtime/`$normalized`"" -ForegroundColor DarkGray + Write-TestResult -Name "Join-Path backslash literals outside allowlist" -Status Fail ` + -Message "$($unexpected.Count) outside-allowlist hit(s); see message above for fix pattern." + [void](Write-TestSummary -LayerName "Layer 1: No Backslash Paths") + exit 1 +} + +Write-TestResult -Name "Join-Path backslash literals outside allowlist" -Status Pass ` + -Message "All $allowedHits hit(s) are inside the allowlist." +[void](Write-TestSummary -LayerName "Layer 1: No Backslash Paths") +exit 0 diff --git a/tests/Test-TaskActions.ps1 b/tests/Test-TaskActions.ps1 index 281f81bb..9256183e 100644 --- a/tests/Test-TaskActions.ps1 +++ b/tests/Test-TaskActions.ps1 @@ -541,7 +541,7 @@ try { -Pattern 'UrlDecode\(\(\$url -replace "\^/api/task/history/", ""\)\)' Assert-FileContains -Name "Server imports TaskAPI with name checking disabled" ` -Path $serverScriptPath ` - -Pattern 'Import-Module \(Join-Path \$PSScriptRoot "modules\\TaskAPI\.psm1"\) -Force -DisableNameChecking' + -Pattern 'Import-Module \(Join-Path \$PSScriptRoot "modules/TaskAPI\.psm1"\) -Force -DisableNameChecking' Assert-FileContains -Name "Deleted archive UI renders RESTORED state" ` -Path $roadmapActionsScript ` -Pattern 'RESTORED' diff --git a/tests/Test-WorkflowIntegration.ps1 b/tests/Test-WorkflowIntegration.ps1 index 65776c4d..58b69608 100644 --- a/tests/Test-WorkflowIntegration.ps1 +++ b/tests/Test-WorkflowIntegration.ps1 @@ -764,7 +764,7 @@ if (Test-Path $serverFile) { -Message "Endpoint does not read request body for form data" Assert-True -Name "Workflow run endpoint saves briefing files" ` - -Condition ($serverContent -match 'workspace\\product\\briefing') ` + -Condition ($serverContent -match 'workspace/product/briefing') ` -Message "Endpoint does not save briefing files" Assert-True -Name "Workflow run endpoint saves user prompt" ` diff --git a/workflows/start-from-jira/hooks/verify/03-research-completeness.ps1 b/workflows/start-from-jira/hooks/verify/03-research-completeness.ps1 index b8d11ff1..1aa5062f 100644 --- a/workflows/start-from-jira/hooks/verify/03-research-completeness.ps1 +++ b/workflows/start-from-jira/hooks/verify/03-research-completeness.ps1 @@ -1,8 +1,8 @@ # 03-research-completeness.ps1 # Verify all required research artifacts exist before proceeding to implementation -$briefingDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\product\briefing" -$productDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\product" +$briefingDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/product/briefing" +$productDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/product" $errors = @() $warnings = @() diff --git a/workflows/start-from-jira/systems/mcp/tools/repo-clone/script.ps1 b/workflows/start-from-jira/systems/mcp/tools/repo-clone/script.ps1 index 57a3dc8c..04c6df57 100644 --- a/workflows/start-from-jira/systems/mcp/tools/repo-clone/script.ps1 +++ b/workflows/start-from-jira/systems/mcp/tools/repo-clone/script.ps1 @@ -31,7 +31,7 @@ function Invoke-RepoClone { $clonePath = Join-Path $reposDir $repo # Read jira-context.md to get Jira key for branch name - $initiativePath = Join-Path $global:DotbotProjectRoot ".bot\workspace\product\briefing\jira-context.md" + $initiativePath = Join-Path $global:DotbotProjectRoot ".bot/workspace/product/briefing/jira-context.md" $jiraKey = $null if (Test-Path $initiativePath) { $content = Get-Content $initiativePath -Raw @@ -44,7 +44,7 @@ function Invoke-RepoClone { $branchPrefix = "initiative" $botRoot = Join-Path $global:DotbotProjectRoot ".bot" if (-not (Get-Module SettingsLoader)) { - Import-Module (Join-Path $botRoot "systems\runtime\modules\SettingsLoader.psm1") -DisableNameChecking -Global + Import-Module (Join-Path $botRoot "systems/runtime/modules/SettingsLoader.psm1") -DisableNameChecking -Global } $settings = Get-MergedSettings -BotRoot $botRoot @@ -118,7 +118,7 @@ function Invoke-RepoClone { # --------------------------------------------------------------------------- # Configure NuGet authentication (for .NET repos) # --------------------------------------------------------------------------- - $nugetConfig = Join-Path $clonePath "src\NuGet.config" + $nugetConfig = Join-Path $clonePath "src/NuGet.config" if (-not (Test-Path $nugetConfig)) { $nugetConfig = Join-Path $clonePath "NuGet.config" } diff --git a/workflows/start-from-jira/systems/mcp/tools/repo-list/script.ps1 b/workflows/start-from-jira/systems/mcp/tools/repo-list/script.ps1 index f583e884..9a894266 100644 --- a/workflows/start-from-jira/systems/mcp/tools/repo-list/script.ps1 +++ b/workflows/start-from-jira/systems/mcp/tools/repo-list/script.ps1 @@ -35,17 +35,17 @@ function Invoke-RepoList { $status = "cloned" # Check for analysis artifacts in initiative repo's briefing - $deepDivePath = Join-Path $global:DotbotProjectRoot ".bot\workspace\product\briefing\repos\$repoName.md" + $deepDivePath = Join-Path $global:DotbotProjectRoot ".bot/workspace/product/briefing/repos/$repoName.md" $hasDeepDive = Test-Path $deepDivePath # Check for per-repo plan/outcomes/handoff - $planPath = Join-Path $repoPath ".bot\workspace\product\${repoName}_Plan.md" + $planPath = Join-Path $repoPath ".bot/workspace/product/${repoName}_Plan.md" $hasPlan = Test-Path $planPath - $outcomesPath = Join-Path $repoPath ".bot\workspace\product\${repoName}_Outcomes.md" + $outcomesPath = Join-Path $repoPath ".bot/workspace/product/${repoName}_Outcomes.md" $hasOutcomes = Test-Path $outcomesPath - $handoffPath = Join-Path $repoPath ".bot\workspace\product\${repoName}-handoff.md" + $handoffPath = Join-Path $repoPath ".bot/workspace/product/${repoName}-handoff.md" $hasHandoff = Test-Path $handoffPath # Determine status based on artifacts diff --git a/workflows/start-from-jira/systems/mcp/tools/repo-list/test.ps1 b/workflows/start-from-jira/systems/mcp/tools/repo-list/test.ps1 index 8e1c6a52..803277a7 100644 --- a/workflows/start-from-jira/systems/mcp/tools/repo-list/test.ps1 +++ b/workflows/start-from-jira/systems/mcp/tools/repo-list/test.ps1 @@ -49,7 +49,7 @@ try { -Actual $result.repos[0].name # Test 4: Deep dive artifact advances status to "analyzed" - $briefingRepos = Join-Path $testRoot ".bot\workspace\product\briefing\repos" + $briefingRepos = Join-Path $testRoot ".bot/workspace/product/briefing/repos" New-Item -Path $briefingRepos -ItemType Directory -Force | Out-Null "# Deep dive" | Set-Content (Join-Path $briefingRepos "FakeRepo.md") diff --git a/workflows/start-from-jira/systems/mcp/tools/research-status/script.ps1 b/workflows/start-from-jira/systems/mcp/tools/research-status/script.ps1 index aece833f..04e94024 100644 --- a/workflows/start-from-jira/systems/mcp/tools/research-status/script.ps1 +++ b/workflows/start-from-jira/systems/mcp/tools/research-status/script.ps1 @@ -1,8 +1,8 @@ function Invoke-ResearchStatus { param([hashtable]$Arguments) - $briefingDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\product\briefing" - $productDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\product" + $briefingDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/product/briefing" + $productDir = Join-Path $global:DotbotProjectRoot ".bot/workspace/product" # --------------------------------------------------------------------------- # Check core artifacts diff --git a/workflows/start-from-jira/systems/mcp/tools/research-status/test.ps1 b/workflows/start-from-jira/systems/mcp/tools/research-status/test.ps1 index 3cf28808..e793d2e3 100644 --- a/workflows/start-from-jira/systems/mcp/tools/research-status/test.ps1 +++ b/workflows/start-from-jira/systems/mcp/tools/research-status/test.ps1 @@ -9,8 +9,8 @@ $testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "dotbot-test-research-$( New-Item -Path $testRoot -ItemType Directory -Force | Out-Null $global:DotbotProjectRoot = $testRoot -$briefingDir = Join-Path $testRoot ".bot\workspace\product\briefing" -$productDir = Join-Path $testRoot ".bot\workspace\product" +$briefingDir = Join-Path $testRoot ".bot/workspace/product/briefing" +$productDir = Join-Path $testRoot ".bot/workspace/product" New-Item -Path $briefingDir -ItemType Directory -Force | Out-Null try {