Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
477 changes: 388 additions & 89 deletions plugins/flow/mcp-server/dist/cli.js

Large diffs are not rendered by default.

465 changes: 382 additions & 83 deletions plugins/flow/mcp-server/dist/index.js

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions plugins/flow/mcp-server/src/__tests__/dev-pre-pr-gate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,24 @@ async function setupRepo(): Promise<TestContext> {
const srcDir = path.join(repoRoot, "src");
await fs.mkdir(srcDir, { recursive: true });
await atomicWriteFile(path.join(srcDir, "index.ts"), "export const x = 1;\n");

// Flow-SHAPED build home so the structural toolchain resolver (Story
// native:01KVTB3Z) resolves cwd=plugins/flow + packageManager=pnpm + a knip
// script (so the bloat gate runs) PURELY from on-disk structure — no
// `.flow/config.yaml`. Mirrors the real Flow repo's dogfood path.
const flowDir = path.join(repoRoot, "plugins", "flow");
await fs.mkdir(flowDir, { recursive: true });
await atomicWriteFile(
path.join(flowDir, "package.json"),
JSON.stringify(
{ name: "flow", private: true, scripts: { build: "pnpm -r build", test: "pnpm -r test", knip: "knip --no-progress" } },
null,
2,
),
);
await atomicWriteFile(path.join(flowDir, "pnpm-workspace.yaml"), "packages:\n - mcp-server\n");
await atomicWriteFile(path.join(flowDir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n");

await realExeca("git", ["-C", repoRoot, "add", "."]);
await realExeca("git", ["-C", repoRoot, "commit", "-m", "chore: initial commit"]);

Expand Down
11 changes: 11 additions & 0 deletions plugins/flow/mcp-server/src/__tests__/inline-ac-extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ async function setupRepo(): Promise<TestContext> {
const srcDir = path.join(repoRoot, "src");
await fs.mkdir(srcDir, { recursive: true });
await atomicWriteFile(path.join(srcDir, "index.ts"), "export const x = 1;\n");
// Flow-shaped build home (Story native:01KVTB3Z) so the structural toolchain
// resolver lands on plugins/flow + pnpm. NO `knip` script here, so the bloat
// gate is SKIPPED (knipCmd null) — this fixture's stub only handles build/test.
const flowDir = path.join(repoRoot, "plugins", "flow");
await fs.mkdir(flowDir, { recursive: true });
await atomicWriteFile(
path.join(flowDir, "package.json"),
JSON.stringify({ name: "flow", private: true, scripts: { build: "pnpm -r build", test: "pnpm -r test" } }, null, 2),
);
await atomicWriteFile(path.join(flowDir, "pnpm-workspace.yaml"), "packages:\n - mcp-server\n");
await atomicWriteFile(path.join(flowDir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n");
await realExeca("git", ["-C", repoRoot, "add", "."]);
await realExeca("git", ["-C", repoRoot, "commit", "-m", "chore: initial commit"]);

Expand Down
26 changes: 26 additions & 0 deletions plugins/flow/mcp-server/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@ export class InvalidWorkspaceConfigError extends DomainError {
}
}

/**
* `.flow/config.yaml` carries a `build:` block (the toolchain escape hatch) that
* failed schema validation — e.g. an unrecognised `packageManager` value, or a
* non-string command field. The resolver surfaces this TYPED error rather than
* silently falling back to structural detection, so a deliberate-but-malformed
* override is loud (the operator fixes the block) instead of being quietly
* ignored. (Story native:01KVTB3Z AC3.)
*/
export class ToolchainConfigError extends DomainError {
readonly configPath: string;
readonly yamlPath: string;
readonly detail: string;

constructor(opts: { configPath: string; yamlPath: string; detail: string }) {
super(
`${opts.configPath} has an invalid 'build:' block at '${opts.yamlPath}': ${opts.detail}. ` +
`Fix the build override (or remove it to fall back to structural toolchain ` +
`detection). Recognised packageManager values: pnpm | npm | yarn | bun. ` +
`See mcp-server/src/lib/resolve-project-toolchain.ts.`,
);
this.configPath = opts.configPath;
this.yamlPath = opts.yamlPath;
this.detail = opts.detail;
}
}

/**
* `.flow/config.yaml` declares an `adapter:` name that does not match
* any registered adapter. The user must either install the matching
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Test helper: seed a Flow-SHAPED build home under a tmpdir repo root.
*
* Story native:01KVTB3Z made the dev pre-PR gates and the reviewer derive their
* build/test/bloat toolchain from `resolveProjectToolchain`, which detects the
* build home STRUCTURALLY (a pnpm-workspace.yaml whose root package.json owns a
* `build` script → `plugins/flow`, pnpm). Tests that previously relied on the
* hardcoded `<repo>/plugins/flow` + `pnpm` assumption must now seed that on-disk
* structure so the resolver lands on the same place — mirroring the real Flow
* repo's dogfood path on a clean worktree (where `.flow/config.yaml`, being
* gitignored, is absent).
*
* Writes:
* <repoRoot>/plugins/flow/package.json (scripts: build/test/knip)
* <repoRoot>/plugins/flow/pnpm-workspace.yaml (packages: [mcp-server])
* <repoRoot>/plugins/flow/pnpm-lock.yaml (so PM detects pnpm)
*/

import * as path from "node:path";
import { promises as fs } from "node:fs";

/** Seed the Flow-shaped build home at `<repoRoot>/plugins/flow`. */
export async function seedFlowShapedBuildHome(repoRoot: string): Promise<string> {
const flowDir = path.join(repoRoot, "plugins", "flow");
await fs.mkdir(flowDir, { recursive: true });
await fs.writeFile(
path.join(flowDir, "package.json"),
JSON.stringify(
{
name: "flow",
private: true,
scripts: {
build: "pnpm -r build",
test: "pnpm -r test",
knip: "knip --no-progress",
},
},
null,
2,
),
"utf8",
);
await fs.writeFile(
path.join(flowDir, "pnpm-workspace.yaml"),
"packages:\n - mcp-server\n",
"utf8",
);
await fs.writeFile(
path.join(flowDir, "pnpm-lock.yaml"),
"lockfileVersion: '9.0'\n",
"utf8",
);
return flowDir;
}
Loading
Loading