@protoboxai/codeloop 0.3.0

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 (290) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +589 -0
  3. package/dist/commands/adopt.d.ts +2 -0
  4. package/dist/commands/adopt.js +26 -0
  5. package/dist/commands/adopt.js.map +1 -0
  6. package/dist/commands/brief.d.ts +2 -0
  7. package/dist/commands/brief.js +22 -0
  8. package/dist/commands/brief.js.map +1 -0
  9. package/dist/commands/card.d.ts +6 -0
  10. package/dist/commands/card.js +159 -0
  11. package/dist/commands/card.js.map +1 -0
  12. package/dist/commands/check.d.ts +4 -0
  13. package/dist/commands/check.js +76 -0
  14. package/dist/commands/check.js.map +1 -0
  15. package/dist/commands/cloud.d.ts +8 -0
  16. package/dist/commands/cloud.js +123 -0
  17. package/dist/commands/cloud.js.map +1 -0
  18. package/dist/commands/guard.d.ts +2 -0
  19. package/dist/commands/guard.js +26 -0
  20. package/dist/commands/guard.js.map +1 -0
  21. package/dist/commands/import.d.ts +2 -0
  22. package/dist/commands/import.js +24 -0
  23. package/dist/commands/import.js.map +1 -0
  24. package/dist/commands/inbox.d.ts +2 -0
  25. package/dist/commands/inbox.js +48 -0
  26. package/dist/commands/inbox.js.map +1 -0
  27. package/dist/commands/init.d.ts +2 -0
  28. package/dist/commands/init.js +142 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/commands/install.d.ts +2 -0
  31. package/dist/commands/install.js +125 -0
  32. package/dist/commands/install.js.map +1 -0
  33. package/dist/commands/lane.d.ts +2 -0
  34. package/dist/commands/lane.js +100 -0
  35. package/dist/commands/lane.js.map +1 -0
  36. package/dist/commands/list.d.ts +2 -0
  37. package/dist/commands/list.js +35 -0
  38. package/dist/commands/list.js.map +1 -0
  39. package/dist/commands/login.d.ts +2 -0
  40. package/dist/commands/login.js +77 -0
  41. package/dist/commands/login.js.map +1 -0
  42. package/dist/commands/mock.d.ts +3 -0
  43. package/dist/commands/mock.js +22 -0
  44. package/dist/commands/mock.js.map +1 -0
  45. package/dist/commands/pack.d.ts +2 -0
  46. package/dist/commands/pack.js +19 -0
  47. package/dist/commands/pack.js.map +1 -0
  48. package/dist/commands/publish.d.ts +2 -0
  49. package/dist/commands/publish.js +125 -0
  50. package/dist/commands/publish.js.map +1 -0
  51. package/dist/commands/remove.d.ts +2 -0
  52. package/dist/commands/remove.js +31 -0
  53. package/dist/commands/remove.js.map +1 -0
  54. package/dist/commands/render.d.ts +5 -0
  55. package/dist/commands/render.js +47 -0
  56. package/dist/commands/render.js.map +1 -0
  57. package/dist/commands/run.d.ts +2 -0
  58. package/dist/commands/run.js +45 -0
  59. package/dist/commands/run.js.map +1 -0
  60. package/dist/commands/scan.d.ts +2 -0
  61. package/dist/commands/scan.js +26 -0
  62. package/dist/commands/scan.js.map +1 -0
  63. package/dist/commands/schedule.d.ts +2 -0
  64. package/dist/commands/schedule.js +51 -0
  65. package/dist/commands/schedule.js.map +1 -0
  66. package/dist/commands/search.d.ts +2 -0
  67. package/dist/commands/search.js +85 -0
  68. package/dist/commands/search.js.map +1 -0
  69. package/dist/commands/serve.d.ts +7 -0
  70. package/dist/commands/serve.js +126 -0
  71. package/dist/commands/serve.js.map +1 -0
  72. package/dist/commands/spec.d.ts +3 -0
  73. package/dist/commands/spec.js +64 -0
  74. package/dist/commands/spec.js.map +1 -0
  75. package/dist/commands/status.d.ts +3 -0
  76. package/dist/commands/status.js +251 -0
  77. package/dist/commands/status.js.map +1 -0
  78. package/dist/commands/update.d.ts +2 -0
  79. package/dist/commands/update.js +83 -0
  80. package/dist/commands/update.js.map +1 -0
  81. package/dist/commands/verify.d.ts +3 -0
  82. package/dist/commands/verify.js +75 -0
  83. package/dist/commands/verify.js.map +1 -0
  84. package/dist/commands/watch.d.ts +6 -0
  85. package/dist/commands/watch.js +111 -0
  86. package/dist/commands/watch.js.map +1 -0
  87. package/dist/commands/wiki.d.ts +3 -0
  88. package/dist/commands/wiki.js +85 -0
  89. package/dist/commands/wiki.js.map +1 -0
  90. package/dist/index.d.ts +2 -0
  91. package/dist/index.js +86 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/agent.d.ts +27 -0
  94. package/dist/lib/agent.js +178 -0
  95. package/dist/lib/agent.js.map +1 -0
  96. package/dist/lib/board.d.ts +38 -0
  97. package/dist/lib/board.js +86 -0
  98. package/dist/lib/board.js.map +1 -0
  99. package/dist/lib/cards.d.ts +82 -0
  100. package/dist/lib/cards.js +107 -0
  101. package/dist/lib/cards.js.map +1 -0
  102. package/dist/lib/clock.d.ts +5 -0
  103. package/dist/lib/clock.js +14 -0
  104. package/dist/lib/clock.js.map +1 -0
  105. package/dist/lib/cloud.d.ts +122 -0
  106. package/dist/lib/cloud.js +580 -0
  107. package/dist/lib/cloud.js.map +1 -0
  108. package/dist/lib/competitors.d.ts +28 -0
  109. package/dist/lib/competitors.js +90 -0
  110. package/dist/lib/competitors.js.map +1 -0
  111. package/dist/lib/config.d.ts +7 -0
  112. package/dist/lib/config.js +25 -0
  113. package/dist/lib/config.js.map +1 -0
  114. package/dist/lib/cron.d.ts +5 -0
  115. package/dist/lib/cron.js +82 -0
  116. package/dist/lib/cron.js.map +1 -0
  117. package/dist/lib/detect.d.ts +13 -0
  118. package/dist/lib/detect.js +60 -0
  119. package/dist/lib/detect.js.map +1 -0
  120. package/dist/lib/engine.d.ts +88 -0
  121. package/dist/lib/engine.js +435 -0
  122. package/dist/lib/engine.js.map +1 -0
  123. package/dist/lib/flow.d.ts +44 -0
  124. package/dist/lib/flow.js +121 -0
  125. package/dist/lib/flow.js.map +1 -0
  126. package/dist/lib/glob.d.ts +2 -0
  127. package/dist/lib/glob.js +10 -0
  128. package/dist/lib/glob.js.map +1 -0
  129. package/dist/lib/import.d.ts +13 -0
  130. package/dist/lib/import.js +127 -0
  131. package/dist/lib/import.js.map +1 -0
  132. package/dist/lib/inbox.d.ts +42 -0
  133. package/dist/lib/inbox.js +65 -0
  134. package/dist/lib/inbox.js.map +1 -0
  135. package/dist/lib/lane.d.ts +71 -0
  136. package/dist/lib/lane.js +105 -0
  137. package/dist/lib/lane.js.map +1 -0
  138. package/dist/lib/lock.d.ts +6 -0
  139. package/dist/lib/lock.js +64 -0
  140. package/dist/lib/lock.js.map +1 -0
  141. package/dist/lib/mcp.d.ts +2 -0
  142. package/dist/lib/mcp.js +57 -0
  143. package/dist/lib/mcp.js.map +1 -0
  144. package/dist/lib/mock.d.ts +17 -0
  145. package/dist/lib/mock.js +137 -0
  146. package/dist/lib/mock.js.map +1 -0
  147. package/dist/lib/pack.d.ts +24 -0
  148. package/dist/lib/pack.js +77 -0
  149. package/dist/lib/pack.js.map +1 -0
  150. package/dist/lib/proposals.d.ts +48 -0
  151. package/dist/lib/proposals.js +240 -0
  152. package/dist/lib/proposals.js.map +1 -0
  153. package/dist/lib/render.d.ts +7 -0
  154. package/dist/lib/render.js +84 -0
  155. package/dist/lib/render.js.map +1 -0
  156. package/dist/lib/research.d.ts +8 -0
  157. package/dist/lib/research.js +39 -0
  158. package/dist/lib/research.js.map +1 -0
  159. package/dist/lib/run.d.ts +38 -0
  160. package/dist/lib/run.js +215 -0
  161. package/dist/lib/run.js.map +1 -0
  162. package/dist/lib/scaffold.d.ts +16 -0
  163. package/dist/lib/scaffold.js +175 -0
  164. package/dist/lib/scaffold.js.map +1 -0
  165. package/dist/lib/scan.d.ts +22 -0
  166. package/dist/lib/scan.js +85 -0
  167. package/dist/lib/scan.js.map +1 -0
  168. package/dist/lib/schedule.d.ts +20 -0
  169. package/dist/lib/schedule.js +66 -0
  170. package/dist/lib/schedule.js.map +1 -0
  171. package/dist/lib/server.d.ts +68 -0
  172. package/dist/lib/server.js +235 -0
  173. package/dist/lib/server.js.map +1 -0
  174. package/dist/lib/shell.d.ts +1 -0
  175. package/dist/lib/shell.js +25 -0
  176. package/dist/lib/shell.js.map +1 -0
  177. package/dist/lib/skills.d.ts +20 -0
  178. package/dist/lib/skills.js +92 -0
  179. package/dist/lib/skills.js.map +1 -0
  180. package/dist/lib/spec.d.ts +24 -0
  181. package/dist/lib/spec.js +102 -0
  182. package/dist/lib/spec.js.map +1 -0
  183. package/dist/lib/stats.d.ts +13 -0
  184. package/dist/lib/stats.js +43 -0
  185. package/dist/lib/stats.js.map +1 -0
  186. package/dist/lib/verify.d.ts +25 -0
  187. package/dist/lib/verify.js +135 -0
  188. package/dist/lib/verify.js.map +1 -0
  189. package/dist/lib/version.d.ts +10 -0
  190. package/dist/lib/version.js +27 -0
  191. package/dist/lib/version.js.map +1 -0
  192. package/dist/lib/wiki.d.ts +51 -0
  193. package/dist/lib/wiki.js +164 -0
  194. package/dist/lib/wiki.js.map +1 -0
  195. package/dist/registry/index.d.ts +7 -0
  196. package/dist/registry/index.js +8 -0
  197. package/dist/registry/index.js.map +1 -0
  198. package/dist/registry/installer.d.ts +30 -0
  199. package/dist/registry/installer.js +133 -0
  200. package/dist/registry/installer.js.map +1 -0
  201. package/dist/registry/local-index.d.ts +32 -0
  202. package/dist/registry/local-index.js +58 -0
  203. package/dist/registry/local-index.js.map +1 -0
  204. package/dist/registry/lockfile.d.ts +40 -0
  205. package/dist/registry/lockfile.js +85 -0
  206. package/dist/registry/lockfile.js.map +1 -0
  207. package/dist/registry/security.d.ts +25 -0
  208. package/dist/registry/security.js +100 -0
  209. package/dist/registry/security.js.map +1 -0
  210. package/dist/registry/skill-schema.d.ts +30 -0
  211. package/dist/registry/skill-schema.js +95 -0
  212. package/dist/registry/skill-schema.js.map +1 -0
  213. package/dist/ui/404.html +1 -0
  214. package/dist/ui/_next/static/6wzGE5sCtbYkSyhMWxL32/_buildManifest.js +1 -0
  215. package/dist/ui/_next/static/6wzGE5sCtbYkSyhMWxL32/_ssgManifest.js +1 -0
  216. package/dist/ui/_next/static/chunks/255-54d3085ce94738a4.js +1 -0
  217. package/dist/ui/_next/static/chunks/423-bb541b7ae2733575.js +1 -0
  218. package/dist/ui/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  219. package/dist/ui/_next/static/chunks/app/_not-found/page-d6bc774f7acb716e.js +1 -0
  220. package/dist/ui/_next/static/chunks/app/layout-e5fc8e78e1c8da95.js +1 -0
  221. package/dist/ui/_next/static/chunks/app/page-0c34b6e119cef236.js +1 -0
  222. package/dist/ui/_next/static/chunks/framework-de98b93a850cfc71.js +1 -0
  223. package/dist/ui/_next/static/chunks/main-49fd204fc9037ea3.js +1 -0
  224. package/dist/ui/_next/static/chunks/main-app-c46afa2f48f3aaef.js +1 -0
  225. package/dist/ui/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  226. package/dist/ui/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  227. package/dist/ui/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  228. package/dist/ui/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
  229. package/dist/ui/_next/static/css/1bf01240dbfd6088.css +1 -0
  230. package/dist/ui/index.html +1 -0
  231. package/dist/ui/index.txt +19 -0
  232. package/dist/watch/index.d.ts +21 -0
  233. package/dist/watch/index.js +88 -0
  234. package/dist/watch/index.js.map +1 -0
  235. package/dist/watch/reporter.d.ts +11 -0
  236. package/dist/watch/reporter.js +44 -0
  237. package/dist/watch/reporter.js.map +1 -0
  238. package/dist/watch/signals.d.ts +38 -0
  239. package/dist/watch/signals.js +119 -0
  240. package/dist/watch/signals.js.map +1 -0
  241. package/dist/watch/triggers.d.ts +10 -0
  242. package/dist/watch/triggers.js +67 -0
  243. package/dist/watch/triggers.js.map +1 -0
  244. package/package.json +64 -0
  245. package/registry/index.json +106 -0
  246. package/starters/generic.yaml +95 -0
  247. package/starters/go.yaml +99 -0
  248. package/starters/node-typescript.yaml +108 -0
  249. package/starters/python.yaml +105 -0
  250. package/templates/ci/codeloop-pr.yml +29 -0
  251. package/templates/ci/codeloop-prod.yml +27 -0
  252. package/templates/ci/codeloop-staging.yml +34 -0
  253. package/templates/codeloop/board.json +5 -0
  254. package/templates/codeloop/gotchas.md +13 -0
  255. package/templates/codeloop/patterns.md +15 -0
  256. package/templates/codeloop/principles.md +49 -0
  257. package/templates/codeloop/rules.md +23 -0
  258. package/templates/commands/commit.md +255 -0
  259. package/templates/commands/debug.md +142 -0
  260. package/templates/commands/deploy.md +144 -0
  261. package/templates/commands/design.md +102 -0
  262. package/templates/commands/manage.md +77 -0
  263. package/templates/commands/plan.md +84 -0
  264. package/templates/commands/qa.md +155 -0
  265. package/templates/commands/reflect.md +93 -0
  266. package/templates/commands/ship.md +187 -0
  267. package/templates/commands/test.md +133 -0
  268. package/templates/hooks/commit-msg +8 -0
  269. package/templates/lanes/analyze.yaml +25 -0
  270. package/templates/lanes/build.yaml +45 -0
  271. package/templates/lanes/deploy.yaml +24 -0
  272. package/templates/lanes/learn.yaml +20 -0
  273. package/templates/lanes/market.yaml +25 -0
  274. package/templates/lanes/plan.yaml +24 -0
  275. package/templates/lanes/scan.yaml +11 -0
  276. package/templates/lanes/triage.yaml +24 -0
  277. package/templates/mock/template.html +82 -0
  278. package/templates/seeds/go-gotchas.md +28 -0
  279. package/templates/seeds/go-patterns.md +22 -0
  280. package/templates/seeds/node-typescript-gotchas.md +30 -0
  281. package/templates/seeds/node-typescript-patterns.md +27 -0
  282. package/templates/seeds/python-gotchas.md +30 -0
  283. package/templates/seeds/python-patterns.md +19 -0
  284. package/templates/seeds/universal-gotchas.md +11 -0
  285. package/templates/seeds/universal-patterns.md +11 -0
  286. package/templates/spec/plan.md +7 -0
  287. package/templates/spec/research.md +19 -0
  288. package/templates/spec/spec.md +14 -0
  289. package/templates/spec/tasks.md +10 -0
  290. package/templates/tasks/todo.md +3 -0
@@ -0,0 +1,66 @@
1
+ import { spawnSync } from 'child_process';
2
+ import { existsSync, mkdirSync, readFileSync, statSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { RefusalError } from './cards.js';
5
+ import { cronError } from './cron.js';
6
+ export const RUN_LOG = '.codeloop/state/run.log';
7
+ const quote = (value) => `'${value.replaceAll("'", `'\\''`)}'`;
8
+ const marker = (projectDir) => `# codeloop:${projectDir}`;
9
+ /** `30m`, `1h`, `6h`: only intervals that divide the hour or the day, since cron cannot say "every 45 minutes". */
10
+ export function everyToCron(every) {
11
+ const m = /^(\d+)(m|h)$/.exec(every);
12
+ const n = m ? parseInt(m[1], 10) : 0;
13
+ if (m?.[2] === 'm' && n >= 1 && n < 60 && 60 % n === 0)
14
+ return `*/${n} * * * *`;
15
+ if (m?.[2] === 'h' && n >= 1 && n <= 24 && 24 % n === 0)
16
+ return n === 1 ? '0 * * * *' : n === 24 ? '0 0 * * *' : `0 */${n} * * *`;
17
+ throw new RefusalError(`--every "${every}" is not an interval cron can run (5m, 10m, 15m, 20m, 30m, 1h, 2h, 3h, 4h, 6h, 12h, 24h); use --cron for anything else`);
18
+ }
19
+ /**
20
+ * One crontab line. cron starts with a bare PATH, so the PATH of the installing shell is written
21
+ * into the line: the agent command (claude, codex) has to be found at 3am too.
22
+ */
23
+ export function scheduleLine(projectDir, opts) {
24
+ const problem = cronError(opts.cron);
25
+ if (problem)
26
+ throw new RefusalError(problem);
27
+ const entry = opts.entry ?? process.argv[1];
28
+ const run = opts.agent ? ` --agent${opts.agent === true ? '' : ` ${opts.agent}`}` : '';
29
+ const command = `cd ${quote(projectDir)} && PATH=${quote(opts.path ?? process.env.PATH ?? '')} ${quote(process.execPath)} ${quote(entry)} run${run} >> ${RUN_LOG} 2>&1`;
30
+ // cron reads an unescaped % as the end of the command.
31
+ return `${opts.cron} ${command.replaceAll('%', '\\%')} ${marker(projectDir)}`;
32
+ }
33
+ function readTable(crontab) {
34
+ const run = spawnSync(crontab, ['-l'], { encoding: 'utf-8' });
35
+ if (run.error)
36
+ throw new RefusalError(`could not run ${crontab}: ${run.error.message}`);
37
+ // `crontab -l` exits 1 when the user has no crontab yet.
38
+ return run.status === 0 ? run.stdout.split('\n').filter(l => l !== '') : [];
39
+ }
40
+ function writeTable(crontab, lines) {
41
+ const run = spawnSync(crontab, ['-'], { input: lines.length ? lines.join('\n') + '\n' : '', encoding: 'utf-8' });
42
+ if (run.error || run.status !== 0)
43
+ throw new RefusalError(`${crontab} refused the new table: ${run.error?.message ?? run.stderr.trim()}`);
44
+ }
45
+ const mine = (projectDir) => (line) => line.endsWith(marker(projectDir));
46
+ export function installSchedule(projectDir, line, crontab = 'crontab') {
47
+ mkdirSync(dirname(join(projectDir, RUN_LOG)), { recursive: true });
48
+ writeTable(crontab, [...readTable(crontab).filter(l => !mine(projectDir)(l)), line]);
49
+ }
50
+ export function removeSchedule(projectDir, crontab = 'crontab') {
51
+ const table = readTable(crontab);
52
+ const kept = table.filter(l => !mine(projectDir)(l));
53
+ if (kept.length === table.length)
54
+ return false;
55
+ writeTable(crontab, kept);
56
+ return true;
57
+ }
58
+ export function scheduleStatus(projectDir, crontab = 'crontab') {
59
+ const log = join(projectDir, RUN_LOG);
60
+ const ran = existsSync(log);
61
+ return {
62
+ line: readTable(crontab).find(mine(projectDir)),
63
+ ...(ran ? { lastRun: statSync(log).mtime, lastOutput: readFileSync(log, 'utf-8').trimEnd().split('\n').slice(-5).join('\n') } : {}),
64
+ };
65
+ }
66
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/lib/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,CAAC,MAAM,OAAO,GAAG,yBAAyB,CAAC;AAEjD,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AACvE,MAAM,MAAM,GAAG,CAAC,UAAkB,EAAE,EAAE,CAAC,cAAc,UAAU,EAAE,CAAC;AAElE,mHAAmH;AACnH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IAChF,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAClI,MAAM,IAAI,YAAY,CAAC,YAAY,KAAK,wHAAwH,CAAC,CAAC;AACpK,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,UAAkB,EAClB,IAA4E;IAE5E,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,MAAM,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,OAAO,OAAO,CAAC;IACxK,uDAAuD;IACvD,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,SAAS,CAAC,OAAe;IAChC,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC9D,IAAI,GAAG,CAAC,KAAK;QAAE,MAAM,IAAI,YAAY,CAAC,iBAAiB,OAAO,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,yDAAyD;IACzD,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,KAAe;IAClD,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACjH,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,GAAG,OAAO,2BAA2B,GAAG,CAAC,KAAK,EAAE,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC5I,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,UAAkB,EAAE,EAAE,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAEzF,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,IAAY,EAAE,OAAO,GAAG,SAAS;IACnF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,OAAO,GAAG,SAAS;IACpE,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/C,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,OAAO,GAAG,SAAS;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpI,CAAC;AACJ,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { Hono } from 'hono';
2
+ /** Cards, the lanes they move through and the inbox summary: everything the Cards view draws. */
3
+ export declare function cardsPayload(projectDir: string, owner: boolean): {
4
+ version: number;
5
+ owner: boolean;
6
+ cards: (import("./cards.js").Card | {
7
+ mock: string;
8
+ id: string;
9
+ title: string;
10
+ lane: string;
11
+ laneVersion: number;
12
+ stage: string;
13
+ key?: string;
14
+ spec?: string;
15
+ description?: string;
16
+ source?: string;
17
+ dedupe?: string;
18
+ gate?: string;
19
+ awaiting?: string;
20
+ retries: Record<string, number>;
21
+ failures?: Record<string, string>;
22
+ evidence: string[];
23
+ events: import("./cards.js").CardEvent[];
24
+ createdAt: string;
25
+ updatedAt: string;
26
+ })[];
27
+ lanes: {
28
+ id: string;
29
+ version: number;
30
+ stages: {
31
+ id: string;
32
+ skill: string | undefined;
33
+ output: string | undefined;
34
+ gate: import("./lane.js").Gate | undefined;
35
+ }[];
36
+ }[];
37
+ inbox: {
38
+ summary: string;
39
+ needs_you: {
40
+ id: string;
41
+ title: string;
42
+ lane: string;
43
+ stage: string;
44
+ gate: string;
45
+ awaiting?: string;
46
+ read?: string;
47
+ mock?: string;
48
+ source?: string;
49
+ last_check: string;
50
+ since: string;
51
+ last_event?: import("./cards.js").CardEvent;
52
+ }[];
53
+ };
54
+ };
55
+ /**
56
+ * `owner` is set only by `codeloop serve --owner`. Without it the board is read-only for cards.
57
+ * With it, approve and reject still need the start-up token like every other write.
58
+ */
59
+ export declare function createApp(projectDir: string, uiDir?: string, opts?: {
60
+ owner?: boolean;
61
+ token?: string;
62
+ anyHost?: boolean;
63
+ }): {
64
+ app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
65
+ broadcast: () => void;
66
+ broadcastCards: () => void;
67
+ token: string;
68
+ };
@@ -0,0 +1,235 @@
1
+ import { Hono } from 'hono';
2
+ import { streamSSE } from 'hono/streaming';
3
+ import { randomBytes, timingSafeEqual } from 'crypto';
4
+ import { existsSync, readFileSync, statSync } from 'fs';
5
+ import { join, extname, resolve, sep } from 'path';
6
+ import { loadBoard, saveBoard, addTask, updateTask, deleteTask, getTask, } from './board.js';
7
+ import { ConflictError, readCards, RefusalError } from './cards.js';
8
+ import { rejectCard } from './engine.js';
9
+ import { approveFlow } from './flow.js';
10
+ import { buildInbox } from './inbox.js';
11
+ import { loadLanes } from './lane.js';
12
+ import { findMock, MOCKS_DIR } from './mock.js';
13
+ const MIME_TYPES = {
14
+ '.html': 'text/html',
15
+ '.js': 'application/javascript',
16
+ '.css': 'text/css',
17
+ '.json': 'application/json',
18
+ '.png': 'image/png',
19
+ '.svg': 'image/svg+xml',
20
+ '.ico': 'image/x-icon',
21
+ '.txt': 'text/plain',
22
+ };
23
+ /** Cards, the lanes they move through and the inbox summary: everything the Cards view draws. */
24
+ export function cardsPayload(projectDir, owner) {
25
+ const file = readCards(projectDir);
26
+ const inbox = buildInbox(projectDir);
27
+ return {
28
+ version: file.version,
29
+ owner,
30
+ // `mock` is where the board serves the card's mock, when it has one.
31
+ cards: file.cards.map(c => {
32
+ const mock = findMock(projectDir, c.id);
33
+ return mock ? { ...c, mock: `/${mock.replace(`${MOCKS_DIR}/`, 'mocks/')}` } : c;
34
+ }),
35
+ lanes: loadLanes(projectDir).map(l => ({ id: l.id, version: l.version, stages: l.stages.map(s => ({ id: s.id, skill: s.skill, output: s.output, gate: s.gate })) })),
36
+ inbox: { summary: inbox.summary, needs_you: inbox.needs_you },
37
+ };
38
+ }
39
+ /**
40
+ * `owner` is set only by `codeloop serve --owner`. Without it the board is read-only for cards.
41
+ * With it, approve and reject still need the start-up token like every other write.
42
+ */
43
+ export function createApp(projectDir, uiDir, opts = {}) {
44
+ const app = new Hono();
45
+ const token = opts.token ?? randomBytes(24).toString('hex');
46
+ const sameToken = (given) => !!given && given.length === token.length && timingSafeEqual(Buffer.from(given), Buffer.from(token));
47
+ const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);
48
+ const sameHost = (origin, host) => {
49
+ try {
50
+ return new URL(origin).host === host;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ };
56
+ // There is no login, so the server protects itself three ways. It answers only to a loopback
57
+ // host name (a rebound DNS name cannot read the board). It sends no CORS headers, so other
58
+ // origins cannot read responses. And every write needs the token printed at start-up, a JSON
59
+ // content type, and no foreign Origin, which a page on another site cannot provide.
60
+ app.use('*', async (c, next) => {
61
+ const host = c.req.header('host') ?? new URL(c.req.url).host;
62
+ if (!opts.anyHost && !LOOPBACK.has(host.replace(/:\d+$/, '')))
63
+ return c.json({ error: `this board answers only on localhost, not ${host}` }, 403);
64
+ if (c.req.method === 'GET' || c.req.method === 'HEAD')
65
+ return next();
66
+ const origin = c.req.header('origin');
67
+ if (origin && !sameHost(origin, host))
68
+ return c.json({ error: 'cross-origin requests are not accepted' }, 403);
69
+ if (!sameToken(c.req.header('x-codeloop-token')))
70
+ return c.json({ error: 'missing or wrong token: open the URL that `codeloop serve` printed' }, 403);
71
+ if (c.req.method !== 'DELETE' && !(c.req.header('content-type') ?? '').startsWith('application/json'))
72
+ return c.json({ error: 'content type must be application/json' }, 415);
73
+ return next();
74
+ });
75
+ // Track SSE clients for broadcasting
76
+ const sseClients = new Set();
77
+ const cardClients = new Set();
78
+ const owner = opts.owner === true;
79
+ function broadcast() {
80
+ const board = loadBoard(projectDir);
81
+ for (const send of sseClients) {
82
+ send(board);
83
+ }
84
+ }
85
+ function broadcastCards() {
86
+ const payload = JSON.stringify(cardsPayload(projectDir, owner));
87
+ for (const send of cardClients)
88
+ send(payload);
89
+ }
90
+ app.get('/api/cards', (c) => c.json(cardsPayload(projectDir, owner)));
91
+ // Approve and reject go through the same engine as the CLI, as the owner.
92
+ const decide = (run) => async (c) => {
93
+ if (!owner)
94
+ return c.json({ error: 'read-only: start the server with `codeloop serve --owner` to approve or reject from the board' }, 403);
95
+ const body = await c.req.json().catch(() => ({ note: undefined }));
96
+ try {
97
+ run(c.req.param('id'), body.note ?? '');
98
+ }
99
+ catch (e) {
100
+ if (e instanceof RefusalError)
101
+ return c.json({ error: e.message }, 400);
102
+ if (e instanceof ConflictError)
103
+ return c.json({ error: e.message }, 409);
104
+ throw e;
105
+ }
106
+ broadcastCards();
107
+ return c.json(cardsPayload(projectDir, owner));
108
+ };
109
+ app.post('/api/cards/:id/approve', decide((id, note) => void approveFlow(projectDir, id, 'owner', note || undefined)));
110
+ app.post('/api/cards/:id/reject', decide((id, note) => void rejectCard(projectDir, id, 'owner', note)));
111
+ // The mock gallery and each mock, straight from docs/mocks. Read-only, and nothing outside that folder.
112
+ app.get('/mocks/*', (c) => {
113
+ const root = resolve(projectDir, MOCKS_DIR);
114
+ let wanted;
115
+ try {
116
+ wanted = decodeURIComponent(c.req.path.replace(/^\/mocks\/?/, '')) || 'index.html';
117
+ }
118
+ catch {
119
+ return c.text('Not found', 404);
120
+ }
121
+ const file = resolve(root, wanted);
122
+ if (!file.startsWith(root + sep) || !existsSync(file) || !statSync(file).isFile())
123
+ return c.text('Not found', 404);
124
+ return new Response(readFileSync(file), { headers: { 'Content-Type': MIME_TYPES[extname(file)] || 'application/octet-stream' } });
125
+ });
126
+ app.get('/mocks', (c) => c.redirect('/mocks/'));
127
+ // GET /api/board — full board
128
+ app.get('/api/board', (c) => {
129
+ const board = loadBoard(projectDir);
130
+ return c.json(board);
131
+ });
132
+ // POST /api/tasks — create task
133
+ app.post('/api/tasks', async (c) => {
134
+ const body = await c.req.json();
135
+ let board = loadBoard(projectDir);
136
+ board = addTask(board, body);
137
+ saveBoard(projectDir, board);
138
+ const task = board.tasks[board.tasks.length - 1];
139
+ broadcast();
140
+ return c.json(task, 201);
141
+ });
142
+ // PATCH /api/tasks/:id — update task
143
+ app.patch('/api/tasks/:id', async (c) => {
144
+ const id = c.req.param('id');
145
+ const patch = await c.req.json();
146
+ let board = loadBoard(projectDir);
147
+ try {
148
+ board = updateTask(board, id, patch);
149
+ }
150
+ catch (e) {
151
+ return c.json({ error: e.message }, 404);
152
+ }
153
+ saveBoard(projectDir, board);
154
+ const task = getTask(board, id);
155
+ broadcast();
156
+ return c.json(task);
157
+ });
158
+ // DELETE /api/tasks/:id — remove task
159
+ app.delete('/api/tasks/:id', (c) => {
160
+ const id = c.req.param('id');
161
+ let board = loadBoard(projectDir);
162
+ try {
163
+ board = deleteTask(board, id);
164
+ }
165
+ catch (e) {
166
+ return c.json({ error: e.message }, 404);
167
+ }
168
+ saveBoard(projectDir, board);
169
+ broadcast();
170
+ return c.json({ ok: true });
171
+ });
172
+ // GET /api/events — SSE stream
173
+ app.get('/api/events', (c) => {
174
+ return streamSSE(c, async (stream) => {
175
+ // Send initial board state
176
+ const board = loadBoard(projectDir);
177
+ await stream.writeSSE({ data: JSON.stringify(board), event: 'board' });
178
+ // Register for broadcasts
179
+ const handler = async (board) => {
180
+ try {
181
+ await stream.writeSSE({ data: JSON.stringify(board), event: 'board' });
182
+ }
183
+ catch {
184
+ // Client disconnected
185
+ sseClients.delete(handler);
186
+ }
187
+ };
188
+ sseClients.add(handler);
189
+ const sendCards = async (payload) => {
190
+ try {
191
+ await stream.writeSSE({ data: payload, event: 'cards' });
192
+ }
193
+ catch {
194
+ cardClients.delete(sendCards);
195
+ }
196
+ };
197
+ await sendCards(JSON.stringify(cardsPayload(projectDir, owner)));
198
+ cardClients.add(sendCards);
199
+ // Keep alive until client disconnects
200
+ await new Promise((resolve) => {
201
+ stream.onAbort(() => {
202
+ sseClients.delete(handler);
203
+ cardClients.delete(sendCards);
204
+ resolve();
205
+ });
206
+ });
207
+ });
208
+ });
209
+ // Static file serving for the UI
210
+ if (uiDir) {
211
+ app.get('*', (c) => {
212
+ const urlPath = c.req.path === '/' ? '/index.html' : c.req.path;
213
+ const filePath = join(uiDir, urlPath);
214
+ if (existsSync(filePath)) {
215
+ const content = readFileSync(filePath);
216
+ const ext = extname(filePath);
217
+ const mime = MIME_TYPES[ext] || 'application/octet-stream';
218
+ return new Response(content, {
219
+ headers: { 'Content-Type': mime },
220
+ });
221
+ }
222
+ // SPA fallback — serve index.html for unmatched routes
223
+ const indexPath = join(uiDir, 'index.html');
224
+ if (existsSync(indexPath)) {
225
+ const content = readFileSync(indexPath);
226
+ return new Response(content, {
227
+ headers: { 'Content-Type': 'text/html' },
228
+ });
229
+ }
230
+ return c.text('Not found', 404);
231
+ });
232
+ }
233
+ return { app, broadcast, broadcastCards, token };
234
+ }
235
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/lib/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function checkEnv(entry?: string | undefined): NodeJS.ProcessEnv;
@@ -0,0 +1,25 @@
1
+ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
2
+ import { tmpdir } from 'os';
3
+ import { delimiter, join } from 'path';
4
+ const shims = new Map();
5
+ // Lane checks call `codeloop check ...`. The CLI is often run via npx or a local build and is not
6
+ // on PATH, so checks run with a shim that points back at the entry script of this process.
7
+ // The shim goes first on PATH, so its directory must be one nobody else can write to: mkdtemp
8
+ // gives a fresh 0700 directory with an unguessable name, removed when this process exits.
9
+ export function checkEnv(entry = process.argv[1]) {
10
+ if (!entry)
11
+ return { ...process.env, CODELOOP_NO_SYNC: '1' };
12
+ let dir = shims.get(entry);
13
+ if (!dir) {
14
+ dir = mkdtempSync(join(tmpdir(), 'codeloop-shim-'));
15
+ const shim = join(dir, 'codeloop');
16
+ writeFileSync(shim, `#!/bin/sh\nexec "${process.execPath}" "${entry}" "$@"\n`);
17
+ chmodSync(shim, 0o700);
18
+ if (shims.size === 0)
19
+ process.on('exit', () => shims.forEach(d => rmSync(d, { recursive: true, force: true })));
20
+ shims.set(entry, dir);
21
+ }
22
+ // The command that started this check or agent has already synced with the cloud.
23
+ return { ...process.env, CODELOOP_NO_SYNC: '1', PATH: `${dir}${delimiter}${process.env.PATH ?? ''}` };
24
+ }
25
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/lib/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEvC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,kGAAkG;AAClG,2FAA2F;AAC3F,8FAA8F;AAC9F,0FAA0F;AAC1F,MAAM,UAAU,QAAQ,CAAC,QAA4B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;IAC7D,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACnC,aAAa,CAAC,IAAI,EAAE,oBAAoB,OAAO,CAAC,QAAQ,MAAM,KAAK,UAAU,CAAC,CAAC;QAC/E,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAChH,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,kFAAkF;IAClF,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC;AACxG,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type Lane, type SkillEntry } from './lane.js';
2
+ export declare function defaultSkillDirs(projectDir: string): string[];
3
+ export declare function parseFrontmatter(text: string): {
4
+ data: Record<string, unknown>;
5
+ body: string;
6
+ };
7
+ /** `<name>/SKILL.md` is a skill; `*.md` is a command; `<group>/*.md` is the command `group:name`. */
8
+ export declare function scanSkills(projectDir: string, dirs: string[]): SkillEntry[];
9
+ export declare function writeSkillsIndex(projectDir: string, entries: SkillEntry[]): void;
10
+ /** Writes the index only when there is none: an existing one may hold entries from `adopt --from`. */
11
+ export declare function ensureSkillsIndex(projectDir: string, scan: () => SkillEntry[]): void;
12
+ export declare function duplicateSkills(entries: SkillEntry[]): {
13
+ name: string;
14
+ sources: string[];
15
+ }[];
16
+ export declare function doneGaps(lanes: Lane[]): {
17
+ lane: string;
18
+ stage: string;
19
+ reason: string;
20
+ }[];
@@ -0,0 +1,92 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { basename, join, relative, resolve } from 'path';
4
+ import { parse as parseYaml, stringify } from 'yaml';
5
+ import { SKILLS_INDEX } from './lane.js';
6
+ import { withLock } from './lock.js';
7
+ export function defaultSkillDirs(projectDir) {
8
+ return [
9
+ join(projectDir, '.claude/skills'),
10
+ join(projectDir, '.claude/commands'),
11
+ join(projectDir, '.cursor/commands'),
12
+ join(projectDir, '.agents/skills'),
13
+ join(homedir(), '.claude/skills'),
14
+ join(homedir(), '.claude/commands'),
15
+ ];
16
+ }
17
+ export function parseFrontmatter(text) {
18
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(text);
19
+ if (!m)
20
+ return { data: {}, body: text };
21
+ try {
22
+ return { data: (parseYaml(m[1]) ?? {}), body: m[2] };
23
+ }
24
+ catch {
25
+ // Command frontmatter in the wild is not always valid YAML (unquoted colons in descriptions).
26
+ const data = {};
27
+ for (const line of m[1].split('\n')) {
28
+ const kv = /^(\w[\w-]*):\s*(.*)$/.exec(line);
29
+ if (kv)
30
+ data[kv[1]] = kv[2];
31
+ }
32
+ return { data, body: m[2] };
33
+ }
34
+ }
35
+ function entry(projectDir, file, kind, fallbackName) {
36
+ const { data } = parseFrontmatter(readFileSync(file, 'utf-8'));
37
+ const inProject = !relative(projectDir, file).startsWith('..');
38
+ return {
39
+ name: typeof data.name === 'string' && data.name ? data.name : fallbackName,
40
+ source: inProject ? relative(projectDir, file) : file,
41
+ kind,
42
+ description: typeof data.description === 'string' ? data.description.trim() : '',
43
+ };
44
+ }
45
+ const isDir = (p) => statSync(p).isDirectory();
46
+ /** `<name>/SKILL.md` is a skill; `*.md` is a command; `<group>/*.md` is the command `group:name`. */
47
+ export function scanSkills(projectDir, dirs) {
48
+ const found = [];
49
+ for (const dir of dirs.map(d => resolve(projectDir, d))) {
50
+ if (!existsSync(dir))
51
+ continue;
52
+ for (const name of readdirSync(dir).sort()) {
53
+ const path = join(dir, name);
54
+ if (name.endsWith('.md') && !isDir(path)) {
55
+ found.push(entry(projectDir, path, 'command', basename(name, '.md')));
56
+ }
57
+ else if (isDir(path) && existsSync(join(path, 'SKILL.md'))) {
58
+ found.push(entry(projectDir, join(path, 'SKILL.md'), 'skill', name));
59
+ }
60
+ else if (isDir(path)) {
61
+ for (const sub of readdirSync(path).filter(f => f.endsWith('.md')).sort()) {
62
+ found.push(entry(projectDir, join(path, sub), 'command', `${name}:${basename(sub, '.md')}`));
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return found;
68
+ }
69
+ export function writeSkillsIndex(projectDir, entries) {
70
+ const file = join(projectDir, SKILLS_INDEX);
71
+ withLock(file, () => writeFileSync(file, stringify(entries)));
72
+ }
73
+ /** Writes the index only when there is none: an existing one may hold entries from `adopt --from`. */
74
+ export function ensureSkillsIndex(projectDir, scan) {
75
+ const file = join(projectDir, SKILLS_INDEX);
76
+ withLock(file, () => {
77
+ if (!existsSync(file))
78
+ writeFileSync(file, stringify(scan()));
79
+ });
80
+ }
81
+ export function duplicateSkills(entries) {
82
+ const byName = new Map();
83
+ for (const e of entries)
84
+ byName.set(e.name, [...(byName.get(e.name) ?? []), e.source]);
85
+ return [...byName].filter(([, sources]) => sources.length > 1).map(([name, sources]) => ({ name, sources }));
86
+ }
87
+ export function doneGaps(lanes) {
88
+ return lanes.flatMap(lane => lane.stages
89
+ .filter(s => !s.done?.cmd)
90
+ .map(s => ({ lane: lane.id, stage: s.id, reason: s.done?.event ? `done is only the event "${s.done.event}"` : 'no done check' })));
91
+ }
92
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type Role } from './engine.js';
2
+ export declare const LAYERS: readonly ["api", "sdk", "ui", "test", "docs"];
3
+ export interface Task {
4
+ id: string;
5
+ done: boolean;
6
+ parallel: boolean;
7
+ accept?: string;
8
+ layer: string;
9
+ text: string;
10
+ }
11
+ export declare function slugify(text: string): string;
12
+ /** `ref` is a card id or its number. A folder recorded on the card wins over a folder found by number. */
13
+ export declare function resolveSpecDir(projectDir: string, ref: string): string;
14
+ export declare function newSpec(projectDir: string, cardId: string, role?: Role): {
15
+ dir: string;
16
+ created: string[];
17
+ };
18
+ export declare function readTasks(projectDir: string, dir: string): {
19
+ tasks: Task[];
20
+ malformed: string[];
21
+ };
22
+ export declare function readAcceptance(projectDir: string, dir: string): string[];
23
+ export declare function checkSpec(projectDir: string, dir: string): string[];
24
+ export declare function tickTask(projectDir: string, dir: string, taskId: string): void;