forgeos 0.1.0-alpha.25 → 0.1.0-alpha.27
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/.npmignore +6 -0
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +25 -0
- package/bin/forge.mjs +0 -0
- package/docs/changelog.md +22 -0
- package/package.json +7 -1
- package/src/forge/_generated/releaseManifest.json +1 -1
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/agent-memory/bridge.ts +4 -1
- package/src/forge/cli/changed.ts +32 -26
- package/src/forge/cli/handoff.ts +5 -2
- package/src/forge/cli/output.ts +1 -1
- package/src/forge/compiler/agent-contract/build.ts +50 -0
- package/src/forge/compiler/integration/add.ts +38 -1
- package/src/forge/compiler/integration/plan.ts +2 -11
- package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
- package/src/forge/compiler/orchestrator/manifest.ts +1 -1
- package/src/forge/compiler/orchestrator/plan.ts +163 -1
- package/src/forge/compiler/orchestrator/types.ts +1 -1
- package/src/forge/delta/recorder.ts +1 -1
- package/src/forge/delta/store.ts +36 -3
- package/src/forge/make/index.ts +11 -2
- package/src/forge/runtime/executor.ts +18 -15
- package/src/forge/version.ts +1 -1
- package/src/forge/workspace/change-summary.ts +34 -0
- package/src/forge/workspace/git-summary.ts +57 -8
- package/adapters/java/target/classes/dev/forgeos/adapter/Auth.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Diagnostic.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Entry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/EntryKind.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ErrorInfo.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Forge.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeCall.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeContext.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHandler.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHttpHandler.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$EntryOption.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegisteredEntry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegistryOption.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Json.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Manifest.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/RequestEnvelope.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ResponseEnvelope.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Risk.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Schemas.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Service.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/TransactionMode.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/TypedForgeHandler.class +0 -0
- package/adapters/java/target/forge-java-adapter-0.1.0-alpha.11.jar +0 -0
- package/adapters/java/target/maven-archiver/pom.properties +0 -3
- package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +0 -23
- package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +0 -20
- package/adapters/java-spring-boot-starter/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +0 -1
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeCommand.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeExternalService.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeQuery.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringRuntime.class +0 -0
- package/adapters/java-spring-boot-starter/target/forge-java-spring-boot-starter-0.1.0-alpha.11.jar +0 -0
- package/adapters/java-spring-boot-starter/target/maven-archiver/pom.properties +0 -3
- package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +0 -6
- package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +0 -6
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/CreateInvoiceInput.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Invoice.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$EmptyInput.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$Options.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main.class +0 -0
- package/examples/java-billing/target/java-billing-0.1.0-alpha.11-all.jar +0 -0
- package/examples/java-billing/target/java-billing-0.1.0-alpha.11.jar +0 -0
- package/examples/java-billing/target/maven-archiver/pom.properties +0 -3
- package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +0 -5
- package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +0 -3
package/.npmignore
CHANGED
package/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.27 input=6c5ffcc15b409d33158dc8e5a5c4684c2d59b0e31e7c9064bdb6dacbe95ac874 content=0d493cf0e41b71cb652d5e0e1b0c1f83d2a1281b748321f0b00f0773ba93074e
|
|
2
2
|
# AGENTS.md
|
|
3
3
|
|
|
4
4
|
<!-- forge-generated:start -->
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# forgeos
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.27
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Stabilize `forge add convex` and generated integration artifacts.
|
|
8
|
+
|
|
9
|
+
- Re-emit integration adapters, docs, and testkits from the main generator so `forge generate --check` and `forge verify --smoke` stay clean after `forge add`.
|
|
10
|
+
- Keep partial `forge add` plans from deleting unrelated generated files before the full generator can reconcile the workspace.
|
|
11
|
+
- Include changed package-manager files such as `package.json` and lockfiles in `forge add --json` handoffs.
|
|
12
|
+
- Replace stale fast-check manifest hashes instead of merging them, and invalidate old manifest schemas to avoid phantom generated drift.
|
|
13
|
+
- Skip Bun module mock registration when the active Bun runtime does not expose `Bun.mock.module`, while still applying mock secret env vars.
|
|
14
|
+
- Filter generated and operational filesystem noise from `forge changed --authored` in non-git workspaces.
|
|
15
|
+
- Keep Java build outputs such as `target/`, `.class`, and `.jar` files out of the published npm tarball.
|
|
16
|
+
|
|
17
|
+
## 0.1.0-alpha.26
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Harden the field-demo loop after the Team Onboarding app exercise.
|
|
22
|
+
|
|
23
|
+
- Let `forge changed` and `forge handoff` summarize non-git workspaces with a filesystem inventory instead of reporting zero useful changes.
|
|
24
|
+
- Keep `forge make resource` global by default unless a tenants table exists or `--tenant-scoped` is explicit.
|
|
25
|
+
- Expand capability-map table detection for aliased `ctx.db` usage.
|
|
26
|
+
- Wait through short-lived DeltaDB writer locks before reporting `FORGE_DELTA_BUSY`.
|
|
27
|
+
|
|
3
28
|
## Unreleased
|
|
4
29
|
|
|
5
30
|
## 0.1.0-alpha.25
|
package/bin/forge.mjs
CHANGED
|
File without changes
|
package/docs/changelog.md
CHANGED
|
@@ -6,6 +6,28 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.0-alpha.27
|
|
10
|
+
|
|
11
|
+
- Stabilized the `forge add convex` loop: integration docs/testkits are now
|
|
12
|
+
re-emitted by the main generator, partial `forge add` plans no longer remove
|
|
13
|
+
unrelated generated files, stale fast-check manifest hashes are pruned instead
|
|
14
|
+
of merged, and `forge add --json` includes changed package-manager files.
|
|
15
|
+
- Made runtime mock mode tolerate Bun builds without `Bun.mock.module` while
|
|
16
|
+
still applying mock secret environment variables.
|
|
17
|
+
- Filtered generated and operational filesystem noise from
|
|
18
|
+
`forge changed --authored` in non-git workspaces.
|
|
19
|
+
- Excluded Java build outputs such as `target/`, `.class`, and `.jar` files
|
|
20
|
+
from the published npm tarball.
|
|
21
|
+
|
|
22
|
+
## 0.1.0-alpha.26
|
|
23
|
+
|
|
24
|
+
- Hardened the field-demo loop after the Team Onboarding app exercise:
|
|
25
|
+
non-git workspaces now get a filesystem-backed `changed`/`handoff` summary,
|
|
26
|
+
`forge make resource` stays global unless tenant scope is explicit or already
|
|
27
|
+
modeled, capability-map extraction sees aliased `ctx.db` table usage, and
|
|
28
|
+
Agent Memory waits through short-lived DeltaDB writer locks before reporting
|
|
29
|
+
`FORGE_DELTA_BUSY`.
|
|
30
|
+
|
|
9
31
|
## 0.1.0-alpha.25
|
|
10
32
|
|
|
11
33
|
- Hardened DeltaDB and Agent Memory for concurrent `forge dev` usage:
|
package/package.json
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgeos",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.27",
|
|
4
4
|
"description": "Agent-native application framework and compiler for building Forge apps without a mandatory dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"files": [
|
|
8
8
|
"adapters/",
|
|
9
|
+
"!adapters/**/target/**",
|
|
10
|
+
"!adapters/**/*.class",
|
|
11
|
+
"!adapters/**/*.jar",
|
|
9
12
|
"bin/",
|
|
10
13
|
"docs/cair-protocol.md",
|
|
11
14
|
"docs/forge-protocol.md",
|
|
12
15
|
"examples/go-billing/",
|
|
13
16
|
"examples/java-billing/",
|
|
17
|
+
"!examples/**/target/**",
|
|
18
|
+
"!examples/**/*.class",
|
|
19
|
+
"!examples/**/*.jar",
|
|
14
20
|
"packages/eslint-plugin-forge/",
|
|
15
21
|
"schemas/",
|
|
16
22
|
"src/",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.
|
|
1
|
+
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.27","releaseId":"forgeos@0.1.0-alpha.27+unknown","schemaVersion":"0.1.0"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.27 input=6c5ffcc15b409d33158dc8e5a5c4684c2d59b0e31e7c9064bdb6dacbe95ac874 content=3a34de2474ecf200ab734c0a01f9dd2c6a780af76bd650d3238c8b46313542b3
|
|
2
2
|
export const releaseManifest = {
|
|
3
3
|
"defaultProvider": "local",
|
|
4
4
|
"diagnostics": [],
|
|
@@ -19,7 +19,7 @@ export const releaseManifest = {
|
|
|
19
19
|
"custom"
|
|
20
20
|
],
|
|
21
21
|
"packageName": "forgeos",
|
|
22
|
-
"packageVersion": "0.1.0-alpha.
|
|
23
|
-
"releaseId": "forgeos@0.1.0-alpha.
|
|
22
|
+
"packageVersion": "0.1.0-alpha.27",
|
|
23
|
+
"releaseId": "forgeos@0.1.0-alpha.27+unknown",
|
|
24
24
|
"schemaVersion": "0.1.0"
|
|
25
25
|
} as const;
|
|
@@ -116,7 +116,10 @@ async function openMemoryStore(
|
|
|
116
116
|
const retryDelays = access === "write" ? [25, 75, 150] : [];
|
|
117
117
|
for (let attempt = 0; ; attempt += 1) {
|
|
118
118
|
try {
|
|
119
|
-
return await DeltaStore.open(workspaceRoot, {
|
|
119
|
+
return await DeltaStore.open(workspaceRoot, {
|
|
120
|
+
access,
|
|
121
|
+
...(access === "write" ? { waitMs: 1_500, retryDelayMs: 50 } : {}),
|
|
122
|
+
});
|
|
120
123
|
} catch (error) {
|
|
121
124
|
if (!(error instanceof DeltaStoreBusyError) || attempt >= retryDelays.length) {
|
|
122
125
|
return memoryUnavailable(error, workspaceRoot);
|
package/src/forge/cli/changed.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { CategorizedFileSummary, DiffPlan } from "../workspace/change-summary.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
buildDiffPlanFromChangeSummary,
|
|
4
|
+
filterCategorizedSummary,
|
|
5
|
+
summarizeChangeTypes,
|
|
6
|
+
} from "../workspace/change-summary.ts";
|
|
3
7
|
import { buildWorkspaceGitSummary, type WorkspaceGitSummary } from "../workspace/git-summary.ts";
|
|
4
8
|
|
|
5
9
|
export interface ChangedCommandResult {
|
|
@@ -39,6 +43,8 @@ interface GeneratedChangeExplanation {
|
|
|
39
43
|
summary: string;
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
const AUTHORED_CHANGE_TYPES = ["source", "tests", "docs", "config", "assets", "other"] as const;
|
|
47
|
+
|
|
42
48
|
function emptyCategory(summary: CategorizedFileSummary, category: keyof CategorizedFileSummary["byType"]): boolean {
|
|
43
49
|
return summary.byType[category].count === 0;
|
|
44
50
|
}
|
|
@@ -82,8 +88,7 @@ function buildRisks(git: WorkspaceGitSummary): string[] {
|
|
|
82
88
|
const risks: string[] = [];
|
|
83
89
|
const changed = git.changeSummary.changed;
|
|
84
90
|
if (!git.available) {
|
|
85
|
-
risks.push("git status is unavailable;
|
|
86
|
-
return risks;
|
|
91
|
+
risks.push("git status is unavailable; using filesystem inventory as untracked-file analysis");
|
|
87
92
|
}
|
|
88
93
|
if (git.untracked.count > 0) {
|
|
89
94
|
risks.push(`${git.untracked.count} untracked file(s) are not in git history`);
|
|
@@ -103,7 +108,7 @@ function buildRisks(git: WorkspaceGitSummary): string[] {
|
|
|
103
108
|
|
|
104
109
|
function buildRecommendedCommands(git: WorkspaceGitSummary): string[] {
|
|
105
110
|
if (!git.available) {
|
|
106
|
-
return ["
|
|
111
|
+
return ["forge status --json", "forge handoff --json", "git init"];
|
|
107
112
|
}
|
|
108
113
|
if (git.changeSummary.changed.total.count === 0) {
|
|
109
114
|
return ["forge status --json", "forge dev --once --json"];
|
|
@@ -184,54 +189,55 @@ export function runChangedCommand(workspaceRoot: string, options: { authoredOnly
|
|
|
184
189
|
const changed = git.changeSummary.changed;
|
|
185
190
|
const humanChanges = selectHumanChangeSummary(changed);
|
|
186
191
|
const derivedChanges = selectDerivedChangeSummary(changed);
|
|
187
|
-
const authoredChanged
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
generated: { count: 0, sample: [], hidden: 0 },
|
|
193
|
-
},
|
|
194
|
-
primaryTypes: changed.primaryTypes.filter((type) => type !== "generated"),
|
|
195
|
-
};
|
|
192
|
+
const authoredChanged = filterCategorizedSummary(changed, [...AUTHORED_CHANGE_TYPES]);
|
|
193
|
+
const authoredStaged = filterCategorizedSummary(git.changeSummary.staged, [...AUTHORED_CHANGE_TYPES]);
|
|
194
|
+
const authoredUnstaged = filterCategorizedSummary(git.changeSummary.unstaged, [...AUTHORED_CHANGE_TYPES]);
|
|
195
|
+
const authoredUntracked = filterCategorizedSummary(git.changeSummary.untracked, [...AUTHORED_CHANGE_TYPES]);
|
|
196
|
+
const viewHumanChanges = options.authoredOnly ? selectHumanChangeSummary(authoredChanged) : humanChanges;
|
|
196
197
|
const viewChanged = options.authoredOnly ? authoredChanged : changed;
|
|
198
|
+
const viewStaged = options.authoredOnly ? authoredStaged : git.changeSummary.staged;
|
|
199
|
+
const viewUnstaged = options.authoredOnly ? authoredUnstaged : git.changeSummary.unstaged;
|
|
200
|
+
const viewUntracked = options.authoredOnly ? authoredUntracked : git.changeSummary.untracked;
|
|
197
201
|
const viewDerivedChanges: DerivedChangeSummary = options.authoredOnly
|
|
198
202
|
? { total: 0, generated: { count: 0, sample: [], hidden: 0 } }
|
|
199
203
|
: derivedChanges;
|
|
200
204
|
const risks = buildRisks(git);
|
|
201
205
|
const recommendedCommands = buildRecommendedCommands(git);
|
|
202
|
-
const reviewFocus = buildReviewFocus(
|
|
203
|
-
const generatedExplanation = buildGeneratedChangeExplanation(
|
|
206
|
+
const reviewFocus = buildReviewFocus(viewHumanChanges, viewDerivedChanges);
|
|
207
|
+
const generatedExplanation = buildGeneratedChangeExplanation(viewHumanChanges, viewDerivedChanges);
|
|
204
208
|
const diffPlan: DiffPlan = buildDiffPlanFromChangeSummary(viewChanged);
|
|
209
|
+
const ok = git.available || git.source === "filesystem";
|
|
205
210
|
|
|
206
211
|
return {
|
|
207
|
-
ok
|
|
212
|
+
ok,
|
|
208
213
|
data: {
|
|
209
214
|
schemaVersion: "0.1.0",
|
|
210
|
-
ok
|
|
215
|
+
ok,
|
|
211
216
|
summary: {
|
|
212
217
|
branch: git.branch,
|
|
213
218
|
commit: git.commit,
|
|
214
219
|
view: options.authoredOnly ? "authored" : "all",
|
|
215
220
|
changedFiles: viewChanged.total.count,
|
|
216
|
-
humanFiles:
|
|
221
|
+
humanFiles: viewHumanChanges.total,
|
|
217
222
|
generatedFiles: viewDerivedChanges.total,
|
|
218
|
-
stagedFiles:
|
|
219
|
-
unstagedFiles:
|
|
220
|
-
untrackedFiles:
|
|
223
|
+
stagedFiles: viewStaged.total.count,
|
|
224
|
+
unstagedFiles: viewUnstaged.total.count,
|
|
225
|
+
untrackedFiles: viewUntracked.total.count,
|
|
221
226
|
primaryTypes: viewChanged.primaryTypes,
|
|
222
227
|
changeTypes: summarizeChangeTypes(viewChanged),
|
|
223
228
|
},
|
|
224
229
|
git: {
|
|
225
230
|
available: git.available,
|
|
231
|
+
source: git.source,
|
|
226
232
|
...(git.error ? { error: git.error } : {}),
|
|
227
233
|
branch: git.branch,
|
|
228
234
|
commit: git.commit,
|
|
229
235
|
changed: viewChanged,
|
|
230
|
-
staged:
|
|
231
|
-
unstaged:
|
|
232
|
-
untracked:
|
|
236
|
+
staged: viewStaged,
|
|
237
|
+
unstaged: viewUnstaged,
|
|
238
|
+
untracked: viewUntracked,
|
|
233
239
|
},
|
|
234
|
-
humanChanges,
|
|
240
|
+
humanChanges: viewHumanChanges,
|
|
235
241
|
derivedChanges: viewDerivedChanges,
|
|
236
242
|
reviewFocus,
|
|
237
243
|
generatedExplanation,
|
|
@@ -240,7 +246,7 @@ export function runChangedCommand(workspaceRoot: string, options: { authoredOnly
|
|
|
240
246
|
recommendedCommands,
|
|
241
247
|
nextActions: recommendedCommands,
|
|
242
248
|
},
|
|
243
|
-
exitCode:
|
|
249
|
+
exitCode: ok ? 0 : 1,
|
|
244
250
|
};
|
|
245
251
|
}
|
|
246
252
|
|
package/src/forge/cli/handoff.ts
CHANGED
|
@@ -140,6 +140,7 @@ function buildOpeningBrief(input: {
|
|
|
140
140
|
}): string {
|
|
141
141
|
const agent = input.dev.summary.agentContext;
|
|
142
142
|
const changedByType = summarizeChangeTypes(input.git.changeSummary.changed);
|
|
143
|
+
const changedFiles = Math.max(agent.changedFiles, input.git.changed.count);
|
|
143
144
|
const tests = input.recentRuns.test
|
|
144
145
|
? input.recentRuns.test.ok
|
|
145
146
|
? "last test run passed"
|
|
@@ -150,7 +151,7 @@ function buildOpeningBrief(input: {
|
|
|
150
151
|
: "no blocking issues";
|
|
151
152
|
return [
|
|
152
153
|
`ForgeOS handoff: ${input.dev.ok ? "dev diagnostics are clean" : "dev diagnostics need attention"}.`,
|
|
153
|
-
`${
|
|
154
|
+
`${changedFiles} changed file(s)${changedByType ? `: ${changedByType}` : ""}; ${input.git.staged.count} staged, ${input.git.untracked.count} untracked.`,
|
|
154
155
|
`${tests}; ${blockers}.`,
|
|
155
156
|
`Next command: ${input.dev.summary.primaryAction?.command ?? input.dev.nextActions[0]?.command ?? "forge dev"}.`,
|
|
156
157
|
].join(" ");
|
|
@@ -168,6 +169,7 @@ export async function runHandoffCommand(options: HandoffCommandOptions): Promise
|
|
|
168
169
|
const agent = dev.summary.agentContext;
|
|
169
170
|
const risks = [
|
|
170
171
|
...agent.blockingIssues,
|
|
172
|
+
...(!git.available ? ["git status is unavailable; using filesystem inventory as untracked-file analysis"] : []),
|
|
171
173
|
...(git.untracked.count > 0 ? [`${git.untracked.count} untracked file(s) are not in git history`] : []),
|
|
172
174
|
...(recentRuns.test && !recentRuns.test.ok ? ["last test run failed"] : []),
|
|
173
175
|
...(recentRuns.ui && !recentRuns.ui.ok ? ["last UI run failed"] : []),
|
|
@@ -181,6 +183,7 @@ export async function runHandoffCommand(options: HandoffCommandOptions): Promise
|
|
|
181
183
|
agent.blockingIssues.length === 0 &&
|
|
182
184
|
(!recentRuns.test || recentRuns.test.ok) &&
|
|
183
185
|
(!recentRuns.ui || recentRuns.ui.ok);
|
|
186
|
+
const changedFiles = Math.max(agent.changedFiles, git.changed.count);
|
|
184
187
|
|
|
185
188
|
return {
|
|
186
189
|
schemaVersion: "0.1.0",
|
|
@@ -192,7 +195,7 @@ export async function runHandoffCommand(options: HandoffCommandOptions): Promise
|
|
|
192
195
|
generatedChanged: agent.generatedChanged,
|
|
193
196
|
generatedChangedFiles: agent.generatedChangedFiles,
|
|
194
197
|
frontendReady: agent.frontendReady,
|
|
195
|
-
changedFiles
|
|
198
|
+
changedFiles,
|
|
196
199
|
stagedFiles: git.staged.count,
|
|
197
200
|
unstagedFiles: git.unstaged.count,
|
|
198
201
|
untrackedFiles: git.untracked.count,
|
package/src/forge/cli/output.ts
CHANGED
|
@@ -157,11 +157,11 @@ export function buildAddJson(result: ForgeAddResult): Record<string, unknown> {
|
|
|
157
157
|
const integrationNextActions =
|
|
158
158
|
result.exitCode === 0 && result.mode === "integration" && result.targetKind === "forge-integration"
|
|
159
159
|
? [
|
|
160
|
+
"forge generate",
|
|
160
161
|
...(result.recipePackages ?? []).map((pkg) => `forge deps inspect ${pkg} --json`),
|
|
161
162
|
...((result.requiredSecrets?.length ?? 0) > 0 || (result.optionalSecrets?.length ?? 0) > 0
|
|
162
163
|
? ["forge secrets check --json", "forge inspect secrets --json"]
|
|
163
164
|
: []),
|
|
164
|
-
"forge generate",
|
|
165
165
|
"forge check --json",
|
|
166
166
|
"forge verify --smoke",
|
|
167
167
|
]
|
|
@@ -186,6 +186,38 @@ function sourceText(workspaceRoot: string, file: string | undefined): string {
|
|
|
186
186
|
return nodeFileSystem.readText(absolute) ?? "";
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
function escapeRegExp(value: string): string {
|
|
190
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function addDbAliasesForText(
|
|
194
|
+
text: string,
|
|
195
|
+
tableNames: Set<string>,
|
|
196
|
+
aliases: Map<string, string>,
|
|
197
|
+
): void {
|
|
198
|
+
for (const match of text.matchAll(
|
|
199
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*ctx\.db(?:\.([A-Za-z_$][A-Za-z0-9_$]*)|\[\s*["'`]([^"'`]+)["'`]\s*\])/g,
|
|
200
|
+
)) {
|
|
201
|
+
const alias = match[1] ?? "";
|
|
202
|
+
const table = match[2] ?? match[3] ?? "";
|
|
203
|
+
if (alias && tableNames.has(table)) {
|
|
204
|
+
aliases.set(alias, table);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const match of text.matchAll(/\b(?:const|let|var)\s*\{([^}]+)\}\s*=\s*ctx\.db/g)) {
|
|
209
|
+
const body = match[1] ?? "";
|
|
210
|
+
for (const part of body.split(",")) {
|
|
211
|
+
const [rawTable, rawAlias] = part.split(":").map((value) => value.trim());
|
|
212
|
+
const table = rawTable?.replace(/["'`]/g, "") ?? "";
|
|
213
|
+
const alias = (rawAlias ?? rawTable ?? "").replace(/\s*=.*$/, "").trim();
|
|
214
|
+
if (tableNames.has(table) && alias) {
|
|
215
|
+
aliases.set(alias, table);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
189
221
|
function dbTablesForText(
|
|
190
222
|
text: string,
|
|
191
223
|
tableNames: Set<string>,
|
|
@@ -199,6 +231,24 @@ function dbTablesForText(
|
|
|
199
231
|
tables.push(table);
|
|
200
232
|
}
|
|
201
233
|
}
|
|
234
|
+
for (const match of text.matchAll(/ctx\.db\s*\[\s*["'`]([^"'`]+)["'`]\s*\]\s*\.\s*([A-Za-z_$][A-Za-z0-9_$]*)/g)) {
|
|
235
|
+
const table = match[1] ?? "";
|
|
236
|
+
const op = match[2] ?? "";
|
|
237
|
+
if (tableNames.has(table) && ops.has(op)) {
|
|
238
|
+
tables.push(table);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const aliases = new Map<string, string>();
|
|
242
|
+
addDbAliasesForText(text, tableNames, aliases);
|
|
243
|
+
for (const [alias, table] of aliases) {
|
|
244
|
+
const aliasPattern = new RegExp(`\\b${escapeRegExp(alias)}\\s*\\.\\s*([A-Za-z_$][A-Za-z0-9_$]*)`, "g");
|
|
245
|
+
for (const match of text.matchAll(aliasPattern)) {
|
|
246
|
+
const op = match[1] ?? "";
|
|
247
|
+
if (ops.has(op)) {
|
|
248
|
+
tables.push(table);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
202
252
|
return uniqueSorted(tables);
|
|
203
253
|
}
|
|
204
254
|
|
|
@@ -344,6 +344,39 @@ function dependencyFromInstall(
|
|
|
344
344
|
};
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
function fileHashOrNull(path: string): string | null {
|
|
348
|
+
if (!nodeFileSystem.exists(path)) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
return hashStable(nodeFileSystem.readText(path) ?? "");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function snapshotPackageManagerFiles(
|
|
355
|
+
workspaceRoot: string,
|
|
356
|
+
pm: PackageManagerAdapter,
|
|
357
|
+
): Map<string, string | null> {
|
|
358
|
+
const paths = ["package.json", pm.lockfile];
|
|
359
|
+
return new Map(
|
|
360
|
+
paths.map((path) => [
|
|
361
|
+
path,
|
|
362
|
+
fileHashOrNull(join(workspaceRoot, path)),
|
|
363
|
+
]),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function changedPackageManagerFiles(
|
|
368
|
+
workspaceRoot: string,
|
|
369
|
+
before: Map<string, string | null>,
|
|
370
|
+
): string[] {
|
|
371
|
+
const changed: string[] = [];
|
|
372
|
+
for (const [path, previousHash] of before) {
|
|
373
|
+
if (fileHashOrNull(join(workspaceRoot, path)) !== previousHash) {
|
|
374
|
+
changed.push(path);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return changed.sort();
|
|
378
|
+
}
|
|
379
|
+
|
|
347
380
|
async function analyzeRecipePackages(
|
|
348
381
|
recipe: NonNullable<ReturnType<typeof resolveRecipe>>,
|
|
349
382
|
ctx: ReturnType<typeof discover>,
|
|
@@ -635,6 +668,7 @@ export async function forgeAdd(
|
|
|
635
668
|
const snapshot = snapshotVersionControlled(options.workspaceRoot);
|
|
636
669
|
|
|
637
670
|
try {
|
|
671
|
+
const packageManagerBefore = snapshotPackageManagerFiles(options.workspaceRoot, pm);
|
|
638
672
|
for (const pkg of recipe.packages) {
|
|
639
673
|
await pm.add(pkg.packageName, {
|
|
640
674
|
cwd: options.workspaceRoot,
|
|
@@ -735,7 +769,10 @@ export async function forgeAdd(
|
|
|
735
769
|
alias: normalized,
|
|
736
770
|
mode: "integration",
|
|
737
771
|
...recipeResultMetadata(recipe),
|
|
738
|
-
changed:
|
|
772
|
+
changed: [
|
|
773
|
+
...changedPackageManagerFiles(options.workspaceRoot, packageManagerBefore),
|
|
774
|
+
...emitResult.changed,
|
|
775
|
+
],
|
|
739
776
|
unchanged: emitResult.unchanged,
|
|
740
777
|
warnings: warningsCombined,
|
|
741
778
|
errors: [],
|
|
@@ -20,7 +20,6 @@ import { PACKAGE_ANALYZER_VERSION } from "../package-graph/constants.ts";
|
|
|
20
20
|
import { RECIPE_SCHEMA_VERSION } from "../recipes/definitions.ts";
|
|
21
21
|
import { hashStable } from "../primitives/hash.ts";
|
|
22
22
|
import { stableSortEmitFiles, stableSortStrings } from "../primitives/index.ts";
|
|
23
|
-
import { detectOrphanedGeneratedFiles } from "../orchestrator/orphans.ts";
|
|
24
23
|
import type { DiscoverContext } from "../orchestrator/types.ts";
|
|
25
24
|
import {
|
|
26
25
|
createRenderContext,
|
|
@@ -254,17 +253,9 @@ export function buildIntegrationEmitPlan(input: IntegrationPlanInput): EmitPlan
|
|
|
254
253
|
const lockEntry = buildLockEntry(recipe, classified, generatedFiles);
|
|
255
254
|
const lock = mergeLockPackages(input.existingLock, lockEntry, ctx);
|
|
256
255
|
|
|
257
|
-
const sortedFiles = stableSortEmitFiles(files);
|
|
258
|
-
const plannedPathSet = new Set(sortedFiles.map((file) => file.path));
|
|
259
|
-
const orphanedFiles = detectOrphanedGeneratedFiles(
|
|
260
|
-
ctx.workspaceRoot,
|
|
261
|
-
ctx.generatedDir,
|
|
262
|
-
plannedPathSet,
|
|
263
|
-
);
|
|
264
|
-
|
|
265
256
|
return {
|
|
266
|
-
files:
|
|
267
|
-
orphanedFiles,
|
|
257
|
+
files: stableSortEmitFiles(files),
|
|
258
|
+
orphanedFiles: [],
|
|
268
259
|
lock,
|
|
269
260
|
};
|
|
270
261
|
}
|
|
@@ -8,6 +8,7 @@ import type { GenerateResult } from "../types/cli.ts";
|
|
|
8
8
|
import type { Diagnostic } from "../types/diagnostic.ts";
|
|
9
9
|
import { discover } from "./discover.ts";
|
|
10
10
|
import { loadManifest } from "./manifest.ts";
|
|
11
|
+
import { ORCHESTRATOR_MANIFEST_VERSION } from "./types.ts";
|
|
11
12
|
|
|
12
13
|
export type FastGenerateCheckResult =
|
|
13
14
|
| {
|
|
@@ -55,6 +56,9 @@ export function runFastGenerateCheck(workspaceRootInput: string): FastGenerateCh
|
|
|
55
56
|
const manifest = loadManifest(cacheDir);
|
|
56
57
|
const trackedFiles = Object.keys(manifest.fileHashes).sort();
|
|
57
58
|
|
|
59
|
+
if (manifest.schemaVersion !== ORCHESTRATOR_MANIFEST_VERSION) {
|
|
60
|
+
return { kind: "miss", reason: "manifest schema version changed" };
|
|
61
|
+
}
|
|
58
62
|
if (!manifest.inputFingerprint || trackedFiles.length === 0) {
|
|
59
63
|
return { kind: "miss", reason: "manifest missing input fingerprint or file hashes" };
|
|
60
64
|
}
|
|
@@ -83,7 +83,7 @@ export function updateManifestAfterWrite(
|
|
|
83
83
|
): OrchestratorManifest {
|
|
84
84
|
return {
|
|
85
85
|
...manifest,
|
|
86
|
-
fileHashes
|
|
86
|
+
fileHashes,
|
|
87
87
|
priorAppGraph,
|
|
88
88
|
inputFingerprint,
|
|
89
89
|
...(sourceFileIndex !== undefined ? { sourceFileIndex } : {}),
|