@fro.bot/systematic 1.11.0 → 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 +1 -1
- package/README.md +92 -27
- package/dist/cli.js +14 -2
- package/dist/{index-we4f9de3.js → index-0ftaxvrt.js} +166 -61
- package/dist/index.js +8 -5
- package/dist/lib/agents.d.ts +3 -1
- package/dist/lib/manifest.d.ts +21 -0
- package/dist/lib/validation.d.ts +2 -0
- package/package.json +10 -1
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
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
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
[](https://github.com/marcusrbrown/systematic/actions)
|
|
12
12
|
[](https://www.npmjs.com/package/@fro.bot/systematic)
|
|
13
|
+
[](https://fro.bot/systematic)
|
|
13
14
|
[](LICENSE)
|
|
14
15
|
|
|
15
16
|
<br>
|
|
16
17
|
|
|
17
|
-
**[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)**
|
|
18
19
|
|
|
19
20
|
</div>
|
|
20
21
|
|
|
@@ -37,11 +38,12 @@ Most AI coding assistants respond to requests without structure or methodology.
|
|
|
37
38
|
|
|
38
39
|
### Key Features
|
|
39
40
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
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
|
|
45
47
|
|
|
46
48
|
## Quick Start
|
|
47
49
|
|
|
@@ -168,16 +170,52 @@ Commands are slash-invokable shortcuts that trigger workflows or actions.
|
|
|
168
170
|
| `/workflows:plan` | Create detailed implementation plans |
|
|
169
171
|
| `/workflows:review` | Run code review with specialized agents |
|
|
170
172
|
| `/workflows:work` | Execute planned work systematically |
|
|
171
|
-
| `/workflows:compound` |
|
|
173
|
+
| `/workflows:compound` | Document recently solved problems to build team knowledge |
|
|
172
174
|
|
|
173
175
|
### Utility Commands
|
|
174
176
|
|
|
175
177
|
| Command | Description |
|
|
176
178
|
|---------|-------------|
|
|
177
|
-
| `/systematic:lfg` |
|
|
178
|
-
| `/systematic:create-agent-skill` | Create a new skill with guidance |
|
|
179
|
-
| `/systematic:deepen-plan` |
|
|
180
|
-
| `/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
|
+
```
|
|
181
219
|
|
|
182
220
|
## Configuration
|
|
183
221
|
|
|
@@ -185,16 +223,31 @@ Systematic works out of the box, but you can customize it via configuration file
|
|
|
185
223
|
|
|
186
224
|
### Plugin Configuration
|
|
187
225
|
|
|
188
|
-
|
|
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)
|
|
189
231
|
|
|
190
232
|
```json
|
|
191
233
|
{
|
|
192
234
|
"disabled_skills": ["git-worktree"],
|
|
193
235
|
"disabled_agents": [],
|
|
194
|
-
"disabled_commands": []
|
|
236
|
+
"disabled_commands": [],
|
|
237
|
+
"bootstrap": {
|
|
238
|
+
"enabled": true
|
|
239
|
+
}
|
|
195
240
|
}
|
|
196
241
|
```
|
|
197
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
|
+
|
|
198
251
|
### Project-Specific Content
|
|
199
252
|
|
|
200
253
|
Add your own skills, agents, and commands alongside bundled ones:
|
|
@@ -218,7 +271,7 @@ The plugin exposes one tool to OpenCode:
|
|
|
218
271
|
|
|
219
272
|
| Tool | Description |
|
|
220
273
|
|------|-------------|
|
|
221
|
-
| `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. |
|
|
222
275
|
|
|
223
276
|
For non-Systematic skills (project or user-level), use OpenCode's native `skill` tool.
|
|
224
277
|
|
|
@@ -246,9 +299,9 @@ flowchart TB
|
|
|
246
299
|
style G fill:#0f0f23,stroke:#F5A623,color:#B2F5EA
|
|
247
300
|
```
|
|
248
301
|
|
|
249
|
-
1. **`config` hook** —
|
|
250
|
-
2. **`tool` hook** — Registers the `systematic_skill` tool
|
|
251
|
-
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.
|
|
252
305
|
|
|
253
306
|
This architecture ensures skills, agents, and commands are available immediately without manual setup.
|
|
254
307
|
|
|
@@ -285,20 +338,27 @@ bun test
|
|
|
285
338
|
### Project Structure
|
|
286
339
|
|
|
287
340
|
```
|
|
341
|
+
systematic/
|
|
288
342
|
├── src/
|
|
289
|
-
│ ├── index.ts # Plugin entry point
|
|
343
|
+
│ ├── index.ts # Plugin entry point (SystematicPlugin)
|
|
290
344
|
│ ├── cli.ts # CLI entry point
|
|
291
345
|
│ └── lib/
|
|
292
346
|
│ ├── bootstrap.ts # System prompt injection
|
|
293
|
-
│ ├── config.ts # JSONC config loading
|
|
294
|
-
│ ├── config-handler.ts # OpenCode config hook
|
|
295
|
-
│ ├──
|
|
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
|
|
296
352
|
│ ├── skills.ts # Skill discovery
|
|
297
353
|
│ ├── agents.ts # Agent discovery
|
|
298
|
-
│
|
|
299
|
-
├──
|
|
300
|
-
├──
|
|
301
|
-
|
|
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
|
|
302
362
|
├── tests/
|
|
303
363
|
│ ├── unit/ # Unit tests
|
|
304
364
|
│ └── integration/ # Integration tests
|
|
@@ -316,6 +376,9 @@ bun test tests/unit/skills.test.ts
|
|
|
316
376
|
|
|
317
377
|
# Run integration tests
|
|
318
378
|
bun test tests/integration
|
|
379
|
+
|
|
380
|
+
# Run all tests
|
|
381
|
+
bun test
|
|
319
382
|
```
|
|
320
383
|
|
|
321
384
|
### Contributing
|
|
@@ -324,13 +387,15 @@ See [`AGENTS.md`](./AGENTS.md) for detailed development guidelines, code style c
|
|
|
324
387
|
|
|
325
388
|
## Converting from Claude Code
|
|
326
389
|
|
|
327
|
-
Migrating skills, agents, or commands from Claude Code (CEP) to Systematic? See the [Conversion Guide](
|
|
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).
|
|
328
391
|
|
|
329
392
|
## References
|
|
330
393
|
|
|
394
|
+
- [Systematic Documentation](https://fro.bot/systematic) — Full documentation site
|
|
331
395
|
- [OpenCode Documentation](https://opencode.ai/docs/) — Official OpenCode platform docs
|
|
396
|
+
- [OpenCode Plugin API](https://opencode.ai/docs/plugins) — Plugin development reference
|
|
332
397
|
- [Compound Engineering Plugin](https://github.com/EveryInc/compound-engineering-plugin) — Original Claude Code workflows
|
|
333
|
-
- [
|
|
398
|
+
- [Source Code](https://github.com/marcusrbrown/systematic) — View the implementation
|
|
334
399
|
|
|
335
400
|
## License
|
|
336
401
|
|
package/dist/cli.js
CHANGED
|
@@ -6,12 +6,24 @@ import {
|
|
|
6
6
|
findCommandsInDir,
|
|
7
7
|
findSkillsInDir,
|
|
8
8
|
getConfigPaths
|
|
9
|
-
} from "./index-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
340
|
-
"
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
|
|
350
|
-
|
|
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
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
533
|
+
result.description = description;
|
|
443
534
|
} else if (name) {
|
|
444
|
-
|
|
535
|
+
result.description = `${name} agent`;
|
|
445
536
|
}
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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-
|
|
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 \`
|
|
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
|
-
|
|
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 (
|
|
180
|
-
config.
|
|
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;
|
package/dist/lib/agents.d.ts
CHANGED
|
@@ -21,7 +21,9 @@ export interface AgentFrontmatter {
|
|
|
21
21
|
/** Hex color code */
|
|
22
22
|
color?: string;
|
|
23
23
|
/** Max agentic iterations */
|
|
24
|
-
|
|
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[];
|
package/dist/lib/validation.d.ts
CHANGED
|
@@ -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,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Structured engineering workflows for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"homepage": "https://fro.bot/systematic",
|
|
6
7
|
"main": "./dist/index.js",
|
|
7
8
|
"bin": {
|
|
8
9
|
"systematic": "./dist/cli.js"
|
|
@@ -19,6 +20,9 @@
|
|
|
19
20
|
"agents",
|
|
20
21
|
"commands"
|
|
21
22
|
],
|
|
23
|
+
"workspaces": [
|
|
24
|
+
"docs"
|
|
25
|
+
],
|
|
22
26
|
"scripts": {
|
|
23
27
|
"clean": "rimraf dist",
|
|
24
28
|
"build": "bun run clean && bun build src/index.ts src/cli.ts --outdir dist --target bun --splitting --packages external && tsc --emitDeclarationOnly",
|
|
@@ -29,6 +33,10 @@
|
|
|
29
33
|
"typecheck": "tsc --noEmit",
|
|
30
34
|
"lint": "biome check .",
|
|
31
35
|
"fix": "bun run lint --fix",
|
|
36
|
+
"docs:dev": "bun run --cwd docs dev",
|
|
37
|
+
"docs:build": "bun run docs:generate && bun run --cwd docs build",
|
|
38
|
+
"docs:preview": "bun run --cwd docs preview",
|
|
39
|
+
"docs:generate": "bun docs/scripts/transform-content.ts",
|
|
32
40
|
"prepublishOnly": "bun run build"
|
|
33
41
|
},
|
|
34
42
|
"keywords": [
|
|
@@ -53,6 +61,7 @@
|
|
|
53
61
|
"devDependencies": {
|
|
54
62
|
"@biomejs/biome": "^2.0.0",
|
|
55
63
|
"@opencode-ai/plugin": "^1.1.30",
|
|
64
|
+
"@opencode-ai/sdk": "^1.1.30",
|
|
56
65
|
"@types/bun": "latest",
|
|
57
66
|
"@types/js-yaml": "^4.0.9",
|
|
58
67
|
"@types/node": "^24.0.0",
|