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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@ docs/backlog/
.cursor/*
!.cursor/rules/
!.cursor/rules/**
# Live MBA-staged cards (named after the connected model)
.cursor/rules/*.mdc
!.cursor/rules/npm-publish.mdc
.cursorignore
.cursorindexingignore
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ mba status

`mba models pull` downloads the GGUF (resume + sha256). HuggingFace repos take the digest from LFS metadata; other sources need `--sha256`. After verify it parses the header locally, writes a TODO-marked adapter (and a family tier if that family is new), empty BCB/TCB/`server_setup` bindings, and empty `instructions.md` (the model reads this later) plus `notes.md` (you read this; never injected). A failed verify deletes the partial and leaves no scaffold.

`mba s boot` resolves that adapter tree into llama.cpp flags — the same chain as the preview — then boots. `mba models search` is the interactive HuggingFace path into the same pull. `mba models stage` copies a non-empty winning `instructions.md` into a file the harness already injects (`CLAUDE.local.md`, Cursor rules, …). `mba connect` does that and mints a Bearer token; once any session exists, chat through the MBA proxy requires it. `notes.md` stays in the store.
`mba s boot` resolves that adapter tree into llama.cpp flags — the same chain as the preview — then boots. `mba models search` is the interactive HuggingFace path into the same pull. `mba models stage` copies a non-empty winning `instructions.md` into the file the harness already injects (`CLAUDE.local.md`, `.cursor/rules/mba.mdc`, …). The model id is in that card; the filename stays the harness slot. `mba connect` does that and mints a Bearer token; once any session exists, chat through the MBA proxy requires it. `notes.md` stays in the store.

## CLI

Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/cli/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Client plane: list paired sessions, add operator clients, connect, revoke.
*/

import { formatClientLabel } from "../service/env-context.js";
import { fail, serviceGet, servicePost } from "./client.js";
import { listHarnessChoices } from "./harness-choices.js";
import { askValueInteractive, pickLabeledInteractive } from "./interactive.js";
Expand All @@ -20,6 +21,7 @@ interface PublicSession {
readonly ide?: string;
readonly projectRoot: string;
readonly createdAt: string;
readonly card?: boolean;
}

async function listSessions(baseUrl: string): Promise<readonly PublicSession[]> {
Expand All @@ -30,9 +32,10 @@ async function listSessions(baseUrl: string): Promise<readonly PublicSession[]>
}

function printSession(s: PublicSession): void {
const who = s.ide ? `${s.harness}+${s.ide}` : s.harness;
const who = formatClientLabel(s.harness, s.ide);
const tag = (s.card ? "card" : "pair").padEnd(4);
process.stdout.write(
` ${paint(who.padEnd(18), BOLD)} ${s.modelId.padEnd(22)} ${dim(shortenHome(s.projectRoot))}\n`,
` ${paint(who.padEnd(18), BOLD)} ${s.modelId.padEnd(22)} ${s.card ? paint(tag, BOLD) : dim(tag)} ${dim(shortenHome(s.projectRoot))}\n`,
);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/cli/harness-choices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* operator-defined clients in mba/clients.json.
*/

import { builtInEnvelopeBindings, envelopeRelativePath } from "../mba/envelope.js";
import { builtInEnvelopeBindings } from "../mba/envelope.js";
import { defaultStorePaths } from "../service/config-store.js";
import { readOperatorClients } from "../service/operator-clients.js";

Expand Down Expand Up @@ -37,7 +37,7 @@ export function harnessPickerRows(): Array<{
label: h.source === "added" ? `${h.name} (added)` : h.name,
value: h.name,
preview: [
["envelope", envelopeRelativePath(h.name, undefined, [{ name: h.name, envelope: h.envelope }]) ?? h.envelope],
["envelope", h.envelope],
["source", h.source],
] as const,
}));
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/cli/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,14 @@ interface ConnectResult {
readonly harness: string;
readonly projectRoot: string;
readonly stage:
| { readonly action: string; readonly reason?: string; readonly envelope?: string; readonly dest?: string }
| {
readonly action: string;
readonly reason?: string;
readonly envelope?: string;
readonly dest?: string;
readonly owner?: string;
readonly replaced?: string;
}
| { readonly action: "conflict"; readonly error: string };
}

Expand Down Expand Up @@ -554,6 +561,11 @@ export async function cmdModelsConnect(
process.stdout.write(`[mba] point the client at ${baseUrl}/v1 (Authorization: Bearer <token>)\n`);
if ("envelope" in result.stage && result.stage.action === "wrote") {
process.stdout.write(`[mba] staged → ${result.stage.envelope}\n`);
if (result.stage.replaced) {
process.stdout.write(
`[mba] envelope now ${result.modelId} (replaced ${result.stage.replaced})\n`,
);
}
} else if ("error" in result.stage) {
process.stdout.write(`[mba] card not overwritten — ${result.stage.error}\n`);
}
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/cli/status.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { formatClientLabel } from "../service/env-context.js";
import { resolveServiceUrl, serviceGet } from "./client.js";
import { brand, dim, heading, kv, paint, shortenHome, BOLD, GRN, RED } from "./style.js";
import type { ModelEntry } from "./interactive.js";
Expand All @@ -10,6 +11,7 @@ interface PublicSession {
readonly ide?: string;
readonly projectRoot: string;
readonly createdAt: string;
readonly card?: boolean;
}

interface StatusBody {
Expand All @@ -29,9 +31,10 @@ function printServerRow(s: ServerEntry): void {
}

function printSessionRow(s: PublicSession): void {
const who = s.ide ? `${s.harness}+${s.ide}` : s.harness;
const who = formatClientLabel(s.harness, s.ide);
const tag = (s.card ? "card" : "pair").padEnd(4);
process.stdout.write(
` ${paint(who.padEnd(18), BOLD)} ${s.modelId.padEnd(22)} ${dim(shortenHome(s.projectRoot))}\n`,
` ${paint(who.padEnd(18), BOLD)} ${s.modelId.padEnd(22)} ${s.card ? paint(tag, GRN) : dim(tag)} ${dim(shortenHome(s.projectRoot))}\n`,
);
}

Expand Down
58 changes: 57 additions & 1 deletion packages/core/src/mba/envelope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,36 @@ import { describe, expect, it } from "vitest";
import {
KNOWN_HARNESSES,
MBA_STAGE_MARKER,
envelopeFileStem,
envelopeRelativePath,
isMbaStaged,
normalizeHarness,
stagedModelId,
wrapStagedCard,
} from "./envelope.js";

describe("envelopeRelativePath", () => {
it("maps each known harness to a slot the client already injects", () => {
expect(envelopeRelativePath("claude-code")).toBe("CLAUDE.local.md");
expect(envelopeRelativePath("claude-code", undefined, [], "deepseek_test")).toBe(
"CLAUDE.local.md",
);
expect(envelopeRelativePath("cursor")).toBe(".cursor/rules/mba.mdc");
expect(envelopeRelativePath("cursor", undefined, [], "deepseek_test")).toBe(
".cursor/rules/mba.mdc",
);
expect(envelopeRelativePath("cline")).toBe(".clinerules/mba.md");
expect(envelopeRelativePath("cline", undefined, [], "deepseek_test")).toBe(
".clinerules/mba.md",
);
expect(envelopeRelativePath("copilot")).toBe(".github/instructions/mba.instructions.md");
expect(envelopeRelativePath("copilot", undefined, [], "deepseek_test")).toBe(
".github/instructions/mba.instructions.md",
);
expect(envelopeRelativePath("continue")).toBe(".continue/rules/mba.md");
expect(envelopeRelativePath("continue", undefined, [], "deepseek_test")).toBe(
".continue/rules/mba.md",
);
});

it("folds aliases onto the closed set", () => {
Expand All @@ -40,6 +57,11 @@ describe("envelopeRelativePath", () => {
{ name: "windsurf", envelope: ".windsurf/mba.md" },
]),
).toBe(".cursor/rules/mba.mdc");
expect(
envelopeRelativePath("windsurf", undefined, [
{ name: "windsurf", envelope: ".windsurf/{model}.md" },
], "deepseek_test"),
).toBe(".windsurf/deepseek_test.md");
});

it("keeps the closed set small", () => {
Expand All @@ -56,11 +78,19 @@ describe("wrapStagedCard", () => {
});

it("adds Cursor alwaysApply frontmatter", () => {
const wrapped = wrapStagedCard("cursor", "# card");
const wrapped = wrapStagedCard("cursor", "# card", "deepseek_test");
expect(wrapped).toContain("alwaysApply: true");
expect(wrapped).toContain('"deepseek_test model card (live copy; do not edit)"');
expect(wrapped).toContain("<!-- mba-model: deepseek_test -->");
expect(isMbaStaged(wrapped)).toBe(true);
});

it("names the model in the staged marker for every harness", () => {
const wrapped = wrapStagedCard("claude-code", "# card", "deepseek_test");
expect(wrapped).toContain("<!-- mba-model: deepseek_test -->");
expect(wrapped.startsWith(MBA_STAGE_MARKER)).toBe(true);
});

it("adds Copilot applyTo frontmatter", () => {
const wrapped = wrapStagedCard("copilot", "# card");
expect(wrapped).toContain('applyTo: "**"');
Expand All @@ -71,3 +101,29 @@ describe("wrapStagedCard", () => {
expect(wrapped).toBe(`${MBA_STAGE_MARKER}\n\n# card\n`);
});
});

describe("envelopeFileStem", () => {
it("keeps a safe model id and falls back to mba", () => {
expect(envelopeFileStem("deepseek_test")).toBe("deepseek_test");
expect(envelopeFileStem("qwen3-coder-30b")).toBe("qwen3-coder-30b");
expect(envelopeFileStem("../odd name")).toBe("odd-name");
expect(envelopeFileStem("...")).toBe("mba");
expect(envelopeFileStem("")).toBe("mba");
expect(envelopeFileStem(undefined)).toBe("mba");
});
});

describe("stagedModelId", () => {
it("reads the model marker from a wrapped card", () => {
const wrapped = wrapStagedCard("cursor", "# card", "deepseek_test");
expect(stagedModelId(wrapped)).toBe("deepseek_test");
expect(stagedModelId("# no marker")).toBeUndefined();
});

it("does not let a model id close the HTML comment", () => {
const wrapped = wrapStagedCard("cline", "# card", "foo-->bar");
expect(wrapped).toContain("<!-- mba-model: foo-bar -->");
expect(wrapped).not.toContain("-->bar");
expect(stagedModelId(wrapped)).toBe("foo-bar");
});
});
104 changes: 95 additions & 9 deletions packages/core/src/mba/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,45 +62,131 @@ export function builtInEnvelopeBindings(): readonly EnvelopeBinding[] {
return KNOWN_HARNESSES.map((name) => ({ name, envelope: ENVELOPE_BY_HARNESS[name] }));
}

/**
* Filesystem-safe stem when an operator envelope uses `{model}`.
* Built-in slots do not use this — they are the filenames the harness
* already injects. Empty / junk → `mba`.
*/
function isStemChar(ch: string): boolean {
return (
(ch >= "A" && ch <= "Z") ||
(ch >= "a" && ch <= "z") ||
(ch >= "0" && ch <= "9") ||
ch === "." ||
ch === "_" ||
ch === "-"
);
}

export function envelopeFileStem(modelId?: string): string {
// Walk the id; no regex (CodeQL js/polynomial-redos on `[-.]+`).
let stem = "";
for (const ch of (modelId ?? "").trim()) {
if (isStemChar(ch)) stem += ch;
else if (stem.length > 0 && !stem.endsWith("-")) stem += "-";
}
while (stem.startsWith("-") || stem.startsWith(".")) stem = stem.slice(1);
while (stem.endsWith("-") || stem.endsWith(".")) stem = stem.slice(0, -1);
return stem.length > 0 ? stem.slice(0, 64) : "mba";
}

function fillEnvelopeTemplate(template: string, modelId?: string): string {
if (!template.includes("{model}")) return template;
return template.replaceAll("{model}", envelopeFileStem(modelId));
}

function modelMarkerPayload(modelId?: string): string {
// Strip comment terminators without a HTML-filter regex (CodeQL js/bad-tag-filter).
let raw = (modelId ?? "").trim();
while (raw.includes("--")) raw = raw.split("--").join("-");
let out = "";
for (const ch of raw) {
if (ch !== ">") out += ch;
}
return out.trim();
}

function modelMarkerLine(modelId?: string): string {
const raw = modelMarkerPayload(modelId);
return raw.length > 0 ? `\n<!-- mba-model: ${raw} -->` : "";
}

/**
* Project-relative path the harness already injects.
* `ide` is accepted so the door matches env (`harness` + `ide`); this cut
* keys the filename on harness. Unknown harness with no extra → undefined.
* keys the filename on harness. Built-in paths are stable so the client
* keeps injecting the same slot; `modelId` is written into the card body.
* An added client may put `{model}` in its envelope. Unknown harness with
* no extra → undefined.
*/
export function envelopeRelativePath(
harness: string,
_ide?: string,
extras: readonly EnvelopeBinding[] = [],
modelId?: string,
): string | undefined {
const known = normalizeHarness(harness);
if (known) return ENVELOPE_BY_HARNESS[known];
if (known) return fillEnvelopeTemplate(ENVELOPE_BY_HARNESS[known], modelId);
const key = compactHarnessKey(harness);
const extra = extras.find((e) => compactHarnessKey(e.name) === key);
return extra?.envelope;
return extra ? fillEnvelopeTemplate(extra.envelope, modelId) : undefined;
}

export function isMbaStaged(text: string): boolean {
return text.includes(MBA_STAGE_MARKER);
}

function isSpace(ch: string | undefined): boolean {
return ch === " " || ch === "\t" || ch === "\n" || ch === "\r";
}

/** Model id planted in a staged envelope, if present. */
export function stagedModelId(text: string): string | undefined {
const needle = "mba-model:";
let from = 0;
while (from < text.length) {
const tag = text.indexOf(needle, from);
if (tag < 0) return undefined;
const open = text.lastIndexOf("<!--", tag);
if (open >= 0 && text.slice(open + 4, tag).trim() === "") {
let p = tag + needle.length;
while (isSpace(text[p])) p += 1;
const idStart = p;
while (p < text.length) {
const c = text[p]!;
if (isSpace(c) || c === ">") break;
p += 1;
}
const id = text.slice(idStart, p);
if (id.length > 0 && text.indexOf("-->", p) >= 0) return id;
}
from = tag + needle.length;
}
return undefined;
}

/** Wrap store card text so the harness file is identifiable and (when needed) always-on. */
export function wrapStagedCard(harness: string, body: string): string {
const trimmed = body.replace(/^\uFEFF/, "").trimEnd() + "\n";
export function wrapStagedCard(harness: string, body: string, modelId?: string): string {
const withoutBom = body.startsWith("\uFEFF") ? body.slice(1) : body;
const trimmed = withoutBom.trimEnd() + "\n";
const known = normalizeHarness(harness);
const marker = `${MBA_STAGE_MARKER}${modelMarkerLine(modelId)}`;
if (known === "cursor") {
const label = modelId && modelId.trim().length > 0 ? modelId.trim() : "MBA";
const description = JSON.stringify(`${label} model card (live copy; do not edit)`);
return [
"---",
"description: MBA model card (live copy; do not edit)",
`description: ${description}`,
"alwaysApply: true",
"---",
"",
MBA_STAGE_MARKER,
marker,
"",
trimmed,
].join("\n");
}
if (known === "copilot") {
return ["---", 'applyTo: "**"', "---", "", MBA_STAGE_MARKER, "", trimmed].join("\n");
return ["---", 'applyTo: "**"', "---", "", marker, "", trimmed].join("\n");
}
return `${MBA_STAGE_MARKER}\n\n${trimmed}`;
return `${marker}\n\n${trimmed}`;
}
Loading
Loading