@withone/cli 2.3.2 → 2.4.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.
package/dist/index.js CHANGED
@@ -147,7 +147,7 @@ import {
147
147
 
148
148
  // src/cli.ts
149
149
  import { createRequire as createRequire3 } from "module";
150
- import path13 from "path";
150
+ import path15 from "path";
151
151
  import { Command } from "commander";
152
152
 
153
153
  // src/commands/init.ts
@@ -1686,25 +1686,25 @@ async function loginCommand() {
1686
1686
  let targetScope = "global";
1687
1687
  const existingKey = getApiKey();
1688
1688
  if (existingKey) {
1689
- const pc17 = (await import("picocolors")).default;
1689
+ const pc18 = (await import("picocolors")).default;
1690
1690
  const resolved = resolveConfig();
1691
1691
  const whoami2 = resolved.config?.whoami;
1692
1692
  const env2 = getEnvFromApiKey(existingKey);
1693
- const envLabel2 = env2 === "test" ? pc17.yellow("test") : pc17.green("live");
1694
- const currentScope = resolved.scope === "project" ? pc17.cyan("local config") : pc17.magenta("global config");
1693
+ const envLabel2 = env2 === "test" ? pc18.yellow("test") : pc18.green("live");
1694
+ const currentScope = resolved.scope === "project" ? pc18.cyan("local config") : pc18.magenta("global config");
1695
1695
  const lines = ["You are already logged in.", ""];
1696
1696
  if (whoami2) {
1697
1697
  const contextParts2 = [];
1698
1698
  if (whoami2.organization) contextParts2.push(whoami2.organization.name);
1699
1699
  if (whoami2.project) contextParts2.push(whoami2.project.name);
1700
1700
  const scopeDisplay2 = contextParts2.length > 0 ? contextParts2.join(" / ") : "Personal";
1701
- lines.push(`${pc17.bold(scopeDisplay2)} ${pc17.dim("\xB7")} ${envLabel2}`);
1702
- lines.push(`${whoami2.user.name} ${pc17.dim(`(${whoami2.user.email})`)}`);
1703
- if (whoami2.organization) lines.push(`${pc17.dim("Org:")} ${whoami2.organization.name}`);
1704
- if (whoami2.project) lines.push(`${pc17.dim("Project:")} ${whoami2.project.name}`);
1701
+ lines.push(`${pc18.bold(scopeDisplay2)} ${pc18.dim("\xB7")} ${envLabel2}`);
1702
+ lines.push(`${whoami2.user.name} ${pc18.dim(`(${whoami2.user.email})`)}`);
1703
+ if (whoami2.organization) lines.push(`${pc18.dim("Org:")} ${whoami2.organization.name}`);
1704
+ if (whoami2.project) lines.push(`${pc18.dim("Project:")} ${whoami2.project.name}`);
1705
1705
  }
1706
1706
  lines.push("");
1707
- lines.push(`${pc17.dim("Stored in")} ${currentScope}`);
1707
+ lines.push(`${pc18.dim("Stored in")} ${currentScope}`);
1708
1708
  p2.note(lines.join("\n"));
1709
1709
  const scopeChoice = await p2.select({
1710
1710
  message: "Where would you like to log in?",
@@ -1723,42 +1723,42 @@ async function loginCommand() {
1723
1723
  if (!result) return;
1724
1724
  const { apiKey, whoami, keyName, grant, knowledgeOnly } = result;
1725
1725
  saveCredentials(apiKey, targetScope, { keyName, whoami, knowledgeOnly });
1726
- const pc16 = (await import("picocolors")).default;
1726
+ const pc17 = (await import("picocolors")).default;
1727
1727
  const env = getEnvFromApiKey(apiKey);
1728
1728
  const contextParts = [];
1729
1729
  if (whoami.organization) contextParts.push(whoami.organization.name);
1730
1730
  if (whoami.project) contextParts.push(whoami.project.name);
1731
1731
  const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1732
- const envLabel = env === "test" ? pc16.yellow("test") : pc16.green("live");
1733
- const configLabel = targetScope === "project" ? pc16.cyan("local config") : pc16.magenta("global config");
1732
+ const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
1733
+ const configLabel = targetScope === "project" ? pc17.cyan("local config") : pc17.magenta("global config");
1734
1734
  const infoLines = [
1735
- `${pc16.bold(scopeDisplay)} ${pc16.dim("\xB7")} ${envLabel}`,
1736
- `${whoami.user.name} ${pc16.dim(`(${whoami.user.email})`)}`
1735
+ `${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`,
1736
+ `${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`
1737
1737
  ];
1738
- if (keyName) infoLines.push(`${pc16.dim("Key:")} ${keyName}`);
1739
- infoLines.push(...grantNoteLines(grant, pc16.dim("Access:")));
1740
- if (whoami.organization) infoLines.push(`${pc16.dim("Org:")} ${whoami.organization.name}`);
1741
- if (whoami.project) infoLines.push(`${pc16.dim("Project:")} ${whoami.project.name}`);
1738
+ if (keyName) infoLines.push(`${pc17.dim("Key:")} ${keyName}`);
1739
+ infoLines.push(...grantNoteLines(grant, pc17.dim("Access:")));
1740
+ if (whoami.organization) infoLines.push(`${pc17.dim("Org:")} ${whoami.organization.name}`);
1741
+ if (whoami.project) infoLines.push(`${pc17.dim("Project:")} ${whoami.project.name}`);
1742
1742
  infoLines.push("");
1743
- infoLines.push(`${pc16.dim("Stored in")} ${configLabel}`);
1743
+ infoLines.push(`${pc17.dim("Stored in")} ${configLabel}`);
1744
1744
  p2.note(infoLines.join("\n"), "Logged in");
1745
1745
  console.log();
1746
- console.log(` ${pc16.dim("Next steps:")}`);
1747
- console.log(` ${pc16.cyan("one add <platform>")} ${pc16.dim("\u2014")} Connect a platform (e.g. gmail, slack, stripe)`);
1746
+ console.log(` ${pc17.dim("Next steps:")}`);
1747
+ console.log(` ${pc17.cyan("one add <platform>")} ${pc17.dim("\u2014")} Connect a platform (e.g. gmail, slack, stripe)`);
1748
1748
  console.log();
1749
- console.log(pc16.bold(" Copy-paste this to your AI agent:"));
1749
+ console.log(pc17.bold(" Copy-paste this to your AI agent:"));
1750
1750
  console.log();
1751
- console.log(pc16.cyan(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1752
- console.log(pc16.cyan(" \u2502"));
1753
- console.log(pc16.cyan(" \u2502 The One CLI (`one`) is now installed on this machine. It"));
1754
- console.log(pc16.cyan(" \u2502 lets you interact with 750+ third-party platforms (Gmail,"));
1755
- console.log(pc16.cyan(" \u2502 Slack, Shopify, HubSpot, Stripe, GitHub, etc.) through"));
1756
- console.log(pc16.cyan(" \u2502 their APIs. Whenever you need to interact with any"));
1757
- console.log(pc16.cyan(" \u2502 third-party platform or external service, use the `one`"));
1758
- console.log(pc16.cyan(" \u2502 skill. Please save this to your project instructions or"));
1759
- console.log(pc16.cyan(" \u2502 memory so you remember it in future conversations."));
1760
- console.log(pc16.cyan(" \u2502"));
1761
- console.log(pc16.cyan(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1751
+ console.log(pc17.cyan(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1752
+ console.log(pc17.cyan(" \u2502"));
1753
+ console.log(pc17.cyan(" \u2502 The One CLI (`one`) is now installed on this machine. It"));
1754
+ console.log(pc17.cyan(" \u2502 lets you interact with 750+ third-party platforms (Gmail,"));
1755
+ console.log(pc17.cyan(" \u2502 Slack, Shopify, HubSpot, Stripe, GitHub, etc.) through"));
1756
+ console.log(pc17.cyan(" \u2502 their APIs. Whenever you need to interact with any"));
1757
+ console.log(pc17.cyan(" \u2502 third-party platform or external service, use the `one`"));
1758
+ console.log(pc17.cyan(" \u2502 skill. Please save this to your project instructions or"));
1759
+ console.log(pc17.cyan(" \u2502 memory so you remember it in future conversations."));
1760
+ console.log(pc17.cyan(" \u2502"));
1761
+ console.log(pc17.cyan(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1762
1762
  console.log();
1763
1763
  p2.outro("Happy building!");
1764
1764
  }
@@ -3789,7 +3789,7 @@ var CUSTOM_ACTION_NOTE = '\n\nThis is a custom action: ALSO include "connectionK
3789
3789
  function codeGuideAction(action, knowledge, platform, tags, apiBase) {
3790
3790
  const base = apiBase.replace(/\/$/, "");
3791
3791
  const method = action.method.toUpperCase();
3792
- const path14 = action.path.startsWith("/") ? action.path : `/${action.path}`;
3792
+ const path16 = action.path.startsWith("/") ? action.path : `/${action.path}`;
3793
3793
  const connEnv = `ONE_${platformEnvSegment(platform)}_CONNECTION_KEY`;
3794
3794
  const customNote = tags.includes("custom") ? CUSTOM_ACTION_NOTE : "";
3795
3795
  return `### ${actionHeading(action)}
@@ -3798,7 +3798,7 @@ function codeGuideAction(action, knowledge, platform, tags, apiBase) {
3798
3798
  ${knowledge ?? NO_KNOWLEDGE}
3799
3799
 
3800
3800
  ## Calling this action from code
3801
- URL: ${base}/passthrough${path14}
3801
+ URL: ${base}/passthrough${path16}
3802
3802
  Method: ${method}
3803
3803
  Headers:
3804
3804
  - x-one-secret: the value of the ONE_SECRET env var
@@ -3809,7 +3809,7 @@ Headers:
3809
3809
  Environment variables: ONE_SECRET (their One API key) and ${connEnv} (the key of their ${platform} connection), both from the One dashboard.
3810
3810
 
3811
3811
  \`\`\`typescript
3812
- const response = await fetch("${base}/passthrough${path14}", {
3812
+ const response = await fetch("${base}/passthrough${path16}", {
3813
3813
  method: "${method}",
3814
3814
  headers: {
3815
3815
  "x-one-secret": process.env.ONE_SECRET,
@@ -4554,32 +4554,32 @@ function validateStepsArray(steps, pathPrefix, errors) {
4554
4554
  const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
4555
4555
  for (let i = 0; i < steps.length; i++) {
4556
4556
  const step = steps[i];
4557
- const path14 = `${pathPrefix}[${i}]`;
4557
+ const path16 = `${pathPrefix}[${i}]`;
4558
4558
  if (!step || typeof step !== "object" || Array.isArray(step)) {
4559
- errors.push({ path: path14, message: "Step must be an object" });
4559
+ errors.push({ path: path16, message: "Step must be an object" });
4560
4560
  continue;
4561
4561
  }
4562
4562
  const s = step;
4563
4563
  if (!s.id || typeof s.id !== "string") {
4564
- errors.push({ path: `${path14}.id`, message: 'Step must have a string "id"' });
4564
+ errors.push({ path: `${path16}.id`, message: 'Step must have a string "id"' });
4565
4565
  }
4566
4566
  if (!s.name || typeof s.name !== "string") {
4567
- errors.push({ path: `${path14}.name`, message: 'Step must have a string "name"' });
4567
+ errors.push({ path: `${path16}.name`, message: 'Step must have a string "name"' });
4568
4568
  }
4569
4569
  if (!s.type || !validTypes.includes(s.type)) {
4570
- errors.push({ path: `${path14}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
4570
+ errors.push({ path: `${path16}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
4571
4571
  continue;
4572
4572
  }
4573
4573
  if (s.requires !== void 0) {
4574
4574
  if (!Array.isArray(s.requires)) {
4575
- errors.push({ path: `${path14}.requires`, message: '"requires" must be an array of selector strings (e.g. ["$.steps.foo.output.bar"])' });
4575
+ errors.push({ path: `${path16}.requires`, message: '"requires" must be an array of selector strings (e.g. ["$.steps.foo.output.bar"])' });
4576
4576
  } else {
4577
4577
  for (let r = 0; r < s.requires.length; r++) {
4578
4578
  const sel = s.requires[r];
4579
4579
  if (typeof sel !== "string") {
4580
- errors.push({ path: `${path14}.requires[${r}]`, message: '"requires" entry must be a selector string' });
4580
+ errors.push({ path: `${path16}.requires[${r}]`, message: '"requires" entry must be a selector string' });
4581
4581
  } else if (!sel.startsWith("$.")) {
4582
- errors.push({ path: `${path14}.requires[${r}]`, message: `"requires" entry "${sel}" must be a selector starting with "$." (e.g. "$.steps.foo.output.bar")` });
4582
+ errors.push({ path: `${path16}.requires[${r}]`, message: `"requires" entry "${sel}" must be a selector starting with "$." (e.g. "$.steps.foo.output.bar")` });
4583
4583
  }
4584
4584
  }
4585
4585
  }
@@ -4587,7 +4587,7 @@ function validateStepsArray(steps, pathPrefix, errors) {
4587
4587
  if (s.onError && typeof s.onError === "object") {
4588
4588
  const oe = s.onError;
4589
4589
  if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
4590
- errors.push({ path: `${path14}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
4590
+ errors.push({ path: `${path16}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
4591
4591
  }
4592
4592
  }
4593
4593
  const descriptor = getStepTypeDescriptor(s.type);
@@ -4597,14 +4597,14 @@ function validateStepsArray(steps, pathPrefix, errors) {
4597
4597
  if (!configObj || typeof configObj !== "object") {
4598
4598
  const hint = detectFlatConfigHint(s, descriptor);
4599
4599
  errors.push({
4600
- path: `${path14}.${configKey}`,
4600
+ path: `${path16}.${configKey}`,
4601
4601
  message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
4602
4602
  });
4603
4603
  continue;
4604
4604
  }
4605
4605
  const config2 = configObj;
4606
4606
  for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
4607
- const fieldPath = `${path14}.${configKey}.${fieldName}`;
4607
+ const fieldPath = `${path16}.${configKey}.${fieldName}`;
4608
4608
  const value = config2[fieldName];
4609
4609
  if (fd.required && (value === void 0 || value === null || value === "")) {
4610
4610
  errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
@@ -4637,30 +4637,30 @@ function validateStepsArray(steps, pathPrefix, errors) {
4637
4637
  }
4638
4638
  }
4639
4639
  if (descriptor.type === "action") {
4640
- validateConnectionForm(config2, `${path14}.${configKey}`, errors);
4640
+ validateConnectionForm(config2, `${path16}.${configKey}`, errors);
4641
4641
  }
4642
4642
  if (descriptor.type === "code") {
4643
4643
  const hasSource = typeof config2.source === "string" && config2.source.length > 0;
4644
4644
  const hasModule = typeof config2.module === "string" && config2.module.length > 0;
4645
4645
  if (!hasSource && !hasModule) {
4646
- errors.push({ path: `${path14}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
4646
+ errors.push({ path: `${path16}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
4647
4647
  } else if (hasSource && hasModule) {
4648
- errors.push({ path: `${path14}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
4648
+ errors.push({ path: `${path16}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
4649
4649
  }
4650
4650
  if (hasModule) {
4651
4651
  const m = config2.module;
4652
4652
  if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
4653
- errors.push({ path: `${path14}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
4653
+ errors.push({ path: `${path16}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
4654
4654
  } else if (m.split(/[\\/]/).includes("..")) {
4655
- errors.push({ path: `${path14}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
4655
+ errors.push({ path: `${path16}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
4656
4656
  } else if (!m.endsWith(".mjs")) {
4657
- errors.push({ path: `${path14}.${configKey}.module`, message: "Code module must be a .mjs file" });
4657
+ errors.push({ path: `${path16}.${configKey}.module`, message: "Code module must be a .mjs file" });
4658
4658
  }
4659
4659
  }
4660
4660
  if (hasSource) {
4661
4661
  const syntaxError = checkCodeSourceSyntax(config2.source);
4662
4662
  if (syntaxError) {
4663
- errors.push({ path: `${path14}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
4663
+ errors.push({ path: `${path16}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
4664
4664
  }
4665
4665
  }
4666
4666
  }
@@ -4732,16 +4732,16 @@ function validateStepIds(flow2) {
4732
4732
  function collectIds(steps, pathPrefix) {
4733
4733
  for (let i = 0; i < steps.length; i++) {
4734
4734
  const step = steps[i];
4735
- const path14 = `${pathPrefix}[${i}]`;
4735
+ const path16 = `${pathPrefix}[${i}]`;
4736
4736
  if (seen.has(step.id)) {
4737
- errors.push({ path: `${path14}.id`, message: `Duplicate step ID: "${step.id}"` });
4737
+ errors.push({ path: `${path16}.id`, message: `Duplicate step ID: "${step.id}"` });
4738
4738
  } else {
4739
4739
  seen.add(step.id);
4740
4740
  }
4741
4741
  for (const { configKey, fieldName } of nestedKeys) {
4742
4742
  const config2 = step[configKey];
4743
4743
  if (config2 && Array.isArray(config2[fieldName])) {
4744
- collectIds(config2[fieldName], `${path14}.${configKey}.${fieldName}`);
4744
+ collectIds(config2[fieldName], `${path16}.${configKey}.${fieldName}`);
4745
4745
  }
4746
4746
  }
4747
4747
  }
@@ -4789,7 +4789,7 @@ function validateSelectorReferences(flow2, rootDir) {
4789
4789
  }
4790
4790
  return selectors;
4791
4791
  }
4792
- function checkSelectors(selectors, path14, precedingStepIds) {
4792
+ function checkSelectors(selectors, path16, precedingStepIds) {
4793
4793
  for (const selector of selectors) {
4794
4794
  const parts = selector.split(".");
4795
4795
  if (parts.length < 3) continue;
@@ -4797,15 +4797,15 @@ function validateSelectorReferences(flow2, rootDir) {
4797
4797
  if (root === "input") {
4798
4798
  const inputName = parts[2];
4799
4799
  if (!inputNames.has(inputName)) {
4800
- errors.push({ path: path14, message: `Selector "${selector}" references undefined input "${inputName}"` });
4800
+ errors.push({ path: path16, message: `Selector "${selector}" references undefined input "${inputName}"` });
4801
4801
  }
4802
4802
  } else if (root === "steps") {
4803
4803
  const stepId = parts[2].replace(/[\[\]]/g, "").split(/[\[\]]/)[0];
4804
4804
  if (!allStepIds.has(stepId)) {
4805
- errors.push({ path: path14, message: `Selector "${selector}" references undefined step "${stepId}"` });
4805
+ errors.push({ path: path16, message: `Selector "${selector}" references undefined step "${stepId}"` });
4806
4806
  } else if (precedingStepIds && !precedingStepIds.has(stepId)) {
4807
4807
  errors.push({
4808
- path: path14,
4808
+ path: path16,
4809
4809
  message: `Selector "${selector}" references step "${stepId}" which is declared after the current step. Steps execute in declaration order, so this will always resolve to undefined at runtime \u2014 move the dependency earlier in the steps array.`
4810
4810
  });
4811
4811
  }
@@ -4813,20 +4813,20 @@ function validateSelectorReferences(flow2, rootDir) {
4813
4813
  }
4814
4814
  }
4815
4815
  const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
4816
- function checkOperatorsInSelectorField(value, path14) {
4816
+ function checkOperatorsInSelectorField(value, path16) {
4817
4817
  if (typeof value === "string" && value.startsWith("$.")) {
4818
4818
  if (value.includes("||")) {
4819
- errors.push({ path: path14, 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.` });
4819
+ errors.push({ path: path16, 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.` });
4820
4820
  } else if (value.includes("&&")) {
4821
- errors.push({ path: path14, 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.` });
4821
+ errors.push({ path: path16, 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.` });
4822
4822
  }
4823
4823
  } else if (value && typeof value === "object" && !Array.isArray(value)) {
4824
4824
  for (const [k, v] of Object.entries(value)) {
4825
- checkOperatorsInSelectorField(v, `${path14}.${k}`);
4825
+ checkOperatorsInSelectorField(v, `${path16}.${k}`);
4826
4826
  }
4827
4827
  } else if (Array.isArray(value)) {
4828
4828
  for (let i = 0; i < value.length; i++) {
4829
- checkOperatorsInSelectorField(value[i], `${path14}[${i}]`);
4829
+ checkOperatorsInSelectorField(value[i], `${path16}[${i}]`);
4830
4830
  }
4831
4831
  }
4832
4832
  }
@@ -4911,10 +4911,10 @@ var VALID_OUTPUT_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "number", "bo
4911
4911
  function isOutputSchemaObject(v) {
4912
4912
  return !!v && typeof v === "object" && !Array.isArray(v);
4913
4913
  }
4914
- function walkOutputSchema(schema, path14) {
4914
+ function walkOutputSchema(schema, path16) {
4915
4915
  let current = schema;
4916
- for (let i = 0; i < path14.length; i++) {
4917
- const seg = path14[i];
4916
+ for (let i = 0; i < path16.length; i++) {
4917
+ const seg = path16[i];
4918
4918
  if (typeof current === "string") {
4919
4919
  return current === "unknown" || current === "object" || current === "array" ? "opaque" : "opaque";
4920
4920
  }
@@ -6861,8 +6861,8 @@ function sleep(ms) {
6861
6861
  return new Promise((resolve) => setTimeout(resolve, ms));
6862
6862
  }
6863
6863
  function interpolate(template, record) {
6864
- return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_, path14) => {
6865
- const parts = path14.split(".");
6864
+ return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_, path16) => {
6865
+ const parts = path16.split(".");
6866
6866
  let value = record;
6867
6867
  for (const part2 of parts) {
6868
6868
  if (typeof value !== "object" || value === null) return "";
@@ -6890,8 +6890,8 @@ function deepMerge(target, source) {
6890
6890
  }
6891
6891
  return result;
6892
6892
  }
6893
- function getByDotPath2(obj, path14) {
6894
- const parts = path14.split(".");
6893
+ function getByDotPath2(obj, path16) {
6894
+ const parts = path16.split(".");
6895
6895
  let current = obj;
6896
6896
  for (const part2 of parts) {
6897
6897
  if (current === null || current === void 0 || typeof current !== "object") return void 0;
@@ -6900,8 +6900,8 @@ function getByDotPath2(obj, path14) {
6900
6900
  return current;
6901
6901
  }
6902
6902
  function stripExcludedFields(obj, paths) {
6903
- for (const path14 of paths) {
6904
- stripOnePath(obj, path14.replace(/\[\]/g, ".*").split("."));
6903
+ for (const path16 of paths) {
6904
+ stripOnePath(obj, path16.replace(/\[\]/g, ".*").split("."));
6905
6905
  }
6906
6906
  }
6907
6907
  function stripOnePath(obj, parts) {
@@ -7322,8 +7322,8 @@ function sleep2(ms) {
7322
7322
  return new Promise((resolve) => setTimeout(resolve, ms));
7323
7323
  }
7324
7324
  function stripFields(record, paths) {
7325
- for (const path14 of paths) {
7326
- stripOnePath2(record, path14.split("."));
7325
+ for (const path16 of paths) {
7326
+ stripOnePath2(record, path16.split("."));
7327
7327
  }
7328
7328
  }
7329
7329
  function stripOnePath2(obj, parts) {
@@ -7657,7 +7657,7 @@ async function syncModel(api, profile, options) {
7657
7657
  records,
7658
7658
  tableCreated
7659
7659
  ) : void 0;
7660
- const report = await writePageToMemory(
7660
+ const report2 = await writePageToMemory(
7661
7661
  profile,
7662
7662
  records,
7663
7663
  {
@@ -7669,12 +7669,12 @@ async function syncModel(api, profile, options) {
7669
7669
  alreadyEnrichedIds
7670
7670
  }
7671
7671
  );
7672
- pageReport = report;
7673
- memPreserved += report.preserved;
7674
- for (const key of report.sourceKeysSeen) seenSourceKeys.add(key);
7672
+ pageReport = report2;
7673
+ memPreserved += report2.preserved;
7674
+ for (const key of report2.sourceKeysSeen) seenSourceKeys.add(key);
7675
7675
  if (hasHooks && !db) {
7676
- inserts = report.inserts;
7677
- updates = report.updates;
7676
+ inserts = report2.inserts;
7677
+ updates = report2.updates;
7678
7678
  }
7679
7679
  } catch (err) {
7680
7680
  const msg = err instanceof Error ? err.message : String(err);
@@ -7993,7 +7993,7 @@ function detectColumnType2(value) {
7993
7993
  }
7994
7994
  async function testSyncProfile(api, profile) {
7995
7995
  const checks = [];
7996
- const report = {
7996
+ const report2 = {
7997
7997
  platform: profile.platform,
7998
7998
  model: profile.model,
7999
7999
  ok: false,
@@ -8025,7 +8025,7 @@ async function testSyncProfile(api, profile) {
8025
8025
  ok: false,
8026
8026
  detail: err instanceof Error ? err.message : String(err)
8027
8027
  });
8028
- return report;
8028
+ return report2;
8029
8029
  }
8030
8030
  let actionDetails;
8031
8031
  try {
@@ -8037,7 +8037,7 @@ async function testSyncProfile(api, profile) {
8037
8037
  ok: false,
8038
8038
  detail: err instanceof Error ? err.message : String(err)
8039
8039
  });
8040
- return report;
8040
+ return report2;
8041
8041
  }
8042
8042
  if (actionDetails.tags?.includes("custom")) {
8043
8043
  checks.push({
@@ -8045,7 +8045,7 @@ async function testSyncProfile(api, profile) {
8045
8045
  ok: false,
8046
8046
  detail: `Action ${profile.actionId} is tagged "custom". Sync only supports passthrough actions. Run 'one actions find ${profile.platform} "list ${profile.model}" -t knowledge' to find one.`
8047
8047
  });
8048
- return report;
8048
+ return report2;
8049
8049
  }
8050
8050
  let responseData;
8051
8051
  try {
@@ -8062,7 +8062,7 @@ async function testSyncProfile(api, profile) {
8062
8062
  } catch (err) {
8063
8063
  const detail = err instanceof ApiError ? `HTTP ${err.status}: ${err.message}` : err instanceof Error ? err.message : String(err);
8064
8064
  checks.push({ name: "single-page fetch", ok: false, detail });
8065
- return report;
8065
+ return report2;
8066
8066
  }
8067
8067
  let resolvedResultsPath = profile.resultsPath;
8068
8068
  const rawCandidate = isRootPath(resolvedResultsPath) ? responseData : getByDotPath(responseData, resolvedResultsPath);
@@ -8071,8 +8071,8 @@ async function testSyncProfile(api, profile) {
8071
8071
  if (Array.isArray(responseData)) {
8072
8072
  resolvedResultsPath = "";
8073
8073
  records = responseData;
8074
- report.autoFixed = report.autoFixed ?? {};
8075
- report.autoFixed.resultsPath = "";
8074
+ report2.autoFixed = report2.autoFixed ?? {};
8075
+ report2.autoFixed.resultsPath = "";
8076
8076
  checks.push({
8077
8077
  name: `resultsPath auto-discovered \u2192 <root>`,
8078
8078
  ok: true,
@@ -8084,8 +8084,8 @@ async function testSyncProfile(api, profile) {
8084
8084
  if (arrayKey) {
8085
8085
  resolvedResultsPath = arrayKey;
8086
8086
  records = topObj[arrayKey];
8087
- report.autoFixed = report.autoFixed ?? {};
8088
- report.autoFixed.resultsPath = arrayKey;
8087
+ report2.autoFixed = report2.autoFixed ?? {};
8088
+ report2.autoFixed.resultsPath = arrayKey;
8089
8089
  checks.push({
8090
8090
  name: `resultsPath auto-discovered \u2192 "${arrayKey}"`,
8091
8091
  ok: true,
@@ -8101,7 +8101,7 @@ async function testSyncProfile(api, profile) {
8101
8101
  ok: false,
8102
8102
  detail: `Not an array. Response keys: [${topKeys.join(", ")}]`
8103
8103
  });
8104
- return report;
8104
+ return report2;
8105
8105
  }
8106
8106
  let wrappedPrimitives = false;
8107
8107
  if (records.length > 0 && typeof records[0] !== "object") {
@@ -8122,8 +8122,8 @@ async function testSyncProfile(api, profile) {
8122
8122
  });
8123
8123
  if (records.length === 0) {
8124
8124
  checks.push({ name: "sample record available", ok: false, detail: "empty result set" });
8125
- report.ok = checks.every((c) => c.ok === true || c.name === "sample record available");
8126
- return report;
8125
+ report2.ok = checks.every((c) => c.ok === true || c.name === "sample record available");
8126
+ return report2;
8127
8127
  }
8128
8128
  const first = records[0];
8129
8129
  let resolvedIdField = profile.idField;
@@ -8137,8 +8137,8 @@ async function testSyncProfile(api, profile) {
8137
8137
  if (found) {
8138
8138
  resolvedIdField = found;
8139
8139
  idValue = getByDotPath(first, found);
8140
- report.autoFixed = report.autoFixed ?? {};
8141
- report.autoFixed.idField = found;
8140
+ report2.autoFixed = report2.autoFixed ?? {};
8141
+ report2.autoFixed.idField = found;
8142
8142
  checks.push({
8143
8143
  name: `idField auto-discovered \u2192 "${found}"`,
8144
8144
  ok: true,
@@ -8175,7 +8175,7 @@ async function testSyncProfile(api, profile) {
8175
8175
  ok: true,
8176
8176
  detail: JSON.stringify(nextParams).slice(0, 120)
8177
8177
  });
8178
- report.paginationPreview = nextParams;
8178
+ report2.paginationPreview = nextParams;
8179
8179
  } else {
8180
8180
  checks.push({
8181
8181
  name: `pagination "${profile.pagination.type}"`,
@@ -8183,22 +8183,22 @@ async function testSyncProfile(api, profile) {
8183
8183
  detail: "no next page (single-page result or end-of-data)"
8184
8184
  });
8185
8185
  }
8186
- report.detectedColumns = Object.entries(first).map(([name, value]) => ({
8186
+ report2.detectedColumns = Object.entries(first).map(([name, value]) => ({
8187
8187
  name,
8188
8188
  type: detectColumnType2(value)
8189
8189
  }));
8190
- report.sample = first;
8191
- report.samples = records.slice(0, SEARCHABLE_SAMPLE_SIZE);
8192
- if (profile.enrich && (profile.identityKeys?.length ?? 0) > 0 && report.samples.length > 0) {
8193
- const merged = await enrichOneForPreview(api, profile, report.samples[0], connectionKey);
8190
+ report2.sample = first;
8191
+ report2.samples = records.slice(0, SEARCHABLE_SAMPLE_SIZE);
8192
+ if (profile.enrich && (profile.identityKeys?.length ?? 0) > 0 && report2.samples.length > 0) {
8193
+ const merged = await enrichOneForPreview(api, profile, report2.samples[0], connectionKey);
8194
8194
  if (merged) {
8195
8195
  const preview = buildIdentityKeysPreview([merged], profile);
8196
- if (preview) report.identityKeysPreview = { ...preview, previewedAfterEnrich: true };
8196
+ if (preview) report2.identityKeysPreview = { ...preview, previewedAfterEnrich: true };
8197
8197
  }
8198
8198
  }
8199
- report.identityKeysPreview ??= buildIdentityKeysPreview(report.samples, profile);
8200
- report.ok = checks.every((c) => c.ok);
8201
- return report;
8199
+ report2.identityKeysPreview ??= buildIdentityKeysPreview(report2.samples, profile);
8200
+ report2.ok = checks.every((c) => c.ok);
8201
+ return report2;
8202
8202
  }
8203
8203
 
8204
8204
  // src/lib/memory/sync/infer.ts
@@ -8974,11 +8974,11 @@ function isNoise(s) {
8974
8974
  if (/^-?\d+(\.\d+)?([eE][-+]?\d+)?$/.test(s)) return true;
8975
8975
  return false;
8976
8976
  }
8977
- function recordSample(stats, path14, value, jsType, recordIndex, totalSamples) {
8978
- let s = stats.get(path14);
8977
+ function recordSample(stats, path16, value, jsType, recordIndex, totalSamples) {
8978
+ let s = stats.get(path16);
8979
8979
  if (!s) {
8980
8980
  s = {
8981
- path: path14,
8981
+ path: path16,
8982
8982
  total: totalSamples,
8983
8983
  recordIndices: /* @__PURE__ */ new Set(),
8984
8984
  lenSum: 0,
@@ -8987,7 +8987,7 @@ function recordSample(stats, path14, value, jsType, recordIndex, totalSamples) {
8987
8987
  primaryType: jsType,
8988
8988
  examples: []
8989
8989
  };
8990
- stats.set(path14, s);
8990
+ stats.set(path16, s);
8991
8991
  }
8992
8992
  s.recordIndices.add(recordIndex);
8993
8993
  s.lenSum += value.length;
@@ -8996,28 +8996,28 @@ function recordSample(stats, path14, value, jsType, recordIndex, totalSamples) {
8996
8996
  if (s.primaryType !== jsType) s.primaryType = "mixed";
8997
8997
  if (s.examples.length < 3 && value.length < 200) s.examples.push(value);
8998
8998
  }
8999
- function walkRecord(record, path14, stats, recordIndex, totalSamples) {
8999
+ function walkRecord(record, path16, stats, recordIndex, totalSamples) {
9000
9000
  if (record === null || record === void 0) return;
9001
9001
  if (typeof record === "string") {
9002
9002
  const trimmed = record.trim();
9003
- if (trimmed) recordSample(stats, path14, trimmed, "string", recordIndex, totalSamples);
9003
+ if (trimmed) recordSample(stats, path16, trimmed, "string", recordIndex, totalSamples);
9004
9004
  return;
9005
9005
  }
9006
9006
  if (typeof record === "number" || typeof record === "boolean") {
9007
9007
  const str = String(record);
9008
9008
  const kind = typeof record === "number" ? "number" : "boolean";
9009
- if (str) recordSample(stats, path14, str, kind, recordIndex, totalSamples);
9009
+ if (str) recordSample(stats, path16, str, kind, recordIndex, totalSamples);
9010
9010
  return;
9011
9011
  }
9012
9012
  if (Array.isArray(record)) {
9013
- const childPath = path14 ? `${path14}[]` : "[]";
9013
+ const childPath = path16 ? `${path16}[]` : "[]";
9014
9014
  for (const item of record) walkRecord(item, childPath, stats, recordIndex, totalSamples);
9015
9015
  return;
9016
9016
  }
9017
9017
  if (typeof record === "object") {
9018
9018
  for (const [key, value] of Object.entries(record)) {
9019
9019
  if (key.startsWith("_")) continue;
9020
- const childPath = path14 ? `${path14}.${key}` : key;
9020
+ const childPath = path16 ? `${path16}.${key}` : key;
9021
9021
  walkRecord(value, childPath, stats, recordIndex, totalSamples);
9022
9022
  }
9023
9023
  }
@@ -9371,38 +9371,38 @@ async function syncTestCommand(platformModel, options = {}) {
9371
9371
  );
9372
9372
  }
9373
9373
  const api = getApi();
9374
- const report = await testSyncProfile(api, profile);
9375
- if (report.autoFixed && Object.keys(report.autoFixed).length > 0) {
9374
+ const report2 = await testSyncProfile(api, profile);
9375
+ if (report2.autoFixed && Object.keys(report2.autoFixed).length > 0) {
9376
9376
  try {
9377
9377
  const existing = readProfile(platform, model);
9378
9378
  if (existing) {
9379
- const patched = { ...existing, ...report.autoFixed };
9379
+ const patched = { ...existing, ...report2.autoFixed };
9380
9380
  writeProfile(patched);
9381
9381
  }
9382
9382
  } catch {
9383
9383
  }
9384
9384
  }
9385
- const searchablePreview = options.showSearchable ? buildSearchablePreview(profile, report.samples) : null;
9385
+ const searchablePreview = options.showSearchable ? buildSearchablePreview(profile, report2.samples) : null;
9386
9386
  if (isAgentMode()) {
9387
- json({ ...report, ...searchablePreview ? { searchable: searchablePreview } : {} });
9387
+ json({ ...report2, ...searchablePreview ? { searchable: searchablePreview } : {} });
9388
9388
  return;
9389
9389
  }
9390
- for (const c of report.checks) {
9390
+ for (const c of report2.checks) {
9391
9391
  const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
9392
9392
  console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
9393
9393
  }
9394
- if (report.detectedColumns && report.detectedColumns.length > 0) {
9394
+ if (report2.detectedColumns && report2.detectedColumns.length > 0) {
9395
9395
  console.log(`
9396
9396
  ${pc11.bold("Detected columns:")}`);
9397
- for (const col of report.detectedColumns.slice(0, 20)) {
9397
+ for (const col of report2.detectedColumns.slice(0, 20)) {
9398
9398
  console.log(` ${col.name.padEnd(30)} ${pc11.dim(col.type)}`);
9399
9399
  }
9400
- if (report.detectedColumns.length > 20) {
9401
- console.log(pc11.dim(` ... and ${report.detectedColumns.length - 20} more`));
9400
+ if (report2.detectedColumns.length > 20) {
9401
+ console.log(pc11.dim(` ... and ${report2.detectedColumns.length - 20} more`));
9402
9402
  }
9403
9403
  }
9404
- if (report.identityKeysPreview) {
9405
- const { perRecord, sampleKeys, entityFanOut, resolvesAfterEnrich } = report.identityKeysPreview;
9404
+ if (report2.identityKeysPreview) {
9405
+ const { perRecord, sampleKeys, entityFanOut, resolvesAfterEnrich } = report2.identityKeysPreview;
9406
9406
  const total = perRecord.reduce((a, b) => a + b, 0);
9407
9407
  const min = perRecord.length ? Math.min(...perRecord) : 0;
9408
9408
  const max = perRecord.length ? Math.max(...perRecord) : 0;
@@ -9444,7 +9444,7 @@ async function syncTestCommand(platformModel, options = {}) {
9444
9444
  }
9445
9445
  console.log(
9446
9446
  `
9447
- ${report.ok ? pc11.green("Profile looks good.") : pc11.red("Profile has issues.")} ` + (report.ok ? `Run: ${pc11.bold(`one sync run ${platform} --models ${model}`)}` : "Fix the issues above and test again.")
9447
+ ${report2.ok ? pc11.green("Profile looks good.") : pc11.red("Profile has issues.")} ` + (report2.ok ? `Run: ${pc11.bold(`one sync run ${platform} --models ${model}`)}` : "Fix the issues above and test again.")
9448
9448
  );
9449
9449
  }
9450
9450
  function buildSearchablePreview(profile, samples) {
@@ -9452,7 +9452,7 @@ function buildSearchablePreview(profile, samples) {
9452
9452
  const first = samples[0];
9453
9453
  const paths = getSearchablePaths(profile);
9454
9454
  if (paths) {
9455
- const perPathAgg = paths.map((path14) => ({ path: path14, hits: 0, total: samples.length, sample: "" }));
9455
+ const perPathAgg = paths.map((path16) => ({ path: path16, hits: 0, total: samples.length, sample: "" }));
9456
9456
  for (const record of samples) {
9457
9457
  const { paths: perPath } = extractSearchableFromPaths(record, paths);
9458
9458
  perPath.forEach((p9, i) => {
@@ -9720,8 +9720,8 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
9720
9720
  );
9721
9721
  }
9722
9722
  const api = getApi();
9723
- const report = await testSyncProfile(api, profile);
9724
- const samples = report.samples ?? [];
9723
+ const report2 = await testSyncProfile(api, profile);
9724
+ const samples = report2.samples ?? [];
9725
9725
  if (samples.length === 0) {
9726
9726
  error(
9727
9727
  "No sample records available to rank against. Fix the failures in `sync test` first, then re-run suggest-searchable."
@@ -10409,16 +10409,16 @@ function projectEmbeddingApiKey(cfg) {
10409
10409
  }
10410
10410
  function redactSecrets(cfg) {
10411
10411
  const copy = JSON.parse(JSON.stringify(cfg));
10412
- for (const path14 of SECRET_PATHS) {
10413
- const val = getPath(copy, path14);
10412
+ for (const path16 of SECRET_PATHS) {
10413
+ const val = getPath(copy, path16);
10414
10414
  if (typeof val === "string" && val.length > 0) {
10415
- setPath(copy, path14, `${val.slice(0, 6)}\u2026(redacted, use --show-secrets)`);
10415
+ setPath(copy, path16, `${val.slice(0, 6)}\u2026(redacted, use --show-secrets)`);
10416
10416
  }
10417
10417
  }
10418
10418
  return copy;
10419
10419
  }
10420
- function getPath(obj, path14) {
10421
- const parts = path14.split(".");
10420
+ function getPath(obj, path16) {
10421
+ const parts = path16.split(".");
10422
10422
  let cur = obj;
10423
10423
  for (const part2 of parts) {
10424
10424
  if (cur == null || typeof cur !== "object") return void 0;
@@ -10426,8 +10426,8 @@ function getPath(obj, path14) {
10426
10426
  }
10427
10427
  return cur;
10428
10428
  }
10429
- function setPath(obj, path14, value) {
10430
- const parts = path14.split(".");
10429
+ function setPath(obj, path16, value) {
10430
+ const parts = path16.split(".");
10431
10431
  let cur = obj;
10432
10432
  for (let i = 0; i < parts.length - 1; i++) {
10433
10433
  const part2 = parts[i];
@@ -10437,8 +10437,8 @@ function setPath(obj, path14, value) {
10437
10437
  cur[parts[parts.length - 1]] = value;
10438
10438
  return obj;
10439
10439
  }
10440
- function unsetPath(obj, path14) {
10441
- const parts = path14.split(".");
10440
+ function unsetPath(obj, path16) {
10441
+ const parts = path16.split(".");
10442
10442
  let cur = obj;
10443
10443
  for (let i = 0; i < parts.length - 1; i++) {
10444
10444
  const part2 = parts[i];
@@ -11413,6 +11413,7 @@ Request specific sections:
11413
11413
  - Connection keys come from \`one connection list\` \u2014 don't hardcode them
11414
11414
  - \`connection list\` also reports an \`access\` field per connection (\`full\` / \`methods\` / \`actions\`) \u2014 read it before planning so you don't propose an action the access config will reject
11415
11415
  - 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\`
11416
+ - Building an app whose users connect their own tools? \`one skills add connect\` (run inside the app) installs the One Connect skill from the \`@withone/connect\` version the app uses, into \`.agents/skills/one-connect\` and \`.claude/skills/one-connect\`. \`one --agent skills list\` shows what is installed; any \`one\` command in the project refreshes it after an SDK upgrade
11416
11417
  `;
11417
11418
  var GUIDE_ACTIONS = `# One Actions \u2014 Reference
11418
11419
 
@@ -12806,22 +12807,22 @@ function buildWorkflowIdeas(connections) {
12806
12807
  // src/commands/logout.ts
12807
12808
  import fs13 from "fs";
12808
12809
  import * as p8 from "@clack/prompts";
12809
- function formatWhoami(config2, apiKey, pc16) {
12810
+ function formatWhoami(config2, apiKey, pc17) {
12810
12811
  const whoami = config2.whoami;
12811
12812
  const env = getEnvFromApiKey(apiKey);
12812
- const envLabel = env === "test" ? pc16.yellow("test") : pc16.green("live");
12813
+ const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
12813
12814
  const lines = [];
12814
12815
  if (whoami) {
12815
12816
  const contextParts = [];
12816
12817
  if (whoami.organization) contextParts.push(whoami.organization.name);
12817
12818
  if (whoami.project) contextParts.push(whoami.project.name);
12818
12819
  const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
12819
- lines.push(`${pc16.bold(scopeDisplay)} ${pc16.dim("\xB7")} ${envLabel}`);
12820
- lines.push(`${whoami.user.name} ${pc16.dim(`(${whoami.user.email})`)}`);
12821
- if (config2.apiKeyName) lines.push(`${pc16.dim("Key:")} ${config2.apiKeyName}`);
12820
+ lines.push(`${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
12821
+ lines.push(`${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
12822
+ if (config2.apiKeyName) lines.push(`${pc17.dim("Key:")} ${config2.apiKeyName}`);
12822
12823
  } else {
12823
12824
  const keyLabel = config2.apiKeyName ?? `${apiKey.slice(0, 8)}...`;
12824
- lines.push(`${pc16.dim("Key:")} ${keyLabel} ${pc16.dim("\xB7")} ${envLabel}`);
12825
+ lines.push(`${pc17.dim("Key:")} ${keyLabel} ${pc17.dim("\xB7")} ${envLabel}`);
12825
12826
  }
12826
12827
  return lines;
12827
12828
  }
@@ -12850,7 +12851,7 @@ async function logoutCommand() {
12850
12851
  json({ status: cleared ? "logged_out" : "not_logged_in", message: cleared ? "Credentials cleared." : "No config found." });
12851
12852
  return;
12852
12853
  }
12853
- const pc16 = (await import("picocolors")).default;
12854
+ const pc17 = (await import("picocolors")).default;
12854
12855
  const globalConfig = readGlobalConfig();
12855
12856
  const projectConfig = readProjectConfig();
12856
12857
  const hasGlobal = globalConfig?.apiKey != null;
@@ -12859,13 +12860,13 @@ async function logoutCommand() {
12859
12860
  if (hasGlobal && hasProject) {
12860
12861
  const infoLines = ["You are logged in with multiple configs.", ""];
12861
12862
  if (projectConfig) {
12862
- infoLines.push(`${pc16.cyan("Local config:")}`);
12863
- infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc16));
12863
+ infoLines.push(`${pc17.cyan("Local config:")}`);
12864
+ infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
12864
12865
  infoLines.push("");
12865
12866
  }
12866
12867
  if (globalConfig) {
12867
- infoLines.push(`${pc16.magenta("Global config:")}`);
12868
- infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc16));
12868
+ infoLines.push(`${pc17.magenta("Global config:")}`);
12869
+ infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
12869
12870
  }
12870
12871
  p8.note(infoLines.join("\n"));
12871
12872
  const choice = await p8.select({
@@ -12883,14 +12884,14 @@ async function logoutCommand() {
12883
12884
  targetScope = choice;
12884
12885
  } else if (hasProject) {
12885
12886
  const infoLines = ["You are logged in.", ""];
12886
- infoLines.push(`${pc16.dim("Stored in")} ${pc16.cyan("local config")}`);
12887
- infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc16));
12887
+ infoLines.push(`${pc17.dim("Stored in")} ${pc17.cyan("local config")}`);
12888
+ infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
12888
12889
  p8.note(infoLines.join("\n"));
12889
12890
  targetScope = "project";
12890
12891
  } else if (hasGlobal) {
12891
12892
  const infoLines = ["You are logged in.", ""];
12892
- infoLines.push(`${pc16.dim("Stored in")} ${pc16.magenta("global config")}`);
12893
- infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc16));
12893
+ infoLines.push(`${pc17.dim("Stored in")} ${pc17.magenta("global config")}`);
12894
+ infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
12894
12895
  p8.note(infoLines.join("\n"));
12895
12896
  targetScope = "global";
12896
12897
  } else {
@@ -12924,6 +12925,538 @@ async function logoutCommand() {
12924
12925
  p8.outro("Logged out.");
12925
12926
  }
12926
12927
 
12928
+ // src/lib/project-skills.ts
12929
+ import crypto2 from "crypto";
12930
+ import fs14 from "fs";
12931
+ import os3 from "os";
12932
+ import path13 from "path";
12933
+ import zlib from "zlib";
12934
+ var OPTIONAL_SKILLS = [
12935
+ {
12936
+ name: "connect",
12937
+ dirName: "one-connect",
12938
+ title: "One Connect",
12939
+ description: "Add One Connect to an app: your users grant it scoped, revocable access to their own tools.",
12940
+ packageName: "@withone/connect",
12941
+ packagePath: "skills/one-connect"
12942
+ }
12943
+ ];
12944
+ function findOptionalSkill(name) {
12945
+ const wanted = name.trim().toLowerCase();
12946
+ return OPTIONAL_SKILLS.find((s) => s.name === wanted || s.dirName === wanted);
12947
+ }
12948
+ var UNIVERSAL_PROJECT_DIR = ".agents/skills";
12949
+ var SKILL_AGENT_TARGETS = [
12950
+ { id: "claude-code", name: "Claude Code", projectDir: ".claude/skills", projectMarker: null, homeDir: ".claude/skills" },
12951
+ { id: "codex", name: "Codex", projectDir: UNIVERSAL_PROJECT_DIR, projectMarker: null, homeDir: ".codex/skills" },
12952
+ { id: "cursor", name: "Cursor", projectDir: UNIVERSAL_PROJECT_DIR, projectMarker: null, homeDir: ".cursor/skills" },
12953
+ { id: "amp", name: "Amp", projectDir: UNIVERSAL_PROJECT_DIR, projectMarker: null, homeDir: ".amp/skills" },
12954
+ { id: "opencode", name: "OpenCode", projectDir: UNIVERSAL_PROJECT_DIR, projectMarker: null, homeDir: ".opencode/skills" },
12955
+ { id: "windsurf", name: "Windsurf", projectDir: ".windsurf/skills", projectMarker: ".windsurf", homeDir: ".codeium/windsurf/skills" },
12956
+ { id: "kiro", name: "Kiro", projectDir: ".kiro/skills", projectMarker: ".kiro", homeDir: ".kiro/skills" },
12957
+ { id: "goose", name: "Goose", projectDir: ".goose/skills", projectMarker: ".goose", homeDir: ".config/goose/skills" },
12958
+ { id: "roo", name: "Roo", projectDir: ".roo/skills", projectMarker: ".roo", homeDir: ".roo/skills" }
12959
+ ];
12960
+ function findAgentTarget(id) {
12961
+ return SKILL_AGENT_TARGETS.find((a) => a.id === id.trim().toLowerCase());
12962
+ }
12963
+ function findProjectRoot(start) {
12964
+ let dir = path13.resolve(start);
12965
+ for (; ; ) {
12966
+ if (fs14.existsSync(path13.join(dir, "package.json")) || fs14.existsSync(path13.join(dir, ".git"))) return dir;
12967
+ const parent = path13.dirname(dir);
12968
+ if (parent === dir) return null;
12969
+ dir = parent;
12970
+ }
12971
+ }
12972
+ function findInstalledPackage(start, packageName) {
12973
+ let dir = path13.resolve(start);
12974
+ for (; ; ) {
12975
+ const pkgDir = path13.join(dir, "node_modules", ...packageName.split("/"));
12976
+ const manifest = path13.join(pkgDir, "package.json");
12977
+ if (fs14.existsSync(manifest)) {
12978
+ try {
12979
+ const { version: version2 } = JSON.parse(fs14.readFileSync(manifest, "utf-8"));
12980
+ if (version2) return { dir: pkgDir, version: version2 };
12981
+ } catch {
12982
+ }
12983
+ }
12984
+ const parent = path13.dirname(dir);
12985
+ if (parent === dir) return null;
12986
+ dir = parent;
12987
+ }
12988
+ }
12989
+ var SkillSourceError = class extends Error {
12990
+ constructor(message, code) {
12991
+ super(message);
12992
+ this.code = code;
12993
+ }
12994
+ };
12995
+ function projectSkillSource(skill, projectDir) {
12996
+ const installed = findInstalledPackage(projectDir, skill.packageName);
12997
+ if (!installed) return null;
12998
+ const dir = path13.join(installed.dir, ...skill.packagePath.split("/"));
12999
+ if (!fs14.existsSync(path13.join(dir, "SKILL.md"))) {
13000
+ throw new SkillSourceError(
13001
+ `${skill.packageName} ${installed.version} does not include the ${skill.title} skill. Upgrade it: npm install ${skill.packageName}@latest`,
13002
+ "skill-missing"
13003
+ );
13004
+ }
13005
+ return { kind: "project", version: installed.version, dir, origin: installed.dir, cleanup: () => {
13006
+ } };
13007
+ }
13008
+ function registryBase() {
13009
+ return (process.env.ONE_SKILLS_REGISTRY || "https://registry.npmjs.org").replace(/\/+$/, "");
13010
+ }
13011
+ async function registrySkillSource(skill, fetchImpl = fetch) {
13012
+ const metaUrl = `${registryBase()}/${skill.packageName.replace("/", "%2f")}/latest`;
13013
+ let meta;
13014
+ try {
13015
+ const res = await fetchImpl(metaUrl, { headers: { accept: "application/json" } });
13016
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
13017
+ meta = await res.json();
13018
+ } catch (err) {
13019
+ throw new SkillSourceError(`Could not reach the npm registry for ${skill.packageName} (${err.message}).`, "registry-failed");
13020
+ }
13021
+ const version2 = meta.version;
13022
+ const tarball = meta.dist?.tarball;
13023
+ const integrity = meta.dist?.integrity;
13024
+ if (!version2 || !tarball || !integrity) {
13025
+ throw new SkillSourceError(`The npm registry returned no tarball for ${skill.packageName}.`, "registry-failed");
13026
+ }
13027
+ let bytes;
13028
+ try {
13029
+ const res = await fetchImpl(tarball);
13030
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
13031
+ bytes = Buffer.from(await res.arrayBuffer());
13032
+ } catch (err) {
13033
+ throw new SkillSourceError(`Could not download ${skill.packageName}@${version2} (${err.message}).`, "registry-failed");
13034
+ }
13035
+ if (!matchesIntegrity(bytes, integrity)) {
13036
+ throw new SkillSourceError(`${skill.packageName}@${version2} failed its integrity check; nothing was installed.`, "integrity");
13037
+ }
13038
+ const files = readTarEntries(zlib.gunzipSync(bytes), `package/${skill.packagePath}/`);
13039
+ if (!files.some((f) => f.path === "SKILL.md")) {
13040
+ throw new SkillSourceError(`${skill.packageName}@${version2} does not include the ${skill.title} skill.`, "not-in-package");
13041
+ }
13042
+ const tmp = fs14.mkdtempSync(path13.join(os3.tmpdir(), "one-skill-"));
13043
+ for (const file of files) {
13044
+ const dest = path13.join(tmp, ...file.path.split("/"));
13045
+ fs14.mkdirSync(path13.dirname(dest), { recursive: true });
13046
+ fs14.writeFileSync(dest, file.data);
13047
+ }
13048
+ return {
13049
+ kind: "registry",
13050
+ version: version2,
13051
+ dir: tmp,
13052
+ origin: tarball,
13053
+ cleanup: () => {
13054
+ try {
13055
+ fs14.rmSync(tmp, { recursive: true, force: true });
13056
+ } catch {
13057
+ }
13058
+ }
13059
+ };
13060
+ }
13061
+ function matchesIntegrity(bytes, integrity) {
13062
+ return integrity.split(/\s+/).some((entry) => {
13063
+ const dash = entry.indexOf("-");
13064
+ if (dash < 0) return false;
13065
+ const algorithm = entry.slice(0, dash);
13066
+ if (!["sha512", "sha384", "sha256"].includes(algorithm)) return false;
13067
+ return crypto2.createHash(algorithm).update(bytes).digest("base64") === entry.slice(dash + 1);
13068
+ });
13069
+ }
13070
+ function readTarEntries(tar, prefix) {
13071
+ const out = [];
13072
+ let offset = 0;
13073
+ let paxPath = null;
13074
+ const text5 = (start, length) => {
13075
+ const raw = tar.subarray(start, start + length);
13076
+ const end = raw.indexOf(0);
13077
+ return raw.subarray(0, end < 0 ? raw.length : end).toString("utf-8");
13078
+ };
13079
+ while (offset + 512 <= tar.length) {
13080
+ const header = tar.subarray(offset, offset + 512);
13081
+ if (header.every((b) => b === 0)) break;
13082
+ const size = parseInt(text5(offset + 124, 12).trim() || "0", 8);
13083
+ const type = String.fromCharCode(header[156] || 48);
13084
+ let name = text5(offset, 100);
13085
+ if (text5(offset + 257, 5) === "ustar") {
13086
+ const pre = text5(offset + 345, 155);
13087
+ if (pre) name = `${pre}/${name}`;
13088
+ }
13089
+ const body = tar.subarray(offset + 512, offset + 512 + size);
13090
+ if (type === "x") {
13091
+ const match = /\d+ path=([^\n]*)\n/.exec(body.toString("utf-8"));
13092
+ paxPath = match ? match[1] : null;
13093
+ } else {
13094
+ const full = paxPath ?? name;
13095
+ paxPath = null;
13096
+ if ((type === "0" || type === "\0") && full.startsWith(prefix)) {
13097
+ const rel = full.slice(prefix.length);
13098
+ const parts = rel.split("/");
13099
+ if (rel && !path13.isAbsolute(rel) && !parts.includes("..") && !parts.includes("")) {
13100
+ out.push({ path: rel, data: Buffer.from(body) });
13101
+ }
13102
+ }
13103
+ }
13104
+ offset += 512 + Math.ceil(size / 512) * 512;
13105
+ }
13106
+ return out;
13107
+ }
13108
+ var SKILL_STAMP = ".one-skill.json";
13109
+ function canonicalSkillDir(skill, plan) {
13110
+ return path13.join(plan.root, ...UNIVERSAL_PROJECT_DIR.split("/"), skill.dirName);
13111
+ }
13112
+ function readStamp(dir) {
13113
+ try {
13114
+ return JSON.parse(fs14.readFileSync(path13.join(dir, SKILL_STAMP), "utf-8"));
13115
+ } catch {
13116
+ return null;
13117
+ }
13118
+ }
13119
+ function resolveAgentFolders(plan) {
13120
+ const explicit = plan.agents.length > 0;
13121
+ const chosen = SKILL_AGENT_TARGETS.filter((agent) => {
13122
+ if (explicit) return plan.agents.includes(agent.id);
13123
+ if (plan.scope === "global") {
13124
+ return fs14.existsSync(path13.join(plan.root, path13.dirname(agent.homeDir)));
13125
+ }
13126
+ return agent.projectMarker === null || fs14.existsSync(path13.join(plan.root, agent.projectMarker));
13127
+ });
13128
+ const readers = [];
13129
+ const byDir = /* @__PURE__ */ new Map();
13130
+ for (const agent of chosen) {
13131
+ const dir = plan.scope === "project" ? agent.projectDir : agent.homeDir;
13132
+ if (dir === UNIVERSAL_PROJECT_DIR) {
13133
+ readers.push(agent.name);
13134
+ continue;
13135
+ }
13136
+ byDir.set(dir, [...byDir.get(dir) ?? [], agent.name]);
13137
+ }
13138
+ return { readers, folders: [...byDir].map(([dir, agents]) => ({ dir, agents })) };
13139
+ }
13140
+ function copyDir(src, dest) {
13141
+ fs14.mkdirSync(dest, { recursive: true });
13142
+ for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
13143
+ if (entry.name === SKILL_STAMP) continue;
13144
+ const from = path13.join(src, entry.name);
13145
+ const to = path13.join(dest, entry.name);
13146
+ if (entry.isDirectory()) copyDir(from, to);
13147
+ else fs14.copyFileSync(from, to);
13148
+ }
13149
+ }
13150
+ function removePath(target) {
13151
+ try {
13152
+ const stat = fs14.lstatSync(target);
13153
+ if (stat.isSymbolicLink()) fs14.unlinkSync(target);
13154
+ else fs14.rmSync(target, { recursive: true, force: true });
13155
+ } catch {
13156
+ }
13157
+ }
13158
+ function linkDir(target, linkPath) {
13159
+ removePath(linkPath);
13160
+ fs14.mkdirSync(path13.dirname(linkPath), { recursive: true });
13161
+ try {
13162
+ fs14.symlinkSync(path13.relative(path13.dirname(linkPath), target), linkPath, "dir");
13163
+ return "symlink";
13164
+ } catch {
13165
+ }
13166
+ try {
13167
+ fs14.symlinkSync(target, linkPath, "junction");
13168
+ return "junction";
13169
+ } catch {
13170
+ }
13171
+ copyDir(target, linkPath);
13172
+ return "copy";
13173
+ }
13174
+ function installSkill(skill, source, plan, cliVersion2) {
13175
+ const canonical = canonicalSkillDir(skill, plan);
13176
+ const previous = readStamp(canonical);
13177
+ const { readers, folders } = resolveAgentFolders(plan);
13178
+ const nextPaths = new Set(folders.map((f) => `${f.dir}/${skill.dirName}`));
13179
+ for (const link of previous?.links ?? []) {
13180
+ if (!nextPaths.has(toPosix(link.path))) removePath(fromStamp(plan.root, link.path));
13181
+ }
13182
+ removePath(canonical);
13183
+ copyDir(source.dir, canonical);
13184
+ const links = folders.map((folder) => {
13185
+ const rel = `${folder.dir}/${skill.dirName}`;
13186
+ return { path: rel, kind: linkDir(canonical, fromStamp(plan.root, rel)), agents: folder.agents };
13187
+ });
13188
+ const stamp = {
13189
+ skill: skill.name,
13190
+ package: skill.packageName,
13191
+ version: source.version,
13192
+ source: source.kind,
13193
+ scope: plan.scope,
13194
+ links,
13195
+ installedBy: `@withone/cli@${cliVersion2}`,
13196
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
13197
+ };
13198
+ fs14.writeFileSync(path13.join(canonical, SKILL_STAMP), JSON.stringify(stamp, null, 2) + "\n");
13199
+ return { skill, version: source.version, source: source.kind, origin: source.origin, scope: plan.scope, root: plan.root, canonical, readers, links };
13200
+ }
13201
+ function removeSkill(skill, plan) {
13202
+ const canonical = canonicalSkillDir(skill, plan);
13203
+ const stamp = readStamp(canonical);
13204
+ if (!stamp && !fs14.existsSync(path13.join(canonical, "SKILL.md"))) return { removed: false, paths: [] };
13205
+ const paths = [];
13206
+ for (const link of stamp?.links ?? []) {
13207
+ const full = fromStamp(plan.root, link.path);
13208
+ if (fs14.existsSync(full) || isDanglingLink(full)) {
13209
+ removePath(full);
13210
+ paths.push(full);
13211
+ }
13212
+ }
13213
+ removePath(canonical);
13214
+ paths.push(canonical);
13215
+ return { removed: true, paths };
13216
+ }
13217
+ function toPosix(p9) {
13218
+ return p9.split("\\").join("/");
13219
+ }
13220
+ function fromStamp(root, rel) {
13221
+ return path13.join(root, ...toPosix(rel).split("/"));
13222
+ }
13223
+ function isDanglingLink(p9) {
13224
+ try {
13225
+ return fs14.lstatSync(p9).isSymbolicLink();
13226
+ } catch {
13227
+ return false;
13228
+ }
13229
+ }
13230
+ function installedSkill(skill, plan) {
13231
+ const canonical = canonicalSkillDir(skill, plan);
13232
+ if (!fs14.existsSync(path13.join(canonical, "SKILL.md"))) return null;
13233
+ const stamp = readStamp(canonical);
13234
+ return { scope: plan.scope, root: plan.root, canonical, version: stamp?.version ?? null, source: stamp?.source ?? null, links: stamp?.links ?? [] };
13235
+ }
13236
+ function globalRoot() {
13237
+ return homeDir();
13238
+ }
13239
+ function refreshProjectSkills(cwd, cliVersion2) {
13240
+ const root = findProjectRoot(cwd);
13241
+ if (!root || path13.resolve(root) === path13.resolve(homeDir())) return [];
13242
+ const results = [];
13243
+ for (const skill of OPTIONAL_SKILLS) {
13244
+ const canonical = canonicalSkillDir(skill, { scope: "project", root });
13245
+ const stamp = readStamp(canonical);
13246
+ if (!stamp || stamp.scope !== "project" || stamp.source !== "project") continue;
13247
+ const installed = findInstalledPackage(root, skill.packageName);
13248
+ if (!installed || installed.version === stamp.version) continue;
13249
+ let source;
13250
+ try {
13251
+ source = projectSkillSource(skill, root);
13252
+ } catch {
13253
+ continue;
13254
+ }
13255
+ if (!source) continue;
13256
+ const agents = SKILL_AGENT_TARGETS.filter((a) => stamp.links.some((l) => l.agents.includes(a.name)) || a.projectDir === UNIVERSAL_PROJECT_DIR).map((a) => a.id);
13257
+ installSkill(skill, source, { scope: "project", root, agents }, cliVersion2);
13258
+ results.push({ skill: skill.name, from: stamp.version, to: source.version, root });
13259
+ }
13260
+ return results;
13261
+ }
13262
+
13263
+ // src/commands/skills.ts
13264
+ import path14 from "path";
13265
+ import pc16 from "picocolors";
13266
+ function requireSkill(name) {
13267
+ const skill = findOptionalSkill(name);
13268
+ if (!skill) {
13269
+ const known = OPTIONAL_SKILLS.map((s) => s.name).join(", ");
13270
+ error(`Unknown skill "${name}". Available: ${known}. The One skill itself is installed by \`one init\`.`);
13271
+ }
13272
+ return skill;
13273
+ }
13274
+ function parseAgents(raw) {
13275
+ if (!raw) return [];
13276
+ const ids = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
13277
+ for (const id of ids) {
13278
+ if (!findAgentTarget(id)) {
13279
+ error(`Unknown agent "${id}". Use: ${SKILL_AGENT_TARGETS.map((a) => a.id).join(", ")}.`);
13280
+ }
13281
+ }
13282
+ return ids;
13283
+ }
13284
+ function resolvePlan(options) {
13285
+ const agents = parseAgents(options.agents);
13286
+ if (options.global) return { scope: "global", root: globalRoot(), agents };
13287
+ const root = findProjectRoot(process.cwd());
13288
+ if (!root || path14.resolve(root) === path14.resolve(homeDir())) {
13289
+ error("No project here: run this inside your app (a folder with package.json or .git), or pass --global.");
13290
+ }
13291
+ return { scope: "project", root, agents };
13292
+ }
13293
+ async function resolveSource(skill, plan) {
13294
+ try {
13295
+ const local = projectSkillSource(skill, plan.scope === "project" ? plan.root : process.cwd());
13296
+ if (local) return local;
13297
+ return await registrySkillSource(skill);
13298
+ } catch (err) {
13299
+ if (err instanceof SkillSourceError) error(err.message);
13300
+ throw err;
13301
+ }
13302
+ }
13303
+ function display(p9, root) {
13304
+ const rel = path14.relative(root, p9);
13305
+ return rel && !rel.startsWith("..") ? rel : p9;
13306
+ }
13307
+ function report(command, result, packageInProject) {
13308
+ const where = (p9) => display(p9, result.root);
13309
+ if (isAgentMode()) {
13310
+ json({
13311
+ command,
13312
+ skill: result.skill.name,
13313
+ name: result.skill.dirName,
13314
+ version: result.version,
13315
+ source: { kind: result.source, from: result.origin },
13316
+ scope: result.scope,
13317
+ root: result.root,
13318
+ installedTo: [
13319
+ { path: result.canonical, kind: "folder", agents: result.readers },
13320
+ ...result.links.map((l) => ({ path: path14.join(result.root, ...l.path.split("/")), kind: l.kind, agents: l.agents }))
13321
+ ],
13322
+ next: nextSteps(result, packageInProject)
13323
+ });
13324
+ return;
13325
+ }
13326
+ const scopeLabel2 = result.scope === "project" ? "this project" : "every project on this machine";
13327
+ console.log();
13328
+ console.log(` ${pc16.green("\u2713")} ${pc16.bold(`${result.skill.title} skill ${result.version}`)} installed for ${scopeLabel2}`);
13329
+ console.log();
13330
+ const from = result.source === "project" ? `${where(result.origin)} ${pc16.dim("(the version your code uses)")}` : `npm ${pc16.dim(result.scope === "project" ? `(latest ${result.skill.packageName}; not installed in this project yet)` : `(latest ${result.skill.packageName})`)}`;
13331
+ console.log(` ${pc16.dim("From")} ${from}`);
13332
+ const rows = [
13333
+ { path: where(result.canonical), agents: result.readers },
13334
+ ...result.links.map((l) => ({ path: `${l.path}${l.kind === "copy" ? pc16.dim(" (copy)") : ""}`, agents: l.agents }))
13335
+ ];
13336
+ rows.forEach((row, i) => {
13337
+ const agents = row.agents.length ? pc16.dim(row.agents.join(", ")) : pc16.dim("shared copy the links point to");
13338
+ console.log(` ${pc16.dim(i === 0 ? "To " : " ")} ${row.path} ${agents}`);
13339
+ });
13340
+ console.log();
13341
+ console.log(` ${pc16.bold("Next")}`);
13342
+ nextSteps(result, packageInProject).forEach((step, i) => console.log(` ${i + 1}. ${step}`));
13343
+ console.log();
13344
+ }
13345
+ function nextSteps(result, packageInProject) {
13346
+ const steps = [];
13347
+ if (result.scope === "project" && !packageInProject) steps.push(`Install the SDK: npm install ${result.skill.packageName}`);
13348
+ steps.push(`Ask your agent: "Add ${result.skill.title} to this app." The skill tells it every step.`);
13349
+ steps.push("Set the secrets it names in your server environment yourself; never paste them into a chat.");
13350
+ if (result.scope === "project") {
13351
+ const folders = [`${UNIVERSAL_DIR}/${result.skill.dirName}`, ...result.links.map((l) => l.path)];
13352
+ steps.push(`Commit ${folders.join(" and ")} so your team's agents share it. Any \`one\` command here refreshes it when ${result.skill.packageName} is upgraded.`);
13353
+ } else {
13354
+ steps.push(`Refresh it after a new ${result.skill.packageName} release: one skills update ${result.skill.name} --global`);
13355
+ }
13356
+ return steps;
13357
+ }
13358
+ var UNIVERSAL_DIR = ".agents/skills";
13359
+ async function skillsAddCommand(name, options) {
13360
+ const skill = requireSkill(name);
13361
+ const plan = resolvePlan(options);
13362
+ const source = await resolveSource(skill, plan);
13363
+ try {
13364
+ const result = installSkill(skill, source, plan, getCurrentVersion());
13365
+ const packageInProject = plan.scope === "project" && findInstalledPackage(plan.root, skill.packageName) !== null;
13366
+ report("skills add", result, packageInProject);
13367
+ } finally {
13368
+ source.cleanup();
13369
+ }
13370
+ }
13371
+ async function skillsUpdateCommand(name, options) {
13372
+ const skills2 = name ? [requireSkill(name)] : OPTIONAL_SKILLS;
13373
+ const plan = resolvePlan(options);
13374
+ const updated = [];
13375
+ const skipped = [];
13376
+ for (const skill of skills2) {
13377
+ const current = installedSkill(skill, plan);
13378
+ if (!current) {
13379
+ skipped.push(skill.name);
13380
+ continue;
13381
+ }
13382
+ const agents = plan.agents.length ? plan.agents : SKILL_AGENT_TARGETS.filter((a) => current.links.some((l) => l.agents.includes(a.name)) || plan.scope === "project" && a.projectDir === UNIVERSAL_DIR).map((a) => a.id);
13383
+ const source = await resolveSource(skill, plan);
13384
+ try {
13385
+ updated.push(installSkill(skill, source, { ...plan, agents }, getCurrentVersion()));
13386
+ } finally {
13387
+ source.cleanup();
13388
+ }
13389
+ }
13390
+ if (name && updated.length === 0) error(`${skills2[0].title} skill is not installed here. Add it: one skills add ${skills2[0].name}${plan.scope === "global" ? " --global" : ""}`);
13391
+ if (isAgentMode()) {
13392
+ json({
13393
+ command: "skills update",
13394
+ scope: plan.scope,
13395
+ updated: updated.map((r) => ({ skill: r.skill.name, version: r.version, source: r.source, path: r.canonical })),
13396
+ notInstalled: skipped
13397
+ });
13398
+ return;
13399
+ }
13400
+ if (updated.length === 0) {
13401
+ console.log("No optional skills are installed here. See: one skills list");
13402
+ return;
13403
+ }
13404
+ for (const r of updated) console.log(` ${pc16.green("\u2713")} ${r.skill.title} skill ${r.version} ${pc16.dim(display(r.canonical, r.root))}`);
13405
+ }
13406
+ async function skillsRemoveCommand(name, options) {
13407
+ const skill = requireSkill(name);
13408
+ const plan = resolvePlan(options);
13409
+ const result = removeSkill(skill, plan);
13410
+ if (isAgentMode()) {
13411
+ json({ command: "skills remove", skill: skill.name, scope: plan.scope, removed: result.removed, paths: result.paths });
13412
+ return;
13413
+ }
13414
+ if (!result.removed) {
13415
+ console.log(`${skill.title} skill is not installed ${plan.scope === "global" ? "globally" : "in this project"}.`);
13416
+ return;
13417
+ }
13418
+ console.log(` ${pc16.green("\u2713")} Removed the ${skill.title} skill`);
13419
+ for (const p9 of result.paths) console.log(` ${pc16.dim(display(p9, plan.root))}`);
13420
+ }
13421
+ async function skillsListCommand() {
13422
+ const projectRoot = findProjectRoot(process.cwd());
13423
+ const inProject = projectRoot && path14.resolve(projectRoot) !== path14.resolve(homeDir()) ? projectRoot : null;
13424
+ const optional = OPTIONAL_SKILLS.map((skill) => {
13425
+ const project = inProject ? installedSkill(skill, { scope: "project", root: inProject }) : null;
13426
+ const global = installedSkill(skill, { scope: "global", root: globalRoot() });
13427
+ const pkg = inProject ? findInstalledPackage(inProject, skill.packageName) : null;
13428
+ return {
13429
+ skill: skill.name,
13430
+ name: skill.dirName,
13431
+ title: skill.title,
13432
+ description: skill.description,
13433
+ package: skill.packageName,
13434
+ packageInProject: pkg?.version ?? null,
13435
+ project: project ? { version: project.version, path: project.canonical, upToDate: !pkg || project.version === pkg.version } : null,
13436
+ global: global ? { version: global.version, path: global.canonical } : null
13437
+ };
13438
+ });
13439
+ const oneSkill = {
13440
+ skill: "one",
13441
+ title: "One CLI",
13442
+ managedBy: "one init",
13443
+ installed: isSkillInstalled(),
13444
+ version: readInstalledSkillVersion()
13445
+ };
13446
+ if (isAgentMode()) {
13447
+ json({ command: "skills list", project: inProject, skills: [oneSkill, ...optional] });
13448
+ return;
13449
+ }
13450
+ console.log();
13451
+ console.log(` ${pc16.bold("one")} ${oneSkill.installed ? pc16.green(`installed ${oneSkill.version ?? ""}`.trim()) : pc16.yellow("not installed")} ${pc16.dim("managed by one init")}`);
13452
+ for (const s of optional) {
13453
+ const state = s.project ? pc16.green(`project ${s.project.version ?? ""}`.trim()) + (s.project.upToDate ? "" : pc16.yellow(` (package is ${s.packageInProject}: run one skills update ${s.skill})`)) : s.global ? pc16.green(`global ${s.global.version ?? ""}`.trim()) : pc16.dim(`not installed \u2014 one skills add ${s.skill}`);
13454
+ console.log(` ${pc16.bold(s.skill.padEnd(8))} ${state}`);
13455
+ console.log(` ${" ".repeat(8)} ${pc16.dim(s.description)}`);
13456
+ }
13457
+ console.log();
13458
+ }
13459
+
12927
13460
  // src/cli.ts
12928
13461
  silenceWarningsInAgentMode();
12929
13462
  var require4 = createRequire3(import.meta.url);
@@ -12946,6 +13479,12 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
12946
13479
  3. one actions execute <p> <id> <key> Execute the action
12947
13480
  one actions load <id> --section <s> More of an action's docs: a section a digest left out, --full, or --toc
12948
13481
 
13482
+ Skills for your coding agent:
13483
+ one skills list The One skill and the optional ones (installed or not)
13484
+ one skills add connect Add the One Connect skill to this project (--global for every project)
13485
+ one skills update [connect] Re-copy from the installed @withone/connect after an upgrade
13486
+ one skills remove connect Remove it
13487
+
12949
13488
  Guide:
12950
13489
  one guide [topic] Full CLI guide (topics: overview, actions, workflows, memory, sync, all)
12951
13490
 
@@ -13019,6 +13558,12 @@ program.hook("preAction", (thisCommand, actionCommand) => {
13019
13558
  } catch {
13020
13559
  }
13021
13560
  }
13561
+ if (commandName !== "skills") {
13562
+ try {
13563
+ refreshProjectSkills(process.cwd(), getCurrentVersion());
13564
+ } catch {
13565
+ }
13566
+ }
13022
13567
  });
13023
13568
  program.hook("postAction", async () => {
13024
13569
  await closeBackendIfCached();
@@ -13041,6 +13586,19 @@ program.command("login").description("Authenticate with One via browser").action
13041
13586
  program.command("logout").description("Clear local credentials").action(async () => {
13042
13587
  await logoutCommand();
13043
13588
  });
13589
+ var skills = program.command("skills").description("Optional skills for your coding agent, such as One Connect (the One skill itself comes from one init)");
13590
+ skills.command("list").description("Show the One skill and each optional skill, with where it is installed").action(async () => {
13591
+ await skillsListCommand();
13592
+ });
13593
+ skills.command("add <name>").description("Install an optional skill (connect) into this project, from the SDK version it uses").option("-g, --global", "Install for every project on this machine instead").option("--agents <ids>", "Comma-separated agents to link: claude-code, codex, cursor, amp, opencode, windsurf, kiro, goose, roo").action(async (name, options) => {
13594
+ await skillsAddCommand(name, options);
13595
+ });
13596
+ skills.command("update [name]").description("Re-copy installed optional skills from their source (all when no name is given)").option("-g, --global", "Update the global install").option("--agents <ids>", "Change which agents are linked").action(async (name, options) => {
13597
+ await skillsUpdateCommand(name, options);
13598
+ });
13599
+ skills.command("remove <name>").alias("rm").description("Remove an optional skill and the agent folders that point at it").option("-g, --global", "Remove the global install").action(async (name, options) => {
13600
+ await skillsRemoveCommand(name, options);
13601
+ });
13044
13602
  var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
13045
13603
  await configCommand();
13046
13604
  });
@@ -13140,8 +13698,8 @@ config.command("reset").description("Remove the project config for the current d
13140
13698
  return;
13141
13699
  }
13142
13700
  }
13143
- const fs15 = await import("fs");
13144
- fs15.unlinkSync(globalPath);
13701
+ const fs16 = await import("fs");
13702
+ fs16.unlinkSync(globalPath);
13145
13703
  if (isAgentMode()) {
13146
13704
  json({ deleted: true, scope: "global" });
13147
13705
  } else {
@@ -13158,15 +13716,15 @@ config.command("reset").description("Remove the project config for the current d
13158
13716
  }
13159
13717
  return;
13160
13718
  }
13161
- const fs14 = await import("fs");
13162
- const configContent = fs14.readFileSync(resolved.path, "utf-8");
13163
- fs14.unlinkSync(resolved.path);
13719
+ const fs15 = await import("fs");
13720
+ const configContent = fs15.readFileSync(resolved.path, "utf-8");
13721
+ fs15.unlinkSync(resolved.path);
13164
13722
  const next = resolveConfig();
13165
- fs14.mkdirSync(path13.dirname(resolved.path), { recursive: true });
13166
- fs14.writeFileSync(resolved.path, configContent);
13723
+ fs15.mkdirSync(path15.dirname(resolved.path), { recursive: true });
13724
+ fs15.writeFileSync(resolved.path, configContent);
13167
13725
  let fallbackLabel;
13168
13726
  if (next.scope === "project") {
13169
- fallbackLabel = `parent project config (${path13.basename(next.projectRoot)})`;
13727
+ fallbackLabel = `parent project config (${path15.basename(next.projectRoot)})`;
13170
13728
  } else if (next.scope === "global") {
13171
13729
  fallbackLabel = "global config";
13172
13730
  } else {
@@ -13175,7 +13733,7 @@ config.command("reset").description("Remove the project config for the current d
13175
13733
  if (!isAgentMode()) {
13176
13734
  const p9 = await import("@clack/prompts");
13177
13735
  const confirmed = await p9.confirm({
13178
- message: `Delete project config for ${path13.basename(resolved.projectRoot)}? Will fall back to ${fallbackLabel}.`,
13736
+ message: `Delete project config for ${path15.basename(resolved.projectRoot)}? Will fall back to ${fallbackLabel}.`,
13179
13737
  initialValue: false
13180
13738
  });
13181
13739
  if (p9.isCancel(confirmed) || !confirmed) {
@@ -13183,9 +13741,9 @@ config.command("reset").description("Remove the project config for the current d
13183
13741
  return;
13184
13742
  }
13185
13743
  }
13186
- fs14.unlinkSync(resolved.path);
13744
+ fs15.unlinkSync(resolved.path);
13187
13745
  try {
13188
- fs14.rmdirSync(path13.dirname(resolved.path));
13746
+ fs15.rmdirSync(path15.dirname(resolved.path));
13189
13747
  } catch {
13190
13748
  }
13191
13749
  if (isAgentMode()) {
@@ -13348,22 +13906,22 @@ program.command("whoami").description("Show the user, organization, and project
13348
13906
  });
13349
13907
  return;
13350
13908
  }
13351
- const pc16 = (await import("picocolors")).default;
13909
+ const pc17 = (await import("picocolors")).default;
13352
13910
  const contextParts = [];
13353
13911
  if (whoami.organization) contextParts.push(whoami.organization.name);
13354
13912
  if (whoami.project) contextParts.push(whoami.project.name);
13355
13913
  const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
13356
- const envLabel = env === "test" ? pc16.yellow("test") : pc16.green("live");
13357
- const configLabel = configScope === "project" ? pc16.cyan("project config") : pc16.magenta("global config");
13914
+ const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
13915
+ const configLabel = configScope === "project" ? pc17.cyan("project config") : pc17.magenta("global config");
13358
13916
  console.log();
13359
- console.log(` ${pc16.bold(scopeDisplay)} ${pc16.dim("\xB7")} ${envLabel}`);
13360
- console.log(` ${whoami.user.name} ${pc16.dim(`(${whoami.user.email})`)}`);
13361
- if (keyName) console.log(` ${pc16.dim("Key:")} ${keyName}`);
13362
- if (whoami.organization) console.log(` ${pc16.dim("Org:")} ${whoami.organization.name} ${pc16.dim(`(${whoami.organization.id})`)}`);
13363
- if (whoami.project) console.log(` ${pc16.dim("Project:")} ${whoami.project.name} ${pc16.dim(`(${whoami.project.id})`)}`);
13917
+ console.log(` ${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
13918
+ console.log(` ${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
13919
+ if (keyName) console.log(` ${pc17.dim("Key:")} ${keyName}`);
13920
+ if (whoami.organization) console.log(` ${pc17.dim("Org:")} ${whoami.organization.name} ${pc17.dim(`(${whoami.organization.id})`)}`);
13921
+ if (whoami.project) console.log(` ${pc17.dim("Project:")} ${whoami.project.name} ${pc17.dim(`(${whoami.project.id})`)}`);
13364
13922
  console.log();
13365
- console.log(` ${pc16.dim("Using")} ${configLabel}`);
13366
- console.log(` ${pc16.dim("API:")} ${apiBase}`);
13923
+ console.log(` ${pc17.dim("Using")} ${configLabel}`);
13924
+ console.log(` ${pc17.dim("API:")} ${apiBase}`);
13367
13925
  console.log();
13368
13926
  });
13369
13927
  program.command("add [platform]").description("Shortcut for: connection add").option("--tag <name>", "Tag the new connection (disambiguates multiple connections per platform in sync/flow profiles)").action(async (platform, options) => {