projscan 4.2.0 → 4.3.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/README.md +93 -26
- package/dist/cli/commands/missionProof.d.ts +1 -0
- package/dist/cli/commands/missionProof.js +478 -0
- package/dist/cli/commands/missionProof.js.map +1 -0
- package/dist/cli/commands/start.js +18 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/missionOutcome.d.ts +2 -0
- package/dist/core/missionOutcome.js +219 -0
- package/dist/core/missionOutcome.js.map +1 -0
- package/dist/core/missionProof.d.ts +6 -0
- package/dist/core/missionProof.js +92 -0
- package/dist/core/missionProof.js.map +1 -0
- package/dist/core/missionProofBaseline.d.ts +10 -0
- package/dist/core/missionProofBaseline.js +131 -0
- package/dist/core/missionProofBaseline.js.map +1 -0
- package/dist/core/missionProofMarkdown.d.ts +2 -0
- package/dist/core/missionProofMarkdown.js +80 -0
- package/dist/core/missionProofMarkdown.js.map +1 -0
- package/dist/core/missionProofSummary.d.ts +2 -0
- package/dist/core/missionProofSummary.js +16 -0
- package/dist/core/missionProofSummary.js.map +1 -0
- package/dist/core/start.d.ts +1 -0
- package/dist/core/start.js +9 -4
- package/dist/core/start.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/start.js +5 -0
- package/dist/mcp/tools/start.js.map +1 -1
- package/dist/projscan-sbom.cdx.json +6 -6
- package/dist/tool-manifest.json +6 -2
- package/dist/types.d.ts +91 -0
- package/dist/utils/formatSupport.d.ts +1 -0
- package/dist/utils/formatSupport.js +1 -0
- package/dist/utils/formatSupport.js.map +1 -1
- package/docs/GUIDE.md +23 -1
- package/docs/demos/projscan-4-1-demo.html +78 -94
- package/docs/demos/projscan-mission-control.tape +13 -0
- package/docs/demos/projscan-mission-proof.tape +25 -0
- package/docs/projscan-mission-control.gif +0 -0
- package/docs/projscan-mission-control.png +0 -0
- package/docs/projscan-mission-proof.gif +0 -0
- package/docs/projscan-proof-router.png +0 -0
- package/package.json +9 -2
- package/scripts/capture-vhs-demos.mjs +80 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const MISSION_PROOF_BASELINE_STATUSES = [
|
|
4
|
+
'passed',
|
|
5
|
+
'failed',
|
|
6
|
+
'running',
|
|
7
|
+
'not_run',
|
|
8
|
+
'unknown',
|
|
9
|
+
];
|
|
10
|
+
const BASELINE_NUMERIC_FIELDS = ['failedGates', 'reruns', 'minutesSpent', 'reviewerApprovals'];
|
|
11
|
+
export async function loadMissionProofBaseline(rootPath, baselineFile) {
|
|
12
|
+
const resolved = path.resolve(rootPath, baselineFile);
|
|
13
|
+
const input = parseMissionProofBaselineInput(await readMissionProofBaselineFile(resolved, baselineFile), baselineFile);
|
|
14
|
+
const runs = validateMissionProofBaselineRuns(input, baselineFile);
|
|
15
|
+
const totals = totalsFromBaselineRuns(runs);
|
|
16
|
+
return {
|
|
17
|
+
path: path.relative(rootPath, resolved) || resolved,
|
|
18
|
+
runs,
|
|
19
|
+
totals,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function missionProofBaselineTemplate() {
|
|
23
|
+
return {
|
|
24
|
+
schemaVersion: 1,
|
|
25
|
+
runs: [],
|
|
26
|
+
exampleRun: {
|
|
27
|
+
id: 'manual-before-mission-control',
|
|
28
|
+
status: 'passed',
|
|
29
|
+
minutesSpent: 30,
|
|
30
|
+
reruns: 1,
|
|
31
|
+
failedGates: 0,
|
|
32
|
+
reviewerApprovals: 1,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function parseMissionProofBaselineInput(raw, baselineFile) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw new Error(`Mission proof baseline file is not valid JSON: ${baselineFile}\n` +
|
|
43
|
+
'Expected shape: {"schemaVersion":1,"runs":[...]}', { cause: err });
|
|
44
|
+
}
|
|
45
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
46
|
+
throw baselineValidationError(baselineFile, 'root', 'expected an object.');
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
export function validateMissionProofBaselineRuns(input, baselineFile) {
|
|
51
|
+
if (typeof input.runs === 'undefined')
|
|
52
|
+
return [];
|
|
53
|
+
if (!Array.isArray(input.runs)) {
|
|
54
|
+
throw baselineValidationError(baselineFile, 'runs', 'expected an array.');
|
|
55
|
+
}
|
|
56
|
+
const runs = input.runs.map((run, index) => validateMissionProofBaselineRun(run, index, baselineFile));
|
|
57
|
+
validateUniqueBaselineRunIds(runs, baselineFile);
|
|
58
|
+
return runs;
|
|
59
|
+
}
|
|
60
|
+
function validateMissionProofBaselineRun(run, index, baselineFile) {
|
|
61
|
+
if (!run || typeof run !== 'object') {
|
|
62
|
+
throw baselineValidationError(baselineFile, `runs[${index}]`, 'expected an object.');
|
|
63
|
+
}
|
|
64
|
+
const candidate = run;
|
|
65
|
+
if (typeof candidate.id !== 'string' || candidate.id.trim().length === 0) {
|
|
66
|
+
throw baselineValidationError(baselineFile, `runs[${index}].id`, 'expected a non-empty string.');
|
|
67
|
+
}
|
|
68
|
+
if (!MISSION_PROOF_BASELINE_STATUSES.includes(candidate.status)) {
|
|
69
|
+
throw baselineValidationError(baselineFile, `runs[${index}].status`, 'expected passed, failed, running, not_run, or unknown.');
|
|
70
|
+
}
|
|
71
|
+
for (const field of BASELINE_NUMERIC_FIELDS) {
|
|
72
|
+
const value = candidate[field];
|
|
73
|
+
if (typeof value !== 'undefined' && (typeof value !== 'number' || !Number.isFinite(value) || value < 0)) {
|
|
74
|
+
throw baselineValidationError(baselineFile, `runs[${index}].${field}`, 'expected a non-negative number.');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
function validateUniqueBaselineRunIds(runs, baselineFile) {
|
|
80
|
+
const seen = new Set();
|
|
81
|
+
for (const [index, run] of runs.entries()) {
|
|
82
|
+
if (seen.has(run.id)) {
|
|
83
|
+
throw baselineValidationError(baselineFile, `runs[${index}].id`, `duplicate id ${run.id}.`);
|
|
84
|
+
}
|
|
85
|
+
seen.add(run.id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function totalsFromBaselineRuns(runs) {
|
|
89
|
+
const passed = runs.filter((run) => run.status === 'passed').length;
|
|
90
|
+
return {
|
|
91
|
+
missions: runs.length,
|
|
92
|
+
passed,
|
|
93
|
+
failed: runs.filter((run) => run.status === 'failed').length,
|
|
94
|
+
running: runs.filter((run) => run.status === 'running').length,
|
|
95
|
+
notRun: runs.filter((run) => run.status === 'not_run').length,
|
|
96
|
+
unavailable: runs.filter((run) => run.status === 'unknown').length,
|
|
97
|
+
proofCompletionRate: runs.length > 0 ? round(passed / runs.length) : 0,
|
|
98
|
+
reruns: sum(runs.map((run) => run.reruns)),
|
|
99
|
+
failedGates: sum(runs.map((run) => run.failedGates)),
|
|
100
|
+
reviewerApprovals: sum(runs.map((run) => run.reviewerApprovals)),
|
|
101
|
+
minutesSpent: sum(runs.map((run) => run.minutesSpent)),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function sum(values) {
|
|
105
|
+
return values.reduce((total, value) => total + (typeof value === 'number' && Number.isFinite(value) ? value : 0), 0);
|
|
106
|
+
}
|
|
107
|
+
async function readMissionProofBaselineFile(resolved, baselineFile) {
|
|
108
|
+
try {
|
|
109
|
+
return await fs.readFile(resolved, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (isNodeError(err) && err.code === 'ENOENT') {
|
|
113
|
+
throw new Error(`Mission proof baseline file not found: ${baselineFile}\n` +
|
|
114
|
+
`Create one with: projscan mission-proof --init-baseline ${shellToken(baselineFile)}`, { cause: err });
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function baselineValidationError(baselineFile, pathLabel, message) {
|
|
120
|
+
return new Error(`Mission proof baseline invalid at ${pathLabel}: ${message}\nFile: ${baselineFile}`);
|
|
121
|
+
}
|
|
122
|
+
function shellToken(value) {
|
|
123
|
+
return /^[A-Za-z0-9_./:-]+$/.test(value) ? value : JSON.stringify(value);
|
|
124
|
+
}
|
|
125
|
+
function isNodeError(value) {
|
|
126
|
+
return value instanceof Error && 'code' in value;
|
|
127
|
+
}
|
|
128
|
+
function round(value) {
|
|
129
|
+
return Math.round(value * 100) / 100;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=missionProofBaseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missionProofBaseline.js","sourceRoot":"","sources":["../../src/core/missionProofBaseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function renderMissionProofMarkdown(report) {
|
|
2
|
+
const totals = report.missionControl.totals;
|
|
3
|
+
const lines = [
|
|
4
|
+
'# Mission Proof Report',
|
|
5
|
+
'',
|
|
6
|
+
'## Summary',
|
|
7
|
+
`- Mission bundles: ${totals.missions}`,
|
|
8
|
+
`- Passed: ${totals.passed}`,
|
|
9
|
+
`- Failed: ${totals.failed}`,
|
|
10
|
+
`- Running: ${totals.running}`,
|
|
11
|
+
`- Not run: ${totals.notRun}`,
|
|
12
|
+
`- Unavailable: ${totals.unavailable}`,
|
|
13
|
+
`- Proof completion: ${formatPercent(totals.proofCompletionRate)}`,
|
|
14
|
+
`- Reruns: ${totals.reruns}`,
|
|
15
|
+
`- Failed gates: ${totals.failedGates}`,
|
|
16
|
+
`- Reviewer approvals: ${totals.reviewerApprovals}`,
|
|
17
|
+
'',
|
|
18
|
+
report.summary,
|
|
19
|
+
'',
|
|
20
|
+
'## Mission Outcomes',
|
|
21
|
+
'',
|
|
22
|
+
];
|
|
23
|
+
for (const outcome of report.missionControl.missions) {
|
|
24
|
+
lines.push(...renderMissionOutcome(outcome));
|
|
25
|
+
}
|
|
26
|
+
if (report.baseline && report.comparison) {
|
|
27
|
+
lines.push(...renderBaselineComparison(report.baseline.path, report.baseline.totals, report.comparison));
|
|
28
|
+
}
|
|
29
|
+
lines.push('## Risk Avoided', '');
|
|
30
|
+
for (const item of report.riskAvoided) {
|
|
31
|
+
lines.push(`- ${item}`);
|
|
32
|
+
}
|
|
33
|
+
lines.push('', '## Next Actions', '');
|
|
34
|
+
for (const action of report.nextActions) {
|
|
35
|
+
lines.push(`- ${action.label}`);
|
|
36
|
+
if (action.command) {
|
|
37
|
+
lines.push('', '```bash', action.command, '```', '');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
41
|
+
}
|
|
42
|
+
function renderMissionOutcome(outcome) {
|
|
43
|
+
const lines = [
|
|
44
|
+
`### ${outcome.missionDir}`,
|
|
45
|
+
'',
|
|
46
|
+
`- Status: ${outcome.status}`,
|
|
47
|
+
`- Commands: ${outcome.proof.completedCommands} completed, ${outcome.proof.failedCommands} failed, ${outcome.proof.reruns} reruns`,
|
|
48
|
+
`- Version candidate: ${outcome.versionCandidate.recommendation}`,
|
|
49
|
+
`- Changed: ${joinEvidence(outcome.whatChanged)}`,
|
|
50
|
+
`- Remains: ${joinEvidence(outcome.whatRemains)}`,
|
|
51
|
+
];
|
|
52
|
+
if (!outcome.available && outcome.reason) {
|
|
53
|
+
lines.push(`- Reason: ${outcome.reason}`);
|
|
54
|
+
}
|
|
55
|
+
if (outcome.review.decisions.length > 0) {
|
|
56
|
+
lines.push(`- Reviewer decisions: ${outcome.review.decisions.map((decision) => decision.decision).join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push('');
|
|
59
|
+
return lines;
|
|
60
|
+
}
|
|
61
|
+
function renderBaselineComparison(baselinePath, baselineTotals, comparison) {
|
|
62
|
+
return [
|
|
63
|
+
'## Baseline Comparison',
|
|
64
|
+
'',
|
|
65
|
+
`- Baseline: ${baselinePath}`,
|
|
66
|
+
`- Baseline runs: ${baselineTotals.missions}`,
|
|
67
|
+
`- Completion delta: ${comparison.completionRateDelta}`,
|
|
68
|
+
`- Reruns avoided: ${comparison.rerunsAvoided}`,
|
|
69
|
+
`- Failed gates avoided: ${comparison.failedGatesAvoided}`,
|
|
70
|
+
`- Minutes saved: ${comparison.minutesSaved}`,
|
|
71
|
+
'',
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
function joinEvidence(values) {
|
|
75
|
+
return values.length > 0 ? values.join('; ') : 'None recorded.';
|
|
76
|
+
}
|
|
77
|
+
function formatPercent(value) {
|
|
78
|
+
return `${Math.round(value * 100)}%`;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=missionProofMarkdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missionProofMarkdown.js","sourceRoot":"","sources":["../../src/core/missionProofMarkdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function renderMissionProofSummary(report) {
|
|
2
|
+
const totals = report.missionControl.totals;
|
|
3
|
+
const status = totals.missions > 0 && totals.passed === totals.missions ? 'PASS' : 'FAIL';
|
|
4
|
+
return `Mission proof: ${status} (${summarizeTotals(totals)}).`;
|
|
5
|
+
}
|
|
6
|
+
function summarizeTotals(totals) {
|
|
7
|
+
const details = [
|
|
8
|
+
`${totals.passed} of ${totals.missions} passed`,
|
|
9
|
+
totals.failed > 0 ? `${totals.failed} failed` : undefined,
|
|
10
|
+
totals.running > 0 ? `${totals.running} running` : undefined,
|
|
11
|
+
totals.notRun > 0 ? `${totals.notRun} not run` : undefined,
|
|
12
|
+
totals.unavailable > 0 ? `${totals.unavailable} unavailable` : undefined,
|
|
13
|
+
].filter(Boolean);
|
|
14
|
+
return details.join('; ');
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=missionProofSummary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missionProofSummary.js","sourceRoot":"","sources":["../../src/core/missionProofSummary.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,yBAAyB,CAAC,MAA0B;IAClE,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1F,OAAO,kBAAkB,MAAM,KAAK,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC;AAClE,CAAC;AAED,SAAS,eAAe,CAAC,MAA0B;IACjD,MAAM,OAAO,GAAG;QACd,GAAG,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,QAAQ,SAAS;QAC/C,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,SAAS;QACzD,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,SAAS;QAC5D,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,SAAS;QAC1D,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,cAAc,CAAC,CAAC,CAAC,SAAS;KACzE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/core/start.d.ts
CHANGED
package/dist/core/start.js
CHANGED
|
@@ -5,6 +5,7 @@ import { buildFirstTenMinutes } from './onboarding.js';
|
|
|
5
5
|
import { computeQualityScorecard } from './qualityScorecard.js';
|
|
6
6
|
import { buildWorkplanHandoff, computeWorkplan, isWorkplanMode } from './workplan.js';
|
|
7
7
|
import { routeIntent } from './intentRouter.js';
|
|
8
|
+
import { loadMissionOutcome } from './missionOutcome.js';
|
|
8
9
|
import { getChangedFiles } from '../utils/changedFiles.js';
|
|
9
10
|
const DEFAULT_MAX_TASKS = 5;
|
|
10
11
|
const DEFAULT_MAX_RISKS = 5;
|
|
@@ -15,11 +16,12 @@ export async function computeStartReport(rootPath, options = {}) {
|
|
|
15
16
|
const mode = modeResolution.mode;
|
|
16
17
|
const maxTasks = normalizeLimit(options.maxTasks, DEFAULT_MAX_TASKS, 12);
|
|
17
18
|
const maxRisks = normalizeLimit(options.maxRisks, DEFAULT_MAX_RISKS, 12);
|
|
18
|
-
const [setup, workplan, quality, riskSources] = await Promise.all([
|
|
19
|
+
const [setup, workplan, quality, riskSources, missionOutcome] = await Promise.all([
|
|
19
20
|
computeFirstRunDiagnostics(rootPath),
|
|
20
21
|
computeWorkplan(rootPath, { mode, maxTasks }),
|
|
21
22
|
computeQualityScorecard(rootPath, { maxRisks }),
|
|
22
23
|
buildStartRiskSources(rootPath),
|
|
24
|
+
options.missionDir ? loadMissionOutcome(rootPath, options.missionDir) : Promise.resolve(undefined),
|
|
23
25
|
]);
|
|
24
26
|
const workflow = chooseWorkflow(mode, getWorkflowRecipes().recipes);
|
|
25
27
|
const topRisks = combineRisks(workplan, quality.topRisks, maxRisks);
|
|
@@ -46,6 +48,7 @@ export async function computeStartReport(rootPath, options = {}) {
|
|
|
46
48
|
adoptionGaps,
|
|
47
49
|
coordinationHints,
|
|
48
50
|
riskSources,
|
|
51
|
+
missionOutcome,
|
|
49
52
|
});
|
|
50
53
|
const nextActions = dedupeActions([
|
|
51
54
|
missionControl.primaryAction,
|
|
@@ -276,7 +279,7 @@ function buildMissionControl(input) {
|
|
|
276
279
|
successCriteria,
|
|
277
280
|
proofCommands,
|
|
278
281
|
});
|
|
279
|
-
const resume = missionResume(executionPlan);
|
|
282
|
+
const resume = missionResume(executionPlan, input.missionOutcome);
|
|
280
283
|
const reviewProof = buildMissionReviewProof(resume, proofCommands);
|
|
281
284
|
const whyNow = routed
|
|
282
285
|
? routedWhyNow(routed, actionPlan)
|
|
@@ -333,6 +336,7 @@ function buildMissionControl(input) {
|
|
|
333
336
|
runbook,
|
|
334
337
|
reviewGate,
|
|
335
338
|
taskCard,
|
|
339
|
+
...(input.missionOutcome ? { outcome: input.missionOutcome } : {}),
|
|
336
340
|
handoffPrompt,
|
|
337
341
|
};
|
|
338
342
|
}
|
|
@@ -680,7 +684,7 @@ function formatTaskCardToolCall(toolCall) {
|
|
|
680
684
|
? `${toolCall.tool} ${JSON.stringify(toolCall.args)}`
|
|
681
685
|
: toolCall.tool;
|
|
682
686
|
}
|
|
683
|
-
function missionResume(plan) {
|
|
687
|
+
function missionResume(plan, outcome) {
|
|
684
688
|
const cursor = plan.cursor;
|
|
685
689
|
const commandBlock = cursor.command && isRunnableCommand(cursor.command) ? cursor.command : undefined;
|
|
686
690
|
const toolCall = resumeToolCall(plan, cursor);
|
|
@@ -700,11 +704,12 @@ function missionResume(plan) {
|
|
|
700
704
|
const prompt = commandBlock
|
|
701
705
|
? `Resume at ${cursor.stepId} in ${cursor.phaseId}: run \`${commandBlock}\`.${resumeUnlocksSentence(unlocks, cursor.unlocks)}`
|
|
702
706
|
: `Resume at ${cursor.stepId} in ${cursor.phaseId}: ${instruction}${resumeBlockersSentence(blockedBy, cursor.blockedBy)}`;
|
|
707
|
+
const finalPrompt = outcome?.available ? `${outcome.resumePrompt} ${prompt}` : prompt;
|
|
703
708
|
return {
|
|
704
709
|
currentStep: cursor,
|
|
705
710
|
status: cursor.status,
|
|
706
711
|
instruction,
|
|
707
|
-
prompt,
|
|
712
|
+
prompt: finalPrompt,
|
|
708
713
|
...(commandBlock ? { commandBlock } : {}),
|
|
709
714
|
...(toolCall ? { toolCall } : {}),
|
|
710
715
|
...(followUps.length > 0 ? { followUps } : {}),
|