agent-bober 0.1.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 (212) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +495 -0
  4. package/agents/bober-evaluator.md +323 -0
  5. package/agents/bober-generator.md +245 -0
  6. package/agents/bober-planner.md +248 -0
  7. package/dist/cli/commands/eval.d.ts +6 -0
  8. package/dist/cli/commands/eval.d.ts.map +1 -0
  9. package/dist/cli/commands/eval.js +129 -0
  10. package/dist/cli/commands/eval.js.map +1 -0
  11. package/dist/cli/commands/init.d.ts +5 -0
  12. package/dist/cli/commands/init.d.ts.map +1 -0
  13. package/dist/cli/commands/init.js +547 -0
  14. package/dist/cli/commands/init.js.map +1 -0
  15. package/dist/cli/commands/plan.d.ts +5 -0
  16. package/dist/cli/commands/plan.d.ts.map +1 -0
  17. package/dist/cli/commands/plan.js +87 -0
  18. package/dist/cli/commands/plan.js.map +1 -0
  19. package/dist/cli/commands/run.d.ts +5 -0
  20. package/dist/cli/commands/run.d.ts.map +1 -0
  21. package/dist/cli/commands/run.js +120 -0
  22. package/dist/cli/commands/run.js.map +1 -0
  23. package/dist/cli/commands/sprint.d.ts +6 -0
  24. package/dist/cli/commands/sprint.d.ts.map +1 -0
  25. package/dist/cli/commands/sprint.js +206 -0
  26. package/dist/cli/commands/sprint.js.map +1 -0
  27. package/dist/cli/index.d.ts +3 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +124 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/config/defaults.d.ts +15 -0
  32. package/dist/config/defaults.d.ts.map +1 -0
  33. package/dist/config/defaults.js +226 -0
  34. package/dist/config/defaults.js.map +1 -0
  35. package/dist/config/index.d.ts +4 -0
  36. package/dist/config/index.d.ts.map +1 -0
  37. package/dist/config/index.js +8 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/config/loader.d.ts +18 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +189 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/config/schema.d.ts +904 -0
  44. package/dist/config/schema.d.ts.map +1 -0
  45. package/dist/config/schema.js +181 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/contracts/eval-result.d.ts +205 -0
  48. package/dist/contracts/eval-result.d.ts.map +1 -0
  49. package/dist/contracts/eval-result.js +87 -0
  50. package/dist/contracts/eval-result.js.map +1 -0
  51. package/dist/contracts/index.d.ts +4 -0
  52. package/dist/contracts/index.d.ts.map +1 -0
  53. package/dist/contracts/index.js +16 -0
  54. package/dist/contracts/index.js.map +1 -0
  55. package/dist/contracts/spec.d.ts +101 -0
  56. package/dist/contracts/spec.d.ts.map +1 -0
  57. package/dist/contracts/spec.js +51 -0
  58. package/dist/contracts/spec.js.map +1 -0
  59. package/dist/contracts/sprint-contract.d.ts +141 -0
  60. package/dist/contracts/sprint-contract.d.ts.map +1 -0
  61. package/dist/contracts/sprint-contract.js +80 -0
  62. package/dist/contracts/sprint-contract.js.map +1 -0
  63. package/dist/evaluators/builtin/api-check.d.ts +13 -0
  64. package/dist/evaluators/builtin/api-check.d.ts.map +1 -0
  65. package/dist/evaluators/builtin/api-check.js +152 -0
  66. package/dist/evaluators/builtin/api-check.js.map +1 -0
  67. package/dist/evaluators/builtin/build-check.d.ts +17 -0
  68. package/dist/evaluators/builtin/build-check.d.ts.map +1 -0
  69. package/dist/evaluators/builtin/build-check.js +155 -0
  70. package/dist/evaluators/builtin/build-check.js.map +1 -0
  71. package/dist/evaluators/builtin/command-runner.d.ts +26 -0
  72. package/dist/evaluators/builtin/command-runner.d.ts.map +1 -0
  73. package/dist/evaluators/builtin/command-runner.js +114 -0
  74. package/dist/evaluators/builtin/command-runner.js.map +1 -0
  75. package/dist/evaluators/builtin/lint.d.ts +17 -0
  76. package/dist/evaluators/builtin/lint.d.ts.map +1 -0
  77. package/dist/evaluators/builtin/lint.js +264 -0
  78. package/dist/evaluators/builtin/lint.js.map +1 -0
  79. package/dist/evaluators/builtin/playwright.d.ts +16 -0
  80. package/dist/evaluators/builtin/playwright.d.ts.map +1 -0
  81. package/dist/evaluators/builtin/playwright.js +238 -0
  82. package/dist/evaluators/builtin/playwright.js.map +1 -0
  83. package/dist/evaluators/builtin/typescript-check.d.ts +12 -0
  84. package/dist/evaluators/builtin/typescript-check.d.ts.map +1 -0
  85. package/dist/evaluators/builtin/typescript-check.js +155 -0
  86. package/dist/evaluators/builtin/typescript-check.js.map +1 -0
  87. package/dist/evaluators/builtin/unit-test.d.ts +18 -0
  88. package/dist/evaluators/builtin/unit-test.d.ts.map +1 -0
  89. package/dist/evaluators/builtin/unit-test.js +279 -0
  90. package/dist/evaluators/builtin/unit-test.js.map +1 -0
  91. package/dist/evaluators/index.d.ts +11 -0
  92. package/dist/evaluators/index.d.ts.map +1 -0
  93. package/dist/evaluators/index.js +13 -0
  94. package/dist/evaluators/index.js.map +1 -0
  95. package/dist/evaluators/plugin-interface.d.ts +50 -0
  96. package/dist/evaluators/plugin-interface.d.ts.map +1 -0
  97. package/dist/evaluators/plugin-interface.js +2 -0
  98. package/dist/evaluators/plugin-interface.js.map +1 -0
  99. package/dist/evaluators/plugin-loader.d.ts +18 -0
  100. package/dist/evaluators/plugin-loader.d.ts.map +1 -0
  101. package/dist/evaluators/plugin-loader.js +107 -0
  102. package/dist/evaluators/plugin-loader.js.map +1 -0
  103. package/dist/evaluators/registry.d.ts +78 -0
  104. package/dist/evaluators/registry.d.ts.map +1 -0
  105. package/dist/evaluators/registry.js +238 -0
  106. package/dist/evaluators/registry.js.map +1 -0
  107. package/dist/index.d.ts +17 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +22 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/orchestrator/context-handoff.d.ts +543 -0
  112. package/dist/orchestrator/context-handoff.d.ts.map +1 -0
  113. package/dist/orchestrator/context-handoff.js +133 -0
  114. package/dist/orchestrator/context-handoff.js.map +1 -0
  115. package/dist/orchestrator/evaluator-agent.d.ts +15 -0
  116. package/dist/orchestrator/evaluator-agent.d.ts.map +1 -0
  117. package/dist/orchestrator/evaluator-agent.js +233 -0
  118. package/dist/orchestrator/evaluator-agent.js.map +1 -0
  119. package/dist/orchestrator/generator-agent.d.ts +16 -0
  120. package/dist/orchestrator/generator-agent.d.ts.map +1 -0
  121. package/dist/orchestrator/generator-agent.js +147 -0
  122. package/dist/orchestrator/generator-agent.js.map +1 -0
  123. package/dist/orchestrator/pipeline.d.ts +24 -0
  124. package/dist/orchestrator/pipeline.d.ts.map +1 -0
  125. package/dist/orchestrator/pipeline.js +290 -0
  126. package/dist/orchestrator/pipeline.js.map +1 -0
  127. package/dist/orchestrator/planner-agent.d.ts +10 -0
  128. package/dist/orchestrator/planner-agent.d.ts.map +1 -0
  129. package/dist/orchestrator/planner-agent.js +187 -0
  130. package/dist/orchestrator/planner-agent.js.map +1 -0
  131. package/dist/state/helpers.d.ts +5 -0
  132. package/dist/state/helpers.d.ts.map +1 -0
  133. package/dist/state/helpers.js +8 -0
  134. package/dist/state/helpers.js.map +1 -0
  135. package/dist/state/history.d.ts +39 -0
  136. package/dist/state/history.d.ts.map +1 -0
  137. package/dist/state/history.js +162 -0
  138. package/dist/state/history.js.map +1 -0
  139. package/dist/state/index.d.ts +8 -0
  140. package/dist/state/index.d.ts.map +1 -0
  141. package/dist/state/index.js +22 -0
  142. package/dist/state/index.js.map +1 -0
  143. package/dist/state/plan-state.d.ts +21 -0
  144. package/dist/state/plan-state.d.ts.map +1 -0
  145. package/dist/state/plan-state.js +108 -0
  146. package/dist/state/plan-state.js.map +1 -0
  147. package/dist/state/sprint-state.d.ts +20 -0
  148. package/dist/state/sprint-state.d.ts.map +1 -0
  149. package/dist/state/sprint-state.js +98 -0
  150. package/dist/state/sprint-state.js.map +1 -0
  151. package/dist/utils/fs.d.ts +31 -0
  152. package/dist/utils/fs.d.ts.map +1 -0
  153. package/dist/utils/fs.js +67 -0
  154. package/dist/utils/fs.js.map +1 -0
  155. package/dist/utils/git.d.ts +35 -0
  156. package/dist/utils/git.d.ts.map +1 -0
  157. package/dist/utils/git.js +84 -0
  158. package/dist/utils/git.js.map +1 -0
  159. package/dist/utils/index.d.ts +4 -0
  160. package/dist/utils/index.d.ts.map +1 -0
  161. package/dist/utils/index.js +4 -0
  162. package/dist/utils/index.js.map +1 -0
  163. package/dist/utils/logger.d.ts +45 -0
  164. package/dist/utils/logger.d.ts.map +1 -0
  165. package/dist/utils/logger.js +73 -0
  166. package/dist/utils/logger.js.map +1 -0
  167. package/hooks/hooks.json +10 -0
  168. package/package.json +67 -0
  169. package/scripts/detect-stack.sh +287 -0
  170. package/scripts/init-project.sh +206 -0
  171. package/scripts/run-eval.sh +175 -0
  172. package/skills/bober.anchor/SKILL.md +365 -0
  173. package/skills/bober.anchor/references/anchor-guide.md +567 -0
  174. package/skills/bober.brownfield/SKILL.md +422 -0
  175. package/skills/bober.brownfield/references/codebase-analysis.md +304 -0
  176. package/skills/bober.eval/SKILL.md +235 -0
  177. package/skills/bober.eval/references/eval-strategies.md +407 -0
  178. package/skills/bober.eval/references/feedback-format.md +182 -0
  179. package/skills/bober.plan/SKILL.md +244 -0
  180. package/skills/bober.plan/references/clarification-guide.md +124 -0
  181. package/skills/bober.plan/references/spec-schema.md +253 -0
  182. package/skills/bober.react/SKILL.md +330 -0
  183. package/skills/bober.react/references/react-scaffold.md +344 -0
  184. package/skills/bober.run/SKILL.md +303 -0
  185. package/skills/bober.solidity/SKILL.md +416 -0
  186. package/skills/bober.solidity/references/solidity-guide.md +487 -0
  187. package/skills/bober.sprint/SKILL.md +280 -0
  188. package/skills/bober.sprint/references/contract-schema.md +251 -0
  189. package/templates/base/CLAUDE.md +20 -0
  190. package/templates/base/bober.config.json +35 -0
  191. package/templates/brownfield/CLAUDE.md +34 -0
  192. package/templates/brownfield/bober.config.json +37 -0
  193. package/templates/presets/anchor/CLAUDE.md +163 -0
  194. package/templates/presets/anchor/bober.config.json +9 -0
  195. package/templates/presets/api-node/CLAUDE.md +153 -0
  196. package/templates/presets/api-node/bober.config.json +10 -0
  197. package/templates/presets/nextjs/CLAUDE.md +82 -0
  198. package/templates/presets/nextjs/bober.config.json +14 -0
  199. package/templates/presets/python-api/CLAUDE.md +202 -0
  200. package/templates/presets/python-api/bober.config.json +9 -0
  201. package/templates/presets/react-vite/CLAUDE.md +71 -0
  202. package/templates/presets/react-vite/bober.config.json +53 -0
  203. package/templates/presets/react-vite/scaffold/package.json +45 -0
  204. package/templates/presets/react-vite/scaffold/server/index.ts +38 -0
  205. package/templates/presets/react-vite/scaffold/server/tsconfig.json +24 -0
  206. package/templates/presets/react-vite/scaffold/src/App.tsx +37 -0
  207. package/templates/presets/react-vite/scaffold/src/index.html +12 -0
  208. package/templates/presets/react-vite/scaffold/src/main.tsx +12 -0
  209. package/templates/presets/react-vite/scaffold/tsconfig.json +27 -0
  210. package/templates/presets/react-vite/scaffold/vite.config.ts +34 -0
  211. package/templates/presets/solidity/CLAUDE.md +106 -0
  212. package/templates/presets/solidity/bober.config.json +9 -0
@@ -0,0 +1,84 @@
1
+ import { execa } from "execa";
2
+ // ── Git Helpers ────────────────────────────────────────────────────
3
+ /**
4
+ * Get the name of the currently checked-out branch.
5
+ */
6
+ export async function getCurrentBranch(cwd) {
7
+ const { stdout } = await execa("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
8
+ cwd,
9
+ });
10
+ return stdout.trim();
11
+ }
12
+ /**
13
+ * Create a new branch and check it out.
14
+ */
15
+ export async function createBranch(cwd, name) {
16
+ await execa("git", ["checkout", "-b", name], { cwd });
17
+ }
18
+ /**
19
+ * Stage all changes and create a commit.
20
+ *
21
+ * @returns The short hash of the new commit.
22
+ */
23
+ export async function commitAll(cwd, message) {
24
+ await execa("git", ["add", "-A"], { cwd });
25
+ await execa("git", ["commit", "-m", message], { cwd });
26
+ const { stdout } = await execa("git", ["rev-parse", "--short", "HEAD"], {
27
+ cwd,
28
+ });
29
+ return stdout.trim();
30
+ }
31
+ /**
32
+ * Get a list of files changed since a given ref (defaults to HEAD).
33
+ *
34
+ * Returns relative paths as reported by git.
35
+ */
36
+ export async function getChangedFiles(cwd, since) {
37
+ const ref = since ?? "HEAD";
38
+ const { stdout } = await execa("git", ["diff", "--name-only", ref], { cwd, reject: false });
39
+ return stdout
40
+ .split("\n")
41
+ .map((l) => l.trim())
42
+ .filter(Boolean);
43
+ }
44
+ /**
45
+ * Get the unified diff since a given ref (defaults to HEAD).
46
+ */
47
+ export async function getDiff(cwd, since) {
48
+ const ref = since ?? "HEAD";
49
+ const { stdout } = await execa("git", ["diff", ref], {
50
+ cwd,
51
+ reject: false,
52
+ });
53
+ return stdout;
54
+ }
55
+ /**
56
+ * Check whether the working tree has uncommitted changes (staged or unstaged).
57
+ */
58
+ export async function hasUncommittedChanges(cwd) {
59
+ const { stdout } = await execa("git", ["status", "--porcelain"], {
60
+ cwd,
61
+ reject: false,
62
+ });
63
+ return stdout.trim().length > 0;
64
+ }
65
+ /**
66
+ * Stash any current changes, run the provided function, then restore.
67
+ *
68
+ * If the stash is empty (nothing to save) the restore step is skipped.
69
+ */
70
+ export async function stashAndRestore(cwd, fn) {
71
+ const dirty = await hasUncommittedChanges(cwd);
72
+ if (dirty) {
73
+ await execa("git", ["stash", "push", "-m", "bober-auto-stash"], { cwd });
74
+ }
75
+ try {
76
+ return await fn();
77
+ }
78
+ finally {
79
+ if (dirty) {
80
+ await execa("git", ["stash", "pop"], { cwd, reject: false });
81
+ }
82
+ }
83
+ }
84
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,sEAAsE;AAEtE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE;QAC3E,GAAG;KACJ,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,IAAY;IAC1D,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,OAAe;IAEf,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAEvD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;QACtE,GAAG;KACJ,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,KAAc;IAEd,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAC5B,KAAK,EACL,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAC5B,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CACvB,CAAC;IACF,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,KAAc;IAEd,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;QACnD,GAAG;QACH,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAW;IACrD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE;QAC/D,GAAG;QACH,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,EAAoB;IAEpB,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAE/C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Logger, logger, type LogLevel } from "./logger.js";
2
+ export { getCurrentBranch, createBranch, commitAll, getChangedFiles, getDiff, stashAndRestore, hasUncommittedChanges, } from "./git.js";
3
+ export { fileExists, readJson, writeJson, ensureDir, findProjectRoot, } from "./fs.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,OAAO,EACP,eAAe,EACf,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,SAAS,EACT,eAAe,GAChB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Logger, logger } from "./logger.js";
2
+ export { getCurrentBranch, createBranch, commitAll, getChangedFiles, getDiff, stashAndRestore, hasUncommittedChanges, } from "./git.js";
3
+ export { fileExists, readJson, writeJson, ensureDir, findProjectRoot, } from "./fs.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,OAAO,EACP,eAAe,EACf,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,SAAS,EACT,eAAe,GAChB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,45 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export declare class Logger {
3
+ verbose: boolean;
4
+ /** Informational message (cyan prefix). */
5
+ info(message: string, ...args: unknown[]): void;
6
+ /** Success message (green prefix + checkmark). */
7
+ success(message: string, ...args: unknown[]): void;
8
+ /** Warning message (yellow prefix). */
9
+ warn(message: string, ...args: unknown[]): void;
10
+ /** Error message (red prefix). */
11
+ error(message: string, ...args: unknown[]): void;
12
+ /** Debug message — only shown when verbose mode is enabled. */
13
+ debug(message: string, ...args: unknown[]): void;
14
+ /**
15
+ * Print a prominent phase transition header.
16
+ *
17
+ * Example output:
18
+ * ```
19
+ * ═══ PLANNING PHASE ═══
20
+ * ```
21
+ */
22
+ phase(name: string): void;
23
+ /**
24
+ * Print a sprint status update.
25
+ *
26
+ * @param id Sprint identifier.
27
+ * @param status Current status label.
28
+ */
29
+ sprint(id: string, status: string): void;
30
+ /**
31
+ * Print a simple text-based progress indicator.
32
+ *
33
+ * Example: `[████████░░░░░░░░] 4/10 Generating...`
34
+ *
35
+ * @param current Completed items.
36
+ * @param total Total items.
37
+ * @param label Optional description.
38
+ */
39
+ progress(current: number, total: number, label?: string): void;
40
+ }
41
+ /**
42
+ * Singleton logger instance used across the application.
43
+ */
44
+ export declare const logger: Logger;
45
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAI3D,qBAAa,MAAM;IACjB,OAAO,UAAS;IAEhB,2CAA2C;IAC3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,kDAAkD;IAClD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIlD,uCAAuC;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,kCAAkC;IAClC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,+DAA+D;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAKhD;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOzB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAKxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAS/D;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,QAAe,CAAC"}
@@ -0,0 +1,73 @@
1
+ import chalk from "chalk";
2
+ // ── Logger ─────────────────────────────────────────────────────────
3
+ export class Logger {
4
+ verbose = false;
5
+ /** Informational message (cyan prefix). */
6
+ info(message, ...args) {
7
+ console.log(chalk.cyan("info"), message, ...args);
8
+ }
9
+ /** Success message (green prefix + checkmark). */
10
+ success(message, ...args) {
11
+ console.log(chalk.green(" ✓"), message, ...args);
12
+ }
13
+ /** Warning message (yellow prefix). */
14
+ warn(message, ...args) {
15
+ console.warn(chalk.yellow("warn"), message, ...args);
16
+ }
17
+ /** Error message (red prefix). */
18
+ error(message, ...args) {
19
+ console.error(chalk.red("error"), message, ...args);
20
+ }
21
+ /** Debug message — only shown when verbose mode is enabled. */
22
+ debug(message, ...args) {
23
+ if (!this.verbose)
24
+ return;
25
+ console.log(chalk.gray("debug"), message, ...args);
26
+ }
27
+ /**
28
+ * Print a prominent phase transition header.
29
+ *
30
+ * Example output:
31
+ * ```
32
+ * ═══ PLANNING PHASE ═══
33
+ * ```
34
+ */
35
+ phase(name) {
36
+ const banner = `═══ ${name.toUpperCase()} ═══`;
37
+ console.log();
38
+ console.log(chalk.bold.magenta(banner));
39
+ console.log();
40
+ }
41
+ /**
42
+ * Print a sprint status update.
43
+ *
44
+ * @param id Sprint identifier.
45
+ * @param status Current status label.
46
+ */
47
+ sprint(id, status) {
48
+ const tag = chalk.bold.blue(`[${id}]`);
49
+ console.log(`${tag} ${status}`);
50
+ }
51
+ /**
52
+ * Print a simple text-based progress indicator.
53
+ *
54
+ * Example: `[████████░░░░░░░░] 4/10 Generating...`
55
+ *
56
+ * @param current Completed items.
57
+ * @param total Total items.
58
+ * @param label Optional description.
59
+ */
60
+ progress(current, total, label) {
61
+ const width = 20;
62
+ const filled = Math.round((current / Math.max(total, 1)) * width);
63
+ const empty = width - filled;
64
+ const bar = chalk.green("█".repeat(filled)) + chalk.gray("░".repeat(empty));
65
+ const suffix = label ? ` ${label}` : "";
66
+ console.log(` [${bar}] ${current}/${total}${suffix}`);
67
+ }
68
+ }
69
+ /**
70
+ * Singleton logger instance used across the application.
71
+ */
72
+ export const logger = new Logger();
73
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,sEAAsE;AAEtE,MAAM,OAAO,MAAM;IACjB,OAAO,GAAG,KAAK,CAAC;IAEhB,2CAA2C;IAC3C,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,kDAAkD;IAClD,OAAO,CAAC,OAAe,EAAE,GAAG,IAAe;QACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,uCAAuC;IACvC,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,kCAAkC;IAClC,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtD,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAY;QAChB,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;QAC/C,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,EAAU,EAAE,MAAc;QAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAe,EAAE,KAAa,EAAE,KAAc;QACrD,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;QAC7B,MAAM,GAAG,GACP,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,OAAO,IAAI,KAAK,GAAG,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "command": "echo 'Files modified \u2014 run /bober:eval to verify'"
7
+ }
8
+ ]
9
+ }
10
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "agent-bober",
3
+ "version": "0.1.0",
4
+ "description": "Generator-Evaluator multi-agent harness for building applications autonomously with Claude. Implements planner, sprint, and evaluator patterns.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "agent-bober": "dist/cli/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "lint": "eslint src/",
15
+ "typecheck": "tsc --noEmit",
16
+ "test": "vitest",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "claude",
21
+ "agent",
22
+ "harness",
23
+ "multi-agent",
24
+ "autonomous",
25
+ "generator-evaluator",
26
+ "claude-code",
27
+ "plugin"
28
+ ],
29
+ "author": "bober4ik",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/bober4ik/agent-bober"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "files": [
39
+ "dist/",
40
+ "skills/",
41
+ "agents/",
42
+ "hooks/",
43
+ "scripts/",
44
+ "templates/",
45
+ ".claude-plugin/"
46
+ ],
47
+ "dependencies": {
48
+ "@anthropic-ai/sdk": "^0.39.0",
49
+ "chalk": "^5.4.1",
50
+ "commander": "^13.1.0",
51
+ "execa": "^9.5.2",
52
+ "glob": "^11.0.1",
53
+ "ora": "^8.1.1",
54
+ "prompts": "^2.4.2",
55
+ "zod": "^3.24.2"
56
+ },
57
+ "devDependencies": {
58
+ "@eslint/js": "^10.0.1",
59
+ "@types/node": "^22.13.0",
60
+ "@types/prompts": "^2.4.9",
61
+ "@typescript-eslint/eslint-plugin": "^8.22.0",
62
+ "@typescript-eslint/parser": "^8.22.0",
63
+ "eslint": "^9.19.0",
64
+ "typescript": "^5.7.3",
65
+ "vitest": "^3.0.5"
66
+ }
67
+ }
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────────────
3
+ # detect-stack.sh — Auto-detect the technology stack of a project.
4
+ #
5
+ # Usage:
6
+ # bash scripts/detect-stack.sh [project-dir]
7
+ #
8
+ # If no directory is given, uses the current working directory.
9
+ # Outputs a JSON object describing the detected stack.
10
+ # ──────────────────────────────────────────────────────────────────────
11
+
12
+ set -euo pipefail
13
+
14
+ PROJECT_DIR="${1:-.}"
15
+ PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
16
+
17
+ # ── Helpers ─────────────────────────────────────────────────────────
18
+
19
+ # Check if a file exists in the project
20
+ has_file() {
21
+ [[ -f "$PROJECT_DIR/$1" ]]
22
+ }
23
+
24
+ # Check if package.json contains a dependency (dev or prod)
25
+ has_dep() {
26
+ if has_file "package.json"; then
27
+ grep -q "\"$1\"" "$PROJECT_DIR/package.json" 2>/dev/null
28
+ else
29
+ return 1
30
+ fi
31
+ }
32
+
33
+ # ── Language Detection ──────────────────────────────────────────────
34
+
35
+ LANGUAGES=()
36
+
37
+ if has_file "package.json"; then
38
+ LANGUAGES+=("javascript")
39
+ fi
40
+
41
+ if has_file "tsconfig.json" || has_dep "typescript"; then
42
+ LANGUAGES+=("typescript")
43
+ fi
44
+
45
+ if has_file "requirements.txt" || has_file "pyproject.toml" || has_file "Pipfile" || has_file "setup.py"; then
46
+ LANGUAGES+=("python")
47
+ fi
48
+
49
+ if has_file "go.mod"; then
50
+ LANGUAGES+=("go")
51
+ fi
52
+
53
+ if has_file "Cargo.toml"; then
54
+ LANGUAGES+=("rust")
55
+ fi
56
+
57
+ if has_file "pom.xml" || has_file "build.gradle" || has_file "build.gradle.kts"; then
58
+ LANGUAGES+=("java")
59
+ fi
60
+
61
+ if has_file "mix.exs"; then
62
+ LANGUAGES+=("elixir")
63
+ fi
64
+
65
+ if has_file "Gemfile"; then
66
+ LANGUAGES+=("ruby")
67
+ fi
68
+
69
+ # ── Framework Detection ─────────────────────────────────────────────
70
+
71
+ FRAMEWORKS=()
72
+
73
+ if has_dep "react"; then
74
+ FRAMEWORKS+=("react")
75
+ fi
76
+
77
+ if has_dep "vue"; then
78
+ FRAMEWORKS+=("vue")
79
+ fi
80
+
81
+ if has_dep "@angular/core"; then
82
+ FRAMEWORKS+=("angular")
83
+ fi
84
+
85
+ if has_dep "svelte"; then
86
+ FRAMEWORKS+=("svelte")
87
+ fi
88
+
89
+ if has_dep "next"; then
90
+ FRAMEWORKS+=("nextjs")
91
+ fi
92
+
93
+ if has_dep "nuxt"; then
94
+ FRAMEWORKS+=("nuxt")
95
+ fi
96
+
97
+ if has_dep "astro"; then
98
+ FRAMEWORKS+=("astro")
99
+ fi
100
+
101
+ if has_dep "remix" || has_dep "@remix-run/node"; then
102
+ FRAMEWORKS+=("remix")
103
+ fi
104
+
105
+ if has_dep "express"; then
106
+ FRAMEWORKS+=("express")
107
+ fi
108
+
109
+ if has_dep "fastify"; then
110
+ FRAMEWORKS+=("fastify")
111
+ fi
112
+
113
+ if has_dep "hono"; then
114
+ FRAMEWORKS+=("hono")
115
+ fi
116
+
117
+ if has_dep "koa"; then
118
+ FRAMEWORKS+=("koa")
119
+ fi
120
+
121
+ if has_dep "nestjs" || has_dep "@nestjs/core"; then
122
+ FRAMEWORKS+=("nestjs")
123
+ fi
124
+
125
+ # Python frameworks
126
+ if has_file "requirements.txt" || has_file "pyproject.toml"; then
127
+ COMBINED=""
128
+ [[ -f "$PROJECT_DIR/requirements.txt" ]] && COMBINED+="$(cat "$PROJECT_DIR/requirements.txt")"
129
+ [[ -f "$PROJECT_DIR/pyproject.toml" ]] && COMBINED+="$(cat "$PROJECT_DIR/pyproject.toml")"
130
+
131
+ echo "$COMBINED" | grep -qi "django" && FRAMEWORKS+=("django")
132
+ echo "$COMBINED" | grep -qi "flask" && FRAMEWORKS+=("flask")
133
+ echo "$COMBINED" | grep -qi "fastapi" && FRAMEWORKS+=("fastapi")
134
+ fi
135
+
136
+ # ── Test Framework Detection ────────────────────────────────────────
137
+
138
+ TEST_FRAMEWORKS=()
139
+
140
+ if has_dep "vitest"; then
141
+ TEST_FRAMEWORKS+=("vitest")
142
+ fi
143
+
144
+ if has_dep "jest"; then
145
+ TEST_FRAMEWORKS+=("jest")
146
+ fi
147
+
148
+ if has_dep "mocha"; then
149
+ TEST_FRAMEWORKS+=("mocha")
150
+ fi
151
+
152
+ if has_dep "@playwright/test"; then
153
+ TEST_FRAMEWORKS+=("playwright")
154
+ fi
155
+
156
+ if has_dep "cypress"; then
157
+ TEST_FRAMEWORKS+=("cypress")
158
+ fi
159
+
160
+ if has_dep "@testing-library/react" || has_dep "@testing-library/vue"; then
161
+ TEST_FRAMEWORKS+=("testing-library")
162
+ fi
163
+
164
+ # Python test frameworks
165
+ if has_file "requirements.txt" || has_file "pyproject.toml" || has_file "setup.cfg"; then
166
+ for f in requirements.txt pyproject.toml setup.cfg; do
167
+ if has_file "$f" && grep -qi "pytest" "$PROJECT_DIR/$f" 2>/dev/null; then
168
+ TEST_FRAMEWORKS+=("pytest")
169
+ break
170
+ fi
171
+ done
172
+ fi
173
+
174
+ # Go testing is built-in; check for testify
175
+ if has_file "go.mod" && grep -q "testify" "$PROJECT_DIR/go.mod" 2>/dev/null; then
176
+ TEST_FRAMEWORKS+=("testify")
177
+ fi
178
+
179
+ # ── Linter Detection ───────────────────────────────────────────────
180
+
181
+ LINTERS=()
182
+
183
+ if has_dep "eslint" || has_file ".eslintrc.json" || has_file ".eslintrc.js" || has_file "eslint.config.js" || has_file "eslint.config.mjs"; then
184
+ LINTERS+=("eslint")
185
+ fi
186
+
187
+ if has_dep "@biomejs/biome" || has_file "biome.json" || has_file "biome.jsonc"; then
188
+ LINTERS+=("biome")
189
+ fi
190
+
191
+ if has_dep "prettier" || has_file ".prettierrc" || has_file ".prettierrc.json" || has_file "prettier.config.js"; then
192
+ LINTERS+=("prettier")
193
+ fi
194
+
195
+ if has_file ".pylintrc" || has_file "pyproject.toml"; then
196
+ if has_file "pyproject.toml" && grep -qi "ruff" "$PROJECT_DIR/pyproject.toml" 2>/dev/null; then
197
+ LINTERS+=("ruff")
198
+ fi
199
+ if has_file ".pylintrc"; then
200
+ LINTERS+=("pylint")
201
+ fi
202
+ fi
203
+
204
+ if has_file ".golangci.yml" || has_file ".golangci.yaml"; then
205
+ LINTERS+=("golangci-lint")
206
+ fi
207
+
208
+ # ── Build Tool Detection ───────────────────────────────────────────
209
+
210
+ BUILD_TOOLS=()
211
+
212
+ if has_dep "vite" || has_file "vite.config.ts" || has_file "vite.config.js"; then
213
+ BUILD_TOOLS+=("vite")
214
+ fi
215
+
216
+ if has_dep "webpack" || has_file "webpack.config.js" || has_file "webpack.config.ts"; then
217
+ BUILD_TOOLS+=("webpack")
218
+ fi
219
+
220
+ if has_dep "esbuild"; then
221
+ BUILD_TOOLS+=("esbuild")
222
+ fi
223
+
224
+ if has_dep "turbo" || has_file "turbo.json"; then
225
+ BUILD_TOOLS+=("turborepo")
226
+ fi
227
+
228
+ if has_dep "rollup" || has_file "rollup.config.js" || has_file "rollup.config.mjs"; then
229
+ BUILD_TOOLS+=("rollup")
230
+ fi
231
+
232
+ if has_dep "tsup"; then
233
+ BUILD_TOOLS+=("tsup")
234
+ fi
235
+
236
+ if has_file "Makefile"; then
237
+ BUILD_TOOLS+=("make")
238
+ fi
239
+
240
+ if has_file "Dockerfile" || has_file "docker-compose.yml" || has_file "docker-compose.yaml"; then
241
+ BUILD_TOOLS+=("docker")
242
+ fi
243
+
244
+ # ── Package Manager Detection ──────────────────────────────────────
245
+
246
+ PACKAGE_MANAGER="unknown"
247
+
248
+ if has_file "pnpm-lock.yaml"; then
249
+ PACKAGE_MANAGER="pnpm"
250
+ elif has_file "yarn.lock"; then
251
+ PACKAGE_MANAGER="yarn"
252
+ elif has_file "bun.lockb" || has_file "bun.lock"; then
253
+ PACKAGE_MANAGER="bun"
254
+ elif has_file "package-lock.json"; then
255
+ PACKAGE_MANAGER="npm"
256
+ elif has_file "package.json"; then
257
+ PACKAGE_MANAGER="npm"
258
+ fi
259
+
260
+ # ── Output JSON ─────────────────────────────────────────────────────
261
+
262
+ json_array() {
263
+ local arr=("$@")
264
+ if [[ ${#arr[@]} -eq 0 ]]; then
265
+ echo "[]"
266
+ return
267
+ fi
268
+ local result="["
269
+ for i in "${!arr[@]}"; do
270
+ [[ $i -gt 0 ]] && result+=","
271
+ result+="\"${arr[$i]}\""
272
+ done
273
+ result+="]"
274
+ echo "$result"
275
+ }
276
+
277
+ cat <<EOF
278
+ {
279
+ "projectDir": "$PROJECT_DIR",
280
+ "languages": $(json_array "${LANGUAGES[@]+"${LANGUAGES[@]}"}"),
281
+ "frameworks": $(json_array "${FRAMEWORKS[@]+"${FRAMEWORKS[@]}"}"),
282
+ "testFrameworks": $(json_array "${TEST_FRAMEWORKS[@]+"${TEST_FRAMEWORKS[@]}"}"),
283
+ "linters": $(json_array "${LINTERS[@]+"${LINTERS[@]}"}"),
284
+ "buildTools": $(json_array "${BUILD_TOOLS[@]+"${BUILD_TOOLS[@]}"}"),
285
+ "packageManager": "$PACKAGE_MANAGER"
286
+ }
287
+ EOF