@agjs/tsforge 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 (216) hide show
  1. package/bin/tsforge.js +2 -0
  2. package/package.json +35 -0
  3. package/src/agent/agent.constants.ts +382 -0
  4. package/src/agent/agent.types.ts +34 -0
  5. package/src/agent/index.ts +4 -0
  6. package/src/agent/model-agent.ts +297 -0
  7. package/src/agent/tool-repair.ts +194 -0
  8. package/src/agent/tools.ts +190 -0
  9. package/src/browser/checks.ts +96 -0
  10. package/src/browser/index.ts +8 -0
  11. package/src/browser/oracle.ts +303 -0
  12. package/src/classify.ts +48 -0
  13. package/src/cli.ts +1333 -0
  14. package/src/config/config.constants.ts +9 -0
  15. package/src/config/flags.ts +32 -0
  16. package/src/config/index.ts +8 -0
  17. package/src/config/tsforge-config.ts +301 -0
  18. package/src/constitution/baseline.ts +257 -0
  19. package/src/detect-gate.ts +498 -0
  20. package/src/eval/eval.types.ts +36 -0
  21. package/src/eval/index.ts +3 -0
  22. package/src/eval/judge.ts +62 -0
  23. package/src/eval/score.ts +39 -0
  24. package/src/files/create.ts +22 -0
  25. package/src/files/edit.ts +193 -0
  26. package/src/files/files.constants.ts +11 -0
  27. package/src/files/files.types.ts +81 -0
  28. package/src/files/hashline-format.ts +110 -0
  29. package/src/files/hashline.ts +689 -0
  30. package/src/files/index.ts +19 -0
  31. package/src/index.ts +8 -0
  32. package/src/inference/index.ts +6 -0
  33. package/src/inference/inference.constants.ts +34 -0
  34. package/src/inference/inference.types.ts +123 -0
  35. package/src/inference/openai-compatible.ts +113 -0
  36. package/src/inference/stream-guard.ts +161 -0
  37. package/src/inference/stream.ts +370 -0
  38. package/src/inference/transport.ts +78 -0
  39. package/src/inference/wire.ts +0 -0
  40. package/src/lib/fs/fs.ts +126 -0
  41. package/src/lib/fs/fs.types.ts +5 -0
  42. package/src/lib/fs/index.ts +3 -0
  43. package/src/lib/fs/process.ts +146 -0
  44. package/src/lib/guards/guards.ts +9 -0
  45. package/src/lib/guards/index.ts +1 -0
  46. package/src/lib/json/index.ts +1 -0
  47. package/src/lib/json/json.ts +12 -0
  48. package/src/lib/scope/index.ts +2 -0
  49. package/src/lib/scope/scope.constants.ts +3 -0
  50. package/src/lib/scope/scope.ts +40 -0
  51. package/src/loop/astgrep-fix.ts +228 -0
  52. package/src/loop/feedback/feedback.ts +138 -0
  53. package/src/loop/feedback/index.ts +8 -0
  54. package/src/loop/feedback/meta-rule-docs.ts +41 -0
  55. package/src/loop/feedback/meta-rule-feedback.ts +61 -0
  56. package/src/loop/feedback/rule-docs.generated.json +112 -0
  57. package/src/loop/feedback/rule-docs.ts +342 -0
  58. package/src/loop/index.ts +19 -0
  59. package/src/loop/loop.constants.ts +68 -0
  60. package/src/loop/loop.types.ts +99 -0
  61. package/src/loop/prompt/index.ts +2 -0
  62. package/src/loop/prompt/project-map.ts +69 -0
  63. package/src/loop/prompt/prompt.ts +107 -0
  64. package/src/loop/quality.ts +174 -0
  65. package/src/loop/rule-docs.generated.json +367 -0
  66. package/src/loop/run-spec.ts +88 -0
  67. package/src/loop/run.ts +400 -0
  68. package/src/loop/session.ts +1410 -0
  69. package/src/loop/tools/add-dependency.ts +71 -0
  70. package/src/loop/tools/condense.ts +498 -0
  71. package/src/loop/tools/edit-hashline.ts +80 -0
  72. package/src/loop/tools/execute-tool.ts +80 -0
  73. package/src/loop/tools/file-ops.ts +323 -0
  74. package/src/loop/tools/index.ts +2 -0
  75. package/src/loop/tools/lsp-ops.ts +222 -0
  76. package/src/loop/tools/scaffold-routes.ts +68 -0
  77. package/src/loop/tools/scaffold-ui.ts +62 -0
  78. package/src/loop/tools/scaffold-web.ts +35 -0
  79. package/src/loop/tools/tool-context.ts +126 -0
  80. package/src/loop/ttsr-defaults.ts +53 -0
  81. package/src/loop/ttsr.ts +322 -0
  82. package/src/loop/turn.ts +856 -0
  83. package/src/lsp/index.ts +2 -0
  84. package/src/lsp/lsp.types.ts +56 -0
  85. package/src/lsp/service.ts +500 -0
  86. package/src/meta-rules/context.ts +195 -0
  87. package/src/meta-rules/index.ts +9 -0
  88. package/src/meta-rules/meta-rules.types.ts +47 -0
  89. package/src/meta-rules/parsers/package-json-parser.ts +51 -0
  90. package/src/meta-rules/registry.ts +37 -0
  91. package/src/meta-rules/rules/ci/workflow-actions-pinned.ts +59 -0
  92. package/src/meta-rules/rules/ci/workflow-runner-pinned.ts +57 -0
  93. package/src/meta-rules/rules/ci/workflow-timeout-required.ts +114 -0
  94. package/src/meta-rules/rules/config/tsconfig-paths-exist.ts +117 -0
  95. package/src/meta-rules/rules/config/tsconfig-strict.ts +91 -0
  96. package/src/meta-rules/rules/source-text/no-eslint-disable-comments.ts +34 -0
  97. package/src/meta-rules/rules/source-text/no-ts-suppressions.ts +38 -0
  98. package/src/meta-rules/rules/supply-chain/no-overlapping-libs.ts +57 -0
  99. package/src/meta-rules/rules/supply-chain/package-exact-deps.ts +55 -0
  100. package/src/meta-rules/rules/testing/test-sibling-required.ts +110 -0
  101. package/src/meta-rules/runner.ts +64 -0
  102. package/src/models-config.ts +196 -0
  103. package/src/render/ansi.ts +289 -0
  104. package/src/render/banner.ts +113 -0
  105. package/src/render/box.ts +134 -0
  106. package/src/render/index.ts +7 -0
  107. package/src/render/markdown.ts +123 -0
  108. package/src/render/render.types.ts +21 -0
  109. package/src/render/stream-markdown.ts +128 -0
  110. package/src/render/style.ts +26 -0
  111. package/src/rule-packs/bullmq/index.ts +39 -0
  112. package/src/rule-packs/bullmq/rules/index.ts +7 -0
  113. package/src/rule-packs/bullmq/rules/job-name-must-be-constant.ts +141 -0
  114. package/src/rule-packs/bullmq/rules/job-options-must-set-attempts.ts +174 -0
  115. package/src/rule-packs/bullmq/rules/no-blocking-concurrency-zero.ts +103 -0
  116. package/src/rule-packs/bullmq/rules/queue-options-must-set-removeoncomplete.ts +130 -0
  117. package/src/rule-packs/bullmq/rules/queue-options-must-set-removeonfail.ts +130 -0
  118. package/src/rule-packs/bullmq/rules/worker-must-implement-close.ts +182 -0
  119. package/src/rule-packs/bullmq/rules/worker-must-listen-failed.ts +140 -0
  120. package/src/rule-packs/bullmq/utils.ts +334 -0
  121. package/src/rule-packs/code-flow/index.ts +25 -0
  122. package/src/rule-packs/code-flow/rules/index.ts +3 -0
  123. package/src/rule-packs/code-flow/rules/no-bare-date-now.ts +138 -0
  124. package/src/rule-packs/code-flow/rules/no-template-trim-empty-ternary.ts +87 -0
  125. package/src/rule-packs/code-flow/rules/prefer-early-return.ts +80 -0
  126. package/src/rule-packs/code-flow/utils/prefer-early-return.ts +132 -0
  127. package/src/rule-packs/comment-hygiene/index.ts +25 -0
  128. package/src/rule-packs/comment-hygiene/rules/index.ts +3 -0
  129. package/src/rule-packs/comment-hygiene/rules/no-historical-comments.ts +102 -0
  130. package/src/rule-packs/comment-hygiene/rules/no-narration-comments.ts +83 -0
  131. package/src/rule-packs/comment-hygiene/rules/no-pr-reference-comments.ts +90 -0
  132. package/src/rule-packs/create-rule.ts +9 -0
  133. package/src/rule-packs/drizzle/index.ts +41 -0
  134. package/src/rule-packs/drizzle/rules/account-scoped-tables-require-where.ts +371 -0
  135. package/src/rule-packs/drizzle/rules/index.ts +8 -0
  136. package/src/rule-packs/drizzle/rules/no-nested-db-transaction.ts +127 -0
  137. package/src/rule-packs/drizzle/rules/no-raw-sql-outside-allowlist.ts +100 -0
  138. package/src/rule-packs/drizzle/rules/relations-must-cover-fks.ts +209 -0
  139. package/src/rule-packs/drizzle/rules/schema-files-must-not-import-driver.ts +127 -0
  140. package/src/rule-packs/drizzle/rules/schema-files-must-only-export-schema.ts +149 -0
  141. package/src/rule-packs/drizzle/rules/tables-must-have-timestamps.ts +312 -0
  142. package/src/rule-packs/drizzle/rules/timestamp-must-specify-mode.ts +166 -0
  143. package/src/rule-packs/drizzle/utils.ts +115 -0
  144. package/src/rule-packs/elysia/index.ts +43 -0
  145. package/src/rule-packs/elysia/rules/consistent-status-via-set.ts +69 -0
  146. package/src/rule-packs/elysia/rules/no-decorate-state-collision.ts +276 -0
  147. package/src/rule-packs/elysia/rules/no-separate-model-interfaces.ts +144 -0
  148. package/src/rule-packs/elysia/rules/prefer-destructured-context.ts +155 -0
  149. package/src/rule-packs/elysia/rules/prefer-direct-return.ts +176 -0
  150. package/src/rule-packs/elysia/rules/prefer-static-services.ts +159 -0
  151. package/src/rule-packs/elysia/rules/prefer-throw-status.ts +151 -0
  152. package/src/rule-packs/elysia/rules/require-hooks-before-routes.ts +209 -0
  153. package/src/rule-packs/elysia/rules/require-plugin-name.ts +107 -0
  154. package/src/rule-packs/elysia/utils/elysiaChain.ts +306 -0
  155. package/src/rule-packs/env-access/index.ts +23 -0
  156. package/src/rule-packs/env-access/rules/index.ts +2 -0
  157. package/src/rule-packs/env-access/rules/no-direct-process-env.ts +133 -0
  158. package/src/rule-packs/env-access/rules/no-process-exit.ts +95 -0
  159. package/src/rule-packs/i18n-keys/index.ts +19 -0
  160. package/src/rule-packs/i18n-keys/rules/static-translation-key-exists.ts +173 -0
  161. package/src/rule-packs/index.ts +139 -0
  162. package/src/rule-packs/jwt-cookies/index.ts +25 -0
  163. package/src/rule-packs/jwt-cookies/rules/auth-cookie-must-be-httponly.ts +150 -0
  164. package/src/rule-packs/jwt-cookies/rules/auth-cookie-must-be-secure-in-prod.ts +149 -0
  165. package/src/rule-packs/jwt-cookies/rules/bcrypt-rounds-min.ts +195 -0
  166. package/src/rule-packs/jwt-cookies/utils.ts +188 -0
  167. package/src/rule-packs/oauth-security/index.ts +25 -0
  168. package/src/rule-packs/oauth-security/rules/pkce-required-for-oidc.ts +296 -0
  169. package/src/rule-packs/oauth-security/rules/state-must-be-redis-backed.ts +193 -0
  170. package/src/rule-packs/oauth-security/rules/state-ttl-bounded.ts +219 -0
  171. package/src/rule-packs/oauth-security/utils.ts +127 -0
  172. package/src/rule-packs/react-component-architecture/index.ts +35 -0
  173. package/src/rule-packs/react-component-architecture/rules/component-folder-structure.ts +123 -0
  174. package/src/rule-packs/react-component-architecture/rules/forwardref-display-name.ts +93 -0
  175. package/src/rule-packs/react-component-architecture/rules/index-must-reexport-default.ts +123 -0
  176. package/src/rule-packs/react-component-architecture/rules/max-hooks-per-file.ts +122 -0
  177. package/src/rule-packs/react-component-architecture/rules/no-cross-feature-imports.ts +170 -0
  178. package/src/rule-packs/react-component-architecture/rules/no-inline-jsx-functions.ts +66 -0
  179. package/src/rule-packs/react-component-architecture/utils.ts +47 -0
  180. package/src/rule-packs/rule-packs.types.ts +18 -0
  181. package/src/rule-packs/structured-logging/index.ts +26 -0
  182. package/src/rule-packs/structured-logging/rules/mask-pii-fields.ts +221 -0
  183. package/src/rule-packs/structured-logging/rules/no-error-stringify.ts +217 -0
  184. package/src/rule-packs/structured-logging/rules/require-event-field.ts +136 -0
  185. package/src/rule-packs/structured-logging/utils/logger.ts +104 -0
  186. package/src/rule-packs/tanstack-query/index.ts +20 -0
  187. package/src/rule-packs/tanstack-query/rules/prefix-query-key-must-use-set-queries-data.ts +321 -0
  188. package/src/rule-packs/test-conventions/index.ts +23 -0
  189. package/src/rule-packs/test-conventions/rules/index.ts +2 -0
  190. package/src/rule-packs/test-conventions/rules/no-focused-tests.ts +170 -0
  191. package/src/rule-packs/test-conventions/rules/test-file-mirrors-source.ts +127 -0
  192. package/src/rule-packs/utils.ts +142 -0
  193. package/src/session-store.ts +359 -0
  194. package/src/spec/generate-tests.ts +213 -0
  195. package/src/spec/index.ts +5 -0
  196. package/src/spec/parse.ts +152 -0
  197. package/src/spec/review-tests.ts +162 -0
  198. package/src/spec/spec.constants.ts +13 -0
  199. package/src/spec/spec.types.ts +79 -0
  200. package/src/stack-detection/detect.ts +246 -0
  201. package/src/stack-detection/index.ts +3 -0
  202. package/src/stack-detection/packs.ts +174 -0
  203. package/src/stack-detection/stack-detection.types.ts +47 -0
  204. package/src/validate/accept.ts +49 -0
  205. package/src/validate/errors.ts +35 -0
  206. package/src/validate/index.ts +12 -0
  207. package/src/validate/parse.ts +148 -0
  208. package/src/validate/run-tests.ts +59 -0
  209. package/src/validate/validate.ts +40 -0
  210. package/src/validate/validate.types.ts +52 -0
  211. package/src/web-components.ts +638 -0
  212. package/src/web-coverage.ts +89 -0
  213. package/src/web-routes.ts +151 -0
  214. package/src/web-templates.ts +1011 -0
  215. package/strict.eslint.config.mjs +84 -0
  216. package/strict.web.eslint.config.mjs +185 -0
package/bin/tsforge.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import "../src/cli.ts";
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@agjs/tsforge",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "license": "MIT",
6
+ "description": "TypeScript coding harness with a deterministic gate, stack-aware guardrails, and stream-level correction.",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/agjs/tsforge.git",
10
+ "directory": "packages/core"
11
+ },
12
+ "homepage": "https://tsforge.dev",
13
+ "bugs": "https://github.com/agjs/tsforge/issues",
14
+ "bin": {
15
+ "tsforge": "./bin/tsforge.js"
16
+ },
17
+ "files": [
18
+ "bin",
19
+ "src",
20
+ "strict.eslint.config.mjs",
21
+ "strict.web.eslint.config.mjs"
22
+ ],
23
+ "engines": {
24
+ "bun": ">=1.3.14"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "test": "bun test"
31
+ },
32
+ "dependencies": {
33
+ "cli-highlight": "2.1.11"
34
+ }
35
+ }
@@ -0,0 +1,382 @@
1
+ /**
2
+ * The canonical tool names. Schemas, dispatch, and any name comparison reference
3
+ * these — never a bare string literal (so a rename is one edit and typos can't
4
+ * silently miss). The 4 base tools are always offered; the rest are the LSP nav
5
+ * set, gated to existing-code runs (see run.ts toolsFor).
6
+ */
7
+ export const TOOL_NAME = {
8
+ read: "read",
9
+ run: "run",
10
+ edit: "edit",
11
+ editLines: "edit_lines",
12
+ create: "create",
13
+ search: "search",
14
+ symbolSearch: "symbol_search",
15
+ findReferences: "find_references",
16
+ typeAt: "type_at",
17
+ diagnostics: "diagnostics",
18
+ renameSymbol: "rename_symbol",
19
+ organizeImports: "organize_imports",
20
+ scaffoldUi: "scaffold_ui",
21
+ scaffoldRoutes: "scaffold_routes",
22
+ scaffoldWeb: "scaffold_web",
23
+ addDependency: "add_dependency",
24
+ yieldStatus: "yield_status",
25
+ } as const;
26
+
27
+ /** Tools that cannot mutate the workspace — the PLAN-MODE set. `run` is absent
28
+ * on purpose: it is special-cased (allowed only for read-only commands — see
29
+ * isReadOnlyCommand in loop/tools/file-ops). */
30
+ export const READ_ONLY_TOOL_NAMES: ReadonlySet<string> = new Set([
31
+ TOOL_NAME.read,
32
+ TOOL_NAME.search,
33
+ TOOL_NAME.symbolSearch,
34
+ TOOL_NAME.findReferences,
35
+ TOOL_NAME.typeAt,
36
+ TOOL_NAME.diagnostics,
37
+ ]);
38
+
39
+ /** The model's own decision to start a from-scratch WEB app: scaffolds the stack
40
+ * (Vite + the chosen framework + deps) and switches the session to the web gate.
41
+ * Offered on a fresh interactive session so the AGENT decides whether to scaffold
42
+ * — NOT a brittle classifier. Call it ONLY for "build a web app/UI"; for a
43
+ * question, a CLI script, or editing code, just do the work directly. */
44
+ export const SCAFFOLD_WEB_TOOL = {
45
+ type: "function",
46
+ function: {
47
+ name: TOOL_NAME.scaffoldWeb,
48
+ description:
49
+ "Start a NEW web application from scratch: scaffolds a Vite project (React full kit — shadcn/ui + TanStack Router + Query — or vanilla TS) with dependencies installed, and switches the build to the web gate (tsc + eslint + vite build + browser render). Call this ONCE, FIRST, ONLY when the user wants you to BUILD a browser app or UI. Do NOT call it for: answering a question, writing a CLI/Node script, printing output (e.g. 'render a table in the CLI'), or editing an existing project — just do those directly. After it returns, write your type contract, then implement the routes/features.",
50
+ parameters: {
51
+ type: "object",
52
+ properties: {
53
+ framework: {
54
+ type: "string",
55
+ enum: ["react", "vanilla"],
56
+ description:
57
+ "react = full kit (shadcn/ui + TanStack Router + Query); vanilla = Vite + TypeScript + Tailwind. Default react.",
58
+ },
59
+ },
60
+ required: ["framework"],
61
+ },
62
+ },
63
+ };
64
+
65
+ /** The two file-mutation tools the model is always offered. */
66
+ export const EDIT_TOOL = {
67
+ type: "function",
68
+ function: {
69
+ name: TOOL_NAME.edit,
70
+ description: "Replace an exact, unique snippet in an existing file.",
71
+ parameters: {
72
+ type: "object",
73
+ properties: {
74
+ file: { type: "string" },
75
+ oldString: { type: "string" },
76
+ newString: { type: "string" },
77
+ },
78
+ required: ["file", "oldString", "newString"],
79
+ },
80
+ },
81
+ };
82
+
83
+ export const CREATE_TOOL = {
84
+ type: "function",
85
+ function: {
86
+ name: TOOL_NAME.create,
87
+ description: "Create a new file with the given content.",
88
+ parameters: {
89
+ type: "object",
90
+ properties: {
91
+ file: { type: "string" },
92
+ content: { type: "string" },
93
+ },
94
+ required: ["file", "content"],
95
+ },
96
+ },
97
+ };
98
+
99
+ export const EDIT_LINES_TOOL = {
100
+ type: "function",
101
+ function: {
102
+ name: TOOL_NAME.editLines,
103
+ description:
104
+ "Edit file lines by content hash (stale-anchor recovery). Copy the hash from the read output (¶path#HASH), then use: ¶path#HASH / replace N..M: +line1 +line2 / delete N..M / insert before|after N: +line.",
105
+ parameters: {
106
+ type: "object",
107
+ properties: {
108
+ file: { type: "string" },
109
+ hash: {
110
+ type: "string",
111
+ description: "optional: file content hash (¶path#HASH)",
112
+ },
113
+ input: { type: "string", description: "hashline edit operations" },
114
+ },
115
+ required: ["file", "input"],
116
+ },
117
+ },
118
+ };
119
+
120
+ /** Materialize tested, THEMED UI primitives (button/card/input/…) into
121
+ * src/components/ui/ so you never hand-write a base component. Web builds only. */
122
+ export const SCAFFOLD_UI_TOOL = {
123
+ type: "function",
124
+ function: {
125
+ name: TOOL_NAME.scaffoldUi,
126
+ description:
127
+ "Generate tested, accessible UI building blocks into src/components/ui/, styled to a chosen vibe — so you NEVER hand-write base components or view chrome. Two tiers: PRIMITIVES (button, card, input, label, textarea, select, badge, separator, table) and COMPOSITION BLOCKS (app-shell = sidebar+nav layout, page-header, field = label+control+error, form-actions, toolbar, empty-state). Call this ONCE near the start with everything the app needs, then import and COMPOSE: e.g. build a list view from page-header + toolbar + table, a form from field + form-actions, the layout from app-shell. NEVER hand-roll these; it wastes time and breaks theme coherence. Also writes the matching design tokens into src/index.css.",
128
+ parameters: {
129
+ type: "object",
130
+ properties: {
131
+ theme: {
132
+ type: "string",
133
+ enum: ["minimal", "warm", "futuristic"],
134
+ description: "The visual vibe, derived from the user's request.",
135
+ },
136
+ components: {
137
+ type: "array",
138
+ items: {
139
+ type: "string",
140
+ enum: [
141
+ "button",
142
+ "card",
143
+ "input",
144
+ "label",
145
+ "textarea",
146
+ "select",
147
+ "badge",
148
+ "separator",
149
+ "table",
150
+ "app-shell",
151
+ "page-header",
152
+ "field",
153
+ "form-actions",
154
+ "toolbar",
155
+ "empty-state",
156
+ ],
157
+ },
158
+ description: "Which building blocks to generate.",
159
+ },
160
+ },
161
+ required: ["theme", "components"],
162
+ },
163
+ },
164
+ };
165
+
166
+ /** Materialize ALL of an app's routes as working file-based stubs in one call,
167
+ * from a model-declared list of paths — so the route union is complete up front
168
+ * and no `<Link>` can forward-reference a missing route. Web builds only. */
169
+ export const SCAFFOLD_ROUTES_TOOL = {
170
+ type: "function",
171
+ function: {
172
+ name: TOOL_NAME.scaffoldRoutes,
173
+ description:
174
+ "Create ALL the app's pages as TanStack file-based route STUBS in one call — you give the list of route paths your app needs (from the user's request); the harness writes each src/routes/*.tsx with a correct createFileRoute() and a placeholder component, and regenerates the route tree. Call this ONCE, right after your types + data services, with EVERY page the app needs — list pages, detail pages (use $param, e.g. /accounts/$accountId), and create/edit pages (e.g. /deals/create). After this, every route exists, the app navigates, and every <Link to>/navigate target type-checks — so you can build components in any order without 'not assignable to route' errors. Then FILL each route's component (replace the placeholder) one feature at a time. Do NOT hand-write route files or call this per-route.",
175
+ parameters: {
176
+ type: "object",
177
+ properties: {
178
+ routes: {
179
+ type: "array",
180
+ items: { type: "string" },
181
+ description:
182
+ 'Every page path the app needs, e.g. ["/", "/accounts", "/accounts/$accountId", "/accounts/create", "/settings/profile"].',
183
+ },
184
+ },
185
+ required: ["routes"],
186
+ },
187
+ },
188
+ };
189
+
190
+ export const RUN_TOOL = {
191
+ type: "function",
192
+ function: {
193
+ name: TOOL_NAME.run,
194
+ description:
195
+ "Run a shell command in the working directory and get its stdout/stderr/exit code. Use this to run the acceptance command, `tsc`, `eslint`, or `bun test` and see the real result — don't guess whether your code passes.",
196
+ parameters: {
197
+ type: "object",
198
+ properties: { command: { type: "string" } },
199
+ required: ["command"],
200
+ },
201
+ },
202
+ };
203
+
204
+ export const READ_TOOL = {
205
+ type: "function",
206
+ function: {
207
+ name: TOOL_NAME.read,
208
+ description: "Read a file's current contents from the working directory.",
209
+ parameters: {
210
+ type: "object",
211
+ properties: { file: { type: "string" } },
212
+ required: ["file"],
213
+ },
214
+ },
215
+ };
216
+
217
+ /**
218
+ * Semantic + search tools backed by the in-process TypeScript LanguageService
219
+ * (+ ripgrep). Read-only tools (find_references, type_at, symbol_search,
220
+ * diagnostics) are unrestricted; the writers (rename_symbol, organize_imports)
221
+ * are scope-enforced in dispatch.
222
+ */
223
+ /** The STOP tool for forced-tools mode (TSFORGE_FORCE_TOOLS): with tool_choice
224
+ * "required" the model can never end a turn in prose, so this is how it stops —
225
+ * every turn is grammar-constrained and the malformed-call class is impossible.
226
+ * The session converts a yield_status call back into a normal "model stopped"
227
+ * turn (summary becomes the reply; the gate confirms as usual). */
228
+ export const YIELD_STATUS_TOOL = {
229
+ type: "function",
230
+ function: {
231
+ name: TOOL_NAME.yieldStatus,
232
+ description:
233
+ "Call this when you are DONE working on the request (or have a final answer/question for the user) — it ends your turn. Put your reply in `summary`. Do not call it together with other tools; finish the work first.",
234
+ parameters: {
235
+ type: "object",
236
+ properties: {
237
+ summary: {
238
+ type: "string",
239
+ description:
240
+ "your reply to the user: what you did, or your answer/question",
241
+ },
242
+ },
243
+ required: ["summary"],
244
+ },
245
+ },
246
+ };
247
+
248
+ /** Install npm packages with bun — the measured next frontier blocker (builds
249
+ * dead-ended whenever a feature needed a dep the scaffold didn't ship). Names
250
+ * are validated handler-side (no flags/shell metacharacters reach the shell). */
251
+ export const ADD_DEPENDENCY_TOOL = {
252
+ type: "function",
253
+ function: {
254
+ name: TOOL_NAME.addDependency,
255
+ description:
256
+ "Install one or more npm packages into this project (bun add). Use it when a feature genuinely needs a library the project doesn't have — check package.json first. Plain package names only (e.g. 'date-fns' or 'zod@3'), no flags.",
257
+ parameters: {
258
+ type: "object",
259
+ properties: {
260
+ packages: {
261
+ type: "string",
262
+ description:
263
+ "space-separated package names, each optionally @versioned, e.g. 'date-fns zod@3'",
264
+ },
265
+ dev: {
266
+ type: "boolean",
267
+ description: "install as devDependency (default false)",
268
+ },
269
+ },
270
+ required: ["packages"],
271
+ },
272
+ },
273
+ };
274
+
275
+ /** Ripgrep over the workspace — read-only, deps-free, and useful WITHOUT a
276
+ * tsconfig, so it is also offered standalone in interactive sessions (the
277
+ * plan-mode explorer's main tool besides `read`). */
278
+ export const SEARCH_TOOL = {
279
+ type: "function",
280
+ function: {
281
+ name: TOOL_NAME.search,
282
+ description:
283
+ "ripgrep the working directory for a pattern — your primary way to FIND code without knowing file paths. Returns file:line matches.",
284
+ parameters: {
285
+ type: "object",
286
+ properties: {
287
+ pattern: { type: "string" },
288
+ glob: {
289
+ type: "string",
290
+ description: "optional path glob to scope the search",
291
+ },
292
+ },
293
+ required: ["pattern"],
294
+ },
295
+ },
296
+ };
297
+
298
+ export const LSP_TOOLS = [
299
+ SEARCH_TOOL,
300
+ {
301
+ type: "function",
302
+ function: {
303
+ name: TOOL_NAME.symbolSearch,
304
+ description:
305
+ "Find where a symbol (type/function/const) is declared across the project, by name. Returns kind, name, file:line.",
306
+ parameters: {
307
+ type: "object",
308
+ properties: { query: { type: "string" } },
309
+ required: ["query"],
310
+ },
311
+ },
312
+ },
313
+ {
314
+ type: "function",
315
+ function: {
316
+ name: TOOL_NAME.findReferences,
317
+ description:
318
+ "List every reference to a symbol across the project (semantic, not text). Give the file it's declared/used in and the symbol name.",
319
+ parameters: {
320
+ type: "object",
321
+ properties: { file: { type: "string" }, symbol: { type: "string" } },
322
+ required: ["file", "symbol"],
323
+ },
324
+ },
325
+ },
326
+ {
327
+ type: "function",
328
+ function: {
329
+ name: TOOL_NAME.typeAt,
330
+ description:
331
+ "Get the inferred TypeScript type of a symbol (so you don't guess types). Give the file and symbol name.",
332
+ parameters: {
333
+ type: "object",
334
+ properties: { file: { type: "string" }, symbol: { type: "string" } },
335
+ required: ["file", "symbol"],
336
+ },
337
+ },
338
+ },
339
+ {
340
+ type: "function",
341
+ function: {
342
+ name: TOOL_NAME.diagnostics,
343
+ description:
344
+ "Get the TypeScript semantic diagnostics (type errors) for one file on demand.",
345
+ parameters: {
346
+ type: "object",
347
+ properties: { file: { type: "string" } },
348
+ required: ["file"],
349
+ },
350
+ },
351
+ },
352
+ {
353
+ type: "function",
354
+ function: {
355
+ name: TOOL_NAME.renameSymbol,
356
+ description:
357
+ "Semantically rename a symbol across ALL its references in one step (no manual multi-file edits). Rejected if any reference is in a read-only/out-of-scope file.",
358
+ parameters: {
359
+ type: "object",
360
+ properties: {
361
+ file: { type: "string" },
362
+ symbol: { type: "string" },
363
+ newName: { type: "string" },
364
+ },
365
+ required: ["file", "symbol", "newName"],
366
+ },
367
+ },
368
+ },
369
+ {
370
+ type: "function",
371
+ function: {
372
+ name: TOOL_NAME.organizeImports,
373
+ description:
374
+ "Sort + dedupe + drop unused imports in an editable file (deterministic).",
375
+ parameters: {
376
+ type: "object",
377
+ properties: { file: { type: "string" } },
378
+ required: ["file"],
379
+ },
380
+ },
381
+ },
382
+ ];
@@ -0,0 +1,34 @@
1
+ import type { ITask } from "../spec";
2
+ import type { ErrorSet } from "../validate";
3
+ import type { Reporter } from "../loop";
4
+ import { type TOOL_NAME } from "./agent.constants";
5
+
6
+ export type ToolName = (typeof TOOL_NAME)[keyof typeof TOOL_NAME];
7
+
8
+ /** Captured output of a spawned shell command (the `run` tool). */
9
+ export interface IShellResult {
10
+ stdout: string;
11
+ stderr: string;
12
+ exitCode: number;
13
+ }
14
+
15
+ export interface IAgentContext {
16
+ /** Working directory the agent may edit within. */
17
+ cwd: string;
18
+ /** The chunk to implement. */
19
+ task: ITask;
20
+ /** Standing failures from the last gate run (empty on the first attempt). */
21
+ errors: ErrorSet;
22
+ /** 1-based repair cycle. */
23
+ cycle: number;
24
+ /** Optional progress sink (e.g. to report applied edits). */
25
+ report?: Reporter;
26
+ }
27
+
28
+ /**
29
+ * The seam where the model plugs in. The walking skeleton uses a canned stub;
30
+ * later this is backed by the inference adapter (Qwen3.6 via vLLM/Ollama).
31
+ */
32
+ export interface IAgent {
33
+ implement(ctx: IAgentContext): Promise<void>;
34
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./agent.types";
2
+ export * from "./agent.constants";
3
+ export * from "./tools";
4
+ export { modelAgent } from "./model-agent";