@agentskit/cli 0.11.3 → 0.12.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/bin.cjs +528 -26
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-7SBFFCM7.js → chunk-X4PVLZSO.js} +531 -29
- package/dist/chunk-X4PVLZSO.js.map +1 -0
- package/dist/index.cjs +528 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +23 -18
- package/dist/chunk-7SBFFCM7.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -14,6 +14,7 @@ var fs = require('fs');
|
|
|
14
14
|
var tools = require('@agentskit/tools');
|
|
15
15
|
var skills = require('@agentskit/skills');
|
|
16
16
|
var memory = require('@agentskit/memory');
|
|
17
|
+
var integrations = require('@agentskit/integrations');
|
|
17
18
|
var child_process = require('child_process');
|
|
18
19
|
var jsxRuntime = require('react/jsx-runtime');
|
|
19
20
|
var url = require('url');
|
|
@@ -24,6 +25,8 @@ var chokidar = require('chokidar');
|
|
|
24
25
|
var rag = require('@agentskit/rag');
|
|
25
26
|
var agentSchema = require('@agentskit/core/agent-schema');
|
|
26
27
|
var yaml = require('yaml');
|
|
28
|
+
var security = require('@agentskit/core/security');
|
|
29
|
+
var core = require('@agentskit/core');
|
|
27
30
|
|
|
28
31
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
32
|
|
|
@@ -152,7 +155,7 @@ function createDemoAdapter(provider, model) {
|
|
|
152
155
|
].join(" ");
|
|
153
156
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
154
157
|
if (cancelled) return;
|
|
155
|
-
await new Promise((
|
|
158
|
+
await new Promise((resolve6) => setTimeout(resolve6, 35));
|
|
156
159
|
yield { type: "text", content: chunk };
|
|
157
160
|
}
|
|
158
161
|
yield { type: "done" };
|
|
@@ -607,7 +610,7 @@ function instantiate(kind) {
|
|
|
607
610
|
case "filesystem":
|
|
608
611
|
return tools.filesystem({ basePath: process.cwd() });
|
|
609
612
|
case "shell":
|
|
610
|
-
return [tools.shell({ timeout: 3e4 })];
|
|
613
|
+
return [tools.shell({ timeout: 3e4, allowAny: true })];
|
|
611
614
|
}
|
|
612
615
|
}
|
|
613
616
|
function gateTool(tool) {
|
|
@@ -627,9 +630,19 @@ function resolveTools(toolNames) {
|
|
|
627
630
|
case "shell":
|
|
628
631
|
tools.push(...instantiate(name));
|
|
629
632
|
break;
|
|
630
|
-
default:
|
|
631
|
-
|
|
633
|
+
default: {
|
|
634
|
+
if (integrations.getIntegration(name)) {
|
|
635
|
+
const { tools: projected, credentialFound, envVar } = integrations.integrationToolsFromEnv(name, process.env);
|
|
636
|
+
if (!credentialFound) {
|
|
637
|
+
process.stderr.write(`Integration "${name}" needs ${envVar} set in the environment.
|
|
632
638
|
`);
|
|
639
|
+
}
|
|
640
|
+
tools.push(...projected);
|
|
641
|
+
} else {
|
|
642
|
+
process.stderr.write(`Unknown tool: ${name}
|
|
643
|
+
`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
633
646
|
}
|
|
634
647
|
}
|
|
635
648
|
return tools;
|
|
@@ -864,20 +877,39 @@ function configHooksToHandlers(config) {
|
|
|
864
877
|
function runShellHook(entry, payload) {
|
|
865
878
|
return new Promise((resolvePromise) => {
|
|
866
879
|
const timeoutMs = entry.timeout ?? 5e3;
|
|
880
|
+
let settled = false;
|
|
881
|
+
const settle = (result) => {
|
|
882
|
+
if (settled) return;
|
|
883
|
+
settled = true;
|
|
884
|
+
clearTimeout(timer);
|
|
885
|
+
resolvePromise(result);
|
|
886
|
+
};
|
|
867
887
|
const child = child_process.spawn("sh", ["-c", entry.run], {
|
|
868
|
-
stdio: ["pipe", "pipe", "inherit"]
|
|
888
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
889
|
+
detached: true
|
|
869
890
|
});
|
|
870
891
|
let stdout = "";
|
|
871
892
|
child.stdout.on("data", (chunk) => {
|
|
872
893
|
stdout += chunk.toString();
|
|
873
894
|
});
|
|
874
895
|
const timer = setTimeout(() => {
|
|
875
|
-
child.
|
|
896
|
+
if (child.pid !== void 0) {
|
|
897
|
+
try {
|
|
898
|
+
process.kill(-child.pid, "SIGKILL");
|
|
899
|
+
} catch {
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
try {
|
|
903
|
+
child.kill("SIGKILL");
|
|
904
|
+
} catch {
|
|
905
|
+
}
|
|
906
|
+
settle({ decision: "block", reason: `shell hook timed out after ${timeoutMs}ms` });
|
|
876
907
|
}, timeoutMs);
|
|
908
|
+
child.stdin.on("error", () => {
|
|
909
|
+
});
|
|
877
910
|
child.on("close", (code) => {
|
|
878
|
-
clearTimeout(timer);
|
|
879
911
|
if (code !== 0) {
|
|
880
|
-
|
|
912
|
+
settle({
|
|
881
913
|
decision: "block",
|
|
882
914
|
reason: `shell hook exited with code ${code}`
|
|
883
915
|
});
|
|
@@ -885,19 +917,18 @@ function runShellHook(entry, payload) {
|
|
|
885
917
|
}
|
|
886
918
|
const trimmed = stdout.trim();
|
|
887
919
|
if (!trimmed) {
|
|
888
|
-
|
|
920
|
+
settle({ decision: "continue" });
|
|
889
921
|
return;
|
|
890
922
|
}
|
|
891
923
|
try {
|
|
892
924
|
const parsed = JSON.parse(trimmed);
|
|
893
|
-
|
|
925
|
+
settle(parsed);
|
|
894
926
|
} catch {
|
|
895
|
-
|
|
927
|
+
settle({ decision: "continue" });
|
|
896
928
|
}
|
|
897
929
|
});
|
|
898
930
|
child.on("error", (err) => {
|
|
899
|
-
|
|
900
|
-
resolvePromise({ decision: "block", reason: err.message });
|
|
931
|
+
settle({ decision: "block", reason: err.message });
|
|
901
932
|
});
|
|
902
933
|
try {
|
|
903
934
|
child.stdin.write(JSON.stringify(payload));
|
|
@@ -3687,13 +3718,6 @@ function registerConfigCommand(program) {
|
|
|
3687
3718
|
`);
|
|
3688
3719
|
process.exit(2);
|
|
3689
3720
|
}
|
|
3690
|
-
if (fs.existsSync(targetPath) && !options.force) {
|
|
3691
|
-
process.stderr.write(
|
|
3692
|
-
`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
3693
|
-
`
|
|
3694
|
-
);
|
|
3695
|
-
process.exit(1);
|
|
3696
|
-
}
|
|
3697
3721
|
const template = {
|
|
3698
3722
|
defaults: {
|
|
3699
3723
|
provider: "openai",
|
|
@@ -3704,7 +3728,20 @@ function registerConfigCommand(program) {
|
|
|
3704
3728
|
}
|
|
3705
3729
|
};
|
|
3706
3730
|
fs.mkdirSync(path__default.default.dirname(targetPath), { recursive: true });
|
|
3707
|
-
|
|
3731
|
+
try {
|
|
3732
|
+
fs.writeFileSync(targetPath, JSON.stringify(template, null, 2) + "\n", {
|
|
3733
|
+
flag: options.force ? "w" : "wx"
|
|
3734
|
+
});
|
|
3735
|
+
} catch (err) {
|
|
3736
|
+
if (err.code === "EEXIST") {
|
|
3737
|
+
process.stderr.write(
|
|
3738
|
+
`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
3739
|
+
`
|
|
3740
|
+
);
|
|
3741
|
+
process.exit(1);
|
|
3742
|
+
}
|
|
3743
|
+
throw err;
|
|
3744
|
+
}
|
|
3708
3745
|
process.stdout.write(
|
|
3709
3746
|
`Wrote ${targetPath}
|
|
3710
3747
|
Edit it to taste, then run:
|
|
@@ -4002,12 +4039,12 @@ function scaffoldAgent(schema) {
|
|
|
4002
4039
|
return files;
|
|
4003
4040
|
}
|
|
4004
4041
|
async function writeScaffold(files, outDir, opts = {}) {
|
|
4005
|
-
const { writeFile:
|
|
4006
|
-
const { dirname, join:
|
|
4042
|
+
const { writeFile: writeFile3, mkdir: mkdir3, access } = await import('fs/promises');
|
|
4043
|
+
const { dirname: dirname2, join: join6 } = await import('path');
|
|
4007
4044
|
const written = [];
|
|
4008
4045
|
for (const f of files) {
|
|
4009
|
-
const full =
|
|
4010
|
-
await
|
|
4046
|
+
const full = join6(outDir, f.path);
|
|
4047
|
+
await mkdir3(dirname2(full), { recursive: true });
|
|
4011
4048
|
if (!opts.overwrite) {
|
|
4012
4049
|
try {
|
|
4013
4050
|
await access(full);
|
|
@@ -4015,7 +4052,7 @@ async function writeScaffold(files, outDir, opts = {}) {
|
|
|
4015
4052
|
} catch {
|
|
4016
4053
|
}
|
|
4017
4054
|
}
|
|
4018
|
-
await
|
|
4055
|
+
await writeFile3(full, f.content, "utf8");
|
|
4019
4056
|
written.push(f.path);
|
|
4020
4057
|
}
|
|
4021
4058
|
return written;
|
|
@@ -4168,6 +4205,469 @@ function registerFlowCommand(program) {
|
|
|
4168
4205
|
}
|
|
4169
4206
|
});
|
|
4170
4207
|
}
|
|
4208
|
+
function lintTaxonomyFile(filePath) {
|
|
4209
|
+
const absolute = path.resolve(filePath);
|
|
4210
|
+
let raw;
|
|
4211
|
+
try {
|
|
4212
|
+
raw = fs.readFileSync(absolute, "utf8");
|
|
4213
|
+
} catch (err) {
|
|
4214
|
+
return {
|
|
4215
|
+
file: absolute,
|
|
4216
|
+
ruleCount: 0,
|
|
4217
|
+
result: {
|
|
4218
|
+
ok: false,
|
|
4219
|
+
issues: [{ index: -1, path: "", message: `cannot read file: ${err.message}` }]
|
|
4220
|
+
}
|
|
4221
|
+
};
|
|
4222
|
+
}
|
|
4223
|
+
let parsed;
|
|
4224
|
+
try {
|
|
4225
|
+
parsed = JSON.parse(raw);
|
|
4226
|
+
} catch (err) {
|
|
4227
|
+
return {
|
|
4228
|
+
file: absolute,
|
|
4229
|
+
ruleCount: 0,
|
|
4230
|
+
result: {
|
|
4231
|
+
ok: false,
|
|
4232
|
+
issues: [{ index: -1, path: "", message: `invalid JSON: ${err.message}` }]
|
|
4233
|
+
}
|
|
4234
|
+
};
|
|
4235
|
+
}
|
|
4236
|
+
const result = security.validatePIITaxonomy(parsed);
|
|
4237
|
+
const ruleCount = parsed && typeof parsed === "object" && Array.isArray(parsed.rules) ? parsed.rules.length : 0;
|
|
4238
|
+
return { file: absolute, result, ruleCount };
|
|
4239
|
+
}
|
|
4240
|
+
function renderLintReport(report, opts = {}) {
|
|
4241
|
+
const { color = false } = opts;
|
|
4242
|
+
const red = (s) => color ? `\x1B[31m${s}\x1B[0m` : s;
|
|
4243
|
+
const green = (s) => color ? `\x1B[32m${s}\x1B[0m` : s;
|
|
4244
|
+
const dim = (s) => color ? `\x1B[2m${s}\x1B[0m` : s;
|
|
4245
|
+
const lines = [];
|
|
4246
|
+
lines.push(dim(report.file));
|
|
4247
|
+
if (report.result.ok) {
|
|
4248
|
+
lines.push(green(`\u2713 valid \xB7 ${report.ruleCount} rule${report.ruleCount === 1 ? "" : "s"}`));
|
|
4249
|
+
} else {
|
|
4250
|
+
lines.push(red(`\u2717 ${report.result.issues.length} issue${report.result.issues.length === 1 ? "" : "s"}`));
|
|
4251
|
+
for (const issue of report.result.issues) {
|
|
4252
|
+
lines.push(` ${red("\u2022")} ${issue.path || "<root>"}: ${issue.message}`);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
return `${lines.join("\n")}
|
|
4256
|
+
`;
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
// src/commands/pii.ts
|
|
4260
|
+
function registerPiiCommand(program) {
|
|
4261
|
+
const pii = program.command("pii").description("Inspect and validate PII taxonomies.");
|
|
4262
|
+
pii.command("lint <file...>").description("Validate one or more PII taxonomy JSON files.").option("--json", "Emit JSON instead of formatted output").action((files, options) => {
|
|
4263
|
+
const reports = files.map(lintTaxonomyFile);
|
|
4264
|
+
const failed = reports.filter((r) => !r.result.ok).length;
|
|
4265
|
+
if (options.json) {
|
|
4266
|
+
process.stdout.write(JSON.stringify(reports, null, 2) + "\n");
|
|
4267
|
+
} else {
|
|
4268
|
+
for (const report of reports) {
|
|
4269
|
+
process.stdout.write(renderLintReport(report, { color: process.stdout.isTTY }));
|
|
4270
|
+
}
|
|
4271
|
+
if (failed > 0) {
|
|
4272
|
+
process.stderr.write(`
|
|
4273
|
+
${failed} of ${reports.length} taxonomy file${reports.length === 1 ? "" : "s"} failed validation
|
|
4274
|
+
`);
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
if (failed > 0) process.exit(1);
|
|
4278
|
+
});
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4281
|
+
// src/rules/cursor.ts
|
|
4282
|
+
var CURSOR_RULE = `---
|
|
4283
|
+
description: AgentsKit conventions \u2014 apply to every file in this workspace
|
|
4284
|
+
globs: ["**/*.ts", "**/*.tsx"]
|
|
4285
|
+
alwaysApply: true
|
|
4286
|
+
---
|
|
4287
|
+
|
|
4288
|
+
# AgentsKit project rules
|
|
4289
|
+
|
|
4290
|
+
When writing or editing TypeScript in this workspace, follow these rules. They
|
|
4291
|
+
are non-negotiable and enforced by CI.
|
|
4292
|
+
|
|
4293
|
+
## Imports
|
|
4294
|
+
|
|
4295
|
+
- Use **named exports only**. No \`export default\`.
|
|
4296
|
+
- Import from package roots: \`@agentskit/core\`, \`@agentskit/runtime\`, \`@agentskit/react\`,
|
|
4297
|
+
\`@agentskit/ink\`, \`@agentskit/adapters\`, \`@agentskit/tools\`, \`@agentskit/skills\`,
|
|
4298
|
+
\`@agentskit/memory\`, \`@agentskit/rag\`, \`@agentskit/observability\`, \`@agentskit/eval\`,
|
|
4299
|
+
\`@agentskit/sandbox\`, \`@agentskit/cli\`, \`@agentskit/templates\`.
|
|
4300
|
+
- For tools subpaths, use \`@agentskit/tools/integrations\`, \`@agentskit/tools/mcp\`,
|
|
4301
|
+
\`@agentskit/tools/mcp-devtools\`.
|
|
4302
|
+
|
|
4303
|
+
## Types
|
|
4304
|
+
|
|
4305
|
+
- TypeScript strict mode is on. **Do not use \`any\`** \u2014 use \`unknown\` and narrow.
|
|
4306
|
+
- Headless React components: no hardcoded styles, use \`data-ak-*\` attributes.
|
|
4307
|
+
|
|
4308
|
+
## Errors
|
|
4309
|
+
|
|
4310
|
+
- Never \`throw new Error(...)\` inside package source. Use the typed errors from
|
|
4311
|
+
\`@agentskit/core\`: \`AdapterError\`, \`ToolError\`, \`MemoryError\`, \`RuntimeError\`,
|
|
4312
|
+
\`SandboxError\`, \`SkillError\`, \`ConfigError\`. Pair with \`ErrorCodes.<...>\`.
|
|
4313
|
+
|
|
4314
|
+
## Tests
|
|
4315
|
+
|
|
4316
|
+
- vitest. Place tests under \`packages/<pkg>/tests/\` mirroring \`src/\`.
|
|
4317
|
+
- E2E lives in \`apps/example-*\` with Playwright.
|
|
4318
|
+
|
|
4319
|
+
## Versioning
|
|
4320
|
+
|
|
4321
|
+
- Every PR with a behavior change requires a Changeset (\`pnpm changeset\`).
|
|
4322
|
+
|
|
4323
|
+
## Where to look first
|
|
4324
|
+
|
|
4325
|
+
- Architecture, contracts, ADRs: \`docs/architecture/adrs/\`
|
|
4326
|
+
- Per-package conventions: \`packages/<pkg>/CONVENTIONS.md\`
|
|
4327
|
+
- Agent-facing index: \`AGENTS.md\` and \`apps/docs-next/content/docs/for-agents/\`
|
|
4328
|
+
`;
|
|
4329
|
+
|
|
4330
|
+
// src/rules/windsurf.ts
|
|
4331
|
+
var WINDSURF_RULE = `# AgentsKit project rules (read first)
|
|
4332
|
+
|
|
4333
|
+
This workspace builds AgentsKit \u2014 a JavaScript agent toolkit with a 10 KB core,
|
|
4334
|
+
six formal contracts, and ~19 plug-and-play packages. When generating or
|
|
4335
|
+
editing code, follow these rules \u2014 they are enforced by CI.
|
|
4336
|
+
|
|
4337
|
+
## Imports
|
|
4338
|
+
- **Named exports only.** No \`export default\`.
|
|
4339
|
+
- Import from package roots (\`@agentskit/core\`, \`@agentskit/runtime\`, \`@agentskit/react\`, etc.).
|
|
4340
|
+
- Tools subpaths: \`@agentskit/tools/integrations\`, \`@agentskit/tools/mcp\`, \`@agentskit/tools/mcp-devtools\`.
|
|
4341
|
+
|
|
4342
|
+
## Types
|
|
4343
|
+
- Strict mode TypeScript. **No \`any\`** \u2014 use \`unknown\` and narrow.
|
|
4344
|
+
|
|
4345
|
+
## Errors
|
|
4346
|
+
- **Do not** \`throw new Error(...)\` in package source. Use \`AdapterError\` /
|
|
4347
|
+
\`ToolError\` / \`MemoryError\` / \`RuntimeError\` / \`SandboxError\` / \`SkillError\` /
|
|
4348
|
+
\`ConfigError\` from \`@agentskit/core\`, paired with \`ErrorCodes\`.
|
|
4349
|
+
|
|
4350
|
+
## Tests
|
|
4351
|
+
- vitest. Tests live in \`packages/<pkg>/tests/\` mirroring \`src/\`.
|
|
4352
|
+
- E2E lives in \`apps/example-*\` with Playwright.
|
|
4353
|
+
|
|
4354
|
+
## Versioning
|
|
4355
|
+
- Every behavior change needs a Changeset (\`pnpm changeset\`).
|
|
4356
|
+
|
|
4357
|
+
## Read first
|
|
4358
|
+
- \`AGENTS.md\` \u2014 universal agent guidance
|
|
4359
|
+
- \`apps/docs-next/content/docs/for-agents/\` \u2014 per-package agent docs
|
|
4360
|
+
- \`docs/architecture/adrs/\` \u2014 six core contracts (Adapter, Tool, Memory, Retriever, Skill, Runtime)
|
|
4361
|
+
- \`packages/<pkg>/CONVENTIONS.md\` \u2014 per-package contribution rules
|
|
4362
|
+
`;
|
|
4363
|
+
|
|
4364
|
+
// src/rules/codex.ts
|
|
4365
|
+
var CODEX_PROFILE = `<!-- agentskit-codex-profile:start -->
|
|
4366
|
+
## Codex / Aider profile
|
|
4367
|
+
|
|
4368
|
+
Structured hints for CLI coding agents (OpenAI Codex, Aider, Claude Code,
|
|
4369
|
+
Cursor) running against this workspace. Update via \`agentskit rules codex\`.
|
|
4370
|
+
|
|
4371
|
+
\`\`\`yaml
|
|
4372
|
+
# AgentsKit Codex profile v1
|
|
4373
|
+
profile: agentskit
|
|
4374
|
+
runtime: node-25
|
|
4375
|
+
package_manager: pnpm
|
|
4376
|
+
test_runner: vitest
|
|
4377
|
+
build_runner: turborepo
|
|
4378
|
+
|
|
4379
|
+
allowed_commands:
|
|
4380
|
+
- pnpm install
|
|
4381
|
+
- pnpm build
|
|
4382
|
+
- pnpm test
|
|
4383
|
+
- pnpm lint
|
|
4384
|
+
- pnpm changeset
|
|
4385
|
+
- pnpm --filter @agentskit/* test
|
|
4386
|
+
- pnpm --filter @agentskit/* lint
|
|
4387
|
+
- pnpm --filter @agentskit/* build
|
|
4388
|
+
|
|
4389
|
+
restricted_paths:
|
|
4390
|
+
- packages/core/src/errors.ts # Touch carefully \u2014 every package depends on the typed errors here.
|
|
4391
|
+
- packages/core/src/security/ # Security-sensitive \u2014 small surface, requires review.
|
|
4392
|
+
- docs/architecture/adrs/ # Contract changes need a new ADR + major bump.
|
|
4393
|
+
|
|
4394
|
+
models_recommended:
|
|
4395
|
+
- claude-sonnet-4-6 # Default \u2014 long context, fast.
|
|
4396
|
+
- gpt-5 # Strong reasoning for refactors / cross-package work.
|
|
4397
|
+
- claude-opus-4-7 # Heavy refactors, contract redesigns.
|
|
4398
|
+
|
|
4399
|
+
invariants:
|
|
4400
|
+
core_max_kb_gzip: 10
|
|
4401
|
+
no_default_exports: true
|
|
4402
|
+
strict_typescript: true
|
|
4403
|
+
no_bare_throw_new_error: true
|
|
4404
|
+
named_exports_only: true
|
|
4405
|
+
changeset_required_for_behavior_changes: true
|
|
4406
|
+
|
|
4407
|
+
read_first:
|
|
4408
|
+
- AGENTS.md
|
|
4409
|
+
- apps/docs-next/content/docs/for-agents/
|
|
4410
|
+
- docs/architecture/adrs/
|
|
4411
|
+
\`\`\`
|
|
4412
|
+
<!-- agentskit-codex-profile:end -->
|
|
4413
|
+
`;
|
|
4414
|
+
|
|
4415
|
+
// src/rules/claude-code.ts
|
|
4416
|
+
var CLAUDE_CODE_SKILL = [
|
|
4417
|
+
{
|
|
4418
|
+
path: "SKILL.md",
|
|
4419
|
+
contents: `---
|
|
4420
|
+
name: agentskit
|
|
4421
|
+
description: Scaffold AgentsKit projects, add tools/skills, run doctor, and inspect the runtime \u2014 wraps the agentskit CLI.
|
|
4422
|
+
---
|
|
4423
|
+
|
|
4424
|
+
# AgentsKit
|
|
4425
|
+
|
|
4426
|
+
Skill bundle for working with the AgentsKit toolkit from inside Claude Code.
|
|
4427
|
+
|
|
4428
|
+
Pair this skill with the project-scoped slash commands at \`.claude/commands/agentskit-*.md\`:
|
|
4429
|
+
|
|
4430
|
+
- \`/agentskit-new-agent\` \u2014 interactive scaffold (\`agentskit init\`)
|
|
4431
|
+
- \`/agentskit-doctor\` \u2014 diagnose the local environment (\`agentskit doctor\`)
|
|
4432
|
+
- \`/agentskit-add-tool\` \u2014 add a tool integration template
|
|
4433
|
+
- \`/agentskit-add-skill\` \u2014 add a skill template
|
|
4434
|
+
- \`/agentskit-lint-pii\` \u2014 validate a PII taxonomy file
|
|
4435
|
+
- \`/agentskit-rules\` \u2014 write \`.cursor/rules\` / \`.windsurfrules\` / Codex profile
|
|
4436
|
+
|
|
4437
|
+
When the user is inside an AgentsKit workspace, prefer the slash commands over
|
|
4438
|
+
hand-rolling shell invocations \u2014 they share the canonical defaults.
|
|
4439
|
+
|
|
4440
|
+
For convention reminders (named exports only, no bare throw, etc.), read
|
|
4441
|
+
\`AGENTS.md\` at the workspace root.
|
|
4442
|
+
`
|
|
4443
|
+
}
|
|
4444
|
+
];
|
|
4445
|
+
var CLAUDE_CODE_SLASH_COMMANDS = [
|
|
4446
|
+
{
|
|
4447
|
+
path: "agentskit-new-agent.md",
|
|
4448
|
+
contents: `---
|
|
4449
|
+
description: Scaffold a new AgentsKit project (interactive)
|
|
4450
|
+
allowed-tools: Bash(npx agentskit init:*)
|
|
4451
|
+
---
|
|
4452
|
+
|
|
4453
|
+
Run \`npx @agentskit/cli init\` interactively in the user's chosen directory.
|
|
4454
|
+
After scaffold completes, summarise the layout (templates created, next
|
|
4455
|
+
commands the user should run).
|
|
4456
|
+
`
|
|
4457
|
+
},
|
|
4458
|
+
{
|
|
4459
|
+
path: "agentskit-doctor.md",
|
|
4460
|
+
contents: `---
|
|
4461
|
+
description: Run the AgentsKit environment doctor and summarise findings
|
|
4462
|
+
allowed-tools: Bash(npx agentskit doctor:*)
|
|
4463
|
+
---
|
|
4464
|
+
|
|
4465
|
+
Run \`npx @agentskit/cli doctor\` in the workspace root. Format the output
|
|
4466
|
+
into pass / warn / fail buckets and propose a fix for each fail / warn.
|
|
4467
|
+
`
|
|
4468
|
+
},
|
|
4469
|
+
{
|
|
4470
|
+
path: "agentskit-add-tool.md",
|
|
4471
|
+
contents: `---
|
|
4472
|
+
description: Add a tool template to the current package
|
|
4473
|
+
---
|
|
4474
|
+
|
|
4475
|
+
Ask the user which tool to add. Use \`packages/templates/src/blueprints/tool.ts\`
|
|
4476
|
+
to scaffold the file under \`packages/<pkg>/src/tools/\`. Update the package's
|
|
4477
|
+
\`src/index.ts\` to re-export the new tool. Add a vitest mock test under
|
|
4478
|
+
\`packages/<pkg>/tests/\`.
|
|
4479
|
+
`
|
|
4480
|
+
},
|
|
4481
|
+
{
|
|
4482
|
+
path: "agentskit-add-skill.md",
|
|
4483
|
+
contents: `---
|
|
4484
|
+
description: Add a skill template to @agentskit/skills
|
|
4485
|
+
---
|
|
4486
|
+
|
|
4487
|
+
Use \`packages/templates/src/blueprints/skill.ts\` to scaffold a new skill
|
|
4488
|
+
under \`packages/skills/src/\`. Re-export from the package index. Add a
|
|
4489
|
+
golden-dataset test fixture under \`packages/skills/tests/\` (10\u201350 input/
|
|
4490
|
+
expected examples, per the conventions).
|
|
4491
|
+
`
|
|
4492
|
+
},
|
|
4493
|
+
{
|
|
4494
|
+
path: "agentskit-lint-pii.md",
|
|
4495
|
+
contents: `---
|
|
4496
|
+
description: Validate a PII taxonomy JSON file
|
|
4497
|
+
allowed-tools: Bash(npx agentskit pii lint:*)
|
|
4498
|
+
---
|
|
4499
|
+
|
|
4500
|
+
Ask the user for a path. Run \`npx @agentskit/cli pii lint <path>\` and
|
|
4501
|
+
display the report. If issues exist, suggest concrete fixes per the
|
|
4502
|
+
issue messages.
|
|
4503
|
+
`
|
|
4504
|
+
},
|
|
4505
|
+
{
|
|
4506
|
+
path: "agentskit-rules.md",
|
|
4507
|
+
contents: `---
|
|
4508
|
+
description: Write editor rule files (Cursor / Windsurf / Codex / Claude Code)
|
|
4509
|
+
allowed-tools: Bash(npx agentskit rules:*)
|
|
4510
|
+
---
|
|
4511
|
+
|
|
4512
|
+
Ask which editor (or "all"). Run the matching:
|
|
4513
|
+
- \`npx @agentskit/cli rules cursor\`
|
|
4514
|
+
- \`npx @agentskit/cli rules windsurf\`
|
|
4515
|
+
- \`npx @agentskit/cli rules codex\`
|
|
4516
|
+
- \`npx @agentskit/cli rules claude-code\`
|
|
4517
|
+
|
|
4518
|
+
After writing, summarise which files landed and what each one does.
|
|
4519
|
+
`
|
|
4520
|
+
}
|
|
4521
|
+
];
|
|
4522
|
+
|
|
4523
|
+
// src/rules.ts
|
|
4524
|
+
var CODEX_BLOCK_START = "<!-- agentskit-codex-profile:start -->";
|
|
4525
|
+
var CODEX_BLOCK_END = "<!-- agentskit-codex-profile:end -->";
|
|
4526
|
+
async function ensureDir2(path5) {
|
|
4527
|
+
await promises.mkdir(path.dirname(path5), { recursive: true });
|
|
4528
|
+
}
|
|
4529
|
+
async function writeIfChanged(absPath, contents, force) {
|
|
4530
|
+
let existing;
|
|
4531
|
+
try {
|
|
4532
|
+
existing = await promises.readFile(absPath, "utf8");
|
|
4533
|
+
} catch {
|
|
4534
|
+
}
|
|
4535
|
+
if (existing === contents) return "skipped";
|
|
4536
|
+
if (existing !== void 0 && !force) {
|
|
4537
|
+
return "skipped";
|
|
4538
|
+
}
|
|
4539
|
+
await ensureDir2(absPath);
|
|
4540
|
+
await promises.writeFile(absPath, contents, "utf8");
|
|
4541
|
+
return existing === void 0 ? "wrote" : "updated";
|
|
4542
|
+
}
|
|
4543
|
+
async function writeCursor(rootDir, force) {
|
|
4544
|
+
const path5 = path.join(rootDir, ".cursor", "rules", "agentskit.mdc");
|
|
4545
|
+
const action = await writeIfChanged(path5, CURSOR_RULE, force);
|
|
4546
|
+
return [{ path: path5, action }];
|
|
4547
|
+
}
|
|
4548
|
+
async function writeWindsurf(rootDir, force) {
|
|
4549
|
+
const path5 = path.join(rootDir, ".windsurfrules");
|
|
4550
|
+
const action = await writeIfChanged(path5, WINDSURF_RULE, force);
|
|
4551
|
+
return [{ path: path5, action }];
|
|
4552
|
+
}
|
|
4553
|
+
async function writeCodex(rootDir, force) {
|
|
4554
|
+
const path5 = path.join(rootDir, "AGENTS.md");
|
|
4555
|
+
let existing = "";
|
|
4556
|
+
try {
|
|
4557
|
+
existing = await promises.readFile(path5, "utf8");
|
|
4558
|
+
} catch {
|
|
4559
|
+
}
|
|
4560
|
+
const startIdx = existing.indexOf(CODEX_BLOCK_START);
|
|
4561
|
+
const endIdx = startIdx >= 0 ? existing.lastIndexOf(CODEX_BLOCK_END, existing.length) : -1;
|
|
4562
|
+
if (startIdx >= 0 && endIdx <= startIdx) {
|
|
4563
|
+
throw new core.ConfigError({
|
|
4564
|
+
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
4565
|
+
message: `${path5}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
|
|
4566
|
+
hint: "Restore the closing sentinel or remove the partial block, then re-run."
|
|
4567
|
+
});
|
|
4568
|
+
}
|
|
4569
|
+
let next;
|
|
4570
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
4571
|
+
next = existing.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing.slice(endIdx + CODEX_BLOCK_END.length);
|
|
4572
|
+
} else if (existing) {
|
|
4573
|
+
next = `${existing.replace(/\s+$/, "")}
|
|
4574
|
+
|
|
4575
|
+
${CODEX_PROFILE}`;
|
|
4576
|
+
} else {
|
|
4577
|
+
next = CODEX_PROFILE;
|
|
4578
|
+
}
|
|
4579
|
+
if (next === existing) return [{ path: path5, action: "skipped" }];
|
|
4580
|
+
if (existing && !force) {
|
|
4581
|
+
return [{ path: path5, action: "skipped" }];
|
|
4582
|
+
}
|
|
4583
|
+
await ensureDir2(path5);
|
|
4584
|
+
await promises.writeFile(path5, next, "utf8");
|
|
4585
|
+
return [{ path: path5, action: existing ? "updated" : "wrote" }];
|
|
4586
|
+
}
|
|
4587
|
+
async function writeClaudeCode(rootDir, force) {
|
|
4588
|
+
const out = [];
|
|
4589
|
+
const skillRoot = path.join(rootDir, ".claude", "skills", "agentskit");
|
|
4590
|
+
for (const file of CLAUDE_CODE_SKILL) {
|
|
4591
|
+
const abs = path.join(skillRoot, file.path);
|
|
4592
|
+
const action = await writeIfChanged(abs, file.contents, force);
|
|
4593
|
+
out.push({ path: abs, action });
|
|
4594
|
+
}
|
|
4595
|
+
const commandsRoot = path.join(rootDir, ".claude", "commands");
|
|
4596
|
+
for (const cmd of CLAUDE_CODE_SLASH_COMMANDS) {
|
|
4597
|
+
const abs = path.join(commandsRoot, cmd.path);
|
|
4598
|
+
const action = await writeIfChanged(abs, cmd.contents, force);
|
|
4599
|
+
out.push({ path: abs, action });
|
|
4600
|
+
}
|
|
4601
|
+
return out;
|
|
4602
|
+
}
|
|
4603
|
+
async function writeRules(editor, options = {}) {
|
|
4604
|
+
const root = path.resolve(options.rootDir ?? process.cwd());
|
|
4605
|
+
const force = options.force === true;
|
|
4606
|
+
if (editor === "all") {
|
|
4607
|
+
return [
|
|
4608
|
+
{ editor: "cursor", files: await writeCursor(root, force) },
|
|
4609
|
+
{ editor: "windsurf", files: await writeWindsurf(root, force) },
|
|
4610
|
+
{ editor: "codex", files: await writeCodex(root, force) },
|
|
4611
|
+
{ editor: "claude-code", files: await writeClaudeCode(root, force) }
|
|
4612
|
+
];
|
|
4613
|
+
}
|
|
4614
|
+
switch (editor) {
|
|
4615
|
+
case "cursor":
|
|
4616
|
+
return [{ editor, files: await writeCursor(root, force) }];
|
|
4617
|
+
case "windsurf":
|
|
4618
|
+
return [{ editor, files: await writeWindsurf(root, force) }];
|
|
4619
|
+
case "codex":
|
|
4620
|
+
return [{ editor, files: await writeCodex(root, force) }];
|
|
4621
|
+
case "claude-code":
|
|
4622
|
+
return [{ editor, files: await writeClaudeCode(root, force) }];
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
|
|
4626
|
+
// src/commands/rules.ts
|
|
4627
|
+
var VALID = ["cursor", "windsurf", "codex", "claude-code", "all"];
|
|
4628
|
+
function registerRulesCommand(program) {
|
|
4629
|
+
program.command("rules <editor>").description(
|
|
4630
|
+
"Write editor rule files (cursor | windsurf | codex | claude-code | all). Teaches the editor AgentsKit conventions so generated code respects named-export-only, package boundaries, and the for-agents/* manifest."
|
|
4631
|
+
).option("--out <dir>", "Workspace root to write files into (default: cwd)").option("-f, --force", "Overwrite existing rule files (codex / claude-code skill always update in place)").action(async (editor, options) => {
|
|
4632
|
+
if (!VALID.includes(editor)) {
|
|
4633
|
+
process.stderr.write(`unknown editor "${editor}". Valid: ${VALID.join(", ")}
|
|
4634
|
+
`);
|
|
4635
|
+
process.exit(1);
|
|
4636
|
+
}
|
|
4637
|
+
try {
|
|
4638
|
+
const results = await writeRules(editor, {
|
|
4639
|
+
rootDir: options.out,
|
|
4640
|
+
force: options.force === true
|
|
4641
|
+
});
|
|
4642
|
+
const counts = { wrote: 0, updated: 0, skipped: 0 };
|
|
4643
|
+
for (const { editor: ed, files } of results) {
|
|
4644
|
+
process.stdout.write(`
|
|
4645
|
+
[${ed}]
|
|
4646
|
+
`);
|
|
4647
|
+
for (const f of files) {
|
|
4648
|
+
counts[f.action]++;
|
|
4649
|
+
process.stdout.write(` ${f.action.padEnd(7)} ${f.path}
|
|
4650
|
+
`);
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
process.stdout.write(
|
|
4654
|
+
`
|
|
4655
|
+
Done \u2014 ${counts.wrote} wrote, ${counts.updated} updated, ${counts.skipped} skipped.
|
|
4656
|
+
`
|
|
4657
|
+
);
|
|
4658
|
+
if (counts.skipped > 0 && options.force !== true) {
|
|
4659
|
+
process.stdout.write(`(re-run with --force to overwrite skipped files)
|
|
4660
|
+
`);
|
|
4661
|
+
}
|
|
4662
|
+
} catch (err) {
|
|
4663
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4664
|
+
process.stderr.write(`
|
|
4665
|
+
rules failed: ${message}
|
|
4666
|
+
`);
|
|
4667
|
+
process.exit(1);
|
|
4668
|
+
}
|
|
4669
|
+
});
|
|
4670
|
+
}
|
|
4171
4671
|
|
|
4172
4672
|
// src/commands/index.ts
|
|
4173
4673
|
function createCli() {
|
|
@@ -4183,6 +4683,8 @@ function createCli() {
|
|
|
4183
4683
|
registerRagCommand(program);
|
|
4184
4684
|
registerAiCommand(program);
|
|
4185
4685
|
registerFlowCommand(program);
|
|
4686
|
+
registerPiiCommand(program);
|
|
4687
|
+
registerRulesCommand(program);
|
|
4186
4688
|
return program;
|
|
4187
4689
|
}
|
|
4188
4690
|
|