@m8t-stack/cli 0.2.12 → 0.2.13
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/dist/cli.js +140 -23
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1195,7 +1195,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1195
1195
|
import { Builtins, Cli } from "clipanion";
|
|
1196
1196
|
|
|
1197
1197
|
// src/lib/package-version.ts
|
|
1198
|
-
var CLI_VERSION = "0.2.
|
|
1198
|
+
var CLI_VERSION = "0.2.13";
|
|
1199
1199
|
|
|
1200
1200
|
// src/lib/render-error.ts
|
|
1201
1201
|
init_errors();
|
|
@@ -15064,10 +15064,9 @@ async function deployPromptAdvisor(args) {
|
|
|
15064
15064
|
const fillableFields = fm["fillable-fields"] ?? [];
|
|
15065
15065
|
const valuesMap = {};
|
|
15066
15066
|
for (const field of fillableFields) {
|
|
15067
|
-
if (field.default !== void 0)
|
|
15068
|
-
valuesMap[field.name] = field.default;
|
|
15069
|
-
}
|
|
15067
|
+
if (field.default !== void 0) valuesMap[field.name] = field.default;
|
|
15070
15068
|
}
|
|
15069
|
+
for (const [k, v] of Object.entries(args.fieldOverrides ?? {})) valuesMap[k] = v;
|
|
15071
15070
|
const instructions = renderPersonaBody(personaPath, valuesMap);
|
|
15072
15071
|
return createPromptVersion({
|
|
15073
15072
|
credential: args.credential,
|
|
@@ -23160,9 +23159,9 @@ function companyProfileBodyEquals(a, b) {
|
|
|
23160
23159
|
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
23161
23160
|
return strip(a) === strip(b);
|
|
23162
23161
|
}
|
|
23163
|
-
function upsertMemoryIndex(existing, line2) {
|
|
23162
|
+
function upsertMemoryIndex(existing, line2, targetPath2 = COMPANY_PROFILE_PATH) {
|
|
23164
23163
|
const lines = existing.split("\n");
|
|
23165
|
-
const existingIdx = lines.findIndex((l) => l.includes(`\`${
|
|
23164
|
+
const existingIdx = lines.findIndex((l) => l.includes(`\`${targetPath2}\``));
|
|
23166
23165
|
if (existingIdx >= 0) {
|
|
23167
23166
|
lines[existingIdx] = line2;
|
|
23168
23167
|
return lines.join("\n");
|
|
@@ -23174,6 +23173,88 @@ function upsertMemoryIndex(existing, line2) {
|
|
|
23174
23173
|
}
|
|
23175
23174
|
return existing.replace(/\s*$/, "\n\n") + line2 + "\n";
|
|
23176
23175
|
}
|
|
23176
|
+
var FOUNDER_RECORD_PATH = "memory/founder.md";
|
|
23177
|
+
var NOT_CAPTURED = "_not captured yet \u2014 add it anytime (run `m8t bootstrap seed-profile`, or just tell me)_";
|
|
23178
|
+
function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
23179
|
+
const pick = (...vals) => vals.map((v) => v?.trim()).find(Boolean) ?? "";
|
|
23180
|
+
const founderName = pick(block.founder_name, inputs.azIdentity?.name);
|
|
23181
|
+
const founderEmail = pick(block.founder_email, inputs.azIdentity?.email);
|
|
23182
|
+
const advisorName = (block.advisor_name ?? "").trim();
|
|
23183
|
+
const advisorEmail = (block.advisor_email ?? "").trim();
|
|
23184
|
+
const subscription = (inputs.subscriptionId ?? "").trim();
|
|
23185
|
+
const teamSize = (block.team_size ?? "").trim();
|
|
23186
|
+
const advisorRendered = advisorName || advisorEmail ? [advisorName, advisorEmail ? `<${advisorEmail}>` : ""].filter(Boolean).join(" ") : NOT_CAPTURED;
|
|
23187
|
+
const founderMd = [
|
|
23188
|
+
`---`,
|
|
23189
|
+
`type: memory`,
|
|
23190
|
+
`title: "Founder & install context"`,
|
|
23191
|
+
`created: ${now}`,
|
|
23192
|
+
`updated: ${now}`,
|
|
23193
|
+
`tags: [founder, install-context, onboarding]`,
|
|
23194
|
+
`origin: operator`,
|
|
23195
|
+
`---`,
|
|
23196
|
+
``,
|
|
23197
|
+
`# Founder & install context`,
|
|
23198
|
+
``,
|
|
23199
|
+
`_Seeded at onboarding from your Azure identity + the questionnaire. Authoritative \u2014 read it; don't rewrite it (origin: operator)._`,
|
|
23200
|
+
``,
|
|
23201
|
+
`- **Founder:** ${founderName || NOT_CAPTURED}`,
|
|
23202
|
+
`- **Founder email (company_email):** ${founderEmail || NOT_CAPTURED}`,
|
|
23203
|
+
`- **Microsoft Startup Advisor (SA):** ${advisorRendered}`,
|
|
23204
|
+
`- **Azure subscription:** ${subscription || NOT_CAPTURED}`,
|
|
23205
|
+
`- **Team size:** ${teamSize || "\u2014"}`,
|
|
23206
|
+
``
|
|
23207
|
+
].join("\n");
|
|
23208
|
+
const idxAdvisor = advisorEmail || advisorName || "\u2014";
|
|
23209
|
+
const idxSub = subscription ? `${subscription.slice(0, 8)}\u2026` : "\u2014";
|
|
23210
|
+
const memoryIndexLine = `- \`${FOUNDER_RECORD_PATH}\` \u2014 **Founder & install context**: ${founderName || "founder"} \xB7 advisor ${idxAdvisor} \xB7 sub ${idxSub}. (seeded from onboarding)`;
|
|
23211
|
+
return { founderMd, memoryIndexLine };
|
|
23212
|
+
}
|
|
23213
|
+
function founderRecordBodyEquals(a, b) {
|
|
23214
|
+
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
23215
|
+
return strip(a) === strip(b);
|
|
23216
|
+
}
|
|
23217
|
+
|
|
23218
|
+
// src/lib/founder-identity.ts
|
|
23219
|
+
function deriveEmailCandidate(raw) {
|
|
23220
|
+
const mail = (raw.mail ?? "").trim();
|
|
23221
|
+
if (mail) return mail;
|
|
23222
|
+
const upn = (raw.upn ?? "").trim();
|
|
23223
|
+
if (!upn) return "";
|
|
23224
|
+
const ext = upn.indexOf("#EXT#");
|
|
23225
|
+
if (ext > 0) {
|
|
23226
|
+
const local = upn.slice(0, ext);
|
|
23227
|
+
const at = local.lastIndexOf("_");
|
|
23228
|
+
return at > 0 ? `${local.slice(0, at)}@${local.slice(at + 1)}` : "";
|
|
23229
|
+
}
|
|
23230
|
+
return upn.includes("@") ? upn : "";
|
|
23231
|
+
}
|
|
23232
|
+
async function getSignedInUserIdentity(runAzImpl = runAz) {
|
|
23233
|
+
try {
|
|
23234
|
+
const raw = await runAzImpl([
|
|
23235
|
+
"ad",
|
|
23236
|
+
"signed-in-user",
|
|
23237
|
+
"show",
|
|
23238
|
+
"--query",
|
|
23239
|
+
"{displayName:displayName, mail:mail, upn:userPrincipalName}",
|
|
23240
|
+
"-o",
|
|
23241
|
+
"json"
|
|
23242
|
+
]);
|
|
23243
|
+
const p = JSON.parse(raw);
|
|
23244
|
+
return { name: (p.displayName ?? "").trim(), email: deriveEmailCandidate({ mail: p.mail, upn: p.upn }) };
|
|
23245
|
+
} catch {
|
|
23246
|
+
return { name: "", email: "" };
|
|
23247
|
+
}
|
|
23248
|
+
}
|
|
23249
|
+
function composeFounderIdentityNote(id) {
|
|
23250
|
+
if (id.name && id.email) {
|
|
23251
|
+
return `You're speaking with **${id.name}**, signed in to Azure (their email looks like **${id.email}**). Open by confirming both, in your own voice \u2014 e.g. "I see you're signed in as ${id.name}, and I'll use ${id.email} for your advisor handoffs and cost reports \u2014 is that right, or should I change either?" Record exactly what they approve or correct.`;
|
|
23252
|
+
}
|
|
23253
|
+
if (id.name) {
|
|
23254
|
+
return `You're speaking with **${id.name}** (signed in to Azure). Confirm their name, then ask for the best contact email to reach them (that's where advisor handoffs and cost reports go).`;
|
|
23255
|
+
}
|
|
23256
|
+
return `Ask the founder their name and the best contact email to reach them (that's where advisor handoffs and cost reports go).`;
|
|
23257
|
+
}
|
|
23177
23258
|
|
|
23178
23259
|
// src/lib/company-profile-seed.ts
|
|
23179
23260
|
function readGithubAppCreds(credsPath) {
|
|
@@ -23188,18 +23269,21 @@ async function resolveSeedContext(opts) {
|
|
|
23188
23269
|
const appCreds = opts.appCredsOverride ?? readGithubAppCreds(opts.credsPath);
|
|
23189
23270
|
if (!appCreds) return null;
|
|
23190
23271
|
let endpoint = opts.endpointOverride;
|
|
23272
|
+
let subscriptionId = opts.subscriptionId;
|
|
23191
23273
|
if (!endpoint) {
|
|
23192
23274
|
const state = await readBootstrapState();
|
|
23193
23275
|
if (!state) {
|
|
23194
23276
|
throw new LocalCliError({ code: "BOOTSTRAP_NO_STATE", message: "No bootstrap state found.", hint: "Run 'm8t bootstrap launch' first, or pass --endpoint." });
|
|
23195
23277
|
}
|
|
23278
|
+
subscriptionId ??= state.subscriptionId;
|
|
23196
23279
|
const doc = await readStatusBlob({ saName: state.statusSaName, resourceGroup: state.resourceGroup, subscriptionId: state.subscriptionId });
|
|
23197
23280
|
endpoint = doc.result?.foundryEndpoint;
|
|
23198
23281
|
if (!endpoint) {
|
|
23199
23282
|
throw new LocalCliError({ code: "SEED_NO_ENDPOINT", message: "Could not resolve the Foundry endpoint from the install status.", hint: "Pass --endpoint <foundryEndpoint>." });
|
|
23200
23283
|
}
|
|
23201
23284
|
}
|
|
23202
|
-
|
|
23285
|
+
const brainRepos = opts.brainOverride ? [opts.brainOverride] : [`${appCreds.org}/stacey-brain`, `${appCreds.org}/azzy-brain`];
|
|
23286
|
+
return { endpoint, brainRepos, appCreds, subscriptionId };
|
|
23203
23287
|
}
|
|
23204
23288
|
async function applyProfileToBrain(args) {
|
|
23205
23289
|
const token = await mintInstallationTokenFromPem({
|
|
@@ -23208,20 +23292,31 @@ async function applyProfileToBrain(args) {
|
|
|
23208
23292
|
installationId: args.appCreds.installationId,
|
|
23209
23293
|
fetchImpl: args.fetchImpl
|
|
23210
23294
|
});
|
|
23211
|
-
const { profileMd, memoryIndexLine } = renderCompanyProfile(args.block, args.now);
|
|
23212
|
-
const
|
|
23213
|
-
|
|
23214
|
-
|
|
23215
|
-
|
|
23295
|
+
const { profileMd, memoryIndexLine: companyLine } = renderCompanyProfile(args.block, args.now);
|
|
23296
|
+
const { founderMd, memoryIndexLine: founderLine } = renderFounderRecord(
|
|
23297
|
+
args.block,
|
|
23298
|
+
{ subscriptionId: args.subscriptionId, azIdentity: args.azIdentity },
|
|
23299
|
+
args.now
|
|
23300
|
+
);
|
|
23301
|
+
const read = (p) => readRepoFileViaApp({ token, repo: args.brainRepo, path: p, fetchImpl: args.fetchImpl });
|
|
23302
|
+
const existingProfile = await read(COMPANY_PROFILE_PATH);
|
|
23303
|
+
const existingFounder = await read(FOUNDER_RECORD_PATH);
|
|
23304
|
+
const existingIndex = await read("memory/MEMORY.md") ?? DEFAULT_MEMORY_INDEX_HEADER;
|
|
23305
|
+
let nextIndex = upsertMemoryIndex(existingIndex, companyLine, COMPANY_PROFILE_PATH);
|
|
23306
|
+
nextIndex = upsertMemoryIndex(nextIndex, founderLine, FOUNDER_RECORD_PATH);
|
|
23307
|
+
const profileSame = existingProfile != null && companyProfileBodyEquals(existingProfile, profileMd);
|
|
23308
|
+
const founderSame = existingFounder != null && founderRecordBodyEquals(existingFounder, founderMd);
|
|
23309
|
+
if (profileSame && founderSame && nextIndex === existingIndex) {
|
|
23216
23310
|
return;
|
|
23217
23311
|
}
|
|
23218
23312
|
await commitFilesViaApp({
|
|
23219
23313
|
token,
|
|
23220
23314
|
repo: args.brainRepo,
|
|
23221
23315
|
branch: args.branch,
|
|
23222
|
-
message: "seed(brain): company profile from onboarding
|
|
23316
|
+
message: "seed(brain): founder + company profile from onboarding",
|
|
23223
23317
|
files: [
|
|
23224
23318
|
{ path: COMPANY_PROFILE_PATH, content: profileMd },
|
|
23319
|
+
{ path: FOUNDER_RECORD_PATH, content: founderMd },
|
|
23225
23320
|
{ path: "memory/MEMORY.md", content: nextIndex }
|
|
23226
23321
|
],
|
|
23227
23322
|
fetchImpl: args.fetchImpl
|
|
@@ -23241,20 +23336,31 @@ function spawnDetachedSeedWatch() {
|
|
|
23241
23336
|
}
|
|
23242
23337
|
}
|
|
23243
23338
|
async function reactiveSeedOnFinish(args) {
|
|
23244
|
-
const ctx = await resolveSeedContext({ endpointOverride: args.endpoint, appCredsOverride: args.appCredsOverride });
|
|
23339
|
+
const ctx = await resolveSeedContext({ endpointOverride: args.endpoint, appCredsOverride: args.appCredsOverride, subscriptionId: args.subscriptionId });
|
|
23245
23340
|
if (!ctx) return;
|
|
23246
23341
|
const token = await (args.getFoundryTokenImpl ?? getFoundryToken)();
|
|
23247
23342
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token, fetchImpl: args.fetchImpl });
|
|
23248
23343
|
if (block) {
|
|
23249
|
-
|
|
23250
|
-
|
|
23344
|
+
const azIdentity = args.azIdentity ?? await (args.getSignedInUserIdentityImpl ?? getSignedInUserIdentity)();
|
|
23345
|
+
for (const brainRepo of ctx.brainRepos) {
|
|
23346
|
+
await applyProfileToBrain({
|
|
23347
|
+
block,
|
|
23348
|
+
brainRepo,
|
|
23349
|
+
branch: "main",
|
|
23350
|
+
appCreds: ctx.appCreds,
|
|
23351
|
+
subscriptionId: ctx.subscriptionId,
|
|
23352
|
+
azIdentity,
|
|
23353
|
+
fetchImpl: args.fetchImpl
|
|
23354
|
+
});
|
|
23355
|
+
}
|
|
23356
|
+
args.stdout(`${colors.success("\u2713")} Your advisors now know your company + how to reach you (seeded ${ctx.brainRepos.join(", ")}).
|
|
23251
23357
|
`);
|
|
23252
23358
|
return;
|
|
23253
23359
|
}
|
|
23254
23360
|
if (!hadIntake) return;
|
|
23255
23361
|
(args.spawnWatch ?? spawnDetachedSeedWatch)();
|
|
23256
23362
|
args.stdout(
|
|
23257
|
-
`${colors.dim("\u2139
|
|
23363
|
+
`${colors.dim("\u2139 Your advisors will pick up your company profile when you finish the questionnaire (watching in the background).")}
|
|
23258
23364
|
${colors.hint("or run:")} m8t bootstrap seed-profile
|
|
23259
23365
|
`
|
|
23260
23366
|
);
|
|
@@ -23371,6 +23477,7 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
|
|
|
23371
23477
|
try {
|
|
23372
23478
|
await reactiveSeedOnFinish({
|
|
23373
23479
|
endpoint: doc.result?.foundryEndpoint,
|
|
23480
|
+
subscriptionId,
|
|
23374
23481
|
stdout: (s) => this.context.stdout.write(s)
|
|
23375
23482
|
});
|
|
23376
23483
|
} catch (e) {
|
|
@@ -23617,7 +23724,8 @@ async function deploySimpleStacey(args) {
|
|
|
23617
23724
|
endpoint: args.endpoint,
|
|
23618
23725
|
repoRoot: args.repoRoot,
|
|
23619
23726
|
persona: SIMPLE_STACEY_PERSONA,
|
|
23620
|
-
agentName: SIMPLE_STACEY_AGENT
|
|
23727
|
+
agentName: SIMPLE_STACEY_AGENT,
|
|
23728
|
+
fieldOverrides: args.fieldOverrides
|
|
23621
23729
|
});
|
|
23622
23730
|
} catch (e) {
|
|
23623
23731
|
if (e && typeof e === "object" && "code" in e) {
|
|
@@ -23735,7 +23843,13 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
23735
23843
|
const oid = await getSignedInUserOid();
|
|
23736
23844
|
await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
|
|
23737
23845
|
out("deploying Simple Stacey (stacey-intake)\u2026");
|
|
23738
|
-
const
|
|
23846
|
+
const identity = await getSignedInUserIdentity();
|
|
23847
|
+
const version = await deploySimpleStacey({
|
|
23848
|
+
credential: credential2,
|
|
23849
|
+
endpoint,
|
|
23850
|
+
repoRoot,
|
|
23851
|
+
fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) }
|
|
23852
|
+
});
|
|
23739
23853
|
const envPath = writeWebEnvLocal({
|
|
23740
23854
|
repoRoot,
|
|
23741
23855
|
tenantId: account.tenantId,
|
|
@@ -23788,8 +23902,8 @@ import { Command as Command51, Option as Option49 } from "clipanion";
|
|
|
23788
23902
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
23789
23903
|
static paths = [["bootstrap", "seed-profile"]];
|
|
23790
23904
|
static usage = Command51.Usage({
|
|
23791
|
-
description: "Seed
|
|
23792
|
-
details: "Reads the latest stacey-intake conversation, renders memory/company-profile.md (+
|
|
23905
|
+
description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
|
|
23906
|
+
details: "Reads the latest stacey-intake conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/azzy-brain via the GitHub App. Idempotent. --watch polls until the founder completes the questionnaire.",
|
|
23793
23907
|
examples: [
|
|
23794
23908
|
["Seed now (idempotent)", "$0 bootstrap seed-profile"],
|
|
23795
23909
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
@@ -23821,8 +23935,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
23821
23935
|
const token = await getFoundryToken();
|
|
23822
23936
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
23823
23937
|
if (block) {
|
|
23824
|
-
|
|
23825
|
-
|
|
23938
|
+
const azIdentity = await getSignedInUserIdentity();
|
|
23939
|
+
for (const brainRepo of ctx.brainRepos) {
|
|
23940
|
+
await applyProfileToBrain({ block, brainRepo, branch: "main", appCreds: ctx.appCreds, subscriptionId: ctx.subscriptionId, azIdentity });
|
|
23941
|
+
}
|
|
23942
|
+
this.context.stdout.write(`${colors.success("\u2713")} seeded your advisors' brains (${ctx.brainRepos.join(", ")}) from your questionnaire.
|
|
23826
23943
|
`);
|
|
23827
23944
|
return 0;
|
|
23828
23945
|
}
|