@pikaa-ai/pikaa 0.3.22 → 0.3.24

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 +448 -181
  6. package/dist/index.js +22 -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
@@ -5228,6 +5228,27 @@ class SessionPersistenceManager {
5228
5228
  return null;
5229
5229
  }
5230
5230
  }
5231
+ unbindSession() {
5232
+ for (const unsub of this.unsubscribers) {
5233
+ try {
5234
+ unsub();
5235
+ } catch {}
5236
+ }
5237
+ this.unsubscribers = [];
5238
+ }
5239
+ resumeIntoSession(session, threadId) {
5240
+ const restored = this.loadSession(threadId);
5241
+ if (!restored)
5242
+ return null;
5243
+ this.unbindSession();
5244
+ session.threadId = restored.thread.id;
5245
+ session.setHistory(restored.items);
5246
+ if (restored.thread.model) {
5247
+ session.model = restored.thread.model;
5248
+ }
5249
+ this.bindSession(session, restored.thread.role);
5250
+ return restored;
5251
+ }
5231
5252
  listSessions(options) {
5232
5253
  return this.store.listThreads(options);
5233
5254
  }
@@ -5248,7 +5269,6 @@ class SessionPersistenceManager {
5248
5269
  // src/skills/loader.ts
5249
5270
  import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
5250
5271
  import { resolve as resolve13, join as join8 } from "path";
5251
- import { homedir as homedir3 } from "os";
5252
5272
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
5253
5273
 
5254
5274
  class SkillsLoader {
@@ -5324,7 +5344,7 @@ class SkillsLoader {
5324
5344
  }
5325
5345
  }
5326
5346
  if (this.includeGlobal) {
5327
- roots.push(getGlobalSkillsDir(), resolve13(homedir3(), ".gemini", "config", "skills"));
5347
+ roots.push(getGlobalSkillsDir());
5328
5348
  }
5329
5349
  roots.push(...this.customRoots.map((r) => resolve13(r)));
5330
5350
  return roots.filter((r) => existsSync16(r));
@@ -5470,9 +5490,77 @@ When tackling complex specialized tasks that match any of these skills, autonomo
5470
5490
  }
5471
5491
  }
5472
5492
 
5493
+ // src/skills/installer.ts
5494
+ import { existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
5495
+ import { resolve as resolve14, join as join9 } from "path";
5496
+ async function installSkill(skillName, options) {
5497
+ const cleanName = skillName.trim().toLowerCase().replace(/^@/, "");
5498
+ if (!cleanName) {
5499
+ throw new Error("Skill name cannot be empty.");
5500
+ }
5501
+ const credStore = new CredentialsStore;
5502
+ const creds = credStore.load();
5503
+ let backend = (options.backendUrl || process.env.PIKAA_BACKEND_URL || process.env.GROUPY_BACKEND_URL || creds?.baseUrl || "https://api.groupy-hub.store").replace(/\/+$/, "");
5504
+ if (backend.endsWith("/v1")) {
5505
+ backend = backend.slice(0, -3).replace(/\/+$/, "");
5506
+ }
5507
+ const endpoint = `${backend}/api/skills/${encodeURIComponent(cleanName)}`;
5508
+ let resp;
5509
+ try {
5510
+ resp = await fetch(endpoint, {
5511
+ headers: creds?.accessToken ? { Authorization: `Bearer ${creds.accessToken}` } : {}
5512
+ });
5513
+ } catch (netErr) {
5514
+ throw new Error(`Failed to connect to backend at ${backend}: ${netErr.message}`);
5515
+ }
5516
+ if (!resp.ok) {
5517
+ if (resp.status === 404) {
5518
+ throw new Error(`Skill '${cleanName}' not found in catalog (${endpoint}).`);
5519
+ }
5520
+ const errText = await resp.text();
5521
+ throw new Error(`Backend error (${resp.status}): ${errText}`);
5522
+ }
5523
+ const skillData = await resp.json();
5524
+ if (!skillData.content) {
5525
+ throw new Error(`Skill '${cleanName}' does not contain markdown content.`);
5526
+ }
5527
+ const scope = options.global ? "global" : "workspace";
5528
+ const targetDir = options.global ? resolve14(getGlobalSkillsDir(), cleanName) : resolve14(options.cwd, ".agents", "skills", cleanName);
5529
+ mkdirSync10(targetDir, { recursive: true });
5530
+ const targetFile = join9(targetDir, "SKILL.md");
5531
+ let fileContent = skillData.content;
5532
+ if (!fileContent.startsWith("---")) {
5533
+ const desc = skillData.description ? `description: "${skillData.description.replace(/"/g, "\\\"")}"
5534
+ ` : "";
5535
+ fileContent = `---
5536
+ name: ${cleanName}
5537
+ ${desc}---
5538
+
5539
+ ${fileContent}`;
5540
+ }
5541
+ writeFileSync5(targetFile, fileContent, "utf-8");
5542
+ return {
5543
+ success: true,
5544
+ skillName: cleanName,
5545
+ title: skillData.title || cleanName,
5546
+ targetPath: targetFile,
5547
+ scope,
5548
+ message: `Installed skill '${skillData.title || cleanName}' (@${cleanName}) into ${scope} at ${targetFile}`
5549
+ };
5550
+ }
5551
+ function removeSkill(skillName, options) {
5552
+ const cleanName = skillName.trim().toLowerCase().replace(/^@/, "");
5553
+ const targetDir = options.global ? resolve14(getGlobalSkillsDir(), cleanName) : resolve14(options.cwd, ".agents", "skills", cleanName);
5554
+ if (!existsSync17(targetDir)) {
5555
+ return { success: false, targetDir, removed: false };
5556
+ }
5557
+ rmSync2(targetDir, { recursive: true, force: true });
5558
+ return { success: true, targetDir, removed: true };
5559
+ }
5560
+
5473
5561
  // src/memories/store.ts
5474
- import { existsSync as existsSync17, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync10, readdirSync as readdirSync7 } from "fs";
5475
- import { resolve as resolve14, join as join9, basename, dirname as dirname8 } from "path";
5562
+ import { existsSync as existsSync18, readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync11, readdirSync as readdirSync7 } from "fs";
5563
+ import { resolve as resolve15, join as join10, basename, dirname as dirname8 } from "path";
5476
5564
  import { createHash } from "crypto";
5477
5565
  class MemoryStore {
5478
5566
  globalPath;
@@ -5482,14 +5570,14 @@ class MemoryStore {
5482
5570
  this.customWorkspacePath = options.workspacePath;
5483
5571
  }
5484
5572
  findProjectRoot(cwd) {
5485
- let current = resolve14(cwd);
5573
+ let current = resolve15(cwd);
5486
5574
  while (true) {
5487
- if (existsSync17(join9(current, ".git"))) {
5575
+ if (existsSync18(join10(current, ".git"))) {
5488
5576
  return current;
5489
5577
  }
5490
5578
  const parent = dirname8(current);
5491
5579
  if (parent === current) {
5492
- return resolve14(cwd);
5580
+ return resolve15(cwd);
5493
5581
  }
5494
5582
  current = parent;
5495
5583
  }
@@ -5497,30 +5585,30 @@ class MemoryStore {
5497
5585
  getProjectSlug(cwd) {
5498
5586
  const root = this.findProjectRoot(cwd);
5499
5587
  const folderName = basename(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
5500
- const hash = createHash("sha256").update(resolve14(root)).digest("hex").slice(0, 6);
5588
+ const hash = createHash("sha256").update(resolve15(root)).digest("hex").slice(0, 6);
5501
5589
  return `${folderName}-${hash}`;
5502
5590
  }
5503
5591
  getProjectMemoryDir(cwd) {
5504
5592
  if (this.customWorkspacePath) {
5505
- const dir2 = resolve14(this.customWorkspacePath);
5506
- if (!existsSync17(dir2)) {
5593
+ const dir2 = resolve15(this.customWorkspacePath);
5594
+ if (!existsSync18(dir2)) {
5507
5595
  try {
5508
- mkdirSync10(dir2, { recursive: true });
5596
+ mkdirSync11(dir2, { recursive: true });
5509
5597
  } catch {}
5510
5598
  }
5511
5599
  return dir2;
5512
5600
  }
5513
5601
  const slug = this.getProjectSlug(cwd);
5514
- const dir = join9(getProjectsDir(), slug, "memory");
5515
- if (!existsSync17(dir)) {
5602
+ const dir = join10(getProjectsDir(), slug, "memory");
5603
+ if (!existsSync18(dir)) {
5516
5604
  try {
5517
- mkdirSync10(dir, { recursive: true });
5605
+ mkdirSync11(dir, { recursive: true });
5518
5606
  } catch {}
5519
5607
  }
5520
5608
  return dir;
5521
5609
  }
5522
5610
  getMemoryIndexPath(cwd) {
5523
- return join9(this.getProjectMemoryDir(cwd), "MEMORY.md");
5611
+ return join10(this.getProjectMemoryDir(cwd), "MEMORY.md");
5524
5612
  }
5525
5613
  normalizeCategory(raw) {
5526
5614
  const cat = raw.toLowerCase().trim();
@@ -5539,7 +5627,7 @@ class MemoryStore {
5539
5627
  const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
5540
5628
  const memoryDir = this.getProjectMemoryDir(params.cwd);
5541
5629
  const fileName = `${type}_${sanitizedName}.md`;
5542
- const filePath = join9(memoryDir, fileName);
5630
+ const filePath = join10(memoryDir, fileName);
5543
5631
  const nowIso = new Date().toISOString();
5544
5632
  const cleanContent = params.content.trim();
5545
5633
  const desc = (params.description || cleanContent.split(`
@@ -5558,7 +5646,7 @@ class MemoryStore {
5558
5646
  ""
5559
5647
  ].join(`
5560
5648
  `);
5561
- writeFileSync5(filePath, frontmatter, "utf8");
5649
+ writeFileSync6(filePath, frontmatter, "utf8");
5562
5650
  this.syncMemoryIndex(params.cwd);
5563
5651
  return {
5564
5652
  id: `mem_${sanitizedName}`,
@@ -5574,17 +5662,17 @@ class MemoryStore {
5574
5662
  }
5575
5663
  readTopicMemory(topicNameOrFile, cwd) {
5576
5664
  const memoryDir = this.getProjectMemoryDir(cwd);
5577
- let targetPath = join9(memoryDir, topicNameOrFile);
5578
- if (!existsSync17(targetPath)) {
5665
+ let targetPath = join10(memoryDir, topicNameOrFile);
5666
+ if (!existsSync18(targetPath)) {
5579
5667
  if (!topicNameOrFile.endsWith(".md")) {
5580
- targetPath = join9(memoryDir, `${topicNameOrFile}.md`);
5668
+ targetPath = join10(memoryDir, `${topicNameOrFile}.md`);
5581
5669
  }
5582
5670
  }
5583
- if (!existsSync17(targetPath)) {
5671
+ if (!existsSync18(targetPath)) {
5584
5672
  const files = readdirSync7(memoryDir);
5585
5673
  const match = files.find((f) => f.includes(topicNameOrFile));
5586
5674
  if (match) {
5587
- targetPath = join9(memoryDir, match);
5675
+ targetPath = join10(memoryDir, match);
5588
5676
  } else {
5589
5677
  return null;
5590
5678
  }
@@ -5645,12 +5733,12 @@ class MemoryStore {
5645
5733
  }
5646
5734
  syncMemoryIndex(cwd) {
5647
5735
  const memoryDir = this.getProjectMemoryDir(cwd);
5648
- const indexPath = join9(memoryDir, "MEMORY.md");
5649
- const files = existsSync17(memoryDir) ? readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5736
+ const indexPath = join10(memoryDir, "MEMORY.md");
5737
+ const files = existsSync18(memoryDir) ? readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5650
5738
  const items = [];
5651
5739
  for (const f of files) {
5652
5740
  try {
5653
- const full = join9(memoryDir, f);
5741
+ const full = join10(memoryDir, f);
5654
5742
  const parsed = this.parseTopicFile(readFileSync10(full, "utf8"), full);
5655
5743
  items.push({
5656
5744
  type: parsed.type,
@@ -5671,13 +5759,13 @@ class MemoryStore {
5671
5759
  indexLines.push(`- [${item.type}] **${item.name}**: ${item.desc} (topic: ${item.file})`);
5672
5760
  }
5673
5761
  const boundedLines = indexLines.slice(0, 200);
5674
- writeFileSync5(indexPath, boundedLines.join(`
5762
+ writeFileSync6(indexPath, boundedLines.join(`
5675
5763
  `) + `
5676
5764
  `, "utf8");
5677
5765
  }
5678
5766
  loadMemoryIndex(cwd) {
5679
5767
  const indexPath = this.getMemoryIndexPath(cwd);
5680
- if (!existsSync17(indexPath))
5768
+ if (!existsSync18(indexPath))
5681
5769
  return "";
5682
5770
  try {
5683
5771
  const raw = readFileSync10(indexPath, "utf8");
@@ -5693,13 +5781,13 @@ class MemoryStore {
5693
5781
  }
5694
5782
  listProjectMemories(cwd) {
5695
5783
  const memoryDir = this.getProjectMemoryDir(cwd);
5696
- if (!existsSync17(memoryDir))
5784
+ if (!existsSync18(memoryDir))
5697
5785
  return [];
5698
5786
  const files = readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
5699
5787
  const list = [];
5700
5788
  for (const f of files) {
5701
5789
  try {
5702
- const full = join9(memoryDir, f);
5790
+ const full = join10(memoryDir, f);
5703
5791
  list.push(this.parseTopicFile(readFileSync10(full, "utf8"), full));
5704
5792
  } catch {}
5705
5793
  }
@@ -5751,11 +5839,11 @@ class MemoryStore {
5751
5839
  }
5752
5840
 
5753
5841
  // src/worktree/manager.ts
5754
- import { resolve as resolve16, join as join10 } from "path";
5755
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
5842
+ import { resolve as resolve17, join as join11 } from "path";
5843
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync7, readFileSync as readFileSync11 } from "fs";
5756
5844
 
5757
5845
  // src/worktree/git.ts
5758
- import { resolve as resolve15 } from "path";
5846
+ import { resolve as resolve16 } from "path";
5759
5847
  var NON_INHERITABLE_GIT_ENV = [
5760
5848
  "GIT_DIR",
5761
5849
  "GIT_WORK_TREE",
@@ -5799,13 +5887,13 @@ async function getRepoRoot(cwd) {
5799
5887
  const res = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
5800
5888
  if (res.exitCode !== 0 || !res.stdout)
5801
5889
  return null;
5802
- return resolve15(res.stdout);
5890
+ return resolve16(res.stdout);
5803
5891
  }
5804
5892
  async function getMainRepoRoot(cwd) {
5805
5893
  const res = await runGit(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
5806
5894
  if (res.exitCode === 0 && res.stdout) {
5807
- const commonDir = resolve15(cwd, res.stdout);
5808
- return resolve15(commonDir, "..");
5895
+ const commonDir = resolve16(cwd, res.stdout);
5896
+ return resolve16(commonDir, "..");
5809
5897
  }
5810
5898
  return getRepoRoot(cwd);
5811
5899
  }
@@ -5835,7 +5923,7 @@ async function listWorktreesGit(repoRoot) {
5835
5923
  let isLocked = false;
5836
5924
  for (const line of lines) {
5837
5925
  if (line.startsWith("worktree ")) {
5838
- path = resolve15(line.slice(9).trim());
5926
+ path = resolve16(line.slice(9).trim());
5839
5927
  } else if (line.startsWith("HEAD ")) {
5840
5928
  commitHash = line.slice(5).trim();
5841
5929
  } else if (line.startsWith("branch ")) {
@@ -5900,19 +5988,19 @@ class WorktreeManager {
5900
5988
  }
5901
5989
  const taskId = `task_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
5902
5990
  const branchName = options.branch || `groupy/${taskId}`;
5903
- const targetDir = options.worktreePath || (this.baseStorageDir ? resolve16(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve16(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5904
- const worktreeParent = resolve16(targetDir, "..");
5905
- if (!existsSync18(worktreeParent)) {
5906
- mkdirSync11(worktreeParent, { recursive: true });
5991
+ const targetDir = options.worktreePath || (this.baseStorageDir ? resolve17(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve17(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5992
+ const worktreeParent = resolve17(targetDir, "..");
5993
+ if (!existsSync19(worktreeParent)) {
5994
+ mkdirSync12(worktreeParent, { recursive: true });
5907
5995
  }
5908
5996
  const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
5909
5997
  const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
5910
5998
  if (!result.success) {
5911
5999
  throw new Error(`Failed to create git worktree: ${result.error}`);
5912
6000
  }
5913
- const metaPath = join10(targetDir, "groupy-thread.json");
6001
+ const metaPath = join11(targetDir, "groupy-thread.json");
5914
6002
  try {
5915
- writeFileSync6(metaPath, JSON.stringify({
6003
+ writeFileSync7(metaPath, JSON.stringify({
5916
6004
  version: 1,
5917
6005
  ownerThreadId: options.threadId || null,
5918
6006
  branch: branchName,
@@ -5921,7 +6009,7 @@ class WorktreeManager {
5921
6009
  }, null, 2), "utf8");
5922
6010
  } catch {}
5923
6011
  const worktrees = await listWorktreesGit(repoRoot);
5924
- const created = worktrees.find((w) => resolve16(w.path) === resolve16(targetDir));
6012
+ const created = worktrees.find((w) => resolve17(w.path) === resolve17(targetDir));
5925
6013
  return created || {
5926
6014
  path: targetDir,
5927
6015
  branch: branchName,
@@ -5936,8 +6024,8 @@ class WorktreeManager {
5936
6024
  return [];
5937
6025
  const worktrees = await listWorktreesGit(repoRoot);
5938
6026
  return worktrees.map((wt) => {
5939
- const metaPath = join10(wt.path, "groupy-thread.json");
5940
- if (existsSync18(metaPath)) {
6027
+ const metaPath = join11(wt.path, "groupy-thread.json");
6028
+ if (existsSync19(metaPath)) {
5941
6029
  try {
5942
6030
  const raw = JSON.parse(readFileSync11(metaPath, "utf8"));
5943
6031
  return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
@@ -5947,7 +6035,7 @@ class WorktreeManager {
5947
6035
  });
5948
6036
  }
5949
6037
  async mergeWorktree(worktreePath, options = {}) {
5950
- const resolvedPath = resolve16(worktreePath);
6038
+ const resolvedPath = resolve17(worktreePath);
5951
6039
  const repoRoot = await getMainRepoRoot(resolvedPath);
5952
6040
  if (!repoRoot) {
5953
6041
  return { success: false, message: "Worktree path is not a valid git repository." };
@@ -5984,7 +6072,7 @@ class WorktreeManager {
5984
6072
  };
5985
6073
  }
5986
6074
  async removeWorktree(worktreePath, deleteBranch = false) {
5987
- const resolvedPath = resolve16(worktreePath);
6075
+ const resolvedPath = resolve17(worktreePath);
5988
6076
  const repoRoot = await getMainRepoRoot(resolvedPath);
5989
6077
  if (!repoRoot)
5990
6078
  return false;
@@ -6065,8 +6153,8 @@ class AuthClient {
6065
6153
  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`;
6066
6154
  let serverResolve;
6067
6155
  let serverReject;
6068
- const codePromise = new Promise((resolve17, reject) => {
6069
- serverResolve = resolve17;
6156
+ const codePromise = new Promise((resolve18, reject) => {
6157
+ serverResolve = resolve18;
6070
6158
  serverReject = reject;
6071
6159
  });
6072
6160
  if (options.openBrowser !== false) {
@@ -6635,7 +6723,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6635
6723
  // package.json
6636
6724
  var package_default = {
6637
6725
  name: "@pikaa-ai/pikaa",
6638
- version: "0.3.22",
6726
+ version: "0.3.24",
6639
6727
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6640
6728
  main: "./dist/index.js",
6641
6729
  module: "./dist/index.js",
@@ -6651,7 +6739,6 @@ var package_default = {
6651
6739
  "bin",
6652
6740
  "templates",
6653
6741
  "assets",
6654
- "skills/**/SKILL.md",
6655
6742
  "README.md",
6656
6743
  "LICENSE"
6657
6744
  ],
@@ -6713,7 +6800,7 @@ function getCliVersion(options = {}) {
6713
6800
  }
6714
6801
 
6715
6802
  // src/cli/ui/animation/banner-animation.ts
6716
- import { homedir as homedir4 } from "os";
6803
+ import { homedir as homedir3 } from "os";
6717
6804
  import { execSync } from "child_process";
6718
6805
  var ROSE = "\x1B[38;2;205;105;74m";
6719
6806
  var ROSE_DIM = "\x1B[38;2;120;60;45m";
@@ -6723,7 +6810,7 @@ var BOLD = "\x1B[1m";
6723
6810
  var ITALIC = "\x1B[3m";
6724
6811
  var RESET = "\x1B[0m";
6725
6812
  function shortenPath(cwd) {
6726
- const home = homedir4();
6813
+ const home = homedir3();
6727
6814
  if (cwd.startsWith(home)) {
6728
6815
  return `~${cwd.slice(home.length).replace(/\\/g, "/")}`;
6729
6816
  }
@@ -7281,15 +7368,15 @@ class InteractiveLineEditor {
7281
7368
  }
7282
7369
  async readLine() {
7283
7370
  if (!process.stdin.isTTY) {
7284
- return new Promise((resolve17) => {
7371
+ return new Promise((resolve18) => {
7285
7372
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7286
7373
  rl.question(this.promptSymbol, (answer) => {
7287
7374
  rl.close();
7288
- resolve17(answer);
7375
+ resolve18(answer);
7289
7376
  });
7290
7377
  });
7291
7378
  }
7292
- return new Promise((resolve17) => {
7379
+ return new Promise((resolve18) => {
7293
7380
  let buffer = "";
7294
7381
  let cursor = 0;
7295
7382
  let selectedIndex = 0;
@@ -7495,7 +7582,7 @@ class InteractiveLineEditor {
7495
7582
  process.stdout.write(`
7496
7583
  `);
7497
7584
  }
7498
- resolve17(result);
7585
+ resolve18(result);
7499
7586
  };
7500
7587
  const onKeypress = (_str, key) => {
7501
7588
  if (!key)
@@ -7683,7 +7770,7 @@ async function promptChoice(config) {
7683
7770
  const def = choices[defaultIndex] || choices[0];
7684
7771
  return def ? def.value : "";
7685
7772
  }
7686
- return new Promise((resolve17) => {
7773
+ return new Promise((resolve18) => {
7687
7774
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
7688
7775
  const promptText = ` ${message} (${choices.map((c3) => `[${c3.key}] ${c3.label}`).join(", ")}): `;
7689
7776
  rl.question(promptText, (answer) => {
@@ -7691,15 +7778,15 @@ async function promptChoice(config) {
7691
7778
  const trimmed = answer.trim().toLowerCase();
7692
7779
  const match = choices.find((c3) => c3.key.toLowerCase() === trimmed);
7693
7780
  if (match) {
7694
- resolve17(match.value);
7781
+ resolve18(match.value);
7695
7782
  } else {
7696
7783
  const def = choices[defaultIndex] || choices[0];
7697
- resolve17(def ? def.value : "");
7784
+ resolve18(def ? def.value : "");
7698
7785
  }
7699
7786
  });
7700
7787
  });
7701
7788
  }
7702
- return new Promise((resolve17) => {
7789
+ return new Promise((resolve18) => {
7703
7790
  let selectedIndex = defaultIndex;
7704
7791
  if (selectedIndex < 0 || selectedIndex >= choices.length)
7705
7792
  selectedIndex = 0;
@@ -7731,7 +7818,7 @@ async function promptChoice(config) {
7731
7818
  } catch {}
7732
7819
  process.stdout.write(`\r\x1B[1A\r\x1B[2K ${style.bold(message)} ${style.cyan(`[${confirmedChoice.key}] ${confirmedChoice.label}`)}
7733
7820
  \r\x1B[2K`);
7734
- resolve17(confirmedChoice.value);
7821
+ resolve18(confirmedChoice.value);
7735
7822
  };
7736
7823
  const onKeypress = (_str, key) => {
7737
7824
  if (!key)
@@ -7797,7 +7884,7 @@ async function promptToolApproval(params) {
7797
7884
  renderCard(1);
7798
7885
  return "yes";
7799
7886
  }
7800
- return new Promise((resolve17) => {
7887
+ return new Promise((resolve18) => {
7801
7888
  let selectedIndex = 1;
7802
7889
  const wasRaw = process.stdin.isRaw;
7803
7890
  try {
@@ -7817,7 +7904,7 @@ async function promptToolApproval(params) {
7817
7904
  process.stdin.setRawMode(wasRaw ?? false);
7818
7905
  } catch {}
7819
7906
  console.log();
7820
- resolve17(val);
7907
+ resolve18(val);
7821
7908
  };
7822
7909
  const onKeypress = (_str, key) => {
7823
7910
  if (!key)
@@ -7900,7 +7987,7 @@ async function promptUserQuestion(params) {
7900
7987
  if (!process.stdin.isTTY || false || !process.stdin.readable) {
7901
7988
  return options[0] || "yes";
7902
7989
  }
7903
- return new Promise((resolve17) => {
7990
+ return new Promise((resolve18) => {
7904
7991
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
7905
7992
  const promptLabel = options.length > 0 ? `Select [1-${options.length}] or type custom response: ` : `Your response: `;
7906
7993
  rl.question(` ${style.bold(promptLabel)}`, (answer) => {
@@ -7910,7 +7997,7 @@ async function promptUserQuestion(params) {
7910
7997
  const fallback = options[0] || "";
7911
7998
  console.log(style.dim(` \u21B3 Default: ${fallback || "(empty)"}
7912
7999
  `));
7913
- resolve17(fallback);
8000
+ resolve18(fallback);
7914
8001
  return;
7915
8002
  }
7916
8003
  const num = parseInt(trimmed, 10);
@@ -7918,26 +8005,26 @@ async function promptUserQuestion(params) {
7918
8005
  const picked = options[num - 1];
7919
8006
  console.log(style.green(` \u2714 Selected: ${picked}
7920
8007
  `));
7921
- resolve17(picked);
8008
+ resolve18(picked);
7922
8009
  return;
7923
8010
  }
7924
8011
  if (options.length === 2) {
7925
8012
  if (/^(y|yes)$/i.test(trimmed)) {
7926
8013
  console.log(style.green(` \u2714 Selected: ${options[0]}
7927
8014
  `));
7928
- resolve17(options[0]);
8015
+ resolve18(options[0]);
7929
8016
  return;
7930
8017
  }
7931
8018
  if (/^(n|no)$/i.test(trimmed)) {
7932
8019
  console.log(style.green(` \u2714 Selected: ${options[1]}
7933
8020
  `));
7934
- resolve17(options[1]);
8021
+ resolve18(options[1]);
7935
8022
  return;
7936
8023
  }
7937
8024
  }
7938
8025
  console.log(style.green(` \u2714 Answer: ${trimmed}
7939
8026
  `));
7940
- resolve17(trimmed);
8027
+ resolve18(trimmed);
7941
8028
  });
7942
8029
  });
7943
8030
  }
@@ -7966,7 +8053,7 @@ async function promptInteractiveList(config) {
7966
8053
  `);
7967
8054
  return { selectedIndex: -1, action: "close" };
7968
8055
  }
7969
- return new Promise((resolve17) => {
8056
+ return new Promise((resolve18) => {
7970
8057
  let selectedIndex = Math.max(0, Math.min(defaultIndex, items.length - 1));
7971
8058
  let scrollTop = 0;
7972
8059
  let renderedLines = 0;
@@ -8061,7 +8148,7 @@ async function promptInteractiveList(config) {
8061
8148
  try {
8062
8149
  process.stdin.setRawMode(wasRaw ?? false);
8063
8150
  } catch {}
8064
- resolve17(res);
8151
+ resolve18(res);
8065
8152
  };
8066
8153
  const onKeypress = async (_str, key) => {
8067
8154
  if (!key)
@@ -8132,8 +8219,8 @@ async function promptInteractiveList(config) {
8132
8219
  }
8133
8220
 
8134
8221
  // src/security/scanner.ts
8135
- import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8136
- import { join as join11, relative as relative2, resolve as resolve17 } from "path";
8222
+ import { existsSync as existsSync20, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8223
+ import { join as join12, relative as relative2, resolve as resolve18 } from "path";
8137
8224
  var SECURITY_RULES = [
8138
8225
  {
8139
8226
  id: "SEC-001",
@@ -8265,13 +8352,13 @@ var IGNORED_FILES = new Set([
8265
8352
  ]);
8266
8353
  async function runSecurityScan(targetDir, options = {}) {
8267
8354
  const startTime = performance.now();
8268
- const root = resolve17(targetDir);
8355
+ const root = resolve18(targetDir);
8269
8356
  const isDirectTestDir = targetDir.includes("test") || Boolean(options.includeTests);
8270
8357
  const maxFiles = options.maxFiles || 2000;
8271
8358
  const findings = [];
8272
8359
  let scannedCount = 0;
8273
8360
  function walk(current) {
8274
- if (scannedCount >= maxFiles || !existsSync19(current))
8361
+ if (scannedCount >= maxFiles || !existsSync20(current))
8275
8362
  return;
8276
8363
  let entries;
8277
8364
  try {
@@ -8282,7 +8369,7 @@ async function runSecurityScan(targetDir, options = {}) {
8282
8369
  for (const entry of entries) {
8283
8370
  if (scannedCount >= maxFiles)
8284
8371
  break;
8285
- const fullPath = join11(current, entry);
8372
+ const fullPath = join12(current, entry);
8286
8373
  let stat;
8287
8374
  try {
8288
8375
  stat = statSync5(fullPath);
@@ -8360,38 +8447,38 @@ async function runSecurityScan(targetDir, options = {}) {
8360
8447
  }
8361
8448
 
8362
8449
  // src/mcp/servers/chrome-devtools/index.ts
8363
- import { resolve as resolve18 } from "path";
8450
+ import { resolve as resolve19 } from "path";
8364
8451
 
8365
8452
  // src/mcp/servers/chrome-devtools/server.ts
8366
8453
  if (false) {}
8367
8454
 
8368
8455
  // src/mcp/servers/chrome-devtools/index.ts
8369
8456
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/chrome-devtools";
8370
- var CHROME_DEVTOOLS_MCP_SERVER_PATH = resolve18(__dirname, "server.ts");
8457
+ var CHROME_DEVTOOLS_MCP_SERVER_PATH = resolve19(__dirname, "server.ts");
8371
8458
 
8372
8459
  // src/mcp/servers/web-search/index.ts
8373
- import { resolve as resolve19 } from "path";
8460
+ import { resolve as resolve20 } from "path";
8374
8461
 
8375
8462
  // src/mcp/servers/web-search/server.ts
8376
8463
  if (false) {}
8377
8464
 
8378
8465
  // src/mcp/servers/web-search/index.ts
8379
8466
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/web-search";
8380
- var WEB_SEARCH_MCP_SERVER_PATH = resolve19(__dirname, "server.ts");
8467
+ var WEB_SEARCH_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
8381
8468
 
8382
8469
  // src/mcp/servers/sqlite/index.ts
8383
- import { resolve as resolve20 } from "path";
8470
+ import { resolve as resolve21 } from "path";
8384
8471
 
8385
8472
  // src/mcp/servers/sqlite/server.ts
8386
8473
  if (false) {}
8387
8474
 
8388
8475
  // src/mcp/servers/sqlite/index.ts
8389
8476
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
8390
- var SQLITE_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
8477
+ var SQLITE_MCP_SERVER_PATH = resolve21(__dirname, "server.ts");
8391
8478
 
8392
8479
  // src/init/project-analyzer.ts
8393
- import { existsSync as existsSync20, readFileSync as readFileSync13, readdirSync as readdirSync9 } from "fs";
8394
- import { join as join12, basename as basename3 } from "path";
8480
+ import { existsSync as existsSync21, readFileSync as readFileSync13, readdirSync as readdirSync9 } from "fs";
8481
+ import { join as join13, basename as basename3 } from "path";
8395
8482
 
8396
8483
  class ProjectAnalyzer {
8397
8484
  cwd;
@@ -8409,8 +8496,8 @@ class ProjectAnalyzer {
8409
8496
  const architectureNotes = [];
8410
8497
  const codeConventions = [];
8411
8498
  let description = readmeInfo.description;
8412
- const pkgPath = join12(this.cwd, "package.json");
8413
- if (existsSync20(pkgPath)) {
8499
+ const pkgPath = join13(this.cwd, "package.json");
8500
+ if (existsSync21(pkgPath)) {
8414
8501
  try {
8415
8502
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8416
8503
  if (!description && pkg.description)
@@ -8483,8 +8570,8 @@ class ProjectAnalyzer {
8483
8570
  }
8484
8571
  } catch {}
8485
8572
  }
8486
- const tsconfigPath = join12(this.cwd, "tsconfig.json");
8487
- if (existsSync20(tsconfigPath)) {
8573
+ const tsconfigPath = join13(this.cwd, "tsconfig.json");
8574
+ if (existsSync21(tsconfigPath)) {
8488
8575
  try {
8489
8576
  const tsconfig = JSON.parse(readFileSync13(tsconfigPath, "utf8"));
8490
8577
  if (tsconfig.compilerOptions?.strict) {
@@ -8495,8 +8582,8 @@ class ProjectAnalyzer {
8495
8582
  }
8496
8583
  } catch {}
8497
8584
  }
8498
- const cargoPath = join12(this.cwd, "Cargo.toml");
8499
- if (existsSync20(cargoPath)) {
8585
+ const cargoPath = join13(this.cwd, "Cargo.toml");
8586
+ if (existsSync21(cargoPath)) {
8500
8587
  try {
8501
8588
  commands.dev = commands.dev || "cargo run";
8502
8589
  commands.build = commands.build || "cargo build";
@@ -8505,8 +8592,8 @@ class ProjectAnalyzer {
8505
8592
  frameworks.push("Rust Cargo");
8506
8593
  } catch {}
8507
8594
  }
8508
- const goModPath = join12(this.cwd, "go.mod");
8509
- if (existsSync20(goModPath)) {
8595
+ const goModPath = join13(this.cwd, "go.mod");
8596
+ if (existsSync21(goModPath)) {
8510
8597
  try {
8511
8598
  commands.dev = commands.dev || "go run .";
8512
8599
  commands.build = commands.build || "go build ./...";
@@ -8515,37 +8602,37 @@ class ProjectAnalyzer {
8515
8602
  frameworks.push("Go Modules");
8516
8603
  } catch {}
8517
8604
  }
8518
- const pyprojectPath = join12(this.cwd, "pyproject.toml");
8519
- const requirementsPath = join12(this.cwd, "requirements.txt");
8520
- if (existsSync20(pyprojectPath) || existsSync20(requirementsPath)) {
8605
+ const pyprojectPath = join13(this.cwd, "pyproject.toml");
8606
+ const requirementsPath = join13(this.cwd, "requirements.txt");
8607
+ if (existsSync21(pyprojectPath) || existsSync21(requirementsPath)) {
8521
8608
  commands.test = commands.test || "pytest";
8522
8609
  commands.lint = commands.lint || "ruff check .";
8523
- if (existsSync20(join12(this.cwd, "uv.lock"))) {
8610
+ if (existsSync21(join13(this.cwd, "uv.lock"))) {
8524
8611
  frameworks.push("uv");
8525
8612
  commands.test = "uv run pytest";
8526
- } else if (existsSync20(join12(this.cwd, "poetry.lock"))) {
8613
+ } else if (existsSync21(join13(this.cwd, "poetry.lock"))) {
8527
8614
  frameworks.push("Poetry");
8528
8615
  commands.test = "poetry run pytest";
8529
8616
  }
8530
8617
  }
8531
- if (existsSync20(join12(this.cwd, "Dockerfile"))) {
8618
+ if (existsSync21(join13(this.cwd, "Dockerfile"))) {
8532
8619
  infrastructure.push("Docker");
8533
8620
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
8534
8621
  commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
8535
8622
  }
8536
- if (existsSync20(join12(this.cwd, "nginx.conf"))) {
8623
+ if (existsSync21(join13(this.cwd, "nginx.conf"))) {
8537
8624
  infrastructure.push("Nginx");
8538
8625
  }
8539
- if (existsSync20(join12(this.cwd, "src/api.ts")) || existsSync20(join12(this.cwd, "src/api"))) {
8626
+ if (existsSync21(join13(this.cwd, "src/api.ts")) || existsSync21(join13(this.cwd, "src/api"))) {
8540
8627
  architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
8541
8628
  }
8542
- if (existsSync20(join12(this.cwd, "src/components"))) {
8629
+ if (existsSync21(join13(this.cwd, "src/components"))) {
8543
8630
  architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
8544
8631
  }
8545
- if (existsSync20(join12(this.cwd, "src/types.ts")) || existsSync20(join12(this.cwd, "src/types"))) {
8632
+ if (existsSync21(join13(this.cwd, "src/types.ts")) || existsSync21(join13(this.cwd, "src/types"))) {
8546
8633
  architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
8547
8634
  }
8548
- if (existsSync20(join12(this.cwd, ".env.example"))) {
8635
+ if (existsSync21(join13(this.cwd, ".env.example"))) {
8549
8636
  architectureNotes.push("Environment configuration template is in `.env.example`.");
8550
8637
  }
8551
8638
  if (commands.typecheck || commands.lint || commands.test) {
@@ -8562,7 +8649,7 @@ class ProjectAnalyzer {
8562
8649
  let hasExistingInstructions = false;
8563
8650
  let existingInstructionFile;
8564
8651
  for (const f of instructionFiles) {
8565
- if (existsSync20(join12(this.cwd, f))) {
8652
+ if (existsSync21(join13(this.cwd, f))) {
8566
8653
  hasExistingInstructions = true;
8567
8654
  existingInstructionFile = f;
8568
8655
  break;
@@ -8643,8 +8730,8 @@ class ProjectAnalyzer {
8643
8730
  extractReadmeMetadata() {
8644
8731
  const readmeFiles = ["README.md", "readme.md", "README.MD"];
8645
8732
  for (const file of readmeFiles) {
8646
- const fullPath = join12(this.cwd, file);
8647
- if (existsSync20(fullPath)) {
8733
+ const fullPath = join13(this.cwd, file);
8734
+ if (existsSync21(fullPath)) {
8648
8735
  try {
8649
8736
  const content = readFileSync13(fullPath, "utf8");
8650
8737
  const lines = content.split(`
@@ -8669,8 +8756,8 @@ class ProjectAnalyzer {
8669
8756
  return {};
8670
8757
  }
8671
8758
  detectProjectName() {
8672
- const pkgPath = join12(this.cwd, "package.json");
8673
- if (existsSync20(pkgPath)) {
8759
+ const pkgPath = join13(this.cwd, "package.json");
8760
+ if (existsSync21(pkgPath)) {
8674
8761
  try {
8675
8762
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8676
8763
  if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
@@ -8678,16 +8765,16 @@ class ProjectAnalyzer {
8678
8765
  }
8679
8766
  } catch {}
8680
8767
  }
8681
- const cargoPath = join12(this.cwd, "Cargo.toml");
8682
- if (existsSync20(cargoPath)) {
8768
+ const cargoPath = join13(this.cwd, "Cargo.toml");
8769
+ if (existsSync21(cargoPath)) {
8683
8770
  try {
8684
8771
  const match = readFileSync13(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
8685
8772
  if (match?.[1])
8686
8773
  return match[1];
8687
8774
  } catch {}
8688
8775
  }
8689
- const goModPath = join12(this.cwd, "go.mod");
8690
- if (existsSync20(goModPath)) {
8776
+ const goModPath = join13(this.cwd, "go.mod");
8777
+ if (existsSync21(goModPath)) {
8691
8778
  try {
8692
8779
  const match = readFileSync13(goModPath, "utf8").match(/module\s+([^\s]+)/);
8693
8780
  if (match?.[1])
@@ -8698,47 +8785,47 @@ class ProjectAnalyzer {
8698
8785
  }
8699
8786
  detectLanguages() {
8700
8787
  const langs = new Set;
8701
- if (existsSync20(join12(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8788
+ if (existsSync21(join13(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8702
8789
  langs.add("TypeScript");
8703
8790
  }
8704
- if (existsSync20(join12(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8791
+ if (existsSync21(join13(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8705
8792
  langs.add("JavaScript");
8706
8793
  }
8707
- if (existsSync20(join12(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8794
+ if (existsSync21(join13(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8708
8795
  langs.add("Rust");
8709
8796
  }
8710
- if (existsSync20(join12(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8797
+ if (existsSync21(join13(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8711
8798
  langs.add("Go");
8712
8799
  }
8713
- if (existsSync20(join12(this.cwd, "pyproject.toml")) || existsSync20(join12(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8800
+ if (existsSync21(join13(this.cwd, "pyproject.toml")) || existsSync21(join13(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8714
8801
  langs.add("Python");
8715
8802
  }
8716
- if (existsSync20(join12(this.cwd, "pom.xml")) || existsSync20(join12(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8803
+ if (existsSync21(join13(this.cwd, "pom.xml")) || existsSync21(join13(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8717
8804
  langs.add("Java");
8718
8805
  }
8719
- if (existsSync20(join12(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8806
+ if (existsSync21(join13(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8720
8807
  langs.add("C/C++");
8721
8808
  }
8722
8809
  return Array.from(langs);
8723
8810
  }
8724
8811
  detectPackageManager() {
8725
- if (existsSync20(join12(this.cwd, "bun.lockb")) || existsSync20(join12(this.cwd, "bun.lock")))
8812
+ if (existsSync21(join13(this.cwd, "bun.lockb")) || existsSync21(join13(this.cwd, "bun.lock")))
8726
8813
  return "bun";
8727
- if (existsSync20(join12(this.cwd, "pnpm-lock.yaml")))
8814
+ if (existsSync21(join13(this.cwd, "pnpm-lock.yaml")))
8728
8815
  return "pnpm";
8729
- if (existsSync20(join12(this.cwd, "yarn.lock")))
8816
+ if (existsSync21(join13(this.cwd, "yarn.lock")))
8730
8817
  return "yarn";
8731
- if (existsSync20(join12(this.cwd, "package-lock.json")))
8818
+ if (existsSync21(join13(this.cwd, "package-lock.json")))
8732
8819
  return "npm";
8733
- if (existsSync20(join12(this.cwd, "Cargo.lock")) || existsSync20(join12(this.cwd, "Cargo.toml")))
8820
+ if (existsSync21(join13(this.cwd, "Cargo.lock")) || existsSync21(join13(this.cwd, "Cargo.toml")))
8734
8821
  return "cargo";
8735
- if (existsSync20(join12(this.cwd, "uv.lock")))
8822
+ if (existsSync21(join13(this.cwd, "uv.lock")))
8736
8823
  return "uv";
8737
- if (existsSync20(join12(this.cwd, "poetry.lock")))
8824
+ if (existsSync21(join13(this.cwd, "poetry.lock")))
8738
8825
  return "poetry";
8739
- if (existsSync20(join12(this.cwd, "go.sum")) || existsSync20(join12(this.cwd, "go.mod")))
8826
+ if (existsSync21(join13(this.cwd, "go.sum")) || existsSync21(join13(this.cwd, "go.mod")))
8740
8827
  return "go";
8741
- if (existsSync20(join12(this.cwd, "package.json")))
8828
+ if (existsSync21(join13(this.cwd, "package.json")))
8742
8829
  return "npm";
8743
8830
  return;
8744
8831
  }
@@ -8752,17 +8839,17 @@ class ProjectAnalyzer {
8752
8839
  }
8753
8840
  }
8754
8841
  // src/init/init-command.ts
8755
- import { existsSync as existsSync21, writeFileSync as writeFileSync7 } from "fs";
8756
- import { join as join13 } from "path";
8842
+ import { existsSync as existsSync22, writeFileSync as writeFileSync8 } from "fs";
8843
+ import { join as join14 } from "path";
8757
8844
  function runProjectInit(options = {}) {
8758
8845
  const cwd = options.cwd || process.cwd();
8759
8846
  const filename = options.filename || "AGENTS.md";
8760
- const targetPath = join13(cwd, filename);
8847
+ const targetPath = join14(cwd, filename);
8761
8848
  const analyzer = new ProjectAnalyzer(cwd);
8762
8849
  const analysis = analyzer.analyze();
8763
8850
  const content = analyzer.generateAgentsMarkdown(analysis);
8764
- const alreadyExists = existsSync21(targetPath);
8765
- writeFileSync7(targetPath, content, "utf8");
8851
+ const alreadyExists = existsSync22(targetPath);
8852
+ writeFileSync8(targetPath, content, "utf8");
8766
8853
  return {
8767
8854
  success: true,
8768
8855
  filePath: targetPath,
@@ -8834,10 +8921,11 @@ var AVAILABLE_SLASH_COMMANDS = [
8834
8921
  { name: "/manual", description: "Switch to Manual Mode (all tools require user confirmation)" },
8835
8922
  { name: "/accept-edits", description: "Switch to Accept Edits Mode (file edits auto-approved, shell prompts)" },
8836
8923
  { name: "/plan", description: "Switch to Plan Mode (read-only planning, mutations blocked)" },
8837
- { name: "/skills", description: "List domain skills in workspace & global" },
8924
+ { name: "/skills", description: "Manage domain skills (/skills, /skill add <name>, /skill remove <name>)" },
8838
8925
  { name: "/memories", description: "View learned preferences & memories" },
8839
8926
  { name: "/worktrees", description: "List active isolated Git Worktrees" },
8840
- { name: "/sessions", description: "List saved past sessions from SQLite store" },
8927
+ { name: "/resume", description: "Resume a previous conversation session (/resume [id])" },
8928
+ { name: "/sessions", description: "List and manage saved past sessions" },
8841
8929
  { name: "/role", description: "Select and inspect specialized agent roles & personas" },
8842
8930
  { name: "/security", description: "Scan codebase for vulnerabilities & secrets (Strix)" },
8843
8931
  { name: "/agents", description: "List active sub-agents & execution status" },
@@ -8945,9 +9033,10 @@ async function handleSlashCommand(input, ctx) {
8945
9033
  case "/worktree":
8946
9034
  await printWorktrees(ctx);
8947
9035
  return true;
9036
+ case "/resume":
8948
9037
  case "/sessions":
8949
9038
  case "/session":
8950
- await printSessions(ctx);
9039
+ await handleResumeCommand(ctx, args);
8951
9040
  return true;
8952
9041
  case "/security":
8953
9042
  case "/audit":
@@ -9335,6 +9424,46 @@ async function handleSkillsCommand(ctx, args) {
9335
9424
  }
9336
9425
  const sub = (args[0] || "").toLowerCase();
9337
9426
  const target = (args[1] || "").toLowerCase();
9427
+ if (sub === "install" || sub === "add") {
9428
+ if (!target) {
9429
+ console.log(style.yellow("Usage: /skill add <skill-name> [--global]"));
9430
+ return;
9431
+ }
9432
+ const isGlobal = args.includes("--global") || args.includes("-g");
9433
+ try {
9434
+ console.log(style.dim(`
9435
+ Fetching skill '${target}' from catalog...`));
9436
+ const res = await installSkill(target, { cwd: ctx.session.cwd, global: isGlobal });
9437
+ loader.clearCache();
9438
+ console.log(style.green(`
9439
+ \u2713 ${res.message}
9440
+ `));
9441
+ } catch (err) {
9442
+ console.log(style.red(`
9443
+ \u2715 Failed to install skill: ${err.message}
9444
+ `));
9445
+ }
9446
+ return;
9447
+ }
9448
+ if (sub === "remove" || sub === "uninstall" || sub === "rm") {
9449
+ if (!target) {
9450
+ console.log(style.yellow("Usage: /skill remove <skill-name> [--global]"));
9451
+ return;
9452
+ }
9453
+ const isGlobal = args.includes("--global") || args.includes("-g");
9454
+ const res = removeSkill(target, { cwd: ctx.session.cwd, global: isGlobal });
9455
+ loader.clearCache();
9456
+ if (res.removed) {
9457
+ console.log(style.green(`
9458
+ \u2713 Skill '${target}' removed from ${res.targetDir}
9459
+ `));
9460
+ } else {
9461
+ console.log(style.yellow(`
9462
+ \u2715 Skill '${target}' was not found in ${res.targetDir}
9463
+ `));
9464
+ }
9465
+ return;
9466
+ }
9338
9467
  if (sub === "disable" || sub === "off") {
9339
9468
  if (!target) {
9340
9469
  console.log(style.yellow("Usage: /skills disable <skill-name>"));
@@ -9482,41 +9611,104 @@ function printMemories(ctx) {
9482
9611
  }
9483
9612
  console.log();
9484
9613
  }
9485
- async function printSessions(ctx) {
9614
+ function formatRelativeTime(timestamp) {
9615
+ const diffMs = Math.max(0, Date.now() - timestamp);
9616
+ const diffSec = Math.floor(diffMs / 1000);
9617
+ if (diffSec < 60)
9618
+ return "just now";
9619
+ const diffMin = Math.floor(diffSec / 60);
9620
+ if (diffMin < 60)
9621
+ return `${diffMin}m ago`;
9622
+ const diffHours = Math.floor(diffMin / 60);
9623
+ if (diffHours < 24)
9624
+ return `${diffHours}h ago`;
9625
+ const diffDays = Math.floor(diffHours / 24);
9626
+ if (diffDays < 30)
9627
+ return `${diffDays}d ago`;
9628
+ return new Date(timestamp).toLocaleDateString();
9629
+ }
9630
+ function printRecentConversation(items) {
9631
+ if (!items || items.length === 0)
9632
+ return;
9633
+ const dialog = items.filter((i) => i.type === "user_message" || i.type === "agent_message" || i.role === "user" || i.role === "assistant").slice(-3);
9634
+ if (dialog.length === 0)
9635
+ return;
9636
+ console.log(style.dim(" Recent context:"));
9637
+ for (const item of dialog) {
9638
+ const isUser = item.role === "user" || item.type === "user_message";
9639
+ const text = typeof item.content === "string" ? item.content : item.text || "";
9640
+ const singleLine = text.trim().replace(/\s+/g, " ");
9641
+ const preview = singleLine.length > 85 ? singleLine.slice(0, 82) + "..." : singleLine;
9642
+ const badge = isUser ? style.brand(" \u276F User:") : style.cyan(" \u25CF Assistant:");
9643
+ console.log(`${badge} ${style.dim(preview)}`);
9644
+ }
9645
+ console.log();
9646
+ }
9647
+ async function handleResumeCommand(ctx, args = []) {
9486
9648
  const storage = ctx.storageManager;
9487
9649
  if (!storage) {
9488
- console.log(style.yellow("Session persistence manager not active."));
9650
+ console.log(style.yellow(`
9651
+ Session persistence manager not active.
9652
+ `));
9489
9653
  return;
9490
9654
  }
9491
- const threads = storage.listSessions();
9492
- if (threads.length === 0) {
9655
+ const rawThreads = storage.listSessions();
9656
+ if (rawThreads.length === 0) {
9493
9657
  console.log(style.dim(`
9494
- No saved sessions in SQLite database.
9658
+ No saved conversation sessions found in SQLite database.
9495
9659
  `));
9496
9660
  return;
9497
9661
  }
9662
+ const threads = [...rawThreads].sort((a, b) => b.updatedAt - a.updatedAt);
9663
+ const filterArg = args[0]?.trim();
9664
+ let defaultIdx = 0;
9665
+ if (filterArg) {
9666
+ const foundIdx = threads.findIndex((t) => t.id === filterArg || t.id.toLowerCase().startsWith(filterArg.toLowerCase()));
9667
+ if (foundIdx !== -1) {
9668
+ defaultIdx = foundIdx;
9669
+ }
9670
+ }
9498
9671
  if (!process.stdin.isTTY || false || !process.stdin.readable) {
9672
+ if (filterArg) {
9673
+ const match = threads[defaultIdx];
9674
+ const restored = storage.resumeIntoSession(ctx.session, match.id);
9675
+ if (restored) {
9676
+ console.log(style.green(`
9677
+ \u2713 Resumed past session: ${style.bold(match.id)} (${restored.items.length} items, model: ${restored.thread.model || ctx.session.model})
9678
+ `));
9679
+ printRecentConversation(restored.items);
9680
+ } else {
9681
+ console.log(style.red(`
9682
+ \u2715 Failed to load session '${match.id}'.
9683
+ `));
9684
+ }
9685
+ return;
9686
+ }
9499
9687
  console.log();
9500
9688
  console.log(style.bold(" Saved Sessions in SQLite Store:"));
9501
9689
  for (const t of threads) {
9502
- const dateStr = new Date(t.updatedAt).toLocaleString();
9503
- console.log(` \u2022 ${style.cyan(t.id)} [${style.dim(t.model)}] - ${style.dim(dateStr)} (${t.itemsCount} items)`);
9690
+ const timeStr = formatRelativeTime(t.updatedAt);
9691
+ const titleStr = t.title && t.title !== "New Session" ? ` - "${t.title}"` : "";
9692
+ console.log(` \u2022 ${style.cyan(t.id)} [${style.dim(t.model)}]${titleStr} - ${style.dim(timeStr)} (${t.itemsCount ?? 0} items)`);
9504
9693
  }
9505
9694
  console.log();
9506
9695
  return;
9507
9696
  }
9508
9697
  const items = threads.map((t) => {
9509
- const dateStr = new Date(t.updatedAt).toLocaleString();
9698
+ const timeStr = formatRelativeTime(t.updatedAt);
9699
+ const titlePart = t.title && t.title !== "New Session" ? `"${t.title.slice(0, 36)}"` : undefined;
9510
9700
  return {
9511
9701
  id: t.id,
9512
9702
  label: t.id,
9513
- description: `${t.model} \xB7 ${t.itemsCount} items \xB7 ${dateStr}`
9703
+ badge: t.model,
9704
+ description: [titlePart, `${t.itemsCount ?? 0} items`, timeStr].filter(Boolean).join(" \xB7 ")
9514
9705
  };
9515
9706
  });
9516
9707
  const res = await promptInteractiveList({
9517
- title: `\uD83D\uDDC4\uFE0F Saved SQLite Sessions (${threads.length} total)`,
9708
+ title: `\uD83D\uDDC4\uFE0F Resume Past Conversation (${threads.length} saved)`,
9518
9709
  items,
9519
9710
  mode: "select",
9711
+ defaultIndex: defaultIdx,
9520
9712
  onAction: (key, item, idx) => {
9521
9713
  if (key === "d") {
9522
9714
  storage.deleteSession(item.id);
@@ -9526,18 +9718,24 @@ async function printSessions(ctx) {
9526
9718
  }
9527
9719
  return false;
9528
9720
  },
9529
- customKeyHints: "\u2191/\u2193: navigate \xB7 Enter: resume session \xB7 d: delete \xB7 Esc: exit"
9721
+ customKeyHints: "\u2191/\u2193: navigate \xB7 Enter: resume conversation \xB7 d: delete \xB7 Esc: cancel"
9530
9722
  });
9531
9723
  if (res.action === "select" && res.selectedItem) {
9532
- const session = storage.loadSession(res.selectedItem.id);
9533
- if (session) {
9534
- ctx.session.setHistory(session.items);
9535
- if (session.thread.model)
9536
- ctx.session.model = session.thread.model;
9724
+ const restored = storage.resumeIntoSession(ctx.session, res.selectedItem.id);
9725
+ if (restored) {
9537
9726
  console.log(style.green(`
9538
- \u2713 Resumed past session: ${style.bold(res.selectedItem.id)} (${session.items.length} items)
9727
+ \u2713 Resumed past conversation: ${style.bold(res.selectedItem.id)} (${restored.items.length} items, model: ${restored.thread.model || ctx.session.model})
9728
+ `));
9729
+ printRecentConversation(restored.items);
9730
+ } else {
9731
+ console.log(style.red(`
9732
+ \u2715 Failed to load session '${res.selectedItem.id}'.
9539
9733
  `));
9540
9734
  }
9735
+ } else {
9736
+ console.log(style.dim(`
9737
+ Session resume cancelled.
9738
+ `));
9541
9739
  }
9542
9740
  }
9543
9741
  function printSessionStats(ctx) {
@@ -10236,9 +10434,9 @@ class MarkdownHighlighter {
10236
10434
  }
10237
10435
 
10238
10436
  // src/cli/update-checker.ts
10239
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
10240
- import { homedir as homedir5 } from "os";
10241
- import { join as join14 } from "path";
10437
+ import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
10438
+ import { homedir as homedir4 } from "os";
10439
+ import { join as join15 } from "path";
10242
10440
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
10243
10441
  function parseSemver(v) {
10244
10442
  const clean = v.replace(/^v/, "").trim();
@@ -10259,8 +10457,8 @@ function isNewerVersion(current, remote) {
10259
10457
  return remPatch > curPatch;
10260
10458
  }
10261
10459
  function getUpdateCachePath() {
10262
- const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join14(homedir5(), ".pikaa");
10263
- return join14(baseDir, "update-cache.json");
10460
+ const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join15(homedir4(), ".pikaa");
10461
+ return join15(baseDir, "update-cache.json");
10264
10462
  }
10265
10463
  async function fetchLatestNpmVersion(packageName, timeoutMs = 1500) {
10266
10464
  const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
@@ -10293,7 +10491,7 @@ async function checkForUpdates(options = {}) {
10293
10491
  const cachePath = options.cachePath || getUpdateCachePath();
10294
10492
  const now = Date.now();
10295
10493
  let cached = null;
10296
- if (!options.force && existsSync22(cachePath)) {
10494
+ if (!options.force && existsSync23(cachePath)) {
10297
10495
  try {
10298
10496
  const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
10299
10497
  if (raw && typeof raw.lastChecked === "number" && typeof raw.latestVersion === "string") {
@@ -10323,16 +10521,16 @@ async function checkForUpdates(options = {}) {
10323
10521
  return null;
10324
10522
  }
10325
10523
  try {
10326
- const parentDir = join14(cachePath, "..");
10327
- if (!existsSync22(parentDir)) {
10328
- mkdirSync12(parentDir, { recursive: true });
10524
+ const parentDir = join15(cachePath, "..");
10525
+ if (!existsSync23(parentDir)) {
10526
+ mkdirSync13(parentDir, { recursive: true });
10329
10527
  }
10330
10528
  const cacheData = {
10331
10529
  lastChecked: now,
10332
10530
  latestVersion,
10333
10531
  packageName
10334
10532
  };
10335
- writeFileSync8(cachePath, JSON.stringify(cacheData, null, 2), "utf8");
10533
+ writeFileSync9(cachePath, JSON.stringify(cacheData, null, 2), "utf8");
10336
10534
  } catch {}
10337
10535
  const updateAvailable = isNewerVersion(currentVersion, latestVersion);
10338
10536
  return updateAvailable ? {
@@ -10498,9 +10696,9 @@ class CliRepl {
10498
10696
  filesModified: Array.from(this.turnFilesModified)
10499
10697
  });
10500
10698
  if (this.turnDoneResolver) {
10501
- const resolve21 = this.turnDoneResolver;
10699
+ const resolve22 = this.turnDoneResolver;
10502
10700
  this.turnDoneResolver = undefined;
10503
- resolve21();
10701
+ resolve22();
10504
10702
  }
10505
10703
  break;
10506
10704
  case "Error":
@@ -10516,9 +10714,9 @@ class CliRepl {
10516
10714
  console.error(style.red(`Error: ${msg.message}
10517
10715
  `));
10518
10716
  if (this.turnDoneResolver) {
10519
- const resolve21 = this.turnDoneResolver;
10717
+ const resolve22 = this.turnDoneResolver;
10520
10718
  this.turnDoneResolver = undefined;
10521
- resolve21();
10719
+ resolve22();
10522
10720
  }
10523
10721
  break;
10524
10722
  }
@@ -10588,9 +10786,9 @@ class CliRepl {
10588
10786
  `));
10589
10787
  this.isProcessing = false;
10590
10788
  if (this.turnDoneResolver) {
10591
- const resolve21 = this.turnDoneResolver;
10789
+ const resolve22 = this.turnDoneResolver;
10592
10790
  this.turnDoneResolver = undefined;
10593
- resolve21();
10791
+ resolve22();
10594
10792
  }
10595
10793
  }
10596
10794
  }
@@ -10627,8 +10825,8 @@ class CliRepl {
10627
10825
  }
10628
10826
  async submitTurn(text) {
10629
10827
  try {
10630
- const turnPromise = new Promise((resolve21) => {
10631
- this.turnDoneResolver = resolve21;
10828
+ const turnPromise = new Promise((resolve22) => {
10829
+ this.turnDoneResolver = resolve22;
10632
10830
  });
10633
10831
  await this.session.submit({
10634
10832
  type: "TurnInput",
@@ -10694,9 +10892,22 @@ async function main() {
10694
10892
  } else if (arg === "list" || arg === "sessions" || arg === "--list") {
10695
10893
  printSessionsList(storageManager);
10696
10894
  process.exit(0);
10697
- } else if (arg === "skills") {
10698
- printSkillsList(skillsLoader, cwd);
10699
- process.exit(0);
10895
+ } else if (arg === "skills" || arg === "skill") {
10896
+ const subAction = args[i + 1];
10897
+ const target = args[i + 2];
10898
+ const isGlobal = args.includes("--global") || args.includes("-g");
10899
+ if (subAction && !subAction.startsWith("-")) {
10900
+ await handleSkillCliCommand(subAction, target, {
10901
+ cwd,
10902
+ isGlobal,
10903
+ baseUrl,
10904
+ loader: skillsLoader
10905
+ });
10906
+ process.exit(0);
10907
+ } else {
10908
+ printSkillsList(skillsLoader, cwd);
10909
+ process.exit(0);
10910
+ }
10700
10911
  } else if (arg === "memories" || arg === "memory") {
10701
10912
  printMemoriesList(memoryStore, cwd);
10702
10913
  process.exit(0);
@@ -10718,7 +10929,7 @@ async function main() {
10718
10929
  explicitApiKey = args[++i];
10719
10930
  apiKey = explicitApiKey || apiKey;
10720
10931
  } else if (arg === "--cwd" || arg === "-C") {
10721
- cwd = resolve21(args[++i] || cwd);
10932
+ cwd = resolve22(args[++i] || cwd);
10722
10933
  } else if (arg === "--role" || arg === "-r") {
10723
10934
  role = args[++i] || role;
10724
10935
  } else if (arg === "--mcp") {
@@ -10744,11 +10955,11 @@ async function main() {
10744
10955
  const mcpManager = new McpManager;
10745
10956
  const candidateConfigs = [
10746
10957
  mcpConfigFile,
10747
- resolve21(cwd, ".mcp.json"),
10748
- resolve21(cwd, "mcp_config.json")
10958
+ resolve22(cwd, ".mcp.json"),
10959
+ resolve22(cwd, "mcp_config.json")
10749
10960
  ].filter(Boolean);
10750
10961
  for (const cfg of candidateConfigs) {
10751
- if (existsSync23(cfg)) {
10962
+ if (existsSync24(cfg)) {
10752
10963
  try {
10753
10964
  await mcpManager.loadConfigFile(cfg);
10754
10965
  mcpManager.registerToolsIntoRouter(tools4);
@@ -10951,6 +11162,62 @@ Resume a session with: ${style.bold("groupy --resume <thread_id>")}`);
10951
11162
  }
10952
11163
  console.log();
10953
11164
  }
11165
+ async function handleSkillCliCommand(action, targetName, options) {
11166
+ const act = action.toLowerCase();
11167
+ if (act === "add" || act === "install") {
11168
+ if (!targetName) {
11169
+ console.error(style.yellow(`
11170
+ Usage: pikaa skill add <skill-name> [--global]
11171
+ `));
11172
+ process.exit(1);
11173
+ }
11174
+ try {
11175
+ console.log(style.dim(`
11176
+ Fetching skill '${targetName}' from catalog...`));
11177
+ const res = await installSkill(targetName, {
11178
+ cwd: options.cwd,
11179
+ global: options.isGlobal,
11180
+ backendUrl: options.baseUrl
11181
+ });
11182
+ console.log(style.green(`
11183
+ \u2713 ${res.message}`));
11184
+ console.log(style.dim(` To start using this skill in an AI coding session, simply run: pikaa
11185
+ `));
11186
+ } catch (err) {
11187
+ console.error(style.red(`
11188
+ \u2715 Failed to install skill: ${err.message}
11189
+ `));
11190
+ process.exit(1);
11191
+ }
11192
+ } else if (act === "remove" || act === "uninstall" || act === "rm") {
11193
+ if (!targetName) {
11194
+ console.error(style.yellow(`
11195
+ Usage: pikaa skill remove <skill-name> [--global]
11196
+ `));
11197
+ process.exit(1);
11198
+ }
11199
+ const res = removeSkill(targetName, { cwd: options.cwd, global: options.isGlobal });
11200
+ if (res.removed) {
11201
+ console.log(style.green(`
11202
+ \u2713 Skill '${targetName}' removed from ${res.targetDir}
11203
+ `));
11204
+ } else {
11205
+ console.log(style.yellow(`
11206
+ \u2715 Skill '${targetName}' was not found in ${res.targetDir}
11207
+ `));
11208
+ }
11209
+ } else if (act === "list" || act === "ls") {
11210
+ printSkillsList(options.loader, options.cwd);
11211
+ } else {
11212
+ if (act && !targetName) {
11213
+ return handleSkillCliCommand("add", act, options);
11214
+ }
11215
+ console.error(style.yellow(`
11216
+ Unknown skill action '${action}'. Available: add, remove, list
11217
+ `));
11218
+ process.exit(1);
11219
+ }
11220
+ }
10954
11221
  function printSkillsList(loader, cwd) {
10955
11222
  const skills = loader.discoverSkills(cwd);
10956
11223
  console.log();