@botdocs/cli 0.3.2 → 0.5.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 +145 -36
- package/dist/commands/backups.d.ts +4 -0
- package/dist/commands/backups.js +291 -0
- package/dist/commands/edit.js +16 -8
- package/dist/commands/ingest.d.ts +2 -0
- package/dist/commands/ingest.js +162 -28
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.js +40 -3
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.js +240 -75
- package/dist/commands/sync.d.ts +16 -0
- package/dist/commands/sync.js +337 -25
- package/dist/commands/team.d.ts +2 -0
- package/dist/commands/team.js +251 -0
- package/dist/commands/undo.d.ts +19 -0
- package/dist/commands/undo.js +88 -0
- package/dist/commands/views/conflict-prompt.d.ts +24 -0
- package/dist/commands/views/conflict-prompt.js +19 -0
- package/dist/commands/views/login-app.d.ts +30 -0
- package/dist/commands/views/login-app.js +57 -0
- package/dist/commands/views/sync-app.d.ts +27 -0
- package/dist/commands/views/sync-app.js +147 -0
- package/dist/commands/views/sync-state.d.ts +84 -0
- package/dist/commands/views/sync-state.js +93 -0
- package/dist/commands/views/theme.d.ts +16 -0
- package/dist/commands/views/theme.js +16 -0
- package/dist/commands/whoami.js +13 -13
- package/dist/index.js +46 -39
- package/dist/lib/api.d.ts +2 -3
- package/dist/lib/api.js +14 -7
- package/dist/lib/auto-detect.js +46 -0
- package/dist/lib/backup.d.ts +121 -0
- package/dist/lib/backup.js +387 -0
- package/dist/lib/canonical.d.ts +1 -1
- package/dist/lib/canonical.js +43 -1
- package/dist/lib/config.d.ts +8 -1
- package/dist/lib/config.js +18 -9
- package/dist/lib/lockfile.d.ts +9 -0
- package/dist/lib/prompts.d.ts +10 -0
- package/dist/lib/prompts.js +36 -12
- package/package.json +27 -7
- package/templates/agents.md +60 -47
- package/templates/ecosystem-prompts/compile-antigravity.md +14 -0
- package/templates/ecosystem-prompts/compile-copilot.md +14 -0
- package/templates/ecosystem-prompts/compile-gemini.md +14 -0
- package/templates/ecosystem-prompts/compile-opencode.md +13 -0
- package/templates/ecosystem-prompts/compile-windsurf.md +13 -0
- package/dist/commands/check-updates.test.d.ts +0 -1
- package/dist/commands/check-updates.test.js +0 -128
- package/dist/commands/clone.d.ts +0 -3
- package/dist/commands/clone.js +0 -70
- package/dist/commands/compile.test.d.ts +0 -1
- package/dist/commands/compile.test.js +0 -110
- package/dist/commands/diff.d.ts +0 -3
- package/dist/commands/diff.js +0 -65
- package/dist/commands/edit.test.d.ts +0 -1
- package/dist/commands/edit.test.js +0 -102
- package/dist/commands/endorse.d.ts +0 -7
- package/dist/commands/endorse.js +0 -70
- package/dist/commands/ingest.test.d.ts +0 -1
- package/dist/commands/ingest.test.js +0 -109
- package/dist/commands/install.test.d.ts +0 -1
- package/dist/commands/install.test.js +0 -253
- package/dist/commands/list.test.d.ts +0 -1
- package/dist/commands/list.test.js +0 -51
- package/dist/commands/publish.test.d.ts +0 -1
- package/dist/commands/publish.test.js +0 -138
- package/dist/commands/pull.d.ts +0 -3
- package/dist/commands/pull.js +0 -78
- package/dist/commands/sync.test.d.ts +0 -1
- package/dist/commands/sync.test.js +0 -263
- package/dist/commands/uninstall.test.d.ts +0 -1
- package/dist/commands/uninstall.test.js +0 -67
- package/dist/lib/auto-detect.test.d.ts +0 -1
- package/dist/lib/auto-detect.test.js +0 -58
- package/dist/lib/canonical.test.d.ts +0 -1
- package/dist/lib/canonical.test.js +0 -48
- package/dist/lib/diff.test.d.ts +0 -1
- package/dist/lib/diff.test.js +0 -28
- package/dist/lib/library-sync.test.d.ts +0 -1
- package/dist/lib/library-sync.test.js +0 -63
- package/dist/lib/llm.test.d.ts +0 -1
- package/dist/lib/llm.test.js +0 -72
- package/dist/lib/lockfile.test.d.ts +0 -1
- package/dist/lib/lockfile.test.js +0 -99
- package/dist/lib/manifest.test.d.ts +0 -1
- package/dist/lib/manifest.test.js +0 -72
- package/dist/lib/shell-hook.test.d.ts +0 -1
- package/dist/lib/shell-hook.test.js +0 -68
- package/dist/test-utils.d.ts +0 -43
- package/dist/test-utils.js +0 -101
package/dist/commands/ingest.js
CHANGED
|
@@ -1,6 +1,94 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { apiFetch } from '../lib/api.js';
|
|
4
|
+
/**
|
|
5
|
+
* Single source of truth for ecosystem detection. New ecosystems should be
|
|
6
|
+
* added here — both auto-detect mode and future `--from-tool` mode consult
|
|
7
|
+
* this table.
|
|
8
|
+
*/
|
|
9
|
+
const DETECTORS = {
|
|
10
|
+
claude: {
|
|
11
|
+
pathPrefix: 'claude/',
|
|
12
|
+
extensions: ['/SKILL.md'],
|
|
13
|
+
nested: true,
|
|
14
|
+
slugFor: (abs, root) => {
|
|
15
|
+
const rel = path.relative(root, abs).split(path.sep).join('/');
|
|
16
|
+
// Match `<anything>/<slug>/SKILL.md` — slug is the parent dir of SKILL.md.
|
|
17
|
+
if (!rel.endsWith('/SKILL.md'))
|
|
18
|
+
return null;
|
|
19
|
+
const parts = rel.split('/');
|
|
20
|
+
if (parts.length < 2)
|
|
21
|
+
return null;
|
|
22
|
+
return parts[parts.length - 2] ?? null;
|
|
23
|
+
},
|
|
24
|
+
canonicalFilename: (slug) => `claude/${slug}/SKILL.md`,
|
|
25
|
+
},
|
|
26
|
+
'claude-code': {
|
|
27
|
+
pathPrefix: 'claude-code/commands/',
|
|
28
|
+
extensions: ['.md'],
|
|
29
|
+
nested: false,
|
|
30
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
31
|
+
canonicalFilename: (slug) => `claude-code/commands/${slug}.md`,
|
|
32
|
+
},
|
|
33
|
+
cursor: {
|
|
34
|
+
pathPrefix: 'cursor/rules/',
|
|
35
|
+
extensions: ['.mdc'],
|
|
36
|
+
nested: false,
|
|
37
|
+
slugFor: (abs) => path.basename(abs).replace(/\.mdc$/, '') || null,
|
|
38
|
+
canonicalFilename: (slug) => `cursor/rules/${slug}.mdc`,
|
|
39
|
+
},
|
|
40
|
+
chatgpt: {
|
|
41
|
+
pathPrefix: 'chatgpt/',
|
|
42
|
+
extensions: ['.md'],
|
|
43
|
+
nested: false,
|
|
44
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
45
|
+
canonicalFilename: (slug) => `chatgpt/${slug}.md`,
|
|
46
|
+
},
|
|
47
|
+
codex: {
|
|
48
|
+
pathPrefix: 'codex/',
|
|
49
|
+
extensions: ['.md'],
|
|
50
|
+
nested: false,
|
|
51
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
52
|
+
canonicalFilename: (slug) => `codex/${slug}.md`,
|
|
53
|
+
},
|
|
54
|
+
copilot: {
|
|
55
|
+
pathPrefix: 'copilot/instructions/',
|
|
56
|
+
// IMPORTANT: full multi-dot extension — strip BEFORE the .md to get clean slug.
|
|
57
|
+
extensions: ['.instructions.md'],
|
|
58
|
+
nested: false,
|
|
59
|
+
slugFor: (abs) => path.basename(abs).replace(/\.instructions\.md$/, '') || null,
|
|
60
|
+
canonicalFilename: (slug) => `copilot/instructions/${slug}.instructions.md`,
|
|
61
|
+
},
|
|
62
|
+
windsurf: {
|
|
63
|
+
pathPrefix: 'windsurf/rules/',
|
|
64
|
+
extensions: ['.md'],
|
|
65
|
+
nested: false,
|
|
66
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
67
|
+
canonicalFilename: (slug) => `windsurf/rules/${slug}.md`,
|
|
68
|
+
},
|
|
69
|
+
gemini: {
|
|
70
|
+
pathPrefix: 'gemini/instructions/',
|
|
71
|
+
extensions: ['.md'],
|
|
72
|
+
nested: false,
|
|
73
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
74
|
+
canonicalFilename: (slug) => `gemini/instructions/${slug}.md`,
|
|
75
|
+
},
|
|
76
|
+
antigravity: {
|
|
77
|
+
pathPrefix: 'antigravity/skills/',
|
|
78
|
+
extensions: ['.md'],
|
|
79
|
+
nested: false,
|
|
80
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
81
|
+
canonicalFilename: (slug) => `antigravity/skills/${slug}.md`,
|
|
82
|
+
},
|
|
83
|
+
opencode: {
|
|
84
|
+
pathPrefix: 'opencode/instructions/',
|
|
85
|
+
extensions: ['.md'],
|
|
86
|
+
nested: false,
|
|
87
|
+
slugFor: (abs) => path.basename(abs).replace(/\.md$/, '') || null,
|
|
88
|
+
canonicalFilename: (slug) => `opencode/instructions/${slug}.md`,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export const SUPPORTED_TOOLS = Object.keys(DETECTORS);
|
|
4
92
|
const IGNORED_DIRS = new Set(['node_modules', '.git', '.next', 'dist', 'build', '.turbo']);
|
|
5
93
|
function walkFiles(root) {
|
|
6
94
|
const out = [];
|
|
@@ -19,65 +107,111 @@ function walkFiles(root) {
|
|
|
19
107
|
walk(root);
|
|
20
108
|
return out;
|
|
21
109
|
}
|
|
22
|
-
|
|
110
|
+
/** Does the filename end with any of the detector's declared extensions? */
|
|
111
|
+
function matchesExtension(absPath, detector) {
|
|
112
|
+
return detector.extensions.some((ext) => absPath.endsWith(ext));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Auto-detect mode: iterate every detector and pick the first one whose
|
|
116
|
+
* path-prefix + extension match. Returns null if nothing matches.
|
|
117
|
+
*/
|
|
118
|
+
function detectAuto(absPath, root, content) {
|
|
23
119
|
const rel = path.relative(root, absPath).split(path.sep).join('/');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
120
|
+
for (const [ecosystem, detector] of Object.entries(DETECTORS)) {
|
|
121
|
+
if (!rel.startsWith(detector.pathPrefix))
|
|
122
|
+
continue;
|
|
123
|
+
if (!matchesExtension(absPath, detector))
|
|
124
|
+
continue;
|
|
125
|
+
const slug = detector.slugFor(absPath, root);
|
|
126
|
+
if (!slug)
|
|
127
|
+
continue;
|
|
128
|
+
return {
|
|
129
|
+
filename: detector.canonicalFilename(slug),
|
|
130
|
+
content,
|
|
131
|
+
ecosystem,
|
|
132
|
+
slug,
|
|
133
|
+
};
|
|
35
134
|
}
|
|
36
135
|
return null;
|
|
37
136
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
137
|
+
/**
|
|
138
|
+
* `--from-tool` mode: every file matching the chosen ecosystem's extension
|
|
139
|
+
* becomes a draft skill for that ecosystem, regardless of path prefix.
|
|
140
|
+
* Slug + canonical filename come from the detector, so the upload uses the
|
|
141
|
+
* canonical BotDocs layout — that's what lets downstream `botdocs install`
|
|
142
|
+
* route the file back to the right on-disk destination.
|
|
143
|
+
*/
|
|
144
|
+
function detectFromTool(absPath, root, content, ecosystem) {
|
|
145
|
+
const detector = DETECTORS[ecosystem];
|
|
146
|
+
if (!detector)
|
|
147
|
+
return null;
|
|
148
|
+
if (!matchesExtension(absPath, detector))
|
|
149
|
+
return null;
|
|
150
|
+
const slug = detector.slugFor(absPath, root);
|
|
151
|
+
if (!slug)
|
|
152
|
+
return null;
|
|
153
|
+
return {
|
|
154
|
+
filename: detector.canonicalFilename(slug),
|
|
155
|
+
content,
|
|
156
|
+
ecosystem,
|
|
157
|
+
slug,
|
|
158
|
+
};
|
|
45
159
|
}
|
|
46
160
|
function titleFromContent(content, slug) {
|
|
47
161
|
const m = content.match(/^#\s+(.+)$/m);
|
|
48
162
|
return m?.[1]?.trim() ?? slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
49
163
|
}
|
|
164
|
+
/** Human-readable list of all canonical path prefixes for the empty-result message. */
|
|
165
|
+
function ecosystemPrefixSummary() {
|
|
166
|
+
return Object.values(DETECTORS)
|
|
167
|
+
.map((d) => d.pathPrefix)
|
|
168
|
+
.join(', ');
|
|
169
|
+
}
|
|
50
170
|
export async function ingest(rootPath, options) {
|
|
51
171
|
const root = path.resolve(rootPath);
|
|
52
172
|
if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
|
|
53
173
|
console.error(`\n ✗ Not a directory: ${rootPath}\n`);
|
|
54
174
|
process.exit(1);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (options.fromTool && !SUPPORTED_TOOLS.includes(options.fromTool)) {
|
|
178
|
+
console.error(`\n ✗ Unknown --from-tool "${options.fromTool}". Supported: ${SUPPORTED_TOOLS.join(', ')}\n`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
return;
|
|
55
181
|
}
|
|
56
182
|
const detected = [];
|
|
57
183
|
for (const filePath of walkFiles(root)) {
|
|
58
184
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
59
|
-
const d =
|
|
185
|
+
const d = options.fromTool
|
|
186
|
+
? detectFromTool(filePath, root, content, options.fromTool)
|
|
187
|
+
: detectAuto(filePath, root, content);
|
|
60
188
|
if (d)
|
|
61
189
|
detected.push(d);
|
|
62
190
|
}
|
|
63
191
|
if (detected.length === 0) {
|
|
64
|
-
|
|
192
|
+
if (options.fromTool) {
|
|
193
|
+
const detector = DETECTORS[options.fromTool];
|
|
194
|
+
console.log(`\n ⚠ No files matched ecosystem "${options.fromTool}" (expected ${detector.extensions.join(' or ')} files) in ${root}.\n`);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log(`\n No skills detected. Looked for: ${ecosystemPrefixSummary()}.\n`);
|
|
198
|
+
}
|
|
65
199
|
return;
|
|
66
200
|
}
|
|
67
|
-
// Group into logical skills by slug
|
|
201
|
+
// Group into logical skills by slug, merging files that share a slug
|
|
202
|
+
// across ecosystems (e.g. a `code-review` Claude SKILL.md + a Cursor rule).
|
|
68
203
|
const grouped = new Map();
|
|
69
204
|
for (const f of detected) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
slug,
|
|
74
|
-
title: titleFromContent(f.content, slug),
|
|
205
|
+
if (!grouped.has(f.slug)) {
|
|
206
|
+
grouped.set(f.slug, {
|
|
207
|
+
slug: f.slug,
|
|
208
|
+
title: titleFromContent(f.content, f.slug),
|
|
75
209
|
description: '',
|
|
76
210
|
sourceEcosystem: f.ecosystem,
|
|
77
211
|
files: [],
|
|
78
212
|
});
|
|
79
213
|
}
|
|
80
|
-
grouped.get(slug).files.push({ filename: f.filename, content: f.content });
|
|
214
|
+
grouped.get(f.slug).files.push({ filename: f.filename, content: f.content });
|
|
81
215
|
}
|
|
82
216
|
const skills = [...grouped.values()];
|
|
83
217
|
console.log(`\n ✓ Found ${skills.length} skill(s):`);
|
|
@@ -3,6 +3,10 @@ interface InstallOptions {
|
|
|
3
3
|
flat?: boolean;
|
|
4
4
|
clean?: boolean;
|
|
5
5
|
json?: boolean;
|
|
6
|
+
/** When true, skip backups before overwriting existing files. Intended for
|
|
7
|
+
* CI where backups are noise; default behavior backs up untracked or
|
|
8
|
+
* locally-edited files to `.botdocs-backup/<ts>/` before the overwrite. */
|
|
9
|
+
noBackup?: boolean;
|
|
6
10
|
}
|
|
7
11
|
export declare function install(rawRef: string, options: InstallOptions): Promise<void>;
|
|
8
12
|
export {};
|
package/dist/commands/install.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { ApiError, apiFetch, fetchRawContent } from '../lib/api.js';
|
|
5
5
|
import { detectDestination } from '../lib/auto-detect.js';
|
|
6
6
|
import { fingerprintContent, fingerprintFile, loadLockfile, upsertInstall, } from '../lib/lockfile.js';
|
|
7
|
+
import { backupFile, isLockfileOwnedAndUnchanged } from '../lib/backup.js';
|
|
7
8
|
import { syncLibrary } from '../lib/library-sync.js';
|
|
8
9
|
function parseRef(raw) {
|
|
9
10
|
const cleaned = raw.startsWith('@') ? raw.slice(1) : raw;
|
|
@@ -25,16 +26,33 @@ function buildContext(scope, slug, options) {
|
|
|
25
26
|
function ensureDir(filePath) {
|
|
26
27
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
27
28
|
}
|
|
28
|
-
async function downloadAndWrite(file, dest, options) {
|
|
29
|
+
async function downloadAndWrite(file, dest, options, projectDir) {
|
|
29
30
|
const content = await fetchRawContent(file.rawUrl);
|
|
30
31
|
if (fs.existsSync(dest) && !options.clean) {
|
|
31
32
|
const existingFp = fingerprintFile(dest);
|
|
32
33
|
const tmpFp = fingerprintContent(content);
|
|
33
34
|
if (existingFp === tmpFp) {
|
|
34
|
-
// Already present at same fingerprint — additive no-op.
|
|
35
|
+
// Already present at same fingerprint — additive no-op. No backup
|
|
36
|
+
// needed: we're about to write the same bytes anyway.
|
|
35
37
|
return { src: file.filename, dest, fingerprint: existingFp };
|
|
36
38
|
}
|
|
37
39
|
}
|
|
40
|
+
// About to overwrite. If the existing file isn't something we own and
|
|
41
|
+
// haven't touched, take a backup first so a hand-written rule at a
|
|
42
|
+
// colliding path isn't silently lost.
|
|
43
|
+
if (fs.existsSync(dest) && !options.noBackup && !isLockfileOwnedAndUnchanged(dest)) {
|
|
44
|
+
const result = backupFile(dest, projectDir);
|
|
45
|
+
if (!options.json) {
|
|
46
|
+
if (result.ok) {
|
|
47
|
+
const relSrc = path.relative(process.cwd(), dest);
|
|
48
|
+
const relDest = path.relative(process.cwd(), result.dest);
|
|
49
|
+
console.log(` ⚠ Backed up existing file: ${relSrc} → ${relDest}`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(` ⚠ Could not back up ${dest}: ${result.error} — proceeding with overwrite.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
38
56
|
ensureDir(dest);
|
|
39
57
|
fs.writeFileSync(dest, content, 'utf-8');
|
|
40
58
|
return { src: file.filename, dest, fingerprint: fingerprintFile(dest) };
|
|
@@ -42,6 +60,7 @@ async function downloadAndWrite(file, dest, options) {
|
|
|
42
60
|
async function installSkill(ref, manifest, options, scope) {
|
|
43
61
|
const ctx = buildContext(scope, ref.slug, options);
|
|
44
62
|
const filesInstalled = [];
|
|
63
|
+
let manualPromptsShown = 0;
|
|
45
64
|
for (const file of manifest.files) {
|
|
46
65
|
const detection = detectDestination(file.filename, ctx);
|
|
47
66
|
if (detection.kind === 'skip')
|
|
@@ -52,12 +71,30 @@ async function installSkill(ref, manifest, options, scope) {
|
|
|
52
71
|
const content = await fetchRawContent(file.rawUrl);
|
|
53
72
|
console.log(`\n Manual paste required for ${file.filename}:\n${content}\n`);
|
|
54
73
|
}
|
|
74
|
+
// A manual paste prompt counts as "we surfaced this file to the user",
|
|
75
|
+
// so don't trigger the no-installable-files warning below.
|
|
76
|
+
manualPromptsShown += 1;
|
|
55
77
|
continue;
|
|
56
78
|
}
|
|
57
|
-
const installed = await downloadAndWrite(file, detection.dest, options);
|
|
79
|
+
const installed = await downloadAndWrite(file, detection.dest, options, ctx.projectDir);
|
|
58
80
|
if (installed)
|
|
59
81
|
filesInstalled.push(installed);
|
|
60
82
|
}
|
|
83
|
+
// Surface clear feedback when the manifest produced nothing actionable.
|
|
84
|
+
// Suppressed under --json so machine-readable output stays clean.
|
|
85
|
+
if (!options.json) {
|
|
86
|
+
const refStr = `@${ref.username}/${ref.slug}`;
|
|
87
|
+
if (manifest.files.length === 0) {
|
|
88
|
+
console.log(`\n ⚠ ${refStr} has no files. Nothing to install.\n` +
|
|
89
|
+
` This BotDoc may have been published before any content was added.\n`);
|
|
90
|
+
}
|
|
91
|
+
else if (filesInstalled.length === 0 && manualPromptsShown === 0) {
|
|
92
|
+
console.log(`\n ⚠ ${refStr} has files but none target a supported agent.\n` +
|
|
93
|
+
` It may be a spec-only BotDoc without compiled per-agent files\n` +
|
|
94
|
+
` (claude/SKILL.md, cursor/rules/*.mdc, etc.).\n` +
|
|
95
|
+
` Ask the author to run \`botdocs compile\` and re-publish.\n`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
61
98
|
return {
|
|
62
99
|
ref: `@${ref.username}/${ref.slug}`,
|
|
63
100
|
type: 'SKILL',
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
interface LoginOptions {
|
|
2
2
|
syncLibrary?: boolean;
|
|
3
|
+
/** Skip the browser flow and store this token directly. Used for CI/headless
|
|
4
|
+
* environments where the user has already minted a token at /settings/tokens. */
|
|
5
|
+
token?: string;
|
|
6
|
+
/** Force the plain-text rendering path even on a real TTY. Useful for users
|
|
7
|
+
* who prefer screen-reader-friendly output, or anyone disturbed by live
|
|
8
|
+
* redraws. The non-TTY path is taken automatically when stdout is piped. */
|
|
9
|
+
noInk?: boolean;
|
|
3
10
|
}
|
|
4
11
|
export declare function login(options?: LoginOptions): Promise<void>;
|
|
5
12
|
export {};
|