@sundaeswap/sprinkles 0.6.0 → 0.7.0

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 (158) hide show
  1. package/dist/cjs/Sprinkle/__tests__/action-integration.test.js +590 -0
  2. package/dist/cjs/Sprinkle/__tests__/action-integration.test.js.map +1 -0
  3. package/dist/cjs/Sprinkle/__tests__/action-registry.test.js +193 -0
  4. package/dist/cjs/Sprinkle/__tests__/action-registry.test.js.map +1 -0
  5. package/dist/cjs/Sprinkle/__tests__/action-runner.test.js +304 -0
  6. package/dist/cjs/Sprinkle/__tests__/action-runner.test.js.map +1 -0
  7. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js +1110 -0
  8. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
  9. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js +722 -0
  10. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
  11. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +138 -0
  12. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  13. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js +713 -0
  14. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
  15. package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js +334 -0
  16. package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
  17. package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js +749 -0
  18. package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
  19. package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js +61 -0
  20. package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
  21. package/dist/cjs/Sprinkle/actions/builtin/index.js +117 -0
  22. package/dist/cjs/Sprinkle/actions/builtin/index.js.map +1 -0
  23. package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js +202 -0
  24. package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
  25. package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js +87 -0
  26. package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
  27. package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js +345 -0
  28. package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
  29. package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js +212 -0
  30. package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
  31. package/dist/cjs/Sprinkle/actions/cli-adapter.js +372 -0
  32. package/dist/cjs/Sprinkle/actions/cli-adapter.js.map +1 -0
  33. package/dist/cjs/Sprinkle/actions/index.js +127 -0
  34. package/dist/cjs/Sprinkle/actions/index.js.map +1 -0
  35. package/dist/cjs/Sprinkle/actions/mcp-adapter.js +415 -0
  36. package/dist/cjs/Sprinkle/actions/mcp-adapter.js.map +1 -0
  37. package/dist/cjs/Sprinkle/actions/registry.js +92 -0
  38. package/dist/cjs/Sprinkle/actions/registry.js.map +1 -0
  39. package/dist/cjs/Sprinkle/actions/runner.js +190 -0
  40. package/dist/cjs/Sprinkle/actions/runner.js.map +1 -0
  41. package/dist/cjs/Sprinkle/actions/tui-helpers.js +96 -0
  42. package/dist/cjs/Sprinkle/actions/tui-helpers.js.map +1 -0
  43. package/dist/cjs/Sprinkle/actions/types.js +68 -0
  44. package/dist/cjs/Sprinkle/actions/types.js.map +1 -0
  45. package/dist/cjs/Sprinkle/index.js +451 -4
  46. package/dist/cjs/Sprinkle/index.js.map +1 -1
  47. package/dist/cjs/Sprinkle/prompts.js +12 -7
  48. package/dist/cjs/Sprinkle/prompts.js.map +1 -1
  49. package/dist/cjs/Sprinkle/type-guards.js +7 -1
  50. package/dist/cjs/Sprinkle/type-guards.js.map +1 -1
  51. package/dist/esm/Sprinkle/__tests__/action-integration.test.js +588 -0
  52. package/dist/esm/Sprinkle/__tests__/action-integration.test.js.map +1 -0
  53. package/dist/esm/Sprinkle/__tests__/action-registry.test.js +192 -0
  54. package/dist/esm/Sprinkle/__tests__/action-registry.test.js.map +1 -0
  55. package/dist/esm/Sprinkle/__tests__/action-runner.test.js +302 -0
  56. package/dist/esm/Sprinkle/__tests__/action-runner.test.js.map +1 -0
  57. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js +1107 -0
  58. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
  59. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js +720 -0
  60. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
  61. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +138 -0
  62. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  63. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js +712 -0
  64. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
  65. package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js +332 -0
  66. package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
  67. package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js +747 -0
  68. package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
  69. package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js +55 -0
  70. package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
  71. package/dist/esm/Sprinkle/actions/builtin/index.js +32 -0
  72. package/dist/esm/Sprinkle/actions/builtin/index.js.map +1 -0
  73. package/dist/esm/Sprinkle/actions/builtin/profile-actions.js +197 -0
  74. package/dist/esm/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
  75. package/dist/esm/Sprinkle/actions/builtin/settings-actions.js +81 -0
  76. package/dist/esm/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
  77. package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js +340 -0
  78. package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
  79. package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js +207 -0
  80. package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
  81. package/dist/esm/Sprinkle/actions/cli-adapter.js +361 -0
  82. package/dist/esm/Sprinkle/actions/cli-adapter.js.map +1 -0
  83. package/dist/esm/Sprinkle/actions/index.js +12 -0
  84. package/dist/esm/Sprinkle/actions/index.js.map +1 -0
  85. package/dist/esm/Sprinkle/actions/mcp-adapter.js +407 -0
  86. package/dist/esm/Sprinkle/actions/mcp-adapter.js.map +1 -0
  87. package/dist/esm/Sprinkle/actions/registry.js +85 -0
  88. package/dist/esm/Sprinkle/actions/registry.js.map +1 -0
  89. package/dist/esm/Sprinkle/actions/runner.js +182 -0
  90. package/dist/esm/Sprinkle/actions/runner.js.map +1 -0
  91. package/dist/esm/Sprinkle/actions/tui-helpers.js +91 -0
  92. package/dist/esm/Sprinkle/actions/tui-helpers.js.map +1 -0
  93. package/dist/esm/Sprinkle/actions/types.js +61 -0
  94. package/dist/esm/Sprinkle/actions/types.js.map +1 -0
  95. package/dist/esm/Sprinkle/index.js +299 -4
  96. package/dist/esm/Sprinkle/index.js.map +1 -1
  97. package/dist/esm/Sprinkle/prompts.js +12 -7
  98. package/dist/esm/Sprinkle/prompts.js.map +1 -1
  99. package/dist/esm/Sprinkle/type-guards.js +3 -0
  100. package/dist/esm/Sprinkle/type-guards.js.map +1 -1
  101. package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts +39 -0
  102. package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts.map +1 -0
  103. package/dist/types/Sprinkle/actions/builtin/index.d.ts +26 -0
  104. package/dist/types/Sprinkle/actions/builtin/index.d.ts.map +1 -0
  105. package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts +55 -0
  106. package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts.map +1 -0
  107. package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts +32 -0
  108. package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts.map +1 -0
  109. package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts +70 -0
  110. package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts.map +1 -0
  111. package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts +50 -0
  112. package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts.map +1 -0
  113. package/dist/types/Sprinkle/actions/cli-adapter.d.ts +104 -0
  114. package/dist/types/Sprinkle/actions/cli-adapter.d.ts.map +1 -0
  115. package/dist/types/Sprinkle/actions/index.d.ts +12 -0
  116. package/dist/types/Sprinkle/actions/index.d.ts.map +1 -0
  117. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts +92 -0
  118. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts.map +1 -0
  119. package/dist/types/Sprinkle/actions/registry.d.ts +42 -0
  120. package/dist/types/Sprinkle/actions/registry.d.ts.map +1 -0
  121. package/dist/types/Sprinkle/actions/runner.d.ts +45 -0
  122. package/dist/types/Sprinkle/actions/runner.d.ts.map +1 -0
  123. package/dist/types/Sprinkle/actions/tui-helpers.d.ts +53 -0
  124. package/dist/types/Sprinkle/actions/tui-helpers.d.ts.map +1 -0
  125. package/dist/types/Sprinkle/actions/types.d.ts +76 -0
  126. package/dist/types/Sprinkle/actions/types.d.ts.map +1 -0
  127. package/dist/types/Sprinkle/index.d.ts +81 -1
  128. package/dist/types/Sprinkle/index.d.ts.map +1 -1
  129. package/dist/types/Sprinkle/prompts.d.ts.map +1 -1
  130. package/dist/types/Sprinkle/type-guards.d.ts +4 -1
  131. package/dist/types/Sprinkle/type-guards.d.ts.map +1 -1
  132. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  133. package/package.json +9 -2
  134. package/src/Sprinkle/__tests__/action-integration.test.ts +558 -0
  135. package/src/Sprinkle/__tests__/action-registry.test.ts +187 -0
  136. package/src/Sprinkle/__tests__/action-runner.test.ts +324 -0
  137. package/src/Sprinkle/__tests__/builtin-actions.test.ts +1022 -0
  138. package/src/Sprinkle/__tests__/cli-adapter.test.ts +715 -0
  139. package/src/Sprinkle/__tests__/fill-in-struct.test.ts +144 -0
  140. package/src/Sprinkle/__tests__/mcp-adapter.test.ts +718 -0
  141. package/src/Sprinkle/__tests__/tui-helpers.test.ts +325 -0
  142. package/src/Sprinkle/__tests__/wallet-transaction-actions.test.ts +695 -0
  143. package/src/Sprinkle/actions/builtin/blaze-helper.ts +89 -0
  144. package/src/Sprinkle/actions/builtin/index.ts +86 -0
  145. package/src/Sprinkle/actions/builtin/profile-actions.ts +229 -0
  146. package/src/Sprinkle/actions/builtin/settings-actions.ts +99 -0
  147. package/src/Sprinkle/actions/builtin/transaction-actions.ts +381 -0
  148. package/src/Sprinkle/actions/builtin/wallet-actions.ts +233 -0
  149. package/src/Sprinkle/actions/cli-adapter.ts +430 -0
  150. package/src/Sprinkle/actions/index.ts +32 -0
  151. package/src/Sprinkle/actions/mcp-adapter.ts +463 -0
  152. package/src/Sprinkle/actions/registry.ts +97 -0
  153. package/src/Sprinkle/actions/runner.ts +200 -0
  154. package/src/Sprinkle/actions/tui-helpers.ts +114 -0
  155. package/src/Sprinkle/actions/types.ts +91 -0
  156. package/src/Sprinkle/index.ts +395 -3
  157. package/src/Sprinkle/prompts.ts +118 -72
  158. package/src/Sprinkle/type-guards.ts +9 -0
@@ -6,6 +6,7 @@ import { CborSet, VkeyWitness, TxCBOR } from "@blaze-cardano/core";
6
6
  import { selectCancellable, selectWithClear, inputCancellable, inputWithClear, passwordWithClear, confirmCancellable, searchCancellable } from "./prompts.js";
7
7
  import colors from "yoctocolors-cjs";
8
8
  import { OptionalKind } from "@sinclair/typebox";
9
+ import { Value } from "@sinclair/typebox/value";
9
10
  import * as fs from "fs";
10
11
  import * as path from "path";
11
12
  export * from "@sinclair/typebox";
@@ -19,7 +20,7 @@ import { UserCancelledError } from "./types.js";
19
20
  export { NetworkSchema, MultisigScriptModule, MultisigScript, ProviderSettingsSchema, WalletSettingsSchema } from "./schemas.js";
20
21
  // Import and re-export type guards
21
22
  import { isOptional, isImport, isArray, isBigInt, isLiteral, isObject, isRef, isString, isThis, isTuple, isUnion, isSensitive } from "./type-guards.js";
22
- export { isOptional, isImport, isArray, isBigInt, isLiteral, isObject, isRef, isString, isThis, isTuple, isUnion, isSensitive, isNull, isNullable, unwrapNullable, hasDefault, getDefault } from "./type-guards.js";
23
+ export { isOptional, isImport, isArray, isBigInt, isBoolean, isInteger, isLiteral, isNumber, isObject, isRef, isString, isThis, isTuple, isUnion, isSensitive, isNull, isNullable, unwrapNullable, hasDefault, getDefault } from "./type-guards.js";
23
24
 
24
25
  // Import schemas for use in this file
25
26
 
@@ -36,6 +37,10 @@ import { countSignatures, getRequiredSigners, getTxBodyHash, formatHash, mergeSi
36
37
  // Import menu modules
37
38
  import { promptObject, promptArray } from "./menus/index.js";
38
39
  import { formatPath } from "./utils/formatting.js";
40
+
41
+ // Import and re-export action system
42
+ import { ActionRegistry, ActionError, executeAction, detectMode, parseCliArgs, generateActionHelp, generateAppHelp, runCli, runMcp } from "./actions/index.js";
43
+ export { ActionRegistry, ActionError, executeAction, detectMode, parseCliArgs, generateActionHelp, camelToKebab, kebabToCamel, coerceValue, parseArgvWithSchema, generateAppHelp, runCli, typeboxToJsonSchema, coerceMcpInput, getMcpSdk, createMcpServer, runMcp, getBuiltinActions, promptAndExecute } from "./actions/index.js";
39
44
  export class Sprinkle {
40
45
  constructor(type, storagePath, options) {
41
46
  _defineProperty(this, "storagePath", void 0);
@@ -49,9 +54,11 @@ export class Sprinkle {
49
54
  createdAt: "",
50
55
  updatedAt: ""
51
56
  });
57
+ _defineProperty(this, "actionRegistry", void 0);
52
58
  this.type = type;
53
59
  this.storagePath = storagePath;
54
60
  this.options = options ?? {};
61
+ this.actionRegistry = new ActionRegistry();
55
62
  }
56
63
 
57
64
  // --- Current Profile Accessor ---
@@ -435,8 +442,15 @@ export class Sprinkle {
435
442
  }, {
436
443
  title: "Edit settings",
437
444
  action: async () => {
438
- this.settings = await this.EditStruct(this.type, this.settings);
439
- this.saveSettings();
445
+ try {
446
+ this.settings = await this.EditStruct(this.type, this.settings);
447
+ this.saveSettings();
448
+ } catch (e) {
449
+ if (e instanceof UserCancelledError) {
450
+ return; // User cancelled, return to menu
451
+ }
452
+ throw e;
453
+ }
440
454
  }
441
455
  }, {
442
456
  title: "Switch profile",
@@ -562,6 +576,87 @@ export class Sprinkle {
562
576
  getDisplaySettings() {
563
577
  return maskSensitiveFields(this.settings, this.type);
564
578
  }
579
+
580
+ // --- Non-interactive profile management (for CLI/MCP actions) ---
581
+
582
+ /**
583
+ * Look up a profile entry by its ID without loading it.
584
+ * Returns undefined if no profile with the given ID exists.
585
+ */
586
+ getProfileById(id) {
587
+ return this.scanProfiles().find(p => p.id === id);
588
+ }
589
+
590
+ /**
591
+ * Create a new profile file without interactive prompts.
592
+ * Does NOT switch the active profile.
593
+ *
594
+ * @throws ActionError with code DUPLICATE_PROFILE if a profile with the same name already exists.
595
+ * @returns The created IProfileEntry
596
+ */
597
+ async createProfileNonInteractive(name, description, initialSettings) {
598
+ const profiles = this.scanProfiles();
599
+ const nameLower = name.toLowerCase();
600
+ const duplicate = profiles.find(p => p.meta.name.toLowerCase() === nameLower);
601
+ if (duplicate) {
602
+ throw new ActionError(`A profile named "${name}" already exists.`, "DUPLICATE_PROFILE", {
603
+ existingId: duplicate.id
604
+ });
605
+ }
606
+ const profilesDir = Sprinkle.profilesDir(this.storagePath);
607
+ if (!fs.existsSync(profilesDir)) {
608
+ fs.mkdirSync(profilesDir, {
609
+ recursive: true
610
+ });
611
+ }
612
+ const baseId = Sprinkle.sanitizeProfileId(name);
613
+ const id = Sprinkle.findAvailableId(profilesDir, baseId);
614
+ const now = new Date().toISOString();
615
+ const meta = {
616
+ name,
617
+ description,
618
+ createdAt: now,
619
+ updatedAt: now
620
+ };
621
+ const profileData = {
622
+ meta,
623
+ settings: initialSettings ?? {},
624
+ defaults: {}
625
+ };
626
+ fs.writeFileSync(path.join(profilesDir, `${id}.json`), JSON.stringify(profileData, bigIntReplacer, 2), "utf-8");
627
+ return {
628
+ id,
629
+ meta
630
+ };
631
+ }
632
+
633
+ /**
634
+ * Delete a profile file by ID.
635
+ *
636
+ * @throws ActionError with code PROFILE_NOT_FOUND if no profile with the given ID exists.
637
+ * @throws ActionError with code CANNOT_DELETE_ONLY_PROFILE if this is the only profile.
638
+ * @throws ActionError with code CANNOT_DELETE_ACTIVE_PROFILE if this is the currently active profile.
639
+ */
640
+ deleteProfileById(id) {
641
+ const profiles = this.scanProfiles();
642
+ const profile = profiles.find(p => p.id === id);
643
+ if (!profile) {
644
+ throw new ActionError(`Profile "${id}" not found.`, "PROFILE_NOT_FOUND", {
645
+ id
646
+ });
647
+ }
648
+ if (profiles.length === 1) {
649
+ throw new ActionError("Cannot delete the only profile.", "CANNOT_DELETE_ONLY_PROFILE", {
650
+ id
651
+ });
652
+ }
653
+ if (id === this.profileId) {
654
+ throw new ActionError("Cannot delete the active profile. Switch to a different profile first.", "CANNOT_DELETE_ACTIVE_PROFILE", {
655
+ id
656
+ });
657
+ }
658
+ fs.unlinkSync(Sprinkle.profilePath(this.storagePath, id));
659
+ }
565
660
  async TxDialog(blaze, tx, opts) {
566
661
  let currentTx = tx;
567
662
  let expanded = false;
@@ -917,7 +1012,35 @@ export class Sprinkle {
917
1012
  throw new UserCancelledError();
918
1013
  }
919
1014
  const selection = selectionResult;
920
- return this._fillInStruct(selection, path, defs);
1015
+ // Determine if the provided default value matches the selected variant.
1016
+ // For discriminated unions (objects with literal fields like `type`), check
1017
+ // if the literal field values in the selected variant match those in `def`.
1018
+ // For non-discriminated unions, fall back to structural matching with Value.Check.
1019
+ let matchedDef = undefined;
1020
+ if (def !== undefined) {
1021
+ if (isObject(selection)) {
1022
+ // Check if all literal fields in the selected variant match def
1023
+ const literalFields = Object.entries(selection.properties ?? {}).filter(([, fieldSchema]) => isLiteral(fieldSchema));
1024
+ if (literalFields.length > 0) {
1025
+ const defRecord = def;
1026
+ const allLiteralsMatch = literalFields.every(([fieldName, fieldSchema]) => defRecord[fieldName] === fieldSchema.const);
1027
+ if (allLiteralsMatch) {
1028
+ matchedDef = def;
1029
+ }
1030
+ } else {
1031
+ // No literal discriminators - use structural check
1032
+ if (Value.Check(selection, def)) {
1033
+ matchedDef = def;
1034
+ }
1035
+ }
1036
+ } else {
1037
+ // Non-object variant - use structural check
1038
+ if (Value.Check(selection, def)) {
1039
+ matchedDef = def;
1040
+ }
1041
+ }
1042
+ }
1043
+ return this._fillInStruct(selection, path, defs, matchedDef);
921
1044
  }
922
1045
  if (isString(type)) {
923
1046
  // Special handling for hot wallet private key - offer generation option
@@ -1076,5 +1199,177 @@ export class Sprinkle {
1076
1199
  }
1077
1200
  return def;
1078
1201
  }
1202
+
1203
+ // --- Action System ---
1204
+
1205
+ /**
1206
+ * Register an action on this Sprinkle instance.
1207
+ * Delegates to the internal ActionRegistry with name and schema validation.
1208
+ */
1209
+ registerAction(action) {
1210
+ this.actionRegistry.register(action);
1211
+ }
1212
+
1213
+ /**
1214
+ * Retrieve a registered action by name.
1215
+ * @returns The action, or undefined if not registered
1216
+ */
1217
+ getAction(name) {
1218
+ return this.actionRegistry.get(name);
1219
+ }
1220
+
1221
+ /**
1222
+ * List all registered actions.
1223
+ */
1224
+ listActions() {
1225
+ return this.actionRegistry.list();
1226
+ }
1227
+
1228
+ /**
1229
+ * List all registered actions grouped by category.
1230
+ * Uncategorized actions are grouped under "default".
1231
+ */
1232
+ listActionsByCategory() {
1233
+ return this.actionRegistry.listByCategory();
1234
+ }
1235
+
1236
+ /**
1237
+ * Execute a registered action by name with raw (unvalidated) input.
1238
+ * Input is validated against the action's inputSchema before execution.
1239
+ *
1240
+ * @throws Error if the action name is not registered
1241
+ */
1242
+ async runAction(name, input) {
1243
+ const action = this.actionRegistry.get(name);
1244
+ if (!action) {
1245
+ throw new Error(`Action "${name}" is not registered. Available actions: ${this.actionRegistry.list().map(a => a.name).join(", ") || "(none)"}`);
1246
+ }
1247
+ const context = {
1248
+ sprinkle: this,
1249
+ settings: this.settings
1250
+ };
1251
+ return executeAction(action, input, context);
1252
+ }
1253
+
1254
+ /**
1255
+ * Non-interactive profile initialization for CLI/MCP modes.
1256
+ * Resolves a profile without any user prompts.
1257
+ *
1258
+ * Resolution order:
1259
+ * 1. If profileName provided, load that profile directly
1260
+ * 2. If exactly one profile exists, auto-select it
1261
+ * 3. If multiple profiles exist and no name given, throw with available names
1262
+ * 4. If no profiles exist, throw instructing user to run in interactive mode
1263
+ */
1264
+ async initNonInteractive(profileName) {
1265
+ await this.migrateIfNeeded();
1266
+ if (profileName) {
1267
+ await this.loadProfile(profileName);
1268
+ return;
1269
+ }
1270
+ const profiles = this.scanProfiles();
1271
+ if (profiles.length === 0) {
1272
+ throw new Error("No profiles found. Run in interactive mode to create one.");
1273
+ }
1274
+ if (profiles.length === 1) {
1275
+ await this.loadProfile(profiles[0].id);
1276
+ return;
1277
+ }
1278
+
1279
+ // Multiple profiles without a --profile flag
1280
+ const names = profiles.map(p => `"${p.id}" (${p.meta.name})`).join(", ");
1281
+ throw new Error(`Multiple profiles found. Specify one with --profile <id>. Available profiles: ${names}`);
1282
+ }
1283
+
1284
+ /**
1285
+ * Static entry point that detects mode and runs accordingly.
1286
+ *
1287
+ * Modes:
1288
+ * - "tui" -- Interactive TUI menu (requires menu option)
1289
+ * - "cli" -- CLI action execution (parses argv for action name and args)
1290
+ * - "mcp" -- MCP protocol mode (not yet implemented)
1291
+ * - "help" -- Print available actions and exit
1292
+ */
1293
+ static async run(opts) {
1294
+ const argv = opts.argv ?? process.argv.slice(2);
1295
+ const mode = detectMode(argv);
1296
+ if (mode === "tui") {
1297
+ if (!opts.menu) {
1298
+ throw new Error("TUI mode requires a menu. Provide a menu option or pass --help to see available actions.");
1299
+ }
1300
+ const sprinkle = await Sprinkle.New(opts.type, opts.storagePath, opts.options);
1301
+ // Register any provided actions
1302
+ for (const action of opts.actions ?? []) {
1303
+ sprinkle.registerAction(action);
1304
+ }
1305
+ await sprinkle.showMenu(opts.menu);
1306
+ return;
1307
+ }
1308
+ if (mode === "help") {
1309
+ const sprinkle = new Sprinkle(opts.type, opts.storagePath, opts.options);
1310
+ for (const action of opts.actions ?? []) {
1311
+ sprinkle.registerAction(action);
1312
+ }
1313
+ console.log(generateAppHelp(sprinkle.listActions()));
1314
+ return;
1315
+ }
1316
+ if (mode === "cli") {
1317
+ const {
1318
+ actionName,
1319
+ args
1320
+ } = parseCliArgs(argv);
1321
+
1322
+ // Extract --profile flag from args if present
1323
+ const profileName = typeof args["profile"] === "string" ? args["profile"] : undefined;
1324
+ const sprinkle = new Sprinkle(opts.type, opts.storagePath, opts.options);
1325
+ for (const action of opts.actions ?? []) {
1326
+ sprinkle.registerAction(action);
1327
+ }
1328
+
1329
+ // Handle action-specific --help BEFORE profile initialization
1330
+ // (help should work without a profile)
1331
+ if (args["help"] === true) {
1332
+ const action = sprinkle.getAction(actionName);
1333
+ if (action) {
1334
+ console.log(generateActionHelp(action));
1335
+ } else {
1336
+ console.log(`Unknown action "${actionName}". Run --help to see available actions.`);
1337
+ }
1338
+ return;
1339
+ }
1340
+ await sprinkle.initNonInteractive(profileName);
1341
+
1342
+ // Remove internal flags from args before passing to action
1343
+ const actionArgs = {
1344
+ ...args
1345
+ };
1346
+ delete actionArgs["profile"];
1347
+ delete actionArgs["help"];
1348
+
1349
+ // Build context and delegate to runCli for proper error routing
1350
+ const context = {
1351
+ sprinkle,
1352
+ settings: sprinkle.settings
1353
+ };
1354
+ await runCli(sprinkle, actionName, actionArgs, context);
1355
+ return;
1356
+ }
1357
+ if (mode === "mcp") {
1358
+ // Derive a server name from the storage path basename, falling back to a
1359
+ // sensible default.
1360
+ const serverName = path.basename(opts.storagePath) || "sprinkle-mcp";
1361
+ const sprinkle = new Sprinkle(opts.type, opts.storagePath, opts.options);
1362
+ for (const action of opts.actions ?? []) {
1363
+ sprinkle.registerAction(action);
1364
+ }
1365
+
1366
+ // Read profile name from environment variable (MCP clients have no
1367
+ // interactive terminal to prompt for profile selection).
1368
+ const profileName = process.env["SPRINKLE_PROFILE"];
1369
+ await sprinkle.initNonInteractive(profileName);
1370
+ await runMcp(sprinkle, serverName);
1371
+ return;
1372
+ }
1373
+ }
1079
1374
  }
1080
1375
  //# sourceMappingURL=index.js.map