@syntesseraai/opencode-feature-factory 0.6.20 → 0.7.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run documentation mini-loop until gate passes
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  loop:
@@ -12,3 +12,5 @@ return:
12
12
  - /mini-loop/documentation/gate $RESULT[mini-doc-review]
13
13
  - If `MINI_LOOP_DOCUMENTATION_GATE=REWORK`, continue loop with prior documentation review feedback included in the next documentation pass.
14
14
  ---
15
+
16
+ Reply with only: "Documentation phase started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run implementation mini-loop until gate passes
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  loop:
@@ -12,3 +12,5 @@ return:
12
12
  - /mini-loop/implementation/gate $RESULT[mini-impl-review]
13
13
  - If `MINI_LOOP_IMPLEMENTATION_GATE=REWORK`, continue loop with prior review feedback included in the next build input.
14
14
  ---
15
+
16
+ Reply with only: "Implementation phase started"
@@ -1,9 +1,11 @@
1
1
  ---
2
2
  description: Mini-loop two-stage workflow entrypoint
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  return:
7
7
  - /mini-loop/implementation/run {as:implementation-phase} $ARGUMENTS
8
8
  - /mini-loop/documentation/run $RESULT[implementation-phase]
9
9
  ---
10
+
11
+ Reply with only: "Mini-loop started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run build phase from approved plan
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  return:
@@ -9,3 +9,5 @@ return:
9
9
  - /pipeline/building/implement-batch {as:build-implementation} $RESULT[build-batches]
10
10
  - /pipeline/reviewing/run {as:review-phase} $RESULT[build-implementation]
11
11
  ---
12
+
13
+ Reply with only: "Building phase started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run documentation loop after review approval
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  loop:
@@ -12,3 +12,5 @@ return:
12
12
  - /pipeline/documentation/gate $RESULT[doc-review]
13
13
  - If `DOCUMENTATION_GATE=REWORK`, continue loop with review feedback for the next document pass.
14
14
  ---
15
+
16
+ Reply with only: "Documentation phase started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Execute one planning iteration
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  parallel:
@@ -11,3 +11,5 @@ return:
11
11
  - /pipeline/planning/synthesize {as:plan-consensus} $RESULT[plan-opus] $RESULT[plan-gemini] $RESULT[plan-codex]
12
12
  - /pipeline/planning/gate $RESULT[plan-consensus]
13
13
  ---
14
+
15
+ Reply with only: "Planning phase started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Run review loop for completed tasks
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  loop:
@@ -17,3 +17,5 @@ return:
17
17
  - /pipeline/reviewing/gate $RESULT[review-synthesis]
18
18
  - If `REVIEW_GATE=REWORK`, invoke `/pipeline/building/implement-batch` to apply fixes before the next loop iteration.
19
19
  ---
20
+
21
+ Reply with only: "Reviewing phase started"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Pipeline workflow entrypoint
3
- subtask: true
3
+ subtask: false
4
4
  agent: general
5
5
  model: openai/gpt-5.4
6
6
  return:
@@ -10,3 +10,5 @@ return:
10
10
  - /pipeline/documentation/run $RESULT[build-phase]
11
11
  - /pipeline/complete
12
12
  ---
13
+
14
+ Reply with only: "Workflow started"
@@ -0,0 +1,38 @@
1
+ type BunShell = any;
2
+ /**
3
+ * Command configuration type matching the OpenCode `command` config schema.
4
+ */
5
+ export interface CommandConfig {
6
+ template: string;
7
+ description?: string;
8
+ agent?: string;
9
+ model?: string;
10
+ subtask?: boolean;
11
+ }
12
+ export interface CommandConfigs {
13
+ [commandName: string]: CommandConfig;
14
+ }
15
+ /**
16
+ * Default commands to register in the global OpenCode config.
17
+ *
18
+ * "Feature-Factory" is a Socratic entrypoint that guides the user through
19
+ * clarifying requirements, choosing between pipeline and mini-loop, confirming
20
+ * model selections, and then invoking the appropriate workflow tool.
21
+ */
22
+ export declare const DEFAULT_COMMANDS: Record<string, CommandConfig>;
23
+ /**
24
+ * Merge command configs, preserving existing settings and adding new ones.
25
+ * Existing command configs take precedence (we never overwrite them).
26
+ */
27
+ export declare function mergeCommandConfigs(existing: CommandConfigs | undefined, defaults: Record<string, CommandConfig>): CommandConfigs;
28
+ /**
29
+ * Update the command configuration in global opencode.json.
30
+ *
31
+ * This function:
32
+ * 1. Reads existing config from ~/.config/opencode/opencode.json
33
+ * 2. Preserves existing command settings
34
+ * 3. Adds default Feature Factory commands that don't exist yet
35
+ * 4. Writes updated config back to ~/.config/opencode/opencode.json
36
+ */
37
+ export declare function updateCommandConfig($: BunShell): Promise<void>;
38
+ export {};
@@ -0,0 +1,128 @@
1
+ import { isRecord, updateGlobalOpenCodeConfigBlock } from './opencode-global-config.js';
2
+ /**
3
+ * Default commands to register in the global OpenCode config.
4
+ *
5
+ * "Feature-Factory" is a Socratic entrypoint that guides the user through
6
+ * clarifying requirements, choosing between pipeline and mini-loop, confirming
7
+ * model selections, and then invoking the appropriate workflow tool.
8
+ */
9
+ export const DEFAULT_COMMANDS = {
10
+ 'feature-factory': {
11
+ description: 'Feature Factory — guided workflow for planning and building features',
12
+ agent: 'general',
13
+ subtask: false,
14
+ template: `You are the Feature Factory workflow assistant. Your job is to guide the user through a structured process before launching either the full pipeline or the mini-loop.
15
+
16
+ Work through this process step by step. Do NOT skip steps or launch a tool until all steps are complete.
17
+
18
+ ---
19
+
20
+ ## Step 1: Understand the request
21
+
22
+ The user said: $ARGUMENTS
23
+
24
+ Work through their request in a Socratic manner:
25
+ - Ask clarifying questions about scope, constraints, and acceptance criteria.
26
+ - Summarise your understanding back to them and ask them to confirm or correct it.
27
+ - Continue until you have a clear, unambiguous set of requirements.
28
+
29
+ If $ARGUMENTS is empty, ask the user what they would like to build.
30
+
31
+ ---
32
+
33
+ ## Step 2: Choose workflow
34
+
35
+ Once requirements are agreed, present the two workflow options:
36
+
37
+ **Pipeline** (full multi-model workflow)
38
+ - Multi-model fan-out planning (3 models propose plans, then consensus synthesis)
39
+ - Build phase with task breakdown and batch validation
40
+ - Multi-model fan-out code review
41
+ - Documentation with review loop
42
+ - Best for: complex features, architectural changes, high-risk work
43
+
44
+ **Mini-loop** (lightweight workflow)
45
+ - Direct build → review loop (single model per step)
46
+ - Documentation with review loop
47
+ - Best for: small features, bug fixes, incremental improvements, well-understood changes
48
+
49
+ Ask the user which workflow they prefer. If they are unsure, recommend one based on the complexity of the requirements.
50
+
51
+ ---
52
+
53
+ ## Step 3: Confirm models
54
+
55
+ Present the default models that will be used for the chosen workflow:
56
+
57
+ **Pipeline defaults:**
58
+ - Planning fan-out: opus (anthropic/claude-opus-4-6), gemini (opencode/gemini-3.1-pro), codex (openai/gpt-5.3-codex)
59
+ - Review fan-out: same as planning
60
+ - Orchestrator (synthesis/triage): openai/gpt-5.4
61
+ - Build: openai/gpt-5.3-codex
62
+ - Validate: opencode/gemini-3.1-pro
63
+ - Documentation: openai/gpt-5.3-codex
64
+ - Doc review: opencode/gemini-3.1-pro
65
+
66
+ **Mini-loop defaults:**
67
+ - Build: openai/gpt-5.3-codex
68
+ - Review: openai/gpt-5.4
69
+ - Documentation: openai/gpt-5.3-codex
70
+ - Doc review: opencode/gemini-3.1-pro
71
+
72
+ Ask: "Would you like to use these default models, or would you like to override any of them?"
73
+
74
+ If they want to override, collect the provider/model strings for each role they want to change.
75
+
76
+ ---
77
+
78
+ ## Step 4: Launch
79
+
80
+ Once all three steps are confirmed, call the appropriate tool:
81
+
82
+ - If they chose **Pipeline**: call the \`ff_pipeline\` tool with the agreed requirements and any model overrides.
83
+ - If they chose **Mini-loop**: call the \`ff_mini_loop\` tool with the agreed requirements and any model overrides.
84
+
85
+ Pass only the model parameters that were explicitly overridden; omit any that should use defaults.`,
86
+ },
87
+ };
88
+ /**
89
+ * Merge command configs, preserving existing settings and adding new ones.
90
+ * Existing command configs take precedence (we never overwrite them).
91
+ */
92
+ export function mergeCommandConfigs(existing, defaults) {
93
+ const existingCommands = existing ?? {};
94
+ const result = { ...existingCommands };
95
+ for (const [commandName, commandConfig] of Object.entries(defaults)) {
96
+ if (!result[commandName]) {
97
+ result[commandName] = commandConfig;
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ /**
103
+ * Update the command configuration in global opencode.json.
104
+ *
105
+ * This function:
106
+ * 1. Reads existing config from ~/.config/opencode/opencode.json
107
+ * 2. Preserves existing command settings
108
+ * 3. Adds default Feature Factory commands that don't exist yet
109
+ * 4. Writes updated config back to ~/.config/opencode/opencode.json
110
+ */
111
+ export async function updateCommandConfig($) {
112
+ void $;
113
+ await updateGlobalOpenCodeConfigBlock({
114
+ blockName: 'command',
115
+ warningLabel: 'command config',
116
+ update: (existingBlock) => {
117
+ const existingCommandConfigs = isRecord(existingBlock)
118
+ ? existingBlock
119
+ : undefined;
120
+ const updatedCommandConfigs = mergeCommandConfigs(existingCommandConfigs, DEFAULT_COMMANDS);
121
+ const hasChanges = Object.keys(DEFAULT_COMMANDS).some((commandName) => !existingCommandConfigs?.[commandName]);
122
+ return {
123
+ nextBlock: updatedCommandConfigs,
124
+ changed: hasChanges,
125
+ };
126
+ },
127
+ });
128
+ }
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@ import { StopQualityGateHooksPlugin } from './stop-quality-gate.js';
2
2
  import { updateMCPConfig } from './mcp-config.js';
3
3
  import { updateAgentConfig } from './agent-config.js';
4
4
  import { updatePluginConfig } from './plugin-config.js';
5
+ import { updateCommandConfig } from './command-config.js';
6
+ import { createWorkflowTools } from './tools/index.js';
5
7
  import { $ } from 'bun';
6
8
  /**
7
9
  * Feature Factory Plugin
@@ -40,10 +42,24 @@ export const FeatureFactoryPlugin = async (input) => {
40
42
  catch {
41
43
  console.error('Failed to update plugin config in OpenCode plugin');
42
44
  }
45
+ // Update command configuration in global OpenCode config
46
+ // This registers the Feature-Factory Socratic entrypoint command
47
+ try {
48
+ await updateCommandConfig($);
49
+ }
50
+ catch {
51
+ console.error('Failed to update command config in OpenCode plugin');
52
+ }
43
53
  // Load hooks from the quality gate plugin
44
54
  const qualityGateHooks = await StopQualityGateHooksPlugin(input).catch(() => ({}));
45
- // Tool registry intentionally empty after hard removal of ff-* artifact tools.
46
- const tools = {};
55
+ // Tool registry: workflow tools (ff_pipeline, ff_mini_loop) powered by
56
+ // the SDK client for programmatic session orchestration.
57
+ // The old ff-* artifact tools were removed; these replace the subtask2
58
+ // command-based workflow with TypeScript-driven orchestration.
59
+ const workflowTools = createWorkflowTools(input.client);
60
+ // Merge workflow tools with any existing tools from quality gate hooks
61
+ const existingTools = qualityGateHooks.tool ?? {};
62
+ const tools = { ...existingTools, ...workflowTools };
47
63
  // Return combined hooks and tools
48
64
  return {
49
65
  ...qualityGateHooks,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tool registry for Feature Factory.
3
+ *
4
+ * Exports a function that creates all FF tools given the SDK client.
5
+ * Only entrypoint tools (pipeline, mini-loop) are exposed to the LLM.
6
+ * Internal workflow steps are orchestrated programmatically, not as
7
+ * individually callable tools.
8
+ */
9
+ import type { ToolDefinition } from '@opencode-ai/plugin/tool';
10
+ import type { Client } from '../workflow/fan-out.js';
11
+ /**
12
+ * Build the tool map to be merged into the plugin's `Hooks.tool` output.
13
+ *
14
+ * @param client - The OpenCode SDK client from PluginInput
15
+ */
16
+ export declare function createWorkflowTools(client: Client): Record<string, ToolDefinition>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tool registry for Feature Factory.
3
+ *
4
+ * Exports a function that creates all FF tools given the SDK client.
5
+ * Only entrypoint tools (pipeline, mini-loop) are exposed to the LLM.
6
+ * Internal workflow steps are orchestrated programmatically, not as
7
+ * individually callable tools.
8
+ */
9
+ import { createPipelineTool } from './pipeline.js';
10
+ import { createMiniLoopTool } from './mini-loop.js';
11
+ /**
12
+ * Build the tool map to be merged into the plugin's `Hooks.tool` output.
13
+ *
14
+ * @param client - The OpenCode SDK client from PluginInput
15
+ */
16
+ export function createWorkflowTools(client) {
17
+ return {
18
+ ff_pipeline: createPipelineTool(client),
19
+ ff_mini_loop: createMiniLoopTool(client),
20
+ };
21
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ff_mini_loop — Lightweight build → review → document workflow tool.
3
+ *
4
+ * A simpler two-stage workflow (no multi-model fan-out for planning).
5
+ * Implements a build/review loop, then a documentation loop.
6
+ */
7
+ import { type Client } from '../workflow/orchestrator.js';
8
+ export declare function createMiniLoopTool(client: Client): {
9
+ description: string;
10
+ args: {
11
+ requirements: import("zod").ZodString;
12
+ build_model: import("zod").ZodOptional<import("zod").ZodString>;
13
+ review_model: import("zod").ZodOptional<import("zod").ZodString>;
14
+ doc_model: import("zod").ZodOptional<import("zod").ZodString>;
15
+ doc_review_model: import("zod").ZodOptional<import("zod").ZodString>;
16
+ };
17
+ execute(args: {
18
+ requirements: string;
19
+ build_model?: string | undefined;
20
+ review_model?: string | undefined;
21
+ doc_model?: string | undefined;
22
+ doc_review_model?: string | undefined;
23
+ }, context: import("@opencode-ai/plugin/tool").ToolContext): Promise<string>;
24
+ };
@@ -0,0 +1,133 @@
1
+ /**
2
+ * ff_mini_loop — Lightweight build → review → document workflow tool.
3
+ *
4
+ * A simpler two-stage workflow (no multi-model fan-out for planning).
5
+ * Implements a build/review loop, then a documentation loop.
6
+ */
7
+ import { tool } from '@opencode-ai/plugin/tool';
8
+ import { promptSession, evaluateMiniLoopImplGate, evaluateMiniLoopDocGate, BUILD_MODEL, DOC_MODEL, DOC_REVIEW_MODEL, ORCHESTRATOR_MODEL, parseModelString, } from '../workflow/orchestrator.js';
9
+ import { miniBuildPrompt, miniReviewPrompt, documentPrompt, docReviewPrompt } from './prompts.js';
10
+ import { parseMiniReview, parseDocReview } from './parsers.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Tool factory
13
+ // ---------------------------------------------------------------------------
14
+ export function createMiniLoopTool(client) {
15
+ return tool({
16
+ description: 'Run the Feature Factory mini-loop: build → review (with rework loop) → documentation. ' +
17
+ 'Lighter weight than the full pipeline — no multi-model planning phase. ' +
18
+ 'All model parameters are optional and use sensible defaults.',
19
+ args: {
20
+ requirements: tool.schema
21
+ .string()
22
+ .describe('The feature requirements or task description to implement'),
23
+ build_model: tool.schema
24
+ .string()
25
+ .optional()
26
+ .describe('provider/model for building and implementation. Defaults to openai/gpt-5.3-codex.'),
27
+ review_model: tool.schema
28
+ .string()
29
+ .optional()
30
+ .describe('provider/model for implementation review. Defaults to openai/gpt-5.4.'),
31
+ doc_model: tool.schema
32
+ .string()
33
+ .optional()
34
+ .describe('provider/model for documentation writing. Defaults to the build model.'),
35
+ doc_review_model: tool.schema
36
+ .string()
37
+ .optional()
38
+ .describe('provider/model for documentation review. Defaults to opencode/gemini-3.1-pro.'),
39
+ },
40
+ async execute(args, context) {
41
+ const sessionId = context.sessionID;
42
+ const { requirements } = args;
43
+ // Resolve models — use provided overrides or fall back to defaults
44
+ const buildModel = args.build_model
45
+ ? parseModelString(args.build_model)
46
+ : BUILD_MODEL;
47
+ const reviewModel = args.review_model
48
+ ? parseModelString(args.review_model)
49
+ : ORCHESTRATOR_MODEL;
50
+ const docModel = args.doc_model
51
+ ? parseModelString(args.doc_model)
52
+ : args.build_model
53
+ ? buildModel
54
+ : DOC_MODEL;
55
+ const docReviewModel = args.doc_review_model
56
+ ? parseModelString(args.doc_review_model)
57
+ : DOC_REVIEW_MODEL;
58
+ const report = [];
59
+ const addReport = (phase, msg) => {
60
+ report.push(`## ${phase}\n${msg}`);
61
+ };
62
+ // ===================================================================
63
+ // PHASE 1: IMPLEMENTATION LOOP (build → review → gate, up to 10)
64
+ // ===================================================================
65
+ let implGate = { decision: 'REWORK', feedback: requirements };
66
+ let lastImplRaw = '';
67
+ for (let implIter = 0; implIter < 10 && implGate.decision === 'REWORK'; implIter++) {
68
+ const buildInput = implIter === 0
69
+ ? requirements
70
+ : `${requirements}\n\nPrevious review feedback:\n${implGate.feedback}`;
71
+ // Build
72
+ lastImplRaw = await promptSession(client, sessionId, miniBuildPrompt(buildInput, implIter > 0 ? implGate.feedback : undefined), { model: buildModel, agent: 'building', title: `ff-mini-build-${implIter + 1}` });
73
+ // Review
74
+ const reviewRaw = await promptSession(client, sessionId, miniReviewPrompt(lastImplRaw), {
75
+ model: reviewModel,
76
+ agent: 'reviewing',
77
+ title: `ff-mini-review-${implIter + 1}`,
78
+ });
79
+ const review = parseMiniReview(reviewRaw);
80
+ // Gate (deterministic)
81
+ implGate = evaluateMiniLoopImplGate(review, implIter + 1);
82
+ if (implGate.decision === 'APPROVED') {
83
+ addReport('Implementation', `APPROVED (confidence: ${review.confidence}, iteration: ${implIter + 1})`);
84
+ }
85
+ else if (implGate.decision === 'ESCALATE') {
86
+ addReport('Implementation', `ESCALATE: ${implGate.reason}`);
87
+ return report.join('\n\n');
88
+ }
89
+ // REWORK continues the loop
90
+ }
91
+ if (implGate.decision !== 'APPROVED') {
92
+ addReport('Implementation', `REWORK exhausted (10 iterations). Last feedback:\n${implGate.feedback}`);
93
+ return report.join('\n\n');
94
+ }
95
+ // ===================================================================
96
+ // PHASE 2: DOCUMENTATION LOOP (document → review → gate, up to 5)
97
+ // ===================================================================
98
+ let docInput = lastImplRaw;
99
+ let docGate = { decision: 'REWORK' };
100
+ for (let docIter = 0; docIter < 5 && docGate.decision === 'REWORK'; docIter++) {
101
+ // Write docs
102
+ const docRaw = await promptSession(client, sessionId, documentPrompt(docInput), {
103
+ model: docModel,
104
+ agent: 'documenting',
105
+ title: `ff-mini-doc-write-${docIter + 1}`,
106
+ });
107
+ // Review docs
108
+ const docRevRaw = await promptSession(client, sessionId, docReviewPrompt(docRaw), {
109
+ model: docReviewModel,
110
+ agent: 'reviewing',
111
+ title: `ff-mini-doc-review-${docIter + 1}`,
112
+ });
113
+ const docReview = parseDocReview(docRevRaw);
114
+ // Gate (deterministic)
115
+ docGate = evaluateMiniLoopDocGate(docReview, docIter + 1);
116
+ if (docGate.decision === 'APPROVED') {
117
+ addReport('Documentation', `APPROVED (confidence: ${docReview.confidence}, iteration: ${docIter + 1})`);
118
+ }
119
+ else if (docGate.decision === 'ESCALATE') {
120
+ addReport('Documentation', `ESCALATE: ${docGate.reason}`);
121
+ }
122
+ else {
123
+ docInput = `${docInput}\n\nDocumentation review feedback:\n${docGate.feedback}`;
124
+ }
125
+ }
126
+ // ===================================================================
127
+ // FINAL REPORT
128
+ // ===================================================================
129
+ addReport('Complete', 'Mini-loop finished.');
130
+ return report.join('\n\n');
131
+ },
132
+ });
133
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Response parsers that extract structured data from LLM text output.
3
+ *
4
+ * These are intentionally tolerant — they fall back to raw text when
5
+ * structured sections cannot be found. The gate evaluators work on
6
+ * the parsed numbers/booleans, not on freeform text.
7
+ */
8
+ import type { PlanProposal, ConsensusPlan, ReviewReport, ReviewSynthesis, DocReview, ImplementationReport, DocUpdate } from '../workflow/types.js';
9
+ /** Extract the first integer found after a label in the text. */
10
+ export declare function extractNumber(text: string, label: string, fallback?: number): number;
11
+ /** Extract text between two section headers (markdown-style). */
12
+ export declare function extractSection(text: string, heading: string): string;
13
+ /** Check if a YES/NO field is YES. */
14
+ export declare function isYes(text: string, label: string): boolean;
15
+ export declare function parsePlanProposal(tag: string, raw: string): PlanProposal;
16
+ export declare function parseConsensusPlan(raw: string): ConsensusPlan;
17
+ export declare function parseReviewReport(tag: string, raw: string): ReviewReport;
18
+ export declare function parseReviewSynthesis(raw: string): ReviewSynthesis;
19
+ export declare function parseDocUpdate(raw: string): DocUpdate;
20
+ export declare function parseDocReview(raw: string): DocReview;
21
+ export declare function parseImplementationReport(raw: string): ImplementationReport;
22
+ export declare function parseMiniReview(raw: string): {
23
+ confidence: number;
24
+ changeRequested: boolean;
25
+ unresolvedIssues: number;
26
+ reworkInstructions?: string;
27
+ raw: string;
28
+ };