@tenonhq/dovetail-core 0.0.85

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 (78) hide show
  1. package/README.md +6 -0
  2. package/dist/FileLogger.js +207 -0
  3. package/dist/FileUtils.js +387 -0
  4. package/dist/Logger.js +88 -0
  5. package/dist/MultiScopeWatcher.js +615 -0
  6. package/dist/PluginManager.js +125 -0
  7. package/dist/_defaultOptionsOLD.js +44 -0
  8. package/dist/allScopesCommands.js +418 -0
  9. package/dist/appUtils.js +929 -0
  10. package/dist/benchmark.js +104 -0
  11. package/dist/bootstrap.js +62 -0
  12. package/dist/claudeCommand.js +60 -0
  13. package/dist/clickupCommands.js +472 -0
  14. package/dist/clickupPushHelper.js +33 -0
  15. package/dist/commander.js +466 -0
  16. package/dist/commands.js +378 -0
  17. package/dist/config.js +697 -0
  18. package/dist/constants.js +15 -0
  19. package/dist/createRecordCommand.js +314 -0
  20. package/dist/dashboardCommand.js +38 -0
  21. package/dist/defaultOptions.js +14 -0
  22. package/dist/deleteRecordCommand.js +282 -0
  23. package/dist/flowDesigner/values.js +55 -0
  24. package/dist/genericUtils.js +189 -0
  25. package/dist/gitUtils.js +99 -0
  26. package/dist/index.js +35 -0
  27. package/dist/initSystem/corePlugin.js +344 -0
  28. package/dist/initSystem/discovery.js +107 -0
  29. package/dist/initSystem/orchestrator.js +408 -0
  30. package/dist/logMessages.js +82 -0
  31. package/dist/loginCommand.js +20 -0
  32. package/dist/migrateCommand.js +123 -0
  33. package/dist/projectFiles.js +32 -0
  34. package/dist/recentEdits.js +66 -0
  35. package/dist/schemaCommand.js +85 -0
  36. package/dist/snClient.js +519 -0
  37. package/dist/tests/benchmarkRefresh.test.js +265 -0
  38. package/dist/tests/clickupCommands.test.js +384 -0
  39. package/dist/tests/clickupPushHelper.test.js +124 -0
  40. package/dist/tests/configFallback.test.js +68 -0
  41. package/dist/tests/cred.test.js +59 -0
  42. package/dist/tests/discovery.test.js +67 -0
  43. package/dist/tests/ensureUpdateSetWarnings.test.js +218 -0
  44. package/dist/tests/errorLogLevels.test.js +273 -0
  45. package/dist/tests/example.test.js +6 -0
  46. package/dist/tests/fileContextSkipReason.test.js +116 -0
  47. package/dist/tests/globalDebounce.test.js +308 -0
  48. package/dist/tests/migrateCommand.test.js +113 -0
  49. package/dist/tests/multi-scope-watcher.test.js +558 -0
  50. package/dist/tests/orchestrator.test.js +23 -0
  51. package/dist/tests/pushFiles.test.js +248 -0
  52. package/dist/tests/rateLimitCoordination.test.js +271 -0
  53. package/dist/tests/resolveConfigReadOnly.test.js +67 -0
  54. package/dist/tests/retryOnHttpErr.test.js +154 -0
  55. package/dist/tests/scopeCaching.test.js +115 -0
  56. package/dist/tests/serializeUpdateSetConfig.test.js +325 -0
  57. package/dist/tests/snClient.fallback.test.js +120 -0
  58. package/dist/tests/syncManifestRefresh.test.js +287 -0
  59. package/dist/tests/syncManifestWhitelist.test.js +192 -0
  60. package/dist/tests/taskClear.test.js +170 -0
  61. package/dist/tests/taskStaleness.test.js +220 -0
  62. package/dist/tests/unwrapSNResponseError.test.js +145 -0
  63. package/dist/tests/v2Values.test.js +79 -0
  64. package/dist/tests/validateTaskId.test.js +304 -0
  65. package/dist/tests/verifyUpdateSetSwitch.test.js +277 -0
  66. package/dist/tests/writeEnvVar.test.js +119 -0
  67. package/dist/updateSetCommands.js +702 -0
  68. package/dist/wizard.js +40 -0
  69. package/package.json +69 -0
  70. package/skills/dove-configure-pipeline.md +158 -0
  71. package/skills/dove-create-plugin.md +174 -0
  72. package/skills/dove-create-record.md +135 -0
  73. package/skills/dove-debug-build.md +117 -0
  74. package/skills/dove-delete-record.md +104 -0
  75. package/skills/dove-manage-tables.md +174 -0
  76. package/skills/dove-manage-update-sets.md +88 -0
  77. package/skills/dove-setup-project.md +154 -0
  78. package/skills/dove-troubleshoot-sync.md +113 -0
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.corePlugin = void 0;
40
+ exports.validateCoreLogin = validateCoreLogin;
41
+ exports.normalizeInstance = normalizeInstance;
42
+ const snClient_1 = require("../snClient");
43
+ const Logger_1 = require("../Logger");
44
+ const ConfigManager = __importStar(require("../config"));
45
+ const allScopesCommands_1 = require("../allScopesCommands");
46
+ const inquirer_1 = __importDefault(require("inquirer"));
47
+ const chalk_1 = __importDefault(require("chalk"));
48
+ const fs_1 = __importDefault(require("fs"));
49
+ const path_1 = __importDefault(require("path"));
50
+ // Read whichever project config exists (dove.config.js preferred, sinc.config.js as legacy fallback).
51
+ // Returns the parsed module or null if neither exists / cannot be required.
52
+ function readExistingProjectConfig(rootDir) {
53
+ var names = ["dove.config.js", "sinc.config.js"];
54
+ for (var i = 0; i < names.length; i++) {
55
+ var p = path_1.default.join(rootDir, names[i]);
56
+ if (fs_1.default.existsSync(p)) {
57
+ try {
58
+ return require(p);
59
+ }
60
+ catch (e) {
61
+ return null;
62
+ }
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ /**
68
+ * @description Core init plugin — handles ServiceNow authentication, app selection, and file download.
69
+ * This plugin is always included in `dove init` and `dove login`.
70
+ */
71
+ exports.corePlugin = {
72
+ name: "core",
73
+ displayName: "ServiceNow",
74
+ description: "Connect to a ServiceNow instance and sync application files",
75
+ login: [
76
+ {
77
+ envKey: "SN_INSTANCE",
78
+ prompt: {
79
+ type: "input",
80
+ message: "ServiceNow instance (e.g. mycompany.service-now.com):",
81
+ },
82
+ required: true,
83
+ },
84
+ {
85
+ envKey: "SN_USER",
86
+ prompt: {
87
+ type: "input",
88
+ message: "Username:",
89
+ },
90
+ required: true,
91
+ },
92
+ {
93
+ envKey: "SN_PASSWORD",
94
+ prompt: {
95
+ type: "password",
96
+ message: "Password:",
97
+ mask: "*",
98
+ },
99
+ required: true,
100
+ },
101
+ ],
102
+ configure: [
103
+ {
104
+ key: "apps",
105
+ label: "Selecting ServiceNow applications",
106
+ run: async (context) => {
107
+ var baseUrl = instanceBaseUrl(context.env.SN_INSTANCE);
108
+ var client = (0, snClient_1.snClient)(baseUrl, context.env.SN_USER, context.env.SN_PASSWORD);
109
+ Logger_1.logger.info("Fetching application list...");
110
+ var apps = await (0, snClient_1.unwrapSNResponse)(client.getAppList());
111
+ if (apps.length === 0) {
112
+ Logger_1.logger.warn("No applications found on this instance.");
113
+ return null;
114
+ }
115
+ // Pre-check scopes that exist in the current config
116
+ var existingScopes = new Set();
117
+ if (context.hasConfig) {
118
+ var existingConfig = readExistingProjectConfig(context.rootDir);
119
+ if (existingConfig && existingConfig.scopes) {
120
+ Object.keys(existingConfig.scopes).forEach(function (s) {
121
+ existingScopes.add(s);
122
+ });
123
+ }
124
+ }
125
+ var choices = apps.map(function (app) {
126
+ return {
127
+ name: app.displayName + " (" + app.scope + ")",
128
+ value: app.scope,
129
+ short: app.displayName,
130
+ checked: existingScopes.has(app.scope),
131
+ };
132
+ });
133
+ var answer = await inquirer_1.default.prompt([{
134
+ type: "checkbox",
135
+ name: "apps",
136
+ message: "Which apps would you like to work with? (space to select, enter to confirm)",
137
+ choices: choices,
138
+ validate: function (input) {
139
+ if (input.length === 0)
140
+ return "Select at least one application.";
141
+ return true;
142
+ },
143
+ }]);
144
+ var selectedScopes = answer.apps;
145
+ context.answers.selectedScopes = selectedScopes;
146
+ context.answers.selectedScope = selectedScopes[0]; // backward compat
147
+ context.answers.apps = apps;
148
+ // Prompt for source directory per selected scope
149
+ var scopeDirectories = {};
150
+ Logger_1.logger.info("");
151
+ Logger_1.logger.info(chalk_1.default.bold(" Source directories:"));
152
+ for (var i = 0; i < selectedScopes.length; i++) {
153
+ var scope = selectedScopes[i];
154
+ var app = apps.find(function (a) { return a.scope === scope; });
155
+ var displayName = app ? app.displayName : scope;
156
+ // Check if existing config already has a sourceDirectory for this scope
157
+ var existingDir = "";
158
+ if (existingScopes.has(scope)) {
159
+ var existingCfg = readExistingProjectConfig(context.rootDir);
160
+ var cfgScopes = existingCfg && existingCfg.scopes;
161
+ if (cfgScopes && cfgScopes[scope] && cfgScopes[scope].sourceDirectory) {
162
+ existingDir = cfgScopes[scope].sourceDirectory;
163
+ }
164
+ }
165
+ // Suggest a friendly name from displayName, or use existing
166
+ var suggestedDir = existingDir || ("src/" + displayName.replace(/\s+/g, ""));
167
+ var dirAnswer = await inquirer_1.default.prompt([{
168
+ type: "input",
169
+ name: "dir",
170
+ message: scope + ":",
171
+ default: suggestedDir,
172
+ }]);
173
+ scopeDirectories[scope] = dirAnswer.dir;
174
+ }
175
+ context.answers.scopeDirectories = scopeDirectories;
176
+ return selectedScopes;
177
+ },
178
+ },
179
+ ],
180
+ initialize: async (context) => {
181
+ var selectedScopes = context.answers.selectedScopes || [];
182
+ if (selectedScopes.length === 0) {
183
+ // Backward compat: single scope from old flow
184
+ if (context.answers.selectedScope) {
185
+ selectedScopes = [context.answers.selectedScope];
186
+ }
187
+ else {
188
+ Logger_1.logger.warn("No applications selected — skipping initialization.");
189
+ return;
190
+ }
191
+ }
192
+ var rootDir = context.rootDir;
193
+ var doveConfigPath = path_1.default.join(rootDir, "dove.config.js");
194
+ var sincConfigPath = path_1.default.join(rootDir, "sinc.config.js");
195
+ var scopeDirectories = context.answers.scopeDirectories || {};
196
+ // Write or preserve config. New configs are always written as dove.config.js;
197
+ // existing sinc.config.js files are honored as legacy until the user runs `dove migrate`.
198
+ var configAction = context.answers.configAction || "keep";
199
+ var existingConfigPath = null;
200
+ if (fs_1.default.existsSync(doveConfigPath))
201
+ existingConfigPath = doveConfigPath;
202
+ else if (fs_1.default.existsSync(sincConfigPath))
203
+ existingConfigPath = sincConfigPath;
204
+ if (!existingConfigPath || configAction === "replace") {
205
+ Logger_1.logger.info("Generating dove.config.js...");
206
+ var scopeEntries = selectedScopes.map(function (scope) {
207
+ return {
208
+ scope: scope,
209
+ sourceDirectory: scopeDirectories[scope] || ("src/" + scope),
210
+ };
211
+ });
212
+ fs_1.default.writeFileSync(doveConfigPath, ConfigManager.generateConfigFile({ scopes: scopeEntries }), "utf8");
213
+ Logger_1.logger.success(chalk_1.default.green("✓ Generated dove.config.js with " + selectedScopes.length + " scope(s)"));
214
+ if (existingConfigPath === sincConfigPath) {
215
+ Logger_1.logger.warn("Legacy sinc.config.js still present alongside new dove.config.js. Run 'dove migrate' or delete sinc.config.js once you're satisfied.");
216
+ }
217
+ }
218
+ else {
219
+ var configFileName = path_1.default.basename(existingConfigPath);
220
+ Logger_1.logger.info(configFileName + " already exists — preserving configuration.");
221
+ }
222
+ // Reload configs so ConfigManager picks up the new/existing config
223
+ try {
224
+ await ConfigManager.loadConfigs();
225
+ }
226
+ catch (e) {
227
+ Logger_1.logger.warn("Config reload incomplete — this is expected during first-time init.");
228
+ }
229
+ // Check which scopes already have manifests
230
+ var scopesWithManifests = [];
231
+ var scopesToDownload = [];
232
+ for (var i = 0; i < selectedScopes.length; i++) {
233
+ var scope = selectedScopes[i];
234
+ var doveManifestPath = path_1.default.join(rootDir, "dove.manifest." + scope + ".json");
235
+ var sincManifestPath = path_1.default.join(rootDir, "dove.manifest." + scope + ".json");
236
+ var hasManifest = fs_1.default.existsSync(doveManifestPath) || fs_1.default.existsSync(sincManifestPath);
237
+ if (hasManifest) {
238
+ scopesWithManifests.push(scope);
239
+ }
240
+ else {
241
+ scopesToDownload.push(scope);
242
+ }
243
+ }
244
+ // Batch prompt for scopes that already have manifests
245
+ if (scopesWithManifests.length > 0) {
246
+ var redownload = await inquirer_1.default.prompt([{
247
+ type: "confirm",
248
+ name: "confirmed",
249
+ message: scopesWithManifests.length + " scope(s) already have manifests (" + scopesWithManifests.join(", ") + "). Re-download?",
250
+ default: false,
251
+ }]);
252
+ if (redownload.confirmed) {
253
+ scopesToDownload = scopesToDownload.concat(scopesWithManifests);
254
+ }
255
+ }
256
+ if (scopesToDownload.length === 0) {
257
+ Logger_1.logger.info("No scopes to download — all manifests up to date.");
258
+ return;
259
+ }
260
+ // Download all scopes using the battle-tested processScope pipeline
261
+ Logger_1.logger.info("Downloading " + scopesToDownload.length + " scope(s)...");
262
+ var config = ConfigManager.getConfig();
263
+ var scopePromises = scopesToDownload.map(function (scopeName) {
264
+ var scopeConfig = (config.scopes && config.scopes[scopeName]) || {};
265
+ return (0, allScopesCommands_1.processScope)(scopeName, scopeConfig, 0);
266
+ });
267
+ var results = await Promise.allSettled(scopePromises);
268
+ // Write per-scope manifest files and tally results
269
+ var successCount = 0;
270
+ var failCount = 0;
271
+ for (var r = 0; r < results.length; r++) {
272
+ var result = results[r];
273
+ var scopeName = scopesToDownload[r];
274
+ if (result.status === "fulfilled" && result.value.success) {
275
+ successCount++;
276
+ // Write per-scope manifest
277
+ if (result.value.manifest) {
278
+ var scopeManifestPath = ConfigManager.getScopeManifestPath(scopeName);
279
+ fs_1.default.writeFileSync(scopeManifestPath, JSON.stringify(result.value.manifest, null, 2), "utf8");
280
+ }
281
+ }
282
+ else {
283
+ failCount++;
284
+ var error = result.status === "rejected" ? result.reason : (result.value && result.value.error);
285
+ Logger_1.logger.error("Failed to initialize " + scopeName + ": " + (error && error.message ? error.message : "Unknown error"));
286
+ }
287
+ }
288
+ // Summary
289
+ Logger_1.logger.info("");
290
+ if (failCount === 0) {
291
+ Logger_1.logger.success(chalk_1.default.green("✓ ServiceNow configured — " + successCount + " scope(s) initialized"));
292
+ }
293
+ else {
294
+ Logger_1.logger.warn(successCount + "/" + scopesToDownload.length + " scopes initialized, " + failCount + " failed");
295
+ }
296
+ },
297
+ };
298
+ /**
299
+ * @description Validates ServiceNow credentials by testing the connection.
300
+ * Called by the orchestrator after all core login hooks are collected.
301
+ */
302
+ async function validateCoreLogin(context) {
303
+ const instance = context.env.SN_INSTANCE;
304
+ const user = context.env.SN_USER;
305
+ const password = context.env.SN_PASSWORD;
306
+ if (!instance || !user || !password) {
307
+ return "Missing required credentials";
308
+ }
309
+ const instanceUrl = normalizeInstance(instance);
310
+ const baseUrl = instanceBaseUrl(instance);
311
+ try {
312
+ const client = (0, snClient_1.snClient)(baseUrl, user, password);
313
+ await (0, snClient_1.unwrapSNResponse)(client.getAppList());
314
+ context.env.SN_INSTANCE = instanceUrl;
315
+ return true;
316
+ }
317
+ catch (e) {
318
+ const msg = e instanceof Error ? e.message : String(e);
319
+ const status = e && e.response && e.response.status;
320
+ if (msg.includes("Invalid URL") || msg.includes("ENOTFOUND") || msg.includes("getaddrinfo")) {
321
+ return "Instance not found — check the URL (got: " + instanceUrl + ")";
322
+ }
323
+ if (status === 401 || msg.includes("401")) {
324
+ return "Invalid username or password.";
325
+ }
326
+ if (status === 403 || msg.includes("403")) {
327
+ return "Access denied — user may lack required roles.";
328
+ }
329
+ if (msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("ECONNRESET")) {
330
+ return "Could not reach " + instanceUrl + " — check network connectivity.";
331
+ }
332
+ return "Connection failed: " + msg;
333
+ }
334
+ }
335
+ function normalizeInstance(instance) {
336
+ let url = instance.trim().replace("https://", "").replace("http://", "");
337
+ if (url.endsWith("/")) {
338
+ url = url.slice(0, -1);
339
+ }
340
+ return url;
341
+ }
342
+ function instanceBaseUrl(instance) {
343
+ return "https://" + normalizeInstance(instance) + "/";
344
+ }
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.discoverPlugins = discoverPlugins;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const Logger_1 = require("../Logger");
40
+ // Skip packages that are not init plugins:
41
+ // - core/types: the discovery system itself
42
+ // - dashboard: starts Express server on require (side effect)
43
+ // - schema: library imported directly by schemaCommand, not a plugin
44
+ // Both new (dovetail-*) and legacy (sincronia-*) names are listed so the deprecation
45
+ // shim packages on npm are also skipped during the rebrand transition window.
46
+ const SKIP_PACKAGES = new Set([
47
+ "dovetail-core",
48
+ "dovetail-types",
49
+ "dovetail-dashboard",
50
+ "dovetail-schema",
51
+ "sincronia-core",
52
+ "sincronia-types",
53
+ "sincronia-dashboard",
54
+ "sincronia-schema",
55
+ ]);
56
+ const PLUGIN_PACKAGE_PREFIXES = ["dovetail-", "sincronia-"];
57
+ const MAX_PARENT_DEPTH = 3;
58
+ /**
59
+ * @description Scans node_modules for @tenonhq/dovetail-* (and legacy @tenonhq/sincronia-*) packages that export a sincPlugin.
60
+ * @returns {Sinc.InitPlugin[]} Array of discovered init plugins.
61
+ */
62
+ function discoverPlugins() {
63
+ const plugins = [];
64
+ const seen = new Set();
65
+ const searchPaths = [
66
+ path.resolve(process.cwd(), "node_modules", "@tenonhq"),
67
+ path.resolve(__dirname, "..", "..", "..", "@tenonhq"),
68
+ ];
69
+ // Check parent directories for monorepo hoisted node_modules (capped depth)
70
+ let current = process.cwd();
71
+ for (let depth = 0; depth < MAX_PARENT_DEPTH; depth++) {
72
+ const parent = path.dirname(current);
73
+ if (parent === current)
74
+ break;
75
+ const hoisted = path.join(parent, "node_modules", "@tenonhq");
76
+ if (!searchPaths.includes(hoisted)) {
77
+ searchPaths.push(hoisted);
78
+ }
79
+ current = parent;
80
+ }
81
+ for (const searchPath of searchPaths) {
82
+ let dirs;
83
+ try {
84
+ dirs = fs.readdirSync(searchPath);
85
+ }
86
+ catch (e) {
87
+ continue;
88
+ }
89
+ dirs
90
+ .filter(name => PLUGIN_PACKAGE_PREFIXES.some(p => name.startsWith(p)) && !SKIP_PACKAGES.has(name) && !seen.has(name))
91
+ .forEach(dirName => {
92
+ seen.add(dirName);
93
+ try {
94
+ const pkg = require("@tenonhq/" + dirName);
95
+ if (pkg && pkg.sincPlugin && pkg.sincPlugin.name && pkg.sincPlugin.displayName) {
96
+ plugins.push(pkg.sincPlugin);
97
+ Logger_1.logger.debug("Discovered init plugin: " + pkg.sincPlugin.displayName + " (" + dirName + ")");
98
+ }
99
+ }
100
+ catch (e) {
101
+ const message = e instanceof Error ? e.message : String(e);
102
+ Logger_1.logger.warn("Failed to load plugin " + dirName + ": " + message);
103
+ }
104
+ });
105
+ }
106
+ return plugins;
107
+ }