@vucinatim/agentic-devtools 0.1.0 → 0.1.2
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 +10 -1
- package/adapters/codex/npm/SKILL.md +1 -0
- package/docs/publishing.md +18 -1
- package/docs/testing.md +15 -0
- package/docs/usage.md +5 -1
- package/package.json +2 -1
- package/src/cli.mjs +28 -0
- package/src/core/tool-registry.mjs +3 -3
- package/src/index.mjs +5 -0
- package/src/tools/npm/mcp.mjs +50 -0
- package/src/tools/npm/trust-cli.mjs +144 -0
package/README.md
CHANGED
|
@@ -23,6 +23,12 @@ npx -y @vucinatim/agentic-devtools connect namecheap
|
|
|
23
23
|
npx -y @vucinatim/agentic-devtools connect npm
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
npm publishing setup can also be driven through the package:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx -y @vucinatim/agentic-devtools setup-publishing npm
|
|
30
|
+
```
|
|
31
|
+
|
|
26
32
|
Start here:
|
|
27
33
|
|
|
28
34
|
- [docs/architecture.md](docs/architecture.md)
|
|
@@ -118,7 +124,9 @@ npm:
|
|
|
118
124
|
```
|
|
119
125
|
|
|
120
126
|
Use `connect npm` for guided token setup. For real package releases, prefer
|
|
121
|
-
GitHub Actions Trusted Publishing over local write tokens.
|
|
127
|
+
GitHub Actions Trusted Publishing over local write tokens. Use
|
|
128
|
+
`setup-publishing npm` to run npm's official Trusted Publishing setup command
|
|
129
|
+
through the package.
|
|
122
130
|
|
|
123
131
|
### Global CLI
|
|
124
132
|
|
|
@@ -167,6 +175,7 @@ Current validation commands:
|
|
|
167
175
|
```bash
|
|
168
176
|
npm run build
|
|
169
177
|
npm test
|
|
178
|
+
npm run test:published -- --version <published-version>
|
|
170
179
|
npm run test:namecheap
|
|
171
180
|
npm run test:railway
|
|
172
181
|
npm run coverage
|
package/docs/publishing.md
CHANGED
|
@@ -155,7 +155,24 @@ After npm Trusted Publishing is configured for this exact repo and workflow,
|
|
|
155
155
|
|
|
156
156
|
## npm Trusted Publisher Setup
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
Use the package-owned setup flow:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npx -y @vucinatim/agentic-devtools setup-publishing npm
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The flow runs npm's official trust command, using the saved npm token from
|
|
165
|
+
`connect npm` when one is available:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npx -y npm@^11.10.0 login --auth-type=web --registry https://registry.npmjs.org
|
|
169
|
+
npx -y npm@^11.10.0 trust github @vucinatim/agentic-devtools --repo vucinatim/agentic-devtools --file publish.yml --yes
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
This keeps the setup aligned with npm's beta Trusted Publishing flow and lets
|
|
173
|
+
npm handle web/security-key two-factor authentication when required.
|
|
174
|
+
|
|
175
|
+
For this repository it configures:
|
|
159
176
|
|
|
160
177
|
- provider: GitHub Actions
|
|
161
178
|
- organization/user: `vucinatim`
|
package/docs/testing.md
CHANGED
|
@@ -12,6 +12,7 @@ The test suite should validate three layers:
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
npm test
|
|
15
|
+
npm run test:published -- --version 0.1.1
|
|
15
16
|
npm run test:namecheap
|
|
16
17
|
npm run test:railway
|
|
17
18
|
npm run coverage
|
|
@@ -25,6 +26,20 @@ npm run check
|
|
|
25
26
|
3. production dependency audit
|
|
26
27
|
4. package dry-run validation
|
|
27
28
|
|
|
29
|
+
## Published Package Smoke Tests
|
|
30
|
+
|
|
31
|
+
`npm run test:published -- --version <published-version>` validates the actual
|
|
32
|
+
npm artifact after release. It exercises:
|
|
33
|
+
|
|
34
|
+
- direct `npx` execution
|
|
35
|
+
- simulated global CLI install with `npm install -g --prefix`
|
|
36
|
+
- project dependency install and ESM imports
|
|
37
|
+
- MCP entrypoint help for each public tool
|
|
38
|
+
|
|
39
|
+
The publish workflow runs this after `npm publish`, so a release is only
|
|
40
|
+
considered good after the registry-hosted package passes real installation and
|
|
41
|
+
usage checks.
|
|
42
|
+
|
|
28
43
|
## Coverage Scope
|
|
29
44
|
|
|
30
45
|
Coverage measures library code under `src/`, excluding:
|
package/docs/usage.md
CHANGED
|
@@ -13,6 +13,7 @@ in MCP host config:
|
|
|
13
13
|
npx -y @vucinatim/agentic-devtools connect railway
|
|
14
14
|
npx -y @vucinatim/agentic-devtools connect namecheap
|
|
15
15
|
npx -y @vucinatim/agentic-devtools connect npm
|
|
16
|
+
npx -y @vucinatim/agentic-devtools setup-publishing npm
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Primary: MCP Host With `npx`
|
|
@@ -73,7 +74,9 @@ npm:
|
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
Use `connect npm` for guided token setup when package inspection or token
|
|
76
|
-
operations require authentication.
|
|
77
|
+
operations require authentication. Use `setup-publishing npm` when the package
|
|
78
|
+
needs to attach GitHub Actions Trusted Publishing through npm's official trust
|
|
79
|
+
setup flow.
|
|
77
80
|
|
|
78
81
|
## Secondary: Global CLI
|
|
79
82
|
|
|
@@ -125,6 +128,7 @@ Namecheap supports:
|
|
|
125
128
|
npm supports:
|
|
126
129
|
|
|
127
130
|
- guided local setup through `agentic-devtools connect npm`
|
|
131
|
+
- guided GitHub Actions Trusted Publishing setup through `agentic-devtools setup-publishing npm`
|
|
128
132
|
- `NPM_TOKEN`
|
|
129
133
|
- `NODE_AUTH_TOKEN`
|
|
130
134
|
- `.npmrc` auth token resolution
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vucinatim/agentic-devtools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "MCP-first devtools for AI agents.",
|
|
6
6
|
"type": "module",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"audit": "npm audit --omit=dev",
|
|
60
60
|
"pack:dry": "npm pack --dry-run",
|
|
61
61
|
"test": "vitest run",
|
|
62
|
+
"test:published": "node scripts/test-published-package.mjs",
|
|
62
63
|
"test:watch": "vitest",
|
|
63
64
|
"test:namecheap": "vitest run tests/tools/namecheap",
|
|
64
65
|
"test:railway": "vitest run tests/tools/railway",
|
package/src/cli.mjs
CHANGED
|
@@ -7,6 +7,7 @@ const usage = () => `Usage:
|
|
|
7
7
|
agentic-devtools tools
|
|
8
8
|
agentic-devtools mcp <namecheap|railway|npm>
|
|
9
9
|
agentic-devtools connect <namecheap|railway|npm>
|
|
10
|
+
agentic-devtools setup-publishing npm
|
|
10
11
|
agentic-devtools disconnect <namecheap|railway|npm>
|
|
11
12
|
agentic-devtools auth-status <namecheap|railway|npm>
|
|
12
13
|
agentic-devtools test-connection <namecheap|railway|npm>
|
|
@@ -90,6 +91,33 @@ if (args[0] === "connect") {
|
|
|
90
91
|
throw new Error("connect expects one of: namecheap, railway, npm");
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
if (args[0] === "setup-publishing") {
|
|
95
|
+
const toolName = args[1];
|
|
96
|
+
if (toolName === "npm") {
|
|
97
|
+
const { runNpmTrustGithubSetup } = await import(
|
|
98
|
+
"./tools/npm/trust-cli.mjs"
|
|
99
|
+
);
|
|
100
|
+
process.stderr.write(
|
|
101
|
+
"Running npm's official GitHub Trusted Publishing setup flow...\n",
|
|
102
|
+
);
|
|
103
|
+
const result = await runNpmTrustGithubSetup({
|
|
104
|
+
stdio: "inherit",
|
|
105
|
+
loginFirst: true,
|
|
106
|
+
});
|
|
107
|
+
if (!result.ok) {
|
|
108
|
+
process.exit(result.status || 1);
|
|
109
|
+
}
|
|
110
|
+
printJson({
|
|
111
|
+
ok: true,
|
|
112
|
+
command: result.command,
|
|
113
|
+
args: result.args,
|
|
114
|
+
tokenSource: result.tokenSource,
|
|
115
|
+
});
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
throw new Error("setup-publishing expects: npm");
|
|
119
|
+
}
|
|
120
|
+
|
|
93
121
|
if (args[0] === "disconnect") {
|
|
94
122
|
const toolName = args[1];
|
|
95
123
|
if (toolName === "namecheap") {
|
|
@@ -2,17 +2,17 @@ export const tools = {
|
|
|
2
2
|
namecheap: {
|
|
3
3
|
name: "namecheap",
|
|
4
4
|
description: "Inspect and manage Namecheap domains and DNS.",
|
|
5
|
-
mcpModule: "
|
|
5
|
+
mcpModule: "./tools/namecheap/mcp.mjs",
|
|
6
6
|
},
|
|
7
7
|
railway: {
|
|
8
8
|
name: "railway",
|
|
9
9
|
description: "Inspect Railway projects, environments, and deployments.",
|
|
10
|
-
mcpModule: "
|
|
10
|
+
mcpModule: "./tools/railway/mcp.mjs",
|
|
11
11
|
},
|
|
12
12
|
npm: {
|
|
13
13
|
name: "npm",
|
|
14
14
|
description: "Inspect npm packages, auth, tokens, and publishing setup.",
|
|
15
|
-
mcpModule: "
|
|
15
|
+
mcpModule: "./tools/npm/mcp.mjs",
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
|
package/src/index.mjs
CHANGED
|
@@ -44,4 +44,9 @@ export {
|
|
|
44
44
|
encodePackageName,
|
|
45
45
|
NpmRegistryError,
|
|
46
46
|
} from "./tools/npm/client.mjs";
|
|
47
|
+
export {
|
|
48
|
+
buildNpmWebLoginArgs,
|
|
49
|
+
buildNpmTrustGithubArgs,
|
|
50
|
+
runNpmTrustGithubSetup,
|
|
51
|
+
} from "./tools/npm/trust-cli.mjs";
|
|
47
52
|
export { getTool, listTools, tools } from "./core/tool-registry.mjs";
|
package/src/tools/npm/mcp.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
runNpmBrowserAuthFlow,
|
|
11
11
|
} from "./auth.mjs";
|
|
12
12
|
import { createNpmClient } from "./client.mjs";
|
|
13
|
+
import { runNpmTrustGithubSetup } from "./trust-cli.mjs";
|
|
13
14
|
|
|
14
15
|
const HELP_TEXT = `Usage: agentic-devtools mcp npm
|
|
15
16
|
|
|
@@ -21,6 +22,12 @@ Optional environment variables:
|
|
|
21
22
|
NPM_CONFIG_REGISTRY
|
|
22
23
|
AGENTIC_DEVTOOLS_NPM_AUTH_CONFIG_PATH
|
|
23
24
|
|
|
25
|
+
Standalone flags:
|
|
26
|
+
--connect
|
|
27
|
+
--setup-publishing
|
|
28
|
+
--auth-status
|
|
29
|
+
--test-connection
|
|
30
|
+
|
|
24
31
|
Prefer GitHub Actions Trusted Publishing for real package publishing. Local publishing is supported but explicit confirmation is required.
|
|
25
32
|
`;
|
|
26
33
|
|
|
@@ -75,6 +82,23 @@ const createServer = () => {
|
|
|
75
82
|
}),
|
|
76
83
|
);
|
|
77
84
|
|
|
85
|
+
server.registerTool(
|
|
86
|
+
"setupNpmGitHubTrustedPublisher",
|
|
87
|
+
{
|
|
88
|
+
description:
|
|
89
|
+
"Run npm's official GitHub Actions Trusted Publisher setup flow. Uses npm@^11.10.0 through npx so security-key 2FA can be handled by npm.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
packageName: packageNameSchema.default("@vucinatim/agentic-devtools"),
|
|
92
|
+
repository: z.string().min(1).default("vucinatim/agentic-devtools"),
|
|
93
|
+
workflowFile: z.string().min(1).default("publish.yml"),
|
|
94
|
+
environment: z.string().optional(),
|
|
95
|
+
loginFirst: z.boolean().default(true),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
async (args) =>
|
|
99
|
+
createToolResult(await runNpmTrustGithubSetup(args ?? {})),
|
|
100
|
+
);
|
|
101
|
+
|
|
78
102
|
server.registerTool(
|
|
79
103
|
"disconnectNpm",
|
|
80
104
|
{
|
|
@@ -320,6 +344,32 @@ if (argv.includes("--connect")) {
|
|
|
320
344
|
process.exit(0);
|
|
321
345
|
}
|
|
322
346
|
|
|
347
|
+
if (argv.includes("--setup-publishing")) {
|
|
348
|
+
process.stdout.write(
|
|
349
|
+
"Running npm's official GitHub Trusted Publishing setup flow...\n",
|
|
350
|
+
);
|
|
351
|
+
const result = await runNpmTrustGithubSetup({
|
|
352
|
+
stdio: "inherit",
|
|
353
|
+
loginFirst: true,
|
|
354
|
+
});
|
|
355
|
+
if (!result.ok) {
|
|
356
|
+
process.exit(result.status || 1);
|
|
357
|
+
}
|
|
358
|
+
process.stdout.write(
|
|
359
|
+
`${JSON.stringify(
|
|
360
|
+
{
|
|
361
|
+
ok: true,
|
|
362
|
+
command: result.command,
|
|
363
|
+
args: result.args,
|
|
364
|
+
tokenSource: result.tokenSource,
|
|
365
|
+
},
|
|
366
|
+
null,
|
|
367
|
+
2,
|
|
368
|
+
)}\n`,
|
|
369
|
+
);
|
|
370
|
+
process.exit(0);
|
|
371
|
+
}
|
|
372
|
+
|
|
323
373
|
if (argv.includes("--test-connection")) {
|
|
324
374
|
const client = createNpmClient();
|
|
325
375
|
process.stdout.write(
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { DEFAULT_NPM_REGISTRY, resolveNpmAuthConfig } from "./auth.mjs";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_NPM_TRUST_CLI_SPEC = "npm@^11.10.0";
|
|
8
|
+
|
|
9
|
+
const normalizeRegistry = (registry = DEFAULT_NPM_REGISTRY) =>
|
|
10
|
+
String(registry || DEFAULT_NPM_REGISTRY).replace(/\/+$/, "");
|
|
11
|
+
|
|
12
|
+
export const buildNpmTrustGithubArgs = ({
|
|
13
|
+
packageName = "@vucinatim/agentic-devtools",
|
|
14
|
+
repository = "vucinatim/agentic-devtools",
|
|
15
|
+
workflowFile = "publish.yml",
|
|
16
|
+
environment = "",
|
|
17
|
+
yes = true,
|
|
18
|
+
} = {}) => {
|
|
19
|
+
const args = [
|
|
20
|
+
DEFAULT_NPM_TRUST_CLI_SPEC,
|
|
21
|
+
"trust",
|
|
22
|
+
"github",
|
|
23
|
+
String(packageName).trim(),
|
|
24
|
+
"--repo",
|
|
25
|
+
String(repository).trim(),
|
|
26
|
+
"--file",
|
|
27
|
+
String(workflowFile).trim(),
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
if (environment) {
|
|
31
|
+
args.push("--env", String(environment).trim());
|
|
32
|
+
}
|
|
33
|
+
if (yes) {
|
|
34
|
+
args.push("--yes");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return args;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const buildNpmWebLoginArgs = () => [
|
|
41
|
+
DEFAULT_NPM_TRUST_CLI_SPEC,
|
|
42
|
+
"login",
|
|
43
|
+
"--auth-type=web",
|
|
44
|
+
"--registry",
|
|
45
|
+
DEFAULT_NPM_REGISTRY,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
export const runNpmTrustGithubSetup = async ({
|
|
49
|
+
env = process.env,
|
|
50
|
+
stdio = "pipe",
|
|
51
|
+
loginFirst = false,
|
|
52
|
+
...options
|
|
53
|
+
} = {}) => {
|
|
54
|
+
const auth = resolveNpmAuthConfig(env);
|
|
55
|
+
const registry = normalizeRegistry(auth.registry);
|
|
56
|
+
const tempDir = await mkdtemp(path.join(os.tmpdir(), "agentic-npm-trust-"));
|
|
57
|
+
const userconfigPath = path.join(tempDir, ".npmrc");
|
|
58
|
+
|
|
59
|
+
if (auth.token && !loginFirst) {
|
|
60
|
+
const registryUrl = new URL(registry);
|
|
61
|
+
await writeFile(
|
|
62
|
+
userconfigPath,
|
|
63
|
+
`registry=${registry}/\n//${registryUrl.host}/:_authToken=${auth.token}\n`,
|
|
64
|
+
);
|
|
65
|
+
await chmod(userconfigPath, 0o600).catch(() => {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const commandEnv = {
|
|
69
|
+
...process.env,
|
|
70
|
+
...env,
|
|
71
|
+
NPM_CONFIG_REGISTRY: registry,
|
|
72
|
+
...(auth.token && !loginFirst ? { NPM_CONFIG_USERCONFIG: userconfigPath } : {}),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
if (loginFirst) {
|
|
77
|
+
const loginArgs = ["-y", ...buildNpmWebLoginArgs()];
|
|
78
|
+
const loginResult = await spawnCommand("npx", loginArgs, {
|
|
79
|
+
env: commandEnv,
|
|
80
|
+
stdio,
|
|
81
|
+
});
|
|
82
|
+
if (loginResult.status !== 0) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
status: loginResult.status,
|
|
86
|
+
command: "npx",
|
|
87
|
+
args: loginArgs,
|
|
88
|
+
step: "login",
|
|
89
|
+
tokenSource: auth.source,
|
|
90
|
+
stdout: loginResult.stdout,
|
|
91
|
+
stderr: loginResult.stderr,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const args = ["-y", ...buildNpmTrustGithubArgs(options)];
|
|
97
|
+
const result = await spawnCommand("npx", args, {
|
|
98
|
+
env: commandEnv,
|
|
99
|
+
stdio,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
ok: result.status === 0,
|
|
104
|
+
status: result.status,
|
|
105
|
+
command: "npx",
|
|
106
|
+
args,
|
|
107
|
+
step: "trust",
|
|
108
|
+
tokenSource: auth.source,
|
|
109
|
+
stdout: result.stdout,
|
|
110
|
+
stderr: result.stderr,
|
|
111
|
+
};
|
|
112
|
+
} finally {
|
|
113
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const spawnCommand = (command, args, { env, stdio }) =>
|
|
118
|
+
new Promise((resolve, reject) => {
|
|
119
|
+
const shouldCapture = stdio === "pipe";
|
|
120
|
+
let stdout = "";
|
|
121
|
+
let stderr = "";
|
|
122
|
+
const child = spawn(command, args, {
|
|
123
|
+
env,
|
|
124
|
+
stdio,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (shouldCapture) {
|
|
128
|
+
child.stdout?.on("data", (chunk) => {
|
|
129
|
+
stdout += chunk;
|
|
130
|
+
});
|
|
131
|
+
child.stderr?.on("data", (chunk) => {
|
|
132
|
+
stderr += chunk;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
child.on("error", reject);
|
|
137
|
+
child.on("close", (status) => {
|
|
138
|
+
resolve({
|
|
139
|
+
status: status ?? 1,
|
|
140
|
+
stdout,
|
|
141
|
+
stderr,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|