@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,224 @@
1
+ "use strict";
2
+ /**
3
+ * Pi extension management — install/uninstall Pi extensions that replicate
4
+ * Claude Code hooks and MCP functionality.
5
+ *
6
+ * Pi uses TypeScript extensions instead of:
7
+ * - settings.json hooks (Stop, SessionStart, PostToolUse)
8
+ * - .mcp.json / `claude mcp add` (MCP servers)
9
+ *
10
+ * This module generates and installs extensions from bundled templates.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.installPiExtensions = installPiExtensions;
47
+ exports.uninstallPiExtensions = uninstallPiExtensions;
48
+ exports.getPiExtensionStatus = getPiExtensionStatus;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const os = __importStar(require("os"));
52
+ const logger_1 = require("./logger");
53
+ const PI_EXTENSIONS_DIR = path.join(os.homedir(), '.pi', 'agent', 'extensions');
54
+ /** Marker comment that identifies workspace-manager managed extensions */
55
+ const MANAGED_MARKER = '(auto-generated by workspace-manager)';
56
+ function getTemplatesDir() {
57
+ // When compiled: dist/utils/ -> dist/pi-extensions/
58
+ const distPath = path.resolve(__dirname, '..', 'pi-extensions');
59
+ // When running from source: src/utils/ -> src/pi-extensions/
60
+ const srcPath = path.resolve(__dirname, '..', '..', 'src', 'pi-extensions');
61
+ if (fs.existsSync(distPath))
62
+ return distPath;
63
+ if (fs.existsSync(srcPath))
64
+ return srcPath;
65
+ throw new Error('Could not find pi-extensions/ directory.');
66
+ }
67
+ function getSyncPermissionsScript() {
68
+ const scriptPath = path.resolve(__dirname, '..', '..', 'sync-permissions.sh');
69
+ if (fs.existsSync(scriptPath))
70
+ return scriptPath;
71
+ throw new Error('Could not find sync-permissions.sh');
72
+ }
73
+ function getExtensionTemplates(options = {}) {
74
+ let syncScript;
75
+ try {
76
+ syncScript = getSyncPermissionsScript();
77
+ }
78
+ catch {
79
+ (0, logger_1.logWarning)('Could not locate sync-permissions.sh — permission-sync extension will need manual path configuration');
80
+ syncScript = ''; // Empty placeholder — user must configure manually
81
+ }
82
+ const templates = [
83
+ {
84
+ templateFile: 'permission-sync.ts.template',
85
+ installedName: 'ws-permission-sync.ts',
86
+ replacements: syncScript ? { '__SYNC_PERMISSIONS_SCRIPT__': syncScript } : {},
87
+ },
88
+ {
89
+ templateFile: 'review-reminder.ts.template',
90
+ installedName: 'ws-review-reminder.ts',
91
+ },
92
+ {
93
+ templateFile: 'permissions-gate.ts.template',
94
+ installedName: 'ws-permissions-gate.ts',
95
+ },
96
+ {
97
+ templateFile: 'pr-fix-trigger.ts.template',
98
+ installedName: 'ws-pr-fix-trigger.ts',
99
+ },
100
+ ];
101
+ // Workspace input-status widget — enabled by default, opt-out via config
102
+ if (options.workspaceInputStatus !== false) {
103
+ templates.push({
104
+ templateFile: 'workspace-input-status.ts.template',
105
+ installedName: 'ws-workspace-input-status.ts',
106
+ });
107
+ }
108
+ return templates;
109
+ }
110
+ /**
111
+ * Install all workspace-manager Pi extensions.
112
+ * Idempotent — overwrites existing managed extensions with latest templates.
113
+ *
114
+ * @param extensionsDir Override the extensions directory (for testing).
115
+ */
116
+ function installPiExtensions(extensionsDir, options = {}) {
117
+ const targetDir = extensionsDir ?? PI_EXTENSIONS_DIR;
118
+ let templatesDir;
119
+ try {
120
+ templatesDir = getTemplatesDir();
121
+ }
122
+ catch {
123
+ (0, logger_1.logWarning)('Could not find Pi extension templates — skipping');
124
+ return;
125
+ }
126
+ if (!fs.existsSync(targetDir)) {
127
+ fs.mkdirSync(targetDir, { recursive: true });
128
+ }
129
+ const templates = getExtensionTemplates(options);
130
+ let installed = 0;
131
+ for (const tpl of templates) {
132
+ const templatePath = path.join(templatesDir, tpl.templateFile);
133
+ if (!fs.existsSync(templatePath)) {
134
+ (0, logger_1.logWarning)(`Template not found: ${tpl.templateFile} — skipping`);
135
+ continue;
136
+ }
137
+ let content = fs.readFileSync(templatePath, 'utf-8');
138
+ // Apply replacements
139
+ if (tpl.replacements) {
140
+ for (const [placeholder, value] of Object.entries(tpl.replacements)) {
141
+ content = content.replace(new RegExp(escapeRegex(placeholder), 'g'), value);
142
+ }
143
+ }
144
+ const targetPath = path.join(targetDir, tpl.installedName);
145
+ fs.writeFileSync(targetPath, content, 'utf-8');
146
+ installed++;
147
+ }
148
+ if (installed > 0) {
149
+ (0, logger_1.logSuccess)(`Installed ${installed} Pi extension(s) in ${targetDir}`);
150
+ }
151
+ }
152
+ /**
153
+ * Uninstall workspace-manager managed Pi extensions.
154
+ * Only removes extensions that contain the managed marker or match
155
+ * known installed names.
156
+ *
157
+ * @param extensionsDir Override the extensions directory (for testing).
158
+ */
159
+ /**
160
+ * Uninstall workspace-manager managed Pi extensions.
161
+ * Pass `onlyNames` to remove only specific extension files (for opt-out).
162
+ *
163
+ * @param extensionsDir Override the extensions directory (for testing).
164
+ * @param options.onlyNames If set, only remove files in this list.
165
+ */
166
+ function uninstallPiExtensions(extensionsDir, options) {
167
+ const targetDir = extensionsDir ?? PI_EXTENSIONS_DIR;
168
+ if (!fs.existsSync(targetDir))
169
+ return;
170
+ const templates = getExtensionTemplates({ workspaceInputStatus: true });
171
+ const knownNames = new Set(templates.map(t => t.installedName));
172
+ const onlyNames = options?.onlyNames ? new Set(options.onlyNames) : null;
173
+ let removed = 0;
174
+ for (const file of fs.readdirSync(targetDir)) {
175
+ if (!file.endsWith('.ts'))
176
+ continue;
177
+ // When onlyNames is set, skip files not in that list
178
+ if (onlyNames && !onlyNames.has(file))
179
+ continue;
180
+ const filePath = path.join(targetDir, file);
181
+ // Remove if it matches a known managed name
182
+ if (knownNames.has(file)) {
183
+ fs.unlinkSync(filePath);
184
+ removed++;
185
+ continue;
186
+ }
187
+ // Also remove if it contains the managed marker (for renamed files)
188
+ if (!onlyNames) {
189
+ try {
190
+ const content = fs.readFileSync(filePath, 'utf-8');
191
+ if (content.includes(MANAGED_MARKER)) {
192
+ fs.unlinkSync(filePath);
193
+ removed++;
194
+ }
195
+ }
196
+ catch { /* ignore read errors */ }
197
+ }
198
+ }
199
+ if (removed > 0) {
200
+ (0, logger_1.logSuccess)(`Removed ${removed} Pi extension(s) from ${targetDir}`);
201
+ }
202
+ }
203
+ /**
204
+ * Check which managed Pi extensions are installed.
205
+ */
206
+ function getPiExtensionStatus(extensionsDir) {
207
+ const targetDir = extensionsDir ?? PI_EXTENSIONS_DIR;
208
+ const templates = getExtensionTemplates({ workspaceInputStatus: true });
209
+ const installed = [];
210
+ const missing = [];
211
+ for (const tpl of templates) {
212
+ const targetPath = path.join(targetDir, tpl.installedName);
213
+ if (fs.existsSync(targetPath)) {
214
+ installed.push(tpl.installedName);
215
+ }
216
+ else {
217
+ missing.push(tpl.installedName);
218
+ }
219
+ }
220
+ return { installed, missing };
221
+ }
222
+ function escapeRegex(str) {
223
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
224
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createSimpleProgressBar = exports.ProgressTracker = void 0;
37
+ const cliProgress = __importStar(require("cli-progress"));
38
+ class ProgressTracker {
39
+ constructor() {
40
+ this.multibar = new cliProgress.MultiBar({
41
+ clearOnComplete: false,
42
+ hideCursor: true,
43
+ format: ' {bar} | {percentage}% | {value}/{total} | {task} | {duration}',
44
+ }, cliProgress.Presets.shades_classic);
45
+ this.bars = new Map();
46
+ this.startTimes = new Map();
47
+ }
48
+ addTask(taskId, total, taskName) {
49
+ const bar = this.multibar.create(total, 0, { task: taskName, duration: '0s' });
50
+ this.bars.set(taskId, bar);
51
+ this.startTimes.set(taskId, Date.now());
52
+ }
53
+ updateTask(taskId, current, taskName) {
54
+ const bar = this.bars.get(taskId);
55
+ const startTime = this.startTimes.get(taskId);
56
+ if (bar && startTime) {
57
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
58
+ const duration = this.formatDuration(elapsed);
59
+ bar.update(current, {
60
+ task: taskName || 'Processing',
61
+ duration,
62
+ });
63
+ }
64
+ }
65
+ completeTask(taskId) {
66
+ const bar = this.bars.get(taskId);
67
+ if (bar) {
68
+ bar.stop();
69
+ this.bars.delete(taskId);
70
+ this.startTimes.delete(taskId);
71
+ }
72
+ }
73
+ stop() {
74
+ this.multibar.stop();
75
+ }
76
+ formatDuration(seconds) {
77
+ if (seconds < 60) {
78
+ return `${seconds}s`;
79
+ }
80
+ const minutes = Math.floor(seconds / 60);
81
+ const remainingSeconds = seconds % 60;
82
+ return `${minutes}m ${remainingSeconds}s`;
83
+ }
84
+ }
85
+ exports.ProgressTracker = ProgressTracker;
86
+ const createSimpleProgressBar = (total, taskName) => {
87
+ return new cliProgress.SingleBar({
88
+ format: ` ${taskName} | {bar} | {percentage}% | {value}/{total} | {duration}`,
89
+ hideCursor: true,
90
+ }, cliProgress.Presets.shades_classic);
91
+ };
92
+ exports.createSimpleProgressBar = createSimpleProgressBar;
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.promptMultiWorkspaceSelection = exports.promptWorkspaceSelection = exports.confirmWorkspaceCreation = exports.promptWorkspaceName = exports.promptRepositorySelection = exports.promptInstanceSuffix = void 0;
40
+ const inquirer_1 = __importDefault(require("inquirer"));
41
+ const inquirer_autocomplete_prompt_1 = __importDefault(require("inquirer-autocomplete-prompt"));
42
+ const fuzzy = __importStar(require("fuzzy"));
43
+ const validation_1 = require("./validation");
44
+ const colors_1 = require("./colors");
45
+ // Register autocomplete prompt type
46
+ inquirer_1.default.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
47
+ const promptInstanceSuffix = async (repoName, existingDirectoryNames) => {
48
+ const { suffix } = await inquirer_1.default.prompt([
49
+ {
50
+ type: 'input',
51
+ name: 'suffix',
52
+ message: `"${repoName}" already exists. Enter a suffix for this instance:`,
53
+ validate: (input) => {
54
+ if (!input || input.trim().length === 0) {
55
+ return 'Suffix cannot be empty';
56
+ }
57
+ const trimmed = input.trim();
58
+ // Validate characters (alphanumeric, hyphens, underscores)
59
+ if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
60
+ return 'Suffix can only contain letters, numbers, hyphens, and underscores';
61
+ }
62
+ const candidateName = `${repoName}-${trimmed}`;
63
+ if (existingDirectoryNames.includes(candidateName)) {
64
+ return `"${candidateName}" already exists. Choose a different suffix`;
65
+ }
66
+ return true;
67
+ },
68
+ },
69
+ ]);
70
+ return suffix.trim();
71
+ };
72
+ exports.promptInstanceSuffix = promptInstanceSuffix;
73
+ const promptRepositorySelection = async (repos, existingDirectoryNames = []) => {
74
+ const selectedEntries = [];
75
+ const repoMap = new Map(repos.map(repo => [repo.name, repo]));
76
+ // Track all directory names (existing + newly selected) for collision detection
77
+ const allDirectoryNames = [...existingDirectoryNames];
78
+ console.log('\n' + (0, colors_1.colorize)('Repository Selection', 'bright'));
79
+ console.log('Type to search repositories. Use arrow keys to navigate, Enter to select.');
80
+ console.log('Type "done" when finished selecting repositories.\n');
81
+ while (true) {
82
+ try {
83
+ const { repoName } = await inquirer_1.default.prompt([
84
+ {
85
+ type: 'autocomplete',
86
+ name: 'repoName',
87
+ message: `Search and select repository (${(0, colors_1.colorize)(String(selectedEntries.length), 'cyan')} selected):`,
88
+ source: async (_answersSoFar, input) => {
89
+ const searchInput = input || '';
90
+ // Always include done option
91
+ const doneOption = { name: (0, colors_1.colorize)('done - Finish selection', 'green'), value: 'done' };
92
+ // If no input or "done" typed, show done + top repos
93
+ if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
94
+ return [
95
+ doneOption,
96
+ ...repos.slice(0, 15).map(repo => ({
97
+ name: `${repo.name}${repo.description ? ` - ${(0, colors_1.colorize)(repo.description, 'gray')}` : ''}`,
98
+ value: repo.name,
99
+ }))
100
+ ];
101
+ }
102
+ // Perform fuzzy search
103
+ const results = fuzzy.filter(searchInput, repos, {
104
+ extract: (repo) => `${repo.name} ${repo.description || ''}`,
105
+ });
106
+ const suggestions = results.slice(0, 15).map(result => ({
107
+ name: `${result.original.name}${result.original.description ? ` - ${(0, colors_1.colorize)(result.original.description, 'gray')}` : ''}`,
108
+ value: result.original.name,
109
+ }));
110
+ return [doneOption, ...suggestions];
111
+ },
112
+ pageSize: 16,
113
+ },
114
+ ]);
115
+ if (repoName === 'done') {
116
+ if (selectedEntries.length === 0) {
117
+ console.log((0, colors_1.colorize)('\nYou must select at least one repository\n', 'yellow'));
118
+ continue;
119
+ }
120
+ break;
121
+ }
122
+ const repo = repoMap.get(repoName);
123
+ if (!repo)
124
+ continue;
125
+ let directoryName = repo.name;
126
+ // Check if this directory name already exists (in workspace or in current selection)
127
+ if (allDirectoryNames.includes(directoryName)) {
128
+ const suffix = await (0, exports.promptInstanceSuffix)(repo.name, allDirectoryNames);
129
+ directoryName = `${repo.name}-${suffix}`;
130
+ }
131
+ selectedEntries.push({ repo, directoryName });
132
+ allDirectoryNames.push(directoryName);
133
+ const displayName = directoryName !== repo.name
134
+ ? `${directoryName} (${repo.name})`
135
+ : repo.name;
136
+ console.log((0, colors_1.colorize)(`Added: ${displayName}`, 'green'));
137
+ }
138
+ catch (error) {
139
+ // Handle Ctrl+C
140
+ console.log('\n');
141
+ process.exit(0);
142
+ }
143
+ }
144
+ console.log(`\n${(0, colors_1.colorize)(`Selected ${selectedEntries.length} repositories:`, 'cyan')}`);
145
+ selectedEntries.forEach(entry => {
146
+ const displayName = entry.directoryName !== entry.repo.name
147
+ ? `${entry.directoryName} (${entry.repo.name})`
148
+ : entry.repo.name;
149
+ console.log(` - ${displayName}`);
150
+ });
151
+ console.log('');
152
+ return selectedEntries;
153
+ };
154
+ exports.promptRepositorySelection = promptRepositorySelection;
155
+ const promptWorkspaceName = async () => {
156
+ const { workspaceName } = await inquirer_1.default.prompt([
157
+ {
158
+ type: 'input',
159
+ name: 'workspaceName',
160
+ message: 'Enter workspace name:',
161
+ validate: (input) => {
162
+ const validationResult = (0, validation_1.validateWorkspaceName)(input);
163
+ if (validationResult !== true) {
164
+ return validationResult;
165
+ }
166
+ return true;
167
+ },
168
+ filter: (input) => (0, validation_1.sanitizeWorkspaceName)(input),
169
+ },
170
+ ]);
171
+ return workspaceName;
172
+ };
173
+ exports.promptWorkspaceName = promptWorkspaceName;
174
+ const confirmWorkspaceCreation = async (workspaceName, repoCount, workspacePath) => {
175
+ console.log('\n' + '='.repeat(60));
176
+ console.log('Workspace Configuration');
177
+ console.log('='.repeat(60));
178
+ console.log(`Name: ${(0, colors_1.colorize)(workspaceName, 'cyan')}`);
179
+ console.log(`Location: ${(0, colors_1.colorize)(workspacePath, 'gray')}`);
180
+ console.log(`Repositories: ${(0, colors_1.colorize)(String(repoCount), 'yellow')}`);
181
+ console.log('='.repeat(60) + '\n');
182
+ const { confirmed } = await inquirer_1.default.prompt([
183
+ {
184
+ type: 'confirm',
185
+ name: 'confirmed',
186
+ message: 'Create workspace with these settings?',
187
+ default: true,
188
+ },
189
+ ]);
190
+ return confirmed;
191
+ };
192
+ exports.confirmWorkspaceCreation = confirmWorkspaceCreation;
193
+ const promptWorkspaceSelection = async (workspaces) => {
194
+ if (workspaces.length === 0) {
195
+ throw new Error('No workspaces found. Create one first with: workspace create');
196
+ }
197
+ const choices = workspaces.map(ws => ({
198
+ name: ws.metadata
199
+ ? `${ws.name} (${ws.metadata.repositories.length} repos)`
200
+ : ws.name,
201
+ value: ws.name,
202
+ short: ws.name,
203
+ }));
204
+ const { workspaceName } = await inquirer_1.default.prompt([
205
+ {
206
+ type: 'list',
207
+ name: 'workspaceName',
208
+ message: 'Select workspace to update:',
209
+ choices,
210
+ pageSize: 15,
211
+ },
212
+ ]);
213
+ return workspaceName;
214
+ };
215
+ exports.promptWorkspaceSelection = promptWorkspaceSelection;
216
+ const promptMultiWorkspaceSelection = async (workspaces) => {
217
+ const selectedNames = [];
218
+ const workspaceNames = workspaces.map(ws => ws.name);
219
+ console.log('\n' + (0, colors_1.colorize)('Workspace Selection', 'bright'));
220
+ console.log('Type to search workspaces. Use arrow keys to navigate, Enter to select.');
221
+ console.log('Type "done" when finished selecting workspaces.\n');
222
+ while (true) {
223
+ try {
224
+ const availableNames = workspaceNames.filter(name => !selectedNames.includes(name));
225
+ if (availableNames.length === 0) {
226
+ console.log((0, colors_1.colorize)('All workspaces selected.', 'yellow'));
227
+ break;
228
+ }
229
+ const { workspaceName } = await inquirer_1.default.prompt([
230
+ {
231
+ type: 'autocomplete',
232
+ name: 'workspaceName',
233
+ message: `Search and select workspace (${(0, colors_1.colorize)(String(selectedNames.length), 'cyan')} selected):`,
234
+ source: async (_answersSoFar, input) => {
235
+ const searchInput = input || '';
236
+ const doneOption = { name: (0, colors_1.colorize)('done - Finish selection', 'green'), value: 'done' };
237
+ if (!searchInput || searchInput.toLowerCase().startsWith('done')) {
238
+ return [
239
+ doneOption,
240
+ ...availableNames.slice(0, 15).map(name => ({
241
+ name,
242
+ value: name,
243
+ }))
244
+ ];
245
+ }
246
+ const results = fuzzy.filter(searchInput, availableNames);
247
+ const suggestions = results.slice(0, 15).map(result => ({
248
+ name: result.original,
249
+ value: result.original,
250
+ }));
251
+ return [doneOption, ...suggestions];
252
+ },
253
+ pageSize: 16,
254
+ },
255
+ ]);
256
+ if (workspaceName === 'done') {
257
+ if (selectedNames.length === 0) {
258
+ console.log((0, colors_1.colorize)('\nYou must select at least one workspace\n', 'yellow'));
259
+ continue;
260
+ }
261
+ break;
262
+ }
263
+ selectedNames.push(workspaceName);
264
+ console.log((0, colors_1.colorize)(`Added: ${workspaceName}`, 'green'));
265
+ }
266
+ catch (error) {
267
+ // Handle Ctrl+C
268
+ console.log('\n');
269
+ process.exit(0);
270
+ }
271
+ }
272
+ console.log(`\n${(0, colors_1.colorize)(`Selected ${selectedNames.length} workspace${selectedNames.length === 1 ? '' : 's'}:`, 'cyan')}`);
273
+ selectedNames.forEach(name => {
274
+ console.log(` - ${name}`);
275
+ });
276
+ console.log('');
277
+ return selectedNames;
278
+ };
279
+ exports.promptMultiWorkspaceSelection = promptMultiWorkspaceSelection;