sparkecoder 0.1.120 → 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 (147) hide show
  1. package/dist/agent/index.js.map +1 -1
  2. package/dist/cli.js +379 -125
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +338 -84
  5. package/dist/index.js.map +1 -1
  6. package/dist/server/index.js +338 -84
  7. package/dist/server/index.js.map +1 -1
  8. package/dist/tools/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/web/.next/BUILD_ID +1 -1
  11. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  12. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  13. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  14. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
  15. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  16. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  17. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  18. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
  19. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
  20. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  21. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  22. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  23. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  30. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  31. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  33. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  36. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  37. package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  45. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  77. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  92. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  93. package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
  94. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
  95. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  97. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  98. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  100. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  101. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_6ab1f7b7._.js +3 -0
  102. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f3e6443f._.js → [root-of-the-server]__4de426bd._.js} +2 -2
  103. package/web/.next/standalone/web/.next/server/chunks/ssr/web_62ca4286._.js +3 -0
  104. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +3 -1
  105. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  106. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  107. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  108. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  109. package/web/.next/standalone/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
  110. package/web/.next/standalone/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
  111. package/web/.next/standalone/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  112. package/web/.next/standalone/web/.next/static/chunks/9b88f148788e4504.js +3 -0
  113. package/web/.next/standalone/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
  114. package/web/.next/standalone/web/.next/static/static/chunks/74ae1f17d607b2fc.js +7 -0
  115. package/web/.next/standalone/web/.next/static/static/chunks/883ea0d08f88e366.js +1 -0
  116. package/web/.next/standalone/web/.next/static/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  117. package/web/.next/standalone/web/.next/static/static/chunks/9b88f148788e4504.js +3 -0
  118. package/web/.next/standalone/web/.next/static/static/chunks/acb0fc66f5414af6.css +1 -0
  119. package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +464 -1
  120. package/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
  121. package/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
  122. package/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
  123. package/web/.next/static/chunks/9b88f148788e4504.js +3 -0
  124. package/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
  125. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +0 -3
  126. package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +0 -3
  127. package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
  128. package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
  129. package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
  130. package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
  131. package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +0 -1
  132. package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +0 -7
  133. package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +0 -1
  134. package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +0 -1
  135. package/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
  136. package/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
  137. package/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
  138. package/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
  139. /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  140. /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  141. /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
  142. /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  143. /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  144. /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
  145. /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
  146. /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
  147. /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → 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);
@@ -9707,11 +9742,11 @@ function waitForTaskQuestionAnswer(question) {
9707
9742
  if (pendingQuestions.has(k)) {
9708
9743
  return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
9709
9744
  }
9710
- return new Promise((resolve13, reject) => {
9745
+ return new Promise((resolve14, reject) => {
9711
9746
  pendingQuestions.set(k, {
9712
9747
  ...question,
9713
9748
  createdAt: /* @__PURE__ */ new Date(),
9714
- resolve: resolve13,
9749
+ resolve: resolve14,
9715
9750
  reject
9716
9751
  });
9717
9752
  });
@@ -10860,14 +10895,14 @@ ${p.text}` : p.text;
10860
10895
  const result = await recorder.encode();
10861
10896
  recorder.clear();
10862
10897
  if (!result) return [];
10863
- const { readFile: readFile12, unlink: unlink3 } = await import("fs/promises");
10898
+ const { readFile: readFile13, unlink: unlink4 } = await import("fs/promises");
10864
10899
  const uploadInfo = await storageQueries2.getUploadUrl(
10865
10900
  this.session.id,
10866
10901
  `browser-recording-${Date.now()}.mp4`,
10867
10902
  "video/mp4",
10868
10903
  "browser-recording"
10869
10904
  );
10870
- const fileData = await readFile12(result.path);
10905
+ const fileData = await readFile13(result.path);
10871
10906
  await fetch(uploadInfo.uploadUrl, {
10872
10907
  method: "PUT",
10873
10908
  headers: { "Content-Type": "video/mp4" },
@@ -10875,7 +10910,7 @@ ${p.text}` : p.text;
10875
10910
  });
10876
10911
  await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
10877
10912
  const dlInfo = await storageQueries2.getDownloadUrl(uploadInfo.fileId);
10878
- await unlink3(result.path).catch(() => {
10913
+ await unlink4(result.path).catch(() => {
10879
10914
  });
10880
10915
  console.log(`[TASK] Browser recording uploaded (${result.sizeBytes} bytes)`);
10881
10916
  return [dlInfo.downloadUrl];
@@ -10893,13 +10928,13 @@ ${p.text}` : p.text;
10893
10928
  try {
10894
10929
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
10895
10930
  if (!isRemoteConfigured2()) return [];
10896
- const { readFile: readFile12 } = await import("fs/promises");
10897
- 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");
10898
10933
  const urls = [];
10899
10934
  for (const filePath of filePaths) {
10900
10935
  try {
10901
- const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
10902
- const fileName = basename6(fullPath);
10936
+ const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
10937
+ const fileName = basename7(fullPath);
10903
10938
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
10904
10939
  const mimeMap = {
10905
10940
  pdf: "application/pdf",
@@ -10923,7 +10958,7 @@ ${p.text}` : p.text;
10923
10958
  contentType,
10924
10959
  "task-output"
10925
10960
  );
10926
- const fileData = await readFile12(fullPath);
10961
+ const fileData = await readFile13(fullPath);
10927
10962
  await fetch(uploadInfo.uploadUrl, {
10928
10963
  method: "PUT",
10929
10964
  headers: { "Content-Type": contentType },
@@ -10972,8 +11007,8 @@ ${p.text}` : p.text;
10972
11007
  this.pendingApprovals.set(toolCallId, await execution);
10973
11008
  options.onApprovalRequired?.(await execution);
10974
11009
  await sessionQueries.updateStatus(this.session.id, "waiting");
10975
- const approved = await new Promise((resolve13) => {
10976
- 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 });
10977
11012
  });
10978
11013
  const resolverData = approvalResolvers.get(toolCallId);
10979
11014
  approvalResolvers.delete(toolCallId);
@@ -11079,8 +11114,8 @@ async function withSessionLock(sessionId, fn) {
11079
11114
  state2.pending++;
11080
11115
  const prev = state2.tail;
11081
11116
  let release;
11082
- const next = new Promise((resolve13) => {
11083
- release = resolve13;
11117
+ const next = new Promise((resolve14) => {
11118
+ release = resolve14;
11084
11119
  });
11085
11120
  state2.tail = prev.then(() => next);
11086
11121
  await prev;
@@ -11693,17 +11728,17 @@ import chalk from "chalk";
11693
11728
  import ora from "ora";
11694
11729
  import "dotenv/config";
11695
11730
  import { createInterface } from "readline";
11696
- import { dirname as dirname9 } from "path";
11731
+ import { dirname as dirname10 } from "path";
11697
11732
  import { fileURLToPath as fileURLToPath5 } from "url";
11698
11733
 
11699
11734
  // src/server/index.ts
11700
11735
  import "dotenv/config";
11701
- import { Hono as Hono9 } from "hono";
11736
+ import { Hono as Hono10 } from "hono";
11702
11737
  import { serve } from "@hono/node-server";
11703
11738
  import { cors } from "hono/cors";
11704
11739
  import { logger } from "hono/logger";
11705
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
11706
- 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";
11707
11742
  import { spawn as spawn2 } from "child_process";
11708
11743
  import { createServer as createNetServer } from "net";
11709
11744
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -12878,7 +12913,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
12878
12913
  toolCallId,
12879
12914
  argsTextDelta: chunk
12880
12915
  }));
12881
- await new Promise((resolve13) => setTimeout(resolve13, 0));
12916
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
12882
12917
  }
12883
12918
  }
12884
12919
  function buildDevtoolsContextXml(sessionId) {
@@ -13133,7 +13168,7 @@ ${prompt}` });
13133
13168
  chunkIndex,
13134
13169
  chunkCount
13135
13170
  }));
13136
- await new Promise((resolve13) => setTimeout(resolve13, 0));
13171
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
13137
13172
  }
13138
13173
  const browserPort = progress.data?.browserStreamPort;
13139
13174
  const browserClosed = progress.data?.browserClosed;
@@ -13680,7 +13715,7 @@ agents.post(
13680
13715
  chunkIndex,
13681
13716
  chunkCount
13682
13717
  }));
13683
- await new Promise((resolve13) => setTimeout(resolve13, 0));
13718
+ await new Promise((resolve14) => setTimeout(resolve14, 0));
13684
13719
  }
13685
13720
  const browserPort = progress.data?.browserStreamPort;
13686
13721
  const browserClosed = progress.data?.browserClosed;
@@ -15188,6 +15223,224 @@ mcpRouter.post("/:id/test", async (c) => {
15188
15223
  return c.json(result);
15189
15224
  });
15190
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
+
15191
15444
  // src/server/auth/cf-access.ts
15192
15445
  init_config();
15193
15446
  import { createRemoteJWKSet, jwtVerify } from "jose";
@@ -15444,13 +15697,13 @@ var DEFAULT_WEB_PORT = 6969;
15444
15697
  var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
15445
15698
  function getWebDirectory() {
15446
15699
  try {
15447
- const currentDir = dirname8(fileURLToPath4(import.meta.url));
15448
- const webDir = resolve11(currentDir, "..", "web");
15449
- 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"))) {
15450
15703
  return webDir;
15451
15704
  }
15452
- const altWebDir = resolve11(currentDir, "..", "..", "web");
15453
- if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
15705
+ const altWebDir = resolve12(currentDir, "..", "..", "web");
15706
+ if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
15454
15707
  return altWebDir;
15455
15708
  }
15456
15709
  return null;
@@ -15473,18 +15726,18 @@ async function isSparkcoderWebRunning(port) {
15473
15726
  }
15474
15727
  }
15475
15728
  function isPortInUse(port) {
15476
- return new Promise((resolve13) => {
15729
+ return new Promise((resolve14) => {
15477
15730
  const server = createNetServer();
15478
15731
  server.once("error", (err) => {
15479
15732
  if (err.code === "EADDRINUSE") {
15480
- resolve13(true);
15733
+ resolve14(true);
15481
15734
  } else {
15482
- resolve13(false);
15735
+ resolve14(false);
15483
15736
  }
15484
15737
  });
15485
15738
  server.once("listening", () => {
15486
15739
  server.close();
15487
- resolve13(false);
15740
+ resolve14(false);
15488
15741
  });
15489
15742
  server.listen(port, "0.0.0.0");
15490
15743
  });
@@ -15508,30 +15761,30 @@ async function findWebPort(preferredPort) {
15508
15761
  return { port: preferredPort, alreadyRunning: false };
15509
15762
  }
15510
15763
  function hasProductionBuild(webDir) {
15511
- const buildIdPath = join15(webDir, ".next", "BUILD_ID");
15512
- return existsSync20(buildIdPath);
15764
+ const buildIdPath = join16(webDir, ".next", "BUILD_ID");
15765
+ return existsSync21(buildIdPath);
15513
15766
  }
15514
15767
  function hasSourceFiles(webDir) {
15515
- const appDir = join15(webDir, "src", "app");
15516
- const pagesDir = join15(webDir, "src", "pages");
15517
- const rootAppDir = join15(webDir, "app");
15518
- const rootPagesDir = join15(webDir, "pages");
15519
- 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);
15520
15773
  }
15521
15774
  function getStandaloneServerPath(webDir) {
15522
15775
  const possiblePaths2 = [
15523
- join15(webDir, ".next", "standalone", "server.js"),
15524
- join15(webDir, ".next", "standalone", "web", "server.js")
15776
+ join16(webDir, ".next", "standalone", "server.js"),
15777
+ join16(webDir, ".next", "standalone", "web", "server.js")
15525
15778
  ];
15526
15779
  for (const serverPath of possiblePaths2) {
15527
- if (existsSync20(serverPath)) {
15780
+ if (existsSync21(serverPath)) {
15528
15781
  return serverPath;
15529
15782
  }
15530
15783
  }
15531
15784
  return null;
15532
15785
  }
15533
15786
  function runCommand(command, args, cwd, env) {
15534
- return new Promise((resolve13) => {
15787
+ return new Promise((resolve14) => {
15535
15788
  const child = spawn2(command, args, {
15536
15789
  cwd,
15537
15790
  stdio: ["ignore", "pipe", "pipe"],
@@ -15546,10 +15799,10 @@ function runCommand(command, args, cwd, env) {
15546
15799
  output += data.toString();
15547
15800
  });
15548
15801
  child.on("close", (code) => {
15549
- resolve13({ success: code === 0, output });
15802
+ resolve14({ success: code === 0, output });
15550
15803
  });
15551
15804
  child.on("error", (err) => {
15552
- resolve13({ success: false, output: err.message });
15805
+ resolve14({ success: false, output: err.message });
15553
15806
  });
15554
15807
  });
15555
15808
  }
@@ -15564,13 +15817,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15564
15817
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
15565
15818
  return { process: null, port: actualPort };
15566
15819
  }
15567
- const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
15568
- 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"));
15569
15822
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
15570
15823
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
15571
15824
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
15572
15825
  const runtimeConfig = { apiBaseUrl: apiUrl };
15573
- const runtimeConfigPath = join15(webDir, "runtime-config.json");
15826
+ const runtimeConfigPath = join16(webDir, "runtime-config.json");
15574
15827
  try {
15575
15828
  writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15576
15829
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
@@ -15592,7 +15845,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15592
15845
  if (standaloneServerPath) {
15593
15846
  command = "node";
15594
15847
  args = ["server.js"];
15595
- cwd = dirname8(standaloneServerPath);
15848
+ cwd = dirname9(standaloneServerPath);
15596
15849
  webEnv.PORT = String(actualPort);
15597
15850
  webEnv.HOSTNAME = "0.0.0.0";
15598
15851
  if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
@@ -15633,10 +15886,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15633
15886
  let started = false;
15634
15887
  let exited = false;
15635
15888
  let exitCode = null;
15636
- const startedPromise = new Promise((resolve13) => {
15889
+ const startedPromise = new Promise((resolve14) => {
15637
15890
  const timeout = setTimeout(() => {
15638
15891
  if (!started && !exited) {
15639
- resolve13(false);
15892
+ resolve14(false);
15640
15893
  }
15641
15894
  }, startupTimeout);
15642
15895
  child.stdout?.on("data", (data) => {
@@ -15650,7 +15903,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15650
15903
  if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
15651
15904
  started = true;
15652
15905
  clearTimeout(timeout);
15653
- resolve13(true);
15906
+ resolve14(true);
15654
15907
  }
15655
15908
  });
15656
15909
  child.stderr?.on("data", (data) => {
@@ -15662,14 +15915,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15662
15915
  child.on("error", (err) => {
15663
15916
  if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
15664
15917
  clearTimeout(timeout);
15665
- resolve13(false);
15918
+ resolve14(false);
15666
15919
  });
15667
15920
  child.on("exit", (code) => {
15668
15921
  exited = true;
15669
15922
  exitCode = code;
15670
15923
  if (!started) {
15671
15924
  clearTimeout(timeout);
15672
- resolve13(false);
15925
+ resolve14(false);
15673
15926
  }
15674
15927
  webUIProcess = null;
15675
15928
  });
@@ -15692,7 +15945,7 @@ function stopWebUI() {
15692
15945
  }
15693
15946
  }
15694
15947
  async function createApp(options = {}) {
15695
- const app = new Hono9();
15948
+ const app = new Hono10();
15696
15949
  app.use("*", cors({
15697
15950
  origin: "*",
15698
15951
  // Allow all origins
@@ -15720,6 +15973,7 @@ async function createApp(options = {}) {
15720
15973
  app.route("/api/schedules", schedulesRouter);
15721
15974
  app.route("/api/orchestrator", orchestratorRouter);
15722
15975
  app.route("/api/mcp", mcpRouter);
15976
+ app.route("/api/skills", skills);
15723
15977
  app.route("/api/webhooks", webhooksRouter);
15724
15978
  const config = getConfig();
15725
15979
  const webhookToken = config?.webhooks?.token;
@@ -15785,7 +16039,7 @@ async function startServer(options = {}) {
15785
16039
  if (options.workingDirectory) {
15786
16040
  config.resolvedWorkingDirectory = options.workingDirectory;
15787
16041
  }
15788
- if (!existsSync20(config.resolvedWorkingDirectory)) {
16042
+ if (!existsSync21(config.resolvedWorkingDirectory)) {
15789
16043
  mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
15790
16044
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
15791
16045
  }
@@ -16344,14 +16598,14 @@ function generateOpenAPISpec() {
16344
16598
  init_config();
16345
16599
  init_semantic();
16346
16600
  init_db();
16347
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync21, statSync as statSync3 } from "fs";
16348
- 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";
16349
16603
  function getCliVersion() {
16350
- const here = dirname9(fileURLToPath5(import.meta.url));
16604
+ const here = dirname10(fileURLToPath5(import.meta.url));
16351
16605
  const candidates = [
16352
- join16(here, "..", "package.json"),
16353
- join16(here, "..", "..", "package.json"),
16354
- join16(process.cwd(), "package.json")
16606
+ join17(here, "..", "package.json"),
16607
+ join17(here, "..", "..", "package.json"),
16608
+ join17(process.cwd(), "package.json")
16355
16609
  ];
16356
16610
  for (const p of candidates) {
16357
16611
  try {
@@ -16396,18 +16650,18 @@ async function getActiveStream(baseUrl, sessionId) {
16396
16650
  return { hasActiveStream: false };
16397
16651
  }
16398
16652
  function promptApproval(rl, toolName, input) {
16399
- return new Promise((resolve13) => {
16653
+ return new Promise((resolve14) => {
16400
16654
  const inputStr = JSON.stringify(input);
16401
16655
  const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
16402
16656
  console.log(chalk.dim(` Command: ${truncatedInput}`));
16403
16657
  rl.question(chalk.yellow(` Approve? [y/n/a(lways)]: `), (answer) => {
16404
16658
  const lower = answer.toLowerCase().trim();
16405
16659
  if (lower === "a" || lower === "always") {
16406
- resolve13("always");
16660
+ resolve14("always");
16407
16661
  } else if (lower.startsWith("y")) {
16408
- resolve13("approve");
16662
+ resolve14("approve");
16409
16663
  } else {
16410
- resolve13("reject");
16664
+ resolve14("reject");
16411
16665
  }
16412
16666
  });
16413
16667
  });
@@ -16604,9 +16858,9 @@ async function runChat(options) {
16604
16858
  input: process.stdin,
16605
16859
  output: process.stdout
16606
16860
  });
16607
- const apiKey = await new Promise((resolve13) => {
16861
+ const apiKey = await new Promise((resolve14) => {
16608
16862
  keyRl.question(chalk.cyan("Enter your AI Gateway API key: "), (answer) => {
16609
- resolve13(answer.trim());
16863
+ resolve14(answer.trim());
16610
16864
  });
16611
16865
  });
16612
16866
  keyRl.close();
@@ -17002,7 +17256,7 @@ program.command("task").description("Run an autonomous task that completes witho
17002
17256
  let outputSchema;
17003
17257
  try {
17004
17258
  const schemaStr = options.schema;
17005
- if (existsSync21(schemaStr)) {
17259
+ if (existsSync22(schemaStr)) {
17006
17260
  outputSchema = JSON.parse(readFileSync10(schemaStr, "utf-8"));
17007
17261
  } else {
17008
17262
  outputSchema = JSON.parse(schemaStr);
@@ -17070,13 +17324,13 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
17070
17324
  let configLocation;
17071
17325
  if (options.global) {
17072
17326
  const appDataDir = ensureAppDataDirectory();
17073
- configPath = join16(appDataDir, "sparkecoder.config.json");
17327
+ configPath = join17(appDataDir, "sparkecoder.config.json");
17074
17328
  configLocation = "global";
17075
17329
  } else {
17076
- configPath = resolve12(process.cwd(), "sparkecoder.config.json");
17330
+ configPath = resolve13(process.cwd(), "sparkecoder.config.json");
17077
17331
  configLocation = "local";
17078
17332
  }
17079
- if (existsSync21(configPath) && !options.force) {
17333
+ if (existsSync22(configPath) && !options.force) {
17080
17334
  console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
17081
17335
  console.log(chalk.dim(` ${configPath}`));
17082
17336
  return;
@@ -17089,9 +17343,9 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
17089
17343
  });
17090
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) => {
17091
17345
  const rl = createInterface({ input: process.stdin, output: process.stdout });
17092
- const ask = (label, fallback = "") => new Promise((resolve13) => {
17346
+ const ask = (label, fallback = "") => new Promise((resolve14) => {
17093
17347
  const suffix = fallback ? ` (${fallback})` : "";
17094
- rl.question(`${label}${suffix}: `, (answer) => resolve13(answer.trim() || fallback));
17348
+ rl.question(`${label}${suffix}: `, (answer) => resolve14(answer.trim() || fallback));
17095
17349
  });
17096
17350
  try {
17097
17351
  const botToken = options.botToken || await ask("Slack bot token (xoxb-...)");
@@ -17101,9 +17355,9 @@ program.command("slack-setup").description("Interactively configure Slack integr
17101
17355
  console.error(chalk.red("Both bot token and signing secret are required."));
17102
17356
  process.exit(1);
17103
17357
  }
17104
- 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");
17105
17359
  let existing = {};
17106
- if (existsSync21(configPath)) {
17360
+ if (existsSync22(configPath)) {
17107
17361
  try {
17108
17362
  existing = JSON.parse(readFileSync10(configPath, "utf-8"));
17109
17363
  } catch {
@@ -17369,9 +17623,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17369
17623
  }
17370
17624
  const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
17371
17625
  console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
17372
- const cfDir = join16(homedir2(), ".cloudflared");
17373
- const certPath = join16(cfDir, "cert.pem");
17374
- if (!existsSync21(certPath)) {
17626
+ const cfDir = join17(homedir2(), ".cloudflared");
17627
+ const certPath = join17(cfDir, "cert.pem");
17628
+ if (!existsSync22(certPath)) {
17375
17629
  console.log(chalk.yellow("No Cloudflare login cert found."));
17376
17630
  if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
17377
17631
  run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
@@ -17415,8 +17669,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17415
17669
  return;
17416
17670
  }
17417
17671
  }
17418
- const credsFile = tunnel.credentials_file || join16(cfDir, `${tunnel.id}.json`);
17419
- if (!existsSync21(credsFile)) {
17672
+ const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
17673
+ if (!existsSync22(credsFile)) {
17420
17674
  console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
17421
17675
  }
17422
17676
  let hostname = options.hostname;
@@ -17439,7 +17693,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17439
17693
  console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
17440
17694
  }
17441
17695
  }
17442
- const configPath = join16(cfDir, "config.yml");
17696
+ const configPath = join17(cfDir, "config.yml");
17443
17697
  const configBody = `tunnel: ${tunnel.id}
17444
17698
  credentials-file: ${credsFile}
17445
17699
  ingress:
@@ -17450,7 +17704,7 @@ ingress:
17450
17704
  - service: http_status:404
17451
17705
  `;
17452
17706
  let wroteConfig = false;
17453
- if (existsSync21(configPath)) {
17707
+ if (existsSync22(configPath)) {
17454
17708
  const existing = readFileSync10(configPath, "utf8");
17455
17709
  if (existing.trim() === configBody.trim()) {
17456
17710
  console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
@@ -17566,7 +17820,7 @@ program.command("config").description("Show current configuration").option("-c,
17566
17820
  });
17567
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) => {
17568
17822
  try {
17569
- const workingDir = options.workingDir ? resolve12(options.workingDir) : process.cwd();
17823
+ const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
17570
17824
  let config = loadConfig(options.config, workingDir);
17571
17825
  const remoteUrl = config.resolvedRemoteServer.url;
17572
17826
  if (!remoteUrl) {
@@ -17700,7 +17954,7 @@ Indexing ${chalk.cyan(namespace)}...
17700
17954
  });
17701
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) => {
17702
17956
  try {
17703
- const workingDir = options.workingDir ? resolve12(options.workingDir) : process.cwd();
17957
+ const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
17704
17958
  const config = loadConfig(options.config, workingDir);
17705
17959
  const remoteUrl = config.resolvedRemoteServer.url;
17706
17960
  if (!remoteUrl) {
@@ -17955,10 +18209,10 @@ program.command("request-permissions").description("Open System Settings to the
17955
18209
  });
17956
18210
  {
17957
18211
  let stateFilePath = function() {
17958
- return join16(ensureAppDataDirectory(), "recordings.json");
18212
+ return join17(ensureAppDataDirectory(), "recordings.json");
17959
18213
  }, readState = function() {
17960
18214
  const p = stateFilePath();
17961
- if (!existsSync21(p)) return [];
18215
+ if (!existsSync22(p)) return [];
17962
18216
  try {
17963
18217
  return JSON.parse(readFileSync10(p, "utf-8"));
17964
18218
  } catch {
@@ -17980,16 +18234,16 @@ program.command("request-permissions").description("Open System Settings to the
17980
18234
  const record = program.command("record").description("Start/stop screen recordings");
17981
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) => {
17982
18236
  const { homedir: homedir2, platform: osPlatform } = await import("os");
17983
- 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");
17984
18238
  mkdirSync10(outDir, { recursive: true });
17985
18239
  const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
17986
18240
  const ext = osPlatform() === "darwin" ? "mov" : "mp4";
17987
18241
  const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
17988
- const path = join16(outDir, filename);
18242
+ const path = join17(outDir, filename);
17989
18243
  let cmd;
17990
18244
  let args;
17991
18245
  if (osPlatform() === "darwin") {
17992
- cmd = existsSync21("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
18246
+ cmd = existsSync22("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
17993
18247
  args = ["-v", "-C", "-k", path];
17994
18248
  } else if (osPlatform() === "linux") {
17995
18249
  const display = process.env.DISPLAY || ":0.0";
@@ -18092,8 +18346,8 @@ program.command("request-permissions").description("Open System Settings to the
18092
18346
  }
18093
18347
  }
18094
18348
  writeState(rows.filter((r) => r.id !== id));
18095
- const fileExists = existsSync21(row.path);
18096
- 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;
18097
18351
  console.log(JSON.stringify({
18098
18352
  id,
18099
18353
  path: row.path,
@@ -18127,7 +18381,7 @@ program.command("request-permissions").description("Open System Settings to the
18127
18381
  }
18128
18382
  }
18129
18383
  }
18130
- 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) });
18131
18385
  }
18132
18386
  writeState([]);
18133
18387
  console.log(JSON.stringify({ stopped }));