argsbarg 6.2.2 → 6.3.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 +22 -1
- package/README.md +2 -2
- package/docs/README.md +2 -2
- package/docs/ai-skills.md +7 -7
- package/docs/bundled-docs.md +2 -2
- package/docs/cli-program.md +4 -4
- package/docs/configure.md +36 -14
- package/docs/developing.md +7 -3
- package/docs/distribution-homebrew.md +2 -2
- package/docs/mcp.md +3 -3
- package/examples/full-example/.cursor/hooks/run-tests-on-stop.ts +56 -0
- package/examples/full-example/.cursor/hooks.json +12 -0
- package/examples/full-example/Formula/full-example.rb +1 -1
- package/examples/full-example/docs/cli-schema.json +6 -6
- package/examples/full-example/docs/cli.md +6 -6
- package/examples/full-example/docs/mcp.md +2 -2
- package/examples/full-example/docs/skill.md +1 -1
- package/examples/full-example/justfile +10 -14
- package/examples/full-example/scripts/formula-shared.ts +1 -1
- package/examples/full-example-json/.cursor/hooks/run-tests-on-stop.ts +56 -0
- package/examples/full-example-json/.cursor/hooks.json +12 -0
- package/examples/full-example-json/Formula/full-example-json.rb +1 -1
- package/examples/full-example-json/docs/cli-schema.json +27 -27
- package/examples/full-example-json/docs/cli.md +27 -27
- package/examples/full-example-json/docs/mcp.md +2 -2
- package/examples/full-example-json/docs/skill.md +1 -1
- package/examples/full-example-json/justfile +10 -14
- package/examples/full-example-json/scripts/formula-shared.ts +1 -1
- package/index.d.ts +25 -4
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +7 -7
- package/src/builtins/config.test.ts +16 -16
- package/src/builtins/configure-copy.ts +2 -2
- package/src/builtins/configure.ts +4 -4
- package/src/config/context.test.ts +12 -12
- package/src/config/file.test.ts +4 -4
- package/src/configure/artifacts/binary-placement.test.ts +4 -4
- package/src/configure/artifacts/mcp-codex.test.ts +5 -5
- package/src/configure/artifacts/mcp-openclaw.test.ts +5 -5
- package/src/configure/artifacts/mcp-opencode.test.ts +5 -5
- package/src/configure/artifacts/status.test.ts +5 -5
- package/src/configure/configure.test.ts +90 -17
- package/src/configure/index.ts +45 -13
- package/src/core/parse.test.ts +7 -7
- package/src/core/types.ts +21 -4
- package/src/docs/docs.test.ts +2 -2
- package/src/docs/mcp-guide.ts +2 -2
- package/src/index.ts +2 -0
- package/src/paths/host.test.ts +58 -0
- package/src/paths/host.ts +12 -3
- package/src/skill/generate.ts +1 -1
- package/src/test/integration/config.test.ts +6 -6
package/src/configure/index.ts
CHANGED
|
@@ -4,10 +4,10 @@ Interactive and automated `configure` command orchestration (agent artifacts and
|
|
|
4
4
|
|
|
5
5
|
import { displayAppConfigPath, runConfigure } from "../config/bootstrap.ts";
|
|
6
6
|
import { ensureAppConfigFile } from "../config/file.ts";
|
|
7
|
-
import type { CliProgram } from "../core/types.ts";
|
|
7
|
+
import type { CliProgram, ConfigureHookContext } from "../core/types.ts";
|
|
8
8
|
import { resolveCapabilities } from "../runtime/capabilities.ts";
|
|
9
9
|
import { cliSkillInstall, isAgentSkillActionKind } from "../skill/install.ts";
|
|
10
|
-
import { displayInstallPath, resolveInstallPaths } from "./artifacts/paths.ts";
|
|
10
|
+
import { displayInstallPath, type InstallPaths, resolveInstallPaths } from "./artifacts/paths.ts";
|
|
11
11
|
import { buildInstallPlan, buildUpdatePlan } from "./artifacts/plan.ts";
|
|
12
12
|
import {
|
|
13
13
|
installErr,
|
|
@@ -38,7 +38,7 @@ export function appConfigHasEntries(program: CliProgram): boolean {
|
|
|
38
38
|
|
|
39
39
|
/** Parsed flags for the top-level `configure` built-in. */
|
|
40
40
|
export interface ConfigureOpts {
|
|
41
|
-
|
|
41
|
+
refresh?: boolean;
|
|
42
42
|
removeAll?: boolean;
|
|
43
43
|
removeConfig?: boolean;
|
|
44
44
|
status?: boolean;
|
|
@@ -51,7 +51,7 @@ export interface ConfigureOpts {
|
|
|
51
51
|
export function parseConfigureOpts(raw: Record<string, string>): ConfigureOpts {
|
|
52
52
|
const flag = (name: string) => raw[name] === "1";
|
|
53
53
|
return {
|
|
54
|
-
|
|
54
|
+
refresh: flag("refresh"),
|
|
55
55
|
removeAll: flag("remove-all"),
|
|
56
56
|
removeConfig: flag("remove-config"),
|
|
57
57
|
status: flag("status"),
|
|
@@ -63,15 +63,15 @@ export function parseConfigureOpts(raw: Record<string, string>): ConfigureOpts {
|
|
|
63
63
|
|
|
64
64
|
/** Returns an error message when configure flags are inconsistent; otherwise null. */
|
|
65
65
|
export function validateConfigureOpts(opts: ConfigureOpts): string | null {
|
|
66
|
-
const flags = [opts.
|
|
66
|
+
const flags = [opts.refresh, opts.removeAll, opts.removeConfig, opts.status].filter(Boolean);
|
|
67
67
|
if (flags.length > 1) {
|
|
68
|
-
return "Use only one of --
|
|
68
|
+
return "Use only one of --refresh, --remove-all, --remove-config, or --status.";
|
|
69
69
|
}
|
|
70
70
|
if (opts.json) {
|
|
71
71
|
opts.yes = true;
|
|
72
72
|
}
|
|
73
|
-
if ((opts.
|
|
74
|
-
return "--yes is required with --
|
|
73
|
+
if ((opts.refresh || opts.removeAll || opts.removeConfig) && !opts.yes) {
|
|
74
|
+
return "--yes is required with --refresh, --remove-all, or --remove-config.";
|
|
75
75
|
}
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
@@ -81,7 +81,7 @@ function configureToInstallOpts(opts: ConfigureOpts): InstallOpts {
|
|
|
81
81
|
if (opts.status) {
|
|
82
82
|
return { status: true, yes: opts.yes, dry: opts.dry, json: opts.json };
|
|
83
83
|
}
|
|
84
|
-
if (opts.
|
|
84
|
+
if (opts.refresh) {
|
|
85
85
|
return { reinstall: true, yes: true, dry: opts.dry, json: opts.json };
|
|
86
86
|
}
|
|
87
87
|
if (opts.removeAll) {
|
|
@@ -141,10 +141,10 @@ export function formatConfigureMutationSummary(summary: ConfigureMutationSummary
|
|
|
141
141
|
return n === 1 ? "Removed 1 artifact." : `Removed ${n} artifacts.`;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
if (opts.
|
|
144
|
+
if (opts.refresh) {
|
|
145
145
|
const n = summary.installed;
|
|
146
146
|
if (n === 0) return null;
|
|
147
|
-
return n === 1 ? "
|
|
147
|
+
return n === 1 ? "Refreshed 1 artifact." : `Refreshed ${n} artifacts.`;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
const parts: string[] = [];
|
|
@@ -184,6 +184,29 @@ function runPlanAction(
|
|
|
184
184
|
return action.run();
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
function configureHookContext(root: CliProgram, paths: InstallPaths, dry: boolean): ConfigureHookContext {
|
|
188
|
+
return {
|
|
189
|
+
program: root,
|
|
190
|
+
dry,
|
|
191
|
+
paths: {
|
|
192
|
+
agentsSkillDir: paths.agentsSkillDir,
|
|
193
|
+
agentsMcpPath: paths.agentsMcpPath,
|
|
194
|
+
mcpName: paths.mcpName,
|
|
195
|
+
skillDirName: paths.skillDirName,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function runConfigureLifecycleHook(
|
|
201
|
+
hook: ((ctx: ConfigureHookContext) => void | Promise<void>) | undefined,
|
|
202
|
+
root: CliProgram,
|
|
203
|
+
paths: InstallPaths,
|
|
204
|
+
dry: boolean,
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
if (!hook) return;
|
|
207
|
+
await hook(configureHookContext(root, paths, dry));
|
|
208
|
+
}
|
|
209
|
+
|
|
187
210
|
/** Runs install or uninstall actions and collects changed paths. */
|
|
188
211
|
function executePlan(
|
|
189
212
|
root: CliProgram,
|
|
@@ -253,7 +276,7 @@ function actionsForTarget(
|
|
|
253
276
|
/** Walks enabled targets with per-target prompts (TTY required). */
|
|
254
277
|
async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<ConfigureMutationSummary> {
|
|
255
278
|
if (!process.stdin.isTTY) {
|
|
256
|
-
throw new Error("Interactive configure requires a TTY. Use flags such as --
|
|
279
|
+
throw new Error("Interactive configure requires a TTY. Use flags such as --refresh --yes.");
|
|
257
280
|
}
|
|
258
281
|
|
|
259
282
|
writeInteractiveInstallIntro(root);
|
|
@@ -319,6 +342,10 @@ async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Pro
|
|
|
319
342
|
|
|
320
343
|
const summary = emptyMutationSummary();
|
|
321
344
|
|
|
345
|
+
if (opts.removeAll) {
|
|
346
|
+
await runConfigureLifecycleHook(root.configure?.beforeRemoveAll, root, paths, !!installOpts.dry);
|
|
347
|
+
}
|
|
348
|
+
|
|
322
349
|
if (installOpts.reinstall && !installOpts.uninstall) {
|
|
323
350
|
const bootstrapped = ensureAppConfigFile(root, !!installOpts.dry);
|
|
324
351
|
if (bootstrapped) {
|
|
@@ -345,6 +372,11 @@ async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Pro
|
|
|
345
372
|
}
|
|
346
373
|
|
|
347
374
|
mergeMutationSummary(summary, executePlan(root, actions, installOpts, true));
|
|
375
|
+
|
|
376
|
+
if (opts.refresh) {
|
|
377
|
+
await runConfigureLifecycleHook(root.configure?.afterRefresh, root, paths, !!installOpts.dry);
|
|
378
|
+
}
|
|
379
|
+
|
|
348
380
|
return summary;
|
|
349
381
|
}
|
|
350
382
|
|
|
@@ -357,7 +389,7 @@ export async function cliConfigure(root: CliProgram, rawOpts: Record<string, str
|
|
|
357
389
|
process.exit(1);
|
|
358
390
|
}
|
|
359
391
|
|
|
360
|
-
const isInteractive = !opts.
|
|
392
|
+
const isInteractive = !opts.refresh && !opts.removeAll && !opts.removeConfig && !opts.status;
|
|
361
393
|
|
|
362
394
|
let summary = emptyMutationSummary();
|
|
363
395
|
try {
|
package/src/core/parse.test.ts
CHANGED
|
@@ -1212,20 +1212,20 @@ test("cliSkillInstall writes project agent skill files", () => {
|
|
|
1212
1212
|
}
|
|
1213
1213
|
});
|
|
1214
1214
|
|
|
1215
|
-
/** CliSkillInstall global uses
|
|
1216
|
-
test("cliSkillInstall global uses
|
|
1215
|
+
/** CliSkillInstall global uses TEST_USER_HOME agents skills directory. */
|
|
1216
|
+
test("cliSkillInstall global uses TEST_USER_HOME agents skills directory", () => {
|
|
1217
1217
|
const home = mkdtempSync(join(tmpdir(), "argsbarg-home-"));
|
|
1218
|
-
const
|
|
1219
|
-
process.env.
|
|
1218
|
+
const prevTestHome = process.env.TEST_USER_HOME;
|
|
1219
|
+
process.env.TEST_USER_HOME = home;
|
|
1220
1220
|
try {
|
|
1221
1221
|
const files = cliSkillInstall(nestedMcpFixture, { global: true, rimraf: true });
|
|
1222
1222
|
expect(files.some((f) => f.includes(join(home, ".agents", "skills", "nested.ts")))).toBe(true);
|
|
1223
1223
|
expect(existsSync(join(home, ".agents", "skills", "nested.ts", "SKILL.md"))).toBe(true);
|
|
1224
1224
|
} finally {
|
|
1225
|
-
if (
|
|
1226
|
-
delete process.env.
|
|
1225
|
+
if (prevTestHome === undefined) {
|
|
1226
|
+
delete process.env.TEST_USER_HOME;
|
|
1227
1227
|
} else {
|
|
1228
|
-
process.env.
|
|
1228
|
+
process.env.TEST_USER_HOME = prevTestHome;
|
|
1229
1229
|
}
|
|
1230
1230
|
rmSync(home, { recursive: true, force: true });
|
|
1231
1231
|
}
|
package/src/core/types.ts
CHANGED
|
@@ -406,16 +406,33 @@ export interface CliCompletionConfig {
|
|
|
406
406
|
|
|
407
407
|
/** Opt-in agent skill install to `~/.agents/skills/<key>/` (default: disabled). */
|
|
408
408
|
export interface CliSkillConfig {
|
|
409
|
-
/** When `true`, install and
|
|
409
|
+
/** When `true`, install and refresh the agent skill via `configure --refresh`. Default false when omitted. */
|
|
410
410
|
enabled?: boolean;
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
+
/** Context for {@link CliConfigureConfig} lifecycle hooks. */
|
|
414
|
+
export interface ConfigureHookContext {
|
|
415
|
+
program: CliProgram;
|
|
416
|
+
/** When true, the configure run is `--dry` (hook should not write files). */
|
|
417
|
+
dry: boolean;
|
|
418
|
+
paths: {
|
|
419
|
+
agentsSkillDir: string;
|
|
420
|
+
agentsMcpPath: string;
|
|
421
|
+
mcpName: string;
|
|
422
|
+
skillDirName: string;
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
413
426
|
/** @experimental */
|
|
414
427
|
export interface CliConfigureConfig {
|
|
415
428
|
/** When `false`, hide/disable `configure` (default: enabled). */
|
|
416
429
|
enabled?: boolean;
|
|
417
|
-
/** Per-artifact gates for configure
|
|
430
|
+
/** Per-artifact gates for configure refresh and interactive wizard. See {@link resolveEffectiveInstallTargets}. */
|
|
418
431
|
targets?: CliConfigureTargets;
|
|
432
|
+
/** Runs after framework artifacts are installed/refreshed (`configure --refresh`). */
|
|
433
|
+
afterRefresh?: (ctx: ConfigureHookContext) => void | Promise<void>;
|
|
434
|
+
/** Runs before framework artifacts are removed (`configure --remove-all`). */
|
|
435
|
+
beforeRemoveAll?: (ctx: ConfigureHookContext) => void | Promise<void>;
|
|
419
436
|
}
|
|
420
437
|
|
|
421
438
|
/** Boolean or structured gate for one install artifact. */
|
|
@@ -424,7 +441,7 @@ export type InstallTargetSpec =
|
|
|
424
441
|
| {
|
|
425
442
|
/** When false, artifact is never installed (even with scoped CLI flags). Default true. */
|
|
426
443
|
enabled?: boolean;
|
|
427
|
-
/** When true, included in `configure --
|
|
444
|
+
/** When true, included in `configure --refresh`. Default varies by key. */
|
|
428
445
|
includedInAll?: boolean;
|
|
429
446
|
};
|
|
430
447
|
|
|
@@ -437,7 +454,7 @@ export interface ResolvedInstallTarget {
|
|
|
437
454
|
export interface CliConfigureTargets {
|
|
438
455
|
/** App binary status only (Homebrew PATH); no self-install. */
|
|
439
456
|
app?: InstallTargetSpec;
|
|
440
|
-
/** App config: interactive wizard step in `configure`. Default not in
|
|
457
|
+
/** App config: interactive wizard step in `configure`. Default not in refresh. */
|
|
441
458
|
configure?: InstallTargetSpec;
|
|
442
459
|
}
|
|
443
460
|
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -153,7 +153,7 @@ test("docs mcp when MCP enabled", async () => {
|
|
|
153
153
|
expect(result.stdout).toContain("MCP server (myapp)");
|
|
154
154
|
expect(result.stdout).toContain("myapp mcp");
|
|
155
155
|
expect(result.stdout).toContain("claude_desktop_config.json");
|
|
156
|
-
expect(result.stdout).toContain("configure --
|
|
156
|
+
expect(result.stdout).toContain("configure --refresh --yes");
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
test("docs rejects unknown subcommand", async () => {
|
|
@@ -304,7 +304,7 @@ test("generateMcpGuide includes schema URI and .agents install", () => {
|
|
|
304
304
|
expect(guide).toContain("claude_desktop_config.json");
|
|
305
305
|
expect(guide).toContain("## Installation");
|
|
306
306
|
expect(guide).toContain("## Running directly");
|
|
307
|
-
expect(guide).toContain("configure --
|
|
307
|
+
expect(guide).toContain("configure --refresh");
|
|
308
308
|
expect(guide).toContain("dotagentsprotocol.com");
|
|
309
309
|
expect(guide).not.toContain("OpenAI Codex");
|
|
310
310
|
});
|
package/src/docs/mcp-guide.ts
CHANGED
|
@@ -81,7 +81,7 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
81
81
|
"",
|
|
82
82
|
"### `.agents` auto-install",
|
|
83
83
|
"",
|
|
84
|
-
"When `mcpServer.enabled` is set, `configure --
|
|
84
|
+
"When `mcpServer.enabled` is set, `configure --refresh` merges this server into `~/.agents/mcp.json` per the https://dotagentsprotocol.com.",
|
|
85
85
|
"",
|
|
86
86
|
];
|
|
87
87
|
|
|
@@ -93,7 +93,7 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
93
93
|
|
|
94
94
|
lines.push(
|
|
95
95
|
"```bash",
|
|
96
|
-
`${root.key} configure --
|
|
96
|
+
`${root.key} configure --refresh --yes`,
|
|
97
97
|
"```",
|
|
98
98
|
"",
|
|
99
99
|
"Writes or updates `~/.agents/mcp.json` with a `mcpServers` entry for this app.",
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ export type {
|
|
|
53
53
|
CliRespondBody,
|
|
54
54
|
CliRespondOptions,
|
|
55
55
|
CliSkillConfig,
|
|
56
|
+
ConfigureHookContext,
|
|
56
57
|
ErrorHookContext,
|
|
57
58
|
InstallTargetSpec,
|
|
58
59
|
InvokeFailureKind,
|
|
@@ -83,6 +84,7 @@ export type { EcsLogEvent, LogEnrichContext } from "./log/ecs.ts";
|
|
|
83
84
|
export { ECS_VERSION, formatEcsLine } from "./log/ecs.ts";
|
|
84
85
|
export type { McpBundlePaths, PackMcpBundleOpts } from "./mcp/bundle.ts";
|
|
85
86
|
export { defaultMcpBundlePaths, generateMcpManifest, packMcpBundle } from "./mcp/bundle.ts";
|
|
87
|
+
export { userHome } from "./paths/host.ts";
|
|
86
88
|
export { Cli, type CliInvokeKind, type CliInvokeResult } from "./runtime/cli.ts";
|
|
87
89
|
export { cliErrWithHelp } from "./runtime/cli-errors.ts";
|
|
88
90
|
export { isInteractiveTty } from "./utils.ts";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for paths/host module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
7
|
+
import { tmpdir, userInfo } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { userHome } from "./host.ts";
|
|
10
|
+
|
|
11
|
+
describe("userHome", () => {
|
|
12
|
+
let prevTestHome: string | undefined;
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
if (prevTestHome === undefined) delete process.env.TEST_USER_HOME;
|
|
16
|
+
else process.env.TEST_USER_HOME = prevTestHome;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("uses TEST_USER_HOME when set", () => {
|
|
20
|
+
const home = mkdtempSync(join(tmpdir(), "argsbarg-test-home-"));
|
|
21
|
+
prevTestHome = process.env.TEST_USER_HOME;
|
|
22
|
+
process.env.TEST_USER_HOME = home;
|
|
23
|
+
try {
|
|
24
|
+
expect(userHome()).toBe(home);
|
|
25
|
+
} finally {
|
|
26
|
+
rmSync(home, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("ignores sandboxed HOME (Homebrew post_install)", () => {
|
|
31
|
+
prevTestHome = process.env.TEST_USER_HOME;
|
|
32
|
+
delete process.env.TEST_USER_HOME;
|
|
33
|
+
const prevHome = process.env.HOME;
|
|
34
|
+
process.env.HOME = "/private/tmp/sqsp-workspaces-postinstall-20260807-fake";
|
|
35
|
+
try {
|
|
36
|
+
const resolved = userHome();
|
|
37
|
+
expect(resolved).not.toBe(process.env.HOME);
|
|
38
|
+
if (process.platform === "darwin" && process.env.USER) {
|
|
39
|
+
expect(resolved).toBe(`/Users/${process.env.USER}`);
|
|
40
|
+
expect(existsSync(resolved)).toBe(true);
|
|
41
|
+
} else {
|
|
42
|
+
expect(resolved).toBe(userInfo().homedir);
|
|
43
|
+
}
|
|
44
|
+
} finally {
|
|
45
|
+
if (prevHome === undefined) delete process.env.HOME;
|
|
46
|
+
else process.env.HOME = prevHome;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("resolves macOS default home when TEST_USER_HOME unset", () => {
|
|
51
|
+
prevTestHome = process.env.TEST_USER_HOME;
|
|
52
|
+
delete process.env.TEST_USER_HOME;
|
|
53
|
+
if (process.platform !== "darwin" || !process.env.USER) return;
|
|
54
|
+
const expected = `/Users/${process.env.USER}`;
|
|
55
|
+
if (!existsSync(expected)) return;
|
|
56
|
+
expect(userHome()).toBe(expected);
|
|
57
|
+
});
|
|
58
|
+
});
|
package/src/paths/host.ts
CHANGED
|
@@ -2,12 +2,21 @@
|
|
|
2
2
|
Shared host path primitives for install, config, and skill modules.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { userInfo } from "node:os";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the user home directory without depending on `$HOME`.
|
|
11
|
+
* This is helpful for when homebrew post-install hooks run with a temporary `$HOME`.
|
|
12
|
+
*/
|
|
9
13
|
export function userHome(): string {
|
|
10
|
-
|
|
14
|
+
const user = process.env.USER ?? process.env.LOGNAME;
|
|
15
|
+
return (
|
|
16
|
+
[process.env.TEST_USER_HOME, user && `/Users/${user}`, user && `/home/${user}`, process.env.USERPROFILE].find(
|
|
17
|
+
(p) => p && existsSync(p),
|
|
18
|
+
) ?? userInfo().homedir
|
|
19
|
+
);
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
/** Expands a leading `~` or `~/` in a path using {@link userHome}. */
|
package/src/skill/generate.ts
CHANGED
|
@@ -154,7 +154,7 @@ function buildSkillMd(root: CliProgram, dirName: string): string {
|
|
|
154
154
|
"",
|
|
155
155
|
"Install follows the https://dotagentsprotocol.com:",
|
|
156
156
|
"",
|
|
157
|
-
`- Auto-install: \`${root.key} configure --
|
|
157
|
+
`- Auto-install: \`${root.key} configure --refresh --yes\` when \`skill.enabled\` → \`~/.agents/skills/${dirName}/\``,
|
|
158
158
|
`- Cursor and most coding agents read \`~/.agents/skills/\` natively`,
|
|
159
159
|
"",
|
|
160
160
|
"**Claude Code (manual):** symlink or copy into Claude's skill directory:",
|
|
@@ -13,8 +13,8 @@ import { mcpRequest, testProgram } from "../fixtures.ts";
|
|
|
13
13
|
/** Tests that bootstrapAppConfig prefers host env over config file. */
|
|
14
14
|
test("bootstrapAppConfig prefers host env over config file", () => {
|
|
15
15
|
const dir = mkdtempSync(join(tmpdir(), "argsbarg-env-"));
|
|
16
|
-
const
|
|
17
|
-
process.env.
|
|
16
|
+
const prevTestHome = process.env.TEST_USER_HOME;
|
|
17
|
+
process.env.TEST_USER_HOME = dir;
|
|
18
18
|
process.env.FOO = "original";
|
|
19
19
|
try {
|
|
20
20
|
const p = testProgram({
|
|
@@ -36,8 +36,8 @@ test("bootstrapAppConfig prefers host env over config file", () => {
|
|
|
36
36
|
expect(process.env.FOO).toBe("original");
|
|
37
37
|
expect(process.env.BAR).toBe("bar");
|
|
38
38
|
} finally {
|
|
39
|
-
if (
|
|
40
|
-
else process.env.
|
|
39
|
+
if (prevTestHome === undefined) delete process.env.TEST_USER_HOME;
|
|
40
|
+
else process.env.TEST_USER_HOME = prevTestHome;
|
|
41
41
|
delete process.env.FOO;
|
|
42
42
|
delete process.env.BAR;
|
|
43
43
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -106,7 +106,7 @@ test("MCP config file loads and exports vars for tool handlers", async () => {
|
|
|
106
106
|
],
|
|
107
107
|
{
|
|
108
108
|
script: "src/test/mcp-integration-fixture.ts",
|
|
109
|
-
env: {
|
|
109
|
+
env: { TEST_USER_HOME: dir, ARGS_TEST_SECRET: "present" },
|
|
110
110
|
},
|
|
111
111
|
);
|
|
112
112
|
const res = responses.get(15) as {
|
|
@@ -148,7 +148,7 @@ const program = {
|
|
|
148
148
|
await new Cli(program).run(process.argv.slice(2));
|
|
149
149
|
`,
|
|
150
150
|
);
|
|
151
|
-
const env = { ...process.env,
|
|
151
|
+
const env = { ...process.env, TEST_USER_HOME: dir } as Record<string, string | undefined>;
|
|
152
152
|
delete env.DOCS_SKIP_RUN_TOKEN;
|
|
153
153
|
try {
|
|
154
154
|
const proc = Bun.spawn(["bun", "run", mainPath, "docs", "cli"], {
|