@powerformer/refly-cli 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/refly.js CHANGED
@@ -34,10 +34,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  ));
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
- // ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.12.14_@swc+helpers@0.5.17__jiti@2.4.2_postcss@8.5.6_tsx@4.20.6_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js
37
+ // ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.12.14_@swc+helpers@0.5.17__jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js
38
38
  var getImportMetaUrl, importMetaUrl;
39
39
  var init_cjs_shims = __esm({
40
- "../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.12.14_@swc+helpers@0.5.17__jiti@2.4.2_postcss@8.5.6_tsx@4.20.6_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js"() {
40
+ "../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.12.14_@swc+helpers@0.5.17__jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js"() {
41
41
  "use strict";
42
42
  getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
43
43
  importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
@@ -989,8 +989,8 @@ var require_command = __commonJS({
989
989
  init_cjs_shims();
990
990
  var EventEmitter = require("events").EventEmitter;
991
991
  var childProcess2 = require("child_process");
992
- var path16 = require("path");
993
- var fs20 = require("fs");
992
+ var path22 = require("path");
993
+ var fs27 = require("fs");
994
994
  var process8 = require("process");
995
995
  var { Argument: Argument2, humanReadableArgName } = require_argument();
996
996
  var { CommanderError: CommanderError2 } = require_error();
@@ -1922,11 +1922,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1922
1922
  let launchWithNode = false;
1923
1923
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1924
1924
  function findFile(baseDir, baseName) {
1925
- const localBin = path16.resolve(baseDir, baseName);
1926
- if (fs20.existsSync(localBin)) return localBin;
1927
- if (sourceExt.includes(path16.extname(baseName))) return void 0;
1925
+ const localBin = path22.resolve(baseDir, baseName);
1926
+ if (fs27.existsSync(localBin)) return localBin;
1927
+ if (sourceExt.includes(path22.extname(baseName))) return void 0;
1928
1928
  const foundExt = sourceExt.find(
1929
- (ext) => fs20.existsSync(`${localBin}${ext}`)
1929
+ (ext) => fs27.existsSync(`${localBin}${ext}`)
1930
1930
  );
1931
1931
  if (foundExt) return `${localBin}${foundExt}`;
1932
1932
  return void 0;
@@ -1938,21 +1938,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1938
1938
  if (this._scriptPath) {
1939
1939
  let resolvedScriptPath;
1940
1940
  try {
1941
- resolvedScriptPath = fs20.realpathSync(this._scriptPath);
1941
+ resolvedScriptPath = fs27.realpathSync(this._scriptPath);
1942
1942
  } catch (err) {
1943
1943
  resolvedScriptPath = this._scriptPath;
1944
1944
  }
1945
- executableDir = path16.resolve(
1946
- path16.dirname(resolvedScriptPath),
1945
+ executableDir = path22.resolve(
1946
+ path22.dirname(resolvedScriptPath),
1947
1947
  executableDir
1948
1948
  );
1949
1949
  }
1950
1950
  if (executableDir) {
1951
1951
  let localFile = findFile(executableDir, executableFile);
1952
1952
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1953
- const legacyName = path16.basename(
1953
+ const legacyName = path22.basename(
1954
1954
  this._scriptPath,
1955
- path16.extname(this._scriptPath)
1955
+ path22.extname(this._scriptPath)
1956
1956
  );
1957
1957
  if (legacyName !== this._name) {
1958
1958
  localFile = findFile(
@@ -1963,7 +1963,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1963
1963
  }
1964
1964
  executableFile = localFile || executableFile;
1965
1965
  }
1966
- launchWithNode = sourceExt.includes(path16.extname(executableFile));
1966
+ launchWithNode = sourceExt.includes(path22.extname(executableFile));
1967
1967
  let proc;
1968
1968
  if (process8.platform !== "win32") {
1969
1969
  if (launchWithNode) {
@@ -2803,7 +2803,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2803
2803
  * @return {Command}
2804
2804
  */
2805
2805
  nameFromFilename(filename) {
2806
- this._name = path16.basename(filename, path16.extname(filename));
2806
+ this._name = path22.basename(filename, path22.extname(filename));
2807
2807
  return this;
2808
2808
  }
2809
2809
  /**
@@ -2817,9 +2817,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2817
2817
  * @param {string} [path]
2818
2818
  * @return {(string|null|Command)}
2819
2819
  */
2820
- executableDir(path17) {
2821
- if (path17 === void 0) return this._executableDir;
2822
- this._executableDir = path17;
2820
+ executableDir(path23) {
2821
+ if (path23 === void 0) return this._executableDir;
2822
+ this._executableDir = path23;
2823
2823
  return this;
2824
2824
  }
2825
2825
  /**
@@ -3055,15 +3055,22 @@ var require_commander = __commonJS({
3055
3055
  var paths_exports = {};
3056
3056
  __export(paths_exports, {
3057
3057
  claudeDirectoriesExist: () => claudeDirectoriesExist,
3058
+ ensureClaudeSkillsDir: () => ensureClaudeSkillsDir,
3058
3059
  ensureDir: () => ensureDir,
3060
+ ensureReflySkillsDir: () => ensureReflySkillsDir,
3059
3061
  ensureSkillsDir: () => ensureSkillsDir,
3060
3062
  getCacheDir: () => getCacheDir,
3061
3063
  getClaudeCommandsDir: () => getClaudeCommandsDir,
3062
3064
  getClaudeSkillDir: () => getClaudeSkillDir,
3065
+ getClaudeSkillSymlinkPath: () => getClaudeSkillSymlinkPath,
3066
+ getClaudeSkillsDir: () => getClaudeSkillsDir,
3063
3067
  getCliVersion: () => getCliVersion,
3064
3068
  getConfigPath: () => getConfigPath,
3065
3069
  getLegacyBuilderDir: () => getLegacyBuilderDir,
3070
+ getReflyBaseSkillDir: () => getReflyBaseSkillDir,
3066
3071
  getReflyDir: () => getReflyDir,
3072
+ getReflyDomainSkillDir: () => getReflyDomainSkillDir,
3073
+ getReflySkillsDir: () => getReflySkillsDir,
3067
3074
  getSkillsDir: () => getSkillsDir
3068
3075
  });
3069
3076
  function getCliVersion() {
@@ -3119,6 +3126,33 @@ async function ensureSkillsDir() {
3119
3126
  fs.mkdirSync(dir, { recursive: true, mode: 448 });
3120
3127
  }
3121
3128
  }
3129
+ function getReflySkillsDir() {
3130
+ return path.join(getReflyDir(), "skills");
3131
+ }
3132
+ function getReflyBaseSkillDir() {
3133
+ return path.join(getReflySkillsDir(), "base");
3134
+ }
3135
+ function getReflyDomainSkillDir(skillName) {
3136
+ return path.join(getReflySkillsDir(), skillName);
3137
+ }
3138
+ function getClaudeSkillsDir() {
3139
+ return path.join(os.homedir(), ".claude", "skills");
3140
+ }
3141
+ function getClaudeSkillSymlinkPath(skillName) {
3142
+ return path.join(getClaudeSkillsDir(), skillName);
3143
+ }
3144
+ function ensureReflySkillsDir() {
3145
+ const dir = getReflySkillsDir();
3146
+ if (!fs.existsSync(dir)) {
3147
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
3148
+ }
3149
+ }
3150
+ function ensureClaudeSkillsDir() {
3151
+ const dir = getClaudeSkillsDir();
3152
+ if (!fs.existsSync(dir)) {
3153
+ fs.mkdirSync(dir, { recursive: true, mode: 493 });
3154
+ }
3155
+ }
3122
3156
  var os, path, fs;
3123
3157
  var init_paths = __esm({
3124
3158
  "src/config/paths.ts"() {
@@ -3226,6 +3260,356 @@ var init_logger = __esm({
3226
3260
  }
3227
3261
  });
3228
3262
 
3263
+ // src/skill/symlink.ts
3264
+ var symlink_exports = {};
3265
+ __export(symlink_exports, {
3266
+ createReflySkillWithSymlink: () => createReflySkillWithSymlink,
3267
+ createSkillSymlink: () => createSkillSymlink,
3268
+ deleteDomainSkillWithSymlink: () => deleteDomainSkillWithSymlink,
3269
+ generateReflySkillMd: () => generateReflySkillMd,
3270
+ initializeBaseSkillSymlink: () => initializeBaseSkillSymlink,
3271
+ isSkillSymlinkValid: () => isSkillSymlinkValid,
3272
+ listSkillSymlinks: () => listSkillSymlinks,
3273
+ parseReflySkillMd: () => parseReflySkillMd,
3274
+ removeSkillSymlink: () => removeSkillSymlink
3275
+ });
3276
+ function createSkillSymlink(skillName) {
3277
+ const reflyPath = skillName === "refly" ? getReflyBaseSkillDir() : getReflyDomainSkillDir(skillName);
3278
+ const claudePath = getClaudeSkillSymlinkPath(skillName);
3279
+ try {
3280
+ ensureReflySkillsDir();
3281
+ ensureClaudeSkillsDir();
3282
+ if (!fs4.existsSync(reflyPath)) {
3283
+ return {
3284
+ success: false,
3285
+ skillName,
3286
+ reflyPath,
3287
+ claudePath,
3288
+ error: `Source skill directory does not exist: ${reflyPath}`
3289
+ };
3290
+ }
3291
+ if (fs4.existsSync(claudePath) || fs4.lstatSync(claudePath).isSymbolicLink()) {
3292
+ const stat = fs4.lstatSync(claudePath);
3293
+ if (stat.isSymbolicLink()) {
3294
+ fs4.unlinkSync(claudePath);
3295
+ logger.debug(`Removed existing symlink: ${claudePath}`);
3296
+ } else if (stat.isDirectory()) {
3297
+ logger.warn(`Cannot create symlink: ${claudePath} is a directory, not a symlink`);
3298
+ return {
3299
+ success: false,
3300
+ skillName,
3301
+ reflyPath,
3302
+ claudePath,
3303
+ error: `Target path is a directory, not a symlink: ${claudePath}`
3304
+ };
3305
+ }
3306
+ }
3307
+ fs4.symlinkSync(reflyPath, claudePath, "dir");
3308
+ logger.info(`Created symlink: ${claudePath} -> ${reflyPath}`);
3309
+ return {
3310
+ success: true,
3311
+ skillName,
3312
+ reflyPath,
3313
+ claudePath
3314
+ };
3315
+ } catch (err) {
3316
+ if (err.code === "ENOENT") {
3317
+ try {
3318
+ fs4.symlinkSync(reflyPath, claudePath, "dir");
3319
+ logger.info(`Created symlink: ${claudePath} -> ${reflyPath}`);
3320
+ return {
3321
+ success: true,
3322
+ skillName,
3323
+ reflyPath,
3324
+ claudePath
3325
+ };
3326
+ } catch (innerErr) {
3327
+ return {
3328
+ success: false,
3329
+ skillName,
3330
+ reflyPath,
3331
+ claudePath,
3332
+ error: innerErr.message
3333
+ };
3334
+ }
3335
+ }
3336
+ return {
3337
+ success: false,
3338
+ skillName,
3339
+ reflyPath,
3340
+ claudePath,
3341
+ error: err.message
3342
+ };
3343
+ }
3344
+ }
3345
+ function removeSkillSymlink(skillName) {
3346
+ const claudePath = getClaudeSkillSymlinkPath(skillName);
3347
+ try {
3348
+ if (!fs4.existsSync(claudePath)) {
3349
+ logger.debug(`Symlink not found: ${claudePath}`);
3350
+ return false;
3351
+ }
3352
+ const stat = fs4.lstatSync(claudePath);
3353
+ if (!stat.isSymbolicLink()) {
3354
+ logger.warn(`Not a symlink: ${claudePath}`);
3355
+ return false;
3356
+ }
3357
+ fs4.unlinkSync(claudePath);
3358
+ logger.info(`Removed symlink: ${claudePath}`);
3359
+ return true;
3360
+ } catch (err) {
3361
+ logger.error(`Failed to remove symlink ${claudePath}:`, err);
3362
+ return false;
3363
+ }
3364
+ }
3365
+ function isSkillSymlinkValid(skillName) {
3366
+ const claudePath = getClaudeSkillSymlinkPath(skillName);
3367
+ const expectedTarget = skillName === "refly" ? getReflyBaseSkillDir() : getReflyDomainSkillDir(skillName);
3368
+ try {
3369
+ if (!fs4.existsSync(claudePath)) {
3370
+ return { exists: false, isSymlink: false, isValid: false };
3371
+ }
3372
+ const stat = fs4.lstatSync(claudePath);
3373
+ if (!stat.isSymbolicLink()) {
3374
+ return { exists: true, isSymlink: false, isValid: false };
3375
+ }
3376
+ const target = fs4.readlinkSync(claudePath);
3377
+ const resolvedTarget = path4.resolve(path4.dirname(claudePath), target);
3378
+ const isValid2 = resolvedTarget === expectedTarget && fs4.existsSync(resolvedTarget);
3379
+ return {
3380
+ exists: true,
3381
+ isSymlink: true,
3382
+ isValid: isValid2,
3383
+ target: resolvedTarget
3384
+ };
3385
+ } catch {
3386
+ return { exists: false, isSymlink: false, isValid: false };
3387
+ }
3388
+ }
3389
+ function initializeBaseSkillSymlink() {
3390
+ const baseDir = getReflyBaseSkillDir();
3391
+ ensureDir(baseDir);
3392
+ ensureDir(path4.join(baseDir, "rules"));
3393
+ return createSkillSymlink("refly");
3394
+ }
3395
+ function createReflySkillWithSymlink(skillName, skillMdContent) {
3396
+ const skillDir = getReflyDomainSkillDir(skillName);
3397
+ try {
3398
+ ensureReflySkillsDir();
3399
+ if (fs4.existsSync(skillDir)) {
3400
+ return {
3401
+ success: false,
3402
+ skillName,
3403
+ reflyPath: skillDir,
3404
+ claudePath: getClaudeSkillSymlinkPath(skillName),
3405
+ error: `Skill directory already exists: ${skillDir}`
3406
+ };
3407
+ }
3408
+ fs4.mkdirSync(skillDir, { recursive: true, mode: 493 });
3409
+ const skillMdPath = path4.join(skillDir, "SKILL.md");
3410
+ fs4.writeFileSync(skillMdPath, skillMdContent, { encoding: "utf-8", mode: 420 });
3411
+ logger.debug(`Created SKILL.md: ${skillMdPath}`);
3412
+ return createSkillSymlink(skillName);
3413
+ } catch (err) {
3414
+ return {
3415
+ success: false,
3416
+ skillName,
3417
+ reflyPath: skillDir,
3418
+ claudePath: getClaudeSkillSymlinkPath(skillName),
3419
+ error: err.message
3420
+ };
3421
+ }
3422
+ }
3423
+ function deleteDomainSkillWithSymlink(skillName) {
3424
+ const symlinkRemoved = removeSkillSymlink(skillName);
3425
+ const skillDir = getReflyDomainSkillDir(skillName);
3426
+ let directoryRemoved = false;
3427
+ try {
3428
+ if (fs4.existsSync(skillDir)) {
3429
+ fs4.rmSync(skillDir, { recursive: true, force: true });
3430
+ directoryRemoved = true;
3431
+ logger.info(`Removed skill directory: ${skillDir}`);
3432
+ }
3433
+ } catch (err) {
3434
+ logger.error(`Failed to remove skill directory ${skillDir}:`, err);
3435
+ }
3436
+ return { symlinkRemoved, directoryRemoved };
3437
+ }
3438
+ function listSkillSymlinks() {
3439
+ const claudeSkillsDir = getClaudeSkillsDir();
3440
+ const results = [];
3441
+ if (!fs4.existsSync(claudeSkillsDir)) {
3442
+ return results;
3443
+ }
3444
+ try {
3445
+ const entries = fs4.readdirSync(claudeSkillsDir, { withFileTypes: true });
3446
+ for (const entry of entries) {
3447
+ const fullPath = path4.join(claudeSkillsDir, entry.name);
3448
+ try {
3449
+ const stat = fs4.lstatSync(fullPath);
3450
+ if (stat.isSymbolicLink()) {
3451
+ const target = fs4.readlinkSync(fullPath);
3452
+ const resolvedTarget = path4.resolve(path4.dirname(fullPath), target);
3453
+ const isValid2 = fs4.existsSync(resolvedTarget);
3454
+ results.push({
3455
+ name: entry.name,
3456
+ claudePath: fullPath,
3457
+ target: resolvedTarget,
3458
+ isValid: isValid2
3459
+ });
3460
+ }
3461
+ } catch {
3462
+ }
3463
+ }
3464
+ } catch {
3465
+ }
3466
+ return results;
3467
+ }
3468
+ function generateReflySkillMd(options) {
3469
+ const {
3470
+ name,
3471
+ displayName,
3472
+ description,
3473
+ skillId,
3474
+ workflowId,
3475
+ installationId,
3476
+ triggers = [],
3477
+ tags = [],
3478
+ version = "1.0.0",
3479
+ inputSchema,
3480
+ outputSchema
3481
+ } = options;
3482
+ const frontmatterLines = ["---", `name: ${name}`];
3483
+ frontmatterLines.push(`description: ${description}`);
3484
+ if (tags.length > 0) {
3485
+ frontmatterLines.push("tags:");
3486
+ frontmatterLines.push(...tags.map((t) => ` - ${t}`));
3487
+ }
3488
+ frontmatterLines.push(`version: ${version}`);
3489
+ frontmatterLines.push(`skillId: ${skillId}`);
3490
+ frontmatterLines.push(`workflowId: ${workflowId}`);
3491
+ if (installationId) {
3492
+ frontmatterLines.push(`installationId: ${installationId}`);
3493
+ }
3494
+ if (triggers.length > 0) {
3495
+ frontmatterLines.push("triggers:");
3496
+ frontmatterLines.push(...triggers.map((t) => ` - ${t}`));
3497
+ }
3498
+ frontmatterLines.push("---");
3499
+ const title = displayName || name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3500
+ const inputExample = inputSchema ? JSON.stringify(inputSchema, null, 2) : `{
3501
+ "query": "your input here"
3502
+ }`;
3503
+ const runCommand = installationId ? `refly skill run ${installationId} --input '${inputSchema ? JSON.stringify(inputSchema) : "{}"}'` : `refly workflow run ${workflowId} --input '${inputSchema ? JSON.stringify(inputSchema) : "{}"}'`;
3504
+ const outputSection = outputSchema ? `The skill returns:
3505
+
3506
+ \`\`\`json
3507
+ ${JSON.stringify(outputSchema, null, 2)}
3508
+ \`\`\`` : "The skill returns the workflow execution result.";
3509
+ const content = `
3510
+
3511
+ # ${title}
3512
+
3513
+ ${description}
3514
+
3515
+ ## Usage
3516
+
3517
+ This skill is executed via Refly CLI:
3518
+
3519
+ \`\`\`bash
3520
+ ${runCommand}
3521
+ \`\`\`
3522
+
3523
+ ## Input
3524
+
3525
+ Provide input as JSON:
3526
+
3527
+ \`\`\`json
3528
+ ${inputExample}
3529
+ \`\`\`
3530
+
3531
+ ## Output
3532
+
3533
+ ${outputSection}
3534
+
3535
+ ## Rules
3536
+
3537
+ For workflow operations, refer to the base skill rules:
3538
+ - Workflow: \`~/.claude/skills/refly/rules/workflow.md\`
3539
+ - Node: \`~/.claude/skills/refly/rules/node.md\`
3540
+ - File: \`~/.claude/skills/refly/rules/file.md\`
3541
+ `;
3542
+ return frontmatterLines.join("\n") + content;
3543
+ }
3544
+ function parseReflySkillMd(content) {
3545
+ const frontmatterRegex = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
3546
+ const match = content.match(frontmatterRegex);
3547
+ if (!match) {
3548
+ throw new Error("Invalid SKILL.md format: missing frontmatter");
3549
+ }
3550
+ const [, frontmatterStr, body] = match;
3551
+ const meta = {};
3552
+ const lines = frontmatterStr.split("\n");
3553
+ let currentKey = null;
3554
+ let currentArray = [];
3555
+ for (const line of lines) {
3556
+ const trimmed = line.trim();
3557
+ if (trimmed.startsWith("- ")) {
3558
+ if (currentKey) {
3559
+ currentArray.push(trimmed.slice(2).trim());
3560
+ }
3561
+ continue;
3562
+ }
3563
+ if (currentKey && currentArray.length > 0) {
3564
+ meta[currentKey] = currentArray;
3565
+ currentArray = [];
3566
+ currentKey = null;
3567
+ }
3568
+ const colonIndex = trimmed.indexOf(":");
3569
+ if (colonIndex > 0) {
3570
+ const key = trimmed.slice(0, colonIndex).trim();
3571
+ const value = trimmed.slice(colonIndex + 1).trim();
3572
+ if (value === "") {
3573
+ currentKey = key;
3574
+ currentArray = [];
3575
+ } else {
3576
+ meta[key] = value;
3577
+ }
3578
+ }
3579
+ }
3580
+ if (currentKey && currentArray.length > 0) {
3581
+ meta[currentKey] = currentArray;
3582
+ }
3583
+ if (!meta.name) {
3584
+ throw new Error('Invalid SKILL.md: missing required field "name"');
3585
+ }
3586
+ if (!meta.description) {
3587
+ throw new Error('Invalid SKILL.md: missing required field "description"');
3588
+ }
3589
+ if (!meta.skillId) {
3590
+ throw new Error('Invalid SKILL.md: missing required field "skillId"');
3591
+ }
3592
+ if (!meta.workflowId) {
3593
+ throw new Error('Invalid SKILL.md: missing required field "workflowId"');
3594
+ }
3595
+ return {
3596
+ meta,
3597
+ body: body.trim(),
3598
+ raw: content
3599
+ };
3600
+ }
3601
+ var fs4, path4;
3602
+ var init_symlink = __esm({
3603
+ "src/skill/symlink.ts"() {
3604
+ "use strict";
3605
+ init_cjs_shims();
3606
+ fs4 = __toESM(require("fs"));
3607
+ path4 = __toESM(require("path"));
3608
+ init_paths();
3609
+ init_logger();
3610
+ }
3611
+ });
3612
+
3229
3613
  // src/bin/refly.ts
3230
3614
  init_cjs_shims();
3231
3615
 
@@ -4023,6 +4407,10 @@ var OutputFormatter = class {
4023
4407
  if (error.code) {
4024
4408
  console.log(UI.keyValue("Code", UI.dim(error.code)));
4025
4409
  }
4410
+ if (error.recoverable !== void 0) {
4411
+ const recoverableText = error.recoverable ? "\u{1F504} Recoverable: Fix the parameter and retry the SAME command" : "\u274C Not recoverable: Consider a different approach";
4412
+ console.log(UI.dim(` ${recoverableText}`));
4413
+ }
4026
4414
  if (error.details && Object.keys(error.details).length > 0) {
4027
4415
  console.log();
4028
4416
  console.log(UI.indent(UI.dim("Details:")));
@@ -4040,6 +4428,13 @@ var OutputFormatter = class {
4040
4428
  console.log();
4041
4429
  console.log(UI.dim(` \u{1F4A1} Hint: ${error.hint}`));
4042
4430
  }
4431
+ if (error.suggestedFix && Object.keys(error.suggestedFix).length > 0) {
4432
+ console.log();
4433
+ console.log(UI.dim(" \u2705 Suggested fix:"));
4434
+ console.log(
4435
+ UI.indent(this.formatObject(error.suggestedFix, 2), 4)
4436
+ );
4437
+ }
4043
4438
  console.log();
4044
4439
  }
4045
4440
  // === Workflow List Format (Phase 2: Docker-style table) ===
@@ -4265,6 +4660,9 @@ var OutputFormatter = class {
4265
4660
  if (error.hint) {
4266
4661
  console.log(UI.dim(` ${error.hint}`));
4267
4662
  }
4663
+ if (error.suggestedFix) {
4664
+ console.log(UI.dim(` fix: ${this.formatValue(error.suggestedFix)}`));
4665
+ }
4268
4666
  }
4269
4667
  // === Plain Format ===
4270
4668
  outputPlain(type, payload) {
@@ -4282,6 +4680,9 @@ var OutputFormatter = class {
4282
4680
  if (error.hint) {
4283
4681
  console.log(` hint: ${error.hint}`);
4284
4682
  }
4683
+ if (error.suggestedFix) {
4684
+ console.log(` suggestedFix: ${this.formatValue(error.suggestedFix)}`);
4685
+ }
4285
4686
  }
4286
4687
  // === Helper Methods ===
4287
4688
  humanizeType(type) {
@@ -4403,13 +4804,29 @@ function print(type, payload) {
4403
4804
  const formatter = getFormatter();
4404
4805
  formatter.success(type, payload);
4405
4806
  }
4807
+ function isRecoverableError(code, hasSuggestedFix) {
4808
+ const recoverableCodes = [
4809
+ "INVALID_INPUT",
4810
+ "VALIDATION_ERROR",
4811
+ "INVALID_NODE_INPUT",
4812
+ "TIMEOUT",
4813
+ // Can retry
4814
+ "RATE_LIMIT"
4815
+ // Can retry after waiting
4816
+ ];
4817
+ if (hasSuggestedFix) return true;
4818
+ return recoverableCodes.includes(code);
4819
+ }
4406
4820
  function fail(code, message, options) {
4407
4821
  const formatter = getFormatter();
4822
+ const recoverable = options?.recoverable ?? isRecoverableError(code, !!options?.suggestedFix);
4408
4823
  formatter.error({
4409
4824
  code,
4410
4825
  message,
4411
4826
  details: options?.details,
4412
- hint: options?.hint
4827
+ hint: options?.hint,
4828
+ suggestedFix: options?.suggestedFix,
4829
+ recoverable
4413
4830
  });
4414
4831
  process.exit(options?.exitCode ?? getExitCode(code));
4415
4832
  }
@@ -4419,7 +4836,8 @@ function printError(code, message, options) {
4419
4836
  code,
4420
4837
  message,
4421
4838
  details: options?.details,
4422
- hint: options?.hint
4839
+ hint: options?.hint,
4840
+ suggestedFix: options?.suggestedFix
4423
4841
  });
4424
4842
  }
4425
4843
  function getExitCode(code) {
@@ -4467,8 +4885,8 @@ var ErrorCodes = {
4467
4885
  };
4468
4886
 
4469
4887
  // src/bin/refly.ts
4470
- var fs19 = __toESM(require("fs"));
4471
- var path15 = __toESM(require("path"));
4888
+ var fs26 = __toESM(require("fs"));
4889
+ var path21 = __toESM(require("path"));
4472
4890
 
4473
4891
  // src/commands/init.ts
4474
4892
  init_cjs_shims();
@@ -4973,8 +5391,8 @@ function getErrorMap() {
4973
5391
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
4974
5392
  init_cjs_shims();
4975
5393
  var makeIssue = (params) => {
4976
- const { data, path: path16, errorMaps, issueData } = params;
4977
- const fullPath = [...path16, ...issueData.path || []];
5394
+ const { data, path: path22, errorMaps, issueData } = params;
5395
+ const fullPath = [...path22, ...issueData.path || []];
4978
5396
  const fullIssue = {
4979
5397
  ...issueData,
4980
5398
  path: fullPath
@@ -5094,11 +5512,11 @@ var errorUtil;
5094
5512
 
5095
5513
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
5096
5514
  var ParseInputLazyPath = class {
5097
- constructor(parent, value, path16, key) {
5515
+ constructor(parent, value, path22, key) {
5098
5516
  this._cachedPath = [];
5099
5517
  this.parent = parent;
5100
5518
  this.data = value;
5101
- this._path = path16;
5519
+ this._path = path22;
5102
5520
  this._key = key;
5103
5521
  }
5104
5522
  get path() {
@@ -8706,23 +9124,41 @@ init_paths();
8706
9124
 
8707
9125
  // src/skill/installer.ts
8708
9126
  init_cjs_shims();
8709
- var fs4 = __toESM(require("fs"));
8710
- var path4 = __toESM(require("path"));
9127
+ var fs5 = __toESM(require("fs"));
9128
+ var path5 = __toESM(require("path"));
8711
9129
  init_paths();
8712
9130
  init_logger();
9131
+ init_symlink();
9132
+ function removeOldSkillDirectory() {
9133
+ const claudeSkillPath = getClaudeSkillSymlinkPath("refly");
9134
+ if (!fs5.existsSync(claudeSkillPath)) {
9135
+ return;
9136
+ }
9137
+ try {
9138
+ const stat = fs5.lstatSync(claudeSkillPath);
9139
+ if (stat.isSymbolicLink()) {
9140
+ fs5.unlinkSync(claudeSkillPath);
9141
+ } else if (stat.isDirectory()) {
9142
+ fs5.rmSync(claudeSkillPath, { recursive: true, force: true });
9143
+ logger.info("Removed old skill directory");
9144
+ }
9145
+ } catch (err) {
9146
+ logger.warn("Failed to remove old directory:", err);
9147
+ }
9148
+ }
8713
9149
  function getPackageSkillDir() {
8714
9150
  const possiblePaths = [
8715
- path4.join(__dirname, "..", "..", "skill"),
9151
+ path5.join(__dirname, "..", "..", "skill"),
8716
9152
  // Built package: dist/bin/../../skill
8717
- path4.join(__dirname, "..", "..", "..", "skill"),
9153
+ path5.join(__dirname, "..", "..", "..", "skill"),
8718
9154
  // Development: dist/bin/../../../skill
8719
- path4.join(__dirname, "..", "skill")
9155
+ path5.join(__dirname, "..", "skill")
8720
9156
  // Alternative: dist/../skill
8721
9157
  ];
8722
9158
  logger.debug("Looking for skill files, __dirname:", __dirname);
8723
9159
  for (const p of possiblePaths) {
8724
- const resolved = path4.resolve(p);
8725
- const exists = fs4.existsSync(resolved);
9160
+ const resolved = path5.resolve(p);
9161
+ const exists = fs5.existsSync(resolved);
8726
9162
  logger.debug(` Checking path: ${resolved} - exists: ${exists}`);
8727
9163
  if (exists) {
8728
9164
  return resolved;
@@ -8734,42 +9170,52 @@ function installSkill() {
8734
9170
  const result = {
8735
9171
  skillInstalled: false,
8736
9172
  skillPath: null,
9173
+ symlinkPath: null,
8737
9174
  commandsInstalled: false,
8738
9175
  commandsPath: null,
8739
9176
  version: getSkillVersion()
8740
9177
  };
8741
9178
  const sourceDir = getPackageSkillDir();
8742
9179
  logger.debug("Source skill directory:", sourceDir);
8743
- const targetDir = getClaudeSkillDir();
9180
+ ensureReflySkillsDir();
9181
+ const targetDir = getReflyBaseSkillDir();
8744
9182
  logger.debug("Target skill directory:", targetDir);
8745
9183
  try {
8746
9184
  ensureDir(targetDir);
8747
- ensureDir(path4.join(targetDir, "references"));
9185
+ ensureDir(path5.join(targetDir, "rules"));
8748
9186
  logger.debug("Created target directories");
8749
9187
  } catch (err) {
8750
9188
  logger.error("Failed to create target directories:", err);
8751
9189
  throw err;
8752
9190
  }
8753
- const skillSource = path4.join(sourceDir, "SKILL.md");
8754
- const skillTarget = path4.join(targetDir, "SKILL.md");
9191
+ const skillSource = path5.join(sourceDir, "SKILL.md");
9192
+ const skillTarget = path5.join(targetDir, "SKILL.md");
8755
9193
  logger.debug(`Copying SKILL.md: ${skillSource} -> ${skillTarget}`);
8756
- if (fs4.existsSync(skillSource)) {
8757
- fs4.copyFileSync(skillSource, skillTarget);
9194
+ if (fs5.existsSync(skillSource)) {
9195
+ fs5.copyFileSync(skillSource, skillTarget);
8758
9196
  result.skillInstalled = true;
8759
9197
  result.skillPath = targetDir;
8760
9198
  logger.debug("SKILL.md copied successfully");
8761
9199
  } else {
8762
9200
  logger.warn("SKILL.md source not found:", skillSource);
8763
9201
  }
8764
- const refsSource = path4.join(sourceDir, "references");
8765
- const refsTarget = path4.join(targetDir, "references");
8766
- if (fs4.existsSync(refsSource)) {
8767
- const files = fs4.readdirSync(refsSource);
8768
- logger.debug(`Copying ${files.length} reference files`);
9202
+ const refsSource = path5.join(sourceDir, "references");
9203
+ const rulesTarget = path5.join(targetDir, "rules");
9204
+ if (fs5.existsSync(refsSource)) {
9205
+ const files = fs5.readdirSync(refsSource);
9206
+ logger.debug(`Copying ${files.length} rule files`);
8769
9207
  for (const file of files) {
8770
- fs4.copyFileSync(path4.join(refsSource, file), path4.join(refsTarget, file));
9208
+ fs5.copyFileSync(path5.join(refsSource, file), path5.join(rulesTarget, file));
8771
9209
  }
8772
9210
  }
9211
+ removeOldSkillDirectory();
9212
+ const symlinkResult = createSkillSymlink("refly");
9213
+ if (symlinkResult.success) {
9214
+ result.symlinkPath = symlinkResult.claudePath;
9215
+ logger.info(`Created symlink: ${symlinkResult.claudePath} -> ${symlinkResult.reflyPath}`);
9216
+ } else {
9217
+ logger.warn(`Failed to create symlink: ${symlinkResult.error}`);
9218
+ }
8773
9219
  const commandsDir = getClaudeCommandsDir();
8774
9220
  logger.debug("Commands directory:", commandsDir);
8775
9221
  ensureDir(commandsDir);
@@ -8781,20 +9227,21 @@ function installSkill() {
8781
9227
  updateSkillInfo(result.version);
8782
9228
  logger.info("Skill installation complete:", {
8783
9229
  skillInstalled: result.skillInstalled,
9230
+ symlinkPath: result.symlinkPath,
8784
9231
  commandsInstalled: result.commandsInstalled
8785
9232
  });
8786
9233
  return result;
8787
9234
  }
8788
9235
  function installSlashCommands(sourceDir, targetDir) {
8789
- const commandsSource = path4.join(sourceDir, "..", "commands");
8790
- if (!fs4.existsSync(commandsSource)) {
9236
+ const commandsSource = path5.join(sourceDir, "..", "commands");
9237
+ if (!fs5.existsSync(commandsSource)) {
8791
9238
  return false;
8792
9239
  }
8793
9240
  try {
8794
- const files = fs4.readdirSync(commandsSource);
9241
+ const files = fs5.readdirSync(commandsSource);
8795
9242
  for (const file of files) {
8796
9243
  if (file.endsWith(".md")) {
8797
- fs4.copyFileSync(path4.join(commandsSource, file), path4.join(targetDir, file));
9244
+ fs5.copyFileSync(path5.join(commandsSource, file), path5.join(targetDir, file));
8798
9245
  }
8799
9246
  }
8800
9247
  return files.length > 0;
@@ -8804,8 +9251,8 @@ function installSlashCommands(sourceDir, targetDir) {
8804
9251
  }
8805
9252
  function getSkillVersion() {
8806
9253
  try {
8807
- const skillPath = path4.join(getPackageSkillDir(), "SKILL.md");
8808
- const content = fs4.readFileSync(skillPath, "utf-8");
9254
+ const skillPath = path5.join(getPackageSkillDir(), "SKILL.md");
9255
+ const content = fs5.readFileSync(skillPath, "utf-8");
8809
9256
  const versionMatch = content.match(/version:\s*(\d+\.\d+\.\d+)/);
8810
9257
  if (versionMatch) {
8811
9258
  return versionMatch[1];
@@ -8813,23 +9260,26 @@ function getSkillVersion() {
8813
9260
  } catch {
8814
9261
  }
8815
9262
  try {
8816
- const pkgPath = path4.join(__dirname, "..", "..", "package.json");
8817
- const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
9263
+ const pkgPath = path5.join(__dirname, "..", "..", "package.json");
9264
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
8818
9265
  return pkg.version;
8819
9266
  } catch {
8820
9267
  return "0.1.0";
8821
9268
  }
8822
9269
  }
8823
9270
  function isSkillInstalled() {
8824
- const skillPath = path4.join(getClaudeSkillDir(), "SKILL.md");
8825
- if (!fs4.existsSync(skillPath)) {
9271
+ const skillPath = path5.join(getReflyBaseSkillDir(), "SKILL.md");
9272
+ if (!fs5.existsSync(skillPath)) {
8826
9273
  return { installed: false, upToDate: false };
8827
9274
  }
8828
9275
  const currentVersion = getSkillVersion();
9276
+ const { isSkillSymlinkValid: isSkillSymlinkValid2 } = (init_symlink(), __toCommonJS(symlink_exports));
9277
+ const symlinkStatus = isSkillSymlinkValid2("refly");
8829
9278
  return {
8830
9279
  installed: true,
8831
9280
  upToDate: true,
8832
- currentVersion
9281
+ currentVersion,
9282
+ symlinkValid: symlinkStatus.isValid
8833
9283
  };
8834
9284
  }
8835
9285
 
@@ -9314,14 +9764,14 @@ var baseOpen = async (options) => {
9314
9764
  }
9315
9765
  const subprocess = import_node_child_process5.default.spawn(command, cliArguments, childProcessOptions);
9316
9766
  if (options.wait) {
9317
- return new Promise((resolve6, reject) => {
9767
+ return new Promise((resolve7, reject) => {
9318
9768
  subprocess.once("error", reject);
9319
9769
  subprocess.once("close", (exitCode) => {
9320
9770
  if (!options.allowNonzeroExitCode && exitCode > 0) {
9321
9771
  reject(new Error(`Exited with code ${exitCode}`));
9322
9772
  return;
9323
9773
  }
9324
- resolve6(subprocess);
9774
+ resolve7(subprocess);
9325
9775
  });
9326
9776
  });
9327
9777
  }
@@ -9397,19 +9847,20 @@ var open_default = open;
9397
9847
 
9398
9848
  // src/api/client.ts
9399
9849
  init_cjs_shims();
9400
- var fs10 = __toESM(require("fs"));
9850
+ var fs11 = __toESM(require("fs"));
9401
9851
  var import_node_fs4 = require("fs");
9402
- var path6 = __toESM(require("path"));
9852
+ var path7 = __toESM(require("path"));
9403
9853
  var import_mime = __toESM(require("mime"));
9404
9854
 
9405
9855
  // src/utils/errors.ts
9406
9856
  init_cjs_shims();
9407
9857
  var CLIError = class extends Error {
9408
- constructor(code, message, details, hint) {
9858
+ constructor(code, message, details, hint, suggestedFix) {
9409
9859
  super(message);
9410
9860
  this.code = code;
9411
9861
  this.details = details;
9412
9862
  this.hint = hint;
9863
+ this.suggestedFix = suggestedFix;
9413
9864
  this.name = "CLIError";
9414
9865
  }
9415
9866
  };
@@ -9427,10 +9878,10 @@ var NetworkError = class extends CLIError {
9427
9878
  // src/api/client.ts
9428
9879
  init_logger();
9429
9880
  var DEFAULT_TIMEOUT = 3e4;
9430
- async function apiRequest(path16, options = {}) {
9881
+ async function apiRequest(path22, options = {}) {
9431
9882
  const { method = "GET", body, query, timeout = DEFAULT_TIMEOUT, requireAuth = true } = options;
9432
9883
  const endpoint = getApiEndpoint();
9433
- let url = `${endpoint}${path16}`;
9884
+ let url = `${endpoint}${path22}`;
9434
9885
  if (query && Object.keys(query).length > 0) {
9435
9886
  const params = new URLSearchParams(query);
9436
9887
  url = `${url}?${params.toString()}`;
@@ -9468,7 +9919,7 @@ async function apiRequest(path16, options = {}) {
9468
9919
  const controller = new AbortController();
9469
9920
  const timeoutId = setTimeout(() => controller.abort(), timeout);
9470
9921
  try {
9471
- logger.debug(`API Request: ${method} ${path16}`);
9922
+ logger.debug(`API Request: ${method} ${path22}`);
9472
9923
  const response = await fetch(url, {
9473
9924
  method,
9474
9925
  headers,
@@ -9560,7 +10011,8 @@ function mapAPIError(status, response) {
9560
10011
  cliError.code || "UNKNOWN",
9561
10012
  cliError.message || "Unknown error",
9562
10013
  void 0,
9563
- cliError.hint
10014
+ cliError.hint,
10015
+ cliError.suggestedFix
9564
10016
  );
9565
10017
  }
9566
10018
  const errCode = response.errCode ?? response.error ?? "UNKNOWN";
@@ -9582,10 +10034,10 @@ function mapAPIError(status, response) {
9582
10034
  }
9583
10035
  return new CLIError(errCode, errMsg);
9584
10036
  }
9585
- async function apiRequestStream(path16, options = {}) {
10037
+ async function apiRequestStream(path22, options = {}) {
9586
10038
  const { timeout = 3e5 } = options;
9587
10039
  const endpoint = getApiEndpoint();
9588
- const url = `${endpoint}${path16}`;
10040
+ const url = `${endpoint}${path22}`;
9589
10041
  const headers = {
9590
10042
  "User-Agent": "refly-cli/0.1.0"
9591
10043
  };
@@ -9616,7 +10068,7 @@ async function apiRequestStream(path16, options = {}) {
9616
10068
  const controller = new AbortController();
9617
10069
  const timeoutId = setTimeout(() => controller.abort(), timeout);
9618
10070
  try {
9619
- logger.debug(`API Stream Request: GET ${path16}`);
10071
+ logger.debug(`API Stream Request: GET ${path22}`);
9620
10072
  const response = await fetch(url, {
9621
10073
  method: "GET",
9622
10074
  headers,
@@ -9711,7 +10163,7 @@ async function uploadToPresignedUrl(presignedUrl, filePath, contentType, retryCo
9711
10163
  const controller = new AbortController();
9712
10164
  const timeoutId = setTimeout(() => controller.abort(), timeout);
9713
10165
  try {
9714
- const fileBuffer = await fs10.promises.readFile(filePath);
10166
+ const fileBuffer = await fs11.promises.readFile(filePath);
9715
10167
  const response = await fetch(presignedUrl, {
9716
10168
  method: "PUT",
9717
10169
  headers: {
@@ -9769,7 +10221,7 @@ async function apiGetWorkflow(workflowId) {
9769
10221
  return apiRequest(`/v1/cli/workflow/${workflowId}`);
9770
10222
  }
9771
10223
  async function apiUploadDriveFile(filePath, canvasId, options) {
9772
- const filename = path6.basename(filePath);
10224
+ const filename = path7.basename(filePath);
9773
10225
  const mimeType = getMimeType(filePath);
9774
10226
  const fileStats = (0, import_node_fs4.statSync)(filePath);
9775
10227
  logger.debug(`Starting presigned upload: ${filename} (${fileStats.size} bytes)`);
@@ -10016,7 +10468,7 @@ async function getUserInfoFromToken(accessToken) {
10016
10468
  };
10017
10469
  }
10018
10470
  function sleep(ms) {
10019
- return new Promise((resolve6) => setTimeout(resolve6, ms));
10471
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
10020
10472
  }
10021
10473
 
10022
10474
  // src/commands/init.ts
@@ -10083,6 +10535,7 @@ var initCommand = new Command("init").description("Initialize Refly CLI, install
10083
10535
  apiEndpoint,
10084
10536
  skillInstalled: installResult.skillInstalled,
10085
10537
  skillPath: installResult.skillPath,
10538
+ symlinkPath: installResult.symlinkPath,
10086
10539
  commandsInstalled: installResult.commandsInstalled,
10087
10540
  commandsPath: installResult.commandsPath,
10088
10541
  version: installResult.version
@@ -10135,7 +10588,10 @@ var initCommand = new Command("init").description("Initialize Refly CLI, install
10135
10588
  configDir: getReflyDir(),
10136
10589
  apiEndpoint,
10137
10590
  skillInstalled: installResult.skillInstalled,
10591
+ skillPath: installResult.skillPath,
10592
+ symlinkPath: installResult.symlinkPath,
10138
10593
  commandsInstalled: installResult.commandsInstalled,
10594
+ commandsPath: installResult.commandsPath,
10139
10595
  version: installResult.version,
10140
10596
  authenticated: !!(getAccessToken() || getApiKey())
10141
10597
  });
@@ -10270,7 +10726,7 @@ var import_node_child_process6 = require("child_process");
10270
10726
  var import_node_fs5 = __toESM(require("fs"));
10271
10727
  init_logger();
10272
10728
  init_paths();
10273
- var CLI_VERSION = "0.1.16";
10729
+ var CLI_VERSION = "0.1.17";
10274
10730
  var NPM_TAG = "test";
10275
10731
  function compareSemver(a, b) {
10276
10732
  const parseVersion = (v) => {
@@ -10526,16 +10982,16 @@ configCommand.action(() => {
10526
10982
  };
10527
10983
  ok("config", safeConfig);
10528
10984
  });
10529
- function getNestedValue(obj, path16) {
10530
- return path16.split(".").reduce((current, key) => {
10985
+ function getNestedValue(obj, path22) {
10986
+ return path22.split(".").reduce((current, key) => {
10531
10987
  if (current && typeof current === "object" && key in current) {
10532
10988
  return current[key];
10533
10989
  }
10534
10990
  return void 0;
10535
10991
  }, obj);
10536
10992
  }
10537
- function setNestedValue(obj, path16, value) {
10538
- const keys = path16.split(".");
10993
+ function setNestedValue(obj, path22, value) {
10994
+ const keys = path22.split(".");
10539
10995
  const lastKey = keys.pop();
10540
10996
  let current = obj;
10541
10997
  for (const key of keys) {
@@ -10640,14 +11096,25 @@ var workflowCreateCommand = new Command("create").description("Create a workflow
10640
11096
  spec = Array.isArray(parsed) ? { nodes: parsed } : parsed;
10641
11097
  } catch {
10642
11098
  fail(ErrorCodes.INVALID_INPUT, "Invalid JSON in --spec", {
10643
- hint: "Ensure the spec is valid JSON"
11099
+ hint: `Spec format: '[{"id": "node1", "type": "skill", "query": "task description", "toolsetKeys": ["web_search"]}]'
11100
+ Or full format: '{"nodes": [...], "edges": [...]}'`,
11101
+ suggestedFix: {
11102
+ field: "--spec",
11103
+ format: "json-array | json-object",
11104
+ example: '[{"id": "node1", "type": "skill", "query": "task description", "toolsetKeys": ["web_search"]}]'
11105
+ }
10644
11106
  });
10645
11107
  return;
10646
11108
  }
10647
11109
  const validationError = validateSimplifiedSpec(spec);
10648
11110
  if (validationError) {
10649
11111
  fail(ErrorCodes.INVALID_INPUT, validationError, {
10650
- hint: 'Use simplified format: [{"id":"node1","type":"skill","query":"...","toolsetKeys":["tool_name"],"dependsOn":["other_node"]}]'
11112
+ hint: 'Use simplified format: [{"id":"node1","type":"skill","query":"...","toolsetKeys":["tool_name"],"dependsOn":["other_node"]}]',
11113
+ suggestedFix: {
11114
+ field: "--spec",
11115
+ format: "json-array",
11116
+ example: '[{"id":"node1","type":"skill","query":"...","toolsetKeys":["tool_name"],"dependsOn":["other_node"]}]'
11117
+ }
10651
11118
  });
10652
11119
  return;
10653
11120
  }
@@ -10667,7 +11134,11 @@ var workflowCreateCommand = new Command("create").description("Create a workflow
10667
11134
  });
10668
11135
  } catch (error) {
10669
11136
  if (error instanceof CLIError) {
10670
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11137
+ fail(error.code, error.message, {
11138
+ details: error.details,
11139
+ hint: error.hint,
11140
+ suggestedFix: error.suggestedFix
11141
+ });
10671
11142
  return;
10672
11143
  }
10673
11144
  fail(
@@ -10729,7 +11200,13 @@ var workflowGenerateCommand = new Command("generate").description("Generate a wo
10729
11200
  variables = JSON.parse(options.variables);
10730
11201
  } catch {
10731
11202
  fail(ErrorCodes.INVALID_INPUT, "Invalid JSON in --variables", {
10732
- hint: "Ensure the variables parameter is valid JSON"
11203
+ hint: `Variables format: '[{"name": "varName", "variableType": "string", "description": "desc", "required": true}]'
11204
+ Variable types: "string" (text input) or "resource" (file input)`,
11205
+ suggestedFix: {
11206
+ field: "--variables",
11207
+ format: "json-array",
11208
+ example: '[{"name": "varName", "variableType": "string", "description": "desc", "required": true}]'
11209
+ }
10733
11210
  });
10734
11211
  }
10735
11212
  }
@@ -10782,7 +11259,8 @@ var workflowGenerateCommand = new Command("generate").description("Generate a wo
10782
11259
  if (error instanceof CLIError) {
10783
11260
  fail(error.code, error.message, {
10784
11261
  details: { ...error.details, cleanedUp: cleanupResult?.deleted },
10785
- hint: error.hint
11262
+ hint: error.hint,
11263
+ suggestedFix: error.suggestedFix
10786
11264
  });
10787
11265
  }
10788
11266
  fail(
@@ -10820,7 +11298,11 @@ var workflowListCommand = new Command("list").description("List all workflows").
10820
11298
  });
10821
11299
  } catch (error) {
10822
11300
  if (error instanceof CLIError) {
10823
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11301
+ fail(error.code, error.message, {
11302
+ details: error.details,
11303
+ hint: error.hint,
11304
+ suggestedFix: error.suggestedFix
11305
+ });
10824
11306
  }
10825
11307
  fail(
10826
11308
  ErrorCodes.INTERNAL_ERROR,
@@ -10837,133 +11319,15 @@ var workflowGetCommand = new Command("get").description("Get workflow details").
10837
11319
  ok("workflow.get", result);
10838
11320
  } catch (error) {
10839
11321
  if (error instanceof CLIError) {
10840
- fail(error.code, error.message, { details: error.details, hint: error.hint });
10841
- }
10842
- fail(
10843
- ErrorCodes.INTERNAL_ERROR,
10844
- error instanceof Error ? error.message : "Failed to get workflow"
10845
- );
10846
- }
10847
- });
10848
-
10849
- // src/commands/workflow/edit.ts
10850
- init_cjs_shims();
10851
- function transformOperations(ops) {
10852
- const opMapping = {
10853
- add: "add_node",
10854
- remove: "remove_node",
10855
- update: "update_node",
10856
- addEdge: "add_edge",
10857
- removeEdge: "remove_edge",
10858
- // Also support direct backend format
10859
- add_node: "add_node",
10860
- remove_node: "remove_node",
10861
- update_node: "update_node",
10862
- add_edge: "add_edge",
10863
- remove_edge: "remove_edge"
10864
- };
10865
- return ops.map((op) => {
10866
- const { op: opType, ...rest } = op;
10867
- const backendType = opMapping[opType];
10868
- if (!backendType) {
10869
- throw new Error(`Unknown operation type: ${opType}`);
10870
- }
10871
- return {
10872
- type: backendType,
10873
- ...rest
10874
- };
10875
- });
10876
- }
10877
- var workflowEditCommand = new Command("edit").description("Edit an existing workflow").argument("<workflowId>", "Workflow ID or URL").option("--name <name>", "New workflow name").option("--ops <json>", "Node/edge operations as JSON array").option("--variables <json>", "Workflow variables as JSON array").option("--toolsets <keys>", 'Toolset inventory keys (comma-separated, e.g., "tavily,fal_audio")').option("--auto-layout", "Enable auto-layout to prevent node overlapping").action(async (workflowIdOrUrl, options) => {
10878
- try {
10879
- let workflowId = workflowIdOrUrl;
10880
- if (workflowIdOrUrl.includes("/workflow/")) {
10881
- const match = workflowIdOrUrl.match(/\/workflow\/(c-[a-z0-9]+)/);
10882
- if (match) {
10883
- workflowId = match[1];
10884
- }
10885
- }
10886
- const body = {};
10887
- if (options.name) {
10888
- body.name = options.name;
10889
- }
10890
- let toolsetKeys;
10891
- if (options.toolsets) {
10892
- toolsetKeys = options.toolsets.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
10893
- }
10894
- if (options.ops) {
10895
- try {
10896
- const rawOps = JSON.parse(options.ops);
10897
- body.operations = transformOperations(rawOps);
10898
- if (toolsetKeys && toolsetKeys.length > 0 && body.operations) {
10899
- body.operations = body.operations.map((op) => {
10900
- if (op.type === "add_node" && op.node) {
10901
- const node = op.node;
10902
- const data = node.data || {};
10903
- const metadata = data.metadata || {};
10904
- return {
10905
- ...op,
10906
- node: {
10907
- ...node,
10908
- data: {
10909
- ...data,
10910
- metadata: {
10911
- ...metadata,
10912
- toolsetKeys
10913
- }
10914
- }
10915
- }
10916
- };
10917
- }
10918
- return op;
10919
- });
10920
- }
10921
- } catch (error) {
10922
- fail(ErrorCodes.INVALID_INPUT, `Invalid operations: ${error.message}`, {
10923
- hint: "Ensure the operations are a valid JSON array with correct op types"
10924
- });
10925
- }
10926
- }
10927
- if (options.variables) {
10928
- try {
10929
- body.variables = JSON.parse(options.variables);
10930
- } catch {
10931
- fail(ErrorCodes.INVALID_INPUT, "Invalid JSON in --variables", {
10932
- hint: "Ensure the variables are a valid JSON array"
10933
- });
10934
- }
10935
- }
10936
- if (!options.name && !options.ops && !options.variables) {
10937
- fail(ErrorCodes.INVALID_INPUT, "No update options provided", {
10938
- hint: "Use --name, --ops, --variables, or --toolsets to specify what to update"
11322
+ fail(error.code, error.message, {
11323
+ details: error.details,
11324
+ hint: error.hint,
11325
+ suggestedFix: error.suggestedFix
10939
11326
  });
10940
11327
  }
10941
- let endpoint = `/v1/cli/workflow/${workflowId}`;
10942
- const queryParams = [];
10943
- if (toolsetKeys && toolsetKeys.length > 0) {
10944
- queryParams.push("resolveToolsetKeys=true");
10945
- }
10946
- if (options.autoLayout) {
10947
- queryParams.push("autoLayout=true");
10948
- }
10949
- if (queryParams.length > 0) {
10950
- endpoint += `?${queryParams.join("&")}`;
10951
- }
10952
- await apiRequest(endpoint, {
10953
- method: "PATCH",
10954
- body
10955
- });
10956
- ok("workflow.edit", {
10957
- message: "Workflow updated successfully",
10958
- workflowId
10959
- });
10960
- } catch (error) {
10961
- if (error instanceof CLIError) {
10962
- fail(error.code, error.message, { details: error.details, hint: error.hint });
10963
- }
10964
11328
  fail(
10965
11329
  ErrorCodes.INTERNAL_ERROR,
10966
- error instanceof Error ? error.message : "Failed to edit workflow"
11330
+ error instanceof Error ? error.message : "Failed to get workflow"
10967
11331
  );
10968
11332
  }
10969
11333
  });
@@ -10981,7 +11345,11 @@ var workflowDeleteCommand = new Command("delete").description("Delete a workflow
10981
11345
  });
10982
11346
  } catch (error) {
10983
11347
  if (error instanceof CLIError) {
10984
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11348
+ fail(error.code, error.message, {
11349
+ details: error.details,
11350
+ hint: error.hint,
11351
+ suggestedFix: error.suggestedFix
11352
+ });
10985
11353
  }
10986
11354
  fail(
10987
11355
  ErrorCodes.INTERNAL_ERROR,
@@ -10994,14 +11362,14 @@ var workflowDeleteCommand = new Command("delete").description("Delete a workflow
10994
11362
  init_cjs_shims();
10995
11363
  var readline2 = __toESM(require("readline/promises"));
10996
11364
  var import_node_process8 = require("process");
10997
- var path9 = __toESM(require("path"));
11365
+ var path10 = __toESM(require("path"));
10998
11366
 
10999
11367
  // src/utils/prompt.ts
11000
11368
  init_cjs_shims();
11001
11369
  var readline = __toESM(require("readline/promises"));
11002
11370
  var import_node_process7 = require("process");
11003
- var fs12 = __toESM(require("fs"));
11004
- var path7 = __toESM(require("path"));
11371
+ var fs13 = __toESM(require("fs"));
11372
+ var path8 = __toESM(require("path"));
11005
11373
  function isInteractive() {
11006
11374
  return process.stdin?.isTTY ?? false;
11007
11375
  }
@@ -11025,12 +11393,12 @@ async function promptForFilePath(variableName, resourceTypes, isRequired) {
11025
11393
  const homeDir = process.env.HOME || process.env.USERPROFILE || "";
11026
11394
  resolvedPath = resolvedPath.replace("~", homeDir);
11027
11395
  }
11028
- resolvedPath = path7.resolve(resolvedPath);
11029
- if (!fs12.existsSync(resolvedPath)) {
11396
+ resolvedPath = path8.resolve(resolvedPath);
11397
+ if (!fs13.existsSync(resolvedPath)) {
11030
11398
  console.log(` File not found: ${resolvedPath}`);
11031
11399
  continue;
11032
11400
  }
11033
- const stats = fs12.statSync(resolvedPath);
11401
+ const stats = fs13.statSync(resolvedPath);
11034
11402
  if (!stats.isFile()) {
11035
11403
  console.log(` Not a file: ${resolvedPath}`);
11036
11404
  continue;
@@ -11044,12 +11412,12 @@ async function promptForFilePath(variableName, resourceTypes, isRequired) {
11044
11412
 
11045
11413
  // src/utils/file-type.ts
11046
11414
  init_cjs_shims();
11047
- var path8 = __toESM(require("path"));
11415
+ var path9 = __toESM(require("path"));
11048
11416
  var IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico", "tiff", "tif"];
11049
11417
  var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "avi", "mkv", "flv", "wmv", "m4v"];
11050
11418
  var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "m4a", "aac", "wma", "opus"];
11051
11419
  function determineFileType(filePath, mimeType) {
11052
- const ext = path8.extname(filePath).slice(1).toLowerCase();
11420
+ const ext = path9.extname(filePath).slice(1).toLowerCase();
11053
11421
  if (IMAGE_EXTENSIONS.includes(ext)) {
11054
11422
  return "image";
11055
11423
  }
@@ -11104,12 +11472,12 @@ async function pollToolsStatus(workflowId, maxWaitTime = 15 * 60 * 1e3, pollInte
11104
11472
  );
11105
11473
  previousRemainingCount = remainingCount;
11106
11474
  }
11107
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
11475
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
11108
11476
  } catch (error) {
11109
11477
  console.log(`
11110
11478
  \u26A0\uFE0F Failed to check authorization status: ${error.message}`);
11111
11479
  console.log("Continuing to wait...");
11112
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
11480
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
11113
11481
  }
11114
11482
  }
11115
11483
  console.log("\n\u23F0 Timeout waiting for tool authorization.");
@@ -11149,24 +11517,57 @@ async function collectFileVariables(workflowId, existingInput, noPrompt) {
11149
11517
  if (resourceVars.length === 0) {
11150
11518
  return [];
11151
11519
  }
11152
- const providedIds = new Set(existingInput.map((v) => v.variableId).filter(Boolean));
11153
- const providedNames = new Set(existingInput.map((v) => v.name).filter(Boolean));
11520
+ const invalidFormatVars = [];
11154
11521
  const missingVars = resourceVars.filter((v) => {
11155
- if (v.variableId && providedIds.has(v.variableId)) return false;
11156
- if (v.name && providedNames.has(v.name)) return false;
11157
- return true;
11522
+ const provided = existingInput.find(
11523
+ (input3) => v.variableId && input3.variableId === v.variableId || v.name && input3.name === v.name
11524
+ );
11525
+ if (!provided) {
11526
+ return true;
11527
+ }
11528
+ const values = provided.value;
11529
+ if (!Array.isArray(values) || values.length === 0) {
11530
+ invalidFormatVars.push({
11531
+ name: v.name,
11532
+ reason: "value must be an array with at least one file"
11533
+ });
11534
+ return true;
11535
+ }
11536
+ const hasValidFileId = values.some((val) => {
11537
+ const fileId = val?.resource?.fileId || val?.fileId;
11538
+ return typeof fileId === "string" && fileId.length > 0;
11539
+ });
11540
+ if (!hasValidFileId) {
11541
+ invalidFormatVars.push({
11542
+ name: v.name,
11543
+ reason: "no valid fileId found in value"
11544
+ });
11545
+ return true;
11546
+ }
11547
+ return false;
11158
11548
  });
11159
11549
  if (missingVars.length === 0) {
11160
11550
  return [];
11161
11551
  }
11162
11552
  if (noPrompt || !isInteractive()) {
11163
- const names = missingVars.map((v) => v.name).join(", ");
11164
- throw new CLIError(
11165
- ErrorCodes.INVALID_INPUT,
11166
- `Missing required file variables: ${names}`,
11167
- void 0,
11168
- "Provide files via --input or run interactively without --no-prompt"
11169
- );
11553
+ const missingNames = missingVars.map((v) => v.name);
11554
+ const formatIssues = invalidFormatVars.filter((f) => missingNames.includes(f.name));
11555
+ let message;
11556
+ let hint;
11557
+ if (formatIssues.length > 0) {
11558
+ const details = formatIssues.map((f) => ` - ${f.name}: ${f.reason}`).join("\n");
11559
+ message = `Invalid format for file variables:
11560
+ ${details}`;
11561
+ hint = `For file variables, use format: '{"varName": "df-fileId"}' or '{"varName": [{"fileId": "df-xxx"}]}'`;
11562
+ } else {
11563
+ message = `Missing required file variables: ${missingNames.join(", ")}`;
11564
+ hint = "Provide files via --input or run interactively without --no-prompt";
11565
+ }
11566
+ throw new CLIError(ErrorCodes.INVALID_INPUT, message, void 0, hint, {
11567
+ field: "--input",
11568
+ format: "json-object",
11569
+ example: '{"fileVar": "df-fileId"}'
11570
+ });
11170
11571
  }
11171
11572
  console.log("");
11172
11573
  console.log("This workflow requires file inputs:");
@@ -11180,7 +11581,7 @@ async function collectFileVariables(workflowId, existingInput, noPrompt) {
11180
11581
  if (!filePath) {
11181
11582
  continue;
11182
11583
  }
11183
- const filename = path9.basename(filePath);
11584
+ const filename = path10.basename(filePath);
11184
11585
  process.stdout.write(` Uploading ${filename}...`);
11185
11586
  try {
11186
11587
  const uploadResult = await apiUploadDriveFile(filePath, workflowId);
@@ -11216,18 +11617,79 @@ async function collectFileVariables(workflowId, existingInput, noPrompt) {
11216
11617
  console.log("");
11217
11618
  return uploadedVars;
11218
11619
  }
11219
- async function runWorkflow(workflowId, options) {
11220
- let inputVars = [];
11221
- try {
11222
- const parsed = JSON.parse(options?.input ?? "{}");
11620
+ function convertKeyValueToVariables(obj, workflow) {
11621
+ const variables = [];
11622
+ for (const [name, rawValue] of Object.entries(obj)) {
11623
+ const varDef = workflow?.variables?.find((v) => v.name === name || v.variableId === name);
11624
+ const isResourceVar = varDef?.variableType === "resource";
11625
+ const looksLikeFileId = typeof rawValue === "string" && rawValue.startsWith("df-");
11626
+ const looksLikeFileArray = Array.isArray(rawValue) && rawValue.length > 0 && typeof rawValue[0] === "object" && rawValue[0] !== null && ("fileId" in rawValue[0] || "type" in rawValue[0]);
11627
+ if (isResourceVar || looksLikeFileId || looksLikeFileArray) {
11628
+ let fileValues = [];
11629
+ if (typeof rawValue === "string") {
11630
+ fileValues = [
11631
+ {
11632
+ type: "resource",
11633
+ resource: { fileId: rawValue }
11634
+ }
11635
+ ];
11636
+ } else if (Array.isArray(rawValue)) {
11637
+ fileValues = rawValue.map((item) => ({
11638
+ type: "resource",
11639
+ resource: { fileId: item.fileId || item.resource?.fileId || "" }
11640
+ }));
11641
+ }
11642
+ variables.push({
11643
+ variableId: varDef?.variableId,
11644
+ name,
11645
+ variableType: "resource",
11646
+ value: fileValues,
11647
+ required: varDef?.required
11648
+ });
11649
+ } else {
11650
+ let value = [];
11651
+ if (typeof rawValue === "string") {
11652
+ value = [{ type: "text", text: rawValue }];
11653
+ } else if (Array.isArray(rawValue)) {
11654
+ value = rawValue;
11655
+ }
11656
+ variables.push({
11657
+ variableId: varDef?.variableId,
11658
+ name,
11659
+ variableType: "string",
11660
+ value,
11661
+ required: varDef?.required
11662
+ });
11663
+ }
11664
+ }
11665
+ return variables;
11666
+ }
11667
+ async function runWorkflow(workflowId, options) {
11668
+ let workflow;
11669
+ try {
11670
+ workflow = await apiGetWorkflow(workflowId);
11671
+ } catch {
11672
+ }
11673
+ let inputVars = [];
11674
+ try {
11675
+ const parsed = JSON.parse(options?.input ?? "{}");
11223
11676
  if (Array.isArray(parsed)) {
11224
11677
  inputVars = parsed;
11225
11678
  } else if (parsed.variables && Array.isArray(parsed.variables)) {
11226
11679
  inputVars = parsed.variables;
11680
+ } else if (typeof parsed === "object" && parsed !== null && Object.keys(parsed).length > 0) {
11681
+ inputVars = convertKeyValueToVariables(parsed, workflow);
11227
11682
  }
11228
11683
  } catch {
11229
11684
  fail(ErrorCodes.INVALID_INPUT, "Invalid JSON in --input", {
11230
- hint: 'Ensure the input is valid JSON, e.g., {"variables":[...]}'
11685
+ hint: `Ensure the input is valid JSON. Supported formats:
11686
+ - Simple: '{"varName": "value", "fileVar": "df-xxx"}'
11687
+ - Array: '[{"name": "varName", "value": [...]}]'`,
11688
+ suggestedFix: {
11689
+ field: "--input",
11690
+ format: "json-object | json-array",
11691
+ example: '{"varName": "value", "fileVar": "df-xxx"}'
11692
+ }
11231
11693
  });
11232
11694
  return;
11233
11695
  }
@@ -11322,7 +11784,11 @@ var workflowRunCommand = new Command("run").description("Start a workflow execut
11322
11784
  await runWorkflow(workflowId, options);
11323
11785
  } catch (error) {
11324
11786
  if (error instanceof CLIError) {
11325
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11787
+ fail(error.code, error.message, {
11788
+ details: error.details,
11789
+ hint: error.hint,
11790
+ suggestedFix: error.suggestedFix
11791
+ });
11326
11792
  }
11327
11793
  fail(
11328
11794
  ErrorCodes.INTERNAL_ERROR,
@@ -11365,7 +11831,11 @@ var workflowRunsCommand = new Command("runs").description("List workflow executi
11365
11831
  });
11366
11832
  } catch (error) {
11367
11833
  if (error instanceof CLIError) {
11368
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11834
+ fail(error.code, error.message, {
11835
+ details: error.details,
11836
+ hint: error.hint,
11837
+ suggestedFix: error.suggestedFix
11838
+ });
11369
11839
  }
11370
11840
  fail(
11371
11841
  ErrorCodes.INTERNAL_ERROR,
@@ -11387,7 +11857,11 @@ var workflowAbortCommand = new Command("abort").description("Abort a running wor
11387
11857
  });
11388
11858
  } catch (error) {
11389
11859
  if (error instanceof CLIError) {
11390
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11860
+ fail(error.code, error.message, {
11861
+ details: error.details,
11862
+ hint: error.hint,
11863
+ suggestedFix: error.suggestedFix
11864
+ });
11391
11865
  }
11392
11866
  fail(
11393
11867
  ErrorCodes.INTERNAL_ERROR,
@@ -11471,7 +11945,7 @@ var workflowStatusCommand = new Command("status").description("Get detailed work
11471
11945
  });
11472
11946
  prevStatus = status;
11473
11947
  while (status.status === "init" || status.status === "executing") {
11474
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
11948
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
11475
11949
  status = await fetchStatus();
11476
11950
  if (options.full || hasStatusChanged(prevStatus, status)) {
11477
11951
  if (options.full) {
@@ -11507,7 +11981,11 @@ var workflowStatusCommand = new Command("status").description("Get detailed work
11507
11981
  }
11508
11982
  } catch (error) {
11509
11983
  if (error instanceof CLIError) {
11510
- fail(error.code, error.message, { details: error.details, hint: error.hint });
11984
+ fail(error.code, error.message, {
11985
+ details: error.details,
11986
+ hint: error.hint,
11987
+ suggestedFix: error.suggestedFix
11988
+ });
11511
11989
  }
11512
11990
  fail(
11513
11991
  ErrorCodes.INTERNAL_ERROR,
@@ -11580,7 +12058,11 @@ var workflowDetailCommand = new Command("detail").description("Get detailed work
11580
12058
  });
11581
12059
  } catch (error) {
11582
12060
  if (error instanceof CLIError) {
11583
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12061
+ fail(error.code, error.message, {
12062
+ details: error.details,
12063
+ hint: error.hint,
12064
+ suggestedFix: error.suggestedFix
12065
+ });
11584
12066
  }
11585
12067
  fail(
11586
12068
  ErrorCodes.INTERNAL_ERROR,
@@ -11632,7 +12114,11 @@ var workflowToolcallsCommand = new Command("toolcalls").description("Get all too
11632
12114
  });
11633
12115
  } catch (error) {
11634
12116
  if (error instanceof CLIError) {
11635
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12117
+ fail(error.code, error.message, {
12118
+ details: error.details,
12119
+ hint: error.hint,
12120
+ suggestedFix: error.suggestedFix
12121
+ });
11636
12122
  }
11637
12123
  fail(
11638
12124
  ErrorCodes.INTERNAL_ERROR,
@@ -11664,7 +12150,11 @@ var workflowToolsetKeysCommand = new Command("toolset-keys").description("List a
11664
12150
  });
11665
12151
  } catch (error) {
11666
12152
  if (error instanceof CLIError) {
11667
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12153
+ fail(error.code, error.message, {
12154
+ details: error.details,
12155
+ hint: error.hint,
12156
+ suggestedFix: error.suggestedFix
12157
+ });
11668
12158
  }
11669
12159
  fail(
11670
12160
  ErrorCodes.INTERNAL_ERROR,
@@ -11704,7 +12194,11 @@ var workflowLayoutCommand = new Command("layout").description("Auto-layout workf
11704
12194
  });
11705
12195
  } catch (error) {
11706
12196
  if (error instanceof CLIError) {
11707
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12197
+ fail(error.code, error.message, {
12198
+ details: error.details,
12199
+ hint: error.hint,
12200
+ suggestedFix: error.suggestedFix
12201
+ });
11708
12202
  }
11709
12203
  fail(
11710
12204
  ErrorCodes.INTERNAL_ERROR,
@@ -11751,7 +12245,11 @@ var workflowNodesCommand = new Command("nodes").description("List all nodes in a
11751
12245
  ok("workflow.nodes", output3);
11752
12246
  } catch (error) {
11753
12247
  if (error instanceof CLIError) {
11754
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12248
+ fail(error.code, error.message, {
12249
+ details: error.details,
12250
+ hint: error.hint,
12251
+ suggestedFix: error.suggestedFix
12252
+ });
11755
12253
  }
11756
12254
  fail(
11757
12255
  ErrorCodes.INTERNAL_ERROR,
@@ -11812,7 +12310,11 @@ var workflowNodeGetCommand = new Command("node").description("Get single node in
11812
12310
  ok("workflow.node", output3);
11813
12311
  } catch (error) {
11814
12312
  if (error instanceof CLIError) {
11815
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12313
+ fail(error.code, error.message, {
12314
+ details: error.details,
12315
+ hint: error.hint,
12316
+ suggestedFix: error.suggestedFix
12317
+ });
11816
12318
  }
11817
12319
  fail(
11818
12320
  ErrorCodes.INTERNAL_ERROR,
@@ -11821,6 +12323,289 @@ var workflowNodeGetCommand = new Command("node").description("Get single node in
11821
12323
  }
11822
12324
  });
11823
12325
 
12326
+ // src/commands/workflow/node-add.ts
12327
+ init_cjs_shims();
12328
+ var workflowNodeAddCommand = new Command("node-add").description("Add a node to a workflow").argument("<workflowId>", "Workflow ID (c-xxx)").requiredOption("--type <type>", "Node type (skillResponse, start, document, resource, memo)").option("--id <nodeId>", "Custom node ID (auto-generated if not provided)").option("--query <query>", "Query/prompt for the node").option("--title <title>", "Node title").option("--toolset-keys <keys>", 'Comma-separated toolset keys (e.g., "web_search,execute_code")').option("--position <x,y>", 'Node position as "x,y" (default: 0,0)').option("--connect-from <nodeId>", "Connect from this node (creates edge)").option("--connect-to <nodeId>", "Connect to this node (creates edge)").option("--resolve-toolset-keys", "Resolve toolset keys to full IDs").option("--auto-layout", "Enable auto-layout to prevent overlapping").action(async (workflowId, options) => {
12329
+ if (!workflowId.startsWith("c-")) {
12330
+ fail(ErrorCodes.INVALID_INPUT, `Invalid workflow ID: ${workflowId}`, {
12331
+ hint: 'Workflow ID should start with "c-"',
12332
+ suggestedFix: {
12333
+ field: "<workflowId>",
12334
+ format: "c-<id>",
12335
+ example: "c-123456"
12336
+ }
12337
+ });
12338
+ return;
12339
+ }
12340
+ const validTypes = ["skillResponse", "start", "document", "resource", "memo"];
12341
+ if (!validTypes.includes(options.type)) {
12342
+ fail(ErrorCodes.INVALID_INPUT, `Invalid node type: ${options.type}`, {
12343
+ hint: `Valid types: ${validTypes.join(", ")}`,
12344
+ suggestedFix: {
12345
+ field: "--type",
12346
+ format: "string",
12347
+ example: "skillResponse"
12348
+ }
12349
+ });
12350
+ return;
12351
+ }
12352
+ try {
12353
+ const nodeId = options.id || `node-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
12354
+ let position = { x: 0, y: 0 };
12355
+ if (options.position) {
12356
+ const parts = options.position.split(",").map((p) => Number.parseFloat(p.trim()));
12357
+ if (parts.length === 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) {
12358
+ position = { x: parts[0], y: parts[1] };
12359
+ }
12360
+ }
12361
+ const metadata = {};
12362
+ if (options.query) {
12363
+ metadata.query = options.query;
12364
+ }
12365
+ if (options.title) {
12366
+ metadata.title = options.title;
12367
+ }
12368
+ if (options.toolsetKeys) {
12369
+ metadata.toolsetKeys = options.toolsetKeys.split(",").map((k) => k.trim());
12370
+ }
12371
+ const node = {
12372
+ id: nodeId,
12373
+ type: options.type,
12374
+ position,
12375
+ data: {
12376
+ metadata
12377
+ }
12378
+ };
12379
+ const operations = [
12380
+ {
12381
+ type: "add_node",
12382
+ node
12383
+ }
12384
+ ];
12385
+ if (options.connectFrom) {
12386
+ operations.push({
12387
+ type: "add_edge",
12388
+ edge: {
12389
+ id: `edge-${options.connectFrom}-${nodeId}`,
12390
+ source: options.connectFrom,
12391
+ target: nodeId
12392
+ }
12393
+ });
12394
+ }
12395
+ if (options.connectTo) {
12396
+ operations.push({
12397
+ type: "add_edge",
12398
+ edge: {
12399
+ id: `edge-${nodeId}-${options.connectTo}`,
12400
+ source: nodeId,
12401
+ target: options.connectTo
12402
+ }
12403
+ });
12404
+ }
12405
+ const body = { operations };
12406
+ const queryParams = [];
12407
+ if (options.resolveToolsetKeys) queryParams.push("resolveToolsetKeys=true");
12408
+ if (options.autoLayout) queryParams.push("autoLayout=true");
12409
+ const queryString = queryParams.length > 0 ? `?${queryParams.join("&")}` : "";
12410
+ await apiRequest(`/v1/cli/workflow/${workflowId}${queryString}`, {
12411
+ method: "PATCH",
12412
+ body
12413
+ });
12414
+ ok("workflow.node.add", {
12415
+ message: "Node added successfully",
12416
+ workflowId,
12417
+ nodeId,
12418
+ type: options.type,
12419
+ position,
12420
+ ...options.query && { query: options.query },
12421
+ ...options.toolsetKeys && {
12422
+ toolsetKeys: options.toolsetKeys.split(",").map((k) => k.trim())
12423
+ },
12424
+ ...options.connectFrom && { connectedFrom: options.connectFrom },
12425
+ ...options.connectTo && { connectedTo: options.connectTo },
12426
+ nextSteps: [
12427
+ `View node: \`refly workflow node ${workflowId} ${nodeId}\``,
12428
+ `List all nodes: \`refly workflow nodes ${workflowId}\``
12429
+ ]
12430
+ });
12431
+ } catch (error) {
12432
+ if (error instanceof CLIError) {
12433
+ fail(error.code, error.message, {
12434
+ details: error.details,
12435
+ hint: error.hint,
12436
+ suggestedFix: error.suggestedFix
12437
+ });
12438
+ return;
12439
+ }
12440
+ fail(
12441
+ ErrorCodes.INTERNAL_ERROR,
12442
+ error instanceof Error ? error.message : "Failed to add node"
12443
+ );
12444
+ }
12445
+ });
12446
+
12447
+ // src/commands/workflow/node-update.ts
12448
+ init_cjs_shims();
12449
+ var workflowNodeUpdateCommand = new Command("node-update").description("Update a node in a workflow").argument("<workflowId>", "Workflow ID (c-xxx)").argument("<nodeId>", "Node ID to update").option("--query <query>", "Update the query/prompt for the node").option("--title <title>", "Update the node title").option("--toolset-keys <keys>", 'Comma-separated toolset keys (e.g., "web_search,execute_code")').option("--data <json>", "Full node data as JSON (advanced usage)").option("--resolve-toolset-keys", "Resolve toolset keys to full IDs").action(async (workflowId, nodeId, options) => {
12450
+ if (!workflowId.startsWith("c-")) {
12451
+ fail(ErrorCodes.INVALID_INPUT, `Invalid workflow ID: ${workflowId}`, {
12452
+ hint: 'Workflow ID should start with "c-"',
12453
+ suggestedFix: {
12454
+ field: "<workflowId>",
12455
+ format: "c-<id>",
12456
+ example: "c-123456"
12457
+ }
12458
+ });
12459
+ return;
12460
+ }
12461
+ try {
12462
+ let nodeData = {};
12463
+ if (options.data) {
12464
+ try {
12465
+ nodeData = JSON.parse(options.data);
12466
+ } catch {
12467
+ fail(ErrorCodes.INVALID_INPUT, "Invalid JSON in --data", {
12468
+ hint: `Data format: '{"data": {"metadata": {"query": "...", "toolsetKeys": ["..."]}}}'`,
12469
+ suggestedFix: {
12470
+ field: "--data",
12471
+ format: "json-object",
12472
+ example: '{"data": {"metadata": {"query": "Search for information"}}}'
12473
+ }
12474
+ });
12475
+ return;
12476
+ }
12477
+ }
12478
+ const metadataUpdates = {};
12479
+ if (options.query) {
12480
+ metadataUpdates.query = options.query;
12481
+ }
12482
+ if (options.title) {
12483
+ metadataUpdates.title = options.title;
12484
+ }
12485
+ if (options.toolsetKeys) {
12486
+ const keys = options.toolsetKeys.split(",").map((k) => k.trim());
12487
+ metadataUpdates.toolsetKeys = keys;
12488
+ }
12489
+ if (Object.keys(metadataUpdates).length > 0) {
12490
+ nodeData = {
12491
+ ...nodeData,
12492
+ data: {
12493
+ ...nodeData.data || {},
12494
+ metadata: {
12495
+ ...nodeData.data?.metadata || {},
12496
+ ...metadataUpdates
12497
+ }
12498
+ }
12499
+ };
12500
+ }
12501
+ if (Object.keys(nodeData).length === 0) {
12502
+ fail(ErrorCodes.INVALID_INPUT, "No update provided", {
12503
+ hint: "Use --query, --title, --toolset-keys, or --data to specify what to update",
12504
+ suggestedFix: {
12505
+ field: "--query",
12506
+ format: "string",
12507
+ example: '--query "Search for the latest news"'
12508
+ }
12509
+ });
12510
+ return;
12511
+ }
12512
+ const body = {
12513
+ operations: [
12514
+ {
12515
+ type: "update_node",
12516
+ nodeId,
12517
+ data: nodeData
12518
+ }
12519
+ ]
12520
+ };
12521
+ const queryParams = options.resolveToolsetKeys ? "?resolveToolsetKeys=true" : "";
12522
+ await apiRequest(`/v1/cli/workflow/${workflowId}${queryParams}`, {
12523
+ method: "PATCH",
12524
+ body
12525
+ });
12526
+ ok("workflow.node.update", {
12527
+ message: "Node updated successfully",
12528
+ workflowId,
12529
+ nodeId,
12530
+ updates: {
12531
+ ...options.query && { query: options.query },
12532
+ ...options.title && { title: options.title },
12533
+ ...options.toolsetKeys && {
12534
+ toolsetKeys: options.toolsetKeys.split(",").map((k) => k.trim())
12535
+ },
12536
+ ...options.data && { data: "custom data applied" }
12537
+ },
12538
+ nextSteps: [
12539
+ `View updated node: \`refly workflow node ${workflowId} ${nodeId}\``,
12540
+ `List all nodes: \`refly workflow nodes ${workflowId}\``
12541
+ ]
12542
+ });
12543
+ } catch (error) {
12544
+ if (error instanceof CLIError) {
12545
+ fail(error.code, error.message, {
12546
+ details: error.details,
12547
+ hint: error.hint,
12548
+ suggestedFix: error.suggestedFix
12549
+ });
12550
+ return;
12551
+ }
12552
+ fail(
12553
+ ErrorCodes.INTERNAL_ERROR,
12554
+ error instanceof Error ? error.message : "Failed to update node"
12555
+ );
12556
+ }
12557
+ });
12558
+
12559
+ // src/commands/workflow/node-delete.ts
12560
+ init_cjs_shims();
12561
+ var workflowNodeDeleteCommand = new Command("node-delete").description("Delete a node from a workflow").argument("<workflowId>", "Workflow ID (c-xxx)").argument("<nodeId>", "Node ID to delete").action(async (workflowId, nodeId) => {
12562
+ if (!workflowId.startsWith("c-")) {
12563
+ fail(ErrorCodes.INVALID_INPUT, `Invalid workflow ID: ${workflowId}`, {
12564
+ hint: 'Workflow ID should start with "c-"',
12565
+ suggestedFix: {
12566
+ field: "<workflowId>",
12567
+ format: "c-<id>",
12568
+ example: "c-123456"
12569
+ }
12570
+ });
12571
+ return;
12572
+ }
12573
+ try {
12574
+ const body = {
12575
+ operations: [
12576
+ {
12577
+ type: "remove_node",
12578
+ nodeId
12579
+ }
12580
+ ]
12581
+ };
12582
+ await apiRequest(`/v1/cli/workflow/${workflowId}`, {
12583
+ method: "PATCH",
12584
+ body
12585
+ });
12586
+ ok("workflow.node.delete", {
12587
+ message: "Node deleted successfully",
12588
+ workflowId,
12589
+ nodeId,
12590
+ note: "Connected edges were also removed",
12591
+ nextSteps: [`List remaining nodes: \`refly workflow nodes ${workflowId}\``]
12592
+ });
12593
+ } catch (error) {
12594
+ if (error instanceof CLIError) {
12595
+ fail(error.code, error.message, {
12596
+ details: error.details,
12597
+ hint: error.hint,
12598
+ suggestedFix: error.suggestedFix
12599
+ });
12600
+ return;
12601
+ }
12602
+ fail(
12603
+ ErrorCodes.INTERNAL_ERROR,
12604
+ error instanceof Error ? error.message : "Failed to delete node"
12605
+ );
12606
+ }
12607
+ });
12608
+
11824
12609
  // src/commands/workflow/node-output.ts
11825
12610
  init_cjs_shims();
11826
12611
  var workflowNodeOutputCommand = new Command("node-output").description("Get node execution output content").argument("<id>", "Workflow ID (c-xxx) or Run ID (we-xxx)").argument("<nodeId>", "Node ID").option("--include-tool-calls", "Include tool call details in output").option("--raw", "Output raw content without formatting").action(async (id, nodeId, options) => {
@@ -11856,7 +12641,11 @@ var workflowNodeOutputCommand = new Command("node-output").description("Get node
11856
12641
  });
11857
12642
  } catch (error) {
11858
12643
  if (error instanceof CLIError) {
11859
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12644
+ fail(error.code, error.message, {
12645
+ details: error.details,
12646
+ hint: error.hint,
12647
+ suggestedFix: error.suggestedFix
12648
+ });
11860
12649
  }
11861
12650
  fail(
11862
12651
  ErrorCodes.INTERNAL_ERROR,
@@ -11868,30 +12657,51 @@ var workflowNodeOutputCommand = new Command("node-output").description("Get node
11868
12657
  }
11869
12658
  });
11870
12659
 
11871
- // src/commands/workflow/patch.ts
12660
+ // src/commands/workflow/edit.ts
11872
12661
  init_cjs_shims();
11873
- var fs13 = __toESM(require("fs"));
11874
- var workflowPatchCommand = new Command("patch").description("Apply semantic patch operations to a workflow plan").argument("<planId>", "Workflow Plan ID").option("--ops <json>", "Operations array as JSON").option("--ops-file <path>", "Read operations from file").option("--update-title <title>", "Shortcut: update workflow title").option("--delete-task <taskId>", "Shortcut: delete a task").option("--delete-variable <variableId>", "Shortcut: delete a variable").action(async (planId, options) => {
12662
+ var fs14 = __toESM(require("fs"));
12663
+ var workflowEditCommand = new Command("edit").description("Edit a workflow plan using semantic operations").argument("<planId>", "Workflow Plan ID").option("--ops <json>", "Operations array as JSON").option("--ops-file <path>", "Read operations from file").option("--update-title <title>", "Shortcut: update workflow title").option("--delete-task <taskId>", "Shortcut: delete a task").option("--delete-variable <variableId>", "Shortcut: delete a variable").action(async (planId, options) => {
11875
12664
  try {
11876
12665
  const operations = [];
12666
+ const opsFormatHint = `Operations format: '[{"op": "updateTitle", "title": "New Title"}]'
12667
+ Available ops: updateTitle, createTask, updateTask, deleteTask, createVariable, updateVariable, deleteVariable
12668
+ Examples:
12669
+ - Update title: {"op": "updateTitle", "title": "New Title"}
12670
+ - Delete task: {"op": "deleteTask", "taskId": "task-id"}
12671
+ - Update task: {"op": "updateTask", "taskId": "task-id", "data": {"prompt": "new prompt"}}`;
11877
12672
  if (options.opsFile) {
11878
12673
  try {
11879
12674
  const filePath = options.opsFile;
11880
- if (!fs13.existsSync(filePath)) {
12675
+ if (!fs14.existsSync(filePath)) {
11881
12676
  fail(ErrorCodes.NOT_FOUND, `Operations file not found: ${filePath}`);
11882
12677
  }
11883
- const fileContent = fs13.readFileSync(filePath, "utf-8");
12678
+ const fileContent = fs14.readFileSync(filePath, "utf-8");
11884
12679
  const fileOps = JSON.parse(fileContent);
11885
12680
  if (Array.isArray(fileOps)) {
11886
12681
  operations.push(...fileOps);
11887
12682
  } else {
11888
- fail(ErrorCodes.INVALID_INPUT, "Operations file must contain a JSON array");
12683
+ fail(ErrorCodes.INVALID_INPUT, "Operations file must contain a JSON array", {
12684
+ hint: opsFormatHint,
12685
+ suggestedFix: {
12686
+ field: "--ops-file",
12687
+ format: "json-array",
12688
+ example: '[{"op": "updateTitle", "title": "New Title"}]'
12689
+ }
12690
+ });
11889
12691
  }
11890
12692
  } catch (error) {
11891
12693
  if (error instanceof CLIError) throw error;
11892
12694
  fail(
11893
12695
  ErrorCodes.INVALID_INPUT,
11894
- `Failed to parse operations file: ${error.message}`
12696
+ `Failed to parse operations file: ${error.message}`,
12697
+ {
12698
+ hint: opsFormatHint,
12699
+ suggestedFix: {
12700
+ field: "--ops-file",
12701
+ format: "json-array",
12702
+ example: '[{"op": "updateTitle", "title": "New Title"}]'
12703
+ }
12704
+ }
11895
12705
  );
11896
12706
  }
11897
12707
  }
@@ -11901,11 +12711,23 @@ var workflowPatchCommand = new Command("patch").description("Apply semantic patc
11901
12711
  if (Array.isArray(jsonOps)) {
11902
12712
  operations.push(...jsonOps);
11903
12713
  } else {
11904
- fail(ErrorCodes.INVALID_INPUT, "--ops must be a JSON array");
12714
+ fail(ErrorCodes.INVALID_INPUT, "--ops must be a JSON array", {
12715
+ hint: opsFormatHint,
12716
+ suggestedFix: {
12717
+ field: "--ops",
12718
+ format: "json-array",
12719
+ example: '[{"op": "updateTitle", "title": "New Title"}]'
12720
+ }
12721
+ });
11905
12722
  }
11906
12723
  } catch (error) {
11907
12724
  fail(ErrorCodes.INVALID_INPUT, `Invalid JSON in --ops: ${error.message}`, {
11908
- hint: "Ensure the operations are a valid JSON array"
12725
+ hint: opsFormatHint,
12726
+ suggestedFix: {
12727
+ field: "--ops",
12728
+ format: "json-array",
12729
+ example: '[{"op": "updateTitle", "title": "New Title"}]'
12730
+ }
11909
12731
  });
11910
12732
  }
11911
12733
  }
@@ -11929,7 +12751,12 @@ var workflowPatchCommand = new Command("patch").description("Apply semantic patc
11929
12751
  }
11930
12752
  if (operations.length === 0) {
11931
12753
  fail(ErrorCodes.INVALID_INPUT, "No operations provided", {
11932
- hint: "Use --ops, --ops-file, or shortcut options (--update-title, --delete-task, --delete-variable)"
12754
+ hint: "Use --ops, --ops-file, or shortcut options (--update-title, --delete-task, --delete-variable)",
12755
+ suggestedFix: {
12756
+ field: "--ops",
12757
+ format: "json-array",
12758
+ example: '[{"op": "updateTitle", "title": "New Title"}]'
12759
+ }
11933
12760
  });
11934
12761
  }
11935
12762
  const body = {
@@ -11941,7 +12768,7 @@ var workflowPatchCommand = new Command("patch").description("Apply semantic patc
11941
12768
  body
11942
12769
  });
11943
12770
  const plan = response.data ?? response;
11944
- ok("workflow.patch", {
12771
+ ok("workflow.edit", {
11945
12772
  planId,
11946
12773
  operationsApplied: operations.length,
11947
12774
  plan: {
@@ -11960,18 +12787,22 @@ var workflowPatchCommand = new Command("patch").description("Apply semantic patc
11960
12787
  });
11961
12788
  } catch (error) {
11962
12789
  if (error instanceof CLIError) {
11963
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12790
+ fail(error.code, error.message, {
12791
+ details: error.details,
12792
+ hint: error.hint,
12793
+ suggestedFix: error.suggestedFix
12794
+ });
11964
12795
  return;
11965
12796
  }
11966
12797
  fail(
11967
12798
  ErrorCodes.INTERNAL_ERROR,
11968
- error instanceof Error ? error.message : "Failed to patch workflow plan"
12799
+ error instanceof Error ? error.message : "Failed to edit workflow plan"
11969
12800
  );
11970
12801
  }
11971
12802
  });
11972
12803
 
11973
12804
  // src/commands/workflow/index.ts
11974
- var workflowCommand = new Command("workflow").description("Manage and run workflows").addCommand(workflowCreateCommand).addCommand(workflowGenerateCommand).addCommand(workflowListCommand).addCommand(workflowGetCommand).addCommand(workflowEditCommand).addCommand(workflowDeleteCommand).addCommand(workflowRunCommand).addCommand(workflowRunsCommand).addCommand(workflowStatusCommand).addCommand(workflowDetailCommand).addCommand(workflowToolcallsCommand).addCommand(workflowAbortCommand).addCommand(workflowToolsetKeysCommand).addCommand(workflowLayoutCommand).addCommand(workflowNodesCommand).addCommand(workflowNodeGetCommand).addCommand(workflowNodeOutputCommand).addCommand(workflowPatchCommand);
12805
+ var workflowCommand = new Command("workflow").description("Manage and run workflows").addCommand(workflowCreateCommand).addCommand(workflowGenerateCommand).addCommand(workflowListCommand).addCommand(workflowGetCommand).addCommand(workflowDeleteCommand).addCommand(workflowRunCommand).addCommand(workflowRunsCommand).addCommand(workflowStatusCommand).addCommand(workflowDetailCommand).addCommand(workflowToolcallsCommand).addCommand(workflowAbortCommand).addCommand(workflowToolsetKeysCommand).addCommand(workflowLayoutCommand).addCommand(workflowNodesCommand).addCommand(workflowNodeGetCommand).addCommand(workflowNodeAddCommand).addCommand(workflowNodeUpdateCommand).addCommand(workflowNodeDeleteCommand).addCommand(workflowNodeOutputCommand).addCommand(workflowEditCommand);
11975
12806
 
11976
12807
  // src/commands/tool/index.ts
11977
12808
  init_cjs_shims();
@@ -12003,7 +12834,11 @@ var toolCallsCommand = new Command("calls").description("Get tool execution resu
12003
12834
  });
12004
12835
  } catch (error) {
12005
12836
  if (error instanceof CLIError) {
12006
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12837
+ fail(error.code, error.message, {
12838
+ details: error.details,
12839
+ hint: error.hint,
12840
+ suggestedFix: error.suggestedFix
12841
+ });
12007
12842
  }
12008
12843
  fail(
12009
12844
  ErrorCodes.INTERNAL_ERROR,
@@ -12048,7 +12883,11 @@ var toolGetCommand = new Command("get").description("Get full details for a sing
12048
12883
  });
12049
12884
  } catch (error) {
12050
12885
  if (error instanceof CLIError) {
12051
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12886
+ fail(error.code, error.message, {
12887
+ details: error.details,
12888
+ hint: error.hint,
12889
+ suggestedFix: error.suggestedFix
12890
+ });
12052
12891
  }
12053
12892
  fail(
12054
12893
  ErrorCodes.INTERNAL_ERROR,
@@ -12096,7 +12935,11 @@ var fileListCommand = new Command("list").description("List files").option("--pa
12096
12935
  });
12097
12936
  } catch (error) {
12098
12937
  if (error instanceof CLIError) {
12099
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12938
+ fail(error.code, error.message, {
12939
+ details: error.details,
12940
+ hint: error.hint,
12941
+ suggestedFix: error.suggestedFix
12942
+ });
12100
12943
  }
12101
12944
  fail(
12102
12945
  ErrorCodes.INTERNAL_ERROR,
@@ -12124,7 +12967,11 @@ var fileGetCommand = new Command("get").description("Get file details").argument
12124
12967
  });
12125
12968
  } catch (error) {
12126
12969
  if (error instanceof CLIError) {
12127
- fail(error.code, error.message, { details: error.details, hint: error.hint });
12970
+ fail(error.code, error.message, {
12971
+ details: error.details,
12972
+ hint: error.hint,
12973
+ suggestedFix: error.suggestedFix
12974
+ });
12128
12975
  }
12129
12976
  fail(
12130
12977
  ErrorCodes.INTERNAL_ERROR,
@@ -12135,16 +12982,16 @@ var fileGetCommand = new Command("get").description("Get file details").argument
12135
12982
 
12136
12983
  // src/commands/file/download.ts
12137
12984
  init_cjs_shims();
12138
- var fs14 = __toESM(require("fs"));
12139
- var path10 = __toESM(require("path"));
12985
+ var fs15 = __toESM(require("fs"));
12986
+ var path11 = __toESM(require("path"));
12140
12987
  var fileDownloadCommand = new Command("download").description("Download file to local filesystem").argument("<fileId>", "File ID").option("-o, --output <path>", "Output file path (defaults to original filename)").action(async (fileId, options) => {
12141
12988
  try {
12142
12989
  const { data, filename, contentType, size } = await apiRequestStream(
12143
12990
  `/v1/cli/drive/files/${fileId}/download`
12144
12991
  );
12145
12992
  const outputPath = options.output || filename || `${fileId}`;
12146
- const resolvedPath = path10.resolve(outputPath);
12147
- fs14.writeFileSync(resolvedPath, data);
12993
+ const resolvedPath = path11.resolve(outputPath);
12994
+ fs15.writeFileSync(resolvedPath, data);
12148
12995
  ok("file.download", {
12149
12996
  fileId,
12150
12997
  path: resolvedPath,
@@ -12154,7 +13001,11 @@ var fileDownloadCommand = new Command("download").description("Download file to
12154
13001
  });
12155
13002
  } catch (error) {
12156
13003
  if (error instanceof CLIError) {
12157
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13004
+ fail(error.code, error.message, {
13005
+ details: error.details,
13006
+ hint: error.hint,
13007
+ suggestedFix: error.suggestedFix
13008
+ });
12158
13009
  }
12159
13010
  fail(
12160
13011
  ErrorCodes.INTERNAL_ERROR,
@@ -12165,8 +13016,8 @@ var fileDownloadCommand = new Command("download").description("Download file to
12165
13016
 
12166
13017
  // src/commands/file/upload.ts
12167
13018
  init_cjs_shims();
12168
- var fs15 = __toESM(require("fs"));
12169
- var path11 = __toESM(require("path"));
13019
+ var fs16 = __toESM(require("fs"));
13020
+ var path12 = __toESM(require("path"));
12170
13021
  var MAX_FILES = 10;
12171
13022
  function formatSize(bytes) {
12172
13023
  if (bytes < 1024) return `${bytes}B`;
@@ -12176,8 +13027,8 @@ function formatSize(bytes) {
12176
13027
  var fileUploadCommand = new Command("upload").description("Upload file(s) to a canvas").argument("<path>", "File or directory path").requiredOption("--canvas-id <id>", "Canvas ID (required)").option("--filter <extensions>", "Filter by extensions (e.g., pdf,docx,png)").action(async (inputPath, options) => {
12177
13028
  const formatter = getFormatter();
12178
13029
  try {
12179
- const resolvedPath = path11.resolve(inputPath);
12180
- if (!fs15.existsSync(resolvedPath)) {
13030
+ const resolvedPath = path12.resolve(inputPath);
13031
+ if (!fs16.existsSync(resolvedPath)) {
12181
13032
  fail(ErrorCodes.NOT_FOUND, `Path not found: ${inputPath}`, {
12182
13033
  hint: "Check if the file or directory exists"
12183
13034
  });
@@ -12195,8 +13046,8 @@ var fileUploadCommand = new Command("upload").description("Upload file(s) to a c
12195
13046
  const errors = [];
12196
13047
  for (let i = 0; i < files.length; i++) {
12197
13048
  const filePath = files[i];
12198
- const filename = path11.basename(filePath);
12199
- const fileStats = fs15.statSync(filePath);
13049
+ const filename = path12.basename(filePath);
13050
+ const fileStats = fs16.statSync(filePath);
12200
13051
  const sizeStr = formatSize(fileStats.size);
12201
13052
  let currentStage = "presign";
12202
13053
  const updateProgress = () => {
@@ -12264,7 +13115,11 @@ var fileUploadCommand = new Command("upload").description("Upload file(s) to a c
12264
13115
  });
12265
13116
  } catch (error) {
12266
13117
  if (error instanceof CLIError) {
12267
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13118
+ fail(error.code, error.message, {
13119
+ details: error.details,
13120
+ hint: error.hint,
13121
+ suggestedFix: error.suggestedFix
13122
+ });
12268
13123
  }
12269
13124
  fail(
12270
13125
  ErrorCodes.INTERNAL_ERROR,
@@ -12273,11 +13128,11 @@ var fileUploadCommand = new Command("upload").description("Upload file(s) to a c
12273
13128
  }
12274
13129
  });
12275
13130
  function resolveFilesToUpload(inputPath, filter) {
12276
- const stats = fs15.statSync(inputPath);
13131
+ const stats = fs16.statSync(inputPath);
12277
13132
  if (stats.isFile()) {
12278
13133
  if (filter) {
12279
13134
  const filterExts = filter.split(",").map((e) => e.trim().toLowerCase());
12280
- const ext = path11.extname(inputPath).slice(1).toLowerCase();
13135
+ const ext = path12.extname(inputPath).slice(1).toLowerCase();
12281
13136
  if (!filterExts.includes(ext)) {
12282
13137
  return [];
12283
13138
  }
@@ -12285,21 +13140,21 @@ function resolveFilesToUpload(inputPath, filter) {
12285
13140
  return [inputPath];
12286
13141
  }
12287
13142
  if (stats.isDirectory()) {
12288
- const entries = fs15.readdirSync(inputPath);
13143
+ const entries = fs16.readdirSync(inputPath);
12289
13144
  const filterExts = filter?.split(",").map((e) => e.trim().toLowerCase());
12290
- const files = entries.map((e) => path11.join(inputPath, e)).filter((p) => {
13145
+ const files = entries.map((e) => path12.join(inputPath, e)).filter((p) => {
12291
13146
  try {
12292
- return fs15.statSync(p).isFile();
13147
+ return fs16.statSync(p).isFile();
12293
13148
  } catch {
12294
13149
  return false;
12295
13150
  }
12296
13151
  }).filter((p) => {
12297
13152
  if (!filterExts) return true;
12298
- const ext = path11.extname(p).slice(1).toLowerCase();
13153
+ const ext = path12.extname(p).slice(1).toLowerCase();
12299
13154
  return filterExts.includes(ext);
12300
13155
  }).sort((a, b) => {
12301
13156
  try {
12302
- return fs15.statSync(a).size - fs15.statSync(b).size;
13157
+ return fs16.statSync(a).size - fs16.statSync(b).size;
12303
13158
  } catch {
12304
13159
  return 0;
12305
13160
  }
@@ -12344,7 +13199,11 @@ var skillListCommand = new Command("list").description("List skill packages").op
12344
13199
  });
12345
13200
  } catch (error) {
12346
13201
  if (error instanceof CLIError) {
12347
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13202
+ fail(error.code, error.message, {
13203
+ details: error.details,
13204
+ hint: error.hint,
13205
+ suggestedFix: error.suggestedFix
13206
+ });
12348
13207
  return;
12349
13208
  }
12350
13209
  fail(
@@ -12383,7 +13242,11 @@ var skillGetCommand = new Command("get").description("Get skill package details"
12383
13242
  });
12384
13243
  } catch (error) {
12385
13244
  if (error instanceof CLIError) {
12386
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13245
+ fail(error.code, error.message, {
13246
+ details: error.details,
13247
+ hint: error.hint,
13248
+ suggestedFix: error.suggestedFix
13249
+ });
12387
13250
  return;
12388
13251
  }
12389
13252
  fail(
@@ -12395,388 +13258,10 @@ var skillGetCommand = new Command("get").description("Get skill package details"
12395
13258
 
12396
13259
  // src/commands/skill/create.ts
12397
13260
  init_cjs_shims();
12398
-
12399
- // src/skill/storage.ts
12400
- init_cjs_shims();
12401
- var fs16 = __toESM(require("fs"));
12402
- var path12 = __toESM(require("path"));
13261
+ var fs17 = __toESM(require("fs"));
13262
+ init_symlink();
12403
13263
  init_paths();
12404
13264
  init_logger();
12405
-
12406
- // src/skill/types.ts
12407
- init_cjs_shims();
12408
- var SKILL_NAME_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
12409
- var SKILL_NAME_MAX_LENGTH = 64;
12410
- var SKILL_DESCRIPTION_MAX_LENGTH = 200;
12411
- var SKILL_TRIGGERS_MIN = 1;
12412
- var SKILL_TRIGGERS_MAX = 10;
12413
- var REGISTRY_VERSION = 1;
12414
- function isSkillRegistry(obj) {
12415
- return typeof obj === "object" && obj !== null && typeof obj.version === "number" && typeof obj.updatedAt === "string" && Array.isArray(obj.skills);
12416
- }
12417
- function isValidSkillName(name) {
12418
- return typeof name === "string" && name.length >= 1 && name.length <= SKILL_NAME_MAX_LENGTH && SKILL_NAME_REGEX.test(name);
12419
- }
12420
- function createSkillError(code, message, options) {
12421
- return {
12422
- code,
12423
- message,
12424
- details: options?.details,
12425
- suggestions: options?.suggestions
12426
- };
12427
- }
12428
- function validateCommonSkillFields(data, issues) {
12429
- if (typeof data.name !== "string") {
12430
- issues.push({ path: "name", message: "Name is required", value: data.name });
12431
- } else if (!isValidSkillName(data.name)) {
12432
- issues.push({
12433
- path: "name",
12434
- message: `Name must match pattern ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ and be 1-${SKILL_NAME_MAX_LENGTH} chars`,
12435
- value: data.name
12436
- });
12437
- }
12438
- if (typeof data.description !== "string") {
12439
- issues.push({
12440
- path: "description",
12441
- message: "Description is required",
12442
- value: data.description
12443
- });
12444
- } else if (data.description.length > SKILL_DESCRIPTION_MAX_LENGTH) {
12445
- issues.push({
12446
- path: "description",
12447
- message: `Description must be <= ${SKILL_DESCRIPTION_MAX_LENGTH} chars`,
12448
- value: data.description.length
12449
- });
12450
- }
12451
- if (typeof data.workflowId !== "string" || data.workflowId.trim() === "") {
12452
- issues.push({ path: "workflowId", message: "WorkflowId is required", value: data.workflowId });
12453
- }
12454
- validateTriggers(data.triggers, issues);
12455
- }
12456
- function validateTriggers(triggers, issues) {
12457
- if (!Array.isArray(triggers)) {
12458
- issues.push({ path: "triggers", message: "Triggers must be an array", value: triggers });
12459
- return;
12460
- }
12461
- if (triggers.length < SKILL_TRIGGERS_MIN || triggers.length > SKILL_TRIGGERS_MAX) {
12462
- issues.push({
12463
- path: "triggers",
12464
- message: `Triggers must have ${SKILL_TRIGGERS_MIN}-${SKILL_TRIGGERS_MAX} items`,
12465
- value: triggers.length
12466
- });
12467
- }
12468
- for (let i = 0; i < triggers.length; i++) {
12469
- if (typeof triggers[i] !== "string" || triggers[i].trim() === "") {
12470
- issues.push({
12471
- path: `triggers[${i}]`,
12472
- message: "Each trigger must be a non-empty string",
12473
- value: triggers[i]
12474
- });
12475
- }
12476
- }
12477
- }
12478
- function validateOptionalSkillFields(data, issues) {
12479
- if (data.tags !== void 0) {
12480
- if (!Array.isArray(data.tags)) {
12481
- issues.push({ path: "tags", message: "Tags must be an array if provided", value: data.tags });
12482
- } else {
12483
- for (let i = 0; i < data.tags.length; i++) {
12484
- if (typeof data.tags[i] !== "string") {
12485
- issues.push({
12486
- path: `tags[${i}]`,
12487
- message: "Each tag must be a string",
12488
- value: data.tags[i]
12489
- });
12490
- }
12491
- }
12492
- }
12493
- }
12494
- if (data.author !== void 0 && typeof data.author !== "string") {
12495
- issues.push({
12496
- path: "author",
12497
- message: "Author must be a string if provided",
12498
- value: data.author
12499
- });
12500
- }
12501
- if (data.version !== void 0 && typeof data.version !== "string") {
12502
- issues.push({
12503
- path: "version",
12504
- message: "Version must be a string if provided",
12505
- value: data.version
12506
- });
12507
- }
12508
- }
12509
-
12510
- // src/skill/storage.ts
12511
- function getDomainSkillsDir() {
12512
- return path12.join(getClaudeSkillDir(), "domain-skills");
12513
- }
12514
- function getSkillDir(name) {
12515
- if (!isValidSkillName(name)) {
12516
- throw createSkillError("INVALID_NAME" /* INVALID_NAME */, `Invalid skill name: ${name}`, {
12517
- suggestions: [
12518
- "Skill name must match pattern ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
12519
- "Use lowercase letters, numbers, and hyphens only",
12520
- "Name must be 1-64 characters"
12521
- ]
12522
- });
12523
- }
12524
- return path12.join(getDomainSkillsDir(), name);
12525
- }
12526
- function getSkillFilePath(name) {
12527
- return path12.join(getSkillDir(name), "skill.md");
12528
- }
12529
- function getSkillRelativePath(name) {
12530
- return `domain-skills/${name}`;
12531
- }
12532
- function skillExists(name) {
12533
- try {
12534
- const skillDir = getSkillDir(name);
12535
- return fs16.existsSync(skillDir);
12536
- } catch {
12537
- return false;
12538
- }
12539
- }
12540
- function createSkillDir(name) {
12541
- const skillDir = getSkillDir(name);
12542
- const domainSkillsDir = getDomainSkillsDir();
12543
- ensureDir(domainSkillsDir);
12544
- if (fs16.existsSync(skillDir)) {
12545
- throw createSkillError("SKILL_EXISTS" /* SKILL_EXISTS */, `Skill directory already exists: ${name}`, {
12546
- suggestions: ["Choose a different name or delete the existing skill first"]
12547
- });
12548
- }
12549
- fs16.mkdirSync(skillDir, { recursive: true, mode: 493 });
12550
- logger.debug(`Created skill directory: ${skillDir}`);
12551
- return skillDir;
12552
- }
12553
- function writeSkillFile(name, content) {
12554
- const skillDir = getSkillDir(name);
12555
- const skillFile = getSkillFilePath(name);
12556
- if (!fs16.existsSync(skillDir)) {
12557
- createSkillDir(name);
12558
- }
12559
- fs16.writeFileSync(skillFile, content, { encoding: "utf-8", mode: 420 });
12560
- logger.debug(`Wrote skill file: ${skillFile}`);
12561
- return skillFile;
12562
- }
12563
- function readSkillFile(name) {
12564
- const skillFile = getSkillFilePath(name);
12565
- if (!fs16.existsSync(skillFile)) {
12566
- throw createSkillError("SKILL_DIR_NOT_FOUND" /* SKILL_DIR_NOT_FOUND */, `Skill file not found: ${name}`, {
12567
- details: { path: skillFile },
12568
- suggestions: [
12569
- "Check if the skill exists with `refly skill list`",
12570
- "Create a new skill with `refly skill create`"
12571
- ]
12572
- });
12573
- }
12574
- return fs16.readFileSync(skillFile, "utf-8");
12575
- }
12576
- function listSkillDirectories() {
12577
- const domainSkillsDir = getDomainSkillsDir();
12578
- if (!fs16.existsSync(domainSkillsDir)) {
12579
- return [];
12580
- }
12581
- const entries = fs16.readdirSync(domainSkillsDir, { withFileTypes: true });
12582
- return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => isValidSkillName(name));
12583
- }
12584
- function generateSkillTemplate(options) {
12585
- const { name, description, workflowId, triggers, tags, author, version } = options;
12586
- const frontmatterLines = [
12587
- "---",
12588
- `name: ${name}`,
12589
- `description: ${description}`,
12590
- `workflowId: ${workflowId}`,
12591
- "triggers:",
12592
- ...triggers.map((t) => ` - ${t}`)
12593
- ];
12594
- if (tags && tags.length > 0) {
12595
- frontmatterLines.push("tags:");
12596
- frontmatterLines.push(...tags.map((t) => ` - ${t}`));
12597
- }
12598
- if (author) {
12599
- frontmatterLines.push(`author: ${author}`);
12600
- }
12601
- if (version) {
12602
- frontmatterLines.push(`version: ${version}`);
12603
- }
12604
- frontmatterLines.push("---");
12605
- const titleName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
12606
- const content = `
12607
- # ${titleName}
12608
-
12609
- ## Overview
12610
-
12611
- ${description}
12612
-
12613
- ## Quick Start
12614
-
12615
- \`\`\`bash
12616
- refly skill run ${name} --input '{}'
12617
- \`\`\`
12618
-
12619
- ## Workflow
12620
-
12621
- This skill executes workflow \`${workflowId}\`.
12622
-
12623
- ## Input
12624
-
12625
- Provide input as JSON:
12626
-
12627
- \`\`\`json
12628
- {
12629
- // Add your input parameters here
12630
- }
12631
- \`\`\`
12632
-
12633
- ## Output
12634
-
12635
- The skill returns the workflow execution result.
12636
- `;
12637
- return frontmatterLines.join("\n") + content;
12638
- }
12639
- function syncCloudSkillToLocal(options) {
12640
- const { name, description, workflowId, triggers = [], tags, version, skillId } = options;
12641
- const finalTriggers = triggers.length > 0 ? triggers : [name];
12642
- const content = generateSkillTemplate({
12643
- name,
12644
- description: description || `Skill: ${name}`,
12645
- workflowId,
12646
- triggers: finalTriggers,
12647
- tags,
12648
- version
12649
- });
12650
- const filePath = writeSkillFile(name, content);
12651
- const registryEntry = {
12652
- name,
12653
- description: description || `Skill: ${name}`,
12654
- workflowId,
12655
- triggers: finalTriggers,
12656
- path: getSkillRelativePath(name),
12657
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
12658
- source: "refly-cloud",
12659
- tags,
12660
- version,
12661
- skillId
12662
- };
12663
- logger.info(`Synced cloud skill '${name}' to local: ${filePath}`);
12664
- return { filePath, registryEntry };
12665
- }
12666
-
12667
- // src/skill/registry.ts
12668
- init_cjs_shims();
12669
- var fs17 = __toESM(require("fs"));
12670
- var path13 = __toESM(require("path"));
12671
- var crypto2 = __toESM(require("crypto"));
12672
- init_paths();
12673
- init_logger();
12674
- function getRegistryPath() {
12675
- return path13.join(getClaudeSkillDir(), "registry.json");
12676
- }
12677
- function createEmptyRegistry() {
12678
- return {
12679
- version: REGISTRY_VERSION,
12680
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12681
- skills: []
12682
- };
12683
- }
12684
- function readRegistry() {
12685
- const registryPath = getRegistryPath();
12686
- if (!fs17.existsSync(registryPath)) {
12687
- logger.debug("Registry file not found, returning empty registry");
12688
- return createEmptyRegistry();
12689
- }
12690
- try {
12691
- const content = fs17.readFileSync(registryPath, "utf-8");
12692
- const data = JSON.parse(content);
12693
- if (!isSkillRegistry(data)) {
12694
- throw createSkillError("REGISTRY_CORRUPTED" /* REGISTRY_CORRUPTED */, "Registry structure is invalid", {
12695
- suggestions: ["Run `refly skill validate --fix` to repair the registry"]
12696
- });
12697
- }
12698
- logger.debug(`Registry loaded with ${data.skills.length} skills`);
12699
- return data;
12700
- } catch (err) {
12701
- if (err instanceof SyntaxError) {
12702
- throw createSkillError("REGISTRY_CORRUPTED" /* REGISTRY_CORRUPTED */, "Registry JSON is malformed", {
12703
- details: { error: err.message },
12704
- suggestions: ["Run `refly skill validate --fix` to repair the registry"]
12705
- });
12706
- }
12707
- throw err;
12708
- }
12709
- }
12710
- function writeRegistry(registry) {
12711
- const registryPath = getRegistryPath();
12712
- const registryDir = path13.dirname(registryPath);
12713
- ensureDir(registryDir);
12714
- registry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
12715
- const content = JSON.stringify(registry, null, 2);
12716
- const tempPath = path13.join(registryDir, `.registry-${crypto2.randomUUID()}.tmp`);
12717
- try {
12718
- fs17.writeFileSync(tempPath, content, { encoding: "utf-8", mode: 384 });
12719
- fs17.renameSync(tempPath, registryPath);
12720
- logger.debug(`Registry saved with ${registry.skills.length} skills`);
12721
- } catch (err) {
12722
- try {
12723
- if (fs17.existsSync(tempPath)) {
12724
- fs17.unlinkSync(tempPath);
12725
- }
12726
- } catch {
12727
- }
12728
- throw err;
12729
- }
12730
- }
12731
- function findSkill(name) {
12732
- const registry = readRegistry();
12733
- return registry.skills.find((s) => s.name === name) ?? null;
12734
- }
12735
- function addSkill(entry) {
12736
- const registry = readRegistry();
12737
- if (registry.skills.some((s) => s.name === entry.name)) {
12738
- throw createSkillError("SKILL_EXISTS" /* SKILL_EXISTS */, `Skill '${entry.name}' already exists`, {
12739
- suggestions: ["Choose a different name or use `refly skill delete` first"]
12740
- });
12741
- }
12742
- const validationErrors = validateSkillEntry(entry);
12743
- if (validationErrors.length > 0) {
12744
- throw createSkillError("VALIDATION_ERROR" /* VALIDATION_ERROR */, "Invalid skill entry", {
12745
- details: { errors: validationErrors },
12746
- suggestions: validationErrors.map((e) => e.message)
12747
- });
12748
- }
12749
- registry.skills.push(entry);
12750
- writeRegistry(registry);
12751
- logger.info(`Skill '${entry.name}' added to registry`);
12752
- }
12753
- function validateSkillEntry(entry) {
12754
- const issues = [];
12755
- if (!entry || typeof entry !== "object") {
12756
- issues.push({ path: "", message: "Entry must be an object", value: entry });
12757
- return issues;
12758
- }
12759
- const e = entry;
12760
- validateCommonSkillFields(e, issues);
12761
- if (typeof e.path !== "string" || e.path.trim() === "") {
12762
- issues.push({ path: "path", message: "Path is required", value: e.path });
12763
- }
12764
- if (typeof e.createdAt !== "string") {
12765
- issues.push({ path: "createdAt", message: "CreatedAt is required", value: e.createdAt });
12766
- }
12767
- if (e.source !== "local" && e.source !== "refly-cloud") {
12768
- issues.push({
12769
- path: "source",
12770
- message: "Source must be 'local' or 'refly-cloud'",
12771
- value: e.source
12772
- });
12773
- }
12774
- validateOptionalSkillFields(e, issues);
12775
- return issues;
12776
- }
12777
-
12778
- // src/commands/skill/create.ts
12779
- init_logger();
12780
13265
  var skillCreateCommand = new Command("create").description("Create a new skill package with workflow").requiredOption("--name <name>", "Skill package name").option("--version <version>", "Semantic version", "1.0.0").option("--description <description>", "Skill description").option("--triggers <triggers>", "Trigger phrases (comma-separated)").option("--tags <tags>", "Category tags (comma-separated)").option("--workflow <workflowId>", "Bind existing workflow ID").option("--workflow-ids <workflowIds>", "Bind multiple workflow IDs (comma-separated)").option("--workflow-spec <json>", "Workflow spec JSON (structured)").option("--workflow-query <query>", "Natural language workflow description (be specific)").option("--verbose", "Include workflow details in output").action(async (options) => {
12781
13266
  try {
12782
13267
  const hasWorkflowOption = options.workflow || options.workflowIds || options.workflowSpec || options.workflowQuery;
@@ -12818,25 +13303,36 @@ var skillCreateCommand = new Command("create").description("Create a new skill p
12818
13303
  body: input3
12819
13304
  });
12820
13305
  const result = response.payload;
12821
- let localSkillPath;
13306
+ let localPath;
13307
+ let symlinkPath;
12822
13308
  if (result.workflowId) {
12823
13309
  try {
12824
13310
  const localName = options.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
12825
- if (findSkill(localName) || skillExists(localName)) {
13311
+ const skillDir = getReflyDomainSkillDir(localName);
13312
+ const symlinkStatus = isSkillSymlinkValid(localName);
13313
+ if (fs17.existsSync(skillDir) || symlinkStatus.exists) {
12826
13314
  logger.debug(`Local skill '${localName}' already exists, skipping sync`);
12827
13315
  } else {
12828
- const { filePath, registryEntry } = syncCloudSkillToLocal({
13316
+ const skillMdContent = generateReflySkillMd({
12829
13317
  name: localName,
13318
+ displayName: options.name,
12830
13319
  description: input3.description || `Skill: ${options.name}`,
13320
+ skillId: result.skillId,
12831
13321
  workflowId: result.workflowId,
12832
13322
  triggers: input3.triggers,
12833
13323
  tags: input3.tags,
12834
13324
  version: options.version,
12835
- skillId: result.skillId
13325
+ inputSchema: result.inputSchema,
13326
+ outputSchema: result.outputSchema
12836
13327
  });
12837
- addSkill(registryEntry);
12838
- localSkillPath = filePath;
12839
- logger.info(`Created local domain skill: ${localName}`);
13328
+ const symlinkResult = createReflySkillWithSymlink(localName, skillMdContent);
13329
+ if (symlinkResult.success) {
13330
+ localPath = symlinkResult.reflyPath;
13331
+ symlinkPath = symlinkResult.claudePath;
13332
+ logger.info(`Created local domain skill: ${localName}`);
13333
+ } else {
13334
+ logger.warn(`Failed to create local skill: ${symlinkResult.error}`);
13335
+ }
12840
13336
  }
12841
13337
  } catch (syncError) {
12842
13338
  logger.warn(`Failed to sync to local: ${syncError.message}`);
@@ -12850,8 +13346,9 @@ var skillCreateCommand = new Command("create").description("Create a new skill p
12850
13346
  workflowId: result.workflowId,
12851
13347
  url: `${getWebUrl()}/skill/${result.skillId}`
12852
13348
  };
12853
- if (localSkillPath) {
12854
- payload.localSkillPath = localSkillPath;
13349
+ if (localPath) {
13350
+ payload.localPath = localPath;
13351
+ payload.symlinkPath = symlinkPath;
12855
13352
  }
12856
13353
  if (options.verbose) {
12857
13354
  payload.workflowIds = result.workflowIds;
@@ -12860,7 +13357,11 @@ var skillCreateCommand = new Command("create").description("Create a new skill p
12860
13357
  ok("skill.create", payload);
12861
13358
  } catch (error) {
12862
13359
  if (error instanceof CLIError) {
12863
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13360
+ fail(error.code, error.message, {
13361
+ details: error.details,
13362
+ hint: error.hint,
13363
+ suggestedFix: error.suggestedFix
13364
+ });
12864
13365
  return;
12865
13366
  }
12866
13367
  fail(
@@ -12870,37 +13371,188 @@ var skillCreateCommand = new Command("create").description("Create a new skill p
12870
13371
  }
12871
13372
  });
12872
13373
 
12873
- // src/commands/skill/delete.ts
13374
+ // src/commands/skill/update.ts
12874
13375
  init_cjs_shims();
12875
- var skillDeleteCommand = new Command("delete").description("Delete a skill package").argument("<skillId>", "Skill package ID to delete").option("--force", "Skip confirmation").action(async (skillId, _options) => {
13376
+ var fs18 = __toESM(require("fs"));
13377
+ var path13 = __toESM(require("path"));
13378
+ init_paths();
13379
+ init_symlink();
13380
+ var MIN_DESCRIPTION_WORDS = 20;
13381
+ function validateDescription(description, skillName) {
13382
+ if (!description) {
13383
+ return {
13384
+ message: "Skill description is required for Claude Code discovery",
13385
+ hint: `Add a description field with at least ${MIN_DESCRIPTION_WORDS} words to your SKILL.md`,
13386
+ example: generateDescriptionExample(skillName)
13387
+ };
13388
+ }
13389
+ const wordCount = description.trim().split(/\s+/).length;
13390
+ if (wordCount < MIN_DESCRIPTION_WORDS) {
13391
+ return {
13392
+ message: `Description too short (${wordCount} words). Minimum ${MIN_DESCRIPTION_WORDS} words required for Claude Code discovery`,
13393
+ hint: `Expand the description in your SKILL.md to at least ${MIN_DESCRIPTION_WORDS} words`,
13394
+ example: generateDescriptionExample(skillName)
13395
+ };
13396
+ }
13397
+ return null;
13398
+ }
13399
+ function generateDescriptionExample(skillName) {
13400
+ const baseName = skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
13401
+ return `${baseName} automation and processing. Use when Claude needs to: (1) [describe primary use case], (2) [describe secondary use case], or [general catch-all scenario].`;
13402
+ }
13403
+ var skillUpdateCommand = new Command("update").description("Update skill installation from local SKILL.md").requiredOption("--name <name>", "Local skill name (directory in ~/.refly/skills/)").option("--id <installationId>", "Override installation ID (default: from SKILL.md)").option("--skip-description", "Skip description validation").action(async (options) => {
12876
13404
  try {
12877
- await apiRequest(`/v1/skill-packages/${skillId}`, {
12878
- method: "DELETE"
12879
- });
12880
- ok("skill.delete", {
12881
- skillId,
12882
- deleted: true
13405
+ const name = options.name;
13406
+ const skillDir = getReflyDomainSkillDir(name);
13407
+ const skillMdPath = path13.join(skillDir, "SKILL.md");
13408
+ if (!fs18.existsSync(skillMdPath)) {
13409
+ const skillsDir = getReflySkillsDir();
13410
+ fail(ErrorCodes.NOT_FOUND, `SKILL.md not found at ${skillMdPath}`, {
13411
+ hint: `Make sure the skill '${name}' exists in ${skillsDir}/
13412
+
13413
+ To see installed skills: refly skill list`
13414
+ });
13415
+ return;
13416
+ }
13417
+ const skillContent = fs18.readFileSync(skillMdPath, "utf-8");
13418
+ let meta;
13419
+ try {
13420
+ const parsed = parseReflySkillMd(skillContent);
13421
+ meta = parsed.meta;
13422
+ } catch (parseError) {
13423
+ fail(
13424
+ ErrorCodes.INVALID_INPUT,
13425
+ `Failed to parse SKILL.md: ${parseError.message}`,
13426
+ {
13427
+ hint: "Make sure SKILL.md has valid frontmatter with required fields: name, description, skillId, workflowId"
13428
+ }
13429
+ );
13430
+ return;
13431
+ }
13432
+ const installationId = options.id || meta.installationId;
13433
+ if (!installationId) {
13434
+ fail(ErrorCodes.INVALID_INPUT, "Installation ID not found", {
13435
+ hint: "Provide --id <installationId> or ensure your SKILL.md contains the installationId field"
13436
+ });
13437
+ return;
13438
+ }
13439
+ const skipDescription = options.skipDescription;
13440
+ if (!skipDescription) {
13441
+ const descriptionError = validateDescription(meta.description, name);
13442
+ if (descriptionError) {
13443
+ fail(ErrorCodes.VALIDATION_ERROR, descriptionError.message, {
13444
+ hint: descriptionError.hint,
13445
+ recoverable: true,
13446
+ suggestedFix: {
13447
+ field: "description",
13448
+ format: "[What it does]. Use when [scenarios]: (1) [case1], (2) [case2], or [catch-all].",
13449
+ example: descriptionError.example
13450
+ }
13451
+ });
13452
+ return;
13453
+ }
13454
+ }
13455
+ const updatePayload = {
13456
+ ...meta.name && { name: meta.name },
13457
+ ...meta.description && { description: meta.description },
13458
+ ...meta.workflowId && { workflowId: meta.workflowId },
13459
+ ...meta.triggers?.length && { triggers: meta.triggers },
13460
+ ...meta.tags?.length && { tags: meta.tags },
13461
+ ...meta.version && { version: meta.version }
13462
+ };
13463
+ if (Object.keys(updatePayload).length === 0) {
13464
+ fail(ErrorCodes.INVALID_INPUT, "No updateable fields found in SKILL.md", {
13465
+ hint: "Ensure SKILL.md contains fields like name, description, triggers, tags, or version"
13466
+ });
13467
+ return;
13468
+ }
13469
+ const response = await apiRequest(
13470
+ `/v1/skill-installations/${installationId}`,
13471
+ {
13472
+ method: "PATCH",
13473
+ body: updatePayload
13474
+ }
13475
+ );
13476
+ ok("skill.update", {
13477
+ ...response,
13478
+ localName: name,
13479
+ updated: true,
13480
+ fields: Object.keys(updatePayload)
12883
13481
  });
12884
13482
  } catch (error) {
12885
13483
  if (error instanceof CLIError) {
12886
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13484
+ fail(error.code, error.message, {
13485
+ details: error.details,
13486
+ hint: error.hint,
13487
+ suggestedFix: error.suggestedFix
13488
+ });
12887
13489
  return;
12888
13490
  }
12889
13491
  fail(
12890
13492
  ErrorCodes.INTERNAL_ERROR,
12891
- error instanceof Error ? error.message : "Failed to delete skill package"
13493
+ error instanceof Error ? error.message : "Failed to update skill installation"
12892
13494
  );
12893
13495
  }
12894
13496
  });
12895
13497
 
12896
13498
  // src/commands/skill/publish.ts
12897
13499
  init_cjs_shims();
12898
- var skillPublishCommand = new Command("publish").description("Publish a skill package to make it publicly available").argument("<skillId>", "Skill package ID to publish").action(async (skillId) => {
13500
+ var fs19 = __toESM(require("fs"));
13501
+ var path14 = __toESM(require("path"));
13502
+ init_symlink();
13503
+ init_paths();
13504
+ var skillPublishCommand = new Command("publish").description("Publish a skill package using local SKILL.md").option("--id <skillId>", "Skill ID (skp-xxx) - overrides ID from SKILL.md").option("--name <name>", "Local skill name (directory in ~/.refly/skills/)").action(async (options) => {
12899
13505
  try {
13506
+ const skillsDir = getReflySkillsDir();
13507
+ if (!options.name) {
13508
+ fail(ErrorCodes.INVALID_INPUT, "Missing required option: --name", {
13509
+ hint: `The publish command requires --name to locate the local SKILL.md.
13510
+
13511
+ Usage:
13512
+ refly skill publish --name <name>
13513
+ refly skill publish --name <name> --id <skillId> # override skillId
13514
+
13515
+ To find your skill name:
13516
+ refly skill list
13517
+ ls ${skillsDir}/`
13518
+ });
13519
+ return;
13520
+ }
13521
+ const name = options.name;
13522
+ const skillDir = getReflyDomainSkillDir(name);
13523
+ const skillMdPath = path14.join(skillDir, "SKILL.md");
13524
+ if (!fs19.existsSync(skillMdPath)) {
13525
+ fail(ErrorCodes.NOT_FOUND, `SKILL.md not found at ${skillMdPath}`, {
13526
+ hint: `Make sure the skill '${name}' exists in ${skillsDir}/
13527
+
13528
+ To see installed skills: refly skill list
13529
+ To create a new skill: refly skill create --name "${name}" --workflow-query "..."`
13530
+ });
13531
+ return;
13532
+ }
13533
+ const skillContent = fs19.readFileSync(skillMdPath, "utf-8");
13534
+ let parsedSkill;
13535
+ try {
13536
+ parsedSkill = parseReflySkillMd(skillContent);
13537
+ } catch (parseError) {
13538
+ fail(
13539
+ ErrorCodes.INVALID_INPUT,
13540
+ `Failed to parse SKILL.md: ${parseError.message}`,
13541
+ {
13542
+ hint: "Make sure SKILL.md has valid frontmatter with required fields: name, description, skillId, workflowId"
13543
+ }
13544
+ );
13545
+ return;
13546
+ }
13547
+ const { meta } = parsedSkill;
13548
+ const skillId = options.id || meta.skillId;
12900
13549
  const result = await apiRequest(
12901
13550
  `/v1/skill-packages/${skillId}/publish`,
12902
13551
  {
12903
- method: "POST"
13552
+ method: "POST",
13553
+ body: {
13554
+ skillContent
13555
+ }
12904
13556
  }
12905
13557
  );
12906
13558
  ok("skill.publish", {
@@ -12910,11 +13562,18 @@ var skillPublishCommand = new Command("publish").description("Publish a skill pa
12910
13562
  status: result.status,
12911
13563
  isPublic: result.isPublic,
12912
13564
  shareId: result.shareId,
12913
- shareUrl: result.shareId ? `https://refly.ai/skill/${result.shareId}` : void 0
13565
+ shareUrl: result.shareId ? `https://refly.ai/skill/${result.shareId}` : void 0,
13566
+ githubPrUrl: result.githubPrUrl,
13567
+ githubPrNumber: result.githubPrNumber,
13568
+ localPath: skillMdPath
12914
13569
  });
12915
13570
  } catch (error) {
12916
13571
  if (error instanceof CLIError) {
12917
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13572
+ fail(error.code, error.message, {
13573
+ details: error.details,
13574
+ hint: error.hint,
13575
+ suggestedFix: error.suggestedFix
13576
+ });
12918
13577
  return;
12919
13578
  }
12920
13579
  fail(
@@ -12926,19 +13585,72 @@ var skillPublishCommand = new Command("publish").description("Publish a skill pa
12926
13585
 
12927
13586
  // src/commands/skill/unpublish.ts
12928
13587
  init_cjs_shims();
12929
- var skillUnpublishCommand = new Command("unpublish").description("Unpublish a skill package to make it private").argument("<skillId>", "Skill package ID to unpublish").action(async (skillId) => {
13588
+ var fs20 = __toESM(require("fs"));
13589
+ var path15 = __toESM(require("path"));
13590
+ init_symlink();
13591
+ init_paths();
13592
+ var skillUnpublishCommand = new Command("unpublish").description("Unpublish a skill package to make it private").option("--id <skillId>", "Skill ID (skp-xxx)").option("--name <name>", "Local skill name (directory in ~/.refly/skills/)").action(async (options) => {
12930
13593
  try {
13594
+ const skillsDir = getReflySkillsDir();
13595
+ if (!options.id && !options.name) {
13596
+ fail(ErrorCodes.INVALID_INPUT, "Missing required option: --id or --name", {
13597
+ hint: `Usage:
13598
+ refly skill unpublish --name <name>
13599
+ refly skill unpublish --id <skillId>
13600
+
13601
+ To find your skill name:
13602
+ refly skill list
13603
+ ls ${skillsDir}/`
13604
+ });
13605
+ return;
13606
+ }
13607
+ let skillId;
13608
+ let name;
13609
+ if (options.name) {
13610
+ name = options.name;
13611
+ const skillDir = getReflyDomainSkillDir(name);
13612
+ const skillMdPath = path15.join(skillDir, "SKILL.md");
13613
+ if (!fs20.existsSync(skillMdPath)) {
13614
+ fail(ErrorCodes.NOT_FOUND, `SKILL.md not found at ${skillMdPath}`, {
13615
+ hint: `Make sure the skill '${name}' exists in ${skillsDir}/
13616
+
13617
+ To see installed skills: refly skill list`
13618
+ });
13619
+ return;
13620
+ }
13621
+ const skillContent = fs20.readFileSync(skillMdPath, "utf-8");
13622
+ try {
13623
+ const { meta } = parseReflySkillMd(skillContent);
13624
+ skillId = options.id || meta.skillId;
13625
+ } catch (parseError) {
13626
+ fail(
13627
+ ErrorCodes.INVALID_INPUT,
13628
+ `Failed to parse SKILL.md: ${parseError.message}`,
13629
+ {
13630
+ hint: "Make sure SKILL.md has valid frontmatter with required fields: name, description, skillId, workflowId"
13631
+ }
13632
+ );
13633
+ return;
13634
+ }
13635
+ } else {
13636
+ skillId = options.id;
13637
+ }
12931
13638
  await apiRequest(`/v1/skill-packages/${skillId}/unpublish`, {
12932
13639
  method: "POST"
12933
13640
  });
12934
13641
  ok("skill.unpublish", {
13642
+ name,
12935
13643
  skillId,
12936
13644
  status: "draft",
12937
13645
  isPublic: false
12938
13646
  });
12939
13647
  } catch (error) {
12940
13648
  if (error instanceof CLIError) {
12941
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13649
+ fail(error.code, error.message, {
13650
+ details: error.details,
13651
+ hint: error.hint,
13652
+ suggestedFix: error.suggestedFix
13653
+ });
12942
13654
  return;
12943
13655
  }
12944
13656
  fail(
@@ -12950,18 +13662,94 @@ var skillUnpublishCommand = new Command("unpublish").description("Unpublish a sk
12950
13662
 
12951
13663
  // src/commands/skill/run.ts
12952
13664
  init_cjs_shims();
12953
- var skillRunCommand = new Command("run").description("Run an installed skill").argument("<installationId>", "Installation ID").option("--input <json>", "Input JSON for the skill").option("--workflow <skillWorkflowId>", "Run specific workflow only").option("--async", "Run asynchronously").action(async (installationId, options) => {
13665
+ var fs21 = __toESM(require("fs"));
13666
+ var path16 = __toESM(require("path"));
13667
+ init_symlink();
13668
+ init_paths();
13669
+ var skillRunCommand = new Command("run").description("Run an installed skill").option("--id <installationId>", "Installation ID (skpi-xxx)").option("--name <name>", "Local skill name (directory in ~/.refly/skills/)").option("--input <json>", "Input JSON for the skill").option("--workflow <skillWorkflowId>", "Run specific workflow only").option("--async", "Run asynchronously").action(async (options) => {
12954
13670
  try {
13671
+ const skillsDir = getReflySkillsDir();
13672
+ if (!options.id && !options.name) {
13673
+ fail(ErrorCodes.INVALID_INPUT, "Missing required option: --id or --name", {
13674
+ hint: `Usage:
13675
+ refly skill run --name <name>
13676
+ refly skill run --id <installationId>
13677
+
13678
+ To find your skill name:
13679
+ refly skill list
13680
+ ls ${skillsDir}/`
13681
+ });
13682
+ return;
13683
+ }
13684
+ let installationId;
13685
+ let name;
13686
+ let skillId;
13687
+ if (options.name) {
13688
+ name = options.name;
13689
+ const skillDir = getReflyDomainSkillDir(name);
13690
+ const skillMdPath = path16.join(skillDir, "SKILL.md");
13691
+ if (!fs21.existsSync(skillMdPath)) {
13692
+ fail(ErrorCodes.NOT_FOUND, `SKILL.md not found at ${skillMdPath}`, {
13693
+ hint: `Make sure the skill '${name}' exists in ${skillsDir}/
13694
+
13695
+ To see installed skills: refly skill list
13696
+ To install a skill: refly skill install <skillId>`
13697
+ });
13698
+ return;
13699
+ }
13700
+ const skillContent = fs21.readFileSync(skillMdPath, "utf-8");
13701
+ try {
13702
+ const { meta } = parseReflySkillMd(skillContent);
13703
+ skillId = meta.skillId;
13704
+ if (options.id) {
13705
+ installationId = options.id;
13706
+ } else if (meta.installationId) {
13707
+ installationId = meta.installationId;
13708
+ } else {
13709
+ fail(ErrorCodes.INVALID_INPUT, `Skill '${name}' does not have an installationId`, {
13710
+ hint: `This skill may have been created locally but not installed.
13711
+
13712
+ To install: refly skill install ${meta.skillId}`
13713
+ });
13714
+ return;
13715
+ }
13716
+ } catch (parseError) {
13717
+ fail(
13718
+ ErrorCodes.INVALID_INPUT,
13719
+ `Failed to parse SKILL.md: ${parseError.message}`,
13720
+ {
13721
+ hint: "Make sure SKILL.md has valid frontmatter with required fields: name, description, skillId, workflowId"
13722
+ }
13723
+ );
13724
+ return;
13725
+ }
13726
+ } else {
13727
+ installationId = options.id;
13728
+ }
12955
13729
  let input3 = {};
12956
13730
  if (options.input) {
12957
13731
  try {
12958
13732
  input3 = JSON.parse(options.input);
12959
13733
  if (typeof input3 !== "object" || input3 === null || Array.isArray(input3)) {
12960
- fail(ErrorCodes.INVALID_INPUT, "Input must be a JSON object");
13734
+ fail(ErrorCodes.INVALID_INPUT, "Input must be a JSON object", {
13735
+ hint: `Use format: '{"varName": "value", "fileVar": "df-fileId"}'`,
13736
+ suggestedFix: {
13737
+ field: "--input",
13738
+ format: "json-object",
13739
+ example: '{"varName": "value", "fileVar": "df-fileId"}'
13740
+ }
13741
+ });
12961
13742
  return;
12962
13743
  }
12963
13744
  } catch {
12964
- fail(ErrorCodes.INVALID_INPUT, "Invalid JSON input");
13745
+ fail(ErrorCodes.INVALID_INPUT, "Invalid JSON input", {
13746
+ hint: `Ensure the input is valid JSON, e.g., '{"varName": "value"}'`,
13747
+ suggestedFix: {
13748
+ field: "--input",
13749
+ format: "json-object",
13750
+ example: '{"varName": "value"}'
13751
+ }
13752
+ });
12965
13753
  return;
12966
13754
  }
12967
13755
  }
@@ -12976,6 +13764,8 @@ var skillRunCommand = new Command("run").description("Run an installed skill").a
12976
13764
  }
12977
13765
  );
12978
13766
  ok("skill.run", {
13767
+ name,
13768
+ skillId,
12979
13769
  executionId: result.executionId,
12980
13770
  installationId: result.installationId,
12981
13771
  status: result.status,
@@ -12985,7 +13775,11 @@ var skillRunCommand = new Command("run").description("Run an installed skill").a
12985
13775
  });
12986
13776
  } catch (error) {
12987
13777
  if (error instanceof CLIError) {
12988
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13778
+ fail(error.code, error.message, {
13779
+ details: error.details,
13780
+ hint: error.hint,
13781
+ suggestedFix: error.suggestedFix
13782
+ });
12989
13783
  return;
12990
13784
  }
12991
13785
  fail(
@@ -13023,7 +13817,11 @@ var skillSearchCommand = new Command("search").description("Search public skill
13023
13817
  });
13024
13818
  } catch (error) {
13025
13819
  if (error instanceof CLIError) {
13026
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13820
+ fail(error.code, error.message, {
13821
+ details: error.details,
13822
+ hint: error.hint,
13823
+ suggestedFix: error.suggestedFix
13824
+ });
13027
13825
  return;
13028
13826
  }
13029
13827
  fail(
@@ -13035,6 +13833,8 @@ var skillSearchCommand = new Command("search").description("Search public skill
13035
13833
 
13036
13834
  // src/commands/skill/install.ts
13037
13835
  init_cjs_shims();
13836
+ init_symlink();
13837
+ init_logger();
13038
13838
  var skillInstallCommand = new Command("install").description("Install a skill package").argument("<skillId>", "Skill package ID to install").option("--version <version>", "Specific version to install").option("--share-id <shareId>", "Share ID for private skills").option("--config <json>", "Installation config JSON").action(async (skillId, options) => {
13039
13839
  try {
13040
13840
  const body = { skillId };
@@ -13044,7 +13844,14 @@ var skillInstallCommand = new Command("install").description("Install a skill pa
13044
13844
  try {
13045
13845
  body.config = JSON.parse(options.config);
13046
13846
  } catch {
13047
- fail(ErrorCodes.INVALID_INPUT, "Invalid config JSON");
13847
+ fail(ErrorCodes.INVALID_INPUT, "Invalid config JSON", {
13848
+ hint: `Config must be a valid JSON object, e.g., '{"key": "value"}'`,
13849
+ suggestedFix: {
13850
+ field: "--config",
13851
+ format: "json-object",
13852
+ example: '{"key": "value"}'
13853
+ }
13854
+ });
13048
13855
  return;
13049
13856
  }
13050
13857
  }
@@ -13055,6 +13862,37 @@ var skillInstallCommand = new Command("install").description("Install a skill pa
13055
13862
  body
13056
13863
  }
13057
13864
  );
13865
+ let localPath;
13866
+ let symlinkPath;
13867
+ const skillName = result.skillPackage?.name;
13868
+ const workflowId = result.skillPackage?.workflowId;
13869
+ if (skillName && workflowId) {
13870
+ try {
13871
+ const skillMdContent = generateReflySkillMd({
13872
+ name: skillName,
13873
+ displayName: result.skillPackage?.displayName,
13874
+ description: result.skillPackage?.description || `Skill: ${skillName}`,
13875
+ skillId: result.skillId,
13876
+ workflowId,
13877
+ installationId: result.installationId,
13878
+ triggers: result.skillPackage?.triggers,
13879
+ tags: result.skillPackage?.tags,
13880
+ version: result.installedVersion,
13881
+ inputSchema: result.skillPackage?.inputSchema,
13882
+ outputSchema: result.skillPackage?.outputSchema
13883
+ });
13884
+ const symlinkResult = createReflySkillWithSymlink(skillName, skillMdContent);
13885
+ if (symlinkResult.success) {
13886
+ localPath = symlinkResult.reflyPath;
13887
+ symlinkPath = symlinkResult.claudePath;
13888
+ logger.info(`Created local skill: ${localPath}`);
13889
+ } else {
13890
+ logger.warn(`Failed to create local skill: ${symlinkResult.error}`);
13891
+ }
13892
+ } catch (err) {
13893
+ logger.warn(`Failed to create local skill: ${err.message}`);
13894
+ }
13895
+ }
13058
13896
  ok("skill.install", {
13059
13897
  installationId: result.installationId,
13060
13898
  skillId: result.skillId,
@@ -13062,11 +13900,17 @@ var skillInstallCommand = new Command("install").description("Install a skill pa
13062
13900
  skillVersion: result.installedVersion,
13063
13901
  status: result.status,
13064
13902
  config: result.userConfig,
13065
- installedAt: result.createdAt
13903
+ installedAt: result.createdAt,
13904
+ localPath,
13905
+ symlinkPath
13066
13906
  });
13067
13907
  } catch (error) {
13068
13908
  if (error instanceof CLIError) {
13069
- fail(error.code, error.message, { details: error.details, hint: error.hint });
13909
+ fail(error.code, error.message, {
13910
+ details: error.details,
13911
+ hint: error.hint,
13912
+ suggestedFix: error.suggestedFix
13913
+ });
13070
13914
  return;
13071
13915
  }
13072
13916
  fail(
@@ -13078,18 +13922,101 @@ var skillInstallCommand = new Command("install").description("Install a skill pa
13078
13922
 
13079
13923
  // src/commands/skill/uninstall.ts
13080
13924
  init_cjs_shims();
13081
- var skillUninstallCommand = new Command("uninstall").description("Uninstall a skill").argument("<installationId>", "Installation ID to uninstall").option("--force", "Skip confirmation").action(async (installationId, _options) => {
13925
+ var fs22 = __toESM(require("fs"));
13926
+ var path17 = __toESM(require("path"));
13927
+ init_symlink();
13928
+ init_paths();
13929
+ init_logger();
13930
+ var skillUninstallCommand = new Command("uninstall").description("Uninstall a skill").option("--id <installationId>", "Installation ID (skpi-xxx)").option("--name <name>", "Local skill name (directory in ~/.refly/skills/)").option("--force", "Skip confirmation").option("--keep-local", "Keep local skill files and symlink").action(async (options) => {
13082
13931
  try {
13083
- await apiRequest(`/v1/skill-installations/${installationId}`, {
13084
- method: "DELETE"
13085
- });
13932
+ const skillsDir = getReflySkillsDir();
13933
+ if (!options.id && !options.name) {
13934
+ fail(ErrorCodes.INVALID_INPUT, "Missing required option: --id or --name", {
13935
+ hint: `Usage:
13936
+ refly skill uninstall --name <name>
13937
+ refly skill uninstall --id <installationId>
13938
+
13939
+ To find your skill name:
13940
+ refly skill list
13941
+ ls ${skillsDir}/`
13942
+ });
13943
+ return;
13944
+ }
13945
+ let installationId;
13946
+ let skillId;
13947
+ let name;
13948
+ if (options.name) {
13949
+ name = options.name;
13950
+ const skillDir = getReflyDomainSkillDir(name);
13951
+ const skillMdPath = path17.join(skillDir, "SKILL.md");
13952
+ if (!fs22.existsSync(skillMdPath)) {
13953
+ fail(ErrorCodes.NOT_FOUND, `SKILL.md not found at ${skillMdPath}`, {
13954
+ hint: `Make sure the skill '${name}' exists in ${skillsDir}/
13955
+
13956
+ To see installed skills: refly skill list`
13957
+ });
13958
+ return;
13959
+ }
13960
+ const skillContent = fs22.readFileSync(skillMdPath, "utf-8");
13961
+ try {
13962
+ const { meta } = parseReflySkillMd(skillContent);
13963
+ skillId = meta.skillId;
13964
+ installationId = options.id || meta.installationId;
13965
+ } catch (parseError) {
13966
+ fail(
13967
+ ErrorCodes.INVALID_INPUT,
13968
+ `Failed to parse SKILL.md: ${parseError.message}`,
13969
+ {
13970
+ hint: "Make sure SKILL.md has valid frontmatter with required fields: name, description, skillId, workflowId"
13971
+ }
13972
+ );
13973
+ return;
13974
+ }
13975
+ } else {
13976
+ installationId = options.id;
13977
+ }
13978
+ if (installationId) {
13979
+ try {
13980
+ await apiRequest(`/v1/skill-installations/${installationId}`, {
13981
+ method: "DELETE"
13982
+ });
13983
+ } catch (error) {
13984
+ if (error instanceof CLIError && error.code === "NOT_FOUND") {
13985
+ logger.debug("Installation not found on server, proceeding with local cleanup");
13986
+ } else {
13987
+ throw error;
13988
+ }
13989
+ }
13990
+ }
13991
+ let symlinkRemoved = false;
13992
+ let directoryRemoved = false;
13993
+ if (name && !options.keepLocal) {
13994
+ try {
13995
+ const cleanup = deleteDomainSkillWithSymlink(name);
13996
+ symlinkRemoved = cleanup.symlinkRemoved;
13997
+ directoryRemoved = cleanup.directoryRemoved;
13998
+ logger.info(`Cleaned up local skill: ${name}`);
13999
+ } catch (err) {
14000
+ logger.warn(`Failed to clean up local skill: ${err.message}`);
14001
+ }
14002
+ }
13086
14003
  ok("skill.uninstall", {
14004
+ name,
14005
+ skillId,
13087
14006
  installationId,
13088
- uninstalled: true
14007
+ uninstalled: true,
14008
+ localCleanup: {
14009
+ symlinkRemoved,
14010
+ directoryRemoved
14011
+ }
13089
14012
  });
13090
14013
  } catch (error) {
13091
14014
  if (error instanceof CLIError) {
13092
- fail(error.code, error.message, { details: error.details, hint: error.hint });
14015
+ fail(error.code, error.message, {
14016
+ details: error.details,
14017
+ hint: error.hint,
14018
+ suggestedFix: error.suggestedFix
14019
+ });
13093
14020
  return;
13094
14021
  }
13095
14022
  fail(
@@ -13127,7 +14054,11 @@ var skillInstallationsCommand = new Command("installations").description("List i
13127
14054
  });
13128
14055
  } catch (error) {
13129
14056
  if (error instanceof CLIError) {
13130
- fail(error.code, error.message, { details: error.details, hint: error.hint });
14057
+ fail(error.code, error.message, {
14058
+ details: error.details,
14059
+ hint: error.hint,
14060
+ suggestedFix: error.suggestedFix
14061
+ });
13131
14062
  return;
13132
14063
  }
13133
14064
  fail(
@@ -13142,8 +14073,114 @@ init_cjs_shims();
13142
14073
 
13143
14074
  // src/skill/loader.ts
13144
14075
  init_cjs_shims();
14076
+ var fs23 = __toESM(require("fs"));
14077
+ var path18 = __toESM(require("path"));
13145
14078
  var import_gray_matter = __toESM(require("gray-matter"));
13146
14079
  init_logger();
14080
+
14081
+ // src/skill/types.ts
14082
+ init_cjs_shims();
14083
+ var SKILL_NAME_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
14084
+ var SKILL_NAME_MAX_LENGTH = 64;
14085
+ var SKILL_DESCRIPTION_MAX_LENGTH = 200;
14086
+ var SKILL_TRIGGERS_MIN = 1;
14087
+ var SKILL_TRIGGERS_MAX = 10;
14088
+ function isValidSkillName(name) {
14089
+ return typeof name === "string" && name.length >= 1 && name.length <= SKILL_NAME_MAX_LENGTH && SKILL_NAME_REGEX.test(name);
14090
+ }
14091
+ function createSkillError(code, message, options) {
14092
+ return {
14093
+ code,
14094
+ message,
14095
+ details: options?.details,
14096
+ suggestions: options?.suggestions
14097
+ };
14098
+ }
14099
+ function validateCommonSkillFields(data, issues) {
14100
+ if (typeof data.name !== "string") {
14101
+ issues.push({ path: "name", message: "Name is required", value: data.name });
14102
+ } else if (!isValidSkillName(data.name)) {
14103
+ issues.push({
14104
+ path: "name",
14105
+ message: `Name must match pattern ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ and be 1-${SKILL_NAME_MAX_LENGTH} chars`,
14106
+ value: data.name
14107
+ });
14108
+ }
14109
+ if (typeof data.description !== "string") {
14110
+ issues.push({
14111
+ path: "description",
14112
+ message: "Description is required",
14113
+ value: data.description
14114
+ });
14115
+ } else if (data.description.length > SKILL_DESCRIPTION_MAX_LENGTH) {
14116
+ issues.push({
14117
+ path: "description",
14118
+ message: `Description must be <= ${SKILL_DESCRIPTION_MAX_LENGTH} chars`,
14119
+ value: data.description.length
14120
+ });
14121
+ }
14122
+ if (typeof data.workflowId !== "string" || data.workflowId.trim() === "") {
14123
+ issues.push({ path: "workflowId", message: "WorkflowId is required", value: data.workflowId });
14124
+ }
14125
+ validateTriggers(data.triggers, issues);
14126
+ }
14127
+ function validateTriggers(triggers, issues) {
14128
+ if (!Array.isArray(triggers)) {
14129
+ issues.push({ path: "triggers", message: "Triggers must be an array", value: triggers });
14130
+ return;
14131
+ }
14132
+ if (triggers.length < SKILL_TRIGGERS_MIN || triggers.length > SKILL_TRIGGERS_MAX) {
14133
+ issues.push({
14134
+ path: "triggers",
14135
+ message: `Triggers must have ${SKILL_TRIGGERS_MIN}-${SKILL_TRIGGERS_MAX} items`,
14136
+ value: triggers.length
14137
+ });
14138
+ }
14139
+ for (let i = 0; i < triggers.length; i++) {
14140
+ if (typeof triggers[i] !== "string" || triggers[i].trim() === "") {
14141
+ issues.push({
14142
+ path: `triggers[${i}]`,
14143
+ message: "Each trigger must be a non-empty string",
14144
+ value: triggers[i]
14145
+ });
14146
+ }
14147
+ }
14148
+ }
14149
+ function validateOptionalSkillFields(data, issues) {
14150
+ if (data.tags !== void 0) {
14151
+ if (!Array.isArray(data.tags)) {
14152
+ issues.push({ path: "tags", message: "Tags must be an array if provided", value: data.tags });
14153
+ } else {
14154
+ for (let i = 0; i < data.tags.length; i++) {
14155
+ if (typeof data.tags[i] !== "string") {
14156
+ issues.push({
14157
+ path: `tags[${i}]`,
14158
+ message: "Each tag must be a string",
14159
+ value: data.tags[i]
14160
+ });
14161
+ }
14162
+ }
14163
+ }
14164
+ }
14165
+ if (data.author !== void 0 && typeof data.author !== "string") {
14166
+ issues.push({
14167
+ path: "author",
14168
+ message: "Author must be a string if provided",
14169
+ value: data.author
14170
+ });
14171
+ }
14172
+ if (data.version !== void 0 && typeof data.version !== "string") {
14173
+ issues.push({
14174
+ path: "version",
14175
+ message: "Version must be a string if provided",
14176
+ value: data.version
14177
+ });
14178
+ }
14179
+ }
14180
+
14181
+ // src/skill/loader.ts
14182
+ init_paths();
14183
+ init_symlink();
13147
14184
  function parseFrontmatter(content) {
13148
14185
  try {
13149
14186
  const result = (0, import_gray_matter.default)(content);
@@ -13185,23 +14222,6 @@ function toSkillFrontmatter(data) {
13185
14222
  version: data.version
13186
14223
  };
13187
14224
  }
13188
- function loadSkill(name) {
13189
- logger.debug(`Loading skill: ${name}`);
13190
- const content = readSkillFile(name);
13191
- const filePath = getSkillFilePath(name);
13192
- const parsed = parseFrontmatter(content);
13193
- const frontmatter = toSkillFrontmatter(parsed.data);
13194
- if (frontmatter.name !== name) {
13195
- logger.warn(
13196
- `Skill name mismatch: file path says '${name}', frontmatter says '${frontmatter.name}'`
13197
- );
13198
- }
13199
- return {
13200
- frontmatter,
13201
- content: parsed.content,
13202
- filePath
13203
- };
13204
- }
13205
14225
  function extractSkillMetadata(content) {
13206
14226
  try {
13207
14227
  const parsed = parseFrontmatter(content);
@@ -13234,23 +14254,23 @@ function extractSkillMetadata(content) {
13234
14254
 
13235
14255
  // src/commands/skill/validate.ts
13236
14256
  init_paths();
13237
- var fs18 = __toESM(require("fs"));
13238
- var path14 = __toESM(require("path"));
14257
+ var fs24 = __toESM(require("fs"));
14258
+ var path19 = __toESM(require("path"));
13239
14259
  var skillValidateCommand = new Command("validate").description("Validate local skill files").argument("[skillPath]", "Path to skill file or directory (defaults to ~/.refly/skills)").option("--fix", "Attempt to fix common issues").action(async (skillPath, _options) => {
13240
14260
  try {
13241
14261
  const results = [];
13242
14262
  let targetPath;
13243
14263
  if (skillPath) {
13244
- targetPath = path14.resolve(skillPath);
14264
+ targetPath = path19.resolve(skillPath);
13245
14265
  } else {
13246
14266
  await ensureSkillsDir();
13247
14267
  targetPath = getSkillsDir();
13248
14268
  }
13249
- if (!fs18.existsSync(targetPath)) {
14269
+ if (!fs24.existsSync(targetPath)) {
13250
14270
  fail(ErrorCodes.NOT_FOUND, `Path not found: ${targetPath}`);
13251
14271
  return;
13252
14272
  }
13253
- const stats = fs18.statSync(targetPath);
14273
+ const stats = fs24.statSync(targetPath);
13254
14274
  if (stats.isFile()) {
13255
14275
  const result = validateSkillFile(targetPath);
13256
14276
  results.push(result);
@@ -13283,7 +14303,7 @@ var skillValidateCommand = new Command("validate").description("Validate local s
13283
14303
  });
13284
14304
  function validateSkillFile(filePath) {
13285
14305
  try {
13286
- const content = fs18.readFileSync(filePath, "utf-8");
14306
+ const content = fs24.readFileSync(filePath, "utf-8");
13287
14307
  const { frontmatter, issues } = extractSkillMetadata(content);
13288
14308
  const errors = issues.map((i) => `${i.path}: ${i.message}`);
13289
14309
  const warnings = [];
@@ -13309,9 +14329,9 @@ function validateSkillFile(filePath) {
13309
14329
  }
13310
14330
  }
13311
14331
  function findSkillFiles(dir, files = []) {
13312
- const entries = fs18.readdirSync(dir, { withFileTypes: true });
14332
+ const entries = fs24.readdirSync(dir, { withFileTypes: true });
13313
14333
  for (const entry of entries) {
13314
- const fullPath = path14.join(dir, entry.name);
14334
+ const fullPath = path19.join(dir, entry.name);
13315
14335
  if (entry.isDirectory()) {
13316
14336
  if (!entry.name.startsWith(".")) {
13317
14337
  findSkillFiles(fullPath, files);
@@ -13325,85 +14345,76 @@ function findSkillFiles(dir, files = []) {
13325
14345
 
13326
14346
  // src/commands/skill/sync.ts
13327
14347
  init_cjs_shims();
13328
- var skillSyncCommand = new Command("sync").description("Sync local skill registry with filesystem").option("--dry-run", "Show changes without writing registry").option("--prune", "Remove registry entries that have no local files").action(async (options) => {
14348
+ init_symlink();
14349
+ init_paths();
14350
+ var fs25 = __toESM(require("fs"));
14351
+ var path20 = __toESM(require("path"));
14352
+ var skillSyncCommand = new Command("sync").description("Validate and repair skill symlinks").option("--dry-run", "Show issues without making changes").option("--fix", "Attempt to repair broken symlinks").option("--prune", "Remove orphan symlinks (symlinks without source directory)").action(async (options) => {
13329
14353
  try {
13330
- const registry = readRegistry();
13331
- const now = (/* @__PURE__ */ new Date()).toISOString();
13332
- const existingLocal = new Map(
13333
- registry.skills.filter((s) => s.source === "local").map((s) => [s.name, s])
13334
- );
13335
- const nonLocal = registry.skills.filter((s) => s.source !== "local");
13336
- const skillDirs = listSkillDirectories();
14354
+ const symlinks = listSkillSymlinks();
13337
14355
  const warnings = [];
13338
14356
  const errors = [];
13339
- const updatedLocal = [];
13340
- let added = 0;
13341
- let updated = 0;
13342
- let unchanged = 0;
13343
- let skipped = 0;
13344
- for (const dirName of skillDirs) {
13345
- try {
13346
- const loaded = loadSkill(dirName);
13347
- const frontmatter = loaded.frontmatter;
13348
- if (frontmatter.name !== dirName) {
13349
- warnings.push(`Skill name mismatch: dir=${dirName}, frontmatter=${frontmatter.name}`);
13350
- skipped += 1;
13351
- continue;
13352
- }
13353
- const prev = existingLocal.get(frontmatter.name);
13354
- const entry = {
13355
- name: frontmatter.name,
13356
- description: frontmatter.description,
13357
- workflowId: frontmatter.workflowId,
13358
- triggers: frontmatter.triggers,
13359
- path: getSkillRelativePath(frontmatter.name),
13360
- createdAt: prev?.createdAt ?? now,
13361
- updatedAt: now,
13362
- source: "local",
13363
- tags: frontmatter.tags,
13364
- author: frontmatter.author,
13365
- version: frontmatter.version
13366
- };
13367
- updatedLocal.push(entry);
13368
- if (!prev) {
13369
- added += 1;
13370
- } else if (prev.description !== entry.description || prev.workflowId !== entry.workflowId || JSON.stringify(prev.triggers) !== JSON.stringify(entry.triggers) || JSON.stringify(prev.tags ?? []) !== JSON.stringify(entry.tags ?? []) || prev.author !== entry.author || prev.version !== entry.version || prev.path !== entry.path) {
13371
- updated += 1;
13372
- } else {
13373
- unchanged += 1;
14357
+ let valid = 0;
14358
+ let invalid = 0;
14359
+ let repaired = 0;
14360
+ let orphans = 0;
14361
+ for (const symlink of symlinks) {
14362
+ if (symlink.isValid) {
14363
+ valid += 1;
14364
+ } else {
14365
+ invalid += 1;
14366
+ warnings.push(`Invalid symlink: ${symlink.claudePath} -> ${symlink.target}`);
14367
+ if (options.fix && !options.dryRun) {
14368
+ const result = createSkillSymlink(symlink.name);
14369
+ if (result.success) {
14370
+ repaired += 1;
14371
+ } else {
14372
+ errors.push(`Failed to repair ${symlink.name}: ${result.error}`);
14373
+ }
13374
14374
  }
13375
- } catch (err) {
13376
- errors.push(err instanceof Error ? err.message : "Failed to load skill");
13377
- skipped += 1;
13378
14375
  }
13379
14376
  }
13380
- const updatedNames = new Set(updatedLocal.map((s) => s.name));
13381
- const orphans = Array.from(existingLocal.keys()).filter((name) => !updatedNames.has(name));
13382
- const pruned = options.prune ? orphans.length : 0;
13383
- if (!options.prune && orphans.length > 0) {
13384
- warnings.push(`Orphan registry entries (no local files): ${orphans.join(", ")}`);
14377
+ const skillsDir = getReflySkillsDir();
14378
+ if (fs25.existsSync(skillsDir)) {
14379
+ const entries = fs25.readdirSync(skillsDir, { withFileTypes: true });
14380
+ const symlinkNames = new Set(symlinks.map((s) => s.name));
14381
+ for (const entry of entries) {
14382
+ if (entry.isDirectory() && entry.name !== "base") {
14383
+ if (!symlinkNames.has(entry.name)) {
14384
+ orphans += 1;
14385
+ warnings.push(`Orphan directory (no symlink): ${path20.join(skillsDir, entry.name)}`);
14386
+ if (options.prune && !options.dryRun) {
14387
+ const result = createSkillSymlink(entry.name);
14388
+ if (result.success) {
14389
+ repaired += 1;
14390
+ }
14391
+ }
14392
+ }
14393
+ }
14394
+ }
13385
14395
  }
13386
- const preservedLocal = options.prune ? [] : registry.skills.filter((s) => s.source === "local" && orphans.includes(s.name));
13387
- const finalSkills = [...nonLocal, ...updatedLocal, ...preservedLocal];
13388
- if (!options.dryRun) {
13389
- writeRegistry({
13390
- ...registry,
13391
- skills: finalSkills,
13392
- updatedAt: now
13393
- });
14396
+ for (const symlink of symlinks) {
14397
+ if (!symlink.isValid && options.prune && !options.dryRun) {
14398
+ removeSkillSymlink(symlink.name);
14399
+ }
13394
14400
  }
13395
14401
  const summary = {
13396
- scanned: skillDirs.length,
13397
- added,
13398
- updated,
13399
- unchanged,
13400
- skipped,
13401
- pruned,
14402
+ scanned: symlinks.length,
14403
+ valid,
14404
+ invalid,
14405
+ repaired,
14406
+ orphans,
13402
14407
  warnings: warnings.length
13403
14408
  };
13404
14409
  ok("skill.sync", {
13405
14410
  dryRun: Boolean(options.dryRun),
13406
14411
  summary,
14412
+ symlinks: symlinks.map((s) => ({
14413
+ name: s.name,
14414
+ claudePath: s.claudePath,
14415
+ target: s.target,
14416
+ valid: s.isValid
14417
+ })),
13407
14418
  warnings: warnings.length ? warnings : void 0,
13408
14419
  errors: errors.length ? errors : void 0
13409
14420
  });
@@ -13416,13 +14427,13 @@ var skillSyncCommand = new Command("sync").description("Sync local skill registr
13416
14427
  });
13417
14428
 
13418
14429
  // src/commands/skill/index.ts
13419
- var skillCommand = new Command("skill").description("Manage skill packages and local skills").addCommand(skillListCommand).addCommand(skillGetCommand).addCommand(skillCreateCommand).addCommand(skillDeleteCommand).addCommand(skillPublishCommand).addCommand(skillUnpublishCommand).addCommand(skillSearchCommand).addCommand(skillInstallCommand).addCommand(skillUninstallCommand).addCommand(skillInstallationsCommand).addCommand(skillRunCommand).addCommand(skillValidateCommand).addCommand(skillSyncCommand);
14430
+ var skillCommand = new Command("skill").description("Manage skill packages and local skills").addCommand(skillListCommand).addCommand(skillGetCommand).addCommand(skillCreateCommand).addCommand(skillUpdateCommand).addCommand(skillPublishCommand).addCommand(skillUnpublishCommand).addCommand(skillSearchCommand).addCommand(skillInstallCommand).addCommand(skillUninstallCommand).addCommand(skillInstallationsCommand).addCommand(skillRunCommand).addCommand(skillValidateCommand).addCommand(skillSyncCommand);
13420
14431
 
13421
14432
  // src/bin/refly.ts
13422
14433
  function getVersion() {
13423
14434
  try {
13424
- const pkgPath = path15.join(__dirname, "..", "..", "package.json");
13425
- const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
14435
+ const pkgPath = path21.join(__dirname, "..", "..", "package.json");
14436
+ const pkg = JSON.parse(fs26.readFileSync(pkgPath, "utf-8"));
13426
14437
  return pkg.version || "0.1.0";
13427
14438
  } catch {
13428
14439
  return "0.1.0";