@pikaa-ai/pikaa 0.3.23 → 0.3.25

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 (191) hide show
  1. package/assets/brand/orbit-logo-option4-whale.jpg +0 -0
  2. package/assets/brand/orbit-logo.jpg +0 -0
  3. package/assets/brand/orbit-logo.png +0 -0
  4. package/assets/brand/orbit-logo.svg +3 -0
  5. package/dist/cli.js +407 -219
  6. package/dist/index.js +7 -2
  7. package/package.json +1 -2
  8. package/skills/adaptyv/SKILL.md +0 -240
  9. package/skills/aeon/SKILL.md +0 -402
  10. package/skills/analytical-method-validation/SKILL.md +0 -299
  11. package/skills/anndata/SKILL.md +0 -431
  12. package/skills/arbor/SKILL.md +0 -152
  13. package/skills/arboreto/SKILL.md +0 -267
  14. package/skills/astropy/SKILL.md +0 -353
  15. package/skills/autoskill/SKILL.md +0 -233
  16. package/skills/benchling-integration/SKILL.md +0 -229
  17. package/skills/bgpt-paper-search/SKILL.md +0 -75
  18. package/skills/bids/SKILL.md +0 -237
  19. package/skills/biopython/SKILL.md +0 -472
  20. package/skills/bioservices/SKILL.md +0 -399
  21. package/skills/bulk-rnaseq/SKILL.md +0 -198
  22. package/skills/cellxgene-census/SKILL.md +0 -283
  23. package/skills/cirq/SKILL.md +0 -370
  24. package/skills/citation-management/SKILL.md +0 -329
  25. package/skills/clinical-decision-support/SKILL.md +0 -238
  26. package/skills/clinical-decision-support/references/README.md +0 -62
  27. package/skills/clinical-reports/SKILL.md +0 -248
  28. package/skills/clinical-reports/references/README.md +0 -34
  29. package/skills/cobrapy/SKILL.md +0 -496
  30. package/skills/consciousness-council/SKILL.md +0 -151
  31. package/skills/dask/SKILL.md +0 -482
  32. package/skills/database-lookup/SKILL.md +0 -386
  33. package/skills/datamol/SKILL.md +0 -200
  34. package/skills/deepchem/SKILL.md +0 -244
  35. package/skills/deepspot-m/SKILL.md +0 -175
  36. package/skills/deeptools/SKILL.md +0 -412
  37. package/skills/depmap/SKILL.md +0 -301
  38. package/skills/dhdna-profiler/SKILL.md +0 -184
  39. package/skills/diffdock/SKILL.md +0 -488
  40. package/skills/dnanexus-integration/SKILL.md +0 -325
  41. package/skills/docx/SKILL.md +0 -99
  42. package/skills/esm/SKILL.md +0 -334
  43. package/skills/etetoolkit/SKILL.md +0 -327
  44. package/skills/exa-search/SKILL.md +0 -102
  45. package/skills/executing-plans/SKILL.md +0 -14
  46. package/skills/experimental-design/SKILL.md +0 -234
  47. package/skills/exploratory-data-analysis/SKILL.md +0 -280
  48. package/skills/flowio/SKILL.md +0 -310
  49. package/skills/fluidsim/SKILL.md +0 -279
  50. package/skills/frontend-design/SKILL.md +0 -100
  51. package/skills/generate-image/SKILL.md +0 -304
  52. package/skills/geniml/SKILL.md +0 -310
  53. package/skills/genomic-coordinates/SKILL.md +0 -189
  54. package/skills/genomic-intelligence/SKILL.md +0 -243
  55. package/skills/geomaster/README.md +0 -105
  56. package/skills/geomaster/SKILL.md +0 -366
  57. package/skills/geopandas/SKILL.md +0 -250
  58. package/skills/get-available-resources/SKILL.md +0 -260
  59. package/skills/gget/SKILL.md +0 -153
  60. package/skills/ginkgo-cloud-lab/SKILL.md +0 -106
  61. package/skills/glycoengineering/SKILL.md +0 -339
  62. package/skills/gtars/SKILL.md +0 -282
  63. package/skills/guardian-rails/SKILL.md +0 -54
  64. package/skills/histolab/SKILL.md +0 -243
  65. package/skills/hugging-science/SKILL.md +0 -132
  66. package/skills/hypogenic/SKILL.md +0 -290
  67. package/skills/hypothesis-generation/SKILL.md +0 -264
  68. package/skills/imaging-data-commons/SKILL.md +0 -496
  69. package/skills/infographics/SKILL.md +0 -315
  70. package/skills/iso-standards-readiness/SKILL.md +0 -352
  71. package/skills/lab-hardware-cad/SKILL.md +0 -372
  72. package/skills/labarchive-integration/SKILL.md +0 -216
  73. package/skills/lamindb/SKILL.md +0 -408
  74. package/skills/latchbio-integration/SKILL.md +0 -227
  75. package/skills/latex-posters/SKILL.md +0 -369
  76. package/skills/latex-posters/references/README.md +0 -439
  77. package/skills/liteparse/SKILL.md +0 -295
  78. package/skills/literature-review/SKILL.md +0 -263
  79. package/skills/markdown-mermaid-writing/SKILL.md +0 -322
  80. package/skills/market-research-reports/SKILL.md +0 -337
  81. package/skills/markitdown/SKILL.md +0 -264
  82. package/skills/matchms/SKILL.md +0 -276
  83. package/skills/matlab/SKILL.md +0 -274
  84. package/skills/matplotlib/SKILL.md +0 -378
  85. package/skills/medchem/SKILL.md +0 -321
  86. package/skills/modal/SKILL.md +0 -468
  87. package/skills/molecular-dynamics/SKILL.md +0 -458
  88. package/skills/molfeat/SKILL.md +0 -348
  89. package/skills/ncats-arax/SKILL.md +0 -178
  90. package/skills/networkx/SKILL.md +0 -440
  91. package/skills/neurokit2/SKILL.md +0 -323
  92. package/skills/neuropixels-analysis/SKILL.md +0 -412
  93. package/skills/nextflow/SKILL.md +0 -195
  94. package/skills/omero-integration/SKILL.md +0 -222
  95. package/skills/onekgpd/SKILL.md +0 -371
  96. package/skills/ontology-term-resolution/SKILL.md +0 -147
  97. package/skills/open-notebook/SKILL.md +0 -297
  98. package/skills/openpiv/SKILL.md +0 -469
  99. package/skills/opentrons-integration/SKILL.md +0 -322
  100. package/skills/optimize-for-gpu/SKILL.md +0 -176
  101. package/skills/owasp-top10/SKILL.md +0 -48
  102. package/skills/pacsomatic/LICENSE +0 -21
  103. package/skills/pacsomatic/SKILL.md +0 -150
  104. package/skills/paper-lookup/SKILL.md +0 -263
  105. package/skills/paperclip/SKILL.md +0 -413
  106. package/skills/paperzilla/SKILL.md +0 -159
  107. package/skills/parallel-web/SKILL.md +0 -128
  108. package/skills/pathml/SKILL.md +0 -222
  109. package/skills/pathogen-variant-surveillance/SKILL.md +0 -208
  110. package/skills/pathway-enrichment/SKILL.md +0 -194
  111. package/skills/pdf/SKILL.md +0 -322
  112. package/skills/peer-review/SKILL.md +0 -288
  113. package/skills/penetration-testing/SKILL.md +0 -31
  114. package/skills/pennylane/SKILL.md +0 -240
  115. package/skills/phylogenetics/SKILL.md +0 -409
  116. package/skills/pi-agent/SKILL.md +0 -83
  117. package/skills/pkpd-modeling/SKILL.md +0 -381
  118. package/skills/polars/SKILL.md +0 -393
  119. package/skills/polars-bio/SKILL.md +0 -379
  120. package/skills/ponytail/SKILL.md +0 -31
  121. package/skills/ponytail-audit/SKILL.md +0 -18
  122. package/skills/pptx/SKILL.md +0 -246
  123. package/skills/pptx-posters/SKILL.md +0 -258
  124. package/skills/primekg/SKILL.md +0 -99
  125. package/skills/protocolsio-integration/SKILL.md +0 -236
  126. package/skills/pufferlib/SKILL.md +0 -328
  127. package/skills/pydeseq2/SKILL.md +0 -369
  128. package/skills/pydicom/SKILL.md +0 -381
  129. package/skills/pyhealth/SKILL.md +0 -124
  130. package/skills/pylabrobot/SKILL.md +0 -216
  131. package/skills/pymatgen/SKILL.md +0 -404
  132. package/skills/pymc/SKILL.md +0 -310
  133. package/skills/pymoo/SKILL.md +0 -276
  134. package/skills/pyopenms/SKILL.md +0 -179
  135. package/skills/pysam/SKILL.md +0 -330
  136. package/skills/pytdc/SKILL.md +0 -297
  137. package/skills/pytorch-lightning/SKILL.md +0 -191
  138. package/skills/pyzotero/SKILL.md +0 -137
  139. package/skills/qiskit/SKILL.md +0 -259
  140. package/skills/qutip/SKILL.md +0 -317
  141. package/skills/rdkit/SKILL.md +0 -94
  142. package/skills/relsa-severity-assessment/SKILL.md +0 -354
  143. package/skills/research-grants/SKILL.md +0 -296
  144. package/skills/research-grants/references/README.md +0 -287
  145. package/skills/research-lookup/README.md +0 -106
  146. package/skills/research-lookup/SKILL.md +0 -338
  147. package/skills/rowan/SKILL.md +0 -398
  148. package/skills/scanpy/SKILL.md +0 -303
  149. package/skills/scholar-evaluation/SKILL.md +0 -296
  150. package/skills/scientific-brainstorming/SKILL.md +0 -282
  151. package/skills/scientific-critical-thinking/SKILL.md +0 -180
  152. package/skills/scientific-schematics/SKILL.md +0 -370
  153. package/skills/scientific-slides/SKILL.md +0 -379
  154. package/skills/scientific-visualization/SKILL.md +0 -285
  155. package/skills/scientific-writing/SKILL.md +0 -356
  156. package/skills/scikit-bio/SKILL.md +0 -470
  157. package/skills/scikit-learn/SKILL.md +0 -324
  158. package/skills/scikit-survival/SKILL.md +0 -313
  159. package/skills/scvelo/SKILL.md +0 -328
  160. package/skills/scvi-tools/SKILL.md +0 -201
  161. package/skills/seaborn/SKILL.md +0 -254
  162. package/skills/security-auditor/SKILL.md +0 -37
  163. package/skills/shap/SKILL.md +0 -282
  164. package/skills/simpy/SKILL.md +0 -283
  165. package/skills/stable-baselines3/SKILL.md +0 -325
  166. package/skills/statistical-analysis/SKILL.md +0 -446
  167. package/skills/statistical-power/SKILL.md +0 -200
  168. package/skills/statsmodels/SKILL.md +0 -238
  169. package/skills/sympy/SKILL.md +0 -354
  170. package/skills/systematic-debugging/SKILL.md +0 -35
  171. package/skills/tamarind/SKILL.md +0 -285
  172. package/skills/tdd/SKILL.md +0 -26
  173. package/skills/tiledbvcf/SKILL.md +0 -456
  174. package/skills/timesfm-forecasting/SKILL.md +0 -408
  175. package/skills/timesfm-forecasting/examples/global-temperature/README.md +0 -178
  176. package/skills/torch-geometric/SKILL.md +0 -458
  177. package/skills/torchdrug/SKILL.md +0 -241
  178. package/skills/transformers/SKILL.md +0 -195
  179. package/skills/treatment-plans/SKILL.md +0 -174
  180. package/skills/treatment-plans/references/README.md +0 -19
  181. package/skills/umap-learn/SKILL.md +0 -488
  182. package/skills/uncertainty-and-units/SKILL.md +0 -384
  183. package/skills/usfiscaldata/SKILL.md +0 -171
  184. package/skills/vaex/SKILL.md +0 -204
  185. package/skills/venue-templates/SKILL.md +0 -269
  186. package/skills/verification-before-completion/SKILL.md +0 -22
  187. package/skills/waypoint-bio/SKILL.md +0 -273
  188. package/skills/what-if-oracle/SKILL.md +0 -184
  189. package/skills/writing-plans/SKILL.md +0 -15
  190. package/skills/xlsx/SKILL.md +0 -110
  191. package/skills/zarr-python/SKILL.md +0 -241
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli/index.ts
5
- import { resolve as resolve21 } from "path";
6
- import { existsSync as existsSync23 } from "fs";
5
+ import { resolve as resolve22 } from "path";
6
+ import { existsSync as existsSync24 } from "fs";
7
7
  import { createInterface } from "readline";
8
8
 
9
9
  // src/auth/store.ts
@@ -1910,6 +1910,12 @@ class WindowsSandbox {
1910
1910
  return null;
1911
1911
  }
1912
1912
  try {
1913
+ if (this.jobObjectHandle) {
1914
+ try {
1915
+ this.kernel32.symbols.CloseHandle(this.jobObjectHandle);
1916
+ } catch {}
1917
+ this.jobObjectHandle = null;
1918
+ }
1913
1919
  const jobHandle = this.kernel32.symbols.CreateJobObjectW(null, null);
1914
1920
  if (!jobHandle || jobHandle === 0) {
1915
1921
  return null;
@@ -5269,7 +5275,6 @@ class SessionPersistenceManager {
5269
5275
  // src/skills/loader.ts
5270
5276
  import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
5271
5277
  import { resolve as resolve13, join as join8 } from "path";
5272
- import { homedir as homedir3 } from "os";
5273
5278
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
5274
5279
 
5275
5280
  class SkillsLoader {
@@ -5345,7 +5350,7 @@ class SkillsLoader {
5345
5350
  }
5346
5351
  }
5347
5352
  if (this.includeGlobal) {
5348
- roots.push(getGlobalSkillsDir(), resolve13(homedir3(), ".gemini", "config", "skills"));
5353
+ roots.push(getGlobalSkillsDir());
5349
5354
  }
5350
5355
  roots.push(...this.customRoots.map((r) => resolve13(r)));
5351
5356
  return roots.filter((r) => existsSync16(r));
@@ -5491,9 +5496,77 @@ When tackling complex specialized tasks that match any of these skills, autonomo
5491
5496
  }
5492
5497
  }
5493
5498
 
5499
+ // src/skills/installer.ts
5500
+ import { existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
5501
+ import { resolve as resolve14, join as join9 } from "path";
5502
+ async function installSkill(skillName, options) {
5503
+ const cleanName = skillName.trim().toLowerCase().replace(/^@/, "");
5504
+ if (!cleanName) {
5505
+ throw new Error("Skill name cannot be empty.");
5506
+ }
5507
+ const credStore = new CredentialsStore;
5508
+ const creds = credStore.load();
5509
+ let backend = (options.backendUrl || process.env.PIKAA_BACKEND_URL || process.env.GROUPY_BACKEND_URL || creds?.baseUrl || "https://api.groupy-hub.store").replace(/\/+$/, "");
5510
+ if (backend.endsWith("/v1")) {
5511
+ backend = backend.slice(0, -3).replace(/\/+$/, "");
5512
+ }
5513
+ const endpoint = `${backend}/api/skills/${encodeURIComponent(cleanName)}`;
5514
+ let resp;
5515
+ try {
5516
+ resp = await fetch(endpoint, {
5517
+ headers: creds?.accessToken ? { Authorization: `Bearer ${creds.accessToken}` } : {}
5518
+ });
5519
+ } catch (netErr) {
5520
+ throw new Error(`Failed to connect to backend at ${backend}: ${netErr.message}`);
5521
+ }
5522
+ if (!resp.ok) {
5523
+ if (resp.status === 404) {
5524
+ throw new Error(`Skill '${cleanName}' not found in catalog (${endpoint}).`);
5525
+ }
5526
+ const errText = await resp.text();
5527
+ throw new Error(`Backend error (${resp.status}): ${errText}`);
5528
+ }
5529
+ const skillData = await resp.json();
5530
+ if (!skillData.content) {
5531
+ throw new Error(`Skill '${cleanName}' does not contain markdown content.`);
5532
+ }
5533
+ const scope = options.global ? "global" : "workspace";
5534
+ const targetDir = options.global ? resolve14(getGlobalSkillsDir(), cleanName) : resolve14(options.cwd, ".agents", "skills", cleanName);
5535
+ mkdirSync10(targetDir, { recursive: true });
5536
+ const targetFile = join9(targetDir, "SKILL.md");
5537
+ let fileContent = skillData.content;
5538
+ if (!fileContent.startsWith("---")) {
5539
+ const desc = skillData.description ? `description: "${skillData.description.replace(/"/g, "\\\"")}"
5540
+ ` : "";
5541
+ fileContent = `---
5542
+ name: ${cleanName}
5543
+ ${desc}---
5544
+
5545
+ ${fileContent}`;
5546
+ }
5547
+ writeFileSync5(targetFile, fileContent, "utf-8");
5548
+ return {
5549
+ success: true,
5550
+ skillName: cleanName,
5551
+ title: skillData.title || cleanName,
5552
+ targetPath: targetFile,
5553
+ scope,
5554
+ message: `Installed skill '${skillData.title || cleanName}' (@${cleanName}) into ${scope} at ${targetFile}`
5555
+ };
5556
+ }
5557
+ function removeSkill(skillName, options) {
5558
+ const cleanName = skillName.trim().toLowerCase().replace(/^@/, "");
5559
+ const targetDir = options.global ? resolve14(getGlobalSkillsDir(), cleanName) : resolve14(options.cwd, ".agents", "skills", cleanName);
5560
+ if (!existsSync17(targetDir)) {
5561
+ return { success: false, targetDir, removed: false };
5562
+ }
5563
+ rmSync2(targetDir, { recursive: true, force: true });
5564
+ return { success: true, targetDir, removed: true };
5565
+ }
5566
+
5494
5567
  // src/memories/store.ts
5495
- import { existsSync as existsSync17, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync10, readdirSync as readdirSync7 } from "fs";
5496
- import { resolve as resolve14, join as join9, basename, dirname as dirname8 } from "path";
5568
+ import { existsSync as existsSync18, readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync11, readdirSync as readdirSync7 } from "fs";
5569
+ import { resolve as resolve15, join as join10, basename, dirname as dirname8 } from "path";
5497
5570
  import { createHash } from "crypto";
5498
5571
  class MemoryStore {
5499
5572
  globalPath;
@@ -5503,14 +5576,14 @@ class MemoryStore {
5503
5576
  this.customWorkspacePath = options.workspacePath;
5504
5577
  }
5505
5578
  findProjectRoot(cwd) {
5506
- let current = resolve14(cwd);
5579
+ let current = resolve15(cwd);
5507
5580
  while (true) {
5508
- if (existsSync17(join9(current, ".git"))) {
5581
+ if (existsSync18(join10(current, ".git"))) {
5509
5582
  return current;
5510
5583
  }
5511
5584
  const parent = dirname8(current);
5512
5585
  if (parent === current) {
5513
- return resolve14(cwd);
5586
+ return resolve15(cwd);
5514
5587
  }
5515
5588
  current = parent;
5516
5589
  }
@@ -5518,30 +5591,30 @@ class MemoryStore {
5518
5591
  getProjectSlug(cwd) {
5519
5592
  const root = this.findProjectRoot(cwd);
5520
5593
  const folderName = basename(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
5521
- const hash = createHash("sha256").update(resolve14(root)).digest("hex").slice(0, 6);
5594
+ const hash = createHash("sha256").update(resolve15(root)).digest("hex").slice(0, 6);
5522
5595
  return `${folderName}-${hash}`;
5523
5596
  }
5524
5597
  getProjectMemoryDir(cwd) {
5525
5598
  if (this.customWorkspacePath) {
5526
- const dir2 = resolve14(this.customWorkspacePath);
5527
- if (!existsSync17(dir2)) {
5599
+ const dir2 = resolve15(this.customWorkspacePath);
5600
+ if (!existsSync18(dir2)) {
5528
5601
  try {
5529
- mkdirSync10(dir2, { recursive: true });
5602
+ mkdirSync11(dir2, { recursive: true });
5530
5603
  } catch {}
5531
5604
  }
5532
5605
  return dir2;
5533
5606
  }
5534
5607
  const slug = this.getProjectSlug(cwd);
5535
- const dir = join9(getProjectsDir(), slug, "memory");
5536
- if (!existsSync17(dir)) {
5608
+ const dir = join10(getProjectsDir(), slug, "memory");
5609
+ if (!existsSync18(dir)) {
5537
5610
  try {
5538
- mkdirSync10(dir, { recursive: true });
5611
+ mkdirSync11(dir, { recursive: true });
5539
5612
  } catch {}
5540
5613
  }
5541
5614
  return dir;
5542
5615
  }
5543
5616
  getMemoryIndexPath(cwd) {
5544
- return join9(this.getProjectMemoryDir(cwd), "MEMORY.md");
5617
+ return join10(this.getProjectMemoryDir(cwd), "MEMORY.md");
5545
5618
  }
5546
5619
  normalizeCategory(raw) {
5547
5620
  const cat = raw.toLowerCase().trim();
@@ -5560,7 +5633,7 @@ class MemoryStore {
5560
5633
  const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
5561
5634
  const memoryDir = this.getProjectMemoryDir(params.cwd);
5562
5635
  const fileName = `${type}_${sanitizedName}.md`;
5563
- const filePath = join9(memoryDir, fileName);
5636
+ const filePath = join10(memoryDir, fileName);
5564
5637
  const nowIso = new Date().toISOString();
5565
5638
  const cleanContent = params.content.trim();
5566
5639
  const desc = (params.description || cleanContent.split(`
@@ -5579,7 +5652,7 @@ class MemoryStore {
5579
5652
  ""
5580
5653
  ].join(`
5581
5654
  `);
5582
- writeFileSync5(filePath, frontmatter, "utf8");
5655
+ writeFileSync6(filePath, frontmatter, "utf8");
5583
5656
  this.syncMemoryIndex(params.cwd);
5584
5657
  return {
5585
5658
  id: `mem_${sanitizedName}`,
@@ -5595,17 +5668,17 @@ class MemoryStore {
5595
5668
  }
5596
5669
  readTopicMemory(topicNameOrFile, cwd) {
5597
5670
  const memoryDir = this.getProjectMemoryDir(cwd);
5598
- let targetPath = join9(memoryDir, topicNameOrFile);
5599
- if (!existsSync17(targetPath)) {
5671
+ let targetPath = join10(memoryDir, topicNameOrFile);
5672
+ if (!existsSync18(targetPath)) {
5600
5673
  if (!topicNameOrFile.endsWith(".md")) {
5601
- targetPath = join9(memoryDir, `${topicNameOrFile}.md`);
5674
+ targetPath = join10(memoryDir, `${topicNameOrFile}.md`);
5602
5675
  }
5603
5676
  }
5604
- if (!existsSync17(targetPath)) {
5677
+ if (!existsSync18(targetPath)) {
5605
5678
  const files = readdirSync7(memoryDir);
5606
5679
  const match = files.find((f) => f.includes(topicNameOrFile));
5607
5680
  if (match) {
5608
- targetPath = join9(memoryDir, match);
5681
+ targetPath = join10(memoryDir, match);
5609
5682
  } else {
5610
5683
  return null;
5611
5684
  }
@@ -5666,12 +5739,12 @@ class MemoryStore {
5666
5739
  }
5667
5740
  syncMemoryIndex(cwd) {
5668
5741
  const memoryDir = this.getProjectMemoryDir(cwd);
5669
- const indexPath = join9(memoryDir, "MEMORY.md");
5670
- const files = existsSync17(memoryDir) ? readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5742
+ const indexPath = join10(memoryDir, "MEMORY.md");
5743
+ const files = existsSync18(memoryDir) ? readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5671
5744
  const items = [];
5672
5745
  for (const f of files) {
5673
5746
  try {
5674
- const full = join9(memoryDir, f);
5747
+ const full = join10(memoryDir, f);
5675
5748
  const parsed = this.parseTopicFile(readFileSync10(full, "utf8"), full);
5676
5749
  items.push({
5677
5750
  type: parsed.type,
@@ -5692,13 +5765,13 @@ class MemoryStore {
5692
5765
  indexLines.push(`- [${item.type}] **${item.name}**: ${item.desc} (topic: ${item.file})`);
5693
5766
  }
5694
5767
  const boundedLines = indexLines.slice(0, 200);
5695
- writeFileSync5(indexPath, boundedLines.join(`
5768
+ writeFileSync6(indexPath, boundedLines.join(`
5696
5769
  `) + `
5697
5770
  `, "utf8");
5698
5771
  }
5699
5772
  loadMemoryIndex(cwd) {
5700
5773
  const indexPath = this.getMemoryIndexPath(cwd);
5701
- if (!existsSync17(indexPath))
5774
+ if (!existsSync18(indexPath))
5702
5775
  return "";
5703
5776
  try {
5704
5777
  const raw = readFileSync10(indexPath, "utf8");
@@ -5714,13 +5787,13 @@ class MemoryStore {
5714
5787
  }
5715
5788
  listProjectMemories(cwd) {
5716
5789
  const memoryDir = this.getProjectMemoryDir(cwd);
5717
- if (!existsSync17(memoryDir))
5790
+ if (!existsSync18(memoryDir))
5718
5791
  return [];
5719
5792
  const files = readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
5720
5793
  const list = [];
5721
5794
  for (const f of files) {
5722
5795
  try {
5723
- const full = join9(memoryDir, f);
5796
+ const full = join10(memoryDir, f);
5724
5797
  list.push(this.parseTopicFile(readFileSync10(full, "utf8"), full));
5725
5798
  } catch {}
5726
5799
  }
@@ -5772,11 +5845,11 @@ class MemoryStore {
5772
5845
  }
5773
5846
 
5774
5847
  // src/worktree/manager.ts
5775
- import { resolve as resolve16, join as join10 } from "path";
5776
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
5848
+ import { resolve as resolve17, join as join11 } from "path";
5849
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync7, readFileSync as readFileSync11 } from "fs";
5777
5850
 
5778
5851
  // src/worktree/git.ts
5779
- import { resolve as resolve15 } from "path";
5852
+ import { resolve as resolve16 } from "path";
5780
5853
  var NON_INHERITABLE_GIT_ENV = [
5781
5854
  "GIT_DIR",
5782
5855
  "GIT_WORK_TREE",
@@ -5820,13 +5893,13 @@ async function getRepoRoot(cwd) {
5820
5893
  const res = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
5821
5894
  if (res.exitCode !== 0 || !res.stdout)
5822
5895
  return null;
5823
- return resolve15(res.stdout);
5896
+ return resolve16(res.stdout);
5824
5897
  }
5825
5898
  async function getMainRepoRoot(cwd) {
5826
5899
  const res = await runGit(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
5827
5900
  if (res.exitCode === 0 && res.stdout) {
5828
- const commonDir = resolve15(cwd, res.stdout);
5829
- return resolve15(commonDir, "..");
5901
+ const commonDir = resolve16(cwd, res.stdout);
5902
+ return resolve16(commonDir, "..");
5830
5903
  }
5831
5904
  return getRepoRoot(cwd);
5832
5905
  }
@@ -5856,7 +5929,7 @@ async function listWorktreesGit(repoRoot) {
5856
5929
  let isLocked = false;
5857
5930
  for (const line of lines) {
5858
5931
  if (line.startsWith("worktree ")) {
5859
- path = resolve15(line.slice(9).trim());
5932
+ path = resolve16(line.slice(9).trim());
5860
5933
  } else if (line.startsWith("HEAD ")) {
5861
5934
  commitHash = line.slice(5).trim();
5862
5935
  } else if (line.startsWith("branch ")) {
@@ -5921,19 +5994,19 @@ class WorktreeManager {
5921
5994
  }
5922
5995
  const taskId = `task_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
5923
5996
  const branchName = options.branch || `groupy/${taskId}`;
5924
- const targetDir = options.worktreePath || (this.baseStorageDir ? resolve16(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve16(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5925
- const worktreeParent = resolve16(targetDir, "..");
5926
- if (!existsSync18(worktreeParent)) {
5927
- mkdirSync11(worktreeParent, { recursive: true });
5997
+ const targetDir = options.worktreePath || (this.baseStorageDir ? resolve17(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve17(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5998
+ const worktreeParent = resolve17(targetDir, "..");
5999
+ if (!existsSync19(worktreeParent)) {
6000
+ mkdirSync12(worktreeParent, { recursive: true });
5928
6001
  }
5929
6002
  const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
5930
6003
  const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
5931
6004
  if (!result.success) {
5932
6005
  throw new Error(`Failed to create git worktree: ${result.error}`);
5933
6006
  }
5934
- const metaPath = join10(targetDir, "groupy-thread.json");
6007
+ const metaPath = join11(targetDir, "groupy-thread.json");
5935
6008
  try {
5936
- writeFileSync6(metaPath, JSON.stringify({
6009
+ writeFileSync7(metaPath, JSON.stringify({
5937
6010
  version: 1,
5938
6011
  ownerThreadId: options.threadId || null,
5939
6012
  branch: branchName,
@@ -5942,7 +6015,7 @@ class WorktreeManager {
5942
6015
  }, null, 2), "utf8");
5943
6016
  } catch {}
5944
6017
  const worktrees = await listWorktreesGit(repoRoot);
5945
- const created = worktrees.find((w) => resolve16(w.path) === resolve16(targetDir));
6018
+ const created = worktrees.find((w) => resolve17(w.path) === resolve17(targetDir));
5946
6019
  return created || {
5947
6020
  path: targetDir,
5948
6021
  branch: branchName,
@@ -5957,8 +6030,8 @@ class WorktreeManager {
5957
6030
  return [];
5958
6031
  const worktrees = await listWorktreesGit(repoRoot);
5959
6032
  return worktrees.map((wt) => {
5960
- const metaPath = join10(wt.path, "groupy-thread.json");
5961
- if (existsSync18(metaPath)) {
6033
+ const metaPath = join11(wt.path, "groupy-thread.json");
6034
+ if (existsSync19(metaPath)) {
5962
6035
  try {
5963
6036
  const raw = JSON.parse(readFileSync11(metaPath, "utf8"));
5964
6037
  return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
@@ -5968,7 +6041,7 @@ class WorktreeManager {
5968
6041
  });
5969
6042
  }
5970
6043
  async mergeWorktree(worktreePath, options = {}) {
5971
- const resolvedPath = resolve16(worktreePath);
6044
+ const resolvedPath = resolve17(worktreePath);
5972
6045
  const repoRoot = await getMainRepoRoot(resolvedPath);
5973
6046
  if (!repoRoot) {
5974
6047
  return { success: false, message: "Worktree path is not a valid git repository." };
@@ -6005,7 +6078,7 @@ class WorktreeManager {
6005
6078
  };
6006
6079
  }
6007
6080
  async removeWorktree(worktreePath, deleteBranch = false) {
6008
- const resolvedPath = resolve16(worktreePath);
6081
+ const resolvedPath = resolve17(worktreePath);
6009
6082
  const repoRoot = await getMainRepoRoot(resolvedPath);
6010
6083
  if (!repoRoot)
6011
6084
  return false;
@@ -6086,8 +6159,8 @@ class AuthClient {
6086
6159
  const authUrl = `${backendUrl}/api/auth/authorize?response_type=code&client_id=groupy-cli&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${encodeURIComponent(codeChallenge)}&code_challenge_method=S256&state=${state}&direct=1`;
6087
6160
  let serverResolve;
6088
6161
  let serverReject;
6089
- const codePromise = new Promise((resolve17, reject) => {
6090
- serverResolve = resolve17;
6162
+ const codePromise = new Promise((resolve18, reject) => {
6163
+ serverResolve = resolve18;
6091
6164
  serverReject = reject;
6092
6165
  });
6093
6166
  if (options.openBrowser !== false) {
@@ -6656,7 +6729,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6656
6729
  // package.json
6657
6730
  var package_default = {
6658
6731
  name: "@pikaa-ai/pikaa",
6659
- version: "0.3.23",
6732
+ version: "0.3.25",
6660
6733
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6661
6734
  main: "./dist/index.js",
6662
6735
  module: "./dist/index.js",
@@ -6672,7 +6745,6 @@ var package_default = {
6672
6745
  "bin",
6673
6746
  "templates",
6674
6747
  "assets",
6675
- "skills/**/SKILL.md",
6676
6748
  "README.md",
6677
6749
  "LICENSE"
6678
6750
  ],
@@ -6734,7 +6806,7 @@ function getCliVersion(options = {}) {
6734
6806
  }
6735
6807
 
6736
6808
  // src/cli/ui/animation/banner-animation.ts
6737
- import { homedir as homedir4 } from "os";
6809
+ import { homedir as homedir3 } from "os";
6738
6810
  import { execSync } from "child_process";
6739
6811
  var ROSE = "\x1B[38;2;205;105;74m";
6740
6812
  var ROSE_DIM = "\x1B[38;2;120;60;45m";
@@ -6744,7 +6816,7 @@ var BOLD = "\x1B[1m";
6744
6816
  var ITALIC = "\x1B[3m";
6745
6817
  var RESET = "\x1B[0m";
6746
6818
  function shortenPath(cwd) {
6747
- const home = homedir4();
6819
+ const home = homedir3();
6748
6820
  if (cwd.startsWith(home)) {
6749
6821
  return `~${cwd.slice(home.length).replace(/\\/g, "/")}`;
6750
6822
  }
@@ -7224,8 +7296,25 @@ import readline2 from "readline";
7224
7296
  import readline from "readline";
7225
7297
  var keypressEventsInitialized = false;
7226
7298
  var listeners = new Set;
7299
+ var rawModeState = null;
7300
+ function ensureRawMode(active) {
7301
+ if (!process.stdin.isTTY)
7302
+ return;
7303
+ if (rawModeState === active)
7304
+ return;
7305
+ try {
7306
+ process.stdin.setRawMode(active);
7307
+ rawModeState = active;
7308
+ if (active) {
7309
+ process.stdin.resume();
7310
+ }
7311
+ } catch {}
7312
+ }
7227
7313
  function initGlobalKeypress() {
7228
7314
  if (!keypressEventsInitialized && process.stdin.isTTY) {
7315
+ try {
7316
+ process.stdin.on("error", () => {});
7317
+ } catch {}
7229
7318
  readline.emitKeypressEvents(process.stdin);
7230
7319
  keypressEventsInitialized = true;
7231
7320
  process.stdin.on("keypress", (str, key) => {
@@ -7302,15 +7391,15 @@ class InteractiveLineEditor {
7302
7391
  }
7303
7392
  async readLine() {
7304
7393
  if (!process.stdin.isTTY) {
7305
- return new Promise((resolve17) => {
7394
+ return new Promise((resolve18) => {
7306
7395
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7307
7396
  rl.question(this.promptSymbol, (answer) => {
7308
7397
  rl.close();
7309
- resolve17(answer);
7398
+ resolve18(answer);
7310
7399
  });
7311
7400
  });
7312
7401
  }
7313
- return new Promise((resolve17) => {
7402
+ return new Promise((resolve18) => {
7314
7403
  let buffer = "";
7315
7404
  let cursor = 0;
7316
7405
  let selectedIndex = 0;
@@ -7318,12 +7407,7 @@ class InteractiveLineEditor {
7318
7407
  let popupDismissed = false;
7319
7408
  let lastCursorRowFromTop = 0;
7320
7409
  let unsubscribeKeypress = null;
7321
- if (process.stdin.isTTY) {
7322
- try {
7323
- process.stdin.setRawMode(true);
7324
- } catch {}
7325
- }
7326
- process.stdin.resume();
7410
+ ensureRawMode(true);
7327
7411
  const getMatchingCommands = () => {
7328
7412
  if (!buffer.startsWith("/") || popupDismissed)
7329
7413
  return [];
@@ -7357,7 +7441,7 @@ class InteractiveLineEditor {
7357
7441
  };
7358
7442
  const getRule = () => {
7359
7443
  const cols = getTerminalCols();
7360
- const ruleLen = Math.max(10, cols - 4);
7444
+ const ruleLen = Math.max(10, cols - 6);
7361
7445
  return "\u2500".repeat(ruleLen);
7362
7446
  };
7363
7447
  const ensureVisible = (totalItems, visibleRows) => {
@@ -7383,11 +7467,12 @@ class InteractiveLineEditor {
7383
7467
  const redraw = () => {
7384
7468
  const termCols = getTerminalCols();
7385
7469
  const visiblePromptWidth = this.promptSymbol.replace(/\x1b\[[0-9;]*m/g, "").length;
7470
+ let frame = "";
7386
7471
  if (lastCursorRowFromTop > 0) {
7387
- process.stdout.write(`\x1B[${lastCursorRowFromTop}A`);
7472
+ frame += `\x1B[${lastCursorRowFromTop}A`;
7388
7473
  }
7389
- process.stdout.write("\r\x1B[J");
7390
- process.stdout.write(`${this.promptSymbol}${buffer}`);
7474
+ frame += "\r\x1B[J";
7475
+ frame += `${this.promptSymbol}${buffer}`;
7391
7476
  const totalPromptChars = visiblePromptWidth + buffer.length;
7392
7477
  const promptRows = Math.max(1, Math.floor(totalPromptChars / termCols) + 1);
7393
7478
  const slashMatches = getMatchingCommands();
@@ -7395,7 +7480,7 @@ class InteractiveLineEditor {
7395
7480
  const fileMatches = activeFile ? getMatchingFiles(activeFile.query) : [];
7396
7481
  let menuRows = 0;
7397
7482
  if (buffer.startsWith("/") && !popupDismissed && slashMatches.length > 0) {
7398
- const BOX_WIDTH = Math.max(20, Math.min(termCols - 4, 120));
7483
+ const BOX_WIDTH = Math.max(20, Math.min(termCols - 6, 120));
7399
7484
  const maxVisible = Math.min(slashMatches.length, 7);
7400
7485
  ensureVisible(slashMatches.length, maxVisible);
7401
7486
  const visibleMatches = slashMatches.slice(scrollTop, scrollTop + maxVisible);
@@ -7422,12 +7507,12 @@ class InteractiveLineEditor {
7422
7507
  }
7423
7508
  menuLines.push(` ${RULE_COLOR2}${rule}${RESET2}`);
7424
7509
  for (const line of menuLines) {
7425
- process.stdout.write(`
7426
- \x1B[2K${line}`);
7510
+ frame += `
7511
+ \x1B[2K${line}`;
7427
7512
  }
7428
7513
  menuRows = menuLines.length;
7429
7514
  } else if (activeFile && !popupDismissed && fileMatches.length > 0) {
7430
- const BOX_WIDTH = Math.max(20, Math.min(termCols - 4, 120));
7515
+ const BOX_WIDTH = Math.max(20, Math.min(termCols - 6, 120));
7431
7516
  const maxVisible = Math.min(fileMatches.length, 7);
7432
7517
  ensureVisible(fileMatches.length, maxVisible);
7433
7518
  const visibleMatches = fileMatches.slice(scrollTop, scrollTop + maxVisible);
@@ -7459,31 +7544,34 @@ class InteractiveLineEditor {
7459
7544
  menuLines.push(` ${style.dim("\u2502")}${style.dim(footerPadded)}${style.dim("\u2502")}`);
7460
7545
  menuLines.push(` ${style.dim("\u2514" + "\u2500".repeat(BOX_WIDTH) + "\u2518")}`);
7461
7546
  for (const line of menuLines) {
7462
- process.stdout.write(`
7463
- \x1B[2K${line}`);
7547
+ frame += `
7548
+ \x1B[2K${line}`;
7464
7549
  }
7465
7550
  menuRows = menuLines.length;
7466
7551
  } else {
7467
7552
  const RULE_COLOR2 = "\x1B[38;2;60;60;68m";
7468
7553
  const bottomRule = ` ${RULE_COLOR2}${getRule()}\x1B[0m`;
7469
7554
  const modeLine = this.getModeLine();
7470
- process.stdout.write(`
7555
+ frame += `
7471
7556
  \x1B[2K${bottomRule}
7472
- \x1B[2K${modeLine}`);
7557
+ \x1B[2K${modeLine}`;
7473
7558
  menuRows = 2;
7474
7559
  }
7475
7560
  const cursorCharsFromTop = visiblePromptWidth + cursor;
7476
7561
  const cursorRow = Math.floor(cursorCharsFromTop / termCols);
7477
7562
  const cursorCol = cursorCharsFromTop % termCols;
7478
- const moveUpRows = promptRows - 1 - cursorRow + menuRows;
7563
+ const moveUpRows = Math.max(0, promptRows - 1 - cursorRow + menuRows);
7479
7564
  if (moveUpRows > 0) {
7480
- process.stdout.write(`\x1B[${moveUpRows}A`);
7565
+ frame += `\x1B[${moveUpRows}A`;
7481
7566
  }
7482
- process.stdout.write("\r");
7567
+ frame += "\r";
7483
7568
  if (cursorCol > 0) {
7484
- process.stdout.write(`\x1B[${cursorCol}C`);
7569
+ frame += `\x1B[${cursorCol}C`;
7485
7570
  }
7486
7571
  lastCursorRowFromTop = cursorRow;
7572
+ try {
7573
+ process.stdout.write(frame);
7574
+ } catch {}
7487
7575
  };
7488
7576
  const onResize = () => {
7489
7577
  redraw();
@@ -7499,24 +7587,24 @@ class InteractiveLineEditor {
7499
7587
  unsubscribeKeypress();
7500
7588
  unsubscribeKeypress = null;
7501
7589
  }
7502
- if (process.stdin.isTTY) {
7503
- try {
7504
- process.stdin.setRawMode(false);
7505
- } catch {}
7506
- }
7590
+ ensureRawMode(false);
7591
+ let cleanupFrame = "";
7507
7592
  if (lastCursorRowFromTop > 0) {
7508
- process.stdout.write(`\x1B[${lastCursorRowFromTop}A`);
7593
+ cleanupFrame += `\x1B[${lastCursorRowFromTop}A`;
7509
7594
  }
7510
- process.stdout.write("\r\x1B[J");
7595
+ cleanupFrame += "\r\x1B[J";
7511
7596
  if (result.trim().length > 0 && !result.startsWith("/")) {
7512
- process.stdout.write(`${CliFormatter.formatClaudeUserPrompt(result)}
7597
+ cleanupFrame += `${CliFormatter.formatClaudeUserPrompt(result)}
7513
7598
 
7514
- `);
7599
+ `;
7515
7600
  } else {
7516
- process.stdout.write(`
7517
- `);
7601
+ cleanupFrame += `
7602
+ `;
7518
7603
  }
7519
- resolve17(result);
7604
+ try {
7605
+ process.stdout.write(cleanupFrame);
7606
+ } catch {}
7607
+ resolve18(result);
7520
7608
  };
7521
7609
  const onKeypress = (_str, key) => {
7522
7610
  if (!key)
@@ -7704,7 +7792,7 @@ async function promptChoice(config) {
7704
7792
  const def = choices[defaultIndex] || choices[0];
7705
7793
  return def ? def.value : "";
7706
7794
  }
7707
- return new Promise((resolve17) => {
7795
+ return new Promise((resolve18) => {
7708
7796
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
7709
7797
  const promptText = ` ${message} (${choices.map((c3) => `[${c3.key}] ${c3.label}`).join(", ")}): `;
7710
7798
  rl.question(promptText, (answer) => {
@@ -7712,23 +7800,20 @@ async function promptChoice(config) {
7712
7800
  const trimmed = answer.trim().toLowerCase();
7713
7801
  const match = choices.find((c3) => c3.key.toLowerCase() === trimmed);
7714
7802
  if (match) {
7715
- resolve17(match.value);
7803
+ resolve18(match.value);
7716
7804
  } else {
7717
7805
  const def = choices[defaultIndex] || choices[0];
7718
- resolve17(def ? def.value : "");
7806
+ resolve18(def ? def.value : "");
7719
7807
  }
7720
7808
  });
7721
7809
  });
7722
7810
  }
7723
- return new Promise((resolve17) => {
7811
+ return new Promise((resolve18) => {
7724
7812
  let selectedIndex = defaultIndex;
7725
7813
  if (selectedIndex < 0 || selectedIndex >= choices.length)
7726
7814
  selectedIndex = 0;
7727
- const wasRaw = process.stdin.isRaw;
7728
- try {
7729
- process.stdin.setRawMode(true);
7730
- } catch {}
7731
- process.stdin.resume();
7815
+ const wasRaw = process.stdin.isRaw ?? false;
7816
+ ensureRawMode(true);
7732
7817
  const render = () => {
7733
7818
  const parts = choices.map((choice, idx) => {
7734
7819
  const isSelected = idx === selectedIndex;
@@ -7747,12 +7832,10 @@ async function promptChoice(config) {
7747
7832
  unsubscribe();
7748
7833
  unsubscribe = null;
7749
7834
  }
7750
- try {
7751
- process.stdin.setRawMode(wasRaw ?? false);
7752
- } catch {}
7835
+ ensureRawMode(wasRaw);
7753
7836
  process.stdout.write(`\r\x1B[1A\r\x1B[2K ${style.bold(message)} ${style.cyan(`[${confirmedChoice.key}] ${confirmedChoice.label}`)}
7754
7837
  \r\x1B[2K`);
7755
- resolve17(confirmedChoice.value);
7838
+ resolve18(confirmedChoice.value);
7756
7839
  };
7757
7840
  const onKeypress = (_str, key) => {
7758
7841
  if (!key)
@@ -7818,13 +7901,10 @@ async function promptToolApproval(params) {
7818
7901
  renderCard(1);
7819
7902
  return "yes";
7820
7903
  }
7821
- return new Promise((resolve17) => {
7904
+ return new Promise((resolve18) => {
7822
7905
  let selectedIndex = 1;
7823
- const wasRaw = process.stdin.isRaw;
7824
- try {
7825
- process.stdin.setRawMode(true);
7826
- } catch {}
7827
- process.stdin.resume();
7906
+ const wasRaw = process.stdin.isRaw ?? false;
7907
+ ensureRawMode(true);
7828
7908
  const render = () => {
7829
7909
  renderCard(selectedIndex);
7830
7910
  };
@@ -7834,11 +7914,9 @@ async function promptToolApproval(params) {
7834
7914
  unsubscribe();
7835
7915
  unsubscribe = null;
7836
7916
  }
7837
- try {
7838
- process.stdin.setRawMode(wasRaw ?? false);
7839
- } catch {}
7917
+ ensureRawMode(wasRaw);
7840
7918
  console.log();
7841
- resolve17(val);
7919
+ resolve18(val);
7842
7920
  };
7843
7921
  const onKeypress = (_str, key) => {
7844
7922
  if (!key)
@@ -7921,7 +7999,7 @@ async function promptUserQuestion(params) {
7921
7999
  if (!process.stdin.isTTY || false || !process.stdin.readable) {
7922
8000
  return options[0] || "yes";
7923
8001
  }
7924
- return new Promise((resolve17) => {
8002
+ return new Promise((resolve18) => {
7925
8003
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
7926
8004
  const promptLabel = options.length > 0 ? `Select [1-${options.length}] or type custom response: ` : `Your response: `;
7927
8005
  rl.question(` ${style.bold(promptLabel)}`, (answer) => {
@@ -7931,7 +8009,7 @@ async function promptUserQuestion(params) {
7931
8009
  const fallback = options[0] || "";
7932
8010
  console.log(style.dim(` \u21B3 Default: ${fallback || "(empty)"}
7933
8011
  `));
7934
- resolve17(fallback);
8012
+ resolve18(fallback);
7935
8013
  return;
7936
8014
  }
7937
8015
  const num = parseInt(trimmed, 10);
@@ -7939,26 +8017,26 @@ async function promptUserQuestion(params) {
7939
8017
  const picked = options[num - 1];
7940
8018
  console.log(style.green(` \u2714 Selected: ${picked}
7941
8019
  `));
7942
- resolve17(picked);
8020
+ resolve18(picked);
7943
8021
  return;
7944
8022
  }
7945
8023
  if (options.length === 2) {
7946
8024
  if (/^(y|yes)$/i.test(trimmed)) {
7947
8025
  console.log(style.green(` \u2714 Selected: ${options[0]}
7948
8026
  `));
7949
- resolve17(options[0]);
8027
+ resolve18(options[0]);
7950
8028
  return;
7951
8029
  }
7952
8030
  if (/^(n|no)$/i.test(trimmed)) {
7953
8031
  console.log(style.green(` \u2714 Selected: ${options[1]}
7954
8032
  `));
7955
- resolve17(options[1]);
8033
+ resolve18(options[1]);
7956
8034
  return;
7957
8035
  }
7958
8036
  }
7959
8037
  console.log(style.green(` \u2714 Answer: ${trimmed}
7960
8038
  `));
7961
- resolve17(trimmed);
8039
+ resolve18(trimmed);
7962
8040
  });
7963
8041
  });
7964
8042
  }
@@ -7987,15 +8065,12 @@ async function promptInteractiveList(config) {
7987
8065
  `);
7988
8066
  return { selectedIndex: -1, action: "close" };
7989
8067
  }
7990
- return new Promise((resolve17) => {
8068
+ return new Promise((resolve18) => {
7991
8069
  let selectedIndex = Math.max(0, Math.min(defaultIndex, items.length - 1));
7992
8070
  let scrollTop = 0;
7993
8071
  let renderedLines = 0;
7994
- const wasRaw = process.stdin.isRaw;
7995
- try {
7996
- process.stdin.setRawMode(true);
7997
- } catch {}
7998
- process.stdin.resume();
8072
+ const wasRaw = process.stdin.isRaw ?? false;
8073
+ ensureRawMode(true);
7999
8074
  process.stdout.write("\x1B[?25l");
8000
8075
  const BOX_WIDTH = Math.min(process.stdout.columns ?? 80, 76);
8001
8076
  const ensureVisible = () => {
@@ -8079,10 +8154,8 @@ async function promptInteractiveList(config) {
8079
8154
  unsubscribe();
8080
8155
  unsubscribe = null;
8081
8156
  }
8082
- try {
8083
- process.stdin.setRawMode(wasRaw ?? false);
8084
- } catch {}
8085
- resolve17(res);
8157
+ ensureRawMode(wasRaw);
8158
+ resolve18(res);
8086
8159
  };
8087
8160
  const onKeypress = async (_str, key) => {
8088
8161
  if (!key)
@@ -8153,8 +8226,8 @@ async function promptInteractiveList(config) {
8153
8226
  }
8154
8227
 
8155
8228
  // src/security/scanner.ts
8156
- import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8157
- import { join as join11, relative as relative2, resolve as resolve17 } from "path";
8229
+ import { existsSync as existsSync20, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8230
+ import { join as join12, relative as relative2, resolve as resolve18 } from "path";
8158
8231
  var SECURITY_RULES = [
8159
8232
  {
8160
8233
  id: "SEC-001",
@@ -8286,13 +8359,13 @@ var IGNORED_FILES = new Set([
8286
8359
  ]);
8287
8360
  async function runSecurityScan(targetDir, options = {}) {
8288
8361
  const startTime = performance.now();
8289
- const root = resolve17(targetDir);
8362
+ const root = resolve18(targetDir);
8290
8363
  const isDirectTestDir = targetDir.includes("test") || Boolean(options.includeTests);
8291
8364
  const maxFiles = options.maxFiles || 2000;
8292
8365
  const findings = [];
8293
8366
  let scannedCount = 0;
8294
8367
  function walk(current) {
8295
- if (scannedCount >= maxFiles || !existsSync19(current))
8368
+ if (scannedCount >= maxFiles || !existsSync20(current))
8296
8369
  return;
8297
8370
  let entries;
8298
8371
  try {
@@ -8303,7 +8376,7 @@ async function runSecurityScan(targetDir, options = {}) {
8303
8376
  for (const entry of entries) {
8304
8377
  if (scannedCount >= maxFiles)
8305
8378
  break;
8306
- const fullPath = join11(current, entry);
8379
+ const fullPath = join12(current, entry);
8307
8380
  let stat;
8308
8381
  try {
8309
8382
  stat = statSync5(fullPath);
@@ -8381,38 +8454,38 @@ async function runSecurityScan(targetDir, options = {}) {
8381
8454
  }
8382
8455
 
8383
8456
  // src/mcp/servers/chrome-devtools/index.ts
8384
- import { resolve as resolve18 } from "path";
8457
+ import { resolve as resolve19 } from "path";
8385
8458
 
8386
8459
  // src/mcp/servers/chrome-devtools/server.ts
8387
8460
  if (false) {}
8388
8461
 
8389
8462
  // src/mcp/servers/chrome-devtools/index.ts
8390
8463
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/chrome-devtools";
8391
- var CHROME_DEVTOOLS_MCP_SERVER_PATH = resolve18(__dirname, "server.ts");
8464
+ var CHROME_DEVTOOLS_MCP_SERVER_PATH = resolve19(__dirname, "server.ts");
8392
8465
 
8393
8466
  // src/mcp/servers/web-search/index.ts
8394
- import { resolve as resolve19 } from "path";
8467
+ import { resolve as resolve20 } from "path";
8395
8468
 
8396
8469
  // src/mcp/servers/web-search/server.ts
8397
8470
  if (false) {}
8398
8471
 
8399
8472
  // src/mcp/servers/web-search/index.ts
8400
8473
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/web-search";
8401
- var WEB_SEARCH_MCP_SERVER_PATH = resolve19(__dirname, "server.ts");
8474
+ var WEB_SEARCH_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
8402
8475
 
8403
8476
  // src/mcp/servers/sqlite/index.ts
8404
- import { resolve as resolve20 } from "path";
8477
+ import { resolve as resolve21 } from "path";
8405
8478
 
8406
8479
  // src/mcp/servers/sqlite/server.ts
8407
8480
  if (false) {}
8408
8481
 
8409
8482
  // src/mcp/servers/sqlite/index.ts
8410
8483
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
8411
- var SQLITE_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
8484
+ var SQLITE_MCP_SERVER_PATH = resolve21(__dirname, "server.ts");
8412
8485
 
8413
8486
  // src/init/project-analyzer.ts
8414
- import { existsSync as existsSync20, readFileSync as readFileSync13, readdirSync as readdirSync9 } from "fs";
8415
- import { join as join12, basename as basename3 } from "path";
8487
+ import { existsSync as existsSync21, readFileSync as readFileSync13, readdirSync as readdirSync9 } from "fs";
8488
+ import { join as join13, basename as basename3 } from "path";
8416
8489
 
8417
8490
  class ProjectAnalyzer {
8418
8491
  cwd;
@@ -8430,8 +8503,8 @@ class ProjectAnalyzer {
8430
8503
  const architectureNotes = [];
8431
8504
  const codeConventions = [];
8432
8505
  let description = readmeInfo.description;
8433
- const pkgPath = join12(this.cwd, "package.json");
8434
- if (existsSync20(pkgPath)) {
8506
+ const pkgPath = join13(this.cwd, "package.json");
8507
+ if (existsSync21(pkgPath)) {
8435
8508
  try {
8436
8509
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8437
8510
  if (!description && pkg.description)
@@ -8504,8 +8577,8 @@ class ProjectAnalyzer {
8504
8577
  }
8505
8578
  } catch {}
8506
8579
  }
8507
- const tsconfigPath = join12(this.cwd, "tsconfig.json");
8508
- if (existsSync20(tsconfigPath)) {
8580
+ const tsconfigPath = join13(this.cwd, "tsconfig.json");
8581
+ if (existsSync21(tsconfigPath)) {
8509
8582
  try {
8510
8583
  const tsconfig = JSON.parse(readFileSync13(tsconfigPath, "utf8"));
8511
8584
  if (tsconfig.compilerOptions?.strict) {
@@ -8516,8 +8589,8 @@ class ProjectAnalyzer {
8516
8589
  }
8517
8590
  } catch {}
8518
8591
  }
8519
- const cargoPath = join12(this.cwd, "Cargo.toml");
8520
- if (existsSync20(cargoPath)) {
8592
+ const cargoPath = join13(this.cwd, "Cargo.toml");
8593
+ if (existsSync21(cargoPath)) {
8521
8594
  try {
8522
8595
  commands.dev = commands.dev || "cargo run";
8523
8596
  commands.build = commands.build || "cargo build";
@@ -8526,8 +8599,8 @@ class ProjectAnalyzer {
8526
8599
  frameworks.push("Rust Cargo");
8527
8600
  } catch {}
8528
8601
  }
8529
- const goModPath = join12(this.cwd, "go.mod");
8530
- if (existsSync20(goModPath)) {
8602
+ const goModPath = join13(this.cwd, "go.mod");
8603
+ if (existsSync21(goModPath)) {
8531
8604
  try {
8532
8605
  commands.dev = commands.dev || "go run .";
8533
8606
  commands.build = commands.build || "go build ./...";
@@ -8536,37 +8609,37 @@ class ProjectAnalyzer {
8536
8609
  frameworks.push("Go Modules");
8537
8610
  } catch {}
8538
8611
  }
8539
- const pyprojectPath = join12(this.cwd, "pyproject.toml");
8540
- const requirementsPath = join12(this.cwd, "requirements.txt");
8541
- if (existsSync20(pyprojectPath) || existsSync20(requirementsPath)) {
8612
+ const pyprojectPath = join13(this.cwd, "pyproject.toml");
8613
+ const requirementsPath = join13(this.cwd, "requirements.txt");
8614
+ if (existsSync21(pyprojectPath) || existsSync21(requirementsPath)) {
8542
8615
  commands.test = commands.test || "pytest";
8543
8616
  commands.lint = commands.lint || "ruff check .";
8544
- if (existsSync20(join12(this.cwd, "uv.lock"))) {
8617
+ if (existsSync21(join13(this.cwd, "uv.lock"))) {
8545
8618
  frameworks.push("uv");
8546
8619
  commands.test = "uv run pytest";
8547
- } else if (existsSync20(join12(this.cwd, "poetry.lock"))) {
8620
+ } else if (existsSync21(join13(this.cwd, "poetry.lock"))) {
8548
8621
  frameworks.push("Poetry");
8549
8622
  commands.test = "poetry run pytest";
8550
8623
  }
8551
8624
  }
8552
- if (existsSync20(join12(this.cwd, "Dockerfile"))) {
8625
+ if (existsSync21(join13(this.cwd, "Dockerfile"))) {
8553
8626
  infrastructure.push("Docker");
8554
8627
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
8555
8628
  commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
8556
8629
  }
8557
- if (existsSync20(join12(this.cwd, "nginx.conf"))) {
8630
+ if (existsSync21(join13(this.cwd, "nginx.conf"))) {
8558
8631
  infrastructure.push("Nginx");
8559
8632
  }
8560
- if (existsSync20(join12(this.cwd, "src/api.ts")) || existsSync20(join12(this.cwd, "src/api"))) {
8633
+ if (existsSync21(join13(this.cwd, "src/api.ts")) || existsSync21(join13(this.cwd, "src/api"))) {
8561
8634
  architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
8562
8635
  }
8563
- if (existsSync20(join12(this.cwd, "src/components"))) {
8636
+ if (existsSync21(join13(this.cwd, "src/components"))) {
8564
8637
  architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
8565
8638
  }
8566
- if (existsSync20(join12(this.cwd, "src/types.ts")) || existsSync20(join12(this.cwd, "src/types"))) {
8639
+ if (existsSync21(join13(this.cwd, "src/types.ts")) || existsSync21(join13(this.cwd, "src/types"))) {
8567
8640
  architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
8568
8641
  }
8569
- if (existsSync20(join12(this.cwd, ".env.example"))) {
8642
+ if (existsSync21(join13(this.cwd, ".env.example"))) {
8570
8643
  architectureNotes.push("Environment configuration template is in `.env.example`.");
8571
8644
  }
8572
8645
  if (commands.typecheck || commands.lint || commands.test) {
@@ -8583,7 +8656,7 @@ class ProjectAnalyzer {
8583
8656
  let hasExistingInstructions = false;
8584
8657
  let existingInstructionFile;
8585
8658
  for (const f of instructionFiles) {
8586
- if (existsSync20(join12(this.cwd, f))) {
8659
+ if (existsSync21(join13(this.cwd, f))) {
8587
8660
  hasExistingInstructions = true;
8588
8661
  existingInstructionFile = f;
8589
8662
  break;
@@ -8664,8 +8737,8 @@ class ProjectAnalyzer {
8664
8737
  extractReadmeMetadata() {
8665
8738
  const readmeFiles = ["README.md", "readme.md", "README.MD"];
8666
8739
  for (const file of readmeFiles) {
8667
- const fullPath = join12(this.cwd, file);
8668
- if (existsSync20(fullPath)) {
8740
+ const fullPath = join13(this.cwd, file);
8741
+ if (existsSync21(fullPath)) {
8669
8742
  try {
8670
8743
  const content = readFileSync13(fullPath, "utf8");
8671
8744
  const lines = content.split(`
@@ -8690,8 +8763,8 @@ class ProjectAnalyzer {
8690
8763
  return {};
8691
8764
  }
8692
8765
  detectProjectName() {
8693
- const pkgPath = join12(this.cwd, "package.json");
8694
- if (existsSync20(pkgPath)) {
8766
+ const pkgPath = join13(this.cwd, "package.json");
8767
+ if (existsSync21(pkgPath)) {
8695
8768
  try {
8696
8769
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8697
8770
  if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
@@ -8699,16 +8772,16 @@ class ProjectAnalyzer {
8699
8772
  }
8700
8773
  } catch {}
8701
8774
  }
8702
- const cargoPath = join12(this.cwd, "Cargo.toml");
8703
- if (existsSync20(cargoPath)) {
8775
+ const cargoPath = join13(this.cwd, "Cargo.toml");
8776
+ if (existsSync21(cargoPath)) {
8704
8777
  try {
8705
8778
  const match = readFileSync13(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
8706
8779
  if (match?.[1])
8707
8780
  return match[1];
8708
8781
  } catch {}
8709
8782
  }
8710
- const goModPath = join12(this.cwd, "go.mod");
8711
- if (existsSync20(goModPath)) {
8783
+ const goModPath = join13(this.cwd, "go.mod");
8784
+ if (existsSync21(goModPath)) {
8712
8785
  try {
8713
8786
  const match = readFileSync13(goModPath, "utf8").match(/module\s+([^\s]+)/);
8714
8787
  if (match?.[1])
@@ -8719,47 +8792,47 @@ class ProjectAnalyzer {
8719
8792
  }
8720
8793
  detectLanguages() {
8721
8794
  const langs = new Set;
8722
- if (existsSync20(join12(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8795
+ if (existsSync21(join13(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8723
8796
  langs.add("TypeScript");
8724
8797
  }
8725
- if (existsSync20(join12(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8798
+ if (existsSync21(join13(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8726
8799
  langs.add("JavaScript");
8727
8800
  }
8728
- if (existsSync20(join12(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8801
+ if (existsSync21(join13(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8729
8802
  langs.add("Rust");
8730
8803
  }
8731
- if (existsSync20(join12(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8804
+ if (existsSync21(join13(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8732
8805
  langs.add("Go");
8733
8806
  }
8734
- if (existsSync20(join12(this.cwd, "pyproject.toml")) || existsSync20(join12(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8807
+ if (existsSync21(join13(this.cwd, "pyproject.toml")) || existsSync21(join13(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8735
8808
  langs.add("Python");
8736
8809
  }
8737
- if (existsSync20(join12(this.cwd, "pom.xml")) || existsSync20(join12(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8810
+ if (existsSync21(join13(this.cwd, "pom.xml")) || existsSync21(join13(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8738
8811
  langs.add("Java");
8739
8812
  }
8740
- if (existsSync20(join12(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8813
+ if (existsSync21(join13(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8741
8814
  langs.add("C/C++");
8742
8815
  }
8743
8816
  return Array.from(langs);
8744
8817
  }
8745
8818
  detectPackageManager() {
8746
- if (existsSync20(join12(this.cwd, "bun.lockb")) || existsSync20(join12(this.cwd, "bun.lock")))
8819
+ if (existsSync21(join13(this.cwd, "bun.lockb")) || existsSync21(join13(this.cwd, "bun.lock")))
8747
8820
  return "bun";
8748
- if (existsSync20(join12(this.cwd, "pnpm-lock.yaml")))
8821
+ if (existsSync21(join13(this.cwd, "pnpm-lock.yaml")))
8749
8822
  return "pnpm";
8750
- if (existsSync20(join12(this.cwd, "yarn.lock")))
8823
+ if (existsSync21(join13(this.cwd, "yarn.lock")))
8751
8824
  return "yarn";
8752
- if (existsSync20(join12(this.cwd, "package-lock.json")))
8825
+ if (existsSync21(join13(this.cwd, "package-lock.json")))
8753
8826
  return "npm";
8754
- if (existsSync20(join12(this.cwd, "Cargo.lock")) || existsSync20(join12(this.cwd, "Cargo.toml")))
8827
+ if (existsSync21(join13(this.cwd, "Cargo.lock")) || existsSync21(join13(this.cwd, "Cargo.toml")))
8755
8828
  return "cargo";
8756
- if (existsSync20(join12(this.cwd, "uv.lock")))
8829
+ if (existsSync21(join13(this.cwd, "uv.lock")))
8757
8830
  return "uv";
8758
- if (existsSync20(join12(this.cwd, "poetry.lock")))
8831
+ if (existsSync21(join13(this.cwd, "poetry.lock")))
8759
8832
  return "poetry";
8760
- if (existsSync20(join12(this.cwd, "go.sum")) || existsSync20(join12(this.cwd, "go.mod")))
8833
+ if (existsSync21(join13(this.cwd, "go.sum")) || existsSync21(join13(this.cwd, "go.mod")))
8761
8834
  return "go";
8762
- if (existsSync20(join12(this.cwd, "package.json")))
8835
+ if (existsSync21(join13(this.cwd, "package.json")))
8763
8836
  return "npm";
8764
8837
  return;
8765
8838
  }
@@ -8773,17 +8846,17 @@ class ProjectAnalyzer {
8773
8846
  }
8774
8847
  }
8775
8848
  // src/init/init-command.ts
8776
- import { existsSync as existsSync21, writeFileSync as writeFileSync7 } from "fs";
8777
- import { join as join13 } from "path";
8849
+ import { existsSync as existsSync22, writeFileSync as writeFileSync8 } from "fs";
8850
+ import { join as join14 } from "path";
8778
8851
  function runProjectInit(options = {}) {
8779
8852
  const cwd = options.cwd || process.cwd();
8780
8853
  const filename = options.filename || "AGENTS.md";
8781
- const targetPath = join13(cwd, filename);
8854
+ const targetPath = join14(cwd, filename);
8782
8855
  const analyzer = new ProjectAnalyzer(cwd);
8783
8856
  const analysis = analyzer.analyze();
8784
8857
  const content = analyzer.generateAgentsMarkdown(analysis);
8785
- const alreadyExists = existsSync21(targetPath);
8786
- writeFileSync7(targetPath, content, "utf8");
8858
+ const alreadyExists = existsSync22(targetPath);
8859
+ writeFileSync8(targetPath, content, "utf8");
8787
8860
  return {
8788
8861
  success: true,
8789
8862
  filePath: targetPath,
@@ -8855,7 +8928,7 @@ var AVAILABLE_SLASH_COMMANDS = [
8855
8928
  { name: "/manual", description: "Switch to Manual Mode (all tools require user confirmation)" },
8856
8929
  { name: "/accept-edits", description: "Switch to Accept Edits Mode (file edits auto-approved, shell prompts)" },
8857
8930
  { name: "/plan", description: "Switch to Plan Mode (read-only planning, mutations blocked)" },
8858
- { name: "/skills", description: "List domain skills in workspace & global" },
8931
+ { name: "/skills", description: "Manage domain skills (/skills, /skill add <name>, /skill remove <name>)" },
8859
8932
  { name: "/memories", description: "View learned preferences & memories" },
8860
8933
  { name: "/worktrees", description: "List active isolated Git Worktrees" },
8861
8934
  { name: "/resume", description: "Resume a previous conversation session (/resume [id])" },
@@ -9358,6 +9431,46 @@ async function handleSkillsCommand(ctx, args) {
9358
9431
  }
9359
9432
  const sub = (args[0] || "").toLowerCase();
9360
9433
  const target = (args[1] || "").toLowerCase();
9434
+ if (sub === "install" || sub === "add") {
9435
+ if (!target) {
9436
+ console.log(style.yellow("Usage: /skill add <skill-name> [--global]"));
9437
+ return;
9438
+ }
9439
+ const isGlobal = args.includes("--global") || args.includes("-g");
9440
+ try {
9441
+ console.log(style.dim(`
9442
+ Fetching skill '${target}' from catalog...`));
9443
+ const res = await installSkill(target, { cwd: ctx.session.cwd, global: isGlobal });
9444
+ loader.clearCache();
9445
+ console.log(style.green(`
9446
+ \u2713 ${res.message}
9447
+ `));
9448
+ } catch (err) {
9449
+ console.log(style.red(`
9450
+ \u2715 Failed to install skill: ${err.message}
9451
+ `));
9452
+ }
9453
+ return;
9454
+ }
9455
+ if (sub === "remove" || sub === "uninstall" || sub === "rm") {
9456
+ if (!target) {
9457
+ console.log(style.yellow("Usage: /skill remove <skill-name> [--global]"));
9458
+ return;
9459
+ }
9460
+ const isGlobal = args.includes("--global") || args.includes("-g");
9461
+ const res = removeSkill(target, { cwd: ctx.session.cwd, global: isGlobal });
9462
+ loader.clearCache();
9463
+ if (res.removed) {
9464
+ console.log(style.green(`
9465
+ \u2713 Skill '${target}' removed from ${res.targetDir}
9466
+ `));
9467
+ } else {
9468
+ console.log(style.yellow(`
9469
+ \u2715 Skill '${target}' was not found in ${res.targetDir}
9470
+ `));
9471
+ }
9472
+ return;
9473
+ }
9361
9474
  if (sub === "disable" || sub === "off") {
9362
9475
  if (!target) {
9363
9476
  console.log(style.yellow("Usage: /skills disable <skill-name>"));
@@ -10328,9 +10441,9 @@ class MarkdownHighlighter {
10328
10441
  }
10329
10442
 
10330
10443
  // src/cli/update-checker.ts
10331
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
10332
- import { homedir as homedir5 } from "os";
10333
- import { join as join14 } from "path";
10444
+ import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
10445
+ import { homedir as homedir4 } from "os";
10446
+ import { join as join15 } from "path";
10334
10447
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
10335
10448
  function parseSemver(v) {
10336
10449
  const clean = v.replace(/^v/, "").trim();
@@ -10351,8 +10464,8 @@ function isNewerVersion(current, remote) {
10351
10464
  return remPatch > curPatch;
10352
10465
  }
10353
10466
  function getUpdateCachePath() {
10354
- const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join14(homedir5(), ".pikaa");
10355
- return join14(baseDir, "update-cache.json");
10467
+ const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join15(homedir4(), ".pikaa");
10468
+ return join15(baseDir, "update-cache.json");
10356
10469
  }
10357
10470
  async function fetchLatestNpmVersion(packageName, timeoutMs = 1500) {
10358
10471
  const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
@@ -10385,7 +10498,7 @@ async function checkForUpdates(options = {}) {
10385
10498
  const cachePath = options.cachePath || getUpdateCachePath();
10386
10499
  const now = Date.now();
10387
10500
  let cached = null;
10388
- if (!options.force && existsSync22(cachePath)) {
10501
+ if (!options.force && existsSync23(cachePath)) {
10389
10502
  try {
10390
10503
  const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
10391
10504
  if (raw && typeof raw.lastChecked === "number" && typeof raw.latestVersion === "string") {
@@ -10415,16 +10528,16 @@ async function checkForUpdates(options = {}) {
10415
10528
  return null;
10416
10529
  }
10417
10530
  try {
10418
- const parentDir = join14(cachePath, "..");
10419
- if (!existsSync22(parentDir)) {
10420
- mkdirSync12(parentDir, { recursive: true });
10531
+ const parentDir = join15(cachePath, "..");
10532
+ if (!existsSync23(parentDir)) {
10533
+ mkdirSync13(parentDir, { recursive: true });
10421
10534
  }
10422
10535
  const cacheData = {
10423
10536
  lastChecked: now,
10424
10537
  latestVersion,
10425
10538
  packageName
10426
10539
  };
10427
- writeFileSync8(cachePath, JSON.stringify(cacheData, null, 2), "utf8");
10540
+ writeFileSync9(cachePath, JSON.stringify(cacheData, null, 2), "utf8");
10428
10541
  } catch {}
10429
10542
  const updateAvailable = isNewerVersion(currentVersion, latestVersion);
10430
10543
  return updateAvailable ? {
@@ -10590,9 +10703,9 @@ class CliRepl {
10590
10703
  filesModified: Array.from(this.turnFilesModified)
10591
10704
  });
10592
10705
  if (this.turnDoneResolver) {
10593
- const resolve21 = this.turnDoneResolver;
10706
+ const resolve22 = this.turnDoneResolver;
10594
10707
  this.turnDoneResolver = undefined;
10595
- resolve21();
10708
+ resolve22();
10596
10709
  }
10597
10710
  break;
10598
10711
  case "Error":
@@ -10608,9 +10721,9 @@ class CliRepl {
10608
10721
  console.error(style.red(`Error: ${msg.message}
10609
10722
  `));
10610
10723
  if (this.turnDoneResolver) {
10611
- const resolve21 = this.turnDoneResolver;
10724
+ const resolve22 = this.turnDoneResolver;
10612
10725
  this.turnDoneResolver = undefined;
10613
- resolve21();
10726
+ resolve22();
10614
10727
  }
10615
10728
  break;
10616
10729
  }
@@ -10680,15 +10793,21 @@ class CliRepl {
10680
10793
  `));
10681
10794
  this.isProcessing = false;
10682
10795
  if (this.turnDoneResolver) {
10683
- const resolve21 = this.turnDoneResolver;
10796
+ const resolve22 = this.turnDoneResolver;
10684
10797
  this.turnDoneResolver = undefined;
10685
- resolve21();
10798
+ resolve22();
10686
10799
  }
10687
10800
  }
10688
10801
  }
10689
10802
  }
10690
10803
  });
10691
10804
  while (!this.isClosed) {
10805
+ await new Promise((resolve22) => setTimeout(resolve22, 20));
10806
+ if (typeof Bun !== "undefined" && typeof Bun.gc === "function") {
10807
+ try {
10808
+ Bun.gc(false);
10809
+ } catch {}
10810
+ }
10692
10811
  let rawLine;
10693
10812
  try {
10694
10813
  rawLine = await editor.readLine();
@@ -10719,8 +10838,8 @@ class CliRepl {
10719
10838
  }
10720
10839
  async submitTurn(text) {
10721
10840
  try {
10722
- const turnPromise = new Promise((resolve21) => {
10723
- this.turnDoneResolver = resolve21;
10841
+ const turnPromise = new Promise((resolve22) => {
10842
+ this.turnDoneResolver = resolve22;
10724
10843
  });
10725
10844
  await this.session.submit({
10726
10845
  type: "TurnInput",
@@ -10786,9 +10905,22 @@ async function main() {
10786
10905
  } else if (arg === "list" || arg === "sessions" || arg === "--list") {
10787
10906
  printSessionsList(storageManager);
10788
10907
  process.exit(0);
10789
- } else if (arg === "skills") {
10790
- printSkillsList(skillsLoader, cwd);
10791
- process.exit(0);
10908
+ } else if (arg === "skills" || arg === "skill") {
10909
+ const subAction = args[i + 1];
10910
+ const target = args[i + 2];
10911
+ const isGlobal = args.includes("--global") || args.includes("-g");
10912
+ if (subAction && !subAction.startsWith("-")) {
10913
+ await handleSkillCliCommand(subAction, target, {
10914
+ cwd,
10915
+ isGlobal,
10916
+ baseUrl,
10917
+ loader: skillsLoader
10918
+ });
10919
+ process.exit(0);
10920
+ } else {
10921
+ printSkillsList(skillsLoader, cwd);
10922
+ process.exit(0);
10923
+ }
10792
10924
  } else if (arg === "memories" || arg === "memory") {
10793
10925
  printMemoriesList(memoryStore, cwd);
10794
10926
  process.exit(0);
@@ -10810,7 +10942,7 @@ async function main() {
10810
10942
  explicitApiKey = args[++i];
10811
10943
  apiKey = explicitApiKey || apiKey;
10812
10944
  } else if (arg === "--cwd" || arg === "-C") {
10813
- cwd = resolve21(args[++i] || cwd);
10945
+ cwd = resolve22(args[++i] || cwd);
10814
10946
  } else if (arg === "--role" || arg === "-r") {
10815
10947
  role = args[++i] || role;
10816
10948
  } else if (arg === "--mcp") {
@@ -10836,11 +10968,11 @@ async function main() {
10836
10968
  const mcpManager = new McpManager;
10837
10969
  const candidateConfigs = [
10838
10970
  mcpConfigFile,
10839
- resolve21(cwd, ".mcp.json"),
10840
- resolve21(cwd, "mcp_config.json")
10971
+ resolve22(cwd, ".mcp.json"),
10972
+ resolve22(cwd, "mcp_config.json")
10841
10973
  ].filter(Boolean);
10842
10974
  for (const cfg of candidateConfigs) {
10843
- if (existsSync23(cfg)) {
10975
+ if (existsSync24(cfg)) {
10844
10976
  try {
10845
10977
  await mcpManager.loadConfigFile(cfg);
10846
10978
  mcpManager.registerToolsIntoRouter(tools4);
@@ -11043,6 +11175,62 @@ Resume a session with: ${style.bold("groupy --resume <thread_id>")}`);
11043
11175
  }
11044
11176
  console.log();
11045
11177
  }
11178
+ async function handleSkillCliCommand(action, targetName, options) {
11179
+ const act = action.toLowerCase();
11180
+ if (act === "add" || act === "install") {
11181
+ if (!targetName) {
11182
+ console.error(style.yellow(`
11183
+ Usage: pikaa skill add <skill-name> [--global]
11184
+ `));
11185
+ process.exit(1);
11186
+ }
11187
+ try {
11188
+ console.log(style.dim(`
11189
+ Fetching skill '${targetName}' from catalog...`));
11190
+ const res = await installSkill(targetName, {
11191
+ cwd: options.cwd,
11192
+ global: options.isGlobal,
11193
+ backendUrl: options.baseUrl
11194
+ });
11195
+ console.log(style.green(`
11196
+ \u2713 ${res.message}`));
11197
+ console.log(style.dim(` To start using this skill in an AI coding session, simply run: pikaa
11198
+ `));
11199
+ } catch (err) {
11200
+ console.error(style.red(`
11201
+ \u2715 Failed to install skill: ${err.message}
11202
+ `));
11203
+ process.exit(1);
11204
+ }
11205
+ } else if (act === "remove" || act === "uninstall" || act === "rm") {
11206
+ if (!targetName) {
11207
+ console.error(style.yellow(`
11208
+ Usage: pikaa skill remove <skill-name> [--global]
11209
+ `));
11210
+ process.exit(1);
11211
+ }
11212
+ const res = removeSkill(targetName, { cwd: options.cwd, global: options.isGlobal });
11213
+ if (res.removed) {
11214
+ console.log(style.green(`
11215
+ \u2713 Skill '${targetName}' removed from ${res.targetDir}
11216
+ `));
11217
+ } else {
11218
+ console.log(style.yellow(`
11219
+ \u2715 Skill '${targetName}' was not found in ${res.targetDir}
11220
+ `));
11221
+ }
11222
+ } else if (act === "list" || act === "ls") {
11223
+ printSkillsList(options.loader, options.cwd);
11224
+ } else {
11225
+ if (act && !targetName) {
11226
+ return handleSkillCliCommand("add", act, options);
11227
+ }
11228
+ console.error(style.yellow(`
11229
+ Unknown skill action '${action}'. Available: add, remove, list
11230
+ `));
11231
+ process.exit(1);
11232
+ }
11233
+ }
11046
11234
  function printSkillsList(loader, cwd) {
11047
11235
  const skills = loader.discoverSkills(cwd);
11048
11236
  console.log();