@loomweaver/mcp 0.14.2 → 0.14.4

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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/main.mjs +131 -131
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,9 @@ is not inside this repo (a chat assistant, a remote client) can scaffold and val
9
9
  - `list_generators` — the available generators.
10
10
  - `scaffold_weaver` · `scaffold_frame_plugin` · `scaffold_distribution` · `scaffold_auth_source` ·
11
11
  `scaffold_settings_store` · `scaffold_theme` · `scaffold_layout` — return a **file map**
12
- (`path -> content`); the client writes the files into its project.
12
+ (`path -> content`); the client writes the files into its project. The server cannot read that
13
+ project, so pass `scaffold_weaver` the selector prefix your application declares as `prefix`;
14
+ without it the components are named `app-…`.
13
15
  - `validate_manifest` · `validate_i18n` · `validate_catalog` · `validate_commands` — return structured `Finding[]`.
14
16
 
15
17
  ## Consume from npm (product repos)
package/dist/main.mjs CHANGED
@@ -31154,9 +31154,9 @@ function renderRegions(indent) {
31154
31154
  // ../devkit/src/recipes/angular-weaver/agent-panel.ts
31155
31155
  function panelFile(weaver) {
31156
31156
  return `import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
31157
- import { EventType, type BaseEvent, type Tool, type ToolMessage } from '@ag-ui/core';
31158
- import { ${weaver.propertyName}Agent } from './${weaver.id}-agent';
31159
- import { askAgent } from './${weaver.id}-agent-source';
31157
+ import { EventType, type AGUIEvent, type Tool, type ToolMessage } from '@ag-ui/core';
31158
+ import { askAgent } from './${weaver.id}-agent';
31159
+ import { ${weaver.propertyName}Tools } from './${weaver.id}-connection';
31160
31160
 
31161
31161
  interface Line {
31162
31162
  readonly kind: 'you' | 'agent' | 'call' | 'result' | 'note';
@@ -31186,11 +31186,11 @@ export class ${weaver.className}AgentPanel {
31186
31186
  // The list is read again whenever the panel gains focus, because the weaver's own translations
31187
31187
  // and other plugins' commands may arrive after this component was built.
31188
31188
  protected refreshOffered(): void {
31189
- this.offered.set(${weaver.propertyName}Agent()?.list() ?? []);
31189
+ this.offered.set(${weaver.propertyName}Tools()?.list() ?? []);
31190
31190
  }
31191
31191
 
31192
31192
  protected async ask(name: string): Promise<void> {
31193
- const tools = ${weaver.propertyName}Agent();
31193
+ const tools = ${weaver.propertyName}Tools();
31194
31194
  if (!tools || this.busy()) {
31195
31195
  return;
31196
31196
  }
@@ -31233,24 +31233,19 @@ export class ${weaver.className}AgentPanel {
31233
31233
  }
31234
31234
  }
31235
31235
 
31236
- private draw(event: BaseEvent): void {
31237
- const raw = event as unknown as Record<string, unknown>;
31236
+ private draw(event: AGUIEvent): void {
31238
31237
  switch (event.type) {
31239
31238
  case EventType.TEXT_MESSAGE_START:
31240
31239
  this.push({ kind: 'agent', text: '' });
31241
31240
  break;
31242
31241
  case EventType.TEXT_MESSAGE_CONTENT:
31243
- this.grow('text', String(raw['delta'] ?? ''));
31242
+ this.grow('text', event.delta);
31244
31243
  break;
31245
31244
  case EventType.TOOL_CALL_START:
31246
- this.push({
31247
- kind: 'call',
31248
- text: String(raw['toolCallName'] ?? ''),
31249
- args: '',
31250
- });
31245
+ this.push({ kind: 'call', text: event.toolCallName, args: '' });
31251
31246
  break;
31252
31247
  case EventType.TOOL_CALL_ARGS:
31253
- this.grow('args', String(raw['delta'] ?? ''));
31248
+ this.grow('args', event.delta);
31254
31249
  break;
31255
31250
  default:
31256
31251
  break;
@@ -31279,7 +31274,7 @@ function panelTemplateFile(weaver) {
31279
31274
  class="shrink-0 rounded-md border border-border bg-surface-raised px-3 py-2 text-xs text-content-muted"
31280
31275
  >
31281
31276
  This is a stand-in, not an assistant. It speaks the protocol so you can watch the whole path;
31282
- replace <code class="text-content">${weaver.id}-agent-source.ts</code> with your own transport.
31277
+ replace <code class="text-content">${weaver.id}-agent.ts</code> with your own transport.
31283
31278
  </p>
31284
31279
 
31285
31280
  <div class="min-h-0 flex-1 overflow-auto">
@@ -31347,7 +31342,7 @@ function panelTemplateFile(weaver) {
31347
31342
 
31348
31343
  // ../devkit/src/recipes/angular-weaver/agent-stand-in.ts
31349
31344
  function standInFile(weaver) {
31350
- return `import { EventType, type BaseEvent, type Tool } from '@ag-ui/core';
31345
+ return `import { EventType, type AGUIEvent, type Tool } from '@ag-ui/core';
31351
31346
 
31352
31347
  // WHAT THIS IS: a stand-in for an agent, and not an agent. It speaks the AG-UI protocol and nothing
31353
31348
  // else \u2014 no model, no network, no judgement \u2014 so that the whole path from the offered tools through
@@ -31367,15 +31362,16 @@ export interface AgentRequest {
31367
31362
 
31368
31363
  export async function* askAgent(
31369
31364
  request: AgentRequest,
31370
- ): AsyncGenerator<BaseEvent> {
31365
+ ): AsyncGenerator<AGUIEvent> {
31371
31366
  const picked = request.tools.find((tool) =>
31372
31367
  request.prompt.includes(tool.name),
31373
31368
  );
31374
31369
 
31375
- yield event(EventType.RUN_STARTED, {
31370
+ yield {
31371
+ type: EventType.RUN_STARTED,
31376
31372
  threadId: '${weaver.id}-stand-in',
31377
31373
  runId: request.runId,
31378
- });
31374
+ };
31379
31375
  yield* speak(
31380
31376
  \`\${request.runId}.says\`,
31381
31377
  picked
@@ -31385,37 +31381,35 @@ export async function* askAgent(
31385
31381
 
31386
31382
  if (picked) {
31387
31383
  const toolCallId = \`\${request.runId}.call\`;
31388
- yield event(EventType.TOOL_CALL_START, {
31389
- toolCallId,
31390
- toolCallName: picked.name,
31391
- });
31384
+ yield { type: EventType.TOOL_CALL_START, toolCallId, toolCallName: picked.name };
31392
31385
  // Arguments arrive in pieces, exactly as they do from a real model. The adapter assembles them;
31393
31386
  // nothing downstream ever sees a half-written call. The values come from the JSON Schema the
31394
31387
  // workbench described in picked.parameters: the first declared choice of the first argument that
31395
31388
  // has choices, and nothing for the rest, so the command applies its own defaults there.
31396
31389
  for (const piece of chunks(JSON.stringify(sampleArguments(picked)), 4)) {
31397
- yield event(EventType.TOOL_CALL_ARGS, { toolCallId, delta: piece });
31390
+ yield { type: EventType.TOOL_CALL_ARGS, toolCallId, delta: piece };
31398
31391
  await pause();
31399
31392
  }
31400
- yield event(EventType.TOOL_CALL_END, { toolCallId });
31393
+ yield { type: EventType.TOOL_CALL_END, toolCallId };
31401
31394
  }
31402
31395
 
31403
- yield event(EventType.RUN_FINISHED, {
31396
+ yield {
31397
+ type: EventType.RUN_FINISHED,
31404
31398
  threadId: '${weaver.id}-stand-in',
31405
31399
  runId: request.runId,
31406
- });
31400
+ };
31407
31401
  }
31408
31402
 
31409
31403
  async function* speak(
31410
31404
  messageId: string,
31411
31405
  text: string,
31412
- ): AsyncGenerator<BaseEvent> {
31413
- yield event(EventType.TEXT_MESSAGE_START, { messageId, role: 'assistant' });
31406
+ ): AsyncGenerator<AGUIEvent> {
31407
+ yield { type: EventType.TEXT_MESSAGE_START, messageId, role: 'assistant' };
31414
31408
  for (const piece of chunks(text, 10)) {
31415
- yield event(EventType.TEXT_MESSAGE_CONTENT, { messageId, delta: piece });
31409
+ yield { type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta: piece };
31416
31410
  await pause();
31417
31411
  }
31418
- yield event(EventType.TEXT_MESSAGE_END, { messageId });
31412
+ yield { type: EventType.TEXT_MESSAGE_END, messageId };
31419
31413
  }
31420
31414
 
31421
31415
  function sampleArguments(tool: Tool): Record<string, unknown> {
@@ -31438,10 +31432,6 @@ function chunks(text: string, size: number): readonly string[] {
31438
31432
  return pieces;
31439
31433
  }
31440
31434
 
31441
- function event(type: EventType, fields: Record<string, unknown>): BaseEvent {
31442
- return { type, ...fields } as unknown as BaseEvent;
31443
- }
31444
-
31445
31435
  function pause(): Promise<void> {
31446
31436
  return new Promise((done) => setTimeout(done, PAUSE_MS));
31447
31437
  }
@@ -31460,15 +31450,18 @@ import {
31460
31450
  } from '@loomweaver/ag-ui';
31461
31451
  import type { PluginContext } from '@loomweaver/plugin-sdk';
31462
31452
 
31453
+ // The connection is the workbench's half: the commands offered as tools, and each call put to the
31454
+ // workbench and answered. The agent is yours and lives beside it, in ${weaver.id}-agent.ts.
31455
+ //
31463
31456
  // A factory, not a module-level connection: everything a run needs lives in the closure, so a second
31464
31457
  // one never shares state with the first.
31465
- export function ${weaver.propertyName}Connection(ctx: PluginContext): CommandTools {
31458
+ export function connect${weaver.className}(ctx: PluginContext): CommandTools {
31466
31459
  return commandTools(ctx, { before: (call) => decide(ctx, call) });
31467
31460
  }
31468
31461
 
31469
31462
  // The one connection this plugin activates, published for its panel. Set in activate(), cleared in
31470
31463
  // deactivate(), so the panel renders an honest empty state either side of that.
31471
- export const ${weaver.propertyName}Agent = signal<CommandTools | null>(null);
31464
+ export const ${weaver.propertyName}Tools = signal<CommandTools | null>(null);
31472
31465
 
31473
31466
  // What an agent's word is enough for is the command's own statement, declared where the command is
31474
31467
  // registered and read off the call here. No list of ids lives beside the commands: a list drifts from
@@ -31503,9 +31496,10 @@ async function decide(
31503
31496
  `;
31504
31497
  }
31505
31498
  function specFile(weaver) {
31506
- return `import { EventType, type BaseEvent } from '@ag-ui/core';
31499
+ return `import { EventType, type AGUIEvent, type ToolMessage } from '@ag-ui/core';
31500
+ import type { CommandTools } from '@loomweaver/ag-ui';
31507
31501
  import type { CommandArguments, PluginContext } from '@loomweaver/plugin-sdk';
31508
- import { ${weaver.propertyName}Connection } from './${weaver.id}-agent';
31502
+ import { connect${weaver.className} } from './${weaver.id}-connection';
31509
31503
 
31510
31504
  interface Asked {
31511
31505
  readonly id: string;
@@ -31526,37 +31520,36 @@ function contextThat(confirms: boolean, ran: Asked[]): PluginContext {
31526
31520
  } as unknown as PluginContext;
31527
31521
  }
31528
31522
 
31529
- function event(type: EventType, fields: Record<string, unknown>): BaseEvent {
31530
- return { type, ...fields } as unknown as BaseEvent;
31523
+ function streamedCall(toolCallId: string, ...deltas: string[]): AGUIEvent[] {
31524
+ return [
31525
+ { type: EventType.TOOL_CALL_START, toolCallId, toolCallName: '${weaver.id}.hello' },
31526
+ ...deltas.map((delta): AGUIEvent => ({ type: EventType.TOOL_CALL_ARGS, toolCallId, delta })),
31527
+ { type: EventType.TOOL_CALL_END, toolCallId },
31528
+ ];
31529
+ }
31530
+
31531
+ async function answersTo(tools: CommandTools, events: AGUIEvent[]): Promise<ToolMessage[]> {
31532
+ const answers: ToolMessage[] = [];
31533
+ for (const event of events) {
31534
+ const answer = await tools.receive(event);
31535
+ if (answer) {
31536
+ answers.push(answer);
31537
+ }
31538
+ }
31539
+ return answers;
31531
31540
  }
31532
31541
 
31533
- describe('${weaver.propertyName}Connection', () => {
31542
+ describe('connect${weaver.className}', () => {
31534
31543
  it('offers what the workbench offers', () => {
31535
- const tools = ${weaver.propertyName}Connection(contextThat(true, []));
31544
+ const tools = connect${weaver.className}(contextThat(true, []));
31536
31545
  expect(tools.list().map((tool) => tool.name)).toEqual(['${weaver.id}.hello']);
31537
31546
  });
31538
31547
 
31539
31548
  it('assembles a call from its events and answers with the outcome', async () => {
31540
31549
  const ran: Asked[] = [];
31541
- const tools = ${weaver.propertyName}Connection(contextThat(true, ran));
31550
+ const tools = connect${weaver.className}(contextThat(true, ran));
31542
31551
 
31543
- expect(
31544
- await tools.receive(
31545
- event(EventType.TOOL_CALL_START, {
31546
- toolCallId: 'c1',
31547
- toolCallName: '${weaver.id}.hello',
31548
- }),
31549
- ),
31550
- ).toBeNull();
31551
- await tools.receive(
31552
- event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c1', delta: '{"tone"' }),
31553
- );
31554
- await tools.receive(
31555
- event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c1', delta: ':"success"}' }),
31556
- );
31557
- const answer = await tools.receive(
31558
- event(EventType.TOOL_CALL_END, { toolCallId: 'c1' }),
31559
- );
31552
+ const [answer] = await answersTo(tools, streamedCall('c1', '{"tone"', ':"success"}'));
31560
31553
 
31561
31554
  expect(ran).toEqual([{ id: '${weaver.id}.hello', args: { tone: 'success' } }]);
31562
31555
  expect(JSON.parse(answer?.content ?? '{}').tone).toBe('success');
@@ -31565,20 +31558,9 @@ describe('${weaver.propertyName}Connection', () => {
31565
31558
 
31566
31559
  it('never reaches the workbench when a consequential call is declined', async () => {
31567
31560
  const ran: Asked[] = [];
31568
- const tools = ${weaver.propertyName}Connection(contextThat(false, ran));
31561
+ const tools = connect${weaver.className}(contextThat(false, ran));
31569
31562
 
31570
- await tools.receive(
31571
- event(EventType.TOOL_CALL_START, {
31572
- toolCallId: 'c2',
31573
- toolCallName: '${weaver.id}.hello',
31574
- }),
31575
- );
31576
- await tools.receive(
31577
- event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c2', delta: '{}' }),
31578
- );
31579
- const answer = await tools.receive(
31580
- event(EventType.TOOL_CALL_END, { toolCallId: 'c2' }),
31581
- );
31563
+ const [answer] = await answersTo(tools, streamedCall('c2', '{}'));
31582
31564
 
31583
31565
  expect(ran).toEqual([]);
31584
31566
  expect(answer?.error).toContain('did not run');
@@ -31600,13 +31582,13 @@ function agentSurfaceBlock(weaver) {
31600
31582
  }
31601
31583
  function agentFiles(weaver) {
31602
31584
  const files = {
31603
- [`src/lib/agent/${weaver.id}-agent.ts`]: connectionFile(weaver),
31604
- [`src/lib/agent/${weaver.id}-agent-source.ts`]: standInFile(weaver),
31585
+ [`src/lib/agent/${weaver.id}-connection.ts`]: connectionFile(weaver),
31586
+ [`src/lib/agent/${weaver.id}-agent.ts`]: standInFile(weaver),
31605
31587
  [`src/lib/agent/${weaver.id}-agent-panel.ts`]: panelFile(weaver),
31606
31588
  [`src/lib/agent/${weaver.id}-agent-panel.html`]: panelTemplateFile(weaver)
31607
31589
  };
31608
31590
  if (weaver.features.spec) {
31609
- files[`src/lib/agent/${weaver.id}-agent.spec.ts`] = specFile(weaver);
31591
+ files[`src/lib/agent/${weaver.id}-connection.spec.ts`] = specFile(weaver);
31610
31592
  }
31611
31593
  return files;
31612
31594
  }
@@ -31645,6 +31627,45 @@ function i18nFile(weaver) {
31645
31627
  return JSON.stringify(i18nBundle(weaver), null, 2) + "\n";
31646
31628
  }
31647
31629
 
31630
+ // ../devkit/src/lib/scaffolds/scaffold-values.ts
31631
+ var NEUTRAL_PREFIX = "app";
31632
+ function stringValue(values, name) {
31633
+ const value = values[name];
31634
+ return typeof value === "string" ? value : void 0;
31635
+ }
31636
+ function booleanValue(values, name) {
31637
+ const value = values[name];
31638
+ return typeof value === "boolean" ? value : void 0;
31639
+ }
31640
+ var PLACEMENT_OPTIONS = [
31641
+ {
31642
+ name: "directory",
31643
+ type: "string",
31644
+ description: "Project root, relative to the workspace root.",
31645
+ workspaceOnly: true
31646
+ },
31647
+ {
31648
+ name: "tags",
31649
+ type: "string",
31650
+ description: "Comma-separated Nx tags for the project.",
31651
+ workspaceOnly: true
31652
+ },
31653
+ {
31654
+ name: "unitTestRunner",
31655
+ type: "string",
31656
+ description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
31657
+ choices: ["vitest", "none"],
31658
+ default: "vitest",
31659
+ workspaceOnly: true
31660
+ }
31661
+ ];
31662
+ var APP_OPTION = {
31663
+ name: "app",
31664
+ type: "string",
31665
+ description: "Application to drop into. Inferred when the workspace has exactly one.",
31666
+ workspaceOnly: true
31667
+ };
31668
+
31648
31669
  // ../devkit/src/lib/validate/manifest.ts
31649
31670
  var KNOWN_CAPABILITIES = [
31650
31671
  "contributions",
@@ -31804,6 +31825,18 @@ function deriveCapabilities(features) {
31804
31825
  }
31805
31826
  return KNOWN_CAPABILITIES.filter((capability) => set2.has(capability));
31806
31827
  }
31828
+ function resolvePrefix(prefix) {
31829
+ const trimmed = prefix?.trim();
31830
+ if (!trimmed) {
31831
+ return NEUTRAL_PREFIX;
31832
+ }
31833
+ if (!isKebabId(trimmed)) {
31834
+ throw new Error(
31835
+ `Selector prefix must be kebab-case (e.g. "acme"); got "${trimmed}".`
31836
+ );
31837
+ }
31838
+ return trimmed;
31839
+ }
31807
31840
  function resolveWeaverInput(input) {
31808
31841
  if (!isKebabId(input.id)) {
31809
31842
  throw new Error(
@@ -31819,7 +31852,7 @@ function resolveWeaverInput(input) {
31819
31852
  propertyName: toCamelCase(input.id),
31820
31853
  capabilities,
31821
31854
  features,
31822
- prefix: input.prefix?.trim() || "lw",
31855
+ prefix: resolvePrefix(input.prefix),
31823
31856
  importPath: input.importPath?.trim() || `@loomweaver/${input.id}-weaver`
31824
31857
  };
31825
31858
  }
@@ -32016,7 +32049,7 @@ function pluginImports(weaver) {
32016
32049
  }
32017
32050
  if (weaver.features.agent) {
32018
32051
  imports.push(
32019
- `import { ${weaver.propertyName}Agent, ${weaver.propertyName}Connection } from '../agent/${weaver.id}-agent';`,
32052
+ `import { connect${weaver.className}, ${weaver.propertyName}Tools } from '../agent/${weaver.id}-connection';`,
32020
32053
  `import { ${weaver.className}AgentPanel } from '../agent/${weaver.id}-agent-panel';`
32021
32054
  );
32022
32055
  }
@@ -32041,7 +32074,7 @@ function pluginBody(weaver) {
32041
32074
  const body = [` ctx.contributeIcons({ '${weaver.id}': icon });`];
32042
32075
  if (weaver.features.agent) {
32043
32076
  body.push(
32044
- ` ${weaver.propertyName}Agent.set(${weaver.propertyName}Connection(ctx));`
32077
+ ` ${weaver.propertyName}Tools.set(connect${weaver.className}(ctx));`
32045
32078
  );
32046
32079
  }
32047
32080
  if (weaver.features.command) body.push(commandBlock(weaver));
@@ -32058,7 +32091,7 @@ function pluginFile(weaver) {
32058
32091
  const body = pluginBody(weaver);
32059
32092
  const deactivate = weaver.features.agent ? `
32060
32093
  deactivate() {
32061
- ${weaver.propertyName}Agent.set(null);
32094
+ ${weaver.propertyName}Tools.set(null);
32062
32095
  },` : "";
32063
32096
  return `${pluginImports(weaver).join("\n")}
32064
32097
 
@@ -32081,7 +32114,7 @@ function accessLine(weaver) {
32081
32114
  }
32082
32115
 
32083
32116
  // ../devkit/src/recipes/platform-version.ts
32084
- var PLATFORM_VERSION = "0.14.2";
32117
+ var PLATFORM_VERSION = "0.14.4";
32085
32118
 
32086
32119
  // ../devkit/src/recipes/readme-notes.ts
32087
32120
  var UNTAGGED_NOTE = [
@@ -32155,11 +32188,11 @@ function agentNotes(weaver) {
32155
32188
  "",
32156
32189
  `\`src/lib/agent/\` holds three files and one of them is meant to be thrown away.`,
32157
32190
  "",
32158
- `- \`${weaver.id}-agent.ts\` is the connection: the workbench's own commands offered as tools, and a`,
32191
+ `- \`${weaver.id}-connection.ts\` is the connection: the workbench's own commands offered as tools, and a`,
32159
32192
  " seam where this weaver decides about a call before it runs. Nothing is registered twice \u2014 the",
32160
32193
  " list comes from the workbench, already narrowed by everything that would refuse the call.",
32161
32194
  `- \`${weaver.id}-agent-panel.ts\` shows what is offered, the call as it streams, and the outcome.`,
32162
- `- \`${weaver.id}-agent-source.ts\` is a **stand-in**, not an assistant: it produces the protocol's own`,
32195
+ `- \`${weaver.id}-agent.ts\` is a **stand-in** for the agent, not an assistant: it produces the protocol's own`,
32163
32196
  " events so the whole path runs before you have connected anything. Replace that one file with",
32164
32197
  " your transport and nothing else changes. No transport, credential or model is generated for",
32165
32198
  " you, because none of them can be guessed.",
@@ -33524,52 +33557,6 @@ var layout = {
33524
33557
  }
33525
33558
  };
33526
33559
 
33527
- // ../devkit/src/lib/scaffolds/scaffold-values.ts
33528
- function stringValue(values, name) {
33529
- const value = values[name];
33530
- return typeof value === "string" ? value : void 0;
33531
- }
33532
- function booleanValue(values, name) {
33533
- const value = values[name];
33534
- return typeof value === "boolean" ? value : void 0;
33535
- }
33536
- var PLACEMENT_OPTIONS = [
33537
- {
33538
- name: "directory",
33539
- type: "string",
33540
- description: "Project root, relative to the workspace root.",
33541
- workspaceOnly: true
33542
- },
33543
- {
33544
- name: "tags",
33545
- type: "string",
33546
- description: "Comma-separated Nx tags for the project.",
33547
- workspaceOnly: true
33548
- },
33549
- {
33550
- name: "prefix",
33551
- type: "string",
33552
- description: "Selector prefix for generated components and directives.",
33553
- default: "lw",
33554
- pattern: KEBAB_ID_PATTERN,
33555
- workspaceOnly: true
33556
- },
33557
- {
33558
- name: "unitTestRunner",
33559
- type: "string",
33560
- description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
33561
- choices: ["vitest", "none"],
33562
- default: "vitest",
33563
- workspaceOnly: true
33564
- }
33565
- ];
33566
- var APP_OPTION = {
33567
- name: "app",
33568
- type: "string",
33569
- description: "Application to drop into. Inferred when the workspace has exactly one.",
33570
- workspaceOnly: true
33571
- };
33572
-
33573
33560
  // ../devkit/src/recipes/angular-distribution/scaffold.ts
33574
33561
  function distributionInput(values) {
33575
33562
  return {
@@ -33608,6 +33595,13 @@ var distributionScaffold = {
33608
33595
  description: "Compose into the application already at that path, replacing the bootstrap files this scaffold owns and merging its build targets. Without it an existing project is an error.",
33609
33596
  workspaceOnly: true
33610
33597
  },
33598
+ {
33599
+ name: "prefix",
33600
+ type: "string",
33601
+ description: "Selector prefix the application declares for its components, which a weaver generated into it takes. Defaults to 'app'.",
33602
+ pattern: KEBAB_ID_PATTERN,
33603
+ workspaceOnly: true
33604
+ },
33611
33605
  ...PLACEMENT_OPTIONS
33612
33606
  ],
33613
33607
  build: (values) => generate(angularDistribution, distributionInput(values)),
@@ -33771,6 +33765,12 @@ var weaverScaffold = {
33771
33765
  description: "Import path for the workspace alias. Defaults to the workspace scope plus the project name.",
33772
33766
  workspaceOnly: true
33773
33767
  },
33768
+ {
33769
+ name: "prefix",
33770
+ type: "string",
33771
+ description: "Selector prefix for the generated components: the one the application declares, as the prefix of its project in angular.json or project.json. Read from the workspace where it can be, else 'app'.",
33772
+ pattern: KEBAB_ID_PATTERN
33773
+ },
33774
33774
  APP_OPTION,
33775
33775
  ...PLACEMENT_OPTIONS
33776
33776
  ],
@@ -34570,7 +34570,7 @@ function inputSchema(descriptor) {
34570
34570
  function createMcpServer() {
34571
34571
  const server = new McpServer({
34572
34572
  name: "loomweaver-devkit",
34573
- version: "0.14.2"
34573
+ version: "0.14.4"
34574
34574
  });
34575
34575
  server.registerTool(
34576
34576
  "list_generators",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loomweaver/mcp",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "description": "LoomWeaver: open-source plugin platform for Angular workbenches. The MCP server: scaffolding and validation as tools, so an AI assistant in any product repo can build weavers without a checkout.",
5
5
  "keywords": [
6
6
  "loomweaver",