@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,28 @@
1
+ /** One wiki page per competitor: docs and changelog links in the front matter, findings added below by each research stage. */
2
+ export declare const COMPETITORS_DIR = ".codeloop/wiki/competitors";
3
+ export interface Competitor {
4
+ /** The file name without `.md`. */
5
+ name: string;
6
+ path: string;
7
+ title: string;
8
+ docs?: string;
9
+ changelog?: string;
10
+ body: string;
11
+ }
12
+ export declare function listCompetitors(projectDir: string): Competitor[];
13
+ /** Adding a name that already has a page updates its links and keeps its findings. */
14
+ export declare function addCompetitor(projectDir: string, input: {
15
+ name: string;
16
+ title?: string;
17
+ docs?: string;
18
+ changelog?: string;
19
+ }): Competitor;
20
+ /**
21
+ * Copies each competitor's rows from a card's research onto that competitor's page, so findings
22
+ * build up across features. A page that already has rows from the card is left alone.
23
+ * Returns the pages that were changed.
24
+ */
25
+ export declare function appendFindings(projectDir: string, card: {
26
+ id: string;
27
+ title: string;
28
+ }, research: string): string[];
@@ -0,0 +1,90 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { stringify } from 'yaml';
4
+ import { RefusalError, safeName } from './cards.js';
5
+ import { CLOUD_COPY, writeCloudWikiPage } from './cloud.js';
6
+ import { withLock } from './lock.js';
7
+ import { parseFrontmatter } from './skills.js';
8
+ /** One wiki page per competitor: docs and changelog links in the front matter, findings added below by each research stage. */
9
+ export const COMPETITORS_DIR = '.codeloop/wiki/competitors';
10
+ const pagePath = (name) => `${COMPETITORS_DIR}/${safeName(name, 'competitor name').toLowerCase()}.md`;
11
+ function render(c) {
12
+ const frontmatter = { title: c.title, ...(c.docs ? { docs: c.docs } : {}), ...(c.changelog ? { changelog: c.changelog } : {}) };
13
+ return `---\n${stringify(frontmatter)}---\n\n${c.body.trim()}\n`;
14
+ }
15
+ export function listCompetitors(projectDir) {
16
+ const dir = join(projectDir, COMPETITORS_DIR);
17
+ if (!existsSync(dir))
18
+ return [];
19
+ return readdirSync(dir).filter(f => f.endsWith('.md') && !f.endsWith(CLOUD_COPY)).sort().map(file => {
20
+ const { data, body } = parseFrontmatter(readFileSync(join(dir, file), 'utf-8'));
21
+ const name = file.replace(/\.md$/, '');
22
+ return {
23
+ name,
24
+ path: `${COMPETITORS_DIR}/${file}`,
25
+ title: String(data.title ?? name),
26
+ ...(data.docs ? { docs: String(data.docs) } : {}),
27
+ ...(data.changelog ? { changelog: String(data.changelog) } : {}),
28
+ body: body.trim(),
29
+ };
30
+ });
31
+ }
32
+ /** Adding a name that already has a page updates its links and keeps its findings. */
33
+ export function addCompetitor(projectDir, input) {
34
+ const path = pagePath(input.name);
35
+ for (const url of [input.docs, input.changelog]) {
36
+ if (url && !/^https?:\/\//.test(url))
37
+ throw new RefusalError(`"${url}" is not an http(s) URL`);
38
+ }
39
+ return withLock(join(projectDir, path), () => {
40
+ const existing = listCompetitors(projectDir).find(c => c.path === path);
41
+ const page = {
42
+ name: path.replace(/^.*\//, '').replace(/\.md$/, ''),
43
+ path,
44
+ title: input.title ?? existing?.title ?? input.name,
45
+ docs: input.docs ?? existing?.docs,
46
+ changelog: input.changelog ?? existing?.changelog,
47
+ body: existing?.body ?? '## Findings',
48
+ };
49
+ mkdirSync(dirname(join(projectDir, path)), { recursive: true });
50
+ writeFileSync(join(projectDir, path), render(page));
51
+ writeCloudWikiPage(projectDir, path);
52
+ return page;
53
+ });
54
+ }
55
+ // A findings row is a table row or a list item in research.md whose first cell or label is a competitor's name.
56
+ function rowFor(line, competitor) {
57
+ const names = [competitor.name.toLowerCase(), competitor.title.toLowerCase()];
58
+ const table = /^\|\s*([^|]+?)\s*\|(.+)$/.exec(line.trim());
59
+ if (table && names.includes(table[1].toLowerCase()))
60
+ return table[2].replace(/\|\s*$/, '').split('|').map(c => c.trim()).join(' | ');
61
+ const item = /^[-*]\s+\**([^:*]+?)\**\s*:\s*(.+)$/.exec(line.trim());
62
+ return item && names.includes(item[1].toLowerCase()) ? item[2].trim() : null;
63
+ }
64
+ /**
65
+ * Copies each competitor's rows from a card's research onto that competitor's page, so findings
66
+ * build up across features. A page that already has rows from the card is left alone.
67
+ * Returns the pages that were changed.
68
+ */
69
+ export function appendFindings(projectDir, card, research) {
70
+ const changed = [];
71
+ for (const competitor of listCompetitors(projectDir)) {
72
+ const rows = research.split('\n').map(l => rowFor(l, competitor)).filter((r) => r !== null);
73
+ if (rows.length === 0)
74
+ continue;
75
+ const done = withLock(join(projectDir, competitor.path), () => {
76
+ const current = listCompetitors(projectDir).find(c => c.path === competitor.path);
77
+ if (current.body.split('\n').some(l => l.startsWith(`- ${card.id} `)))
78
+ return false;
79
+ const body = `${current.body.includes('## Findings') ? current.body : `${current.body}\n\n## Findings`}\n\n${rows.map(r => `- ${card.id} ${card.title}: ${r}`).join('\n')}`;
80
+ writeFileSync(join(projectDir, competitor.path), render({ ...current, body }));
81
+ return true;
82
+ });
83
+ if (!done)
84
+ continue;
85
+ writeCloudWikiPage(projectDir, competitor.path);
86
+ changed.push(competitor.path);
87
+ }
88
+ return changed;
89
+ }
90
+ //# sourceMappingURL=competitors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"competitors.js","sourceRoot":"","sources":["../../src/lib/competitors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare const CONFIG_FILE = ".codeloop/config.yaml";
2
+ /** Never synced and never committed: keys, tokens and paths that are true only on this machine. */
3
+ export declare const LOCAL_FILE = ".codeloop/local.yaml";
4
+ type Values = Record<string, any>;
5
+ /** config.yaml with local.yaml laid over it. Every reader of project config goes through here. */
6
+ export declare function loadConfig(projectDir: string): Values;
7
+ export {};
@@ -0,0 +1,25 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ export const CONFIG_FILE = '.codeloop/config.yaml';
5
+ /** Never synced and never committed: keys, tokens and paths that are true only on this machine. */
6
+ export const LOCAL_FILE = '.codeloop/local.yaml';
7
+ const isMap = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
8
+ function read(projectDir, path) {
9
+ const file = join(projectDir, path);
10
+ const parsed = existsSync(file) ? parseYaml(readFileSync(file, 'utf-8')) : null;
11
+ return isMap(parsed) ? parsed : {};
12
+ }
13
+ // Maps merge key by key, so local.yaml can change one agent's command and leave the rest shared.
14
+ // A list or a plain value in local.yaml replaces the shared one.
15
+ function overlay(base, over) {
16
+ const out = { ...base };
17
+ for (const [key, value] of Object.entries(over))
18
+ out[key] = isMap(value) && isMap(base[key]) ? overlay(base[key], value) : value;
19
+ return out;
20
+ }
21
+ /** config.yaml with local.yaml laid over it. Every reader of project config goes through here. */
22
+ export function loadConfig(projectDir) {
23
+ return overlay(read(projectDir, CONFIG_FILE), read(projectDir, LOCAL_FILE));
24
+ }
25
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAE1C,MAAM,CAAC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AACnD,mGAAmG;AACnG,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAIjD,MAAM,KAAK,GAAG,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAEpG,SAAS,IAAI,CAAC,UAAkB,EAAE,IAAY;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AACrC,CAAC;AAED,iGAAiG;AACjG,iEAAiE;AACjE,SAAS,OAAO,CAAC,IAAY,EAAE,IAAY;IACzC,MAAM,GAAG,GAAW,EAAE,GAAG,IAAI,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACjI,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function cronError(expr: string): string | null;
2
+ /** Matches in local time, as cron does. */
3
+ export declare function cronMatches(expr: string, date: Date): boolean;
4
+ /** The latest minute in (after, now] that the expression matches, or null. */
5
+ export declare function lastDueSlot(expr: string, after: Date, now: Date): Date | null;
@@ -0,0 +1,82 @@
1
+ const DOW = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];
2
+ // One field: a comma list of `*`, `*/n`, `a`, `a-b` or `a-b/n`. Day names are accepted for day-of-week.
3
+ function parseField(field, min, max, names) {
4
+ if (field === '*')
5
+ return null;
6
+ const unsupported = () => new Error(`unsupported cron field "${field}" (use *, */n, a, a-b, a-b/n or a comma list${names ? '; MON..SUN allowed' : ''}; values ${min}-${max})`);
7
+ const value = (text) => {
8
+ const named = names ? names.indexOf(text.toUpperCase()) : -1;
9
+ const n = named >= 0 ? named : /^\d+$/.test(text) ? parseInt(text, 10) : NaN;
10
+ if (Number.isNaN(n) || n < min || n > max)
11
+ throw unsupported();
12
+ return n;
13
+ };
14
+ const out = new Set();
15
+ for (const part of field.split(',')) {
16
+ const [range, stepText, extra] = part.split('/');
17
+ if (extra !== undefined || range === '')
18
+ throw unsupported();
19
+ const step = stepText === undefined ? 1 : /^\d+$/.test(stepText) ? parseInt(stepText, 10) : 0;
20
+ if (step < 1)
21
+ throw unsupported();
22
+ const bounds = range === '*' ? [min, max] : range.split('-').map(value);
23
+ if (bounds.length > 2)
24
+ throw unsupported();
25
+ const from = bounds[0];
26
+ // `5/10` means "from 5 to the end, every 10", as in cron.
27
+ const to = bounds.length === 2 ? bounds[1] : stepText === undefined ? from : max;
28
+ if (from > to)
29
+ throw unsupported();
30
+ for (let n = from; n <= to; n += step)
31
+ out.add(n);
32
+ }
33
+ return out;
34
+ }
35
+ function parseCron(expr) {
36
+ const f = expr.trim().split(/\s+/);
37
+ if (f.length !== 5)
38
+ throw new Error(`cron "${expr}" must have 5 fields`);
39
+ const dow = parseField(f[4], 0, 7, DOW);
40
+ if (dow?.has(7))
41
+ dow.add(0);
42
+ return {
43
+ minute: parseField(f[0], 0, 59),
44
+ hour: parseField(f[1], 0, 23),
45
+ dom: parseField(f[2], 1, 31),
46
+ month: parseField(f[3], 1, 12),
47
+ dow,
48
+ };
49
+ }
50
+ export function cronError(expr) {
51
+ try {
52
+ parseCron(expr);
53
+ return null;
54
+ }
55
+ catch (e) {
56
+ return e.message;
57
+ }
58
+ }
59
+ /** Matches in local time, as cron does. */
60
+ export function cronMatches(expr, date) {
61
+ const c = parseCron(expr);
62
+ const ok = (set, v) => set === null || set.has(v);
63
+ if (!ok(c.minute, date.getMinutes()) || !ok(c.hour, date.getHours()) || !ok(c.month, date.getMonth() + 1)) {
64
+ return false;
65
+ }
66
+ // Standard cron: when both day fields are restricted, either one matching is enough.
67
+ if (c.dom && c.dow)
68
+ return c.dom.has(date.getDate()) || c.dow.has(date.getDay());
69
+ return ok(c.dom, date.getDate()) && ok(c.dow, date.getDay());
70
+ }
71
+ /** The latest minute in (after, now] that the expression matches, or null. */
72
+ export function lastDueSlot(expr, after, now) {
73
+ const slot = new Date(now);
74
+ slot.setSeconds(0, 0);
75
+ while (slot.getTime() > after.getTime()) {
76
+ if (cronMatches(expr, slot))
77
+ return slot;
78
+ slot.setMinutes(slot.getMinutes() - 1);
79
+ }
80
+ return null;
81
+ }
82
+ //# sourceMappingURL=cron.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/lib/cron.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type StackId = 'node-typescript' | 'python' | 'go' | 'generic';
2
+ export type ToolId = 'claude' | 'cursor' | 'codex';
3
+ export interface DetectionResult {
4
+ stack: StackId;
5
+ description: string;
6
+ matchedFile: string | null;
7
+ }
8
+ export declare function detectStack(projectDir: string): DetectionResult;
9
+ /**
10
+ * Detect which AI coding tools are present in the project.
11
+ * Looks for tool-specific directories.
12
+ */
13
+ export declare function detectTools(projectDir: string): ToolId[];
@@ -0,0 +1,60 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ const rules = [
4
+ {
5
+ stack: 'node-typescript',
6
+ files: ['tsconfig.json'],
7
+ description: 'TypeScript (Node.js)',
8
+ },
9
+ {
10
+ stack: 'python',
11
+ files: ['pyproject.toml', 'setup.py', 'requirements.txt', 'Pipfile'],
12
+ description: 'Python',
13
+ },
14
+ {
15
+ stack: 'go',
16
+ files: ['go.mod'],
17
+ description: 'Go',
18
+ },
19
+ ];
20
+ export function detectStack(projectDir) {
21
+ for (const rule of rules) {
22
+ for (const file of rule.files) {
23
+ if (existsSync(join(projectDir, file))) {
24
+ return {
25
+ stack: rule.stack,
26
+ description: rule.description,
27
+ matchedFile: file,
28
+ };
29
+ }
30
+ }
31
+ }
32
+ return {
33
+ stack: 'generic',
34
+ description: 'Generic project',
35
+ matchedFile: null,
36
+ };
37
+ }
38
+ /**
39
+ * Detect which AI coding tools are present in the project.
40
+ * Looks for tool-specific directories.
41
+ */
42
+ export function detectTools(projectDir) {
43
+ const detected = [];
44
+ // Claude Code: .claude/ directory or CLAUDE.md
45
+ if (existsSync(join(projectDir, '.claude')) || existsSync(join(projectDir, 'CLAUDE.md'))) {
46
+ detected.push('claude');
47
+ }
48
+ // Cursor: .cursor/ directory or .cursorrules
49
+ if (existsSync(join(projectDir, '.cursor')) || existsSync(join(projectDir, '.cursorrules'))) {
50
+ detected.push('cursor');
51
+ }
52
+ // Codex: .codex/ directory or AGENTS.md or .agents/
53
+ if (existsSync(join(projectDir, '.codex')) ||
54
+ existsSync(join(projectDir, 'AGENTS.md')) ||
55
+ existsSync(join(projectDir, '.agents'))) {
56
+ detected.push('codex');
57
+ }
58
+ return detected;
59
+ }
60
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,MAAM,KAAK,GAAoB;IAC7B;QACE,KAAK,EAAE,iBAAiB;QACxB,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,WAAW,EAAE,sBAAsB;KACpC;IACD;QACE,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,CAAC,gBAAgB,EAAE,UAAU,EAAE,kBAAkB,EAAE,SAAS,CAAC;QACpE,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,WAAW,EAAE,IAAI;KAClB;CACF,CAAC;AAQF,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBACvC,OAAO;oBACL,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,WAAW,EAAE,IAAI;iBAClB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,+CAA+C;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QACzF,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,6CAA6C;IAC7C,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QAC5F,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,oDAAoD;IACpD,IACE,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACtC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACzC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,EACvC,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { RefusalError, type Card, type CardEvent } from './cards.js';
2
+ export { RefusalError };
3
+ import { type Stage } from './lane.js';
4
+ export type Role = 'owner' | 'reviewer' | 'agent';
5
+ export interface EngineConfig {
6
+ gatesMode: 'all' | 'trusted';
7
+ gatesPerDay?: number;
8
+ }
9
+ export declare function loadEngineConfig(projectDir: string): EngineConfig;
10
+ /**
11
+ * `--as`, then CODELOOP_ROLE, then the terminal: a person typing at a TTY is the owner, anything
12
+ * piped or spawned (an agent, CI, the MCP server) is an agent.
13
+ */
14
+ export declare function resolveRole(flag?: string, tty?: boolean): Role;
15
+ export type Outcome = 'moved' | 'parked' | 'done' | 'failed' | 'stuck' | 'unchanged';
16
+ export interface AdvanceResult {
17
+ card: Card;
18
+ outcome: Outcome;
19
+ output?: string;
20
+ started: Card[];
21
+ }
22
+ interface Clock {
23
+ now?: Date;
24
+ }
25
+ /** True for a public step nobody has approved yet: no work and no check may run for it. */
26
+ export declare const awaitsApproval: (card: Card, stage: Stage) => boolean;
27
+ /** Runs the stage's done command without touching the card. null when the stage has no command. */
28
+ export declare function runCheck(projectDir: string, card: Card, stage: Stage): {
29
+ passed: boolean;
30
+ output: string;
31
+ } | null;
32
+ /** Records a spec folder or an evidence path on a card. It never changes the stage or the gate. */
33
+ export declare function annotateCard(projectDir: string, id: string, patch: {
34
+ spec?: string;
35
+ evidence?: string;
36
+ }, action: string, opts?: {
37
+ note?: string;
38
+ role?: Role;
39
+ } & Clock): Card;
40
+ /**
41
+ * Appends one event to a card; it never changes the stage or the gate. `build` sees the board the
42
+ * write is checked against, so a limit it enforces by throwing cannot be passed by two writers.
43
+ */
44
+ export declare function recordEvent(projectDir: string, id: string, build: (cards: Card[], card: Card) => Pick<CardEvent, 'action'> & Partial<CardEvent>, now?: Date): CardEvent;
45
+ /**
46
+ * Cards for work that already exists elsewhere (Spec Kit, BMAD). They are an inventory, so the
47
+ * wip limit does not apply and each lands at the stage its source status maps to.
48
+ */
49
+ export declare function importCards(projectDir: string, items: {
50
+ lane: string;
51
+ title: string;
52
+ stage?: string;
53
+ note: string;
54
+ spec: (id: string) => string;
55
+ }[], now?: Date): Card[];
56
+ /** Started work that is parked at a gate. Proposals are not started work, so they do not use up capacity.gates_per_day. */
57
+ export declare const waitingOnOwner: (cards: Card[]) => number;
58
+ /**
59
+ * A card that waits for the owner before it enters its lane: `approve` promotes it to the first
60
+ * stage, `reject` drops it. Nothing starts it. With `dedupe`, a second proposal made from the same
61
+ * finding returns the first card instead of creating another.
62
+ */
63
+ export declare function proposeCard(projectDir: string, input: {
64
+ lane: string;
65
+ title: string;
66
+ description?: string;
67
+ source?: string;
68
+ dedupe?: string;
69
+ role?: Role | 'engine';
70
+ } & Clock): {
71
+ card: Card;
72
+ created: boolean;
73
+ };
74
+ export declare function createCard(projectDir: string, input: {
75
+ lane: string;
76
+ title: string;
77
+ id?: string;
78
+ role?: Role;
79
+ note?: string;
80
+ } & Clock): Card;
81
+ export declare function advanceCard(projectDir: string, ref: string, opts?: {
82
+ event?: string;
83
+ unattended?: boolean;
84
+ } & Clock): AdvanceResult;
85
+ export declare function approveCard(projectDir: string, ref: string, role: Role, opts?: {
86
+ note?: string;
87
+ } & Clock): Card;
88
+ export declare function rejectCard(projectDir: string, ref: string, role: Role, note: string, opts?: Clock): Card;