@withone/cli 1.27.1 → 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 +35 -4
- package/dist/index.js +227 -41
- 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
|
|
|
@@ -391,6 +398,30 @@ Settings propagate automatically to all installed agent configs.
|
|
|
391
398
|
|
|
392
399
|
Auto-sync refuses to resurrect skills if you opted out of skill installation during `one init` — the canonical dir has to already exist.
|
|
393
400
|
|
|
401
|
+
### Project config (`.onerc`)
|
|
402
|
+
|
|
403
|
+
Drop a `.onerc` file in your project root to override global settings per-project. Simple `KEY=VALUE` format; `#` for comments. Read from the current working directory (no parent lookup).
|
|
404
|
+
|
|
405
|
+
| Key | Purpose |
|
|
406
|
+
|-----|---------|
|
|
407
|
+
| `ONE_SECRET` | API key (also honored as env var) |
|
|
408
|
+
| `ONE_API_BASE` | API base URL (also honored as env var) |
|
|
409
|
+
| `ONE_PERMISSIONS` | `admin` / `write` / `read` |
|
|
410
|
+
| `ONE_CONNECTION_KEYS` | Comma-separated connection-key allowlist |
|
|
411
|
+
| `ONE_ACTION_IDS` | Comma-separated action-ID allowlist |
|
|
412
|
+
| `ONE_KNOWLEDGE_AGENT` | `true` / `false` — knowledge-only mode |
|
|
413
|
+
|
|
414
|
+
Precedence: env var > `.onerc` > `~/.one/config.json`.
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# .onerc
|
|
418
|
+
ONE_SECRET=sk_live_xxx
|
|
419
|
+
ONE_API_BASE=https://development-api.withone.ai
|
|
420
|
+
ONE_PERMISSIONS=read
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
> ⚠️ **Add `.onerc` to your `.gitignore`.** If you put `ONE_SECRET` in it, committing the file will leak your API key. Treat `.onerc` like `.env` — never check it in.
|
|
424
|
+
|
|
394
425
|
## The workflow
|
|
395
426
|
|
|
396
427
|
The power of One is in the workflow. Every interaction follows the same pattern:
|
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
|
}
|
|
@@ -879,24 +943,98 @@ async function initCommand(options) {
|
|
|
879
943
|
if (isAgentMode()) {
|
|
880
944
|
error("This command requires interactive input. Run without --agent.");
|
|
881
945
|
}
|
|
882
|
-
const existingConfig = readConfig();
|
|
883
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();
|
|
884
953
|
if (existingConfig) {
|
|
885
|
-
await handleExistingConfig(existingConfig.apiKey, options);
|
|
954
|
+
await handleExistingConfig(existingConfig.apiKey, scope, options);
|
|
886
955
|
return;
|
|
887
956
|
}
|
|
888
|
-
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]");
|
|
889
1023
|
}
|
|
890
|
-
|
|
1024
|
+
function scopedMessage(scope, message) {
|
|
1025
|
+
return `${scopeLabel(scope)} ${message}`;
|
|
1026
|
+
}
|
|
1027
|
+
async function handleExistingConfig(apiKey, scope, options) {
|
|
891
1028
|
const statuses = getAgentStatuses();
|
|
892
1029
|
const masked = maskApiKey(apiKey);
|
|
893
1030
|
const skillInstalled = isSkillInstalled2();
|
|
1031
|
+
const activeConfigPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
|
|
894
1032
|
console.log();
|
|
895
|
-
console.log(` ${pc2.bold("Current Setup")}`);
|
|
1033
|
+
console.log(` ${pc2.bold("Current Setup")} ${scopeLabel(scope)}`);
|
|
896
1034
|
console.log(` ${pc2.dim("\u2500".repeat(42))}`);
|
|
897
1035
|
console.log(` ${pc2.dim("API Key:")} ${masked}`);
|
|
898
1036
|
console.log(` ${pc2.dim("Skill:")} ${skillInstalled ? pc2.green("installed") : pc2.yellow("not installed")}`);
|
|
899
|
-
console.log(` ${pc2.dim("Config:")} ${
|
|
1037
|
+
console.log(` ${pc2.dim("Config:")} ${tildify(activeConfigPath)}`);
|
|
900
1038
|
const ac = getAccessControl();
|
|
901
1039
|
if (Object.keys(ac).length > 0) {
|
|
902
1040
|
console.log();
|
|
@@ -938,7 +1076,7 @@ async function handleExistingConfig(apiKey, options) {
|
|
|
938
1076
|
label: "Start fresh (reconfigure everything)"
|
|
939
1077
|
});
|
|
940
1078
|
const action = await p3.select({
|
|
941
|
-
message: "What would you like to do?",
|
|
1079
|
+
message: scopedMessage(scope, "What would you like to do?"),
|
|
942
1080
|
options: actionOptions
|
|
943
1081
|
});
|
|
944
1082
|
if (p3.isCancel(action)) {
|
|
@@ -965,21 +1103,21 @@ async function handleExistingConfig(apiKey, options) {
|
|
|
965
1103
|
p3.outro("Done.");
|
|
966
1104
|
break;
|
|
967
1105
|
case "update-key":
|
|
968
|
-
await handleUpdateKey(statuses);
|
|
1106
|
+
await handleUpdateKey(statuses, scope);
|
|
969
1107
|
break;
|
|
970
1108
|
case "access-control":
|
|
971
1109
|
await configCommand();
|
|
972
1110
|
break;
|
|
973
1111
|
case "start-fresh":
|
|
974
|
-
await freshSetup({ yes: true });
|
|
1112
|
+
await freshSetup(scope, { yes: true });
|
|
975
1113
|
break;
|
|
976
1114
|
}
|
|
977
1115
|
}
|
|
978
|
-
async function handleUpdateKey(statuses) {
|
|
1116
|
+
async function handleUpdateKey(statuses, scope) {
|
|
979
1117
|
p3.note(`Get your API key at:
|
|
980
|
-
${pc2.cyan(getApiKeyUrl())}`,
|
|
1118
|
+
${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
|
|
981
1119
|
const openBrowser = await p3.confirm({
|
|
982
|
-
message: "Open browser to get API key?",
|
|
1120
|
+
message: scopedMessage(scope, "Open browser to get API key?"),
|
|
983
1121
|
initialValue: true
|
|
984
1122
|
});
|
|
985
1123
|
if (p3.isCancel(openBrowser)) {
|
|
@@ -990,7 +1128,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
990
1128
|
await openApiKeyPage();
|
|
991
1129
|
}
|
|
992
1130
|
const newKey = await p3.text({
|
|
993
|
-
message: "Enter your new One API key:",
|
|
1131
|
+
message: scopedMessage(scope, "Enter your new One API key:"),
|
|
994
1132
|
placeholder: "sk_live_...",
|
|
995
1133
|
validate: (value) => {
|
|
996
1134
|
if (!value) return "API key is required";
|
|
@@ -1026,13 +1164,18 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1026
1164
|
reinstalled.push(`${s.agent.name} (project)`);
|
|
1027
1165
|
}
|
|
1028
1166
|
}
|
|
1029
|
-
const
|
|
1030
|
-
writeConfig(
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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
|
+
);
|
|
1036
1179
|
if (reinstalled.length > 0) {
|
|
1037
1180
|
p3.log.success(`Updated MCP configs: ${reinstalled.join(", ")}`);
|
|
1038
1181
|
}
|
|
@@ -1195,11 +1338,11 @@ function printOnboardingPrompt() {
|
|
|
1195
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"));
|
|
1196
1339
|
console.log();
|
|
1197
1340
|
}
|
|
1198
|
-
async function freshSetup(options) {
|
|
1341
|
+
async function freshSetup(scope, options) {
|
|
1199
1342
|
p3.note(`Get your API key at:
|
|
1200
|
-
${pc2.cyan(getApiKeyUrl())}`,
|
|
1343
|
+
${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
|
|
1201
1344
|
const openBrowser = await p3.confirm({
|
|
1202
|
-
message: "Open browser to get API key?",
|
|
1345
|
+
message: scopedMessage(scope, "Open browser to get API key?"),
|
|
1203
1346
|
initialValue: true
|
|
1204
1347
|
});
|
|
1205
1348
|
if (p3.isCancel(openBrowser)) {
|
|
@@ -1210,7 +1353,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1210
1353
|
await openApiKeyPage();
|
|
1211
1354
|
}
|
|
1212
1355
|
const apiKey = await p3.text({
|
|
1213
|
-
message: "Enter your One API key:",
|
|
1356
|
+
message: scopedMessage(scope, "Enter your One API key:"),
|
|
1214
1357
|
placeholder: "sk_live_...",
|
|
1215
1358
|
validate: (value) => {
|
|
1216
1359
|
if (!value) return "API key is required";
|
|
@@ -1234,15 +1377,24 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
1234
1377
|
process.exit(1);
|
|
1235
1378
|
}
|
|
1236
1379
|
spinner5.stop("API key validated");
|
|
1237
|
-
writeConfig(
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1380
|
+
writeConfig(
|
|
1381
|
+
{
|
|
1382
|
+
apiKey,
|
|
1383
|
+
installedAgents: [],
|
|
1384
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1385
|
+
},
|
|
1386
|
+
scope
|
|
1387
|
+
);
|
|
1242
1388
|
await promptSkillInstall();
|
|
1243
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.`;
|
|
1244
1393
|
p3.note(
|
|
1245
|
-
|
|
1394
|
+
`${scopeLabel(scope)} Config saved to:
|
|
1395
|
+
${pc2.dim(tildify(savedPath))}
|
|
1396
|
+
|
|
1397
|
+
${resolutionHint}`,
|
|
1246
1398
|
"Setup Complete"
|
|
1247
1399
|
);
|
|
1248
1400
|
printOnboardingPrompt();
|
|
@@ -3870,7 +4022,7 @@ var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
|
|
|
3870
4022
|
|
|
3871
4023
|
## Setup
|
|
3872
4024
|
|
|
3873
|
-
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)
|
|
3874
4026
|
2. Run \`one add <platform>\` to connect platforms via OAuth
|
|
3875
4027
|
3. Run \`one --agent connection list\` to verify connections
|
|
3876
4028
|
|
|
@@ -4650,12 +4802,46 @@ program.hook("postAction", async () => {
|
|
|
4650
4802
|
if (!isNewerVersion(info.version, current)) return;
|
|
4651
4803
|
autoUpdate(info.version, info.publishedAt);
|
|
4652
4804
|
});
|
|
4653
|
-
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) => {
|
|
4654
4806
|
await initCommand(options);
|
|
4655
4807
|
});
|
|
4656
4808
|
var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
|
|
4657
4809
|
await configCommand();
|
|
4658
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
|
+
});
|
|
4659
4845
|
var configSkills = config.command("skills").description("Manage locally-installed skill files");
|
|
4660
4846
|
configSkills.command("sync").description("Re-copy packaged skill files over the local install (runs automatically after CLI upgrades)").action(async () => {
|
|
4661
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.
|