@mediadatafusion/pi-workflow-suite 0.0.1
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/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +9 -0
- package/LICENSE.md +201 -0
- package/NOTICE +6 -0
- package/README.md +1208 -0
- package/SECURITY.md +7 -0
- package/SUPPORT.md +9 -0
- package/TRADEMARKS.md +14 -0
- package/VERSION +1 -0
- package/agents/codebase-research.md +42 -0
- package/agents/general-worker.md +26 -0
- package/agents/implementation-planning.md +46 -0
- package/agents/quality-validation.md +43 -0
- package/agents/workflow-orchestrator.md +44 -0
- package/config/prompts/execute-approved-plan.md +43 -0
- package/config/prompts/mission-checkpoint.md +26 -0
- package/config/prompts/mission-final-validation.md +21 -0
- package/config/prompts/mission-plan.md +129 -0
- package/config/prompts/mission-repair.md +33 -0
- package/config/prompts/mission-run.md +37 -0
- package/config/prompts/validate-approved-plan.md +42 -0
- package/config/prompts/workflow-plan-prompt.md +93 -0
- package/config/prompts/workflow-repair.md +20 -0
- package/config/prompts/workflow-summary.md +23 -0
- package/config/workflow-settings.example.json +335 -0
- package/docs/assets/mediadatafusion-logo.png +0 -0
- package/docs/assets/pi-workflow-suite-card.png +0 -0
- package/docs/assets/pi-workflow-suite-header.png +0 -0
- package/docs/assets/pi-workflow-suite-video-thumb.png +0 -0
- package/docs/assets/readme-link-commands.svg +10 -0
- package/docs/assets/readme-link-install.svg +10 -0
- package/docs/assets/readme-link-quick-start.svg +10 -0
- package/docs/assets/readme-link-settings.svg +10 -0
- package/extensions/subagent/agents.ts +149 -0
- package/extensions/subagent/index.ts +1136 -0
- package/extensions/subagent/runner.ts +291 -0
- package/extensions/workflow-model-router.ts +1485 -0
- package/extensions/workflow-modes.ts +14778 -0
- package/extensions/workflow-parsers.ts +212 -0
- package/extensions/workflow-settings-capabilities.ts +282 -0
- package/extensions/workflow-state.ts +978 -0
- package/extensions/workflow-subagent-policy.ts +180 -0
- package/extensions/workflow-summary.ts +381 -0
- package/extensions/workflow-tool-guard.ts +302 -0
- package/extensions/workflow-validation-classifier.ts +102 -0
- package/extensions/workflow-web-tools.ts +356 -0
- package/package.json +1 -0
- package/scripts/audit-live.sh +69 -0
- package/scripts/audit-settings.sh +136 -0
- package/scripts/backup-live.sh +63 -0
- package/scripts/bootstrap-project.sh +220 -0
- package/scripts/install-to-live.sh +87 -0
- package/scripts/quarantine-live-junk.sh +69 -0
- package/scripts/verify-live.sh +128 -0
- package/skills/codebase-discovery/SKILL.md +20 -0
- package/skills/find-skills/SKILL.md +155 -0
- package/skills/git-safe-summary/SKILL.md +20 -0
- package/skills/implementation-planning/SKILL.md +20 -0
- package/skills/project-rules-audit/SKILL.md +20 -0
- package/skills/safe-execution/SKILL.md +20 -0
- package/skills/validation-review/SKILL.md +20 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsing and formatting helpers for Pi Workflow Suite.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from workflow-modes.ts so they are independently testable
|
|
5
|
+
* without importing the full extension entry point.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ClarificationAnswer, ClarificationQuestion, MissionAutonomy, PlanValidationStatus, WorkflowState } from "./workflow-state.js";
|
|
9
|
+
import type { StandardClarificationMode, StandardModelRole, StandardTodoTriggerMode, WorkflowAgentScope, WorkflowSettingsScope } from "./workflow-model-router.js";
|
|
10
|
+
|
|
11
|
+
// ── Command / settings parse helpers ─────────────────────────────
|
|
12
|
+
|
|
13
|
+
export function parseScope(value: string | undefined): WorkflowSettingsScope | undefined {
|
|
14
|
+
return value === "global" || value === "project" ? value : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseBool(value: string | undefined): boolean | undefined {
|
|
18
|
+
if (value === "true" || value === "on" || value === "enabled" || value === "yes") return true;
|
|
19
|
+
if (value === "false" || value === "off" || value === "disabled" || value === "no") return false;
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parsePlanningDepth(value: string | undefined): "fast" | "standard" | "deep" | "maximum" | undefined {
|
|
24
|
+
return value === "fast" || value === "standard" || value === "deep" || value === "maximum" ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parseClarificationMode(value: string | undefined): "auto" | "always_for_nontrivial" | "never" | undefined {
|
|
28
|
+
return value === "auto" || value === "always_for_nontrivial" || value === "never" ? value : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseStandardTodoTriggerMode(value: string | undefined): StandardTodoTriggerMode | undefined {
|
|
32
|
+
const normalized = value?.trim().toLowerCase().replace(/[()]/g, "").replace(/[\s_-]+/g, " ");
|
|
33
|
+
if (!normalized) return undefined;
|
|
34
|
+
if (normalized === "off" || normalized === "disabled") return "off";
|
|
35
|
+
if (normalized === "manual" || normalized === "on request" || normalized === "only when requested" || normalized === "only when explicitly requested") return "manual";
|
|
36
|
+
if (normalized === "auto" || normalized === "automatic" || normalized === "automatic when useful" || normalized === "when useful") return "auto";
|
|
37
|
+
if (normalized === "required" || normalized === "require" || normalized === "always") return "required";
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseStandardClarificationMode(value: string | undefined): StandardClarificationMode | undefined {
|
|
42
|
+
const normalized = value?.trim().toLowerCase().replace(/[()]/g, "").replace(/[\s-]+/g, "_");
|
|
43
|
+
if (!normalized) return undefined;
|
|
44
|
+
if (normalized === "off" || normalized === "never" || normalized === "disabled") return "never";
|
|
45
|
+
if (normalized === "minimal" || normalized === "auto" || normalized === "automatic") return "auto";
|
|
46
|
+
if (normalized === "always_for_nontrivial" || normalized === "always" || normalized === "required" || normalized === "force" || normalized === "forced") return "always_for_nontrivial";
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function parseStandardModelRole(value: string | undefined): StandardModelRole | undefined {
|
|
51
|
+
const normalized = value?.trim().toLowerCase().replace(/\s+pi\s+model/g, "").replace(/[ -]+/g, "_");
|
|
52
|
+
if (normalized === "current") return "current";
|
|
53
|
+
return normalized === "planner" || normalized === "executor" || normalized === "reviewer" || normalized === "validator" ? normalized : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function parseWorkflowAgentScope(value: string | undefined): WorkflowAgentScope | undefined {
|
|
57
|
+
return value === "user" || value === "project" || value === "both" ? value : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function parseClarificationTiming(value: string | undefined): "immediate" | "after_initial_analysis" | undefined {
|
|
61
|
+
return value === "immediate" || value === "after_initial_analysis" ? value : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function parseSubagentPolicy(value: string | undefined): "off" | "auto" | "deep" | "maximum" | "forced" | undefined {
|
|
65
|
+
return value === "off" || value === "auto" || value === "deep" || value === "maximum" || value === "forced" ? value : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function parseSubagentPlanningPolicy(value: string | undefined): "off" | "auto" | "deep" | "maximum" | "forced" | undefined {
|
|
69
|
+
return parseSubagentPolicy(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function parseEditConcurrencyMode(value: string | undefined): "sequential" | "scoped" | "blocked" | undefined {
|
|
73
|
+
return value === "sequential" || value === "scoped" || value === "blocked" ? value : undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function parsePlanningOrchestrationPolicy(value: string | undefined): "off" | "auto" | "orchestrator_first" | "forced_orchestrated" | undefined {
|
|
77
|
+
return value === "off" || value === "auto" || value === "orchestrator_first" || value === "forced_orchestrated" ? value : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseCompactionMode(value: string | undefined): "pi_default" | "custom_model" | "custom_agent" | "disabled" | undefined {
|
|
81
|
+
const normalized = value?.trim().toLowerCase().replace(/[ -]+/g, "_");
|
|
82
|
+
return normalized === "pi_default" || normalized === "custom_model" || normalized === "custom_agent" || normalized === "disabled" ? normalized : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function parseWorkflowCompactionCheckMode(value: string | undefined): "boundary" | "in_session" | undefined {
|
|
86
|
+
const normalized = value?.trim().toLowerCase().replace(/[ -]+/g, "_");
|
|
87
|
+
if (normalized === "boundary" || normalized === "boundary_only") return "boundary";
|
|
88
|
+
if (normalized === "in_session" || normalized === "session") return "in_session";
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function parseMissionAutonomy(value: string | undefined): MissionAutonomy | undefined {
|
|
93
|
+
return value === "manual" || value === "approval_gated" || value === "supervised_auto" || value === "full_auto" ? value : undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function parseValidationRetryMode(value: string | undefined): "off" | "safe_only" | "aggressive_within_scope" | undefined {
|
|
97
|
+
return value === "off" || value === "safe_only" || value === "aggressive_within_scope" ? value : undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function parsePositiveInt(value: string | undefined): number | undefined {
|
|
101
|
+
if (!value || !/^\d+$/.test(value)) return undefined;
|
|
102
|
+
const parsed = Number(value);
|
|
103
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function updatedMessage(scope: WorkflowSettingsScope, file: string, setting: string, value: string): string {
|
|
107
|
+
return `# Workflow Setting Updated\n\nUpdated:\nScope: ${scope}\nFile: ${file}\nSetting: ${setting}\nNew Value: ${value}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Clarification parsing ────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse clarifying questions from the planner's response text.
|
|
114
|
+
* Tolerates the canonical ### Q1. format plus older Q1., 1., and ## Question 1 forms.
|
|
115
|
+
*/
|
|
116
|
+
export function parseClarifyingQuestions(text: string): ClarificationQuestion[] {
|
|
117
|
+
const questions: ClarificationQuestion[] = [];
|
|
118
|
+
const lines = text.split("\n");
|
|
119
|
+
let currentQ: { index: number; question: string; options: string[] } | null = null;
|
|
120
|
+
|
|
121
|
+
for (const rawLine of lines) {
|
|
122
|
+
const line = rawLine.trim();
|
|
123
|
+
if (!line || /^skip this question$/i.test(line) || /^clarifying questions:?$/i.test(line) || /^#+\s*clarifying questions:?$/i.test(line)) continue;
|
|
124
|
+
const qMatch = line.match(/^(?:#{1,3}\s*)?(?:(?:Question|Q)\s*)?(\d+)\.?\s*[:.)-]?\s*(.*)$/i);
|
|
125
|
+
if (qMatch && !/^[A-D][.:)]/i.test(line)) {
|
|
126
|
+
if (currentQ && currentQ.options.length > 0) questions.push(currentQ);
|
|
127
|
+
currentQ = { index: parseInt(qMatch[1], 10), question: qMatch[2].trim(), options: [] };
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (currentQ && !currentQ.question && !/^[A-D][.:)]/i.test(line)) {
|
|
131
|
+
currentQ.question = line;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const optMatch = line.match(/^([A-D])[.:)]\s*(.+)/i);
|
|
135
|
+
if (optMatch && currentQ) currentQ.options.push(`${optMatch[1].toUpperCase()}. ${optMatch[2].trim()}`);
|
|
136
|
+
}
|
|
137
|
+
if (currentQ && currentQ.options.length > 0) questions.push(currentQ);
|
|
138
|
+
return questions;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse a user's shorthand answer like "1A, 2C, 3D: custom text" or "1S" to skip.
|
|
143
|
+
* Returns array of { index, letter, custom?, skipped? } objects.
|
|
144
|
+
*/
|
|
145
|
+
export function parseShorthandAnswers(input: string): ClarificationAnswer[] {
|
|
146
|
+
const answers: ClarificationAnswer[] = [];
|
|
147
|
+
const cleaned = input.trim();
|
|
148
|
+
|
|
149
|
+
let customSuffix = "";
|
|
150
|
+
const colonMatch = cleaned.match(/:\s*([^,]+)\s*$/);
|
|
151
|
+
if (colonMatch) {
|
|
152
|
+
const beforeColon = cleaned.slice(0, cleaned.lastIndexOf(":"));
|
|
153
|
+
const lastAnswer = beforeColon.trim().split(/[\s,]+/).pop() ?? "";
|
|
154
|
+
if (/^[1-9][A-DSa-ds]$/.test(lastAnswer)) {
|
|
155
|
+
customSuffix = colonMatch[1].trim();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const pairs = cleaned.matchAll(/\b(\d)([A-DSa-ds])\b/g);
|
|
160
|
+
const seen = new Map<number, ClarificationAnswer>();
|
|
161
|
+
const matchList = [...pairs];
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < matchList.length; i++) {
|
|
164
|
+
const m = matchList[i];
|
|
165
|
+
const idx = parseInt(m[1]);
|
|
166
|
+
const letter = m[2].toUpperCase();
|
|
167
|
+
const answer: ClarificationAnswer = letter === "S" ? { index: idx, letter, skipped: true } : { index: idx, letter };
|
|
168
|
+
if (i === matchList.length - 1 && customSuffix) {
|
|
169
|
+
answer.custom = customSuffix;
|
|
170
|
+
}
|
|
171
|
+
seen.set(idx, answer);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const a of seen.values()) {
|
|
175
|
+
answers.push(a);
|
|
176
|
+
}
|
|
177
|
+
return answers.sort((a, b) => a.index - b.index);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Format parsed answers into a readable summary for the planner.
|
|
182
|
+
*/
|
|
183
|
+
export function formatAnswersForPlanner(questions: ClarificationQuestion[], answers: ClarificationAnswer[]): string {
|
|
184
|
+
const lines: string[] = ["User's answers to clarifying questions:"];
|
|
185
|
+
for (const q of questions) {
|
|
186
|
+
const answer = answers.find(a => a.index === q.index);
|
|
187
|
+
if (answer) {
|
|
188
|
+
if (answer.skipped || answer.letter === "S") {
|
|
189
|
+
lines.push(`${q.index}. ${q.question}\n → skipped by user`);
|
|
190
|
+
} else if (answer.custom) {
|
|
191
|
+
lines.push(`${q.index}. ${q.question}\n → ${answer.letter} (Custom): ${answer.custom}`);
|
|
192
|
+
} else {
|
|
193
|
+
const optionText = q.options.find(o => o.startsWith(`${answer.letter}.`));
|
|
194
|
+
lines.push(`${q.index}. ${q.question}\n → ${optionText ?? answer.letter}`);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
lines.push(`${q.index}. ${q.question}\n → (no answer provided)`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return lines.join("\n");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Validation verdict helpers ────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
export function planValidationStatusForVerdict(verdict: WorkflowState["validationVerdict"]): PlanValidationStatus {
|
|
206
|
+
if (verdict === "PASS") return "pass";
|
|
207
|
+
if (verdict === "UNKNOWN" || verdict === "PARTIAL PASS") return "unknown";
|
|
208
|
+
return "fail";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// No-op default export so this helper module can be safely auto-discovered as a Pi extension.
|
|
212
|
+
export default function workflowSuiteNoopExtension(): void {}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import type { WorkflowSettings } from "./workflow-model-router.js";
|
|
2
|
+
|
|
3
|
+
export type WorkflowSettingCapabilityStatus = "wired" | "partial" | "conflict" | "future" | "unclear";
|
|
4
|
+
|
|
5
|
+
export interface WorkflowSettingCapability {
|
|
6
|
+
path: string;
|
|
7
|
+
domain: "models" | "workflow" | "standard" | "missions" | "safety" | "ui" | "subagents" | "planning" | "context" | "presets";
|
|
8
|
+
intent: string;
|
|
9
|
+
owner: string;
|
|
10
|
+
status: WorkflowSettingCapabilityStatus;
|
|
11
|
+
related: string[];
|
|
12
|
+
risk: string;
|
|
13
|
+
action: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type CapabilityFactory = (settings: WorkflowSettings) => WorkflowSettingCapability;
|
|
17
|
+
|
|
18
|
+
function capability(entry: WorkflowSettingCapability): WorkflowSettingCapability {
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function conflictWhen(condition: boolean, entry: WorkflowSettingCapability, conflictAction: string): WorkflowSettingCapability {
|
|
23
|
+
return condition ? { ...entry, status: "conflict", action: conflictAction } : entry;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PLAN_MODE_CAPABILITIES: CapabilityFactory[] = [
|
|
27
|
+
(settings) => conflictWhen(
|
|
28
|
+
settings.workflow.requireApprovalBeforeExecution !== undefined && settings.workflow.requireApprovalBeforeExecution !== settings.workflow.requirePlanApprovalBeforeExecute,
|
|
29
|
+
capability({
|
|
30
|
+
path: "workflow.requirePlanApprovalBeforeExecute",
|
|
31
|
+
domain: "workflow",
|
|
32
|
+
intent: "Require an explicit user approval gate before Plan Mode execution can begin.",
|
|
33
|
+
owner: "beginExecution / approveCurrentPlan",
|
|
34
|
+
status: "wired",
|
|
35
|
+
related: ["workflow.requireApprovalBeforeExecution"],
|
|
36
|
+
risk: "If aliases diverge, health and runtime may appear to disagree about the approval gate.",
|
|
37
|
+
action: "Preserve both fields; use effective approval diagnostics before changing runtime behavior.",
|
|
38
|
+
}),
|
|
39
|
+
"Resolve alias precedence between requirePlanApprovalBeforeExecute and requireApprovalBeforeExecution before changing Plan execution gates.",
|
|
40
|
+
),
|
|
41
|
+
(settings) => conflictWhen(
|
|
42
|
+
settings.workflow.validateAfterExecution !== undefined && settings.workflow.validateAfterExecution !== settings.workflow.autoRunValidationAfterExecute,
|
|
43
|
+
capability({
|
|
44
|
+
path: "workflow.autoRunValidationAfterExecute",
|
|
45
|
+
domain: "workflow",
|
|
46
|
+
intent: "Automatically start validator after approved Plan execution completes.",
|
|
47
|
+
owner: "beginExecution / beginValidation / showPostExecutionMenu",
|
|
48
|
+
status: "wired",
|
|
49
|
+
related: ["workflow.validateAfterExecution", "workflow.offerValidationAfterExecute"],
|
|
50
|
+
risk: "If aliases diverge, validation may be described as automatic while effective behavior says disabled or optional.",
|
|
51
|
+
action: "Preserve both fields; define canonical effective validation behavior before changing runtime branches.",
|
|
52
|
+
}),
|
|
53
|
+
"Resolve validation alias precedence between autoRunValidationAfterExecute and validateAfterExecution before changing validation flow.",
|
|
54
|
+
),
|
|
55
|
+
() => capability({
|
|
56
|
+
path: "workflow.offerReviewerBeforeExecute",
|
|
57
|
+
domain: "workflow",
|
|
58
|
+
intent: "Offer a manual reviewer gate before Plan execution when automatic review is not enabled.",
|
|
59
|
+
owner: "approveCurrentPlan / continueAfterPlanApproval / beginReview",
|
|
60
|
+
status: "wired",
|
|
61
|
+
related: ["workflow.autoRunReviewerBeforeExecute", "models.reviewer", "subagents.reviewPolicy"],
|
|
62
|
+
risk: "Reviewer can feel inconsistent if model routing or review sub-agent policy is unavailable.",
|
|
63
|
+
action: "Keep; test manual and automatic reviewer paths separately.",
|
|
64
|
+
}),
|
|
65
|
+
() => capability({
|
|
66
|
+
path: "workflow.autoRunReviewerBeforeExecute",
|
|
67
|
+
domain: "workflow",
|
|
68
|
+
intent: "Automatically run reviewer before Plan execution when configured.",
|
|
69
|
+
owner: "continueAfterPlanApproval / beginReview",
|
|
70
|
+
status: "wired",
|
|
71
|
+
related: ["workflow.offerReviewerBeforeExecute", "models.reviewer", "subagents.reviewPolicy"],
|
|
72
|
+
risk: "Can add latency when enabled with forced review sub-agents.",
|
|
73
|
+
action: "Keep; expose latency expectations in health when auto reviewer and forced review workers are both enabled.",
|
|
74
|
+
}),
|
|
75
|
+
(settings) => conflictWhen(
|
|
76
|
+
settings.workflow.repairRetry?.gates?.validation?.autoRepairFailures !== undefined && settings.workflow.repairRetry.gates.validation.autoRepairFailures !== settings.workflow.autoRepairValidationFailures,
|
|
77
|
+
capability({
|
|
78
|
+
path: "workflow.repairRetry.gates.validation.autoRepairFailures",
|
|
79
|
+
domain: "workflow",
|
|
80
|
+
intent: "Control whether validation failures can trigger safe automatic repair and revalidation.",
|
|
81
|
+
owner: "handleWorkflowRetryCommand / startWorkflowRepair / final validation menu",
|
|
82
|
+
status: "wired",
|
|
83
|
+
related: ["workflow.autoRepairValidationFailures", "workflow.validationRetryMode", "workflow.maxValidationRetriesPerPlan", "workflow.maxValidationRetriesPerWorkflow"],
|
|
84
|
+
risk: "Alias mismatch can make repair look enabled while effective gate blocks or vice versa.",
|
|
85
|
+
action: "Preserve nested and legacy fields; align effective gate resolution before changing repair behavior.",
|
|
86
|
+
}),
|
|
87
|
+
"Resolve validation repair aliases before changing repair/retry flow.",
|
|
88
|
+
),
|
|
89
|
+
(settings) => conflictWhen(
|
|
90
|
+
settings.workflow.repairRetry?.gates?.review?.autoRepairFailures !== undefined && settings.workflow.repairRetry.gates.review.autoRepairFailures !== settings.workflow.autoRepairReviewFailures,
|
|
91
|
+
capability({
|
|
92
|
+
path: "workflow.repairRetry.gates.review.autoRepairFailures",
|
|
93
|
+
domain: "workflow",
|
|
94
|
+
intent: "Control whether reviewer failures can trigger safe plan repair/revision before execution.",
|
|
95
|
+
owner: "beginReview / continueAfterPlanApproval / repair retry helpers",
|
|
96
|
+
status: "wired",
|
|
97
|
+
related: ["workflow.autoRepairReviewFailures", "workflow.reviewRetryMode", "workflow.maxReviewRetriesPerPlan"],
|
|
98
|
+
risk: "Alias mismatch can cause unexpected reviewer repair prompts or missing repair opportunities.",
|
|
99
|
+
action: "Preserve nested and legacy fields; align effective gate resolution before reviewer repair changes.",
|
|
100
|
+
}),
|
|
101
|
+
"Resolve review repair aliases before changing reviewer repair flow.",
|
|
102
|
+
),
|
|
103
|
+
() => capability({
|
|
104
|
+
path: "workflow.requireApprovalPerStep",
|
|
105
|
+
domain: "workflow",
|
|
106
|
+
intent: "Require approval at individual approved-plan steps when step-gated execution is enabled.",
|
|
107
|
+
owner: "executePrompt / workflow progress handling",
|
|
108
|
+
status: "partial",
|
|
109
|
+
related: ["workflow.validateAfterEachStep", "workflow.planProgressEnabled"],
|
|
110
|
+
risk: "May be visible in prompts/settings without a complete interactive approval loop per step.",
|
|
111
|
+
action: "Preserve; audit step-gated execution before advertising as fully wired.",
|
|
112
|
+
}),
|
|
113
|
+
() => capability({
|
|
114
|
+
path: "workflow.validateAfterEachStep",
|
|
115
|
+
domain: "workflow",
|
|
116
|
+
intent: "Validate individual approved-plan steps instead of only validating the full execution.",
|
|
117
|
+
owner: "executePrompt / beginValidation / planStepValidationIndex",
|
|
118
|
+
status: "partial",
|
|
119
|
+
related: ["workflow.requireApprovalPerStep", "workflow.planProgressEnabled"],
|
|
120
|
+
risk: "Partial step validation can confuse full-plan validation if next-action messaging is unclear.",
|
|
121
|
+
action: "Preserve; build targeted Plan Mode tests before expanding step-level validation.",
|
|
122
|
+
}),
|
|
123
|
+
() => capability({
|
|
124
|
+
path: "workflow.planProgressEnabled",
|
|
125
|
+
domain: "workflow",
|
|
126
|
+
intent: "Track approved Plan steps and expose execution/validation progress.",
|
|
127
|
+
owner: "mergePlanProgress / workflow_progress tool / plan progress widget",
|
|
128
|
+
status: "wired",
|
|
129
|
+
related: ["workflow.planRuntimeEnabled", "workflow.validateAfterEachStep"],
|
|
130
|
+
risk: "Progress can drift if plan parser cannot extract implementation steps.",
|
|
131
|
+
action: "Keep; continue parser-safe plan step tests.",
|
|
132
|
+
}),
|
|
133
|
+
() => capability({
|
|
134
|
+
path: "workflow.returnToPlanModeAfterWorkflow",
|
|
135
|
+
domain: "workflow",
|
|
136
|
+
intent: "Return to Plan input after a completed workflow instead of ending in idle.",
|
|
137
|
+
owner: "showFinalMenu / enterPlanModeAwaitingInput",
|
|
138
|
+
status: "wired",
|
|
139
|
+
related: ["workflow.savePlans", "workflow.planHistoryLimit"],
|
|
140
|
+
risk: "Can feel surprising now that new sessions intentionally start idle.",
|
|
141
|
+
action: "Keep; health/status should explain session start and completion return behavior separately.",
|
|
142
|
+
}),
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const SUBAGENT_CAPABILITIES: CapabilityFactory[] = [
|
|
146
|
+
() => capability({
|
|
147
|
+
path: "subagents.enabled",
|
|
148
|
+
domain: "subagents",
|
|
149
|
+
intent: "Master switch for Workflow Suite use of sub-agents across Plan, Standard, Mission, review, validation, and repair.",
|
|
150
|
+
owner: "planToolsFor / executionToolsFor / phase policy helpers",
|
|
151
|
+
status: "wired",
|
|
152
|
+
related: ["standard.allowSubagents", "missions.subagentPolicy"],
|
|
153
|
+
risk: "If disabled, forced policies cannot be satisfied and workflows should block clearly.",
|
|
154
|
+
action: "Keep; make blockers explicit when disabled conflicts with forced policies.",
|
|
155
|
+
}),
|
|
156
|
+
() => capability({
|
|
157
|
+
path: "subagents.planningPolicy",
|
|
158
|
+
domain: "subagents",
|
|
159
|
+
intent: "Control planning sub-agent aggressiveness for Plan Mode planning.",
|
|
160
|
+
owner: "beginPlanning / beginForcedSubagentPhase / planPrompt",
|
|
161
|
+
status: "wired",
|
|
162
|
+
related: ["subagents.minPlanningWorkersForDeep", "subagents.minPlanningWorkersForMaximum", "planning.useSubagentsBeforeClarification"],
|
|
163
|
+
risk: "Forced planning can add latency or block if workers are unavailable.",
|
|
164
|
+
action: "Keep; redesign agent contracts before changing forced policy behavior.",
|
|
165
|
+
}),
|
|
166
|
+
() => capability({
|
|
167
|
+
path: "subagents.executionPolicy",
|
|
168
|
+
domain: "subagents",
|
|
169
|
+
intent: "Control execution-phase support agents for approved Plan and Mission execution.",
|
|
170
|
+
owner: "beginExecution / beginMissionRun / phasePromptPolicyBlock",
|
|
171
|
+
status: "wired",
|
|
172
|
+
related: ["subagents.allowParallelExecution", "subagents.minExecutionWorkersForMaximum"],
|
|
173
|
+
risk: "Execution support agents may be confused with parallel file edits.",
|
|
174
|
+
action: "Keep; health must distinguish parallel agents from parallel file writes.",
|
|
175
|
+
}),
|
|
176
|
+
() => capability({
|
|
177
|
+
path: "subagents.repairPolicy",
|
|
178
|
+
domain: "subagents",
|
|
179
|
+
intent: "Control repair-phase support agents for validation/review repair loops.",
|
|
180
|
+
owner: "phasePolicy / repairPolicySource / startWorkflowRepair",
|
|
181
|
+
status: "wired",
|
|
182
|
+
related: ["subagents.executionPolicy", "workflow.repairRetry"],
|
|
183
|
+
risk: "Repair inherits execution-like write risk unless prompts and guards keep writes serialized.",
|
|
184
|
+
action: "Keep; preserve serialized writes until scoped edit protection is implemented.",
|
|
185
|
+
}),
|
|
186
|
+
() => capability({
|
|
187
|
+
path: "subagents.allowParallelReadOnly",
|
|
188
|
+
domain: "subagents",
|
|
189
|
+
intent: "Allow multiple read-only/planning/review/validation agents to run concurrently.",
|
|
190
|
+
owner: "workflow-subagent-policy / runner",
|
|
191
|
+
status: "wired",
|
|
192
|
+
related: ["subagents.allowParallelPlanning", "subagents.allowParallelReview", "subagents.allowParallelValidation"],
|
|
193
|
+
risk: "Can increase token/runtime cost but is valuable for quality and speed.",
|
|
194
|
+
action: "Keep; expose cost/latency tradeoff in health rather than disabling it.",
|
|
195
|
+
}),
|
|
196
|
+
() => capability({
|
|
197
|
+
path: "subagents.allowParallelEdits",
|
|
198
|
+
domain: "subagents",
|
|
199
|
+
intent: "Allow simultaneous file-writing agents only when conflict protection exists.",
|
|
200
|
+
owner: "fileWriteModeLabel / prompts / safety diagnostics",
|
|
201
|
+
status: "partial",
|
|
202
|
+
related: ["subagents.editConcurrencyMode", "subagents.requireParallelEditConflictProtection"],
|
|
203
|
+
risk: "Unsafe if treated the same as parallel research agents.",
|
|
204
|
+
action: "Preserve setting; keep main writes serialized until scoped conflict protection is explicitly implemented.",
|
|
205
|
+
}),
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
const SAFETY_CAPABILITIES: CapabilityFactory[] = [
|
|
210
|
+
() => capability({
|
|
211
|
+
path: "safety.repoLockEnabled",
|
|
212
|
+
domain: "safety",
|
|
213
|
+
intent: "Scope built-in file tools to the active repository and reduce cross-repository scanning when enabled.",
|
|
214
|
+
owner: "workflow-tool-guard / Global Safety menu",
|
|
215
|
+
status: "wired",
|
|
216
|
+
related: ["safety.blockDestructiveCommands", "subagents.enabled", "standard.allowSubagents"],
|
|
217
|
+
risk: "Bash is constrained by conservative command inspection, not by an OS-level sandbox; sub-agent child processes run with --no-extensions and need separate hardening for full sandbox parity.",
|
|
218
|
+
action: "Keep visible as Repo Lock; do not describe it as a container or kernel sandbox.",
|
|
219
|
+
}),
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
const STANDARD_MISSION_CONTEXT_CAPABILITIES: CapabilityFactory[] = [
|
|
223
|
+
() => capability({
|
|
224
|
+
path: "standard.todoTriggerMode",
|
|
225
|
+
domain: "standard",
|
|
226
|
+
intent: "Control whether Standard Mode uses no To Do, manual To Do, automatic To Do, or required To Do tracking.",
|
|
227
|
+
owner: "standardPrompt / standard_todo tool / workflow-tool-guard",
|
|
228
|
+
status: "wired",
|
|
229
|
+
related: ["standard.autoTodoEnabled", "standard.todoProgressVisible"],
|
|
230
|
+
risk: "Required mode can feel heavy if task-size detection is poor.",
|
|
231
|
+
action: "Keep; tune Standard prompts only after Plan Mode is stable.",
|
|
232
|
+
}),
|
|
233
|
+
() => capability({
|
|
234
|
+
path: "missions.watchdogEnabled",
|
|
235
|
+
domain: "missions",
|
|
236
|
+
intent: "Expose a user-supervised watchdog/recovery direction for stale missions.",
|
|
237
|
+
owner: "mission settings diagnostics / future recovery actions",
|
|
238
|
+
status: "future",
|
|
239
|
+
related: ["missions.watchdogStaleMinutes", "missions.heartbeatEnabled"],
|
|
240
|
+
risk: "Can look fully enforced when current design keeps recovery supervised.",
|
|
241
|
+
action: "Preserve; label as planned/partial until user-supervised recovery actions are implemented.",
|
|
242
|
+
}),
|
|
243
|
+
() => capability({
|
|
244
|
+
path: "context.compactionMode",
|
|
245
|
+
domain: "context",
|
|
246
|
+
intent: "Select Pi default, custom model, custom agent, or disabled compaction behavior.",
|
|
247
|
+
owner: "compaction hook / maybeRunWorkflowAutoCompaction",
|
|
248
|
+
status: "partial",
|
|
249
|
+
related: ["context.customCompactionEnabled", "context.compactionModelProvider", "context.compactionAgent"],
|
|
250
|
+
risk: "custom_agent mode is reserved/planned and custom_model needs provider/model routing.",
|
|
251
|
+
action: "Preserve; keep fallback diagnostics clear.",
|
|
252
|
+
}),
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
export function workflowSettingsCapabilities(settings: WorkflowSettings): WorkflowSettingCapability[] {
|
|
256
|
+
return [
|
|
257
|
+
...PLAN_MODE_CAPABILITIES,
|
|
258
|
+
...SUBAGENT_CAPABILITIES,
|
|
259
|
+
...SAFETY_CAPABILITIES,
|
|
260
|
+
...STANDARD_MISSION_CONTEXT_CAPABILITIES,
|
|
261
|
+
].map((factory) => factory(settings));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function workflowSettingsCapabilitiesByStatus(settings: WorkflowSettings): Record<WorkflowSettingCapabilityStatus, WorkflowSettingCapability[]> {
|
|
265
|
+
const groups: Record<WorkflowSettingCapabilityStatus, WorkflowSettingCapability[]> = {
|
|
266
|
+
wired: [],
|
|
267
|
+
partial: [],
|
|
268
|
+
conflict: [],
|
|
269
|
+
future: [],
|
|
270
|
+
unclear: [],
|
|
271
|
+
};
|
|
272
|
+
for (const item of workflowSettingsCapabilities(settings)) groups[item.status].push(item);
|
|
273
|
+
return groups;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function renderWorkflowSettingsCapabilityMatrix(settings: WorkflowSettings): string {
|
|
277
|
+
const items = workflowSettingsCapabilities(settings);
|
|
278
|
+
const rows = items.map((item) => `- ${item.path} [${item.status}] — ${item.intent} Owner: ${item.owner}. Action: ${item.action}`);
|
|
279
|
+
return rows.join("\n");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export default function workflowSettingsCapabilitiesNoopExtension(): void {}
|