@loomweaver/cli 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.
- package/README.md +3 -0
- package/dist/main.mjs +156 -137
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,9 @@ the style pipeline, the build target, the entry stylesheet, the composition root
|
|
|
17
17
|
the output needs, listing each line it added. Anywhere else it writes the files and names what is
|
|
18
18
|
left to wire by hand.
|
|
19
19
|
|
|
20
|
+
A weaver's components are named with the selector prefix the Angular application declares in
|
|
21
|
+
`angular.json`, or `app` where it declares none; `--prefix` names another.
|
|
22
|
+
|
|
20
23
|
It also validates what the platform parses defensively, which is where mistakes go unreported:
|
|
21
24
|
|
|
22
25
|
```bash
|
package/dist/main.mjs
CHANGED
|
@@ -638,9 +638,9 @@ function renderRegions(indent) {
|
|
|
638
638
|
// ../devkit/src/recipes/angular-weaver/agent-panel.ts
|
|
639
639
|
function panelFile(weaver) {
|
|
640
640
|
return `import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
641
|
-
import { EventType, type
|
|
642
|
-
import {
|
|
643
|
-
import {
|
|
641
|
+
import { EventType, type AGUIEvent, type Tool, type ToolMessage } from '@ag-ui/core';
|
|
642
|
+
import { askAgent } from './${weaver.id}-agent';
|
|
643
|
+
import { ${weaver.propertyName}Tools } from './${weaver.id}-connection';
|
|
644
644
|
|
|
645
645
|
interface Line {
|
|
646
646
|
readonly kind: 'you' | 'agent' | 'call' | 'result' | 'note';
|
|
@@ -670,11 +670,11 @@ export class ${weaver.className}AgentPanel {
|
|
|
670
670
|
// The list is read again whenever the panel gains focus, because the weaver's own translations
|
|
671
671
|
// and other plugins' commands may arrive after this component was built.
|
|
672
672
|
protected refreshOffered(): void {
|
|
673
|
-
this.offered.set(${weaver.propertyName}
|
|
673
|
+
this.offered.set(${weaver.propertyName}Tools()?.list() ?? []);
|
|
674
674
|
}
|
|
675
675
|
|
|
676
676
|
protected async ask(name: string): Promise<void> {
|
|
677
|
-
const tools = ${weaver.propertyName}
|
|
677
|
+
const tools = ${weaver.propertyName}Tools();
|
|
678
678
|
if (!tools || this.busy()) {
|
|
679
679
|
return;
|
|
680
680
|
}
|
|
@@ -717,24 +717,19 @@ export class ${weaver.className}AgentPanel {
|
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
-
private draw(event:
|
|
721
|
-
const raw = event as unknown as Record<string, unknown>;
|
|
720
|
+
private draw(event: AGUIEvent): void {
|
|
722
721
|
switch (event.type) {
|
|
723
722
|
case EventType.TEXT_MESSAGE_START:
|
|
724
723
|
this.push({ kind: 'agent', text: '' });
|
|
725
724
|
break;
|
|
726
725
|
case EventType.TEXT_MESSAGE_CONTENT:
|
|
727
|
-
this.grow('text',
|
|
726
|
+
this.grow('text', event.delta);
|
|
728
727
|
break;
|
|
729
728
|
case EventType.TOOL_CALL_START:
|
|
730
|
-
this.push({
|
|
731
|
-
kind: 'call',
|
|
732
|
-
text: String(raw['toolCallName'] ?? ''),
|
|
733
|
-
args: '',
|
|
734
|
-
});
|
|
729
|
+
this.push({ kind: 'call', text: event.toolCallName, args: '' });
|
|
735
730
|
break;
|
|
736
731
|
case EventType.TOOL_CALL_ARGS:
|
|
737
|
-
this.grow('args',
|
|
732
|
+
this.grow('args', event.delta);
|
|
738
733
|
break;
|
|
739
734
|
default:
|
|
740
735
|
break;
|
|
@@ -763,7 +758,7 @@ function panelTemplateFile(weaver) {
|
|
|
763
758
|
class="shrink-0 rounded-md border border-border bg-surface-raised px-3 py-2 text-xs text-content-muted"
|
|
764
759
|
>
|
|
765
760
|
This is a stand-in, not an assistant. It speaks the protocol so you can watch the whole path;
|
|
766
|
-
replace <code class="text-content">${weaver.id}-agent
|
|
761
|
+
replace <code class="text-content">${weaver.id}-agent.ts</code> with your own transport.
|
|
767
762
|
</p>
|
|
768
763
|
|
|
769
764
|
<div class="min-h-0 flex-1 overflow-auto">
|
|
@@ -831,7 +826,7 @@ function panelTemplateFile(weaver) {
|
|
|
831
826
|
|
|
832
827
|
// ../devkit/src/recipes/angular-weaver/agent-stand-in.ts
|
|
833
828
|
function standInFile(weaver) {
|
|
834
|
-
return `import { EventType, type
|
|
829
|
+
return `import { EventType, type AGUIEvent, type Tool } from '@ag-ui/core';
|
|
835
830
|
|
|
836
831
|
// WHAT THIS IS: a stand-in for an agent, and not an agent. It speaks the AG-UI protocol and nothing
|
|
837
832
|
// else \u2014 no model, no network, no judgement \u2014 so that the whole path from the offered tools through
|
|
@@ -851,15 +846,16 @@ export interface AgentRequest {
|
|
|
851
846
|
|
|
852
847
|
export async function* askAgent(
|
|
853
848
|
request: AgentRequest,
|
|
854
|
-
): AsyncGenerator<
|
|
849
|
+
): AsyncGenerator<AGUIEvent> {
|
|
855
850
|
const picked = request.tools.find((tool) =>
|
|
856
851
|
request.prompt.includes(tool.name),
|
|
857
852
|
);
|
|
858
853
|
|
|
859
|
-
yield
|
|
854
|
+
yield {
|
|
855
|
+
type: EventType.RUN_STARTED,
|
|
860
856
|
threadId: '${weaver.id}-stand-in',
|
|
861
857
|
runId: request.runId,
|
|
862
|
-
}
|
|
858
|
+
};
|
|
863
859
|
yield* speak(
|
|
864
860
|
\`\${request.runId}.says\`,
|
|
865
861
|
picked
|
|
@@ -869,37 +865,35 @@ export async function* askAgent(
|
|
|
869
865
|
|
|
870
866
|
if (picked) {
|
|
871
867
|
const toolCallId = \`\${request.runId}.call\`;
|
|
872
|
-
yield
|
|
873
|
-
toolCallId,
|
|
874
|
-
toolCallName: picked.name,
|
|
875
|
-
});
|
|
868
|
+
yield { type: EventType.TOOL_CALL_START, toolCallId, toolCallName: picked.name };
|
|
876
869
|
// Arguments arrive in pieces, exactly as they do from a real model. The adapter assembles them;
|
|
877
870
|
// nothing downstream ever sees a half-written call. The values come from the JSON Schema the
|
|
878
871
|
// workbench described in picked.parameters: the first declared choice of the first argument that
|
|
879
872
|
// has choices, and nothing for the rest, so the command applies its own defaults there.
|
|
880
873
|
for (const piece of chunks(JSON.stringify(sampleArguments(picked)), 4)) {
|
|
881
|
-
yield
|
|
874
|
+
yield { type: EventType.TOOL_CALL_ARGS, toolCallId, delta: piece };
|
|
882
875
|
await pause();
|
|
883
876
|
}
|
|
884
|
-
yield
|
|
877
|
+
yield { type: EventType.TOOL_CALL_END, toolCallId };
|
|
885
878
|
}
|
|
886
879
|
|
|
887
|
-
yield
|
|
880
|
+
yield {
|
|
881
|
+
type: EventType.RUN_FINISHED,
|
|
888
882
|
threadId: '${weaver.id}-stand-in',
|
|
889
883
|
runId: request.runId,
|
|
890
|
-
}
|
|
884
|
+
};
|
|
891
885
|
}
|
|
892
886
|
|
|
893
887
|
async function* speak(
|
|
894
888
|
messageId: string,
|
|
895
889
|
text: string,
|
|
896
|
-
): AsyncGenerator<
|
|
897
|
-
yield
|
|
890
|
+
): AsyncGenerator<AGUIEvent> {
|
|
891
|
+
yield { type: EventType.TEXT_MESSAGE_START, messageId, role: 'assistant' };
|
|
898
892
|
for (const piece of chunks(text, 10)) {
|
|
899
|
-
yield
|
|
893
|
+
yield { type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta: piece };
|
|
900
894
|
await pause();
|
|
901
895
|
}
|
|
902
|
-
yield
|
|
896
|
+
yield { type: EventType.TEXT_MESSAGE_END, messageId };
|
|
903
897
|
}
|
|
904
898
|
|
|
905
899
|
function sampleArguments(tool: Tool): Record<string, unknown> {
|
|
@@ -922,10 +916,6 @@ function chunks(text: string, size: number): readonly string[] {
|
|
|
922
916
|
return pieces;
|
|
923
917
|
}
|
|
924
918
|
|
|
925
|
-
function event(type: EventType, fields: Record<string, unknown>): BaseEvent {
|
|
926
|
-
return { type, ...fields } as unknown as BaseEvent;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
919
|
function pause(): Promise<void> {
|
|
930
920
|
return new Promise((done) => setTimeout(done, PAUSE_MS));
|
|
931
921
|
}
|
|
@@ -944,15 +934,18 @@ import {
|
|
|
944
934
|
} from '@loomweaver/ag-ui';
|
|
945
935
|
import type { PluginContext } from '@loomweaver/plugin-sdk';
|
|
946
936
|
|
|
937
|
+
// The connection is the workbench's half: the commands offered as tools, and each call put to the
|
|
938
|
+
// workbench and answered. The agent is yours and lives beside it, in ${weaver.id}-agent.ts.
|
|
939
|
+
//
|
|
947
940
|
// A factory, not a module-level connection: everything a run needs lives in the closure, so a second
|
|
948
941
|
// one never shares state with the first.
|
|
949
|
-
export function ${weaver.
|
|
942
|
+
export function connect${weaver.className}(ctx: PluginContext): CommandTools {
|
|
950
943
|
return commandTools(ctx, { before: (call) => decide(ctx, call) });
|
|
951
944
|
}
|
|
952
945
|
|
|
953
946
|
// The one connection this plugin activates, published for its panel. Set in activate(), cleared in
|
|
954
947
|
// deactivate(), so the panel renders an honest empty state either side of that.
|
|
955
|
-
export const ${weaver.propertyName}
|
|
948
|
+
export const ${weaver.propertyName}Tools = signal<CommandTools | null>(null);
|
|
956
949
|
|
|
957
950
|
// What an agent's word is enough for is the command's own statement, declared where the command is
|
|
958
951
|
// registered and read off the call here. No list of ids lives beside the commands: a list drifts from
|
|
@@ -987,9 +980,10 @@ async function decide(
|
|
|
987
980
|
`;
|
|
988
981
|
}
|
|
989
982
|
function specFile(weaver) {
|
|
990
|
-
return `import { EventType, type
|
|
983
|
+
return `import { EventType, type AGUIEvent, type ToolMessage } from '@ag-ui/core';
|
|
984
|
+
import type { CommandTools } from '@loomweaver/ag-ui';
|
|
991
985
|
import type { CommandArguments, PluginContext } from '@loomweaver/plugin-sdk';
|
|
992
|
-
import { ${weaver.
|
|
986
|
+
import { connect${weaver.className} } from './${weaver.id}-connection';
|
|
993
987
|
|
|
994
988
|
interface Asked {
|
|
995
989
|
readonly id: string;
|
|
@@ -1010,37 +1004,36 @@ function contextThat(confirms: boolean, ran: Asked[]): PluginContext {
|
|
|
1010
1004
|
} as unknown as PluginContext;
|
|
1011
1005
|
}
|
|
1012
1006
|
|
|
1013
|
-
function
|
|
1014
|
-
return
|
|
1007
|
+
function streamedCall(toolCallId: string, ...deltas: string[]): AGUIEvent[] {
|
|
1008
|
+
return [
|
|
1009
|
+
{ type: EventType.TOOL_CALL_START, toolCallId, toolCallName: '${weaver.id}.hello' },
|
|
1010
|
+
...deltas.map((delta): AGUIEvent => ({ type: EventType.TOOL_CALL_ARGS, toolCallId, delta })),
|
|
1011
|
+
{ type: EventType.TOOL_CALL_END, toolCallId },
|
|
1012
|
+
];
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
async function answersTo(tools: CommandTools, events: AGUIEvent[]): Promise<ToolMessage[]> {
|
|
1016
|
+
const answers: ToolMessage[] = [];
|
|
1017
|
+
for (const event of events) {
|
|
1018
|
+
const answer = await tools.receive(event);
|
|
1019
|
+
if (answer) {
|
|
1020
|
+
answers.push(answer);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return answers;
|
|
1015
1024
|
}
|
|
1016
1025
|
|
|
1017
|
-
describe('${weaver.
|
|
1026
|
+
describe('connect${weaver.className}', () => {
|
|
1018
1027
|
it('offers what the workbench offers', () => {
|
|
1019
|
-
const tools = ${weaver.
|
|
1028
|
+
const tools = connect${weaver.className}(contextThat(true, []));
|
|
1020
1029
|
expect(tools.list().map((tool) => tool.name)).toEqual(['${weaver.id}.hello']);
|
|
1021
1030
|
});
|
|
1022
1031
|
|
|
1023
1032
|
it('assembles a call from its events and answers with the outcome', async () => {
|
|
1024
1033
|
const ran: Asked[] = [];
|
|
1025
|
-
const tools = ${weaver.
|
|
1034
|
+
const tools = connect${weaver.className}(contextThat(true, ran));
|
|
1026
1035
|
|
|
1027
|
-
|
|
1028
|
-
await tools.receive(
|
|
1029
|
-
event(EventType.TOOL_CALL_START, {
|
|
1030
|
-
toolCallId: 'c1',
|
|
1031
|
-
toolCallName: '${weaver.id}.hello',
|
|
1032
|
-
}),
|
|
1033
|
-
),
|
|
1034
|
-
).toBeNull();
|
|
1035
|
-
await tools.receive(
|
|
1036
|
-
event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c1', delta: '{"tone"' }),
|
|
1037
|
-
);
|
|
1038
|
-
await tools.receive(
|
|
1039
|
-
event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c1', delta: ':"success"}' }),
|
|
1040
|
-
);
|
|
1041
|
-
const answer = await tools.receive(
|
|
1042
|
-
event(EventType.TOOL_CALL_END, { toolCallId: 'c1' }),
|
|
1043
|
-
);
|
|
1036
|
+
const [answer] = await answersTo(tools, streamedCall('c1', '{"tone"', ':"success"}'));
|
|
1044
1037
|
|
|
1045
1038
|
expect(ran).toEqual([{ id: '${weaver.id}.hello', args: { tone: 'success' } }]);
|
|
1046
1039
|
expect(JSON.parse(answer?.content ?? '{}').tone).toBe('success');
|
|
@@ -1049,20 +1042,9 @@ describe('${weaver.propertyName}Connection', () => {
|
|
|
1049
1042
|
|
|
1050
1043
|
it('never reaches the workbench when a consequential call is declined', async () => {
|
|
1051
1044
|
const ran: Asked[] = [];
|
|
1052
|
-
const tools = ${weaver.
|
|
1045
|
+
const tools = connect${weaver.className}(contextThat(false, ran));
|
|
1053
1046
|
|
|
1054
|
-
await tools
|
|
1055
|
-
event(EventType.TOOL_CALL_START, {
|
|
1056
|
-
toolCallId: 'c2',
|
|
1057
|
-
toolCallName: '${weaver.id}.hello',
|
|
1058
|
-
}),
|
|
1059
|
-
);
|
|
1060
|
-
await tools.receive(
|
|
1061
|
-
event(EventType.TOOL_CALL_ARGS, { toolCallId: 'c2', delta: '{}' }),
|
|
1062
|
-
);
|
|
1063
|
-
const answer = await tools.receive(
|
|
1064
|
-
event(EventType.TOOL_CALL_END, { toolCallId: 'c2' }),
|
|
1065
|
-
);
|
|
1047
|
+
const [answer] = await answersTo(tools, streamedCall('c2', '{}'));
|
|
1066
1048
|
|
|
1067
1049
|
expect(ran).toEqual([]);
|
|
1068
1050
|
expect(answer?.error).toContain('did not run');
|
|
@@ -1084,13 +1066,13 @@ function agentSurfaceBlock(weaver) {
|
|
|
1084
1066
|
}
|
|
1085
1067
|
function agentFiles(weaver) {
|
|
1086
1068
|
const files = {
|
|
1087
|
-
[`src/lib/agent/${weaver.id}-
|
|
1088
|
-
[`src/lib/agent/${weaver.id}-agent
|
|
1069
|
+
[`src/lib/agent/${weaver.id}-connection.ts`]: connectionFile(weaver),
|
|
1070
|
+
[`src/lib/agent/${weaver.id}-agent.ts`]: standInFile(weaver),
|
|
1089
1071
|
[`src/lib/agent/${weaver.id}-agent-panel.ts`]: panelFile(weaver),
|
|
1090
1072
|
[`src/lib/agent/${weaver.id}-agent-panel.html`]: panelTemplateFile(weaver)
|
|
1091
1073
|
};
|
|
1092
1074
|
if (weaver.features.spec) {
|
|
1093
|
-
files[`src/lib/agent/${weaver.id}-
|
|
1075
|
+
files[`src/lib/agent/${weaver.id}-connection.spec.ts`] = specFile(weaver);
|
|
1094
1076
|
}
|
|
1095
1077
|
return files;
|
|
1096
1078
|
}
|
|
@@ -1129,6 +1111,45 @@ function i18nFile(weaver) {
|
|
|
1129
1111
|
return JSON.stringify(i18nBundle(weaver), null, 2) + "\n";
|
|
1130
1112
|
}
|
|
1131
1113
|
|
|
1114
|
+
// ../devkit/src/lib/scaffolds/scaffold-values.ts
|
|
1115
|
+
var NEUTRAL_PREFIX = "app";
|
|
1116
|
+
function stringValue(values, name) {
|
|
1117
|
+
const value = values[name];
|
|
1118
|
+
return typeof value === "string" ? value : void 0;
|
|
1119
|
+
}
|
|
1120
|
+
function booleanValue(values, name) {
|
|
1121
|
+
const value = values[name];
|
|
1122
|
+
return typeof value === "boolean" ? value : void 0;
|
|
1123
|
+
}
|
|
1124
|
+
var PLACEMENT_OPTIONS = [
|
|
1125
|
+
{
|
|
1126
|
+
name: "directory",
|
|
1127
|
+
type: "string",
|
|
1128
|
+
description: "Project root, relative to the workspace root.",
|
|
1129
|
+
workspaceOnly: true
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
name: "tags",
|
|
1133
|
+
type: "string",
|
|
1134
|
+
description: "Comma-separated Nx tags for the project.",
|
|
1135
|
+
workspaceOnly: true
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
name: "unitTestRunner",
|
|
1139
|
+
type: "string",
|
|
1140
|
+
description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
|
|
1141
|
+
choices: ["vitest", "none"],
|
|
1142
|
+
default: "vitest",
|
|
1143
|
+
workspaceOnly: true
|
|
1144
|
+
}
|
|
1145
|
+
];
|
|
1146
|
+
var APP_OPTION = {
|
|
1147
|
+
name: "app",
|
|
1148
|
+
type: "string",
|
|
1149
|
+
description: "Application to drop into. Inferred when the workspace has exactly one.",
|
|
1150
|
+
workspaceOnly: true
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1132
1153
|
// ../devkit/src/lib/validate/manifest.ts
|
|
1133
1154
|
var KNOWN_CAPABILITIES = [
|
|
1134
1155
|
"contributions",
|
|
@@ -1288,6 +1309,18 @@ function deriveCapabilities(features) {
|
|
|
1288
1309
|
}
|
|
1289
1310
|
return KNOWN_CAPABILITIES.filter((capability) => set.has(capability));
|
|
1290
1311
|
}
|
|
1312
|
+
function resolvePrefix(prefix) {
|
|
1313
|
+
const trimmed = prefix?.trim();
|
|
1314
|
+
if (!trimmed) {
|
|
1315
|
+
return NEUTRAL_PREFIX;
|
|
1316
|
+
}
|
|
1317
|
+
if (!isKebabId(trimmed)) {
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
`Selector prefix must be kebab-case (e.g. "acme"); got "${trimmed}".`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
return trimmed;
|
|
1323
|
+
}
|
|
1291
1324
|
function resolveWeaverInput(input) {
|
|
1292
1325
|
if (!isKebabId(input.id)) {
|
|
1293
1326
|
throw new Error(
|
|
@@ -1303,7 +1336,7 @@ function resolveWeaverInput(input) {
|
|
|
1303
1336
|
propertyName: toCamelCase(input.id),
|
|
1304
1337
|
capabilities,
|
|
1305
1338
|
features,
|
|
1306
|
-
prefix: input.prefix
|
|
1339
|
+
prefix: resolvePrefix(input.prefix),
|
|
1307
1340
|
importPath: input.importPath?.trim() || `@loomweaver/${input.id}-weaver`
|
|
1308
1341
|
};
|
|
1309
1342
|
}
|
|
@@ -1500,7 +1533,7 @@ function pluginImports(weaver) {
|
|
|
1500
1533
|
}
|
|
1501
1534
|
if (weaver.features.agent) {
|
|
1502
1535
|
imports.push(
|
|
1503
|
-
`import { ${weaver.
|
|
1536
|
+
`import { connect${weaver.className}, ${weaver.propertyName}Tools } from '../agent/${weaver.id}-connection';`,
|
|
1504
1537
|
`import { ${weaver.className}AgentPanel } from '../agent/${weaver.id}-agent-panel';`
|
|
1505
1538
|
);
|
|
1506
1539
|
}
|
|
@@ -1525,7 +1558,7 @@ function pluginBody(weaver) {
|
|
|
1525
1558
|
const body = [` ctx.contributeIcons({ '${weaver.id}': icon });`];
|
|
1526
1559
|
if (weaver.features.agent) {
|
|
1527
1560
|
body.push(
|
|
1528
|
-
` ${weaver.propertyName}
|
|
1561
|
+
` ${weaver.propertyName}Tools.set(connect${weaver.className}(ctx));`
|
|
1529
1562
|
);
|
|
1530
1563
|
}
|
|
1531
1564
|
if (weaver.features.command) body.push(commandBlock(weaver));
|
|
@@ -1542,7 +1575,7 @@ function pluginFile(weaver) {
|
|
|
1542
1575
|
const body = pluginBody(weaver);
|
|
1543
1576
|
const deactivate = weaver.features.agent ? `
|
|
1544
1577
|
deactivate() {
|
|
1545
|
-
${weaver.propertyName}
|
|
1578
|
+
${weaver.propertyName}Tools.set(null);
|
|
1546
1579
|
},` : "";
|
|
1547
1580
|
return `${pluginImports(weaver).join("\n")}
|
|
1548
1581
|
|
|
@@ -1565,7 +1598,7 @@ function accessLine(weaver) {
|
|
|
1565
1598
|
}
|
|
1566
1599
|
|
|
1567
1600
|
// ../devkit/src/recipes/platform-version.ts
|
|
1568
|
-
var PLATFORM_VERSION = "0.14.
|
|
1601
|
+
var PLATFORM_VERSION = "0.14.4";
|
|
1569
1602
|
|
|
1570
1603
|
// ../devkit/src/recipes/readme-notes.ts
|
|
1571
1604
|
var UNTAGGED_NOTE = [
|
|
@@ -1639,11 +1672,11 @@ function agentNotes(weaver) {
|
|
|
1639
1672
|
"",
|
|
1640
1673
|
`\`src/lib/agent/\` holds three files and one of them is meant to be thrown away.`,
|
|
1641
1674
|
"",
|
|
1642
|
-
`- \`${weaver.id}-
|
|
1675
|
+
`- \`${weaver.id}-connection.ts\` is the connection: the workbench's own commands offered as tools, and a`,
|
|
1643
1676
|
" seam where this weaver decides about a call before it runs. Nothing is registered twice \u2014 the",
|
|
1644
1677
|
" list comes from the workbench, already narrowed by everything that would refuse the call.",
|
|
1645
1678
|
`- \`${weaver.id}-agent-panel.ts\` shows what is offered, the call as it streams, and the outcome.`,
|
|
1646
|
-
`- \`${weaver.id}-agent
|
|
1679
|
+
`- \`${weaver.id}-agent.ts\` is a **stand-in** for the agent, not an assistant: it produces the protocol's own`,
|
|
1647
1680
|
" events so the whole path runs before you have connected anything. Replace that one file with",
|
|
1648
1681
|
" your transport and nothing else changes. No transport, credential or model is generated for",
|
|
1649
1682
|
" you, because none of them can be guessed.",
|
|
@@ -3008,52 +3041,6 @@ var layout = {
|
|
|
3008
3041
|
}
|
|
3009
3042
|
};
|
|
3010
3043
|
|
|
3011
|
-
// ../devkit/src/lib/scaffolds/scaffold-values.ts
|
|
3012
|
-
function stringValue(values, name) {
|
|
3013
|
-
const value = values[name];
|
|
3014
|
-
return typeof value === "string" ? value : void 0;
|
|
3015
|
-
}
|
|
3016
|
-
function booleanValue(values, name) {
|
|
3017
|
-
const value = values[name];
|
|
3018
|
-
return typeof value === "boolean" ? value : void 0;
|
|
3019
|
-
}
|
|
3020
|
-
var PLACEMENT_OPTIONS = [
|
|
3021
|
-
{
|
|
3022
|
-
name: "directory",
|
|
3023
|
-
type: "string",
|
|
3024
|
-
description: "Project root, relative to the workspace root.",
|
|
3025
|
-
workspaceOnly: true
|
|
3026
|
-
},
|
|
3027
|
-
{
|
|
3028
|
-
name: "tags",
|
|
3029
|
-
type: "string",
|
|
3030
|
-
description: "Comma-separated Nx tags for the project.",
|
|
3031
|
-
workspaceOnly: true
|
|
3032
|
-
},
|
|
3033
|
-
{
|
|
3034
|
-
name: "prefix",
|
|
3035
|
-
type: "string",
|
|
3036
|
-
description: "Selector prefix for generated components and directives.",
|
|
3037
|
-
default: "lw",
|
|
3038
|
-
pattern: KEBAB_ID_PATTERN,
|
|
3039
|
-
workspaceOnly: true
|
|
3040
|
-
},
|
|
3041
|
-
{
|
|
3042
|
-
name: "unitTestRunner",
|
|
3043
|
-
type: "string",
|
|
3044
|
-
description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
|
|
3045
|
-
choices: ["vitest", "none"],
|
|
3046
|
-
default: "vitest",
|
|
3047
|
-
workspaceOnly: true
|
|
3048
|
-
}
|
|
3049
|
-
];
|
|
3050
|
-
var APP_OPTION = {
|
|
3051
|
-
name: "app",
|
|
3052
|
-
type: "string",
|
|
3053
|
-
description: "Application to drop into. Inferred when the workspace has exactly one.",
|
|
3054
|
-
workspaceOnly: true
|
|
3055
|
-
};
|
|
3056
|
-
|
|
3057
3044
|
// ../devkit/src/recipes/angular-distribution/scaffold.ts
|
|
3058
3045
|
function distributionInput(values) {
|
|
3059
3046
|
return {
|
|
@@ -3092,6 +3079,13 @@ var distributionScaffold = {
|
|
|
3092
3079
|
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.",
|
|
3093
3080
|
workspaceOnly: true
|
|
3094
3081
|
},
|
|
3082
|
+
{
|
|
3083
|
+
name: "prefix",
|
|
3084
|
+
type: "string",
|
|
3085
|
+
description: "Selector prefix the application declares for its components, which a weaver generated into it takes. Defaults to 'app'.",
|
|
3086
|
+
pattern: KEBAB_ID_PATTERN,
|
|
3087
|
+
workspaceOnly: true
|
|
3088
|
+
},
|
|
3095
3089
|
...PLACEMENT_OPTIONS
|
|
3096
3090
|
],
|
|
3097
3091
|
build: (values) => generate(angularDistribution, distributionInput(values)),
|
|
@@ -3255,6 +3249,12 @@ var weaverScaffold = {
|
|
|
3255
3249
|
description: "Import path for the workspace alias. Defaults to the workspace scope plus the project name.",
|
|
3256
3250
|
workspaceOnly: true
|
|
3257
3251
|
},
|
|
3252
|
+
{
|
|
3253
|
+
name: "prefix",
|
|
3254
|
+
type: "string",
|
|
3255
|
+
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'.",
|
|
3256
|
+
pattern: KEBAB_ID_PATTERN
|
|
3257
|
+
},
|
|
3258
3258
|
APP_OPTION,
|
|
3259
3259
|
...PLACEMENT_OPTIONS
|
|
3260
3260
|
],
|
|
@@ -3986,7 +3986,8 @@ function buildProjects(config) {
|
|
|
3986
3986
|
const projects = asObject2(asObject2(config)?.["projects"]) ?? {};
|
|
3987
3987
|
return Object.entries(projects).filter(([, project]) => buildTargetIn(project) !== void 0).map(([name, project]) => ({
|
|
3988
3988
|
name,
|
|
3989
|
-
root: normaliseRoot(asObject2(project)?.["root"])
|
|
3989
|
+
root: normaliseRoot(asObject2(project)?.["root"]),
|
|
3990
|
+
prefix: declaredPrefix(asObject2(project)?.["prefix"])
|
|
3990
3991
|
}));
|
|
3991
3992
|
}
|
|
3992
3993
|
function buildTargetOf(config, project) {
|
|
@@ -4015,6 +4016,9 @@ function buildTargetIn(project) {
|
|
|
4015
4016
|
}
|
|
4016
4017
|
return void 0;
|
|
4017
4018
|
}
|
|
4019
|
+
function declaredPrefix(value) {
|
|
4020
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
4021
|
+
}
|
|
4018
4022
|
function normaliseRoot(value) {
|
|
4019
4023
|
return typeof value === "string" ? withoutTrailingSlashes(value.replace(/^\.?\/*/, "")) : "";
|
|
4020
4024
|
}
|
|
@@ -4153,18 +4157,33 @@ function directoryFromOut(out) {
|
|
|
4153
4157
|
}
|
|
4154
4158
|
return below || ".";
|
|
4155
4159
|
}
|
|
4160
|
+
function declaredPrefix2(out) {
|
|
4161
|
+
const target = resolve3(out ?? ".");
|
|
4162
|
+
const workspace = findWorkspace(target);
|
|
4163
|
+
if (workspace?.kind !== "angular") {
|
|
4164
|
+
return void 0;
|
|
4165
|
+
}
|
|
4166
|
+
try {
|
|
4167
|
+
return resolveBuildProject(workspace, target).prefix;
|
|
4168
|
+
} catch (error) {
|
|
4169
|
+
if (error instanceof WorkspaceError) {
|
|
4170
|
+
return void 0;
|
|
4171
|
+
}
|
|
4172
|
+
throw error;
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4156
4175
|
function scaffoldValues(scaffold2, args) {
|
|
4176
|
+
const flag = args.flags["out"];
|
|
4177
|
+
const out = typeof flag === "string" ? flag : void 0;
|
|
4157
4178
|
const values = valuesFor(scaffold2, args);
|
|
4179
|
+
const readsPrefix = values["prefix"] === void 0 && portableOptions(scaffold2).some((option) => option.name === "prefix");
|
|
4158
4180
|
const takesDirectory = scaffold2.options.some(
|
|
4159
4181
|
(option) => option.name === "directory"
|
|
4160
4182
|
);
|
|
4161
|
-
if (!takesDirectory) {
|
|
4162
|
-
return values;
|
|
4163
|
-
}
|
|
4164
|
-
const out = args.flags["out"];
|
|
4165
4183
|
return {
|
|
4166
4184
|
...values,
|
|
4167
|
-
|
|
4185
|
+
...readsPrefix && { prefix: declaredPrefix2(out) },
|
|
4186
|
+
...takesDirectory && { directory: directoryFromOut(out) }
|
|
4168
4187
|
};
|
|
4169
4188
|
}
|
|
4170
4189
|
|
|
@@ -5180,7 +5199,7 @@ function validateCatalogCommand(args, io) {
|
|
|
5180
5199
|
}
|
|
5181
5200
|
|
|
5182
5201
|
// src/lib/run.ts
|
|
5183
|
-
var VERSION = "0.14.
|
|
5202
|
+
var VERSION = "0.14.4";
|
|
5184
5203
|
var COMMANDS = /* @__PURE__ */ new Map([
|
|
5185
5204
|
["init", init],
|
|
5186
5205
|
["list", list],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomweaver/cli",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.4",
|
|
4
4
|
"description": "LoomWeaver: open-source plugin platform for Angular workbenches. The scaffolding CLI: generates weavers, distributions and integrations into any project, with no Nx workspace and no checkout required.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"loomweaver",
|