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.
Files changed (47) hide show
  1. package/README.md +93 -26
  2. package/dist/cli/commands/missionProof.d.ts +1 -0
  3. package/dist/cli/commands/missionProof.js +478 -0
  4. package/dist/cli/commands/missionProof.js.map +1 -0
  5. package/dist/cli/commands/start.js +18 -0
  6. package/dist/cli/commands/start.js.map +1 -1
  7. package/dist/cli/index.js +2 -0
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/core/missionOutcome.d.ts +2 -0
  10. package/dist/core/missionOutcome.js +219 -0
  11. package/dist/core/missionOutcome.js.map +1 -0
  12. package/dist/core/missionProof.d.ts +6 -0
  13. package/dist/core/missionProof.js +92 -0
  14. package/dist/core/missionProof.js.map +1 -0
  15. package/dist/core/missionProofBaseline.d.ts +10 -0
  16. package/dist/core/missionProofBaseline.js +131 -0
  17. package/dist/core/missionProofBaseline.js.map +1 -0
  18. package/dist/core/missionProofMarkdown.d.ts +2 -0
  19. package/dist/core/missionProofMarkdown.js +80 -0
  20. package/dist/core/missionProofMarkdown.js.map +1 -0
  21. package/dist/core/missionProofSummary.d.ts +2 -0
  22. package/dist/core/missionProofSummary.js +16 -0
  23. package/dist/core/missionProofSummary.js.map +1 -0
  24. package/dist/core/start.d.ts +1 -0
  25. package/dist/core/start.js +9 -4
  26. package/dist/core/start.js.map +1 -1
  27. package/dist/index.d.ts +6 -1
  28. package/dist/index.js +5 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp/tools/start.js +5 -0
  31. package/dist/mcp/tools/start.js.map +1 -1
  32. package/dist/projscan-sbom.cdx.json +6 -6
  33. package/dist/tool-manifest.json +6 -2
  34. package/dist/types.d.ts +91 -0
  35. package/dist/utils/formatSupport.d.ts +1 -0
  36. package/dist/utils/formatSupport.js +1 -0
  37. package/dist/utils/formatSupport.js.map +1 -1
  38. package/docs/GUIDE.md +23 -1
  39. package/docs/demos/projscan-4-1-demo.html +78 -94
  40. package/docs/demos/projscan-mission-control.tape +13 -0
  41. package/docs/demos/projscan-mission-proof.tape +25 -0
  42. package/docs/projscan-mission-control.gif +0 -0
  43. package/docs/projscan-mission-control.png +0 -0
  44. package/docs/projscan-mission-proof.gif +0 -0
  45. package/docs/projscan-proof-router.png +0 -0
  46. package/package.json +9 -2
  47. 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,2 @@
1
+ import type { MissionProofReport } from '../types.js';
2
+ export declare function renderMissionProofMarkdown(report: MissionProofReport): string;
@@ -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,2 @@
1
+ import type { MissionProofReport } from '../types.js';
2
+ export declare function renderMissionProofSummary(report: MissionProofReport): string;
@@ -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"}
@@ -2,6 +2,7 @@ import type { StartReport, WorkplanMode } from '../types.js';
2
2
  export interface ComputeStartOptions {
3
3
  mode?: WorkplanMode;
4
4
  intent?: string;
5
+ missionDir?: string;
5
6
  maxTasks?: number;
6
7
  maxRisks?: number;
7
8
  includeHandoff?: boolean;
@@ -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 } : {}),