@ipul-labs/forge 1.16.8 → 1.17.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/README.md +39 -21
- package/lib/bin/cli/analyze.d.ts +9 -0
- package/lib/bin/cli/analyze.d.ts.map +1 -0
- package/lib/bin/cli/analyze.js +491 -0
- package/lib/bin/cli/analyze.js.map +1 -0
- package/lib/bin/cli/apply.d.ts +2 -0
- package/lib/bin/cli/apply.d.ts.map +1 -0
- package/lib/bin/cli/apply.js +262 -0
- package/lib/bin/cli/apply.js.map +1 -0
- package/lib/bin/cli/cifix.d.ts +2 -0
- package/lib/bin/cli/cifix.d.ts.map +1 -0
- package/lib/bin/cli/cifix.js +310 -0
- package/lib/bin/cli/cifix.js.map +1 -0
- package/lib/bin/cli/close.d.ts +2 -0
- package/lib/bin/cli/close.d.ts.map +1 -0
- package/lib/bin/cli/close.js +113 -0
- package/lib/bin/cli/close.js.map +1 -0
- package/lib/bin/cli/createIssue.d.ts +2 -0
- package/lib/bin/cli/createIssue.d.ts.map +1 -0
- package/lib/bin/cli/createIssue.js +108 -0
- package/lib/bin/cli/createIssue.js.map +1 -0
- package/lib/bin/cli/doctor.d.ts +2 -0
- package/lib/bin/cli/doctor.d.ts.map +1 -0
- package/lib/bin/cli/doctor.js +145 -0
- package/lib/bin/cli/doctor.js.map +1 -0
- package/lib/bin/cli/init.d.ts +4 -0
- package/lib/bin/cli/init.d.ts.map +1 -0
- package/lib/bin/cli/init.js +288 -0
- package/lib/bin/cli/init.js.map +1 -0
- package/lib/bin/cli/ink.d.ts +23 -0
- package/lib/bin/cli/ink.d.ts.map +1 -0
- package/{bin → lib/bin}/cli/ink.js +18 -19
- package/lib/bin/cli/ink.js.map +1 -0
- package/lib/bin/cli/issue.d.ts +2 -0
- package/lib/bin/cli/issue.d.ts.map +1 -0
- package/lib/bin/cli/issue.js +114 -0
- package/lib/bin/cli/issue.js.map +1 -0
- package/lib/bin/cli/issues.d.ts +2 -0
- package/lib/bin/cli/issues.d.ts.map +1 -0
- package/lib/bin/cli/issues.js +149 -0
- package/lib/bin/cli/issues.js.map +1 -0
- package/lib/bin/cli/merge.d.ts +2 -0
- package/lib/bin/cli/merge.d.ts.map +1 -0
- package/lib/bin/cli/merge.js +162 -0
- package/lib/bin/cli/merge.js.map +1 -0
- package/lib/bin/cli/refine.d.ts +2 -0
- package/lib/bin/cli/refine.d.ts.map +1 -0
- package/lib/bin/cli/refine.js +249 -0
- package/lib/bin/cli/refine.js.map +1 -0
- package/lib/bin/cli/repl.d.ts +2 -0
- package/lib/bin/cli/repl.d.ts.map +1 -0
- package/lib/bin/cli/repl.js +309 -0
- package/lib/bin/cli/repl.js.map +1 -0
- package/lib/bin/cli/repos.d.ts +2 -0
- package/lib/bin/cli/repos.d.ts.map +1 -0
- package/lib/bin/cli/repos.js +86 -0
- package/lib/bin/cli/repos.js.map +1 -0
- package/lib/bin/cli/resolution.d.ts +2 -0
- package/lib/bin/cli/resolution.d.ts.map +1 -0
- package/lib/bin/cli/resolution.js +139 -0
- package/lib/bin/cli/resolution.js.map +1 -0
- package/lib/bin/cli/serverClient.d.ts +26 -0
- package/lib/bin/cli/serverClient.d.ts.map +1 -0
- package/lib/bin/cli/serverClient.js +111 -0
- package/lib/bin/cli/serverClient.js.map +1 -0
- package/lib/bin/cli/session.d.ts +8 -0
- package/lib/bin/cli/session.d.ts.map +1 -0
- package/lib/bin/cli/session.js +212 -0
- package/lib/bin/cli/session.js.map +1 -0
- package/lib/bin/cli/sessionStore.d.ts +69 -0
- package/lib/bin/cli/sessionStore.d.ts.map +1 -0
- package/lib/bin/cli/sessionStore.js +196 -0
- package/lib/bin/cli/sessionStore.js.map +1 -0
- package/lib/bin/cli/ship.d.ts +33 -0
- package/lib/bin/cli/ship.d.ts.map +1 -0
- package/lib/bin/cli/ship.js +737 -0
- package/lib/bin/cli/ship.js.map +1 -0
- package/lib/bin/cli/types.d.ts +59 -0
- package/lib/bin/cli/types.d.ts.map +1 -0
- package/lib/bin/cli/types.js +2 -0
- package/lib/bin/cli/types.js.map +1 -0
- package/lib/bin/cli/update.d.ts +2 -0
- package/lib/bin/cli/update.d.ts.map +1 -0
- package/lib/bin/cli/update.js +87 -0
- package/lib/bin/cli/update.js.map +1 -0
- package/lib/bin/cli/utils.d.ts +43 -0
- package/lib/bin/cli/utils.d.ts.map +1 -0
- package/lib/bin/cli/utils.js +171 -0
- package/lib/bin/cli/utils.js.map +1 -0
- package/lib/bin/cli/validate.d.ts +2 -0
- package/lib/bin/cli/validate.d.ts.map +1 -0
- package/lib/bin/cli/validate.js +283 -0
- package/lib/bin/cli/validate.js.map +1 -0
- package/lib/bin/cli/watch.d.ts +5 -0
- package/lib/bin/cli/watch.d.ts.map +1 -0
- package/lib/bin/cli/watch.js +263 -0
- package/lib/bin/cli/watch.js.map +1 -0
- package/lib/bin/forge-agent.d.ts +3 -0
- package/lib/bin/forge-agent.d.ts.map +1 -0
- package/{bin → lib/bin}/forge-agent.js +104 -105
- package/lib/bin/forge-agent.js.map +1 -0
- package/lib/server/forge/core/ForgeAgent.d.ts +260 -0
- package/lib/server/forge/core/ForgeAgent.d.ts.map +1 -0
- package/lib/server/forge/core/ForgeAgent.js +995 -0
- package/lib/server/forge/core/ForgeAgent.js.map +1 -0
- package/lib/server/forge/core/agentClient.d.ts +39 -0
- package/lib/server/forge/core/agentClient.d.ts.map +1 -0
- package/{server → lib/server}/forge/core/agentClient.js +7 -12
- package/lib/server/forge/core/agentClient.js.map +1 -0
- package/lib/server/forge/core/branchName.d.ts +20 -0
- package/lib/server/forge/core/branchName.d.ts.map +1 -0
- package/lib/server/forge/core/branchName.js +39 -0
- package/lib/server/forge/core/branchName.js.map +1 -0
- package/lib/server/forge/core/claudeClient.d.ts +60 -0
- package/lib/server/forge/core/claudeClient.d.ts.map +1 -0
- package/lib/server/forge/core/claudeClient.js +347 -0
- package/lib/server/forge/core/claudeClient.js.map +1 -0
- package/lib/server/forge/core/configStore.d.ts +76 -0
- package/lib/server/forge/core/configStore.d.ts.map +1 -0
- package/lib/server/forge/core/configStore.js +213 -0
- package/lib/server/forge/core/configStore.js.map +1 -0
- package/lib/server/forge/core/gitlabClient.d.ts +144 -0
- package/lib/server/forge/core/gitlabClient.d.ts.map +1 -0
- package/{server → lib/server}/forge/core/gitlabClient.js +98 -180
- package/lib/server/forge/core/gitlabClient.js.map +1 -0
- package/lib/server/forge/core/jobs.d.ts +67 -0
- package/lib/server/forge/core/jobs.d.ts.map +1 -0
- package/lib/server/forge/core/jobs.js +185 -0
- package/lib/server/forge/core/jobs.js.map +1 -0
- package/lib/server/forge/core/k8s.d.ts +18 -0
- package/lib/server/forge/core/k8s.d.ts.map +1 -0
- package/lib/server/forge/core/k8s.js +49 -0
- package/lib/server/forge/core/k8s.js.map +1 -0
- package/lib/server/forge/core/localRepo.d.ts +36 -0
- package/lib/server/forge/core/localRepo.d.ts.map +1 -0
- package/lib/server/forge/core/localRepo.js +165 -0
- package/lib/server/forge/core/localRepo.js.map +1 -0
- package/lib/server/forge/core/openclawClient.d.ts +61 -0
- package/lib/server/forge/core/openclawClient.d.ts.map +1 -0
- package/lib/server/forge/core/openclawClient.js +111 -0
- package/lib/server/forge/core/openclawClient.js.map +1 -0
- package/lib/server/forge/core/prompts.d.ts +96 -0
- package/lib/server/forge/core/prompts.d.ts.map +1 -0
- package/lib/server/forge/core/prompts.js +450 -0
- package/lib/server/forge/core/prompts.js.map +1 -0
- package/lib/server/forge/core/providers/azuredevops.d.ts +134 -0
- package/lib/server/forge/core/providers/azuredevops.d.ts.map +1 -0
- package/lib/server/forge/core/providers/azuredevops.js +360 -0
- package/lib/server/forge/core/providers/azuredevops.js.map +1 -0
- package/lib/server/forge/core/providers/base.d.ts +114 -0
- package/lib/server/forge/core/providers/base.d.ts.map +1 -0
- package/lib/server/forge/core/providers/base.js +98 -0
- package/lib/server/forge/core/providers/base.js.map +1 -0
- package/lib/server/forge/core/providers/github.d.ts +133 -0
- package/lib/server/forge/core/providers/github.d.ts.map +1 -0
- package/lib/server/forge/core/providers/github.js +307 -0
- package/lib/server/forge/core/providers/github.js.map +1 -0
- package/lib/server/forge/core/providers/gitlab.d.ts +144 -0
- package/lib/server/forge/core/providers/gitlab.d.ts.map +1 -0
- package/lib/server/forge/core/providers/gitlab.js +407 -0
- package/lib/server/forge/core/providers/gitlab.js.map +1 -0
- package/lib/server/forge/core/providers/index.d.ts +21 -0
- package/lib/server/forge/core/providers/index.d.ts.map +1 -0
- package/lib/server/forge/core/providers/index.js +43 -0
- package/lib/server/forge/core/providers/index.js.map +1 -0
- package/lib/server/forge/core/providers/types.d.ts +80 -0
- package/lib/server/forge/core/providers/types.d.ts.map +1 -0
- package/lib/server/forge/core/providers/types.js +2 -0
- package/lib/server/forge/core/providers/types.js.map +1 -0
- package/lib/server/forge/core/quotaClient.d.ts +48 -0
- package/lib/server/forge/core/quotaClient.d.ts.map +1 -0
- package/lib/server/forge/core/quotaClient.js +88 -0
- package/lib/server/forge/core/quotaClient.js.map +1 -0
- package/lib/server/forge/core/types.d.ts +160 -0
- package/lib/server/forge/core/types.d.ts.map +1 -0
- package/lib/server/forge/core/types.js +2 -0
- package/lib/server/forge/core/types.js.map +1 -0
- package/lib/server/forge/core/validator/attribute.d.ts +31 -0
- package/lib/server/forge/core/validator/attribute.d.ts.map +1 -0
- package/lib/server/forge/core/validator/attribute.js +82 -0
- package/lib/server/forge/core/validator/attribute.js.map +1 -0
- package/lib/server/forge/core/validator/clone.d.ts +14 -0
- package/lib/server/forge/core/validator/clone.d.ts.map +1 -0
- package/lib/server/forge/core/validator/clone.js +99 -0
- package/lib/server/forge/core/validator/clone.js.map +1 -0
- package/lib/server/forge/core/validator/cmd.d.ts +23 -0
- package/lib/server/forge/core/validator/cmd.d.ts.map +1 -0
- package/lib/server/forge/core/validator/cmd.js +87 -0
- package/lib/server/forge/core/validator/cmd.js.map +1 -0
- package/lib/server/forge/core/validator/fs.d.ts +5 -0
- package/lib/server/forge/core/validator/fs.d.ts.map +1 -0
- package/{server → lib/server}/forge/core/validator/fs.js +8 -3
- package/lib/server/forge/core/validator/fs.js.map +1 -0
- package/lib/server/forge/core/validator/overlay.d.ts +13 -0
- package/lib/server/forge/core/validator/overlay.d.ts.map +1 -0
- package/lib/server/forge/core/validator/overlay.js +53 -0
- package/lib/server/forge/core/validator/overlay.js.map +1 -0
- package/lib/server/forge/core/validator/projectType.d.ts +20 -0
- package/lib/server/forge/core/validator/projectType.d.ts.map +1 -0
- package/lib/server/forge/core/validator/projectType.js +94 -0
- package/lib/server/forge/core/validator/projectType.js.map +1 -0
- package/lib/server/forge/core/validator/runners/go.d.ts +21 -0
- package/lib/server/forge/core/validator/runners/go.d.ts.map +1 -0
- package/lib/server/forge/core/validator/runners/go.js +99 -0
- package/lib/server/forge/core/validator/runners/go.js.map +1 -0
- package/lib/server/forge/core/validator/runners/node.d.ts +12 -0
- package/lib/server/forge/core/validator/runners/node.d.ts.map +1 -0
- package/lib/server/forge/core/validator/runners/node.js +75 -0
- package/lib/server/forge/core/validator/runners/node.js.map +1 -0
- package/lib/server/forge/core/validator/tdd.d.ts +30 -0
- package/lib/server/forge/core/validator/tdd.d.ts.map +1 -0
- package/lib/server/forge/core/validator/tdd.js +96 -0
- package/lib/server/forge/core/validator/tdd.js.map +1 -0
- package/lib/server/forge/core/validator.d.ts +58 -0
- package/lib/server/forge/core/validator.d.ts.map +1 -0
- package/lib/server/forge/core/validator.js +150 -0
- package/lib/server/forge/core/validator.js.map +1 -0
- package/lib/server/forge/routes.d.ts +22 -0
- package/lib/server/forge/routes.d.ts.map +1 -0
- package/lib/server/forge/routes.js +984 -0
- package/lib/server/forge/routes.js.map +1 -0
- package/lib/server.d.ts +2 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +52 -0
- package/lib/server.js.map +1 -0
- package/lib/shared/events.d.ts +12 -0
- package/lib/shared/events.d.ts.map +1 -0
- package/lib/shared/events.js +12 -0
- package/lib/shared/events.js.map +1 -0
- package/package.json +15 -13
- package/scripts/fix-shebang.cjs +10 -0
- package/bin/cli/analyze.js +0 -516
- package/bin/cli/apply.js +0 -264
- package/bin/cli/cifix.js +0 -300
- package/bin/cli/close.js +0 -125
- package/bin/cli/createIssue.js +0 -112
- package/bin/cli/doctor.js +0 -161
- package/bin/cli/init.js +0 -306
- package/bin/cli/issue.js +0 -116
- package/bin/cli/issues.js +0 -155
- package/bin/cli/merge.js +0 -175
- package/bin/cli/refine.js +0 -253
- package/bin/cli/repl.js +0 -301
- package/bin/cli/repos.js +0 -91
- package/bin/cli/resolution.js +0 -126
- package/bin/cli/session.js +0 -230
- package/bin/cli/sessionStore.js +0 -211
- package/bin/cli/ship.js +0 -762
- package/bin/cli/update.js +0 -95
- package/bin/cli/utils.js +0 -145
- package/bin/cli/validate.js +0 -280
- package/bin/cli/watch.js +0 -274
- package/server/forge/core/ForgeAgent.js +0 -1179
- package/server/forge/core/ForgeAgent.test.js +0 -179
- package/server/forge/core/branchName.js +0 -41
- package/server/forge/core/claudeClient.js +0 -378
- package/server/forge/core/configStore.js +0 -229
- package/server/forge/core/configStore.test.js +0 -89
- package/server/forge/core/jobs.js +0 -180
- package/server/forge/core/k8s.js +0 -54
- package/server/forge/core/localRepo.js +0 -163
- package/server/forge/core/openclawClient.js +0 -125
- package/server/forge/core/prompts.js +0 -483
- package/server/forge/core/prompts.test.js +0 -219
- package/server/forge/core/providers/azuredevops.js +0 -409
- package/server/forge/core/providers/base.js +0 -208
- package/server/forge/core/providers/github.js +0 -348
- package/server/forge/core/providers/gitlab.js +0 -529
- package/server/forge/core/providers/index.js +0 -47
- package/server/forge/core/quotaClient.js +0 -101
- package/server/forge/core/validator/attribute.js +0 -84
- package/server/forge/core/validator/clone.js +0 -116
- package/server/forge/core/validator/cmd.js +0 -98
- package/server/forge/core/validator/overlay.js +0 -52
- package/server/forge/core/validator/projectType.js +0 -87
- package/server/forge/core/validator/runners/go.js +0 -107
- package/server/forge/core/validator/runners/node.js +0 -84
- package/server/forge/core/validator/tdd.js +0 -105
- package/server/forge/core/validator.js +0 -167
- package/server/forge/routes.js +0 -1027
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/fs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAsB,MAAM,CAAC,CAAC,EAAE,MAAM,oBAErC"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tiny fs helpers shared by clone / overlay / project-type detection.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
import fs from "node:fs/promises";
|
|
6
|
-
|
|
7
5
|
export async function exists(p) {
|
|
8
|
-
|
|
6
|
+
try {
|
|
7
|
+
await fs.access(p);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
9
13
|
}
|
|
14
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/fs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACrC,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay proposed changes onto the cloned working tree, and read formatter
|
|
3
|
+
* rewrites back into the analysis so committed text matches what was validated.
|
|
4
|
+
*/
|
|
5
|
+
import type { ForgeChange, ForgeEvent } from "../types.ts";
|
|
6
|
+
export declare function overlayChanges(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* After a formatter step (npm run format, go fmt …), re-read the overlayed
|
|
9
|
+
* files. If the formatter rewrote any of them, mutate `c.proposed` in place
|
|
10
|
+
* and emit a `changes_formatted` event so the frontend can sync.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readbackFormatted(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/overlay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,iBAkB1G;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,iBAkB7G"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay proposed changes onto the cloned working tree, and read formatter
|
|
3
|
+
* rewrites back into the analysis so committed text matches what was validated.
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
export async function overlayChanges(dir, changes, emit) {
|
|
8
|
+
emit({ type: "progress", step: "overlay", status: "running" });
|
|
9
|
+
const realBase = await fs.realpath(dir);
|
|
10
|
+
for (const c of changes) {
|
|
11
|
+
const full = path.resolve(dir, c.file);
|
|
12
|
+
if (!full.startsWith(realBase + path.sep) && full !== realBase) {
|
|
13
|
+
throw new Error(`Refusing to write outside clone dir: ${c.file}`);
|
|
14
|
+
}
|
|
15
|
+
if (c.action === "delete") {
|
|
16
|
+
await fs.rm(full, { force: true });
|
|
17
|
+
emit({ type: "log", step: "overlay", message: `- ${c.file}` });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
21
|
+
await fs.writeFile(full, c.proposed);
|
|
22
|
+
emit({ type: "log", step: "overlay", message: `+ ${c.file} (${c.action})` });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
emit({ type: "step_result", step: "overlay", passed: true });
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* After a formatter step (npm run format, go fmt …), re-read the overlayed
|
|
29
|
+
* files. If the formatter rewrote any of them, mutate `c.proposed` in place
|
|
30
|
+
* and emit a `changes_formatted` event so the frontend can sync.
|
|
31
|
+
*/
|
|
32
|
+
export async function readbackFormatted(dir, changes, emit) {
|
|
33
|
+
const updated = [];
|
|
34
|
+
for (const c of changes) {
|
|
35
|
+
if (c.action === "delete")
|
|
36
|
+
continue;
|
|
37
|
+
try {
|
|
38
|
+
const after = await fs.readFile(path.join(dir, c.file), "utf8");
|
|
39
|
+
if (after !== c.proposed) {
|
|
40
|
+
c.proposed = after;
|
|
41
|
+
updated.push({ file: c.file, proposed: after });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// File may have been removed or renamed by the formatter
|
|
46
|
+
// (uncommon for prettier/eslint --fix); skip silently.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (updated.length) {
|
|
50
|
+
emit({ type: "changes_formatted", files: updated });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/overlay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,OAAsB,EAAE,IAAiC;IAC1G,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACP,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QAC9E,CAAC;IACF,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAW,EAAE,OAAsB,EAAE,IAAiC;IAC7G,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QACpC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAChE,IAAI,KAAK,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC1B,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YACjD,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,yDAAyD;YACzD,uDAAuD;QACxD,CAAC;IACF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACrD,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-repo project-type resolution.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (strongest → weakest signal):
|
|
5
|
+
* 1. configStore — the user's explicit choice in Settings → Repositories.
|
|
6
|
+
* Honoured verbatim when not "auto".
|
|
7
|
+
* 2. Changeset extensions — what the user is actually editing. Safe to
|
|
8
|
+
* trust as the primary signal because auto-attribution upstream
|
|
9
|
+
* guarantees that only repos that actually own a change ever reach
|
|
10
|
+
* this resolver. A polyglot repo with a root `package.json` and a
|
|
11
|
+
* nested Go module still classifies as Go when the changeset is `.go`.
|
|
12
|
+
* 3. Root marker — `go.mod` / `package.json` at the repo root.
|
|
13
|
+
* 4. Shallow tree scan — last resort, walks one level to find a nested
|
|
14
|
+
* marker (monorepo layouts).
|
|
15
|
+
* 5. Fallback — Node.
|
|
16
|
+
*/
|
|
17
|
+
import type { ForgeChange } from "../types.ts";
|
|
18
|
+
export declare const SUPPORTED_TYPES: string[];
|
|
19
|
+
export declare function resolveProjectType(repoId: string, dir: string, changes: ForgeChange[]): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=projectType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectType.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/projectType.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,eAAO,MAAM,eAAe,UAAiB,CAAC;AAuD9C,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,mBAc3F"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-repo project-type resolution.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (strongest → weakest signal):
|
|
5
|
+
* 1. configStore — the user's explicit choice in Settings → Repositories.
|
|
6
|
+
* Honoured verbatim when not "auto".
|
|
7
|
+
* 2. Changeset extensions — what the user is actually editing. Safe to
|
|
8
|
+
* trust as the primary signal because auto-attribution upstream
|
|
9
|
+
* guarantees that only repos that actually own a change ever reach
|
|
10
|
+
* this resolver. A polyglot repo with a root `package.json` and a
|
|
11
|
+
* nested Go module still classifies as Go when the changeset is `.go`.
|
|
12
|
+
* 3. Root marker — `go.mod` / `package.json` at the repo root.
|
|
13
|
+
* 4. Shallow tree scan — last resort, walks one level to find a nested
|
|
14
|
+
* marker (monorepo layouts).
|
|
15
|
+
* 5. Fallback — Node.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs/promises";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import * as configStore from "../configStore.js";
|
|
20
|
+
import { exists } from "./fs.js";
|
|
21
|
+
export const SUPPORTED_TYPES = ["node", "go"];
|
|
22
|
+
const GO_EXTS = new Set([".go"]);
|
|
23
|
+
const NODE_EXTS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]);
|
|
24
|
+
function detectFromChanges(changes) {
|
|
25
|
+
let go = 0;
|
|
26
|
+
let node = 0;
|
|
27
|
+
for (const c of changes ?? []) {
|
|
28
|
+
const ext = path.extname(c.file ?? "").toLowerCase();
|
|
29
|
+
if (GO_EXTS.has(ext))
|
|
30
|
+
go++;
|
|
31
|
+
else if (NODE_EXTS.has(ext))
|
|
32
|
+
node++;
|
|
33
|
+
}
|
|
34
|
+
if (go > 0 && node === 0)
|
|
35
|
+
return "go";
|
|
36
|
+
if (node > 0 && go === 0)
|
|
37
|
+
return "node";
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
async function detectFromRoot(dir) {
|
|
41
|
+
const hasGo = await exists(path.join(dir, "go.mod"));
|
|
42
|
+
const hasNode = await exists(path.join(dir, "package.json"));
|
|
43
|
+
if (hasGo && !hasNode)
|
|
44
|
+
return "go";
|
|
45
|
+
if (hasNode && !hasGo)
|
|
46
|
+
return "node";
|
|
47
|
+
if (hasGo && hasNode)
|
|
48
|
+
return "go"; // both at root — prefer the more specific toolchain.
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
async function detectFromShallowScan(dir) {
|
|
52
|
+
let hasGo = false;
|
|
53
|
+
let hasNode = false;
|
|
54
|
+
try {
|
|
55
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (!entry.isDirectory())
|
|
58
|
+
continue;
|
|
59
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor")
|
|
60
|
+
continue;
|
|
61
|
+
const sub = path.join(dir, entry.name);
|
|
62
|
+
if (!hasGo && (await exists(path.join(sub, "go.mod"))))
|
|
63
|
+
hasGo = true;
|
|
64
|
+
if (!hasNode && (await exists(path.join(sub, "package.json"))))
|
|
65
|
+
hasNode = true;
|
|
66
|
+
if (hasGo && hasNode)
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// readdir failed — fall through with whatever we already have.
|
|
72
|
+
}
|
|
73
|
+
if (hasGo && !hasNode)
|
|
74
|
+
return "go";
|
|
75
|
+
if (hasNode && !hasGo)
|
|
76
|
+
return "node";
|
|
77
|
+
if (hasGo)
|
|
78
|
+
return "go";
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
export async function resolveProjectType(repoId, dir, changes) {
|
|
82
|
+
const repos = (configStore.get().repos ?? []);
|
|
83
|
+
const cfg = repos.find((r) => r.id === repoId);
|
|
84
|
+
const configured = cfg?.projectType;
|
|
85
|
+
if (configured && configured !== "auto")
|
|
86
|
+
return configured;
|
|
87
|
+
// Auto-attribution already filtered out misattributed changes upstream,
|
|
88
|
+
// so the changeset here genuinely reflects this repo's diff. Trust it.
|
|
89
|
+
return (detectFromChanges(changes)
|
|
90
|
+
?? (await detectFromRoot(dir))
|
|
91
|
+
?? (await detectFromShallowScan(dir))
|
|
92
|
+
?? "node");
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=projectType.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectType.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/projectType.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go runner: emits Go-canonical step names so the UI shows what's actually
|
|
3
|
+
* being run instead of forcing Node-isms onto the toolchain.
|
|
4
|
+
*
|
|
5
|
+
* fmt → go fmt ./... (rewrites .go files; readback feeds the diff)
|
|
6
|
+
* vet → go vet ./... (replaced by golangci-lint when installed)
|
|
7
|
+
* build → go build ./... (compile + type check)
|
|
8
|
+
* test → go test -race -count=1 ./...
|
|
9
|
+
*
|
|
10
|
+
* Module location: `go` commands need to run inside the directory containing
|
|
11
|
+
* the relevant go.mod. Monorepos commonly nest the module under e.g.
|
|
12
|
+
* `services/api/go.mod`, so we walk up from a changed .go file (and shallow-
|
|
13
|
+
* scan the clone) to find it. Multi-module repos with several go.mod files
|
|
14
|
+
* are out of scope for v1 — we use whichever module the changeset hits first.
|
|
15
|
+
*/
|
|
16
|
+
import type { ForgeChange, ForgeEvent } from "../../types.ts";
|
|
17
|
+
import type { StepResult } from "../cmd.ts";
|
|
18
|
+
export declare const STEP_ORDER: string[];
|
|
19
|
+
export declare const FIXABLE_STEPS: string[];
|
|
20
|
+
export declare function runGoRepo(cloneDir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<Record<string, StepResult>>;
|
|
21
|
+
//# sourceMappingURL=go.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"go.d.ts","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/go.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAK5C,eAAO,MAAM,UAAU,UAAiE,CAAC;AACzF,eAAO,MAAM,aAAa,UAA2B,CAAC;AAkDtD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,uCA+B1G"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go runner: emits Go-canonical step names so the UI shows what's actually
|
|
3
|
+
* being run instead of forcing Node-isms onto the toolchain.
|
|
4
|
+
*
|
|
5
|
+
* fmt → go fmt ./... (rewrites .go files; readback feeds the diff)
|
|
6
|
+
* vet → go vet ./... (replaced by golangci-lint when installed)
|
|
7
|
+
* build → go build ./... (compile + type check)
|
|
8
|
+
* test → go test -race -count=1 ./...
|
|
9
|
+
*
|
|
10
|
+
* Module location: `go` commands need to run inside the directory containing
|
|
11
|
+
* the relevant go.mod. Monorepos commonly nest the module under e.g.
|
|
12
|
+
* `services/api/go.mod`, so we walk up from a changed .go file (and shallow-
|
|
13
|
+
* scan the clone) to find it. Multi-module repos with several go.mod files
|
|
14
|
+
* are out of scope for v1 — we use whichever module the changeset hits first.
|
|
15
|
+
*/
|
|
16
|
+
import fs from "node:fs/promises";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { runCmd, runStep } from "../cmd.js";
|
|
19
|
+
import { exists } from "../fs.js";
|
|
20
|
+
import { readbackFormatted } from "../overlay.js";
|
|
21
|
+
// Public step contract for the orchestrator → frontend handshake.
|
|
22
|
+
export const STEP_ORDER = ["clone", "install", "overlay", "fmt", "vet", "build", "test"];
|
|
23
|
+
export const FIXABLE_STEPS = ["vet", "build", "test"];
|
|
24
|
+
async function which(bin) {
|
|
25
|
+
const r = await runCmd("which", [bin]);
|
|
26
|
+
return r.code === 0;
|
|
27
|
+
}
|
|
28
|
+
async function findGoModDir(cloneDir, changes) {
|
|
29
|
+
// Walk up from the first changed .go file looking for the nearest go.mod.
|
|
30
|
+
const goChange = (changes ?? []).find((c) => path.extname(c.file ?? "").toLowerCase() === ".go");
|
|
31
|
+
if (goChange) {
|
|
32
|
+
let cur = path.dirname(path.join(cloneDir, goChange.file));
|
|
33
|
+
while (cur.startsWith(cloneDir)) {
|
|
34
|
+
if (await exists(path.join(cur, "go.mod")))
|
|
35
|
+
return cur;
|
|
36
|
+
const parent = path.dirname(cur);
|
|
37
|
+
if (parent === cur)
|
|
38
|
+
break;
|
|
39
|
+
cur = parent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (await exists(path.join(cloneDir, "go.mod")))
|
|
43
|
+
return cloneDir;
|
|
44
|
+
// Shallow scan for monorepos with no .go changes (e.g. an MR that only
|
|
45
|
+
// touches a config file but the module is in a subdir).
|
|
46
|
+
try {
|
|
47
|
+
const entries = await fs.readdir(cloneDir, { withFileTypes: true });
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory())
|
|
50
|
+
continue;
|
|
51
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor")
|
|
52
|
+
continue;
|
|
53
|
+
const sub = path.join(cloneDir, entry.name);
|
|
54
|
+
if (await exists(path.join(sub, "go.mod")))
|
|
55
|
+
return sub;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// fall through
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
async function ensureInstalled(goDir, emit) {
|
|
64
|
+
emit({ type: "progress", step: "install", status: "running" });
|
|
65
|
+
emit({ type: "log", step: "install", message: "Running go mod download…" });
|
|
66
|
+
const r = await runCmd("go", ["mod", "download"], { cwd: goDir }, (l) => emit({ type: "log", step: "install", message: l }));
|
|
67
|
+
if (r.code !== 0)
|
|
68
|
+
throw new Error(`go mod download failed (code ${r.code})`);
|
|
69
|
+
emit({ type: "step_result", step: "install", passed: true });
|
|
70
|
+
}
|
|
71
|
+
export async function runGoRepo(cloneDir, changes, emit) {
|
|
72
|
+
const goDir = await findGoModDir(cloneDir, changes);
|
|
73
|
+
if (!goDir) {
|
|
74
|
+
throw new Error("Couldn't locate a go.mod in the clone — repo doesn't look like a Go module.");
|
|
75
|
+
}
|
|
76
|
+
const rel = path.relative(cloneDir, goDir) || ".";
|
|
77
|
+
if (rel !== ".")
|
|
78
|
+
emit({ type: "log", step: "overlay", message: `Go module rooted at ${rel}` });
|
|
79
|
+
await ensureInstalled(goDir, emit);
|
|
80
|
+
const results = {};
|
|
81
|
+
results.fmt = await runStep(goDir, "fmt", "go", ["fmt", "./..."], emit);
|
|
82
|
+
if (results.fmt.passed)
|
|
83
|
+
await readbackFormatted(cloneDir, changes, emit);
|
|
84
|
+
// `go vet` is the baseline. If the repo (or developer) has golangci-lint
|
|
85
|
+
// installed, prefer it — it covers gofmt, vet, staticcheck, errcheck, and
|
|
86
|
+
// more in a single pass and is the de-facto community standard.
|
|
87
|
+
if (await which("golangci-lint")) {
|
|
88
|
+
results.vet = await runStep(goDir, "vet", "golangci-lint", ["run", "./..."], emit);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
results.vet = await runStep(goDir, "vet", "go", ["vet", "./..."], emit);
|
|
92
|
+
}
|
|
93
|
+
results.build = await runStep(goDir, "build", "go", ["build", "./..."], emit);
|
|
94
|
+
// -race catches data races; -count=1 disables the test cache so we always
|
|
95
|
+
// re-execute against the overlayed source. Both are standard Go CI practice.
|
|
96
|
+
results.test = await runStep(goDir, "test", "go", ["test", "-race", "-count=1", "./..."], emit);
|
|
97
|
+
return results;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=go.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"go.js","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/go.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node runner: `npm ci` (lockfile-hash cached) followed by the standard
|
|
3
|
+
* format / lint / typecheck / test scripts. Missing scripts are skipped, not
|
|
4
|
+
* failed. The formatter step is read back so its rewrites flow into the
|
|
5
|
+
* proposal the user commits.
|
|
6
|
+
*/
|
|
7
|
+
import type { ForgeChange, ForgeEvent } from "../../types.ts";
|
|
8
|
+
import type { StepResult } from "../cmd.ts";
|
|
9
|
+
export declare const STEP_ORDER: string[];
|
|
10
|
+
export declare const FIXABLE_STEPS: string[];
|
|
11
|
+
export declare function runNodeRepo(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<Record<string, StepResult>>;
|
|
12
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAU5C,eAAO,MAAM,UAAU,UAAyF,CAAC;AACjH,eAAO,MAAM,aAAa,UAAgC,CAAC;AAuC3D,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,uCAsBvG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node runner: `npm ci` (lockfile-hash cached) followed by the standard
|
|
3
|
+
* format / lint / typecheck / test scripts. Missing scripts are skipped, not
|
|
4
|
+
* failed. The formatter step is read back so its rewrites flow into the
|
|
5
|
+
* proposal the user commits.
|
|
6
|
+
*/
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import fs from "node:fs/promises";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { runCmd, runStep } from "../cmd.js";
|
|
11
|
+
import { exists } from "../fs.js";
|
|
12
|
+
import { readbackFormatted } from "../overlay.js";
|
|
13
|
+
import { runTddPrecheckNode } from "../tdd.js";
|
|
14
|
+
const SCRIPTS = ["format", "lint", "typecheck", "test"];
|
|
15
|
+
// Public step contract for the orchestrator → frontend handshake. `tdd_precheck`
|
|
16
|
+
// sits before `test`; it only actually runs (and appears in the UI) when the
|
|
17
|
+
// proposal bundles new test files with impl changes — see runTddPrecheckNode.
|
|
18
|
+
export const STEP_ORDER = ["clone", "install", "overlay", "format", "lint", "typecheck", "tdd_precheck", "test"];
|
|
19
|
+
export const FIXABLE_STEPS = ["lint", "typecheck", "test"];
|
|
20
|
+
async function readPackageScripts(dir) {
|
|
21
|
+
try {
|
|
22
|
+
const raw = await fs.readFile(path.join(dir, "package.json"), "utf8");
|
|
23
|
+
return JSON.parse(raw).scripts ?? {};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function ensureInstalled(dir, emit) {
|
|
30
|
+
const lockPath = path.join(dir, "package-lock.json");
|
|
31
|
+
if (!(await exists(lockPath)))
|
|
32
|
+
return; // not an npm-managed project — omit the row entirely.
|
|
33
|
+
const hashPath = path.join(dir, "node_modules", ".forge-lock-hash");
|
|
34
|
+
const lockContent = await fs.readFile(lockPath);
|
|
35
|
+
const lockHash = crypto.createHash("sha256").update(lockContent).digest("hex");
|
|
36
|
+
if (await exists(hashPath)) {
|
|
37
|
+
const stored = (await fs.readFile(hashPath, "utf8")).trim();
|
|
38
|
+
if (stored === lockHash)
|
|
39
|
+
return; // node_modules already matches; nothing to install.
|
|
40
|
+
}
|
|
41
|
+
emit({ type: "progress", step: "install", status: "running" });
|
|
42
|
+
emit({ type: "log", step: "install", message: "Running npm ci (first run for this lockfile may take a minute)…" });
|
|
43
|
+
const install = await runCmd("npm", ["ci", "--prefer-offline", "--no-audit", "--no-fund"], { cwd: dir }, (l) => emit({ type: "log", step: "install", message: l }));
|
|
44
|
+
if (install.code !== 0) {
|
|
45
|
+
throw new Error(`npm ci failed (code ${install.code})`);
|
|
46
|
+
}
|
|
47
|
+
await fs.mkdir(path.dirname(hashPath), { recursive: true });
|
|
48
|
+
await fs.writeFile(hashPath, lockHash);
|
|
49
|
+
emit({ type: "step_result", step: "install", passed: true });
|
|
50
|
+
}
|
|
51
|
+
export async function runNodeRepo(dir, changes, emit) {
|
|
52
|
+
await ensureInstalled(dir, emit);
|
|
53
|
+
const scripts = await readPackageScripts(dir);
|
|
54
|
+
const results = {};
|
|
55
|
+
// Only run (and thus only render) scripts the project actually defines —
|
|
56
|
+
// missing scripts are omitted from the UI rather than shown as "skipped".
|
|
57
|
+
for (const s of SCRIPTS) {
|
|
58
|
+
if (!scripts[s])
|
|
59
|
+
continue;
|
|
60
|
+
// TDD precheck runs only when the `test` script exists, a new test file
|
|
61
|
+
// is part of the proposal, and there's impl code to revert. It records
|
|
62
|
+
// its own step_result under `tdd_precheck`.
|
|
63
|
+
if (s === "test") {
|
|
64
|
+
const precheck = await runTddPrecheckNode(dir, changes, emit);
|
|
65
|
+
if (precheck)
|
|
66
|
+
results.tdd_precheck = precheck;
|
|
67
|
+
}
|
|
68
|
+
results[s] = await runStep(dir, s, "npm", ["run", "--silent", s], emit);
|
|
69
|
+
if (s === "format" && results[s].passed) {
|
|
70
|
+
await readbackFormatted(dir, changes, emit);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TDD precheck: when a proposal adds *new* test files alongside impl changes,
|
|
3
|
+
* run just those tests with the impl reverted. If they pass, the tests don't
|
|
4
|
+
* actually exercise the new behavior — that's the gap we surface.
|
|
5
|
+
*
|
|
6
|
+
* Only fires when all three hold:
|
|
7
|
+
* - at least one new test file (action === "create" on a test path)
|
|
8
|
+
* - at least one non-test change (impl)
|
|
9
|
+
* - the project has a `test` script
|
|
10
|
+
*
|
|
11
|
+
* The precheck reverts impl files via git (or `rm` for untracked creates),
|
|
12
|
+
* runs vitest scoped to the new test paths, then re-overlays impl so the
|
|
13
|
+
* downstream `test` step sees the full proposal.
|
|
14
|
+
*/
|
|
15
|
+
import type { ForgeChange, ForgeEvent } from "../types.ts";
|
|
16
|
+
export declare function isNodeTestFile(filePath: string): boolean;
|
|
17
|
+
export declare function partitionChangesNode(changes: ForgeChange[]): {
|
|
18
|
+
impl: ForgeChange[];
|
|
19
|
+
newTests: ForgeChange[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* @returns {Promise<null|{passed: boolean, exitCode: number}>} null when skipped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function runTddPrecheckNode(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<{
|
|
25
|
+
passed: boolean;
|
|
26
|
+
exitCode: number;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
tail: string;
|
|
29
|
+
}>;
|
|
30
|
+
//# sourceMappingURL=tdd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tdd.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/tdd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK3D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,WAE9C;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE;;;EAW1D;AA0BD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;;;;;GAsC9G"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TDD precheck: when a proposal adds *new* test files alongside impl changes,
|
|
3
|
+
* run just those tests with the impl reverted. If they pass, the tests don't
|
|
4
|
+
* actually exercise the new behavior — that's the gap we surface.
|
|
5
|
+
*
|
|
6
|
+
* Only fires when all three hold:
|
|
7
|
+
* - at least one new test file (action === "create" on a test path)
|
|
8
|
+
* - at least one non-test change (impl)
|
|
9
|
+
* - the project has a `test` script
|
|
10
|
+
*
|
|
11
|
+
* The precheck reverts impl files via git (or `rm` for untracked creates),
|
|
12
|
+
* runs vitest scoped to the new test paths, then re-overlays impl so the
|
|
13
|
+
* downstream `test` step sees the full proposal.
|
|
14
|
+
*/
|
|
15
|
+
import fs from "node:fs/promises";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { runCmd } from "./cmd.js";
|
|
18
|
+
const TEST_FILE_RE_NODE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
19
|
+
export function isNodeTestFile(filePath) {
|
|
20
|
+
return TEST_FILE_RE_NODE.test(filePath);
|
|
21
|
+
}
|
|
22
|
+
export function partitionChangesNode(changes) {
|
|
23
|
+
const impl = [];
|
|
24
|
+
const newTests = [];
|
|
25
|
+
for (const c of changes ?? []) {
|
|
26
|
+
if (isNodeTestFile(c.file)) {
|
|
27
|
+
if (c.action === "create")
|
|
28
|
+
newTests.push(c);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
impl.push(c);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { impl, newTests };
|
|
35
|
+
}
|
|
36
|
+
async function revertImplOverlay(dir, impl) {
|
|
37
|
+
for (const c of impl) {
|
|
38
|
+
const full = path.join(dir, c.file);
|
|
39
|
+
if (c.action === "create") {
|
|
40
|
+
await fs.rm(full, { force: true });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// modify or delete — restore the pre-overlay content from HEAD.
|
|
44
|
+
await runCmd("git", ["checkout", "--", c.file], { cwd: dir }, undefined);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function reapplyImplOverlay(dir, impl) {
|
|
49
|
+
for (const c of impl) {
|
|
50
|
+
const full = path.join(dir, c.file);
|
|
51
|
+
if (c.action === "delete") {
|
|
52
|
+
await fs.rm(full, { force: true });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
56
|
+
await fs.writeFile(full, c.proposed);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @returns {Promise<null|{passed: boolean, exitCode: number}>} null when skipped.
|
|
62
|
+
*/
|
|
63
|
+
export async function runTddPrecheckNode(dir, changes, emit) {
|
|
64
|
+
const { impl, newTests } = partitionChangesNode(changes);
|
|
65
|
+
if (newTests.length === 0 || impl.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
const testPaths = newTests.map((c) => c.file);
|
|
68
|
+
emit({ type: "progress", step: "tdd_precheck", status: "running" });
|
|
69
|
+
emit({
|
|
70
|
+
type: "log",
|
|
71
|
+
step: "tdd_precheck",
|
|
72
|
+
message: `Verifying ${newTests.length} new test file(s) fail without impl: ${testPaths.join(", ")}`,
|
|
73
|
+
});
|
|
74
|
+
await revertImplOverlay(dir, impl);
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const res = await runCmd("npx", ["vitest", "--run", "--reporter=default", ...testPaths], { cwd: dir }, (l) => emit({ type: "log", step: "tdd_precheck", message: l }));
|
|
77
|
+
await reapplyImplOverlay(dir, impl);
|
|
78
|
+
// TDD contract: without the impl, the new tests must fail (non-zero exit).
|
|
79
|
+
// Exit 0 means either the tests passed without the impl — they don't
|
|
80
|
+
// exercise the new behavior — or vitest found nothing to run.
|
|
81
|
+
const precheckPassed = res.code !== 0;
|
|
82
|
+
const tail = res.output.split("\n").slice(-60).join("\n");
|
|
83
|
+
emit({
|
|
84
|
+
type: "step_result",
|
|
85
|
+
step: "tdd_precheck",
|
|
86
|
+
passed: precheckPassed,
|
|
87
|
+
exitCode: res.code,
|
|
88
|
+
durationMs: Date.now() - start,
|
|
89
|
+
tail,
|
|
90
|
+
message: precheckPassed
|
|
91
|
+
? `${newTests.length} new test(s) failed without implementation (good TDD).`
|
|
92
|
+
: `${newTests.length} new test(s) passed without the implementation change — they may not actually test the fix.`,
|
|
93
|
+
});
|
|
94
|
+
return { passed: precheckPassed, exitCode: res.code, durationMs: Date.now() - start, tail };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=tdd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tdd.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/tdd.ts"],"names":[],"mappings":"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"}
|