@withone/cli 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +393 -250
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -380,6 +380,17 @@ one config
380
380
 
381
381
  Settings propagate automatically to all installed agent configs.
382
382
 
383
+ #### `one config skills status` / `one config skills sync`
384
+
385
+ `one init` copies the packaged skill files (`SKILL.md`, `references/`) into `~/.agents/skills/one/` and symlinks per-agent paths to that canonical directory. When the CLI self-updates, the skill files in the canonical dir would normally stay frozen at the version that was installed. To prevent stale docs, every CLI command checks a `.one-cli-version` marker in the canonical dir and silently refreshes the skill files if they don't match the running CLI version. No user action required.
386
+
387
+ | Command | What it does |
388
+ |---------|--------------|
389
+ | `one config skills status` | Show installed skill version, current CLI version, and path |
390
+ | `one config skills sync` | Force a re-copy of packaged skill files (for troubleshooting) |
391
+
392
+ Auto-sync refuses to resurrect skills if you opted out of skill installation during `one init` — the canonical dir has to already exist.
393
+
383
394
  ## The workflow
384
395
 
385
396
  The power of One is in the workflow. Every interaction follows the same pattern:
package/dist/index.js CHANGED
@@ -25,10 +25,10 @@ import { Command } from "commander";
25
25
  // src/commands/init.ts
26
26
  import * as p3 from "@clack/prompts";
27
27
  import pc2 from "picocolors";
28
- import fs3 from "fs";
29
- import path3 from "path";
30
- import os3 from "os";
31
- import { fileURLToPath } from "url";
28
+ import fs4 from "fs";
29
+ import path4 from "path";
30
+ import os4 from "os";
31
+ import { fileURLToPath as fileURLToPath2 } from "url";
32
32
 
33
33
  // src/lib/config.ts
34
34
  import fs from "fs";
@@ -53,11 +53,11 @@ function readConfig() {
53
53
  return null;
54
54
  }
55
55
  }
56
- function writeConfig(config) {
56
+ function writeConfig(config2) {
57
57
  if (!fs.existsSync(CONFIG_DIR)) {
58
58
  fs.mkdirSync(CONFIG_DIR, { mode: 448 });
59
59
  }
60
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
60
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2), { mode: 384 });
61
61
  }
62
62
  function readOneRc() {
63
63
  const rcPath = path.join(process.cwd(), ".onerc");
@@ -108,32 +108,32 @@ function getAccessControl() {
108
108
  }
109
109
  var DEFAULT_API_BASE = "https://api.withone.ai/v1";
110
110
  function getApiBase() {
111
- const config = readConfig();
112
- if (config?.apiBase) return `${config.apiBase}/v1`;
111
+ const config2 = readConfig();
112
+ if (config2?.apiBase) return `${config2.apiBase}/v1`;
113
113
  return DEFAULT_API_BASE;
114
114
  }
115
115
  function updateApiBase(url) {
116
- const config = readConfig();
117
- if (!config) return;
116
+ const config2 = readConfig();
117
+ if (!config2) return;
118
118
  if (url) {
119
- config.apiBase = url;
119
+ config2.apiBase = url;
120
120
  } else {
121
- delete config.apiBase;
121
+ delete config2.apiBase;
122
122
  }
123
- writeConfig(config);
123
+ writeConfig(config2);
124
124
  }
125
125
  function getCacheTtl() {
126
126
  if (process.env.ONE_CACHE_TTL) {
127
127
  const val = parseInt(process.env.ONE_CACHE_TTL, 10);
128
128
  if (!isNaN(val) && val > 0) return val;
129
129
  }
130
- const config = readConfig();
131
- if (config?.cacheTtl && config.cacheTtl > 0) return config.cacheTtl;
130
+ const config2 = readConfig();
131
+ if (config2?.cacheTtl && config2.cacheTtl > 0) return config2.cacheTtl;
132
132
  return 3600;
133
133
  }
134
134
  function updateAccessControl(settings) {
135
- const config = readConfig();
136
- if (!config) return;
135
+ const config2 = readConfig();
136
+ if (!config2) return;
137
137
  const cleaned = {};
138
138
  if (settings.permissions && settings.permissions !== "admin") {
139
139
  cleaned.permissions = settings.permissions;
@@ -148,11 +148,11 @@ function updateAccessControl(settings) {
148
148
  cleaned.knowledgeAgent = true;
149
149
  }
150
150
  if (Object.keys(cleaned).length === 0) {
151
- delete config.accessControl;
151
+ delete config2.accessControl;
152
152
  } else {
153
- config.accessControl = cleaned;
153
+ config2.accessControl = cleaned;
154
154
  }
155
- writeConfig(config);
155
+ writeConfig(config2);
156
156
  }
157
157
 
158
158
  // src/lib/agents.ts
@@ -274,16 +274,16 @@ function readAgentConfig(agent, scope = "global") {
274
274
  return {};
275
275
  }
276
276
  }
277
- function writeAgentConfig(agent, config, scope = "global") {
277
+ function writeAgentConfig(agent, config2, scope = "global") {
278
278
  const configPath = getAgentConfigPath(agent, scope);
279
279
  const configDir = path2.dirname(configPath);
280
280
  if (!fs2.existsSync(configDir)) {
281
281
  fs2.mkdirSync(configDir, { recursive: true });
282
282
  }
283
283
  if (agent.configFormat === "toml") {
284
- fs2.writeFileSync(configPath, stringifyToml(config));
284
+ fs2.writeFileSync(configPath, stringifyToml(config2));
285
285
  } else {
286
- fs2.writeFileSync(configPath, JSON.stringify(config, null, 2));
286
+ fs2.writeFileSync(configPath, JSON.stringify(config2, null, 2));
287
287
  }
288
288
  }
289
289
  function getMcpServerConfig(apiKey, accessControl) {
@@ -311,17 +311,17 @@ function getMcpServerConfig(apiKey, accessControl) {
311
311
  };
312
312
  }
313
313
  function installMcpConfig(agent, apiKey, scope = "global", accessControl) {
314
- const config = readAgentConfig(agent, scope);
314
+ const config2 = readAgentConfig(agent, scope);
315
315
  const configKey = agent.configKey;
316
- const mcpServers = config[configKey] || {};
316
+ const mcpServers = config2[configKey] || {};
317
317
  mcpServers["one"] = getMcpServerConfig(apiKey, accessControl);
318
- config[configKey] = mcpServers;
319
- writeAgentConfig(agent, config, scope);
318
+ config2[configKey] = mcpServers;
319
+ writeAgentConfig(agent, config2, scope);
320
320
  }
321
321
  function isMcpInstalled(agent, scope = "global") {
322
- const config = readAgentConfig(agent, scope);
322
+ const config2 = readAgentConfig(agent, scope);
323
323
  const configKey = agent.configKey;
324
- const mcpServers = config[configKey];
324
+ const mcpServers = config2[configKey];
325
325
  return mcpServers?.["one"] !== void 0;
326
326
  }
327
327
  function getAgentStatuses() {
@@ -397,8 +397,8 @@ async function configCommand() {
397
397
  if (isAgentMode()) {
398
398
  error("This command requires interactive input. Run without --agent.");
399
399
  }
400
- const config = readConfig();
401
- if (!config) {
400
+ const config2 = readConfig();
401
+ if (!config2) {
402
402
  p2.log.error(`No One config found. Run ${pc.cyan("one init")} first.`);
403
403
  return;
404
404
  }
@@ -439,7 +439,7 @@ async function configCommand() {
439
439
  }
440
440
  let connectionKeys;
441
441
  if (connectionMode === "specific") {
442
- connectionKeys = await selectConnections(config.apiKey);
442
+ connectionKeys = await selectConnections(config2.apiKey);
443
443
  if (connectionKeys === void 0) {
444
444
  p2.outro("No changes made.");
445
445
  return;
@@ -500,7 +500,7 @@ async function configCommand() {
500
500
  p2.outro("No changes made.");
501
501
  return;
502
502
  }
503
- let newApiKey = config.apiKey;
503
+ let newApiKey = config2.apiKey;
504
504
  if (baseUrlMode === "custom") {
505
505
  const customUrl = await p2.text({
506
506
  message: "Enter API base URL:",
@@ -601,7 +601,7 @@ async function configCommand() {
601
601
  };
602
602
  updateAccessControl(settings);
603
603
  const updatedConfig = readConfig();
604
- if (updatedConfig && newApiKey !== config.apiKey) {
604
+ if (updatedConfig && newApiKey !== config2.apiKey) {
605
605
  updatedConfig.apiKey = newApiKey;
606
606
  writeConfig(updatedConfig);
607
607
  }
@@ -667,6 +667,214 @@ function formatList(list) {
667
667
 
668
668
  // src/commands/init.ts
669
669
  import open2 from "open";
670
+
671
+ // src/lib/skill-sync.ts
672
+ import fs3 from "fs";
673
+ import os3 from "os";
674
+ import path3 from "path";
675
+ import { fileURLToPath } from "url";
676
+
677
+ // src/commands/update.ts
678
+ import { createRequire } from "module";
679
+ import { spawn } from "child_process";
680
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
681
+ import { homedir } from "os";
682
+ import { join } from "path";
683
+ var require2 = createRequire(import.meta.url);
684
+ var { version: currentVersion } = require2("../package.json");
685
+ var CACHE_PATH = join(homedir(), ".one", "update-check.json");
686
+ var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
687
+ var AGE_GATE_MS = 30 * 60 * 1e3;
688
+ async function fetchLatestVersionInfo() {
689
+ try {
690
+ const res = await fetch("https://registry.npmjs.org/@withone/cli");
691
+ if (!res.ok) return null;
692
+ const data = await res.json();
693
+ const latest = data["dist-tags"]?.latest;
694
+ if (!latest) return null;
695
+ return { version: latest, publishedAt: data.time?.[latest] ?? null };
696
+ } catch {
697
+ return null;
698
+ }
699
+ }
700
+ function readCache() {
701
+ try {
702
+ return JSON.parse(readFileSync(CACHE_PATH, "utf8"));
703
+ } catch {
704
+ return null;
705
+ }
706
+ }
707
+ function writeCache(latestVersion, publishedAt) {
708
+ try {
709
+ mkdirSync(join(homedir(), ".one"), { recursive: true });
710
+ writeFileSync(CACHE_PATH, JSON.stringify({ lastCheck: Date.now(), latestVersion, publishedAt }));
711
+ } catch {
712
+ }
713
+ }
714
+ async function checkLatestVersion() {
715
+ const info = await fetchLatestVersionInfo();
716
+ if (info) writeCache(info.version, info.publishedAt);
717
+ return info?.version ?? null;
718
+ }
719
+ async function checkLatestVersionCached() {
720
+ const cache2 = readCache();
721
+ if (cache2 && Date.now() - cache2.lastCheck < CHECK_INTERVAL_MS) {
722
+ return { version: cache2.latestVersion, publishedAt: cache2.publishedAt ?? null };
723
+ }
724
+ const info = await fetchLatestVersionInfo();
725
+ if (info) writeCache(info.version, info.publishedAt);
726
+ return info;
727
+ }
728
+ function getCurrentVersion() {
729
+ return currentVersion;
730
+ }
731
+ async function updateCommand() {
732
+ const s = createSpinner();
733
+ s.start("Checking for updates...");
734
+ const latestVersion = await checkLatestVersion();
735
+ if (!latestVersion) {
736
+ s.stop("");
737
+ error("Failed to check for updates \u2014 could not reach npm registry");
738
+ }
739
+ if (currentVersion === latestVersion) {
740
+ s.stop("Already up to date");
741
+ if (isAgentMode()) {
742
+ json({ current: currentVersion, latest: latestVersion, updated: false, message: "Already up to date" });
743
+ } else {
744
+ console.log(`Already up to date (v${currentVersion})`);
745
+ }
746
+ return;
747
+ }
748
+ s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
749
+ console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
750
+ const code = await new Promise((resolve) => {
751
+ const child = spawn("npm", ["install", "-g", "@withone/cli@latest", "--force"], {
752
+ stdio: isAgentMode() ? "pipe" : "inherit",
753
+ shell: true
754
+ });
755
+ child.on("close", resolve);
756
+ child.on("error", () => resolve(1));
757
+ });
758
+ if (code === 0) {
759
+ if (isAgentMode()) {
760
+ json({ current: currentVersion, latest: latestVersion, updated: true, message: "Updated successfully" });
761
+ } else {
762
+ console.log(`Successfully updated to v${latestVersion}`);
763
+ }
764
+ } else {
765
+ error("Update failed \u2014 try running: npm install -g @withone/cli@latest");
766
+ }
767
+ }
768
+ function isNewerVersion(latest, current) {
769
+ const parse = (v) => v.split(".").map(Number);
770
+ const [lMaj, lMin, lPat] = parse(latest);
771
+ const [cMaj, cMin, cPat] = parse(current);
772
+ if (lMaj !== cMaj) return lMaj > cMaj;
773
+ if (lMin !== cMin) return lMin > cMin;
774
+ return lPat > cPat;
775
+ }
776
+ function autoUpdate(targetVersion, publishedAt) {
777
+ if (publishedAt) {
778
+ const age = Date.now() - new Date(publishedAt).getTime();
779
+ if (age < AGE_GATE_MS) return;
780
+ }
781
+ const child = spawn("npm", ["install", "-g", `@withone/cli@${targetVersion}`], {
782
+ detached: true,
783
+ stdio: "ignore",
784
+ shell: true
785
+ });
786
+ child.unref();
787
+ }
788
+
789
+ // src/lib/skill-sync.ts
790
+ var CANONICAL_SKILL_DIR = ".agents/skills";
791
+ var VERSION_MARKER = ".one-cli-version";
792
+ function getPackagedSkillDir() {
793
+ const here = path3.dirname(fileURLToPath(import.meta.url));
794
+ return path3.resolve(here, "..", "skills", "one");
795
+ }
796
+ function getCanonicalSkillPath() {
797
+ return path3.join(os3.homedir(), CANONICAL_SKILL_DIR, "one");
798
+ }
799
+ function getVersionMarkerPath() {
800
+ return path3.join(getCanonicalSkillPath(), VERSION_MARKER);
801
+ }
802
+ function isSkillInstalled() {
803
+ return fs3.existsSync(path3.join(getCanonicalSkillPath(), "SKILL.md"));
804
+ }
805
+ function readInstalledSkillVersion() {
806
+ try {
807
+ return fs3.readFileSync(getVersionMarkerPath(), "utf-8").trim() || null;
808
+ } catch {
809
+ return null;
810
+ }
811
+ }
812
+ function writeInstalledSkillVersion(version2) {
813
+ try {
814
+ fs3.mkdirSync(getCanonicalSkillPath(), { recursive: true });
815
+ fs3.writeFileSync(getVersionMarkerPath(), `${version2}
816
+ `);
817
+ } catch {
818
+ }
819
+ }
820
+ function copyDirSync(src, dest) {
821
+ fs3.mkdirSync(dest, { recursive: true });
822
+ for (const entry of fs3.readdirSync(src, { withFileTypes: true })) {
823
+ const srcPath = path3.join(src, entry.name);
824
+ const destPath = path3.join(dest, entry.name);
825
+ if (entry.isDirectory()) {
826
+ copyDirSync(srcPath, destPath);
827
+ } else {
828
+ fs3.copyFileSync(srcPath, destPath);
829
+ }
830
+ }
831
+ }
832
+ function syncSkillsIfStale() {
833
+ if (!isSkillInstalled()) {
834
+ return { synced: false, reason: "not-installed" };
835
+ }
836
+ const current = getCurrentVersion();
837
+ const installed = readInstalledSkillVersion();
838
+ if (installed === current) {
839
+ return { synced: false, reason: "up-to-date", from: installed, to: current };
840
+ }
841
+ return performSync(current, installed === null ? "missing-marker" : "stale");
842
+ }
843
+ function forceSyncSkills() {
844
+ if (!isSkillInstalled()) {
845
+ return { synced: false, reason: "not-installed" };
846
+ }
847
+ return performSync(getCurrentVersion(), "forced");
848
+ }
849
+ function performSync(current, reason) {
850
+ const source = getPackagedSkillDir();
851
+ if (!fs3.existsSync(path3.join(source, "SKILL.md"))) {
852
+ return { synced: false, reason: "source-missing" };
853
+ }
854
+ const canonical = getCanonicalSkillPath();
855
+ try {
856
+ copyDirSync(source, canonical);
857
+ writeInstalledSkillVersion(current);
858
+ return { synced: true, reason, from: readInstalledSkillVersion() ?? null, to: current };
859
+ } catch (err) {
860
+ return { synced: false, reason: "error", error: err instanceof Error ? err.message : String(err) };
861
+ }
862
+ }
863
+ function getSkillStatus() {
864
+ const installed = isSkillInstalled();
865
+ const installedVersion = readInstalledSkillVersion();
866
+ const currentVersion2 = getCurrentVersion();
867
+ return {
868
+ installed,
869
+ canonicalPath: getCanonicalSkillPath(),
870
+ installedVersion,
871
+ currentVersion: currentVersion2,
872
+ upToDate: installed && installedVersion === currentVersion2,
873
+ markerExists: installedVersion !== null
874
+ };
875
+ }
876
+
877
+ // src/commands/init.ts
670
878
  async function initCommand(options) {
671
879
  if (isAgentMode()) {
672
880
  error("This command requires interactive input. Run without --agent.");
@@ -682,7 +890,7 @@ async function initCommand(options) {
682
890
  async function handleExistingConfig(apiKey, options) {
683
891
  const statuses = getAgentStatuses();
684
892
  const masked = maskApiKey(apiKey);
685
- const skillInstalled = isSkillInstalled();
893
+ const skillInstalled = isSkillInstalled2();
686
894
  console.log();
687
895
  console.log(` ${pc2.bold("Current Setup")}`);
688
896
  console.log(` ${pc2.dim("\u2500".repeat(42))}`);
@@ -818,12 +1026,12 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
818
1026
  reinstalled.push(`${s.agent.name} (project)`);
819
1027
  }
820
1028
  }
821
- const config = readConfig();
1029
+ const config2 = readConfig();
822
1030
  writeConfig({
823
1031
  apiKey: newKey,
824
- installedAgents: config?.installedAgents ?? [],
825
- createdAt: config?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
826
- accessControl: config?.accessControl
1032
+ installedAgents: config2?.installedAgents ?? [],
1033
+ createdAt: config2?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1034
+ accessControl: config2?.accessControl
827
1035
  });
828
1036
  if (reinstalled.length > 0) {
829
1037
  p3.log.success(`Updated MCP configs: ${reinstalled.join(", ")}`);
@@ -842,48 +1050,49 @@ var SKILL_AGENTS = [
842
1050
  { id: "opencode", name: "OpenCode", skillDir: ".opencode/skills" },
843
1051
  { id: "roo", name: "Roo", skillDir: ".roo/skills" }
844
1052
  ];
845
- var CANONICAL_SKILL_DIR = ".agents/skills";
1053
+ var CANONICAL_SKILL_DIR2 = ".agents/skills";
846
1054
  function getSkillSourceDir() {
847
- const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
848
- return path3.resolve(__dirname2, "..", "skills", "one");
1055
+ const __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
1056
+ return path4.resolve(__dirname2, "..", "skills", "one");
849
1057
  }
850
- function getCanonicalSkillPath() {
851
- return path3.join(os3.homedir(), CANONICAL_SKILL_DIR, "one");
1058
+ function getCanonicalSkillPath2() {
1059
+ return path4.join(os4.homedir(), CANONICAL_SKILL_DIR2, "one");
852
1060
  }
853
1061
  function getAgentSkillPath(agent) {
854
- return path3.join(os3.homedir(), agent.skillDir, "one");
1062
+ return path4.join(os4.homedir(), agent.skillDir, "one");
855
1063
  }
856
- function isSkillInstalled() {
857
- return fs3.existsSync(path3.join(getCanonicalSkillPath(), "SKILL.md"));
1064
+ function isSkillInstalled2() {
1065
+ return fs4.existsSync(path4.join(getCanonicalSkillPath2(), "SKILL.md"));
858
1066
  }
859
1067
  function isSkillInstalledForAgent(agent) {
860
- return fs3.existsSync(path3.join(getAgentSkillPath(agent), "SKILL.md"));
1068
+ return fs4.existsSync(path4.join(getAgentSkillPath(agent), "SKILL.md"));
861
1069
  }
862
- function copyDirSync(src, dest) {
863
- fs3.mkdirSync(dest, { recursive: true });
864
- for (const entry of fs3.readdirSync(src, { withFileTypes: true })) {
865
- const srcPath = path3.join(src, entry.name);
866
- const destPath = path3.join(dest, entry.name);
1070
+ function copyDirSync2(src, dest) {
1071
+ fs4.mkdirSync(dest, { recursive: true });
1072
+ for (const entry of fs4.readdirSync(src, { withFileTypes: true })) {
1073
+ const srcPath = path4.join(src, entry.name);
1074
+ const destPath = path4.join(dest, entry.name);
867
1075
  if (entry.isDirectory()) {
868
- copyDirSync(srcPath, destPath);
1076
+ copyDirSync2(srcPath, destPath);
869
1077
  } else {
870
- fs3.copyFileSync(srcPath, destPath);
1078
+ fs4.copyFileSync(srcPath, destPath);
871
1079
  }
872
1080
  }
873
1081
  }
874
1082
  function installSkillForAgents(agentIds) {
875
1083
  const source = getSkillSourceDir();
876
- const canonical = getCanonicalSkillPath();
1084
+ const canonical = getCanonicalSkillPath2();
877
1085
  const installed = [];
878
1086
  const failed = [];
879
- if (!fs3.existsSync(path3.join(source, "SKILL.md"))) {
1087
+ if (!fs4.existsSync(path4.join(source, "SKILL.md"))) {
880
1088
  return { installed: [], failed: ["skill source not found"] };
881
1089
  }
882
1090
  try {
883
- if (fs3.existsSync(canonical)) {
884
- fs3.rmSync(canonical, { recursive: true });
1091
+ if (fs4.existsSync(canonical)) {
1092
+ fs4.rmSync(canonical, { recursive: true });
885
1093
  }
886
- copyDirSync(source, canonical);
1094
+ copyDirSync2(source, canonical);
1095
+ writeInstalledSkillVersion(getCurrentVersion());
887
1096
  } catch {
888
1097
  return { installed: [], failed: ["canonical copy"] };
889
1098
  }
@@ -897,15 +1106,15 @@ function installSkillForAgents(agentIds) {
897
1106
  continue;
898
1107
  }
899
1108
  try {
900
- const agentSkillsDir = path3.dirname(agentPath);
901
- fs3.mkdirSync(agentSkillsDir, { recursive: true });
1109
+ const agentSkillsDir = path4.dirname(agentPath);
1110
+ fs4.mkdirSync(agentSkillsDir, { recursive: true });
902
1111
  try {
903
- fs3.lstatSync(agentPath);
904
- fs3.rmSync(agentPath, { recursive: true });
1112
+ fs4.lstatSync(agentPath);
1113
+ fs4.rmSync(agentPath, { recursive: true });
905
1114
  } catch {
906
1115
  }
907
- const relative = path3.relative(agentSkillsDir, canonical);
908
- fs3.symlinkSync(relative, agentPath);
1116
+ const relative = path4.relative(agentSkillsDir, canonical);
1117
+ fs4.symlinkSync(relative, agentPath);
909
1118
  installed.push(agent.name);
910
1119
  seen.set(agentPath, true);
911
1120
  } catch {
@@ -1553,35 +1762,35 @@ import * as p6 from "@clack/prompts";
1553
1762
  import pc6 from "picocolors";
1554
1763
 
1555
1764
  // src/lib/cache.ts
1556
- import fs4 from "fs";
1557
- import path4 from "path";
1558
- import os4 from "os";
1559
- var CACHE_BASE = path4.join(os4.homedir(), ".one", "cache");
1560
- var KNOWLEDGE_DIR = path4.join(CACHE_BASE, "knowledge");
1561
- var SEARCH_DIR = path4.join(CACHE_BASE, "search");
1765
+ import fs5 from "fs";
1766
+ import path5 from "path";
1767
+ import os5 from "os";
1768
+ var CACHE_BASE = path5.join(os5.homedir(), ".one", "cache");
1769
+ var KNOWLEDGE_DIR = path5.join(CACHE_BASE, "knowledge");
1770
+ var SEARCH_DIR = path5.join(CACHE_BASE, "search");
1562
1771
  function sanitizeFilename(input) {
1563
1772
  return input.replace(/[^a-zA-Z0-9_\-\.]/g, "_");
1564
1773
  }
1565
1774
  function knowledgeCachePath(actionId) {
1566
- return path4.join(KNOWLEDGE_DIR, `${sanitizeFilename(actionId)}.json`);
1775
+ return path5.join(KNOWLEDGE_DIR, `${sanitizeFilename(actionId)}.json`);
1567
1776
  }
1568
1777
  function searchCachePath(platform, query, type) {
1569
1778
  const key = `${platform}_${sanitizeFilename(query)}_${type || "knowledge"}`;
1570
- return path4.join(SEARCH_DIR, `${key}.json`);
1779
+ return path5.join(SEARCH_DIR, `${key}.json`);
1571
1780
  }
1572
- function readCache(filePath) {
1781
+ function readCache2(filePath) {
1573
1782
  try {
1574
- const content = fs4.readFileSync(filePath, "utf-8");
1783
+ const content = fs5.readFileSync(filePath, "utf-8");
1575
1784
  return JSON.parse(content);
1576
1785
  } catch {
1577
1786
  return null;
1578
1787
  }
1579
1788
  }
1580
- function writeCache(filePath, entry) {
1789
+ function writeCache2(filePath, entry) {
1581
1790
  try {
1582
- const dir = path4.dirname(filePath);
1583
- fs4.mkdirSync(dir, { recursive: true });
1584
- fs4.writeFileSync(filePath, JSON.stringify(entry, null, 2));
1791
+ const dir = path5.dirname(filePath);
1792
+ fs5.mkdirSync(dir, { recursive: true });
1793
+ fs5.writeFileSync(filePath, JSON.stringify(entry, null, 2));
1585
1794
  } catch {
1586
1795
  }
1587
1796
  }
@@ -1619,11 +1828,11 @@ function listCacheEntries() {
1619
1828
  const entries = [];
1620
1829
  for (const [dir, type] of [[KNOWLEDGE_DIR, "knowledge"], [SEARCH_DIR, "search"]]) {
1621
1830
  try {
1622
- const files = fs4.readdirSync(dir);
1831
+ const files = fs5.readdirSync(dir);
1623
1832
  for (const file of files) {
1624
1833
  if (!file.endsWith(".json")) continue;
1625
- const filePath = path4.join(dir, file);
1626
- const entry = readCache(filePath);
1834
+ const filePath = path5.join(dir, file);
1835
+ const entry = readCache2(filePath);
1627
1836
  if (entry) {
1628
1837
  entries.push({ type, filePath, entry });
1629
1838
  }
@@ -1637,12 +1846,12 @@ function clearAll() {
1637
1846
  let count = 0;
1638
1847
  for (const dir of [KNOWLEDGE_DIR, SEARCH_DIR]) {
1639
1848
  try {
1640
- const files = fs4.readdirSync(dir);
1849
+ const files = fs5.readdirSync(dir);
1641
1850
  for (const file of files) {
1642
- fs4.unlinkSync(path4.join(dir, file));
1851
+ fs5.unlinkSync(path5.join(dir, file));
1643
1852
  count++;
1644
1853
  }
1645
- fs4.rmdirSync(dir);
1854
+ fs5.rmdirSync(dir);
1646
1855
  } catch {
1647
1856
  }
1648
1857
  }
@@ -1651,7 +1860,7 @@ function clearAll() {
1651
1860
  function clearEntry(actionId) {
1652
1861
  const filePath = knowledgeCachePath(actionId);
1653
1862
  try {
1654
- fs4.unlinkSync(filePath);
1863
+ fs5.unlinkSync(filePath);
1655
1864
  return true;
1656
1865
  } catch {
1657
1866
  return false;
@@ -1697,7 +1906,7 @@ async function actionsSearchCommand(platform, query, options) {
1697
1906
  const agentType = knowledgeAgent ? "knowledge" : options.type || "execute";
1698
1907
  const useCache = options.cache !== false;
1699
1908
  const cachePath = searchCachePath(platform, query, agentType || "knowledge");
1700
- const cached = useCache ? readCache(cachePath) : null;
1909
+ const cached = useCache ? readCache2(cachePath) : null;
1701
1910
  let cleanedActions;
1702
1911
  let cacheHit = false;
1703
1912
  if (cached && isFresh(cached)) {
@@ -1713,7 +1922,7 @@ async function actionsSearchCommand(platform, query, options) {
1713
1922
  );
1714
1923
  if (result.status === 304 && cached) {
1715
1924
  cached.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
1716
- writeCache(cachePath, cached);
1925
+ writeCache2(cachePath, cached);
1717
1926
  cleanedActions = cached.data.actions;
1718
1927
  cacheHit = true;
1719
1928
  } else {
@@ -1726,7 +1935,7 @@ async function actionsSearchCommand(platform, query, options) {
1726
1935
  method: action.method,
1727
1936
  path: action.path
1728
1937
  }));
1729
- writeCache(cachePath, makeCacheEntry(
1938
+ writeCache2(cachePath, makeCacheEntry(
1730
1939
  `${platform}_${query}_${agentType || "knowledge"}`,
1731
1940
  { actions: cleanedActions },
1732
1941
  result.etag
@@ -1750,7 +1959,7 @@ async function actionsSearchCommand(platform, query, options) {
1750
1959
  if (cacheHit && cached) {
1751
1960
  response._cache = buildCacheMeta(cached, true);
1752
1961
  } else {
1753
- const freshEntry = readCache(cachePath);
1962
+ const freshEntry = readCache2(cachePath);
1754
1963
  response._cache = buildCacheMeta(freshEntry, false);
1755
1964
  }
1756
1965
  json(response);
@@ -1810,7 +2019,7 @@ Execute: ${pc6.cyan(`one actions execute ${platform} <actionId> <connectionK
1810
2019
  async function actionsKnowledgeCommand(platform, actionId, options) {
1811
2020
  const cachePath = knowledgeCachePath(actionId);
1812
2021
  if (options.cacheStatus) {
1813
- const entry = readCache(cachePath);
2022
+ const entry = readCache2(cachePath);
1814
2023
  if (!entry) {
1815
2024
  json({
1816
2025
  cached: false,
@@ -1858,7 +2067,7 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
1858
2067
  spinner5.start(`Loading knowledge for action ${pc6.dim(actionId)}...`);
1859
2068
  try {
1860
2069
  const useCache = options.cache !== false;
1861
- const cached = useCache ? readCache(cachePath) : null;
2070
+ const cached = useCache ? readCache2(cachePath) : null;
1862
2071
  let knowledgeData;
1863
2072
  let cacheHit = false;
1864
2073
  let cacheEntry = cached;
@@ -1873,13 +2082,13 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
1873
2082
  );
1874
2083
  if (result.status === 304 && cached) {
1875
2084
  cached.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
1876
- writeCache(cachePath, cached);
2085
+ writeCache2(cachePath, cached);
1877
2086
  knowledgeData = cached.data;
1878
2087
  cacheHit = true;
1879
2088
  } else {
1880
2089
  knowledgeData = result.data;
1881
2090
  const newEntry = makeCacheEntry(actionId, knowledgeData, result.etag);
1882
- writeCache(cachePath, newEntry);
2091
+ writeCache2(cachePath, newEntry);
1883
2092
  cacheEntry = newEntry;
1884
2093
  }
1885
2094
  } catch (fetchError) {
@@ -2092,26 +2301,26 @@ function validateStepsArray(steps, pathPrefix, errors) {
2092
2301
  const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
2093
2302
  for (let i = 0; i < steps.length; i++) {
2094
2303
  const step = steps[i];
2095
- const path5 = `${pathPrefix}[${i}]`;
2304
+ const path6 = `${pathPrefix}[${i}]`;
2096
2305
  if (!step || typeof step !== "object" || Array.isArray(step)) {
2097
- errors.push({ path: path5, message: "Step must be an object" });
2306
+ errors.push({ path: path6, message: "Step must be an object" });
2098
2307
  continue;
2099
2308
  }
2100
2309
  const s = step;
2101
2310
  if (!s.id || typeof s.id !== "string") {
2102
- errors.push({ path: `${path5}.id`, message: 'Step must have a string "id"' });
2311
+ errors.push({ path: `${path6}.id`, message: 'Step must have a string "id"' });
2103
2312
  }
2104
2313
  if (!s.name || typeof s.name !== "string") {
2105
- errors.push({ path: `${path5}.name`, message: 'Step must have a string "name"' });
2314
+ errors.push({ path: `${path6}.name`, message: 'Step must have a string "name"' });
2106
2315
  }
2107
2316
  if (!s.type || !validTypes.includes(s.type)) {
2108
- errors.push({ path: `${path5}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
2317
+ errors.push({ path: `${path6}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
2109
2318
  continue;
2110
2319
  }
2111
2320
  if (s.onError && typeof s.onError === "object") {
2112
2321
  const oe = s.onError;
2113
2322
  if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
2114
- errors.push({ path: `${path5}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
2323
+ errors.push({ path: `${path6}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
2115
2324
  }
2116
2325
  }
2117
2326
  const descriptor = getStepTypeDescriptor(s.type);
@@ -2121,15 +2330,15 @@ function validateStepsArray(steps, pathPrefix, errors) {
2121
2330
  if (!configObj || typeof configObj !== "object") {
2122
2331
  const hint = detectFlatConfigHint(s, descriptor);
2123
2332
  errors.push({
2124
- path: `${path5}.${configKey}`,
2333
+ path: `${path6}.${configKey}`,
2125
2334
  message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
2126
2335
  });
2127
2336
  continue;
2128
2337
  }
2129
- const config = configObj;
2338
+ const config2 = configObj;
2130
2339
  for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
2131
- const fieldPath = `${path5}.${configKey}.${fieldName}`;
2132
- const value = config[fieldName];
2340
+ const fieldPath = `${path6}.${configKey}.${fieldName}`;
2341
+ const value = config2[fieldName];
2133
2342
  if (fd.required && (value === void 0 || value === null || value === "")) {
2134
2343
  errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
2135
2344
  continue;
@@ -2161,21 +2370,21 @@ function validateStepsArray(steps, pathPrefix, errors) {
2161
2370
  }
2162
2371
  }
2163
2372
  if (descriptor.type === "code") {
2164
- const hasSource = typeof config.source === "string" && config.source.length > 0;
2165
- const hasModule = typeof config.module === "string" && config.module.length > 0;
2373
+ const hasSource = typeof config2.source === "string" && config2.source.length > 0;
2374
+ const hasModule = typeof config2.module === "string" && config2.module.length > 0;
2166
2375
  if (!hasSource && !hasModule) {
2167
- errors.push({ path: `${path5}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
2376
+ errors.push({ path: `${path6}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
2168
2377
  } else if (hasSource && hasModule) {
2169
- errors.push({ path: `${path5}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
2378
+ errors.push({ path: `${path6}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
2170
2379
  }
2171
2380
  if (hasModule) {
2172
- const m = config.module;
2381
+ const m = config2.module;
2173
2382
  if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
2174
- errors.push({ path: `${path5}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
2383
+ errors.push({ path: `${path6}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
2175
2384
  } else if (m.split(/[\\/]/).includes("..")) {
2176
- errors.push({ path: `${path5}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
2385
+ errors.push({ path: `${path6}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
2177
2386
  } else if (!m.endsWith(".mjs")) {
2178
- errors.push({ path: `${path5}.${configKey}.module`, message: "Code module must be a .mjs file" });
2387
+ errors.push({ path: `${path6}.${configKey}.module`, message: "Code module must be a .mjs file" });
2179
2388
  }
2180
2389
  }
2181
2390
  }
@@ -2199,16 +2408,16 @@ function validateStepIds(flow2) {
2199
2408
  function collectIds(steps, pathPrefix) {
2200
2409
  for (let i = 0; i < steps.length; i++) {
2201
2410
  const step = steps[i];
2202
- const path5 = `${pathPrefix}[${i}]`;
2411
+ const path6 = `${pathPrefix}[${i}]`;
2203
2412
  if (seen.has(step.id)) {
2204
- errors.push({ path: `${path5}.id`, message: `Duplicate step ID: "${step.id}"` });
2413
+ errors.push({ path: `${path6}.id`, message: `Duplicate step ID: "${step.id}"` });
2205
2414
  } else {
2206
2415
  seen.add(step.id);
2207
2416
  }
2208
2417
  for (const { configKey, fieldName } of nestedKeys) {
2209
- const config = step[configKey];
2210
- if (config && Array.isArray(config[fieldName])) {
2211
- collectIds(config[fieldName], `${path5}.${configKey}.${fieldName}`);
2418
+ const config2 = step[configKey];
2419
+ if (config2 && Array.isArray(config2[fieldName])) {
2420
+ collectIds(config2[fieldName], `${path6}.${configKey}.${fieldName}`);
2212
2421
  }
2213
2422
  }
2214
2423
  }
@@ -2225,9 +2434,9 @@ function validateSelectorReferences(flow2) {
2225
2434
  for (const step of steps) {
2226
2435
  ids.add(step.id);
2227
2436
  for (const { configKey, fieldName } of nestedKeys) {
2228
- const config = step[configKey];
2229
- if (config && Array.isArray(config[fieldName])) {
2230
- for (const id of getAllStepIds(config[fieldName])) ids.add(id);
2437
+ const config2 = step[configKey];
2438
+ if (config2 && Array.isArray(config2[fieldName])) {
2439
+ for (const id of getAllStepIds(config2[fieldName])) ids.add(id);
2231
2440
  }
2232
2441
  }
2233
2442
  }
@@ -2256,7 +2465,7 @@ function validateSelectorReferences(flow2) {
2256
2465
  }
2257
2466
  return selectors;
2258
2467
  }
2259
- function checkSelectors(selectors, path5) {
2468
+ function checkSelectors(selectors, path6) {
2260
2469
  for (const selector of selectors) {
2261
2470
  const parts = selector.split(".");
2262
2471
  if (parts.length < 3) continue;
@@ -2264,31 +2473,31 @@ function validateSelectorReferences(flow2) {
2264
2473
  if (root === "input") {
2265
2474
  const inputName = parts[2];
2266
2475
  if (!inputNames.has(inputName)) {
2267
- errors.push({ path: path5, message: `Selector "${selector}" references undefined input "${inputName}"` });
2476
+ errors.push({ path: path6, message: `Selector "${selector}" references undefined input "${inputName}"` });
2268
2477
  }
2269
2478
  } else if (root === "steps") {
2270
2479
  const stepId = parts[2];
2271
2480
  if (!allStepIds.has(stepId)) {
2272
- errors.push({ path: path5, message: `Selector "${selector}" references undefined step "${stepId}"` });
2481
+ errors.push({ path: path6, message: `Selector "${selector}" references undefined step "${stepId}"` });
2273
2482
  }
2274
2483
  }
2275
2484
  }
2276
2485
  }
2277
2486
  const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
2278
- function checkOperatorsInSelectorField(value, path5) {
2487
+ function checkOperatorsInSelectorField(value, path6) {
2279
2488
  if (typeof value === "string" && value.startsWith("$.")) {
2280
2489
  if (value.includes("||")) {
2281
- errors.push({ path: path5, message: `Selector "${value}" contains unsupported operator "||". Selectors in data fields use dot-path resolution, not JS evaluation. Use the "default" field on the input definition instead, or use a "code" step for complex expressions.` });
2490
+ errors.push({ path: path6, message: `Selector "${value}" contains unsupported operator "||". Selectors in data fields use dot-path resolution, not JS evaluation. Use the "default" field on the input definition instead, or use a "code" step for complex expressions.` });
2282
2491
  } else if (value.includes("&&")) {
2283
- errors.push({ path: path5, message: `Selector "${value}" contains unsupported operator "&&". Selectors in data fields use dot-path resolution, not JS evaluation. Use a "condition" step or "code" step for complex expressions.` });
2492
+ errors.push({ path: path6, message: `Selector "${value}" contains unsupported operator "&&". Selectors in data fields use dot-path resolution, not JS evaluation. Use a "condition" step or "code" step for complex expressions.` });
2284
2493
  }
2285
2494
  } else if (value && typeof value === "object" && !Array.isArray(value)) {
2286
2495
  for (const [k, v] of Object.entries(value)) {
2287
- checkOperatorsInSelectorField(v, `${path5}.${k}`);
2496
+ checkOperatorsInSelectorField(v, `${path6}.${k}`);
2288
2497
  }
2289
2498
  } else if (Array.isArray(value)) {
2290
2499
  for (let i = 0; i < value.length; i++) {
2291
- checkOperatorsInSelectorField(value[i], `${path5}[${i}]`);
2500
+ checkOperatorsInSelectorField(value[i], `${path6}[${i}]`);
2292
2501
  }
2293
2502
  }
2294
2503
  }
@@ -2297,12 +2506,12 @@ function validateSelectorReferences(flow2) {
2297
2506
  if (step.unless) checkSelectors(extractSelectors(step.unless), `${pathPrefix}.unless`);
2298
2507
  const descriptor = getStepTypeDescriptor(step.type);
2299
2508
  if (descriptor) {
2300
- const config = step[descriptor.configKey];
2301
- if (config && typeof config === "object") {
2509
+ const config2 = step[descriptor.configKey];
2510
+ if (config2 && typeof config2 === "object") {
2302
2511
  if (step.type !== "transform" && step.type !== "code") {
2303
2512
  for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
2304
2513
  if (fd.stepsArray) continue;
2305
- const value = config[fieldName];
2514
+ const value = config2[fieldName];
2306
2515
  if (value !== void 0) {
2307
2516
  const fieldKey = `${descriptor.configKey}.${fieldName}`;
2308
2517
  const fieldPath = `${pathPrefix}.${fieldKey}`;
@@ -2340,7 +2549,7 @@ function validateFlow(flow2) {
2340
2549
  }
2341
2550
 
2342
2551
  // src/commands/flow.ts
2343
- import fs5 from "fs";
2552
+ import fs6 from "fs";
2344
2553
  function getConfig2() {
2345
2554
  const apiKey = getApiKey();
2346
2555
  if (!apiKey) {
@@ -2398,7 +2607,7 @@ async function flowCreateCommand(key, options) {
2398
2607
  if (raw.startsWith("@")) {
2399
2608
  const filePath = raw.slice(1);
2400
2609
  try {
2401
- raw = fs5.readFileSync(filePath, "utf-8");
2610
+ raw = fs6.readFileSync(filePath, "utf-8");
2402
2611
  } catch (err) {
2403
2612
  error(`Cannot read file "${filePath}": ${err.message}`);
2404
2613
  }
@@ -2605,7 +2814,7 @@ async function flowValidateCommand(keyOrPath) {
2605
2814
  let flowData;
2606
2815
  try {
2607
2816
  const flowPath = resolveFlowPath(keyOrPath);
2608
- const content = fs5.readFileSync(flowPath, "utf-8");
2817
+ const content = fs6.readFileSync(flowPath, "utf-8");
2609
2818
  flowData = JSON.parse(content);
2610
2819
  } catch (err) {
2611
2820
  spinner5.stop("Validation failed");
@@ -3363,11 +3572,11 @@ async function cacheUpdateAllCommand() {
3363
3572
  if (e.type === "knowledge") {
3364
3573
  const result = await api.getActionKnowledgeWithMeta(e.entry.key);
3365
3574
  const newEntry = makeCacheEntry(e.entry.key, result.data, result.etag);
3366
- writeCache(e.filePath, newEntry);
3575
+ writeCache2(e.filePath, newEntry);
3367
3576
  updated++;
3368
3577
  } else {
3369
3578
  const refreshed = { ...e.entry, cachedAt: (/* @__PURE__ */ new Date()).toISOString() };
3370
- writeCache(e.filePath, refreshed);
3579
+ writeCache2(e.filePath, refreshed);
3371
3580
  updated++;
3372
3581
  }
3373
3582
  } catch (err) {
@@ -3495,6 +3704,7 @@ Request specific sections:
3495
3704
  - Always use the **exact action ID** from search results \u2014 don't guess
3496
3705
  - Always read **knowledge** before executing any action
3497
3706
  - Connection keys come from \`one connection list\` \u2014 don't hardcode them
3707
+ - Skills stay in lockstep with the CLI version automatically \u2014 every command checks a \`.one-cli-version\` marker in the canonical skill dir and refreshes the files if the CLI has been upgraded. Check manually with \`one config skills status\`; force a resync with \`one config skills sync\`
3498
3708
  `;
3499
3709
  var GUIDE_ACTIONS = `# One Actions \u2014 Reference
3500
3710
 
@@ -4098,118 +4308,6 @@ function buildWorkflowIdeas(connections) {
4098
4308
  return lines.join("\n");
4099
4309
  }
4100
4310
 
4101
- // src/commands/update.ts
4102
- import { createRequire } from "module";
4103
- import { spawn } from "child_process";
4104
- import { readFileSync, writeFileSync, mkdirSync } from "fs";
4105
- import { homedir } from "os";
4106
- import { join } from "path";
4107
- var require2 = createRequire(import.meta.url);
4108
- var { version: currentVersion } = require2("../package.json");
4109
- var CACHE_PATH = join(homedir(), ".one", "update-check.json");
4110
- var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
4111
- var AGE_GATE_MS = 30 * 60 * 1e3;
4112
- async function fetchLatestVersionInfo() {
4113
- try {
4114
- const res = await fetch("https://registry.npmjs.org/@withone/cli");
4115
- if (!res.ok) return null;
4116
- const data = await res.json();
4117
- const latest = data["dist-tags"]?.latest;
4118
- if (!latest) return null;
4119
- return { version: latest, publishedAt: data.time?.[latest] ?? null };
4120
- } catch {
4121
- return null;
4122
- }
4123
- }
4124
- function readCache3() {
4125
- try {
4126
- return JSON.parse(readFileSync(CACHE_PATH, "utf8"));
4127
- } catch {
4128
- return null;
4129
- }
4130
- }
4131
- function writeCache2(latestVersion, publishedAt) {
4132
- try {
4133
- mkdirSync(join(homedir(), ".one"), { recursive: true });
4134
- writeFileSync(CACHE_PATH, JSON.stringify({ lastCheck: Date.now(), latestVersion, publishedAt }));
4135
- } catch {
4136
- }
4137
- }
4138
- async function checkLatestVersion() {
4139
- const info = await fetchLatestVersionInfo();
4140
- if (info) writeCache2(info.version, info.publishedAt);
4141
- return info?.version ?? null;
4142
- }
4143
- async function checkLatestVersionCached() {
4144
- const cache2 = readCache3();
4145
- if (cache2 && Date.now() - cache2.lastCheck < CHECK_INTERVAL_MS) {
4146
- return { version: cache2.latestVersion, publishedAt: cache2.publishedAt ?? null };
4147
- }
4148
- const info = await fetchLatestVersionInfo();
4149
- if (info) writeCache2(info.version, info.publishedAt);
4150
- return info;
4151
- }
4152
- function getCurrentVersion() {
4153
- return currentVersion;
4154
- }
4155
- async function updateCommand() {
4156
- const s = createSpinner();
4157
- s.start("Checking for updates...");
4158
- const latestVersion = await checkLatestVersion();
4159
- if (!latestVersion) {
4160
- s.stop("");
4161
- error("Failed to check for updates \u2014 could not reach npm registry");
4162
- }
4163
- if (currentVersion === latestVersion) {
4164
- s.stop("Already up to date");
4165
- if (isAgentMode()) {
4166
- json({ current: currentVersion, latest: latestVersion, updated: false, message: "Already up to date" });
4167
- } else {
4168
- console.log(`Already up to date (v${currentVersion})`);
4169
- }
4170
- return;
4171
- }
4172
- s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
4173
- console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
4174
- const code = await new Promise((resolve) => {
4175
- const child = spawn("npm", ["install", "-g", "@withone/cli@latest", "--force"], {
4176
- stdio: isAgentMode() ? "pipe" : "inherit",
4177
- shell: true
4178
- });
4179
- child.on("close", resolve);
4180
- child.on("error", () => resolve(1));
4181
- });
4182
- if (code === 0) {
4183
- if (isAgentMode()) {
4184
- json({ current: currentVersion, latest: latestVersion, updated: true, message: "Updated successfully" });
4185
- } else {
4186
- console.log(`Successfully updated to v${latestVersion}`);
4187
- }
4188
- } else {
4189
- error("Update failed \u2014 try running: npm install -g @withone/cli@latest");
4190
- }
4191
- }
4192
- function isNewerVersion(latest, current) {
4193
- const parse = (v) => v.split(".").map(Number);
4194
- const [lMaj, lMin, lPat] = parse(latest);
4195
- const [cMaj, cMin, cPat] = parse(current);
4196
- if (lMaj !== cMaj) return lMaj > cMaj;
4197
- if (lMin !== cMin) return lMin > cMin;
4198
- return lPat > cPat;
4199
- }
4200
- function autoUpdate(targetVersion, publishedAt) {
4201
- if (publishedAt) {
4202
- const age = Date.now() - new Date(publishedAt).getTime();
4203
- if (age < AGE_GATE_MS) return;
4204
- }
4205
- const child = spawn("npm", ["install", "-g", `@withone/cli@${targetVersion}`], {
4206
- detached: true,
4207
- stdio: "ignore",
4208
- shell: true
4209
- });
4210
- child.unref();
4211
- }
4212
-
4213
4311
  // src/index.ts
4214
4312
  var require3 = createRequire2(import.meta.url);
4215
4313
  var { version } = require3("../package.json");
@@ -4275,6 +4373,12 @@ program.hook("preAction", (thisCommand) => {
4275
4373
  if (commandName !== "update") {
4276
4374
  updateCheckPromise = checkLatestVersionCached();
4277
4375
  }
4376
+ if (commandName !== "init" && commandName !== "update") {
4377
+ try {
4378
+ syncSkillsIfStale();
4379
+ } catch {
4380
+ }
4381
+ }
4278
4382
  });
4279
4383
  program.hook("postAction", async () => {
4280
4384
  if (!updateCheckPromise) return;
@@ -4287,9 +4391,48 @@ program.hook("postAction", async () => {
4287
4391
  program.command("init").description("Set up One and install MCP to your AI agents").option("-y, --yes", "Skip confirmations").option("-g, --global", "Install MCP globally (available in all projects)").option("-p, --project", "Install MCP for this project only (creates .mcp.json)").action(async (options) => {
4288
4392
  await initCommand(options);
4289
4393
  });
4290
- program.command("config").description("Configure MCP access control (permissions, connections, actions)").action(async () => {
4394
+ var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
4291
4395
  await configCommand();
4292
4396
  });
4397
+ var configSkills = config.command("skills").description("Manage locally-installed skill files");
4398
+ configSkills.command("sync").description("Re-copy packaged skill files over the local install (runs automatically after CLI upgrades)").action(async () => {
4399
+ const result = forceSyncSkills();
4400
+ if (isAgentMode()) {
4401
+ json({ command: "config skills sync", ...result });
4402
+ return;
4403
+ }
4404
+ if (result.reason === "not-installed") {
4405
+ console.log("No skill is installed yet. Run 'one init' first and opt in to skill installation.");
4406
+ return;
4407
+ }
4408
+ if (result.synced) {
4409
+ console.log(`\u2713 Skills synced to v${result.to}`);
4410
+ return;
4411
+ }
4412
+ if (result.reason === "source-missing") {
4413
+ console.log("\u2717 Packaged skill source not found in this CLI build");
4414
+ return;
4415
+ }
4416
+ console.log(`\u2717 Sync failed${result.error ? ": " + result.error : ""}`);
4417
+ });
4418
+ configSkills.command("status").description("Show installed skill version and whether it matches the current CLI").action(async () => {
4419
+ const status = getSkillStatus();
4420
+ if (isAgentMode()) {
4421
+ json({ command: "config skills status", ...status });
4422
+ return;
4423
+ }
4424
+ if (!status.installed) {
4425
+ console.log("Skill is not installed. Run 'one init' to install it.");
4426
+ console.log(`Canonical path (empty): ${status.canonicalPath}`);
4427
+ return;
4428
+ }
4429
+ const marker = status.installedVersion ?? "(no marker \u2014 pre-sync install)";
4430
+ const state = status.upToDate ? "\u2713 up to date" : "\u26A0 stale \u2014 will sync on next command";
4431
+ console.log(`Skill: ${state}`);
4432
+ console.log(` installed: ${marker}`);
4433
+ console.log(` current: ${status.currentVersion}`);
4434
+ console.log(` path: ${status.canonicalPath}`);
4435
+ });
4293
4436
  var connection = program.command("connection").description("Manage connections");
4294
4437
  connection.command("add [platform]").alias("a").description("Add a new connection").action(async (platform) => {
4295
4438
  await connectionAddCommand(platform);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [