panopticon-cli 0.5.10 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-MOMDECON.js → agents-RL2KUSP3.js} +2 -2
- package/dist/{chunk-4OQ4SXQZ.js → chunk-NLN3ZLCN.js} +18 -18
- package/dist/{chunk-RCYJK3ZC.js → chunk-OMOEGJDB.js} +2 -2
- package/dist/{chunk-BHRMW7BY.js → chunk-S7EJ2OLR.js} +4 -2
- package/dist/chunk-S7EJ2OLR.js.map +1 -0
- package/dist/cli/index.js +19 -19
- package/dist/dashboard/server.js +3 -1
- package/dist/{merge-agent-6YOMGQMX.js → merge-agent-7L7MWJEC.js} +5 -5
- package/dist/{specialist-context-GVF4DV3M.js → specialist-context-L37RF6Z5.js} +2 -2
- package/dist/{specialist-logs-W47SAAIU.js → specialist-logs-B7UC3UDO.js} +2 -2
- package/dist/{specialists-SIXRWCZ3.js → specialists-X4OGA7WX.js} +2 -2
- package/dist/{workspace-manager-Z57ROWBQ.js → workspace-manager-6RP5A5HF.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-BHRMW7BY.js.map +0 -1
- /package/dist/{agents-MOMDECON.js.map → agents-RL2KUSP3.js.map} +0 -0
- /package/dist/{chunk-4OQ4SXQZ.js.map → chunk-NLN3ZLCN.js.map} +0 -0
- /package/dist/{chunk-RCYJK3ZC.js.map → chunk-OMOEGJDB.js.map} +0 -0
- /package/dist/{merge-agent-6YOMGQMX.js.map → merge-agent-7L7MWJEC.js.map} +0 -0
- /package/dist/{specialist-context-GVF4DV3M.js.map → specialist-context-L37RF6Z5.js.map} +0 -0
- /package/dist/{specialist-logs-W47SAAIU.js.map → specialist-logs-B7UC3UDO.js.map} +0 -0
- /package/dist/{specialists-SIXRWCZ3.js.map → specialists-X4OGA7WX.js.map} +0 -0
- /package/dist/{workspace-manager-Z57ROWBQ.js.map → workspace-manager-6RP5A5HF.js.map} +0 -0
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
stopAgent,
|
|
21
21
|
transitionIssueToInProgress,
|
|
22
22
|
transitionIssueToInReview
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-OMOEGJDB.js";
|
|
24
24
|
import "./chunk-PFA5XE2V.js";
|
|
25
25
|
import "./chunk-USYP2SBE.js";
|
|
26
26
|
import "./chunk-QAJAJBFW.js";
|
|
@@ -54,4 +54,4 @@ export {
|
|
|
54
54
|
transitionIssueToInProgress,
|
|
55
55
|
transitionIssueToInReview
|
|
56
56
|
};
|
|
57
|
-
//# sourceMappingURL=agents-
|
|
57
|
+
//# sourceMappingURL=agents-RL2KUSP3.js.map
|
|
@@ -933,9 +933,9 @@ function recordWake(name, sessionId) {
|
|
|
933
933
|
}
|
|
934
934
|
async function spawnEphemeralSpecialist(projectKey, specialistType, task) {
|
|
935
935
|
ensureProjectSpecialistDir(projectKey, specialistType);
|
|
936
|
-
const { loadContextDigest } = await import("./specialist-context-
|
|
936
|
+
const { loadContextDigest } = await import("./specialist-context-L37RF6Z5.js");
|
|
937
937
|
const contextDigest = loadContextDigest(projectKey, specialistType);
|
|
938
|
-
const { createRunLog: createRunLog2 } = await import("./specialist-logs-
|
|
938
|
+
const { createRunLog: createRunLog2 } = await import("./specialist-logs-B7UC3UDO.js");
|
|
939
939
|
const { runId, filePath: logFilePath } = createRunLog2(
|
|
940
940
|
projectKey,
|
|
941
941
|
specialistType,
|
|
@@ -955,7 +955,7 @@ ${basePrompt}`;
|
|
|
955
955
|
const project = getProject(projectKey);
|
|
956
956
|
const cwd = project?.path || getDevrootPath() || homedir2();
|
|
957
957
|
try {
|
|
958
|
-
const { preTrustDirectory } = await import("./workspace-manager-
|
|
958
|
+
const { preTrustDirectory } = await import("./workspace-manager-6RP5A5HF.js");
|
|
959
959
|
preTrustDirectory(cwd);
|
|
960
960
|
} catch {
|
|
961
961
|
}
|
|
@@ -963,7 +963,7 @@ ${basePrompt}`;
|
|
|
963
963
|
try {
|
|
964
964
|
const { stdout: sessions } = await execAsync('tmux list-sessions -F "#{session_name}" 2>/dev/null || echo ""', { encoding: "utf-8" });
|
|
965
965
|
if (sessions.split("\n").map((s) => s.trim()).includes(tmuxSession)) {
|
|
966
|
-
const { getAgentRuntimeState } = await import("./agents-
|
|
966
|
+
const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
967
967
|
const existingState = getAgentRuntimeState(tmuxSession);
|
|
968
968
|
if (existingState?.state === "active") {
|
|
969
969
|
return {
|
|
@@ -1039,7 +1039,7 @@ script -qfec "bash '${innerScript}'" /dev/null 2>&1 | tee -a "${logFilePath}"
|
|
|
1039
1039
|
`tmux new-session -d -s "${tmuxSession}" -c "${cwd}"${envFlags} "bash '${launcherScript}'"`,
|
|
1040
1040
|
{ encoding: "utf-8" }
|
|
1041
1041
|
);
|
|
1042
|
-
const { saveAgentRuntimeState } = await import("./agents-
|
|
1042
|
+
const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1043
1043
|
saveAgentRuntimeState(tmuxSession, {
|
|
1044
1044
|
state: "active",
|
|
1045
1045
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1224,7 +1224,7 @@ async function terminateSpecialist(projectKey, specialistType) {
|
|
|
1224
1224
|
console.error(`[specialist] Failed to kill tmux session ${tmuxSession}:`, error);
|
|
1225
1225
|
}
|
|
1226
1226
|
if (metadata.currentRun) {
|
|
1227
|
-
const { finalizeRunLog: finalizeRunLog2 } = await import("./specialist-logs-
|
|
1227
|
+
const { finalizeRunLog: finalizeRunLog2 } = await import("./specialist-logs-B7UC3UDO.js");
|
|
1228
1228
|
try {
|
|
1229
1229
|
finalizeRunLog2(projectKey, specialistType, metadata.currentRun, {
|
|
1230
1230
|
status: metadata.lastRunStatus || "incomplete",
|
|
@@ -1237,19 +1237,19 @@ async function terminateSpecialist(projectKey, specialistType) {
|
|
|
1237
1237
|
}
|
|
1238
1238
|
const key = `${projectKey}-${specialistType}`;
|
|
1239
1239
|
gracePeriodStates.delete(key);
|
|
1240
|
-
const { saveAgentRuntimeState } = await import("./agents-
|
|
1240
|
+
const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1241
1241
|
saveAgentRuntimeState(tmuxSession, {
|
|
1242
1242
|
state: "suspended",
|
|
1243
1243
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString()
|
|
1244
1244
|
});
|
|
1245
|
-
const { scheduleDigestGeneration } = await import("./specialist-context-
|
|
1245
|
+
const { scheduleDigestGeneration } = await import("./specialist-context-L37RF6Z5.js");
|
|
1246
1246
|
scheduleDigestGeneration(projectKey, specialistType);
|
|
1247
1247
|
scheduleLogCleanup(projectKey, specialistType);
|
|
1248
1248
|
}
|
|
1249
1249
|
function scheduleLogCleanup(projectKey, specialistType) {
|
|
1250
1250
|
Promise.resolve().then(async () => {
|
|
1251
1251
|
try {
|
|
1252
|
-
const { cleanupOldLogs: cleanupOldLogs2 } = await import("./specialist-logs-
|
|
1252
|
+
const { cleanupOldLogs: cleanupOldLogs2 } = await import("./specialist-logs-B7UC3UDO.js");
|
|
1253
1253
|
const { getSpecialistRetention } = await import("./projects-BPGM6IFB.js");
|
|
1254
1254
|
const retention = getSpecialistRetention(projectKey);
|
|
1255
1255
|
const deleted = cleanupOldLogs2(projectKey, specialistType, { maxDays: retention.max_days, maxRuns: retention.max_runs });
|
|
@@ -1438,7 +1438,7 @@ async function getSpecialistStatus(name, projectKey) {
|
|
|
1438
1438
|
const sessionId = getSessionId(name, projectKey);
|
|
1439
1439
|
const running = await isRunning(name, projectKey);
|
|
1440
1440
|
const contextTokens = countContextTokens(name);
|
|
1441
|
-
const { getAgentRuntimeState } = await import("./agents-
|
|
1441
|
+
const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1442
1442
|
const tmuxSession = getTmuxSessionName(name, projectKey);
|
|
1443
1443
|
const runtimeState = getAgentRuntimeState(tmuxSession);
|
|
1444
1444
|
let state;
|
|
@@ -1593,7 +1593,7 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
|
|
|
1593
1593
|
const sessionId = getSessionId(name);
|
|
1594
1594
|
const wasAlreadyRunning = await isRunning(name);
|
|
1595
1595
|
if (wasAlreadyRunning && !options.skipBusyGuard) {
|
|
1596
|
-
const { getAgentRuntimeState } = await import("./agents-
|
|
1596
|
+
const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1597
1597
|
const runtimeState = getAgentRuntimeState(tmuxSession);
|
|
1598
1598
|
if (runtimeState?.state === "active") {
|
|
1599
1599
|
console.warn(`[specialist] ${name} is busy (working on ${runtimeState.currentIssue}), refusing to interrupt`);
|
|
@@ -1617,7 +1617,7 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
|
|
|
1617
1617
|
}
|
|
1618
1618
|
const cwd = getDevrootPath() || join5(process.env.HOME || "/home/eltmon", "Projects");
|
|
1619
1619
|
try {
|
|
1620
|
-
const { preTrustDirectory } = await import("./workspace-manager-
|
|
1620
|
+
const { preTrustDirectory } = await import("./workspace-manager-6RP5A5HF.js");
|
|
1621
1621
|
preTrustDirectory(cwd);
|
|
1622
1622
|
} catch {
|
|
1623
1623
|
}
|
|
@@ -1712,7 +1712,7 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
|
|
|
1712
1712
|
}
|
|
1713
1713
|
}
|
|
1714
1714
|
recordWake(name, sessionId || void 0);
|
|
1715
|
-
const { saveAgentRuntimeState } = await import("./agents-
|
|
1715
|
+
const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1716
1716
|
saveAgentRuntimeState(tmuxSession, {
|
|
1717
1717
|
state: "active",
|
|
1718
1718
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1825,7 +1825,7 @@ CRITICAL: Do NOT delete the feature branch.`;
|
|
|
1825
1825
|
});
|
|
1826
1826
|
console.log(`[specialist] review-agent: auto-passed ${task.issueId} (stale branch)`);
|
|
1827
1827
|
const tmuxSession = getTmuxSessionName("review-agent");
|
|
1828
|
-
const { saveAgentRuntimeState } = await import("./agents-
|
|
1828
|
+
const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1829
1829
|
saveAgentRuntimeState(tmuxSession, {
|
|
1830
1830
|
state: "idle",
|
|
1831
1831
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2107,7 +2107,7 @@ async function wakeSpecialistOrQueue(name, task, options = {}) {
|
|
|
2107
2107
|
}
|
|
2108
2108
|
}
|
|
2109
2109
|
const running = await isRunning(name);
|
|
2110
|
-
const { getAgentRuntimeState } = await import("./agents-
|
|
2110
|
+
const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
2111
2111
|
const tmuxSession = getTmuxSessionName(name);
|
|
2112
2112
|
const runtimeState = getAgentRuntimeState(tmuxSession);
|
|
2113
2113
|
const idle = runtimeState?.state === "idle" || runtimeState?.state === "suspended";
|
|
@@ -2138,7 +2138,7 @@ async function wakeSpecialistOrQueue(name, task, options = {}) {
|
|
|
2138
2138
|
};
|
|
2139
2139
|
}
|
|
2140
2140
|
}
|
|
2141
|
-
const { saveAgentRuntimeState } = await import("./agents-
|
|
2141
|
+
const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
2142
2142
|
saveAgentRuntimeState(tmuxSession, {
|
|
2143
2143
|
state: "active",
|
|
2144
2144
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2258,7 +2258,7 @@ async function sendFeedbackToAgent(feedback) {
|
|
|
2258
2258
|
return false;
|
|
2259
2259
|
}
|
|
2260
2260
|
try {
|
|
2261
|
-
const { messageAgent } = await import("./agents-
|
|
2261
|
+
const { messageAgent } = await import("./agents-RL2KUSP3.js");
|
|
2262
2262
|
const msg = `SPECIALIST FEEDBACK: ${fromSpecialist} reported ${feedback.feedbackType.toUpperCase()} for ${toIssueId}.
|
|
2263
2263
|
Read and address: ${fileResult.relativePath}`;
|
|
2264
2264
|
await messageAgent(agentSession, msg);
|
|
@@ -2772,4 +2772,4 @@ export {
|
|
|
2772
2772
|
getFeedbackStats,
|
|
2773
2773
|
init_specialists
|
|
2774
2774
|
};
|
|
2775
|
-
//# sourceMappingURL=chunk-
|
|
2775
|
+
//# sourceMappingURL=chunk-NLN3ZLCN.js.map
|
|
@@ -673,7 +673,7 @@ exec claude --dangerously-skip-permissions --model ${state.model} "$prompt"
|
|
|
673
673
|
claudeCmd = `claude --dangerously-skip-permissions --model ${state.model}`;
|
|
674
674
|
}
|
|
675
675
|
try {
|
|
676
|
-
const { preTrustDirectory } = await import("./workspace-manager-
|
|
676
|
+
const { preTrustDirectory } = await import("./workspace-manager-6RP5A5HF.js");
|
|
677
677
|
preTrustDirectory(options.workspace);
|
|
678
678
|
} catch {
|
|
679
679
|
}
|
|
@@ -1166,4 +1166,4 @@ export {
|
|
|
1166
1166
|
autoRecoverAgents,
|
|
1167
1167
|
init_agents
|
|
1168
1168
|
};
|
|
1169
|
-
//# sourceMappingURL=chunk-
|
|
1169
|
+
//# sourceMappingURL=chunk-OMOEGJDB.js.map
|
|
@@ -215,7 +215,9 @@ async function createWorktree(repoPath, targetPath, branchName, defaultBranch =
|
|
|
215
215
|
await execAsync("git worktree prune", { cwd: repoPath });
|
|
216
216
|
const { stdout: localBranches } = await execAsync("git branch --list", { cwd: repoPath });
|
|
217
217
|
const { stdout: remoteBranches } = await execAsync("git branch -r --list", { cwd: repoPath });
|
|
218
|
-
const
|
|
218
|
+
const localList = localBranches.split("\n").map((b) => b.replace(/^[*+\s]+/, "").trim()).filter(Boolean);
|
|
219
|
+
const remoteList = remoteBranches.split("\n").map((b) => b.trim()).filter(Boolean);
|
|
220
|
+
const branchExists = localList.includes(branchName) || remoteList.includes(`origin/${branchName}`);
|
|
219
221
|
if (branchExists) {
|
|
220
222
|
await execAsync(`git worktree add "${targetPath}" "${branchName}"`, { cwd: repoPath });
|
|
221
223
|
} else {
|
|
@@ -850,4 +852,4 @@ export {
|
|
|
850
852
|
removeWorkspace,
|
|
851
853
|
init_workspace_manager
|
|
852
854
|
};
|
|
853
|
-
//# sourceMappingURL=chunk-
|
|
855
|
+
//# sourceMappingURL=chunk-S7EJ2OLR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/skills-merge.ts","../src/lib/workspace-manager.ts"],"sourcesContent":["import {\n existsSync,\n readdirSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n copyFileSync,\n statSync,\n} from 'fs';\nimport { join, relative, dirname } from 'path';\nimport { SKILLS_DIR, CACHE_AGENTS_DIR, CACHE_RULES_DIR } from './paths.js';\nimport {\n readManifest,\n writeManifest,\n collectSourceFiles,\n hashFile,\n setManifestEntry,\n compareFileToManifest,\n type Manifest,\n} from './manifest.js';\n\nexport interface MergeResult {\n added: string[];\n updated: string[];\n skipped: string[];\n overlayed: string[];\n}\n\n/**\n * Copy all files from a source directory into a target directory,\n * preserving subdirectory structure. Returns the list of relative paths copied.\n */\nfunction copyTree(sourceDir: string, targetDir: string): string[] {\n const copied: string[] = [];\n if (!existsSync(sourceDir)) return copied;\n\n function walk(dir: string): void {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const rel = relative(sourceDir, fullPath);\n const targetPath = join(targetDir, rel);\n mkdirSync(dirname(targetPath), { recursive: true });\n copyFileSync(fullPath, targetPath);\n copied.push(rel);\n }\n }\n }\n\n walk(sourceDir);\n return copied;\n}\n\n/**\n * Merge Panopticon skills, agents, and rules into a workspace using file copies.\n *\n * Flow:\n * 1. Copy from cache (skills, agent-definitions, rules) → workspace/.claude/\n * 2. Write manifest tracking what was placed\n *\n * Project template overlay is handled separately by workspace-manager.ts\n * (processTemplates + createSymlinks → now also copy-based).\n */\nexport function mergeSkillsIntoWorkspace(workspacePath: string): MergeResult {\n const claudeDir = join(workspacePath, '.claude');\n const manifestPath = join(claudeDir, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n\n const result: MergeResult = {\n added: [],\n updated: [],\n skipped: [],\n overlayed: [],\n };\n\n // Ensure base directories exist\n mkdirSync(join(claudeDir, 'skills'), { recursive: true });\n mkdirSync(join(claudeDir, 'agents'), { recursive: true });\n\n // Sources to copy: category → source cache directory\n const sources: Array<{ category: string; sourceDir: string; targetSubdir: string }> = [\n { category: 'skills', sourceDir: SKILLS_DIR, targetSubdir: 'skills' },\n { category: 'agents', sourceDir: CACHE_AGENTS_DIR, targetSubdir: 'agents' },\n { category: 'rules', sourceDir: CACHE_RULES_DIR, targetSubdir: 'rules' },\n ];\n\n for (const { category, sourceDir, targetSubdir } of sources) {\n if (!existsSync(sourceDir)) continue;\n\n const prefix = targetSubdir ? `${targetSubdir}/` : '';\n const files = collectSourceFiles(sourceDir, '');\n\n for (const file of files) {\n const relativePath = `${prefix}${file.relativePath}`;\n const targetPath = join(claudeDir, relativePath);\n const sourceHash = hashFile(file.absolutePath);\n\n // Check status against manifest\n const status = compareFileToManifest(targetPath, relativePath, manifest);\n\n switch (status.action) {\n case 'new':\n // File doesn't exist at target — copy it\n mkdirSync(dirname(targetPath), { recursive: true });\n copyFileSync(file.absolutePath, targetPath);\n setManifestEntry(manifest, relativePath, sourceHash, 'panopticon');\n result.added.push(relativePath);\n break;\n\n case 'update':\n // File exists and matches manifest — safe to overwrite with latest\n copyFileSync(file.absolutePath, targetPath);\n setManifestEntry(manifest, relativePath, sourceHash, 'panopticon');\n result.updated.push(relativePath);\n break;\n\n case 'modified':\n // User modified the file — skip to preserve their changes\n result.skipped.push(`${relativePath} (modified by user)`);\n break;\n\n case 'user-owned':\n // File exists but wasn't placed by us — never touch\n result.skipped.push(`${relativePath} (user-owned)`);\n break;\n }\n }\n }\n\n // Write updated manifest\n writeManifest(manifestPath, manifest);\n\n return result;\n}\n\n/**\n * Apply project template overlay on top of Panopticon base files in a workspace.\n *\n * This copies files from the project's agent template directory into\n * workspace/.claude/, overwriting Panopticon files where the project\n * provides its own version. Updates the manifest with source=\"project-template\".\n *\n * @param workspacePath - Path to the workspace\n * @param templateDir - Absolute path to the project's agent template directory\n * @param templates - Optional list of specific template files to process (source → target mappings)\n */\nexport function applyProjectTemplateOverlay(\n workspacePath: string,\n templateDir: string,\n templates?: Array<{ source: string; target: string }>,\n): string[] {\n const claudeDir = join(workspacePath, '.claude');\n const manifestPath = join(claudeDir, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n const overlayed: string[] = [];\n\n if (!existsSync(templateDir)) return overlayed;\n\n if (templates && templates.length > 0) {\n // Process specific template mappings\n for (const { source, target } of templates) {\n const sourcePath = join(templateDir, source);\n if (!existsSync(sourcePath)) continue;\n\n const targetPath = join(workspacePath, target);\n mkdirSync(dirname(targetPath), { recursive: true });\n\n // Read template content and check if it's a template file\n if (source.endsWith('.template')) {\n // Template files are handled by workspace-manager's processTemplates\n // We just track them in the manifest after they're processed\n continue;\n }\n\n copyFileSync(sourcePath, targetPath);\n\n // Track in manifest if it's under .claude/\n if (target.startsWith('.claude/')) {\n const relativePath = target.slice('.claude/'.length);\n const hash = hashFile(targetPath);\n setManifestEntry(manifest, relativePath, hash, 'project-template');\n overlayed.push(relativePath);\n }\n }\n } else {\n // Copy all .claude/ subdirectories from template dir\n const claudeInTemplate = join(templateDir, '.claude');\n if (existsSync(claudeInTemplate)) {\n const copied = copyTree(claudeInTemplate, claudeDir);\n for (const rel of copied) {\n const targetPath = join(claudeDir, rel);\n const hash = hashFile(targetPath);\n setManifestEntry(manifest, rel, hash, 'project-template');\n overlayed.push(rel);\n }\n }\n }\n\n // Write updated manifest\n writeManifest(manifestPath, manifest);\n\n return overlayed;\n}\n\n// ─── Legacy exports (kept for migration, to be removed in future) ───\n\n/**\n * @deprecated No longer needed — skills are copies, not symlinks. Kept for migration.\n */\nexport function cleanupGitignore(gitignorePath: string): {\n cleaned: boolean;\n duplicatesRemoved: number;\n entriesAfter: number;\n} {\n if (!existsSync(gitignorePath)) {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n const PANOPTICON_HEADER = '# Panopticon-managed symlinks (not committed)';\n let content: string;\n try {\n content = readFileSync(gitignorePath, 'utf-8');\n } catch {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n // If no Panopticon section, nothing to clean\n if (!content.includes(PANOPTICON_HEADER)) {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n // Remove the entire Panopticon section (skills are copies now, not symlinks)\n const lines = content.split('\\n');\n const newLines: string[] = [];\n let inPanopticonSection = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === PANOPTICON_HEADER) {\n inPanopticonSection = true;\n continue;\n }\n if (inPanopticonSection) {\n if (trimmed.startsWith('#') && trimmed !== '') {\n inPanopticonSection = false;\n newLines.push(line);\n } else if (trimmed === '') {\n // Skip blank lines in Panopticon section\n continue;\n }\n // Skip entries in Panopticon section\n continue;\n }\n newLines.push(line);\n }\n\n // Write cleaned file\n try {\n writeFileSync(gitignorePath, newLines.join('\\n'), 'utf-8');\n return { cleaned: true, duplicatesRemoved: 0, entriesAfter: 0 };\n } catch {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n}\n\n/**\n * @deprecated No longer needed — skills are copies, not symlinks. Kept for migration.\n */\nexport function cleanupWorkspaceGitignore(workspacePath: string): {\n cleaned: boolean;\n duplicatesRemoved: number;\n entriesAfter: number;\n} {\n const gitignorePath = join(workspacePath, '.claude', 'skills', '.gitignore');\n return cleanupGitignore(gitignorePath);\n}\n","/**\n * Workspace Manager\n *\n * Handles workspace creation and removal for both monorepo and polyrepo projects.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, copyFileSync, symlinkSync, chmodSync, realpathSync, rmSync, statSync } from 'fs';\nimport { join, dirname, basename, extname, resolve } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport {\n ProjectConfig,\n WorkspaceConfig,\n TemplatePlaceholders,\n replacePlaceholders,\n getDefaultWorkspaceConfig,\n} from './workspace-config.js';\nimport { addDnsEntry, removeDnsEntry, syncDnsToWindows } from './dns.js';\nimport { addTunnelIngress, removeTunnelIngress } from './tunnel.js';\nimport { createHumeConfig, deleteHumeConfig } from './hume.js';\nimport { mergeSkillsIntoWorkspace } from './skills-merge.js';\n\nconst execAsync = promisify(exec);\n\nexport interface WorkspaceCreateOptions {\n projectConfig: ProjectConfig;\n featureName: string;\n startDocker?: boolean;\n dryRun?: boolean;\n}\n\nexport interface WorkspaceCreateResult {\n success: boolean;\n workspacePath: string;\n errors: string[];\n steps: string[];\n}\n\n/**\n * Create placeholders for template substitution\n */\nfunction createPlaceholders(\n projectConfig: ProjectConfig,\n featureName: string,\n workspacePath: string\n): TemplatePlaceholders {\n const featureFolder = `feature-${featureName}`;\n const domain = projectConfig.workspace?.dns?.domain || 'localhost';\n\n return {\n FEATURE_NAME: featureName,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${featureName}`,\n COMPOSE_PROJECT: `${basename(projectConfig.path)}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: basename(projectConfig.path),\n PROJECT_PATH: projectConfig.path,\n PROJECTS_DIR: dirname(projectConfig.path),\n WORKSPACE_PATH: workspacePath,\n HOME: homedir(),\n };\n}\n\n/**\n * Sanitize docker-compose files to use platform-agnostic paths\n * Replaces hardcoded /home/username paths with ${HOME}\n */\nfunction sanitizeComposeFile(filePath: string): void {\n if (!existsSync(filePath)) return;\n\n let content = readFileSync(filePath, 'utf-8');\n const originalContent = content;\n\n // Pattern to match hardcoded home paths like /home/username or /Users/username\n // Replace with ${HOME} which docker-compose expands\n const homePatterns = [\n /\\/home\\/[a-zA-Z0-9_-]+\\//g, // Linux: /home/username/\n /\\/Users\\/[a-zA-Z0-9_-]+\\//g, // macOS: /Users/username/\n ];\n\n for (const pattern of homePatterns) {\n content = content.replace(pattern, '${HOME}/');\n }\n\n if (content !== originalContent) {\n writeFileSync(filePath, content, 'utf-8');\n }\n}\n\n/**\n * Validate feature name (alphanumeric and hyphens only)\n */\nfunction validateFeatureName(name: string): boolean {\n return /^[a-zA-Z0-9-]+$/.test(name);\n}\n\n/**\n * Create a git worktree\n * @param repoPath Path to the source git repository\n * @param targetPath Where to create the worktree\n * @param branchName Name of the feature branch to create/checkout\n * @param defaultBranch Base branch to create new branches from (default: 'main')\n */\nasync function createWorktree(\n repoPath: string,\n targetPath: string,\n branchName: string,\n defaultBranch: string = 'main'\n): Promise<{ success: boolean; message: string }> {\n try {\n // Fetch latest from origin\n await execAsync('git fetch origin', { cwd: repoPath });\n\n // Prune stale worktree entries (e.g., from deleted workspaces)\n await execAsync('git worktree prune', { cwd: repoPath });\n\n // Check if branch exists locally or remotely (exact match, not substring)\n const { stdout: localBranches } = await execAsync('git branch --list', { cwd: repoPath });\n const { stdout: remoteBranches } = await execAsync('git branch -r --list', { cwd: repoPath });\n\n const localList = localBranches.split('\\n').map(b => b.replace(/^[*+\\s]+/, '').trim()).filter(Boolean);\n const remoteList = remoteBranches.split('\\n').map(b => b.trim()).filter(Boolean);\n const branchExists =\n localList.includes(branchName) ||\n remoteList.includes(`origin/${branchName}`);\n\n if (branchExists) {\n await execAsync(`git worktree add \"${targetPath}\" \"${branchName}\"`, { cwd: repoPath });\n } else {\n // Create new branch from the configured default branch\n await execAsync(`git worktree add \"${targetPath}\" -b \"${branchName}\" \"${defaultBranch}\"`, { cwd: repoPath });\n }\n\n // Configure beads role so agents don't get \"beads.role not configured\" warnings\n await execAsync('git config beads.role agent', { cwd: targetPath }).catch(() => {});\n\n return { success: true, message: `Created worktree at ${targetPath}` };\n } catch (error) {\n return { success: false, message: `Failed to create worktree: ${error}` };\n }\n}\n\n/**\n * Remove a git worktree\n */\nasync function removeWorktree(\n repoPath: string,\n targetPath: string,\n branchName: string\n): Promise<{ success: boolean; message: string }> {\n try {\n // Remove worktree\n await execAsync(`git worktree remove \"${targetPath}\" --force`, { cwd: repoPath }).catch(() => {});\n\n // Optionally delete the branch\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: repoPath }).catch(() => {});\n\n return { success: true, message: `Removed worktree at ${targetPath}` };\n } catch (error) {\n return { success: false, message: `Failed to remove worktree: ${error}` };\n }\n}\n\n// DNS functions (addWsl2HostEntry, removeWsl2HostEntry, syncDnsToWindows)\n// are now in src/lib/dns.ts and imported above\n\n/**\n * Assign a port from a range\n */\nfunction assignPort(\n portFile: string,\n featureFolder: string,\n range: [number, number]\n): number {\n // Ensure port file exists\n if (!existsSync(portFile)) {\n mkdirSync(dirname(portFile), { recursive: true });\n writeFileSync(portFile, '');\n }\n\n const content = readFileSync(portFile, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n\n // Check if already assigned\n for (const line of lines) {\n const [folder, port] = line.split(':');\n if (folder === featureFolder) {\n return parseInt(port, 10);\n }\n }\n\n // Find next available port\n const usedPorts = new Set(lines.map(l => parseInt(l.split(':')[1], 10)));\n for (let port = range[0]; port <= range[1]; port++) {\n if (!usedPorts.has(port)) {\n writeFileSync(portFile, content + (content.endsWith('\\n') ? '' : '\\n') + `${featureFolder}:${port}\\n`);\n return port;\n }\n }\n\n throw new Error(`No available ports in range ${range[0]}-${range[1]}`);\n}\n\n/**\n * Release a port assignment\n */\nfunction releasePort(portFile: string, featureFolder: string): boolean {\n try {\n if (!existsSync(portFile)) return true;\n\n let content = readFileSync(portFile, 'utf-8');\n const lines = content.split('\\n').filter(line => !line.startsWith(`${featureFolder}:`));\n writeFileSync(portFile, lines.join('\\n'));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Process template files with placeholder replacement\n */\nfunction processTemplates(\n templateDir: string,\n targetDir: string,\n placeholders: TemplatePlaceholders,\n templates?: Array<{ source: string; target: string }>\n): string[] {\n const steps: string[] = [];\n\n if (!existsSync(templateDir)) {\n return steps;\n }\n\n // If specific templates are defined, process those\n if (templates && templates.length > 0) {\n for (const { source, target } of templates) {\n const sourcePath = join(templateDir, source);\n const targetPath = join(targetDir, target);\n\n if (existsSync(sourcePath)) {\n const content = readFileSync(sourcePath, 'utf-8');\n const processed = replacePlaceholders(content, placeholders);\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, processed);\n steps.push(`Processed template: ${source} -> ${target}`);\n }\n }\n } else {\n // Process all .template files\n const files = readdirSync(templateDir);\n for (const file of files) {\n if (file.endsWith('.template')) {\n const sourcePath = join(templateDir, file);\n const targetPath = join(targetDir, file.replace('.template', ''));\n\n const content = readFileSync(sourcePath, 'utf-8');\n const processed = replacePlaceholders(content, placeholders);\n writeFileSync(targetPath, processed);\n // Shell scripts need execute permission\n const targetName = file.replace('.template', '');\n if (targetName === 'dev' || targetName.endsWith('.sh')) {\n chmodSync(targetPath, 0o755);\n }\n steps.push(`Processed template: ${file}`);\n }\n }\n }\n\n return steps;\n}\n\n/**\n * @deprecated Use copyProjectTemplateDirs instead. Kept for non-.claude paths.\n */\nfunction createSymlinks(\n sourceDir: string,\n targetDir: string,\n symlinks: string[]\n): string[] {\n const steps: string[] = [];\n\n for (const symlink of symlinks) {\n const sourcePath = join(sourceDir, symlink);\n const targetPath = join(targetDir, symlink);\n\n if (existsSync(sourcePath)) {\n mkdirSync(dirname(targetPath), { recursive: true });\n try {\n symlinkSync(sourcePath, targetPath);\n steps.push(`Created symlink: ${symlink}`);\n } catch {\n // Symlink might already exist\n }\n }\n }\n\n return steps;\n}\n\n/**\n * Copy project template directories into workspace (replaces symlinks).\n * Recursively copies all files from each source directory.\n */\nconst TEXT_EXTENSIONS = new Set([\n '.md', '.sh', '.yml', '.yaml', '.json', '.ts', '.js', '.env', '.txt', '.toml', '.template',\n]);\n\nfunction copyProjectTemplateDirs(\n sourceDir: string,\n targetDir: string,\n dirs: string[],\n placeholders?: TemplatePlaceholders\n): string[] {\n const steps: string[] = [];\n\n for (const dir of dirs) {\n const sourcePath = join(sourceDir, dir);\n const targetPath = join(targetDir, dir);\n\n if (!existsSync(sourcePath)) continue;\n\n // Recursively copy all files, applying placeholder substitution to text files\n function copyDir(src: string, dest: string): number {\n let count = 0;\n mkdirSync(dest, { recursive: true });\n const entries = readdirSync(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcEntry = join(src, entry.name);\n const destEntry = join(dest, entry.name);\n if (entry.isDirectory()) {\n count += copyDir(srcEntry, destEntry);\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n if (placeholders && TEXT_EXTENSIONS.has(ext)) {\n const content = readFileSync(srcEntry, 'utf-8');\n writeFileSync(destEntry, replacePlaceholders(content, placeholders));\n } else {\n copyFileSync(srcEntry, destEntry);\n }\n count++;\n }\n }\n return count;\n }\n\n const count = copyDir(sourcePath, targetPath);\n steps.push(`Copied ${count} files from project template: ${dir}`);\n }\n\n return steps;\n}\n\n/**\n * Create a workspace\n */\nexport async function createWorkspace(options: WorkspaceCreateOptions): Promise<WorkspaceCreateResult> {\n const { projectConfig, featureName, startDocker, dryRun } = options;\n const result: WorkspaceCreateResult = {\n success: true,\n workspacePath: '',\n errors: [],\n steps: [],\n };\n\n // Validate feature name\n if (!validateFeatureName(featureName)) {\n result.success = false;\n result.errors.push('Invalid feature name. Use alphanumeric and hyphens only.');\n return result;\n }\n\n // Reject 'main' as feature name\n if (featureName === 'main') {\n result.success = false;\n result.errors.push('Cannot create workspace for \"main\". Use base repos directly.');\n return result;\n }\n\n const workspaceConfig = projectConfig.workspace || getDefaultWorkspaceConfig();\n const workspacesDir = join(projectConfig.path, workspaceConfig.workspaces_dir || 'workspaces');\n const featureFolder = `feature-${featureName}`;\n const workspacePath = join(workspacesDir, featureFolder);\n result.workspacePath = workspacePath;\n\n // Check if workspace already exists\n if (existsSync(workspacePath)) {\n result.success = false;\n result.errors.push(`Workspace already exists at ${workspacePath}`);\n return result;\n }\n\n if (dryRun) {\n result.steps.push('[DRY RUN] Would create workspace at: ' + workspacePath);\n return result;\n }\n\n // Create placeholders\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n\n // Create workspace directory\n mkdirSync(workspacePath, { recursive: true });\n result.steps.push('Created workspace directory');\n\n // Handle polyrepo vs monorepo\n if (workspaceConfig.type === 'polyrepo' && workspaceConfig.repos) {\n // Create worktrees for each repo\n for (const repo of workspaceConfig.repos) {\n // Resolve symlinks to get the actual git repository path\n // (e.g., myn/frontend -> ../frontend needs to resolve to actual path)\n const rawRepoPath = join(projectConfig.path, repo.path);\n const repoPath = existsSync(rawRepoPath) ? realpathSync(rawRepoPath) : rawRepoPath;\n const targetPath = join(workspacePath, repo.name);\n const branchPrefix = repo.branch_prefix || 'feature/';\n const branchName = `${branchPrefix}${featureName}`;\n // Per-repo default_branch overrides workspace-level, falls back to 'main'\n const defaultBranch = repo.default_branch || workspaceConfig.default_branch || 'main';\n\n const worktreeResult = await createWorktree(repoPath, targetPath, branchName, defaultBranch);\n if (worktreeResult.success) {\n result.steps.push(`Created worktree for ${repo.name}: ${branchName} (from ${defaultBranch})`);\n } else {\n result.errors.push(`${repo.name}: ${worktreeResult.message}`);\n result.success = false; // Fail the entire workspace creation if any worktree fails\n }\n }\n } else {\n // Monorepo: create single worktree\n const branchName = `feature/${featureName}`;\n const defaultBranch = workspaceConfig.default_branch || 'main';\n const worktreeResult = await createWorktree(projectConfig.path, workspacePath, branchName, defaultBranch);\n if (worktreeResult.success) {\n result.steps.push(`Created worktree: ${branchName} (from ${defaultBranch})`);\n } else {\n result.errors.push(worktreeResult.message);\n result.success = false; // Fail the entire workspace creation if worktree fails\n }\n }\n\n // Remove stale .planning/ directory inherited from main branch.\n // This contains STATE.md and other planning artifacts from a PREVIOUS issue.\n // If left in place, the new agent reads it and works on the wrong issue.\n // SAFETY: resolve() to absolute path and verify it's under a known workspace prefix\n // to prevent path traversal from ever reaching rmSync.\n const resolvedWorkspace = resolve(workspacePath);\n const resolvedPlanning = resolve(resolvedWorkspace, '.planning');\n const isUnderWorkspacesDir = resolvedWorkspace.match(/\\/workspaces\\/feature-[a-z0-9-]+$/);\n if (\n isUnderWorkspacesDir &&\n resolvedPlanning === join(resolvedWorkspace, '.planning') &&\n existsSync(join(resolvedWorkspace, '.git')) &&\n existsSync(resolvedPlanning)\n ) {\n rmSync(resolvedPlanning, { recursive: true, force: true });\n result.steps.push('Removed stale .planning/ directory from previous issue');\n }\n\n // Sanitize any docker-compose files in the workspace to use platform-agnostic paths\n // This handles files inherited from worktrees that may have hardcoded home paths\n const devcontainerDir = join(workspacePath, '.devcontainer');\n if (existsSync(devcontainerDir)) {\n const composeFiles = readdirSync(devcontainerDir)\n .filter(f => f.includes('compose') && (f.endsWith('.yml') || f.endsWith('.yaml')));\n for (const composeFile of composeFiles) {\n sanitizeComposeFile(join(devcontainerDir, composeFile));\n }\n if (composeFiles.length > 0) {\n result.steps.push(`Sanitized ${composeFiles.length} compose file(s) for platform compatibility`);\n }\n }\n\n // Setup TLDR code analysis for workspace (after worktree creation to ensure directory is ready)\n try {\n // Check if python3 is available\n await execAsync('python3 --version');\n const venvPath = join(workspacePath, '.venv');\n const tldrBin = join(venvPath, 'bin', 'tldr');\n\n // Check if main branch already has a working venv with llm-tldr\n const mainVenvTldr = join(projectConfig.path, '.venv', 'bin', 'tldr');\n const mainVenvExists = existsSync(mainVenvTldr);\n\n if (mainVenvExists) {\n // Copy the entire venv from main — faster than pip install (seconds vs 30s+)\n await execAsync(`cp -a \"${join(projectConfig.path, '.venv')}\" \"${venvPath}\"`);\n result.steps.push('Copied Python venv from main branch');\n } else {\n // Create fresh venv and install llm-tldr\n await execAsync(`python3 -m venv \"${venvPath}\"`, { cwd: workspacePath });\n const pipPath = join(venvPath, 'bin', 'pip');\n await execAsync(`\"${pipPath}\" install llm-tldr`, { cwd: workspacePath, timeout: 120000 });\n result.steps.push('Created Python venv and installed llm-tldr');\n\n // Apply .tsx/.jsx support patch (upstream llm-tldr only checks .ts)\n const patchScript = join(projectConfig.path, 'scripts', 'patches', 'llm-tldr-tsx-support.py');\n if (existsSync(patchScript)) {\n await execAsync(`python3 \"${patchScript}\" \"${venvPath}\"`);\n result.steps.push('Applied llm-tldr .tsx/.jsx patch');\n }\n }\n\n // Verify tldr binary exists after setup\n if (!existsSync(tldrBin)) {\n result.steps.push('TLDR setup incomplete: tldr binary not found after venv creation');\n } else {\n // Copy .tldr index from main branch if it exists\n const mainTldrDir = join(projectConfig.path, '.tldr');\n const workspaceTldrDir = join(workspacePath, '.tldr');\n\n if (existsSync(mainTldrDir)) {\n await execAsync(`cp -r \"${mainTldrDir}\" \"${workspaceTldrDir}\"`);\n result.steps.push('Copied TLDR index from main branch');\n }\n\n // Start TLDR daemon for this workspace\n const { getTldrDaemonService } = await import('./tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.start(true);\n result.steps.push('Started TLDR daemon');\n\n // Warm the index in the background — ensures workspaces always have a working index\n // even when the main branch cache was empty (nothing to copy)\n try {\n await tldrService.warm(true); // background=true: non-blocking\n result.steps.push('TLDR index warm initiated (background)');\n } catch {\n // Non-fatal — daemon may not support warm yet\n }\n }\n } catch (error: any) {\n // TLDR setup is optional — don't fail workspace creation, but log clearly\n if (error.message?.includes('python3')) {\n result.steps.push('Skipped TLDR setup (python3 not available)');\n } else {\n console.warn(`⚠ TLDR setup failed: ${error.message}`);\n result.steps.push(`TLDR setup failed: ${error.message}`);\n }\n }\n\n // Configure DNS\n if (workspaceConfig.dns) {\n const dnsMethod = workspaceConfig.dns.sync_method || 'wsl2hosts';\n for (const entryPattern of workspaceConfig.dns.entries) {\n const hostname = replacePlaceholders(entryPattern, placeholders);\n\n if (addDnsEntry(dnsMethod, hostname)) {\n result.steps.push(`Added DNS entry: ${hostname} (${dnsMethod})`);\n }\n }\n\n // Sync to Windows if using wsl2hosts method\n if (dnsMethod === 'wsl2hosts') {\n const synced = await syncDnsToWindows();\n if (synced) {\n result.steps.push('Synced DNS to Windows hosts file');\n }\n }\n }\n\n // Assign ports\n if (workspaceConfig.ports) {\n for (const [portName, portConfig] of Object.entries(workspaceConfig.ports)) {\n const portFile = join(projectConfig.path, `.${portName}-ports`);\n try {\n const port = assignPort(portFile, featureFolder, portConfig.range);\n result.steps.push(`Assigned ${portName} port: ${port}`);\n // Add to placeholders for use in templates\n (placeholders as any)[`${portName.toUpperCase()}_PORT`] = String(port);\n } catch (error) {\n result.errors.push(`Failed to assign ${portName} port: ${error}`);\n }\n }\n }\n\n // Install base Panopticon skills/agents/rules from cache\n const mergeResult = mergeSkillsIntoWorkspace(workspacePath);\n const mergeTotal = mergeResult.added.length + mergeResult.updated.length;\n if (mergeTotal > 0) {\n result.steps.push(`Installed ${mergeTotal} Panopticon files (${mergeResult.added.length} new, ${mergeResult.updated.length} updated)`);\n }\n\n // Process agent templates (project template overlay — wins over Panopticon base)\n if (workspaceConfig.agent?.template_dir) {\n const templateDir = join(projectConfig.path, workspaceConfig.agent.template_dir);\n\n // Process template files\n const templateSteps = processTemplates(\n templateDir,\n workspacePath,\n placeholders,\n workspaceConfig.agent.templates\n );\n result.steps.push(...templateSteps);\n\n // Copy .claude/ directories from project template (copy_dirs replaces legacy symlinks)\n const dirsToSync = workspaceConfig.agent.copy_dirs || workspaceConfig.agent.symlinks;\n if (dirsToSync) {\n const copySteps = copyProjectTemplateDirs(templateDir, workspacePath, dirsToSync, placeholders);\n result.steps.push(...copySteps);\n }\n }\n\n // Generate .env file\n if (workspaceConfig.env?.template) {\n const envContent = replacePlaceholders(workspaceConfig.env.template, placeholders);\n writeFileSync(join(workspacePath, '.env'), envContent);\n result.steps.push('Created .env file');\n }\n\n // Process Docker compose templates\n if (workspaceConfig.docker?.compose_template) {\n const templateDir = join(projectConfig.path, workspaceConfig.docker.compose_template);\n const devcontainerDir = join(workspacePath, '.devcontainer');\n mkdirSync(devcontainerDir, { recursive: true });\n\n const templateSteps = processTemplates(templateDir, devcontainerDir, placeholders);\n result.steps.push(...templateSteps);\n\n // Copy non-template files (like Dockerfile)\n if (existsSync(templateDir)) {\n const files = readdirSync(templateDir);\n for (const file of files) {\n if (!file.endsWith('.template')) {\n const sourcePath = join(templateDir, file);\n const targetPath = join(devcontainerDir, file);\n copyFileSync(sourcePath, targetPath);\n }\n }\n }\n\n // Sanitize docker-compose files to use platform-agnostic paths\n // This fixes hardcoded /home/username or /Users/username paths\n const composeFiles = readdirSync(devcontainerDir)\n .filter(f => f.includes('compose') && (f.endsWith('.yml') || f.endsWith('.yaml')));\n for (const composeFile of composeFiles) {\n sanitizeComposeFile(join(devcontainerDir, composeFile));\n }\n if (composeFiles.length > 0) {\n result.steps.push(`Sanitized ${composeFiles.length} compose file(s) for platform compatibility`);\n }\n\n // Create ./dev symlink at workspace root pointing to .devcontainer/dev\n // Symlink keeps changes in sync - editing ./dev updates .devcontainer/dev\n const devScriptInContainer = join(devcontainerDir, 'dev');\n const devScriptAtRoot = join(workspacePath, 'dev');\n if (existsSync(devScriptInContainer) && !existsSync(devScriptAtRoot)) {\n try {\n symlinkSync('.devcontainer/dev', devScriptAtRoot);\n chmodSync(devScriptInContainer, 0o755); // Make executable\n result.steps.push('Created ./dev symlink');\n } catch (error) {\n result.errors.push(`Failed to create ./dev symlink: ${error}`);\n }\n }\n }\n\n // Note: Beads initialization is handled by the calling command (workspace.ts)\n // With beads v0.47.1+, worktrees use shared database with labels for isolation\n // The workspace.ts command creates a bead with workspace:issue-id label\n\n // Set up Cloudflare tunnel for external access (before Docker so containers can use tunnel URLs)\n if (workspaceConfig.tunnel) {\n const tunnelResult = await addTunnelIngress(workspaceConfig.tunnel, placeholders);\n result.steps.push(...tunnelResult.steps);\n if (!tunnelResult.success) {\n result.errors.push('Tunnel setup had failures (see steps for details)');\n }\n }\n\n // Create Hume EVI config and write env file for Docker (before Docker so containers pick up the config ID)\n if (workspaceConfig.hume) {\n const humeResult = await createHumeConfig(workspaceConfig.hume, placeholders);\n result.steps.push(...humeResult.steps);\n if (humeResult.configId) {\n writeFileSync(\n join(workspacePath, '.hume-config'),\n `HUME_CONFIG_ID=${humeResult.configId}\\nVITE_HUME_CONFIG_ID=${humeResult.configId}\\n`,\n );\n result.steps.push('Wrote .hume-config with Hume EVI config ID');\n }\n if (!humeResult.success) {\n result.errors.push('Hume EVI config setup had failures (see steps for details)');\n }\n }\n\n // Start Docker containers if requested\n if (startDocker) {\n // Check for Traefik\n if (workspaceConfig.docker?.traefik) {\n // Always use the installed Traefik location (~/.panopticon/traefik/), not the\n // template source in projects.yaml. The template is copied to ~/.panopticon/traefik/\n // during `pan install`, and the installed copy has the correct volume mounts\n // (dynamic configs, certs) relative to ~/.panopticon/traefik/.\n const traefikPath = join(homedir(), '.panopticon', 'traefik', 'docker-compose.yml');\n if (existsSync(traefikPath)) {\n try {\n await execAsync(`docker compose -f \"${traefikPath}\" up -d`, { cwd: join(homedir(), '.panopticon', 'traefik') });\n result.steps.push('Started Traefik');\n } catch (error: any) {\n const msg = error?.message || String(error);\n if (msg.includes('port is already allocated') || msg.includes('address already in use')) {\n // Traefik (or another reverse proxy) is already running — not an error\n result.steps.push('Traefik already running (port in use)');\n } else {\n result.errors.push(`Failed to start Traefik: ${error}`);\n }\n }\n }\n }\n\n // Start workspace containers\n const composeLocations = [\n join(workspacePath, 'docker-compose.yml'),\n join(workspacePath, 'docker-compose.yaml'),\n join(workspacePath, '.devcontainer', 'docker-compose.yml'),\n join(workspacePath, '.devcontainer', 'docker-compose.devcontainer.yml'),\n ];\n\n for (const composePath of composeLocations) {\n if (existsSync(composePath)) {\n try {\n // Don't pass -p: the compose file's `name:` field is the authority.\n // Passing -p with a different value creates a second Docker project\n // on container restart, splitting services onto separate networks.\n await execAsync(`docker compose -f \"${composePath}\" up -d --build`, { cwd: dirname(composePath), timeout: 300000 });\n result.steps.push(`Started containers from ${basename(composePath)}`);\n } catch (error) {\n result.errors.push(`Failed to start containers: ${error}`);\n }\n break;\n }\n }\n }\n\n // Pre-trust workspace directory in Claude Code so agents don't get the trust prompt\n try {\n preTrustDirectory(workspacePath);\n result.steps.push('Pre-trusted workspace in Claude Code');\n } catch {\n // Non-fatal — agent can still work, user will just see trust prompt\n }\n\n result.success = result.errors.length === 0;\n return result;\n}\n\n/**\n * Pre-register a directory as trusted in Claude Code's ~/.claude.json.\n * This prevents the \"Quick safety check: Is this a project you created or one you trust?\" prompt\n * when agents are spawned in dynamically-created workspace directories.\n */\nexport function preTrustDirectory(dirPath: string): void {\n const claudeJsonPath = join(homedir(), '.claude.json');\n if (!existsSync(claudeJsonPath)) return;\n\n const data = JSON.parse(readFileSync(claudeJsonPath, 'utf8'));\n if (!data.projects) data.projects = {};\n\n // Only add if not already present\n if (data.projects[dirPath]) {\n if (!data.projects[dirPath].hasTrustDialogAccepted) {\n data.projects[dirPath].hasTrustDialogAccepted = true;\n writeFileSync(claudeJsonPath, JSON.stringify(data, null, 2), 'utf8');\n }\n return;\n }\n\n data.projects[dirPath] = {\n allowedTools: [],\n mcpContextUris: [],\n mcpServers: {},\n enabledMcpjsonServers: [],\n disabledMcpjsonServers: [],\n hasTrustDialogAccepted: true,\n projectOnboardingSeenCount: 0,\n hasClaudeMdExternalIncludesApproved: false,\n hasClaudeMdExternalIncludesWarningShown: false,\n };\n\n writeFileSync(claudeJsonPath, JSON.stringify(data, null, 2), 'utf8');\n}\n\nexport interface WorkspaceRemoveOptions {\n projectConfig: ProjectConfig;\n featureName: string;\n dryRun?: boolean;\n}\n\nexport interface WorkspaceRemoveResult {\n success: boolean;\n errors: string[];\n steps: string[];\n}\n\n/**\n * Result of Docker container cleanup for a workspace.\n */\nexport interface DockerCleanupResult {\n /** Whether compose files were found (containers may or may not have been running) */\n containersFound: boolean;\n /** Human-readable log of cleanup steps taken */\n steps: string[];\n}\n\n/**\n * Stop Docker containers and clean up Docker-created files for a workspace.\n *\n * Extracted as a standalone function so it can be used by:\n * - removeWorkspace() during normal workspace removal\n * - deep-wipe endpoint for complete issue cleanup\n * - workspace-migrate for pre-migration cleanup\n *\n * Failures are logged but never thrown — callers should not fail if Docker is unavailable.\n */\nexport async function stopWorkspaceDocker(\n workspacePath: string,\n projectName: string,\n featureName: string,\n): Promise<DockerCleanupResult> {\n const result: DockerCleanupResult = {\n containersFound: false,\n steps: [],\n };\n\n // Find all compose files in devcontainer directory (some projects use multiple)\n const devcontainerDir = join(workspacePath, '.devcontainer');\n const composeFiles: string[] = [];\n\n if (existsSync(devcontainerDir)) {\n const possibleFiles = [\n 'docker-compose.devcontainer.yml',\n 'docker-compose.yml',\n 'compose.yml',\n 'compose.infra.yml',\n 'compose.override.yml',\n ];\n for (const file of possibleFiles) {\n const fullPath = join(devcontainerDir, file);\n if (existsSync(fullPath)) {\n composeFiles.push(fullPath);\n }\n }\n }\n\n // Fallback: check for compose file in workspace root\n if (composeFiles.length === 0) {\n const rootCompose = join(workspacePath, 'docker-compose.yml');\n if (existsSync(rootCompose)) {\n composeFiles.push(rootCompose);\n }\n }\n\n if (composeFiles.length > 0) {\n result.containersFound = true;\n try {\n const fileFlags = composeFiles.map(f => `-f \"${f}\"`).join(' ');\n const cwd = existsSync(devcontainerDir) ? devcontainerDir : workspacePath;\n\n // Derive compose project name from the dev script (same logic as dashboard)\n // or fall back to \"{projectName}-feature-{featureName}\" convention.\n let composeProjectName = `${projectName}-feature-${featureName}`;\n const devScriptPaths = [\n join(workspacePath, '.devcontainer', 'dev'),\n join(workspacePath, 'dev'),\n ];\n for (const devPath of devScriptPaths) {\n try {\n if (existsSync(devPath)) {\n const content = readFileSync(devPath, 'utf-8');\n const match = content.match(/COMPOSE_PROJECT_NAME=\"([^$\"]*)\\$\\{FEATURE_FOLDER\\}\"/);\n if (match) {\n composeProjectName = `${match[1]}feature-${featureName}`;\n break;\n }\n const literalMatch = content.match(/COMPOSE_PROJECT_NAME=\"([^\"]+)\"/);\n if (literalMatch) {\n composeProjectName = literalMatch[1];\n break;\n }\n }\n } catch {\n // Fall through to default\n }\n }\n\n await execAsync(`docker compose ${fileFlags} -p \"${composeProjectName}\" down -v --remove-orphans`, {\n cwd,\n timeout: 60000,\n });\n result.steps.push(`Stopped Docker containers (${composeFiles.length} compose files)`);\n } catch (error: any) {\n // Log but don't fail — containers might not be running\n result.steps.push(`Docker cleanup attempted (${error.message?.split('\\n')[0] || 'containers may not be running'})`);\n }\n }\n\n // Clean up Docker-created files (root-owned in containers)\n try {\n await execAsync(\n `docker run --rm -v \"${workspacePath}:/workspace\" alpine sh -c \"find /workspace -user root -delete 2>&1 | tail -100 || true\"`,\n { timeout: 30000, maxBuffer: 10 * 1024 * 1024 }\n );\n result.steps.push('Cleaned up Docker-created files');\n } catch {\n // Alpine container might not be available\n }\n\n return result;\n}\n\n/**\n * Remove a workspace\n */\nexport async function removeWorkspace(options: WorkspaceRemoveOptions): Promise<WorkspaceRemoveResult> {\n const { projectConfig, featureName, dryRun } = options;\n const result: WorkspaceRemoveResult = {\n success: true,\n errors: [],\n steps: [],\n };\n\n const workspaceConfig = projectConfig.workspace || getDefaultWorkspaceConfig();\n const workspacesDir = join(projectConfig.path, workspaceConfig.workspaces_dir || 'workspaces');\n const featureFolder = `feature-${featureName}`;\n const workspacePath = join(workspacesDir, featureFolder);\n\n if (!existsSync(workspacePath)) {\n result.success = false;\n result.errors.push(`Workspace not found at ${workspacePath}`);\n return result;\n }\n\n if (dryRun) {\n result.steps.push('[DRY RUN] Would remove workspace at: ' + workspacePath);\n return result;\n }\n\n // Stop TLDR daemon for workspace (if it exists)\n const venvPath = join(workspacePath, '.venv');\n if (existsSync(venvPath)) {\n try {\n const { getTldrDaemonService } = await import('./tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.stop();\n result.steps.push('Stopped TLDR daemon');\n } catch (error: any) {\n // Non-fatal - daemon may not be running\n console.warn(`⚠ Failed to stop TLDR daemon: ${error?.message}`);\n }\n }\n\n // Stop Docker containers and clean up Docker-created files\n const dockerResult = await stopWorkspaceDocker(workspacePath, projectConfig.name || 'workspace', featureName);\n result.steps.push(...dockerResult.steps);\n\n // Remove worktrees\n if (workspaceConfig.type === 'polyrepo' && workspaceConfig.repos) {\n for (const repo of workspaceConfig.repos) {\n const repoPath = join(projectConfig.path, repo.path);\n const targetPath = join(workspacePath, repo.name);\n const branchPrefix = repo.branch_prefix || 'feature/';\n const branchName = `${branchPrefix}${featureName}`;\n\n const worktreeResult = await removeWorktree(repoPath, targetPath, branchName);\n if (worktreeResult.success) {\n result.steps.push(`Removed worktree for ${repo.name}`);\n } else {\n result.errors.push(worktreeResult.message);\n }\n }\n } else {\n // Monorepo: remove single worktree\n const branchName = `feature/${featureName}`;\n const worktreeResult = await removeWorktree(projectConfig.path, workspacePath, branchName);\n if (worktreeResult.success) {\n result.steps.push('Removed worktree');\n } else {\n result.errors.push(worktreeResult.message);\n }\n }\n\n // Remove DNS entries\n if (workspaceConfig.dns) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n\n const dnsMethod = workspaceConfig.dns.sync_method || 'wsl2hosts';\n for (const entryPattern of workspaceConfig.dns.entries) {\n const hostname = replacePlaceholders(entryPattern, placeholders);\n if (removeDnsEntry(dnsMethod, hostname)) {\n result.steps.push(`Removed DNS entry: ${hostname}`);\n }\n }\n }\n\n // Remove Cloudflare tunnel entries\n if (workspaceConfig.tunnel) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n const tunnelResult = await removeTunnelIngress(workspaceConfig.tunnel, placeholders);\n result.steps.push(...tunnelResult.steps);\n }\n\n // Remove Hume EVI config\n if (workspaceConfig.hume) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n const humeResult = await deleteHumeConfig(workspaceConfig.hume, placeholders);\n result.steps.push(...humeResult.steps);\n }\n\n // Release ports\n if (workspaceConfig.ports) {\n for (const [portName] of Object.entries(workspaceConfig.ports)) {\n const portFile = join(projectConfig.path, `.${portName}-ports`);\n if (releasePort(portFile, featureFolder)) {\n result.steps.push(`Released ${portName} port`);\n }\n }\n }\n\n // Remove workspace directory\n try {\n await execAsync(`rm -rf \"${workspacePath}\"`, { maxBuffer: 10 * 1024 * 1024 });\n result.steps.push('Removed workspace directory');\n } catch (error) {\n result.errors.push(`Failed to remove workspace directory: ${error}`);\n }\n\n result.success = result.errors.length === 0;\n return result;\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","readdirSync","copyFileSync","join","dirname","copyDir","count","devcontainerDir"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
updateItemStatus,
|
|
69
69
|
updateSubItemStatus,
|
|
70
70
|
wakeSpecialistOrQueue
|
|
71
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-NLN3ZLCN.js";
|
|
72
72
|
import "../chunk-JQBV3Q2W.js";
|
|
73
73
|
import {
|
|
74
74
|
archivePlanning,
|
|
@@ -92,7 +92,7 @@ import {
|
|
|
92
92
|
init_workspace_manager,
|
|
93
93
|
mergeSkillsIntoWorkspace,
|
|
94
94
|
removeWorkspace
|
|
95
|
-
} from "../chunk-
|
|
95
|
+
} from "../chunk-S7EJ2OLR.js";
|
|
96
96
|
import {
|
|
97
97
|
detectDnsSyncMethod,
|
|
98
98
|
detectPlatform,
|
|
@@ -134,7 +134,7 @@ import {
|
|
|
134
134
|
saveSessionId,
|
|
135
135
|
spawnAgent,
|
|
136
136
|
stopAgent
|
|
137
|
-
} from "../chunk-
|
|
137
|
+
} from "../chunk-OMOEGJDB.js";
|
|
138
138
|
import {
|
|
139
139
|
checkHook,
|
|
140
140
|
clearHook,
|
|
@@ -2749,7 +2749,7 @@ async function doneCommand(id, options = {}) {
|
|
|
2749
2749
|
const issueId = id.replace(/^agent-/i, "").toUpperCase();
|
|
2750
2750
|
const agentId = `agent-${issueId.toLowerCase()}`;
|
|
2751
2751
|
if (!options.force) {
|
|
2752
|
-
const { getAgentState: getAgentState2 } = await import("../agents-
|
|
2752
|
+
const { getAgentState: getAgentState2 } = await import("../agents-RL2KUSP3.js");
|
|
2753
2753
|
const agentState = getAgentState2(agentId);
|
|
2754
2754
|
const workspacePath = agentState?.workspace;
|
|
2755
2755
|
if (workspacePath && existsSync13(workspacePath)) {
|
|
@@ -2835,7 +2835,7 @@ async function doneCommand(id, options = {}) {
|
|
|
2835
2835
|
}
|
|
2836
2836
|
}
|
|
2837
2837
|
{
|
|
2838
|
-
const { getAgentState: getAgentState2 } = await import("../agents-
|
|
2838
|
+
const { getAgentState: getAgentState2 } = await import("../agents-RL2KUSP3.js");
|
|
2839
2839
|
const agentState = getAgentState2(`agent-${issueId.toLowerCase()}`);
|
|
2840
2840
|
const workspacePath = agentState?.workspace;
|
|
2841
2841
|
if (workspacePath && existsSync13(workspacePath)) {
|
|
@@ -2892,7 +2892,7 @@ async function doneCommand(id, options = {}) {
|
|
|
2892
2892
|
console.log(chalk12.dim(" LINEAR_API_KEY not set - skipping status update"));
|
|
2893
2893
|
}
|
|
2894
2894
|
}
|
|
2895
|
-
const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-
|
|
2895
|
+
const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-RL2KUSP3.js");
|
|
2896
2896
|
const existingState = getAgentState2(agentId);
|
|
2897
2897
|
if (existingState) {
|
|
2898
2898
|
existingState.status = "stopped";
|
|
@@ -5338,7 +5338,7 @@ Previous state: ${issue.state}`
|
|
|
5338
5338
|
console.log(chalk21.green(`\u2713 ${issue.identifier} reopened and ready for re-work`));
|
|
5339
5339
|
console.log("");
|
|
5340
5340
|
try {
|
|
5341
|
-
const { getAgentState: getAgentState2 } = await import("../agents-
|
|
5341
|
+
const { getAgentState: getAgentState2 } = await import("../agents-RL2KUSP3.js");
|
|
5342
5342
|
const agentId = `agent-${id.toLowerCase()}`;
|
|
5343
5343
|
const agentState = getAgentState2(agentId);
|
|
5344
5344
|
const agentRunning = agentState?.status === "running" || agentState?.status === "starting";
|
|
@@ -6495,7 +6495,7 @@ async function stopTldrDaemon(workspacePath) {
|
|
|
6495
6495
|
async function stopDocker(workspacePath, projectName, issueLower) {
|
|
6496
6496
|
const step = "teardown:docker";
|
|
6497
6497
|
try {
|
|
6498
|
-
const { stopWorkspaceDocker } = await import("../workspace-manager-
|
|
6498
|
+
const { stopWorkspaceDocker } = await import("../workspace-manager-6RP5A5HF.js");
|
|
6499
6499
|
await stopWorkspaceDocker(workspacePath, projectName, issueLower);
|
|
6500
6500
|
return stepOk(step, ["Stopped Docker containers"]);
|
|
6501
6501
|
} catch {
|
|
@@ -11153,7 +11153,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
11153
11153
|
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
|
|
11154
11154
|
const resolved = resolveProjectFromIssue2(issueId);
|
|
11155
11155
|
if (resolved) {
|
|
11156
|
-
const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-
|
|
11156
|
+
const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-X4OGA7WX.js");
|
|
11157
11157
|
const result = await spawnEphemeralSpecialist2(resolved.projectKey, "test-agent", {
|
|
11158
11158
|
issueId,
|
|
11159
11159
|
workspace,
|
|
@@ -11489,8 +11489,8 @@ async function checkSpecialistQueues() {
|
|
|
11489
11489
|
spawnEphemeralSpecialist: spawnEphemeralSpecialist2,
|
|
11490
11490
|
getTmuxSessionName: getTmuxSessionName2,
|
|
11491
11491
|
isRunning: isRunning3
|
|
11492
|
-
} = await import("../specialists-
|
|
11493
|
-
const { getAgentRuntimeState: getAgentRuntimeState2 } = await import("../agents-
|
|
11492
|
+
} = await import("../specialists-X4OGA7WX.js");
|
|
11493
|
+
const { getAgentRuntimeState: getAgentRuntimeState2 } = await import("../agents-RL2KUSP3.js");
|
|
11494
11494
|
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
|
|
11495
11495
|
const specialistTypes = ["review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
11496
11496
|
for (const specialistType of specialistTypes) {
|
|
@@ -11620,7 +11620,7 @@ async function runPatrol() {
|
|
|
11620
11620
|
statuses[issueId].mergeStatus = "merged";
|
|
11621
11621
|
statuses[issueId].readyForMerge = false;
|
|
11622
11622
|
writeFileSync19(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
|
|
11623
|
-
const { postMergeLifecycle } = await import("../merge-agent-
|
|
11623
|
+
const { postMergeLifecycle } = await import("../merge-agent-7L7MWJEC.js");
|
|
11624
11624
|
postMergeLifecycle(issueId, resolved.projectPath).catch(
|
|
11625
11625
|
(err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`)
|
|
11626
11626
|
);
|
|
@@ -13520,7 +13520,7 @@ import { promisify as promisify16 } from "util";
|
|
|
13520
13520
|
var execAsync16 = promisify16(exec16);
|
|
13521
13521
|
async function listLogsCommand(project2, type, options) {
|
|
13522
13522
|
try {
|
|
13523
|
-
const { listRunLogs } = await import("../specialist-logs-
|
|
13523
|
+
const { listRunLogs } = await import("../specialist-logs-B7UC3UDO.js");
|
|
13524
13524
|
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
13525
13525
|
const runs = listRunLogs(project2, type, { limit });
|
|
13526
13526
|
if (options.json) {
|
|
@@ -13565,7 +13565,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
|
|
|
13565
13565
|
}
|
|
13566
13566
|
async function viewLogCommand(project2, type, runId, options) {
|
|
13567
13567
|
try {
|
|
13568
|
-
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-
|
|
13568
|
+
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-B7UC3UDO.js");
|
|
13569
13569
|
const content = getRunLog(project2, type, runId);
|
|
13570
13570
|
if (!content) {
|
|
13571
13571
|
console.error(`\u274C Run log not found: ${runId}`);
|
|
@@ -13589,8 +13589,8 @@ async function viewLogCommand(project2, type, runId, options) {
|
|
|
13589
13589
|
}
|
|
13590
13590
|
async function tailLogCommand(project2, type) {
|
|
13591
13591
|
try {
|
|
13592
|
-
const { getRunLogPath } = await import("../specialist-logs-
|
|
13593
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
13592
|
+
const { getRunLogPath } = await import("../specialist-logs-B7UC3UDO.js");
|
|
13593
|
+
const { getProjectSpecialistMetadata } = await import("../specialists-X4OGA7WX.js");
|
|
13594
13594
|
const metadata = getProjectSpecialistMetadata(project2, type);
|
|
13595
13595
|
if (!metadata.currentRun) {
|
|
13596
13596
|
console.error(`\u274C No active run for ${project2}/${type}`);
|
|
@@ -13659,7 +13659,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13659
13659
|
console.log(" Use --force to confirm.");
|
|
13660
13660
|
process.exit(1);
|
|
13661
13661
|
}
|
|
13662
|
-
const { cleanupAllLogs } = await import("../specialist-logs-
|
|
13662
|
+
const { cleanupAllLogs } = await import("../specialist-logs-B7UC3UDO.js");
|
|
13663
13663
|
console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
|
|
13664
13664
|
const results = cleanupAllLogs();
|
|
13665
13665
|
console.log(`
|
|
@@ -13686,7 +13686,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13686
13686
|
console.log(" Use --force to confirm.");
|
|
13687
13687
|
process.exit(1);
|
|
13688
13688
|
}
|
|
13689
|
-
const { cleanupOldLogs } = await import("../specialist-logs-
|
|
13689
|
+
const { cleanupOldLogs } = await import("../specialist-logs-B7UC3UDO.js");
|
|
13690
13690
|
const { getSpecialistRetention } = await import("../projects-BPGM6IFB.js");
|
|
13691
13691
|
const retention = getSpecialistRetention(projectOrAll);
|
|
13692
13692
|
console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
|
|
@@ -14445,7 +14445,7 @@ async function projectAddCommand(projectPath, options = {}) {
|
|
|
14445
14445
|
}
|
|
14446
14446
|
const isPolyrepo = !hasRootGit && subRepos.length > 0;
|
|
14447
14447
|
try {
|
|
14448
|
-
const { preTrustDirectory } = await import("../workspace-manager-
|
|
14448
|
+
const { preTrustDirectory } = await import("../workspace-manager-6RP5A5HF.js");
|
|
14449
14449
|
preTrustDirectory(fullPath);
|
|
14450
14450
|
} catch {
|
|
14451
14451
|
}
|
package/dist/dashboard/server.js
CHANGED
|
@@ -146982,7 +146982,9 @@ async function createWorktree(repoPath, targetPath, branchName, defaultBranch =
|
|
|
146982
146982
|
await execAsync4("git worktree prune", { cwd: repoPath });
|
|
146983
146983
|
const { stdout: localBranches } = await execAsync4("git branch --list", { cwd: repoPath });
|
|
146984
146984
|
const { stdout: remoteBranches } = await execAsync4("git branch -r --list", { cwd: repoPath });
|
|
146985
|
-
const
|
|
146985
|
+
const localList = localBranches.split("\n").map((b) => b.replace(/^[*+\s]+/, "").trim()).filter(Boolean);
|
|
146986
|
+
const remoteList = remoteBranches.split("\n").map((b) => b.trim()).filter(Boolean);
|
|
146987
|
+
const branchExists = localList.includes(branchName) || remoteList.includes(`origin/${branchName}`);
|
|
146986
146988
|
if (branchExists) {
|
|
146987
146989
|
await execAsync4(`git worktree add "${targetPath}" "${branchName}"`, { cwd: repoPath });
|
|
146988
146990
|
} else {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
recordWake,
|
|
9
9
|
spawnEphemeralSpecialist,
|
|
10
10
|
wakeSpecialist
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NLN3ZLCN.js";
|
|
12
12
|
import "./chunk-JQBV3Q2W.js";
|
|
13
13
|
import {
|
|
14
14
|
init_workspace_config,
|
|
@@ -589,7 +589,7 @@ async function postMergeLifecycle(issueId, projectPath) {
|
|
|
589
589
|
console.warn(`[merge-agent] Beads compaction failed: ${err}`);
|
|
590
590
|
}
|
|
591
591
|
try {
|
|
592
|
-
const { getAgentState, saveAgentState } = await import("./agents-
|
|
592
|
+
const { getAgentState, saveAgentState } = await import("./agents-RL2KUSP3.js");
|
|
593
593
|
const { killSession, sessionExists: sessionExists2 } = await import("./tmux-X2I5SAIJ.js");
|
|
594
594
|
const agentId = `agent-${issueId.toLowerCase()}`;
|
|
595
595
|
const agentState = getAgentState(agentId);
|
|
@@ -610,7 +610,7 @@ async function postMergeLifecycle(issueId, projectPath) {
|
|
|
610
610
|
}
|
|
611
611
|
try {
|
|
612
612
|
const { findWorkspacePath } = await import("./archive-planning-54J6EP6A.js");
|
|
613
|
-
const { stopWorkspaceDocker } = await import("./workspace-manager-
|
|
613
|
+
const { stopWorkspaceDocker } = await import("./workspace-manager-6RP5A5HF.js");
|
|
614
614
|
const issueLower = issueId.toLowerCase();
|
|
615
615
|
const workspacePath = findWorkspacePath(projectPath, issueLower);
|
|
616
616
|
if (workspacePath) {
|
|
@@ -1210,7 +1210,7 @@ Report any issues or conflicts you encountered.`;
|
|
|
1210
1210
|
console.warn(`[merge-agent] Could not resolve project for ${issueId} \u2014 falling back to global specialist. Check projects.yaml configuration.`);
|
|
1211
1211
|
}
|
|
1212
1212
|
if (mergeProjectKey) {
|
|
1213
|
-
const { getAgentRuntimeState, saveAgentRuntimeState } = await import("./agents-
|
|
1213
|
+
const { getAgentRuntimeState, saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
|
|
1214
1214
|
const IDLE_POLL_INTERVAL = 3e3;
|
|
1215
1215
|
const IDLE_MAX_WAIT = 36e4;
|
|
1216
1216
|
const idleStart = Date.now();
|
|
@@ -1722,4 +1722,4 @@ export {
|
|
|
1722
1722
|
spawnMergeAgentForBranches,
|
|
1723
1723
|
syncMainIntoWorkspace
|
|
1724
1724
|
};
|
|
1725
|
-
//# sourceMappingURL=merge-agent-
|
|
1725
|
+
//# sourceMappingURL=merge-agent-7L7MWJEC.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getRecentRunLogs,
|
|
3
3
|
init_specialist_logs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NLN3ZLCN.js";
|
|
5
5
|
import "./chunk-JQBV3Q2W.js";
|
|
6
6
|
import {
|
|
7
7
|
getModelId,
|
|
@@ -257,4 +257,4 @@ export {
|
|
|
257
257
|
regenerateContextDigest,
|
|
258
258
|
scheduleDigestGeneration
|
|
259
259
|
};
|
|
260
|
-
//# sourceMappingURL=specialist-context-
|
|
260
|
+
//# sourceMappingURL=specialist-context-L37RF6Z5.js.map
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
isRunLogActive,
|
|
17
17
|
listRunLogs,
|
|
18
18
|
parseLogMetadata
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-NLN3ZLCN.js";
|
|
20
20
|
import "./chunk-JQBV3Q2W.js";
|
|
21
21
|
import "./chunk-PFA5XE2V.js";
|
|
22
22
|
import "./chunk-USYP2SBE.js";
|
|
@@ -45,4 +45,4 @@ export {
|
|
|
45
45
|
listRunLogs,
|
|
46
46
|
parseLogMetadata
|
|
47
47
|
};
|
|
48
|
-
//# sourceMappingURL=specialist-logs-
|
|
48
|
+
//# sourceMappingURL=specialist-logs-B7UC3UDO.js.map
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
wakeSpecialist,
|
|
56
56
|
wakeSpecialistOrQueue,
|
|
57
57
|
wakeSpecialistWithTask
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-NLN3ZLCN.js";
|
|
59
59
|
import "./chunk-JQBV3Q2W.js";
|
|
60
60
|
import "./chunk-PFA5XE2V.js";
|
|
61
61
|
import "./chunk-USYP2SBE.js";
|
|
@@ -123,4 +123,4 @@ export {
|
|
|
123
123
|
wakeSpecialistOrQueue,
|
|
124
124
|
wakeSpecialistWithTask
|
|
125
125
|
};
|
|
126
|
-
//# sourceMappingURL=specialists-
|
|
126
|
+
//# sourceMappingURL=specialists-X4OGA7WX.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
preTrustDirectory,
|
|
5
5
|
removeWorkspace,
|
|
6
6
|
stopWorkspaceDocker
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-S7EJ2OLR.js";
|
|
8
8
|
import "./chunk-JT4O4YVM.js";
|
|
9
9
|
import "./chunk-ZWZNEA26.js";
|
|
10
10
|
import "./chunk-DW3PKGIS.js";
|
|
@@ -19,4 +19,4 @@ export {
|
|
|
19
19
|
removeWorkspace,
|
|
20
20
|
stopWorkspaceDocker
|
|
21
21
|
};
|
|
22
|
-
//# sourceMappingURL=workspace-manager-
|
|
22
|
+
//# sourceMappingURL=workspace-manager-6RP5A5HF.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/skills-merge.ts","../src/lib/workspace-manager.ts"],"sourcesContent":["import {\n existsSync,\n readdirSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n copyFileSync,\n statSync,\n} from 'fs';\nimport { join, relative, dirname } from 'path';\nimport { SKILLS_DIR, CACHE_AGENTS_DIR, CACHE_RULES_DIR } from './paths.js';\nimport {\n readManifest,\n writeManifest,\n collectSourceFiles,\n hashFile,\n setManifestEntry,\n compareFileToManifest,\n type Manifest,\n} from './manifest.js';\n\nexport interface MergeResult {\n added: string[];\n updated: string[];\n skipped: string[];\n overlayed: string[];\n}\n\n/**\n * Copy all files from a source directory into a target directory,\n * preserving subdirectory structure. Returns the list of relative paths copied.\n */\nfunction copyTree(sourceDir: string, targetDir: string): string[] {\n const copied: string[] = [];\n if (!existsSync(sourceDir)) return copied;\n\n function walk(dir: string): void {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const rel = relative(sourceDir, fullPath);\n const targetPath = join(targetDir, rel);\n mkdirSync(dirname(targetPath), { recursive: true });\n copyFileSync(fullPath, targetPath);\n copied.push(rel);\n }\n }\n }\n\n walk(sourceDir);\n return copied;\n}\n\n/**\n * Merge Panopticon skills, agents, and rules into a workspace using file copies.\n *\n * Flow:\n * 1. Copy from cache (skills, agent-definitions, rules) → workspace/.claude/\n * 2. Write manifest tracking what was placed\n *\n * Project template overlay is handled separately by workspace-manager.ts\n * (processTemplates + createSymlinks → now also copy-based).\n */\nexport function mergeSkillsIntoWorkspace(workspacePath: string): MergeResult {\n const claudeDir = join(workspacePath, '.claude');\n const manifestPath = join(claudeDir, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n\n const result: MergeResult = {\n added: [],\n updated: [],\n skipped: [],\n overlayed: [],\n };\n\n // Ensure base directories exist\n mkdirSync(join(claudeDir, 'skills'), { recursive: true });\n mkdirSync(join(claudeDir, 'agents'), { recursive: true });\n\n // Sources to copy: category → source cache directory\n const sources: Array<{ category: string; sourceDir: string; targetSubdir: string }> = [\n { category: 'skills', sourceDir: SKILLS_DIR, targetSubdir: 'skills' },\n { category: 'agents', sourceDir: CACHE_AGENTS_DIR, targetSubdir: 'agents' },\n { category: 'rules', sourceDir: CACHE_RULES_DIR, targetSubdir: 'rules' },\n ];\n\n for (const { category, sourceDir, targetSubdir } of sources) {\n if (!existsSync(sourceDir)) continue;\n\n const prefix = targetSubdir ? `${targetSubdir}/` : '';\n const files = collectSourceFiles(sourceDir, '');\n\n for (const file of files) {\n const relativePath = `${prefix}${file.relativePath}`;\n const targetPath = join(claudeDir, relativePath);\n const sourceHash = hashFile(file.absolutePath);\n\n // Check status against manifest\n const status = compareFileToManifest(targetPath, relativePath, manifest);\n\n switch (status.action) {\n case 'new':\n // File doesn't exist at target — copy it\n mkdirSync(dirname(targetPath), { recursive: true });\n copyFileSync(file.absolutePath, targetPath);\n setManifestEntry(manifest, relativePath, sourceHash, 'panopticon');\n result.added.push(relativePath);\n break;\n\n case 'update':\n // File exists and matches manifest — safe to overwrite with latest\n copyFileSync(file.absolutePath, targetPath);\n setManifestEntry(manifest, relativePath, sourceHash, 'panopticon');\n result.updated.push(relativePath);\n break;\n\n case 'modified':\n // User modified the file — skip to preserve their changes\n result.skipped.push(`${relativePath} (modified by user)`);\n break;\n\n case 'user-owned':\n // File exists but wasn't placed by us — never touch\n result.skipped.push(`${relativePath} (user-owned)`);\n break;\n }\n }\n }\n\n // Write updated manifest\n writeManifest(manifestPath, manifest);\n\n return result;\n}\n\n/**\n * Apply project template overlay on top of Panopticon base files in a workspace.\n *\n * This copies files from the project's agent template directory into\n * workspace/.claude/, overwriting Panopticon files where the project\n * provides its own version. Updates the manifest with source=\"project-template\".\n *\n * @param workspacePath - Path to the workspace\n * @param templateDir - Absolute path to the project's agent template directory\n * @param templates - Optional list of specific template files to process (source → target mappings)\n */\nexport function applyProjectTemplateOverlay(\n workspacePath: string,\n templateDir: string,\n templates?: Array<{ source: string; target: string }>,\n): string[] {\n const claudeDir = join(workspacePath, '.claude');\n const manifestPath = join(claudeDir, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n const overlayed: string[] = [];\n\n if (!existsSync(templateDir)) return overlayed;\n\n if (templates && templates.length > 0) {\n // Process specific template mappings\n for (const { source, target } of templates) {\n const sourcePath = join(templateDir, source);\n if (!existsSync(sourcePath)) continue;\n\n const targetPath = join(workspacePath, target);\n mkdirSync(dirname(targetPath), { recursive: true });\n\n // Read template content and check if it's a template file\n if (source.endsWith('.template')) {\n // Template files are handled by workspace-manager's processTemplates\n // We just track them in the manifest after they're processed\n continue;\n }\n\n copyFileSync(sourcePath, targetPath);\n\n // Track in manifest if it's under .claude/\n if (target.startsWith('.claude/')) {\n const relativePath = target.slice('.claude/'.length);\n const hash = hashFile(targetPath);\n setManifestEntry(manifest, relativePath, hash, 'project-template');\n overlayed.push(relativePath);\n }\n }\n } else {\n // Copy all .claude/ subdirectories from template dir\n const claudeInTemplate = join(templateDir, '.claude');\n if (existsSync(claudeInTemplate)) {\n const copied = copyTree(claudeInTemplate, claudeDir);\n for (const rel of copied) {\n const targetPath = join(claudeDir, rel);\n const hash = hashFile(targetPath);\n setManifestEntry(manifest, rel, hash, 'project-template');\n overlayed.push(rel);\n }\n }\n }\n\n // Write updated manifest\n writeManifest(manifestPath, manifest);\n\n return overlayed;\n}\n\n// ─── Legacy exports (kept for migration, to be removed in future) ───\n\n/**\n * @deprecated No longer needed — skills are copies, not symlinks. Kept for migration.\n */\nexport function cleanupGitignore(gitignorePath: string): {\n cleaned: boolean;\n duplicatesRemoved: number;\n entriesAfter: number;\n} {\n if (!existsSync(gitignorePath)) {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n const PANOPTICON_HEADER = '# Panopticon-managed symlinks (not committed)';\n let content: string;\n try {\n content = readFileSync(gitignorePath, 'utf-8');\n } catch {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n // If no Panopticon section, nothing to clean\n if (!content.includes(PANOPTICON_HEADER)) {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n\n // Remove the entire Panopticon section (skills are copies now, not symlinks)\n const lines = content.split('\\n');\n const newLines: string[] = [];\n let inPanopticonSection = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === PANOPTICON_HEADER) {\n inPanopticonSection = true;\n continue;\n }\n if (inPanopticonSection) {\n if (trimmed.startsWith('#') && trimmed !== '') {\n inPanopticonSection = false;\n newLines.push(line);\n } else if (trimmed === '') {\n // Skip blank lines in Panopticon section\n continue;\n }\n // Skip entries in Panopticon section\n continue;\n }\n newLines.push(line);\n }\n\n // Write cleaned file\n try {\n writeFileSync(gitignorePath, newLines.join('\\n'), 'utf-8');\n return { cleaned: true, duplicatesRemoved: 0, entriesAfter: 0 };\n } catch {\n return { cleaned: false, duplicatesRemoved: 0, entriesAfter: 0 };\n }\n}\n\n/**\n * @deprecated No longer needed — skills are copies, not symlinks. Kept for migration.\n */\nexport function cleanupWorkspaceGitignore(workspacePath: string): {\n cleaned: boolean;\n duplicatesRemoved: number;\n entriesAfter: number;\n} {\n const gitignorePath = join(workspacePath, '.claude', 'skills', '.gitignore');\n return cleanupGitignore(gitignorePath);\n}\n","/**\n * Workspace Manager\n *\n * Handles workspace creation and removal for both monorepo and polyrepo projects.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, copyFileSync, symlinkSync, chmodSync, realpathSync, rmSync, statSync } from 'fs';\nimport { join, dirname, basename, extname, resolve } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport {\n ProjectConfig,\n WorkspaceConfig,\n TemplatePlaceholders,\n replacePlaceholders,\n getDefaultWorkspaceConfig,\n} from './workspace-config.js';\nimport { addDnsEntry, removeDnsEntry, syncDnsToWindows } from './dns.js';\nimport { addTunnelIngress, removeTunnelIngress } from './tunnel.js';\nimport { createHumeConfig, deleteHumeConfig } from './hume.js';\nimport { mergeSkillsIntoWorkspace } from './skills-merge.js';\n\nconst execAsync = promisify(exec);\n\nexport interface WorkspaceCreateOptions {\n projectConfig: ProjectConfig;\n featureName: string;\n startDocker?: boolean;\n dryRun?: boolean;\n}\n\nexport interface WorkspaceCreateResult {\n success: boolean;\n workspacePath: string;\n errors: string[];\n steps: string[];\n}\n\n/**\n * Create placeholders for template substitution\n */\nfunction createPlaceholders(\n projectConfig: ProjectConfig,\n featureName: string,\n workspacePath: string\n): TemplatePlaceholders {\n const featureFolder = `feature-${featureName}`;\n const domain = projectConfig.workspace?.dns?.domain || 'localhost';\n\n return {\n FEATURE_NAME: featureName,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${featureName}`,\n COMPOSE_PROJECT: `${basename(projectConfig.path)}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: basename(projectConfig.path),\n PROJECT_PATH: projectConfig.path,\n PROJECTS_DIR: dirname(projectConfig.path),\n WORKSPACE_PATH: workspacePath,\n HOME: homedir(),\n };\n}\n\n/**\n * Sanitize docker-compose files to use platform-agnostic paths\n * Replaces hardcoded /home/username paths with ${HOME}\n */\nfunction sanitizeComposeFile(filePath: string): void {\n if (!existsSync(filePath)) return;\n\n let content = readFileSync(filePath, 'utf-8');\n const originalContent = content;\n\n // Pattern to match hardcoded home paths like /home/username or /Users/username\n // Replace with ${HOME} which docker-compose expands\n const homePatterns = [\n /\\/home\\/[a-zA-Z0-9_-]+\\//g, // Linux: /home/username/\n /\\/Users\\/[a-zA-Z0-9_-]+\\//g, // macOS: /Users/username/\n ];\n\n for (const pattern of homePatterns) {\n content = content.replace(pattern, '${HOME}/');\n }\n\n if (content !== originalContent) {\n writeFileSync(filePath, content, 'utf-8');\n }\n}\n\n/**\n * Validate feature name (alphanumeric and hyphens only)\n */\nfunction validateFeatureName(name: string): boolean {\n return /^[a-zA-Z0-9-]+$/.test(name);\n}\n\n/**\n * Create a git worktree\n * @param repoPath Path to the source git repository\n * @param targetPath Where to create the worktree\n * @param branchName Name of the feature branch to create/checkout\n * @param defaultBranch Base branch to create new branches from (default: 'main')\n */\nasync function createWorktree(\n repoPath: string,\n targetPath: string,\n branchName: string,\n defaultBranch: string = 'main'\n): Promise<{ success: boolean; message: string }> {\n try {\n // Fetch latest from origin\n await execAsync('git fetch origin', { cwd: repoPath });\n\n // Prune stale worktree entries (e.g., from deleted workspaces)\n await execAsync('git worktree prune', { cwd: repoPath });\n\n // Check if branch exists locally or remotely\n const { stdout: localBranches } = await execAsync('git branch --list', { cwd: repoPath });\n const { stdout: remoteBranches } = await execAsync('git branch -r --list', { cwd: repoPath });\n\n const branchExists =\n localBranches.includes(branchName) ||\n remoteBranches.includes(`origin/${branchName}`);\n\n if (branchExists) {\n await execAsync(`git worktree add \"${targetPath}\" \"${branchName}\"`, { cwd: repoPath });\n } else {\n // Create new branch from the configured default branch\n await execAsync(`git worktree add \"${targetPath}\" -b \"${branchName}\" \"${defaultBranch}\"`, { cwd: repoPath });\n }\n\n // Configure beads role so agents don't get \"beads.role not configured\" warnings\n await execAsync('git config beads.role agent', { cwd: targetPath }).catch(() => {});\n\n return { success: true, message: `Created worktree at ${targetPath}` };\n } catch (error) {\n return { success: false, message: `Failed to create worktree: ${error}` };\n }\n}\n\n/**\n * Remove a git worktree\n */\nasync function removeWorktree(\n repoPath: string,\n targetPath: string,\n branchName: string\n): Promise<{ success: boolean; message: string }> {\n try {\n // Remove worktree\n await execAsync(`git worktree remove \"${targetPath}\" --force`, { cwd: repoPath }).catch(() => {});\n\n // Optionally delete the branch\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: repoPath }).catch(() => {});\n\n return { success: true, message: `Removed worktree at ${targetPath}` };\n } catch (error) {\n return { success: false, message: `Failed to remove worktree: ${error}` };\n }\n}\n\n// DNS functions (addWsl2HostEntry, removeWsl2HostEntry, syncDnsToWindows)\n// are now in src/lib/dns.ts and imported above\n\n/**\n * Assign a port from a range\n */\nfunction assignPort(\n portFile: string,\n featureFolder: string,\n range: [number, number]\n): number {\n // Ensure port file exists\n if (!existsSync(portFile)) {\n mkdirSync(dirname(portFile), { recursive: true });\n writeFileSync(portFile, '');\n }\n\n const content = readFileSync(portFile, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n\n // Check if already assigned\n for (const line of lines) {\n const [folder, port] = line.split(':');\n if (folder === featureFolder) {\n return parseInt(port, 10);\n }\n }\n\n // Find next available port\n const usedPorts = new Set(lines.map(l => parseInt(l.split(':')[1], 10)));\n for (let port = range[0]; port <= range[1]; port++) {\n if (!usedPorts.has(port)) {\n writeFileSync(portFile, content + (content.endsWith('\\n') ? '' : '\\n') + `${featureFolder}:${port}\\n`);\n return port;\n }\n }\n\n throw new Error(`No available ports in range ${range[0]}-${range[1]}`);\n}\n\n/**\n * Release a port assignment\n */\nfunction releasePort(portFile: string, featureFolder: string): boolean {\n try {\n if (!existsSync(portFile)) return true;\n\n let content = readFileSync(portFile, 'utf-8');\n const lines = content.split('\\n').filter(line => !line.startsWith(`${featureFolder}:`));\n writeFileSync(portFile, lines.join('\\n'));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Process template files with placeholder replacement\n */\nfunction processTemplates(\n templateDir: string,\n targetDir: string,\n placeholders: TemplatePlaceholders,\n templates?: Array<{ source: string; target: string }>\n): string[] {\n const steps: string[] = [];\n\n if (!existsSync(templateDir)) {\n return steps;\n }\n\n // If specific templates are defined, process those\n if (templates && templates.length > 0) {\n for (const { source, target } of templates) {\n const sourcePath = join(templateDir, source);\n const targetPath = join(targetDir, target);\n\n if (existsSync(sourcePath)) {\n const content = readFileSync(sourcePath, 'utf-8');\n const processed = replacePlaceholders(content, placeholders);\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, processed);\n steps.push(`Processed template: ${source} -> ${target}`);\n }\n }\n } else {\n // Process all .template files\n const files = readdirSync(templateDir);\n for (const file of files) {\n if (file.endsWith('.template')) {\n const sourcePath = join(templateDir, file);\n const targetPath = join(targetDir, file.replace('.template', ''));\n\n const content = readFileSync(sourcePath, 'utf-8');\n const processed = replacePlaceholders(content, placeholders);\n writeFileSync(targetPath, processed);\n // Shell scripts need execute permission\n const targetName = file.replace('.template', '');\n if (targetName === 'dev' || targetName.endsWith('.sh')) {\n chmodSync(targetPath, 0o755);\n }\n steps.push(`Processed template: ${file}`);\n }\n }\n }\n\n return steps;\n}\n\n/**\n * @deprecated Use copyProjectTemplateDirs instead. Kept for non-.claude paths.\n */\nfunction createSymlinks(\n sourceDir: string,\n targetDir: string,\n symlinks: string[]\n): string[] {\n const steps: string[] = [];\n\n for (const symlink of symlinks) {\n const sourcePath = join(sourceDir, symlink);\n const targetPath = join(targetDir, symlink);\n\n if (existsSync(sourcePath)) {\n mkdirSync(dirname(targetPath), { recursive: true });\n try {\n symlinkSync(sourcePath, targetPath);\n steps.push(`Created symlink: ${symlink}`);\n } catch {\n // Symlink might already exist\n }\n }\n }\n\n return steps;\n}\n\n/**\n * Copy project template directories into workspace (replaces symlinks).\n * Recursively copies all files from each source directory.\n */\nconst TEXT_EXTENSIONS = new Set([\n '.md', '.sh', '.yml', '.yaml', '.json', '.ts', '.js', '.env', '.txt', '.toml', '.template',\n]);\n\nfunction copyProjectTemplateDirs(\n sourceDir: string,\n targetDir: string,\n dirs: string[],\n placeholders?: TemplatePlaceholders\n): string[] {\n const steps: string[] = [];\n\n for (const dir of dirs) {\n const sourcePath = join(sourceDir, dir);\n const targetPath = join(targetDir, dir);\n\n if (!existsSync(sourcePath)) continue;\n\n // Recursively copy all files, applying placeholder substitution to text files\n function copyDir(src: string, dest: string): number {\n let count = 0;\n mkdirSync(dest, { recursive: true });\n const entries = readdirSync(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcEntry = join(src, entry.name);\n const destEntry = join(dest, entry.name);\n if (entry.isDirectory()) {\n count += copyDir(srcEntry, destEntry);\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n if (placeholders && TEXT_EXTENSIONS.has(ext)) {\n const content = readFileSync(srcEntry, 'utf-8');\n writeFileSync(destEntry, replacePlaceholders(content, placeholders));\n } else {\n copyFileSync(srcEntry, destEntry);\n }\n count++;\n }\n }\n return count;\n }\n\n const count = copyDir(sourcePath, targetPath);\n steps.push(`Copied ${count} files from project template: ${dir}`);\n }\n\n return steps;\n}\n\n/**\n * Create a workspace\n */\nexport async function createWorkspace(options: WorkspaceCreateOptions): Promise<WorkspaceCreateResult> {\n const { projectConfig, featureName, startDocker, dryRun } = options;\n const result: WorkspaceCreateResult = {\n success: true,\n workspacePath: '',\n errors: [],\n steps: [],\n };\n\n // Validate feature name\n if (!validateFeatureName(featureName)) {\n result.success = false;\n result.errors.push('Invalid feature name. Use alphanumeric and hyphens only.');\n return result;\n }\n\n // Reject 'main' as feature name\n if (featureName === 'main') {\n result.success = false;\n result.errors.push('Cannot create workspace for \"main\". Use base repos directly.');\n return result;\n }\n\n const workspaceConfig = projectConfig.workspace || getDefaultWorkspaceConfig();\n const workspacesDir = join(projectConfig.path, workspaceConfig.workspaces_dir || 'workspaces');\n const featureFolder = `feature-${featureName}`;\n const workspacePath = join(workspacesDir, featureFolder);\n result.workspacePath = workspacePath;\n\n // Check if workspace already exists\n if (existsSync(workspacePath)) {\n result.success = false;\n result.errors.push(`Workspace already exists at ${workspacePath}`);\n return result;\n }\n\n if (dryRun) {\n result.steps.push('[DRY RUN] Would create workspace at: ' + workspacePath);\n return result;\n }\n\n // Create placeholders\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n\n // Create workspace directory\n mkdirSync(workspacePath, { recursive: true });\n result.steps.push('Created workspace directory');\n\n // Handle polyrepo vs monorepo\n if (workspaceConfig.type === 'polyrepo' && workspaceConfig.repos) {\n // Create worktrees for each repo\n for (const repo of workspaceConfig.repos) {\n // Resolve symlinks to get the actual git repository path\n // (e.g., myn/frontend -> ../frontend needs to resolve to actual path)\n const rawRepoPath = join(projectConfig.path, repo.path);\n const repoPath = existsSync(rawRepoPath) ? realpathSync(rawRepoPath) : rawRepoPath;\n const targetPath = join(workspacePath, repo.name);\n const branchPrefix = repo.branch_prefix || 'feature/';\n const branchName = `${branchPrefix}${featureName}`;\n // Per-repo default_branch overrides workspace-level, falls back to 'main'\n const defaultBranch = repo.default_branch || workspaceConfig.default_branch || 'main';\n\n const worktreeResult = await createWorktree(repoPath, targetPath, branchName, defaultBranch);\n if (worktreeResult.success) {\n result.steps.push(`Created worktree for ${repo.name}: ${branchName} (from ${defaultBranch})`);\n } else {\n result.errors.push(`${repo.name}: ${worktreeResult.message}`);\n result.success = false; // Fail the entire workspace creation if any worktree fails\n }\n }\n } else {\n // Monorepo: create single worktree\n const branchName = `feature/${featureName}`;\n const defaultBranch = workspaceConfig.default_branch || 'main';\n const worktreeResult = await createWorktree(projectConfig.path, workspacePath, branchName, defaultBranch);\n if (worktreeResult.success) {\n result.steps.push(`Created worktree: ${branchName} (from ${defaultBranch})`);\n } else {\n result.errors.push(worktreeResult.message);\n result.success = false; // Fail the entire workspace creation if worktree fails\n }\n }\n\n // Remove stale .planning/ directory inherited from main branch.\n // This contains STATE.md and other planning artifacts from a PREVIOUS issue.\n // If left in place, the new agent reads it and works on the wrong issue.\n // SAFETY: resolve() to absolute path and verify it's under a known workspace prefix\n // to prevent path traversal from ever reaching rmSync.\n const resolvedWorkspace = resolve(workspacePath);\n const resolvedPlanning = resolve(resolvedWorkspace, '.planning');\n const isUnderWorkspacesDir = resolvedWorkspace.match(/\\/workspaces\\/feature-[a-z0-9-]+$/);\n if (\n isUnderWorkspacesDir &&\n resolvedPlanning === join(resolvedWorkspace, '.planning') &&\n existsSync(join(resolvedWorkspace, '.git')) &&\n existsSync(resolvedPlanning)\n ) {\n rmSync(resolvedPlanning, { recursive: true, force: true });\n result.steps.push('Removed stale .planning/ directory from previous issue');\n }\n\n // Sanitize any docker-compose files in the workspace to use platform-agnostic paths\n // This handles files inherited from worktrees that may have hardcoded home paths\n const devcontainerDir = join(workspacePath, '.devcontainer');\n if (existsSync(devcontainerDir)) {\n const composeFiles = readdirSync(devcontainerDir)\n .filter(f => f.includes('compose') && (f.endsWith('.yml') || f.endsWith('.yaml')));\n for (const composeFile of composeFiles) {\n sanitizeComposeFile(join(devcontainerDir, composeFile));\n }\n if (composeFiles.length > 0) {\n result.steps.push(`Sanitized ${composeFiles.length} compose file(s) for platform compatibility`);\n }\n }\n\n // Setup TLDR code analysis for workspace (after worktree creation to ensure directory is ready)\n try {\n // Check if python3 is available\n await execAsync('python3 --version');\n const venvPath = join(workspacePath, '.venv');\n const tldrBin = join(venvPath, 'bin', 'tldr');\n\n // Check if main branch already has a working venv with llm-tldr\n const mainVenvTldr = join(projectConfig.path, '.venv', 'bin', 'tldr');\n const mainVenvExists = existsSync(mainVenvTldr);\n\n if (mainVenvExists) {\n // Copy the entire venv from main — faster than pip install (seconds vs 30s+)\n await execAsync(`cp -a \"${join(projectConfig.path, '.venv')}\" \"${venvPath}\"`);\n result.steps.push('Copied Python venv from main branch');\n } else {\n // Create fresh venv and install llm-tldr\n await execAsync(`python3 -m venv \"${venvPath}\"`, { cwd: workspacePath });\n const pipPath = join(venvPath, 'bin', 'pip');\n await execAsync(`\"${pipPath}\" install llm-tldr`, { cwd: workspacePath, timeout: 120000 });\n result.steps.push('Created Python venv and installed llm-tldr');\n\n // Apply .tsx/.jsx support patch (upstream llm-tldr only checks .ts)\n const patchScript = join(projectConfig.path, 'scripts', 'patches', 'llm-tldr-tsx-support.py');\n if (existsSync(patchScript)) {\n await execAsync(`python3 \"${patchScript}\" \"${venvPath}\"`);\n result.steps.push('Applied llm-tldr .tsx/.jsx patch');\n }\n }\n\n // Verify tldr binary exists after setup\n if (!existsSync(tldrBin)) {\n result.steps.push('TLDR setup incomplete: tldr binary not found after venv creation');\n } else {\n // Copy .tldr index from main branch if it exists\n const mainTldrDir = join(projectConfig.path, '.tldr');\n const workspaceTldrDir = join(workspacePath, '.tldr');\n\n if (existsSync(mainTldrDir)) {\n await execAsync(`cp -r \"${mainTldrDir}\" \"${workspaceTldrDir}\"`);\n result.steps.push('Copied TLDR index from main branch');\n }\n\n // Start TLDR daemon for this workspace\n const { getTldrDaemonService } = await import('./tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.start(true);\n result.steps.push('Started TLDR daemon');\n\n // Warm the index in the background — ensures workspaces always have a working index\n // even when the main branch cache was empty (nothing to copy)\n try {\n await tldrService.warm(true); // background=true: non-blocking\n result.steps.push('TLDR index warm initiated (background)');\n } catch {\n // Non-fatal — daemon may not support warm yet\n }\n }\n } catch (error: any) {\n // TLDR setup is optional — don't fail workspace creation, but log clearly\n if (error.message?.includes('python3')) {\n result.steps.push('Skipped TLDR setup (python3 not available)');\n } else {\n console.warn(`⚠ TLDR setup failed: ${error.message}`);\n result.steps.push(`TLDR setup failed: ${error.message}`);\n }\n }\n\n // Configure DNS\n if (workspaceConfig.dns) {\n const dnsMethod = workspaceConfig.dns.sync_method || 'wsl2hosts';\n for (const entryPattern of workspaceConfig.dns.entries) {\n const hostname = replacePlaceholders(entryPattern, placeholders);\n\n if (addDnsEntry(dnsMethod, hostname)) {\n result.steps.push(`Added DNS entry: ${hostname} (${dnsMethod})`);\n }\n }\n\n // Sync to Windows if using wsl2hosts method\n if (dnsMethod === 'wsl2hosts') {\n const synced = await syncDnsToWindows();\n if (synced) {\n result.steps.push('Synced DNS to Windows hosts file');\n }\n }\n }\n\n // Assign ports\n if (workspaceConfig.ports) {\n for (const [portName, portConfig] of Object.entries(workspaceConfig.ports)) {\n const portFile = join(projectConfig.path, `.${portName}-ports`);\n try {\n const port = assignPort(portFile, featureFolder, portConfig.range);\n result.steps.push(`Assigned ${portName} port: ${port}`);\n // Add to placeholders for use in templates\n (placeholders as any)[`${portName.toUpperCase()}_PORT`] = String(port);\n } catch (error) {\n result.errors.push(`Failed to assign ${portName} port: ${error}`);\n }\n }\n }\n\n // Install base Panopticon skills/agents/rules from cache\n const mergeResult = mergeSkillsIntoWorkspace(workspacePath);\n const mergeTotal = mergeResult.added.length + mergeResult.updated.length;\n if (mergeTotal > 0) {\n result.steps.push(`Installed ${mergeTotal} Panopticon files (${mergeResult.added.length} new, ${mergeResult.updated.length} updated)`);\n }\n\n // Process agent templates (project template overlay — wins over Panopticon base)\n if (workspaceConfig.agent?.template_dir) {\n const templateDir = join(projectConfig.path, workspaceConfig.agent.template_dir);\n\n // Process template files\n const templateSteps = processTemplates(\n templateDir,\n workspacePath,\n placeholders,\n workspaceConfig.agent.templates\n );\n result.steps.push(...templateSteps);\n\n // Copy .claude/ directories from project template (copy_dirs replaces legacy symlinks)\n const dirsToSync = workspaceConfig.agent.copy_dirs || workspaceConfig.agent.symlinks;\n if (dirsToSync) {\n const copySteps = copyProjectTemplateDirs(templateDir, workspacePath, dirsToSync, placeholders);\n result.steps.push(...copySteps);\n }\n }\n\n // Generate .env file\n if (workspaceConfig.env?.template) {\n const envContent = replacePlaceholders(workspaceConfig.env.template, placeholders);\n writeFileSync(join(workspacePath, '.env'), envContent);\n result.steps.push('Created .env file');\n }\n\n // Process Docker compose templates\n if (workspaceConfig.docker?.compose_template) {\n const templateDir = join(projectConfig.path, workspaceConfig.docker.compose_template);\n const devcontainerDir = join(workspacePath, '.devcontainer');\n mkdirSync(devcontainerDir, { recursive: true });\n\n const templateSteps = processTemplates(templateDir, devcontainerDir, placeholders);\n result.steps.push(...templateSteps);\n\n // Copy non-template files (like Dockerfile)\n if (existsSync(templateDir)) {\n const files = readdirSync(templateDir);\n for (const file of files) {\n if (!file.endsWith('.template')) {\n const sourcePath = join(templateDir, file);\n const targetPath = join(devcontainerDir, file);\n copyFileSync(sourcePath, targetPath);\n }\n }\n }\n\n // Sanitize docker-compose files to use platform-agnostic paths\n // This fixes hardcoded /home/username or /Users/username paths\n const composeFiles = readdirSync(devcontainerDir)\n .filter(f => f.includes('compose') && (f.endsWith('.yml') || f.endsWith('.yaml')));\n for (const composeFile of composeFiles) {\n sanitizeComposeFile(join(devcontainerDir, composeFile));\n }\n if (composeFiles.length > 0) {\n result.steps.push(`Sanitized ${composeFiles.length} compose file(s) for platform compatibility`);\n }\n\n // Create ./dev symlink at workspace root pointing to .devcontainer/dev\n // Symlink keeps changes in sync - editing ./dev updates .devcontainer/dev\n const devScriptInContainer = join(devcontainerDir, 'dev');\n const devScriptAtRoot = join(workspacePath, 'dev');\n if (existsSync(devScriptInContainer) && !existsSync(devScriptAtRoot)) {\n try {\n symlinkSync('.devcontainer/dev', devScriptAtRoot);\n chmodSync(devScriptInContainer, 0o755); // Make executable\n result.steps.push('Created ./dev symlink');\n } catch (error) {\n result.errors.push(`Failed to create ./dev symlink: ${error}`);\n }\n }\n }\n\n // Note: Beads initialization is handled by the calling command (workspace.ts)\n // With beads v0.47.1+, worktrees use shared database with labels for isolation\n // The workspace.ts command creates a bead with workspace:issue-id label\n\n // Set up Cloudflare tunnel for external access (before Docker so containers can use tunnel URLs)\n if (workspaceConfig.tunnel) {\n const tunnelResult = await addTunnelIngress(workspaceConfig.tunnel, placeholders);\n result.steps.push(...tunnelResult.steps);\n if (!tunnelResult.success) {\n result.errors.push('Tunnel setup had failures (see steps for details)');\n }\n }\n\n // Create Hume EVI config and write env file for Docker (before Docker so containers pick up the config ID)\n if (workspaceConfig.hume) {\n const humeResult = await createHumeConfig(workspaceConfig.hume, placeholders);\n result.steps.push(...humeResult.steps);\n if (humeResult.configId) {\n writeFileSync(\n join(workspacePath, '.hume-config'),\n `HUME_CONFIG_ID=${humeResult.configId}\\nVITE_HUME_CONFIG_ID=${humeResult.configId}\\n`,\n );\n result.steps.push('Wrote .hume-config with Hume EVI config ID');\n }\n if (!humeResult.success) {\n result.errors.push('Hume EVI config setup had failures (see steps for details)');\n }\n }\n\n // Start Docker containers if requested\n if (startDocker) {\n // Check for Traefik\n if (workspaceConfig.docker?.traefik) {\n // Always use the installed Traefik location (~/.panopticon/traefik/), not the\n // template source in projects.yaml. The template is copied to ~/.panopticon/traefik/\n // during `pan install`, and the installed copy has the correct volume mounts\n // (dynamic configs, certs) relative to ~/.panopticon/traefik/.\n const traefikPath = join(homedir(), '.panopticon', 'traefik', 'docker-compose.yml');\n if (existsSync(traefikPath)) {\n try {\n await execAsync(`docker compose -f \"${traefikPath}\" up -d`, { cwd: join(homedir(), '.panopticon', 'traefik') });\n result.steps.push('Started Traefik');\n } catch (error: any) {\n const msg = error?.message || String(error);\n if (msg.includes('port is already allocated') || msg.includes('address already in use')) {\n // Traefik (or another reverse proxy) is already running — not an error\n result.steps.push('Traefik already running (port in use)');\n } else {\n result.errors.push(`Failed to start Traefik: ${error}`);\n }\n }\n }\n }\n\n // Start workspace containers\n const composeLocations = [\n join(workspacePath, 'docker-compose.yml'),\n join(workspacePath, 'docker-compose.yaml'),\n join(workspacePath, '.devcontainer', 'docker-compose.yml'),\n join(workspacePath, '.devcontainer', 'docker-compose.devcontainer.yml'),\n ];\n\n for (const composePath of composeLocations) {\n if (existsSync(composePath)) {\n try {\n // Don't pass -p: the compose file's `name:` field is the authority.\n // Passing -p with a different value creates a second Docker project\n // on container restart, splitting services onto separate networks.\n await execAsync(`docker compose -f \"${composePath}\" up -d --build`, { cwd: dirname(composePath), timeout: 300000 });\n result.steps.push(`Started containers from ${basename(composePath)}`);\n } catch (error) {\n result.errors.push(`Failed to start containers: ${error}`);\n }\n break;\n }\n }\n }\n\n // Pre-trust workspace directory in Claude Code so agents don't get the trust prompt\n try {\n preTrustDirectory(workspacePath);\n result.steps.push('Pre-trusted workspace in Claude Code');\n } catch {\n // Non-fatal — agent can still work, user will just see trust prompt\n }\n\n result.success = result.errors.length === 0;\n return result;\n}\n\n/**\n * Pre-register a directory as trusted in Claude Code's ~/.claude.json.\n * This prevents the \"Quick safety check: Is this a project you created or one you trust?\" prompt\n * when agents are spawned in dynamically-created workspace directories.\n */\nexport function preTrustDirectory(dirPath: string): void {\n const claudeJsonPath = join(homedir(), '.claude.json');\n if (!existsSync(claudeJsonPath)) return;\n\n const data = JSON.parse(readFileSync(claudeJsonPath, 'utf8'));\n if (!data.projects) data.projects = {};\n\n // Only add if not already present\n if (data.projects[dirPath]) {\n if (!data.projects[dirPath].hasTrustDialogAccepted) {\n data.projects[dirPath].hasTrustDialogAccepted = true;\n writeFileSync(claudeJsonPath, JSON.stringify(data, null, 2), 'utf8');\n }\n return;\n }\n\n data.projects[dirPath] = {\n allowedTools: [],\n mcpContextUris: [],\n mcpServers: {},\n enabledMcpjsonServers: [],\n disabledMcpjsonServers: [],\n hasTrustDialogAccepted: true,\n projectOnboardingSeenCount: 0,\n hasClaudeMdExternalIncludesApproved: false,\n hasClaudeMdExternalIncludesWarningShown: false,\n };\n\n writeFileSync(claudeJsonPath, JSON.stringify(data, null, 2), 'utf8');\n}\n\nexport interface WorkspaceRemoveOptions {\n projectConfig: ProjectConfig;\n featureName: string;\n dryRun?: boolean;\n}\n\nexport interface WorkspaceRemoveResult {\n success: boolean;\n errors: string[];\n steps: string[];\n}\n\n/**\n * Result of Docker container cleanup for a workspace.\n */\nexport interface DockerCleanupResult {\n /** Whether compose files were found (containers may or may not have been running) */\n containersFound: boolean;\n /** Human-readable log of cleanup steps taken */\n steps: string[];\n}\n\n/**\n * Stop Docker containers and clean up Docker-created files for a workspace.\n *\n * Extracted as a standalone function so it can be used by:\n * - removeWorkspace() during normal workspace removal\n * - deep-wipe endpoint for complete issue cleanup\n * - workspace-migrate for pre-migration cleanup\n *\n * Failures are logged but never thrown — callers should not fail if Docker is unavailable.\n */\nexport async function stopWorkspaceDocker(\n workspacePath: string,\n projectName: string,\n featureName: string,\n): Promise<DockerCleanupResult> {\n const result: DockerCleanupResult = {\n containersFound: false,\n steps: [],\n };\n\n // Find all compose files in devcontainer directory (some projects use multiple)\n const devcontainerDir = join(workspacePath, '.devcontainer');\n const composeFiles: string[] = [];\n\n if (existsSync(devcontainerDir)) {\n const possibleFiles = [\n 'docker-compose.devcontainer.yml',\n 'docker-compose.yml',\n 'compose.yml',\n 'compose.infra.yml',\n 'compose.override.yml',\n ];\n for (const file of possibleFiles) {\n const fullPath = join(devcontainerDir, file);\n if (existsSync(fullPath)) {\n composeFiles.push(fullPath);\n }\n }\n }\n\n // Fallback: check for compose file in workspace root\n if (composeFiles.length === 0) {\n const rootCompose = join(workspacePath, 'docker-compose.yml');\n if (existsSync(rootCompose)) {\n composeFiles.push(rootCompose);\n }\n }\n\n if (composeFiles.length > 0) {\n result.containersFound = true;\n try {\n const fileFlags = composeFiles.map(f => `-f \"${f}\"`).join(' ');\n const cwd = existsSync(devcontainerDir) ? devcontainerDir : workspacePath;\n\n // Derive compose project name from the dev script (same logic as dashboard)\n // or fall back to \"{projectName}-feature-{featureName}\" convention.\n let composeProjectName = `${projectName}-feature-${featureName}`;\n const devScriptPaths = [\n join(workspacePath, '.devcontainer', 'dev'),\n join(workspacePath, 'dev'),\n ];\n for (const devPath of devScriptPaths) {\n try {\n if (existsSync(devPath)) {\n const content = readFileSync(devPath, 'utf-8');\n const match = content.match(/COMPOSE_PROJECT_NAME=\"([^$\"]*)\\$\\{FEATURE_FOLDER\\}\"/);\n if (match) {\n composeProjectName = `${match[1]}feature-${featureName}`;\n break;\n }\n const literalMatch = content.match(/COMPOSE_PROJECT_NAME=\"([^\"]+)\"/);\n if (literalMatch) {\n composeProjectName = literalMatch[1];\n break;\n }\n }\n } catch {\n // Fall through to default\n }\n }\n\n await execAsync(`docker compose ${fileFlags} -p \"${composeProjectName}\" down -v --remove-orphans`, {\n cwd,\n timeout: 60000,\n });\n result.steps.push(`Stopped Docker containers (${composeFiles.length} compose files)`);\n } catch (error: any) {\n // Log but don't fail — containers might not be running\n result.steps.push(`Docker cleanup attempted (${error.message?.split('\\n')[0] || 'containers may not be running'})`);\n }\n }\n\n // Clean up Docker-created files (root-owned in containers)\n try {\n await execAsync(\n `docker run --rm -v \"${workspacePath}:/workspace\" alpine sh -c \"find /workspace -user root -delete 2>&1 | tail -100 || true\"`,\n { timeout: 30000, maxBuffer: 10 * 1024 * 1024 }\n );\n result.steps.push('Cleaned up Docker-created files');\n } catch {\n // Alpine container might not be available\n }\n\n return result;\n}\n\n/**\n * Remove a workspace\n */\nexport async function removeWorkspace(options: WorkspaceRemoveOptions): Promise<WorkspaceRemoveResult> {\n const { projectConfig, featureName, dryRun } = options;\n const result: WorkspaceRemoveResult = {\n success: true,\n errors: [],\n steps: [],\n };\n\n const workspaceConfig = projectConfig.workspace || getDefaultWorkspaceConfig();\n const workspacesDir = join(projectConfig.path, workspaceConfig.workspaces_dir || 'workspaces');\n const featureFolder = `feature-${featureName}`;\n const workspacePath = join(workspacesDir, featureFolder);\n\n if (!existsSync(workspacePath)) {\n result.success = false;\n result.errors.push(`Workspace not found at ${workspacePath}`);\n return result;\n }\n\n if (dryRun) {\n result.steps.push('[DRY RUN] Would remove workspace at: ' + workspacePath);\n return result;\n }\n\n // Stop TLDR daemon for workspace (if it exists)\n const venvPath = join(workspacePath, '.venv');\n if (existsSync(venvPath)) {\n try {\n const { getTldrDaemonService } = await import('./tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.stop();\n result.steps.push('Stopped TLDR daemon');\n } catch (error: any) {\n // Non-fatal - daemon may not be running\n console.warn(`⚠ Failed to stop TLDR daemon: ${error?.message}`);\n }\n }\n\n // Stop Docker containers and clean up Docker-created files\n const dockerResult = await stopWorkspaceDocker(workspacePath, projectConfig.name || 'workspace', featureName);\n result.steps.push(...dockerResult.steps);\n\n // Remove worktrees\n if (workspaceConfig.type === 'polyrepo' && workspaceConfig.repos) {\n for (const repo of workspaceConfig.repos) {\n const repoPath = join(projectConfig.path, repo.path);\n const targetPath = join(workspacePath, repo.name);\n const branchPrefix = repo.branch_prefix || 'feature/';\n const branchName = `${branchPrefix}${featureName}`;\n\n const worktreeResult = await removeWorktree(repoPath, targetPath, branchName);\n if (worktreeResult.success) {\n result.steps.push(`Removed worktree for ${repo.name}`);\n } else {\n result.errors.push(worktreeResult.message);\n }\n }\n } else {\n // Monorepo: remove single worktree\n const branchName = `feature/${featureName}`;\n const worktreeResult = await removeWorktree(projectConfig.path, workspacePath, branchName);\n if (worktreeResult.success) {\n result.steps.push('Removed worktree');\n } else {\n result.errors.push(worktreeResult.message);\n }\n }\n\n // Remove DNS entries\n if (workspaceConfig.dns) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n\n const dnsMethod = workspaceConfig.dns.sync_method || 'wsl2hosts';\n for (const entryPattern of workspaceConfig.dns.entries) {\n const hostname = replacePlaceholders(entryPattern, placeholders);\n if (removeDnsEntry(dnsMethod, hostname)) {\n result.steps.push(`Removed DNS entry: ${hostname}`);\n }\n }\n }\n\n // Remove Cloudflare tunnel entries\n if (workspaceConfig.tunnel) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n const tunnelResult = await removeTunnelIngress(workspaceConfig.tunnel, placeholders);\n result.steps.push(...tunnelResult.steps);\n }\n\n // Remove Hume EVI config\n if (workspaceConfig.hume) {\n const placeholders = createPlaceholders(projectConfig, featureName, workspacePath);\n const humeResult = await deleteHumeConfig(workspaceConfig.hume, placeholders);\n result.steps.push(...humeResult.steps);\n }\n\n // Release ports\n if (workspaceConfig.ports) {\n for (const [portName] of Object.entries(workspaceConfig.ports)) {\n const portFile = join(projectConfig.path, `.${portName}-ports`);\n if (releasePort(portFile, featureFolder)) {\n result.steps.push(`Released ${portName} port`);\n }\n }\n }\n\n // Remove workspace directory\n try {\n await execAsync(`rm -rf \"${workspacePath}\"`, { maxBuffer: 10 * 1024 * 1024 });\n result.steps.push('Removed workspace directory');\n } catch (error) {\n result.errors.push(`Failed to remove workspace directory: ${error}`);\n }\n\n result.success = result.errors.length === 0;\n return result;\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","readdirSync","copyFileSync","join","dirname","copyDir","count","devcontainerDir"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|