argsbarg 4.1.0 → 5.0.1
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/CHANGELOG.md +74 -1
- package/README.md +91 -85
- package/docs/README.md +8 -8
- package/docs/ai-skills.md +9 -9
- package/docs/bundled-docs.md +5 -5
- package/docs/cli-program.md +13 -11
- package/docs/config-schema.md +36 -9
- package/docs/configure.md +177 -0
- package/docs/developing.md +7 -7
- package/docs/distribution-homebrew.md +104 -0
- package/docs/mcp.md +11 -12
- package/docs/output-schema.md +1 -1
- package/examples/full-example/Formula/.gitkeep +0 -0
- package/examples/full-example/README.md +98 -0
- package/examples/full-example/biome.json +22 -0
- package/examples/{consumer-app → full-example}/bun.lock +2 -0
- package/examples/full-example/justfile +134 -0
- package/examples/{consumer-app → full-example}/package.json +10 -3
- package/examples/{consumer-app → full-example}/schemas/generated/app-config.json +1 -1
- package/examples/{consumer-app → full-example}/schemas/generated/status.json +1 -1
- package/examples/full-example/scripts/create-identity.ts +11 -0
- package/examples/full-example/scripts/formula-shared.ts +73 -0
- package/examples/full-example/scripts/gen-dev-formula.ts +26 -0
- package/examples/full-example/scripts/print-identity.ts +27 -0
- package/examples/full-example/src/commands/echo/command.ts +21 -0
- package/examples/full-example/src/commands/status/command.test.ts +10 -0
- package/examples/full-example/src/commands/status/command.ts +36 -0
- package/examples/{consumer-app → full-example}/src/commands/status/types.ts +1 -1
- package/examples/full-example/src/index.ts +10 -0
- package/examples/full-example/src/program.ts +57 -0
- package/examples/{consumer-app → full-example}/src/types.ts +1 -1
- package/examples/nested.ts +1 -3
- package/index.d.ts +49 -81
- package/package.json +2 -2
- package/src/builtins/builtins.test.ts +84 -64
- package/src/builtins/completion-group.ts +17 -17
- package/src/builtins/configure-copy.ts +86 -0
- package/src/builtins/configure.ts +70 -0
- package/src/builtins/dispatch.ts +13 -9
- package/src/builtins/index.ts +1 -1
- package/src/builtins/mcp.ts +2 -2
- package/src/builtins/registry.ts +6 -4
- package/src/capabilities.ts +22 -15
- package/src/cli-tool/cli-smoke.test.ts +29 -0
- package/src/cli-tool/create.test.ts +141 -0
- package/src/cli-tool/create.ts +402 -0
- package/{examples/consumer-app/capabilities.test.ts → src/cli-tool/full-example-capabilities.test.ts} +16 -15
- package/src/cli-tool/main.ts +8 -0
- package/src/cli-tool/post-create.ts +111 -0
- package/src/cli-tool/program.ts +97 -0
- package/src/cli-tool/prompt.ts +28 -0
- package/src/cli-tool/run-create.ts +138 -0
- package/src/cli.ts +0 -2
- package/src/config/bootstrap.ts +27 -18
- package/src/config/file.test.ts +1 -1
- package/src/config/resolve.test.ts +167 -0
- package/src/config/resolve.ts +52 -8
- package/src/configure/configure.test.ts +148 -0
- package/src/configure/index.ts +284 -0
- package/src/configure/prompt.ts +40 -0
- package/src/docs/api-guide.test.ts +4 -5
- package/src/docs/builtin.ts +3 -5
- package/src/docs/docs.test.ts +6 -5
- package/src/docs/mcp-guide.ts +11 -12
- package/src/index.ts +5 -12
- package/src/install/binary-placement.test.ts +101 -0
- package/src/install/binary-placement.ts +47 -0
- package/src/install/install-validate.test.ts +5 -5
- package/src/install/normalize-uninstall.ts +11 -0
- package/src/install/normalize.ts +4 -19
- package/src/install/opts.ts +17 -0
- package/src/install/paths.ts +0 -22
- package/src/install/plan.ts +14 -6
- package/src/install/shell.ts +0 -14
- package/src/install/status.test.ts +6 -6
- package/src/install/status.ts +0 -6
- package/src/install/target-effective.ts +8 -10
- package/src/install/target-scope.ts +26 -36
- package/src/install/target-types.ts +0 -16
- package/src/install/targets/app.ts +19 -28
- package/src/install/targets/configure.ts +6 -2
- package/src/install/targets/index.ts +0 -3
- package/src/install/targets.test.ts +26 -44
- package/src/invoke.test.ts +1 -1
- package/src/mcp/env.test.ts +92 -0
- package/src/mcp/env.ts +15 -14
- package/src/mcp/tools.ts +1 -1
- package/src/mcp.integration.test.ts +4 -4
- package/src/parse.test.ts +13 -14
- package/src/prompt.ts +10 -0
- package/src/schema.ts +1 -1
- package/src/skill/hint.ts +2 -2
- package/src/types.ts +48 -22
- package/src/validate.ts +22 -28
- package/docs/install.md +0 -290
- package/docs/templates/cursor/rules/cli-program.mdc +0 -31
- package/examples/config-app/main.ts +0 -20
- package/examples/config-app/program.ts +0 -78
- package/examples/config-app/schema.ts +0 -37
- package/examples/config-app/types.ts +0 -19
- package/examples/consumer-app/README.md +0 -56
- package/examples/consumer-app/src/main.ts +0 -15
- package/examples/consumer-app/src/program.ts +0 -108
- package/src/builtins/install.ts +0 -136
- package/src/install/app.ts +0 -94
- package/src/install/bootstrap.ts +0 -22
- package/src/install/completions.ts +0 -56
- package/src/install/index.ts +0 -415
- package/src/install/install.test.ts +0 -333
- package/src/install/targets/completions.ts +0 -133
- package/src/install/update.test.ts +0 -123
- package/src/install/update.ts +0 -54
- /package/examples/{consumer-app → full-example}/schemas/configSchemas.ts +0 -0
- /package/examples/{consumer-app → full-example}/schemas/outputSchemas.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.test.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/naming.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen.ts +0 -0
- /package/examples/{consumer-app → full-example}/tsconfig.json +0 -0
package/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
+
/** Resolved absolute path to the app JSON config file (`~/.local/lib/<key>/config.json`). */
|
|
4
|
+
export declare function resolveAppConfigPath(program: CliProgram): string;
|
|
5
|
+
/** Human-readable config path for error messages (`~/…` when under home). */
|
|
6
|
+
export declare function displayAppConfigPath(program: CliProgram): string;
|
|
3
7
|
export type ResolvedConfig = Record<string, unknown>;
|
|
4
8
|
declare class EmptyAppConfigSnapshot {
|
|
5
9
|
private readonly program;
|
|
@@ -245,21 +249,24 @@ export interface CliMcpToolConfig {
|
|
|
245
249
|
*/
|
|
246
250
|
outputSchema?: Record<string, unknown>;
|
|
247
251
|
}
|
|
252
|
+
/** Context passed to {@link CliAppConfigEntry.resolve} for one config key. */
|
|
253
|
+
export interface CliAppConfigResolveContext {
|
|
254
|
+
/** Schema key being resolved. */
|
|
255
|
+
key: string;
|
|
256
|
+
/** Entry metadata for this key. */
|
|
257
|
+
entry: CliAppConfigEntry;
|
|
258
|
+
/** Program root (read-only). */
|
|
259
|
+
program: CliProgram;
|
|
260
|
+
/** Raw value from the config file, if any. */
|
|
261
|
+
fileValue: unknown;
|
|
262
|
+
/** Non-empty host env string when `entry.env` is set; otherwise `undefined`. */
|
|
263
|
+
envValue: string | undefined;
|
|
264
|
+
}
|
|
248
265
|
/**
|
|
249
|
-
*
|
|
266
|
+
* Optional fallback resolver for one config key (e.g. `gh auth token` when `GH_TOKEN` is unset).
|
|
267
|
+
* Return `undefined` to continue resolution (env, then default).
|
|
250
268
|
*/
|
|
251
|
-
export
|
|
252
|
-
/** Path to an executable binary to copy into the install location. */
|
|
253
|
-
path: string;
|
|
254
|
-
/** Release version of `path` (used for already-current checks and success messages). */
|
|
255
|
-
version?: string;
|
|
256
|
-
/** Called after reinstall completes (e.g. remove a temp download directory). */
|
|
257
|
-
cleanup?: () => void | Promise<void>;
|
|
258
|
-
}
|
|
259
|
-
/** Fetches the latest release binary for `install --update`. */
|
|
260
|
-
export type CliUpdateGetLatest = (ctx: {
|
|
261
|
-
version: string;
|
|
262
|
-
}) => Promise<CliUpdateArtifact>;
|
|
269
|
+
export type CliAppConfigResolveFn = (ctx: CliAppConfigResolveContext) => unknown;
|
|
263
270
|
/**
|
|
264
271
|
* Metadata overlay for one key in {@link CliAppConfig.entries}.
|
|
265
272
|
* Types and validation come from {@link CliAppConfig.jsonSchema} when set; otherwise all values are strings.
|
|
@@ -280,6 +287,11 @@ export interface CliAppConfigEntry {
|
|
|
280
287
|
sensitive?: boolean;
|
|
281
288
|
/** When set: non-empty `process.env[env]` overrides file; value exported after resolve. */
|
|
282
289
|
env?: string;
|
|
290
|
+
/**
|
|
291
|
+
* Optional fallback after file when env is empty.
|
|
292
|
+
* Return `undefined` to fall back to `env` (if set) and schema defaults.
|
|
293
|
+
*/
|
|
294
|
+
resolve?: CliAppConfigResolveFn;
|
|
283
295
|
}
|
|
284
296
|
/**
|
|
285
297
|
* App configuration block on the program root ({@link CliProgram.appConfig}).
|
|
@@ -295,23 +307,23 @@ export interface CliAppConfig {
|
|
|
295
307
|
/** Per-key metadata; keys must match `jsonSchema.properties` when `jsonSchema` is set. */
|
|
296
308
|
entries: Record<string, CliAppConfigEntry>;
|
|
297
309
|
}
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
/** Opt-out for the `completion` built-in (default: enabled). */
|
|
311
|
+
export interface CliCompletionConfig {
|
|
312
|
+
/** When `false`, hide/disable `completion` (default: enabled). */
|
|
313
|
+
enabled?: boolean;
|
|
314
|
+
}
|
|
315
|
+
export interface CliConfigureConfig {
|
|
316
|
+
/** When `false`, hide/disable `configure` (default: enabled). */
|
|
300
317
|
enabled?: boolean;
|
|
301
318
|
/**
|
|
302
|
-
* Default agent integration for
|
|
303
|
-
* - `'mcp'` when `mcpServer.enabled` (default): MCP targets in
|
|
304
|
-
* - `'skill'` when MCP is off (default): skill targets in
|
|
305
|
-
* - `'both'`:
|
|
319
|
+
* Default agent integration for sync (`configure --sync`).
|
|
320
|
+
* - `'mcp'` when `mcpServer.enabled` (default): MCP targets in sync; paired skills excluded.
|
|
321
|
+
* - `'skill'` when MCP is off (default): skill targets in sync; paired MCP excluded.
|
|
322
|
+
* - `'both'`: sync MCP and skill for the same host when both are available.
|
|
306
323
|
*/
|
|
307
324
|
agentIntegration?: InstallAgentIntegration;
|
|
308
|
-
/** Per-artifact gates for
|
|
309
|
-
targets?:
|
|
310
|
-
/**
|
|
311
|
-
* When set, enables `install --update` on the program root.
|
|
312
|
-
* Should download or locate the latest release binary and return its path.
|
|
313
|
-
*/
|
|
314
|
-
updateGetLatest?: CliUpdateGetLatest;
|
|
325
|
+
/** Per-artifact gates for configure sync and interactive wizard. See {@link resolveEffectiveInstallTargets}. */
|
|
326
|
+
targets?: CliConfigureTargets;
|
|
315
327
|
}
|
|
316
328
|
/** Agent integration mode for install — MCP vs shell skill per host. */
|
|
317
329
|
export type InstallAgentIntegration = "mcp" | "skill" | "both";
|
|
@@ -319,16 +331,16 @@ export type InstallAgentIntegration = "mcp" | "skill" | "both";
|
|
|
319
331
|
export type InstallTargetSpec = boolean | {
|
|
320
332
|
/** When false, artifact is never installed (even with scoped CLI flags). Default true. */
|
|
321
333
|
enabled?: boolean;
|
|
322
|
-
/** When true, included in
|
|
334
|
+
/** When true, included in `configure --sync`. Default varies by key. */
|
|
323
335
|
includedInAll?: boolean;
|
|
324
336
|
};
|
|
325
337
|
export interface ResolvedInstallTarget {
|
|
326
338
|
enabled: boolean;
|
|
327
339
|
includedInAll: boolean;
|
|
328
340
|
}
|
|
329
|
-
/** Per-artifact gates for
|
|
330
|
-
export interface
|
|
331
|
-
/**
|
|
341
|
+
/** Per-artifact gates for configure. See {@link resolveEffectiveInstallTargets}. */
|
|
342
|
+
export interface CliConfigureTargets {
|
|
343
|
+
/** App binary status only (Homebrew PATH); no self-install. */
|
|
332
344
|
app?: InstallTargetSpec;
|
|
333
345
|
/** ChatGPT desktop MCP. Default false. */
|
|
334
346
|
chatgptMcp?: InstallTargetSpec;
|
|
@@ -342,9 +354,7 @@ export interface CliInstallTargets {
|
|
|
342
354
|
codexMcp?: InstallTargetSpec;
|
|
343
355
|
/** Codex skill. Default false. */
|
|
344
356
|
codexSkill?: InstallTargetSpec;
|
|
345
|
-
/**
|
|
346
|
-
completions?: InstallTargetSpec;
|
|
347
|
-
/** App config: wizard on install, file removal on uninstall. Default includedInAll true. */
|
|
357
|
+
/** App config: interactive wizard step in `configure`. Default not in sync. */
|
|
348
358
|
configure?: InstallTargetSpec;
|
|
349
359
|
/** Cursor MCP. Default false. */
|
|
350
360
|
cursorMcp?: InstallTargetSpec;
|
|
@@ -442,8 +452,10 @@ export type CliProgram = CliNode & {
|
|
|
442
452
|
appConfig?: CliAppConfig;
|
|
443
453
|
/** When set with `enabled: true`, enables the `mcp` built-in subcommand. */
|
|
444
454
|
mcpServer?: CliMcpServerConfig;
|
|
445
|
-
/** Opt-out and defaults for `
|
|
446
|
-
|
|
455
|
+
/** Opt-out and defaults for `configure`. */
|
|
456
|
+
configure?: CliConfigureConfig;
|
|
457
|
+
/** Opt-out for shell completion generation (`completion bash|zsh|fish`). */
|
|
458
|
+
completion?: CliCompletionConfig;
|
|
447
459
|
/** When set with `enabled: true`, enables the `docs` built-in command group. */
|
|
448
460
|
docs?: CliDocsConfig;
|
|
449
461
|
};
|
|
@@ -461,11 +473,10 @@ export declare class CliSchemaValidationError extends Error {
|
|
|
461
473
|
}
|
|
462
474
|
/** Platform builtins derived from program config and runtime. */
|
|
463
475
|
export interface CliCapabilities {
|
|
464
|
-
completion:
|
|
476
|
+
completion: boolean;
|
|
465
477
|
mcp: boolean;
|
|
466
|
-
|
|
478
|
+
configure: boolean;
|
|
467
479
|
docs: boolean;
|
|
468
|
-
update: boolean;
|
|
469
480
|
configCommands: boolean;
|
|
470
481
|
}
|
|
471
482
|
/** JSON-safe command node (no handlers). */
|
|
@@ -547,49 +558,6 @@ export declare function shouldRunHeadlessWithYes(ctx: HeadlessContext, opts: {
|
|
|
547
558
|
export declare function requireYesInNonTty(yes: boolean, hint: string, dryRun?: boolean, interactive?: boolean): void;
|
|
548
559
|
/** Prefixes a success message when running in dry-run mode. */
|
|
549
560
|
export declare function formatDryRunMessage(message: string, dryRun: boolean): string;
|
|
550
|
-
/** Config for {@link ghReleaseUpdateGetLatest}. */
|
|
551
|
-
export interface GhReleaseUpdateConfig {
|
|
552
|
-
/** GitHub `owner/repo` slug. */
|
|
553
|
-
repo: string;
|
|
554
|
-
/** Release asset filename (e.g. `myapp`). */
|
|
555
|
-
asset: string;
|
|
556
|
-
/** Temp directory name prefix for downloads. */
|
|
557
|
-
tempPrefix: string;
|
|
558
|
-
/** Path to the on-disk version-check cache JSON file. */
|
|
559
|
-
cachePath: string;
|
|
560
|
-
/** Optional hint when `gh auth` fails or no releases exist. */
|
|
561
|
-
repoEnvHint?: string;
|
|
562
|
-
}
|
|
563
|
-
/** Config for {@link createGhVersionCheck}. */
|
|
564
|
-
export interface GhVersionCheckConfig {
|
|
565
|
-
/** Installed semver string. */
|
|
566
|
-
currentVersion: string;
|
|
567
|
-
/** CLI command name for update notices (e.g. `qa`). */
|
|
568
|
-
commandName: string;
|
|
569
|
-
/** Path to the on-disk version-check cache JSON file. */
|
|
570
|
-
cachePath: string;
|
|
571
|
-
/** Cache TTL in milliseconds (default 24h). */
|
|
572
|
-
ttlMs?: number;
|
|
573
|
-
/** When true, skip background refresh (e.g. test subprocess). */
|
|
574
|
-
skipRefresh?: () => boolean;
|
|
575
|
-
/** When true, skip refresh because `gh` is unavailable. */
|
|
576
|
-
ghAvailable?: () => boolean;
|
|
577
|
-
/** Fetches latest release version via `gh`. */
|
|
578
|
-
fetchLatest: () => Promise<string>;
|
|
579
|
-
}
|
|
580
|
-
/** Returns whether the installed version matches the latest release. */
|
|
581
|
-
export declare function isAlreadyCurrent(current: string, latest: string): boolean;
|
|
582
|
-
/** Strips a leading `v` from a release tag. */
|
|
583
|
-
export declare function parseReleaseTag(tag: string): string;
|
|
584
|
-
/** Builds a `CliUpdateGetLatest` hook that downloads a release via `gh`. */
|
|
585
|
-
export declare function ghReleaseUpdateGetLatest(config: GhReleaseUpdateConfig): CliUpdateGetLatest;
|
|
586
|
-
/** Version-check cache helpers for summary notices and background refresh. */
|
|
587
|
-
export declare function createGhVersionCheck(config: GhVersionCheckConfig): {
|
|
588
|
-
getUpdateNotice: () => string | null;
|
|
589
|
-
refreshIfStale: () => void;
|
|
590
|
-
};
|
|
591
|
-
/** Shared `gh release view` fetcher for hooks and version-check refresh. */
|
|
592
|
-
export declare function createGhFetchLatest(config: Pick<GhReleaseUpdateConfig, "repo" | "repoEnvHint">): () => Promise<string>;
|
|
593
561
|
/** Resolved paths for `mcp bundle`. */
|
|
594
562
|
export interface McpBundlePaths {
|
|
595
563
|
binaryPath: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "argsbarg",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"bun": ">=1.3"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"module": "./src/index.ts",
|
|
13
13
|
"types": "./index.d.ts",
|
|
14
14
|
"bin": {
|
|
15
|
-
"argsbarg": "src/
|
|
15
|
+
"argsbarg": "src/cli-tool/main.ts"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
3
|
+
import { cliBuiltinDocsGroup } from "../docs/builtin.ts";
|
|
2
4
|
import { ParseKind, parse, postParseValidate } from "../parse.ts";
|
|
3
5
|
import type { CliProgram } from "../types.ts";
|
|
6
|
+
import { cliBuiltinConfigureCommand, configureBuiltinOptions } from "./configure.ts";
|
|
7
|
+
import { configureCommandDescription, configureSyncOptionDescription } from "./configure-copy.ts";
|
|
4
8
|
import { exportPresentationBuiltins } from "./export.ts";
|
|
5
9
|
import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
|
|
6
|
-
import { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
|
|
7
10
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
8
11
|
import { cliParseRoot, cliPresentationRoot } from "./presentation.ts";
|
|
9
12
|
|
|
@@ -21,55 +24,61 @@ const fixture: CliProgram = {
|
|
|
21
24
|
],
|
|
22
25
|
};
|
|
23
26
|
|
|
27
|
+
const noMcp: CliProgram = {
|
|
28
|
+
key: "skillonly",
|
|
29
|
+
version: "0.0.0",
|
|
30
|
+
description: "Skills only.",
|
|
31
|
+
commands: [{ key: "ping", description: "Ping.", handler: () => {} }],
|
|
32
|
+
};
|
|
33
|
+
|
|
24
34
|
describe("builtins help copy", () => {
|
|
25
|
-
test("
|
|
26
|
-
const
|
|
27
|
-
expect(
|
|
28
|
-
expect(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
expect(names).toContain("
|
|
32
|
-
expect(names.
|
|
33
|
-
expect(names
|
|
34
|
-
|
|
35
|
-
expect(names.indexOf("uninstall")).toBeLessThan(names.indexOf("from"));
|
|
36
|
-
expect(names.indexOf("from")).toBeLessThan(names.indexOf("yes"));
|
|
37
|
-
const yesOpt = installBuiltinOptions(fixture).find((o) => o.name === "yes");
|
|
35
|
+
test("configure command includes Homebrew-oriented description", () => {
|
|
36
|
+
const configure = cliBuiltinConfigureCommand(fixture);
|
|
37
|
+
expect(configure.description).toContain("agent skills");
|
|
38
|
+
expect(configure.description).toContain("MCP config");
|
|
39
|
+
expect(configure.notes).toContain("brew install");
|
|
40
|
+
const names = configureBuiltinOptions(fixture).map((o) => o.name);
|
|
41
|
+
expect(names).toContain("sync");
|
|
42
|
+
expect(names).toContain("remove-all");
|
|
43
|
+
expect(names).toContain("status");
|
|
44
|
+
const yesOpt = configureBuiltinOptions(fixture).find((o) => o.name === "yes");
|
|
38
45
|
expect(yesOpt?.shortName).toBe("y");
|
|
39
46
|
});
|
|
40
47
|
|
|
41
|
-
test("
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
test("install omits --mcp option when mcpServer unset", () => {
|
|
51
|
-
const noMcp: CliProgram = { key: "x", version: "0.0.0", description: "x", handler: () => {} };
|
|
52
|
-
const names = installBuiltinOptions(noMcp).map((o) => o.name);
|
|
53
|
-
expect(names).not.toContain("mcp");
|
|
48
|
+
test("configure copy omits MCP when mcpServer unset", () => {
|
|
49
|
+
const caps = resolveCapabilities(noMcp);
|
|
50
|
+
expect(configureCommandDescription(noMcp, caps)).toBe(
|
|
51
|
+
"Set up agent skills for this app (binary via Homebrew).",
|
|
52
|
+
);
|
|
53
|
+
expect(configureCommandDescription(noMcp, caps)).not.toContain("MCP");
|
|
54
|
+
expect(configureSyncOptionDescription(noMcp, caps)).not.toContain("MCP");
|
|
55
|
+
const configure = cliBuiltinConfigureCommand(noMcp);
|
|
56
|
+
expect(configure.description).not.toContain("MCP");
|
|
54
57
|
});
|
|
55
58
|
|
|
56
|
-
test("
|
|
57
|
-
const
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
61
|
-
});
|
|
59
|
+
test("configure notes mention brew upgrade and interactive configure", () => {
|
|
60
|
+
const configure = cliBuiltinConfigureCommand(fixture);
|
|
61
|
+
expect(configure.notes).toContain("brew upgrade");
|
|
62
|
+
expect(configure.notes).toContain("configure --sync --yes");
|
|
63
|
+
expect(configure.notes).toContain(`${fixture.key} configure`);
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
const withUpdate: CliProgram = {
|
|
65
|
+
const withConfig: CliProgram = {
|
|
65
66
|
...fixture,
|
|
66
|
-
|
|
67
|
+
appConfig: {
|
|
68
|
+
entries: { token: { description: "Token.", env: "TOKEN" } },
|
|
69
|
+
},
|
|
67
70
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
expect(configureBuiltinOptions(withConfig).map((o) => o.name)).toContain("remove-config");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("configure -y parses as --yes", () => {
|
|
75
|
+
const root = cliParseRoot(fixture);
|
|
76
|
+
const pr = postParseValidate(root, parse(root, ["configure", "-y", "--sync"]));
|
|
77
|
+
expect(pr.kind).toBe(ParseKind.Ok);
|
|
78
|
+
if (pr.kind === ParseKind.Ok) {
|
|
79
|
+
expect(pr.opts.yes).toBe("1");
|
|
80
|
+
expect(pr.opts.sync).toBe("1");
|
|
81
|
+
}
|
|
73
82
|
});
|
|
74
83
|
|
|
75
84
|
test("mcp builtin description is user-facing", () => {
|
|
@@ -79,38 +88,31 @@ describe("builtins help copy", () => {
|
|
|
79
88
|
};
|
|
80
89
|
const mcp = cliBuiltinMcpCommand(withDocs);
|
|
81
90
|
expect(mcp.description).toContain("MCP server");
|
|
82
|
-
expect(mcp.notes).toContain("
|
|
91
|
+
expect(mcp.notes).toContain("configure");
|
|
83
92
|
expect(mcp.notes).toContain("docs mcp");
|
|
84
93
|
});
|
|
85
94
|
});
|
|
86
95
|
|
|
87
96
|
describe("presentation root", () => {
|
|
88
|
-
test("includes mcp and
|
|
97
|
+
test("includes mcp and configure when enabled", () => {
|
|
89
98
|
const root = cliPresentationRoot(fixture);
|
|
90
99
|
const keys = root.commands?.map((c) => c.key) ?? [];
|
|
91
100
|
expect(keys).toContain("mcp");
|
|
92
|
-
expect(keys).toContain("
|
|
101
|
+
expect(keys).toContain("configure");
|
|
102
|
+
expect(keys).not.toContain("completion");
|
|
103
|
+
expect(keys).not.toContain("install");
|
|
93
104
|
});
|
|
94
105
|
|
|
95
|
-
test("omits
|
|
96
|
-
const disabled: CliProgram = { ...fixture,
|
|
106
|
+
test("omits configure when configure.enabled is false", () => {
|
|
107
|
+
const disabled: CliProgram = { ...fixture, configure: { enabled: false } };
|
|
97
108
|
const root = cliPresentationRoot(disabled);
|
|
98
|
-
expect(root.commands?.map((c) => c.key)).not.toContain("
|
|
109
|
+
expect(root.commands?.map((c) => c.key)).not.toContain("configure");
|
|
99
110
|
});
|
|
111
|
+
|
|
100
112
|
test("includes version builtin", () => {
|
|
101
113
|
const root = cliPresentationRoot(fixture);
|
|
102
114
|
expect(root.commands?.map((c) => c.key)).toContain("version");
|
|
103
115
|
});
|
|
104
|
-
|
|
105
|
-
test("root notes include agent hint when docs enabled", () => {
|
|
106
|
-
const withDocs: CliProgram = {
|
|
107
|
-
...fixture,
|
|
108
|
-
docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
|
|
109
|
-
};
|
|
110
|
-
const root = cliPresentationRoot(withDocs);
|
|
111
|
-
expect(root.notes).toContain("For AI agents: `myapp docs skill`.");
|
|
112
|
-
expect(root.notes).not.toContain("install --skill");
|
|
113
|
-
});
|
|
114
116
|
});
|
|
115
117
|
|
|
116
118
|
describe("completion emitters", () => {
|
|
@@ -119,14 +121,14 @@ describe("completion emitters", () => {
|
|
|
119
121
|
const fish = completionFishScript(schema);
|
|
120
122
|
expect(fish).toContain("complete -c myapp");
|
|
121
123
|
expect(fish).toContain("hello");
|
|
122
|
-
expect(fish).toContain("
|
|
124
|
+
expect(fish).toContain("configure");
|
|
123
125
|
});
|
|
124
126
|
|
|
125
|
-
test("bash script includes
|
|
127
|
+
test("bash script includes configure flags", () => {
|
|
126
128
|
const schema = cliPresentationRoot(fixture);
|
|
127
129
|
const bash = completionBashScript(schema);
|
|
128
|
-
expect(bash).toContain("
|
|
129
|
-
expect(bash).toContain("
|
|
130
|
+
expect(bash).toContain("hello");
|
|
131
|
+
expect(bash).toContain("--sync");
|
|
130
132
|
});
|
|
131
133
|
|
|
132
134
|
test("zsh script registers compdef", () => {
|
|
@@ -154,11 +156,29 @@ describe("schema export builtins", () => {
|
|
|
154
156
|
};
|
|
155
157
|
const builtins = exportPresentationBuiltins(withConfig);
|
|
156
158
|
expect(builtins.map((b) => b.key)).toContain("config");
|
|
159
|
+
expect(builtins.map((b) => b.key)).toContain("configure");
|
|
157
160
|
});
|
|
158
161
|
|
|
159
|
-
test("exportPresentationBuiltins
|
|
162
|
+
test("exportPresentationBuiltins omits hidden completion", () => {
|
|
160
163
|
const builtins = exportPresentationBuiltins(fixture);
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
expect(builtins.map((b) => b.key)).not.toContain("completion");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("docs skill topic copy", () => {
|
|
169
|
+
test("mentions configure when configure is enabled", () => {
|
|
170
|
+
const withDocs: CliProgram = {
|
|
171
|
+
...noMcp,
|
|
172
|
+
docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
|
|
173
|
+
};
|
|
174
|
+
const skill = cliBuiltinDocsGroup(withDocs).commands.find((c) => c.key === "skill");
|
|
175
|
+
expect(skill?.description).toContain("configure");
|
|
176
|
+
|
|
177
|
+
const configureOff: CliProgram = {
|
|
178
|
+
...withDocs,
|
|
179
|
+
configure: { enabled: false },
|
|
180
|
+
};
|
|
181
|
+
const skillOff = cliBuiltinDocsGroup(configureOff).commands.find((c) => c.key === "skill");
|
|
182
|
+
expect(skillOff?.description).not.toContain("configure");
|
|
163
183
|
});
|
|
164
184
|
});
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { CliProgram, CliRouter } from "../types.ts";
|
|
1
|
+
import type { CliProgram } from "../types.ts";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Builds the static `completion` / `bash` / `zsh` / `fish` command subtree (merged into the program root at runtime).
|
|
6
5
|
*/
|
|
7
|
-
export function cliBuiltinCompletionGroup(program: CliProgram): CliRouter {
|
|
6
|
+
export function cliBuiltinCompletionGroup(program: CliProgram): import("../types.ts").CliRouter {
|
|
8
7
|
const appName = program.key;
|
|
9
|
-
const
|
|
10
|
-
const router: CliRouter = {
|
|
8
|
+
const router: import("../types.ts").CliRouter = {
|
|
11
9
|
key: "completion",
|
|
10
|
+
hidden: true,
|
|
12
11
|
description: "Generate the autocompletion script for shells.",
|
|
13
12
|
commands: [
|
|
14
13
|
{
|
|
15
14
|
key: "bash",
|
|
16
15
|
description: "Print a bash tab-completion script.",
|
|
17
16
|
notes:
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
"Homebrew installs completions during `brew install` via generate_completions_from_executable.\n\n" +
|
|
18
|
+
"Ensure your shell loads Homebrew completions:\n" +
|
|
19
|
+
" https://docs.brew.sh/Shell-Completion\n\n" +
|
|
21
20
|
"Try this session only:\n\n" +
|
|
22
21
|
` source <(${appName} completion bash)`,
|
|
23
22
|
handler: () => {},
|
|
@@ -26,9 +25,9 @@ export function cliBuiltinCompletionGroup(program: CliProgram): CliRouter {
|
|
|
26
25
|
key: "zsh",
|
|
27
26
|
description: "Print a zsh tab-completion script.",
|
|
28
27
|
notes:
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"
|
|
28
|
+
"Homebrew installs completions to $(brew --prefix)/share/zsh/site-functions.\n\n" +
|
|
29
|
+
"Ensure brew shellenv + compinit are configured:\n" +
|
|
30
|
+
" https://docs.brew.sh/Shell-Completion\n\n" +
|
|
32
31
|
"Try this session only:\n\n" +
|
|
33
32
|
` eval "$(${appName} completion zsh)"`,
|
|
34
33
|
handler: () => {},
|
|
@@ -37,15 +36,16 @@ export function cliBuiltinCompletionGroup(program: CliProgram): CliRouter {
|
|
|
37
36
|
key: "fish",
|
|
38
37
|
description: "Print a fish tab-completion script.",
|
|
39
38
|
notes:
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"
|
|
39
|
+
"Homebrew installs completions to $(brew --prefix)/share/fish/vendor_completions.d.\n\n" +
|
|
40
|
+
"See: https://docs.brew.sh/Shell-Completion\n\n" +
|
|
41
|
+
"Try this session only:\n\n" +
|
|
42
|
+
` ${appName} completion fish | source`,
|
|
43
43
|
handler: () => {},
|
|
44
44
|
},
|
|
45
45
|
],
|
|
46
46
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
router.notes =
|
|
48
|
+
"Completions are installed by Homebrew during formula install.\n\n" +
|
|
49
|
+
"See: https://docs.brew.sh/Shell-Completion";
|
|
50
50
|
return router;
|
|
51
51
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Capability-aware labels for configure builtin and docs copy. */
|
|
2
|
+
|
|
3
|
+
import type { CliCapabilities } from "../capabilities.ts";
|
|
4
|
+
import type { CliProgram } from "../types.ts";
|
|
5
|
+
|
|
6
|
+
type Kind = "skills" | "mcp" | "config";
|
|
7
|
+
|
|
8
|
+
const LABEL: Record<Kind, { prose: string; short: string }> = {
|
|
9
|
+
skills: { prose: "agent skills", short: "skills" },
|
|
10
|
+
mcp: { prose: "MCP config", short: "MCP" },
|
|
11
|
+
config: { prose: "app config", short: "config" },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function enabledKinds(program: CliProgram, caps: CliCapabilities): Kind[] {
|
|
15
|
+
const kinds: Kind[] = ["skills"];
|
|
16
|
+
if (caps.mcp) kinds.push("mcp");
|
|
17
|
+
if (program.appConfig) kinds.push("config");
|
|
18
|
+
return kinds;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function joinEnglish(items: string[]): string {
|
|
22
|
+
if (items.length === 0) return "agent artifacts";
|
|
23
|
+
if (items.length === 1) return items[0]!;
|
|
24
|
+
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
25
|
+
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function prose(program: CliProgram, caps: CliCapabilities): string {
|
|
29
|
+
return joinEnglish(enabledKinds(program, caps).map((k) => LABEL[k].prose));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function short(program: CliProgram, caps: CliCapabilities): string {
|
|
33
|
+
return joinEnglish(enabledKinds(program, caps).map((k) => LABEL[k].short));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function configureCommandDescription(program: CliProgram, caps: CliCapabilities): string {
|
|
37
|
+
return `Set up ${prose(program, caps)} for this app (binary via Homebrew).`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function configureSyncOptionDescription(program: CliProgram, caps: CliCapabilities): string {
|
|
41
|
+
return `Refresh installed ${short(program, caps)}. Used by Homebrew post_install.`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function docsSkillTopicDescription(program: CliProgram, caps: CliCapabilities): string {
|
|
45
|
+
if (caps.configure) {
|
|
46
|
+
return "Print a reference agent SKILL; run `configure` to install an optimized copy.";
|
|
47
|
+
}
|
|
48
|
+
return "Print a reference agent SKILL for AI agents.";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function configureCommandNotes(program: CliProgram, caps: CliCapabilities): string {
|
|
52
|
+
const app = program.key;
|
|
53
|
+
const lines = [
|
|
54
|
+
"Install the binary via Homebrew (tap-from-repo), then set up agent artifacts:",
|
|
55
|
+
` brew tap <org>/<repo>`,
|
|
56
|
+
` brew install <tap>/${app}`,
|
|
57
|
+
"",
|
|
58
|
+
"Homebrew post_install runs:",
|
|
59
|
+
` ${app} configure --sync --yes`,
|
|
60
|
+
"",
|
|
61
|
+
"Interactive setup (per target):",
|
|
62
|
+
` ${app} configure`,
|
|
63
|
+
"",
|
|
64
|
+
"Upgrade:",
|
|
65
|
+
` brew upgrade ${app}`,
|
|
66
|
+
"",
|
|
67
|
+
"Shell completions are installed by Homebrew during brew install.",
|
|
68
|
+
"See: https://docs.brew.sh/Shell-Completion",
|
|
69
|
+
"",
|
|
70
|
+
"See what is installed:",
|
|
71
|
+
` ${app} configure --status`,
|
|
72
|
+
"",
|
|
73
|
+
"Remove agent artifacts before brew uninstall:",
|
|
74
|
+
` ${app} configure --remove-all --yes`,
|
|
75
|
+
` brew uninstall <tap>/${app}`,
|
|
76
|
+
"",
|
|
77
|
+
];
|
|
78
|
+
if (program.appConfig) {
|
|
79
|
+
lines.push("Remove app config only:", ` ${app} configure --remove-config --yes`, "");
|
|
80
|
+
}
|
|
81
|
+
lines.push(
|
|
82
|
+
"Use --dry to preview changes without writing files.",
|
|
83
|
+
"Use --json for machine-readable output.",
|
|
84
|
+
);
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram } from "../types.ts";
|
|
3
|
+
import {
|
|
4
|
+
configureCommandDescription,
|
|
5
|
+
configureCommandNotes,
|
|
6
|
+
configureSyncOptionDescription,
|
|
7
|
+
} from "./configure-copy.ts";
|
|
8
|
+
|
|
9
|
+
/** Configure command options. */
|
|
10
|
+
export function configureBuiltinOptions(root: CliProgram): CliOption[] {
|
|
11
|
+
const caps = resolveCapabilities(root);
|
|
12
|
+
const opts: CliOption[] = [
|
|
13
|
+
{
|
|
14
|
+
name: "sync",
|
|
15
|
+
description: configureSyncOptionDescription(root, caps),
|
|
16
|
+
kind: CliOptionKind.Presence,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "remove-all",
|
|
20
|
+
description: "Remove all detected agent artifacts (skills and MCP).",
|
|
21
|
+
kind: CliOptionKind.Presence,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
if (root.appConfig) {
|
|
26
|
+
opts.push({
|
|
27
|
+
name: "remove-config",
|
|
28
|
+
description: "Remove the app config file only.",
|
|
29
|
+
kind: CliOptionKind.Presence,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
opts.push(
|
|
34
|
+
{
|
|
35
|
+
name: "status",
|
|
36
|
+
description: "Print what is currently installed (read-only).",
|
|
37
|
+
kind: CliOptionKind.Presence,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "yes",
|
|
41
|
+
description: "Skip confirmation (required for --sync, --remove-all, --remove-config).",
|
|
42
|
+
kind: CliOptionKind.Presence,
|
|
43
|
+
shortName: "y",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "dry",
|
|
47
|
+
description: "Show what would change without writing files.",
|
|
48
|
+
kind: CliOptionKind.Presence,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "json",
|
|
52
|
+
description: "Print changed paths or status JSON on stdout.",
|
|
53
|
+
kind: CliOptionKind.Presence,
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return opts;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Builds the `configure` built-in command. */
|
|
61
|
+
export function cliBuiltinConfigureCommand(root: CliProgram): CliLeaf {
|
|
62
|
+
const caps = resolveCapabilities(root);
|
|
63
|
+
return {
|
|
64
|
+
key: "configure",
|
|
65
|
+
description: configureCommandDescription(root, caps),
|
|
66
|
+
options: configureBuiltinOptions(root),
|
|
67
|
+
notes: configureCommandNotes(root, caps),
|
|
68
|
+
handler: () => {},
|
|
69
|
+
};
|
|
70
|
+
}
|