sparkecoder 0.1.119 → 0.1.121

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 (155) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +2 -0
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +470 -135
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-Bcz0aCAR.d.ts → index-DczYH89U.d.ts} +104 -104
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +429 -94
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-BWbWmfDQ.d.ts → schema-DxrKyetI.d.ts} +3 -3
  12. package/dist/{search-DOzC4ojH.d.ts → search-CVVfuBPZ.d.ts} +4 -4
  13. package/dist/server/index.js +429 -94
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/tools/index.d.ts +3 -3
  16. package/dist/tools/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/web/.next/BUILD_ID +1 -1
  19. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  20. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  21. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  22. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
  23. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  24. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  25. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
  27. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  29. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  45. package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
  50. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  52. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  77. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  81. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  85. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  86. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  87. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  89. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  93. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  94. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  97. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  99. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  100. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  101. package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
  102. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
  103. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  104. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  105. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  106. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  107. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  108. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  109. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_6ab1f7b7._.js +3 -0
  110. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f3e6443f._.js → [root-of-the-server]__4de426bd._.js} +2 -2
  111. package/web/.next/standalone/web/.next/server/chunks/ssr/web_62ca4286._.js +3 -0
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +3 -1
  113. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  114. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  115. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  116. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  117. package/web/.next/standalone/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
  118. package/web/.next/standalone/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
  119. package/web/.next/standalone/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  120. package/web/.next/standalone/web/.next/static/chunks/9b88f148788e4504.js +3 -0
  121. package/web/.next/standalone/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
  122. package/web/.next/standalone/web/.next/static/static/chunks/74ae1f17d607b2fc.js +7 -0
  123. package/web/.next/standalone/web/.next/static/static/chunks/883ea0d08f88e366.js +1 -0
  124. package/web/.next/standalone/web/.next/static/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  125. package/web/.next/standalone/web/.next/static/static/chunks/9b88f148788e4504.js +3 -0
  126. package/web/.next/standalone/web/.next/static/static/chunks/acb0fc66f5414af6.css +1 -0
  127. package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +464 -1
  128. package/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
  129. package/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
  130. package/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  131. package/web/.next/static/chunks/9b88f148788e4504.js +3 -0
  132. package/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
  133. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +0 -3
  134. package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +0 -3
  135. package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
  136. package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
  137. package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
  138. package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
  139. package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +0 -1
  140. package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +0 -7
  141. package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +0 -1
  142. package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +0 -1
  143. package/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
  144. package/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
  145. package/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
  146. package/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
  147. /package/web/.next/standalone/web/.next/static/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  148. /package/web/.next/standalone/web/.next/static/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  149. /package/web/.next/standalone/web/.next/static/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
  150. /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  151. /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  152. /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
  153. /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  154. /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  155. /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -964,6 +964,7 @@ __export(config_exports, {
964
964
  setApiKey: () => setApiKey,
965
965
  setMcpServers: () => setMcpServers,
966
966
  setPublicUrl: () => setPublicUrl,
967
+ setSkillsAdditionalDirectories: () => setSkillsAdditionalDirectories,
967
968
  setSlackConfig: () => setSlackConfig,
968
969
  setWebhookToken: () => setWebhookToken
969
970
  });
@@ -1253,6 +1254,40 @@ function setWebhookToken(token) {
1253
1254
  console.warn("[config] failed to persist webhook token:", err?.message || err);
1254
1255
  }
1255
1256
  }
1257
+ function setSkillsAdditionalDirectories(directories) {
1258
+ if (cachedConfig) {
1259
+ const cur = cachedConfig.skills || {};
1260
+ cachedConfig.skills = { ...cur, additionalDirectories: directories };
1261
+ try {
1262
+ const discovered = discoverSkillDirectories(cachedConfig.resolvedWorkingDirectory);
1263
+ const additionalAbs = directories.map((d) => resolve(cachedConfig.resolvedWorkingDirectory, d)).filter((d) => existsSync(d));
1264
+ cachedConfig.discoveredSkills = discovered;
1265
+ cachedConfig.resolvedSkillsDirectories = [
1266
+ ...discovered.allDirectories,
1267
+ ...additionalAbs
1268
+ ];
1269
+ } catch {
1270
+ }
1271
+ }
1272
+ try {
1273
+ const cwdPath = resolve(process.cwd(), "sparkecoder.config.json");
1274
+ const target = existsSync(cwdPath) ? cwdPath : join(ensureAppDataDirectory(), "sparkecoder.config.json");
1275
+ let raw = {};
1276
+ if (existsSync(target)) {
1277
+ try {
1278
+ raw = JSON.parse(readFileSync(target, "utf-8"));
1279
+ } catch {
1280
+ raw = {};
1281
+ }
1282
+ } else {
1283
+ raw = createDefaultConfig();
1284
+ }
1285
+ raw.skills = { ...raw.skills || {}, additionalDirectories: directories };
1286
+ writeFileSync(target, JSON.stringify(raw, null, 2));
1287
+ } catch (err) {
1288
+ console.warn("[config] failed to persist skill directories:", err?.message || err);
1289
+ }
1290
+ }
1256
1291
  function setPublicUrl(publicUrl) {
1257
1292
  if (cachedConfig) {
1258
1293
  cachedConfig.server = { ...cachedConfig.server ?? {}, publicUrl };
@@ -3014,7 +3049,7 @@ async function createClient(serverId, handle, root) {
3014
3049
  },
3015
3050
  async waitForDiagnostics(filePath, timeoutMs = 5e3) {
3016
3051
  const normalized = normalizePath(filePath);
3017
- return new Promise((resolve13) => {
3052
+ return new Promise((resolve14) => {
3018
3053
  const startTime = Date.now();
3019
3054
  let debounceTimer;
3020
3055
  let resolved = false;
@@ -3033,7 +3068,7 @@ async function createClient(serverId, handle, root) {
3033
3068
  if (resolved) return;
3034
3069
  resolved = true;
3035
3070
  cleanup2();
3036
- resolve13(diagnostics.get(normalized) || []);
3071
+ resolve14(diagnostics.get(normalized) || []);
3037
3072
  };
3038
3073
  const onDiagnostic = () => {
3039
3074
  if (debounceTimer) clearTimeout(debounceTimer);
@@ -3408,7 +3443,7 @@ Working directory: ${options.workingDirectory}`,
3408
3443
  isChunked: true
3409
3444
  });
3410
3445
  if (chunkCount > 1) {
3411
- await new Promise((resolve13) => setTimeout(resolve13, 0));
3446
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
3412
3447
  }
3413
3448
  }
3414
3449
  }
@@ -4027,7 +4062,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
4027
4062
  if (!existsSync10(directory)) {
4028
4063
  return [];
4029
4064
  }
4030
- const skills = [];
4065
+ const skills2 = [];
4031
4066
  const entries = await readdir(directory, { withFileTypes: true });
4032
4067
  for (const entry2 of entries) {
4033
4068
  let filePath;
@@ -4051,7 +4086,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
4051
4086
  if (parsed) {
4052
4087
  const alwaysApply = forceAlwaysApply || parsed.metadata.alwaysApply;
4053
4088
  const loadType = alwaysApply ? "always" : defaultLoadType;
4054
- skills.push({
4089
+ skills2.push({
4055
4090
  name: parsed.metadata.name,
4056
4091
  description: parsed.metadata.description,
4057
4092
  filePath,
@@ -4065,7 +4100,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
4065
4100
  } else {
4066
4101
  const name = getSkillNameFromPath(filePath);
4067
4102
  const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
4068
- skills.push({
4103
+ skills2.push({
4069
4104
  name,
4070
4105
  description: firstParagraph.replace(/^#\s*/, "").trim(),
4071
4106
  filePath,
@@ -4078,7 +4113,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
4078
4113
  });
4079
4114
  }
4080
4115
  }
4081
- return skills.filter(
4116
+ return skills2.filter(
4082
4117
  (s) => s.platforms.length === 0 || s.platforms.includes(process.platform)
4083
4118
  );
4084
4119
  }
@@ -4086,8 +4121,8 @@ async function loadAllSkills(directories) {
4086
4121
  const allSkills = [];
4087
4122
  const seenNames = /* @__PURE__ */ new Set();
4088
4123
  for (const dir of directories) {
4089
- const skills = await loadSkillsFromDirectory(dir);
4090
- for (const skill of skills) {
4124
+ const skills2 = await loadSkillsFromDirectory(dir);
4125
+ for (const skill of skills2) {
4091
4126
  if (!seenNames.has(skill.name.toLowerCase())) {
4092
4127
  seenNames.add(skill.name.toLowerCase());
4093
4128
  allSkills.push(skill);
@@ -4100,12 +4135,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
4100
4135
  const allSkills = [];
4101
4136
  const seenNames = /* @__PURE__ */ new Set();
4102
4137
  for (const { path, priority } of discovered.alwaysLoadedDirs) {
4103
- const skills = await loadSkillsFromDirectory(path, {
4138
+ const skills2 = await loadSkillsFromDirectory(path, {
4104
4139
  priority,
4105
4140
  defaultLoadType: "always",
4106
4141
  forceAlwaysApply: true
4107
4142
  });
4108
- for (const skill of skills) {
4143
+ for (const skill of skills2) {
4109
4144
  if (!seenNames.has(skill.name.toLowerCase())) {
4110
4145
  seenNames.add(skill.name.toLowerCase());
4111
4146
  allSkills.push(skill);
@@ -4113,12 +4148,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
4113
4148
  }
4114
4149
  }
4115
4150
  for (const { path, priority } of discovered.onDemandDirs) {
4116
- const skills = await loadSkillsFromDirectory(path, {
4151
+ const skills2 = await loadSkillsFromDirectory(path, {
4117
4152
  priority,
4118
4153
  defaultLoadType: "on_demand",
4119
4154
  forceAlwaysApply: false
4120
4155
  });
4121
- for (const skill of skills) {
4156
+ for (const skill of skills2) {
4122
4157
  if (!seenNames.has(skill.name.toLowerCase())) {
4123
4158
  seenNames.add(skill.name.toLowerCase());
4124
4159
  allSkills.push(skill);
@@ -4143,7 +4178,7 @@ async function loadAllSkillsFromDiscovered(discovered) {
4143
4178
  all: allSkills
4144
4179
  };
4145
4180
  }
4146
- async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
4181
+ async function getGlobMatchedSkills(skills2, activeFiles, workingDirectory) {
4147
4182
  if (activeFiles.length === 0) {
4148
4183
  return [];
4149
4184
  }
@@ -4153,7 +4188,7 @@ async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
4153
4188
  }
4154
4189
  return f;
4155
4190
  });
4156
- const matchedSkills = skills.filter((skill) => {
4191
+ const matchedSkills = skills2.filter((skill) => {
4157
4192
  if (skill.alwaysApply || skill.loadType === "always") {
4158
4193
  return false;
4159
4194
  }
@@ -4199,8 +4234,8 @@ async function loadSkillContent(skillName, directories) {
4199
4234
  content: parsed ? parsed.body : content
4200
4235
  };
4201
4236
  }
4202
- function formatSkillsForContext(skills) {
4203
- const onDemandSkills = skills.filter((s) => !s.alwaysApply && s.loadType !== "always");
4237
+ function formatSkillsForContext(skills2) {
4238
+ const onDemandSkills = skills2.filter((s) => !s.alwaysApply && s.loadType !== "always");
4204
4239
  if (onDemandSkills.length === 0) {
4205
4240
  return "No on-demand skills available.";
4206
4241
  }
@@ -4211,12 +4246,12 @@ function formatSkillsForContext(skills) {
4211
4246
  }
4212
4247
  return lines.join("\n");
4213
4248
  }
4214
- function formatAlwaysLoadedSkills(skills) {
4215
- if (skills.length === 0) {
4249
+ function formatAlwaysLoadedSkills(skills2) {
4250
+ if (skills2.length === 0) {
4216
4251
  return "";
4217
4252
  }
4218
4253
  const sections = [];
4219
- for (const skill of skills) {
4254
+ for (const skill of skills2) {
4220
4255
  sections.push(`### ${skill.name}
4221
4256
 
4222
4257
  ${skill.content}`);
@@ -4225,12 +4260,12 @@ ${skill.content}`);
4225
4260
 
4226
4261
  ${sections.join("\n\n---\n\n")}`;
4227
4262
  }
4228
- function formatGlobMatchedSkills(skills) {
4229
- if (skills.length === 0) {
4263
+ function formatGlobMatchedSkills(skills2) {
4264
+ if (skills2.length === 0) {
4230
4265
  return "";
4231
4266
  }
4232
4267
  const sections = [];
4233
- for (const skill of skills) {
4268
+ for (const skill of skills2) {
4234
4269
  sections.push(`### ${skill.name}
4235
4270
 
4236
4271
  ${skill.content}`);
@@ -4272,16 +4307,16 @@ Once loaded, a skill's content will be available in the conversation context.`,
4272
4307
  try {
4273
4308
  switch (action) {
4274
4309
  case "list": {
4275
- const skills = await loadAllSkills(options.skillsDirectories);
4310
+ const skills2 = await loadAllSkills(options.skillsDirectories);
4276
4311
  return {
4277
4312
  success: true,
4278
4313
  action: "list",
4279
- skillCount: skills.length,
4280
- skills: skills.map((s) => ({
4314
+ skillCount: skills2.length,
4315
+ skills: skills2.map((s) => ({
4281
4316
  name: s.name,
4282
4317
  description: s.description
4283
4318
  })),
4284
- formatted: formatSkillsForContext(skills)
4319
+ formatted: formatSkillsForContext(skills2)
4285
4320
  };
4286
4321
  }
4287
4322
  case "load": {
@@ -4719,8 +4754,8 @@ var init_subagent = __esm({
4719
4754
  if (eventQueue.length > 0) {
4720
4755
  yield eventQueue.shift();
4721
4756
  } else if (!done) {
4722
- const event = await new Promise((resolve13) => {
4723
- resolveNext = resolve13;
4757
+ const event = await new Promise((resolve14) => {
4758
+ resolveNext = resolve14;
4724
4759
  });
4725
4760
  if (event) {
4726
4761
  yield event;
@@ -5362,7 +5397,7 @@ function formatError(error) {
5362
5397
  }
5363
5398
  }
5364
5399
  function sleep(ms) {
5365
- return new Promise((resolve13) => setTimeout(resolve13, ms));
5400
+ return new Promise((resolve14) => setTimeout(resolve14, ms));
5366
5401
  }
5367
5402
  function isPathExcluded(relativePath, exclude) {
5368
5403
  return exclude.some((pattern) => {
@@ -5380,7 +5415,7 @@ function isPathExcluded(relativePath, exclude) {
5380
5415
  }
5381
5416
  async function walkDirectory(dir, include, exclude, baseDir) {
5382
5417
  const { readdirSync: readdirSync4 } = await import("fs");
5383
- const { join: join17, relative: relative10 } = await import("path");
5418
+ const { join: join18, relative: relative10 } = await import("path");
5384
5419
  const files = [];
5385
5420
  function walk(currentDir) {
5386
5421
  let entries;
@@ -5390,7 +5425,7 @@ async function walkDirectory(dir, include, exclude, baseDir) {
5390
5425
  return;
5391
5426
  }
5392
5427
  for (const entry2 of entries) {
5393
- const fullPath = join17(currentDir, entry2.name);
5428
+ const fullPath = join18(currentDir, entry2.name);
5394
5429
  const relativePath = relative10(baseDir, fullPath);
5395
5430
  if (isPathExcluded(relativePath, exclude)) {
5396
5431
  continue;
@@ -7238,8 +7273,8 @@ async function buildSystemPrompt(options) {
7238
7273
  }
7239
7274
  } else {
7240
7275
  const { loadAllSkills: loadAllSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
7241
- const skills = await loadAllSkills2(skillsDirectories);
7242
- onDemandSkillsContext = formatSkillsForContext(skills);
7276
+ const skills2 = await loadAllSkills2(skillsDirectories);
7277
+ onDemandSkillsContext = formatSkillsForContext(skills2);
7243
7278
  }
7244
7279
  const todos = await todoQueries.getBySession(sessionId);
7245
7280
  const todosContext = formatTodosForContext(todos);
@@ -8545,6 +8580,9 @@ function isSlackConfigured() {
8545
8580
  function getSlackSigningSecret() {
8546
8581
  return readSlackConfig()?.signingSecret ?? null;
8547
8582
  }
8583
+ function getSlackBotToken() {
8584
+ return readSlackConfig()?.botToken ?? null;
8585
+ }
8548
8586
  function getDefaultOrchestratorName() {
8549
8587
  return readSlackConfig()?.defaultOrchestratorName ?? null;
8550
8588
  }
@@ -8599,6 +8637,62 @@ function getSlackAllowlistPolicy() {
8599
8637
  return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
8600
8638
  }
8601
8639
  }
8640
+ async function fetchSlackUserName(userId) {
8641
+ const token = getSlackBotToken();
8642
+ if (!token) return null;
8643
+ try {
8644
+ const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
8645
+ headers: { Authorization: `Bearer ${token}` }
8646
+ });
8647
+ const data = await res.json().catch(() => ({}));
8648
+ if (!data?.ok) {
8649
+ console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
8650
+ return null;
8651
+ }
8652
+ const profile = data.user?.profile || {};
8653
+ const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
8654
+ return name ? String(name) : null;
8655
+ } catch (err) {
8656
+ console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
8657
+ return null;
8658
+ }
8659
+ }
8660
+ async function resolveSlackUserName(userId) {
8661
+ if (!userId) return null;
8662
+ const now = Date.now();
8663
+ const hit = userNameCache.get(userId);
8664
+ if (hit && hit.expiresAt > now) return hit.name;
8665
+ const inflight = userInflight.get(userId);
8666
+ if (inflight) return inflight;
8667
+ const p = (async () => {
8668
+ const name = await fetchSlackUserName(userId);
8669
+ userNameCache.set(userId, {
8670
+ name,
8671
+ expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
8672
+ });
8673
+ userInflight.delete(userId);
8674
+ return name;
8675
+ })();
8676
+ userInflight.set(userId, p);
8677
+ return p;
8678
+ }
8679
+ async function normalizeSlackMentions(text) {
8680
+ if (!text) return text;
8681
+ const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
8682
+ const userIds = /* @__PURE__ */ new Set();
8683
+ for (const m of text.matchAll(userMentionRe)) {
8684
+ if (!m[2]) userIds.add(m[1]);
8685
+ }
8686
+ if (userIds.size > 0) {
8687
+ await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
8688
+ }
8689
+ return text.replace(userMentionRe, (_full, id, label) => {
8690
+ if (label) return `${label} <@${id}>`;
8691
+ const cached = userNameCache.get(id);
8692
+ const name = cached?.name;
8693
+ return name ? `${name} <@${id}>` : `<@${id}>`;
8694
+ });
8695
+ }
8602
8696
  function getSlackDeniedReplyPolicy() {
8603
8697
  try {
8604
8698
  const cfg = getConfig();
@@ -8611,13 +8705,17 @@ function getSlackDeniedReplyPolicy() {
8611
8705
  return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
8612
8706
  }
8613
8707
  }
8614
- var cachedSelf, selfInflight, DEFAULT_DENIED_TEMPLATE;
8708
+ var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
8615
8709
  var init_client3 = __esm({
8616
8710
  "src/integrations/slack/client.ts"() {
8617
8711
  "use strict";
8618
8712
  init_config();
8619
8713
  cachedSelf = null;
8620
8714
  selfInflight = null;
8715
+ USER_TTL_MS = 60 * 60 * 1e3;
8716
+ USER_FAIL_TTL_MS = 5 * 60 * 1e3;
8717
+ userNameCache = /* @__PURE__ */ new Map();
8718
+ userInflight = /* @__PURE__ */ new Map();
8621
8719
  DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
8622
8720
  }
8623
8721
  });
@@ -8632,9 +8730,6 @@ function markThreadOwned(channel, threadTs) {
8632
8730
  function isThreadOwned(channel, threadTs) {
8633
8731
  return ownedThreads.has(threadKey(channel, threadTs));
8634
8732
  }
8635
- function stripMention(text) {
8636
- return String(text || "").replace(/<@[^>]+>/g, "").trim();
8637
- }
8638
8733
  function isSelfAuthored(event, self) {
8639
8734
  if (!self) return true;
8640
8735
  if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
@@ -8649,14 +8744,22 @@ function slackEventToInboundResult(event, opts = {}) {
8649
8744
  return { event: null, dropReason: "bot_message" };
8650
8745
  }
8651
8746
  if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
8652
- return { event: null, dropReason: "bot_message" };
8747
+ return { event: null, dropReason: "ignored_subtype" };
8653
8748
  }
8654
8749
  const isDm = event.type === "message" && event.channel_type === "im";
8655
8750
  const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
8751
+ const isNonThreadChannelMsg = event.type === "message" && !isDm && !isThreadReply && (event.channel_type === "channel" || event.channel_type === "group" || event.channel_type === "mpim" || // Some payload shapes omit channel_type for channel messages.
8752
+ typeof event.channel === "string");
8656
8753
  if (event.type !== "app_mention" && !isDm && !isThreadReply) {
8754
+ if (isNonThreadChannelMsg) {
8755
+ return { event: null, dropReason: "non_thread_channel_msg" };
8756
+ }
8757
+ if (event.type !== "message") {
8758
+ return { event: null, dropReason: "non_message_event" };
8759
+ }
8657
8760
  return { event: null, dropReason: "unsupported_type" };
8658
8761
  }
8659
- const text = event.type === "app_mention" ? stripMention(event.text) : (event.text ?? "").trim();
8762
+ const text = (event.text ?? "").trim();
8660
8763
  if (!text) return { event: null, dropReason: "empty_text" };
8661
8764
  const policy = getSlackAllowlistPolicy();
8662
8765
  const userAllowlistActive = policy.allowedUsers.length > 0;
@@ -9639,11 +9742,11 @@ function waitForTaskQuestionAnswer(question) {
9639
9742
  if (pendingQuestions.has(k)) {
9640
9743
  return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
9641
9744
  }
9642
- return new Promise((resolve13, reject) => {
9745
+ return new Promise((resolve14, reject) => {
9643
9746
  pendingQuestions.set(k, {
9644
9747
  ...question,
9645
9748
  createdAt: /* @__PURE__ */ new Date(),
9646
- resolve: resolve13,
9749
+ resolve: resolve14,
9647
9750
  reject
9648
9751
  });
9649
9752
  });
@@ -10792,14 +10895,14 @@ ${p.text}` : p.text;
10792
10895
  const result = await recorder.encode();
10793
10896
  recorder.clear();
10794
10897
  if (!result) return [];
10795
- const { readFile: readFile12, unlink: unlink3 } = await import("fs/promises");
10898
+ const { readFile: readFile13, unlink: unlink4 } = await import("fs/promises");
10796
10899
  const uploadInfo = await storageQueries2.getUploadUrl(
10797
10900
  this.session.id,
10798
10901
  `browser-recording-${Date.now()}.mp4`,
10799
10902
  "video/mp4",
10800
10903
  "browser-recording"
10801
10904
  );
10802
- const fileData = await readFile12(result.path);
10905
+ const fileData = await readFile13(result.path);
10803
10906
  await fetch(uploadInfo.uploadUrl, {
10804
10907
  method: "PUT",
10805
10908
  headers: { "Content-Type": "video/mp4" },
@@ -10807,7 +10910,7 @@ ${p.text}` : p.text;
10807
10910
  });
10808
10911
  await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
10809
10912
  const dlInfo = await storageQueries2.getDownloadUrl(uploadInfo.fileId);
10810
- await unlink3(result.path).catch(() => {
10913
+ await unlink4(result.path).catch(() => {
10811
10914
  });
10812
10915
  console.log(`[TASK] Browser recording uploaded (${result.sizeBytes} bytes)`);
10813
10916
  return [dlInfo.downloadUrl];
@@ -10825,13 +10928,13 @@ ${p.text}` : p.text;
10825
10928
  try {
10826
10929
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
10827
10930
  if (!isRemoteConfigured2()) return [];
10828
- const { readFile: readFile12 } = await import("fs/promises");
10829
- const { join: join17, basename: basename6 } = await import("path");
10931
+ const { readFile: readFile13 } = await import("fs/promises");
10932
+ const { join: join18, basename: basename7 } = await import("path");
10830
10933
  const urls = [];
10831
10934
  for (const filePath of filePaths) {
10832
10935
  try {
10833
- const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
10834
- const fileName = basename6(fullPath);
10936
+ const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
10937
+ const fileName = basename7(fullPath);
10835
10938
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
10836
10939
  const mimeMap = {
10837
10940
  pdf: "application/pdf",
@@ -10855,7 +10958,7 @@ ${p.text}` : p.text;
10855
10958
  contentType,
10856
10959
  "task-output"
10857
10960
  );
10858
- const fileData = await readFile12(fullPath);
10961
+ const fileData = await readFile13(fullPath);
10859
10962
  await fetch(uploadInfo.uploadUrl, {
10860
10963
  method: "PUT",
10861
10964
  headers: { "Content-Type": contentType },
@@ -10904,8 +11007,8 @@ ${p.text}` : p.text;
10904
11007
  this.pendingApprovals.set(toolCallId, await execution);
10905
11008
  options.onApprovalRequired?.(await execution);
10906
11009
  await sessionQueries.updateStatus(this.session.id, "waiting");
10907
- const approved = await new Promise((resolve13) => {
10908
- approvalResolvers.set(toolCallId, { resolve: resolve13, sessionId: this.session.id });
11010
+ const approved = await new Promise((resolve14) => {
11011
+ approvalResolvers.set(toolCallId, { resolve: resolve14, sessionId: this.session.id });
10909
11012
  });
10910
11013
  const resolverData = approvalResolvers.get(toolCallId);
10911
11014
  approvalResolvers.delete(toolCallId);
@@ -11011,8 +11114,8 @@ async function withSessionLock(sessionId, fn) {
11011
11114
  state2.pending++;
11012
11115
  const prev = state2.tail;
11013
11116
  let release;
11014
- const next = new Promise((resolve13) => {
11015
- release = resolve13;
11117
+ const next = new Promise((resolve14) => {
11118
+ release = resolve14;
11016
11119
  });
11017
11120
  state2.tail = prev.then(() => next);
11018
11121
  await prev;
@@ -11625,17 +11728,17 @@ import chalk from "chalk";
11625
11728
  import ora from "ora";
11626
11729
  import "dotenv/config";
11627
11730
  import { createInterface } from "readline";
11628
- import { dirname as dirname9 } from "path";
11731
+ import { dirname as dirname10 } from "path";
11629
11732
  import { fileURLToPath as fileURLToPath5 } from "url";
11630
11733
 
11631
11734
  // src/server/index.ts
11632
11735
  import "dotenv/config";
11633
- import { Hono as Hono9 } from "hono";
11736
+ import { Hono as Hono10 } from "hono";
11634
11737
  import { serve } from "@hono/node-server";
11635
11738
  import { cors } from "hono/cors";
11636
11739
  import { logger } from "hono/logger";
11637
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
11638
- import { resolve as resolve11, dirname as dirname8, join as join15 } from "path";
11740
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
11741
+ import { resolve as resolve12, dirname as dirname9, join as join16 } from "path";
11639
11742
  import { spawn as spawn2 } from "child_process";
11640
11743
  import { createServer as createNetServer } from "net";
11641
11744
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -12810,7 +12913,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
12810
12913
  toolCallId,
12811
12914
  argsTextDelta: chunk
12812
12915
  }));
12813
- await new Promise((resolve13) => setTimeout(resolve13, 0));
12916
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
12814
12917
  }
12815
12918
  }
12816
12919
  function buildDevtoolsContextXml(sessionId) {
@@ -13065,7 +13168,7 @@ ${prompt}` });
13065
13168
  chunkIndex,
13066
13169
  chunkCount
13067
13170
  }));
13068
- await new Promise((resolve13) => setTimeout(resolve13, 0));
13171
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
13069
13172
  }
13070
13173
  const browserPort = progress.data?.browserStreamPort;
13071
13174
  const browserClosed = progress.data?.browserClosed;
@@ -13612,7 +13715,7 @@ agents.post(
13612
13715
  chunkIndex,
13613
13716
  chunkCount
13614
13717
  }));
13615
- await new Promise((resolve13) => setTimeout(resolve13, 0));
13718
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
13616
13719
  }
13617
13720
  const browserPort = progress.data?.browserStreamPort;
13618
13721
  const browserClosed = progress.data?.browserClosed;
@@ -14625,16 +14728,18 @@ init_webhook_events();
14625
14728
  init_inbox();
14626
14729
  var recentlyHandled = /* @__PURE__ */ new Map();
14627
14730
  var MAX_RECENT = 1e3;
14628
- function alreadyHandled(channel, ts) {
14731
+ function wasHandled(channel, ts) {
14629
14732
  if (!channel || !ts) return false;
14733
+ return recentlyHandled.has(`${channel}\u241F${ts}`);
14734
+ }
14735
+ function markHandled(channel, ts) {
14736
+ if (!channel || !ts) return;
14630
14737
  const key2 = `${channel}\u241F${ts}`;
14631
- if (recentlyHandled.has(key2)) return true;
14632
14738
  recentlyHandled.set(key2, Date.now());
14633
14739
  if (recentlyHandled.size > MAX_RECENT) {
14634
14740
  const oldest = recentlyHandled.keys().next().value;
14635
14741
  if (oldest) recentlyHandled.delete(oldest);
14636
14742
  }
14637
- return false;
14638
14743
  }
14639
14744
  var slack = new Hono6();
14640
14745
  slack.post("/events", async (c) => {
@@ -14671,7 +14776,7 @@ slack.post("/events", async (c) => {
14671
14776
  textSnippet: typeof ev.text === "string" ? ev.text : void 0,
14672
14777
  meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
14673
14778
  });
14674
- if (alreadyHandled(ev.channel, ev.ts)) {
14779
+ if (wasHandled(ev.channel, ev.ts)) {
14675
14780
  updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
14676
14781
  return c.json({ ok: true });
14677
14782
  }
@@ -14695,7 +14800,18 @@ slack.post("/events", async (c) => {
14695
14800
  }
14696
14801
  const orchestratorId = await findOrCreateOrchestratorId();
14697
14802
  if (orchestratorId) {
14803
+ inbound.content = await normalizeSlackMentions(inbound.content);
14804
+ if (ev.user) {
14805
+ const speakerName = await resolveSlackUserName(ev.user);
14806
+ if (speakerName) {
14807
+ inbound.content = inbound.content.replace(
14808
+ `user=${ev.user}`,
14809
+ `user=${speakerName} <@${ev.user}>`
14810
+ );
14811
+ }
14812
+ }
14698
14813
  pushToInbox(orchestratorId, inbound);
14814
+ markHandled(ev.channel, ev.ts);
14699
14815
  updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
14700
14816
  } else {
14701
14817
  updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
@@ -15107,6 +15223,224 @@ mcpRouter.post("/:id/test", async (c) => {
15107
15223
  return c.json(result);
15108
15224
  });
15109
15225
 
15226
+ // src/server/routes/skills.ts
15227
+ init_config();
15228
+ init_skills();
15229
+ import { Hono as Hono9 } from "hono";
15230
+ import { zValidator as zValidator7 } from "@hono/zod-validator";
15231
+ import { z as z22 } from "zod";
15232
+ import { existsSync as existsSync20, statSync as statSync3 } from "fs";
15233
+ import { readFile as readFile12, writeFile as writeFile6, unlink as unlink3, mkdir as mkdir5 } from "fs/promises";
15234
+ import { resolve as resolve11, join as join15, basename as basename6, dirname as dirname8, extname as extname9 } from "path";
15235
+ var skills = new Hono9();
15236
+ function encodeId(filePath) {
15237
+ return Buffer.from(filePath, "utf-8").toString("base64url");
15238
+ }
15239
+ function decodeId(id) {
15240
+ try {
15241
+ const decoded = Buffer.from(id, "base64url").toString("utf-8");
15242
+ return decoded || null;
15243
+ } catch {
15244
+ return null;
15245
+ }
15246
+ }
15247
+ function listAllDirectories() {
15248
+ const cfg = getConfig();
15249
+ const discovered = discoverSkillDirectories(cfg.resolvedWorkingDirectory);
15250
+ const out = [];
15251
+ for (const { path, priority } of discovered.alwaysLoadedDirs) {
15252
+ out.push({
15253
+ path,
15254
+ source: pathToSource(path),
15255
+ label: pathToLabel(path),
15256
+ priority,
15257
+ alwaysApply: true
15258
+ });
15259
+ }
15260
+ for (const { path, priority } of discovered.onDemandDirs) {
15261
+ out.push({
15262
+ path,
15263
+ source: pathToSource(path),
15264
+ label: pathToLabel(path),
15265
+ priority,
15266
+ alwaysApply: false
15267
+ });
15268
+ }
15269
+ const additional = cfg.skills?.additionalDirectories || [];
15270
+ for (const dir of additional) {
15271
+ const abs = resolve11(cfg.resolvedWorkingDirectory, dir);
15272
+ if (out.some((d) => d.path === abs)) continue;
15273
+ out.push({
15274
+ path: abs,
15275
+ source: "additional",
15276
+ label: pathToLabel(abs),
15277
+ priority: 50,
15278
+ alwaysApply: false
15279
+ });
15280
+ }
15281
+ return out;
15282
+ }
15283
+ function pathToSource(path) {
15284
+ if (path.includes("/skills/default") || path.endsWith("/skills/default")) return "builtin";
15285
+ return "project";
15286
+ }
15287
+ function pathToLabel(path) {
15288
+ if (path.includes("/skills/default")) return "Built-in (default)";
15289
+ if (path.includes("/.sparkecoder/rules")) return ".sparkecoder/rules";
15290
+ if (path.includes("/.sparkecoder/skills")) return ".sparkecoder/skills";
15291
+ if (path.includes("/.cursor/rules")) return ".cursor/rules";
15292
+ if (path.includes("/.claude/skills")) return ".claude/skills";
15293
+ return basename6(dirname8(path)) + "/" + basename6(path);
15294
+ }
15295
+ skills.get("/", async (c) => {
15296
+ const dirs = listAllDirectories();
15297
+ const allSkills = [];
15298
+ for (const dir of dirs) {
15299
+ if (!existsSync20(dir.path)) continue;
15300
+ try {
15301
+ const list = await loadSkillsFromDirectory(dir.path, {
15302
+ priority: dir.priority,
15303
+ defaultLoadType: dir.alwaysApply ? "always" : "on_demand",
15304
+ forceAlwaysApply: dir.alwaysApply
15305
+ });
15306
+ for (const s of list) {
15307
+ let size = 0;
15308
+ try {
15309
+ size = statSync3(s.filePath).size;
15310
+ } catch {
15311
+ }
15312
+ allSkills.push({
15313
+ id: encodeId(s.filePath),
15314
+ name: s.name,
15315
+ description: s.description,
15316
+ filePath: s.filePath,
15317
+ fileName: basename6(s.filePath),
15318
+ sourceDir: dir.path,
15319
+ sourceLabel: dir.label,
15320
+ sourceType: dir.source,
15321
+ alwaysApply: s.alwaysApply,
15322
+ globs: s.globs,
15323
+ sizeBytes: size
15324
+ });
15325
+ }
15326
+ } catch (err) {
15327
+ console.warn("[skills] failed to read", dir.path, err?.message || err);
15328
+ }
15329
+ }
15330
+ return c.json({
15331
+ directories: dirs.map((d) => ({
15332
+ path: d.path,
15333
+ label: d.label,
15334
+ source: d.source,
15335
+ alwaysApply: d.alwaysApply,
15336
+ exists: existsSync20(d.path),
15337
+ writable: isWritable(d.path)
15338
+ })),
15339
+ skills: allSkills
15340
+ });
15341
+ });
15342
+ function isWritable(dir) {
15343
+ try {
15344
+ if (!existsSync20(dir)) return false;
15345
+ if (dir.includes("/skills/default")) return false;
15346
+ return true;
15347
+ } catch {
15348
+ return false;
15349
+ }
15350
+ }
15351
+ skills.get("/:id", async (c) => {
15352
+ const filePath = decodeId(c.req.param("id"));
15353
+ if (!filePath || !existsSync20(filePath)) {
15354
+ return c.json({ error: "skill not found" }, 404);
15355
+ }
15356
+ const content = await readFile12(filePath, "utf-8");
15357
+ return c.json({
15358
+ id: c.req.param("id"),
15359
+ filePath,
15360
+ fileName: basename6(filePath),
15361
+ content,
15362
+ writable: !filePath.includes("/skills/default")
15363
+ });
15364
+ });
15365
+ skills.post(
15366
+ "/",
15367
+ zValidator7("json", z22.object({
15368
+ directory: z22.string().min(1),
15369
+ fileName: z22.string().min(1),
15370
+ content: z22.string()
15371
+ })),
15372
+ async (c) => {
15373
+ const { directory, fileName, content } = c.req.valid("json");
15374
+ const cfg = getConfig();
15375
+ const targetDir = resolve11(cfg.resolvedWorkingDirectory, directory);
15376
+ if (targetDir.includes("/skills/default")) {
15377
+ return c.json({ error: "cannot write into built-in skills directory" }, 400);
15378
+ }
15379
+ const safeName = basename6(fileName).replace(/[^A-Za-z0-9._-]/g, "-");
15380
+ const ext = extname9(safeName).toLowerCase();
15381
+ const finalName = ext === ".md" || ext === ".mdc" ? safeName : `${safeName}.md`;
15382
+ const filePath = join15(targetDir, finalName);
15383
+ if (existsSync20(filePath)) {
15384
+ return c.json({ error: `file already exists: ${finalName}` }, 409);
15385
+ }
15386
+ try {
15387
+ await mkdir5(targetDir, { recursive: true });
15388
+ await writeFile6(filePath, content, "utf-8");
15389
+ } catch (err) {
15390
+ return c.json({ error: err?.message || "write failed" }, 500);
15391
+ }
15392
+ return c.json({ id: encodeId(filePath), filePath, fileName: finalName }, 201);
15393
+ }
15394
+ );
15395
+ skills.put(
15396
+ "/:id",
15397
+ zValidator7("json", z22.object({ content: z22.string() })),
15398
+ async (c) => {
15399
+ const filePath = decodeId(c.req.param("id"));
15400
+ if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
15401
+ if (filePath.includes("/skills/default")) {
15402
+ return c.json({ error: "built-in skills are read-only" }, 400);
15403
+ }
15404
+ await writeFile6(filePath, c.req.valid("json").content, "utf-8");
15405
+ return c.json({ ok: true });
15406
+ }
15407
+ );
15408
+ skills.delete("/:id", async (c) => {
15409
+ const filePath = decodeId(c.req.param("id"));
15410
+ if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
15411
+ if (filePath.includes("/skills/default")) {
15412
+ return c.json({ error: "built-in skills are read-only" }, 400);
15413
+ }
15414
+ await unlink3(filePath);
15415
+ return c.json({ ok: true });
15416
+ });
15417
+ skills.post(
15418
+ "/directories",
15419
+ zValidator7("json", z22.object({ path: z22.string().min(1) })),
15420
+ (c) => {
15421
+ const cfg = getConfig();
15422
+ const inputPath = c.req.valid("json").path.trim();
15423
+ const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
15424
+ const current = cfg.skills?.additionalDirectories || [];
15425
+ if (current.some((d) => resolve11(cfg.resolvedWorkingDirectory, d) === abs)) {
15426
+ return c.json({ error: "directory already added" }, 409);
15427
+ }
15428
+ const next = [...current, abs];
15429
+ setSkillsAdditionalDirectories(next);
15430
+ return c.json({ ok: true, path: abs, exists: existsSync20(abs) }, 201);
15431
+ }
15432
+ );
15433
+ skills.delete("/directories", (c) => {
15434
+ const inputPath = c.req.query("path");
15435
+ if (!inputPath) return c.json({ error: "path query param required" }, 400);
15436
+ const cfg = getConfig();
15437
+ const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
15438
+ const current = cfg.skills?.additionalDirectories || [];
15439
+ const next = current.filter((d) => resolve11(cfg.resolvedWorkingDirectory, d) !== abs);
15440
+ setSkillsAdditionalDirectories(next);
15441
+ return c.json({ ok: true });
15442
+ });
15443
+
15110
15444
  // src/server/auth/cf-access.ts
15111
15445
  init_config();
15112
15446
  import { createRemoteJWKSet, jwtVerify } from "jose";
@@ -15363,13 +15697,13 @@ var DEFAULT_WEB_PORT = 6969;
15363
15697
  var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
15364
15698
  function getWebDirectory() {
15365
15699
  try {
15366
- const currentDir = dirname8(fileURLToPath4(import.meta.url));
15367
- const webDir = resolve11(currentDir, "..", "web");
15368
- if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
15700
+ const currentDir = dirname9(fileURLToPath4(import.meta.url));
15701
+ const webDir = resolve12(currentDir, "..", "web");
15702
+ if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
15369
15703
  return webDir;
15370
15704
  }
15371
- const altWebDir = resolve11(currentDir, "..", "..", "web");
15372
- if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
15705
+ const altWebDir = resolve12(currentDir, "..", "..", "web");
15706
+ if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
15373
15707
  return altWebDir;
15374
15708
  }
15375
15709
  return null;
@@ -15392,18 +15726,18 @@ async function isSparkcoderWebRunning(port) {
15392
15726
  }
15393
15727
  }
15394
15728
  function isPortInUse(port) {
15395
- return new Promise((resolve13) => {
15729
+ return new Promise((resolve14) => {
15396
15730
  const server = createNetServer();
15397
15731
  server.once("error", (err) => {
15398
15732
  if (err.code === "EADDRINUSE") {
15399
- resolve13(true);
15733
+ resolve14(true);
15400
15734
  } else {
15401
- resolve13(false);
15735
+ resolve14(false);
15402
15736
  }
15403
15737
  });
15404
15738
  server.once("listening", () => {
15405
15739
  server.close();
15406
- resolve13(false);
15740
+ resolve14(false);
15407
15741
  });
15408
15742
  server.listen(port, "0.0.0.0");
15409
15743
  });
@@ -15427,30 +15761,30 @@ async function findWebPort(preferredPort) {
15427
15761
  return { port: preferredPort, alreadyRunning: false };
15428
15762
  }
15429
15763
  function hasProductionBuild(webDir) {
15430
- const buildIdPath = join15(webDir, ".next", "BUILD_ID");
15431
- return existsSync20(buildIdPath);
15764
+ const buildIdPath = join16(webDir, ".next", "BUILD_ID");
15765
+ return existsSync21(buildIdPath);
15432
15766
  }
15433
15767
  function hasSourceFiles(webDir) {
15434
- const appDir = join15(webDir, "src", "app");
15435
- const pagesDir = join15(webDir, "src", "pages");
15436
- const rootAppDir = join15(webDir, "app");
15437
- const rootPagesDir = join15(webDir, "pages");
15438
- return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
15768
+ const appDir = join16(webDir, "src", "app");
15769
+ const pagesDir = join16(webDir, "src", "pages");
15770
+ const rootAppDir = join16(webDir, "app");
15771
+ const rootPagesDir = join16(webDir, "pages");
15772
+ return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
15439
15773
  }
15440
15774
  function getStandaloneServerPath(webDir) {
15441
15775
  const possiblePaths2 = [
15442
- join15(webDir, ".next", "standalone", "server.js"),
15443
- join15(webDir, ".next", "standalone", "web", "server.js")
15776
+ join16(webDir, ".next", "standalone", "server.js"),
15777
+ join16(webDir, ".next", "standalone", "web", "server.js")
15444
15778
  ];
15445
15779
  for (const serverPath of possiblePaths2) {
15446
- if (existsSync20(serverPath)) {
15780
+ if (existsSync21(serverPath)) {
15447
15781
  return serverPath;
15448
15782
  }
15449
15783
  }
15450
15784
  return null;
15451
15785
  }
15452
15786
  function runCommand(command, args, cwd, env) {
15453
- return new Promise((resolve13) => {
15787
+ return new Promise((resolve14) => {
15454
15788
  const child = spawn2(command, args, {
15455
15789
  cwd,
15456
15790
  stdio: ["ignore", "pipe", "pipe"],
@@ -15465,10 +15799,10 @@ function runCommand(command, args, cwd, env) {
15465
15799
  output += data.toString();
15466
15800
  });
15467
15801
  child.on("close", (code) => {
15468
- resolve13({ success: code === 0, output });
15802
+ resolve14({ success: code === 0, output });
15469
15803
  });
15470
15804
  child.on("error", (err) => {
15471
- resolve13({ success: false, output: err.message });
15805
+ resolve14({ success: false, output: err.message });
15472
15806
  });
15473
15807
  });
15474
15808
  }
@@ -15483,13 +15817,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15483
15817
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
15484
15818
  return { process: null, port: actualPort };
15485
15819
  }
15486
- const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
15487
- const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
15820
+ const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
15821
+ const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
15488
15822
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
15489
15823
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
15490
15824
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
15491
15825
  const runtimeConfig = { apiBaseUrl: apiUrl };
15492
- const runtimeConfigPath = join15(webDir, "runtime-config.json");
15826
+ const runtimeConfigPath = join16(webDir, "runtime-config.json");
15493
15827
  try {
15494
15828
  writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15495
15829
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
@@ -15511,7 +15845,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15511
15845
  if (standaloneServerPath) {
15512
15846
  command = "node";
15513
15847
  args = ["server.js"];
15514
- cwd = dirname8(standaloneServerPath);
15848
+ cwd = dirname9(standaloneServerPath);
15515
15849
  webEnv.PORT = String(actualPort);
15516
15850
  webEnv.HOSTNAME = "0.0.0.0";
15517
15851
  if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
@@ -15552,10 +15886,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15552
15886
  let started = false;
15553
15887
  let exited = false;
15554
15888
  let exitCode = null;
15555
- const startedPromise = new Promise((resolve13) => {
15889
+ const startedPromise = new Promise((resolve14) => {
15556
15890
  const timeout = setTimeout(() => {
15557
15891
  if (!started && !exited) {
15558
- resolve13(false);
15892
+ resolve14(false);
15559
15893
  }
15560
15894
  }, startupTimeout);
15561
15895
  child.stdout?.on("data", (data) => {
@@ -15569,7 +15903,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15569
15903
  if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
15570
15904
  started = true;
15571
15905
  clearTimeout(timeout);
15572
- resolve13(true);
15906
+ resolve14(true);
15573
15907
  }
15574
15908
  });
15575
15909
  child.stderr?.on("data", (data) => {
@@ -15581,14 +15915,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15581
15915
  child.on("error", (err) => {
15582
15916
  if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
15583
15917
  clearTimeout(timeout);
15584
- resolve13(false);
15918
+ resolve14(false);
15585
15919
  });
15586
15920
  child.on("exit", (code) => {
15587
15921
  exited = true;
15588
15922
  exitCode = code;
15589
15923
  if (!started) {
15590
15924
  clearTimeout(timeout);
15591
- resolve13(false);
15925
+ resolve14(false);
15592
15926
  }
15593
15927
  webUIProcess = null;
15594
15928
  });
@@ -15611,7 +15945,7 @@ function stopWebUI() {
15611
15945
  }
15612
15946
  }
15613
15947
  async function createApp(options = {}) {
15614
- const app = new Hono9();
15948
+ const app = new Hono10();
15615
15949
  app.use("*", cors({
15616
15950
  origin: "*",
15617
15951
  // Allow all origins
@@ -15639,6 +15973,7 @@ async function createApp(options = {}) {
15639
15973
  app.route("/api/schedules", schedulesRouter);
15640
15974
  app.route("/api/orchestrator", orchestratorRouter);
15641
15975
  app.route("/api/mcp", mcpRouter);
15976
+ app.route("/api/skills", skills);
15642
15977
  app.route("/api/webhooks", webhooksRouter);
15643
15978
  const config = getConfig();
15644
15979
  const webhookToken = config?.webhooks?.token;
@@ -15704,7 +16039,7 @@ async function startServer(options = {}) {
15704
16039
  if (options.workingDirectory) {
15705
16040
  config.resolvedWorkingDirectory = options.workingDirectory;
15706
16041
  }
15707
- if (!existsSync20(config.resolvedWorkingDirectory)) {
16042
+ if (!existsSync21(config.resolvedWorkingDirectory)) {
15708
16043
  mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
15709
16044
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
15710
16045
  }
@@ -16263,14 +16598,14 @@ function generateOpenAPISpec() {
16263
16598
  init_config();
16264
16599
  init_semantic();
16265
16600
  init_db();
16266
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync21, statSync as statSync3 } from "fs";
16267
- import { resolve as resolve12, join as join16 } from "path";
16601
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync22, statSync as statSync4 } from "fs";
16602
+ import { resolve as resolve13, join as join17 } from "path";
16268
16603
  function getCliVersion() {
16269
- const here = dirname9(fileURLToPath5(import.meta.url));
16604
+ const here = dirname10(fileURLToPath5(import.meta.url));
16270
16605
  const candidates = [
16271
- join16(here, "..", "package.json"),
16272
- join16(here, "..", "..", "package.json"),
16273
- join16(process.cwd(), "package.json")
16606
+ join17(here, "..", "package.json"),
16607
+ join17(here, "..", "..", "package.json"),
16608
+ join17(process.cwd(), "package.json")
16274
16609
  ];
16275
16610
  for (const p of candidates) {
16276
16611
  try {
@@ -16315,18 +16650,18 @@ async function getActiveStream(baseUrl, sessionId) {
16315
16650
  return { hasActiveStream: false };
16316
16651
  }
16317
16652
  function promptApproval(rl, toolName, input) {
16318
- return new Promise((resolve13) => {
16653
+ return new Promise((resolve14) => {
16319
16654
  const inputStr = JSON.stringify(input);
16320
16655
  const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
16321
16656
  console.log(chalk.dim(` Command: ${truncatedInput}`));
16322
16657
  rl.question(chalk.yellow(` Approve? [y/n/a(lways)]: `), (answer) => {
16323
16658
  const lower = answer.toLowerCase().trim();
16324
16659
  if (lower === "a" || lower === "always") {
16325
- resolve13("always");
16660
+ resolve14("always");
16326
16661
  } else if (lower.startsWith("y")) {
16327
- resolve13("approve");
16662
+ resolve14("approve");
16328
16663
  } else {
16329
- resolve13("reject");
16664
+ resolve14("reject");
16330
16665
  }
16331
16666
  });
16332
16667
  });
@@ -16523,9 +16858,9 @@ async function runChat(options) {
16523
16858
  input: process.stdin,
16524
16859
  output: process.stdout
16525
16860
  });
16526
- const apiKey = await new Promise((resolve13) => {
16861
+ const apiKey = await new Promise((resolve14) => {
16527
16862
  keyRl.question(chalk.cyan("Enter your AI Gateway API key: "), (answer) => {
16528
- resolve13(answer.trim());
16863
+ resolve14(answer.trim());
16529
16864
  });
16530
16865
  });
16531
16866
  keyRl.close();
@@ -16921,7 +17256,7 @@ program.command("task").description("Run an autonomous task that completes witho
16921
17256
  let outputSchema;
16922
17257
  try {
16923
17258
  const schemaStr = options.schema;
16924
- if (existsSync21(schemaStr)) {
17259
+ if (existsSync22(schemaStr)) {
16925
17260
  outputSchema = JSON.parse(readFileSync10(schemaStr, "utf-8"));
16926
17261
  } else {
16927
17262
  outputSchema = JSON.parse(schemaStr);
@@ -16989,13 +17324,13 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
16989
17324
  let configLocation;
16990
17325
  if (options.global) {
16991
17326
  const appDataDir = ensureAppDataDirectory();
16992
- configPath = join16(appDataDir, "sparkecoder.config.json");
17327
+ configPath = join17(appDataDir, "sparkecoder.config.json");
16993
17328
  configLocation = "global";
16994
17329
  } else {
16995
- configPath = resolve12(process.cwd(), "sparkecoder.config.json");
17330
+ configPath = resolve13(process.cwd(), "sparkecoder.config.json");
16996
17331
  configLocation = "local";
16997
17332
  }
16998
- if (existsSync21(configPath) && !options.force) {
17333
+ if (existsSync22(configPath) && !options.force) {
16999
17334
  console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
17000
17335
  console.log(chalk.dim(` ${configPath}`));
17001
17336
  return;
@@ -17008,9 +17343,9 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
17008
17343
  });
17009
17344
  program.command("slack-setup").description("Interactively configure Slack integration (bot token + signing secret)").option("--bot-token <token>", "Slack bot token (xoxb-...)").option("--signing-secret <secret>", "Slack app signing secret").option("-g, --global", "Write to global config (~/.sparkecoder/sparkecoder.config.json)").action(async (options) => {
17010
17345
  const rl = createInterface({ input: process.stdin, output: process.stdout });
17011
- const ask = (label, fallback = "") => new Promise((resolve13) => {
17346
+ const ask = (label, fallback = "") => new Promise((resolve14) => {
17012
17347
  const suffix = fallback ? ` (${fallback})` : "";
17013
- rl.question(`${label}${suffix}: `, (answer) => resolve13(answer.trim() || fallback));
17348
+ rl.question(`${label}${suffix}: `, (answer) => resolve14(answer.trim() || fallback));
17014
17349
  });
17015
17350
  try {
17016
17351
  const botToken = options.botToken || await ask("Slack bot token (xoxb-...)");
@@ -17020,9 +17355,9 @@ program.command("slack-setup").description("Interactively configure Slack integr
17020
17355
  console.error(chalk.red("Both bot token and signing secret are required."));
17021
17356
  process.exit(1);
17022
17357
  }
17023
- const configPath = options.global ? join16(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
17358
+ const configPath = options.global ? join17(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve13(process.cwd(), "sparkecoder.config.json");
17024
17359
  let existing = {};
17025
- if (existsSync21(configPath)) {
17360
+ if (existsSync22(configPath)) {
17026
17361
  try {
17027
17362
  existing = JSON.parse(readFileSync10(configPath, "utf-8"));
17028
17363
  } catch {
@@ -17288,9 +17623,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17288
17623
  }
17289
17624
  const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
17290
17625
  console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
17291
- const cfDir = join16(homedir2(), ".cloudflared");
17292
- const certPath = join16(cfDir, "cert.pem");
17293
- if (!existsSync21(certPath)) {
17626
+ const cfDir = join17(homedir2(), ".cloudflared");
17627
+ const certPath = join17(cfDir, "cert.pem");
17628
+ if (!existsSync22(certPath)) {
17294
17629
  console.log(chalk.yellow("No Cloudflare login cert found."));
17295
17630
  if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
17296
17631
  run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
@@ -17334,8 +17669,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17334
17669
  return;
17335
17670
  }
17336
17671
  }
17337
- const credsFile = tunnel.credentials_file || join16(cfDir, `${tunnel.id}.json`);
17338
- if (!existsSync21(credsFile)) {
17672
+ const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
17673
+ if (!existsSync22(credsFile)) {
17339
17674
  console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
17340
17675
  }
17341
17676
  let hostname = options.hostname;
@@ -17358,7 +17693,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17358
17693
  console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
17359
17694
  }
17360
17695
  }
17361
- const configPath = join16(cfDir, "config.yml");
17696
+ const configPath = join17(cfDir, "config.yml");
17362
17697
  const configBody = `tunnel: ${tunnel.id}
17363
17698
  credentials-file: ${credsFile}
17364
17699
  ingress:
@@ -17369,7 +17704,7 @@ ingress:
17369
17704
  - service: http_status:404
17370
17705
  `;
17371
17706
  let wroteConfig = false;
17372
- if (existsSync21(configPath)) {
17707
+ if (existsSync22(configPath)) {
17373
17708
  const existing = readFileSync10(configPath, "utf8");
17374
17709
  if (existing.trim() === configBody.trim()) {
17375
17710
  console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
@@ -17485,7 +17820,7 @@ program.command("config").description("Show current configuration").option("-c,
17485
17820
  });
17486
17821
  program.command("index").description("Index repository for semantic search").option("--status", "Show index status instead of indexing").option("--full", "Force full re-index (ignore existing embeddings)").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").option("-v, --verbose", "Show detailed progress").action(async (options) => {
17487
17822
  try {
17488
- const workingDir = options.workingDir ? resolve12(options.workingDir) : process.cwd();
17823
+ const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
17489
17824
  let config = loadConfig(options.config, workingDir);
17490
17825
  const remoteUrl = config.resolvedRemoteServer.url;
17491
17826
  if (!remoteUrl) {
@@ -17619,7 +17954,7 @@ Indexing ${chalk.cyan(namespace)}...
17619
17954
  });
17620
17955
  program.command("search").description("Search indexed repository using semantic search").argument("<query>", "Search query").option("-n, --num <count>", "Number of results to show", "5").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").action(async (query, options) => {
17621
17956
  try {
17622
- const workingDir = options.workingDir ? resolve12(options.workingDir) : process.cwd();
17957
+ const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
17623
17958
  const config = loadConfig(options.config, workingDir);
17624
17959
  const remoteUrl = config.resolvedRemoteServer.url;
17625
17960
  if (!remoteUrl) {
@@ -17874,10 +18209,10 @@ program.command("request-permissions").description("Open System Settings to the
17874
18209
  });
17875
18210
  {
17876
18211
  let stateFilePath = function() {
17877
- return join16(ensureAppDataDirectory(), "recordings.json");
18212
+ return join17(ensureAppDataDirectory(), "recordings.json");
17878
18213
  }, readState = function() {
17879
18214
  const p = stateFilePath();
17880
- if (!existsSync21(p)) return [];
18215
+ if (!existsSync22(p)) return [];
17881
18216
  try {
17882
18217
  return JSON.parse(readFileSync10(p, "utf-8"));
17883
18218
  } catch {
@@ -17899,16 +18234,16 @@ program.command("request-permissions").description("Open System Settings to the
17899
18234
  const record = program.command("record").description("Start/stop screen recordings");
17900
18235
  record.command("start").description("Start a screen recording (returns id, path, pid as JSON)").option("--name <slug>", "Optional human-readable label for the recording").option("--dir <path>", "Output directory (default: ~/recordings)").action(async (opts) => {
17901
18236
  const { homedir: homedir2, platform: osPlatform } = await import("os");
17902
- const outDir = opts.dir ? resolve12(opts.dir.replace(/^~/, homedir2())) : join16(homedir2(), "recordings");
18237
+ const outDir = opts.dir ? resolve13(opts.dir.replace(/^~/, homedir2())) : join17(homedir2(), "recordings");
17903
18238
  mkdirSync10(outDir, { recursive: true });
17904
18239
  const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
17905
18240
  const ext = osPlatform() === "darwin" ? "mov" : "mp4";
17906
18241
  const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
17907
- const path = join16(outDir, filename);
18242
+ const path = join17(outDir, filename);
17908
18243
  let cmd;
17909
18244
  let args;
17910
18245
  if (osPlatform() === "darwin") {
17911
- cmd = existsSync21("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
18246
+ cmd = existsSync22("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
17912
18247
  args = ["-v", "-C", "-k", path];
17913
18248
  } else if (osPlatform() === "linux") {
17914
18249
  const display = process.env.DISPLAY || ":0.0";
@@ -18011,8 +18346,8 @@ program.command("request-permissions").description("Open System Settings to the
18011
18346
  }
18012
18347
  }
18013
18348
  writeState(rows.filter((r) => r.id !== id));
18014
- const fileExists = existsSync21(row.path);
18015
- const sizeMb = fileExists ? Math.round(statSync3(row.path).size / (1024 * 1024) * 10) / 10 : 0;
18349
+ const fileExists = existsSync22(row.path);
18350
+ const sizeMb = fileExists ? Math.round(statSync4(row.path).size / (1024 * 1024) * 10) / 10 : 0;
18016
18351
  console.log(JSON.stringify({
18017
18352
  id,
18018
18353
  path: row.path,
@@ -18046,7 +18381,7 @@ program.command("request-permissions").description("Open System Settings to the
18046
18381
  }
18047
18382
  }
18048
18383
  }
18049
- stopped.push({ id: r.id, path: r.path, ok: existsSync21(r.path) });
18384
+ stopped.push({ id: r.id, path: r.path, ok: existsSync22(r.path) });
18050
18385
  }
18051
18386
  writeState([]);
18052
18387
  console.log(JSON.stringify({ stopped }));