@looplia/looplia-cli 0.7.0 → 0.7.1

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.
@@ -0,0 +1,1148 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ compileRegistry,
4
+ getCompiledRegistryPath
5
+ } from "./chunk-DN3RSIIJ.js";
6
+ import {
7
+ BRAND,
8
+ DIRTY,
9
+ EMPTY_PATH,
10
+ INVALID,
11
+ NEVER,
12
+ OK,
13
+ ParseStatus,
14
+ ZodAny,
15
+ ZodArray,
16
+ ZodBigInt,
17
+ ZodBoolean,
18
+ ZodBranded,
19
+ ZodCatch,
20
+ ZodDate,
21
+ ZodDefault,
22
+ ZodDiscriminatedUnion,
23
+ ZodEffects,
24
+ ZodEnum,
25
+ ZodError,
26
+ ZodFirstPartyTypeKind,
27
+ ZodFunction,
28
+ ZodIntersection,
29
+ ZodIssueCode,
30
+ ZodLazy,
31
+ ZodLiteral,
32
+ ZodMap,
33
+ ZodNaN,
34
+ ZodNativeEnum,
35
+ ZodNever,
36
+ ZodNull,
37
+ ZodNullable,
38
+ ZodNumber,
39
+ ZodObject,
40
+ ZodOptional,
41
+ ZodParsedType,
42
+ ZodPipeline,
43
+ ZodPromise,
44
+ ZodReadonly,
45
+ ZodRecord,
46
+ ZodSet,
47
+ ZodString,
48
+ ZodSymbol,
49
+ ZodTuple,
50
+ ZodType,
51
+ ZodUndefined,
52
+ ZodUnion,
53
+ ZodUnknown,
54
+ ZodVoid,
55
+ addIssueToContext,
56
+ anyType,
57
+ arrayType,
58
+ bigIntType,
59
+ booleanType,
60
+ coerce,
61
+ custom,
62
+ dateType,
63
+ datetimeRegex,
64
+ discriminatedUnionType,
65
+ effectsType,
66
+ en_default,
67
+ enumType,
68
+ functionType,
69
+ getErrorMap,
70
+ getParsedType,
71
+ instanceOfType,
72
+ intersectionType,
73
+ isAborted,
74
+ isAsync,
75
+ isDirty,
76
+ isValid,
77
+ late,
78
+ lazyType,
79
+ literalType,
80
+ makeIssue,
81
+ mapType,
82
+ nanType,
83
+ nativeEnumType,
84
+ neverType,
85
+ nullType,
86
+ nullableType,
87
+ numberType,
88
+ objectType,
89
+ objectUtil,
90
+ oboolean,
91
+ onumber,
92
+ optionalType,
93
+ ostring,
94
+ pipelineType,
95
+ preprocessType,
96
+ promiseType,
97
+ quotelessJson,
98
+ recordType,
99
+ setErrorMap,
100
+ setType,
101
+ strictObjectType,
102
+ stringType,
103
+ symbolType,
104
+ tupleType,
105
+ undefinedType,
106
+ unionType,
107
+ unknownType,
108
+ util,
109
+ voidType
110
+ } from "./chunk-HSZZVXV5.js";
111
+ import {
112
+ pathExists
113
+ } from "./chunk-VRBGWKZ6.js";
114
+ import {
115
+ __export,
116
+ init_esm_shims
117
+ } from "./chunk-Y55L47HC.js";
118
+
119
+ // ../../packages/provider/dist/index.js
120
+ init_esm_shims();
121
+
122
+ // ../../node_modules/zod/v3/external.js
123
+ var external_exports = {};
124
+ __export(external_exports, {
125
+ BRAND: () => BRAND,
126
+ DIRTY: () => DIRTY,
127
+ EMPTY_PATH: () => EMPTY_PATH,
128
+ INVALID: () => INVALID,
129
+ NEVER: () => NEVER,
130
+ OK: () => OK,
131
+ ParseStatus: () => ParseStatus,
132
+ Schema: () => ZodType,
133
+ ZodAny: () => ZodAny,
134
+ ZodArray: () => ZodArray,
135
+ ZodBigInt: () => ZodBigInt,
136
+ ZodBoolean: () => ZodBoolean,
137
+ ZodBranded: () => ZodBranded,
138
+ ZodCatch: () => ZodCatch,
139
+ ZodDate: () => ZodDate,
140
+ ZodDefault: () => ZodDefault,
141
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
142
+ ZodEffects: () => ZodEffects,
143
+ ZodEnum: () => ZodEnum,
144
+ ZodError: () => ZodError,
145
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
146
+ ZodFunction: () => ZodFunction,
147
+ ZodIntersection: () => ZodIntersection,
148
+ ZodIssueCode: () => ZodIssueCode,
149
+ ZodLazy: () => ZodLazy,
150
+ ZodLiteral: () => ZodLiteral,
151
+ ZodMap: () => ZodMap,
152
+ ZodNaN: () => ZodNaN,
153
+ ZodNativeEnum: () => ZodNativeEnum,
154
+ ZodNever: () => ZodNever,
155
+ ZodNull: () => ZodNull,
156
+ ZodNullable: () => ZodNullable,
157
+ ZodNumber: () => ZodNumber,
158
+ ZodObject: () => ZodObject,
159
+ ZodOptional: () => ZodOptional,
160
+ ZodParsedType: () => ZodParsedType,
161
+ ZodPipeline: () => ZodPipeline,
162
+ ZodPromise: () => ZodPromise,
163
+ ZodReadonly: () => ZodReadonly,
164
+ ZodRecord: () => ZodRecord,
165
+ ZodSchema: () => ZodType,
166
+ ZodSet: () => ZodSet,
167
+ ZodString: () => ZodString,
168
+ ZodSymbol: () => ZodSymbol,
169
+ ZodTransformer: () => ZodEffects,
170
+ ZodTuple: () => ZodTuple,
171
+ ZodType: () => ZodType,
172
+ ZodUndefined: () => ZodUndefined,
173
+ ZodUnion: () => ZodUnion,
174
+ ZodUnknown: () => ZodUnknown,
175
+ ZodVoid: () => ZodVoid,
176
+ addIssueToContext: () => addIssueToContext,
177
+ any: () => anyType,
178
+ array: () => arrayType,
179
+ bigint: () => bigIntType,
180
+ boolean: () => booleanType,
181
+ coerce: () => coerce,
182
+ custom: () => custom,
183
+ date: () => dateType,
184
+ datetimeRegex: () => datetimeRegex,
185
+ defaultErrorMap: () => en_default,
186
+ discriminatedUnion: () => discriminatedUnionType,
187
+ effect: () => effectsType,
188
+ enum: () => enumType,
189
+ function: () => functionType,
190
+ getErrorMap: () => getErrorMap,
191
+ getParsedType: () => getParsedType,
192
+ instanceof: () => instanceOfType,
193
+ intersection: () => intersectionType,
194
+ isAborted: () => isAborted,
195
+ isAsync: () => isAsync,
196
+ isDirty: () => isDirty,
197
+ isValid: () => isValid,
198
+ late: () => late,
199
+ lazy: () => lazyType,
200
+ literal: () => literalType,
201
+ makeIssue: () => makeIssue,
202
+ map: () => mapType,
203
+ nan: () => nanType,
204
+ nativeEnum: () => nativeEnumType,
205
+ never: () => neverType,
206
+ null: () => nullType,
207
+ nullable: () => nullableType,
208
+ number: () => numberType,
209
+ object: () => objectType,
210
+ objectUtil: () => objectUtil,
211
+ oboolean: () => oboolean,
212
+ onumber: () => onumber,
213
+ optional: () => optionalType,
214
+ ostring: () => ostring,
215
+ pipeline: () => pipelineType,
216
+ preprocess: () => preprocessType,
217
+ promise: () => promiseType,
218
+ quotelessJson: () => quotelessJson,
219
+ record: () => recordType,
220
+ set: () => setType,
221
+ setErrorMap: () => setErrorMap,
222
+ strictObject: () => strictObjectType,
223
+ string: () => stringType,
224
+ symbol: () => symbolType,
225
+ transformer: () => effectsType,
226
+ tuple: () => tupleType,
227
+ undefined: () => undefinedType,
228
+ union: () => unionType,
229
+ unknown: () => unknownType,
230
+ util: () => util,
231
+ void: () => voidType
232
+ });
233
+ init_esm_shims();
234
+
235
+ // ../../packages/provider/dist/index.js
236
+ import { exec } from "child_process";
237
+ import { readFile, rm } from "fs/promises";
238
+ import { homedir } from "os";
239
+ import { join } from "path";
240
+ import { promisify } from "util";
241
+
242
+ // ../../packages/core/dist/index.js
243
+ init_esm_shims();
244
+
245
+ // ../../node_modules/zod/index.js
246
+ init_esm_shims();
247
+
248
+ // ../../packages/core/dist/index.js
249
+ var commands = /* @__PURE__ */ new Map();
250
+ function registerCommand(definition) {
251
+ if (commands.has(definition.name)) {
252
+ throw new Error(`Command "${definition.name}" is already registered`);
253
+ }
254
+ commands.set(definition.name, definition);
255
+ }
256
+ function buildWorkflowPrompt(ctx) {
257
+ if (!(ctx.workflowName && ctx.workflowDefinition)) {
258
+ throw new Error(
259
+ "Workflow context is required (workflowName, workflowDefinition)"
260
+ );
261
+ }
262
+ return `Task: Execute workflow "${ctx.workflowName}" for session: contentItem/${ctx.contentId}
263
+
264
+ ## Workflow Definition
265
+
266
+ \`\`\`yaml
267
+ ${ctx.workflowDefinition}
268
+ \`\`\`
269
+
270
+ ${ctx.workflowInstructions ? `## Custom Instructions
271
+
272
+ ${ctx.workflowInstructions}
273
+ ` : ""}
274
+ ## Execution Protocol
275
+
276
+ ### Step 1: Read Validation State
277
+ Read \`contentItem/${ctx.contentId}/validation.json\` to understand:
278
+ - What outputs are required
279
+ - Validation criteria for each output
280
+ - Which outputs have already passed validation
281
+
282
+ If validation.json is missing, the workflow hasn't started properly.
283
+
284
+ ### Step 2: Execute Outputs (Dependency Order)
285
+
286
+ For each output in the workflow (following dependency order):
287
+
288
+ 1. **Check completion**:
289
+ - If artifact exists AND validated=true in validation.json \u2192 skip
290
+
291
+ 2. **If incomplete - use Task tool with custom subagent_type**:
292
+
293
+ Use the **Task** tool to invoke the agent specified in the workflow definition.
294
+ The subagent_type MUST match the \`agent\` field exactly from the workflow YAML.
295
+
296
+ Example for an output with \`agent: content-analyzer\`:
297
+ \`\`\`json
298
+ {
299
+ "name": "Task",
300
+ "input": {
301
+ "subagent_type": "content-analyzer",
302
+ "description": "Generate summary artifact",
303
+ "prompt": "Analyze content at contentItem/${ctx.contentId}/content.md and write summary.json"
304
+ }
305
+ }
306
+ \`\`\`
307
+
308
+ The subagent will:
309
+ - Auto-load its configured skills (from .claude/agents/*.md)
310
+ - Write artifact to \`contentItem/${ctx.contentId}/{artifact}\`
311
+
312
+ 3. **After artifact written - use Skill tool for validation**:
313
+
314
+ Use the **Skill** tool to invoke workflow-validator:
315
+ \`\`\`json
316
+ {
317
+ "name": "Skill",
318
+ "input": { "skill": "workflow-validator" }
319
+ }
320
+ \`\`\`
321
+
322
+ Then follow the skill's instructions to validate the artifact against criteria.
323
+
324
+ 4. **Handle validation result**:
325
+ - If passed: Update validation.json \u2192 set \`outputs.{name}.validated = true\`
326
+ - If failed: Review failed checks, retry subagent with specific feedback
327
+
328
+ ### Step 3: Return Final Output
329
+
330
+ When the output marked \`final: true\` passes validation:
331
+ 1. Read the final artifact JSON
332
+ 2. Return it as structured output
333
+
334
+ ## Workspace Structure
335
+
336
+ - **Content**: \`contentItem/${ctx.contentId}/content.md\`
337
+ - **Validation**: \`contentItem/${ctx.contentId}/validation.json\`
338
+ - **Artifacts**: \`contentItem/${ctx.contentId}/*.json\`
339
+ - **Workflow**: \`workflows/${ctx.workflowName}.md\`
340
+ - **Agents**: \`.claude/agents/*.md\`
341
+ - **Skills**: \`.claude/skills/*/SKILL.md\`
342
+
343
+ ## Important Rules
344
+
345
+ - **Validate after each step** - Never skip validation
346
+ - **Update validation.json** - Mark outputs validated when passed
347
+ - **Follow dependencies** - Complete required outputs before dependent ones
348
+ - **Retry on failure** - Give subagent specific feedback on validation failures
349
+ - **Return structured JSON** - Final output must match expected schema`;
350
+ }
351
+ var workflowCommand = {
352
+ name: "workflow",
353
+ promptTemplate: buildWorkflowPrompt,
354
+ // Generic object schema - allows any JSON object structure
355
+ // Must be an object schema (not z.unknown()) to produce valid JSON schema with "type" field
356
+ outputSchema: external_exports.record(external_exports.string(), external_exports.unknown())
357
+ };
358
+ registerCommand(workflowCommand);
359
+ var RUN_PATTERN = /^agents\/([a-z][a-z0-9-]*)$/;
360
+ function isValidRunFormat(run) {
361
+ return RUN_PATTERN.test(run);
362
+ }
363
+ var INPUTLESS_CAPABLE_SKILLS = ["search"];
364
+ var ARRAY_PATTERN = /^\[(.*)\]$/;
365
+ var INTEGER_PATTERN = /^\d+$/;
366
+ var NON_SPACE_PATTERN = /\S/;
367
+ var FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
368
+ function isSkippableLine(line) {
369
+ const trimmed = line.trim();
370
+ return !trimmed || trimmed.startsWith("#");
371
+ }
372
+ function parseKeyValue(trimmed) {
373
+ const colonIndex = trimmed.indexOf(":");
374
+ if (colonIndex === -1) {
375
+ return null;
376
+ }
377
+ return {
378
+ key: trimmed.slice(0, colonIndex),
379
+ value: trimmed.slice(colonIndex + 1).trim()
380
+ };
381
+ }
382
+ function parseArray(value) {
383
+ const match = value.match(ARRAY_PATTERN);
384
+ if (match?.[1] !== void 0) {
385
+ return match[1].split(",").map((s) => s.trim());
386
+ }
387
+ return null;
388
+ }
389
+ function parseYamlValue(value) {
390
+ const array = parseArray(value);
391
+ if (array) {
392
+ return array;
393
+ }
394
+ if (value === "true") {
395
+ return true;
396
+ }
397
+ if (value === "false") {
398
+ return false;
399
+ }
400
+ if (INTEGER_PATTERN.test(value)) {
401
+ return Number.parseInt(value, 10);
402
+ }
403
+ return value;
404
+ }
405
+ function processYamlLine(state, line) {
406
+ if (isSkippableLine(line)) {
407
+ return;
408
+ }
409
+ const indent = line.search(NON_SPACE_PATTERN);
410
+ const trimmed = line.trim();
411
+ if (indent === 0) {
412
+ processTopLevel(state, trimmed);
413
+ } else if (indent === 2 && state.currentKey === "skills") {
414
+ processSkillItem(state, trimmed);
415
+ } else if (indent === 2 && state.currentKey === "inputs") {
416
+ processInputItem(state, trimmed);
417
+ } else if (indent === 2 && state.currentKey === "steps") {
418
+ processStepItem(state, trimmed);
419
+ } else if (indent === 4 && state.currentInput) {
420
+ processInputProperty(state, trimmed);
421
+ } else if (indent === 4 && state.currentStep) {
422
+ processStepProperty(state, trimmed);
423
+ } else if (indent === 6 && state.currentValidate) {
424
+ processValidateProperty(state, trimmed);
425
+ }
426
+ }
427
+ function processSkillItem(state, trimmed) {
428
+ if (trimmed.startsWith("- ")) {
429
+ const skillName = trimmed.slice(2).trim();
430
+ if (skillName) {
431
+ state.skills.push(skillName);
432
+ }
433
+ }
434
+ }
435
+ function processTopLevel(state, trimmed) {
436
+ if (state.currentStep?.id) {
437
+ state.steps.push(state.currentStep);
438
+ state.currentStep = null;
439
+ }
440
+ if (state.currentInput?.name) {
441
+ state.inputs.push(state.currentInput);
442
+ state.currentInput = null;
443
+ }
444
+ const kv = parseKeyValue(trimmed);
445
+ if (!kv) {
446
+ return;
447
+ }
448
+ state.currentKey = kv.key;
449
+ if (kv.key !== "steps" && kv.key !== "inputs" && kv.key !== "skills" && kv.value) {
450
+ state.result[kv.key] = kv.value;
451
+ }
452
+ state.currentValidate = null;
453
+ }
454
+ function processInputItem(state, trimmed) {
455
+ if (trimmed.startsWith("- ")) {
456
+ if (state.currentInput?.name) {
457
+ state.inputs.push(state.currentInput);
458
+ }
459
+ state.currentInput = {};
460
+ const firstProp = trimmed.slice(2).trim();
461
+ const kv = parseKeyValue(firstProp);
462
+ if (kv) {
463
+ handleInputProperty(state.currentInput, kv.key, kv.value);
464
+ }
465
+ }
466
+ }
467
+ function handleInputProperty(input, key, value) {
468
+ switch (key) {
469
+ case "name":
470
+ input.name = value;
471
+ break;
472
+ case "required":
473
+ input.required = value === "true";
474
+ break;
475
+ case "description":
476
+ input.description = value;
477
+ break;
478
+ case "type":
479
+ if (value === "file" || value === "json") {
480
+ input.type = value;
481
+ }
482
+ break;
483
+ default:
484
+ break;
485
+ }
486
+ }
487
+ function processInputProperty(state, trimmed) {
488
+ if (!state.currentInput) {
489
+ return;
490
+ }
491
+ const kv = parseKeyValue(trimmed);
492
+ if (!kv) {
493
+ return;
494
+ }
495
+ handleInputProperty(state.currentInput, kv.key, kv.value);
496
+ }
497
+ function processStepItem(state, trimmed) {
498
+ if (trimmed.startsWith("- ")) {
499
+ if (state.currentStep?.id) {
500
+ state.steps.push(state.currentStep);
501
+ }
502
+ state.currentStep = {};
503
+ state.currentValidate = null;
504
+ const firstProp = trimmed.slice(2).trim();
505
+ const kv = parseKeyValue(firstProp);
506
+ if (kv) {
507
+ handleStepProperty(state.currentStep, kv.key, kv.value);
508
+ }
509
+ }
510
+ }
511
+ function handleStepProperty(step, key, value) {
512
+ switch (key) {
513
+ case "id":
514
+ step.id = value;
515
+ break;
516
+ case "skill":
517
+ step.skill = value;
518
+ break;
519
+ case "mission":
520
+ step.mission = value;
521
+ break;
522
+ case "run":
523
+ step.run = value;
524
+ break;
525
+ case "input": {
526
+ const arr = parseArray(value);
527
+ step.input = arr ?? value;
528
+ break;
529
+ }
530
+ case "output":
531
+ step.output = value;
532
+ break;
533
+ case "needs": {
534
+ const arr = parseArray(value);
535
+ if (arr) {
536
+ step.needs = arr;
537
+ }
538
+ break;
539
+ }
540
+ case "final":
541
+ step.final = value === "true";
542
+ break;
543
+ case "validate": {
544
+ const validate = {};
545
+ step.validate = validate;
546
+ return validate;
547
+ }
548
+ default:
549
+ break;
550
+ }
551
+ return null;
552
+ }
553
+ function processStepProperty(state, trimmed) {
554
+ if (!state.currentStep) {
555
+ return;
556
+ }
557
+ const kv = parseKeyValue(trimmed);
558
+ if (!kv) {
559
+ return;
560
+ }
561
+ const validate = handleStepProperty(state.currentStep, kv.key, kv.value);
562
+ if (validate) {
563
+ state.currentValidate = validate;
564
+ }
565
+ }
566
+ function processValidateProperty(state, trimmed) {
567
+ if (!state.currentValidate) {
568
+ return;
569
+ }
570
+ const kv = parseKeyValue(trimmed);
571
+ if (!kv) {
572
+ return;
573
+ }
574
+ state.currentValidate[kv.key] = parseYamlValue(kv.value);
575
+ }
576
+ function parseFrontmatter(content) {
577
+ const match = content.match(FRONTMATTER_REGEX);
578
+ if (!match) {
579
+ throw new Error(
580
+ "Invalid workflow file: missing YAML frontmatter (must start with ---)"
581
+ );
582
+ }
583
+ const yamlContent = match[1] ?? "";
584
+ const body = match[2] ?? "";
585
+ const frontmatter = parseSimpleYaml(yamlContent);
586
+ return { frontmatter, body: body.trim() };
587
+ }
588
+ function parseSimpleYaml(yaml) {
589
+ const state = {
590
+ result: {},
591
+ steps: [],
592
+ inputs: [],
593
+ skills: [],
594
+ currentKey: "",
595
+ currentStep: null,
596
+ currentInput: null,
597
+ currentValidate: null
598
+ };
599
+ for (const line of yaml.split("\n")) {
600
+ processYamlLine(state, line);
601
+ }
602
+ if (state.currentInput?.name) {
603
+ state.inputs.push(state.currentInput);
604
+ }
605
+ if (state.currentStep?.id) {
606
+ state.steps.push(state.currentStep);
607
+ }
608
+ if (state.skills.length > 0) {
609
+ state.result.skills = state.skills;
610
+ }
611
+ if (state.inputs.length > 0) {
612
+ state.result.inputs = state.inputs;
613
+ }
614
+ if (state.steps.length > 0) {
615
+ state.result.steps = state.steps;
616
+ }
617
+ return state.result;
618
+ }
619
+ var INPUT_REFERENCE_PATTERN = /\$\{\{\s*inputs\.(\w[\w-]*)\s*\}\}/g;
620
+ function validateInputReferences(input, workflowInputs) {
621
+ const inputs = Array.isArray(input) ? input : [input];
622
+ const declaredNames = new Set(
623
+ workflowInputs.filter((i) => i.name).map((i) => i.name)
624
+ );
625
+ for (const inp of inputs) {
626
+ const matches = inp.matchAll(INPUT_REFERENCE_PATTERN);
627
+ for (const match of matches) {
628
+ const referencedName = match[1];
629
+ if (referencedName && !declaredNames.has(referencedName)) {
630
+ throw new Error(
631
+ `Unknown input reference: inputs.${referencedName}. Declared inputs: ${[...declaredNames].join(", ") || "(none)"}`
632
+ );
633
+ }
634
+ }
635
+ }
636
+ }
637
+ function validateStepExecutionMode(step) {
638
+ const hasSkill = Boolean(step.skill && step.mission);
639
+ const hasRun = Boolean(step.run);
640
+ if (!(hasSkill || hasRun)) {
641
+ throw new Error(
642
+ `Step '${step.id}' must have either 'skill' + 'mission' (v0.6.1) or 'run' (v0.6.0 deprecated)`
643
+ );
644
+ }
645
+ if (hasSkill && hasRun) {
646
+ throw new Error(
647
+ `Step '${step.id}' cannot have both 'skill' and 'run' - use one or the other`
648
+ );
649
+ }
650
+ if (hasRun && step.run && !isValidRunFormat(step.run)) {
651
+ throw new Error(
652
+ `Step '${step.id}' has invalid run format '${step.run}'. Expected 'agents/{name}' where name is lowercase alphanumeric with hyphens.`
653
+ );
654
+ }
655
+ }
656
+ function validateStep(step, workflowInputs = []) {
657
+ if (!step.id) {
658
+ throw new Error("Each step must have an 'id' field");
659
+ }
660
+ validateStepExecutionMode(step);
661
+ const isInputlessCapable = step.skill && INPUTLESS_CAPABLE_SKILLS.includes(step.skill);
662
+ if (!(step.input || isInputlessCapable)) {
663
+ throw new Error(
664
+ `Step '${step.id}' must have an 'input' field (or use an input-less capable skill like: ${INPUTLESS_CAPABLE_SKILLS.join(", ")})`
665
+ );
666
+ }
667
+ if (Array.isArray(step.input) && step.input.length === 0) {
668
+ throw new Error(`Step '${step.id}' input array cannot be empty`);
669
+ }
670
+ if (step.input && workflowInputs.length > 0) {
671
+ validateInputReferences(step.input, workflowInputs);
672
+ }
673
+ if (!step.output) {
674
+ throw new Error(`Step '${step.id}' must have an 'output' field`);
675
+ }
676
+ }
677
+ function parseWorkflow(content) {
678
+ const { frontmatter, body } = parseFrontmatter(content);
679
+ if (!frontmatter.name || typeof frontmatter.name !== "string") {
680
+ throw new Error("Workflow must have a 'name' field");
681
+ }
682
+ if (!frontmatter.description || typeof frontmatter.description !== "string") {
683
+ throw new Error("Workflow must have a 'description' field");
684
+ }
685
+ if (!(frontmatter.steps && Array.isArray(frontmatter.steps)) || frontmatter.steps.length === 0) {
686
+ throw new Error("Workflow must have at least one step defined");
687
+ }
688
+ const workflowInputs = Array.isArray(frontmatter.inputs) ? frontmatter.inputs : [];
689
+ const workflowSkills = Array.isArray(frontmatter.skills) ? frontmatter.skills : [];
690
+ for (const step of frontmatter.steps) {
691
+ validateStep(step, workflowInputs);
692
+ }
693
+ const inputs = workflowInputs.filter((inp) => inp.name).map((inp) => ({
694
+ name: inp.name,
695
+ required: inp.required ?? true,
696
+ description: inp.description,
697
+ type: inp.type
698
+ }));
699
+ return {
700
+ definition: {
701
+ name: frontmatter.name,
702
+ version: frontmatter.version,
703
+ description: frontmatter.description,
704
+ skills: workflowSkills.length > 0 ? workflowSkills : void 0,
705
+ inputs: inputs.length > 0 ? inputs : void 0,
706
+ steps: frontmatter.steps
707
+ },
708
+ instructions: body
709
+ };
710
+ }
711
+ function isInputlessWorkflow(definition) {
712
+ if (definition.inputs?.some((input) => input.required)) {
713
+ return false;
714
+ }
715
+ const firstSteps = definition.steps.filter(
716
+ (step) => !step.needs || step.needs.length === 0
717
+ );
718
+ if (firstSteps.length === 0) {
719
+ return false;
720
+ }
721
+ return firstSteps.every(
722
+ (step) => step.skill && INPUTLESS_CAPABLE_SKILLS.includes(step.skill)
723
+ );
724
+ }
725
+ function extractWorkflowSkills(workflow) {
726
+ if (workflow.definition.skills && workflow.definition.skills.length > 0) {
727
+ return workflow.definition.skills;
728
+ }
729
+ const skills = /* @__PURE__ */ new Set();
730
+ for (const step of workflow.definition.steps) {
731
+ if (step.skill) {
732
+ skills.add(step.skill);
733
+ }
734
+ }
735
+ return [...skills];
736
+ }
737
+ var SourceTypeSchema = external_exports.enum([
738
+ "rss",
739
+ "youtube",
740
+ "podcast",
741
+ "twitter",
742
+ "custom"
743
+ ]);
744
+ var SourceSchema = external_exports.object({
745
+ id: external_exports.string().min(1),
746
+ type: SourceTypeSchema,
747
+ label: external_exports.string().optional(),
748
+ url: external_exports.string(),
749
+ metadata: external_exports.record(external_exports.unknown()).optional()
750
+ });
751
+ var ContentMetadataSchema = external_exports.object({
752
+ language: external_exports.string().length(2).optional(),
753
+ durationSeconds: external_exports.number().positive().optional(),
754
+ author: external_exports.string().optional(),
755
+ wordCount: external_exports.number().positive().optional()
756
+ }).passthrough();
757
+ var ContentItemSchema = external_exports.object({
758
+ id: external_exports.string().min(1),
759
+ source: SourceSchema,
760
+ title: external_exports.string().min(1),
761
+ url: external_exports.string(),
762
+ publishedAt: external_exports.string().optional(),
763
+ rawText: external_exports.string().min(1),
764
+ metadata: ContentMetadataSchema
765
+ });
766
+ var ValidationCriteriaSchema = external_exports.object({
767
+ required_fields: external_exports.array(external_exports.string()).optional(),
768
+ min_quotes: external_exports.number().int().positive().optional(),
769
+ min_key_points: external_exports.number().int().positive().optional(),
770
+ min_outline_sections: external_exports.number().int().positive().optional(),
771
+ has_hooks: external_exports.boolean().optional()
772
+ }).passthrough();
773
+ var WorkflowInputSchema = external_exports.object({
774
+ name: external_exports.string().min(1).regex(/^[a-z][a-z0-9-]*$/, "Input name must be kebab-case"),
775
+ required: external_exports.boolean(),
776
+ description: external_exports.string().optional(),
777
+ type: external_exports.enum(["file", "json"]).optional()
778
+ });
779
+ var WorkflowStepSchema = external_exports.object({
780
+ id: external_exports.string().min(1),
781
+ skill: external_exports.string().min(1).optional(),
782
+ mission: external_exports.string().min(1).optional(),
783
+ run: external_exports.string().min(1).optional(),
784
+ input: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]).optional(),
785
+ output: external_exports.string().min(1),
786
+ needs: external_exports.array(external_exports.string()).optional(),
787
+ final: external_exports.boolean().optional(),
788
+ validate: ValidationCriteriaSchema.optional()
789
+ }).refine(
790
+ (step) => step.skill && step.mission || step.run,
791
+ "Step must have either 'skill' + 'mission' or 'run'"
792
+ ).refine(
793
+ (step) => !(step.skill && step.run),
794
+ "Step cannot have both 'skill' and 'run'"
795
+ );
796
+ var WorkflowDefinitionSchema = external_exports.object({
797
+ name: external_exports.string().min(1),
798
+ version: external_exports.string().optional(),
799
+ description: external_exports.string().min(1),
800
+ inputs: external_exports.array(WorkflowInputSchema).optional(),
801
+ steps: external_exports.array(WorkflowStepSchema)
802
+ });
803
+ var StepValidationStateSchema = external_exports.object({
804
+ output: external_exports.string().min(1),
805
+ validate: ValidationCriteriaSchema.optional(),
806
+ validated: external_exports.boolean()
807
+ });
808
+ var ValidationManifestSchema = external_exports.object({
809
+ workflow: external_exports.string().min(1),
810
+ version: external_exports.string().optional(),
811
+ sandboxId: external_exports.string().optional(),
812
+ createdAt: external_exports.string().optional(),
813
+ steps: external_exports.record(external_exports.string(), StepValidationStateSchema)
814
+ });
815
+ var ValidationCheckSchema = external_exports.object({
816
+ name: external_exports.string().min(1),
817
+ passed: external_exports.boolean(),
818
+ message: external_exports.string()
819
+ });
820
+ var ValidationResultSchema = external_exports.object({
821
+ passed: external_exports.boolean(),
822
+ checks: external_exports.array(ValidationCheckSchema)
823
+ });
824
+ var SessionManifestSchema = external_exports.object({
825
+ version: external_exports.literal(1),
826
+ contentId: external_exports.string().min(1),
827
+ pipeline: external_exports.string().min(1),
828
+ desiredOutput: external_exports.string().min(1),
829
+ updatedAt: external_exports.string(),
830
+ steps: external_exports.record(external_exports.string(), external_exports.literal("done"))
831
+ });
832
+ var UserTopicSchema = external_exports.object({
833
+ topic: external_exports.string().min(1),
834
+ interestLevel: external_exports.union([
835
+ external_exports.literal(1),
836
+ external_exports.literal(2),
837
+ external_exports.literal(3),
838
+ external_exports.literal(4),
839
+ external_exports.literal(5)
840
+ ])
841
+ });
842
+ var WritingStyleSchema = external_exports.object({
843
+ tone: external_exports.enum(["beginner", "intermediate", "expert", "mixed"]),
844
+ targetWordCount: external_exports.number().min(100).max(1e4),
845
+ voice: external_exports.enum(["first-person", "third-person", "instructional"])
846
+ });
847
+ var UserProfileSchema = external_exports.object({
848
+ userId: external_exports.string().min(1),
849
+ topics: external_exports.array(UserTopicSchema),
850
+ style: WritingStyleSchema,
851
+ writingSamples: external_exports.array(external_exports.string()).optional()
852
+ });
853
+ function validateUserProfile(data) {
854
+ const result = UserProfileSchema.safeParse(data);
855
+ if (result.success) {
856
+ return { success: true, data: result.data };
857
+ }
858
+ return { success: false, error: { message: result.error.message } };
859
+ }
860
+
861
+ // ../../packages/provider/dist/index.js
862
+ var execAsync = promisify(exec);
863
+ var GITHUB_FULL_PATTERN = /^https?:\/\/github\.com\/([^/]+\/[^/]+)(?:\/tree\/[^/]+\/(.+))?$/;
864
+ var GITHUB_SIMPLE_PATTERN = /^(?:https?:\/\/)?github\.com\/([^/]+\/[^/]+)\/?$/;
865
+ async function loadCompiledRegistry(autoSync = false) {
866
+ const compiledPath = getCompiledRegistryPath();
867
+ if (!await pathExists(compiledPath)) {
868
+ return await compileRegistry();
869
+ }
870
+ if (autoSync) {
871
+ return await compileRegistry();
872
+ }
873
+ const content = await readFile(compiledPath, "utf-8");
874
+ return JSON.parse(content);
875
+ }
876
+ function findSkill(registry, skillName) {
877
+ return registry.skills.find((s) => s.name === skillName);
878
+ }
879
+ function getInstalledSkills(registry) {
880
+ return registry.skills.filter((s) => s.installed);
881
+ }
882
+ function getAvailableSkills(registry) {
883
+ return registry.skills.filter((s) => !s.installed);
884
+ }
885
+ function getSkillsByCategory(registry, category) {
886
+ return registry.skills.filter((s) => s.category === category);
887
+ }
888
+ function getSkillsBySource(registry, source) {
889
+ return registry.skills.filter((s) => s.source === source);
890
+ }
891
+ async function installThirdPartySkill(skill, showProgress = false) {
892
+ if (!skill.gitUrl) {
893
+ return {
894
+ skill: skill.name,
895
+ status: "failed",
896
+ error: "No git URL available for skill"
897
+ };
898
+ }
899
+ const source = {
900
+ id: `temp:${skill.name}`,
901
+ type: "github",
902
+ url: skill.gitUrl,
903
+ enabled: true,
904
+ priority: 0,
905
+ addedAt: (/* @__PURE__ */ new Date()).toISOString()
906
+ };
907
+ const { syncSource: sync } = await import("./sync-MXQ4NJWI-KGAZYCPW.js");
908
+ const result = await sync(source, {
909
+ showProgress,
910
+ skillPath: skill.skillPath
911
+ // For marketplace skills
912
+ });
913
+ const firstPlugin = result.plugins[0];
914
+ if (firstPlugin) {
915
+ return firstPlugin;
916
+ }
917
+ return {
918
+ skill: skill.name,
919
+ status: "failed",
920
+ error: result.error ?? "Unknown error during installation"
921
+ };
922
+ }
923
+ async function installSkill(skillName, registry) {
924
+ const reg = registry ?? await loadCompiledRegistry();
925
+ const skill = findSkill(reg, skillName);
926
+ if (!skill) {
927
+ return {
928
+ skill: skillName,
929
+ status: "failed",
930
+ error: `Skill not found in registry: ${skillName}`
931
+ };
932
+ }
933
+ if (skill.installed) {
934
+ return {
935
+ skill: skillName,
936
+ status: "already_installed",
937
+ path: skill.installedPath
938
+ };
939
+ }
940
+ if (skill.sourceType === "builtin") {
941
+ return {
942
+ skill: skillName,
943
+ status: "failed",
944
+ error: "Built-in skill not found in local installation. Run 'looplia init' to reinstall."
945
+ };
946
+ }
947
+ return await installThirdPartySkill(skill);
948
+ }
949
+ async function updateSkill(skillName, registry) {
950
+ const reg = registry ?? await loadCompiledRegistry();
951
+ const skill = findSkill(reg, skillName);
952
+ if (!skill) {
953
+ return {
954
+ skill: skillName,
955
+ status: "failed",
956
+ error: `Skill not found in registry: ${skillName}`
957
+ };
958
+ }
959
+ if (!(skill.installed && skill.installedPath)) {
960
+ return {
961
+ skill: skillName,
962
+ status: "failed",
963
+ error: "Skill is not installed"
964
+ };
965
+ }
966
+ if (skill.sourceType === "builtin") {
967
+ return {
968
+ skill: skillName,
969
+ status: "failed",
970
+ error: "Cannot update built-in skills. Run 'looplia init' to refresh."
971
+ };
972
+ }
973
+ try {
974
+ const skillsDir = join(skill.installedPath, "..", "..");
975
+ await execAsync("git pull", { cwd: skillsDir });
976
+ return {
977
+ skill: skillName,
978
+ status: "updated",
979
+ path: skill.installedPath
980
+ };
981
+ } catch (error) {
982
+ const message = error instanceof Error ? error.message : String(error);
983
+ return {
984
+ skill: skillName,
985
+ status: "failed",
986
+ error: message
987
+ };
988
+ }
989
+ }
990
+ async function ensureWorkflowSkills(workflow, registry) {
991
+ const reg = registry ?? await loadCompiledRegistry();
992
+ const requiredSkills = extractWorkflowSkills(workflow);
993
+ const installed = [];
994
+ const failed = [];
995
+ for (const skillName of requiredSkills) {
996
+ const skill = findSkill(reg, skillName);
997
+ if (!skill) {
998
+ failed.push(skillName);
999
+ continue;
1000
+ }
1001
+ if (skill.installed) {
1002
+ continue;
1003
+ }
1004
+ if (skill.sourceType === "builtin") {
1005
+ failed.push(skillName);
1006
+ continue;
1007
+ }
1008
+ const result = await installThirdPartySkill(skill);
1009
+ if (result.status === "failed") {
1010
+ failed.push(skillName);
1011
+ } else {
1012
+ installed.push(result);
1013
+ }
1014
+ }
1015
+ return {
1016
+ ready: failed.length === 0,
1017
+ installed,
1018
+ failed
1019
+ };
1020
+ }
1021
+ async function installSkillFromUrl(url, showProgress = false) {
1022
+ const urlParts = parseGitHubUrl(url);
1023
+ if (!urlParts) {
1024
+ return {
1025
+ skill: "unknown",
1026
+ status: "failed",
1027
+ error: `Invalid GitHub URL: ${url}`
1028
+ };
1029
+ }
1030
+ const { repoUrl, skillPath } = urlParts;
1031
+ const source = {
1032
+ id: `url:${url.replace(/[^a-zA-Z0-9]/g, "-")}`,
1033
+ type: "github",
1034
+ url: repoUrl,
1035
+ enabled: true,
1036
+ priority: 0,
1037
+ addedAt: (/* @__PURE__ */ new Date()).toISOString()
1038
+ };
1039
+ const { syncSource: sync } = await import("./sync-MXQ4NJWI-KGAZYCPW.js");
1040
+ const result = await sync(source, {
1041
+ showProgress,
1042
+ skillPath
1043
+ });
1044
+ if (result.status === "synced") {
1045
+ await compileRegistry();
1046
+ }
1047
+ const firstPlugin = result.plugins[0];
1048
+ if (firstPlugin) {
1049
+ return firstPlugin;
1050
+ }
1051
+ return {
1052
+ skill: "unknown",
1053
+ status: "failed",
1054
+ error: result.error ?? "Unknown error during installation"
1055
+ };
1056
+ }
1057
+ function parseGitHubUrl(url) {
1058
+ const match = url.match(GITHUB_FULL_PATTERN);
1059
+ if (!match) {
1060
+ const simpleMatch = url.match(GITHUB_SIMPLE_PATTERN);
1061
+ if (simpleMatch?.[1]) {
1062
+ return { repoUrl: `https://github.com/${simpleMatch[1]}.git` };
1063
+ }
1064
+ return null;
1065
+ }
1066
+ const repo = match[1];
1067
+ const path = match[2];
1068
+ return {
1069
+ repoUrl: `https://github.com/${repo}.git`,
1070
+ skillPath: path
1071
+ };
1072
+ }
1073
+ async function removeSkill(skillName, registry) {
1074
+ const reg = registry ?? await loadCompiledRegistry();
1075
+ const skill = findSkill(reg, skillName);
1076
+ if (!skill) {
1077
+ return {
1078
+ skill: skillName,
1079
+ status: "failed",
1080
+ error: `Skill not found in registry: ${skillName}`
1081
+ };
1082
+ }
1083
+ if (!skill.installed) {
1084
+ return {
1085
+ skill: skillName,
1086
+ status: "failed",
1087
+ error: "Skill is not installed"
1088
+ };
1089
+ }
1090
+ if (skill.sourceType === "builtin") {
1091
+ return {
1092
+ skill: skillName,
1093
+ status: "failed",
1094
+ error: "Cannot remove built-in skills"
1095
+ };
1096
+ }
1097
+ if (!skill.installedPath) {
1098
+ return {
1099
+ skill: skillName,
1100
+ status: "failed",
1101
+ error: "Skill installation path not found"
1102
+ };
1103
+ }
1104
+ try {
1105
+ const pluginDir = join(skill.installedPath, "..", "..");
1106
+ const loopliaPath = join(homedir(), ".looplia");
1107
+ const pluginsDir = join(loopliaPath, "plugins");
1108
+ if (!pluginDir.startsWith(pluginsDir)) {
1109
+ return {
1110
+ skill: skillName,
1111
+ status: "failed",
1112
+ error: "Cannot remove: path is outside plugins directory"
1113
+ };
1114
+ }
1115
+ await rm(pluginDir, { recursive: true, force: true });
1116
+ await compileRegistry();
1117
+ return {
1118
+ skill: skillName,
1119
+ status: "removed",
1120
+ path: pluginDir
1121
+ };
1122
+ } catch (error) {
1123
+ const message = error instanceof Error ? error.message : String(error);
1124
+ return {
1125
+ skill: skillName,
1126
+ status: "failed",
1127
+ error: message
1128
+ };
1129
+ }
1130
+ }
1131
+
1132
+ export {
1133
+ parseWorkflow,
1134
+ isInputlessWorkflow,
1135
+ extractWorkflowSkills,
1136
+ validateUserProfile,
1137
+ loadCompiledRegistry,
1138
+ findSkill,
1139
+ getInstalledSkills,
1140
+ getAvailableSkills,
1141
+ getSkillsByCategory,
1142
+ getSkillsBySource,
1143
+ installSkill,
1144
+ updateSkill,
1145
+ ensureWorkflowSkills,
1146
+ installSkillFromUrl,
1147
+ removeSkill
1148
+ };