loopgen 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.
- package/LICENSE +21 -0
- package/README.md +414 -0
- package/dist/adapters/agents-md.js +77 -0
- package/dist/adapters/claude.js +88 -0
- package/dist/adapters/codex.js +97 -0
- package/dist/adapters/cursor.js +41 -0
- package/dist/adapters/local-model.js +172 -0
- package/dist/adapters/windsurf.js +41 -0
- package/dist/cli.js +211 -0
- package/dist/core/adapters.js +162 -0
- package/dist/core/diff.js +29 -0
- package/dist/core/fs-plan.js +12 -0
- package/dist/core/generator.js +226 -0
- package/dist/core/scanner.js +304 -0
- package/dist/core/templates.js +624 -0
- package/dist/core/types.js +1 -0
- package/dist/server.js +241 -0
- package/dist-web/assets/index-BrxUKxHo.css +1 -0
- package/dist-web/assets/index-CIWs8r78.js +13 -0
- package/dist-web/index.html +13 -0
- package/examples/demo-webapp/.github/workflows/ci.yml +20 -0
- package/examples/demo-webapp/README.md +13 -0
- package/examples/demo-webapp/package-lock.json +1246 -0
- package/examples/demo-webapp/package.json +15 -0
- package/examples/demo-webapp/src/checkout.test.ts +13 -0
- package/examples/demo-webapp/src/checkout.ts +9 -0
- package/package.json +67 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { DEFAULT_ADAPTER_IDS, normalizeAdapterConfigs } from "./adapters.js";
|
|
2
|
+
export const TEMPLATE_CATEGORIES = [
|
|
3
|
+
{ id: "maintenance", label: "Maintenance", summary: "Repair and keep project health steady." },
|
|
4
|
+
{ id: "delivery", label: "Delivery", summary: "Prepare releases and delivery evidence." },
|
|
5
|
+
{ id: "quality", label: "Quality", summary: "Reduce defects and improve confidence." },
|
|
6
|
+
{ id: "knowledge", label: "Knowledge", summary: "Turn project work into reusable context." },
|
|
7
|
+
{ id: "cross-functional", label: "Cross-functional", summary: "Help technical partners align on outcomes." }
|
|
8
|
+
];
|
|
9
|
+
export const TEMPLATE_AUDIENCES = [
|
|
10
|
+
{ id: "developer", label: "Developer" },
|
|
11
|
+
{ id: "qa", label: "QA" },
|
|
12
|
+
{ id: "product", label: "Product" },
|
|
13
|
+
{ id: "ops", label: "Ops" },
|
|
14
|
+
{ id: "data", label: "Data" },
|
|
15
|
+
{ id: "solutions", label: "Solutions" }
|
|
16
|
+
];
|
|
17
|
+
const FORBIDDEN_PATHS = [
|
|
18
|
+
".env",
|
|
19
|
+
".env.*",
|
|
20
|
+
"secrets/**",
|
|
21
|
+
"production/**",
|
|
22
|
+
"**/*prod*secret*",
|
|
23
|
+
"**/*credential*"
|
|
24
|
+
];
|
|
25
|
+
const SHARED_ACTIONS = [
|
|
26
|
+
"Create or reuse an isolated working branch/worktree before edits.",
|
|
27
|
+
"Read the state file first and append a concise attempt log after every iteration.",
|
|
28
|
+
"Make the smallest change that can satisfy the goal.",
|
|
29
|
+
"Run verification before declaring success.",
|
|
30
|
+
"Ask for human input instead of guessing when a stop criterion is met."
|
|
31
|
+
];
|
|
32
|
+
const TEMPLATE_RECIPES = [
|
|
33
|
+
{
|
|
34
|
+
id: "ci-failure-repair",
|
|
35
|
+
title: "CI failure repair",
|
|
36
|
+
summary: "Diagnose and fix failing CI workflows and jobs.",
|
|
37
|
+
category: "maintenance",
|
|
38
|
+
audience: ["developer", "ops", "qa"],
|
|
39
|
+
difficulty: "intro",
|
|
40
|
+
expectedOutcome: "A narrow fix that restores the failing CI signal with verification output.",
|
|
41
|
+
demoAvailable: true,
|
|
42
|
+
recommendedForDemo: true,
|
|
43
|
+
recommended: true,
|
|
44
|
+
goal: (scan) => `Restore failing CI jobs for ${scan.projectName} with the smallest safe code or configuration change.`,
|
|
45
|
+
trigger: (scan, cadence) => ({
|
|
46
|
+
type: "ci_failure",
|
|
47
|
+
cadence,
|
|
48
|
+
sources: scan.ci.workflowFiles.length > 0 ? scan.ci.workflowFiles : ["manual CI failure URL or log"]
|
|
49
|
+
}),
|
|
50
|
+
contextSources: ["CI failure log", "changed files"],
|
|
51
|
+
actions: [
|
|
52
|
+
"Collect the failing job name, failing command, and relevant log excerpt.",
|
|
53
|
+
"Map the failure to a local verification command before editing."
|
|
54
|
+
],
|
|
55
|
+
verification: (scan) => pickCommands(scan, ["lint", "test", "build"])
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "test-repair",
|
|
59
|
+
title: "Test repair",
|
|
60
|
+
summary: "Find and fix failing tests and flaky test symptoms.",
|
|
61
|
+
category: "maintenance",
|
|
62
|
+
audience: ["developer", "qa"],
|
|
63
|
+
difficulty: "intro",
|
|
64
|
+
expectedOutcome: "A reproduced failing test is fixed without weakening meaningful assertions.",
|
|
65
|
+
demoAvailable: true,
|
|
66
|
+
recommendedForDemo: true,
|
|
67
|
+
recommended: true,
|
|
68
|
+
goal: (scan) => `Diagnose failing tests in ${scan.projectName}, fix the underlying issue, and verify the relevant suite.`,
|
|
69
|
+
trigger: (_scan, cadence) => ({
|
|
70
|
+
type: "test_failure",
|
|
71
|
+
cadence,
|
|
72
|
+
sources: ["test output", "changed files", "related test files"]
|
|
73
|
+
}),
|
|
74
|
+
contextSources: ["tests/**", "**/*.test.*", "**/*.spec.*"],
|
|
75
|
+
actions: [
|
|
76
|
+
"Reproduce the failing test locally when possible.",
|
|
77
|
+
"Prefer fixing source behavior over weakening assertions."
|
|
78
|
+
],
|
|
79
|
+
verification: (scan) => pickCommands(scan, ["test"])
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "dependency-upgrade",
|
|
83
|
+
title: "Dependency upgrade",
|
|
84
|
+
summary: "Safely upgrade dependencies and resolve breakage.",
|
|
85
|
+
category: "maintenance",
|
|
86
|
+
audience: ["developer", "ops"],
|
|
87
|
+
difficulty: "standard",
|
|
88
|
+
expectedOutcome: "A controlled dependency update with install and verification results recorded.",
|
|
89
|
+
demoAvailable: true,
|
|
90
|
+
recommendedForDemo: false,
|
|
91
|
+
recommended: false,
|
|
92
|
+
goal: (scan) => `Apply dependency updates for ${scan.projectName} while preserving current behavior and verification coverage.`,
|
|
93
|
+
trigger: (_scan, cadence) => ({
|
|
94
|
+
type: "dependency_update",
|
|
95
|
+
cadence,
|
|
96
|
+
sources: ["package manifest", "lockfile", "release notes when available"]
|
|
97
|
+
}),
|
|
98
|
+
contextSources: ["lockfiles", "dependency manifests", "release notes"],
|
|
99
|
+
actions: [
|
|
100
|
+
"Update one dependency group at a time.",
|
|
101
|
+
"Read breaking-change notes when verification fails after an upgrade."
|
|
102
|
+
],
|
|
103
|
+
verification: (scan) => pickCommands(scan, ["install", "test", "build"]),
|
|
104
|
+
allowNetwork: true
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "pr-comment-handling",
|
|
108
|
+
title: "PR comment handling",
|
|
109
|
+
summary: "Triage and respond to actionable pull request comments.",
|
|
110
|
+
category: "maintenance",
|
|
111
|
+
audience: ["developer", "qa", "product"],
|
|
112
|
+
difficulty: "standard",
|
|
113
|
+
expectedOutcome: "Actionable review comments are resolved and non-actionable items are documented.",
|
|
114
|
+
demoAvailable: true,
|
|
115
|
+
recommendedForDemo: false,
|
|
116
|
+
recommended: false,
|
|
117
|
+
goal: (scan) => `Triage actionable PR review comments for ${scan.projectName}, implement accepted fixes, and leave non-actionable items documented.`,
|
|
118
|
+
trigger: (_scan, cadence) => ({
|
|
119
|
+
type: "pull_request_review",
|
|
120
|
+
cadence,
|
|
121
|
+
sources: ["PR review comments", "unresolved review threads"]
|
|
122
|
+
}),
|
|
123
|
+
contextSources: ["PR diff", "review comments"],
|
|
124
|
+
actions: [
|
|
125
|
+
"Classify review comments as actionable, answered, or blocked.",
|
|
126
|
+
"Implement only actionable comments and record the response summary."
|
|
127
|
+
],
|
|
128
|
+
verification: (scan) => pickCommands(scan, ["lint", "test"])
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "release-prep",
|
|
132
|
+
title: "Release preparation",
|
|
133
|
+
summary: "Prepare a release branch with checks, notes, and handoff status.",
|
|
134
|
+
category: "delivery",
|
|
135
|
+
audience: ["developer", "ops", "product"],
|
|
136
|
+
difficulty: "standard",
|
|
137
|
+
expectedOutcome: "A release-ready checklist with verified commands, risks, and handoff notes.",
|
|
138
|
+
demoAvailable: true,
|
|
139
|
+
recommendedForDemo: true,
|
|
140
|
+
recommended: true,
|
|
141
|
+
goal: (scan) => `Prepare ${scan.projectName} for release with verified checks, clear risks, and concise handoff notes.`,
|
|
142
|
+
trigger: (_scan, cadence) => ({
|
|
143
|
+
type: "release_preparation",
|
|
144
|
+
cadence,
|
|
145
|
+
sources: ["planned version", "recent commits", "release checklist"]
|
|
146
|
+
}),
|
|
147
|
+
contextSources: ["CHANGELOG.md", "README.md", "package manifest", "release notes"],
|
|
148
|
+
actions: [
|
|
149
|
+
"Identify the intended release version or scope before editing.",
|
|
150
|
+
"Collect verification results, unresolved risks, and release notes in one handoff."
|
|
151
|
+
],
|
|
152
|
+
verification: (scan) => pickCommands(scan, ["lint", "test", "build"])
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "changelog-generation",
|
|
156
|
+
title: "Changelog generation",
|
|
157
|
+
summary: "Turn recent changes into a concise changelog draft.",
|
|
158
|
+
category: "delivery",
|
|
159
|
+
audience: ["developer", "product", "solutions"],
|
|
160
|
+
difficulty: "intro",
|
|
161
|
+
expectedOutcome: "A reviewable changelog entry grouped by user-visible impact and risk.",
|
|
162
|
+
demoAvailable: true,
|
|
163
|
+
recommendedForDemo: false,
|
|
164
|
+
recommended: false,
|
|
165
|
+
goal: (scan) => `Draft a concise changelog for ${scan.projectName} from recent changes and project context.`,
|
|
166
|
+
trigger: (_scan, cadence) => ({
|
|
167
|
+
type: "release_notes",
|
|
168
|
+
cadence,
|
|
169
|
+
sources: ["git history", "merged PRs", "issue references"]
|
|
170
|
+
}),
|
|
171
|
+
contextSources: ["CHANGELOG.md", "README.md", "recent commits", "merged PRs"],
|
|
172
|
+
actions: [
|
|
173
|
+
"Group changes by user-visible behavior, internal maintenance, and known risks.",
|
|
174
|
+
"Keep uncertain items marked for human confirmation instead of inventing release claims."
|
|
175
|
+
],
|
|
176
|
+
verification: (scan) => pickCommands(scan, ["build", "test"])
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "rollback-check",
|
|
180
|
+
title: "Rollback check",
|
|
181
|
+
summary: "Assess whether a change can be safely rolled back.",
|
|
182
|
+
category: "delivery",
|
|
183
|
+
audience: ["developer", "ops", "qa"],
|
|
184
|
+
difficulty: "advanced",
|
|
185
|
+
expectedOutcome: "A rollback plan with touched areas, verification commands, and blockers.",
|
|
186
|
+
demoAvailable: true,
|
|
187
|
+
recommendedForDemo: false,
|
|
188
|
+
recommended: false,
|
|
189
|
+
goal: (scan) => `Assess rollback readiness for ${scan.projectName} and record the safest path back.`,
|
|
190
|
+
trigger: (_scan, cadence) => ({
|
|
191
|
+
type: "rollback_readiness",
|
|
192
|
+
cadence,
|
|
193
|
+
sources: ["release diff", "incident notes", "deployment checklist"]
|
|
194
|
+
}),
|
|
195
|
+
contextSources: ["deployment docs", "release notes", "changed files"],
|
|
196
|
+
actions: [
|
|
197
|
+
"Identify stateful changes, migrations, configuration changes, and external dependencies.",
|
|
198
|
+
"Record rollback blockers before suggesting any revert."
|
|
199
|
+
],
|
|
200
|
+
verification: (scan) => pickCommands(scan, ["test", "build"])
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "version-upgrade-check",
|
|
204
|
+
title: "Version upgrade check",
|
|
205
|
+
summary: "Plan and verify runtime or framework version upgrades.",
|
|
206
|
+
category: "delivery",
|
|
207
|
+
audience: ["developer", "ops"],
|
|
208
|
+
difficulty: "advanced",
|
|
209
|
+
expectedOutcome: "A version upgrade plan with compatibility notes and verification evidence.",
|
|
210
|
+
demoAvailable: true,
|
|
211
|
+
recommendedForDemo: false,
|
|
212
|
+
recommended: false,
|
|
213
|
+
goal: (scan) => `Check a runtime or framework version upgrade for ${scan.projectName} with compatibility evidence.`,
|
|
214
|
+
trigger: (_scan, cadence) => ({
|
|
215
|
+
type: "version_upgrade",
|
|
216
|
+
cadence,
|
|
217
|
+
sources: ["runtime version", "framework version", "upgrade guide"]
|
|
218
|
+
}),
|
|
219
|
+
contextSources: ["package manifest", "lockfiles", "runtime files", "upgrade guides"],
|
|
220
|
+
actions: [
|
|
221
|
+
"Identify the current version, target version, and official upgrade notes.",
|
|
222
|
+
"Apply compatibility fixes in small batches and record any manual follow-up."
|
|
223
|
+
],
|
|
224
|
+
verification: (scan) => pickCommands(scan, ["install", "test", "build"]),
|
|
225
|
+
allowNetwork: true
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "type-error-reduction",
|
|
229
|
+
title: "Type error reduction",
|
|
230
|
+
summary: "Reduce type errors without hiding unsafe behavior.",
|
|
231
|
+
category: "quality",
|
|
232
|
+
audience: ["developer", "qa"],
|
|
233
|
+
difficulty: "standard",
|
|
234
|
+
expectedOutcome: "A smaller type-error set with fixes documented and unsafe casts avoided.",
|
|
235
|
+
demoAvailable: true,
|
|
236
|
+
recommendedForDemo: false,
|
|
237
|
+
recommended: false,
|
|
238
|
+
goal: (scan) => `Reduce type errors in ${scan.projectName} while preserving runtime behavior.`,
|
|
239
|
+
trigger: (_scan, cadence) => ({
|
|
240
|
+
type: "typecheck_failure",
|
|
241
|
+
cadence,
|
|
242
|
+
sources: ["typecheck output", "compiler diagnostics"]
|
|
243
|
+
}),
|
|
244
|
+
contextSources: ["tsconfig*.json", "compiler output", "changed source files"],
|
|
245
|
+
actions: [
|
|
246
|
+
"Group type errors by root cause before editing.",
|
|
247
|
+
"Prefer accurate types and source fixes over broad casts or disabled checks."
|
|
248
|
+
],
|
|
249
|
+
verification: (scan) => pickCommands(scan, ["build", "test"])
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "lint-cleanup",
|
|
253
|
+
title: "Lint cleanup",
|
|
254
|
+
summary: "Fix lint violations while keeping behavior unchanged.",
|
|
255
|
+
category: "quality",
|
|
256
|
+
audience: ["developer", "qa"],
|
|
257
|
+
difficulty: "intro",
|
|
258
|
+
expectedOutcome: "A behavior-preserving lint cleanup with the lint command passing.",
|
|
259
|
+
demoAvailable: true,
|
|
260
|
+
recommendedForDemo: true,
|
|
261
|
+
recommended: true,
|
|
262
|
+
goal: (scan) => `Clean up lint issues in ${scan.projectName} with behavior-preserving edits.`,
|
|
263
|
+
trigger: (_scan, cadence) => ({
|
|
264
|
+
type: "lint_failure",
|
|
265
|
+
cadence,
|
|
266
|
+
sources: ["lint output", "changed files"]
|
|
267
|
+
}),
|
|
268
|
+
contextSources: ["lint config", "source files", "formatter config"],
|
|
269
|
+
actions: [
|
|
270
|
+
"Separate auto-fixable style issues from behavior-sensitive warnings.",
|
|
271
|
+
"Avoid broad formatting churn unless the formatter command is explicitly allowed."
|
|
272
|
+
],
|
|
273
|
+
verification: (scan) => pickCommands(scan, ["lint", "test"])
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: "coverage-gap-fill",
|
|
277
|
+
title: "Coverage gap fill",
|
|
278
|
+
summary: "Add focused tests around uncovered or risky behavior.",
|
|
279
|
+
category: "quality",
|
|
280
|
+
audience: ["developer", "qa"],
|
|
281
|
+
difficulty: "standard",
|
|
282
|
+
expectedOutcome: "Focused tests cover a known gap without brittle implementation coupling.",
|
|
283
|
+
demoAvailable: true,
|
|
284
|
+
recommendedForDemo: false,
|
|
285
|
+
recommended: false,
|
|
286
|
+
goal: (scan) => `Add focused test coverage for a known gap in ${scan.projectName}.`,
|
|
287
|
+
trigger: (_scan, cadence) => ({
|
|
288
|
+
type: "coverage_gap",
|
|
289
|
+
cadence,
|
|
290
|
+
sources: ["coverage report", "changed files", "bug report"]
|
|
291
|
+
}),
|
|
292
|
+
contextSources: ["coverage reports", "tests/**", "related source files"],
|
|
293
|
+
actions: [
|
|
294
|
+
"Identify the behavior to protect before writing a test.",
|
|
295
|
+
"Prefer user-observable behavior and regression cases over implementation snapshots."
|
|
296
|
+
],
|
|
297
|
+
verification: (scan) => pickCommands(scan, ["test"])
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "dead-code-cleanup",
|
|
301
|
+
title: "Dead code cleanup",
|
|
302
|
+
summary: "Remove unused code with verification and a rollback trail.",
|
|
303
|
+
category: "quality",
|
|
304
|
+
audience: ["developer", "ops"],
|
|
305
|
+
difficulty: "advanced",
|
|
306
|
+
expectedOutcome: "Unused code is removed with affected surfaces and verification recorded.",
|
|
307
|
+
demoAvailable: true,
|
|
308
|
+
recommendedForDemo: false,
|
|
309
|
+
recommended: false,
|
|
310
|
+
goal: (scan) => `Remove clearly unused code from ${scan.projectName} without changing supported behavior.`,
|
|
311
|
+
trigger: (_scan, cadence) => ({
|
|
312
|
+
type: "dead_code_cleanup",
|
|
313
|
+
cadence,
|
|
314
|
+
sources: ["static analysis", "search results", "ownership context"]
|
|
315
|
+
}),
|
|
316
|
+
contextSources: ["source files", "tests/**", "usage search results"],
|
|
317
|
+
actions: [
|
|
318
|
+
"Prove the code is unused through search, tests, or ownership context before deleting.",
|
|
319
|
+
"Keep removals small enough to review and revert."
|
|
320
|
+
],
|
|
321
|
+
verification: (scan) => pickCommands(scan, ["lint", "test", "build"])
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: "readme-refresh",
|
|
325
|
+
title: "README refresh",
|
|
326
|
+
summary: "Update setup, commands, and project purpose for new contributors.",
|
|
327
|
+
category: "knowledge",
|
|
328
|
+
audience: ["developer", "product", "solutions"],
|
|
329
|
+
difficulty: "intro",
|
|
330
|
+
expectedOutcome: "A clearer README that reflects current commands and project behavior.",
|
|
331
|
+
demoAvailable: true,
|
|
332
|
+
recommendedForDemo: true,
|
|
333
|
+
recommended: true,
|
|
334
|
+
goal: (scan) => `Refresh ${scan.projectName} documentation so a new technical contributor can get oriented quickly.`,
|
|
335
|
+
trigger: (_scan, cadence) => ({
|
|
336
|
+
type: "documentation_refresh",
|
|
337
|
+
cadence,
|
|
338
|
+
sources: ["README.md", "package scripts", "project scan"]
|
|
339
|
+
}),
|
|
340
|
+
contextSources: ["README.md", "package manifest", "docs/**"],
|
|
341
|
+
actions: [
|
|
342
|
+
"Compare documented commands against the project scan before editing.",
|
|
343
|
+
"Keep setup steps concrete and mark unknown deployment or credential details as TODO."
|
|
344
|
+
],
|
|
345
|
+
verification: (scan) => pickCommands(scan, ["build", "test"])
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
id: "architecture-notes",
|
|
349
|
+
title: "Architecture notes",
|
|
350
|
+
summary: "Summarize how the project is structured and where key flows live.",
|
|
351
|
+
category: "knowledge",
|
|
352
|
+
audience: ["developer", "product", "solutions"],
|
|
353
|
+
difficulty: "standard",
|
|
354
|
+
expectedOutcome: "A concise architecture note with boundaries, entry points, and open questions.",
|
|
355
|
+
demoAvailable: true,
|
|
356
|
+
recommendedForDemo: false,
|
|
357
|
+
recommended: false,
|
|
358
|
+
goal: (scan) => `Create or update architecture notes for ${scan.projectName} from actual project structure.`,
|
|
359
|
+
trigger: (_scan, cadence) => ({
|
|
360
|
+
type: "architecture_context",
|
|
361
|
+
cadence,
|
|
362
|
+
sources: ["project tree", "entrypoints", "configuration files"]
|
|
363
|
+
}),
|
|
364
|
+
contextSources: ["README.md", "src/**", "app/**", "docs/**"],
|
|
365
|
+
actions: [
|
|
366
|
+
"Identify entry points, major modules, and external integrations from source evidence.",
|
|
367
|
+
"Separate confirmed facts from inferences and open questions."
|
|
368
|
+
],
|
|
369
|
+
verification: (scan) => pickCommands(scan, ["build", "test"])
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "onboarding-guide",
|
|
373
|
+
title: "Onboarding guide",
|
|
374
|
+
summary: "Create a practical first-week guide for technical contributors.",
|
|
375
|
+
category: "knowledge",
|
|
376
|
+
audience: ["developer", "qa", "solutions"],
|
|
377
|
+
difficulty: "intro",
|
|
378
|
+
expectedOutcome: "A contributor guide with setup, first checks, and safe starter tasks.",
|
|
379
|
+
demoAvailable: true,
|
|
380
|
+
recommendedForDemo: false,
|
|
381
|
+
recommended: false,
|
|
382
|
+
goal: (scan) => `Create a practical onboarding guide for technical contributors joining ${scan.projectName}.`,
|
|
383
|
+
trigger: (_scan, cadence) => ({
|
|
384
|
+
type: "onboarding_context",
|
|
385
|
+
cadence,
|
|
386
|
+
sources: ["README.md", "scripts", "tests", "project conventions"]
|
|
387
|
+
}),
|
|
388
|
+
contextSources: ["README.md", "CONTRIBUTING.md", "package manifest", "tests/**"],
|
|
389
|
+
actions: [
|
|
390
|
+
"List setup and verification commands only when they are inferred or confirmed.",
|
|
391
|
+
"Include starter tasks that are low-risk and easy to validate."
|
|
392
|
+
],
|
|
393
|
+
verification: (scan) => pickCommands(scan, ["test", "build"])
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: "decision-record-capture",
|
|
397
|
+
title: "Decision record capture",
|
|
398
|
+
summary: "Capture an engineering decision with context, options, and consequences.",
|
|
399
|
+
category: "knowledge",
|
|
400
|
+
audience: ["developer", "product", "ops"],
|
|
401
|
+
difficulty: "standard",
|
|
402
|
+
expectedOutcome: "A decision record that separates context, tradeoffs, decision, and follow-ups.",
|
|
403
|
+
demoAvailable: true,
|
|
404
|
+
recommendedForDemo: false,
|
|
405
|
+
recommended: false,
|
|
406
|
+
goal: (scan) => `Capture an engineering decision for ${scan.projectName} with clear tradeoffs and follow-up actions.`,
|
|
407
|
+
trigger: (_scan, cadence) => ({
|
|
408
|
+
type: "decision_record",
|
|
409
|
+
cadence,
|
|
410
|
+
sources: ["proposal notes", "PR discussion", "architecture context"]
|
|
411
|
+
}),
|
|
412
|
+
contextSources: ["docs/adr/**", "README.md", "PR discussion"],
|
|
413
|
+
actions: [
|
|
414
|
+
"Summarize the decision context and rejected options before writing the outcome.",
|
|
415
|
+
"Record consequences, owners, and review date when they are known."
|
|
416
|
+
],
|
|
417
|
+
verification: (scan) => pickCommands(scan, ["build", "test"])
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "requirements-clarification",
|
|
421
|
+
title: "Requirements clarification",
|
|
422
|
+
summary: "Turn an ambiguous request into testable technical acceptance criteria.",
|
|
423
|
+
category: "cross-functional",
|
|
424
|
+
audience: ["product", "developer", "qa", "solutions"],
|
|
425
|
+
difficulty: "intro",
|
|
426
|
+
expectedOutcome: "A clarified requirement with assumptions, open questions, and acceptance criteria.",
|
|
427
|
+
demoAvailable: true,
|
|
428
|
+
recommendedForDemo: true,
|
|
429
|
+
recommended: true,
|
|
430
|
+
goal: (scan) => `Clarify an ambiguous requirement for ${scan.projectName} into testable implementation criteria.`,
|
|
431
|
+
trigger: (_scan, cadence) => ({
|
|
432
|
+
type: "requirements_clarification",
|
|
433
|
+
cadence,
|
|
434
|
+
sources: ["ticket", "user report", "product brief"]
|
|
435
|
+
}),
|
|
436
|
+
contextSources: ["README.md", "issue or ticket", "related source areas"],
|
|
437
|
+
actions: [
|
|
438
|
+
"Restate the user goal, affected audience, and measurable success criteria.",
|
|
439
|
+
"List assumptions and blockers separately so implementation does not depend on guesses."
|
|
440
|
+
],
|
|
441
|
+
verification: (scan) => pickCommands(scan, ["test", "build"])
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: "qa-acceptance-checklist",
|
|
445
|
+
title: "QA acceptance checklist",
|
|
446
|
+
summary: "Generate a QA checklist from the change intent and project behavior.",
|
|
447
|
+
category: "cross-functional",
|
|
448
|
+
audience: ["qa", "product", "developer"],
|
|
449
|
+
difficulty: "intro",
|
|
450
|
+
expectedOutcome: "A scenario-based checklist covering happy path, edge cases, and regressions.",
|
|
451
|
+
demoAvailable: true,
|
|
452
|
+
recommendedForDemo: true,
|
|
453
|
+
recommended: true,
|
|
454
|
+
goal: (scan) => `Create a QA acceptance checklist for a change in ${scan.projectName}.`,
|
|
455
|
+
trigger: (_scan, cadence) => ({
|
|
456
|
+
type: "qa_acceptance",
|
|
457
|
+
cadence,
|
|
458
|
+
sources: ["ticket", "PR diff", "known user flows"]
|
|
459
|
+
}),
|
|
460
|
+
contextSources: ["README.md", "tests/**", "PR diff", "user flows"],
|
|
461
|
+
actions: [
|
|
462
|
+
"Map the change to user-visible scenarios before listing checks.",
|
|
463
|
+
"Include edge cases, regression checks, and evidence expected for sign-off."
|
|
464
|
+
],
|
|
465
|
+
verification: (scan) => pickCommands(scan, ["test"])
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: "data-processing-check",
|
|
469
|
+
title: "Data processing check",
|
|
470
|
+
summary: "Review a data transformation for inputs, outputs, and validation gaps.",
|
|
471
|
+
category: "cross-functional",
|
|
472
|
+
audience: ["data", "developer", "qa"],
|
|
473
|
+
difficulty: "standard",
|
|
474
|
+
expectedOutcome: "A data-flow check with input assumptions, validation gaps, and verification notes.",
|
|
475
|
+
demoAvailable: true,
|
|
476
|
+
recommendedForDemo: false,
|
|
477
|
+
recommended: false,
|
|
478
|
+
goal: (scan) => `Check a data processing flow in ${scan.projectName} for input assumptions and validation gaps.`,
|
|
479
|
+
trigger: (_scan, cadence) => ({
|
|
480
|
+
type: "data_processing_review",
|
|
481
|
+
cadence,
|
|
482
|
+
sources: ["data contract", "pipeline code", "sample input"]
|
|
483
|
+
}),
|
|
484
|
+
contextSources: ["data docs", "scripts/**", "src/**", "tests/**"],
|
|
485
|
+
actions: [
|
|
486
|
+
"Identify inputs, transformations, outputs, and validation points before editing.",
|
|
487
|
+
"Record sample data limitations and avoid reading sensitive production data."
|
|
488
|
+
],
|
|
489
|
+
verification: (scan) => pickCommands(scan, ["test", "build"])
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
id: "customer-issue-retro",
|
|
493
|
+
title: "Customer issue retro",
|
|
494
|
+
summary: "Turn a customer issue into root cause, fix evidence, and prevention notes.",
|
|
495
|
+
category: "cross-functional",
|
|
496
|
+
audience: ["solutions", "product", "developer", "qa"],
|
|
497
|
+
difficulty: "standard",
|
|
498
|
+
expectedOutcome: "A customer-ready retro with root cause, verification evidence, and prevention steps.",
|
|
499
|
+
demoAvailable: true,
|
|
500
|
+
recommendedForDemo: false,
|
|
501
|
+
recommended: false,
|
|
502
|
+
goal: (scan) => `Analyze a customer issue for ${scan.projectName} and capture fix evidence plus prevention notes.`,
|
|
503
|
+
trigger: (_scan, cadence) => ({
|
|
504
|
+
type: "customer_issue_retro",
|
|
505
|
+
cadence,
|
|
506
|
+
sources: ["customer report", "support notes", "related logs"]
|
|
507
|
+
}),
|
|
508
|
+
contextSources: ["customer report", "support notes", "related source files", "tests/**"],
|
|
509
|
+
actions: [
|
|
510
|
+
"Separate observed symptoms, confirmed root cause, fix evidence, and follow-up prevention.",
|
|
511
|
+
"Keep customer-facing language factual and avoid exposing internal secrets."
|
|
512
|
+
],
|
|
513
|
+
verification: (scan) => pickCommands(scan, ["test", "build"])
|
|
514
|
+
}
|
|
515
|
+
];
|
|
516
|
+
export const TEMPLATE_DEFINITIONS = TEMPLATE_RECIPES.map(publicTemplateDefinition);
|
|
517
|
+
const TEMPLATE_BY_ID = new Map(TEMPLATE_RECIPES.map((template) => [template.id, template]));
|
|
518
|
+
export function getTemplateDefinition(id) {
|
|
519
|
+
const recipe = TEMPLATE_BY_ID.get(id);
|
|
520
|
+
return recipe ? publicTemplateDefinition(recipe) : undefined;
|
|
521
|
+
}
|
|
522
|
+
export function templateIds() {
|
|
523
|
+
return TEMPLATE_RECIPES.map((template) => template.id);
|
|
524
|
+
}
|
|
525
|
+
export function createLoopSpec(id, scan, answers) {
|
|
526
|
+
const recipe = TEMPLATE_BY_ID.get(id);
|
|
527
|
+
if (!recipe) {
|
|
528
|
+
throw new Error(`Unknown template: ${id}`);
|
|
529
|
+
}
|
|
530
|
+
const verificationCommands = recipe.verification(scan);
|
|
531
|
+
const requiresHumanCommandDefinition = verificationCommands.length === 0;
|
|
532
|
+
const commands = requiresHumanCommandDefinition
|
|
533
|
+
? ["echo \"TODO: configure a real verification command before running this loop\""]
|
|
534
|
+
: verificationCommands;
|
|
535
|
+
return {
|
|
536
|
+
id,
|
|
537
|
+
title: recipe.title,
|
|
538
|
+
category: recipe.category,
|
|
539
|
+
audience: recipe.audience,
|
|
540
|
+
difficulty: recipe.difficulty,
|
|
541
|
+
expectedOutcome: recipe.expectedOutcome,
|
|
542
|
+
goal: recipe.goal(scan),
|
|
543
|
+
trigger: recipe.trigger(scan, answers.triggerCadence),
|
|
544
|
+
contextSources: contextFor(recipe, scan),
|
|
545
|
+
actions: [...recipe.actions, ...SHARED_ACTIONS],
|
|
546
|
+
verification: {
|
|
547
|
+
commands,
|
|
548
|
+
acceptanceCriteria: answers.acceptanceCriteria,
|
|
549
|
+
makerChecker: true,
|
|
550
|
+
requiresHumanCommandDefinition
|
|
551
|
+
},
|
|
552
|
+
stopCriteria: {
|
|
553
|
+
maxIterations: answers.maxIterations,
|
|
554
|
+
timeoutMinutes: recipe.timeoutMinutes ?? 45,
|
|
555
|
+
requireHumanInputOn: [
|
|
556
|
+
"verification command is missing or ambiguous",
|
|
557
|
+
"changes require production credentials",
|
|
558
|
+
"more than 20 files would be modified",
|
|
559
|
+
"the same failure repeats after maxIterations"
|
|
560
|
+
]
|
|
561
|
+
},
|
|
562
|
+
stateFile: `.loopgen/state/${id}.md`,
|
|
563
|
+
permissions: {
|
|
564
|
+
allowedCommands: answers.allowedCommands,
|
|
565
|
+
forbiddenPaths: FORBIDDEN_PATHS,
|
|
566
|
+
allowNetwork: recipe.allowNetwork ?? false,
|
|
567
|
+
allowPrCreation: answers.allowPrCreation
|
|
568
|
+
},
|
|
569
|
+
adapters: answers.adapters
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
export function defaultAnswers(scan, overrides = {}) {
|
|
573
|
+
const inferredCommands = [
|
|
574
|
+
scan.commands.install,
|
|
575
|
+
scan.commands.test,
|
|
576
|
+
scan.commands.lint,
|
|
577
|
+
scan.commands.build,
|
|
578
|
+
scan.commands.format
|
|
579
|
+
].filter(Boolean);
|
|
580
|
+
const defaults = {
|
|
581
|
+
selectedTemplates: templateIds(),
|
|
582
|
+
adapters: DEFAULT_ADAPTER_IDS,
|
|
583
|
+
adapterConfigs: normalizeAdapterConfigs(undefined),
|
|
584
|
+
triggerCadence: "manual",
|
|
585
|
+
acceptanceCriteria: "All configured verification commands pass and the generated state file explains what changed.",
|
|
586
|
+
allowPrCreation: false,
|
|
587
|
+
allowedCommands: inferredCommands,
|
|
588
|
+
maxIterations: 3
|
|
589
|
+
};
|
|
590
|
+
return {
|
|
591
|
+
...defaults,
|
|
592
|
+
...definedOverrides(overrides),
|
|
593
|
+
adapterConfigs: normalizeAdapterConfigs(overrides.adapterConfigs ?? defaults.adapterConfigs)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function publicTemplateDefinition(template) {
|
|
597
|
+
return {
|
|
598
|
+
id: template.id,
|
|
599
|
+
title: template.title,
|
|
600
|
+
summary: template.summary,
|
|
601
|
+
category: template.category,
|
|
602
|
+
audience: template.audience,
|
|
603
|
+
difficulty: template.difficulty,
|
|
604
|
+
expectedOutcome: template.expectedOutcome,
|
|
605
|
+
demoAvailable: template.demoAvailable,
|
|
606
|
+
recommendedForDemo: template.recommendedForDemo,
|
|
607
|
+
recommended: template.recommended
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function definedOverrides(overrides) {
|
|
611
|
+
return Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== undefined));
|
|
612
|
+
}
|
|
613
|
+
function contextFor(recipe, scan) {
|
|
614
|
+
return [...new Set([...scan.contextSources, ...recipe.contextSources])];
|
|
615
|
+
}
|
|
616
|
+
function pickCommands(scan, keys) {
|
|
617
|
+
const commands = new Set();
|
|
618
|
+
for (const key of keys) {
|
|
619
|
+
const command = scan.commands[key];
|
|
620
|
+
if (command)
|
|
621
|
+
commands.add(command);
|
|
622
|
+
}
|
|
623
|
+
return [...commands];
|
|
624
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|