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