@fro.bot/systematic 1.11.1 → 1.13.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 +93 -29
- package/commands/workflows/brainstorm.md +17 -8
- package/commands/workflows/compound.md +95 -60
- package/commands/workflows/plan.md +22 -24
- package/commands/workflows/review.md +45 -32
- package/commands/workflows/work.md +91 -19
- package/dist/cli.js +14 -2
- package/dist/{index-we4f9de3.js → index-bky4p9gw.js} +171 -66
- package/dist/index.js +8 -5
- package/dist/lib/agents.d.ts +3 -1
- package/dist/lib/manifest.d.ts +35 -0
- package/dist/lib/validation.d.ts +2 -0
- package/package.json +1 -1
|
@@ -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,13 +319,14 @@ 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
|
-
[/\bTask\s+tool\b/gi, "
|
|
314
|
-
[/\bTask\s+([\w-]+)\s*:/g, "
|
|
315
|
-
[/\bTask\s+([\w-]+)\s*\(/g, "
|
|
316
|
-
[/\bTask\s*\(/g, "
|
|
317
|
-
[/\bTask\b(?=\s+to\s+\w)/g, "
|
|
325
|
+
[/\bTask\s+tool\b/gi, "task tool"],
|
|
326
|
+
[/\bTask\s+([\w-]+)\s*:/g, "task $1:"],
|
|
327
|
+
[/\bTask\s+([\w-]+)\s*\(/g, "task $1("],
|
|
328
|
+
[/\bTask\s*\(/g, "task("],
|
|
329
|
+
[/\bTask\b(?=\s+to\s+\w)/g, "task"],
|
|
318
330
|
[/\bTodoWrite\b/g, "todowrite"],
|
|
319
331
|
[/\bAskUserQuestion\b/g, "question"],
|
|
320
332
|
[/\bWebSearch\b/g, "google_search"],
|
|
@@ -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: "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-bky4p9gw.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,35 @@
|
|
|
1
|
+
export interface ManifestSource {
|
|
2
|
+
repo: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
url: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ManifestRewrite {
|
|
7
|
+
field: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
original?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ManualOverride {
|
|
12
|
+
field: string;
|
|
13
|
+
reason: string;
|
|
14
|
+
original?: string;
|
|
15
|
+
overridden_at: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ManifestDefinition {
|
|
18
|
+
source: string;
|
|
19
|
+
upstream_path: string;
|
|
20
|
+
upstream_commit: string;
|
|
21
|
+
synced_at: string;
|
|
22
|
+
notes: string;
|
|
23
|
+
upstream_content_hash?: string;
|
|
24
|
+
rewrites?: ManifestRewrite[];
|
|
25
|
+
manual_overrides?: ManualOverride[];
|
|
26
|
+
}
|
|
27
|
+
export interface SyncManifest {
|
|
28
|
+
$schema?: string;
|
|
29
|
+
sources: Record<string, ManifestSource>;
|
|
30
|
+
definitions: Record<string, ManifestDefinition>;
|
|
31
|
+
}
|
|
32
|
+
export declare function validateManifest(data: unknown): data is SyncManifest;
|
|
33
|
+
export declare function readManifest(filePath: string): SyncManifest | null;
|
|
34
|
+
export declare function writeManifest(filePath: string, manifest: SyncManifest): void;
|
|
35
|
+
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;
|