@fro.bot/systematic 3.4.1 → 3.5.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/HARNESSES.md +9 -2
- package/agents/review/code-simplicity-reviewer.md +1 -1
- package/dist/cli.js +1420 -41
- package/dist/{index-0vm17gwv.js → index-1mb4baxr.js} +115 -4
- package/dist/index.js +1 -1
- package/dist/lib/config-schema.d.ts +66 -0
- package/dist/lib/config.d.ts +16 -2
- package/dist/lib/pi-subagents-export.d.ts +143 -0
- package/dist/lib/pi-subagents-personas.d.ts +64 -0
- package/dist/schemas/systematic-config.schema.json +168 -3
- package/package.json +4 -3
- package/skills/using-systematic/references/pi-profile.md +28 -2
|
@@ -16206,6 +16206,58 @@ var CategoryOverlaySchema = exports_external.object({
|
|
|
16206
16206
|
description: "Per-category configuration overlay (same fields as agent minus disable)",
|
|
16207
16207
|
examples: [{ model: "anthropic/claude-opus-4-7", temperature: 0.1 }]
|
|
16208
16208
|
});
|
|
16209
|
+
var piSubagentsThinkingSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]).meta({
|
|
16210
|
+
description: "pi-subagents reasoning effort level for exported persona frontmatter",
|
|
16211
|
+
examples: ["off", "medium", "high"]
|
|
16212
|
+
});
|
|
16213
|
+
var piSubagentsMaxTurnsSchema = exports_external.number().int().nonnegative().meta({
|
|
16214
|
+
description: "pi-subagents maximum turns for a delegated persona (0 = unlimited)",
|
|
16215
|
+
examples: [0, 10, 25]
|
|
16216
|
+
});
|
|
16217
|
+
var piSubagentsToolsSchema = exports_external.string().min(1).meta({
|
|
16218
|
+
description: "pi-subagents comma-selector tool string (built-ins, */all/none, or extension selectors)",
|
|
16219
|
+
examples: ["*", "read,grep,glob", "none"]
|
|
16220
|
+
});
|
|
16221
|
+
var piSubagentsSkillsSchema = exports_external.union([exports_external.literal(true), exports_external.string().min(1)]).meta({
|
|
16222
|
+
description: "pi-subagents skills selector: true (all) or a comma-separated list of skill names",
|
|
16223
|
+
examples: [true, "ce:plan,ce:review"]
|
|
16224
|
+
});
|
|
16225
|
+
var PiSubagentsAgentOverlaySchema = exports_external.object({
|
|
16226
|
+
thinking: trustProtected(piSubagentsThinkingSchema).optional(),
|
|
16227
|
+
max_turns: trustAny(piSubagentsMaxTurnsSchema).optional(),
|
|
16228
|
+
tools: trustProtected(piSubagentsToolsSchema).optional(),
|
|
16229
|
+
skills: trustProtected(piSubagentsSkillsSchema).optional()
|
|
16230
|
+
}).strict().meta({
|
|
16231
|
+
description: "Per-agent pi-subagents export overlay (Pi-native fields only; no model)",
|
|
16232
|
+
examples: [{ thinking: "high", max_turns: 10 }]
|
|
16233
|
+
});
|
|
16234
|
+
var PiSubagentsCategoryOverlaySchema = PiSubagentsAgentOverlaySchema.meta({
|
|
16235
|
+
description: "Per-category pi-subagents export overlay (Pi-native fields only; no model)",
|
|
16236
|
+
examples: [{ thinking: "medium" }]
|
|
16237
|
+
});
|
|
16238
|
+
var PiSubagentsSchema = exports_external.object({
|
|
16239
|
+
categories: exports_external.record(exports_external.string(), PiSubagentsCategoryOverlaySchema).default({}).meta({
|
|
16240
|
+
description: "Per-category pi-subagents export overlays keyed by category name",
|
|
16241
|
+
examples: [{ research: { thinking: "high" } }, {}]
|
|
16242
|
+
}),
|
|
16243
|
+
agents: exports_external.record(exports_external.string(), PiSubagentsAgentOverlaySchema).default({}).meta({
|
|
16244
|
+
description: "Per-agent pi-subagents export overlays keyed by bundled agent name",
|
|
16245
|
+
examples: [{ "repo-research-analyst": { max_turns: 10 } }, {}]
|
|
16246
|
+
})
|
|
16247
|
+
}).strict().default({ categories: {}, agents: {} }).meta({
|
|
16248
|
+
description: "Pi-native pi-subagents export field overlays (thinking, max_turns, tools, skills). Category values apply first; per-agent values override. No model field \u2014 model stays in the categories/agents overlay.",
|
|
16249
|
+
examples: [
|
|
16250
|
+
{
|
|
16251
|
+
categories: { research: { thinking: "high" } },
|
|
16252
|
+
agents: { "repo-research-analyst": { max_turns: 10 } }
|
|
16253
|
+
}
|
|
16254
|
+
]
|
|
16255
|
+
});
|
|
16256
|
+
var PI_SUBAGENTS_PROTECTED_FIELDS = [
|
|
16257
|
+
"thinking",
|
|
16258
|
+
"tools",
|
|
16259
|
+
"skills"
|
|
16260
|
+
];
|
|
16209
16261
|
var BootstrapSchema = exports_external.object({
|
|
16210
16262
|
enabled: exports_external.boolean().default(true).meta({
|
|
16211
16263
|
description: "Enable bootstrap prompt injection into every conversation",
|
|
@@ -16276,6 +16328,7 @@ function createSystematicConfigSchema(opts) {
|
|
|
16276
16328
|
]
|
|
16277
16329
|
}),
|
|
16278
16330
|
workflow_guard: WorkflowGuardSchema,
|
|
16331
|
+
pi_subagents: PiSubagentsSchema,
|
|
16279
16332
|
skills_as_commands: exports_external.boolean().default(true).meta({
|
|
16280
16333
|
description: "Register skills discovered from user/project skill directories (OpenCode config and other agent-harness-standard locations) as slash commands. Default true.",
|
|
16281
16334
|
examples: [true, false]
|
|
@@ -16315,6 +16368,7 @@ var DEFAULT_CONFIG = {
|
|
|
16315
16368
|
},
|
|
16316
16369
|
agents: {},
|
|
16317
16370
|
categories: {},
|
|
16371
|
+
pi_subagents: { categories: {}, agents: {} },
|
|
16318
16372
|
skills_as_commands: true
|
|
16319
16373
|
};
|
|
16320
16374
|
var SECURITY_OVERLAY_FIELDS2 = new Set(SECURITY_OVERLAY_FIELDS);
|
|
@@ -16473,16 +16527,18 @@ function mergeArraysUnique(arr1, arr2) {
|
|
|
16473
16527
|
set2.add(item);
|
|
16474
16528
|
return Array.from(set2);
|
|
16475
16529
|
}
|
|
16476
|
-
function loadConfig(projectDir) {
|
|
16477
|
-
return loadConfigWithSources(projectDir).config;
|
|
16530
|
+
function loadConfig(projectDir, options) {
|
|
16531
|
+
return loadConfigWithSources(projectDir, options).config;
|
|
16478
16532
|
}
|
|
16479
|
-
function loadConfigWithSources(projectDir) {
|
|
16533
|
+
function loadConfigWithSources(projectDir, options) {
|
|
16534
|
+
const includeProject = options?.includeProject ?? true;
|
|
16480
16535
|
const paths = getConfigPaths(projectDir);
|
|
16481
16536
|
const userSource = loadConfigSource(paths.userConfig, "user");
|
|
16482
|
-
const projectSource = loadConfigSource(paths.projectConfig, "project");
|
|
16537
|
+
const projectSource = includeProject ? loadConfigSource(paths.projectConfig, "project") : null;
|
|
16483
16538
|
const customSource = paths.customConfig ? loadConfigSource(paths.customConfig, "custom") : null;
|
|
16484
16539
|
const sources = [userSource, projectSource, customSource].filter((source) => source !== null);
|
|
16485
16540
|
const mergedOverlays = mergeOverlaySources(sources);
|
|
16541
|
+
const mergedPiSubagentsOverlays = mergePiSubagentsOverlaySources(sources);
|
|
16486
16542
|
const droppedCategories = Object.keys(mergedOverlays.categories).filter((name) => REMOVED_AGENT_CATEGORIES_SET.has(name));
|
|
16487
16543
|
const warned = new Set;
|
|
16488
16544
|
warnDroppedNames(droppedCategories, "categories", warned, "v3.0.0");
|
|
@@ -16511,6 +16567,10 @@ function loadConfigWithSources(projectDir) {
|
|
|
16511
16567
|
},
|
|
16512
16568
|
agents: overlayValues(overlays.agents),
|
|
16513
16569
|
categories: overlayValues(overlays.categories),
|
|
16570
|
+
pi_subagents: {
|
|
16571
|
+
categories: overlayValues(mergedPiSubagentsOverlays.categories),
|
|
16572
|
+
agents: overlayValues(mergedPiSubagentsOverlays.agents)
|
|
16573
|
+
},
|
|
16514
16574
|
skills_as_commands: customConfig?.skills_as_commands ?? projectConfig?.skills_as_commands ?? userConfig?.skills_as_commands ?? DEFAULT_CONFIG.skills_as_commands
|
|
16515
16575
|
};
|
|
16516
16576
|
const droppedSkills = computeDroppedNames(result.disabled_skills, CURRENT_SKILL_NAMES_SET);
|
|
@@ -16577,6 +16637,57 @@ function preserveSecurityFields(previous, next) {
|
|
|
16577
16637
|
}
|
|
16578
16638
|
return result;
|
|
16579
16639
|
}
|
|
16640
|
+
var PI_SUBAGENTS_PROTECTED_FIELD_SET = new Set(PI_SUBAGENTS_PROTECTED_FIELDS);
|
|
16641
|
+
function mergePiSubagentsOverlaySources(sources) {
|
|
16642
|
+
const result = {
|
|
16643
|
+
agents: {},
|
|
16644
|
+
categories: {}
|
|
16645
|
+
};
|
|
16646
|
+
for (const source of sources) {
|
|
16647
|
+
mergePiSubagentsOverlayMap(result.agents, source, "agents");
|
|
16648
|
+
mergePiSubagentsOverlayMap(result.categories, source, "categories");
|
|
16649
|
+
}
|
|
16650
|
+
return result;
|
|
16651
|
+
}
|
|
16652
|
+
function stripPiSubagentsProtectedFields(value) {
|
|
16653
|
+
const result = {};
|
|
16654
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
16655
|
+
if (PI_SUBAGENTS_PROTECTED_FIELD_SET.has(field))
|
|
16656
|
+
continue;
|
|
16657
|
+
result[field] = fieldValue;
|
|
16658
|
+
}
|
|
16659
|
+
return result;
|
|
16660
|
+
}
|
|
16661
|
+
function preservePiSubagentsProtectedFields(previous, next) {
|
|
16662
|
+
const result = { ...next };
|
|
16663
|
+
for (const field of PI_SUBAGENTS_PROTECTED_FIELD_SET) {
|
|
16664
|
+
if (Object.hasOwn(previous, field)) {
|
|
16665
|
+
result[field] = previous[field];
|
|
16666
|
+
}
|
|
16667
|
+
}
|
|
16668
|
+
return result;
|
|
16669
|
+
}
|
|
16670
|
+
function mergePiSubagentsOverlayMap(target, source, mapKey) {
|
|
16671
|
+
const overlayMap = source.config.pi_subagents?.[mapKey];
|
|
16672
|
+
if (overlayMap === undefined)
|
|
16673
|
+
return;
|
|
16674
|
+
if (!isRecord2(overlayMap)) {
|
|
16675
|
+
throwInvalidOverlay(source.path, `pi_subagents.${mapKey}`);
|
|
16676
|
+
}
|
|
16677
|
+
for (const [key, rawValue] of Object.entries(overlayMap)) {
|
|
16678
|
+
const keyPath = `pi_subagents.${mapKey}.${key}`;
|
|
16679
|
+
if (!isRecord2(rawValue)) {
|
|
16680
|
+
throwInvalidOverlay(source.path, keyPath);
|
|
16681
|
+
}
|
|
16682
|
+
const previous = target[key];
|
|
16683
|
+
const value = source.trust === "project" ? preservePiSubagentsProtectedFields(previous?.value ?? {}, stripPiSubagentsProtectedFields(rawValue)) : rawValue;
|
|
16684
|
+
target[key] = {
|
|
16685
|
+
value,
|
|
16686
|
+
sourcePath: source.path,
|
|
16687
|
+
keyPath
|
|
16688
|
+
};
|
|
16689
|
+
}
|
|
16690
|
+
}
|
|
16580
16691
|
function overlayValues(overlays) {
|
|
16581
16692
|
const result = {};
|
|
16582
16693
|
for (const [key, overlay] of Object.entries(overlays)) {
|
package/dist/index.js
CHANGED
|
@@ -87,6 +87,72 @@ export declare const CategoryOverlaySchema: z.ZodObject<{
|
|
|
87
87
|
deny: "deny";
|
|
88
88
|
}>>]>>>;
|
|
89
89
|
}, z.core.$strict>;
|
|
90
|
+
export declare const PiSubagentsAgentOverlaySchema: z.ZodObject<{
|
|
91
|
+
thinking: z.ZodOptional<z.ZodEnum<{
|
|
92
|
+
off: "off";
|
|
93
|
+
minimal: "minimal";
|
|
94
|
+
low: "low";
|
|
95
|
+
medium: "medium";
|
|
96
|
+
high: "high";
|
|
97
|
+
xhigh: "xhigh";
|
|
98
|
+
max: "max";
|
|
99
|
+
}>>;
|
|
100
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
tools: z.ZodOptional<z.ZodString>;
|
|
102
|
+
skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodString]>>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
export declare const PiSubagentsCategoryOverlaySchema: z.ZodObject<{
|
|
105
|
+
thinking: z.ZodOptional<z.ZodEnum<{
|
|
106
|
+
off: "off";
|
|
107
|
+
minimal: "minimal";
|
|
108
|
+
low: "low";
|
|
109
|
+
medium: "medium";
|
|
110
|
+
high: "high";
|
|
111
|
+
xhigh: "xhigh";
|
|
112
|
+
max: "max";
|
|
113
|
+
}>>;
|
|
114
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
115
|
+
tools: z.ZodOptional<z.ZodString>;
|
|
116
|
+
skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodString]>>;
|
|
117
|
+
}, z.core.$strict>;
|
|
118
|
+
export declare const PiSubagentsSchema: z.ZodDefault<z.ZodObject<{
|
|
119
|
+
categories: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
120
|
+
thinking: z.ZodOptional<z.ZodEnum<{
|
|
121
|
+
off: "off";
|
|
122
|
+
minimal: "minimal";
|
|
123
|
+
low: "low";
|
|
124
|
+
medium: "medium";
|
|
125
|
+
high: "high";
|
|
126
|
+
xhigh: "xhigh";
|
|
127
|
+
max: "max";
|
|
128
|
+
}>>;
|
|
129
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
tools: z.ZodOptional<z.ZodString>;
|
|
131
|
+
skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodString]>>;
|
|
132
|
+
}, z.core.$strict>>>;
|
|
133
|
+
agents: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
134
|
+
thinking: z.ZodOptional<z.ZodEnum<{
|
|
135
|
+
off: "off";
|
|
136
|
+
minimal: "minimal";
|
|
137
|
+
low: "low";
|
|
138
|
+
medium: "medium";
|
|
139
|
+
high: "high";
|
|
140
|
+
xhigh: "xhigh";
|
|
141
|
+
max: "max";
|
|
142
|
+
}>>;
|
|
143
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
144
|
+
tools: z.ZodOptional<z.ZodString>;
|
|
145
|
+
skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodString]>>;
|
|
146
|
+
}, z.core.$strict>>>;
|
|
147
|
+
}, z.core.$strict>>;
|
|
148
|
+
/**
|
|
149
|
+
* Fields in PiSubagentsAgentOverlaySchema/PiSubagentsCategoryOverlaySchema that
|
|
150
|
+
* require a project-or-higher trust source. Project config cannot grant
|
|
151
|
+
* `thinking`, `tools`, or `skills` to an exported persona; `max_turns` is
|
|
152
|
+
* trust-any. Mirrors the hand-coded `PI_SUBAGENTS_PROTECTED_FIELDS` set in
|
|
153
|
+
* `src/lib/config.ts`.
|
|
154
|
+
*/
|
|
155
|
+
export declare const PI_SUBAGENTS_PROTECTED_FIELDS: readonly string[];
|
|
90
156
|
export declare const BootstrapSchema: z.ZodObject<{
|
|
91
157
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
92
158
|
file: z.ZodOptional<z.ZodString>;
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface SourceAwareConfigResult {
|
|
|
22
22
|
config: SystematicConfig;
|
|
23
23
|
overlays: SourcedOverlayConfigMap;
|
|
24
24
|
}
|
|
25
|
+
export interface PiSubagentsOverlayMap {
|
|
26
|
+
categories?: OverlayConfigMap;
|
|
27
|
+
agents?: OverlayConfigMap;
|
|
28
|
+
}
|
|
25
29
|
export interface SystematicConfig {
|
|
26
30
|
disabled_skills: string[];
|
|
27
31
|
disabled_agents: string[];
|
|
@@ -30,6 +34,7 @@ export interface SystematicConfig {
|
|
|
30
34
|
workflow_guard: WorkflowGuardConfig;
|
|
31
35
|
agents?: OverlayConfigMap;
|
|
32
36
|
categories?: OverlayConfigMap;
|
|
37
|
+
pi_subagents?: PiSubagentsOverlayMap;
|
|
33
38
|
skills_as_commands: boolean;
|
|
34
39
|
}
|
|
35
40
|
export declare const DEFAULT_CONFIG: SystematicConfig;
|
|
@@ -47,8 +52,17 @@ export declare function computeDroppedNames(names: readonly string[], allowedSet
|
|
|
47
52
|
* Passing a fresh set per load ensures no cross-load suppression.
|
|
48
53
|
*/
|
|
49
54
|
export declare function warnDroppedNames(dropped: string[], field: string, warned: Set<string>, removalVersion?: string): void;
|
|
50
|
-
export
|
|
51
|
-
|
|
55
|
+
export interface LoadConfigOptions {
|
|
56
|
+
/**
|
|
57
|
+
* When false, the project-level config source (`<cwd>/.opencode/systematic.json`)
|
|
58
|
+
* is not loaded at all — not merged, not trust-stripped, entirely absent from
|
|
59
|
+
* the source chain. Used by global-scoped pi-subagents export so it never
|
|
60
|
+
* absorbs cwd project overlays (plan R7/R19). Defaults to true.
|
|
61
|
+
*/
|
|
62
|
+
includeProject?: boolean;
|
|
63
|
+
}
|
|
64
|
+
export declare function loadConfig(projectDir: string, options?: LoadConfigOptions): SystematicConfig;
|
|
65
|
+
export declare function loadConfigWithSources(projectDir: string, options?: LoadConfigOptions): SourceAwareConfigResult;
|
|
52
66
|
export declare function getConfigPaths(projectDir: string): {
|
|
53
67
|
customConfig?: string | undefined;
|
|
54
68
|
customDir?: string | undefined;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-subagents export lifecycle: resolve, preview, export, refresh, cleanup.
|
|
3
|
+
*
|
|
4
|
+
* Writes user-chosen agents dirs ($PI_CODING_AGENT_DIR/agents or
|
|
5
|
+
* <cwd>/.pi/agents). Batch-transactional with rollback: if any file write or
|
|
6
|
+
* the manifest write fails, the operation rolls back to the pre-operation
|
|
7
|
+
* state. Rollback reports any restoration failures explicitly.
|
|
8
|
+
*
|
|
9
|
+
* Manifest tracks ownership. Malformed or hostile manifests cause every
|
|
10
|
+
* lifecycle verb to refuse before mutation. No writes from module import.
|
|
11
|
+
*/
|
|
12
|
+
export type ExportScope = 'project' | 'global';
|
|
13
|
+
/**
|
|
14
|
+
* Scope-appropriate config resolution for export/preview/refresh. When
|
|
15
|
+
* provided, the effective config (`user → project → custom` for project
|
|
16
|
+
* scope; `user → custom` for global scope — never absorbing cwd project
|
|
17
|
+
* overlays) is applied to each exported persona's frontmatter: `model`
|
|
18
|
+
* resolved from the `categories`/`agents` overlay (per-agent beats category;
|
|
19
|
+
* `model: null` omits) and Pi-native `pi_subagents` fields (`thinking`,
|
|
20
|
+
* `max_turns`, `tools`, `skills`) resolved from the `pi_subagents`
|
|
21
|
+
* namespace after trust filtering. Omitting `configOptions` preserves the
|
|
22
|
+
* model-free, config-neutral export (backward compatible default).
|
|
23
|
+
*/
|
|
24
|
+
export interface ExportConfigOptions {
|
|
25
|
+
scope: ExportScope;
|
|
26
|
+
cwd: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const MANIFEST_FILENAME = ".systematic-personas.json";
|
|
29
|
+
/** Exclusive per-root mutation lock. Guards export/refresh/cleanup; preview is lock-free. */
|
|
30
|
+
export declare const LOCK_FILENAME = ".systematic-personas.lock";
|
|
31
|
+
export interface ManifestFileEntry {
|
|
32
|
+
filename: string;
|
|
33
|
+
hash: string;
|
|
34
|
+
status: 'exported' | 'exported-with-warning';
|
|
35
|
+
}
|
|
36
|
+
export interface PiSubagentsManifest {
|
|
37
|
+
generatedAt: string;
|
|
38
|
+
agentsRoot: string;
|
|
39
|
+
files: ManifestFileEntry[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strict manifest read result — distinguishes absent from malformed.
|
|
43
|
+
*
|
|
44
|
+
* absent → no manifest file; operations proceed as first-export.
|
|
45
|
+
* ok → valid manifest, returned in `manifest`.
|
|
46
|
+
* malformed → manifest file exists but is invalid (bad JSON, wrong schema,
|
|
47
|
+
* duplicate filenames, unsafe filenames). Operations must refuse.
|
|
48
|
+
*/
|
|
49
|
+
export type ManifestReadResult = {
|
|
50
|
+
kind: 'absent';
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'ok';
|
|
53
|
+
manifest: PiSubagentsManifest;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'malformed';
|
|
56
|
+
error: string;
|
|
57
|
+
};
|
|
58
|
+
export declare function resolveAgentsRoot(scope: ExportScope, cwd: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the safety anchor for a scope: the topmost directory whose
|
|
61
|
+
* descendants (down to agentsRoot) are walked and lstat-checked for
|
|
62
|
+
* symlinks/non-directories. Never inspects ancestors above this anchor
|
|
63
|
+
* (avoids false positives from OS-level ancestor symlinks, e.g. macOS
|
|
64
|
+
* `/var` -> `/private/var`).
|
|
65
|
+
*
|
|
66
|
+
* - project: cwd
|
|
67
|
+
* - global with PI_CODING_AGENT_DIR set: the env dir's PARENT (so the env
|
|
68
|
+
* dir itself is included in the walk and checked)
|
|
69
|
+
* - global without PI_CODING_AGENT_DIR: homedir
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveAnchor(scope: ExportScope, cwd: string): string;
|
|
72
|
+
export declare function readManifestStrict(agentsRoot: string): ManifestReadResult;
|
|
73
|
+
/**
|
|
74
|
+
* Convenience wrapper: returns the manifest for 'ok', null for 'absent', throws
|
|
75
|
+
* a structured Error for 'malformed'. Used by callers that already distinguished absent.
|
|
76
|
+
*/
|
|
77
|
+
export declare function readManifest(agentsRoot: string): PiSubagentsManifest | null;
|
|
78
|
+
export declare function writeManifest(agentsRoot: string, manifest: PiSubagentsManifest): void;
|
|
79
|
+
export interface TxResult {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
error?: string;
|
|
82
|
+
rollbackFailed: string[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Run a sequence of filesystem operations under snapshot/rollback protection.
|
|
86
|
+
*
|
|
87
|
+
* 1. Snapshot `pathsToWatch` (current content or absent marker).
|
|
88
|
+
* 2. Execute each `op` in order. On the first throw, stop.
|
|
89
|
+
* 3. On any failure: restore all watched paths to their snapshotted state.
|
|
90
|
+
* Returns `{ ok: false, error, rollbackFailed }` — `rollbackFailed` lists
|
|
91
|
+
* paths whose restoration itself failed (partial rollback, reported honestly).
|
|
92
|
+
* 4. On full success: returns `{ ok: true, rollbackFailed: [] }`.
|
|
93
|
+
*
|
|
94
|
+
* Exported for direct testing; also used by all production commit/delete paths.
|
|
95
|
+
*/
|
|
96
|
+
export declare function runWithRollback(pathsToWatch: string[], ops: Array<() => void>): TxResult;
|
|
97
|
+
export type PlanAction = {
|
|
98
|
+
action: 'create';
|
|
99
|
+
filename: string;
|
|
100
|
+
} | {
|
|
101
|
+
action: 'update';
|
|
102
|
+
filename: string;
|
|
103
|
+
} | {
|
|
104
|
+
action: 'refuse';
|
|
105
|
+
filename: string;
|
|
106
|
+
reason: string;
|
|
107
|
+
} | {
|
|
108
|
+
action: 'remove';
|
|
109
|
+
filename: string;
|
|
110
|
+
} | {
|
|
111
|
+
action: 'skip';
|
|
112
|
+
filename: string;
|
|
113
|
+
};
|
|
114
|
+
export interface ExportPlan {
|
|
115
|
+
status: 'ok' | 'error';
|
|
116
|
+
error?: string;
|
|
117
|
+
agentsRoot: string;
|
|
118
|
+
actions: PlanAction[];
|
|
119
|
+
}
|
|
120
|
+
export declare function preview(agentsRoot: string, configOptions?: ExportConfigOptions): ExportPlan;
|
|
121
|
+
export interface ExportResult {
|
|
122
|
+
status: 'ok' | 'error';
|
|
123
|
+
written: number;
|
|
124
|
+
skipped: number;
|
|
125
|
+
refused: Array<{
|
|
126
|
+
filename: string;
|
|
127
|
+
reason: string;
|
|
128
|
+
}>;
|
|
129
|
+
error?: string;
|
|
130
|
+
}
|
|
131
|
+
export declare function exportPersonas(agentsRoot: string, configOptions?: ExportConfigOptions): ExportResult;
|
|
132
|
+
export interface RefreshResult {
|
|
133
|
+
status: 'ok' | 'error';
|
|
134
|
+
updated: number;
|
|
135
|
+
skippedUnowned: number;
|
|
136
|
+
error?: string;
|
|
137
|
+
}
|
|
138
|
+
export declare function refresh(agentsRoot: string, configOptions?: ExportConfigOptions): RefreshResult;
|
|
139
|
+
export interface CleanupResult {
|
|
140
|
+
status: 'ok' | 'error';
|
|
141
|
+
error?: string;
|
|
142
|
+
}
|
|
143
|
+
export declare function cleanup(agentsRoot: string, configOptions?: ExportConfigOptions): CleanupResult;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure persona generation logic for pi-subagents interop.
|
|
3
|
+
*
|
|
4
|
+
* Contains the curated persona list, compatibility screening, content
|
|
5
|
+
* generation, and generateAll(). Importable from both src/ and scripts/.
|
|
6
|
+
* No filesystem writes; no CLI entrypoint.
|
|
7
|
+
*/
|
|
8
|
+
/** Compatibility severity: info = fully usable, warning = may differ, critical = excluded. */
|
|
9
|
+
export type CompatibilitySeverity = 'info' | 'warning' | 'critical';
|
|
10
|
+
/** Result of classifying a persona's pi-subagents compatibility. */
|
|
11
|
+
export interface CompatibilityStatus {
|
|
12
|
+
severity: CompatibilitySeverity;
|
|
13
|
+
/** Human-readable reasons for the severity (empty for info). */
|
|
14
|
+
reasons: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Per-persona entry in the manifest. */
|
|
17
|
+
export interface ManifestEntry {
|
|
18
|
+
/** Emitted filename, e.g. `systematic-best-practices-researcher.md`. */
|
|
19
|
+
filename: string;
|
|
20
|
+
/** Export status. */
|
|
21
|
+
status: 'exported' | 'exported-with-warning' | 'excluded-critical';
|
|
22
|
+
/** Repo-relative source path, e.g. `agents/research/best-practices-researcher.md`. */
|
|
23
|
+
sourceRelPath: string;
|
|
24
|
+
/** SHA-256 hex of the generated content (only for exported entries). */
|
|
25
|
+
hash: string;
|
|
26
|
+
/** Generated content (only for exported entries). */
|
|
27
|
+
content?: string;
|
|
28
|
+
/** Human-readable reason (for excluded-critical and exported-with-warning). */
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The authoritative curated-include list with per-persona compatibility
|
|
33
|
+
* rationale. Only personas in this list are candidates for export.
|
|
34
|
+
*
|
|
35
|
+
* Exclusion rationale (not in list):
|
|
36
|
+
* - agents/workflow/systematic-implementer.md — CRITICAL: dispatched-by-parent assumption.
|
|
37
|
+
* - agents/design/design-iterator.md — CRITICAL: requires agent-browser + skill load.
|
|
38
|
+
* - agents/review/agent-native-reviewer.md — CRITICAL: Systematic/OpenCode-specific context.
|
|
39
|
+
* - agents/review/project-standards-reviewer.md — CRITICAL: requires orchestrator <standards-paths>.
|
|
40
|
+
* - agents/review/kieran-typescript-reviewer.md — WARNING: excluded by plan recommendation.
|
|
41
|
+
* - agents/research/slack-researcher.md — CRITICAL: requires Slack MCP environment.
|
|
42
|
+
* - agents/research/learnings-researcher.md — CRITICAL: references Systematic skill paths.
|
|
43
|
+
* - agents/workflow/pr-comment-resolver.md — WARNING: "Spawned by the resolve-pr-feedback skill".
|
|
44
|
+
*/
|
|
45
|
+
export interface CuratedPersonaEntry {
|
|
46
|
+
relPath: string;
|
|
47
|
+
rationale: string;
|
|
48
|
+
}
|
|
49
|
+
export declare const CURATED_PERSONAS: CuratedPersonaEntry[];
|
|
50
|
+
/**
|
|
51
|
+
* Sanitize a persona name for use as a pi-subagents filename stem.
|
|
52
|
+
* Returns empty string if no safe characters remain — callers must reject
|
|
53
|
+
* empty to avoid producing `systematic-.md`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function sanitizeName(name: string): string;
|
|
56
|
+
export declare function classifyCompatibility(content: string): CompatibilityStatus;
|
|
57
|
+
export declare function generatePersonaContent(sourceRef: string, rawContent: string): string | null;
|
|
58
|
+
export declare function generatePersonaManifest(sourceRelPath: string, rawContent: string, _repoRoot: string): ManifestEntry;
|
|
59
|
+
/**
|
|
60
|
+
* Generate all curated personas from repoRoot/agents/.
|
|
61
|
+
* Throws on collision, new critical coupling, or read errors.
|
|
62
|
+
* Pure — no writes.
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateAll(repoRoot: string): ManifestEntry[];
|