@fro.bot/systematic 1.11.1 → 1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  # The MIT License (MIT)
2
2
 
3
- Copyright (c) 2022 Marcus R. Brown <git@mrbro.dev>
3
+ Copyright (c) 2026 Marcus R. Brown <git@mrbro.dev>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  <br>
17
17
 
18
- **[Overview](#overview)** · **[Quick Start](#quick-start)** · **[Skills](#skills)** · **[Agents](#agents)** · **[Commands](#commands)** · **[Development](#development)**
18
+ **[Overview](#overview)** · **[Quick Start](#quick-start)** · **[Skills](#skills)** · **[Agents](#agents)** · **[Commands](#commands)** · **[CLI](#cli)** · **[Configuration](#configuration)** · **[Development](#development)**
19
19
 
20
20
  </div>
21
21
 
@@ -38,11 +38,12 @@ Most AI coding assistants respond to requests without structure or methodology.
38
38
 
39
39
  ### Key Features
40
40
 
41
- - **🧠 Structured Skills** — Pre-built workflows for brainstorming, planning, and code review
42
- - **🤖 Specialized Agents** — Purpose-built subagents for architecture, security, and performance
43
- - **⚡ Zero Configuration** — Works immediately after installation via config hooks
44
- - **🔧 Extensible** — Add project-specific skills and commands alongside bundled ones
45
- - **📦 Batteries Included** — Skills, agents, and commands ship with the npm package
41
+ - **Structured Skills** — Pre-built workflows for brainstorming, planning, and code review
42
+ - **Specialized Agents** — Purpose-built subagents for architecture, security, and performance
43
+ - **Zero Configuration** — Works immediately after installation via config hooks
44
+ - **Extensible** — Add project-specific skills and commands alongside bundled ones
45
+ - **Batteries Included** — 8 skills, 11 agents, and 9 commands ship with the npm package
46
+ - **CLI Tooling** — Inspect, list, and convert assets from the command line
46
47
 
47
48
  ## Quick Start
48
49
 
@@ -169,16 +170,52 @@ Commands are slash-invokable shortcuts that trigger workflows or actions.
169
170
  | `/workflows:plan` | Create detailed implementation plans |
170
171
  | `/workflows:review` | Run code review with specialized agents |
171
172
  | `/workflows:work` | Execute planned work systematically |
172
- | `/workflows:compound` | Build compound documentation |
173
+ | `/workflows:compound` | Document recently solved problems to build team knowledge |
173
174
 
174
175
  ### Utility Commands
175
176
 
176
177
  | Command | Description |
177
178
  |---------|-------------|
178
- | `/systematic:lfg` | "Let's go"start working immediately |
179
- | `/systematic:create-agent-skill` | Create a new skill with guidance |
180
- | `/systematic:deepen-plan` | Add detail to existing plans |
181
- | `/systematic:agent-native-audit` | Audit code for agent-native patterns |
179
+ | `/systematic:lfg` | Full autonomous engineering workflow plan, then execute |
180
+ | `/systematic:create-agent-skill` | Create a new skill with expert guidance |
181
+ | `/systematic:deepen-plan` | Enhance a plan with parallel research for each section |
182
+ | `/systematic:agent-native-audit` | Audit code for agent-native architecture patterns |
183
+
184
+ ## CLI
185
+
186
+ Systematic includes a CLI for inspecting and converting assets outside of OpenCode.
187
+
188
+ ```
189
+ systematic <command> [options]
190
+ ```
191
+
192
+ ### Commands
193
+
194
+ | Command | Description |
195
+ |---------|-------------|
196
+ | `list [type]` | List available skills, agents, or commands |
197
+ | `convert <type> <file>` | Convert a CEP file and output the result to stdout |
198
+ | `config show` | Show current configuration and file contents |
199
+ | `config path` | Print config file locations |
200
+
201
+ ### Examples
202
+
203
+ ```bash
204
+ # List all bundled skills
205
+ systematic list skills
206
+
207
+ # List all bundled agents
208
+ systematic list agents
209
+
210
+ # Convert a Claude Code agent to OpenCode format
211
+ systematic convert agent ./agents/my-agent.md
212
+
213
+ # Convert with a specific agent mode
214
+ systematic convert agent ./agents/my-agent.md --mode=primary
215
+
216
+ # Show configuration
217
+ systematic config show
218
+ ```
182
219
 
183
220
  ## Configuration
184
221
 
@@ -186,16 +223,31 @@ Systematic works out of the box, but you can customize it via configuration file
186
223
 
187
224
  ### Plugin Configuration
188
225
 
189
- Create `~/.config/opencode/systematic.json` or `.opencode/systematic.json` to disable specific bundled content:
226
+ Configuration is loaded from multiple locations and merged (later sources override earlier ones):
227
+
228
+ 1. **User config:** `~/.config/opencode/systematic.json`
229
+ 2. **Project config:** `.opencode/systematic.json`
230
+ 3. **Custom config:** `$OPENCODE_CONFIG_DIR/systematic.json` (if `OPENCODE_CONFIG_DIR` is set)
190
231
 
191
232
  ```json
192
233
  {
193
234
  "disabled_skills": ["git-worktree"],
194
235
  "disabled_agents": [],
195
- "disabled_commands": []
236
+ "disabled_commands": [],
237
+ "bootstrap": {
238
+ "enabled": true
239
+ }
196
240
  }
197
241
  ```
198
242
 
243
+ | Option | Type | Default | Description |
244
+ |--------|------|---------|-------------|
245
+ | `disabled_skills` | `string[]` | `[]` | Skills to exclude from registration |
246
+ | `disabled_agents` | `string[]` | `[]` | Agents to exclude from registration |
247
+ | `disabled_commands` | `string[]` | `[]` | Commands to exclude from registration |
248
+ | `bootstrap.enabled` | `boolean` | `true` | Inject the `using-systematic` guide into system prompts |
249
+ | `bootstrap.file` | `string` | — | Custom bootstrap file path (overrides default) |
250
+
199
251
  ### Project-Specific Content
200
252
 
201
253
  Add your own skills, agents, and commands alongside bundled ones:
@@ -219,7 +271,7 @@ The plugin exposes one tool to OpenCode:
219
271
 
220
272
  | Tool | Description |
221
273
  |------|-------------|
222
- | `systematic_skill` | Load Systematic bundled skills by name |
274
+ | `systematic_skill` | Load Systematic bundled skills by name. Lists available skills in its description and returns formatted skill content when invoked. |
223
275
 
224
276
  For non-Systematic skills (project or user-level), use OpenCode's native `skill` tool.
225
277
 
@@ -247,9 +299,9 @@ flowchart TB
247
299
  style G fill:#0f0f23,stroke:#F5A623,color:#B2F5EA
248
300
  ```
249
301
 
250
- 1. **`config` hook** — Merges bundled assets into your OpenCode configuration
251
- 2. **`tool` hook** — Registers the `systematic_skill` tool for loading skills
252
- 3. **`system.transform` hook** — Injects the "Using Systematic" guide into system prompts
302
+ 1. **`config` hook** — Discovers and merges bundled skills, agents, and commands into your OpenCode configuration. Existing config takes precedence over bundled content. Skills are registered as commands with the `systematic:` prefix.
303
+ 2. **`tool` hook** — Registers the `systematic_skill` tool, which lists available skills in its XML description and loads skill content on demand.
304
+ 3. **`system.transform` hook** — Injects the "Using Systematic" bootstrap guide into system prompts, teaching the AI how to discover and invoke skills.
253
305
 
254
306
  This architecture ensures skills, agents, and commands are available immediately without manual setup.
255
307
 
@@ -286,20 +338,27 @@ bun test
286
338
  ### Project Structure
287
339
 
288
340
  ```
341
+ systematic/
289
342
  ├── src/
290
- │ ├── index.ts # Plugin entry point
343
+ │ ├── index.ts # Plugin entry point (SystematicPlugin)
291
344
  │ ├── cli.ts # CLI entry point
292
345
  │ └── lib/
293
346
  │ ├── bootstrap.ts # System prompt injection
294
- │ ├── config.ts # JSONC config loading
295
- │ ├── config-handler.ts # OpenCode config hook
296
- │ ├── skill-tool.ts # systematic_skill tool
347
+ │ ├── config.ts # JSONC config loading + merging
348
+ │ ├── config-handler.ts # OpenCode config hook implementation
349
+ │ ├── converter.ts # CEP-to-OpenCode content conversion
350
+ │ ├── skill-tool.ts # systematic_skill tool factory
351
+ │ ├── skill-loader.ts # Skill content loading + formatting
297
352
  │ ├── skills.ts # Skill discovery
298
353
  │ ├── agents.ts # Agent discovery
299
- └── commands.ts # Command discovery
300
- ├── skills/ # Bundled skills (SKILL.md files)
301
- ├── agents/ # Bundled agents (Markdown)
302
- ├── commands/ # Bundled commands (Markdown)
354
+ ├── commands.ts # Command discovery
355
+ ├── frontmatter.ts # YAML frontmatter parsing
356
+ ├── validation.ts # Agent config validation + type guards
357
+ │ └── walk-dir.ts # Recursive directory walker
358
+ ├── skills/ # 8 bundled skills (SKILL.md files)
359
+ ├── agents/ # 11 bundled agents (4 categories)
360
+ ├── commands/ # 9 bundled commands (with workflows/ subdir)
361
+ ├── docs/ # Starlight documentation site
303
362
  ├── tests/
304
363
  │ ├── unit/ # Unit tests
305
364
  │ └── integration/ # Integration tests
@@ -317,6 +376,9 @@ bun test tests/unit/skills.test.ts
317
376
 
318
377
  # Run integration tests
319
378
  bun test tests/integration
379
+
380
+ # Run all tests
381
+ bun test
320
382
  ```
321
383
 
322
384
  ### Contributing
@@ -325,13 +387,15 @@ See [`AGENTS.md`](./AGENTS.md) for detailed development guidelines, code style c
325
387
 
326
388
  ## Converting from Claude Code
327
389
 
328
- Migrating skills, agents, or commands from Claude Code (CEP) to Systematic? See the [Conversion Guide](https://fro.bot/systematic/guides/conversion-guide) for field mappings and examples. (Also available as [local Markdown](./docs/CONVERSION-GUIDE.md).)
390
+ Migrating skills, agents, or commands from Claude Code (CEP) to Systematic? See the [Conversion Guide](https://fro.bot/systematic/guides/conversion-guide/) for field mappings and examples. Also available as [local Markdown](./docs/CONVERSION-GUIDE.md).
329
391
 
330
392
  ## References
331
393
 
394
+ - [Systematic Documentation](https://fro.bot/systematic) — Full documentation site
332
395
  - [OpenCode Documentation](https://opencode.ai/docs/) — Official OpenCode platform docs
396
+ - [OpenCode Plugin API](https://opencode.ai/docs/plugins) — Plugin development reference
333
397
  - [Compound Engineering Plugin](https://github.com/EveryInc/compound-engineering-plugin) — Original Claude Code workflows
334
- - [Plugin Source Code](https://github.com/marcusrbrown/systematic) — View the implementation
398
+ - [Source Code](https://github.com/marcusrbrown/systematic) — View the implementation
335
399
 
336
400
  ## License
337
401
 
package/dist/cli.js CHANGED
@@ -6,12 +6,24 @@ import {
6
6
  findCommandsInDir,
7
7
  findSkillsInDir,
8
8
  getConfigPaths
9
- } from "./index-we4f9de3.js";
9
+ } from "./index-0ftaxvrt.js";
10
10
 
11
11
  // src/cli.ts
12
12
  import fs from "fs";
13
13
  import path from "path";
14
- var VERSION = "0.1.0";
14
+ var getPackageVersion = () => {
15
+ try {
16
+ const packageJsonPath = path.resolve(import.meta.dirname, "..", "package.json");
17
+ if (!fs.existsSync(packageJsonPath))
18
+ return "unknown";
19
+ const content = fs.readFileSync(packageJsonPath, "utf8");
20
+ const parsed = JSON.parse(content);
21
+ return parsed.version ?? "unknown";
22
+ } catch {
23
+ return "unknown";
24
+ }
25
+ };
26
+ var VERSION = getPackageVersion();
15
27
  var HELP = `
16
28
  systematic - OpenCode plugin for systematic engineering workflows
17
29
 
@@ -138,7 +138,7 @@ function extractBashPermission(data) {
138
138
  }
139
139
  return null;
140
140
  }
141
- function buildPermissionObject(edit, bash, webfetch, doom_loop, external_directory) {
141
+ function buildPermissionObject(edit, bash, webfetch, doom_loop, external_directory, task, skill) {
142
142
  const permission = {};
143
143
  if (edit)
144
144
  permission.edit = edit;
@@ -150,6 +150,10 @@ function buildPermissionObject(edit, bash, webfetch, doom_loop, external_directo
150
150
  permission.doom_loop = doom_loop;
151
151
  if (external_directory)
152
152
  permission.external_directory = external_directory;
153
+ if (task)
154
+ permission.task = task;
155
+ if (skill)
156
+ permission.skill = skill;
153
157
  return Object.keys(permission).length > 0 ? permission : undefined;
154
158
  }
155
159
  function normalizePermission(value) {
@@ -170,7 +174,13 @@ function normalizePermission(value) {
170
174
  const external_directory = extractSimplePermission(value, "external_directory");
171
175
  if (external_directory === null)
172
176
  return;
173
- return buildPermissionObject(edit, bash, webfetch, doom_loop, external_directory);
177
+ const task = extractSimplePermission(value, "task");
178
+ if (task === null)
179
+ return;
180
+ const skill = extractSimplePermission(value, "skill");
181
+ if (skill === null)
182
+ return;
183
+ return buildPermissionObject(edit, bash, webfetch, doom_loop, external_directory, task, skill);
174
184
  }
175
185
  function extractString(data, key, fallback = "") {
176
186
  const value = data[key];
@@ -262,7 +272,8 @@ function extractAgentFrontmatter(content) {
262
272
  disable: extractBoolean(data, "disable"),
263
273
  mode: isAgentMode(data.mode) ? data.mode : undefined,
264
274
  color: extractNonEmptyString(data, "color"),
265
- maxSteps: extractNumber(data, "maxSteps"),
275
+ steps: extractNumber(data, "steps"),
276
+ hidden: extractBoolean(data, "hidden") ?? undefined,
266
277
  permission: normalizePermission(data.permission)
267
278
  };
268
279
  }
@@ -308,6 +319,7 @@ function extractCommandFrontmatter(content) {
308
319
 
309
320
  // src/lib/converter.ts
310
321
  import fs3 from "fs";
322
+ var CONVERTER_VERSION = 2;
311
323
  var cache = new Map;
312
324
  var TOOL_MAPPINGS = [
313
325
  [/\bTask\s+tool\b/gi, "delegate_task tool"],
@@ -336,18 +348,42 @@ var PATH_REPLACEMENTS = [
336
348
  [/\/compound-engineering:/g, "/systematic:"],
337
349
  [/compound-engineering:/g, "systematic:"]
338
350
  ];
339
- var CC_ONLY_SKILL_FIELDS = [
340
- "model",
341
- "allowed-tools",
342
- "allowedTools",
343
- "disable-model-invocation",
344
- "disableModelInvocation",
345
- "user-invocable",
346
- "userInvocable",
347
- "context",
348
- "agent"
349
- ];
350
- var CC_ONLY_COMMAND_FIELDS = ["argument-hint", "argumentHint"];
351
+ var TOOL_NAME_MAP = {
352
+ task: "delegate_task",
353
+ todowrite: "todowrite",
354
+ askuserquestion: "question",
355
+ websearch: "google_search",
356
+ webfetch: "webfetch",
357
+ skill: "skill",
358
+ read: "read",
359
+ write: "write",
360
+ edit: "edit",
361
+ bash: "bash",
362
+ grep: "grep",
363
+ glob: "glob"
364
+ };
365
+ var PERMISSION_MODE_MAP = {
366
+ full: {
367
+ edit: "allow",
368
+ bash: "allow",
369
+ webfetch: "allow"
370
+ },
371
+ default: {
372
+ edit: "ask",
373
+ bash: "ask",
374
+ webfetch: "ask"
375
+ },
376
+ plan: {
377
+ edit: "deny",
378
+ bash: "deny",
379
+ webfetch: "ask"
380
+ },
381
+ bypassPermissions: {
382
+ edit: "allow",
383
+ bash: "allow",
384
+ webfetch: "allow"
385
+ }
386
+ };
351
387
  function inferTemperature(name, description) {
352
388
  const sample = `${name} ${description ?? ""}`.toLowerCase();
353
389
  if (/(review|audit|security|sentinel|oracle|lint|verification|guardian)/.test(sample)) {
@@ -384,27 +420,6 @@ function transformBody(body) {
384
420
  }
385
421
  return result;
386
422
  }
387
- function removeFields(data, fieldsToRemove) {
388
- const result = {};
389
- for (const [key, value] of Object.entries(data)) {
390
- if (!fieldsToRemove.includes(key)) {
391
- result[key] = value;
392
- }
393
- }
394
- return result;
395
- }
396
- function transformSkillFrontmatter(data) {
397
- return removeFields(data, CC_ONLY_SKILL_FIELDS);
398
- }
399
- function transformCommandFrontmatter(data) {
400
- const cleaned = removeFields(data, CC_ONLY_COMMAND_FIELDS);
401
- if (typeof cleaned.model === "string" && cleaned.model !== "inherit") {
402
- cleaned.model = normalizeModel(cleaned.model);
403
- } else if (cleaned.model === "inherit") {
404
- delete cleaned.model;
405
- }
406
- return cleaned;
407
- }
408
423
  function normalizeModel(model) {
409
424
  if (model.includes("/"))
410
425
  return model;
@@ -418,37 +433,127 @@ function normalizeModel(model) {
418
433
  return `google/${model}`;
419
434
  return `anthropic/${model}`;
420
435
  }
421
- function addOptionalFields(target, data) {
422
- if (typeof data.top_p === "number")
423
- target.top_p = data.top_p;
424
- if (isToolsMap(data.tools))
425
- target.tools = data.tools;
426
- if (typeof data.disable === "boolean")
427
- target.disable = data.disable;
428
- if (typeof data.color === "string")
429
- target.color = data.color;
430
- if (typeof data.maxSteps === "number")
431
- target.maxSteps = data.maxSteps;
432
- const permission = normalizePermission(data.permission);
433
- if (permission)
434
- target.permission = permission;
436
+ function canonicalizeToolName(name) {
437
+ const lower = name.trim().toLowerCase();
438
+ return TOOL_NAME_MAP[lower] ?? lower;
439
+ }
440
+ function isValidSteps(value) {
441
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0;
442
+ }
443
+ function mapStepsField(data) {
444
+ if (data.steps !== undefined) {
445
+ if (isValidSteps(data.steps)) {
446
+ delete data.maxTurns;
447
+ delete data.maxSteps;
448
+ }
449
+ return;
450
+ }
451
+ const candidates = [];
452
+ if (isValidSteps(data.maxTurns))
453
+ candidates.push(data.maxTurns);
454
+ if (isValidSteps(data.maxSteps))
455
+ candidates.push(data.maxSteps);
456
+ if (candidates.length > 0) {
457
+ data.steps = Math.min(...candidates);
458
+ delete data.maxTurns;
459
+ delete data.maxSteps;
460
+ }
461
+ }
462
+ function mapToolsField(data) {
463
+ if (data.tools !== undefined && !Array.isArray(data.tools)) {
464
+ if (isToolsMap(data.tools)) {
465
+ mergeDisallowedTools(data);
466
+ }
467
+ return;
468
+ }
469
+ if (Array.isArray(data.tools)) {
470
+ const toolsMap = {};
471
+ for (const tool of data.tools) {
472
+ if (typeof tool === "string") {
473
+ toolsMap[canonicalizeToolName(tool)] = true;
474
+ }
475
+ }
476
+ if (Object.keys(toolsMap).length > 0) {
477
+ data.tools = toolsMap;
478
+ } else {
479
+ delete data.tools;
480
+ }
481
+ }
482
+ mergeDisallowedTools(data);
483
+ }
484
+ function mergeDisallowedTools(data) {
485
+ if (!Array.isArray(data.disallowedTools))
486
+ return;
487
+ const existing = isToolsMap(data.tools) ? data.tools : {};
488
+ for (const tool of data.disallowedTools) {
489
+ if (typeof tool === "string") {
490
+ existing[canonicalizeToolName(tool)] = false;
491
+ }
492
+ }
493
+ if (Object.keys(existing).length > 0) {
494
+ data.tools = existing;
495
+ }
496
+ delete data.disallowedTools;
497
+ }
498
+ function mapPermissionMode(data) {
499
+ if (data.permission !== undefined) {
500
+ const normalized = normalizePermission(data.permission);
501
+ if (normalized) {
502
+ data.permission = normalized;
503
+ delete data.permissionMode;
504
+ return;
505
+ }
506
+ }
507
+ if (typeof data.permissionMode !== "string")
508
+ return;
509
+ const mapped = PERMISSION_MODE_MAP[data.permissionMode];
510
+ data.permission = mapped ?? { edit: "ask", bash: "ask", webfetch: "ask" };
511
+ delete data.permissionMode;
512
+ }
513
+ function mapHiddenField(data) {
514
+ if (data["disable-model-invocation"] === true || data.disableModelInvocation === true) {
515
+ data.hidden = true;
516
+ delete data["disable-model-invocation"];
517
+ delete data.disableModelInvocation;
518
+ }
519
+ }
520
+ function normalizeModelField(data) {
521
+ if (typeof data.model === "string" && data.model !== "inherit") {
522
+ data.model = normalizeModel(data.model);
523
+ } else if (data.model === "inherit") {
524
+ delete data.model;
525
+ }
435
526
  }
436
527
  function transformAgentFrontmatter(data, agentMode) {
528
+ const result = { ...data };
529
+ result.mode = isAgentMode(data.mode) ? data.mode : agentMode;
437
530
  const name = typeof data.name === "string" ? data.name : "";
438
531
  const description = typeof data.description === "string" ? data.description : "";
439
- const mode = isAgentMode(data.mode) ? data.mode : agentMode;
440
- const newData = { mode };
441
532
  if (description) {
442
- newData.description = description;
533
+ result.description = description;
443
534
  } else if (name) {
444
- newData.description = `${name} agent`;
535
+ result.description = `${name} agent`;
445
536
  }
446
- if (typeof data.model === "string" && data.model !== "inherit") {
447
- newData.model = normalizeModel(data.model);
537
+ normalizeModelField(result);
538
+ result.temperature = typeof data.temperature === "number" ? data.temperature : inferTemperature(name, description);
539
+ mapStepsField(result);
540
+ mapToolsField(result);
541
+ mapPermissionMode(result);
542
+ mapHiddenField(result);
543
+ return result;
544
+ }
545
+ function transformSkillFrontmatter(data) {
546
+ const result = { ...data };
547
+ normalizeModelField(result);
548
+ if (result.context === "fork") {
549
+ result.subtask = true;
448
550
  }
449
- newData.temperature = typeof data.temperature === "number" ? data.temperature : inferTemperature(name, description);
450
- addOptionalFields(newData, data);
451
- return newData;
551
+ return result;
552
+ }
553
+ function transformCommandFrontmatter(data) {
554
+ const result = { ...data };
555
+ normalizeModelField(result);
556
+ return result;
452
557
  }
453
558
  function convertContent(content, type, options = {}) {
454
559
  if (content === "")
@@ -458,7 +563,7 @@ function convertContent(content, type, options = {}) {
458
563
  return options.skipBodyTransform ? content : transformBody(content);
459
564
  }
460
565
  if (parseError) {
461
- return content;
566
+ return options.skipBodyTransform ? content : transformBody(content);
462
567
  }
463
568
  const shouldTransformBody = !options.skipBodyTransform;
464
569
  const transformedBody = shouldTransformBody ? transformBody(body) : body;
@@ -484,7 +589,7 @@ function convertFileWithCache(filePath, type, options = {}) {
484
589
  const fd = fs3.openSync(filePath, "r");
485
590
  try {
486
591
  const stats = fs3.fstatSync(fd);
487
- const cacheKey = `${filePath}:${type}:${options.source ?? "bundled"}:${options.agentMode ?? "subagent"}:${options.skipBodyTransform ?? false}`;
592
+ const cacheKey = `${CONVERTER_VERSION}:${filePath}:${type}:${options.source ?? "bundled"}:${options.agentMode ?? "subagent"}:${options.skipBodyTransform ?? false}`;
488
593
  const cached = cache.get(cacheKey);
489
594
  if (cached != null && cached.mtimeMs === stats.mtimeMs) {
490
595
  return cached.converted;
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  findSkillsInDir,
9
9
  loadConfig,
10
10
  parseFrontmatter
11
- } from "./index-we4f9de3.js";
11
+ } from "./index-0ftaxvrt.js";
12
12
 
13
13
  // src/index.ts
14
14
  import fs3 from "fs";
@@ -22,7 +22,7 @@ import path from "path";
22
22
  function getToolMappingTemplate(bundledSkillsDir) {
23
23
  return `**Tool Mapping for OpenCode:**
24
24
  When skills reference tools you don't have, substitute OpenCode equivalents:
25
- - \`TodoWrite\` \u2192 \`update_plan\`
25
+ - \`TodoWrite\` \u2192 \`todowrite\`
26
26
  - \`Task\` tool with subagents \u2192 Use OpenCode's subagent system (@mention)
27
27
  - \`Skill\` tool \u2192 OpenCode's native \`skill\` tool
28
28
  - \`SystematicSkill\` tool \u2192 \`systematic_skill\` (Systematic plugin skills)
@@ -155,7 +155,8 @@ function loadAgentAsConfig(agentInfo) {
155
155
  disable,
156
156
  mode,
157
157
  color,
158
- maxSteps,
158
+ steps,
159
+ hidden,
159
160
  permission
160
161
  } = extractAgentFrontmatter(converted);
161
162
  const config = {
@@ -176,8 +177,10 @@ function loadAgentAsConfig(agentInfo) {
176
177
  config.mode = mode;
177
178
  if (color !== undefined)
178
179
  config.color = color;
179
- if (maxSteps !== undefined)
180
- config.maxSteps = maxSteps;
180
+ if (steps !== undefined)
181
+ config.steps = steps;
182
+ if (hidden !== undefined)
183
+ config.hidden = hidden;
181
184
  if (permission !== undefined)
182
185
  config.permission = permission;
183
186
  return config;
@@ -21,7 +21,9 @@ export interface AgentFrontmatter {
21
21
  /** Hex color code */
22
22
  color?: string;
23
23
  /** Max agentic iterations */
24
- maxSteps?: number;
24
+ steps?: number;
25
+ /** Whether this agent is hidden from model invocation */
26
+ hidden?: boolean;
25
27
  /** Permission settings */
26
28
  permission?: PermissionConfig;
27
29
  }
@@ -0,0 +1,21 @@
1
+ export interface ManifestSource {
2
+ repo: string;
3
+ branch: string;
4
+ url: string;
5
+ }
6
+ export interface ManifestDefinition {
7
+ source: string;
8
+ upstream_path: string;
9
+ upstream_commit: string;
10
+ synced_at: string;
11
+ notes: string;
12
+ }
13
+ export interface SyncManifest {
14
+ $schema?: string;
15
+ sources: Record<string, ManifestSource>;
16
+ definitions: Record<string, ManifestDefinition>;
17
+ }
18
+ export declare function validateManifest(data: unknown): data is SyncManifest;
19
+ export declare function readManifest(filePath: string): SyncManifest | null;
20
+ export declare function writeManifest(filePath: string, manifest: SyncManifest): void;
21
+ export declare function findStaleEntries(manifest: SyncManifest, existingPaths: string[]): string[];
@@ -9,6 +9,8 @@ export interface PermissionConfig {
9
9
  webfetch?: PermissionSetting;
10
10
  doom_loop?: PermissionSetting;
11
11
  external_directory?: PermissionSetting;
12
+ task?: PermissionSetting;
13
+ skill?: PermissionSetting;
12
14
  }
13
15
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
14
16
  export declare function isPermissionSetting(value: unknown): value is PermissionSetting;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "1.11.1",
3
+ "version": "1.12.0",
4
4
  "description": "Structured engineering workflows for OpenCode",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",