@nexrall/code-core 1.4.76 → 1.4.78

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 (143) hide show
  1. package/dist/agent/effortCap.d.ts +24 -0
  2. package/dist/agent/effortCap.d.ts.map +1 -0
  3. package/dist/agent/effortCap.js +66 -0
  4. package/dist/agent/goal.d.ts +55 -0
  5. package/dist/agent/goal.d.ts.map +1 -0
  6. package/dist/agent/goal.js +120 -0
  7. package/dist/agent/handoffBrief.d.ts +82 -0
  8. package/dist/agent/handoffBrief.d.ts.map +1 -0
  9. package/dist/agent/handoffBrief.js +121 -0
  10. package/dist/agent/hooks.d.ts +10 -2
  11. package/dist/agent/hooks.d.ts.map +1 -1
  12. package/dist/agent/hooks.js +9 -3
  13. package/dist/agent/modelCatalogue.d.ts +23 -0
  14. package/dist/agent/modelCatalogue.d.ts.map +1 -1
  15. package/dist/agent/modelCatalogue.js +28 -0
  16. package/dist/agent/outputStyles.d.ts +27 -0
  17. package/dist/agent/outputStyles.d.ts.map +1 -0
  18. package/dist/agent/outputStyles.js +174 -0
  19. package/dist/agent/skills.d.ts.map +1 -1
  20. package/dist/agent/skills.js +21 -1
  21. package/dist/api/billingLines.d.ts +11 -0
  22. package/dist/api/billingLines.d.ts.map +1 -0
  23. package/dist/api/billingLines.js +56 -0
  24. package/dist/api/client.d.ts +2 -0
  25. package/dist/api/client.d.ts.map +1 -1
  26. package/dist/api/scheduleLines.d.ts +53 -0
  27. package/dist/api/scheduleLines.d.ts.map +1 -0
  28. package/dist/api/scheduleLines.js +150 -0
  29. package/dist/context/contextFiles.d.ts +81 -0
  30. package/dist/context/contextFiles.d.ts.map +1 -0
  31. package/dist/context/contextFiles.js +310 -0
  32. package/dist/diff/collect.d.ts +25 -0
  33. package/dist/diff/collect.d.ts.map +1 -0
  34. package/dist/diff/collect.js +169 -0
  35. package/dist/diff/diffView.d.ts +57 -0
  36. package/dist/diff/diffView.d.ts.map +1 -0
  37. package/dist/diff/diffView.js +231 -0
  38. package/dist/diff/reviewQueue.d.ts +53 -0
  39. package/dist/diff/reviewQueue.d.ts.map +1 -0
  40. package/dist/diff/reviewQueue.js +349 -0
  41. package/dist/events/recap.d.ts +54 -0
  42. package/dist/events/recap.d.ts.map +1 -0
  43. package/dist/events/recap.js +161 -0
  44. package/dist/events/sessionEvents.d.ts +54 -0
  45. package/dist/events/sessionEvents.d.ts.map +1 -0
  46. package/dist/events/sessionEvents.js +324 -0
  47. package/dist/events/sessionStats.d.ts +89 -0
  48. package/dist/events/sessionStats.d.ts.map +1 -0
  49. package/dist/events/sessionStats.js +264 -0
  50. package/dist/import/claudeImport.d.ts +44 -0
  51. package/dist/import/claudeImport.d.ts.map +1 -0
  52. package/dist/import/claudeImport.js +337 -0
  53. package/dist/index.d.ts +38 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +38 -0
  56. package/dist/mcp/manager.d.ts +8 -0
  57. package/dist/mcp/manager.d.ts.map +1 -1
  58. package/dist/mcp/manager.js +64 -57
  59. package/dist/permissions/destructiveTokens.d.ts +1 -0
  60. package/dist/permissions/destructiveTokens.d.ts.map +1 -1
  61. package/dist/permissions/destructiveTokens.js +93 -0
  62. package/dist/permissions/rules.d.ts +12 -0
  63. package/dist/permissions/rules.d.ts.map +1 -1
  64. package/dist/permissions/rules.js +89 -7
  65. package/dist/plugins/configStore.d.ts +43 -0
  66. package/dist/plugins/configStore.d.ts.map +1 -0
  67. package/dist/plugins/configStore.js +253 -0
  68. package/dist/plugins/files.d.ts +10 -0
  69. package/dist/plugins/files.d.ts.map +1 -0
  70. package/dist/plugins/files.js +85 -0
  71. package/dist/plugins/index.d.ts +3 -22
  72. package/dist/plugins/index.d.ts.map +1 -1
  73. package/dist/plugins/index.js +30 -40
  74. package/dist/plugins/resolve.d.ts +18 -0
  75. package/dist/plugins/resolve.d.ts.map +1 -0
  76. package/dist/plugins/resolve.js +152 -0
  77. package/dist/plugins/userConfig.d.ts +90 -0
  78. package/dist/plugins/userConfig.d.ts.map +1 -0
  79. package/dist/plugins/userConfig.js +332 -0
  80. package/dist/plugins/validate.d.ts +14 -0
  81. package/dist/plugins/validate.d.ts.map +1 -0
  82. package/dist/plugins/validate.js +222 -0
  83. package/dist/project/projectCommands.d.ts +33 -0
  84. package/dist/project/projectCommands.d.ts.map +1 -0
  85. package/dist/project/projectCommands.js +246 -0
  86. package/dist/search/conversationTurns.d.ts +53 -0
  87. package/dist/search/conversationTurns.d.ts.map +1 -0
  88. package/dist/search/conversationTurns.js +170 -0
  89. package/dist/search/globalSearch.d.ts +144 -0
  90. package/dist/search/globalSearch.d.ts.map +1 -0
  91. package/dist/search/globalSearch.js +350 -0
  92. package/dist/share/sessionChannel.d.ts +136 -0
  93. package/dist/share/sessionChannel.d.ts.map +1 -0
  94. package/dist/share/sessionChannel.js +373 -0
  95. package/dist/share/sessionShare.d.ts +104 -0
  96. package/dist/share/sessionShare.d.ts.map +1 -0
  97. package/dist/share/sessionShare.js +336 -0
  98. package/dist/ui/configCmd.d.ts +48 -0
  99. package/dist/ui/configCmd.d.ts.map +1 -0
  100. package/dist/ui/configCmd.js +146 -0
  101. package/dist/ui/deployPanel.d.ts +91 -0
  102. package/dist/ui/deployPanel.d.ts.map +1 -0
  103. package/dist/ui/deployPanel.js +442 -0
  104. package/dist/ui/mergeAssistant.d.ts +120 -0
  105. package/dist/ui/mergeAssistant.d.ts.map +1 -0
  106. package/dist/ui/mergeAssistant.js +391 -0
  107. package/dist/ui/notify.d.ts +114 -0
  108. package/dist/ui/notify.d.ts.map +1 -0
  109. package/dist/ui/notify.js +235 -0
  110. package/dist/ui/promptSuggest.d.ts +56 -0
  111. package/dist/ui/promptSuggest.d.ts.map +1 -0
  112. package/dist/ui/promptSuggest.js +160 -0
  113. package/dist/ui/taskGraph.d.ts +44 -0
  114. package/dist/ui/taskGraph.d.ts.map +1 -0
  115. package/dist/ui/taskGraph.js +289 -0
  116. package/dist/util/addDir.d.ts +31 -0
  117. package/dist/util/addDir.d.ts.map +1 -0
  118. package/dist/util/addDir.js +124 -0
  119. package/dist/util/batch.d.ts +16 -0
  120. package/dist/util/batch.d.ts.map +1 -0
  121. package/dist/util/batch.js +92 -0
  122. package/dist/util/cellWidth.d.ts +12 -0
  123. package/dist/util/cellWidth.d.ts.map +1 -0
  124. package/dist/util/cellWidth.js +161 -0
  125. package/dist/util/duration.d.ts +8 -0
  126. package/dist/util/duration.d.ts.map +1 -0
  127. package/dist/util/duration.js +50 -0
  128. package/dist/util/loopJobs.d.ts +67 -0
  129. package/dist/util/loopJobs.d.ts.map +1 -0
  130. package/dist/util/loopJobs.js +169 -0
  131. package/dist/util/settingsFile.d.ts +14 -0
  132. package/dist/util/settingsFile.d.ts.map +1 -0
  133. package/dist/util/settingsFile.js +99 -0
  134. package/dist/util/toolLabel.d.ts +2 -0
  135. package/dist/util/toolLabel.d.ts.map +1 -0
  136. package/dist/util/toolLabel.js +17 -0
  137. package/dist/verify/failureTimeline.d.ts +100 -0
  138. package/dist/verify/failureTimeline.d.ts.map +1 -0
  139. package/dist/verify/failureTimeline.js +253 -0
  140. package/dist/verify/verification.d.ts +75 -0
  141. package/dist/verify/verification.d.ts.map +1 -0
  142. package/dist/verify/verification.js +315 -0
  143. package/package.json +5 -4
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ // ─── Resolved plugin surfaces (S24) ──────────────────────────────────────────
3
+ //
4
+ // plugins/index.ts stays pure of VALUES: it merges hooks/mcp across plugins and
5
+ // stamps provenance (which plugin a handler/server came from). This module adds
6
+ // the userConfig layer on top — it resolves each plugin's values (configStore)
7
+ // and hands the loader a transform that bakes them into the surfaces:
8
+ //
9
+ // • hooks → `pluginOptions`: the NEXRALL_PLUGIN_OPTION_<KEY> env map each
10
+ // handler's process receives (attached under the entry's both forms, the
11
+ // same way `plugin` provenance is stamped);
12
+ // • mcp.json → `${user_config.KEY}` substituted through the whole server
13
+ // config (sensitive values INCLUDED — a server config is a trusted
14
+ // execution surface the user explicitly installed);
15
+ // • skill bodies → non-sensitive values only (see skills.ts's expandSkill);
16
+ // a sensitive reference stays the literal token rather than entering model
17
+ // context.
18
+ //
19
+ // Import graph note: this module may import configStore (hooks.ts imports
20
+ // resolve.ts, and configStore deliberately does not import hooks back).
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.resolvePluginHooks = resolvePluginHooks;
56
+ exports.resolvePluginMcpServers = resolvePluginMcpServers;
57
+ exports.pluginConfigForSkillDir = pluginConfigForSkillDir;
58
+ exports.substituteSkillBody = substituteSkillBody;
59
+ const path = __importStar(require("path"));
60
+ const index_1 = require("./index");
61
+ const userConfig_1 = require("./userConfig");
62
+ const configStore_1 = require("./configStore");
63
+ /** Per-plugin resolved config for every ENABLED plugin that declares userConfig. */
64
+ function configsByPlugin(workDir) {
65
+ const out = new Map();
66
+ for (const p of (0, index_1.loadPlugins)(workDir)) {
67
+ if (p.disabled)
68
+ continue;
69
+ const { options } = (0, userConfig_1.parseUserConfig)((0, index_1.readPluginManifest)(p.dir));
70
+ if (!options.length)
71
+ continue;
72
+ out.set(p.name, { options, values: (0, configStore_1.resolvePluginConfig)(workDir, p.name, options).values });
73
+ }
74
+ return out;
75
+ }
76
+ /** pluginHooks + each handler's resolved NEXRALL_PLUGIN_OPTION_* env map. */
77
+ function resolvePluginHooks(workDir) {
78
+ const configs = configsByPlugin(workDir);
79
+ if (!configs.size)
80
+ return (0, index_1.pluginHooks)(workDir);
81
+ return (0, index_1.pluginHooks)(workDir, (entry, plugin) => {
82
+ const cfg = configs.get(plugin);
83
+ if (!cfg)
84
+ return entry;
85
+ const env = (0, userConfig_1.pluginOptionEnvMap)(cfg.options, cfg.values);
86
+ if (!Object.keys(env).length)
87
+ return entry;
88
+ if (!entry || typeof entry !== 'object')
89
+ return entry;
90
+ const e = entry;
91
+ // Both entry forms — the same split pluginHooks' stampPlugin walks.
92
+ if (Array.isArray(e.hooks)) {
93
+ return {
94
+ ...e,
95
+ hooks: e.hooks.map((h) => {
96
+ if (!h || typeof h !== 'object')
97
+ return h;
98
+ const handler = h;
99
+ const prior = handler.pluginOptions;
100
+ const priors = prior && typeof prior === 'object' && !Array.isArray(prior) ? prior : {};
101
+ return { ...handler, pluginOptions: { ...priors, ...env } };
102
+ }),
103
+ };
104
+ }
105
+ const prior = e.pluginOptions;
106
+ const priors = prior && typeof prior === 'object' && !Array.isArray(prior) ? prior : {};
107
+ return { ...e, pluginOptions: { ...priors, ...env } };
108
+ });
109
+ }
110
+ /** pluginMcpServers + `${user_config.KEY}` substitution (sensitive included). */
111
+ function resolvePluginMcpServers(workDir) {
112
+ const configs = configsByPlugin(workDir);
113
+ if (!configs.size)
114
+ return (0, index_1.pluginMcpServers)(workDir);
115
+ return (0, index_1.pluginMcpServers)(workDir, (cfg, plugin) => {
116
+ const c = configs.get(plugin);
117
+ if (!c)
118
+ return cfg;
119
+ return (0, userConfig_1.substituteUserConfigDeep)(cfg, { options: c.options, values: c.values, includeSensitive: true });
120
+ });
121
+ }
122
+ /**
123
+ * The resolved config of the plugin a skill DIRECTORY belongs to, or null.
124
+ * Used by skills.ts's expandSkill: a skill under `<plugin>/skills/<name>/` gets
125
+ * its body substituted with that plugin's NON-SENSITIVE values.
126
+ */
127
+ function pluginConfigForSkillDir(workDir, skillDir) {
128
+ let dir;
129
+ try {
130
+ dir = path.resolve(skillDir);
131
+ }
132
+ catch {
133
+ return null;
134
+ }
135
+ for (const p of (0, index_1.loadPlugins)(workDir)) {
136
+ if (p.disabled)
137
+ continue;
138
+ const root = path.resolve(p.dir);
139
+ if (!dir.startsWith(root + path.sep))
140
+ continue;
141
+ const { options } = (0, userConfig_1.parseUserConfig)((0, index_1.readPluginManifest)(p.dir));
142
+ if (!options.length)
143
+ return null;
144
+ return { options, values: (0, configStore_1.resolvePluginConfig)(workDir, p.name, options).values };
145
+ }
146
+ return null;
147
+ }
148
+ /** Substitute one text with a plugin's values, non-sensitive only (skill bodies). */
149
+ function substituteSkillBody(text, cfg) {
150
+ return (0, userConfig_1.substituteUserConfig)(text, { options: cfg.options, values: cfg.values, includeSensitive: false }).text;
151
+ }
152
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1,90 @@
1
+ /** Supported value types, matching Claude Code's set. */
2
+ export declare const USER_CONFIG_TYPES: readonly ["string", "number", "boolean", "directory", "file"];
3
+ export type UserConfigType = (typeof USER_CONFIG_TYPES)[number];
4
+ export interface UserConfigOption {
5
+ /** Identifier: letters, digits, underscores; may not start with a digit. */
6
+ key: string;
7
+ type: UserConfigType;
8
+ /** Label shown in the configuration surface. */
9
+ title: string;
10
+ /** Help text: what the value is and what it changes. */
11
+ description: string;
12
+ required?: boolean;
13
+ /** Value used when the user provides nothing. */
14
+ default?: string | number | boolean | string[];
15
+ /** For `string`: the values the field accepts (a picker, not free text). */
16
+ options?: string[];
17
+ /** For `string`: allows an array of strings. */
18
+ multiple?: boolean;
19
+ /** Masks the value and stores it OUT of settings.json (our secrets file). */
20
+ sensitive?: boolean;
21
+ /** For `number`: bounds. */
22
+ min?: number;
23
+ max?: number;
24
+ }
25
+ export interface UserConfigParseResult {
26
+ options: UserConfigOption[];
27
+ /** Schema problems, each naming its key. A plugin with errors has NO usable options. */
28
+ errors: string[];
29
+ }
30
+ /**
31
+ * Parse the manifest's `userConfig` block strictly — Claude Code's rule: an
32
+ * unknown key inside an option is an error. One bad option invalidates the
33
+ * WHOLE block (all-or-nothing): a plugin half-configured by a typo would be
34
+ * worse than one that loudly needs its manifest fixed.
35
+ */
36
+ export declare function parseUserConfig(manifest: Record<string, unknown> | null | undefined): UserConfigParseResult;
37
+ /** Validate one value against its option. Returns an error string, or null when valid. */
38
+ export declare function validateUserConfigValue(option: UserConfigOption, value: unknown): string | null;
39
+ /**
40
+ * Coerce a CLI-supplied STRING into the option's type (values arrive from
41
+ * `--config key=value` / `--values-stdin` where everything is text).
42
+ * `parseNumber` is strict: "12abc" must not become 12.
43
+ */
44
+ export declare function coerceUserConfigValue(option: UserConfigOption, raw: string): {
45
+ ok: true;
46
+ value: unknown;
47
+ } | {
48
+ ok: false;
49
+ error: string;
50
+ };
51
+ /** Every `${user_config.KEY}` reference in a string, unique and in first-seen order. */
52
+ export declare function findUserConfigRefs(text: string): string[];
53
+ export interface SubstituteResult {
54
+ text: string;
55
+ /** Keys that were replaced. */
56
+ substituted: string[];
57
+ /** Sensitive keys skipped because the surface does not accept them. */
58
+ skippedSensitive: string[];
59
+ /** Referenced keys with no resolvable value (the literal token is left in place). */
60
+ unresolved: string[];
61
+ }
62
+ /**
63
+ * Replace `${user_config.KEY}` with resolved values. A reference with no value
64
+ * keeps its literal token — a visible failure the plugin's author can debug,
65
+ * never an empty string masquerading as a value. `includeSensitive: false`
66
+ * (skill bodies) leaves sensitive tokens in place; configStore decides which
67
+ * values may be exposed where.
68
+ */
69
+ export declare function substituteUserConfig(text: string, opts: {
70
+ options: UserConfigOption[];
71
+ values: Record<string, unknown>;
72
+ includeSensitive: boolean;
73
+ }): SubstituteResult;
74
+ /** Deep substitution over an object/array graph (MCP server configs). Strings only. */
75
+ export declare function substituteUserConfigDeep(value: unknown, opts: {
76
+ options: UserConfigOption[];
77
+ values: Record<string, unknown>;
78
+ includeSensitive: boolean;
79
+ }): unknown;
80
+ /** All `${user_config.KEY}` refs found anywhere in a JSON value graph, unique. */
81
+ export declare function collectUserConfigRefsDeep(value: unknown): string[];
82
+ /** Claude Code's naming rule: `CLAUDE_PLUGIN_OPTION_<KEY>` → ours mirrors it. */
83
+ export declare function pluginOptionEnvKey(key: string): string;
84
+ /**
85
+ * The env map a plugin's hook processes receive. Only scalar values can ride
86
+ * an environment variable; an array/object value is skipped (a shell would see
87
+ * "[object Object]" — worse than absent).
88
+ */
89
+ export declare function pluginOptionEnvMap(options: UserConfigOption[], values: Record<string, unknown>): Record<string, string>;
90
+ //# sourceMappingURL=userConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userConfig.d.ts","sourceRoot":"","sources":["../../src/plugins/userConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ // ─── Plugin `userConfig` (S24, Claude Code parity — G24) ─────────────────────
3
+ //
4
+ // A plugin that needs a value from the user (an API endpoint, a token, a log
5
+ // directory) declares it in plugin.json instead of asking the user to edit
6
+ // settings by hand:
7
+ //
8
+ // "userConfig": {
9
+ // "api_token": { "type": "string", "title": "API token",
10
+ // "description": "…", "sensitive": true }
11
+ // }
12
+ //
13
+ // Values are resolved by configStore.ts (settings `pluginConfigs` → secrets
14
+ // file → manifest default) and reach the plugin three ways, matching Claude
15
+ // Code's model:
16
+ //
17
+ // • `${user_config.KEY}` substitution — MCP server configs (sensitive values
18
+ // included) and skill bodies (non-sensitive only; a sensitive reference is
19
+ // left as the literal token rather than shipped into model context);
20
+ // • `NEXRALL_PLUGIN_OPTION_<KEY>` environment variables on every hook
21
+ // process the plugin ships (KEY uppercased) — the ONLY way a shell-form
22
+ // hook can read a value, because a shell-form `command` rejects
23
+ // `${user_config.*}` (the substituted value would be re-parsed by the
24
+ // shell; validate.ts reports the reference as an error);
25
+ // • the `nex plugin configure` command and the install-time
26
+ // `--config key=value` flag, which write values back.
27
+ //
28
+ // This module is PURE (no fs): schema parsing, value validation, substitution
29
+ // and env naming. The store lives in configStore.ts.
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.USER_CONFIG_TYPES = void 0;
32
+ exports.parseUserConfig = parseUserConfig;
33
+ exports.validateUserConfigValue = validateUserConfigValue;
34
+ exports.coerceUserConfigValue = coerceUserConfigValue;
35
+ exports.findUserConfigRefs = findUserConfigRefs;
36
+ exports.substituteUserConfig = substituteUserConfig;
37
+ exports.substituteUserConfigDeep = substituteUserConfigDeep;
38
+ exports.collectUserConfigRefsDeep = collectUserConfigRefsDeep;
39
+ exports.pluginOptionEnvKey = pluginOptionEnvKey;
40
+ exports.pluginOptionEnvMap = pluginOptionEnvMap;
41
+ /** Supported value types, matching Claude Code's set. */
42
+ exports.USER_CONFIG_TYPES = ['string', 'number', 'boolean', 'directory', 'file'];
43
+ const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
44
+ /** Fields an option object may carry. Anything else is an error (strict object). */
45
+ const KNOWN_FIELDS = new Set([
46
+ 'type',
47
+ 'title',
48
+ 'description',
49
+ 'required',
50
+ 'default',
51
+ 'options',
52
+ 'multiple',
53
+ 'sensitive',
54
+ 'min',
55
+ 'max',
56
+ ]);
57
+ /**
58
+ * Parse the manifest's `userConfig` block strictly — Claude Code's rule: an
59
+ * unknown key inside an option is an error. One bad option invalidates the
60
+ * WHOLE block (all-or-nothing): a plugin half-configured by a typo would be
61
+ * worse than one that loudly needs its manifest fixed.
62
+ */
63
+ function parseUserConfig(manifest) {
64
+ const raw = manifest?.userConfig;
65
+ if (raw === undefined)
66
+ return { options: [], errors: [] };
67
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
68
+ return { options: [], errors: ['userConfig must be an object of option definitions'] };
69
+ }
70
+ const options = [];
71
+ const errors = [];
72
+ for (const [key, def] of Object.entries(raw)) {
73
+ const problems = parseOne(key, def);
74
+ if (typeof problems === 'string') {
75
+ errors.push(problems);
76
+ continue;
77
+ }
78
+ options.push(problems);
79
+ }
80
+ if (errors.length)
81
+ return { options: [], errors };
82
+ return { options, errors };
83
+ }
84
+ /** Parse one option; returns the option or its first error. */
85
+ function parseOne(key, def) {
86
+ if (!KEY_RE.test(key))
87
+ return `userConfig "${key}": key must be letters, digits and underscores, not starting with a digit`;
88
+ if (!def || typeof def !== 'object' || Array.isArray(def))
89
+ return `userConfig "${key}": must be an object`;
90
+ const d = def;
91
+ for (const field of Object.keys(d)) {
92
+ if (!KNOWN_FIELDS.has(field))
93
+ return `userConfig "${key}": unknown field "${field}" (allowed: ${[...KNOWN_FIELDS].join(', ')})`;
94
+ }
95
+ const type = typeof d.type === 'string' ? d.type : undefined;
96
+ if (!type || !exports.USER_CONFIG_TYPES.includes(type))
97
+ return `userConfig "${key}": type must be ${exports.USER_CONFIG_TYPES.join(' | ')}`;
98
+ if (typeof d.title !== 'string' || !d.title.trim())
99
+ return `userConfig "${key}": title is required`;
100
+ if (typeof d.description !== 'string' || !d.description.trim())
101
+ return `userConfig "${key}": description is required`;
102
+ for (const boolField of ['required', 'multiple', 'sensitive']) {
103
+ if (d[boolField] !== undefined && typeof d[boolField] !== 'boolean')
104
+ return `userConfig "${key}": ${boolField} must be a boolean`;
105
+ }
106
+ if (d.options !== undefined) {
107
+ if (type !== 'string')
108
+ return `userConfig "${key}": options applies to string fields only (this is ${type})`;
109
+ if (d.multiple === true)
110
+ return `userConfig "${key}": options cannot be combined with multiple`;
111
+ if (d.sensitive === true)
112
+ return `userConfig "${key}": options cannot be combined with sensitive`;
113
+ if (!Array.isArray(d.options) || !d.options.length)
114
+ return `userConfig "${key}": options must be a non-empty array`;
115
+ for (const o of d.options) {
116
+ if (typeof o !== 'string' || !o.trim() || o.length > 64 || o.length < 1)
117
+ return `userConfig "${key}": each option must be a plain label of 1-64 characters`;
118
+ }
119
+ }
120
+ if (d.multiple === true && type !== 'string')
121
+ return `userConfig "${key}": multiple applies to string fields only`;
122
+ if (d.min !== undefined || d.max !== undefined) {
123
+ if (type !== 'number')
124
+ return `userConfig "${key}": min/max apply to number fields only`;
125
+ if (d.min !== undefined && typeof d.min !== 'number')
126
+ return `userConfig "${key}": min must be a number`;
127
+ if (d.max !== undefined && typeof d.max !== 'number')
128
+ return `userConfig "${key}": max must be a number`;
129
+ if (typeof d.min === 'number' && typeof d.max === 'number' && d.min > d.max)
130
+ return `userConfig "${key}": min is greater than max`;
131
+ }
132
+ const option = {
133
+ key,
134
+ type,
135
+ title: d.title,
136
+ description: d.description,
137
+ ...(d.required === true ? { required: true } : {}),
138
+ ...(d.multiple === true ? { multiple: true } : {}),
139
+ ...(d.sensitive === true ? { sensitive: true } : {}),
140
+ ...(Array.isArray(d.options) ? { options: [...d.options] } : {}),
141
+ ...(typeof d.min === 'number' ? { min: d.min } : {}),
142
+ ...(typeof d.max === 'number' ? { max: d.max } : {}),
143
+ };
144
+ if (d.default !== undefined) {
145
+ const bad = validateUserConfigValue(option, d.default);
146
+ if (bad)
147
+ return `userConfig "${key}": default is invalid — ${bad}`;
148
+ option.default = d.default;
149
+ }
150
+ return option;
151
+ }
152
+ /** Validate one value against its option. Returns an error string, or null when valid. */
153
+ function validateUserConfigValue(option, value) {
154
+ if (option.multiple) {
155
+ if (!Array.isArray(value))
156
+ return 'expected an array of strings (multiple)';
157
+ for (const v of value) {
158
+ if (typeof v !== 'string')
159
+ return 'every entry must be a string';
160
+ const bad = validateSingle(option, v);
161
+ if (bad)
162
+ return bad;
163
+ }
164
+ return null;
165
+ }
166
+ return validateSingle(option, value);
167
+ }
168
+ function validateSingle(option, value) {
169
+ switch (option.type) {
170
+ case 'string':
171
+ case 'directory':
172
+ case 'file':
173
+ if (typeof value !== 'string')
174
+ return `expected a ${option.type}`;
175
+ if (option.options && !option.options.includes(value))
176
+ return `must be one of: ${option.options.join(', ')}`;
177
+ return null;
178
+ case 'number': {
179
+ if (typeof value !== 'number' || !Number.isFinite(value))
180
+ return 'expected a number';
181
+ if (option.min !== undefined && value < option.min)
182
+ return `must be at least ${option.min}`;
183
+ if (option.max !== undefined && value > option.max)
184
+ return `must be at most ${option.max}`;
185
+ return null;
186
+ }
187
+ case 'boolean':
188
+ return typeof value === 'boolean' ? null : 'expected true or false';
189
+ }
190
+ }
191
+ /**
192
+ * Coerce a CLI-supplied STRING into the option's type (values arrive from
193
+ * `--config key=value` / `--values-stdin` where everything is text).
194
+ * `parseNumber` is strict: "12abc" must not become 12.
195
+ */
196
+ function coerceUserConfigValue(option, raw) {
197
+ if (option.multiple) {
198
+ // JSON array, or comma-separated.
199
+ const text = raw.trim();
200
+ let arr;
201
+ if (text.startsWith('[')) {
202
+ try {
203
+ arr = JSON.parse(text);
204
+ }
205
+ catch (e) {
206
+ return { ok: false, error: `could not parse as a JSON array: ${e.message}` };
207
+ }
208
+ }
209
+ else {
210
+ arr = text ? text.split(',').map((s) => s.trim()) : [];
211
+ }
212
+ const bad = validateUserConfigValue(option, arr);
213
+ return bad ? { ok: false, error: bad } : { ok: true, value: arr };
214
+ }
215
+ switch (option.type) {
216
+ case 'string':
217
+ case 'directory':
218
+ case 'file': {
219
+ const bad = validateUserConfigValue(option, raw);
220
+ return bad ? { ok: false, error: bad } : { ok: true, value: raw };
221
+ }
222
+ case 'number': {
223
+ const t = raw.trim();
224
+ const n = t === '' ? NaN : Number(t);
225
+ if (!Number.isFinite(n))
226
+ return { ok: false, error: `"${raw}" is not a number` };
227
+ const bad = validateUserConfigValue(option, n);
228
+ return bad ? { ok: false, error: bad } : { ok: true, value: n };
229
+ }
230
+ case 'boolean': {
231
+ const t = raw.trim().toLowerCase();
232
+ if (t === 'true' || t === '1' || t === 'yes')
233
+ return { ok: true, value: true };
234
+ if (t === 'false' || t === '0' || t === 'no')
235
+ return { ok: true, value: false };
236
+ return { ok: false, error: `"${raw}" is not a boolean (true/false)` };
237
+ }
238
+ }
239
+ }
240
+ // ─── Substitution ────────────────────────────────────────────────────────────
241
+ const REF_RE = /\$\{user_config\.([A-Za-z_][A-Za-z0-9_]*)\}/g;
242
+ /** Every `${user_config.KEY}` reference in a string, unique and in first-seen order. */
243
+ function findUserConfigRefs(text) {
244
+ const out = new Set();
245
+ for (const m of text.matchAll(REF_RE))
246
+ out.add(m[1]);
247
+ return [...out];
248
+ }
249
+ /**
250
+ * Replace `${user_config.KEY}` with resolved values. A reference with no value
251
+ * keeps its literal token — a visible failure the plugin's author can debug,
252
+ * never an empty string masquerading as a value. `includeSensitive: false`
253
+ * (skill bodies) leaves sensitive tokens in place; configStore decides which
254
+ * values may be exposed where.
255
+ */
256
+ function substituteUserConfig(text, opts) {
257
+ const result = { text, substituted: [], skippedSensitive: [], unresolved: [] };
258
+ const byKey = new Map(opts.options.map((o) => [o.key, o]));
259
+ result.text = text.replace(REF_RE, (whole, key) => {
260
+ const option = byKey.get(key);
261
+ if (!option) {
262
+ result.unresolved.push(key);
263
+ return whole; // undeclared reference: validate.ts reports it; runtime keeps it visible
264
+ }
265
+ if (option.sensitive && !opts.includeSensitive) {
266
+ result.skippedSensitive.push(key);
267
+ return whole;
268
+ }
269
+ const v = opts.values[key];
270
+ if (v === undefined) {
271
+ result.unresolved.push(key);
272
+ return whole;
273
+ }
274
+ if (!result.substituted.includes(key))
275
+ result.substituted.push(key);
276
+ return Array.isArray(v) ? v.join(', ') : String(v);
277
+ });
278
+ return result;
279
+ }
280
+ /** Deep substitution over an object/array graph (MCP server configs). Strings only. */
281
+ function substituteUserConfigDeep(value, opts) {
282
+ if (typeof value === 'string')
283
+ return substituteUserConfig(value, opts).text;
284
+ if (Array.isArray(value))
285
+ return value.map((v) => substituteUserConfigDeep(v, opts));
286
+ if (value && typeof value === 'object') {
287
+ const out = {};
288
+ for (const [k, v] of Object.entries(value)) {
289
+ out[k] = substituteUserConfigDeep(v, opts);
290
+ }
291
+ return out;
292
+ }
293
+ return value;
294
+ }
295
+ /** All `${user_config.KEY}` refs found anywhere in a JSON value graph, unique. */
296
+ function collectUserConfigRefsDeep(value) {
297
+ const out = new Set();
298
+ const walk = (v) => {
299
+ if (typeof v === 'string')
300
+ for (const k of findUserConfigRefs(v))
301
+ out.add(k);
302
+ else if (Array.isArray(v))
303
+ v.forEach(walk);
304
+ else if (v && typeof v === 'object')
305
+ Object.values(v).forEach(walk);
306
+ };
307
+ walk(value);
308
+ return [...out];
309
+ }
310
+ // ─── Environment ─────────────────────────────────────────────────────────────
311
+ /** Claude Code's naming rule: `CLAUDE_PLUGIN_OPTION_<KEY>` → ours mirrors it. */
312
+ function pluginOptionEnvKey(key) {
313
+ return `NEXRALL_PLUGIN_OPTION_${key.toUpperCase()}`;
314
+ }
315
+ /**
316
+ * The env map a plugin's hook processes receive. Only scalar values can ride
317
+ * an environment variable; an array/object value is skipped (a shell would see
318
+ * "[object Object]" — worse than absent).
319
+ */
320
+ function pluginOptionEnvMap(options, values) {
321
+ const env = {};
322
+ for (const o of options) {
323
+ const v = values[o.key];
324
+ if (v === undefined)
325
+ continue;
326
+ if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
327
+ env[pluginOptionEnvKey(o.key)] = String(v);
328
+ }
329
+ }
330
+ return env;
331
+ }
332
+ //# sourceMappingURL=userConfig.js.map
@@ -0,0 +1,14 @@
1
+ import { type UserConfigOption } from './userConfig';
2
+ export interface PluginValidateReport {
3
+ ok: boolean;
4
+ dir: string;
5
+ /** Which manifest file was found (null when none). */
6
+ manifest: string | null;
7
+ userConfig: UserConfigOption[];
8
+ errors: string[];
9
+ warnings: string[];
10
+ }
11
+ export declare function validatePluginDir(dir: string): PluginValidateReport;
12
+ /** One-line human summary lines for the CLI printer (warnings then errors). */
13
+ export declare function formatValidateReport(r: PluginValidateReport): string[];
14
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/plugins/validate.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAkE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAsBD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CA6HnE;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAUtE"}