@nemus-cli/nemus 0.2.1

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 (320) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +275 -0
  4. package/assets/README.md +53 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/banner.svg +66 -0
  7. package/assets/favicon-32.png +0 -0
  8. package/assets/favicon-64.png +0 -0
  9. package/assets/icon-256.png +0 -0
  10. package/assets/icon.svg +23 -0
  11. package/assets/logo-256.png +0 -0
  12. package/assets/logo-512.png +0 -0
  13. package/assets/logo-wordmark.svg +35 -0
  14. package/assets/logo.svg +32 -0
  15. package/bin/workspace.js +130 -0
  16. package/dist/cli/ai-prompt.js +421 -0
  17. package/dist/cli/dashboard/AgentList.js +61 -0
  18. package/dist/cli/dashboard/DashboardSidebar.js +280 -0
  19. package/dist/cli/dashboard/HelpFooter.js +73 -0
  20. package/dist/cli/dashboard/StatusBadge.js +50 -0
  21. package/dist/cli/dashboard/index.js +11 -0
  22. package/dist/cli/dashboard/types.js +2 -0
  23. package/dist/cli/tui.js +48 -0
  24. package/dist/commands/ai-prompt.js +22 -0
  25. package/dist/commands/analyze-deps.js +138 -0
  26. package/dist/commands/archive.js +113 -0
  27. package/dist/commands/branch/create.js +119 -0
  28. package/dist/commands/branch/index.js +104 -0
  29. package/dist/commands/branch/merge.js +90 -0
  30. package/dist/commands/branch/rebase.js +84 -0
  31. package/dist/commands/branch/switch.js +165 -0
  32. package/dist/commands/cache/index.js +80 -0
  33. package/dist/commands/cache/manager.js +198 -0
  34. package/dist/commands/cleanup.js +153 -0
  35. package/dist/commands/configure-claude.js +55 -0
  36. package/dist/commands/configure.js +231 -0
  37. package/dist/commands/create.js +204 -0
  38. package/dist/commands/dashboard/hook-handler.js +162 -0
  39. package/dist/commands/dashboard/index.js +258 -0
  40. package/dist/commands/dashboard/launcher.js +40 -0
  41. package/dist/commands/dashboard/session-picker.js +106 -0
  42. package/dist/commands/dashboard/sidebar.js +39 -0
  43. package/dist/commands/dashboard/workspace-picker.js +95 -0
  44. package/dist/commands/delete.js +194 -0
  45. package/dist/commands/deprecated-aliases.js +44 -0
  46. package/dist/commands/diff.js +129 -0
  47. package/dist/commands/doctor.js +141 -0
  48. package/dist/commands/generate-docs.js +77 -0
  49. package/dist/commands/ghq-status.js +72 -0
  50. package/dist/commands/go.js +154 -0
  51. package/dist/commands/history.js +124 -0
  52. package/dist/commands/list.js +191 -0
  53. package/dist/commands/mcp/index.js +69 -0
  54. package/dist/commands/migrate.js +296 -0
  55. package/dist/commands/remove-repo.js +217 -0
  56. package/dist/commands/report-bug.js +112 -0
  57. package/dist/commands/run.js +147 -0
  58. package/dist/commands/save-context.js +171 -0
  59. package/dist/commands/sessions.js +129 -0
  60. package/dist/commands/status.js +138 -0
  61. package/dist/commands/suite/create.js +212 -0
  62. package/dist/commands/suite/delete.js +64 -0
  63. package/dist/commands/suite/export.js +126 -0
  64. package/dist/commands/suite/import.js +128 -0
  65. package/dist/commands/suite/index.js +94 -0
  66. package/dist/commands/suite/list.js +49 -0
  67. package/dist/commands/suite/use.js +227 -0
  68. package/dist/commands/sync.js +117 -0
  69. package/dist/commands/update.js +203 -0
  70. package/dist/mcp/install.js +550 -0
  71. package/dist/mcp/server.js +398 -0
  72. package/dist/mcp/tools.js +922 -0
  73. package/dist/pi-extensions/permission-sync.ts.template +41 -0
  74. package/dist/pi-extensions/permissions-gate.ts.template +69 -0
  75. package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
  76. package/dist/pi-extensions/review-reminder.ts.template +58 -0
  77. package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
  78. package/dist/program.js +136 -0
  79. package/dist/scripts/workspace-table.py +394 -0
  80. package/dist/types/dashboard.js +11 -0
  81. package/dist/types/index.js +2 -0
  82. package/dist/utils/agent-config.js +263 -0
  83. package/dist/utils/agent-state.js +178 -0
  84. package/dist/utils/banner.js +24 -0
  85. package/dist/utils/branch-operations.js +232 -0
  86. package/dist/utils/bug-report.js +355 -0
  87. package/dist/utils/cache.js +168 -0
  88. package/dist/utils/claude-integration.js +617 -0
  89. package/dist/utils/claude-sessions.js +260 -0
  90. package/dist/utils/cleanup-operations.js +120 -0
  91. package/dist/utils/colors.js +26 -0
  92. package/dist/utils/command-helpers.js +44 -0
  93. package/dist/utils/config.js +150 -0
  94. package/dist/utils/context-file.js +33 -0
  95. package/dist/utils/dashboard-hooks.js +172 -0
  96. package/dist/utils/dependency-analyzer.js +234 -0
  97. package/dist/utils/diff-operations.js +80 -0
  98. package/dist/utils/doc-generator.js +184 -0
  99. package/dist/utils/ghq-integration.js +332 -0
  100. package/dist/utils/git-operations.js +237 -0
  101. package/dist/utils/git-status.js +187 -0
  102. package/dist/utils/github.js +68 -0
  103. package/dist/utils/health-checks.js +345 -0
  104. package/dist/utils/history.js +150 -0
  105. package/dist/utils/hooks.js +100 -0
  106. package/dist/utils/logger.js +40 -0
  107. package/dist/utils/permission-sync.js +681 -0
  108. package/dist/utils/pi-extensions.js +224 -0
  109. package/dist/utils/progress.js +92 -0
  110. package/dist/utils/prompts.js +279 -0
  111. package/dist/utils/repo-resolver.js +260 -0
  112. package/dist/utils/retry.js +65 -0
  113. package/dist/utils/run-operations.js +30 -0
  114. package/dist/utils/suite.js +159 -0
  115. package/dist/utils/sync-operations.js +107 -0
  116. package/dist/utils/tmux-dashboard.js +184 -0
  117. package/dist/utils/validation.js +153 -0
  118. package/dist/utils/version-check.js +121 -0
  119. package/dist/utils/workspace-meta.js +185 -0
  120. package/install-shell-integration.sh +532 -0
  121. package/install.sh +401 -0
  122. package/package.json +90 -0
  123. package/remove-shell-block.awk +44 -0
  124. package/scripts/postinstall.js +85 -0
  125. package/skills/analyze-deps.md +23 -0
  126. package/skills/archive-workspace.md +26 -0
  127. package/skills/branch-create.md +18 -0
  128. package/skills/create-workspace.md +39 -0
  129. package/skills/delete-workspace.md +37 -0
  130. package/skills/list-org-repos.md +26 -0
  131. package/skills/list-suites.md +24 -0
  132. package/skills/list-workspaces.md +27 -0
  133. package/skills/refresh-workspace-docs.md +89 -0
  134. package/skills/remove-repo.md +25 -0
  135. package/skills/run-command.md +23 -0
  136. package/skills/save-context.md +68 -0
  137. package/skills/search-repos.md +31 -0
  138. package/skills/snapshot-list.md +18 -0
  139. package/skills/snapshot-save.md +12 -0
  140. package/skills/switch-branch.md +18 -0
  141. package/skills/update-cache.md +21 -0
  142. package/skills/update-workspace.md +66 -0
  143. package/skills/workspace-cleanup.md +18 -0
  144. package/skills/workspace-diff.md +23 -0
  145. package/skills/workspace-doctor.md +23 -0
  146. package/skills/workspace-info.md +45 -0
  147. package/skills/workspace-manager/SKILL.md +201 -0
  148. package/skills/workspace-manager/references/ai-prompt.md +44 -0
  149. package/skills/workspace-manager/references/analyze-deps.md +45 -0
  150. package/skills/workspace-manager/references/archive-workspace.md +38 -0
  151. package/skills/workspace-manager/references/branch-create.md +31 -0
  152. package/skills/workspace-manager/references/branch-merge.md +22 -0
  153. package/skills/workspace-manager/references/branch-rebase.md +22 -0
  154. package/skills/workspace-manager/references/cache-clear.md +18 -0
  155. package/skills/workspace-manager/references/cache-info.md +22 -0
  156. package/skills/workspace-manager/references/configure-claude.md +31 -0
  157. package/skills/workspace-manager/references/configure.md +35 -0
  158. package/skills/workspace-manager/references/create-workspace.md +45 -0
  159. package/skills/workspace-manager/references/dashboard.md +61 -0
  160. package/skills/workspace-manager/references/delete-workspace.md +33 -0
  161. package/skills/workspace-manager/references/generate-docs.md +36 -0
  162. package/skills/workspace-manager/references/ghq-status.md +28 -0
  163. package/skills/workspace-manager/references/go.md +33 -0
  164. package/skills/workspace-manager/references/history.md +41 -0
  165. package/skills/workspace-manager/references/list-org-repos.md +26 -0
  166. package/skills/workspace-manager/references/list-suites.md +21 -0
  167. package/skills/workspace-manager/references/list-workspaces.md +27 -0
  168. package/skills/workspace-manager/references/mcp.md +49 -0
  169. package/skills/workspace-manager/references/remove-repo.md +26 -0
  170. package/skills/workspace-manager/references/run-command.md +44 -0
  171. package/skills/workspace-manager/references/search-repos.md +24 -0
  172. package/skills/workspace-manager/references/sessions.md +27 -0
  173. package/skills/workspace-manager/references/suite-create.md +22 -0
  174. package/skills/workspace-manager/references/suite-delete.md +21 -0
  175. package/skills/workspace-manager/references/suite-export.md +19 -0
  176. package/skills/workspace-manager/references/suite-import.md +19 -0
  177. package/skills/workspace-manager/references/suite-use.md +27 -0
  178. package/skills/workspace-manager/references/switch-branch.md +25 -0
  179. package/skills/workspace-manager/references/update-cache.md +22 -0
  180. package/skills/workspace-manager/references/update-workspace.md +50 -0
  181. package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
  182. package/skills/workspace-manager/references/workspace-diff.md +27 -0
  183. package/skills/workspace-manager/references/workspace-doctor.md +27 -0
  184. package/skills/workspace-manager/references/workspace-info.md +33 -0
  185. package/skills/workspace-manager/references/workspace-status.md +43 -0
  186. package/skills/workspace-manager/references/workspace-sync.md +26 -0
  187. package/skills/workspace-status.md +26 -0
  188. package/skills/workspace-sync.md +23 -0
  189. package/src/cli/ai-prompt.test.ts +667 -0
  190. package/src/cli/ai-prompt.ts +421 -0
  191. package/src/cli/dashboard/AgentList.tsx +44 -0
  192. package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
  193. package/src/cli/dashboard/HelpFooter.tsx +22 -0
  194. package/src/cli/dashboard/StatusBadge.tsx +23 -0
  195. package/src/cli/dashboard/index.ts +5 -0
  196. package/src/cli/dashboard/types.ts +16 -0
  197. package/src/cli/tui.tsx +51 -0
  198. package/src/cli/workspace-cli.test.ts +30 -0
  199. package/src/commands/ai-prompt.ts +21 -0
  200. package/src/commands/analyze-deps.ts +119 -0
  201. package/src/commands/archive.ts +114 -0
  202. package/src/commands/branch/create.ts +94 -0
  203. package/src/commands/branch/index.ts +74 -0
  204. package/src/commands/branch/merge.ts +61 -0
  205. package/src/commands/branch/rebase.ts +54 -0
  206. package/src/commands/branch/switch.ts +150 -0
  207. package/src/commands/cache/index.ts +51 -0
  208. package/src/commands/cache/manager.ts +168 -0
  209. package/src/commands/cleanup.ts +132 -0
  210. package/src/commands/configure-claude.ts +55 -0
  211. package/src/commands/configure.ts +204 -0
  212. package/src/commands/create.ts +196 -0
  213. package/src/commands/dashboard/hook-handler.ts +138 -0
  214. package/src/commands/dashboard/index.ts +248 -0
  215. package/src/commands/dashboard/launcher.ts +42 -0
  216. package/src/commands/dashboard/session-picker.ts +73 -0
  217. package/src/commands/dashboard/sidebar.js +39 -0
  218. package/src/commands/dashboard/workspace-picker.ts +62 -0
  219. package/src/commands/delete.test.ts +250 -0
  220. package/src/commands/delete.ts +171 -0
  221. package/src/commands/deprecated-aliases.ts +55 -0
  222. package/src/commands/diff.ts +106 -0
  223. package/src/commands/doctor.ts +110 -0
  224. package/src/commands/generate-docs.ts +45 -0
  225. package/src/commands/ghq-status.ts +70 -0
  226. package/src/commands/go.ts +125 -0
  227. package/src/commands/history.ts +136 -0
  228. package/src/commands/list.test.ts +219 -0
  229. package/src/commands/list.ts +176 -0
  230. package/src/commands/mcp/index.ts +39 -0
  231. package/src/commands/migrate.ts +277 -0
  232. package/src/commands/remove-repo.ts +194 -0
  233. package/src/commands/report-bug.ts +88 -0
  234. package/src/commands/run.ts +125 -0
  235. package/src/commands/save-context.test.ts +113 -0
  236. package/src/commands/save-context.ts +141 -0
  237. package/src/commands/sessions.ts +99 -0
  238. package/src/commands/status.ts +111 -0
  239. package/src/commands/suite/create.ts +240 -0
  240. package/src/commands/suite/delete.ts +65 -0
  241. package/src/commands/suite/export.ts +97 -0
  242. package/src/commands/suite/import.ts +100 -0
  243. package/src/commands/suite/index.ts +66 -0
  244. package/src/commands/suite/list.ts +54 -0
  245. package/src/commands/suite/use.ts +232 -0
  246. package/src/commands/sync.ts +97 -0
  247. package/src/commands/update.ts +197 -0
  248. package/src/mcp/install.ts +529 -0
  249. package/src/mcp/server.ts +579 -0
  250. package/src/mcp/tools.test.ts +545 -0
  251. package/src/mcp/tools.ts +1019 -0
  252. package/src/mcp/update-workspace.test.ts +220 -0
  253. package/src/pi-extensions/permission-sync.ts.template +41 -0
  254. package/src/pi-extensions/permissions-gate.ts.template +69 -0
  255. package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
  256. package/src/pi-extensions/review-reminder.ts.template +58 -0
  257. package/src/pi-extensions/workspace-input-status.ts.template +919 -0
  258. package/src/program.ts +112 -0
  259. package/src/scripts/workspace-table.py +394 -0
  260. package/src/types/dashboard.ts +29 -0
  261. package/src/types/index.ts +109 -0
  262. package/src/types/ink.d.ts +171 -0
  263. package/src/utils/agent-config.test.ts +303 -0
  264. package/src/utils/agent-config.ts +264 -0
  265. package/src/utils/agent-state.test.ts +166 -0
  266. package/src/utils/agent-state.ts +135 -0
  267. package/src/utils/banner.ts +24 -0
  268. package/src/utils/branch-operations.ts +237 -0
  269. package/src/utils/bug-report.test.ts +174 -0
  270. package/src/utils/bug-report.ts +355 -0
  271. package/src/utils/cache.ts +150 -0
  272. package/src/utils/claude-integration.test.ts +381 -0
  273. package/src/utils/claude-integration.ts +649 -0
  274. package/src/utils/claude-sessions.test.ts +262 -0
  275. package/src/utils/claude-sessions.ts +251 -0
  276. package/src/utils/cleanup-operations.ts +94 -0
  277. package/src/utils/colors.ts +25 -0
  278. package/src/utils/command-helpers.ts +46 -0
  279. package/src/utils/config.test.ts +203 -0
  280. package/src/utils/config.ts +133 -0
  281. package/src/utils/context-file.ts +36 -0
  282. package/src/utils/dashboard-hooks.test.ts +168 -0
  283. package/src/utils/dashboard-hooks.ts +163 -0
  284. package/src/utils/dependency-analyzer.ts +243 -0
  285. package/src/utils/diff-operations.ts +59 -0
  286. package/src/utils/doc-generator.ts +187 -0
  287. package/src/utils/ghq-integration.test.ts +195 -0
  288. package/src/utils/ghq-integration.ts +317 -0
  289. package/src/utils/git-operations.test.ts +265 -0
  290. package/src/utils/git-operations.ts +233 -0
  291. package/src/utils/git-status.ts +171 -0
  292. package/src/utils/github.ts +74 -0
  293. package/src/utils/health-checks.ts +359 -0
  294. package/src/utils/history.ts +140 -0
  295. package/src/utils/hooks.ts +92 -0
  296. package/src/utils/logger.ts +37 -0
  297. package/src/utils/permission-sync.test.ts +593 -0
  298. package/src/utils/permission-sync.ts +720 -0
  299. package/src/utils/pi-extensions.test.ts +182 -0
  300. package/src/utils/pi-extensions.ts +226 -0
  301. package/src/utils/progress.ts +73 -0
  302. package/src/utils/prompts.test.ts +169 -0
  303. package/src/utils/prompts.ts +296 -0
  304. package/src/utils/repo-resolver.test.ts +313 -0
  305. package/src/utils/repo-resolver.ts +294 -0
  306. package/src/utils/retry.test.ts +124 -0
  307. package/src/utils/retry.ts +85 -0
  308. package/src/utils/run-operations.ts +38 -0
  309. package/src/utils/suite.test.ts +256 -0
  310. package/src/utils/suite.ts +144 -0
  311. package/src/utils/sync-operations.ts +87 -0
  312. package/src/utils/tmux-dashboard.ts +193 -0
  313. package/src/utils/validation.test.ts +139 -0
  314. package/src/utils/validation.ts +128 -0
  315. package/src/utils/version-check.test.ts +108 -0
  316. package/src/utils/version-check.ts +96 -0
  317. package/src/utils/workspace-meta.ts +173 -0
  318. package/sync-permissions.sh +137 -0
  319. package/tsconfig.json +20 -0
  320. package/uninstall.sh +162 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Type declarations for ink v5 (ESM package used via dynamic import).
3
+ * Provides basic types for the components and hooks we use.
4
+ * Full types live in node_modules/ink/build/index.d.ts but can't be
5
+ * resolved with moduleResolution: "node".
6
+ */
7
+
8
+ declare module 'ink' {
9
+ import { FC, ReactNode, Key as ReactKey } from 'react';
10
+
11
+ // Render
12
+ export interface Instance {
13
+ rerender: (tree: ReactNode) => void;
14
+ unmount: () => void;
15
+ waitUntilExit: () => Promise<void>;
16
+ cleanup: () => void;
17
+ clear: () => void;
18
+ }
19
+
20
+ export interface RenderOptions {
21
+ stdout?: NodeJS.WriteStream;
22
+ stdin?: NodeJS.ReadStream;
23
+ stderr?: NodeJS.WriteStream;
24
+ debug?: boolean;
25
+ exitOnCtrlC?: boolean;
26
+ patchConsole?: boolean;
27
+ }
28
+
29
+ export function render(tree: ReactNode, options?: RenderOptions): Instance;
30
+
31
+ // Components
32
+ export interface BoxProps {
33
+ children?: ReactNode;
34
+ flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
35
+ flexGrow?: number;
36
+ flexShrink?: number;
37
+ flexBasis?: number | string;
38
+ alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
39
+ alignSelf?: 'auto' | 'flex-start' | 'center' | 'flex-end' | 'stretch';
40
+ justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
41
+ width?: number | string;
42
+ height?: number | string;
43
+ minWidth?: number;
44
+ minHeight?: number;
45
+ padding?: number;
46
+ paddingX?: number;
47
+ paddingY?: number;
48
+ paddingTop?: number;
49
+ paddingBottom?: number;
50
+ paddingLeft?: number;
51
+ paddingRight?: number;
52
+ margin?: number;
53
+ marginX?: number;
54
+ marginY?: number;
55
+ marginTop?: number;
56
+ marginBottom?: number;
57
+ marginLeft?: number;
58
+ marginRight?: number;
59
+ gap?: number;
60
+ columnGap?: number;
61
+ rowGap?: number;
62
+ borderStyle?: 'single' | 'double' | 'round' | 'bold' | 'singleDouble' | 'doubleSingle' | 'classic' | 'arrow';
63
+ borderColor?: string;
64
+ borderTop?: boolean;
65
+ borderBottom?: boolean;
66
+ borderLeft?: boolean;
67
+ borderRight?: boolean;
68
+ borderDimColor?: boolean;
69
+ display?: 'flex' | 'none';
70
+ overflow?: 'visible' | 'hidden';
71
+ }
72
+
73
+ export const Box: FC<BoxProps>;
74
+
75
+ export interface TextProps {
76
+ children?: ReactNode;
77
+ color?: string;
78
+ backgroundColor?: string;
79
+ dimColor?: boolean;
80
+ bold?: boolean;
81
+ italic?: boolean;
82
+ underline?: boolean;
83
+ strikethrough?: boolean;
84
+ inverse?: boolean;
85
+ wrap?: 'wrap' | 'truncate' | 'truncate-start' | 'truncate-middle' | 'truncate-end';
86
+ }
87
+
88
+ export const Text: FC<TextProps>;
89
+
90
+ export const Newline: FC<{ count?: number }>;
91
+ export const Spacer: FC;
92
+
93
+ export interface StaticProps<T> {
94
+ items: T[];
95
+ children: (item: T, index: number) => ReactNode;
96
+ style?: BoxProps;
97
+ }
98
+ export function Static<T>(props: StaticProps<T>): ReactNode;
99
+
100
+ export interface TransformProps {
101
+ children?: ReactNode;
102
+ transform: (children: string, index: number) => string;
103
+ }
104
+ export const Transform: FC<TransformProps>;
105
+
106
+ // Hooks
107
+ export interface Key {
108
+ upArrow: boolean;
109
+ downArrow: boolean;
110
+ leftArrow: boolean;
111
+ rightArrow: boolean;
112
+ pageDown: boolean;
113
+ pageUp: boolean;
114
+ return: boolean;
115
+ escape: boolean;
116
+ ctrl: boolean;
117
+ shift: boolean;
118
+ tab: boolean;
119
+ backspace: boolean;
120
+ delete: boolean;
121
+ meta: boolean;
122
+ }
123
+
124
+ export function useInput(
125
+ inputHandler: (input: string, key: Key) => void,
126
+ options?: { isActive?: boolean }
127
+ ): void;
128
+
129
+ export function useApp(): { exit: (error?: Error) => void };
130
+ export function useStdin(): { stdin: NodeJS.ReadStream; isRawModeSupported: boolean; setRawMode: (mode: boolean) => void };
131
+ export function useStdout(): { stdout: NodeJS.WriteStream; write: (data: string) => void };
132
+ export function useStderr(): { stderr: NodeJS.WriteStream; write: (data: string) => void };
133
+ export function useFocus(options?: { autoFocus?: boolean; isActive?: boolean; id?: string }): { isFocused: boolean };
134
+ export function useFocusManager(): { focusNext: () => void; focusPrevious: () => void; focus: (id: string) => void; enableFocus: () => void; disableFocus: () => void };
135
+ export function measureElement(ref: { current: any }): { width: number; height: number };
136
+ }
137
+
138
+ declare module 'ink-select-input' {
139
+ import { FC } from 'react';
140
+
141
+ export interface Item<V> {
142
+ key?: string;
143
+ label: string;
144
+ value: V;
145
+ }
146
+
147
+ export interface Props<V> {
148
+ items?: Array<Item<V>>;
149
+ isFocused?: boolean;
150
+ initialIndex?: number;
151
+ limit?: number;
152
+ indicatorComponent?: FC<{ isSelected: boolean }>;
153
+ itemComponent?: FC<{ isSelected: boolean; label: string }>;
154
+ onSelect?: (item: Item<V>) => void;
155
+ onHighlight?: (item: Item<V>) => void;
156
+ }
157
+
158
+ function SelectInput<V>(props: Props<V>): JSX.Element;
159
+ export default SelectInput;
160
+ }
161
+
162
+ declare module 'ink-spinner' {
163
+ import { FC } from 'react';
164
+
165
+ export interface Props {
166
+ type?: string;
167
+ }
168
+
169
+ const Spinner: FC<Props>;
170
+ export default Spinner;
171
+ }
@@ -0,0 +1,303 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+
6
+ // Mock os.homedir so agent-config computes paths relative to our temp dir
7
+ vi.mock('os', async () => {
8
+ const actual = await vi.importActual<typeof import('os')>('os');
9
+ return { ...actual, homedir: vi.fn(actual.homedir) };
10
+ });
11
+
12
+ // Mock child_process for CLI availability checks
13
+ vi.mock('child_process', async () => {
14
+ const actual = await vi.importActual<typeof import('child_process')>('child_process');
15
+ return { ...actual, execSync: vi.fn(actual.execSync) };
16
+ });
17
+
18
+ const execSync = vi.mocked(await import('child_process')).execSync;
19
+
20
+ describe('agent-config', () => {
21
+ let tempDir: string;
22
+ const originalEnv = { ...process.env };
23
+
24
+ beforeEach(() => {
25
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-config-test-'));
26
+ const cacheDir = path.join(tempDir, '.workspace-manager-cache');
27
+ fs.mkdirSync(cacheDir, { recursive: true });
28
+ process.env.WORKSPACE_MANAGER_DIR = path.join(tempDir, 'workspaces');
29
+ process.env.WORKSPACE_MANAGER_CACHE_DIR = cacheDir;
30
+ vi.mocked(os.homedir).mockReturnValue(tempDir);
31
+ vi.resetModules();
32
+ });
33
+
34
+ afterEach(() => {
35
+ process.env = { ...originalEnv };
36
+ fs.rmSync(tempDir, { recursive: true, force: true });
37
+ vi.restoreAllMocks();
38
+ });
39
+
40
+ function writeConfig(cfg: Record<string, unknown>) {
41
+ const cacheDir = path.join(tempDir, '.workspace-manager-cache');
42
+ fs.writeFileSync(path.join(cacheDir, 'config.json'), JSON.stringify(cfg));
43
+ }
44
+
45
+ async function loadModule() {
46
+ // Default mock: only claude and pi CLIs are available
47
+ // Tests that need all agents or specific availability override this before/after calling loadModule
48
+ execSync.mockImplementation((cmd: string) => {
49
+ if (cmd.toString().includes('claude --version')) return Buffer.from('1.0.0');
50
+ if (cmd.toString().includes('pi --version')) return Buffer.from('1.0.0');
51
+ throw new Error('not found');
52
+ });
53
+ const mod = await import('./agent-config');
54
+ // Reset caches to ensure clean state for each test
55
+ mod.resetAgentConfigCache();
56
+ return mod;
57
+ }
58
+
59
+ describe('getAgentPaths', () => {
60
+ it('returns Claude paths for "claude"', async () => {
61
+ const { getAgentPaths } = await loadModule();
62
+ const paths = getAgentPaths('claude');
63
+ expect(paths.type).toBe('claude');
64
+ expect(paths.skillsDir).toContain('.claude');
65
+ expect(paths.skillsDir).toContain('skills');
66
+ expect(paths.contextFileName).toBe('CLAUDE.md');
67
+ expect(paths.launchCommand).toBe('claude');
68
+ expect(paths.supportsMcp).toBe(true);
69
+ expect(paths.supportsHooks).toBe(true);
70
+ });
71
+
72
+ it('returns Pi paths for "pi"', async () => {
73
+ const { getAgentPaths } = await loadModule();
74
+ const paths = getAgentPaths('pi');
75
+ expect(paths.type).toBe('pi');
76
+ expect(paths.skillsDir).toContain('.pi');
77
+ expect(paths.skillsDir).toContain('skills');
78
+ expect(paths.contextFileName).toBe('AGENTS.md');
79
+ expect(paths.launchCommand).toBe('pi');
80
+ expect(paths.supportsMcp).toBe(false);
81
+ expect(paths.supportsHooks).toBe(false);
82
+ });
83
+ });
84
+
85
+ describe('getActiveAgents', () => {
86
+ it('returns only Claude when aiAgent is "claude"', async () => {
87
+ writeConfig({ aiAgent: 'claude' });
88
+ const { getActiveAgents } = await loadModule();
89
+ const agents = getActiveAgents();
90
+ expect(agents).toHaveLength(1);
91
+ expect(agents[0].type).toBe('claude');
92
+ });
93
+
94
+ it('returns only Pi when aiAgent is "pi"', async () => {
95
+ writeConfig({ aiAgent: 'pi' });
96
+ const { getActiveAgents } = await loadModule();
97
+ const agents = getActiveAgents();
98
+ expect(agents).toHaveLength(1);
99
+ expect(agents[0].type).toBe('pi');
100
+ });
101
+
102
+ it('returns all available agents when aiAgent is "both"', async () => {
103
+ writeConfig({ aiAgent: 'both' });
104
+ // Mock all CLIs as available
105
+ execSync.mockImplementation(() => Buffer.from('1.0.0'));
106
+ const { getActiveAgents, resetAgentConfigCache } = await import('./agent-config');
107
+ resetAgentConfigCache();
108
+ const agents = getActiveAgents();
109
+ expect(agents).toHaveLength(5);
110
+ expect(agents.map(a => a.type)).toEqual(['claude', 'pi', 'opencode', 'codex', 'gemini']);
111
+ });
112
+
113
+ it('resolves auto to claude when only claude CLI available', async () => {
114
+ writeConfig({ aiAgent: 'auto' });
115
+ execSync.mockImplementation((cmd: string) => {
116
+ if (cmd.toString().includes('claude --version')) return Buffer.from('1.0.0');
117
+ throw new Error('not found');
118
+ });
119
+ const { getActiveAgents, resetAgentConfigCache } = await import('./agent-config');
120
+ resetAgentConfigCache();
121
+ const agents = getActiveAgents();
122
+ expect(agents).toHaveLength(1);
123
+ expect(agents[0].type).toBe('claude');
124
+ });
125
+
126
+ it('resolves auto to pi when only pi CLI available', async () => {
127
+ writeConfig({ aiAgent: 'auto' });
128
+ execSync.mockImplementation((cmd: string) => {
129
+ if (cmd.toString().includes('pi --version')) return Buffer.from('1.0.0');
130
+ throw new Error('not found');
131
+ });
132
+ const { getActiveAgents, resetAgentConfigCache } = await import('./agent-config');
133
+ resetAgentConfigCache();
134
+ const agents = getActiveAgents();
135
+ expect(agents).toHaveLength(1);
136
+ expect(agents[0].type).toBe('pi');
137
+ });
138
+
139
+ it('resolves auto to both when both CLIs available', async () => {
140
+ writeConfig({ aiAgent: 'auto' });
141
+ execSync.mockImplementation(() => Buffer.from('1.0.0'));
142
+ const { getActiveAgents, resetAgentConfigCache } = await import('./agent-config');
143
+ resetAgentConfigCache();
144
+ const agents = getActiveAgents();
145
+ expect(agents).toHaveLength(5);
146
+ expect(agents.map(a => a.type)).toContain('claude');
147
+ expect(agents.map(a => a.type)).toContain('pi');
148
+ expect(agents.map(a => a.type)).toContain('opencode');
149
+ });
150
+ });
151
+
152
+ describe('getPrimaryAgent', () => {
153
+ it('returns Claude when primaryAgent is "claude"', async () => {
154
+ writeConfig({ primaryAgent: 'claude' });
155
+ const { getPrimaryAgent } = await loadModule();
156
+ expect(getPrimaryAgent().type).toBe('claude');
157
+ });
158
+
159
+ it('returns Pi when primaryAgent is "pi"', async () => {
160
+ writeConfig({ primaryAgent: 'pi' });
161
+ const { getPrimaryAgent } = await loadModule();
162
+ expect(getPrimaryAgent().type).toBe('pi');
163
+ });
164
+
165
+ it('returns an agent when primaryAgent is "auto" (default)', async () => {
166
+ writeConfig({});
167
+ const { getPrimaryAgent } = await loadModule();
168
+ // auto detects installed CLIs — just verify it returns something
169
+ const agent = getPrimaryAgent();
170
+ expect(['claude', 'pi']).toContain(agent.type);
171
+ });
172
+ });
173
+
174
+ describe('getSkillsTargetDirs', () => {
175
+ it('returns one dir for single agent', async () => {
176
+ writeConfig({ aiAgent: 'claude' });
177
+ const { getSkillsTargetDirs } = await loadModule();
178
+ const dirs = getSkillsTargetDirs();
179
+ expect(dirs).toHaveLength(1);
180
+ expect(dirs[0]).toContain('.claude');
181
+ });
182
+
183
+ it('returns two dirs for both agents', async () => {
184
+ writeConfig({ aiAgent: 'both' });
185
+ const { getSkillsTargetDirs } = await loadModule();
186
+ const dirs = getSkillsTargetDirs();
187
+ expect(dirs).toHaveLength(2);
188
+ expect(dirs[0]).toContain('.claude');
189
+ expect(dirs[1]).toContain('.pi');
190
+ });
191
+
192
+ it('returns Pi dir only for pi agent', async () => {
193
+ writeConfig({ aiAgent: 'pi' });
194
+ const { getSkillsTargetDirs } = await loadModule();
195
+ const dirs = getSkillsTargetDirs();
196
+ expect(dirs).toHaveLength(1);
197
+ expect(dirs[0]).toContain('.pi');
198
+ });
199
+ });
200
+
201
+ describe('getContextFileNames', () => {
202
+ it('returns CLAUDE.md for Claude', async () => {
203
+ writeConfig({ aiAgent: 'claude' });
204
+ const { getContextFileNames } = await loadModule();
205
+ expect(getContextFileNames()).toEqual(['CLAUDE.md']);
206
+ });
207
+
208
+ it('returns AGENTS.md for Pi', async () => {
209
+ writeConfig({ aiAgent: 'pi' });
210
+ const { getContextFileNames } = await loadModule();
211
+ expect(getContextFileNames()).toEqual(['AGENTS.md']);
212
+ });
213
+
214
+ it('returns both file names for both agents', async () => {
215
+ writeConfig({ aiAgent: 'both' });
216
+ const { getContextFileNames } = await loadModule();
217
+ const names = getContextFileNames();
218
+ expect(names).toContain('CLAUDE.md');
219
+ expect(names).toContain('AGENTS.md');
220
+ expect(names).toHaveLength(2);
221
+ });
222
+ });
223
+
224
+ describe('getAllKnownContextFileNames', () => {
225
+ it('lists AGENTS.md before CLAUDE.md (per-repo scan priority)', async () => {
226
+ // findContextFilesInDir embeds the FIRST existing candidate per dir.
227
+ // In dual-agent repos CLAUDE.md is often a thin "@AGENTS.md" shim, so
228
+ // AGENTS.md must win to embed the real content, not the pointer.
229
+ const { getAllKnownContextFileNames } = await loadModule();
230
+ const names = getAllKnownContextFileNames();
231
+ expect(names.indexOf('AGENTS.md')).toBeLessThan(names.indexOf('CLAUDE.md'));
232
+ });
233
+
234
+ it('still recognizes legacy .claude.md (kept last)', async () => {
235
+ const { getAllKnownContextFileNames } = await loadModule();
236
+ const names = getAllKnownContextFileNames();
237
+ expect(names).toContain('.claude.md');
238
+ expect(names.indexOf('.claude.md')).toBe(names.length - 1);
239
+ });
240
+ });
241
+
242
+ describe('getHookTargetFiles', () => {
243
+ it('returns Claude settings for Claude agent', async () => {
244
+ writeConfig({ aiAgent: 'claude' });
245
+ const { getHookTargetFiles } = await loadModule();
246
+ const files = getHookTargetFiles();
247
+ expect(files).toHaveLength(1);
248
+ expect(files[0]).toContain('.claude');
249
+ });
250
+
251
+ it('returns empty for Pi-only (Pi does not support hooks)', async () => {
252
+ writeConfig({ aiAgent: 'pi' });
253
+ const { getHookTargetFiles } = await loadModule();
254
+ expect(getHookTargetFiles()).toHaveLength(0);
255
+ });
256
+
257
+ it('returns only Claude settings for both (Pi has no hooks)', async () => {
258
+ writeConfig({ aiAgent: 'both' });
259
+ const { getHookTargetFiles } = await loadModule();
260
+ const files = getHookTargetFiles();
261
+ expect(files).toHaveLength(1);
262
+ expect(files[0]).toContain('.claude');
263
+ });
264
+ });
265
+
266
+ describe('getMcpAgents', () => {
267
+ it('returns Claude for "claude" config', async () => {
268
+ writeConfig({ aiAgent: 'claude' });
269
+ const { getMcpAgents } = await loadModule();
270
+ const agents = getMcpAgents();
271
+ expect(agents).toHaveLength(1);
272
+ expect(agents[0].type).toBe('claude');
273
+ });
274
+
275
+ it('returns empty for Pi-only (Pi has no MCP)', async () => {
276
+ writeConfig({ aiAgent: 'pi' });
277
+ const { getMcpAgents } = await loadModule();
278
+ expect(getMcpAgents()).toHaveLength(0);
279
+ });
280
+
281
+ it('returns only Claude for both (Pi has no MCP)', async () => {
282
+ writeConfig({ aiAgent: 'both' });
283
+ const { getMcpAgents } = await loadModule();
284
+ const agents = getMcpAgents();
285
+ expect(agents).toHaveLength(1);
286
+ expect(agents[0].type).toBe('claude');
287
+ });
288
+ });
289
+
290
+ describe('resumeCommand', () => {
291
+ it('generates correct Claude resume command', async () => {
292
+ const { getAgentPaths } = await loadModule();
293
+ const claude = getAgentPaths('claude');
294
+ expect(claude.resumeCommand('abc-123')).toBe('claude --resume abc-123 --fork-session');
295
+ });
296
+
297
+ it('generates correct Pi resume command', async () => {
298
+ const { getAgentPaths } = await loadModule();
299
+ const pi = getAgentPaths('pi');
300
+ expect(pi.resumeCommand('abc-123')).toBe('pi --session abc-123 --fork');
301
+ });
302
+ });
303
+ });