forgeos 0.1.0-alpha.31 → 0.1.0-alpha.32
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/AGENTS.md +3 -3
- package/CHANGELOG.md +19 -0
- package/docs/changelog.md +40 -1
- package/package.json +1 -1
- package/src/forge/_generated/releaseManifest.json +1 -1
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/agent-adapters/index.ts +28 -2
- package/src/forge/cli/auth.ts +74 -4
- package/src/forge/cli/changed.ts +38 -11
- package/src/forge/cli/commands.ts +71 -25
- package/src/forge/cli/db.ts +9 -8
- package/src/forge/cli/dev.ts +137 -56
- package/src/forge/cli/doctor.ts +3 -2
- package/src/forge/cli/handoff.ts +50 -3
- package/src/forge/cli/main.ts +3 -1
- package/src/forge/cli/new.ts +130 -12
- package/src/forge/cli/output.ts +94 -12
- package/src/forge/cli/parse.ts +13 -1
- package/src/forge/cli/studio.ts +15 -14
- package/src/forge/cli/windows.ts +2 -1
- package/src/forge/cli/workos.ts +5 -4
- package/src/forge/compiler/agent-contract/build.ts +3 -3
- package/src/forge/compiler/data-graph/sql/ddl.ts +11 -1
- package/src/forge/compiler/integration/templates/render.ts +1 -0
- package/src/forge/compiler/integration/templates/workos.ts +5 -5
- package/src/forge/compiler/make-registry/build.ts +2 -2
- package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
- package/src/forge/compiler/recipes/definitions.ts +1 -1
- package/src/forge/delta/status.ts +79 -12
- package/src/forge/dev/server.ts +4 -4
- package/src/forge/dev-console/cycle.ts +55 -20
- package/src/forge/dev-console/types.ts +1 -0
- package/src/forge/make/fields.ts +26 -0
- package/src/forge/make/index.ts +6 -1
- package/src/forge/runtime/db/memory-adapter.ts +139 -32
- package/src/forge/ui/index.ts +129 -0
- package/src/forge/version.ts +1 -1
- package/src/forge/workspace/change-summary.ts +7 -1
- package/src/forge/workspace/forge-cli.ts +29 -1
package/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.32 input=26469f4d84697cd1397a4a6726dff71ec50e70270a82b3ea4c84c75f25ac5f58 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
|
|
2
2
|
# AGENTS.md
|
|
3
3
|
|
|
4
4
|
<!-- forge-generated:start -->
|
|
@@ -205,8 +205,8 @@ node bin/forge.mjs do verify --json
|
|
|
205
205
|
Use:
|
|
206
206
|
|
|
207
207
|
```bash
|
|
208
|
-
node bin/forge.mjs make resource <name> --fields title:text,status:enum
|
|
209
|
-
node bin/forge.mjs make resource <name> --fields title:text,status:enum
|
|
208
|
+
node bin/forge.mjs make resource <name> --fields title:text,status:enum=open+closed --dry-run --json
|
|
209
|
+
node bin/forge.mjs make resource <name> --fields title:text,status:enum=open+closed --with-ui --yes
|
|
210
210
|
node bin/forge.mjs make ui --framework vite --dry-run --json
|
|
211
211
|
node bin/forge.mjs make ui --framework nuxt --dry-run --json
|
|
212
212
|
node bin/forge.mjs make ai-chat support --dry-run --json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# forgeos
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.32
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Harden the alpha field-test loop after real Codex, WorkOS, PGlite, and app-server exercises.
|
|
8
|
+
|
|
9
|
+
- Accept `create-forgeos-app --git` as a supported/no-op compatibility flag when git initialization is already handled by the scaffold path, and keep `npm create forgeos-app@alpha .` working from empty current directories.
|
|
10
|
+
- Reject schema definitions that try to model `id` as a normal text field so generated SQL cannot silently create the wrong primary-key shape.
|
|
11
|
+
- Fix tenant-scoped updates in the in-memory database adapter and bring memory timestamp behavior closer to PGlite by rejecting empty timestamp strings and returning `Date` objects for timestamp columns.
|
|
12
|
+
- Improve smoke/generate drift diagnostics and keep `forge handoff` read-only with respect to generated artifacts.
|
|
13
|
+
- Copy generated WorkOS seed data to a root-level `workos-seed.yml` for app DX, serve `HEAD /auth.md` and `HEAD /.well-known/oauth-protected-resource`, and make local-vs-production auth posture more explicit.
|
|
14
|
+
- Normalize suggested Forge commands in framework checkouts so `agent`, `delta`, `studio`, `status`, `changed`, `handoff`, doctors, and WorkOS/auth helpers recommend `node bin/forge.mjs ...` instead of a possibly stale global `forge`.
|
|
15
|
+
- Return structured JSON diagnostics for Delta/PGlite export/open failures instead of leaking raw PGlite stack output, and improve Windows/PGlite repair guidance.
|
|
16
|
+
- Expand `forge ui audit` with static UX/auth-readiness warnings for missing semantic landmarks, unlabeled form controls, unnamed buttons, missing loading/error/empty states, and tenant/prod-auth apps that still only show local dev auth posture.
|
|
17
|
+
|
|
3
18
|
## 0.1.0-alpha.31
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -10,6 +25,10 @@
|
|
|
10
25
|
- Add `forge auth status` and production-focused `forge auth prove --prod` output so `dev-headers` is clearly labeled as local-only while JWT/OIDC proofs show production auth posture.
|
|
11
26
|
- Expand `forge doctor windows` with local PGlite store posture and cleanup guidance, including safer PGlite abort inspection that does not poison the surrounding process exit code.
|
|
12
27
|
- Add `forge ui audit` and run it during `forge verify --smoke` when a web app is present, catching missing UI scenarios, missing stable Forge test IDs, and missing policy-denied coverage for sensitive routes.
|
|
28
|
+
- Expand `forge ui audit` with static UX/auth-readiness warnings for missing semantic landmarks, unlabeled form controls, unnamed buttons, missing loading/error/empty states, and tenant/prod-auth apps that still only show local dev auth posture.
|
|
29
|
+
- Bring the in-memory DB adapter closer to PGlite for timestamp fields by rejecting empty timestamp values and returning `Date` objects for `timestamp`/`timestamptz` columns.
|
|
30
|
+
- Make `forge handoff` use a read-only generated-artifact check so preparing a handoff no longer rewrites `src/forge/_generated/**`, `forge.lock`, or generated `AGENTS.md` noise; `forge dev --once` still self-heals stale artifacts.
|
|
31
|
+
- Allow `forge new .` / `npm create forgeos-app@alpha .` from an empty current directory, while refusing non-empty directories to avoid overwriting existing apps.
|
|
13
32
|
|
|
14
33
|
## 0.1.0-alpha.30
|
|
15
34
|
|
package/docs/changelog.md
CHANGED
|
@@ -6,6 +6,32 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.0-alpha.32
|
|
10
|
+
|
|
11
|
+
- Accepted `create-forgeos-app --git` as a supported/no-op compatibility flag
|
|
12
|
+
when git initialization is already handled by the scaffold path, and kept
|
|
13
|
+
`npm create forgeos-app@alpha .` working from empty current directories.
|
|
14
|
+
- Added stronger schema validation for `id` so apps cannot accidentally model
|
|
15
|
+
the primary key as a normal text field and generate incorrect SQL.
|
|
16
|
+
- Fixed tenant-scoped updates in the in-memory database adapter and made memory
|
|
17
|
+
timestamp handling closer to PGlite by rejecting empty timestamp strings and
|
|
18
|
+
returning `Date` objects for timestamp columns.
|
|
19
|
+
- Improved smoke/generate drift diagnostics and kept `forge handoff` read-only
|
|
20
|
+
with respect to generated artifacts.
|
|
21
|
+
- Improved WorkOS/auth app DX: root-level `workos-seed.yml`, `HEAD /auth.md`,
|
|
22
|
+
`HEAD /.well-known/oauth-protected-resource`, and clearer local-vs-production
|
|
23
|
+
auth posture.
|
|
24
|
+
- Normalized suggested Forge commands in framework checkouts so `agent`,
|
|
25
|
+
`delta`, `studio`, `status`, `changed`, `handoff`, doctors, and WorkOS/auth
|
|
26
|
+
helpers recommend `node bin/forge.mjs ...` instead of a possibly stale global
|
|
27
|
+
`forge`.
|
|
28
|
+
- Returned structured JSON diagnostics for Delta/PGlite export/open failures
|
|
29
|
+
instead of raw PGlite stack output, and expanded Windows/PGlite repair hints.
|
|
30
|
+
- Expanded `forge ui audit` with static UX/auth-readiness warnings for missing
|
|
31
|
+
semantic landmarks, unlabeled form controls, unnamed buttons, missing
|
|
32
|
+
loading/error/empty states, and tenant/prod-auth apps that still only expose
|
|
33
|
+
local dev auth posture.
|
|
34
|
+
|
|
9
35
|
## 0.1.0-alpha.31
|
|
10
36
|
|
|
11
37
|
- Added Forge workspace baselines for non-git app directories:
|
|
@@ -20,7 +46,20 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
20
46
|
exit code.
|
|
21
47
|
- Added `forge ui audit` and wired it into `forge verify --smoke` for web apps
|
|
22
48
|
to catch missing route scenarios, missing stable Forge test IDs, and missing
|
|
23
|
-
policy-denied coverage for sensitive flows.
|
|
49
|
+
policy-denied coverage for sensitive flows. It now also reports static
|
|
50
|
+
UX/auth-readiness warnings for missing semantic landmarks, unlabeled form
|
|
51
|
+
controls, unnamed buttons, missing loading/error/empty states, and
|
|
52
|
+
tenant/prod-auth apps that still only expose local dev auth posture.
|
|
53
|
+
- Brought the in-memory DB adapter closer to PGlite for timestamp fields by
|
|
54
|
+
rejecting empty timestamp values and returning `Date` objects for
|
|
55
|
+
`timestamp`/`timestamptz` columns.
|
|
56
|
+
- Made `forge handoff` use a read-only generated-artifact check so preparing a
|
|
57
|
+
handoff no longer rewrites `src/forge/_generated/**`, `forge.lock`, or
|
|
58
|
+
generated `AGENTS.md` noise; `forge dev --once` still self-heals stale
|
|
59
|
+
artifacts.
|
|
60
|
+
- Allowed `forge new .` / `npm create forgeos-app@alpha .` from an empty
|
|
61
|
+
current directory, while refusing non-empty directories to avoid overwriting
|
|
62
|
+
existing apps.
|
|
24
63
|
|
|
25
64
|
## 0.1.0-alpha.30
|
|
26
65
|
|
package/package.json
CHANGED
|
@@ -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.32","releaseId":"forgeos@0.1.0-alpha.32+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.32 input=26469f4d84697cd1397a4a6726dff71ec50e70270a82b3ea4c84c75f25ac5f58 content=ba652b73917e09bcc803cfea91f38e1aa2563b17eacc60f6faaebdc949997e9a
|
|
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.32",
|
|
23
|
+
"releaseId": "forgeos@0.1.0-alpha.32+unknown",
|
|
24
24
|
"schemaVersion": "0.1.0"
|
|
25
25
|
} as const;
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
} from "../agent-memory/hook-runner.ts";
|
|
57
57
|
import { releaseManifest } from "../_generated/releaseManifest.ts";
|
|
58
58
|
import { runDevConsoleCycle } from "../dev-console/cycle.ts";
|
|
59
|
+
import { normalizeForgeCliCommandsInValue } from "../workspace/forge-cli.ts";
|
|
59
60
|
|
|
60
61
|
export const AGENT_ADAPTER_VERSION = "agent-adapter-0.1.0";
|
|
61
62
|
export const AGENT_FORMAT_VERSION = "2026-06";
|
|
@@ -77,6 +78,25 @@ function diagnostic(
|
|
|
77
78
|
return createDiagnostic({ severity, code, message, ...(file ? { file } : {}) });
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
function normalizeAgentCliCommandHints<T>(workspaceRoot: string, result: T): T {
|
|
82
|
+
if (!result || typeof result !== "object") {
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
const value = result as Record<string, unknown>;
|
|
86
|
+
const normalized: Record<string, unknown> = { ...value };
|
|
87
|
+
for (const key of ["nextActions", "diagnostics", "commands", "checks", "steps"]) {
|
|
88
|
+
if (key in normalized) {
|
|
89
|
+
normalized[key] = normalizeForgeCliCommandsInValue(workspaceRoot, normalized[key], key);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const key of ["installResult", "ingestResult"]) {
|
|
93
|
+
if (key in normalized) {
|
|
94
|
+
normalized[key] = normalizeAgentCliCommandHints(workspaceRoot, normalized[key]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return normalized as T;
|
|
98
|
+
}
|
|
99
|
+
|
|
80
100
|
function readText(workspaceRoot: string, relative: string): string | null {
|
|
81
101
|
const path = join(workspaceRoot, relative);
|
|
82
102
|
if (!nodeFileSystem.exists(path)) {
|
|
@@ -2222,9 +2242,11 @@ export async function runAgentHooksSmoke(options: AgentCommandOptions): Promise<
|
|
|
2222
2242
|
};
|
|
2223
2243
|
}
|
|
2224
2244
|
|
|
2225
|
-
|
|
2245
|
+
type AgentCommandResult =
|
|
2226
2246
|
AgentExportResult | AgentCheckResult | AgentTargetsResult | AgentPrintContextResult | AgentDoctorResult | AgentPrepareResult | AgentOnboardResult | AgentHooksSmokeResult | AgentHooksStatusResult | AgentTimelineResult | AgentMemoryCommandResult
|
|
2227
|
-
|
|
2247
|
+
;
|
|
2248
|
+
|
|
2249
|
+
async function runAgentCommandRaw(options: AgentCommandOptions): Promise<AgentCommandResult> {
|
|
2228
2250
|
if (options.subcommand === "list-targets") {
|
|
2229
2251
|
return runAgentListTargets(options.workspaceRoot);
|
|
2230
2252
|
}
|
|
@@ -2295,6 +2317,10 @@ export async function runAgentCommand(options: AgentCommandOptions): Promise<
|
|
|
2295
2317
|
};
|
|
2296
2318
|
}
|
|
2297
2319
|
|
|
2320
|
+
export async function runAgentCommand(options: AgentCommandOptions): Promise<AgentCommandResult> {
|
|
2321
|
+
return normalizeAgentCliCommandHints(options.workspaceRoot, await runAgentCommandRaw(options));
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2298
2324
|
export function formatAgentJson(result: Awaited<ReturnType<typeof runAgentCommand>>): string {
|
|
2299
2325
|
if ("timeline" in result && result.timeline === "agent") {
|
|
2300
2326
|
return `${JSON.stringify(result, null, 2)}\n`;
|
package/src/forge/cli/auth.ts
CHANGED
|
@@ -9,6 +9,9 @@ import { mapClaimsToAuthContext } from "../runtime/auth/claims.ts";
|
|
|
9
9
|
import { ForgeAuthError } from "../runtime/auth/errors.ts";
|
|
10
10
|
import { verifyJwtToken } from "../runtime/auth/verifier.ts";
|
|
11
11
|
import { loadSecretRegistry } from "../runtime/secrets/check.ts";
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { normalizeForgeCliCommandsInValue } from "../workspace/forge-cli.ts";
|
|
12
15
|
|
|
13
16
|
export type AuthSubcommand = "check" | "config" | "decode" | "test-token" | "jwks" | "prove" | "status";
|
|
14
17
|
|
|
@@ -18,6 +21,7 @@ export interface AuthCommandOptions {
|
|
|
18
21
|
json: boolean;
|
|
19
22
|
token?: string;
|
|
20
23
|
prod?: boolean;
|
|
24
|
+
scenario?: string;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export interface AuthCommandResult {
|
|
@@ -88,7 +92,7 @@ function buildAuthPosture(workspaceRoot: string) {
|
|
|
88
92
|
const productionMode = config.mode === "jwt" || config.mode === "oidc";
|
|
89
93
|
const errors = configErrors(config);
|
|
90
94
|
const configReady = errors.length === 0;
|
|
91
|
-
return {
|
|
95
|
+
return normalizeForgeCliCommandsInValue(workspaceRoot, {
|
|
92
96
|
schemaVersion: "0.1.0",
|
|
93
97
|
mode: config.mode,
|
|
94
98
|
localOnly: config.mode === "dev-headers",
|
|
@@ -96,6 +100,15 @@ function buildAuthPosture(workspaceRoot: string) {
|
|
|
96
100
|
requiresTenant: config.requiresTenant,
|
|
97
101
|
bearerHeader: productionMode ? "Authorization: Bearer <token>" : null,
|
|
98
102
|
tenantClaim: config.claims.tenantId ?? "tenant_id",
|
|
103
|
+
productionChecklist: [
|
|
104
|
+
{ item: "auth mode is jwt or oidc", ok: productionMode },
|
|
105
|
+
{ item: "FORGE_AUTH_ISSUER configured", ok: Boolean(config.issuer) },
|
|
106
|
+
{ item: "FORGE_AUTH_AUDIENCE configured", ok: Boolean(config.audience) },
|
|
107
|
+
{ item: "FORGE_AUTH_JWKS_URI or OIDC discovery configured", ok: config.mode === "oidc" || Boolean(config.jwksUri) },
|
|
108
|
+
{ item: "tenant claim mapped", ok: Boolean(config.claims.tenantId) },
|
|
109
|
+
{ item: "permission claim mapped", ok: Boolean(config.claims.permissions) },
|
|
110
|
+
{ item: "dev-headers disabled for public runtime", ok: config.mode !== "dev-headers" },
|
|
111
|
+
],
|
|
99
112
|
reason: productionMode
|
|
100
113
|
? configReady
|
|
101
114
|
? "jwt/oidc production auth configuration is present"
|
|
@@ -106,7 +119,58 @@ function buildAuthPosture(workspaceRoot: string) {
|
|
|
106
119
|
? ["forge auth prove --prod --token <jwt> --json", "forge serve --json"]
|
|
107
120
|
: ["forge auth check --json", "configure FORGE_AUTH_ISSUER, FORGE_AUTH_AUDIENCE, and FORGE_AUTH_JWKS_URI or OIDC issuer"]
|
|
108
121
|
: ["set FORGE_AUTH_MODE=jwt or oidc for production", "forge auth prove --prod --token <jwt> --json"],
|
|
109
|
-
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildMultiTenantProof(workspaceRoot: string, workos: ReturnType<typeof detectWorkOS>, requiresTenant: boolean) {
|
|
126
|
+
const rootSeedPresent = existsSync(join(workspaceRoot, "workos-seed.yml"));
|
|
127
|
+
const generatedSeedPresent = existsSync(join(workspaceRoot, "src/forge/_generated/integrations/workos/workos-seed.yml"));
|
|
128
|
+
const authMdPresent = existsSync(join(workspaceRoot, "public/auth.md"));
|
|
129
|
+
const metadataPresent = existsSync(join(workspaceRoot, "public/.well-known/oauth-protected-resource"));
|
|
130
|
+
const permissions = ["onboarding:read", "invitations:create", "tasks:update"];
|
|
131
|
+
const permissionVocabularyPresent = workos.detected && workos.claimStatus.some((claim) => claim.name === "permissions" && claim.ok);
|
|
132
|
+
const checks = [
|
|
133
|
+
{
|
|
134
|
+
id: "tenant-claim",
|
|
135
|
+
ok: requiresTenant && workos.claimStatus.some((claim) => claim.name === "tenantId" && claim.ok),
|
|
136
|
+
evidence: "Forge tenant claim maps to WorkOS organization_id and tenant auth is required.",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "permission-claim",
|
|
140
|
+
ok: permissionVocabularyPresent,
|
|
141
|
+
evidence: "Forge permissions claim maps to WorkOS permissions.",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "seed-organizations",
|
|
145
|
+
ok: rootSeedPresent || generatedSeedPresent,
|
|
146
|
+
evidence: rootSeedPresent ? "workos-seed.yml exists at app root." : "generated WorkOS seed exists.",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "agent-auth-metadata",
|
|
150
|
+
ok: authMdPresent && metadataPresent,
|
|
151
|
+
evidence: "public/auth.md and protected resource metadata are present.",
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
return normalizeForgeCliCommandsInValue(workspaceRoot, {
|
|
155
|
+
scenario: "multi-tenant",
|
|
156
|
+
ok: checks.every((check) => check.ok),
|
|
157
|
+
claims: {
|
|
158
|
+
acme: { organization_id: "org_acme", role: "owner", permissions },
|
|
159
|
+
globex: { organization_id: "org_globex", role: "member", permissions: ["onboarding:read", "tasks:update"] },
|
|
160
|
+
},
|
|
161
|
+
invariants: [
|
|
162
|
+
"Acme and Globex must use different organization_id claim values.",
|
|
163
|
+
"Tenant-scoped reads must include the active organization_id.",
|
|
164
|
+
"Tenant-scoped writes must verify the resource tenant before mutation.",
|
|
165
|
+
"Role-only UI affordances are not sufficient; policies must use permissions/claims.",
|
|
166
|
+
],
|
|
167
|
+
checks,
|
|
168
|
+
nextActions: [
|
|
169
|
+
"forge workos doctor --json",
|
|
170
|
+
"forge workos seed --file workos-seed.yml --dry-run --json",
|
|
171
|
+
"run an HTTP E2E with Acme and Globex tokens before production",
|
|
172
|
+
],
|
|
173
|
+
});
|
|
110
174
|
}
|
|
111
175
|
|
|
112
176
|
function validateConfig(workspaceRoot: string): AuthCommandResult {
|
|
@@ -258,8 +322,12 @@ export async function runAuthCommand(
|
|
|
258
322
|
? tokenProof.error
|
|
259
323
|
: undefined;
|
|
260
324
|
const proofOk = checked.ok && (!options.prod || (productionMode && tokenProof?.ok === true));
|
|
325
|
+
const multiTenantProof = options.scenario === "multi-tenant"
|
|
326
|
+
? buildMultiTenantProof(options.workspaceRoot, workos, config.requiresTenant)
|
|
327
|
+
: null;
|
|
328
|
+
const scenarioOk = !multiTenantProof || multiTenantProof.ok;
|
|
261
329
|
return {
|
|
262
|
-
ok: proofOk,
|
|
330
|
+
ok: proofOk && scenarioOk,
|
|
263
331
|
mode: config.mode,
|
|
264
332
|
data: {
|
|
265
333
|
schemaVersion: "0.1.0",
|
|
@@ -268,8 +336,10 @@ export async function runAuthCommand(
|
|
|
268
336
|
mode: config.mode,
|
|
269
337
|
productionReady: productionMode && checked.ok,
|
|
270
338
|
prod: options.prod === true,
|
|
339
|
+
scenario: options.scenario ?? null,
|
|
271
340
|
authPosture: posture,
|
|
272
341
|
...(tokenProof ? { tokenProof: tokenProof.ok ? tokenProof.data : tokenProof.error } : {}),
|
|
342
|
+
...(multiTenantProof ? { multiTenantProof } : {}),
|
|
273
343
|
invariants: [
|
|
274
344
|
{
|
|
275
345
|
id: "INV-001",
|
|
@@ -305,7 +375,7 @@ export async function runAuthCommand(
|
|
|
305
375
|
checkedAt: "deterministic",
|
|
306
376
|
},
|
|
307
377
|
error: prodError ?? checked.error,
|
|
308
|
-
exitCode: proofOk ? 0 : 1,
|
|
378
|
+
exitCode: proofOk && scenarioOk ? 0 : 1,
|
|
309
379
|
};
|
|
310
380
|
}
|
|
311
381
|
if (options.subcommand === "config") {
|
package/src/forge/cli/changed.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
filterCategorizedSummary,
|
|
5
5
|
summarizeChangeTypes,
|
|
6
6
|
} from "../workspace/change-summary.ts";
|
|
7
|
+
import { forgeCliCommandsForWorkspace } from "../workspace/forge-cli.ts";
|
|
7
8
|
import { buildWorkspaceGitSummary, type WorkspaceGitSummary } from "../workspace/git-summary.ts";
|
|
8
9
|
|
|
9
10
|
export interface ChangedCommandResult {
|
|
@@ -44,6 +45,7 @@ interface GeneratedChangeExplanation {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const AUTHORED_CHANGE_TYPES = ["source", "tests", "docs", "config", "assets", "other"] as const;
|
|
48
|
+
const REVIEW_CHANGE_TYPES = ["source", "tests", "docs", "config", "assets"] as const;
|
|
47
49
|
|
|
48
50
|
function emptyCategory(summary: CategorizedFileSummary, category: keyof CategorizedFileSummary["byType"]): boolean {
|
|
49
51
|
return summary.byType[category].count === 0;
|
|
@@ -102,10 +104,16 @@ function buildRisks(git: WorkspaceGitSummary): string[] {
|
|
|
102
104
|
if (!emptyCategory(changed, "generated") && humanChanges.total === 0) {
|
|
103
105
|
risks.push("only generated artifacts changed; verify the source edit, generator input, or intentional regeneration that produced them");
|
|
104
106
|
}
|
|
107
|
+
return risks;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function buildAdvisories(git: WorkspaceGitSummary): string[] {
|
|
111
|
+
const advisories: string[] = [];
|
|
112
|
+
const changed = git.changeSummary.changed;
|
|
105
113
|
if (changed.total.count > 50) {
|
|
106
|
-
|
|
114
|
+
advisories.push(`${changed.total.count} changed file(s) detected; use grouped summaries before reviewing raw diffs`);
|
|
107
115
|
}
|
|
108
|
-
return
|
|
116
|
+
return advisories;
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
function buildRecommendedCommands(git: WorkspaceGitSummary): string[] {
|
|
@@ -127,7 +135,12 @@ function buildRecommendedCommands(git: WorkspaceGitSummary): string[] {
|
|
|
127
135
|
"forge generate --check --json",
|
|
128
136
|
];
|
|
129
137
|
}
|
|
138
|
+
const authoredGeneratedInputs =
|
|
139
|
+
changed.byType.source.count +
|
|
140
|
+
changed.byType.config.count +
|
|
141
|
+
changed.byType.operational.count;
|
|
130
142
|
return [
|
|
143
|
+
...(authoredGeneratedInputs > 0 ? ["forge generate --check --json"] : []),
|
|
131
144
|
"forge handoff --json",
|
|
132
145
|
"forge test plan --changed --json",
|
|
133
146
|
"forge verify --changed",
|
|
@@ -189,7 +202,7 @@ function buildGeneratedChangeExplanation(
|
|
|
189
202
|
};
|
|
190
203
|
}
|
|
191
204
|
|
|
192
|
-
export function runChangedCommand(workspaceRoot: string, options: { authoredOnly?: boolean } = {}): ChangedCommandResult {
|
|
205
|
+
export function runChangedCommand(workspaceRoot: string, options: { authoredOnly?: boolean; reviewOnly?: boolean } = {}): ChangedCommandResult {
|
|
193
206
|
const git = buildWorkspaceGitSummary(workspaceRoot);
|
|
194
207
|
const changed = git.changeSummary.changed;
|
|
195
208
|
const humanChanges = selectHumanChangeSummary(changed);
|
|
@@ -198,16 +211,25 @@ export function runChangedCommand(workspaceRoot: string, options: { authoredOnly
|
|
|
198
211
|
const authoredStaged = filterCategorizedSummary(git.changeSummary.staged, [...AUTHORED_CHANGE_TYPES]);
|
|
199
212
|
const authoredUnstaged = filterCategorizedSummary(git.changeSummary.unstaged, [...AUTHORED_CHANGE_TYPES]);
|
|
200
213
|
const authoredUntracked = filterCategorizedSummary(git.changeSummary.untracked, [...AUTHORED_CHANGE_TYPES]);
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
214
|
+
const reviewChanged = filterCategorizedSummary(changed, [...REVIEW_CHANGE_TYPES]);
|
|
215
|
+
const reviewStaged = filterCategorizedSummary(git.changeSummary.staged, [...REVIEW_CHANGE_TYPES]);
|
|
216
|
+
const reviewUnstaged = filterCategorizedSummary(git.changeSummary.unstaged, [...REVIEW_CHANGE_TYPES]);
|
|
217
|
+
const reviewUntracked = filterCategorizedSummary(git.changeSummary.untracked, [...REVIEW_CHANGE_TYPES]);
|
|
218
|
+
const viewHumanChanges = options.reviewOnly
|
|
219
|
+
? selectHumanChangeSummary(reviewChanged)
|
|
220
|
+
: options.authoredOnly
|
|
221
|
+
? selectHumanChangeSummary(authoredChanged)
|
|
222
|
+
: humanChanges;
|
|
223
|
+
const viewChanged = options.reviewOnly ? reviewChanged : options.authoredOnly ? authoredChanged : changed;
|
|
224
|
+
const viewStaged = options.reviewOnly ? reviewStaged : options.authoredOnly ? authoredStaged : git.changeSummary.staged;
|
|
225
|
+
const viewUnstaged = options.reviewOnly ? reviewUnstaged : options.authoredOnly ? authoredUnstaged : git.changeSummary.unstaged;
|
|
226
|
+
const viewUntracked = options.reviewOnly ? reviewUntracked : options.authoredOnly ? authoredUntracked : git.changeSummary.untracked;
|
|
227
|
+
const viewDerivedChanges: DerivedChangeSummary = options.authoredOnly || options.reviewOnly
|
|
207
228
|
? { total: 0, generated: { count: 0, sample: [], hidden: 0 } }
|
|
208
229
|
: derivedChanges;
|
|
209
230
|
const risks = buildRisks(git);
|
|
210
|
-
const
|
|
231
|
+
const advisories = buildAdvisories(git);
|
|
232
|
+
const recommendedCommands = forgeCliCommandsForWorkspace(workspaceRoot, buildRecommendedCommands(git));
|
|
211
233
|
const reviewFocus = buildReviewFocus(viewHumanChanges, viewDerivedChanges);
|
|
212
234
|
const generatedExplanation = buildGeneratedChangeExplanation(viewHumanChanges, viewDerivedChanges);
|
|
213
235
|
const diffPlan: DiffPlan = buildDiffPlanFromChangeSummary(viewChanged);
|
|
@@ -223,7 +245,7 @@ export function runChangedCommand(workspaceRoot: string, options: { authoredOnly
|
|
|
223
245
|
commit: git.commit,
|
|
224
246
|
workspaceMode: git.workspaceMode ?? (git.available ? "git" : "nonGit"),
|
|
225
247
|
tracking: git.tracking ?? git.source,
|
|
226
|
-
view: options.authoredOnly ? "authored" : "all",
|
|
248
|
+
view: options.reviewOnly ? "review" : options.authoredOnly ? "authored" : "all",
|
|
227
249
|
changedFiles: viewChanged.total.count,
|
|
228
250
|
humanFiles: viewHumanChanges.total,
|
|
229
251
|
generatedFiles: viewDerivedChanges.total,
|
|
@@ -253,6 +275,7 @@ export function runChangedCommand(workspaceRoot: string, options: { authoredOnly
|
|
|
253
275
|
generatedExplanation,
|
|
254
276
|
diffPlan,
|
|
255
277
|
risks,
|
|
278
|
+
advisories,
|
|
256
279
|
recommendedCommands,
|
|
257
280
|
nextActions: recommendedCommands,
|
|
258
281
|
},
|
|
@@ -268,6 +291,7 @@ export function formatChangedHuman(result: ChangedCommandResult): string {
|
|
|
268
291
|
const generatedExplanation = result.data.generatedExplanation as { summary?: string } | undefined;
|
|
269
292
|
const diffPlan = result.data.diffPlan as { summary?: string; authoredDiffCommand?: string; generatedDiffCommand?: string; generatedCollapsedByDefault?: boolean } | undefined;
|
|
270
293
|
const risks = (result.data.risks as string[] | undefined) ?? [];
|
|
294
|
+
const advisories = (result.data.advisories as string[] | undefined) ?? [];
|
|
271
295
|
const nextActions = (result.data.nextActions as string[] | undefined) ?? [];
|
|
272
296
|
const lines = [
|
|
273
297
|
`Forge changed: ${result.ok ? "ready" : "git unavailable"}`,
|
|
@@ -310,6 +334,9 @@ export function formatChangedHuman(result: ChangedCommandResult): string {
|
|
|
310
334
|
if (risks.length > 0) {
|
|
311
335
|
lines.push("", "Risks:", ...risks.map((risk) => ` ${risk}`));
|
|
312
336
|
}
|
|
337
|
+
if (advisories.length > 0) {
|
|
338
|
+
lines.push("", "Notes:", ...advisories.map((advisory) => ` ${advisory}`));
|
|
339
|
+
}
|
|
313
340
|
|
|
314
341
|
lines.push("", "Next:", ...nextActions.map((command) => ` ${command}`));
|
|
315
342
|
return `${lines.join("\n")}\n`;
|
|
@@ -284,6 +284,7 @@ import { resolveAuthFromCli } from "../runtime/auth/resolve.ts";
|
|
|
284
284
|
import { getActiveDbAdapter } from "../runtime/executor.ts";
|
|
285
285
|
import { CLI_VERSION, FORGEOS_VERSION } from "../version.ts";
|
|
286
286
|
import type { CategorizedFileSummary } from "../workspace/change-summary.ts";
|
|
287
|
+
import { forgeCliCommandsForWorkspace } from "../workspace/forge-cli.ts";
|
|
287
288
|
import { buildWorkspaceGitSummary } from "../workspace/git-summary.ts";
|
|
288
289
|
import { startCommandHeartbeat } from "./progress.ts";
|
|
289
290
|
|
|
@@ -722,33 +723,69 @@ export function runStatusCommand(workspaceRoot: string): StatusCommandResult {
|
|
|
722
723
|
const driftClean = driftSummary.ok === true;
|
|
723
724
|
const ok = driftClean && generatedReady;
|
|
724
725
|
const handoffDefaultReady = handoffSummary.defaultReady === true;
|
|
726
|
+
const changed = gitSummary.changeSummary.changed;
|
|
727
|
+
const generatedGitFiles = changed.byType.generated.count;
|
|
728
|
+
const authoredGitFiles = changed.total.count - generatedGitFiles;
|
|
729
|
+
const authoredGeneratedInputs =
|
|
730
|
+
changed.byType.source.count +
|
|
731
|
+
changed.byType.config.count +
|
|
732
|
+
changed.byType.operational.count;
|
|
733
|
+
const generatedNeedsCheck = generatedReady && driftClean && generatedGitFiles === 0 && authoredGeneratedInputs > 0;
|
|
725
734
|
const generatedState = !generatedReady
|
|
726
735
|
? "missing-artifacts"
|
|
736
|
+
: generatedNeedsCheck
|
|
737
|
+
? "check-needed"
|
|
727
738
|
: driftClean
|
|
728
739
|
? "ready"
|
|
729
740
|
: "drift";
|
|
730
|
-
const
|
|
731
|
-
? ["forge dev", "forge generate --check --json"]
|
|
741
|
+
const generatedNextActionsRaw = generatedState === "ready"
|
|
742
|
+
? ["forge dev", "forge dev --once --json", "forge generate --check --json"]
|
|
743
|
+
: generatedState === "check-needed"
|
|
744
|
+
? ["forge generate --check --json", "forge handoff --json", "forge dev --once --json"]
|
|
732
745
|
: ["forge generate", "forge check --json", "forge inspect drift --json"];
|
|
733
|
-
const
|
|
734
|
-
const generatedGitFiles = changed.byType.generated.count;
|
|
735
|
-
const authoredGitFiles = changed.total.count - generatedGitFiles;
|
|
746
|
+
const generatedNextActions = forgeCliCommandsForWorkspace(workspaceRoot, generatedNextActionsRaw);
|
|
736
747
|
const generatedGitExplanation = generatedGitFiles === 0
|
|
737
|
-
?
|
|
748
|
+
? generatedNeedsCheck
|
|
749
|
+
? "git status has no generated artifact changes, but authored source/config changes mean freshness is unverified until forge generate --check runs"
|
|
750
|
+
: "git status has no generated artifact changes"
|
|
738
751
|
: authoredGitFiles === 0
|
|
739
752
|
? "forge generate --check can be clean while git shows generated artifacts changed: generated files match current workspace inputs but differ from HEAD"
|
|
740
753
|
: "git status includes generated artifacts alongside authored changes; review authored inputs first";
|
|
754
|
+
const [safeDevCommand, generatedCheckCommand, generatedRepairCommand] = forgeCliCommandsForWorkspace(workspaceRoot, [
|
|
755
|
+
"forge dev",
|
|
756
|
+
"forge generate --check --json",
|
|
757
|
+
"forge generate",
|
|
758
|
+
]);
|
|
741
759
|
const frontendPresent = summaryBlock.frontendPresent === true;
|
|
760
|
+
const [
|
|
761
|
+
studioOpenCommand,
|
|
762
|
+
studioAttachCommand,
|
|
763
|
+
studioSnapshotCommand,
|
|
764
|
+
studioWatchCommand,
|
|
765
|
+
studioBridgeCommand,
|
|
766
|
+
studioDoctorCommand,
|
|
767
|
+
studioStartTargetAppCommand,
|
|
768
|
+
studioProbeCommand,
|
|
769
|
+
] = forgeCliCommandsForWorkspace(workspaceRoot, [
|
|
770
|
+
"forge studio open . --preview-port 5174 --target codex --json",
|
|
771
|
+
"forge studio attach . --preview-port 5174 --target codex --json",
|
|
772
|
+
"forge studio snapshot . --preview-port 5174 --target codex --json",
|
|
773
|
+
"forge studio watch . --preview-port 5174 --target codex --json",
|
|
774
|
+
"forge studio bridge . --preview-port 5174 --target codex --studio-url http://127.0.0.1:3765 --json",
|
|
775
|
+
"forge studio doctor . --preview-port 5174 --target codex --json",
|
|
776
|
+
"forge dev --port 3766 --web-port 5174",
|
|
777
|
+
"forge dev --once --json",
|
|
778
|
+
]);
|
|
742
779
|
const studio = {
|
|
743
|
-
openCommand:
|
|
744
|
-
attachCommand:
|
|
745
|
-
snapshotCommand:
|
|
746
|
-
watchCommand:
|
|
747
|
-
bridgeCommand:
|
|
748
|
-
doctorCommand:
|
|
780
|
+
openCommand: studioOpenCommand,
|
|
781
|
+
attachCommand: studioAttachCommand,
|
|
782
|
+
snapshotCommand: studioSnapshotCommand,
|
|
783
|
+
watchCommand: studioWatchCommand,
|
|
784
|
+
bridgeCommand: studioBridgeCommand,
|
|
785
|
+
doctorCommand: studioDoctorCommand,
|
|
749
786
|
targetPreviewUrl: "http://127.0.0.1:5174",
|
|
750
|
-
startTargetAppCommand:
|
|
751
|
-
probeCommand:
|
|
787
|
+
startTargetAppCommand: studioStartTargetAppCommand,
|
|
788
|
+
probeCommand: studioProbeCommand,
|
|
752
789
|
useful: frontendPresent,
|
|
753
790
|
note: frontendPresent
|
|
754
791
|
? "Attach this app to Forge Studio as an external-agent workroom; Studio should preview the target app on 5174."
|
|
@@ -764,11 +801,13 @@ export function runStatusCommand(workspaceRoot: string): StatusCommandResult {
|
|
|
764
801
|
state: generatedState,
|
|
765
802
|
ready: generatedReady,
|
|
766
803
|
driftClean,
|
|
804
|
+
freshness: generatedNeedsCheck ? "unverified" : generatedState === "ready" ? "verified-or-unchanged" : "attention",
|
|
805
|
+
authoredGeneratedInputs,
|
|
767
806
|
missingArtifacts,
|
|
768
807
|
tableDrift,
|
|
769
|
-
safeDevCommand
|
|
770
|
-
checkCommand:
|
|
771
|
-
repairCommand:
|
|
808
|
+
safeDevCommand,
|
|
809
|
+
checkCommand: generatedCheckCommand,
|
|
810
|
+
repairCommand: generatedRepairCommand,
|
|
772
811
|
git: {
|
|
773
812
|
changedFiles: changed.total.count,
|
|
774
813
|
authoredFiles: authoredGitFiles,
|
|
@@ -809,8 +848,11 @@ export function runStatusCommand(workspaceRoot: string): StatusCommandResult {
|
|
|
809
848
|
},
|
|
810
849
|
},
|
|
811
850
|
git,
|
|
812
|
-
nextActions:
|
|
813
|
-
|
|
851
|
+
nextActions: forgeCliCommandsForWorkspace(
|
|
852
|
+
workspaceRoot,
|
|
853
|
+
ok
|
|
854
|
+
? [
|
|
855
|
+
...(generatedState === "check-needed" ? ["forge generate --check --json"] : []),
|
|
814
856
|
"forge handoff --json",
|
|
815
857
|
"forge changed --json",
|
|
816
858
|
"forge dev",
|
|
@@ -819,7 +861,7 @@ export function runStatusCommand(workspaceRoot: string): StatusCommandResult {
|
|
|
819
861
|
"forge inspect handoff --json",
|
|
820
862
|
"forge verify --changed",
|
|
821
863
|
]
|
|
822
|
-
|
|
864
|
+
: [
|
|
823
865
|
"forge generate",
|
|
824
866
|
"forge handoff --json",
|
|
825
867
|
"forge changed --json",
|
|
@@ -827,6 +869,7 @@ export function runStatusCommand(workspaceRoot: string): StatusCommandResult {
|
|
|
827
869
|
"forge inspect drift --json",
|
|
828
870
|
"forge agent prepare --target codex --json",
|
|
829
871
|
],
|
|
872
|
+
),
|
|
830
873
|
},
|
|
831
874
|
exitCode: ok ? 0 : 1,
|
|
832
875
|
};
|
|
@@ -1589,6 +1632,7 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
1589
1632
|
packageManager: command.packageManager,
|
|
1590
1633
|
install: command.install,
|
|
1591
1634
|
git: command.git,
|
|
1635
|
+
fieldTest: command.fieldTest,
|
|
1592
1636
|
forgePackageSpec: command.forgePackageSpec,
|
|
1593
1637
|
localForge: command.localForge,
|
|
1594
1638
|
workspaceRoot: command.workspaceRoot,
|
|
@@ -1651,7 +1695,7 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
1651
1695
|
concurrency: 4,
|
|
1652
1696
|
});
|
|
1653
1697
|
if (command.json) {
|
|
1654
|
-
process.stdout.write(formatJsonResult(buildGenerateJson(result)));
|
|
1698
|
+
process.stdout.write(formatJsonResult(buildGenerateJson(result, { workspaceRoot: command.workspaceRoot })));
|
|
1655
1699
|
} else {
|
|
1656
1700
|
process.stdout.write(formatAgentContractHuman(command.subcommand, result));
|
|
1657
1701
|
writeHumanGenerate(result);
|
|
@@ -2015,7 +2059,7 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
2015
2059
|
});
|
|
2016
2060
|
|
|
2017
2061
|
if (command.json) {
|
|
2018
|
-
process.stdout.write(formatJsonResult(buildGenerateJson(result)));
|
|
2062
|
+
process.stdout.write(formatJsonResult(buildGenerateJson(result, { workspaceRoot: command.workspaceRoot })));
|
|
2019
2063
|
} else {
|
|
2020
2064
|
writeHumanGenerate(result);
|
|
2021
2065
|
}
|
|
@@ -2025,7 +2069,7 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
2025
2069
|
case "add": {
|
|
2026
2070
|
const result = await runAddCommand(command.alias, command.options);
|
|
2027
2071
|
if (command.options.json) {
|
|
2028
|
-
process.stdout.write(formatJsonResult(buildAddJson(result)));
|
|
2072
|
+
process.stdout.write(formatJsonResult(buildAddJson(result, { workspaceRoot: command.options.workspaceRoot })));
|
|
2029
2073
|
} else {
|
|
2030
2074
|
writeHumanAdd(result);
|
|
2031
2075
|
}
|
|
@@ -2061,6 +2105,7 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
2061
2105
|
case "changed": {
|
|
2062
2106
|
const result = runChangedCommand(command.workspaceRoot, {
|
|
2063
2107
|
authoredOnly: command.authoredOnly,
|
|
2108
|
+
reviewOnly: command.reviewOnly,
|
|
2064
2109
|
});
|
|
2065
2110
|
if (command.json) {
|
|
2066
2111
|
process.stdout.write(formatJsonResult(result.data));
|
|
@@ -2171,11 +2216,12 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
|
|
|
2171
2216
|
return result.exitCode;
|
|
2172
2217
|
}
|
|
2173
2218
|
case "check": {
|
|
2174
|
-
const
|
|
2219
|
+
const checkWorkspaceRoot = process.cwd();
|
|
2220
|
+
const result = await runCheckCommand(checkWorkspaceRoot, {
|
|
2175
2221
|
strictSecrets: command.strictSecrets,
|
|
2176
2222
|
});
|
|
2177
2223
|
if (command.json) {
|
|
2178
|
-
process.stdout.write(formatJsonResult(buildCheckJson(result)));
|
|
2224
|
+
process.stdout.write(formatJsonResult(buildCheckJson(result, { workspaceRoot: checkWorkspaceRoot })));
|
|
2179
2225
|
} else {
|
|
2180
2226
|
writeHumanGenerate(result);
|
|
2181
2227
|
}
|