@orth/cli 0.2.2 → 0.2.4

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/README.md CHANGED
@@ -53,13 +53,17 @@ orth api show hunter /v2/domain-search
53
53
 
54
54
  ```bash
55
55
  # GET request with query params
56
- orth api run hunter /v2/domain-search -q domain=stripe.com
56
+ orth run hunter /v2/domain-search -q domain=stripe.com
57
+
58
+ # Multiple query params (two ways)
59
+ orth run searchapi /api/v1/search -q 'engine=amazon_search&q=wireless earbuds'
60
+ orth run searchapi /api/v1/search -q engine=amazon_search -q q=wireless earbuds
57
61
 
58
62
  # POST request with body
59
- orth api run olostep /v1/scrapes --body '{"url": "https://stripe.com"}'
63
+ orth run olostep /v1/scrapes -d '{"url": "https://stripe.com"}'
60
64
 
61
65
  # Raw output for piping
62
- orth api run hunter /v2/domain-search -q domain=stripe.com --raw | jq '.emails'
66
+ orth run hunter /v2/domain-search -q domain=stripe.com --raw | jq '.emails'
63
67
  ```
64
68
 
65
69
  ### Generate Code
@@ -134,6 +138,14 @@ orth skills create owner/repo --path skills/my-skill --ref main
134
138
  orth skills submit ./my-skill
135
139
  orth skills submit --name "My Skill" --tags "react,testing"
136
140
 
141
+ # Update local skill from platform (pull)
142
+ orth skills update owner/my-skill
143
+ orth skills update owner/my-skill ./local-dir --force
144
+
145
+ # Push local changes to platform
146
+ orth skills push owner/my-skill
147
+ orth skills push owner/my-skill ./local-dir
148
+
137
149
  # Request verification (required before discoverability)
138
150
  orth skills request-verification owner/my-skill
139
151
 
@@ -36,7 +36,7 @@ async function apiCommand(slug, path, options) {
36
36
  chalk_1.default.white(api.name || "") +
37
37
  chalk_1.default.gray(` (${api.endpoints?.length || 0} endpoints)`));
38
38
  }
39
- console.log(chalk_1.default.gray("\nRun 'ortho api <slug>' to see endpoints for an API"));
39
+ console.log(chalk_1.default.gray("\nRun 'orth api show <slug>' to see endpoints for an API"));
40
40
  return;
41
41
  }
42
42
  if (path) {
@@ -47,11 +47,6 @@ async function apiCommand(slug, path, options) {
47
47
  // Get description from endpoint object if available
48
48
  const desc = data.description || data.endpoint?.description;
49
49
  console.log(chalk_1.default.gray(desc || "No description"));
50
- // Get price
51
- const price = data.price || data.endpoint?.price;
52
- if (price) {
53
- console.log(chalk_1.default.green(`\nPrice: ${typeof price === 'number' ? '$' + price : price}`));
54
- }
55
50
  // Get params from nested endpoint object if needed
56
51
  const queryParams = data.parameters?.query || data.endpoint?.queryParams || [];
57
52
  const bodyParams = data.parameters?.body || data.endpoint?.bodyParams || [];
@@ -127,20 +122,19 @@ async function apiCommand(slug, path, options) {
127
122
  const api = data.results.find((a) => a.slug === slug);
128
123
  if (!api) {
129
124
  console.log(chalk_1.default.yellow(`API '${slug}' not found.`));
130
- console.log(chalk_1.default.gray("Run 'ortho api' to see available APIs"));
125
+ console.log(chalk_1.default.gray("Run 'orth api' to see available APIs"));
131
126
  return;
132
127
  }
133
128
  console.log(chalk_1.default.bold(`\n${chalk_1.default.cyan(api.name)} (${api.slug})\n`));
134
129
  for (const endpoint of api.endpoints) {
135
130
  const method = chalk_1.default.yellow(endpoint.method.padEnd(6));
136
- const price = endpoint.price ? chalk_1.default.green(`$${endpoint.price.toFixed(2)}`) : chalk_1.default.gray("free");
137
- console.log(`${method} ${chalk_1.default.white(endpoint.path)} ${price}`);
131
+ console.log(`${method} ${chalk_1.default.white(endpoint.path)}`);
138
132
  if (endpoint.description) {
139
133
  console.log(chalk_1.default.gray(` ${endpoint.description.slice(0, 80)}${endpoint.description.length > 80 ? "..." : ""}`));
140
134
  }
141
135
  }
142
- console.log(chalk_1.default.gray("\nRun 'ortho api " + slug + " <path>' for endpoint details"));
143
- console.log(chalk_1.default.gray("Run 'ortho run " + slug + " <path>' to call an endpoint"));
136
+ console.log(chalk_1.default.gray("\nRun 'orth api show " + slug + " <path>' for endpoint details"));
137
+ console.log(chalk_1.default.gray("Run 'orth run " + slug + " <path>' to call an endpoint"));
144
138
  }
145
139
  catch (error) {
146
140
  spinner.stop();
@@ -11,12 +11,19 @@ async function runCommand(api, path, options) {
11
11
  const spinner = (0, ora_1.default)(`Calling ${api}${path}...`).start();
12
12
  try {
13
13
  // Parse query params
14
+ // Supports both `-q key=value -q key2=value2` and `-q 'key=value&key2=value2'`
14
15
  const query = {};
15
16
  if (options.query) {
16
17
  for (const param of options.query) {
17
- const [key, value] = param.split("=");
18
- if (key && value !== undefined) {
19
- query[key] = value;
18
+ // Split on & to handle URL-style query strings
19
+ const parts = param.split("&");
20
+ for (const part of parts) {
21
+ const eqIndex = part.indexOf("=");
22
+ if (eqIndex > 0) {
23
+ const key = part.slice(0, eqIndex);
24
+ const value = part.slice(eqIndex + 1);
25
+ query[key] = decodeURIComponent(value);
26
+ }
20
27
  }
21
28
  }
22
29
  }
@@ -61,10 +68,6 @@ async function runCommand(api, path, options) {
61
68
  console.log(JSON.stringify(result.data, null, 2));
62
69
  }
63
70
  else {
64
- // Show cost
65
- if (result.price) {
66
- console.log(chalk_1.default.gray(`Cost: $${result.price}`));
67
- }
68
71
  // Pretty print the response
69
72
  console.log(chalk_1.default.bold("\nResponse:\n"));
70
73
  console.log(JSON.stringify(result.data, null, 2));
@@ -21,12 +21,9 @@ async function searchCommand(query, options) {
21
21
  console.log(chalk_1.default.cyan.bold(`${api.name}`) + chalk_1.default.gray(` (${api.slug})`));
22
22
  for (const endpoint of api.endpoints.slice(0, 3)) {
23
23
  const method = endpoint.method.padEnd(6);
24
- const price = endpoint.price ? chalk_1.default.green(`$${endpoint.price.toFixed(2)}`) : chalk_1.default.gray("free");
25
24
  console.log(chalk_1.default.gray(" ") +
26
25
  chalk_1.default.yellow(method) +
27
- chalk_1.default.white(endpoint.path) +
28
- chalk_1.default.gray(" - ") +
29
- price);
26
+ chalk_1.default.white(endpoint.path));
30
27
  if (endpoint.description) {
31
28
  console.log(chalk_1.default.gray(` ${endpoint.description.slice(0, 80)}${endpoint.description.length > 80 ? "..." : ""}`));
32
29
  }
@@ -21,6 +21,13 @@ export declare function skillsSubmitCommand(inputPath: string | undefined, optio
21
21
  name?: string;
22
22
  tags?: string;
23
23
  }): Promise<void>;
24
+ export declare function skillsPushCommand(slug: string, inputPath: string | undefined, options: {
25
+ name?: string;
26
+ tags?: string;
27
+ }): Promise<void>;
28
+ export declare function skillsUpdateCommand(slug: string, inputPath: string | undefined, options: {
29
+ force?: boolean;
30
+ }): Promise<void>;
24
31
  export declare function skillsRequestVerificationCommand(slug: string): Promise<void>;
25
32
  export declare function skillsPublishCommand(slug: string, options: {
26
33
  unpublish?: boolean;
@@ -43,6 +43,8 @@ exports.skillsCreateCommand = skillsCreateCommand;
43
43
  exports.skillsInstallCommand = skillsInstallCommand;
44
44
  exports.skillsInitCommand = skillsInitCommand;
45
45
  exports.skillsSubmitCommand = skillsSubmitCommand;
46
+ exports.skillsPushCommand = skillsPushCommand;
47
+ exports.skillsUpdateCommand = skillsUpdateCommand;
46
48
  exports.skillsRequestVerificationCommand = skillsRequestVerificationCommand;
47
49
  exports.skillsPublishCommand = skillsPublishCommand;
48
50
  exports.skillsRequestCommand = skillsRequestCommand;
@@ -63,7 +65,7 @@ const AGENT_DIRS = {
63
65
  openclaw: path.join(os.homedir(), ".openclaw", "skills"),
64
66
  };
65
67
  // ─────────────────────────────────────────────────────────────────────────────
66
- // ortho skills list
68
+ // orth skills list
67
69
  // ─────────────────────────────────────────────────────────────────────────────
68
70
  async function skillsListCommand(options) {
69
71
  const spinner = (0, ora_1.default)("Loading skills...").start();
@@ -93,8 +95,8 @@ async function skillsListCommand(options) {
93
95
  console.log(chalk_1.default.gray(` Slug: ${skill.slug}`));
94
96
  console.log();
95
97
  }
96
- console.log(chalk_1.default.gray("Run 'ortho skills show <slug>' to see skill details"));
97
- console.log(chalk_1.default.gray("Run 'ortho skills add <slug>' to add a skill locally"));
98
+ console.log(chalk_1.default.gray("Run 'orth skills show <slug>' to see skill details"));
99
+ console.log(chalk_1.default.gray("Run 'orth skills add <slug>' to add a skill locally"));
98
100
  }
99
101
  catch (error) {
100
102
  spinner.stop();
@@ -103,7 +105,7 @@ async function skillsListCommand(options) {
103
105
  }
104
106
  }
105
107
  // ─────────────────────────────────────────────────────────────────────────────
106
- // ortho skills search <query>
108
+ // orth skills search <query>
107
109
  // ─────────────────────────────────────────────────────────────────────────────
108
110
  async function skillsSearchCommand(query, options) {
109
111
  const spinner = (0, ora_1.default)("Searching skills...").start();
@@ -128,7 +130,7 @@ async function skillsSearchCommand(query, options) {
128
130
  console.log(chalk_1.default.gray(` Slug: ${skill.slug}`));
129
131
  console.log();
130
132
  }
131
- console.log(chalk_1.default.gray("Run 'ortho skills show <slug>' for full details"));
133
+ console.log(chalk_1.default.gray("Run 'orth skills show <slug>' for full details"));
132
134
  }
133
135
  catch (error) {
134
136
  spinner.stop();
@@ -137,7 +139,7 @@ async function skillsSearchCommand(query, options) {
137
139
  }
138
140
  }
139
141
  // ─────────────────────────────────────────────────────────────────────────────
140
- // ortho skills show <slug>
142
+ // orth skills show <slug>
141
143
  // ─────────────────────────────────────────────────────────────────────────────
142
144
  async function skillsShowCommand(slug) {
143
145
  const spinner = (0, ora_1.default)("Loading skill...").start();
@@ -191,7 +193,7 @@ async function skillsShowCommand(slug) {
191
193
  console.log(chalk_1.default.bold("Install:"));
192
194
  console.log(chalk_1.default.white(` ${skill.installCommand}`));
193
195
  }
194
- console.log(chalk_1.default.gray(`\nRun 'ortho skills add ${skill.slug}' to add locally`));
196
+ console.log(chalk_1.default.gray(`\nRun 'orth skills add ${skill.slug}' to add locally`));
195
197
  }
196
198
  catch (error) {
197
199
  spinner.stop();
@@ -200,7 +202,7 @@ async function skillsShowCommand(slug) {
200
202
  }
201
203
  }
202
204
  // ─────────────────────────────────────────────────────────────────────────────
203
- // ortho skills create <githubRepo>
205
+ // orth skills create <githubRepo>
204
206
  // ─────────────────────────────────────────────────────────────────────────────
205
207
  async function skillsCreateCommand(githubRepo, options) {
206
208
  const spinner = (0, ora_1.default)("Creating skill from GitHub...").start();
@@ -242,7 +244,7 @@ async function skillsCreateCommand(githubRepo, options) {
242
244
  console.log(chalk_1.default.gray(` ${data.skill.description}`));
243
245
  }
244
246
  console.log(chalk_1.default.bold("\nYour skill is on the platform but not yet verified."));
245
- console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`ortho skills request-verification ${data.skill.slug}`)}`));
247
+ console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`orth skills request-verification ${data.skill.slug}`)}`));
246
248
  console.log(chalk_1.default.gray("Once verified, you can toggle discoverability anytime."));
247
249
  }
248
250
  catch (error) {
@@ -252,7 +254,7 @@ async function skillsCreateCommand(githubRepo, options) {
252
254
  }
253
255
  }
254
256
  // ─────────────────────────────────────────────────────────────────────────────
255
- // ortho skills add <slug>
257
+ // orth skills add <slug>
256
258
  // ─────────────────────────────────────────────────────────────────────────────
257
259
  async function skillsInstallCommand(slug, options) {
258
260
  const spinner = (0, ora_1.default)("Fetching skill...").start();
@@ -333,7 +335,7 @@ async function skillsInstallCommand(slug, options) {
333
335
  }
334
336
  }
335
337
  // ─────────────────────────────────────────────────────────────────────────────
336
- // ortho skills init [name]
338
+ // orth skills init [name]
337
339
  // ─────────────────────────────────────────────────────────────────────────────
338
340
  // Known binary/large file extensions to skip when reading local files
339
341
  const SKIP_EXTENSIONS = new Set([
@@ -419,7 +421,7 @@ Describe the next step.
419
421
  console.log(chalk_1.default.bold("\nNext steps:"));
420
422
  console.log(chalk_1.default.white(" 1. Edit SKILL.md with your skill's instructions"));
421
423
  console.log(chalk_1.default.white(" 2. Add any supporting files to scripts/, references/, or assets/"));
422
- console.log(chalk_1.default.white(` 3. Submit to Orthogonal: ${chalk_1.default.cyan(`ortho skills submit ${dirPath}`)}`));
424
+ console.log(chalk_1.default.white(` 3. Submit to Orthogonal: ${chalk_1.default.cyan(`orth skills submit ${dirPath}`)}`));
423
425
  }
424
426
  catch (error) {
425
427
  console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
@@ -427,7 +429,7 @@ Describe the next step.
427
429
  }
428
430
  }
429
431
  // ─────────────────────────────────────────────────────────────────────────────
430
- // ortho skills submit [path]
432
+ // orth skills submit [path]
431
433
  // ─────────────────────────────────────────────────────────────────────────────
432
434
  function readFilesRecursive(dirPath, basePath = "") {
433
435
  const files = [];
@@ -508,7 +510,7 @@ async function skillsSubmitCommand(inputPath, options) {
508
510
  if (!primaryFile) {
509
511
  spinner.stop();
510
512
  console.error(chalk_1.default.red("Error: No SKILL.md found in the root of the directory"));
511
- console.log(chalk_1.default.gray("Run 'ortho skills init' to create a skill template"));
513
+ console.log(chalk_1.default.gray("Run 'orth skills init' to create a skill template"));
512
514
  process.exit(1);
513
515
  }
514
516
  // Parse frontmatter
@@ -565,7 +567,7 @@ async function skillsSubmitCommand(inputPath, options) {
565
567
  console.log(chalk_1.default.gray(` ${data.skill.description.slice(0, 100)}`));
566
568
  }
567
569
  console.log(chalk_1.default.bold("\nYour skill is on the platform but not yet verified."));
568
- console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`ortho skills request-verification ${data.skill.slug}`)}`));
570
+ console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`orth skills request-verification ${data.skill.slug}`)}`));
569
571
  console.log(chalk_1.default.gray("Once verified, you can toggle discoverability anytime."));
570
572
  console.log(chalk_1.default.gray(`Dashboard: https://orthogonal.com/dashboard/skills`));
571
573
  }
@@ -576,7 +578,154 @@ async function skillsSubmitCommand(inputPath, options) {
576
578
  }
577
579
  }
578
580
  // ─────────────────────────────────────────────────────────────────────────────
579
- // ortho skills request-verification <slug>
581
+ // orth skills push <slug> [path]
582
+ // Push local skill files to remote (replaces remote with local)
583
+ // ─────────────────────────────────────────────────────────────────────────────
584
+ async function skillsPushCommand(slug, inputPath, options) {
585
+ const dirPath = inputPath ? path.resolve(inputPath) : process.cwd();
586
+ const spinner = (0, ora_1.default)("Reading skill files...").start();
587
+ try {
588
+ // Validate directory exists
589
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
590
+ spinner.stop();
591
+ console.error(chalk_1.default.red(`Error: ${dirPath} is not a directory`));
592
+ process.exit(1);
593
+ }
594
+ // Read all files
595
+ const files = readFilesRecursive(dirPath);
596
+ if (files.length === 0) {
597
+ spinner.stop();
598
+ console.error(chalk_1.default.red("Error: No files found in the directory"));
599
+ process.exit(1);
600
+ }
601
+ // Validate SKILL.md exists
602
+ const primaryFile = files.find((f) => f.isPrimary);
603
+ if (!primaryFile) {
604
+ spinner.stop();
605
+ console.error(chalk_1.default.red("Error: No SKILL.md found in the root of the directory"));
606
+ process.exit(1);
607
+ }
608
+ // Parse frontmatter
609
+ // Note: Unlike submit, name/description are optional for updates
610
+ // We only send them if provided (to allow partial updates)
611
+ const frontmatter = parseFrontmatter(primaryFile.content);
612
+ const skillName = options.name || frontmatter.name;
613
+ const skillDescription = frontmatter.description;
614
+ // Check size limits
615
+ const totalSize = files.reduce((acc, f) => acc + f.content.length, 0);
616
+ if (files.length > 50) {
617
+ spinner.stop();
618
+ console.error(chalk_1.default.red("Error: Too many files (max 50)"));
619
+ process.exit(1);
620
+ }
621
+ if (totalSize > 1024 * 1024) {
622
+ spinner.stop();
623
+ console.error(chalk_1.default.red("Error: Total content too large (max 1MB)"));
624
+ process.exit(1);
625
+ }
626
+ spinner.text = "Pushing skill to Orthogonal...";
627
+ const tags = options.tags
628
+ ? options.tags.split(",").map((t) => t.trim())
629
+ : undefined;
630
+ const updatePayload = {
631
+ files: files.map((f) => ({
632
+ filePath: f.filePath,
633
+ content: f.content,
634
+ isPrimary: f.isPrimary,
635
+ })),
636
+ };
637
+ if (skillName)
638
+ updatePayload.name = skillName;
639
+ if (skillDescription)
640
+ updatePayload.description = skillDescription;
641
+ if (tags)
642
+ updatePayload.tags = tags;
643
+ const data = await (0, api_js_1.apiRequest)(`/skills/${slug}`, {
644
+ method: "PUT",
645
+ body: updatePayload,
646
+ });
647
+ spinner.stop();
648
+ console.log(chalk_1.default.green(`\n✓ Skill pushed successfully`));
649
+ console.log(chalk_1.default.bold(`\n${data.skill.name}`));
650
+ console.log(chalk_1.default.gray(` Slug: ${data.skill.slug}`));
651
+ console.log(chalk_1.default.gray(` Files: ${files.length}`));
652
+ if (data.skill.description) {
653
+ console.log(chalk_1.default.gray(` ${data.skill.description.slice(0, 100)}`));
654
+ }
655
+ }
656
+ catch (error) {
657
+ spinner.stop();
658
+ console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
659
+ process.exit(1);
660
+ }
661
+ }
662
+ // ─────────────────────────────────────────────────────────────────────────────
663
+ // orth skills update <slug> [path]
664
+ // Pull latest version from Orthogonal and update local files
665
+ // ─────────────────────────────────────────────────────────────────────────────
666
+ async function skillsUpdateCommand(slug, inputPath, options) {
667
+ const spinner = (0, ora_1.default)(`Fetching skill '${slug}' from Orthogonal...`).start();
668
+ try {
669
+ // Fetch skill from API
670
+ const data = await (0, api_js_1.apiRequest)(`/skills/${slug}`);
671
+ const skill = data.skill;
672
+ if (!skill.files || skill.files.length === 0) {
673
+ spinner.stop();
674
+ console.error(chalk_1.default.red("Error: Skill has no files to download"));
675
+ process.exit(1);
676
+ }
677
+ // Determine target directory
678
+ const skillDirName = slug.replace(/\//g, "-");
679
+ const dirPath = inputPath
680
+ ? path.resolve(inputPath)
681
+ : path.join(process.cwd(), skillDirName);
682
+ // Check if directory exists and has content
683
+ if (fs.existsSync(dirPath) && !options.force) {
684
+ const contents = fs.readdirSync(dirPath);
685
+ if (contents.length > 0) {
686
+ spinner.stop();
687
+ console.log(chalk_1.default.yellow(`Directory ${dirPath} already exists with content.`));
688
+ console.log(chalk_1.default.white(`Use ${chalk_1.default.cyan("--force")} to overwrite existing files.`));
689
+ process.exit(1);
690
+ }
691
+ }
692
+ spinner.text = "Writing files...";
693
+ // Create directory
694
+ fs.mkdirSync(dirPath, { recursive: true });
695
+ // Write all files
696
+ for (const file of skill.files) {
697
+ // Sanitize file path to prevent path traversal
698
+ const sanitized = file.filePath.replace(/\.\.\//g, "").replace(/\.\.\\/g, "");
699
+ const filePath = path.resolve(dirPath, sanitized);
700
+ // Ensure resolved path is within dirPath
701
+ if (!filePath.startsWith(path.resolve(dirPath))) {
702
+ console.log(chalk_1.default.yellow(` Skipped unsafe file path: ${file.filePath}`));
703
+ continue;
704
+ }
705
+ const fileDir = path.dirname(filePath);
706
+ fs.mkdirSync(fileDir, { recursive: true });
707
+ fs.writeFileSync(filePath, file.content, "utf-8");
708
+ }
709
+ spinner.stop();
710
+ console.log(chalk_1.default.green(`\n✓ Skill updated from Orthogonal`));
711
+ console.log(chalk_1.default.bold(`\n${skill.name}`));
712
+ console.log(chalk_1.default.gray(` Path: ${dirPath}`));
713
+ console.log(chalk_1.default.gray(` Files: ${skill.files.length}`));
714
+ console.log(chalk_1.default.bold("\nFiles written:"));
715
+ for (const file of skill.files) {
716
+ const primary = file.isPrimary ? chalk_1.default.green(" (primary)") : "";
717
+ console.log(chalk_1.default.gray(" ") + chalk_1.default.white(file.filePath) + primary);
718
+ }
719
+ console.log(chalk_1.default.gray(`\nTo push changes back: ${chalk_1.default.cyan(`orth skills push ${slug} ${dirPath}`)}`));
720
+ }
721
+ catch (error) {
722
+ spinner.stop();
723
+ console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
724
+ process.exit(1);
725
+ }
726
+ }
727
+ // ─────────────────────────────────────────────────────────────────────────────
728
+ // orth skills request-verification <slug>
580
729
  // ─────────────────────────────────────────────────────────────────────────────
581
730
  async function skillsRequestVerificationCommand(slug) {
582
731
  const spinner = (0, ora_1.default)("Submitting verification request...").start();
@@ -604,16 +753,16 @@ async function skillsRequestVerificationCommand(slug) {
604
753
  }
605
754
  }
606
755
  // ─────────────────────────────────────────────────────────────────────────────
607
- // ortho skills publish <slug> (deprecated - redirects to request-verification)
756
+ // orth skills publish <slug> (deprecated - redirects to request-verification)
608
757
  // ─────────────────────────────────────────────────────────────────────────────
609
758
  async function skillsPublishCommand(slug, options) {
610
759
  console.log(chalk_1.default.yellow("Note: Direct publishing has been replaced with a verification workflow."));
611
- console.log(chalk_1.default.white(`\nTo request your skill to be verified, run:\n ${chalk_1.default.cyan(`ortho skills request-verification ${slug}`)}`));
760
+ console.log(chalk_1.default.white(`\nTo request your skill to be verified, run:\n ${chalk_1.default.cyan(`orth skills request-verification ${slug}`)}`));
612
761
  console.log(chalk_1.default.white("Once verified, you can toggle discoverability from your dashboard."));
613
762
  console.log(chalk_1.default.gray("\nOr manage from the dashboard: https://orthogonal.com/dashboard/skills"));
614
763
  }
615
764
  // ─────────────────────────────────────────────────────────────────────────────
616
- // ortho skills request <input>
765
+ // orth skills request <input>
617
766
  // ─────────────────────────────────────────────────────────────────────────────
618
767
  async function skillsRequestCommand(input) {
619
768
  const spinner = (0, ora_1.default)("Submitting skill request...").start();
package/dist/index.js CHANGED
@@ -175,6 +175,23 @@ skillsGroup
175
175
  (0, analytics_js_1.trackEvent)("skills.submit", { path: inputPath });
176
176
  await (0, skills_js_1.skillsSubmitCommand)(inputPath, options);
177
177
  }));
178
+ skillsGroup
179
+ .command("update <slug> [path]")
180
+ .description("Pull latest skill version from Orthogonal to local directory")
181
+ .option("-f, --force", "Overwrite existing files")
182
+ .action(asyncAction(async (slug, inputPath, options) => {
183
+ (0, analytics_js_1.trackEvent)("skills.update", { slug, path: inputPath });
184
+ await (0, skills_js_1.skillsUpdateCommand)(slug, inputPath, options);
185
+ }));
186
+ skillsGroup
187
+ .command("push <slug> [path]")
188
+ .description("Push local skill files to Orthogonal (update remote)")
189
+ .option("-n, --name <name>", "Override skill name from frontmatter")
190
+ .option("-t, --tags <tags>", "Comma-separated tags")
191
+ .action(asyncAction(async (slug, inputPath, options) => {
192
+ (0, analytics_js_1.trackEvent)("skills.push", { slug, path: inputPath });
193
+ await (0, skills_js_1.skillsPushCommand)(slug, inputPath, options);
194
+ }));
178
195
  skillsGroup
179
196
  .command("request-verification <slug>")
180
197
  .description("Request verification for your skill (required before discoverability)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orth/cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "CLI to access all APIs and skills on the Orthogonal platform",
5
5
  "main": "dist/index.js",
6
6
  "bin": {