@withone/cli 1.28.0 → 1.29.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/README.md +11 -4
- package/dist/index.js +227 -45
- package/package.json +1 -1
- package/skills/one/SKILL.md +2 -0
- package/skills/one/references/scoping.md +35 -0
package/README.md
CHANGED
|
@@ -124,15 +124,22 @@ Set up your API key and install the MCP server into your AI agents.
|
|
|
124
124
|
one init
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
Supports Claude Code, Claude Desktop, Cursor, Windsurf, Codex, and Kiro.
|
|
127
|
+
Supports Claude Code, Claude Desktop, Cursor, Windsurf, Codex, and Kiro.
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
**Global vs. project scope.** `one init` is interactive and asks where the setup should live:
|
|
130
|
+
|
|
131
|
+
- **Global** (`~/.one/config.json`) — applies to every folder. Best when you only need one workspace / API key.
|
|
132
|
+
- **Project** (`~/.one/projects/<slug>/config.json`) — scoped to the current project, stored under your home directory so secrets never land in git. Use this when different projects need different API keys, connections, or access control.
|
|
133
|
+
|
|
134
|
+
When you run `one` in a project, it uses the project config if one exists and falls back to the global config otherwise. Use `one config path` to see which config is active and the full resolution order.
|
|
135
|
+
|
|
136
|
+
If you've already set up, `one init` shows your current status for the active scope and lets you update your key, install to more agents, or reconfigure.
|
|
130
137
|
|
|
131
138
|
| Flag | What it does |
|
|
132
139
|
|------|-------------|
|
|
133
140
|
| `-y` | Skip confirmations |
|
|
134
|
-
| `-g` |
|
|
135
|
-
| `-p` |
|
|
141
|
+
| `-g` | Non-interactive: write the One config globally (`~/.one/config.json`) |
|
|
142
|
+
| `-p` | Non-interactive: write the One config for this project (`~/.one/projects/<slug>/config.json`) |
|
|
136
143
|
|
|
137
144
|
### `one add <platform>`
|
|
138
145
|
|
package/dist/index.js
CHANGED
|
@@ -36,24 +36,88 @@ import path from "path";
|
|
|
36
36
|
import os from "os";
|
|
37
37
|
var CONFIG_DIR = path.join(os.homedir(), ".one");
|
|
38
38
|
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
var PROJECTS_DIR = path.join(CONFIG_DIR, "projects");
|
|
40
|
+
function getProjectRoot(cwd = process.cwd()) {
|
|
41
|
+
let dir = path.resolve(cwd);
|
|
42
|
+
const root = path.parse(dir).root;
|
|
43
|
+
while (dir !== root) {
|
|
44
|
+
if (fs.existsSync(path.join(dir, ".git")) || fs.existsSync(path.join(dir, "package.json"))) {
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
dir = path.dirname(dir);
|
|
48
|
+
}
|
|
49
|
+
return path.resolve(cwd);
|
|
41
50
|
}
|
|
42
|
-
function
|
|
43
|
-
return
|
|
51
|
+
function getProjectSlug(projectRoot = getProjectRoot()) {
|
|
52
|
+
return projectRoot.replace(/[\\/]/g, "-");
|
|
44
53
|
}
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
function getProjectConfigDir(projectRoot = getProjectRoot()) {
|
|
55
|
+
return path.join(PROJECTS_DIR, getProjectSlug(projectRoot));
|
|
56
|
+
}
|
|
57
|
+
function getProjectConfigPath(projectRoot = getProjectRoot()) {
|
|
58
|
+
return path.join(getProjectConfigDir(projectRoot), "config.json");
|
|
59
|
+
}
|
|
60
|
+
function getGlobalConfigPath() {
|
|
61
|
+
return CONFIG_FILE;
|
|
62
|
+
}
|
|
63
|
+
function resolveConfig() {
|
|
64
|
+
const projectRoot = getProjectRoot();
|
|
65
|
+
const projectSlug = getProjectSlug(projectRoot);
|
|
66
|
+
const projectPath = getProjectConfigPath(projectRoot);
|
|
67
|
+
if (fs.existsSync(projectPath)) {
|
|
68
|
+
const config2 = readConfigFile(projectPath);
|
|
69
|
+
if (config2) {
|
|
70
|
+
return { config: config2, scope: "project", path: projectPath, projectRoot, projectSlug };
|
|
71
|
+
}
|
|
48
72
|
}
|
|
73
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
74
|
+
const config2 = readConfigFile(CONFIG_FILE);
|
|
75
|
+
if (config2) {
|
|
76
|
+
return { config: config2, scope: "global", path: CONFIG_FILE, projectRoot, projectSlug };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { config: null, scope: null, path: CONFIG_FILE, projectRoot, projectSlug };
|
|
80
|
+
}
|
|
81
|
+
function readConfigFile(filePath) {
|
|
49
82
|
try {
|
|
50
|
-
const content = fs.readFileSync(
|
|
83
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
51
84
|
return JSON.parse(content);
|
|
52
85
|
} catch {
|
|
53
86
|
return null;
|
|
54
87
|
}
|
|
55
88
|
}
|
|
56
|
-
function
|
|
89
|
+
function configExists() {
|
|
90
|
+
return resolveConfig().config !== null;
|
|
91
|
+
}
|
|
92
|
+
function globalConfigExists() {
|
|
93
|
+
return fs.existsSync(CONFIG_FILE);
|
|
94
|
+
}
|
|
95
|
+
function projectConfigExists(projectRoot = getProjectRoot()) {
|
|
96
|
+
return fs.existsSync(getProjectConfigPath(projectRoot));
|
|
97
|
+
}
|
|
98
|
+
function readConfig() {
|
|
99
|
+
return resolveConfig().config;
|
|
100
|
+
}
|
|
101
|
+
function readGlobalConfig() {
|
|
102
|
+
if (!fs.existsSync(CONFIG_FILE)) return null;
|
|
103
|
+
return readConfigFile(CONFIG_FILE);
|
|
104
|
+
}
|
|
105
|
+
function readProjectConfig() {
|
|
106
|
+
const projectPath = getProjectConfigPath();
|
|
107
|
+
if (!fs.existsSync(projectPath)) return null;
|
|
108
|
+
return readConfigFile(projectPath);
|
|
109
|
+
}
|
|
110
|
+
function writeConfig(config2, scope) {
|
|
111
|
+
const targetScope = scope ?? resolveConfig().scope ?? "global";
|
|
112
|
+
if (targetScope === "project") {
|
|
113
|
+
const dir = getProjectConfigDir();
|
|
114
|
+
if (!fs.existsSync(dir)) {
|
|
115
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
116
|
+
}
|
|
117
|
+
const filePath = getProjectConfigPath();
|
|
118
|
+
fs.writeFileSync(filePath, JSON.stringify(config2, null, 2), { mode: 384 });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
57
121
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
58
122
|
fs.mkdirSync(CONFIG_DIR, { mode: 448 });
|
|
59
123
|
}
|
|
@@ -108,10 +172,6 @@ function getAccessControl() {
|
|
|
108
172
|
}
|
|
109
173
|
var DEFAULT_API_BASE = "https://api.withone.ai/v1";
|
|
110
174
|
function getApiBase() {
|
|
111
|
-
const envBase = process.env.ONE_API_BASE;
|
|
112
|
-
if (envBase) return `${envBase.replace(/\/+$/, "").replace(/\/v1$/, "")}/v1`;
|
|
113
|
-
const rc = readOneRc();
|
|
114
|
-
if (rc.ONE_API_BASE) return `${rc.ONE_API_BASE.replace(/\/+$/, "").replace(/\/v1$/, "")}/v1`;
|
|
115
175
|
const config2 = readConfig();
|
|
116
176
|
if (config2?.apiBase) return `${config2.apiBase}/v1`;
|
|
117
177
|
return DEFAULT_API_BASE;
|
|
@@ -883,24 +943,98 @@ async function initCommand(options) {
|
|
|
883
943
|
if (isAgentMode()) {
|
|
884
944
|
error("This command requires interactive input. Run without --agent.");
|
|
885
945
|
}
|
|
886
|
-
const existingConfig = readConfig();
|
|
887
946
|
printBanner();
|
|
947
|
+
const scope = await chooseConfigScope(options);
|
|
948
|
+
if (scope === null) {
|
|
949
|
+
p3.cancel("Setup cancelled.");
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const existingConfig = scope === "project" ? readProjectConfig() : readGlobalConfig();
|
|
888
953
|
if (existingConfig) {
|
|
889
|
-
await handleExistingConfig(existingConfig.apiKey, options);
|
|
954
|
+
await handleExistingConfig(existingConfig.apiKey, scope, options);
|
|
890
955
|
return;
|
|
891
956
|
}
|
|
892
|
-
await freshSetup(options);
|
|
957
|
+
await freshSetup(scope, options);
|
|
958
|
+
}
|
|
959
|
+
async function chooseConfigScope(options) {
|
|
960
|
+
if (options.global) return "global";
|
|
961
|
+
if (options.project) return "project";
|
|
962
|
+
const resolved = resolveConfig();
|
|
963
|
+
const hasGlobal = globalConfigExists();
|
|
964
|
+
const hasProject = projectConfigExists();
|
|
965
|
+
const projectRoot = resolved.projectRoot;
|
|
966
|
+
const projectName = path4.basename(projectRoot);
|
|
967
|
+
const homeGlobal = tildify(getGlobalConfigPath());
|
|
968
|
+
const homeProject = tildify(getProjectConfigPath(projectRoot));
|
|
969
|
+
if (hasProject) {
|
|
970
|
+
console.log();
|
|
971
|
+
console.log(` ${pc2.dim("Project:")} ${projectName} ${pc2.dim(projectRoot)}`);
|
|
972
|
+
console.log(` ${pc2.bold("Active config:")} ${pc2.cyan("project")} ${pc2.dim("\xB7 " + homeProject)}`);
|
|
973
|
+
console.log();
|
|
974
|
+
if (hasGlobal) {
|
|
975
|
+
const which2 = await p3.select({
|
|
976
|
+
message: "Which config do you want to edit?",
|
|
977
|
+
options: [
|
|
978
|
+
{ value: "project", label: `This project (${projectName})`, hint: homeProject },
|
|
979
|
+
{ value: "global", label: "Global (all folders)", hint: homeGlobal }
|
|
980
|
+
],
|
|
981
|
+
initialValue: "project"
|
|
982
|
+
});
|
|
983
|
+
if (p3.isCancel(which2)) return null;
|
|
984
|
+
return which2;
|
|
985
|
+
}
|
|
986
|
+
return "project";
|
|
987
|
+
}
|
|
988
|
+
console.log();
|
|
989
|
+
console.log(` ${pc2.bold("Initializing One")}`);
|
|
990
|
+
console.log(` ${pc2.dim("\u2500".repeat(42))}`);
|
|
991
|
+
console.log(` ${pc2.dim("Project:")} ${projectName} ${pc2.dim(projectRoot)}`);
|
|
992
|
+
console.log(` ${pc2.dim("Global:")} ${hasGlobal ? pc2.green("\u2713 configured") : pc2.yellow("\u2014 not set up")} ${pc2.dim(homeGlobal)}`);
|
|
993
|
+
console.log(` ${pc2.dim("Project:")} ${pc2.yellow("\u2014 not set up")} ${pc2.dim(homeProject)}`);
|
|
994
|
+
console.log();
|
|
995
|
+
const defaultScope = hasGlobal ? "project" : "global";
|
|
996
|
+
const hint = hasGlobal ? "Your global config stays as-is. This folder gets its own setup." : "No global config yet \u2014 this becomes your default for every folder.";
|
|
997
|
+
p3.note(hint, defaultScope === "project" ? "Recommended: project" : "Recommended: global");
|
|
998
|
+
const which = await p3.select({
|
|
999
|
+
message: "Where should this setup live?",
|
|
1000
|
+
options: [
|
|
1001
|
+
{
|
|
1002
|
+
value: "project",
|
|
1003
|
+
label: `This project only (${projectName})`,
|
|
1004
|
+
hint: "different API key / connections just for this folder"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
value: "global",
|
|
1008
|
+
label: "Globally (all folders)",
|
|
1009
|
+
hint: "applies everywhere you run `one`"
|
|
1010
|
+
}
|
|
1011
|
+
],
|
|
1012
|
+
initialValue: defaultScope
|
|
1013
|
+
});
|
|
1014
|
+
if (p3.isCancel(which)) return null;
|
|
1015
|
+
return which;
|
|
1016
|
+
}
|
|
1017
|
+
function tildify(filePath) {
|
|
1018
|
+
const home = os4.homedir();
|
|
1019
|
+
return filePath.startsWith(home) ? "~" + filePath.slice(home.length) : filePath;
|
|
1020
|
+
}
|
|
1021
|
+
function scopeLabel(scope) {
|
|
1022
|
+
return scope === "project" ? pc2.cyan("[project]") : pc2.magenta("[global]");
|
|
893
1023
|
}
|
|
894
|
-
|
|
1024
|
+
function scopedMessage(scope, message) {
|
|
1025
|
+
return `${scopeLabel(scope)} ${message}`;
|
|
1026
|
+
}
|
|
1027
|
+
async function handleExistingConfig(apiKey, scope, options) {
|
|
895
1028
|
const statuses = getAgentStatuses();
|
|
896
1029
|
const masked = maskApiKey(apiKey);
|
|
897
1030
|
const skillInstalled = isSkillInstalled2();
|
|
1031
|
+
const activeConfigPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
|
|
898
1032
|
console.log();
|
|
899
|
-
console.log(` ${pc2.bold("Current Setup")}`);
|
|
1033
|
+
console.log(` ${pc2.bold("Current Setup")} ${scopeLabel(scope)}`);
|
|
900
1034
|
console.log(` ${pc2.dim("\u2500".repeat(42))}`);
|
|
901
1035
|
console.log(` ${pc2.dim("API Key:")} ${masked}`);
|
|
902
1036
|
console.log(` ${pc2.dim("Skill:")} ${skillInstalled ? pc2.green("installed") : pc2.yellow("not installed")}`);
|
|
903
|
-
console.log(` ${pc2.dim("Config:")} ${
|
|
1037
|
+
console.log(` ${pc2.dim("Config:")} ${tildify(activeConfigPath)}`);
|
|
904
1038
|
const ac = getAccessControl();
|
|
905
1039
|
if (Object.keys(ac).length > 0) {
|
|
906
1040
|
console.log();
|
|
@@ -942,7 +1076,7 @@ async function handleExistingConfig(apiKey, options) {
|
|
|
942
1076
|
label: "Start fresh (reconfigure everything)"
|
|
943
1077
|
});
|
|
944
1078
|
const action = await p3.select({
|
|
945
|
-
message: "What would you like to do?",
|
|
1079
|
+
message: scopedMessage(scope, "What would you like to do?"),
|
|
946
1080
|
options: actionOptions
|
|
947
1081
|
});
|
|
948
1082
|
if (p3.isCancel(action)) {
|
|
@@ -969,21 +1103,21 @@ async function handleExistingConfig(apiKey, options) {
|
|
|
969
1103
|
p3.outro("Done.");
|
|
970
1104
|
break;
|
|
971
1105
|
case "update-key":
|
|
972
|
-
await handleUpdateKey(statuses);
|
|
1106
|
+
await handleUpdateKey(statuses, scope);
|
|
973
1107
|
break;
|
|
974
1108
|
case "access-control":
|
|
975
1109
|
await configCommand();
|
|
976
1110
|
break;
|
|
977
1111
|
case "start-fresh":
|
|
978
|
-
await freshSetup({ yes: true });
|
|
1112
|
+
await freshSetup(scope, { yes: true });
|
|
979
1113
|
break;
|
|
980
1114
|
}
|
|
981
1115
|
}
|
|
982
|
-
async function handleUpdateKey(statuses) {
|
|
1116
|
+
async function handleUpdateKey(statuses, scope) {
|
|
983
1117
|
p3.note(`Get your API key at:
|
|
984
|
-
${pc2.cyan(getApiKeyUrl())}`,
|
|
1118
|
+
${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
|
|
985
1119
|
const openBrowser = await p3.confirm({
|
|
986
|
-
message: "Open browser to get API key?",
|
|
1120
|
+
message: scopedMessage(scope, "Open browser to get API key?"),
|
|
987
1121
|
initialValue: true
|
|
988
1122
|
});
|
|
989
1123
|
if (p3.isCancel(openBrowser)) {
|
|
@@ -994,7 +1128,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
994
1128
|
await openApiKeyPage();
|
|
995
1129
|
}
|
|
996
1130
|
const newKey = await p3.text({
|
|
997
|
-
message: "Enter your new One API key:",
|
|
1131
|
+
message: scopedMessage(scope, "Enter your new One API key:"),
|
|
998
1132
|
placeholder: "sk_live_...",
|
|
999
1133
|
validate: (value) => {
|
|
1000
1134
|
if (!value) return "API key is required";
|
|
@@ -1030,13 +1164,18 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1030
1164
|
reinstalled.push(`${s.agent.name} (project)`);
|
|
1031
1165
|
}
|
|
1032
1166
|
}
|
|
1033
|
-
const
|
|
1034
|
-
writeConfig(
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1167
|
+
const current = scope === "project" ? readProjectConfig() : readGlobalConfig();
|
|
1168
|
+
writeConfig(
|
|
1169
|
+
{
|
|
1170
|
+
apiKey: newKey,
|
|
1171
|
+
installedAgents: current?.installedAgents ?? [],
|
|
1172
|
+
createdAt: current?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1173
|
+
accessControl: current?.accessControl,
|
|
1174
|
+
apiBase: current?.apiBase,
|
|
1175
|
+
cacheTtl: current?.cacheTtl
|
|
1176
|
+
},
|
|
1177
|
+
scope
|
|
1178
|
+
);
|
|
1040
1179
|
if (reinstalled.length > 0) {
|
|
1041
1180
|
p3.log.success(`Updated MCP configs: ${reinstalled.join(", ")}`);
|
|
1042
1181
|
}
|
|
@@ -1199,11 +1338,11 @@ function printOnboardingPrompt() {
|
|
|
1199
1338
|
console.log(pc2.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"));
|
|
1200
1339
|
console.log();
|
|
1201
1340
|
}
|
|
1202
|
-
async function freshSetup(options) {
|
|
1341
|
+
async function freshSetup(scope, options) {
|
|
1203
1342
|
p3.note(`Get your API key at:
|
|
1204
|
-
${pc2.cyan(getApiKeyUrl())}`,
|
|
1343
|
+
${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
|
|
1205
1344
|
const openBrowser = await p3.confirm({
|
|
1206
|
-
message: "Open browser to get API key?",
|
|
1345
|
+
message: scopedMessage(scope, "Open browser to get API key?"),
|
|
1207
1346
|
initialValue: true
|
|
1208
1347
|
});
|
|
1209
1348
|
if (p3.isCancel(openBrowser)) {
|
|
@@ -1214,7 +1353,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1214
1353
|
await openApiKeyPage();
|
|
1215
1354
|
}
|
|
1216
1355
|
const apiKey = await p3.text({
|
|
1217
|
-
message: "Enter your One API key:",
|
|
1356
|
+
message: scopedMessage(scope, "Enter your One API key:"),
|
|
1218
1357
|
placeholder: "sk_live_...",
|
|
1219
1358
|
validate: (value) => {
|
|
1220
1359
|
if (!value) return "API key is required";
|
|
@@ -1238,15 +1377,24 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1238
1377
|
process.exit(1);
|
|
1239
1378
|
}
|
|
1240
1379
|
spinner5.stop("API key validated");
|
|
1241
|
-
writeConfig(
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1380
|
+
writeConfig(
|
|
1381
|
+
{
|
|
1382
|
+
apiKey,
|
|
1383
|
+
installedAgents: [],
|
|
1384
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1385
|
+
},
|
|
1386
|
+
scope
|
|
1387
|
+
);
|
|
1246
1388
|
await promptSkillInstall();
|
|
1247
1389
|
await promptConnectIntegrations(apiKey);
|
|
1390
|
+
const savedPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
|
|
1391
|
+
const resolutionHint = scope === "project" ? `When you run ${pc2.cyan("one")} from ${pc2.bold(path4.basename(getProjectRoot()))}, it uses this project config.
|
|
1392
|
+
From anywhere else, it falls back to your global config.` : `This config applies to every folder unless a project config is set.`;
|
|
1248
1393
|
p3.note(
|
|
1249
|
-
|
|
1394
|
+
`${scopeLabel(scope)} Config saved to:
|
|
1395
|
+
${pc2.dim(tildify(savedPath))}
|
|
1396
|
+
|
|
1397
|
+
${resolutionHint}`,
|
|
1250
1398
|
"Setup Complete"
|
|
1251
1399
|
);
|
|
1252
1400
|
printOnboardingPrompt();
|
|
@@ -3874,7 +4022,7 @@ var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
|
|
|
3874
4022
|
|
|
3875
4023
|
## Setup
|
|
3876
4024
|
|
|
3877
|
-
1. Run \`one init\` to configure your API key
|
|
4025
|
+
1. Run \`one init\` to configure your API key (interactive \u2014 can be global or per-project)
|
|
3878
4026
|
2. Run \`one add <platform>\` to connect platforms via OAuth
|
|
3879
4027
|
3. Run \`one --agent connection list\` to verify connections
|
|
3880
4028
|
|
|
@@ -4654,12 +4802,46 @@ program.hook("postAction", async () => {
|
|
|
4654
4802
|
if (!isNewerVersion(info.version, current)) return;
|
|
4655
4803
|
autoUpdate(info.version, info.publishedAt);
|
|
4656
4804
|
});
|
|
4657
|
-
program.command("init").description("Set up One and install MCP to your AI agents").option("-y, --yes", "Skip confirmations").option("-g, --global", "
|
|
4805
|
+
program.command("init").description("Set up One and install MCP to your AI agents (interactive: picks global or project scope)").option("-y, --yes", "Skip confirmations").option("-g, --global", "Write the One config globally (~/.one/config.json) \u2014 skips the scope picker").option("-p, --project", "Write the One config for this project only (~/.one/projects/<slug>/) \u2014 skips the scope picker").action(async (options) => {
|
|
4658
4806
|
await initCommand(options);
|
|
4659
4807
|
});
|
|
4660
4808
|
var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
|
|
4661
4809
|
await configCommand();
|
|
4662
4810
|
});
|
|
4811
|
+
config.command("path").description("Show the active config path, scope, and the fallback chain (project \u2192 global)").action(() => {
|
|
4812
|
+
const resolved = resolveConfig();
|
|
4813
|
+
const globalPath = getGlobalConfigPath();
|
|
4814
|
+
const projectPath = getProjectConfigPath(resolved.projectRoot);
|
|
4815
|
+
const hasGlobal = globalConfigExists();
|
|
4816
|
+
const hasProject = projectConfigExists(resolved.projectRoot);
|
|
4817
|
+
if (isAgentMode()) {
|
|
4818
|
+
json({
|
|
4819
|
+
command: "config path",
|
|
4820
|
+
scope: resolved.scope,
|
|
4821
|
+
path: resolved.path,
|
|
4822
|
+
projectRoot: resolved.projectRoot,
|
|
4823
|
+
projectSlug: resolved.projectSlug,
|
|
4824
|
+
fallback: {
|
|
4825
|
+
project: { path: projectPath, exists: hasProject },
|
|
4826
|
+
global: { path: globalPath, exists: hasGlobal }
|
|
4827
|
+
}
|
|
4828
|
+
});
|
|
4829
|
+
return;
|
|
4830
|
+
}
|
|
4831
|
+
if (!resolved.scope) {
|
|
4832
|
+
console.log("No One config found.");
|
|
4833
|
+
console.log(` project: ${projectPath} (not set up)`);
|
|
4834
|
+
console.log(` global: ${globalPath} (not set up)`);
|
|
4835
|
+
console.log("\nRun 'one init' to get started.");
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
console.log(`Active: ${resolved.scope}`);
|
|
4839
|
+
console.log(`Path: ${resolved.path}`);
|
|
4840
|
+
console.log(`
|
|
4841
|
+
Resolution order (first match wins):`);
|
|
4842
|
+
console.log(` 1. project ${projectPath} ${hasProject ? "\u2713" : "\u2014"}`);
|
|
4843
|
+
console.log(` 2. global ${globalPath} ${hasGlobal ? "\u2713" : "\u2014"}`);
|
|
4844
|
+
});
|
|
4663
4845
|
var configSkills = config.command("skills").description("Manage locally-installed skill files");
|
|
4664
4846
|
configSkills.command("sync").description("Re-copy packaged skill files over the local install (runs automatically after CLI upgrades)").action(async () => {
|
|
4665
4847
|
const result = forceSyncSkills();
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -22,6 +22,8 @@ description: |
|
|
|
22
22
|
|
|
23
23
|
You have access to the One CLI which lets you interact with 250+ third-party platforms through their APIs. Always include the `--agent` flag right after `one` for structured JSON output.
|
|
24
24
|
|
|
25
|
+
If the user wants a separate API key / connections for a specific project (vs. their default), walk them through running `one init` from that project folder and picking the "project" scope — see `references/scoping.md`.
|
|
26
|
+
|
|
25
27
|
## Core Workflow: search -> knowledge -> execute
|
|
26
28
|
|
|
27
29
|
Always follow this sequence when the user wants to do something on a connected platform:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Project vs. global config
|
|
2
|
+
|
|
3
|
+
The One CLI can be configured at two scopes:
|
|
4
|
+
|
|
5
|
+
- **Global** — `~/.one/config.json`. Applies everywhere the user runs `one`.
|
|
6
|
+
- **Project** — `~/.one/projects/<slug>/config.json`, where `<slug>` is the project root path with slashes replaced by dashes (e.g. `/Users/jane/acme` → `-Users-jane-acme`). Only applies when running `one` from inside that project folder.
|
|
7
|
+
|
|
8
|
+
**Resolution order:** env vars → `.onerc` in cwd → project config → global config. Project config wins when present; otherwise the CLI falls back to the global config.
|
|
9
|
+
|
|
10
|
+
## When to suggest project scope
|
|
11
|
+
|
|
12
|
+
Suggest project scope when the user wants any of the following for a specific folder only, without changing their default setup:
|
|
13
|
+
|
|
14
|
+
- A different One API key (e.g. sandbox workspace for a client project)
|
|
15
|
+
- A different set of connections / connection keys
|
|
16
|
+
- Different access control (permissions, scoped connections, knowledge-only mode)
|
|
17
|
+
|
|
18
|
+
## How to set it up
|
|
19
|
+
|
|
20
|
+
Do **not** hand-edit `.onerc` or config files. Walk the user through the interactive init:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd /path/to/the/project
|
|
24
|
+
one init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
When `init` asks "Where should this setup live?", pick **"This project only"**. Init will write the config to `~/.one/projects/<slug>/config.json` and everything else (skill install, MCP) stays untouched.
|
|
28
|
+
|
|
29
|
+
To see which config is currently active and the full fallback chain:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
one --agent config path
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To switch an existing project back to using the global config, delete its project config file — the CLI will automatically fall back to global on the next run.
|