@surething/cockpit 1.0.231 → 1.0.233

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 (198) hide show
  1. package/.next-prod/BUILD_ID +1 -1
  2. package/.next-prod/app-path-routes-manifest.json +3 -3
  3. package/.next-prod/build-manifest.json +2 -2
  4. package/.next-prod/prerender-manifest.json +3 -3
  5. package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
  6. package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
  7. package/.next-prod/server/app/_global-error.html +1 -1
  8. package/.next-prod/server/app/_global-error.rsc +1 -1
  9. package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  11. package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  12. package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
  16. package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next-prod/server/app/_not-found.html +2 -2
  18. package/.next-prod/server/app/_not-found.rsc +3 -3
  19. package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
  20. package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
  21. package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
  22. package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  23. package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  24. package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  25. package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
  26. package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
  27. package/.next-prod/server/app/api/chat/codex/route.js +1 -1
  28. package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
  29. package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
  30. package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
  31. package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
  32. package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
  33. package/.next-prod/server/app/api/chat/ollama/route.js +1 -1
  34. package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
  35. package/.next-prod/server/app/api/chat/pty-input/route.js.nft.json +1 -1
  36. package/.next-prod/server/app/api/chat/route.js +1 -1
  37. package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
  38. package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
  39. package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
  40. package/.next-prod/server/app/api/commands/route.js +1 -1
  41. package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
  42. package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
  43. package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
  44. package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
  45. package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
  46. package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
  47. package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
  48. package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
  49. package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
  50. package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
  51. package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
  52. package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
  53. package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
  54. package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
  55. package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
  56. package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
  57. package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
  58. package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
  59. package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
  60. package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
  61. package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
  62. package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
  63. package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
  64. package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
  65. package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
  66. package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
  67. package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
  68. package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
  69. package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
  70. package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
  71. package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
  72. package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
  73. package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
  74. package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
  75. package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
  76. package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
  77. package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
  78. package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
  79. package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
  80. package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
  81. package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
  82. package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
  83. package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
  84. package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
  85. package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
  86. package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
  87. package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
  88. package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
  89. package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
  90. package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
  91. package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
  92. package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
  93. package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
  94. package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
  95. package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
  96. package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
  97. package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
  98. package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
  99. package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
  100. package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
  101. package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
  102. package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
  103. package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
  104. package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
  105. package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
  106. package/.next-prod/server/app/api/project-state/route.js +2 -2
  107. package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
  108. package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
  109. package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
  110. package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
  111. package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
  112. package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
  113. package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
  114. package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
  115. package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
  116. package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
  117. package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
  118. package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
  119. package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
  120. package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
  121. package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
  122. package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
  123. package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
  124. package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
  125. package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
  126. package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
  127. package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
  128. package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
  129. package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
  130. package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
  131. package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
  132. package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
  133. package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
  134. package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
  135. package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
  136. package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
  137. package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
  138. package/.next-prod/server/app/api/scheduled-tasks/route.js +2 -2
  139. package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
  140. package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
  141. package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
  142. package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
  143. package/.next-prod/server/app/api/session/[sessionId]/history/route.js +1 -1
  144. package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
  145. package/.next-prod/server/app/api/session-by-path/route.js +1 -1
  146. package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
  147. package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
  148. package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
  149. package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
  150. package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
  151. package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
  152. package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
  153. package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
  154. package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
  155. package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
  156. package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
  157. package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
  158. package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
  159. package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
  160. package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
  161. package/.next-prod/server/app/m/page.js.nft.json +1 -1
  162. package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
  163. package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  164. package/.next-prod/server/app/page.js.nft.json +1 -1
  165. package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
  166. package/.next-prod/server/app/project/page.js.nft.json +1 -1
  167. package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
  168. package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
  169. package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
  170. package/.next-prod/server/app-paths-manifest.json +3 -3
  171. package/.next-prod/server/chunks/{3633.js → 1035.js} +373 -3
  172. package/.next-prod/server/chunks/3863.js +2 -2
  173. package/.next-prod/server/chunks/4257.js +3 -3
  174. package/.next-prod/server/chunks/8916.js +1 -1
  175. package/.next-prod/server/chunks/9658.js +16 -6
  176. package/.next-prod/server/middleware-build-manifest.js +1 -1
  177. package/.next-prod/server/pages/404.html +2 -2
  178. package/.next-prod/server/pages/500.html +1 -1
  179. package/.next-prod/server/server-reference-manifest.json +1 -1
  180. package/.next-prod/static/chunks/217-92f800c2c438c550.js +15 -0
  181. package/.next-prod/static/chunks/{2576-439565bf051dc1ba.js → 2576-11e41242c7dd98f1.js} +13 -13
  182. package/.next-prod/static/chunks/6784-8a99d051f03bb100.js +0 -0
  183. package/.next-prod/static/chunks/{9428-5c287b255a9831e6.js → 9428-ffc16bc24c6e0da1.js} +7 -7
  184. package/.next-prod/static/chunks/app/{layout-03c738d41e660f72.js → layout-ac2f1f67df01cc89.js} +1 -1
  185. package/.next-prod/static/chunks/app/page-1f0a6f49656a4c52.js +1 -0
  186. package/.next-prod/static/chunks/app/project/page-1f0a6f49656a4c52.js +1 -0
  187. package/.next-prod/static/css/fa7218e60ff464b5.css +1 -0
  188. package/.next-prod/trace +13 -13
  189. package/.next-prod/trace-build +1 -1
  190. package/dist/scheduledTasks.mjs +482 -26
  191. package/package.json +1 -1
  192. package/.next-prod/static/chunks/217-a25b84ad39cf73ed.js +0 -5
  193. package/.next-prod/static/chunks/6784-b644510a0e060aac.js +0 -0
  194. package/.next-prod/static/chunks/app/page-677439ad5fd63732.js +0 -1
  195. package/.next-prod/static/chunks/app/project/page-677439ad5fd63732.js +0 -1
  196. package/.next-prod/static/css/ee516fc73c03aa88.css +0 -1
  197. /package/.next-prod/static/{eirTPmw6oXFp3wKgUVcXn → WfNQMmytl62j_8KtgCW9_}/_buildManifest.js +0 -0
  198. /package/.next-prod/static/{eirTPmw6oXFp3wKgUVcXn → WfNQMmytl62j_8KtgCW9_}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- [{"name":"run-webpack","duration":19913822,"timestamp":394761478,"id":14,"parentId":1,"tags":{},"startTime":1782573830076,"traceId":"19b1732556652e31"},{"name":"run-typescript","duration":18180157,"timestamp":414679089,"id":1603,"parentId":1,"tags":{},"startTime":1782573849994,"traceId":"19b1732556652e31"},{"name":"static-check","duration":1122581,"timestamp":432893720,"id":1606,"parentId":1,"tags":{},"startTime":1782573868209,"traceId":"19b1732556652e31"},{"name":"static-generation","duration":6167280,"timestamp":434127573,"id":1878,"parentId":1,"tags":{},"startTime":1782573869442,"traceId":"19b1732556652e31"},{"name":"collect-build-traces","duration":19577375,"timestamp":434016895,"id":1875,"parentId":1,"tags":{},"startTime":1782573869332,"traceId":"19b1732556652e31"},{"name":"telemetry-flush","duration":60,"timestamp":453598132,"id":1887,"parentId":1,"tags":{},"startTime":1782573888913,"traceId":"19b1732556652e31"},{"name":"next-build","duration":58977558,"timestamp":394620647,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782573829936,"traceId":"19b1732556652e31"}]
1
+ [{"name":"run-webpack","duration":20957061,"timestamp":151827038,"id":14,"parentId":1,"tags":{},"startTime":1782975840933,"traceId":"313e8d4206ab0a46"},{"name":"run-typescript","duration":19481077,"timestamp":172788182,"id":1603,"parentId":1,"tags":{},"startTime":1782975861895,"traceId":"313e8d4206ab0a46"},{"name":"static-check","duration":1214307,"timestamp":192311801,"id":1606,"parentId":1,"tags":{},"startTime":1782975881418,"traceId":"313e8d4206ab0a46"},{"name":"static-generation","duration":6666062,"timestamp":193689063,"id":1878,"parentId":1,"tags":{},"startTime":1782975882796,"traceId":"313e8d4206ab0a46"},{"name":"collect-build-traces","duration":21646916,"timestamp":193526726,"id":1875,"parentId":1,"tags":{},"startTime":1782975882633,"traceId":"313e8d4206ab0a46"},{"name":"telemetry-flush","duration":93,"timestamp":215178967,"id":1887,"parentId":1,"tags":{},"startTime":1782975904285,"traceId":"313e8d4206ab0a46"},{"name":"next-build","duration":63530263,"timestamp":151648812,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782975840755,"traceId":"313e8d4206ab0a46"}]
@@ -1166,6 +1166,382 @@ Ask for clarification on ambiguous points to avoid unnecessary code changes.
1166
1166
  Follow the KISS principle.
1167
1167
  Output your understanding only; do not modify code.`;
1168
1168
 
1169
+ // packages/feature/agent/src/server/lib/skillifyPrompt.ts
1170
+ var SKILLIFY_LABEL_ZH = "\u76EE\u6807\uFF1A";
1171
+ var SKILLIFY_LABEL_EN = "Target: ";
1172
+ var SKILLIFY_PROMPT_ZH = `---
1173
+ name: skillify
1174
+ description: "\u628A\u4E00\u6B21\u6210\u529F\u7684\u5904\u7406\u6D41\u7A0B / \u8C03\u7814\u65B9\u5F0F / \u5DE5\u5177\u7EC4\u5408 / \u4E60\u60EF\uFF0C\u6C89\u6DC0\u6210\u4E00\u4E2A\u53EF\u590D\u7528\u7684 Skill\u3002\u5148\u5206\u6790\u5386\u53F2\u4F1A\u8BDD\u6709\u6CA1\u6709\u503C\u5F97\u63D0\u53D6\u7684\u77E5\u8BC6\uFF0C\u518D\u63D0\u70BC\uFF0C\u6700\u540E\u6309\u6307\u5B9A\u76EE\u5F55\u4FDD\u5B58\u3002"
1175
+ argument-hint: "[\u653E\u7F6E\u76EE\u5F55] [\u8981 skillify \u7684\u5BF9\u8C61]"
1176
+ ---
1177
+
1178
+ # Skillify
1179
+
1180
+ \u628A"\u521A\u521A\u505A\u6210\u7684\u4E00\u4EF6\u4E8B"\u62BD\u8C61\u6210\u4E00\u4E2A\u53EF\u590D\u7528 skill\u3002\u8FD9\u662F\u4E00\u4E2A **\u5206\u6790 \u2192 \u63D0\u53D6 \u2192 \u4FDD\u5B58** \u7684\u6D41\u7A0B\uFF1A
1181
+
1182
+ - **\u7B2C\u4E00\u6B65\u6C38\u8FDC\u662F\u5206\u6790**\uFF1A\u5148\u5224\u65AD\u5386\u53F2\u4F1A\u8BDD / \u6700\u8FD1\u4E0A\u4E0B\u6587\u91CC\u6709\u6CA1\u6709\u503C\u5F97\u6C89\u6DC0\u6210 skill \u7684\u77E5\u8BC6\u3002\u6CA1\u6709\u5C31\u76F4\u63A5\u8BF4\u660E\u5E76\u505C\u4E0B\uFF0C\u4E0D\u8981\u786C\u51D1\u3002
1183
+ - \u653E\u7F6E\u76EE\u5F55\u7B49\u53C2\u6570**\u53EA\u5728\u6700\u540E\u4FDD\u5B58\u90A3\u4E00\u6B65\u624D\u9700\u8981**\uFF0C\u4E0D\u8981\u4E00\u4E0A\u6765\u5C31\u8FFD\u95EE\u76EE\u5F55\u3002
1184
+
1185
+ \u9002\u7528\u4E8E\u7528\u6237\u8BF4\uFF1A"\u628A\u8FD9\u4E2A\u80FD\u529B\u62BD\u8C61\u6210 skill""\u628A\u521A\u521A\u7684\u7ECF\u9A8C\u6559\u8BAD\u5199\u6210 skill""\u4EE5\u540E\u9047\u5230\u7C7B\u4F3C\u60C5\u51B5\u81EA\u52A8\u8FD9\u6837\u505A""skillify \u4E00\u4E0B""\u628A\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u6C89\u6DC0\u4E0B\u6765"\u3002
1186
+
1187
+ \u6838\u5FC3\u76EE\u6807\uFF1A**\u628A\u4E00\u6B21\u6210\u529F\u7ECF\u9A8C\u53D8\u6210\u7A33\u5B9A\u6D41\u7A0B\uFF0C\u800C\u4E0D\u662F\u628A\u4E34\u65F6\u5BF9\u8BDD\u590D\u5236\u8FDB prompt\u3002**
1188
+
1189
+ ## \u53C2\u6570\uFF08\u90FD\u53EF\u9009\uFF0C\u4FDD\u5B58\u65F6\u624D\u7528\uFF09
1190
+
1191
+ - **\u5C3E\u968F\u6587\u672C**\uFF08\`\u76EE\u6807\uFF1A\`\u540E\u9762\u90A3\u6BB5\uFF09= \u60F3 skillify \u7684\u5BF9\u8C61 / \u7EBF\u7D22\uFF1B\u4E0D\u7ED9\u5C31\u4ECE\u6700\u8FD1\u4E0A\u4E0B\u6587\u91CC\u81EA\u52A8\u63D0\u70BC\u3002
1192
+ - **\u653E\u7F6E\u76EE\u5F55 \`<skills-dir>\`** = skill \u843D\u76D8\u4F4D\u7F6E\uFF0Ccanonical source \u5199\u5230 \`<skills-dir>/<slug>/SKILL.md\`\u3002**\u5230"\u4FDD\u5B58"\u90A3\u4E00\u6B65\u624D\u9700\u8981**\uFF1B\u7528\u6237\u6CA1\u7ED9\u518D\u95EE\uFF0C\u4E0D\u8981\u5728\u5206\u6790\u524D\u5C31\u5148\u95EE\u3002
1193
+
1194
+ ## \u7B2C 1 \u6B65\uFF1A\u5206\u6790 \u2014\u2014 \u6709\u6CA1\u6709\u503C\u5F97\u6C89\u6DC0\u7684\u77E5\u8BC6\uFF08\u626B\u63CF \u2192 \u95F8\u95E8\uFF09
1195
+
1196
+ \u5148\u56DE\u7B54"**\u8BE5\u4E0D\u8BE5\u63D0**"\u3002\u5927\u591A\u6570\u5019\u9009\u6B7B\u5728\u8FD9\u4E00\u6B65\uFF0C\u800C\u4E0D\u662F\u5199\u4F5C\u3002
1197
+
1198
+ ### 1a. \u4E09\u76F8\u626B\u63CF\uFF1A\u522B\u6F0F\u6389\u9690\u5F62 skill
1199
+
1200
+ \u590D\u76D8\u521A\u505A\u5B8C\u7684\u4E8B\uFF0C\u6309\u4E09\u76F8\u5404\u626B\u4E00\u904D\uFF0C\u6BCF\u76F8\u53EA\u95EE\u4E00\u53E5\uFF1A**\u8FD9\u91CC\u6709\u6CA1\u6709\u4E00\u4E2A"\u975E\u663E\u7136\u7684\u52A8\u4F5C / \u5224\u65AD"\uFF1F**
1201
+
1202
+ | \u76F8 | \u626B\u4EC0\u4E48 | \u4E3A\u4EC0\u4E48\u5BB9\u6613\u88AB\u6F0F |
1203
+ |---|---|---|
1204
+ | \u53D1\u73B0 | \u600E\u4E48\u5B9A\u4F4D / \u89E6\u53D1 / \u8BCA\u65AD\u51FA\u771F\u95EE\u9898\u7684\uFF1Ftriage \u7528\u4E86\u54EA\u4E2A\u4FE1\u53F7\uFF1F | \u6CA1\u6709\u53EF\u89C1\u4EA4\u4ED8\u7269\uFF0C\u6700\u5E38\u88AB\u6F0F |
1205
+ | \u51B3\u7B56 | \u5361\u70B9\u4E0A\u90A3\u4E2A\u5224\u65AD call \u51ED\u4EC0\u4E48\uFF1F\u6709\u6CA1\u6709\u53EF\u590D\u7528\u7684\u5224\u636E\uFF1F | \u5F53\u6210"\u51ED\u7ECF\u9A8C"\uFF0C\u6CA1\u610F\u8BC6\u5230\u80FD\u56FA\u5316 |
1206
+ | \u89E3\u51B3 | \u6267\u884C\u9AA8\u67B6 / \u5DE5\u5177\u7EC4\u5408 / \u9A8C\u8BC1\u65B9\u5F0F\u80FD\u590D\u7528\u5417\uFF1F | \u770B\u5F97\u89C1\u3001\u6700\u5BB9\u6613\u53EA\u76EF\u7740\u5B83 |
1207
+
1208
+ \u626B\u63CF\u7684\u76EE\u7684\u662F**\u94FA\u5F00\u3001\u9632\u6F0F**\uFF1A\u4EBA\u5929\u7136\u53EA\u6CE8\u610F\u5230"\u89E3\u51B3"\u7C7B\uFF08\u6709\u4EA4\u4ED8\u7269\uFF09\uFF0C\u800C"\u53D1\u73B0 / \u51B3\u7B56"\u7C7B\u2014\u2014\u8BCA\u65AD\u548C\u5224\u65AD\u7684 move\u2014\u2014\u624D\u662F\u9AD8\u4EF7\u503C\u53C8\u9690\u5F62\u7684\u3002
1209
+
1210
+ > \u4E09\u76F8\u53EA\u7528\u6765**\u626B\u63CF\u5019\u9009**\uFF0C\u4E0D\u5199\u8FDB SKILL.md \u5F53\u7AE0\u8282\u3002skill \u6B63\u6587\u8981\u6309**\u5931\u8D25\u6A21\u5F0F / \u8C03\u67E5\u52A8\u4F5C**\u7EC4\u7EC7\uFF0C\u4E0D\u662F\u6309\u8FD9\u4E09\u7C7B\u5206\u7AE0\u3002
1211
+
1212
+ ### 1b. \u771F\u95F8\u95E8\uFF1A\u522B\u6EE5\u63D0
1213
+
1214
+ \u5BF9\u6BCF\u4E2A\u5019\u9009\uFF0C\u56DB\u95EE\u5168\u8FC7\u624D\u63D0\uFF1A
1215
+
1216
+ - [ ] **\u4F1A\u590D\u53D1**\uFF1A\u4EE5\u540E\u8FD8\u4F1A\u9047\u5230\u540C\u7C7B\u573A\u666F\uFF1F\uFF08\u4E00\u6B21\u6027\u7684 \u2192 \u5199\u5907\u5FD8\u5C31\u591F\uFF0C\u522B\u63D0 skill\uFF09
1217
+ - [ ] **\u975E\u663E\u7136**\uFF1A\u91CC\u9762\u6709\u4E00\u4E2A"\u4E0D\u67E5\u5C31\u4E0D\u4F1A"\u7684\u52A8\u4F5C / \u5224\u65AD\uFF1F\uFF08\u663E\u7136\u7684\u4E8B\u4E0D\u503C\u5F97\u56FA\u5316\uFF09
1218
+ - [ ] **\u4EE3\u4EF7**\uFF1A\u505A\u9519 / \u6F0F\u505A\u7684\u4EE3\u4EF7\u5927\uFF1F
1219
+ - [ ] **\u7A33\u5B9A**\uFF1A\u6D41\u7A0B\u7A33\u5B9A\uFF0C\u4E0D\u7ED1\u6B7B\u5728\u8FD9\u6B21\u7684\u4E34\u65F6\u4E0A\u4E0B\u6587 / \u6570\u636E\u4E0A\uFF1F
1220
+
1221
+ \u7F3A"\u975E\u663E\u7136"\u6216"\u4F1A\u590D\u53D1"\u57FA\u672C\u5C31\u522B\u63D0\u4E86\u3002
1222
+
1223
+ **\u5982\u679C\u5206\u6790\u4E0B\u6765\u6CA1\u6709\u4EFB\u4F55\u5019\u9009\u901A\u8FC7\u95F8\u95E8 \u2192 \u76F4\u63A5\u544A\u8BC9\u7528\u6237"\u8FD9\u6B21\u4E0D\u503C\u5F97\u505A skill"\u5E76\u8BF4\u660E\u539F\u56E0\uFF0C\u7136\u540E\u505C\u4E0B\u3002** \u4E0D\u8981\u4E3A\u4E86\u4EA7\u51FA\u800C\u786C\u51D1\uFF0C\u4E5F\u4E0D\u8981\u5728\u8FD9\u4E00\u6B65\u8FFD\u95EE\u653E\u7F6E\u76EE\u5F55\u3002
1224
+
1225
+ ### 1c. \u5408\u5E76\u8FD8\u662F\u62C6\u5206\uFF1A\u8FB9\u754C\u95EE\u9898
1226
+
1227
+ \u5982\u679C\u4E00\u4E2A\u573A\u666F\u91CC\u53D1\u73B0 / \u51B3\u7B56 / \u89E3\u51B3\u4E09\u76F8**\u90FD**\u8E66\u51FA\u4E86\u5019\u9009\uFF0C\u8981\u51B3\u5B9A\u5B83\u4EEC\u88C5\u8FDB**\u4E00\u4E2A\u95ED\u73AF skill**\u8FD8\u662F**\u62C6\u6210\u51E0\u4E2A\u7EC4\u5408**\u3002\u6309\u4E09\u95EE\u5224\uFF1A
1228
+
1229
+ - **\u4F1A\u5355\u72EC\u8C03\u7528\u67D0\u4E00\u76F8\u5417\uFF1F** \u4F1A \u2192 \u62C6\uFF1B\u6C38\u8FDC\u4ECE\u5934\u8FDB \u2192 \u503E\u5411\u5408\u5E76\u3002
1230
+ - **\u4EF7\u503C\u5728\u76F8\u5185\u8FD8\u662F\u76F8\u95F4\u4EA4\u63A5\uFF1F** \u5728\u4EA4\u63A5\uFF08\u4E0A\u6E38\u671F\u671B\u72B6\u6001 / \u5951\u7EA6\u8981\u5E26\u7ED9\u4E0B\u6E38\u505A\u9A8C\u8BC1\uFF09\u2192 \u5408\u5E76\uFF1B\u6BCF\u76F8\u81EA\u6210\u52A8\u4F5C\u3001\u4EA4\u63A5\u6CA1\u4FE1\u606F\u91CF \u2192 \u53EF\u62C6\u3002
1231
+ - **\u5408\u5E76\u4F1A\u8BA9\u4E00\u4E2A\u6267\u884C\u5355\u5143\u540C\u65F6\u625B\u6B63\u4EA4\u7684\u5931\u8D25\u6A21\u5F0F\u5417\uFF1F** \u4F1A\uFF08\u5982\u9759\u6001\u5E7F\u5EA6 vs \u52A8\u6001\u63A8\u6F14\u6DF1\u5EA6\uFF09\u2192 \u62C6\u3002
1232
+ - **\u8FD9\u4E00\u76F8\u5728\u522B\u7684\u95EE\u9898\u91CC\u4E5F\u5148\u8DD1\u5417\uFF1F** \u6247\u51FA \u22652~3 \u2192 \u62C6\u6210\u53EF\u590D\u7528\u7684 leaf\u3002
1233
+
1234
+ **\u7B2C\u4E09\u6001\uFF08\u4E24\u8005\u90FD\u8981\uFF09**\uFF1A\u95ED\u73AF\u4E0D\u80FD\u65AD\u3001\u4F46\u76F8\u53C8\u8981\u6DF1\u5EA6 / \u590D\u7528\u65F6\uFF0C\u7528 **orchestrator + leaf** \u2014\u2014 \u4E00\u4E2A\u95ED\u73AF skill \u6301\u6709\u5951\u7EA6\u4E0E\u9A8C\u8BC1\uFF0C\u628A\u6B63\u4EA4\u7684\u76F8\u59D4\u6D3E\u7ED9\u53EF\u590D\u7528\u7684 sub-skill\u3002
1235
+
1236
+ **\u9ED8\u8BA4\u5148\u6309\u4E00\u4E2A\u95ED\u73AF skill \u5199**\u3002\u53EA\u6709\u51FA\u73B0\u5B9E\u6D4B\u4FE1\u53F7\uFF08\u67D0\u76F8\u88AB\u7B2C\u4E8C\u4E2A\u573A\u666F\u590D\u7528\uFF0C\u6216\u6CE8\u610F\u529B\u88AB\u644A\u8584\u5BFC\u81F4\u6DF1\u5EA6\u4E0D\u591F\uFF09\u624D\u62C6\u3002
1237
+
1238
+ ### 1d. \u5F52\u7C7B\uFF1A\u51B3\u5B9A\u600E\u4E48\u5199\uFF0C\u4E0D\u51B3\u5B9A\u63D0\u4E0D\u63D0
1239
+
1240
+ | \u7C7B\u578B | \u793A\u4F8B | Skill \u91CD\u70B9 |
1241
+ |---|---|---|
1242
+ | \u8C03\u7814\u6D41\u7A0B | \u62C9\u6570\u636E\u6E90 \u2192 \u5EFA\u65F6\u95F4\u7EBF \u2192 \u5B9A\u4F4D\u95EE\u9898 | \u8BC1\u636E\u6E90\u3001\u6B65\u9AA4\u3001\u8F93\u51FA\u6A21\u677F |
1243
+ | Debug \u6D41\u7A0B | \u590D\u73B0 bug \u2192 \u7EA2\u6D4B \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 | \u4E0D\u53D8\u91CF\u3001\u6D4B\u8BD5\u3001\u9A8C\u6536 |
1244
+ | \u5DE5\u5177\u7EC4\u5408 | \u591A\u4E2A\u65E5\u5FD7/\u76D1\u63A7/\u5206\u6790\u5DE5\u5177\u4E32\u8054 | \u67E5\u8BE2\u987A\u5E8F\u3001\u5173\u8054 ID |
1245
+ | \u4E60\u60EF/\u89C4\u8303 | \u76EE\u5F55\u7ED3\u6784\u3001\u547D\u540D\u3001\u653E\u7F6E\u89C4\u5219 | \u8DEF\u5F84\u89C4\u5219\u3001\u547D\u540D\u89C4\u5219 |
1246
+ | \u5199\u4F5C\u6A21\u677F | issue / PR / \u590D\u76D8\u62A5\u544A | \u7ED3\u6784\u3001\u8BED\u6C14\u3001\u7981\u6B62\u9879 |
1247
+
1248
+ **\u4E0D\u8981**\u628A\u4E00\u6B21\u6027\u7684\u7EC6\u8282\u5199\u8FDB skill\uFF08\u67D0\u4E2A\u4E34\u65F6 session\u3001\u67D0\u6B21\u5177\u4F53 chat \u7684\u5B8C\u6574\u5185\u5BB9\u3001\u4E34\u65F6\u9A8C\u8BC1\u7801\u7B49\uFF09\u3002
1249
+
1250
+ ## \u7B2C 2 \u6B65\uFF1A\u63D0\u53D6 \u2014\u2014 \u63D0\u70BC\u7A33\u5B9A\u539F\u5219\u5E76\u8D77\u8349
1251
+
1252
+ > \u82E5 1c \u5224\u5B9A\u4E3A\u62C6\u5206\u6216 orchestrator + leaf\uFF0C\u5BF9**\u6BCF\u4E2A**\u8981\u843D\u5730\u7684 skill \u5404\u8DD1\u4E00\u904D\u7B2C 2 \u6B65\uFF08\u5404\u81EA\u72EC\u7ACB\u76EE\u5F55\uFF09\u3002
1253
+
1254
+ ### 2a. \u63D0\u70BC\u7A33\u5B9A\u539F\u5219
1255
+
1256
+ \u628A\u7ECF\u9A8C\u62C6\u6210\uFF1A
1257
+
1258
+ - **\u89E6\u53D1\u6761\u4EF6**\uFF1A\u4EC0\u4E48\u65F6\u5019\u5E94\u8BE5\u7528\u8FD9\u4E2A skill
1259
+ - **\u8F93\u5165**\uFF1A\u7528\u6237\u53EF\u80FD\u63D0\u4F9B\u4EC0\u4E48\uFF0C\u7F3A\u4EC0\u4E48\u65F6\u95EE\u4EC0\u4E48
1260
+ - **\u8BC1\u636E\u6E90 / \u5DE5\u5177**\uFF1A\u9700\u8981\u67E5\u54EA\u4E9B source\u3001\u6587\u4EF6\u3001CLI\u3001\u65E5\u5FD7
1261
+ - **\u6B65\u9AA4**\uFF1A\u7A33\u5B9A\u53EF\u590D\u7528\u7684\u6267\u884C\u987A\u5E8F
1262
+ - **\u8F93\u51FA**\uFF1A\u6700\u7EC8\u7ED9\u7528\u6237\u4EC0\u4E48\uFF0C\u6216\u521B\u5EFA\u4EC0\u4E48 artifact
1263
+ - **\u8FB9\u754C**\uFF1A\u4E0D\u8981\u505A\u4EC0\u4E48\uFF0C\u4EC0\u4E48\u60C5\u51B5\u4E0B\u505C\u6B62/\u8BE2\u95EE
1264
+ - **\u9A8C\u8BC1**\uFF1A\u5982\u4F55\u786E\u8BA4 skill \u6709\u6548
1265
+
1266
+ \u5199\u539F\u5219\uFF0C\u4E0D\u5199\u6D41\u6C34\u8D26\u3002
1267
+
1268
+ ### 2b. \u9009 slug
1269
+
1270
+ Slug \u8981 lowercase\u3001alphanumeric + hyphen\u3001\u7B80\u77ED\u6E05\u695A\u3001\u52A8\u8BCD/\u4EFB\u52A1\u6216\u4E3B\u9898\u5BFC\u5411\u3002\u8D77\u8349\u524D\u5148 \`ls <skills-dir>/\` \u770B\u4E00\u773C\uFF0C\u907F\u514D\u51B2\u7A81\u5E76\u4E0E\u5DF2\u6709\u547D\u540D\u98CE\u683C\u4E00\u81F4\uFF08\u4FDD\u5B58\u76EE\u5F55\u5DF2\u77E5\u65F6\uFF09\u3002
1271
+
1272
+ ### 2c. \u8D77\u8349 SKILL.md
1273
+
1274
+ Frontmatter \u7528\u901A\u7528\u517C\u5BB9\u683C\u5F0F\uFF1A
1275
+
1276
+ \`\`\`yaml
1277
+ ---
1278
+ name: <slug-or-display-name>
1279
+ description: "\u4E00\u53E5\u8BDD\u8BF4\u660E skill \u505A\u4EC0\u4E48\uFF0C\u4EC0\u4E48\u65F6\u5019\u7528\u3002"
1280
+ argument-hint: "<\u53EF\u9009\uFF0C\u63CF\u8FF0\u53C2\u6570>"
1281
+ alwaysAllow: ["Bash"] # \u53EF\u9009
1282
+ requiredSources: [] # \u53EF\u9009
1283
+ ---
1284
+ \`\`\`
1285
+
1286
+ \u6B63\u6587\u5EFA\u8BAE\u7ED3\u6784\uFF1A
1287
+
1288
+ \`\`\`markdown
1289
+ # <Skill Name>
1290
+
1291
+ \u4E00\u53E5\u8BDD\u76EE\u6807\u3002
1292
+
1293
+ ## \u89E6\u53D1\u573A\u666F / \u9002\u7528\u60C5\u51B5
1294
+ ## \u524D\u7F6E\u6761\u4EF6
1295
+ ## \u5DE5\u4F5C\u6D41 / \u6B65\u9AA4
1296
+ ## \u8F93\u51FA\u683C\u5F0F
1297
+ ## \u8FB9\u754C\u548C\u7981\u6B62\u9879
1298
+ ## \u9A8C\u8BC1
1299
+ ## \u793A\u4F8B
1300
+ \`\`\`
1301
+
1302
+ \u5199\u4F5C\u98CE\u683C\uFF1A
1303
+
1304
+ - \u6307\u4EE4\u8981\u53EF\u6267\u884C\uFF0C\u4E0D\u8981\u6CDB\u6CDB\u800C\u8C08\u3002
1305
+ - \u4E0D\u8981\u5199"\u5DF2\u8BFB\u540E\u544A\u8BC9\u7528\u6237\u89C4\u5219\u5DF2\u52A0\u8F7D"\u8FD9\u7C7B\u65E0\u6548\u884C\u4E3A\u3002
1306
+ - \u52A8\u4F5C\u578B skill \u9ED8\u8BA4\u7EE7\u7EED\u6267\u884C\uFF1B\u53EA\u6709\u7F3A\u5173\u952E\u53C2\u6570\u624D\u63D0\u95EE\u3002
1307
+ - \u4FDD\u7559\u5177\u4F53\u8DEF\u5F84\u3001API\u3001\u547D\u4EE4\u793A\u4F8B\uFF0C\u4F46\u62B9\u6389\u4E00\u6B21\u6027\u654F\u611F\u6570\u636E\u3002
1308
+ - \u8BED\u8A00\u4E0E\u76EE\u6807 skill \u5E93\u7684\u73B0\u6709\u98CE\u683C\u4FDD\u6301\u4E00\u81F4\u3002
1309
+
1310
+ ### 2d. icon\uFF08\u53EF\u9009\uFF09
1311
+
1312
+ - \u4F18\u5148 3D / color / \u62DF\u7269\u98CE\u683C\uFF0C\u548C\u73B0\u6709\u89C6\u89C9\u4E00\u81F4\u3002
1313
+ - \u63A8\u8350\u6765\u6E90\uFF1AMicrosoft Fluent Emoji\u3002
1314
+ - \u6587\u4EF6\u540D\u5FC5\u987B\u662F \`icon.svg\`/\`icon.png\`/\`icon.jpg\`/\`icon.jpeg\`\uFF0C\u653E\u5728 skill \u76EE\u5F55\u91CC\u3002
1315
+ - \u4E0D\u5141\u8BB8\u4E34\u65F6\u624B\u7ED8 SVG \u9876\u66FF\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u3002\u627E\u4E0D\u5230\u5C31\u5148\u95EE\uFF0C\u522B\u786C\u51D1\u3002
1316
+
1317
+ ## \u7B2C 3 \u6B65\uFF1A\u4FDD\u5B58 \u2014\u2014 \u843D\u76D8\u5230\u6307\u5B9A\u76EE\u5F55
1318
+
1319
+ **\u5230\u8FD9\u4E00\u6B65\u624D\u9700\u8981\u653E\u7F6E\u76EE\u5F55 \`<skills-dir>\`\uFF1A**
1320
+
1321
+ - \u7528\u6237\u4E00\u5F00\u59CB\u5C31\u7ED9\u4E86\u76EE\u5F55 \u2192 \u76F4\u63A5\u7528\u3002
1322
+ - \u6CA1\u7ED9 \u2192 **\u73B0\u5728**\u95EE\u7528\u6237\u653E\u54EA\uFF0C\u4E0D\u8981\u64C5\u81EA\u9009\u76EE\u5F55\u3002
1323
+
1324
+ \u7EA6\u5B9A\uFF1A
1325
+
1326
+ 1. **\u4E00\u4E2A skill \u4E00\u4E2A\u76EE\u5F55**\uFF1A\`<skills-dir>/<slug>/SKILL.md\`\uFF08\u5FC5\u9700\uFF09+ \u53EF\u9009 \`icon.svg\` / \`references/\` / \u8F85\u52A9\u811A\u672C\u3002
1327
+ 2. **\u4E0D\u8981\u5728 \`<skills-dir>\` \u6839\u76EE\u5F55\u76F4\u63A5\u653E SKILL.md**\uFF1A\u5373\u4F7F\u5355\u6587\u4EF6 skill \u4E5F\u8981\u5EFA\u81EA\u5DF1\u7684\u5B50\u76EE\u5F55\u3002
1328
+ 3. **\u8F85\u52A9\u6570\u636E/\u811A\u672C\u653E\u540C\u76EE\u5F55**\uFF0C\u8BA9 skill \u81EA\u5305\u542B\u3002
1329
+
1330
+ \`\`\`bash
1331
+ mkdir -p <skills-dir>/<slug>
1332
+ # \u5199 SKILL.md \u5230\u8BE5\u76EE\u5F55\uFF1B\u5982\u6709\u8F85\u52A9\u811A\u672C/\u6570\u636E\u4E00\u5E76\u653E\u5165
1333
+ \`\`\`
1334
+
1335
+ \`<skills-dir>/\` \u5C31\u662F canonical source\uFF0C\u4E0D\u9700\u8981\u518D symlink \u5230\u522B\u5904\u3002
1336
+
1337
+ ## \u9A8C\u8BC1
1338
+
1339
+ \`\`\`bash
1340
+ ls -la <skills-dir>/<slug>/
1341
+ \`\`\`
1342
+
1343
+ - [ ] \`SKILL.md\` \u5B58\u5728\u4E14 frontmatter \u5408\u6CD5\uFF0C\`name\`/\`description\` \u975E\u7A7A
1344
+ - [ ] \u6B63\u6587\u975E\u7A7A\u3001\u6709\u53EF\u6267\u884C\u6B65\u9AA4
1345
+ - [ ] \u6CA1\u6709\u5199\u6B7B\u654F\u611F\u4FE1\u606F\uFF08\u8D26\u53F7\u3001token\u3001\u4E34\u65F6 session id\uFF09
1346
+ - [ ] slug \u4E0D\u4E0E\u5DF2\u6709\u51B2\u7A81
1347
+
1348
+ ## \u8F93\u51FA\u7ED9\u7528\u6237
1349
+
1350
+ \u5B8C\u6210\u540E\u7B80\u77ED\u8BF4\u660E\uFF1Askill slug\u3001\u6E90\u6587\u4EF6\u8DEF\u5F84\uFF08\`<skills-dir>/<slug>/SKILL.md\`\uFF09\u3001\u662F\u5426\u52A0\u4E86 icon\u3001\u662F\u5426\u542B\u8F85\u52A9\u811A\u672C/\u6570\u636E\u3002\u4E0D\u8981\u7C98\u8D34\u5B8C\u6574 SKILL.md\uFF0C\u9664\u975E\u7528\u6237\u8981\u6C42\u3002
1351
+
1352
+ ## \u5173\u952E\u539F\u5219
1353
+
1354
+ - \u7528\u6237\u8981\u7684\u662F**\u80FD\u529B\u6C89\u6DC0**\uFF0C\u4E0D\u662F\u590D\u5236\u5BF9\u8BDD\u3002
1355
+ - **\u5206\u6790\u5728\u5148**\uFF1A\u5148\u5224\u65AD\u6709\u6CA1\u6709\u503C\u5F97\u6C89\u6DC0\u7684\u77E5\u8BC6\uFF0C\u6CA1\u6709\u5C31\u505C\uFF0C\u522B\u786C\u51D1\uFF0C\u4E5F\u522B\u63D0\u524D\u95EE\u76EE\u5F55\u3002
1356
+ - canonical source \u6C38\u8FDC\u5728 \`<skills-dir>/<slug>/\`\uFF0C\u5355\u6587\u4EF6 skill \u4E5F\u8981\u6709\u81EA\u5DF1\u7684\u5B50\u76EE\u5F55\u3002
1357
+ - \u533A\u5206\u4E8B\u5B9E / \u63A8\u6D4B / \u5F85\u786E\u8BA4\uFF0C\u4E0D\u8981\u8FC7\u65E9\u628A\u89E3\u51B3\u65B9\u6848\u5199\u6B7B\u3002`;
1358
+ var SKILLIFY_PROMPT_EN = `---
1359
+ name: skillify
1360
+ description: "Distil a successful workflow / investigation / tool-combo / habit into a reusable Skill. First analyze whether the conversation holds knowledge worth extracting, then distil it, and finally save it to the given directory."
1361
+ argument-hint: "[placement directory] [target to skillify]"
1362
+ ---
1363
+
1364
+ # Skillify
1365
+
1366
+ Abstract "the thing you just pulled off" into a reusable skill. This is an **analyze \u2192 extract \u2192 save** flow:
1367
+
1368
+ - **Step 1 is always analysis**: first decide whether the conversation / recent context holds knowledge worth distilling into a skill. If not, say so and stop \u2014 never force it.
1369
+ - The placement directory and other args are **only needed at the final save step** \u2014 do NOT front-load a "where should it go?" question.
1370
+
1371
+ Applies when the user says: "abstract this into a skill", "write up what we just learned as a skill", "do this automatically next time", "skillify this", "capture this workflow".
1372
+
1373
+ Core goal: **turn a one-time success into a stable procedure, not copy a transient chat into a prompt.**
1374
+
1375
+ ## Arguments (all optional; used only at save time)
1376
+
1377
+ - **Trailing text** (after \`Target: \`) = the object / lead to skillify; if omitted, distil from recent context.
1378
+ - **Placement directory \`<skills-dir>\`** = where the skill lands; the canonical source goes to \`<skills-dir>/<slug>/SKILL.md\`. **Only needed at the "save" step**; ask only if the user hasn't provided it \u2014 never ask before analysis.
1379
+
1380
+ ## Step 1: Analyze \u2014 is there knowledge worth capturing (scan \u2192 gate)
1381
+
1382
+ Answer "**should this even be extracted**" first. Most candidates die here, not in the writing.
1383
+
1384
+ ### 1a. Three-phase scan: don't miss invisible skills
1385
+
1386
+ Review what you just did, sweeping each phase once, asking one question per phase: **is there a "non-obvious action / judgement" here?**
1387
+
1388
+ | Phase | What to scan | Why it's easily missed |
1389
+ |---|---|---|
1390
+ | Discovery | How did you locate / trigger / diagnose the real problem? Which signal did triage use? | No visible deliverable \u2014 most often missed |
1391
+ | Decision | What justified the judgement call at the sticking point? Any reusable criterion? | Treated as "intuition", not realized it can be codified |
1392
+ | Solution | Is the execution skeleton / tool-combo / verification reusable? | Visible \u2014 easiest to fixate on |
1393
+
1394
+ The scan is about **breadth, anti-omission**: people naturally notice only "solution" (it has deliverables), while "discovery / decision" \u2014 the diagnosing and judging moves \u2014 are high-value yet invisible.
1395
+
1396
+ > The three phases are only for **scanning candidates**, not chapters in SKILL.md. The body should be organized by **failure mode / investigation action**, not by these three categories.
1397
+
1398
+ ### 1b. The real gate: don't over-extract
1399
+
1400
+ For each candidate, all four must pass:
1401
+
1402
+ - [ ] **Recurs**: will you hit this kind of scenario again? (One-off \u2192 a note is enough, no skill.)
1403
+ - [ ] **Non-obvious**: is there an action / judgement you "wouldn't know without looking it up"? (Obvious things aren't worth codifying.)
1404
+ - [ ] **Cost**: is the cost of getting it wrong / missing it high?
1405
+ - [ ] **Stable**: is the procedure stable, not bound to this session's transient context / data?
1406
+
1407
+ Missing "non-obvious" or "recurs" \u2192 basically drop it.
1408
+
1409
+ **If nothing passes the gate \u2192 tell the user "not worth a skill this time", explain why, and stop.** Do not force a deliverable, and do not ask for a placement directory at this step.
1410
+
1411
+ ### 1c. Merge or split: the boundary question
1412
+
1413
+ If discovery / decision / solution **all** surface candidates in one scenario, decide whether they go into **one closed-loop skill** or **split into a combination**:
1414
+
1415
+ - **Will any single phase be invoked alone?** Yes \u2192 split; always entered from the top \u2192 lean merge.
1416
+ - **Is the value inside a phase or at the handoff?** At the handoff (upstream expected-state / contract must reach downstream for verification) \u2192 merge; each phase self-contained, handoff carries no info \u2192 splittable.
1417
+ - **Would merging make one unit carry orthogonal failure modes?** Yes (e.g. static breadth vs dynamic-reasoning depth) \u2192 split.
1418
+ - **Does this phase run first in other problems too?** Fan-out \u22652\u20133 \u2192 split into a reusable leaf.
1419
+
1420
+ **Third state (need both)**: when the loop must stay intact but a phase needs depth / reuse, use **orchestrator + leaf** \u2014 one closed-loop skill holds the contract and verification, delegating orthogonal phases to reusable sub-skills.
1421
+
1422
+ **Default to one closed-loop skill.** Split only on a real signal (a phase reused by a second scenario, or attention spread too thin for depth).
1423
+
1424
+ ### 1d. Classify: decides how to write, not whether to extract
1425
+
1426
+ | Type | Example | Skill focus |
1427
+ |---|---|---|
1428
+ | Investigation | pull sources \u2192 build timeline \u2192 locate issue | evidence sources, steps, output template |
1429
+ | Debug | reproduce \u2192 red test \u2192 fix \u2192 verify | invariants, tests, acceptance |
1430
+ | Tool-combo | chain multiple log/monitor/analysis tools | query order, correlation IDs |
1431
+ | Habit/convention | directory structure, naming, placement | path rules, naming rules |
1432
+ | Writing template | issue / PR / retro report | structure, tone, prohibitions |
1433
+
1434
+ **Do NOT** write one-off details into the skill (a transient session, a full specific chat, a temporary verification code, etc.).
1435
+
1436
+ ## Step 2: Extract \u2014 distil stable principles and draft
1437
+
1438
+ > If 1c decided on a split or orchestrator + leaf, run Step 2 once for **each** skill to land (each its own directory).
1439
+
1440
+ ### 2a. Distil stable principles
1441
+
1442
+ Break the experience into:
1443
+
1444
+ - **Trigger**: when this skill should be used
1445
+ - **Input**: what the user might provide, what to ask when it's missing
1446
+ - **Evidence sources / tools**: which sources, files, CLIs, logs to check
1447
+ - **Steps**: the stable, reusable execution order
1448
+ - **Output**: what to hand the user, or what artifact to create
1449
+ - **Boundaries**: what NOT to do, when to stop / ask
1450
+ - **Verification**: how to confirm the skill worked
1451
+
1452
+ Write principles, not a play-by-play.
1453
+
1454
+ ### 2b. Choose a slug
1455
+
1456
+ Slug: lowercase, alphanumeric + hyphen, short and clear, verb/task- or topic-oriented. Before drafting, \`ls <skills-dir>/\` to avoid collisions and match existing naming style (when the save dir is known).
1457
+
1458
+ ### 2c. Draft SKILL.md
1459
+
1460
+ Use the generic-compatible frontmatter:
1461
+
1462
+ \`\`\`yaml
1463
+ ---
1464
+ name: <slug-or-display-name>
1465
+ description: "One line: what the skill does and when to use it."
1466
+ argument-hint: "<optional, describe args>"
1467
+ alwaysAllow: ["Bash"] # optional
1468
+ requiredSources: [] # optional
1469
+ ---
1470
+ \`\`\`
1471
+
1472
+ Suggested body structure:
1473
+
1474
+ \`\`\`markdown
1475
+ # <Skill Name>
1476
+
1477
+ One-line goal.
1478
+
1479
+ ## Trigger / When to use
1480
+ ## Preconditions
1481
+ ## Workflow / Steps
1482
+ ## Output format
1483
+ ## Boundaries & prohibitions
1484
+ ## Verification
1485
+ ## Examples
1486
+ \`\`\`
1487
+
1488
+ Writing style:
1489
+
1490
+ - Instructions must be executable, not vague.
1491
+ - Don't write dead behaviors like "after reading, tell the user the rules are loaded".
1492
+ - Action-type skills proceed by default; ask only when a key argument is missing.
1493
+ - Keep concrete paths, APIs, command examples, but scrub one-off sensitive data.
1494
+ - Match the existing style of the target skill library.
1495
+
1496
+ ### 2d. Icon (optional)
1497
+
1498
+ - Prefer 3D / color / skeuomorphic style, consistent with existing visuals.
1499
+ - Recommended source: Microsoft Fluent Emoji.
1500
+ - Filename must be \`icon.svg\`/\`icon.png\`/\`icon.jpg\`/\`icon.jpeg\`, placed in the skill directory.
1501
+ - No ad-hoc hand-drawn SVG stand-ins unless the user explicitly asks. If none fits, ask first \u2014 don't force one.
1502
+
1503
+ ## Step 3: Save \u2014 land it in the given directory
1504
+
1505
+ **Only now do you need the placement directory \`<skills-dir>\`:**
1506
+
1507
+ - User gave a directory up front \u2192 use it directly.
1508
+ - Not given \u2192 ask **now** where to put it; do not pick a directory on your own.
1509
+
1510
+ Conventions:
1511
+
1512
+ 1. **One skill, one directory**: \`<skills-dir>/<slug>/SKILL.md\` (required) + optional \`icon.svg\` / \`references/\` / helper scripts.
1513
+ 2. **Never drop a SKILL.md directly in the \`<skills-dir>\` root**: even a single-file skill gets its own subdirectory.
1514
+ 3. **Keep helper data/scripts in the same directory** so the skill is self-contained.
1515
+
1516
+ \`\`\`bash
1517
+ mkdir -p <skills-dir>/<slug>
1518
+ # write SKILL.md into that dir; include helper scripts/data if any
1519
+ \`\`\`
1520
+
1521
+ \`<skills-dir>/\` IS the canonical source \u2014 no need to symlink elsewhere.
1522
+
1523
+ ## Verification
1524
+
1525
+ \`\`\`bash
1526
+ ls -la <skills-dir>/<slug>/
1527
+ \`\`\`
1528
+
1529
+ - [ ] \`SKILL.md\` exists, frontmatter valid, \`name\`/\`description\` non-empty
1530
+ - [ ] Body non-empty with executable steps
1531
+ - [ ] No hardcoded sensitive info (accounts, tokens, transient session ids)
1532
+ - [ ] Slug doesn't collide with an existing one
1533
+
1534
+ ## Output to the user
1535
+
1536
+ When done, briefly report: skill slug, source path (\`<skills-dir>/<slug>/SKILL.md\`), whether an icon was added, whether helper scripts/data are included. Don't paste the full SKILL.md unless asked.
1537
+
1538
+ ## Key principles
1539
+
1540
+ - The user wants a **captured capability**, not a copied conversation.
1541
+ - **Analysis first**: decide whether there's knowledge worth capturing; if not, stop \u2014 don't force it, and don't ask for a directory early.
1542
+ - The canonical source always lives in \`<skills-dir>/<slug>/\`; even a single-file skill gets its own subdirectory.
1543
+ - Distinguish fact / hypothesis / to-be-confirmed; don't hardcode a solution too early.`;
1544
+
1169
1545
  // packages/feature/agent/src/server/lib/slashCommands.ts
1170
1546
  var COMMAND_CONTENT = {
1171
1547
  qa: { zh: QA_PROMPT_ZH, en: QA_PROMPT_EN },
@@ -1180,6 +1556,12 @@ var COMMAND_CONTENT = {
1180
1556
  en: NEW_BRANCH_PROMPT_EN,
1181
1557
  labelZh: NEW_BRANCH_LABEL_ZH,
1182
1558
  labelEn: NEW_BRANCH_LABEL_EN
1559
+ },
1560
+ skillify: {
1561
+ zh: SKILLIFY_PROMPT_ZH,
1562
+ en: SKILLIFY_PROMPT_EN,
1563
+ labelZh: SKILLIFY_LABEL_ZH,
1564
+ labelEn: SKILLIFY_LABEL_EN
1183
1565
  }
1184
1566
  };
1185
1567
  var BUILTIN_SKILLS_DIR = join(COCKPIT_DIR, "skills");
@@ -1400,41 +1782,63 @@ async function runSdkLoop(ctx, buildOptions) {
1400
1782
  }
1401
1783
  }
1402
1784
  if (ctx.prompt) content.push({ type: "text", text: ctx.prompt });
1403
- const firstAbort = follow(ctx);
1404
- const baseOptions = buildOptions(firstAbort, ctx.sessionId);
1405
- let response;
1406
- if (hasImages) {
1407
- const messages = (async function* () {
1408
- yield {
1409
- type: "user",
1410
- message: { role: "user", content },
1411
- parent_tool_use_id: null,
1412
- session_id: ctx.sessionId || `session-${ctx.currentKey()}`
1413
- };
1414
- })();
1415
- response = query({ prompt: messages, options: baseOptions });
1416
- } else {
1417
- response = query({ prompt: ctx.prompt, options: baseOptions });
1785
+ const firstContent = hasImages ? content : ctx.prompt ?? "";
1786
+ const pendingTasks = /* @__PURE__ */ new Set();
1787
+ let closeInput = () => {
1788
+ };
1789
+ if (!ctx.signal.aborted) {
1790
+ ctx.signal.addEventListener("abort", () => closeInput(), { once: true });
1418
1791
  }
1419
- let currentResponse = response;
1420
1792
  for (let attempt = 0; attempt <= MAX_COMPACTION_RETRIES; attempt++) {
1793
+ let gateClosed = false;
1794
+ const gateOpen = new Promise((resolve) => {
1795
+ closeInput = () => {
1796
+ if (!gateClosed) {
1797
+ gateClosed = true;
1798
+ resolve();
1799
+ }
1800
+ };
1801
+ });
1802
+ if (ctx.signal.aborted) closeInput();
1803
+ const isRetry = attempt > 0;
1804
+ const firstYield = {
1805
+ type: "user",
1806
+ // A compaction retry resumes the same session with 'continue'; the first attempt sends the
1807
+ // user turn (text ∪ images).
1808
+ message: { role: "user", content: isRetry ? "continue" : firstContent },
1809
+ parent_tool_use_id: null,
1810
+ session_id: ctx.sessionId || `session-${ctx.currentKey()}`
1811
+ };
1812
+ const input = (async function* () {
1813
+ yield firstYield;
1814
+ await gateOpen;
1815
+ })();
1816
+ const attemptAbort = follow(ctx);
1817
+ const resume = isRetry ? ctx.currentKey() : ctx.sessionId;
1818
+ const options = isRetry ? { ...buildOptions(attemptAbort, resume), resume } : buildOptions(attemptAbort, ctx.sessionId);
1819
+ const response = query({ prompt: input, options });
1421
1820
  let receivedResult = false;
1422
- for await (const message of currentResponse) {
1423
- if (ctx.signal.aborted) break;
1821
+ for await (const message of response) {
1822
+ if (ctx.signal.aborted) {
1823
+ closeInput();
1824
+ break;
1825
+ }
1424
1826
  const msg = message;
1425
1827
  if (msg.type === "system" && msg.subtype === "init" && msg.session_id) {
1426
1828
  ctx.rekey(msg.session_id);
1427
1829
  }
1830
+ if (msg.type === "system" && msg.subtype === "task_started" && msg.task_id) {
1831
+ pendingTasks.add(msg.task_id);
1832
+ }
1833
+ if (msg.type === "system" && msg.subtype === "task_notification" && msg.task_id) {
1834
+ pendingTasks.delete(msg.task_id);
1835
+ }
1428
1836
  if (msg.type === "result") receivedResult = true;
1429
1837
  ctx.emit(message);
1838
+ if (msg.type === "result" && pendingTasks.size === 0) closeInput();
1430
1839
  }
1840
+ closeInput();
1431
1841
  if (receivedResult || ctx.signal.aborted) break;
1432
- const retryAbort = follow(ctx);
1433
- const resume = ctx.currentKey();
1434
- currentResponse = query({
1435
- prompt: "continue",
1436
- options: { ...buildOptions(retryAbort, resume), resume }
1437
- });
1438
1842
  }
1439
1843
  }
1440
1844
 
@@ -1544,6 +1948,23 @@ function ensureHookSettings() {
1544
1948
  return p;
1545
1949
  }
1546
1950
  var ptyInputRegistry = /* @__PURE__ */ new Map();
1951
+ function trackBackgroundLifecycle(line, pending) {
1952
+ const content = line.message?.content;
1953
+ if (line.type === "user" && Array.isArray(content)) {
1954
+ for (const b of content) {
1955
+ if (b.type === "tool_result" && typeof b.content === "string") {
1956
+ const m = b.content.match(/Command running in background with ID:\s*([A-Za-z0-9_-]+)/);
1957
+ if (m) pending.add(m[1]);
1958
+ }
1959
+ }
1960
+ }
1961
+ const origin = line.origin;
1962
+ if (line.type === "user" && origin?.kind === "task-notification") {
1963
+ const raw = typeof content === "string" ? content : Array.isArray(content) ? content.filter((b) => b.type === "text").map((b) => b.text || "").join("") : "";
1964
+ const id = raw.match(/<task-id>([\s\S]*?)<\/task-id>/)?.[1]?.trim();
1965
+ if (id) pending.delete(id);
1966
+ }
1967
+ }
1547
1968
  function runClaudeTurn(opts) {
1548
1969
  const {
1549
1970
  cwd,
@@ -1553,6 +1974,7 @@ function runClaudeTurn(opts) {
1553
1974
  onSubmit,
1554
1975
  onComplete,
1555
1976
  onQuestionEsc,
1977
+ onTurnBoundary,
1556
1978
  signal,
1557
1979
  debug = false,
1558
1980
  idleMs = 3e3,
@@ -1645,6 +2067,8 @@ ${tempImageFiles.map((f) => f.path).join("\n")}` : prompt;
1645
2067
  let crashError = "";
1646
2068
  let timedOut = false;
1647
2069
  const newLines = [];
2070
+ const pendingBg = /* @__PURE__ */ new Set();
2071
+ let awaitingNewTurn = false;
1648
2072
  term.onData((d) => {
1649
2073
  rawBuf += d;
1650
2074
  if (rawBuf.length > 2e5) rawBuf = rawBuf.slice(-15e4);
@@ -1780,6 +2204,11 @@ ${tempImageFiles.map((f) => f.path).join("\n")}` : prompt;
1780
2204
  }
1781
2205
  newLines.push(o);
1782
2206
  lastJsonlTs = Date.now();
2207
+ trackBackgroundLifecycle(o, pendingBg);
2208
+ if (awaitingNewTurn && o.type === "assistant") {
2209
+ awaitingNewTurn = false;
2210
+ onTurnBoundary?.("init");
2211
+ }
1783
2212
  onJsonlLine?.(o);
1784
2213
  const blocks = Array.isArray(o.message?.content) ? o.message.content.map((b) => b.type).join(",") : "";
1785
2214
  if (o.type === "assistant" && blocks.includes("text")) sawAssistantText = true;
@@ -1792,14 +2221,20 @@ ${tempImageFiles.map((f) => f.path).join("\n")}` : prompt;
1792
2221
  }
1793
2222
  }
1794
2223
  if (o.type === "system" && o.subtype === "stop_hook_summary" && !completed) {
1795
- complete("stop_hook_summary");
2224
+ if (pendingBg.size === 0) {
2225
+ complete("stop_hook_summary");
2226
+ } else {
2227
+ log("stop_hook with background task(s) pending \u2192 staying resident", [...pendingBg]);
2228
+ onTurnBoundary?.("result");
2229
+ awaitingNewTurn = true;
2230
+ }
1796
2231
  }
1797
2232
  }
1798
2233
  seen = lines.length;
1799
2234
  }, 150);
1800
2235
  const idleCheck = setInterval(() => {
1801
2236
  if (completed || !promptSent) return;
1802
- if (sawAssistantText && Date.now() - lastJsonlTs > idleMs && Date.now() - lastDataTs > idleMs) {
2237
+ if (sawAssistantText && pendingBg.size === 0 && Date.now() - lastJsonlTs > idleMs && Date.now() - lastDataTs > idleMs) {
1803
2238
  complete("idle");
1804
2239
  }
1805
2240
  }, 300);
@@ -1917,6 +2352,22 @@ function resultEvent(sessionId, opts) {
1917
2352
  function mapLineToEvents(line, sessionId) {
1918
2353
  const out = [];
1919
2354
  const content = line.message?.content;
2355
+ const origin = line.origin;
2356
+ if (line.type === "user" && origin?.kind === "task-notification") {
2357
+ const raw = typeof content === "string" ? content : Array.isArray(content) ? content.filter((b) => b.type === "text").map((b) => b.text || "").join("") : "";
2358
+ const pick2 = (tag) => raw.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`))?.[1]?.trim();
2359
+ const ev = { type: "system", subtype: "task_notification", session_id: sessionId };
2360
+ const taskId = pick2("task-id");
2361
+ if (taskId) ev.task_id = taskId;
2362
+ const status = pick2("status");
2363
+ if (status) ev.status = status;
2364
+ const summary = pick2("summary");
2365
+ if (summary) ev.summary = summary;
2366
+ const outputFile = pick2("output-file");
2367
+ if (outputFile) ev.output_file = outputFile;
2368
+ out.push(ev);
2369
+ return out;
2370
+ }
1920
2371
  if (line.type === "assistant" && Array.isArray(content)) {
1921
2372
  const blocks = content;
1922
2373
  const text = blocks.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("");
@@ -1975,6 +2426,11 @@ async function runPtyTurn(ctx) {
1975
2426
  ctx.emit(ev);
1976
2427
  }
1977
2428
  },
2429
+ // Background-task persistence: the driver stays resident across a background task and signals
2430
+ // synthetic turn boundaries. Map them to the SAME init/result events a normal turn emits, so the
2431
+ // follow-up (auto-run) turn renders in its own bubble and the completion notification's event row
2432
+ // shows (the client keys the row off a turn having ended).
2433
+ onTurnBoundary: (kind) => ctx.emit(kind === "init" ? initEvent(sid) : resultEvent(sid)),
1978
2434
  // floating-window dual-view: raw PTY output forwarded to the frontend xterm.
1979
2435
  onPtyData: (data) => ctx.emit({ type: "pty_output", data }),
1980
2436
  // Startup stuck (REPL not ready, likely a dialog) → ask the user to handle it in the terminal.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surething/cockpit",
3
- "version": "1.0.231",
3
+ "version": "1.0.233",
4
4
  "description": "Claude Code GUI for parallel AI coding — drive Claude (default), Codex, DeepSeek, Kimi, or local Ollama in tabs. Multi-project sessions, terminal, browser & DB bubbles, code review, slash modes. Local-first, MIT.",
5
5
  "author": "Robert",
6
6
  "license": "MIT",