@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,215 @@
1
+ import { spawnSync } from 'child_process';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { AGENT_RUNS_DIR, briefPath, buildBrief, runAgent } from './agent.js';
5
+ import { findCard, inLane, readCards, safeName } from './cards.js';
6
+ import { lastDueSlot } from './cron.js';
7
+ import { advanceCard, awaitsApproval, createCard, loadEngineConfig, recordEvent, RefusalError, runCheck, waitingOnOwner } from './engine.js';
8
+ import { nextHint } from './flow.js';
9
+ import { loadLane, loadLanes, shellQuote } from './lane.js';
10
+ import { clock } from './clock.js';
11
+ import { withLock } from './lock.js';
12
+ export const LAST_RUN = '.codeloop/state/last-run.json';
13
+ const FIRST_RUN_LOOKBACK_MS = 24 * 60 * 60 * 1000;
14
+ // The slot is a local time (cron is local), so its date in the title is the local one too.
15
+ const localDate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
16
+ const GIT_VALUE = {
17
+ 'git.commit': ['rev-parse', 'HEAD'],
18
+ 'git.tag': ['for-each-ref', '--sort=-creatordate', '--count=1', '--format=%(refname:short)', 'refs/tags'],
19
+ };
20
+ function git(projectDir, args) {
21
+ const run = spawnSync('git', args, { cwd: projectDir, encoding: 'utf-8' });
22
+ return run.status === 0 ? run.stdout.trim() : '';
23
+ }
24
+ export function runDue(projectDir, now = clock()) {
25
+ const file = join(projectDir, LAST_RUN);
26
+ // Two schedulers started together must not both see the same slot as new.
27
+ const result = withLock(file, () => startDue(projectDir, file, now));
28
+ for (const { id } of active(projectDir)) {
29
+ try {
30
+ result.advanced.push({ id, outcome: advanceCard(projectDir, id, { now, unattended: true }).outcome });
31
+ }
32
+ catch (e) {
33
+ if (!(e instanceof RefusalError))
34
+ throw e;
35
+ result.advanced.push({ id, outcome: 'refused', note: e.message });
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ const DAY_MS = 24 * 60 * 60 * 1000;
41
+ const active = (projectDir) => readCards(projectDir).cards.filter(c => inLane(c) && !c.gate);
42
+ const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
43
+ // An agent-start with no agent-run after it, younger than the timeout: another run is in the middle of this stage.
44
+ function running(card, agent, now) {
45
+ const last = card.events.filter(e => e.stage === card.stage && (e.action === 'agent-start' || e.action === 'agent-run')).at(-1);
46
+ return last?.action === 'agent-start' && now.getTime() - new Date(last.at).getTime() < agent.timeoutMinutes * 60_000 + 60_000;
47
+ }
48
+ /** Why no agent may start for this card now, from the board as given. `planned` counts starts a dry run has already listed. */
49
+ function limit(projectDir, cards, card, lane, agent, now, planned = 0) {
50
+ const ahead = cards.filter(c => c.lane === lane.id && inLane(c)).findIndex(c => c.id === card.id);
51
+ if (lane.wip !== undefined && ahead >= lane.wip)
52
+ return `lane ${lane.id} works on ${plural(lane.wip, 'card', 'cards')} at a time (wip); this one waits its turn`;
53
+ const { gatesPerDay } = loadEngineConfig(projectDir);
54
+ const parked = waitingOnOwner(cards);
55
+ if (gatesPerDay !== undefined && parked >= gatesPerDay)
56
+ return `${plural(parked, 'card is', 'cards are')} waiting on you (capacity.gates_per_day is ${gatesPerDay}); clear the inbox first`;
57
+ const starts = planned + cards.flatMap(c => c.events).filter(e => e.action === 'agent-start' && e.agent === agent.name && now.getTime() - new Date(e.at).getTime() < DAY_MS).length;
58
+ if (starts >= agent.maxRunsPerDay)
59
+ return `agent ${agent.name} has started ${plural(starts, 'time', 'times')} in the last 24 hours (max_runs_per_day is ${agent.maxRunsPerDay})`;
60
+ return undefined;
61
+ }
62
+ // null when the card needs no agent: its stage has no command to check the work with, or the check passes already.
63
+ async function agentStep(projectDir, id, agent, now) {
64
+ const card = findCard(readCards(projectDir).cards, id);
65
+ const lane = loadLane(projectDir, card.lane);
66
+ const stage = lane.stages.find(s => s.id === card.stage);
67
+ if (!stage?.done?.cmd)
68
+ return null;
69
+ const step = { id, stage: stage.id, agent: agent.name };
70
+ if (awaitsApproval(card, stage))
71
+ return { ...step, started: false, reason: `${stage.id} is a public step that has not been approved` };
72
+ if (running(card, agent, now))
73
+ return { ...step, started: false, busy: true, reason: `agent ${agent.name} is still running on ${stage.id} from an earlier run` };
74
+ // A rejection asks for the work to be done again, so the agent is started with the note even
75
+ // though the check that passed before the gate still passes. Once per rejection.
76
+ const asked = card.events.filter(e => e.stage === stage.id && (e.action === 'reject' || e.action === 'agent-start')).at(-1);
77
+ if (asked?.action !== 'reject' && runCheck(projectDir, card, stage)?.passed)
78
+ return null;
79
+ let log = '';
80
+ try {
81
+ // The limits are judged on the same read of the board the start is written against.
82
+ recordEvent(projectDir, id, (cards, current) => {
83
+ const reason = limit(projectDir, cards, current, lane, agent, now);
84
+ if (reason)
85
+ throw new RefusalError(reason);
86
+ const n = current.events.filter(e => e.action === 'agent-start' && e.stage === stage.id).length + 1;
87
+ log = `${AGENT_RUNS_DIR}/${id}-${safeName(stage.id, 'stage id')}-${n}.log`;
88
+ return { action: 'agent-start', agent: agent.name, log };
89
+ }, now);
90
+ }
91
+ catch (e) {
92
+ if (!(e instanceof RefusalError))
93
+ throw e;
94
+ return { ...step, started: false, reason: e.message };
95
+ }
96
+ const brief = join(projectDir, briefPath(card));
97
+ mkdirSync(dirname(brief), { recursive: true });
98
+ writeFileSync(brief, buildBrief(projectDir, card));
99
+ const run = await runAgent(projectDir, agent, brief, join(projectDir, log));
100
+ const note = run.timedOut ? `timed out after ${agent.timeoutMinutes} minutes` : run.exit === null ? 'was killed' : undefined;
101
+ recordEvent(projectDir, id, () => ({ action: 'agent-run', agent: agent.name, exit: run.exit, durationMs: run.durationMs, log, ...(note ? { note } : {}) }), new Date(now.getTime() + run.durationMs));
102
+ return { ...step, started: true, exit: run.exit, durationMs: run.durationMs, log };
103
+ }
104
+ /** `runDue`, with an agent started first on each card whose stage check does not pass yet. One stage per card. */
105
+ export async function runDueWithAgent(projectDir, agent, now = clock()) {
106
+ const file = join(projectDir, LAST_RUN);
107
+ const result = { ...withLock(file, () => startDue(projectDir, file, now)), agents: [] };
108
+ for (const { id, stage } of active(projectDir)) {
109
+ try {
110
+ const step = await agentStep(projectDir, id, agent, now);
111
+ if (step) {
112
+ const { busy, ...entry } = step;
113
+ result.agents.push(entry);
114
+ if (busy)
115
+ continue;
116
+ }
117
+ const card = findCard(readCards(projectDir).cards, id);
118
+ if (card.stage !== stage || card.gate) {
119
+ result.advanced.push({ id, outcome: 'refused', note: 'the card was moved while the agent ran; nothing more this run' });
120
+ continue;
121
+ }
122
+ // What an agent just did is a new attempt even when it left the output as it was, so it is
123
+ // judged and counted. Without that a failing agent would be started again on every run.
124
+ const advance = advanceCard(projectDir, id, { now: new Date(now.getTime() + (step?.durationMs ?? 0)), unattended: !step?.started });
125
+ const failed = step?.started && (advance.outcome === 'failed' || advance.outcome === 'stuck');
126
+ result.advanced.push({ id, outcome: advance.outcome, ...(failed ? { next: nextHint(projectDir, advance.card, advance.outcome === 'failed' ? advance.output ?? '' : undefined) } : {}) });
127
+ }
128
+ catch (e) {
129
+ if (!(e instanceof RefusalError))
130
+ throw e;
131
+ result.advanced.push({ id, outcome: 'refused', note: e.message });
132
+ }
133
+ }
134
+ return result;
135
+ }
136
+ /** What a run would do, for `--dry-run`. Nothing is started, checked or written. */
137
+ export function planRun(projectDir, agent, now = clock()) {
138
+ const { cards } = readCards(projectDir);
139
+ let planned = 0;
140
+ return cards.filter(c => inLane(c) && !c.gate).map(card => {
141
+ const lane = loadLane(projectDir, card.lane);
142
+ const stage = lane.stages.find(s => s.id === card.stage);
143
+ if (!agent || !stage?.done?.cmd)
144
+ return `${card.id}: would run the ${card.stage} check and move the card if it passes`;
145
+ const reason = awaitsApproval(card, stage)
146
+ ? `${stage.id} is a public step that has not been approved`
147
+ : running(card, agent, now)
148
+ ? `agent ${agent.name} is still running on ${stage.id} from an earlier run`
149
+ : limit(projectDir, cards, card, lane, agent, now, planned);
150
+ if (reason)
151
+ return `${card.id}: would not start agent ${agent.name} on ${stage.id}: ${reason}`;
152
+ planned++;
153
+ return `${card.id}: would start agent ${agent.name} on ${stage.id} unless its check already passes: ${agent.cmd.replaceAll('{brief}', shellQuote(join(projectDir, briefPath(card))))}`;
154
+ });
155
+ }
156
+ function startDue(projectDir, file, now) {
157
+ const last = existsSync(file) ? JSON.parse(readFileSync(file, 'utf-8')) : null;
158
+ const after = last ? new Date(last.at) : new Date(now.getTime() - FIRST_RUN_LOOKBACK_MS);
159
+ const slots = { ...(last?.slots ?? {}) };
160
+ const seen = { ...(last?.git ?? {}) };
161
+ const result = { created: [], skipped: [], advanced: [] };
162
+ for (const lane of loadLanes(projectDir)) {
163
+ const args = GIT_VALUE[lane.trigger?.on ?? ''];
164
+ if (!args)
165
+ continue;
166
+ const value = git(projectDir, args);
167
+ // The last value is recorded per lane, so the same HEAD or tag starts one card however often this runs.
168
+ if (!value || seen[lane.id] === value)
169
+ continue;
170
+ try {
171
+ const label = lane.trigger.on === 'git.commit' ? value.slice(0, 7) : value;
172
+ const card = createCard(projectDir, { lane: lane.id, title: `${lane.id} ${label}`, now, note: `${lane.trigger.on} ${value}` });
173
+ result.created.push({ lane: lane.id, id: card.id, slot: value, trigger: `${lane.trigger.on} ${label}` });
174
+ seen[lane.id] = value;
175
+ }
176
+ catch (e) {
177
+ if (!(e instanceof RefusalError))
178
+ throw e;
179
+ result.skipped.push({ lane: lane.id, reason: e.message });
180
+ }
181
+ }
182
+ for (const lane of loadLanes(projectDir)) {
183
+ if (!lane.trigger?.cron)
184
+ continue;
185
+ // One lane's unreadable schedule is that lane's problem; the others still run.
186
+ let due;
187
+ try {
188
+ due = lastDueSlot(lane.trigger.cron, after, now);
189
+ }
190
+ catch (e) {
191
+ result.skipped.push({ lane: lane.id, reason: e.message });
192
+ continue;
193
+ }
194
+ const slot = due?.toISOString();
195
+ // The slot is recorded per lane, so a re-run inside the same window cannot create a second card.
196
+ if (!slot || slots[lane.id] === slot)
197
+ continue;
198
+ try {
199
+ const card = createCard(projectDir, { lane: lane.id, title: `${lane.id} ${localDate(due)}`, now, note: `cron ${lane.trigger.cron}` });
200
+ result.created.push({ lane: lane.id, id: card.id, slot, trigger: `cron ${lane.trigger.cron}` });
201
+ slots[lane.id] = slot;
202
+ }
203
+ catch (e) {
204
+ if (!(e instanceof RefusalError))
205
+ throw e;
206
+ result.skipped.push({ lane: lane.id, reason: e.message });
207
+ }
208
+ }
209
+ // A refused slot must be found again next run, so the window start only moves when nothing was skipped.
210
+ const at = result.skipped.length ? after.toISOString() : now.toISOString();
211
+ mkdirSync(dirname(file), { recursive: true });
212
+ writeFileSync(file, JSON.stringify({ at, slots, git: seen }, null, 2) + '\n');
213
+ return result;
214
+ }
215
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export type ToolId = 'claude' | 'cursor' | 'codex';
2
+ export interface ScaffoldResult {
3
+ created: string[];
4
+ skipped: string[];
5
+ }
6
+ export declare function scaffold(projectDir: string, starterFile: string, tools: ToolId[]): ScaffoldResult;
7
+ /** Copies the GitHub workflows from templates/ci. An existing workflow file is never overwritten. */
8
+ export declare function installCi(projectDir: string): ScaffoldResult;
9
+ /** Installs the commit-msg hook. An existing hook that is not ours is left alone. */
10
+ export declare function installHooks(projectDir: string): {
11
+ installed: boolean;
12
+ path: string;
13
+ reason?: string;
14
+ };
15
+ export declare function getTemplateVersion(templatePath: string): string | null;
16
+ export declare function getInstalledVersion(projectDir: string, filePath: string): string | null;
@@ -0,0 +1,175 @@
1
+ import fse from 'fs-extra';
2
+ import { spawnSync } from 'child_process';
3
+ import { join, dirname, resolve } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ const { copySync, ensureDirSync, existsSync, readdirSync, readFileSync, writeFileSync } = fse;
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ // Walk up from dist/lib/ to package root
9
+ const PACKAGE_ROOT = join(__dirname, '..', '..');
10
+ /**
11
+ * Map template commands to tool-specific destinations.
12
+ *
13
+ * Claude Code: .claude/commands/*.md (markdown with frontmatter)
14
+ * Cursor: .cursor/commands/*.md (same format — Cursor supports this natively)
15
+ * Codex: .agents/skills/<name>/SKILL.md (YAML frontmatter with name/description)
16
+ */
17
+ function getCommandDestinations(tools) {
18
+ const commands = ['design', 'plan', 'manage', 'test', 'commit', 'qa', 'deploy', 'debug', 'reflect', 'ship'];
19
+ const files = [];
20
+ for (const cmd of commands) {
21
+ const source = `templates/commands/${cmd}.md`;
22
+ if (tools.includes('claude')) {
23
+ files.push({ source, destination: `.claude/commands/${cmd}.md`, overwrite: false });
24
+ }
25
+ if (tools.includes('cursor')) {
26
+ files.push({ source, destination: `.cursor/commands/${cmd}.md`, overwrite: false });
27
+ }
28
+ if (tools.includes('codex')) {
29
+ files.push({ source, destination: `.agents/skills/${cmd}/SKILL.md`, overwrite: false });
30
+ }
31
+ }
32
+ return files;
33
+ }
34
+ function getLaneFiles() {
35
+ const dir = join(PACKAGE_ROOT, 'templates/lanes');
36
+ if (!existsSync(dir))
37
+ return [];
38
+ return readdirSync(dir)
39
+ .filter(f => f.endsWith('.yaml'))
40
+ .map(f => ({ source: `templates/lanes/${f}`, destination: `.codeloop/lanes/${f}`, overwrite: false }));
41
+ }
42
+ function getKnowledgeFiles() {
43
+ return [
44
+ ...getLaneFiles(),
45
+ { source: 'templates/codeloop/rules.md', destination: '.codeloop/rules.md', overwrite: false },
46
+ { source: 'templates/codeloop/gotchas.md', destination: '.codeloop/gotchas.md', overwrite: false },
47
+ { source: 'templates/codeloop/patterns.md', destination: '.codeloop/patterns.md', overwrite: false },
48
+ { source: 'templates/codeloop/principles.md', destination: '.codeloop/principles.md', overwrite: false },
49
+ { source: 'templates/codeloop/board.json', destination: '.codeloop/board.json', overwrite: false },
50
+ { source: 'templates/codeloop/.gitignore', destination: '.codeloop/.gitignore', overwrite: false },
51
+ { source: 'templates/tasks/todo.md', destination: 'tasks/todo.md', overwrite: false },
52
+ ];
53
+ }
54
+ export function scaffold(projectDir, starterFile, tools) {
55
+ const result = { created: [], skipped: [] };
56
+ // 1. Copy command files to tool-specific directories
57
+ const commandFiles = getCommandDestinations(tools);
58
+ for (const file of commandFiles) {
59
+ const destPath = join(projectDir, file.destination);
60
+ const srcPath = join(PACKAGE_ROOT, file.source);
61
+ if (!existsSync(srcPath))
62
+ continue;
63
+ if (existsSync(destPath) && !file.overwrite) {
64
+ result.skipped.push(file.destination);
65
+ continue;
66
+ }
67
+ ensureDirSync(dirname(destPath));
68
+ copySync(srcPath, destPath);
69
+ result.created.push(file.destination);
70
+ }
71
+ // 2. Copy knowledge files → .codeloop/
72
+ for (const file of getKnowledgeFiles()) {
73
+ const destPath = join(projectDir, file.destination);
74
+ const srcPath = join(PACKAGE_ROOT, file.source);
75
+ if (!existsSync(srcPath))
76
+ continue;
77
+ if (existsSync(destPath) && !file.overwrite) {
78
+ result.skipped.push(file.destination);
79
+ continue;
80
+ }
81
+ ensureDirSync(dirname(destPath));
82
+ copySync(srcPath, destPath);
83
+ result.created.push(file.destination);
84
+ }
85
+ // 3. Append seed content to knowledge files based on stack
86
+ const stackId = starterFile.replace('.yaml', '');
87
+ const seedTargets = [
88
+ {
89
+ knowledge: '.codeloop/gotchas.md',
90
+ seeds: ['templates/seeds/universal-gotchas.md', `templates/seeds/${stackId}-gotchas.md`],
91
+ },
92
+ {
93
+ knowledge: '.codeloop/patterns.md',
94
+ seeds: ['templates/seeds/universal-patterns.md', `templates/seeds/${stackId}-patterns.md`],
95
+ },
96
+ ];
97
+ for (const target of seedTargets) {
98
+ const destPath = join(projectDir, target.knowledge);
99
+ // Only append seeds to files we just created (not pre-existing ones)
100
+ if (!result.created.includes(target.knowledge))
101
+ continue;
102
+ if (!existsSync(destPath))
103
+ continue;
104
+ let content = readFileSync(destPath, 'utf-8');
105
+ for (const seedSource of target.seeds) {
106
+ const seedPath = join(PACKAGE_ROOT, seedSource);
107
+ if (existsSync(seedPath)) {
108
+ content += '\n' + readFileSync(seedPath, 'utf-8').trimEnd() + '\n';
109
+ }
110
+ }
111
+ writeFileSync(destPath, content);
112
+ }
113
+ // 4. Copy starter config → .codeloop/config.yaml
114
+ const configDest = join(projectDir, '.codeloop/config.yaml');
115
+ if (!existsSync(configDest)) {
116
+ const starterPath = join(PACKAGE_ROOT, 'starters', starterFile);
117
+ if (existsSync(starterPath)) {
118
+ ensureDirSync(dirname(configDest));
119
+ copySync(starterPath, configDest);
120
+ result.created.push('.codeloop/config.yaml');
121
+ }
122
+ }
123
+ else {
124
+ result.skipped.push('.codeloop/config.yaml');
125
+ }
126
+ return result;
127
+ }
128
+ /** Copies the GitHub workflows from templates/ci. An existing workflow file is never overwritten. */
129
+ export function installCi(projectDir) {
130
+ const result = { created: [], skipped: [] };
131
+ const dir = join(PACKAGE_ROOT, 'templates/ci');
132
+ for (const file of readdirSync(dir).filter(f => f.endsWith('.yml'))) {
133
+ const destination = `.github/workflows/${file}`;
134
+ const dest = join(projectDir, destination);
135
+ if (existsSync(dest)) {
136
+ result.skipped.push(destination);
137
+ continue;
138
+ }
139
+ ensureDirSync(dirname(dest));
140
+ copySync(join(dir, file), dest);
141
+ result.created.push(destination);
142
+ }
143
+ return result;
144
+ }
145
+ /** Installs the commit-msg hook. An existing hook that is not ours is left alone. */
146
+ export function installHooks(projectDir) {
147
+ const run = spawnSync('git', ['rev-parse', '--git-path', 'hooks'], { cwd: projectDir, encoding: 'utf-8' });
148
+ if (run.status !== 0)
149
+ return { installed: false, path: '', reason: 'not a git repository' };
150
+ const path = join(resolve(projectDir, run.stdout.trim()), 'commit-msg');
151
+ const source = readFileSync(join(PACKAGE_ROOT, 'templates/hooks/commit-msg'), 'utf-8');
152
+ if (existsSync(path) && readFileSync(path, 'utf-8') !== source) {
153
+ return { installed: false, path, reason: 'a different commit-msg hook already exists' };
154
+ }
155
+ ensureDirSync(dirname(path));
156
+ writeFileSync(path, source, { mode: 0o755 });
157
+ return { installed: true, path };
158
+ }
159
+ export function getTemplateVersion(templatePath) {
160
+ const fullPath = join(PACKAGE_ROOT, templatePath);
161
+ if (!existsSync(fullPath))
162
+ return null;
163
+ const content = readFileSync(fullPath, 'utf-8');
164
+ const match = content.match(/<!--\s*codeloop-version:\s*([\d.]+)\s*-->/);
165
+ return match ? match[1] : null;
166
+ }
167
+ export function getInstalledVersion(projectDir, filePath) {
168
+ const fullPath = join(projectDir, filePath);
169
+ if (!existsSync(fullPath))
170
+ return null;
171
+ const content = readFileSync(fullPath, 'utf-8');
172
+ const match = content.match(/<!--\s*codeloop-version:\s*([\d.]+)\s*-->/);
173
+ return match ? match[1] : null;
174
+ }
175
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/lib/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC;AAE9F,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,yCAAyC;AACzC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAejD;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,KAAe;IAC7C,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC5G,MAAM,KAAK,GAAmB,EAAE,CAAC;IAEjC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,sBAAsB,GAAG,KAAK,CAAC;QAE9C,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,WAAW,CAAC,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,EAAE,WAAW,EAAE,mBAAmB,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAC3G,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;QACL,GAAG,YAAY,EAAE;QACjB,EAAE,MAAM,EAAE,6BAA6B,EAAE,WAAW,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,EAAE;QAC9F,EAAE,MAAM,EAAE,+BAA+B,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAAE;QAClG,EAAE,MAAM,EAAE,gCAAgC,EAAE,WAAW,EAAE,uBAAuB,EAAE,SAAS,EAAE,KAAK,EAAE;QACpG,EAAE,MAAM,EAAE,kCAAkC,EAAE,WAAW,EAAE,yBAAyB,EAAE,SAAS,EAAE,KAAK,EAAE;QACxG,EAAE,MAAM,EAAE,+BAA+B,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAAE;QAClG,EAAE,MAAM,EAAE,+BAA+B,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAAE;QAClG,EAAE,MAAM,EAAE,yBAAyB,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE;KACtF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAkB,EAAE,WAAmB,EAAE,KAAe;IAC/E,MAAM,MAAM,GAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAE5D,qDAAqD;IACrD,MAAM,YAAY,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEhD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS;QAEnC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QAED,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED,uCAAuC;IACvC,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEhD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS;QAEnC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QAED,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED,2DAA2D;IAC3D,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACjD,MAAM,WAAW,GAA6C;QAC5D;YACE,SAAS,EAAE,sBAAsB;YACjC,KAAK,EAAE,CAAC,sCAAsC,EAAE,mBAAmB,OAAO,aAAa,CAAC;SACzF;QACD;YACE,SAAS,EAAE,uBAAuB;YAClC,KAAK,EAAE,CAAC,uCAAuC,EAAE,mBAAmB,OAAO,cAAc,CAAC;SAC3F;KACF,CAAC;IAEF,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACpD,qEAAqE;QACrE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;YAAE,SAAS;QACzD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEpC,IAAI,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9C,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;YAChD,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzB,OAAO,IAAI,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;YACrE,CAAC;QACH,CAAC;QACD,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,iDAAiD;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAChE,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;YACnC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;YAClC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,MAAM,MAAM,GAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,WAAW,GAAG,qBAAqB,IAAI,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACjC,SAAS;QACX,CAAC;QACD,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3G,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,4BAA4B,CAAC,EAAE,OAAO,CAAC,CAAC;IACvF,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;QAC/D,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;IAC1F,CAAC;IACD,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,QAAgB;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC"}
@@ -0,0 +1,22 @@
1
+ export declare const SCAN_DIR = ".codeloop/state/scan";
2
+ export interface ScanResult {
3
+ name: string;
4
+ /** baseline: first text seen, stored and nothing proposed. already-proposed: a card for this change exists. */
5
+ outcome: 'proposed' | 'already-proposed' | 'unchanged' | 'baseline' | 'skipped';
6
+ card?: string;
7
+ reason?: string;
8
+ }
9
+ /** A changelog page as lines of text. HTML headings become `#` lines so a heading can be told from a sentence. */
10
+ export declare function pageText(raw: string): string;
11
+ /** Lines of `after` that `before` does not have, in page order. */
12
+ export declare function newLines(before: string, after: string): string[];
13
+ /**
14
+ * Reads each competitor page's changelog, compares it with the text stored from the last scan and,
15
+ * when new text appeared, proposes one card in the plan lane for that competitor. The card waits
16
+ * for the owner; nothing starts it. `fromFile` supplies the page text instead of fetching.
17
+ */
18
+ export declare function scanCompetitors(projectDir: string, opts?: {
19
+ fromFile?: string;
20
+ only?: string;
21
+ now?: Date;
22
+ }): Promise<ScanResult[]>;
@@ -0,0 +1,85 @@
1
+ import { createHash } from 'crypto';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import { dirname, join, resolve } from 'path';
4
+ import { listCompetitors } from './competitors.js';
5
+ import { proposeCard } from './engine.js';
6
+ import { clock } from './clock.js';
7
+ import { withLock } from './lock.js';
8
+ export const SCAN_DIR = '.codeloop/state/scan';
9
+ const PLAN_LANE = 'plan';
10
+ const FETCH_TIMEOUT_MS = 10_000;
11
+ const EXCERPT_LINES = 12;
12
+ const TITLE_CHARS = 80;
13
+ const ENTITIES = { '&nbsp;': ' ', '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"', '&#39;': "'" };
14
+ /** A changelog page as lines of text. HTML headings become `#` lines so a heading can be told from a sentence. */
15
+ export function pageText(raw) {
16
+ const text = /<\/?(html|body|h[1-6]|p|div|li)\b/i.test(raw)
17
+ ? raw
18
+ .replace(/<(script|style)\b[\s\S]*?<\/\1>/gi, '')
19
+ .replace(/<h[1-6]\b[^>]*>/gi, '\n# ')
20
+ .replace(/<li\b[^>]*>/gi, '\n- ')
21
+ .replace(/<\/(h[1-6]|p|div|li|ul|ol|tr|section|article)>|<br\s*\/?>/gi, '\n')
22
+ .replace(/<[^>]+>/g, '')
23
+ .replace(/&nbsp;|&amp;|&lt;|&gt;|&quot;|&#39;/g, e => ENTITIES[e])
24
+ : raw;
25
+ return text.split('\n').map(l => l.replace(/\s+/g, ' ').trim()).filter(Boolean).join('\n') + '\n';
26
+ }
27
+ /** Lines of `after` that `before` does not have, in page order. */
28
+ export function newLines(before, after) {
29
+ const seen = new Set(before.split('\n'));
30
+ return after.split('\n').filter(l => l && !seen.has(l));
31
+ }
32
+ async function fetchText(url) {
33
+ try {
34
+ const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), headers: { 'user-agent': 'codeloop-scan' } });
35
+ return res.ok ? { text: await res.text() } : { reason: `${url} answered ${res.status}` };
36
+ }
37
+ catch (e) {
38
+ const cause = e.cause;
39
+ return { reason: `${url} did not answer (${cause?.code ?? e.message})` };
40
+ }
41
+ }
42
+ /**
43
+ * Reads each competitor page's changelog, compares it with the text stored from the last scan and,
44
+ * when new text appeared, proposes one card in the plan lane for that competitor. The card waits
45
+ * for the owner; nothing starts it. `fromFile` supplies the page text instead of fetching.
46
+ */
47
+ export async function scanCompetitors(projectDir, opts = {}) {
48
+ const results = [];
49
+ const now = opts.now ?? clock();
50
+ for (const competitor of listCompetitors(projectDir).filter(c => c.changelog && (!opts.only || c.name === opts.only))) {
51
+ const fetched = opts.fromFile ? { text: readFileSync(resolve(projectDir, opts.fromFile), 'utf-8') } : await fetchText(competitor.changelog);
52
+ if ('reason' in fetched) {
53
+ results.push({ name: competitor.name, outcome: 'skipped', reason: fetched.reason });
54
+ continue;
55
+ }
56
+ const text = pageText(fetched.text);
57
+ const stored = join(projectDir, SCAN_DIR, `${competitor.name}.txt`);
58
+ results.push(withLock(stored, () => {
59
+ const before = existsSync(stored) ? readFileSync(stored, 'utf-8') : null;
60
+ const fresh = before === null ? [] : newLines(before, text);
61
+ let result = { name: competitor.name, outcome: before === null ? 'baseline' : 'unchanged' };
62
+ if (fresh.length) {
63
+ const headline = (fresh.find(l => l.startsWith('#')) ?? fresh[0]).replace(/^[#\-*\s]+/, '').slice(0, TITLE_CHARS);
64
+ const excerpt = fresh.slice(0, EXCERPT_LINES).join('\n');
65
+ const { card, created } = proposeCard(projectDir, {
66
+ lane: PLAN_LANE,
67
+ title: `${competitor.title} shipped: ${headline}`,
68
+ description: `New on ${competitor.title}'s changelog, seen ${now.toISOString().slice(0, 10)}:\n\n${excerpt}${fresh.length > EXCERPT_LINES ? `\n(${fresh.length - EXCERPT_LINES} more new lines)` : ''}\n\nSource: ${competitor.changelog}`,
69
+ source: competitor.changelog,
70
+ // The change itself names the card, so the same new text cannot produce a second one.
71
+ dedupe: `scan:${competitor.name}:${createHash('sha1').update(fresh.join('\n')).digest('hex').slice(0, 16)}`,
72
+ role: 'engine',
73
+ now,
74
+ });
75
+ result = { name: competitor.name, outcome: created ? 'proposed' : 'already-proposed', card: card.id };
76
+ }
77
+ // Stored after the card exists: a crash in between repeats the proposal, which `dedupe` absorbs.
78
+ mkdirSync(dirname(stored), { recursive: true });
79
+ writeFileSync(stored, text);
80
+ return result;
81
+ }));
82
+ }
83
+ return results;
84
+ }
85
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/lib/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare const RUN_LOG = ".codeloop/state/run.log";
2
+ /** `30m`, `1h`, `6h`: only intervals that divide the hour or the day, since cron cannot say "every 45 minutes". */
3
+ export declare function everyToCron(every: string): string;
4
+ /**
5
+ * One crontab line. cron starts with a bare PATH, so the PATH of the installing shell is written
6
+ * into the line: the agent command (claude, codex) has to be found at 3am too.
7
+ */
8
+ export declare function scheduleLine(projectDir: string, opts: {
9
+ cron: string;
10
+ agent?: string | true;
11
+ entry?: string;
12
+ path?: string;
13
+ }): string;
14
+ export declare function installSchedule(projectDir: string, line: string, crontab?: string): void;
15
+ export declare function removeSchedule(projectDir: string, crontab?: string): boolean;
16
+ export declare function scheduleStatus(projectDir: string, crontab?: string): {
17
+ line?: string;
18
+ lastRun?: Date;
19
+ lastOutput?: string;
20
+ };