panopticon-cli 0.5.9 → 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.
Files changed (70) hide show
  1. package/dist/{agents-M2ZOZL3P.js → agents-RL2KUSP3.js} +3 -3
  2. package/dist/{archive-planning-U3AZAKWI.js → archive-planning-54J6EP6A.js} +3 -3
  3. package/dist/{chunk-WEQW3EAT.js → chunk-F4XS2FQN.js} +3 -2
  4. package/dist/chunk-F4XS2FQN.js.map +1 -0
  5. package/dist/{chunk-OJF4QS3S.js → chunk-GIW2TUWI.js} +2 -2
  6. package/dist/{chunk-GM22HPYS.js → chunk-H7T35QDO.js} +21 -3
  7. package/dist/chunk-H7T35QDO.js.map +1 -0
  8. package/dist/{chunk-MJXYTGK5.js → chunk-JZWCL5S5.js} +2 -2
  9. package/dist/{chunk-3WDSD2VK.js → chunk-NLN3ZLCN.js} +186 -88
  10. package/dist/chunk-NLN3ZLCN.js.map +1 -0
  11. package/dist/{chunk-QQ27EVBD.js → chunk-OMOEGJDB.js} +3 -3
  12. package/dist/{chunk-4R6ATXYI.js → chunk-PFA5XE2V.js} +1 -37
  13. package/dist/chunk-PFA5XE2V.js.map +1 -0
  14. package/dist/{chunk-6OYUJ4AJ.js → chunk-R47UJWF6.js} +2 -2
  15. package/dist/{chunk-KPGVCGST.js → chunk-S7EJ2OLR.js} +10 -4
  16. package/dist/chunk-S7EJ2OLR.js.map +1 -0
  17. package/dist/{chunk-R4KPLLRB.js → chunk-SFX3BG6N.js} +1 -1
  18. package/dist/chunk-SFX3BG6N.js.map +1 -0
  19. package/dist/clean-planning-V4SSVU26.js +9 -0
  20. package/dist/cli/index.js +1111 -901
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/close-issue-5OMOP2FU.js +9 -0
  23. package/dist/compact-beads-YQDVF6FQ.js +9 -0
  24. package/dist/dashboard/prompts/merge-agent.md +11 -0
  25. package/dist/dashboard/prompts/review-agent.md +9 -0
  26. package/dist/dashboard/prompts/test-agent.md +9 -0
  27. package/dist/dashboard/prompts/work-agent.md +10 -2
  28. package/dist/dashboard/public/assets/index-5hYjhhGn.js +826 -0
  29. package/dist/dashboard/public/assets/index-DIFh3T1V.css +32 -0
  30. package/dist/dashboard/public/index.html +2 -2
  31. package/dist/dashboard/server.js +2405 -1754
  32. package/dist/index.d.ts +8 -3
  33. package/dist/index.js +3 -3
  34. package/dist/{label-cleanup-4HJVX6NP.js → label-cleanup-4IVZIPGK.js} +2 -2
  35. package/dist/{merge-agent-756U4NPX.js → merge-agent-7L7MWJEC.js} +12 -12
  36. package/dist/{specialist-context-UBVUUFJV.js → specialist-context-L37RF6Z5.js} +3 -3
  37. package/dist/{specialist-logs-FQRI3AIS.js → specialist-logs-B7UC3UDO.js} +3 -3
  38. package/dist/{specialists-CXRGSJY3.js → specialists-X4OGA7WX.js} +3 -3
  39. package/dist/{workspace-manager-OWHLR5BL.js → workspace-manager-6RP5A5HF.js} +2 -2
  40. package/package.json +1 -1
  41. package/skills/pan-new-project/SKILL.md +1 -1
  42. package/skills/pan-oversee/SKILL.md +45 -10
  43. package/skills/plan/SKILL.md +336 -0
  44. package/dist/chunk-3WDSD2VK.js.map +0 -1
  45. package/dist/chunk-4R6ATXYI.js.map +0 -1
  46. package/dist/chunk-GM22HPYS.js.map +0 -1
  47. package/dist/chunk-KPGVCGST.js.map +0 -1
  48. package/dist/chunk-R4KPLLRB.js.map +0 -1
  49. package/dist/chunk-WEQW3EAT.js.map +0 -1
  50. package/dist/clean-planning-7Z5YY64X.js +0 -9
  51. package/dist/close-issue-CTZK777I.js +0 -9
  52. package/dist/compact-beads-72SHALOL.js +0 -9
  53. package/dist/dashboard/public/assets/index-Bx4NCn9A.css +0 -32
  54. package/dist/dashboard/public/assets/index-DqPey4Of.js +0 -756
  55. package/skills/opus-plan/SKILL.md +0 -400
  56. /package/dist/{agents-M2ZOZL3P.js.map → agents-RL2KUSP3.js.map} +0 -0
  57. /package/dist/{archive-planning-U3AZAKWI.js.map → archive-planning-54J6EP6A.js.map} +0 -0
  58. /package/dist/{chunk-OJF4QS3S.js.map → chunk-GIW2TUWI.js.map} +0 -0
  59. /package/dist/{chunk-MJXYTGK5.js.map → chunk-JZWCL5S5.js.map} +0 -0
  60. /package/dist/{chunk-QQ27EVBD.js.map → chunk-OMOEGJDB.js.map} +0 -0
  61. /package/dist/{chunk-6OYUJ4AJ.js.map → chunk-R47UJWF6.js.map} +0 -0
  62. /package/dist/{clean-planning-7Z5YY64X.js.map → clean-planning-V4SSVU26.js.map} +0 -0
  63. /package/dist/{close-issue-CTZK777I.js.map → close-issue-5OMOP2FU.js.map} +0 -0
  64. /package/dist/{compact-beads-72SHALOL.js.map → compact-beads-YQDVF6FQ.js.map} +0 -0
  65. /package/dist/{label-cleanup-4HJVX6NP.js.map → label-cleanup-4IVZIPGK.js.map} +0 -0
  66. /package/dist/{merge-agent-756U4NPX.js.map → merge-agent-7L7MWJEC.js.map} +0 -0
  67. /package/dist/{specialist-context-UBVUUFJV.js.map → specialist-context-L37RF6Z5.js.map} +0 -0
  68. /package/dist/{specialist-logs-FQRI3AIS.js.map → specialist-logs-B7UC3UDO.js.map} +0 -0
  69. /package/dist/{specialists-CXRGSJY3.js.map → specialists-X4OGA7WX.js.map} +0 -0
  70. /package/dist/{workspace-manager-OWHLR5BL.js.map → workspace-manager-6RP5A5HF.js.map} +0 -0
@@ -20,8 +20,8 @@ import {
20
20
  stopAgent,
21
21
  transitionIssueToInProgress,
22
22
  transitionIssueToInReview
23
- } from "./chunk-QQ27EVBD.js";
24
- import "./chunk-4R6ATXYI.js";
23
+ } from "./chunk-OMOEGJDB.js";
24
+ import "./chunk-PFA5XE2V.js";
25
25
  import "./chunk-USYP2SBE.js";
26
26
  import "./chunk-QAJAJBFW.js";
27
27
  import "./chunk-7ZB5D46Y.js";
@@ -54,4 +54,4 @@ export {
54
54
  transitionIssueToInProgress,
55
55
  transitionIssueToInReview
56
56
  };
57
- //# sourceMappingURL=agents-M2ZOZL3P.js.map
57
+ //# sourceMappingURL=agents-RL2KUSP3.js.map
@@ -3,8 +3,8 @@ import {
3
3
  archiveWorkspaceArtifacts,
4
4
  findWorkspacePath,
5
5
  movePrd
6
- } from "./chunk-6OYUJ4AJ.js";
7
- import "./chunk-R4KPLLRB.js";
6
+ } from "./chunk-R47UJWF6.js";
7
+ import "./chunk-SFX3BG6N.js";
8
8
  import "./chunk-ZTFNYOC7.js";
9
9
  import "./chunk-ZHC57RCV.js";
10
10
  export {
@@ -13,4 +13,4 @@ export {
13
13
  findWorkspacePath,
14
14
  movePrd
15
15
  };
16
- //# sourceMappingURL=archive-planning-U3AZAKWI.js.map
16
+ //# sourceMappingURL=archive-planning-54J6EP6A.js.map
@@ -2,7 +2,7 @@ import {
2
2
  stepFailed,
3
3
  stepOk,
4
4
  stepSkipped
5
- } from "./chunk-R4KPLLRB.js";
5
+ } from "./chunk-SFX3BG6N.js";
6
6
  import {
7
7
  init_esm_shims
8
8
  } from "./chunk-ZHC57RCV.js";
@@ -15,6 +15,7 @@ var execAsync = promisify(exec);
15
15
  var EPHEMERAL_PLANNING_FILES = [
16
16
  ".planning/STATE.md",
17
17
  ".planning/PRD.md",
18
+ ".planning/WORKSPACE.md",
18
19
  ".planning/PLANNING_PROMPT.md",
19
20
  ".planning/PLANNING_PROMPT.md.archived",
20
21
  ".planning/.planning-complete"
@@ -75,4 +76,4 @@ async function cleanPlanningArtifacts(ctx) {
75
76
  export {
76
77
  cleanPlanningArtifacts
77
78
  };
78
- //# sourceMappingURL=chunk-WEQW3EAT.js.map
79
+ //# sourceMappingURL=chunk-F4XS2FQN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/lifecycle/clean-planning.ts"],"sourcesContent":["/**\n * clean-planning — Remove ephemeral .planning/ artifacts from main after merge.\n *\n * After a feature branch merges to main, ephemeral planning files\n * (STATE.md, PRD.md, PLANNING_PROMPT.md, .planning-complete, feedback/)\n * land on main and pollute new workspaces that inherit them.\n *\n * This module removes those files from the git index and working tree\n * with a dedicated commit, so new workspaces start clean.\n *\n * Idempotent — if none of the target files are tracked, returns skipped.\n */\n\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\n\nconst execAsync = promisify(exec);\n\n/** Ephemeral planning files to remove from main after merge */\nconst EPHEMERAL_PLANNING_FILES = [\n '.planning/STATE.md',\n '.planning/PRD.md',\n '.planning/WORKSPACE.md',\n '.planning/PLANNING_PROMPT.md',\n '.planning/PLANNING_PROMPT.md.archived',\n '.planning/.planning-complete',\n];\n\n/**\n * Remove ephemeral planning artifacts from main after a feature branch merge.\n *\n * Uses `git rm` to remove tracked files from both the index and working tree,\n * then commits the deletion. Untracked files are silently skipped.\n */\nexport async function cleanPlanningArtifacts(\n ctx: LifecycleContext,\n): Promise<StepResult> {\n const step = 'clean-planning';\n const { issueId, projectPath } = ctx;\n\n try {\n // Build the list of files git is currently tracking in .planning/\n // that match our ephemeral set. We include feedback/ glob separately.\n let trackedFiles: string[] = [];\n\n // Check individual ephemeral files\n for (const file of EPHEMERAL_PLANNING_FILES) {\n try {\n const { stdout } = await execAsync(\n `git ls-files -- ${file}`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (stdout.trim()) {\n trackedFiles.push(file);\n }\n } catch {\n // git ls-files failure is non-fatal\n }\n }\n\n // Check feedback/ directory\n try {\n const { stdout } = await execAsync(\n `git ls-files -- .planning/feedback/`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (stdout.trim()) {\n trackedFiles.push('.planning/feedback/');\n }\n } catch {\n // Non-fatal\n }\n\n if (trackedFiles.length === 0) {\n return stepSkipped(step, ['No tracked ephemeral planning files found on main']);\n }\n\n // Remove tracked files from index and working tree\n const fileArgs = trackedFiles.map(f => `\"${f}\"`).join(' ');\n await execAsync(\n `git rm -rf --ignore-unmatch ${fileArgs}`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n\n // Check if anything was actually staged for deletion\n try {\n await execAsync('git diff --cached --quiet', { cwd: projectPath, encoding: 'utf-8' });\n // Nothing staged — files may have already been removed\n return stepSkipped(step, ['No staged deletions after git rm (already clean)']);\n } catch {\n // There are staged changes — commit them\n await execAsync(\n `git commit -m \"chore: remove ephemeral planning state after ${issueId} merge\"`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n }\n\n return stepOk(step, [\n `Removed ${trackedFiles.length} ephemeral planning file(s) from main`,\n `Files: ${trackedFiles.join(', ')}`,\n ]);\n } catch (err) {\n return stepFailed(step, `Failed to clean planning artifacts: ${(err as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AAaA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAI1B,IAAM,YAAY,UAAU,IAAI;AAGhC,IAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,eAAsB,uBACpB,KACqB;AACrB,QAAM,OAAO;AACb,QAAM,EAAE,SAAS,YAAY,IAAI;AAEjC,MAAI;AAGF,QAAI,eAAyB,CAAC;AAG9B,eAAW,QAAQ,0BAA0B;AAC3C,UAAI;AACF,cAAM,EAAE,OAAO,IAAI,MAAM;AAAA,UACvB,mBAAmB,IAAI;AAAA,UACvB,EAAE,KAAK,aAAa,UAAU,QAAQ;AAAA,QACxC;AACA,YAAI,OAAO,KAAK,GAAG;AACjB,uBAAa,KAAK,IAAI;AAAA,QACxB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM;AAAA,QACvB;AAAA,QACA,EAAE,KAAK,aAAa,UAAU,QAAQ;AAAA,MACxC;AACA,UAAI,OAAO,KAAK,GAAG;AACjB,qBAAa,KAAK,qBAAqB;AAAA,MACzC;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B,aAAO,YAAY,MAAM,CAAC,mDAAmD,CAAC;AAAA,IAChF;AAGA,UAAM,WAAW,aAAa,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG;AACzD,UAAM;AAAA,MACJ,+BAA+B,QAAQ;AAAA,MACvC,EAAE,KAAK,aAAa,UAAU,QAAQ;AAAA,IACxC;AAGA,QAAI;AACF,YAAM,UAAU,6BAA6B,EAAE,KAAK,aAAa,UAAU,QAAQ,CAAC;AAEpF,aAAO,YAAY,MAAM,CAAC,kDAAkD,CAAC;AAAA,IAC/E,QAAQ;AAEN,YAAM;AAAA,QACJ,+DAA+D,OAAO;AAAA,QACtE,EAAE,KAAK,aAAa,UAAU,QAAQ;AAAA,MACxC;AAAA,IACF;AAEA,WAAO,OAAO,MAAM;AAAA,MAClB,WAAW,aAAa,MAAM;AAAA,MAC9B,UAAU,aAAa,KAAK,IAAI,CAAC;AAAA,IACnC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,WAAW,MAAM,uCAAwC,IAAc,OAAO,EAAE;AAAA,EACzF;AACF;","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  stepFailed,
4
4
  stepOk,
5
5
  stepSkipped
6
- } from "./chunk-R4KPLLRB.js";
6
+ } from "./chunk-SFX3BG6N.js";
7
7
  import {
8
8
  init_esm_shims
9
9
  } from "./chunk-ZHC57RCV.js";
@@ -266,4 +266,4 @@ async function applyLabelLinear(ctx, apiKey) {
266
266
  export {
267
267
  closeIssue
268
268
  };
269
- //# sourceMappingURL=chunk-OJF4QS3S.js.map
269
+ //# sourceMappingURL=chunk-GIW2TUWI.js.map
@@ -182,13 +182,28 @@ function isPanopticonSymlink(targetPath) {
182
182
  return false;
183
183
  }
184
184
  }
185
- function migrateFromPersonalSymlinks() {
185
+ function migrateStalePersonalContent() {
186
186
  const claudeDir = join3(homedir2(), ".claude");
187
187
  const result = {
188
188
  removedSymlinks: [],
189
189
  preservedUserContent: [],
190
190
  errors: []
191
191
  };
192
+ const devrootNames = /* @__PURE__ */ new Set();
193
+ const devroot = getDevrootPath();
194
+ if (devroot) {
195
+ for (const subdir of ["skills", "commands", "agents"]) {
196
+ const devrootDir = join3(devroot, ".claude", subdir);
197
+ if (existsSync3(devrootDir)) {
198
+ try {
199
+ for (const entry of readdirSync2(devrootDir)) {
200
+ devrootNames.add(`${subdir}/${entry}`);
201
+ }
202
+ } catch {
203
+ }
204
+ }
205
+ }
206
+ }
192
207
  for (const subdir of ["skills", "commands", "agents"]) {
193
208
  const dir = join3(claudeDir, subdir);
194
209
  if (!existsSync3(dir)) continue;
@@ -206,6 +221,9 @@ function migrateFromPersonalSymlinks() {
206
221
  } else {
207
222
  result.preservedUserContent.push(`${subdir}/${entry}`);
208
223
  }
224
+ } else if (stats.isDirectory() && devrootNames.has(`${subdir}/${entry}`)) {
225
+ rmSync2(entryPath, { recursive: true, force: true });
226
+ result.removedSymlinks.push(`${subdir}/${entry} (stale copy)`);
209
227
  } else {
210
228
  result.preservedUserContent.push(`${subdir}/${entry}`);
211
229
  }
@@ -662,7 +680,7 @@ export {
662
680
  restoreBackup,
663
681
  cleanOldBackups,
664
682
  isPanopticonSymlink,
665
- migrateFromPersonalSymlinks,
683
+ migrateStalePersonalContent,
666
684
  refreshCache,
667
685
  planSync,
668
686
  executeSync,
@@ -674,4 +692,4 @@ export {
674
692
  LinkManager,
675
693
  getLinkManager
676
694
  };
677
- //# sourceMappingURL=chunk-GM22HPYS.js.map
695
+ //# sourceMappingURL=chunk-H7T35QDO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/shell.ts","../src/lib/backup.ts","../src/lib/sync.ts","../src/lib/tracker/linking.ts","../src/lib/tracker/index.ts"],"sourcesContent":["import { existsSync, readFileSync, appendFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport type Shell = 'bash' | 'zsh' | 'fish' | 'unknown';\n\nexport function detectShell(): Shell {\n const shell = process.env.SHELL || '';\n\n if (shell.includes('zsh')) return 'zsh';\n if (shell.includes('bash')) return 'bash';\n if (shell.includes('fish')) return 'fish';\n\n return 'unknown';\n}\n\nexport function getShellRcFile(shell: Shell): string | null {\n const home = homedir();\n\n switch (shell) {\n case 'zsh':\n return join(home, '.zshrc');\n case 'bash':\n // Prefer .bashrc, fall back to .bash_profile\n const bashrc = join(home, '.bashrc');\n if (existsSync(bashrc)) return bashrc;\n return join(home, '.bash_profile');\n case 'fish':\n return join(home, '.config', 'fish', 'config.fish');\n default:\n return null;\n }\n}\n\nconst ALIAS_LINE = 'alias pan=\"panopticon\"';\nconst ALIAS_MARKER = '# Panopticon CLI alias';\n\nexport function hasAlias(rcFile: string): boolean {\n if (!existsSync(rcFile)) return false;\n\n const content = readFileSync(rcFile, 'utf8');\n return content.includes(ALIAS_MARKER) || content.includes(ALIAS_LINE);\n}\n\nexport function addAlias(rcFile: string): void {\n if (hasAlias(rcFile)) return;\n\n const aliasBlock = `\n${ALIAS_MARKER}\n${ALIAS_LINE}\n`;\n\n appendFileSync(rcFile, aliasBlock, 'utf8');\n}\n\nexport function getAliasInstructions(shell: Shell): string {\n const rcFile = getShellRcFile(shell);\n\n if (!rcFile) {\n return `Add this to your shell config:\\n ${ALIAS_LINE}`;\n }\n\n return `Alias added to ${rcFile}. Run:\\n source ${rcFile}`;\n}\n","import { existsSync, mkdirSync, readdirSync, cpSync, rmSync, lstatSync } from 'fs';\nimport { join, basename } from 'path';\nimport { BACKUPS_DIR } from './paths.js';\n\nexport interface BackupInfo {\n timestamp: string;\n path: string;\n targets: string[];\n}\n\nexport function createBackupTimestamp(): string {\n return new Date().toISOString().replace(/[:.]/g, '-');\n}\n\nexport function createBackup(sourceDirs: string[]): BackupInfo {\n const timestamp = createBackupTimestamp();\n const backupPath = join(BACKUPS_DIR, timestamp);\n\n mkdirSync(backupPath, { recursive: true });\n\n const targets: string[] = [];\n\n for (const sourceDir of sourceDirs) {\n if (!existsSync(sourceDir)) continue;\n\n const targetName = basename(sourceDir);\n const targetPath = join(backupPath, targetName);\n\n // Use filter to skip symlinks — sync targets (e.g. ~/.claude/skills/)\n // contain symlinks back into ~/.panopticon/skills/ which causes cpSync\n // to fail with \"cannot copy to a subdirectory of self\".\n cpSync(sourceDir, targetPath, {\n recursive: true,\n filter: (src) => !lstatSync(src).isSymbolicLink(),\n });\n targets.push(targetName);\n }\n\n return {\n timestamp,\n path: backupPath,\n targets,\n };\n}\n\nexport function listBackups(): BackupInfo[] {\n if (!existsSync(BACKUPS_DIR)) return [];\n\n const entries = readdirSync(BACKUPS_DIR, { withFileTypes: true });\n\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => {\n const backupPath = join(BACKUPS_DIR, e.name);\n const contents = readdirSync(backupPath);\n\n return {\n timestamp: e.name,\n path: backupPath,\n targets: contents,\n };\n })\n .sort((a, b) => b.timestamp.localeCompare(a.timestamp));\n}\n\nexport function restoreBackup(timestamp: string, targetDirs: Record<string, string>): void {\n const backupPath = join(BACKUPS_DIR, timestamp);\n\n if (!existsSync(backupPath)) {\n throw new Error(`Backup not found: ${timestamp}`);\n }\n\n const contents = readdirSync(backupPath, { withFileTypes: true });\n\n for (const entry of contents) {\n if (!entry.isDirectory()) continue;\n\n const sourcePath = join(backupPath, entry.name);\n const targetPath = targetDirs[entry.name];\n\n if (!targetPath) continue;\n\n // Remove existing and restore from backup\n if (existsSync(targetPath)) {\n rmSync(targetPath, { recursive: true });\n }\n\n cpSync(sourcePath, targetPath, { recursive: true });\n }\n}\n\nexport function cleanOldBackups(keepCount: number = 10): number {\n const backups = listBackups();\n\n if (backups.length <= keepCount) return 0;\n\n const toRemove = backups.slice(keepCount);\n let removed = 0;\n\n for (const backup of toRemove) {\n rmSync(backup.path, { recursive: true });\n removed++;\n }\n\n return removed;\n}\n","import { existsSync, mkdirSync, readdirSync, symlinkSync, unlinkSync, lstatSync, readlinkSync, rmSync, copyFileSync, chmodSync, readFileSync, writeFileSync } from 'fs';\nimport { join, basename, dirname, relative } from 'path';\nimport { homedir } from 'os';\nimport {\n SKILLS_DIR, COMMANDS_DIR, AGENTS_DIR, BIN_DIR,\n SOURCE_SCRIPTS_DIR, SOURCE_DEV_SKILLS_DIR, SOURCE_SKILLS_DIR, SOURCE_AGENTS_DIR, SOURCE_RULES_DIR,\n CACHE_AGENTS_DIR, CACHE_RULES_DIR, CACHE_MANIFEST,\n SYNC_TARGET, isDevMode,\n} from './paths.js';\nimport {\n buildManifestFromDirectory, writeManifest, readManifest, hashFile,\n setManifestEntry, collectSourceFiles,\n type Manifest, type FileStatus,\n compareFileToManifest,\n} from './manifest.js';\nimport { getDevrootPath } from './config.js';\n\nexport interface SyncItem {\n name: string;\n sourcePath: string;\n targetPath: string;\n status: 'new' | 'exists' | 'conflict' | 'symlink';\n}\n\nexport interface SyncPlan {\n skills: SyncItem[];\n commands: SyncItem[];\n agents: SyncItem[];\n rules: SyncItem[];\n devSkills: SyncItem[]; // Developer-only skills (only synced in dev mode)\n}\n\n/**\n * Remove a file, symlink, or directory safely\n */\nfunction removeTarget(targetPath: string): void {\n const stats = lstatSync(targetPath);\n if (stats.isDirectory() && !stats.isSymbolicLink()) {\n // It's a real directory, remove recursively\n rmSync(targetPath, { recursive: true, force: true });\n } else {\n // It's a file or symlink\n unlinkSync(targetPath);\n }\n}\n\n/**\n * Check if a path is a Panopticon-managed symlink\n */\nexport function isPanopticonSymlink(targetPath: string): boolean {\n if (!existsSync(targetPath)) return false;\n\n try {\n const stats = lstatSync(targetPath);\n if (!stats.isSymbolicLink()) return false;\n\n const linkTarget = readlinkSync(targetPath);\n // It's ours if it points to our skills/commands dir\n return linkTarget.includes('.panopticon');\n } catch {\n return false;\n }\n}\n\nexport interface MigrationResult {\n removedSymlinks: string[];\n preservedUserContent: string[];\n errors: string[];\n}\n\n/**\n * One-time migration: remove Panopticon-managed symlinks from ~/.claude/.\n *\n * Detects symlinks in ~/.claude/skills/ and ~/.claude/agents/ that point to\n * .panopticon directories. Removes only those symlinks, preserving any\n * user-created content (real files/directories).\n *\n * This is safe to run multiple times — it's a no-op if nothing remains to clean up.\n *\n * Removes two kinds of stale Panopticon content from ~/.claude/:\n * 1. Symlinks pointing to .panopticon or panopticon-cli (legacy sync method)\n * 2. Plain directories that also exist in the devroot (stale copies from before\n * the devroot migration — these cause duplicate skill listings)\n */\nexport function migrateStalePersonalContent(): MigrationResult {\n const claudeDir = join(homedir(), '.claude');\n const result: MigrationResult = {\n removedSymlinks: [],\n preservedUserContent: [],\n errors: [],\n };\n\n // Build a set of skill/agent/command names that exist in the devroot\n // so we can identify stale copies in ~/.claude/\n const devrootNames = new Set<string>();\n const devroot = getDevrootPath();\n if (devroot) {\n for (const subdir of ['skills', 'commands', 'agents']) {\n const devrootDir = join(devroot, '.claude', subdir);\n if (existsSync(devrootDir)) {\n try {\n for (const entry of readdirSync(devrootDir)) {\n devrootNames.add(`${subdir}/${entry}`);\n }\n } catch {\n // Ignore read errors on devroot\n }\n }\n }\n }\n\n for (const subdir of ['skills', 'commands', 'agents']) {\n const dir = join(claudeDir, subdir);\n if (!existsSync(dir)) continue;\n\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const entryPath = join(dir, entry);\n try {\n const stats = lstatSync(entryPath);\n if (stats.isSymbolicLink()) {\n const linkTarget = readlinkSync(entryPath);\n if (linkTarget.includes('.panopticon') || linkTarget.includes('panopticon-cli')) {\n unlinkSync(entryPath);\n result.removedSymlinks.push(`${subdir}/${entry}`);\n } else {\n // Symlink to somewhere else — leave it\n result.preservedUserContent.push(`${subdir}/${entry}`);\n }\n } else if (stats.isDirectory() && devrootNames.has(`${subdir}/${entry}`)) {\n // Plain directory that also exists in devroot — stale Panopticon copy.\n // The devroot copy is the canonical one; this personal copy causes\n // duplicate listings and violates principle #4 (never touch ~/.claude/).\n rmSync(entryPath, { recursive: true, force: true });\n result.removedSymlinks.push(`${subdir}/${entry} (stale copy)`);\n } else {\n // Real file/directory with no devroot counterpart — user content, never touch\n result.preservedUserContent.push(`${subdir}/${entry}`);\n }\n } catch (err: any) {\n result.errors.push(`${subdir}/${entry}: ${err.message}`);\n }\n }\n } catch (err: any) {\n result.errors.push(`${subdir}: ${err.message}`);\n }\n }\n\n return result;\n}\n\nexport interface RefreshCacheResult {\n skills: { copied: number; total: number };\n agents: { copied: number; total: number };\n rules: { copied: number; total: number };\n}\n\n/**\n * Recursively copy a directory, overwriting existing files.\n */\nfunction copyDirectoryRecursive(source: string, dest: string): number {\n if (!existsSync(source)) return 0;\n\n mkdirSync(dest, { recursive: true });\n let count = 0;\n\n const entries = readdirSync(source, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(source, entry.name);\n const dstPath = join(dest, entry.name);\n if (entry.isDirectory()) {\n count += copyDirectoryRecursive(srcPath, dstPath);\n } else if (entry.isFile()) {\n copyFileSync(srcPath, dstPath);\n count++;\n }\n }\n return count;\n}\n\n/**\n * Refresh the ~/.panopticon/ cache from the repo source.\n *\n * Always copies (overwrites) skills, agents, and rules from the package's\n * source directories to the cache. Generates ~/.panopticon/.manifest.json\n * tracking all cached files.\n *\n * This replaces the old \"skip if exists\" behavior in `pan install`.\n */\nexport function refreshCache(): RefreshCacheResult {\n const result: RefreshCacheResult = {\n skills: { copied: 0, total: 0 },\n agents: { copied: 0, total: 0 },\n rules: { copied: 0, total: 0 },\n };\n\n // Copy skills from repo to cache (always overwrite)\n if (existsSync(SOURCE_SKILLS_DIR)) {\n const skillDirs = readdirSync(SOURCE_SKILLS_DIR, { withFileTypes: true })\n .filter((d) => d.isDirectory());\n\n result.skills.total = skillDirs.length;\n for (const skillDir of skillDirs) {\n const src = join(SOURCE_SKILLS_DIR, skillDir.name);\n const dst = join(SKILLS_DIR, skillDir.name);\n copyDirectoryRecursive(src, dst);\n result.skills.copied++;\n }\n }\n\n // Copy dev-skills to cache too (in dev mode only)\n if (isDevMode() && existsSync(SOURCE_DEV_SKILLS_DIR)) {\n const devSkillDirs = readdirSync(SOURCE_DEV_SKILLS_DIR, { withFileTypes: true })\n .filter((d) => d.isDirectory());\n\n for (const skillDir of devSkillDirs) {\n const src = join(SOURCE_DEV_SKILLS_DIR, skillDir.name);\n const dst = join(SKILLS_DIR, skillDir.name);\n copyDirectoryRecursive(src, dst);\n result.skills.copied++;\n result.skills.total++;\n }\n }\n\n // Copy agent definitions from repo to cache\n if (existsSync(SOURCE_AGENTS_DIR)) {\n mkdirSync(CACHE_AGENTS_DIR, { recursive: true });\n const agents = readdirSync(SOURCE_AGENTS_DIR, { withFileTypes: true })\n .filter((entry) => entry.isFile() && entry.name.endsWith('.md'));\n\n result.agents.total = agents.length;\n for (const agent of agents) {\n copyFileSync(join(SOURCE_AGENTS_DIR, agent.name), join(CACHE_AGENTS_DIR, agent.name));\n result.agents.copied++;\n }\n }\n\n // Copy rules from repo to cache (directory may not exist yet)\n if (existsSync(SOURCE_RULES_DIR)) {\n const ruleFiles = readdirSync(SOURCE_RULES_DIR, { withFileTypes: true })\n .filter((entry) => entry.isFile());\n\n result.rules.total = ruleFiles.length;\n for (const rule of ruleFiles) {\n mkdirSync(CACHE_RULES_DIR, { recursive: true });\n copyFileSync(join(SOURCE_RULES_DIR, rule.name), join(CACHE_RULES_DIR, rule.name));\n result.rules.copied++;\n }\n }\n\n // Generate cache manifest\n const manifest = buildManifestFromDirectory(\n join(SKILLS_DIR, '..'), // ~/.panopticon/\n ['skills', 'agent-definitions', 'rules'],\n 'panopticon',\n );\n writeManifest(CACHE_MANIFEST, manifest);\n\n return result;\n}\n\n/**\n * Devroot sync item — represents a single file to distribute.\n */\nexport interface DevrootSyncItem {\n /** Relative path from .claude/ (e.g., \"skills/beads/SKILL.md\") */\n relativePath: string;\n /** Absolute path to source file in cache */\n sourcePath: string;\n /** Absolute path to target file at devroot */\n targetPath: string;\n /** What action to take */\n status: FileStatus;\n}\n\n/**\n * Plan what would be synced to devroot (dry run).\n * Reads from cache, targets <devroot>/.claude/, uses manifest comparison.\n */\nexport function planSync(): SyncPlan {\n const plan: SyncPlan = {\n skills: [],\n commands: [],\n agents: [],\n rules: [],\n devSkills: [],\n };\n\n const devrootPath = getDevrootPath();\n if (!devrootPath) return plan;\n\n const targetBase = join(devrootPath, '.claude');\n const manifestPath = join(targetBase, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n\n // Plan skills\n const skillFiles = collectSourceFiles(SKILLS_DIR, 'skills/');\n for (const file of skillFiles) {\n const targetFile = join(targetBase, file.relativePath);\n const status = compareFileToManifest(targetFile, file.relativePath, manifest);\n const skillName = file.relativePath.split('/')[1] || file.relativePath;\n\n let syncStatus: SyncItem['status'] = 'new';\n if (status.action === 'update') syncStatus = 'symlink'; // reusing 'symlink' for \"managed, safe to update\"\n else if (status.action === 'modified') syncStatus = 'conflict';\n else if (status.action === 'user-owned') syncStatus = 'conflict';\n\n plan.skills.push({\n name: file.relativePath,\n sourcePath: file.absolutePath,\n targetPath: targetFile,\n status: syncStatus,\n });\n }\n\n // Plan agents\n const agentFiles = collectSourceFiles(CACHE_AGENTS_DIR, 'agents/');\n for (const file of agentFiles) {\n const targetFile = join(targetBase, file.relativePath);\n const status = compareFileToManifest(targetFile, file.relativePath, manifest);\n\n let syncStatus: SyncItem['status'] = 'new';\n if (status.action === 'update') syncStatus = 'symlink';\n else if (status.action === 'modified') syncStatus = 'conflict';\n else if (status.action === 'user-owned') syncStatus = 'conflict';\n\n plan.agents.push({\n name: file.relativePath,\n sourcePath: file.absolutePath,\n targetPath: targetFile,\n status: syncStatus,\n });\n }\n\n // Plan rules\n const ruleFiles = collectSourceFiles(CACHE_RULES_DIR, 'rules/');\n for (const file of ruleFiles) {\n const targetFile = join(targetBase, file.relativePath);\n const status = compareFileToManifest(targetFile, file.relativePath, manifest);\n\n let syncStatus: SyncItem['status'] = 'new';\n if (status.action === 'update') syncStatus = 'symlink';\n else if (status.action === 'modified') syncStatus = 'conflict';\n else if (status.action === 'user-owned') syncStatus = 'conflict';\n\n plan.rules.push({\n name: file.relativePath,\n sourcePath: file.absolutePath,\n targetPath: targetFile,\n status: syncStatus,\n });\n }\n\n return plan;\n}\n\nexport interface SyncOptions {\n force?: boolean;\n diff?: boolean;\n dryRun?: boolean;\n}\n\nexport interface SyncResult {\n created: string[];\n updated: string[];\n skipped: string[];\n conflicts: string[];\n diffs: Array<{ path: string; sourceContent: string; targetContent: string }>;\n}\n\n/**\n * Execute sync to devroot: copy from cache to <devroot>/.claude/.\n * Uses manifest-based conflict resolution. NEVER touches ~/.claude/.\n */\nexport function executeSync(options: SyncOptions = {}): SyncResult {\n const result: SyncResult = {\n created: [],\n updated: [],\n skipped: [],\n conflicts: [],\n diffs: [],\n };\n\n const devrootPath = getDevrootPath();\n if (!devrootPath) {\n return result;\n }\n\n const targetBase = join(devrootPath, '.claude');\n const manifestPath = join(targetBase, '.panopticon-manifest.json');\n const manifest = readManifest(manifestPath);\n\n // Collect all source files from cache\n const allFiles = [\n ...collectSourceFiles(SKILLS_DIR, 'skills/'),\n ...collectSourceFiles(CACHE_AGENTS_DIR, 'agents/'),\n ...collectSourceFiles(CACHE_RULES_DIR, 'rules/'),\n ];\n\n for (const file of allFiles) {\n const targetFile = join(targetBase, file.relativePath);\n const status = compareFileToManifest(targetFile, file.relativePath, manifest);\n\n switch (status.action) {\n case 'new': {\n // File doesn't exist at target — copy it\n mkdirSync(dirname(targetFile), { recursive: true });\n copyFileSync(file.absolutePath, targetFile);\n const hash = hashFile(targetFile);\n setManifestEntry(manifest, file.relativePath, hash, 'panopticon');\n result.created.push(file.relativePath);\n break;\n }\n\n case 'update': {\n // File exists, hash matches manifest — safe to overwrite (user didn't modify)\n mkdirSync(dirname(targetFile), { recursive: true });\n copyFileSync(file.absolutePath, targetFile);\n const hash = hashFile(targetFile);\n setManifestEntry(manifest, file.relativePath, hash, 'panopticon');\n result.updated.push(file.relativePath);\n break;\n }\n\n case 'modified': {\n // File was modified since we placed it\n if (options.diff) {\n result.diffs.push({\n path: file.relativePath,\n sourceContent: readFileSync(file.absolutePath, 'utf-8'),\n targetContent: readFileSync(targetFile, 'utf-8'),\n });\n }\n\n if (options.force) {\n mkdirSync(dirname(targetFile), { recursive: true });\n copyFileSync(file.absolutePath, targetFile);\n const hash = hashFile(targetFile);\n setManifestEntry(manifest, file.relativePath, hash, 'panopticon');\n result.updated.push(file.relativePath);\n } else {\n result.conflicts.push(file.relativePath);\n }\n break;\n }\n\n case 'user-owned': {\n // User placed this file, never touch it\n result.skipped.push(file.relativePath);\n break;\n }\n }\n }\n\n // Write updated manifest\n writeManifest(manifestPath, manifest);\n\n return result;\n}\n\n/**\n * Hook item for sync planning\n */\nexport interface HookItem {\n name: string;\n sourcePath: string;\n targetPath: string;\n status: 'new' | 'updated' | 'current';\n}\n\n/**\n * Plan hooks sync (checks what would be updated)\n */\nexport function planHooksSync(): HookItem[] {\n const hooks: HookItem[] = [];\n\n if (!existsSync(SOURCE_SCRIPTS_DIR)) {\n return hooks;\n }\n\n // Sync hook scripts (no extension) and bundled JS scripts (.js)\n // Skip source files (.ts), shell helpers (.sh), and other non-hook files (.mjs)\n const scripts = readdirSync(SOURCE_SCRIPTS_DIR, { withFileTypes: true })\n .filter((entry) => entry.isFile() && !entry.name.startsWith('.')\n && (!entry.name.includes('.') || entry.name.endsWith('.js')));\n\n for (const script of scripts) {\n const sourcePath = join(SOURCE_SCRIPTS_DIR, script.name);\n const targetPath = join(BIN_DIR, script.name);\n\n let status: HookItem['status'] = 'new';\n\n if (existsSync(targetPath)) {\n // Could compare file contents/timestamps here for 'current' vs 'updated'\n // For now, always update to ensure latest version\n status = 'updated';\n }\n\n hooks.push({ name: script.name, sourcePath, targetPath, status });\n }\n\n return hooks;\n}\n\n/**\n * Sync hooks (copy scripts to ~/.panopticon/bin/)\n */\nexport function syncHooks(): { synced: string[]; errors: string[] } {\n const result = { synced: [] as string[], errors: [] as string[] };\n\n // Ensure bin directory exists\n mkdirSync(BIN_DIR, { recursive: true });\n\n const hooks = planHooksSync();\n\n for (const hook of hooks) {\n try {\n copyFileSync(hook.sourcePath, hook.targetPath);\n chmodSync(hook.targetPath, 0o755); // Make executable\n result.synced.push(hook.name);\n } catch (error) {\n result.errors.push(`${hook.name}: ${error}`);\n }\n }\n\n return result;\n}\n\n/**\n * Runtime-specific statusline configurations\n * Maps runtime to: config dir, statusline filename, settings file\n */\nconst STATUSLINE_TARGETS: Record<string, { configDir: string; scriptName: string; settingsFile: string }> = {\n claude: {\n configDir: join(homedir(), '.claude'),\n scriptName: 'statusline-command.sh',\n settingsFile: join(homedir(), '.claude', 'settings.json'),\n },\n // Other runtimes can be added as they support statusline\n};\n\n/**\n * Sync statusline script to all supported runtimes\n * Copies the canonical statusline.sh from panopticon scripts to each runtime's config dir\n * and ensures the runtime's settings.json references it.\n */\nexport function syncStatusline(): { synced: string[]; errors: string[] } {\n const result = { synced: [] as string[], errors: [] as string[] };\n\n const sourceScript = join(SOURCE_SCRIPTS_DIR, 'statusline.sh');\n if (!existsSync(sourceScript)) {\n return result;\n }\n\n for (const [runtime, target] of Object.entries(STATUSLINE_TARGETS)) {\n try {\n // Ensure config dir exists\n mkdirSync(target.configDir, { recursive: true });\n\n // Copy statusline script\n const targetScript = join(target.configDir, target.scriptName);\n copyFileSync(sourceScript, targetScript);\n chmodSync(targetScript, 0o755);\n\n // Update settings.json to reference the statusline\n updateSettingsStatusline(target.settingsFile, targetScript);\n\n result.synced.push(runtime);\n } catch (error) {\n result.errors.push(`${runtime}: ${error}`);\n }\n }\n\n return result;\n}\n\n/**\n * Update a settings.json file to include the statusLine configuration\n * Preserves all existing settings (hooks, etc.)\n */\nfunction updateSettingsStatusline(settingsFile: string, scriptPath: string): void {\n let settings: Record<string, any> = {};\n\n if (existsSync(settingsFile)) {\n try {\n settings = JSON.parse(readFileSync(settingsFile, 'utf-8'));\n } catch {\n // If settings file is corrupt, start fresh but preserve the file\n settings = {};\n }\n }\n\n // Only update if statusLine is missing or points to a different script\n const currentCommand = settings.statusLine?.command;\n if (currentCommand === scriptPath && settings.statusLine?.type === 'command') {\n return; // Already configured correctly\n }\n\n settings.statusLine = {\n type: 'command',\n command: scriptPath,\n padding: 0,\n };\n\n mkdirSync(dirname(settingsFile), { recursive: true });\n writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n}\n","/**\n * Cross-Tracker Linking\n *\n * Manages links between issues in different trackers.\n * Links are stored in a local JSON file for persistence.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport type { TrackerType } from './interface.js';\n\n// Link direction types\nexport type LinkDirection = 'blocks' | 'blocked_by' | 'related' | 'duplicate_of';\n\n// A single link between two issues\nexport interface TrackerLink {\n sourceIssueRef: string; // e.g., \"MIN-630\"\n sourceTracker: TrackerType;\n targetIssueRef: string; // e.g., \"#42\"\n targetTracker: TrackerType;\n direction: LinkDirection;\n createdAt: string; // ISO timestamp\n}\n\n// Storage format\ninterface LinkStore {\n version: 1;\n links: TrackerLink[];\n}\n\n/**\n * Parse an issue reference to extract tracker and ID\n * Examples:\n * \"#42\" -> { tracker: \"github\", ref: \"#42\" }\n * \"github#42\" -> { tracker: \"github\", ref: \"#42\" }\n * \"MIN-630\" -> { tracker: \"linear\", ref: \"MIN-630\" }\n * \"gitlab#15\" -> { tracker: \"gitlab\", ref: \"#15\" }\n */\nexport function parseIssueRef(ref: string): { tracker: TrackerType; ref: string } | null {\n // Explicit tracker prefix\n if (ref.startsWith('github#')) {\n return { tracker: 'github', ref: `#${ref.slice(7)}` };\n }\n if (ref.startsWith('gitlab#')) {\n return { tracker: 'gitlab', ref: `#${ref.slice(7)}` };\n }\n if (ref.startsWith('linear:')) {\n return { tracker: 'linear', ref: ref.slice(7) };\n }\n\n // GitHub-style refs (#number)\n if (/^#\\d+$/.test(ref)) {\n return { tracker: 'github', ref };\n }\n\n // Linear-style refs (XXX-123)\n if (/^[A-Z]+-\\d+$/i.test(ref)) {\n return { tracker: 'linear', ref: ref.toUpperCase() };\n }\n\n return null;\n}\n\n/**\n * Format an issue ref with tracker prefix for display\n */\nexport function formatIssueRef(ref: string, tracker: TrackerType): string {\n if (tracker === 'github') {\n return ref.startsWith('#') ? `github${ref}` : `github#${ref}`;\n }\n if (tracker === 'gitlab') {\n return ref.startsWith('#') ? `gitlab${ref}` : `gitlab#${ref}`;\n }\n return ref; // Linear refs are already unique\n}\n\n/**\n * Link Manager for cross-tracker issue linking\n */\nexport class LinkManager {\n private storePath: string;\n private store: LinkStore;\n\n constructor(storePath?: string) {\n this.storePath = storePath ?? join(homedir(), '.panopticon', 'links.json');\n this.store = this.load();\n }\n\n private load(): LinkStore {\n if (existsSync(this.storePath)) {\n try {\n const data = JSON.parse(readFileSync(this.storePath, 'utf-8'));\n if (data.version === 1) {\n return data;\n }\n } catch {\n // Fall through to default\n }\n }\n return { version: 1, links: [] };\n }\n\n private save(): void {\n const dir = join(this.storePath, '..');\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.storePath, JSON.stringify(this.store, null, 2));\n }\n\n /**\n * Add a link between two issues\n */\n addLink(\n source: { ref: string; tracker: TrackerType },\n target: { ref: string; tracker: TrackerType },\n direction: LinkDirection = 'related'\n ): TrackerLink {\n // Check if link already exists\n const existing = this.store.links.find(\n (l) =>\n l.sourceIssueRef === source.ref &&\n l.sourceTracker === source.tracker &&\n l.targetIssueRef === target.ref &&\n l.targetTracker === target.tracker\n );\n\n if (existing) {\n // Update direction if different\n if (existing.direction !== direction) {\n existing.direction = direction;\n this.save();\n }\n return existing;\n }\n\n const link: TrackerLink = {\n sourceIssueRef: source.ref,\n sourceTracker: source.tracker,\n targetIssueRef: target.ref,\n targetTracker: target.tracker,\n direction,\n createdAt: new Date().toISOString(),\n };\n\n this.store.links.push(link);\n this.save();\n return link;\n }\n\n /**\n * Remove a link between two issues\n */\n removeLink(\n source: { ref: string; tracker: TrackerType },\n target: { ref: string; tracker: TrackerType }\n ): boolean {\n const index = this.store.links.findIndex(\n (l) =>\n l.sourceIssueRef === source.ref &&\n l.sourceTracker === source.tracker &&\n l.targetIssueRef === target.ref &&\n l.targetTracker === target.tracker\n );\n\n if (index >= 0) {\n this.store.links.splice(index, 1);\n this.save();\n return true;\n }\n return false;\n }\n\n /**\n * Get all issues linked to a given issue\n */\n getLinkedIssues(ref: string, tracker: TrackerType): TrackerLink[] {\n return this.store.links.filter(\n (l) =>\n (l.sourceIssueRef === ref && l.sourceTracker === tracker) ||\n (l.targetIssueRef === ref && l.targetTracker === tracker)\n );\n }\n\n /**\n * Get all links (for debugging/admin)\n */\n getAllLinks(): TrackerLink[] {\n return [...this.store.links];\n }\n\n /**\n * Find linked issue in another tracker\n */\n findLinkedIssue(\n ref: string,\n sourceTracker: TrackerType,\n targetTracker: TrackerType\n ): string | null {\n // Check as source\n const asSource = this.store.links.find(\n (l) =>\n l.sourceIssueRef === ref &&\n l.sourceTracker === sourceTracker &&\n l.targetTracker === targetTracker\n );\n if (asSource) return asSource.targetIssueRef;\n\n // Check as target\n const asTarget = this.store.links.find(\n (l) =>\n l.targetIssueRef === ref &&\n l.targetTracker === sourceTracker &&\n l.sourceTracker === targetTracker\n );\n if (asTarget) return asTarget.sourceIssueRef;\n\n return null;\n }\n\n /**\n * Clear all links (for testing)\n */\n clear(): void {\n this.store.links = [];\n this.save();\n }\n}\n\n// Singleton instance\nlet _linkManager: LinkManager | null = null;\n\nexport function getLinkManager(): LinkManager {\n if (!_linkManager) {\n _linkManager = new LinkManager();\n }\n return _linkManager;\n}\n","/**\n * Issue Tracker Module\n *\n * Provides a unified interface for different issue tracking systems.\n */\n\n// Core interface and types\nexport type {\n IssueTracker,\n Issue,\n IssueFilters,\n IssueState,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\n\nexport {\n NotImplementedError,\n IssueNotFoundError,\n TrackerAuthError,\n} from './interface.js';\n\n// Tracker implementations\nexport { LinearTracker } from './linear.js';\nexport { GitHubTracker } from './github.js';\nexport { GitLabTracker } from './gitlab.js';\n\n// Factory functions\nexport type { TrackerConfig } from './factory.js';\nexport {\n createTracker,\n createTrackerFromConfig,\n getPrimaryTracker,\n getSecondaryTracker,\n getAllTrackers,\n} from './factory.js';\n\n// Cross-tracker linking\nexport type { TrackerLink, LinkDirection } from './linking.js';\nexport {\n LinkManager,\n getLinkManager,\n parseIssueRef,\n formatIssueRef,\n} from './linking.js';\n"],"mappings":"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","names":["existsSync","join","existsSync","mkdirSync","readdirSync","lstatSync","rmSync","readFileSync","join","homedir","existsSync","lstatSync","join","homedir","readdirSync","rmSync","mkdirSync","readFileSync","readFileSync","writeFileSync","existsSync","mkdirSync","join","homedir"]}
@@ -2,7 +2,7 @@ import {
2
2
  stepFailed,
3
3
  stepOk,
4
4
  stepSkipped
5
- } from "./chunk-R4KPLLRB.js";
5
+ } from "./chunk-SFX3BG6N.js";
6
6
  import {
7
7
  init_esm_shims
8
8
  } from "./chunk-ZHC57RCV.js";
@@ -61,4 +61,4 @@ async function compactBeads(ctx, opts = {}) {
61
61
  export {
62
62
  compactBeads
63
63
  };
64
- //# sourceMappingURL=chunk-MJXYTGK5.js.map
64
+ //# sourceMappingURL=chunk-JZWCL5S5.js.map