goke 6.9.0 → 6.10.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/__test__/completions.test.d.ts +9 -0
- package/dist/__test__/completions.test.d.ts.map +1 -0
- package/dist/__test__/completions.test.js +774 -0
- package/dist/__test__/index.test.js +64 -0
- package/dist/__test__/readme-examples.test.js +141 -5
- package/dist/__test__/types.test-d.js +27 -0
- package/dist/agents.d.ts +38 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +63 -0
- package/dist/completions.d.ts +88 -0
- package/dist/completions.d.ts.map +1 -0
- package/dist/completions.js +315 -0
- package/dist/goke.d.ts +92 -2
- package/dist/goke.d.ts.map +1 -1
- package/dist/goke.js +479 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/runtime-browser.d.ts +1 -1
- package/dist/runtime-browser.d.ts.map +1 -1
- package/dist/runtime-browser.js +1 -1
- package/dist/runtime-node.d.ts +1 -1
- package/dist/runtime-node.d.ts.map +1 -1
- package/dist/runtime-node.js +22 -13
- package/package.json +1 -1
- package/src/__test__/completions.test.ts +902 -0
- package/src/__test__/index.test.ts +75 -0
- package/src/__test__/readme-examples.test.ts +153 -5
- package/src/__test__/types.test-d.ts +27 -0
- package/src/agents.ts +101 -0
- package/src/completions.ts +363 -0
- package/src/goke.ts +529 -2
- package/src/index.ts +11 -2
- package/src/runtime-browser.ts +1 -1
- package/src/runtime-node.ts +19 -11
|
@@ -132,6 +132,35 @@ describe('error formatting', () => {
|
|
|
132
132
|
expect(text).toMatch(/at /);
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
|
+
describe('anonymous action naming', () => {
|
|
136
|
+
test('inline anonymous function gets named after the command', () => {
|
|
137
|
+
const cli = gokeTestable('mycli');
|
|
138
|
+
const cmd = cli.command('deploy', 'Deploy app');
|
|
139
|
+
// Inline arrow functions passed directly to .action() have no name,
|
|
140
|
+
// so goke assigns one based on the command name for better stack traces.
|
|
141
|
+
cmd.action(() => { });
|
|
142
|
+
expect(cmd.commandAction.name).toBe('command:deploy');
|
|
143
|
+
});
|
|
144
|
+
test('inline anonymous function on multi-word command gets full name', () => {
|
|
145
|
+
const cli = gokeTestable('mycli');
|
|
146
|
+
const cmd = cli.command('db migrate', 'Run migrations');
|
|
147
|
+
cmd.action(() => { });
|
|
148
|
+
expect(cmd.commandAction.name).toBe('command:db migrate');
|
|
149
|
+
});
|
|
150
|
+
test('named function keeps its original name', () => {
|
|
151
|
+
const cli = gokeTestable('mycli');
|
|
152
|
+
const cmd = cli.command('build', 'Build app');
|
|
153
|
+
function myBuildAction() { }
|
|
154
|
+
cmd.action(myBuildAction);
|
|
155
|
+
expect(cmd.commandAction.name).toBe('myBuildAction');
|
|
156
|
+
});
|
|
157
|
+
test('default command action gets "command:default" name', () => {
|
|
158
|
+
const cli = gokeTestable('mycli');
|
|
159
|
+
const cmd = cli.command('', 'Default command');
|
|
160
|
+
cmd.action(() => { });
|
|
161
|
+
expect(cmd.commandAction.name).toBe('command:default');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
135
164
|
describe('injected fs', () => {
|
|
136
165
|
test('command actions can use the default node fs for cli storage', async () => {
|
|
137
166
|
const stdout = createTestOutputStream();
|
|
@@ -1955,3 +1984,38 @@ describe('use() with sub-CLI composition', () => {
|
|
|
1955
1984
|
expect(matched).toBe('rollback');
|
|
1956
1985
|
});
|
|
1957
1986
|
});
|
|
1987
|
+
describe('getAction()', () => {
|
|
1988
|
+
test('returns the action callable with correct behavior', () => {
|
|
1989
|
+
const stdout = createTestOutputStream();
|
|
1990
|
+
const cli = goke('mycli', { stdout, exit: () => { } });
|
|
1991
|
+
const cmd = cli
|
|
1992
|
+
.command('deploy', 'Deploy the app')
|
|
1993
|
+
.option('--env <env>', z.enum(['staging', 'production']).describe('Target environment'))
|
|
1994
|
+
.action((options, { console }) => {
|
|
1995
|
+
console.log(`Deploying to ${options.env}`);
|
|
1996
|
+
});
|
|
1997
|
+
const action = cmd.getAction();
|
|
1998
|
+
const ctx = cli.createExecutionContext();
|
|
1999
|
+
action({ env: 'staging', '--': [] }, ctx);
|
|
2000
|
+
expect(stdout.text).toBe('Deploying to staging\n');
|
|
2001
|
+
});
|
|
2002
|
+
test('works with positional args', () => {
|
|
2003
|
+
const stdout = createTestOutputStream();
|
|
2004
|
+
const cli = goke('mycli', { stdout, exit: () => { } });
|
|
2005
|
+
const cmd = cli
|
|
2006
|
+
.command('get <id>', 'Fetch by id')
|
|
2007
|
+
.option('--format <format>', z.string().describe('Output format'))
|
|
2008
|
+
.action((id, options, { console }) => {
|
|
2009
|
+
console.log(`${id}:${options.format}`);
|
|
2010
|
+
});
|
|
2011
|
+
const action = cmd.getAction();
|
|
2012
|
+
const ctx = cli.createExecutionContext();
|
|
2013
|
+
action('abc123', { format: 'json', '--': [] }, ctx);
|
|
2014
|
+
expect(stdout.text).toBe('abc123:json\n');
|
|
2015
|
+
});
|
|
2016
|
+
test('throws when no action is registered', () => {
|
|
2017
|
+
const cli = goke('mycli');
|
|
2018
|
+
const cmd = cli.command('noop', 'No action');
|
|
2019
|
+
expect(() => cmd.getAction()).toThrow(/No action registered/);
|
|
2020
|
+
});
|
|
2021
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { describe, expect, test } from 'vitest';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import goke, { openInBrowser } from '../index.js';
|
|
6
|
+
import goke, { openInBrowser, generateDocs } from '../index.js';
|
|
7
7
|
const ANSI_RE = /\x1B\[[0-9;]*m/g;
|
|
8
8
|
const stripAnsi = (text) => text.replace(ANSI_RE, '');
|
|
9
9
|
function createTestOutputStream() {
|
|
@@ -133,7 +133,7 @@ describe('documented command APIs', () => {
|
|
|
133
133
|
expect(help).toContain('Deploy safely first');
|
|
134
134
|
expect(stdout.text).toBe('');
|
|
135
135
|
});
|
|
136
|
-
test('openInBrowser prints the URL to stdout in non-tty environments', () => {
|
|
136
|
+
test('openInBrowser prints the URL to stdout in non-tty environments', async () => {
|
|
137
137
|
const url = 'https://example.com/dashboard';
|
|
138
138
|
const originalStdoutWrite = process.stdout.write;
|
|
139
139
|
const originalStderrWrite = process.stderr.write;
|
|
@@ -153,7 +153,7 @@ describe('documented command APIs', () => {
|
|
|
153
153
|
return true;
|
|
154
154
|
});
|
|
155
155
|
try {
|
|
156
|
-
openInBrowser(url);
|
|
156
|
+
await openInBrowser(url);
|
|
157
157
|
}
|
|
158
158
|
finally {
|
|
159
159
|
process.stdout.write = originalStdoutWrite;
|
|
@@ -163,7 +163,143 @@ describe('documented command APIs', () => {
|
|
|
163
163
|
value: originalIsTTY,
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
expect(stdout).toBe(
|
|
167
|
-
expect(stderr).toBe(
|
|
166
|
+
expect(stdout).toBe('');
|
|
167
|
+
expect(stderr).toBe(`${url}\n`);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('generateDocs', () => {
|
|
171
|
+
test('generates pages for CLI with multiple commands', () => {
|
|
172
|
+
const cli = gokeTestable('sentry')
|
|
173
|
+
.version('1.0.0')
|
|
174
|
+
.help();
|
|
175
|
+
cli
|
|
176
|
+
.command('event view <id>', 'View details of a specific event')
|
|
177
|
+
.option('-w, --web', 'Open in browser')
|
|
178
|
+
.option('--spans <spans>', z.string().default('3').describe('Span tree depth limit'))
|
|
179
|
+
.example('```\nsentry event view abc123\n```');
|
|
180
|
+
cli
|
|
181
|
+
.command('event list <issue>', 'List events for an issue')
|
|
182
|
+
.option('-n, --limit <limit>', z.number().default(25).describe('Number of events'))
|
|
183
|
+
.option('-q, --query <query>', 'Search query');
|
|
184
|
+
cli
|
|
185
|
+
.command('hidden-cmd', 'Should not appear')
|
|
186
|
+
.hidden();
|
|
187
|
+
const pages = generateDocs({ cli });
|
|
188
|
+
expect(pages.map((p) => p.slug)).toMatchInlineSnapshot(`
|
|
189
|
+
[
|
|
190
|
+
"index",
|
|
191
|
+
"event-view",
|
|
192
|
+
"event-list",
|
|
193
|
+
]
|
|
194
|
+
`);
|
|
195
|
+
// Index page
|
|
196
|
+
expect(pages[0].content).toMatchInlineSnapshot(`
|
|
197
|
+
"# sentry
|
|
198
|
+
|
|
199
|
+
Version: 1.0.0
|
|
200
|
+
|
|
201
|
+
## Commands
|
|
202
|
+
|
|
203
|
+
| Command | Description |
|
|
204
|
+
|---------|-------------|
|
|
205
|
+
| [\`event view\`](./event-view.md) | View details of a specific event |
|
|
206
|
+
| [\`event list\`](./event-list.md) | List events for an issue |
|
|
207
|
+
|
|
208
|
+
## Global Options
|
|
209
|
+
|
|
210
|
+
| Option | Default | Description |
|
|
211
|
+
|--------|---------|-------------|
|
|
212
|
+
| \`-v, --version\` | - | Display version number |
|
|
213
|
+
| \`-h, --help\` | - | Display this message |
|
|
214
|
+
"
|
|
215
|
+
`);
|
|
216
|
+
// Command page with examples
|
|
217
|
+
expect(pages[1].content).toMatchInlineSnapshot(`
|
|
218
|
+
"# event view
|
|
219
|
+
|
|
220
|
+
View details of a specific event
|
|
221
|
+
|
|
222
|
+
## Usage
|
|
223
|
+
|
|
224
|
+
\`\`\`sh
|
|
225
|
+
sentry event view <id>
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Arguments
|
|
229
|
+
|
|
230
|
+
| Argument | Required | Description |
|
|
231
|
+
|----------|----------|-------------|
|
|
232
|
+
| \`<id>\` | Yes | id |
|
|
233
|
+
|
|
234
|
+
## Options
|
|
235
|
+
|
|
236
|
+
| Option | Default | Description |
|
|
237
|
+
|--------|---------|-------------|
|
|
238
|
+
| \`-w, --web\` | - | Open in browser |
|
|
239
|
+
| \`--spans <spans>\` | \`3\` | Span tree depth limit |
|
|
240
|
+
|
|
241
|
+
## Global Options
|
|
242
|
+
|
|
243
|
+
| Option | Default | Description |
|
|
244
|
+
|--------|---------|-------------|
|
|
245
|
+
| \`-v, --version\` | - | Display version number |
|
|
246
|
+
| \`-h, --help\` | - | Display this message |
|
|
247
|
+
|
|
248
|
+
## Examples
|
|
249
|
+
|
|
250
|
+
\`\`\`
|
|
251
|
+
sentry event view abc123
|
|
252
|
+
\`\`\`
|
|
253
|
+
"
|
|
254
|
+
`);
|
|
255
|
+
// Command page without examples
|
|
256
|
+
expect(pages[2].content).toMatchInlineSnapshot(`
|
|
257
|
+
"# event list
|
|
258
|
+
|
|
259
|
+
List events for an issue
|
|
260
|
+
|
|
261
|
+
## Usage
|
|
262
|
+
|
|
263
|
+
\`\`\`sh
|
|
264
|
+
sentry event list <issue>
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
## Arguments
|
|
268
|
+
|
|
269
|
+
| Argument | Required | Description |
|
|
270
|
+
|----------|----------|-------------|
|
|
271
|
+
| \`<issue>\` | Yes | issue |
|
|
272
|
+
|
|
273
|
+
## Options
|
|
274
|
+
|
|
275
|
+
| Option | Default | Description |
|
|
276
|
+
|--------|---------|-------------|
|
|
277
|
+
| \`-n, --limit <limit>\` | \`25\` | Number of events |
|
|
278
|
+
| \`-q, --query <query>\` | - | Search query |
|
|
279
|
+
|
|
280
|
+
## Global Options
|
|
281
|
+
|
|
282
|
+
| Option | Default | Description |
|
|
283
|
+
|--------|---------|-------------|
|
|
284
|
+
| \`-v, --version\` | - | Display version number |
|
|
285
|
+
| \`-h, --help\` | - | Display this message |
|
|
286
|
+
"
|
|
287
|
+
`);
|
|
288
|
+
});
|
|
289
|
+
test('handles CLI with no commands', () => {
|
|
290
|
+
const cli = gokeTestable('empty');
|
|
291
|
+
const pages = generateDocs({ cli });
|
|
292
|
+
expect(pages).toEqual([]);
|
|
293
|
+
});
|
|
294
|
+
test('skips deprecated options', () => {
|
|
295
|
+
const cli = gokeTestable('mycli');
|
|
296
|
+
cli
|
|
297
|
+
.command('deploy', 'Deploy the app')
|
|
298
|
+
.option('--force', 'Skip confirmation')
|
|
299
|
+
.option('--old-flag', z.boolean().meta({ deprecated: true }).describe('Use --force instead'));
|
|
300
|
+
const pages = generateDocs({ cli });
|
|
301
|
+
const deployPage = pages.find((p) => p.slug === 'deploy');
|
|
302
|
+
// The deprecated option should not appear
|
|
303
|
+
expect(deployPage.content).not.toContain('old-flag');
|
|
168
304
|
});
|
|
169
305
|
});
|
|
@@ -434,6 +434,33 @@ describe('type-level: README TypeScript examples', () => {
|
|
|
434
434
|
options.subOnly;
|
|
435
435
|
});
|
|
436
436
|
});
|
|
437
|
+
test('getAction() returns correctly typed function', () => {
|
|
438
|
+
const cmd = goke('test')
|
|
439
|
+
.command('convert <input> <output>', 'Convert file format')
|
|
440
|
+
.option('--quality <quality>', z.number())
|
|
441
|
+
.option('--format <format>', z.enum(['png', 'jpg']))
|
|
442
|
+
.action((input, output, options, ctx) => {
|
|
443
|
+
void input;
|
|
444
|
+
void output;
|
|
445
|
+
void options;
|
|
446
|
+
void ctx;
|
|
447
|
+
});
|
|
448
|
+
const action = cmd.getAction();
|
|
449
|
+
expectTypeOf(action).parameter(0).toEqualTypeOf(); // input
|
|
450
|
+
expectTypeOf(action).parameter(1).toEqualTypeOf(); // output
|
|
451
|
+
});
|
|
452
|
+
test('getAction() with no positional args has (options, ctx) signature', () => {
|
|
453
|
+
const cmd = goke('test')
|
|
454
|
+
.command('deploy', 'Deploy')
|
|
455
|
+
.option('--env <env>', z.enum(['staging', 'production']))
|
|
456
|
+
.action((options, ctx) => {
|
|
457
|
+
void options;
|
|
458
|
+
void ctx;
|
|
459
|
+
});
|
|
460
|
+
const action = cmd.getAction();
|
|
461
|
+
// First param is options with env
|
|
462
|
+
expectTypeOf(action).parameter(0).toMatchTypeOf();
|
|
463
|
+
});
|
|
437
464
|
test('README global options and middleware example stays typed end-to-end', () => {
|
|
438
465
|
// `z.boolean().default(false)` and `z.string().default(...)` are
|
|
439
466
|
// effectively required at runtime: the default applies when the flag is
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI coding agent detection for goke CLIs.
|
|
3
|
+
*
|
|
4
|
+
* Detects whether the current process is running inside an AI coding agent
|
|
5
|
+
* (Claude, Cursor, Codex, Gemini, etc.) by checking environment variables.
|
|
6
|
+
* Ported from unjs/std-env with the same detection logic.
|
|
7
|
+
*
|
|
8
|
+
* CLI authors can use this to adjust behavior: skip interactive prompts,
|
|
9
|
+
* prefer structured output, avoid browser opens, etc.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Known AI coding agent names.
|
|
13
|
+
*/
|
|
14
|
+
export type AgentName = (string & {}) | 'cursor' | 'claude' | 'devin' | 'replit' | 'gemini' | 'codex' | 'auggie' | 'opencode' | 'kiro' | 'goose' | 'pi';
|
|
15
|
+
/**
|
|
16
|
+
* Information about the detected AI coding agent.
|
|
17
|
+
*/
|
|
18
|
+
export type AgentInfo = {
|
|
19
|
+
/** The name of the detected agent, or undefined if no agent was detected. */
|
|
20
|
+
name?: AgentName;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Detect the current AI coding agent from environment variables.
|
|
24
|
+
*
|
|
25
|
+
* Checks `AI_AGENT` env var first (explicit override), then scans for
|
|
26
|
+
* known agent-specific env vars in priority order.
|
|
27
|
+
*
|
|
28
|
+
* Supported agents: `cursor`, `claude`, `devin`, `replit`, `gemini`,
|
|
29
|
+
* `codex`, `auggie`, `opencode`, `kiro`, `goose`, `pi`
|
|
30
|
+
*/
|
|
31
|
+
export declare function detectAgent(): AgentInfo;
|
|
32
|
+
/** Detected agent info, evaluated once at import time. */
|
|
33
|
+
export declare const agentInfo: AgentInfo;
|
|
34
|
+
/** Name of the detected agent, or undefined if not running inside one. */
|
|
35
|
+
export declare const agent: AgentName | undefined;
|
|
36
|
+
/** Whether the current process is running inside an AI coding agent. */
|
|
37
|
+
export declare const isAgent: boolean;
|
|
38
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH;;GAEG;AACH,MAAM,MAAM,SAAS,GACjB,CAAC,MAAM,GAAG,EAAE,CAAC,GACb,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,UAAU,GACV,MAAM,GACN,OAAO,GACP,IAAI,CAAA;AAgCR;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,SAAS,CAavC;AAED,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,SAAyC,CAAA;AAEjE,0EAA0E;AAC1E,eAAO,MAAM,KAAK,EAAE,SAAS,GAAG,SAA0B,CAAA;AAE1D,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,OAA0B,CAAA"}
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI coding agent detection for goke CLIs.
|
|
3
|
+
*
|
|
4
|
+
* Detects whether the current process is running inside an AI coding agent
|
|
5
|
+
* (Claude, Cursor, Codex, Gemini, etc.) by checking environment variables.
|
|
6
|
+
* Ported from unjs/std-env with the same detection logic.
|
|
7
|
+
*
|
|
8
|
+
* CLI authors can use this to adjust behavior: skip interactive prompts,
|
|
9
|
+
* prefer structured output, avoid browser opens, etc.
|
|
10
|
+
*/
|
|
11
|
+
const env = globalThis.process?.env || Object.create(null);
|
|
12
|
+
function envMatcher(envKey, regex) {
|
|
13
|
+
return () => {
|
|
14
|
+
const value = env[envKey];
|
|
15
|
+
return value ? regex.test(value) : false;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Detection order matters: specific agents first, IDE-based agents last
|
|
19
|
+
// so that agents running inside those IDEs are detected by their own env vars first.
|
|
20
|
+
const agents = [
|
|
21
|
+
// CLI agents
|
|
22
|
+
['claude', ['CLAUDECODE', 'CLAUDE_CODE']],
|
|
23
|
+
['replit', ['REPL_ID']],
|
|
24
|
+
['gemini', ['GEMINI_CLI']],
|
|
25
|
+
['codex', ['CODEX_SANDBOX', 'CODEX_THREAD_ID']],
|
|
26
|
+
['opencode', ['OPENCODE']],
|
|
27
|
+
['pi', [envMatcher('PATH', /\.pi[\\/]agent/)]],
|
|
28
|
+
['auggie', ['AUGMENT_AGENT']],
|
|
29
|
+
['goose', ['GOOSE_PROVIDER']],
|
|
30
|
+
// IDE-based agents (checked last)
|
|
31
|
+
['devin', [envMatcher('EDITOR', /devin/)]],
|
|
32
|
+
['cursor', ['CURSOR_AGENT']],
|
|
33
|
+
['kiro', [envMatcher('TERM_PROGRAM', /kiro/)]],
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Detect the current AI coding agent from environment variables.
|
|
37
|
+
*
|
|
38
|
+
* Checks `AI_AGENT` env var first (explicit override), then scans for
|
|
39
|
+
* known agent-specific env vars in priority order.
|
|
40
|
+
*
|
|
41
|
+
* Supported agents: `cursor`, `claude`, `devin`, `replit`, `gemini`,
|
|
42
|
+
* `codex`, `auggie`, `opencode`, `kiro`, `goose`, `pi`
|
|
43
|
+
*/
|
|
44
|
+
export function detectAgent() {
|
|
45
|
+
const aiAgent = env.AI_AGENT;
|
|
46
|
+
if (aiAgent) {
|
|
47
|
+
return { name: aiAgent.toLowerCase() };
|
|
48
|
+
}
|
|
49
|
+
for (const [name, checks] of agents) {
|
|
50
|
+
for (const check of checks) {
|
|
51
|
+
if (typeof check === 'string' ? env[check] : check()) {
|
|
52
|
+
return { name };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
/** Detected agent info, evaluated once at import time. */
|
|
59
|
+
export const agentInfo = /* #__PURE__ */ detectAgent();
|
|
60
|
+
/** Name of the detected agent, or undefined if not running inside one. */
|
|
61
|
+
export const agent = agentInfo.name;
|
|
62
|
+
/** Whether the current process is running inside an AI coding agent. */
|
|
63
|
+
export const isAgent = !!agentInfo.name;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell completion support for goke CLIs.
|
|
3
|
+
*
|
|
4
|
+
* Two pieces work together:
|
|
5
|
+
* 1. A hidden `--get-goke-completions` flag in the CLI binary. When present,
|
|
6
|
+
* the CLI skips normal execution and prints matching completions to stdout.
|
|
7
|
+
* 2. A shell-specific shim script installed into an fpath/completion directory.
|
|
8
|
+
* On each Tab press the shell calls the CLI binary with the flag + current words.
|
|
9
|
+
*
|
|
10
|
+
* The `installCompletions` function finds a writable completion directory and
|
|
11
|
+
* writes the shim. Since the shim calls the binary on every Tab, completions
|
|
12
|
+
* are always up-to-date with the installed CLI.
|
|
13
|
+
*/
|
|
14
|
+
/** The hidden flag the shell script passes to the CLI on each Tab press */
|
|
15
|
+
export declare const COMPLETION_FLAG = "get-goke-completions";
|
|
16
|
+
/**
|
|
17
|
+
* Zsh completion script template.
|
|
18
|
+
*
|
|
19
|
+
* The `#compdef` header lets zsh autoload this as an fpath function.
|
|
20
|
+
* On Tab press it calls the CLI binary with `--get-goke-completions` and
|
|
21
|
+
* all typed words. The binary returns `name:description` pairs (one per line)
|
|
22
|
+
* and zsh renders them with `_describe`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const zshTemplate = "#compdef {{app_name}}\n###-begin-{{app_name}}-completions-###\n_{{app_name_safe}}_completions() {\n local reply\n local si=$IFS\n IFS=$'\\n' reply=($(COMP_CWORD=\"$((CURRENT-1))\" COMP_LINE=\"$BUFFER\" COMP_POINT=\"$CURSOR\" GOKE_COMPLETION_SHELL=zsh {{app_path}} --get-goke-completions \"${words[@]}\"))\n IFS=$si\n if [[ ${#reply} -gt 0 ]]; then\n _describe 'values' reply\n else\n _default\n fi\n}\nif [[ \"'${zsh_eval_context[-1]}\" == \"loadautofunc\" ]]; then\n _{{app_name_safe}}_completions \"$@\"\nelse\n compdef _{{app_name_safe}}_completions {{app_name}}\nfi\n###-end-{{app_name}}-completions-###\n";
|
|
25
|
+
/**
|
|
26
|
+
* Bash completion script template.
|
|
27
|
+
*
|
|
28
|
+
* On Tab press bash calls the function which invokes the CLI binary with
|
|
29
|
+
* `--get-goke-completions` and all typed words. The binary returns plain
|
|
30
|
+
* completion strings (one per line).
|
|
31
|
+
*/
|
|
32
|
+
export declare const bashTemplate = "###-begin-{{app_name}}-completions-###\n_{{app_name_safe}}_completions()\n{\n local cur_word args type_list\n\n cur_word=\"${COMP_WORDS[COMP_CWORD]}\"\n args=(\"${COMP_WORDS[@]}\")\n\n # Bash 3 compatible (no mapfile). Works on macOS default bash.\n local IFS=$'\\n'\n type_list=($(GOKE_COMPLETION_SHELL=bash {{app_path}} --get-goke-completions \"${args[@]}\"))\n unset IFS\n COMPREPLY=($(compgen -W \"$( printf '%q ' \"${type_list[@]}\" )\" -- \"${cur_word}\" |\n awk '/ / { print \"\\\"\"$0\"\\\"\" } /^[^ ]+$/ { print $0 }'))\n\n if [ ${#COMPREPLY[@]} -eq 0 ]; then\n COMPREPLY=()\n fi\n\n return 0\n}\ncomplete -o bashdefault -o default -F _{{app_name_safe}}_completions {{app_name}}\n###-end-{{app_name}}-completions-###\n";
|
|
33
|
+
export type ShellType = 'zsh' | 'bash';
|
|
34
|
+
/**
|
|
35
|
+
* Detect the current shell from environment variables.
|
|
36
|
+
* Returns 'zsh', 'bash', or null if unrecognized.
|
|
37
|
+
*/
|
|
38
|
+
export declare function detectShell(): ShellType | null;
|
|
39
|
+
/**
|
|
40
|
+
* Validate and normalize a shell value from user input.
|
|
41
|
+
* Returns a valid ShellType or throws if the value is invalid.
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateShell(value: unknown): ShellType | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Detect which shell format to use for completion output.
|
|
46
|
+
* Prefers the explicit GOKE_COMPLETION_SHELL env var (set by the shell shim)
|
|
47
|
+
* over the login $SHELL. This prevents format mismatch when a bash shim runs
|
|
48
|
+
* on a system where $SHELL is zsh.
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectCompletionShell(): ShellType | null;
|
|
51
|
+
/**
|
|
52
|
+
* Generate a completion script for the given shell.
|
|
53
|
+
*
|
|
54
|
+
* @param shell - Target shell ('zsh' or 'bash')
|
|
55
|
+
* @param cliName - The CLI binary name (e.g. 'my-cli')
|
|
56
|
+
* @param cliPath - Full path to the CLI binary. If not provided, uses cliName.
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateCompletionScript(shell: ShellType, cliName: string, cliPath?: string): string;
|
|
59
|
+
interface InstallResult {
|
|
60
|
+
/** The file path where the completion script was written */
|
|
61
|
+
path: string;
|
|
62
|
+
/** The shell the script was generated for */
|
|
63
|
+
shell: ShellType;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Find a writable completion directory, generate the shell script, and write it.
|
|
67
|
+
*
|
|
68
|
+
* For zsh, scans `$fpath` directories plus well-known fallbacks.
|
|
69
|
+
* For bash, scans XDG and legacy user completion dirs.
|
|
70
|
+
*
|
|
71
|
+
* Throws if no writable directory is found.
|
|
72
|
+
*
|
|
73
|
+
* @param cliName - The CLI binary name
|
|
74
|
+
* @param cliPath - Full path to the CLI binary
|
|
75
|
+
* @param shell - Target shell. Auto-detected from $SHELL if omitted.
|
|
76
|
+
*/
|
|
77
|
+
export declare function installCompletions(cliName: string, cliPath: string, shell?: ShellType): Promise<InstallResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Remove an installed completion script.
|
|
80
|
+
*
|
|
81
|
+
* Searches the same candidate directories as `installCompletions` and removes
|
|
82
|
+
* any matching completion files found.
|
|
83
|
+
*
|
|
84
|
+
* @returns The paths that were removed, or empty array if none found.
|
|
85
|
+
*/
|
|
86
|
+
export declare function uninstallCompletions(cliName: string, shell?: ShellType): Promise<string[]>;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=completions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../src/completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,2EAA2E;AAC3E,eAAO,MAAM,eAAe,yBAAyB,CAAA;AAIrD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,qnBAmBvB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,ywBAuBxB,CAAA;AAID,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAA;AAEtC;;;GAGG;AACH,wBAAgB,WAAW,IAAI,SAAS,GAAG,IAAI,CAK9C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAInE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,GAAG,IAAI,CAIxD;AAUD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CASR;AA+BD,UAAU,aAAa;IACrB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,KAAK,EAAE,SAAS,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,SAAS,GAChB,OAAO,CAAC,aAAa,CAAC,CA4FxB;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,SAAS,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAsDnB"}
|