@kimbho/kimbho-cli 0.1.18 → 0.1.20

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/index.cjs CHANGED
@@ -10612,7 +10612,7 @@ var {
10612
10612
  // package.json
10613
10613
  var package_default = {
10614
10614
  name: "@kimbho/kimbho-cli",
10615
- version: "0.1.18",
10615
+ version: "0.1.20",
10616
10616
  description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
10617
10617
  type: "module",
10618
10618
  engines: {
@@ -16124,6 +16124,10 @@ async function loadCustomAgents(cwd) {
16124
16124
  ];
16125
16125
  });
16126
16126
  }
16127
+ async function loadCustomAgentById(cwd, id) {
16128
+ const agents = await loadCustomAgents(cwd);
16129
+ return agents.find((agent) => agent.id === id) ?? null;
16130
+ }
16127
16131
  async function loadAgentTeams(cwd) {
16128
16132
  const records = await loadMarkdownRecords(resolveAgentTeamDir(cwd));
16129
16133
  return records.map(({ filePath, source }) => {
@@ -16142,6 +16146,10 @@ async function loadAgentTeams(cwd) {
16142
16146
  };
16143
16147
  });
16144
16148
  }
16149
+ async function loadAgentTeamById(cwd, id) {
16150
+ const teams = await loadAgentTeams(cwd);
16151
+ return teams.find((team) => team.id === id) ?? null;
16152
+ }
16145
16153
  async function createCustomAgentFile(cwd, id, baseRole, options = {}) {
16146
16154
  const directory = resolveCustomAgentDir(cwd);
16147
16155
  const filePath = import_node_path5.default.join(directory, `${id}.md`);
@@ -16176,6 +16184,11 @@ async function createCustomAgentFile(cwd, id, baseRole, options = {}) {
16176
16184
  `, "utf8");
16177
16185
  return filePath;
16178
16186
  }
16187
+ async function deleteCustomAgentFile(cwd, id) {
16188
+ const filePath = import_node_path5.default.join(resolveCustomAgentDir(cwd), `${id}.md`);
16189
+ await (0, import_promises5.rm)(filePath);
16190
+ return filePath;
16191
+ }
16179
16192
  async function createAgentTeamFile(cwd, id, agentIds, label) {
16180
16193
  const directory = resolveAgentTeamDir(cwd);
16181
16194
  const filePath = import_node_path5.default.join(directory, `${id}.md`);
@@ -16199,6 +16212,11 @@ async function createAgentTeamFile(cwd, id, agentIds, label) {
16199
16212
  `, "utf8");
16200
16213
  return filePath;
16201
16214
  }
16215
+ async function deleteAgentTeamFile(cwd, id) {
16216
+ const filePath = import_node_path5.default.join(resolveAgentTeamDir(cwd), `${id}.md`);
16217
+ await (0, import_promises5.rm)(filePath);
16218
+ return filePath;
16219
+ }
16202
16220
  function scoreCustomAgentMatch(definition, task, request) {
16203
16221
  if (definition.baseRole !== task.agentRole) {
16204
16222
  return -1;
@@ -18290,6 +18308,175 @@ var KNOWN_SCAFFOLD_PRESETS = [
18290
18308
  "static-landing",
18291
18309
  "kimbho-cli-monorepo"
18292
18310
  ];
18311
+ function inferVisualAdjustment(goal) {
18312
+ const normalized = goal.toLowerCase();
18313
+ if (!normalized.includes("background") && !normalized.includes("gradient") && !normalized.includes("theme")) {
18314
+ return null;
18315
+ }
18316
+ if (normalized.includes("green")) {
18317
+ return {
18318
+ summaryLabel: "green background",
18319
+ background: "linear-gradient(180deg, #f4fff6 0%, #d8f3dc 100%)"
18320
+ };
18321
+ }
18322
+ if (normalized.includes("amber") || normalized.includes("gold") || normalized.includes("orange")) {
18323
+ return {
18324
+ summaryLabel: "amber background",
18325
+ background: "linear-gradient(180deg, #fff8e7 0%, #f7d08a 100%)"
18326
+ };
18327
+ }
18328
+ if (normalized.includes("blue")) {
18329
+ return {
18330
+ summaryLabel: "blue background",
18331
+ background: "linear-gradient(180deg, #eef6ff 0%, #cfe3ff 100%)"
18332
+ };
18333
+ }
18334
+ if (normalized.includes("pink") || normalized.includes("rose")) {
18335
+ return {
18336
+ summaryLabel: "rose background",
18337
+ background: "linear-gradient(180deg, #fff3f8 0%, #ffd6e7 100%)"
18338
+ };
18339
+ }
18340
+ if (normalized.includes("purple") || normalized.includes("violet")) {
18341
+ return {
18342
+ summaryLabel: "violet background",
18343
+ background: "linear-gradient(180deg, #f6f2ff 0%, #ddd1ff 100%)"
18344
+ };
18345
+ }
18346
+ return null;
18347
+ }
18348
+ function looksLikeVisualAdjustmentGoal(value) {
18349
+ return inferVisualAdjustment(value) !== null;
18350
+ }
18351
+ function inferSupplementalPages(goal, kind) {
18352
+ const normalized = goal.toLowerCase();
18353
+ const requestSignals = [
18354
+ "add more pages",
18355
+ "add pages",
18356
+ "more pages",
18357
+ "new pages",
18358
+ "new page",
18359
+ "additional pages",
18360
+ "additional page"
18361
+ ];
18362
+ const requested = /* @__PURE__ */ new Set();
18363
+ const specificPageSignals = [
18364
+ ["about", "about"],
18365
+ ["contact", "contact"],
18366
+ ["archive", "archive"],
18367
+ ["journal", "journal"],
18368
+ ["stories", "stories"],
18369
+ ["story", "story"],
18370
+ ["menu", "menu"],
18371
+ ["reservation", "reservations"],
18372
+ ["reservations", "reservations"],
18373
+ ["shop", "shop"],
18374
+ ["services", "services"]
18375
+ ];
18376
+ for (const [signal, slug] of specificPageSignals) {
18377
+ if (normalized.includes(signal)) {
18378
+ requested.add(slug);
18379
+ }
18380
+ }
18381
+ const isPageExpansionRequest = requested.size > 0 || requestSignals.some((signal) => normalized.includes(signal));
18382
+ if (!isPageExpansionRequest) {
18383
+ return null;
18384
+ }
18385
+ if (requested.size === 0) {
18386
+ if (kind === "blog") {
18387
+ requested.add("about");
18388
+ requested.add("archive");
18389
+ requested.add("contact");
18390
+ } else if (kind === "restaurant") {
18391
+ requested.add("menu");
18392
+ requested.add("story");
18393
+ requested.add("reservations");
18394
+ } else if (kind === "storefront") {
18395
+ requested.add("shop");
18396
+ requested.add("story");
18397
+ requested.add("contact");
18398
+ } else {
18399
+ requested.add("about");
18400
+ requested.add("services");
18401
+ requested.add("contact");
18402
+ }
18403
+ }
18404
+ const copyBySlug = {
18405
+ about: {
18406
+ title: "About",
18407
+ eyebrow: "Studio notes",
18408
+ lede: "A calmer overview of the people, perspective, and intent behind the project.",
18409
+ body: "Use this page to explain what the site stands for, who it is for, and why the work matters. Keep it human and specific."
18410
+ },
18411
+ contact: {
18412
+ title: "Contact",
18413
+ eyebrow: "Get in touch",
18414
+ lede: "A simple route for collaborations, questions, bookings, or thoughtful replies.",
18415
+ body: "Replace this copy with the right contact method, social links, newsletter details, or inquiry instructions."
18416
+ },
18417
+ archive: {
18418
+ title: "Archive",
18419
+ eyebrow: "Browse the back catalog",
18420
+ lede: "A clean index for essays, notes, dispatches, and the posts worth resurfacing.",
18421
+ body: "Turn this into a chronological archive, tag explorer, or featured reading list as the site grows."
18422
+ },
18423
+ journal: {
18424
+ title: "Journal",
18425
+ eyebrow: "Field notes",
18426
+ lede: "A lighter-weight stream for updates, drafts, studio notes, and ongoing experiments.",
18427
+ body: "Use this page for smaller entries that do not need the weight of longform essays."
18428
+ },
18429
+ stories: {
18430
+ title: "Stories",
18431
+ eyebrow: "Featured writing",
18432
+ lede: "A front door for standout essays, case studies, or narrative pieces.",
18433
+ body: "Pull your strongest writing forward here so visitors can quickly see the tone and quality of the work."
18434
+ },
18435
+ story: {
18436
+ title: "Our Story",
18437
+ eyebrow: "Behind the project",
18438
+ lede: "A richer page for origin, process, values, and the details that build trust.",
18439
+ body: "Tell the story behind the brand or studio in a way that feels personal instead of corporate."
18440
+ },
18441
+ menu: {
18442
+ title: "Menu",
18443
+ eyebrow: "Current offerings",
18444
+ lede: "A dedicated place for featured dishes, seasonal highlights, and signature favorites.",
18445
+ body: "Replace this with menu sections, tasting notes, pricing context, and dietary guidance."
18446
+ },
18447
+ reservations: {
18448
+ title: "Reservations",
18449
+ eyebrow: "Plan your visit",
18450
+ lede: "A conversion-focused page for table bookings, group dining, and private events.",
18451
+ body: "Use this space for booking links, service windows, cancellation policy, and special-event details."
18452
+ },
18453
+ shop: {
18454
+ title: "Shop",
18455
+ eyebrow: "Browse the collection",
18456
+ lede: "A tighter landing point for products, drops, featured items, or curated collections.",
18457
+ body: "Turn this into a product grid, collection overview, or conversion path into your checkout flow."
18458
+ },
18459
+ services: {
18460
+ title: "Services",
18461
+ eyebrow: "What we do",
18462
+ lede: "A practical overview of offers, engagements, deliverables, and working style.",
18463
+ body: "Use this page to clarify the services, retainers, or project formats you want visitors to understand."
18464
+ }
18465
+ };
18466
+ return Array.from(requested).map((slug) => {
18467
+ const resolved = copyBySlug[slug] ?? copyBySlug.about;
18468
+ return {
18469
+ slug,
18470
+ title: resolved.title,
18471
+ eyebrow: resolved.eyebrow,
18472
+ lede: resolved.lede,
18473
+ body: resolved.body
18474
+ };
18475
+ });
18476
+ }
18477
+ function looksLikePageExpansionGoal(value) {
18478
+ return inferSupplementalPages(value, inferLandingKind(value)) !== null;
18479
+ }
18293
18480
  function looksLikeStaticLandingGoal(value) {
18294
18481
  const normalized = value.toLowerCase();
18295
18482
  const backendSignals = [
@@ -18348,6 +18535,12 @@ function looksLikeStaticLandingGoal(value) {
18348
18535
  if (normalized.includes("blog") && (normalized.includes("make") || normalized.includes("improve") || normalized.includes("enhance") || normalized.includes("redesign")) && !backendSignals.some((signal) => normalized.includes(signal))) {
18349
18536
  return true;
18350
18537
  }
18538
+ if (looksLikeVisualAdjustmentGoal(normalized) && !backendSignals.some((signal) => normalized.includes(signal))) {
18539
+ return true;
18540
+ }
18541
+ if (looksLikePageExpansionGoal(normalized) && !backendSignals.some((signal) => normalized.includes(signal))) {
18542
+ return true;
18543
+ }
18351
18544
  return false;
18352
18545
  }
18353
18546
  function sanitizeName(value) {
@@ -18550,6 +18743,195 @@ function renderNextLandingPage(title, content) {
18550
18743
  function renderPagesRouterLandingPage(title, content) {
18551
18744
  return renderNextLandingPage(title, content);
18552
18745
  }
18746
+ function renderNextSupplementalPage(siteTitle, page) {
18747
+ return [
18748
+ `const siteTitle = ${JSON.stringify(siteTitle)};`,
18749
+ `const pageTitle = ${JSON.stringify(page.title)};`,
18750
+ "",
18751
+ "export default function SupplementalPage() {",
18752
+ " return (",
18753
+ " <main",
18754
+ " style={{",
18755
+ " minHeight: '100vh',",
18756
+ " background: 'linear-gradient(180deg, #fffaf2 0%, #f4ede2 100%)',",
18757
+ " color: '#1f1812',",
18758
+ ` fontFamily: "Georgia, 'Times New Roman', serif"`,
18759
+ " }}",
18760
+ " >",
18761
+ " <section style={{ maxWidth: 920, margin: '0 auto', padding: '72px 20px 96px' }}>",
18762
+ ` <p style={{ textTransform: 'uppercase', letterSpacing: '0.18em', fontSize: 12, color: '#9b5c34', margin: 0 }}>{${JSON.stringify(page.eyebrow)}}</p>`,
18763
+ " <h1 style={{ fontSize: 'clamp(2.8rem, 7vw, 4.8rem)', lineHeight: 0.95, margin: '16px 0 18px' }}>{pageTitle}</h1>",
18764
+ ` <p style={{ maxWidth: 680, lineHeight: 1.7, fontSize: '1.08rem', color: '#46372d', margin: 0 }}>{${JSON.stringify(page.lede)}}</p>`,
18765
+ " <section",
18766
+ " style={{",
18767
+ " marginTop: 28,",
18768
+ " background: 'rgba(255,255,255,0.82)',",
18769
+ " border: '1px solid rgba(31,24,18,0.12)',",
18770
+ " borderRadius: 28,",
18771
+ " padding: 28,",
18772
+ " boxShadow: '0 18px 40px rgba(31,24,18,0.06)'",
18773
+ " }}",
18774
+ " >",
18775
+ ` <p style={{ marginTop: 0, marginBottom: 0, lineHeight: 1.75, color: '#56453a' }}>{${JSON.stringify(page.body)}}</p>`,
18776
+ " </section>",
18777
+ " <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginTop: 28 }}>",
18778
+ " <a href='/' style={{ background: '#c96f3b', color: '#fff', padding: '14px 20px', borderRadius: 999, textDecoration: 'none', fontWeight: 700 }}>Back home</a>",
18779
+ " <span style={{ alignSelf: 'center', color: '#7b6659' }}>{siteTitle}</span>",
18780
+ " </div>",
18781
+ " </section>",
18782
+ " </main>",
18783
+ " );",
18784
+ "}"
18785
+ ].join("\n");
18786
+ }
18787
+ function renderStaticSupplementalPage(siteTitle, page) {
18788
+ return [
18789
+ "<!doctype html>",
18790
+ '<html lang="en">',
18791
+ " <head>",
18792
+ ' <meta charset="UTF-8" />',
18793
+ ' <meta name="viewport" content="width=device-width, initial-scale=1.0" />',
18794
+ ` <title>${page.title} | ${siteTitle}</title>`,
18795
+ ' <link rel="stylesheet" href="./styles.css" />',
18796
+ " </head>",
18797
+ " <body>",
18798
+ ' <main class="shell">',
18799
+ ' <section class="hero">',
18800
+ ` <p class="eyebrow">${page.eyebrow}</p>`,
18801
+ ` <h1>${page.title}</h1>`,
18802
+ ` <p class="lede">${page.lede}</p>`,
18803
+ " </section>",
18804
+ ' <section class="story">',
18805
+ ` <p>${page.body}</p>`,
18806
+ " </section>",
18807
+ ' <div class="actions">',
18808
+ ' <a href="./index.html" class="button primary">Back home</a>',
18809
+ ` <a href="./index.html" class="button secondary">${siteTitle}</a>`,
18810
+ " </div>",
18811
+ " </main>",
18812
+ " </body>",
18813
+ "</html>"
18814
+ ].join("\n");
18815
+ }
18816
+ async function inferExistingSiteTitle(cwd, target, fallbackTitle) {
18817
+ const source = await (0, import_promises10.readFile)(import_node_path10.default.join(cwd, target.pagePath), "utf8");
18818
+ const constantMatch = source.match(/const\s+title\s*=\s*["'`](.+?)["'`]/);
18819
+ if (constantMatch?.[1]) {
18820
+ return constantMatch[1];
18821
+ }
18822
+ const headingMatch = source.match(/<h1[^>]*>\s*([^<]+?)\s*<\/h1>/);
18823
+ if (headingMatch?.[1]) {
18824
+ return headingMatch[1];
18825
+ }
18826
+ return fallbackTitle;
18827
+ }
18828
+ function insertNextPageLinks(source, pages) {
18829
+ if (pages.every((page) => source.includes(`href='/${page.slug}'`) || source.includes(`href="/${page.slug}"`))) {
18830
+ return source;
18831
+ }
18832
+ const block = [
18833
+ " <section style={{ marginTop: 26, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 14 }}>",
18834
+ ...pages.map((page) => ` <a href='/${page.slug}' style={{ display: 'block', textDecoration: 'none', padding: '18px 20px', borderRadius: 22, background: 'rgba(255,255,255,0.82)', border: '1px solid rgba(31,24,18,0.1)', color: '#1f1812', boxShadow: '0 14px 32px rgba(31,24,18,0.05)' }}><strong>${page.title}</strong><br /><span style={{ color: '#6c5a4e' }}>Explore ${page.title.toLowerCase()}.</span></a>`),
18835
+ " </section>"
18836
+ ].join("\n");
18837
+ if (source.includes("</main>")) {
18838
+ return source.replace(/(\s*)<\/main>/, `
18839
+ ${block}
18840
+ $1</main>`);
18841
+ }
18842
+ return source;
18843
+ }
18844
+ function insertStaticPageLinks(source, pages) {
18845
+ if (pages.every((page) => source.includes(`./${page.slug}.html`) || source.includes(`"${page.slug}.html"`))) {
18846
+ return source;
18847
+ }
18848
+ const block = [
18849
+ ' <section class="grid">',
18850
+ ...pages.map((page) => ` <article class="card"><h2>${page.title}</h2><p>${page.lede}</p><a class="button secondary" href="./${page.slug}.html">Open ${page.title}</a></article>`),
18851
+ " </section>"
18852
+ ].join("\n");
18853
+ if (source.includes("</main>")) {
18854
+ return source.replace(/(\s*)<\/main>/, `
18855
+ ${block}
18856
+ $1</main>`);
18857
+ }
18858
+ return source;
18859
+ }
18860
+ async function applyExistingStaticPageExpansion(cwd, goal, target) {
18861
+ const pages = inferSupplementalPages(goal, inferLandingKind(goal));
18862
+ if (!pages) {
18863
+ return null;
18864
+ }
18865
+ const projectName = inferProjectName(goal, cwd);
18866
+ const siteTitle = await inferExistingSiteTitle(cwd, target, inferLandingTitle(goal, projectName));
18867
+ const files = {};
18868
+ const artifacts = [];
18869
+ let createdPages = 0;
18870
+ if (target.kind === "next-app") {
18871
+ for (const page of pages) {
18872
+ const relativePath = `src/app/${page.slug}/page.tsx`;
18873
+ const absolutePath = import_node_path10.default.join(cwd, relativePath);
18874
+ if (!await pathExists(absolutePath)) {
18875
+ files[relativePath] = renderNextSupplementalPage(siteTitle, page);
18876
+ createdPages += 1;
18877
+ }
18878
+ artifacts.push(absolutePath);
18879
+ }
18880
+ const currentSource = await (0, import_promises10.readFile)(import_node_path10.default.join(cwd, target.pagePath), "utf8");
18881
+ const nextSource = insertNextPageLinks(currentSource, pages);
18882
+ if (nextSource !== currentSource) {
18883
+ files[target.pagePath] = nextSource;
18884
+ }
18885
+ } else if (target.kind === "next-pages") {
18886
+ for (const page of pages) {
18887
+ const relativePath = `src/pages/${page.slug}.tsx`;
18888
+ const absolutePath = import_node_path10.default.join(cwd, relativePath);
18889
+ if (!await pathExists(absolutePath)) {
18890
+ files[relativePath] = renderNextSupplementalPage(siteTitle, page);
18891
+ createdPages += 1;
18892
+ }
18893
+ artifacts.push(absolutePath);
18894
+ }
18895
+ const currentSource = await (0, import_promises10.readFile)(import_node_path10.default.join(cwd, target.pagePath), "utf8");
18896
+ const nextSource = insertNextPageLinks(currentSource, pages);
18897
+ if (nextSource !== currentSource) {
18898
+ files[target.pagePath] = nextSource;
18899
+ }
18900
+ } else {
18901
+ for (const page of pages) {
18902
+ const relativePath = `${page.slug}.html`;
18903
+ const absolutePath = import_node_path10.default.join(cwd, relativePath);
18904
+ if (!await pathExists(absolutePath)) {
18905
+ files[relativePath] = renderStaticSupplementalPage(siteTitle, page);
18906
+ createdPages += 1;
18907
+ }
18908
+ artifacts.push(absolutePath);
18909
+ }
18910
+ const currentSource = await (0, import_promises10.readFile)(import_node_path10.default.join(cwd, target.pagePath), "utf8");
18911
+ const nextSource = insertStaticPageLinks(currentSource, pages);
18912
+ if (nextSource !== currentSource) {
18913
+ files[target.pagePath] = nextSource;
18914
+ }
18915
+ }
18916
+ const writtenArtifacts = Object.keys(files).length > 0 ? await writeFiles(cwd, files) : [];
18917
+ if (createdPages === 0 && writtenArtifacts.length === 0) {
18918
+ return {
18919
+ preset: "static-landing",
18920
+ projectName,
18921
+ summary: `Confirmed the requested pages already exist: ${pages.map((page) => page.title).join(", ")}.`,
18922
+ artifacts: artifacts.sort((left, right) => left.localeCompare(right))
18923
+ };
18924
+ }
18925
+ return {
18926
+ preset: "static-landing",
18927
+ projectName,
18928
+ summary: `Added ${createdPages > 0 ? createdPages : pages.length} supplemental page${(createdPages > 0 ? createdPages : pages.length) === 1 ? "" : "s"}: ${pages.map((page) => page.title).join(", ")}.`,
18929
+ artifacts: Array.from(/* @__PURE__ */ new Set([
18930
+ ...artifacts,
18931
+ ...writtenArtifacts
18932
+ ])).sort((left, right) => left.localeCompare(right))
18933
+ };
18934
+ }
18553
18935
  async function detectExistingStaticTarget(cwd) {
18554
18936
  const nextAppPage = import_node_path10.default.join(cwd, "src/app/page.tsx");
18555
18937
  if (await pathExists(nextAppPage)) {
@@ -18575,7 +18957,80 @@ async function detectExistingStaticTarget(cwd) {
18575
18957
  }
18576
18958
  return null;
18577
18959
  }
18960
+ function rewriteBackgroundInSource(source, background) {
18961
+ const inlinePatterns = [
18962
+ [/background:\s*'[^']*'/, `background: '${background}'`],
18963
+ [/background:\s*"[^"]*"/, `background: "${background}"`],
18964
+ [/background:\s*`[^`]*`/, `background: \`${background}\``]
18965
+ ];
18966
+ for (const [pattern, replacement] of inlinePatterns) {
18967
+ if (pattern.test(source)) {
18968
+ return source.replace(pattern, replacement);
18969
+ }
18970
+ }
18971
+ const cssPatterns = [
18972
+ [/background:\s*[^;]+;/, `background: ${background};`],
18973
+ [/background-color:\s*[^;]+;/, `background: ${background};`]
18974
+ ];
18975
+ for (const [pattern, replacement] of cssPatterns) {
18976
+ if (pattern.test(source)) {
18977
+ return source.replace(pattern, replacement);
18978
+ }
18979
+ }
18980
+ return source;
18981
+ }
18982
+ async function applyExistingStaticVisualAdjustment(cwd, goal, target) {
18983
+ const adjustment = inferVisualAdjustment(goal);
18984
+ if (!adjustment) {
18985
+ return null;
18986
+ }
18987
+ const candidatePaths = [
18988
+ target.pagePath,
18989
+ target.kind === "next-app" ? "src/app/globals.css" : null,
18990
+ target.kind === "next-pages" ? "styles.css" : null,
18991
+ target.kind === "static-html" ? "styles.css" : null
18992
+ ].filter((value) => Boolean(value));
18993
+ for (const relativePath of candidatePaths) {
18994
+ const absolutePath = import_node_path10.default.join(cwd, relativePath);
18995
+ if (!await pathExists(absolutePath)) {
18996
+ continue;
18997
+ }
18998
+ const currentSource = await (0, import_promises10.readFile)(absolutePath, "utf8");
18999
+ if (currentSource.includes(adjustment.background)) {
19000
+ return {
19001
+ preset: "static-landing",
19002
+ projectName: inferProjectName(goal, cwd),
19003
+ summary: `${relativePath} already uses the requested ${adjustment.summaryLabel}.`,
19004
+ artifacts: [
19005
+ absolutePath
19006
+ ]
19007
+ };
19008
+ }
19009
+ const nextSource = rewriteBackgroundInSource(currentSource, adjustment.background);
19010
+ if (nextSource === currentSource) {
19011
+ continue;
19012
+ }
19013
+ const artifacts = await writeFiles(cwd, {
19014
+ [relativePath]: nextSource
19015
+ });
19016
+ return {
19017
+ preset: "static-landing",
19018
+ projectName: inferProjectName(goal, cwd),
19019
+ summary: `Updated ${relativePath} with a ${adjustment.summaryLabel}.`,
19020
+ artifacts
19021
+ };
19022
+ }
19023
+ return null;
19024
+ }
18578
19025
  async function adaptExistingStaticLanding(cwd, projectName, goal, target) {
19026
+ const pageExpansionResult = await applyExistingStaticPageExpansion(cwd, goal, target);
19027
+ if (pageExpansionResult) {
19028
+ return pageExpansionResult;
19029
+ }
19030
+ const adjustedResult = await applyExistingStaticVisualAdjustment(cwd, goal, target);
19031
+ if (adjustedResult) {
19032
+ return adjustedResult;
19033
+ }
18579
19034
  const title = inferLandingTitle(goal, projectName);
18580
19035
  const content = buildLandingContent(title, inferLandingKind(goal));
18581
19036
  const files = {};
@@ -19364,6 +19819,9 @@ function isVerificationShellCommand(command) {
19364
19819
  const normalized = command.trim().toLowerCase();
19365
19820
  return VERIFICATION_SHELL_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix} `));
19366
19821
  }
19822
+ function isLowRiskScaffoldRequest(input) {
19823
+ return input.preset === "static-landing" && typeof input.goal === "string" && (looksLikeVisualAdjustmentGoal(input.goal) || looksLikePageExpansionGoal(input.goal));
19824
+ }
19367
19825
  function isDestructiveShellCommand(command) {
19368
19826
  return DESTRUCTIVE_SHELL_PATTERNS.some((pattern) => pattern.test(command));
19369
19827
  }
@@ -19432,8 +19890,9 @@ function enforceToolPolicy(toolId, input, descriptor, context) {
19432
19890
  const sandboxMode = context.sandboxMode ?? "workspace-write";
19433
19891
  const approvalMode = context.approvalMode ?? "manual";
19434
19892
  const command = typeof input.command === "string" ? input.command : "";
19893
+ const isVerificationCommand2 = isVerificationShellCommand(command);
19435
19894
  if (sandboxMode === "read-only") {
19436
- if (toolId === "shell.exec" && (isReadOnlyShellCommand(command) || isVerificationShellCommand(command))) {
19895
+ if ((toolId === "shell.exec" || toolId === "tests.run") && (isReadOnlyShellCommand(command) || isVerificationCommand2)) {
19437
19896
  return null;
19438
19897
  }
19439
19898
  if (descriptor.permission !== "safe") {
@@ -19445,7 +19904,10 @@ function enforceToolPolicy(toolId, input, descriptor, context) {
19445
19904
  return new ToolApprovalRequiredError(createApprovalRequest(toolId, input, descriptor, context, `Approval required for destructive shell command: ${command}`));
19446
19905
  }
19447
19906
  }
19448
- if (toolId === "shell.exec" && isVerificationShellCommand(command)) {
19907
+ if ((toolId === "shell.exec" || toolId === "tests.run") && isVerificationCommand2) {
19908
+ return null;
19909
+ }
19910
+ if (toolId === "scaffold.generate" && isLowRiskScaffoldRequest(input)) {
19449
19911
  return null;
19450
19912
  }
19451
19913
  if ((toolId === "file.write" || toolId === "file.patch") && sandboxMode === "workspace-write") {
@@ -27626,6 +28088,37 @@ var MCP_INVENTORY_TIMEOUT_MS = 8e3;
27626
28088
  function toMcpToolId(serverName, toolName) {
27627
28089
  return `mcp.${serverName}.${toolName}`;
27628
28090
  }
28091
+ function normalizeMcpCommandSegment(value) {
28092
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
28093
+ }
28094
+ async function resolveMcpServerInventoryByReference(cwd, serverReference) {
28095
+ const inventory = await loadMcpServerInventory(cwd);
28096
+ const normalizedReference = normalizeMcpCommandSegment(serverReference);
28097
+ const server = inventory.find((candidate) => candidate.name === serverReference || normalizeMcpCommandSegment(candidate.name) === normalizedReference);
28098
+ if (!server) {
28099
+ throw new Error(`MCP server ${serverReference} is not configured or did not respond.`);
28100
+ }
28101
+ if (server.warning) {
28102
+ throw new Error(`MCP server ${server.name} is unavailable: ${server.warning}`);
28103
+ }
28104
+ return server;
28105
+ }
28106
+ async function resolveMcpPromptReference(cwd, serverReference, promptReference) {
28107
+ const server = await resolveMcpServerInventoryByReference(cwd, serverReference);
28108
+ const normalizedPromptReference = normalizeMcpCommandSegment(promptReference);
28109
+ const prompt = server.prompts.find((candidate) => candidate.name === promptReference || normalizeMcpCommandSegment(candidate.name) === normalizedPromptReference);
28110
+ if (!prompt) {
28111
+ throw new Error(`MCP prompt ${promptReference} was not found on server ${server.name}.`);
28112
+ }
28113
+ return {
28114
+ serverName: server.name,
28115
+ promptName: prompt.name
28116
+ };
28117
+ }
28118
+ async function resolveMcpServerName(cwd, serverReference) {
28119
+ const server = await resolveMcpServerInventoryByReference(cwd, serverReference);
28120
+ return server.name;
28121
+ }
27629
28122
  function permissionFromAnnotations(annotations) {
27630
28123
  if (annotations?.destructiveHint) {
27631
28124
  return "destructive";
@@ -27785,23 +28278,24 @@ async function discoverMcpTools(cwd) {
27785
28278
  }
27786
28279
  async function callMcpTool(cwd, serverName, toolName, input) {
27787
28280
  const config2 = await loadMcpConfig(cwd);
27788
- const server = config2.mcpServers[serverName];
28281
+ const resolvedServerName = await resolveMcpServerName(cwd, serverName).catch(() => serverName);
28282
+ const server = config2.mcpServers[resolvedServerName];
27789
28283
  if (!server || server.enabled === false) {
27790
28284
  return {
27791
- toolId: toMcpToolId(serverName, toolName),
28285
+ toolId: toMcpToolId(resolvedServerName, toolName),
27792
28286
  success: false,
27793
- summary: `MCP server ${serverName} is not configured or is disabled.`,
28287
+ summary: `MCP server ${resolvedServerName} is not configured or is disabled.`,
27794
28288
  artifacts: []
27795
28289
  };
27796
28290
  }
27797
- const result = await withMcpClient(cwd, serverName, server, async (client) => client.callTool({
28291
+ const result = await withMcpClient(cwd, resolvedServerName, server, async (client) => client.callTool({
27798
28292
  name: toolName,
27799
28293
  arguments: Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0))
27800
28294
  }));
27801
28295
  return {
27802
- toolId: toMcpToolId(serverName, toolName),
28296
+ toolId: toMcpToolId(resolvedServerName, toolName),
27803
28297
  success: !result.isError,
27804
- summary: result.isError ? `MCP tool ${serverName}/${toolName} reported an error.` : `Ran MCP tool ${serverName}/${toolName}.`,
28298
+ summary: result.isError ? `MCP tool ${resolvedServerName}/${toolName} reported an error.` : `Ran MCP tool ${resolvedServerName}/${toolName}.`,
27805
28299
  stdout: renderMcpContent(result),
27806
28300
  artifacts: []
27807
28301
  };
@@ -27823,18 +28317,19 @@ ${"text" in resource ? resource.text : `[binary ${resource.mimeType ?? "resource
27823
28317
  }).join("\n\n");
27824
28318
  }
27825
28319
  async function invokeMcpPrompt(cwd, serverName, promptName, args) {
28320
+ const { serverName: resolvedServerName, promptName: resolvedPromptName } = await resolveMcpPromptReference(cwd, serverName, promptName);
27826
28321
  const config2 = await loadMcpConfig(cwd);
27827
- const server = config2.mcpServers[serverName];
28322
+ const server = config2.mcpServers[resolvedServerName];
27828
28323
  if (!server || server.enabled === false) {
27829
- throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
28324
+ throw new Error(`MCP server ${resolvedServerName} is not configured or is disabled.`);
27830
28325
  }
27831
- const result = await withMcpClient(cwd, serverName, server, async (client) => client.getPrompt({
27832
- name: promptName,
28326
+ const result = await withMcpClient(cwd, resolvedServerName, server, async (client) => client.getPrompt({
28327
+ name: resolvedPromptName,
27833
28328
  arguments: args
27834
28329
  }));
27835
28330
  return {
27836
- serverName,
27837
- promptName,
28331
+ serverName: resolvedServerName,
28332
+ promptName: resolvedPromptName,
27838
28333
  ...result.description ? {
27839
28334
  description: result.description
27840
28335
  } : {},
@@ -27842,19 +28337,20 @@ async function invokeMcpPrompt(cwd, serverName, promptName, args) {
27842
28337
  };
27843
28338
  }
27844
28339
  async function readMcpResource(cwd, serverName, uri) {
28340
+ const resolvedServerName = await resolveMcpServerName(cwd, serverName);
27845
28341
  const config2 = await loadMcpConfig(cwd);
27846
- const server = config2.mcpServers[serverName];
28342
+ const server = config2.mcpServers[resolvedServerName];
27847
28343
  if (!server || server.enabled === false) {
27848
- throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
28344
+ throw new Error(`MCP server ${resolvedServerName} is not configured or is disabled.`);
27849
28345
  }
27850
- const result = await withMcpClient(cwd, serverName, server, async (client) => client.readResource({
28346
+ const result = await withMcpClient(cwd, resolvedServerName, server, async (client) => client.readResource({
27851
28347
  uri
27852
28348
  }));
27853
28349
  const content = result.contents.map((item) => "text" in item ? `resource: ${item.uri}
27854
28350
  ${item.text}` : `resource: ${item.uri}
27855
28351
  [binary ${item.mimeType ?? "resource"}]`).join("\n\n");
27856
28352
  return {
27857
- serverName,
28353
+ serverName: resolvedServerName,
27858
28354
  uri,
27859
28355
  content
27860
28356
  };
@@ -29177,6 +29673,109 @@ var import_node_path14 = __toESM(require("node:path"), 1);
29177
29673
  function normalizeGoal(goal) {
29178
29674
  return goal.trim().replace(/\s+/g, " ");
29179
29675
  }
29676
+ function looksLikeVisualAdjustmentGoal2(goal) {
29677
+ const lower = goal.toLowerCase();
29678
+ const backendSignals = [
29679
+ "api",
29680
+ "database",
29681
+ "backend",
29682
+ "server",
29683
+ "migration",
29684
+ "schema",
29685
+ "prisma",
29686
+ "postgres",
29687
+ "sqlite",
29688
+ "auth",
29689
+ "billing",
29690
+ "webhook"
29691
+ ];
29692
+ const editSignals = [
29693
+ "make",
29694
+ "change",
29695
+ "update",
29696
+ "turn",
29697
+ "switch",
29698
+ "set",
29699
+ "use",
29700
+ "refresh",
29701
+ "restyle",
29702
+ "redesign"
29703
+ ];
29704
+ const visualSignals = [
29705
+ "background",
29706
+ "color",
29707
+ "green",
29708
+ "amber",
29709
+ "blue",
29710
+ "pink",
29711
+ "purple",
29712
+ "gradient",
29713
+ "theme",
29714
+ "palette",
29715
+ "styling",
29716
+ "style",
29717
+ "look",
29718
+ "visual",
29719
+ "hero"
29720
+ ];
29721
+ const pageContextSignals = [
29722
+ "page",
29723
+ "landing",
29724
+ "lnding",
29725
+ "homepage",
29726
+ "home page",
29727
+ "site",
29728
+ "website",
29729
+ "blog"
29730
+ ];
29731
+ if (backendSignals.some((signal) => lower.includes(signal))) {
29732
+ return false;
29733
+ }
29734
+ return editSignals.some((signal) => lower.includes(signal)) && visualSignals.some((signal) => lower.includes(signal)) && (pageContextSignals.some((signal) => lower.includes(signal)) || lower.includes("background"));
29735
+ }
29736
+ function looksLikePageExpansionGoal2(goal) {
29737
+ const lower = goal.toLowerCase();
29738
+ const backendSignals = [
29739
+ "api",
29740
+ "database",
29741
+ "backend",
29742
+ "server",
29743
+ "migration",
29744
+ "schema",
29745
+ "prisma",
29746
+ "postgres",
29747
+ "sqlite",
29748
+ "auth",
29749
+ "billing",
29750
+ "webhook"
29751
+ ];
29752
+ const directSignals = [
29753
+ "add more pages",
29754
+ "add pages",
29755
+ "more pages",
29756
+ "new pages",
29757
+ "new page",
29758
+ "additional pages",
29759
+ "additional page"
29760
+ ];
29761
+ const namedPages = [
29762
+ "about",
29763
+ "contact",
29764
+ "archive",
29765
+ "journal",
29766
+ "stories",
29767
+ "story",
29768
+ "menu",
29769
+ "reservation",
29770
+ "reservations",
29771
+ "shop",
29772
+ "services"
29773
+ ];
29774
+ if (backendSignals.some((signal) => lower.includes(signal))) {
29775
+ return false;
29776
+ }
29777
+ return directSignals.some((signal) => lower.includes(signal)) || (lower.includes("add") || lower.includes("create") || lower.includes("make")) && lower.includes("page") && namedPages.some((name) => lower.includes(name));
29778
+ }
29180
29779
  function looksLikeStaticSiteGoal(goal) {
29181
29780
  const lower = goal.toLowerCase();
29182
29781
  const backendSignals = [
@@ -29235,6 +29834,12 @@ function looksLikeStaticSiteGoal(goal) {
29235
29834
  if (lower.includes("blog") && (lower.includes("make") || lower.includes("improve") || lower.includes("enhance") || lower.includes("redesign")) && !backendSignals.some((signal) => lower.includes(signal))) {
29236
29835
  return true;
29237
29836
  }
29837
+ if (looksLikeVisualAdjustmentGoal2(lower)) {
29838
+ return true;
29839
+ }
29840
+ if (looksLikePageExpansionGoal2(lower)) {
29841
+ return true;
29842
+ }
29238
29843
  return false;
29239
29844
  }
29240
29845
  function inferProjectShape(goal) {
@@ -31151,7 +31756,10 @@ function isStaticSiteTask(task, request) {
31151
31756
  }
31152
31757
  return task.filesLikelyTouched.some((filePath) => filePath === "src/app/page.tsx" || filePath === "src/app/layout.tsx" || filePath === "src/pages/index.tsx" || filePath === "index.html" || filePath === "styles.css");
31153
31758
  }
31154
- function shouldDelegateTask(task) {
31759
+ function shouldDelegateTask(task, request) {
31760
+ if ((looksLikeVisualAdjustmentGoal(request.goal) || looksLikePageExpansionGoal(request.goal)) && isStaticSiteTask(task, request)) {
31761
+ return false;
31762
+ }
31155
31763
  return (task.swarmDepth ?? 0) === 0 && task.type === "implementation" && ![
31156
31764
  "repo-analyst",
31157
31765
  "planner",
@@ -31168,7 +31776,7 @@ function terminalExpansionTaskIds(tasks) {
31168
31776
  return terminalIds.length > 0 ? terminalIds : tasks.map((task) => task.id);
31169
31777
  }
31170
31778
  function createDefaultDelegationPlan(task, request) {
31171
- if (!shouldDelegateTask(task)) {
31779
+ if (!shouldDelegateTask(task, request)) {
31172
31780
  return null;
31173
31781
  }
31174
31782
  if (task.agentRole === "frontend-specialist" || isStaticSiteTask(task, request)) {
@@ -31317,6 +31925,53 @@ function createDefaultDelegationPlan(task, request) {
31317
31925
  }
31318
31926
  return null;
31319
31927
  }
31928
+ async function detectAppliedVisualAdjustment(cwd, request) {
31929
+ const adjustment = inferVisualAdjustment(request.goal);
31930
+ if (!adjustment) {
31931
+ return null;
31932
+ }
31933
+ const candidates = [
31934
+ "src/app/page.tsx",
31935
+ "src/pages/index.tsx",
31936
+ "src/app/globals.css",
31937
+ "styles.css",
31938
+ "index.html"
31939
+ ];
31940
+ for (const relativePath of candidates) {
31941
+ const absolutePath = import_node_path14.default.join(cwd, relativePath);
31942
+ try {
31943
+ await (0, import_promises14.access)(absolutePath);
31944
+ const contents = await (0, import_promises14.readFile)(absolutePath, "utf8");
31945
+ if (contents.includes(adjustment.background)) {
31946
+ return absolutePath;
31947
+ }
31948
+ } catch {
31949
+ }
31950
+ }
31951
+ return null;
31952
+ }
31953
+ async function detectPreferredVerificationCommand(cwd) {
31954
+ const packagePath = import_node_path14.default.join(cwd, "package.json");
31955
+ try {
31956
+ await (0, import_promises14.access)(packagePath);
31957
+ const raw = await (0, import_promises14.readFile)(packagePath, "utf8");
31958
+ const parsed = JSON.parse(raw);
31959
+ const scripts = parsed.scripts ?? {};
31960
+ const packageManager = parsed.packageManager?.startsWith("pnpm") ? "pnpm" : parsed.packageManager?.startsWith("yarn") ? "yarn" : "npm";
31961
+ if (scripts.build) {
31962
+ return packageManager === "yarn" ? "yarn build" : `${packageManager} run build`;
31963
+ }
31964
+ if (scripts.lint) {
31965
+ return packageManager === "yarn" ? "yarn lint" : `${packageManager} run lint`;
31966
+ }
31967
+ if (scripts.test) {
31968
+ return packageManager === "yarn" ? "yarn test" : `${packageManager} run test`;
31969
+ }
31970
+ } catch {
31971
+ return null;
31972
+ }
31973
+ return null;
31974
+ }
31320
31975
  function materializeDelegatedTasks(plan, task, label, strategy, members) {
31321
31976
  const existingIds = new Set(flattenPlanTasks(plan).filter((candidate) => candidate.id !== task.id).map((candidate) => candidate.id));
31322
31977
  const teamMemberIds = members.map((member) => member.id);
@@ -31952,6 +32607,9 @@ var ExecutionOrchestrator = class {
31952
32607
  if (request.workspaceState === "existing" && isStaticSiteTask(task, request) && (task.type === "scaffold" || task.type === "implementation")) {
31953
32608
  return this.executeDeterministicStaticSiteTask(sessionId, task, request, resolvedApproval, options, emitProgress);
31954
32609
  }
32610
+ if (request.workspaceState === "existing" && looksLikeStaticLandingGoal(request.goal) && task.type === "verification") {
32611
+ return this.executeDeterministicVerificationTask(sessionId, task, request, emitProgress, options.signal);
32612
+ }
31955
32613
  if (task.type === "scaffold") {
31956
32614
  return this.executeScaffoldTask(sessionId, task, request, plan, resolvedApproval, options, emitProgress);
31957
32615
  }
@@ -32086,7 +32744,69 @@ var ExecutionOrchestrator = class {
32086
32744
  ]))
32087
32745
  };
32088
32746
  }
32747
+ async executeDeterministicVerificationTask(sessionId, task, request, emitProgress, signal) {
32748
+ const command = await detectPreferredVerificationCommand(request.cwd);
32749
+ if (!command) {
32750
+ return {
32751
+ status: "completed",
32752
+ summary: "No build, lint, or test script was found; verification was recorded as not applicable.",
32753
+ toolResults: [],
32754
+ artifacts: []
32755
+ };
32756
+ }
32757
+ const input = {
32758
+ command
32759
+ };
32760
+ if (emitProgress) {
32761
+ await emitProgress({
32762
+ type: "tool-started",
32763
+ sessionId,
32764
+ taskId: task.id,
32765
+ agentRole: task.agentRole,
32766
+ toolId: "tests.run",
32767
+ input
32768
+ });
32769
+ }
32770
+ const result = await this.safeRunTool("tests.run", input, {
32771
+ cwd: request.cwd,
32772
+ taskId: task.id,
32773
+ agentRole: task.agentRole,
32774
+ ...signal ? {
32775
+ signal
32776
+ } : {}
32777
+ });
32778
+ if (emitProgress) {
32779
+ await emitProgress({
32780
+ type: "tool-finished",
32781
+ sessionId,
32782
+ taskId: task.id,
32783
+ agentRole: task.agentRole,
32784
+ toolResult: result
32785
+ });
32786
+ }
32787
+ return {
32788
+ status: result.success ? "completed" : "blocked",
32789
+ summary: result.success ? `Verification passed via ${command}.` : `Verification failed via ${command}.`,
32790
+ toolResults: [
32791
+ result
32792
+ ],
32793
+ artifacts: result.artifacts
32794
+ };
32795
+ }
32089
32796
  async executeDeterministicStaticSiteTask(sessionId, task, request, resolvedApproval, options, emitProgress) {
32797
+ if (looksLikeVisualAdjustmentGoal(request.goal)) {
32798
+ const appliedArtifact = await detectAppliedVisualAdjustment(request.cwd, request);
32799
+ if (appliedArtifact) {
32800
+ return {
32801
+ status: "completed",
32802
+ summary: `Confirmed the requested visual adjustment in ${import_node_path14.default.relative(request.cwd, appliedArtifact) || appliedArtifact}.`,
32803
+ toolResults: [],
32804
+ artifacts: [
32805
+ appliedArtifact
32806
+ ]
32807
+ };
32808
+ }
32809
+ }
32090
32810
  const config2 = await loadConfig(request.cwd);
32091
32811
  const scaffoldInput = {
32092
32812
  goal: request.goal,
@@ -32482,7 +33202,7 @@ var ExecutionOrchestrator = class {
32482
33202
  }
32483
33203
  async maybeExpandReadyTaskGraph(sessionId, request, plan, events, emitProgress) {
32484
33204
  const envelope = this.buildEnvelope(request, plan, sessionId);
32485
- const candidate = envelope.readyTasks.find((task) => shouldDelegateTask(task));
33205
+ const candidate = envelope.readyTasks.find((task) => shouldDelegateTask(task, request));
32486
33206
  if (!candidate) {
32487
33207
  return {
32488
33208
  plan
@@ -32703,10 +33423,69 @@ function createAgentsCommand() {
32703
33423
  });
32704
33424
  console.log(`Created ${outputPath}`);
32705
33425
  });
33426
+ command.command("show").description("Show one custom agent or team.").argument("<id>", "Agent or team id").option("--team", "Show a team instead of an agent", false).action(async (id, options) => {
33427
+ if (options.team) {
33428
+ const team = await loadAgentTeamById(process.cwd(), id);
33429
+ if (!team) {
33430
+ throw new Error(`Unknown team "${id}".`);
33431
+ }
33432
+ console.log(team.id);
33433
+ console.log(` label: ${team.label}`);
33434
+ console.log(` agents: ${team.agents.join(", ") || "-"}`);
33435
+ console.log(` match: ${team.match.join(", ") || "-"}`);
33436
+ console.log(` strategy: ${team.strategy}`);
33437
+ console.log(` file: ${team.filePath}`);
33438
+ if (team.description) {
33439
+ console.log("");
33440
+ console.log(team.description);
33441
+ }
33442
+ return;
33443
+ }
33444
+ const agent = await loadCustomAgentById(process.cwd(), id);
33445
+ if (!agent) {
33446
+ throw new Error(`Unknown custom agent "${id}".`);
33447
+ }
33448
+ console.log(agent.id);
33449
+ console.log(` label: ${agent.label}`);
33450
+ console.log(` baseRole: ${agent.baseRole}`);
33451
+ console.log(` brainRole: ${agent.brainRole ?? "-"}`);
33452
+ console.log(` purpose: ${agent.purpose ?? "-"}`);
33453
+ console.log(` match: ${agent.match.join(", ") || "-"}`);
33454
+ console.log(` tools: ${agent.tools.join(", ") || "(inherits base role tools)"}`);
33455
+ console.log(` file: ${agent.filePath}`);
33456
+ if (agent.instructions.trim()) {
33457
+ console.log("");
33458
+ console.log(agent.instructions.trim());
33459
+ }
33460
+ });
33461
+ command.command("delete").description("Delete a custom agent or team markdown file.").argument("<id>", "Agent or team id").option("--team", "Delete a team instead of an agent", false).action(async (id, options) => {
33462
+ const outputPath = options.team ? await deleteAgentTeamFile(process.cwd(), id) : await deleteCustomAgentFile(process.cwd(), id);
33463
+ console.log(`Deleted ${outputPath}`);
33464
+ });
32706
33465
  command.command("team-create").description("Create a markdown-backed agent team definition.").argument("<id>", "Team id").argument("<agents...>", "Member agent ids").option("--label <label>", "Human-readable label").action(async (id, agents, options) => {
32707
33466
  const outputPath = await createAgentTeamFile(process.cwd(), id, agents, options.label);
32708
33467
  console.log(`Created ${outputPath}`);
32709
33468
  });
33469
+ command.command("team-show").description("Show one markdown-backed team definition.").argument("<id>", "Team id").action(async (id) => {
33470
+ const team = await loadAgentTeamById(process.cwd(), id);
33471
+ if (!team) {
33472
+ throw new Error(`Unknown team "${id}".`);
33473
+ }
33474
+ console.log(team.id);
33475
+ console.log(` label: ${team.label}`);
33476
+ console.log(` agents: ${team.agents.join(", ") || "-"}`);
33477
+ console.log(` match: ${team.match.join(", ") || "-"}`);
33478
+ console.log(` strategy: ${team.strategy}`);
33479
+ console.log(` file: ${team.filePath}`);
33480
+ if (team.description) {
33481
+ console.log("");
33482
+ console.log(team.description);
33483
+ }
33484
+ });
33485
+ command.command("team-delete").description("Delete one markdown-backed team definition.").argument("<id>", "Team id").action(async (id) => {
33486
+ const outputPath = await deleteAgentTeamFile(process.cwd(), id);
33487
+ console.log(`Deleted ${outputPath}`);
33488
+ });
32710
33489
  return command;
32711
33490
  }
32712
33491
 
@@ -33523,7 +34302,7 @@ async function renderMcpInventory(cwd, name) {
33523
34302
  ` prompts: ${server.prompts.length}`,
33524
34303
  ...server.prompts.slice(0, 10).flatMap((prompt) => [
33525
34304
  ` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
33526
- ` command: /mcp__${server.name}__${prompt.name}`
34305
+ ` command: /mcp__${normalizeMcpCommandSegment(server.name)}__${normalizeMcpCommandSegment(prompt.name)}`
33527
34306
  ]),
33528
34307
  ` resources: ${server.resources.length}`,
33529
34308
  ...server.resources.slice(0, 10).flatMap((resource) => [
@@ -33549,7 +34328,7 @@ async function renderMcpPromptList(cwd, name) {
33549
34328
  ] : [],
33550
34329
  ...server.prompts.length > 0 ? server.prompts.flatMap((prompt) => [
33551
34330
  ` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
33552
- ` command: /mcp__${server.name}__${prompt.name}`
34331
+ ` command: /mcp__${normalizeMcpCommandSegment(server.name)}__${normalizeMcpCommandSegment(prompt.name)}`
33553
34332
  ]) : [
33554
34333
  " - no prompts"
33555
34334
  ]
@@ -35490,53 +36269,65 @@ function renderBox(lines) {
35490
36269
  function renderHelp() {
35491
36270
  return [
35492
36271
  `${color(DIM, "Commands")}`,
35493
- "/status Show the active role, provider, and workspace state.",
35494
- "/ask <prompt> Send a prompt to the active model without running the agent.",
35495
- "/chat <prompt> Alias for /ask <prompt>.",
35496
- "/reset-chat Clear the active model conversation history.",
35497
- "/model Show current model, provider, and role state.",
36272
+ `${color(BOLD, "Core")}`,
36273
+ "/status Show active model, workspace, approvals, and sandbox state.",
36274
+ "/ask <prompt> Chat with the focused model without running the agent.",
36275
+ "/plan <goal> Create a structured plan only.",
36276
+ "/run <goal> Execute a goal immediately.",
36277
+ "/resume Continue the latest paused or awaiting-approval session.",
36278
+ "/review Review the current git diff and summarize risk.",
36279
+ "/clear Redraw the shell.",
36280
+ "/quit, /exit Leave the shell.",
36281
+ "",
36282
+ `${color(BOLD, "Model")}`,
36283
+ "/model Show current provider/model state.",
35498
36284
  "/model providers List configured providers.",
35499
36285
  "/model add <tpl> [id] Add a provider template.",
35500
36286
  "/model use <provider> [model] Switch provider/model for all roles.",
35501
36287
  "/model find [search] Discover models for the active provider.",
35502
- "/model select <n> Pick a model from the last numbered list.",
35503
- "/model focus <role> Change shell focus to planner, coder, reviewer, or fast.",
35504
- "/permissions Show approval, sandbox, and trusted directory settings.",
36288
+ "/model select <n> Pick from the last numbered model list.",
36289
+ "/model focus <role> Change focus to planner, coder, reviewer, or fast.",
36290
+ "",
36291
+ `${color(BOLD, "Permissions")}`,
36292
+ "/permissions Show layered approval, sandbox, and trust settings.",
35505
36293
  "/permissions auto|manual Change approval mode.",
35506
36294
  "/permissions sandbox <mode> Set read-only, workspace-write, or full.",
35507
36295
  "/permissions trust <path> Add a trusted directory.",
35508
36296
  "/permissions untrust <path> Remove a trusted directory.",
35509
- "/memory Show project, user, and agent memory paths.",
35510
- "/memory refresh Refresh kimbho_init.md and project memory.",
36297
+ "/approve [id] Approve a pending risky action and continue.",
36298
+ "/approve-all Approve every pending action in the current session.",
36299
+ "/deny [id] Deny a pending risky action.",
36300
+ "",
36301
+ `${color(BOLD, "Memory")}`,
36302
+ "/init [--memory-only] Build config and durable project understanding files.",
36303
+ "/memory Show init/project/user/agent memory paths.",
36304
+ "/memory refresh Re-scan the project and refresh markdown memory.",
35511
36305
  "/memory show <scope> Print init, project, user, or agent memory.",
35512
- "/memory add <scope> <text> Append a markdown memory note.",
36306
+ "/memory add <scope> ... Append a markdown memory note.",
35513
36307
  "/config Show config, memory, and MCP file locations.",
36308
+ "",
36309
+ `${color(BOLD, "MCP")}`,
35514
36310
  "/mcp List configured MCP servers.",
36311
+ "/mcp discover [server] Show MCP tools, prompts-as-commands, and resources.",
36312
+ "/mcp prompt <server> <prompt> [key=value ...] Render one MCP prompt.",
36313
+ "/mcp read <server> <uri> Read one MCP resource.",
35515
36314
  "/mcp add <name> --command <cmd> [--arg <value>] [--env KEY=VALUE]",
35516
36315
  " Add an MCP stdio server to .mcp.json.",
35517
- "/mcp tools [server] Discover tools/prompts/resources from MCP servers.",
35518
- "/mcp prompts [server] List MCP prompts.",
35519
- "/mcp resources [server] List MCP resources and templates.",
35520
- "/mcp prompt <server> <prompt> [key=value ...]",
35521
- " Render an MCP prompt.",
35522
- "/mcp read <server> <uri> Read one MCP resource.",
36316
+ "/mcp__server__prompt ... Invoke an MCP prompt directly from the shell.",
36317
+ "@server:uri Attach an MCP resource inside /ask, /plan, or /run prompts.",
36318
+ "",
36319
+ `${color(BOLD, "Agents")}`,
35523
36320
  "/agents List custom agents and teams.",
36321
+ "/agents show <id> Show one custom agent definition.",
36322
+ "/agents delete <id> Delete one custom agent definition.",
35524
36323
  "/agents create <id> --base <role> Create a markdown custom agent.",
35525
36324
  "/agents team create <id> <agent...> Create a markdown team file.",
35526
- "/init [--memory-only] Create or refresh config and markdown memory files.",
35527
- "/plan <goal> Create a structured implementation plan.",
35528
- "/run <goal> Start a Kimbho execution session for a goal.",
35529
- "/resume Show the latest saved session.",
35530
- "/approve [id] Approve a pending risky action and continue the session.",
35531
- "/approve-all Approve all pending actions in the current session.",
35532
- "/deny [id] Deny a pending risky action.",
35533
- "/review Review the current git diff and summarize risk.",
35534
- "/doctor Check local environment and config.",
35535
- "/clear Redraw the shell.",
35536
- "/quit, /exit Leave the shell.",
36325
+ "/agents team show <id> Show one team definition.",
36326
+ "/agents team delete <id> Delete one team definition.",
35537
36327
  "",
35538
36328
  `${color(DIM, "Tip")}`,
35539
36329
  "Type build/change requests directly to run the agent. Type plan/design prompts for planning. Use /ask when you want plain chat.",
36330
+ "MCP prompts behave like slash commands, and MCP resources can be attached inline with @server:uri.",
35540
36331
  "Legacy aliases like /providers, /models, /select, /use-model, /brain, and /approval still work.",
35541
36332
  "Press Ctrl+C during an active run to pause it and return to the shell, then use /resume to continue."
35542
36333
  ].join("\n");
@@ -36769,7 +37560,7 @@ async function handleMcpCommand(cwd, tokens) {
36769
37560
  }
36770
37561
  return;
36771
37562
  }
36772
- if (subcommand === "tools" || subcommand === "inspect") {
37563
+ if (subcommand === "tools" || subcommand === "inspect" || subcommand === "discover") {
36773
37564
  const name = tokens[2]?.trim();
36774
37565
  for (const line of await renderMcpInventory(cwd, name)) {
36775
37566
  console.log(line);
@@ -36805,10 +37596,12 @@ async function handleMcpCommand(cwd, tokens) {
36805
37596
  return;
36806
37597
  }
36807
37598
  if (subcommand === "read") {
36808
- const serverName = tokens[2]?.trim();
36809
- const uri = tokens.slice(3).join(" ").trim();
37599
+ const shorthand = tokens[2]?.trim();
37600
+ const shorthandMatch = shorthand?.match(/^@([^:\s]+):(.+)$/);
37601
+ const serverName = shorthandMatch ? shorthandMatch[1] : tokens[2]?.trim();
37602
+ const uri = shorthandMatch ? shorthandMatch[2] : tokens.slice(3).join(" ").trim();
36810
37603
  if (!serverName || !uri) {
36811
- throw new Error("Usage: /mcp read <server> <uri>");
37604
+ throw new Error("Usage: /mcp read <server> <uri> | /mcp read @server:uri");
36812
37605
  }
36813
37606
  for (const line of await renderMcpResourceRead(cwd, serverName, uri)) {
36814
37607
  console.log(line);
@@ -36878,7 +37671,7 @@ async function handleMcpCommand(cwd, tokens) {
36878
37671
  console.log(`Added MCP server ${name}`);
36879
37672
  return;
36880
37673
  }
36881
- throw new Error("Usage: /mcp [list|tools [server]|prompts [server]|resources [server]|prompt <server> <prompt>|read <server> <uri>|add <name> --command <cmd>|remove <name>|enable <name>|disable <name>]");
37674
+ throw new Error("Usage: /mcp [list|discover [server]|prompts [server]|resources [server]|prompt <server> <prompt>|read <server> <uri>|read @server:uri|add <name> --command <cmd>|remove <name>|enable <name>|disable <name>]");
36882
37675
  }
36883
37676
  async function handleAgentsCommand(cwd, tokens) {
36884
37677
  const subcommand = tokens[1]?.trim().toLowerCase();
@@ -36923,6 +37716,38 @@ async function handleAgentsCommand(cwd, tokens) {
36923
37716
  console.log(`Created ${outputPath}`);
36924
37717
  return;
36925
37718
  }
37719
+ if (subcommand === "show") {
37720
+ const id = tokens[2]?.trim();
37721
+ if (!id) {
37722
+ throw new Error("Usage: /agents show <id>");
37723
+ }
37724
+ const agent = await loadCustomAgentById(cwd, id);
37725
+ if (!agent) {
37726
+ throw new Error(`Unknown custom agent "${id}".`);
37727
+ }
37728
+ console.log(`${agent.id}`);
37729
+ console.log(` label: ${agent.label}`);
37730
+ console.log(` baseRole: ${agent.baseRole}`);
37731
+ console.log(` brainRole: ${agent.brainRole ?? "-"}`);
37732
+ console.log(` purpose: ${agent.purpose ?? "-"}`);
37733
+ console.log(` match: ${agent.match.join(", ") || "-"}`);
37734
+ console.log(` tools: ${agent.tools.join(", ") || "(inherits base role tools)"}`);
37735
+ console.log(` file: ${agent.filePath}`);
37736
+ if (agent.instructions.trim()) {
37737
+ console.log("");
37738
+ console.log(agent.instructions.trim());
37739
+ }
37740
+ return;
37741
+ }
37742
+ if (subcommand === "delete") {
37743
+ const id = tokens[2]?.trim();
37744
+ if (!id) {
37745
+ throw new Error("Usage: /agents delete <id>");
37746
+ }
37747
+ const outputPath = await deleteCustomAgentFile(cwd, id);
37748
+ console.log(`Deleted ${outputPath}`);
37749
+ return;
37750
+ }
36926
37751
  if (subcommand === "team" && tokens[2]?.trim().toLowerCase() === "create") {
36927
37752
  const id = tokens[3]?.trim();
36928
37753
  const agentIds = tokens.slice(4).filter((token) => !token.startsWith("--"));
@@ -36935,7 +37760,37 @@ async function handleAgentsCommand(cwd, tokens) {
36935
37760
  console.log(`Created ${outputPath}`);
36936
37761
  return;
36937
37762
  }
36938
- throw new Error("Usage: /agents [list|create <id> --base <role>|team create <id> <agent...>]");
37763
+ if (subcommand === "team" && tokens[2]?.trim().toLowerCase() === "show") {
37764
+ const id = tokens[3]?.trim();
37765
+ if (!id) {
37766
+ throw new Error("Usage: /agents team show <id>");
37767
+ }
37768
+ const team = await loadAgentTeamById(cwd, id);
37769
+ if (!team) {
37770
+ throw new Error(`Unknown team "${id}".`);
37771
+ }
37772
+ console.log(`${team.id}`);
37773
+ console.log(` label: ${team.label}`);
37774
+ console.log(` agents: ${team.agents.join(", ") || "-"}`);
37775
+ console.log(` match: ${team.match.join(", ") || "-"}`);
37776
+ console.log(` strategy: ${team.strategy}`);
37777
+ console.log(` file: ${team.filePath}`);
37778
+ if (team.description) {
37779
+ console.log("");
37780
+ console.log(team.description);
37781
+ }
37782
+ return;
37783
+ }
37784
+ if (subcommand === "team" && tokens[2]?.trim().toLowerCase() === "delete") {
37785
+ const id = tokens[3]?.trim();
37786
+ if (!id) {
37787
+ throw new Error("Usage: /agents team delete <id>");
37788
+ }
37789
+ const outputPath = await deleteAgentTeamFile(cwd, id);
37790
+ console.log(`Deleted ${outputPath}`);
37791
+ return;
37792
+ }
37793
+ throw new Error("Usage: /agents [list|show <id>|delete <id>|create <id> --base <role>|team create <id> <agent...>|team show <id>|team delete <id>]");
36939
37794
  }
36940
37795
  async function handleModelSurfaceCommand(cwd, tokens, runtime) {
36941
37796
  const subcommand = tokens[1]?.trim().toLowerCase();