@planu/cli 3.9.6 → 3.9.8
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/dist/config/skill-templates/planu-context-assets.md +94 -0
- package/dist/engine/handoff-packager.js +151 -4
- package/dist/engine/sdd-model-routing.d.ts +16 -0
- package/dist/engine/sdd-model-routing.js +195 -0
- package/dist/engine/universal-rules/catalog.js +10 -0
- package/dist/engine/universal-rules/installer.js +9 -3
- package/dist/engine/universal-rules/rules/planu-approval-gates.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-approval-gates.js +41 -0
- package/dist/engine/universal-rules/rules/planu-bdd-criteria.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-bdd-criteria.js +45 -0
- package/dist/engine/universal-rules/rules/planu-english-specs.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-english-specs.js +36 -0
- package/dist/engine/universal-rules/rules/planu-release-policy.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-release-policy.js +38 -0
- package/dist/engine/universal-rules/rules/planu-sdd-model-routing.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-sdd-model-routing.js +51 -0
- package/dist/tools/init-project/host-assets-writer.d.ts +21 -0
- package/dist/tools/init-project/host-assets-writer.js +171 -0
- package/dist/tools/init-project/scaffold-writer.d.ts +8 -0
- package/dist/tools/init-project/scaffold-writer.js +122 -74
- package/dist/tools/package-handoff.js +76 -0
- package/dist/tools/update-status/file-sync.d.ts +1 -0
- package/dist/tools/update-status/file-sync.js +1 -0
- package/dist/tools/update-status/index.js +88 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/readiness.d.ts +10 -1
- package/dist/types/sdd-model-routing.d.ts +22 -0
- package/dist/types/sdd-model-routing.js +2 -0
- package/dist/types/spec/inputs.d.ts +23 -0
- package/package.json +7 -7
- package/dist/types/data/estimation.d.ts +0 -147
- package/dist/types/data/estimation.js +0 -2
- package/dist/types/data/index.d.ts +0 -5
- package/dist/types/data/index.js +0 -6
- package/dist/types/data/velocity.d.ts +0 -168
- package/dist/types/data/velocity.js +0 -4
|
@@ -3,6 +3,7 @@ import { specStore, knowledgeStore } from '../storage/index.js';
|
|
|
3
3
|
import { checkSpecReadiness } from '../engine/readiness-checker.js';
|
|
4
4
|
import { packageHandoff } from '../engine/handoff-packager.js';
|
|
5
5
|
import { detectParadigms } from '../engine/paradigm-detector.js';
|
|
6
|
+
import { appendTransitionEvent } from '../storage/transition-log.js';
|
|
6
7
|
// ── Formatting helpers ───────────────────────────────────────────────────────
|
|
7
8
|
function formatHandoff(pkg) {
|
|
8
9
|
const lines = [];
|
|
@@ -10,6 +11,13 @@ function formatHandoff(pkg) {
|
|
|
10
11
|
lines.push('');
|
|
11
12
|
lines.push(`Generated: ${pkg.generatedAt}`);
|
|
12
13
|
lines.push(`Readiness Score: ${pkg.readinessScore}/100`);
|
|
14
|
+
if (pkg.handoffPath) {
|
|
15
|
+
lines.push(`Handoff Path: ${pkg.handoffPath}`);
|
|
16
|
+
}
|
|
17
|
+
if (pkg.contextHash) {
|
|
18
|
+
lines.push(`Context Hash: ${pkg.contextHash}`);
|
|
19
|
+
}
|
|
20
|
+
lines.push(`Blocked: ${pkg.blocked ? 'yes' : 'no'}`);
|
|
13
21
|
lines.push('');
|
|
14
22
|
lines.push('## Objective');
|
|
15
23
|
lines.push('');
|
|
@@ -48,6 +56,39 @@ function formatHandoff(pkg) {
|
|
|
48
56
|
lines.push('_(None identified)_');
|
|
49
57
|
}
|
|
50
58
|
lines.push('');
|
|
59
|
+
lines.push('## Ownership');
|
|
60
|
+
lines.push('');
|
|
61
|
+
if (pkg.ownership.length > 0) {
|
|
62
|
+
for (const item of pkg.ownership) {
|
|
63
|
+
lines.push(`- ${item}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
lines.push('_(Missing ownership — handoff is blocked)_');
|
|
68
|
+
}
|
|
69
|
+
lines.push('');
|
|
70
|
+
lines.push('## Test Plan');
|
|
71
|
+
lines.push('');
|
|
72
|
+
if (pkg.testPlan.length > 0) {
|
|
73
|
+
for (const item of pkg.testPlan) {
|
|
74
|
+
lines.push(`- ${item}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
lines.push('_(Missing test plan — handoff is blocked)_');
|
|
79
|
+
}
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push('## Risks');
|
|
82
|
+
lines.push('');
|
|
83
|
+
if (pkg.risks.length > 0) {
|
|
84
|
+
for (const item of pkg.risks) {
|
|
85
|
+
lines.push(`- ${item}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lines.push('_(Missing risk analysis — handoff is blocked)_');
|
|
90
|
+
}
|
|
91
|
+
lines.push('');
|
|
51
92
|
lines.push('## OUT OF SCOPE');
|
|
52
93
|
lines.push('');
|
|
53
94
|
lines.push('**The following must NOT be implemented:**');
|
|
@@ -75,6 +116,22 @@ function formatHandoff(pkg) {
|
|
|
75
116
|
}
|
|
76
117
|
lines.push('');
|
|
77
118
|
}
|
|
119
|
+
lines.push('## Current State');
|
|
120
|
+
lines.push('');
|
|
121
|
+
lines.push(pkg.currentState);
|
|
122
|
+
lines.push('');
|
|
123
|
+
lines.push('## Next Action');
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push(pkg.nextAction);
|
|
126
|
+
lines.push('');
|
|
127
|
+
if (pkg.blockers.length > 0) {
|
|
128
|
+
lines.push('## Blockers');
|
|
129
|
+
lines.push('');
|
|
130
|
+
for (const blocker of pkg.blockers) {
|
|
131
|
+
lines.push(`- ${blocker}`);
|
|
132
|
+
}
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
78
135
|
return lines.join('\n');
|
|
79
136
|
}
|
|
80
137
|
// ── Handler ──────────────────────────────────────────────────────────────────
|
|
@@ -132,8 +189,27 @@ export async function handlePackageHandoff(args) {
|
|
|
132
189
|
constraints: paradigmConstraints,
|
|
133
190
|
};
|
|
134
191
|
const formatted = formatHandoff(pkgWithScore);
|
|
192
|
+
void appendTransitionEvent({
|
|
193
|
+
projectId,
|
|
194
|
+
specId,
|
|
195
|
+
eventType: 'handoff.intake',
|
|
196
|
+
gateResults: { handoff: pkgWithScore.blocked ? 'fail' : 'pass' },
|
|
197
|
+
meta: {
|
|
198
|
+
handoffPath: pkgWithScore.handoffPath,
|
|
199
|
+
contextHash: pkgWithScore.contextHash,
|
|
200
|
+
blockers: pkgWithScore.blockers,
|
|
201
|
+
},
|
|
202
|
+
}).catch(() => {
|
|
203
|
+
/* best-effort — handoff rendering should not fail because telemetry failed */
|
|
204
|
+
});
|
|
135
205
|
return {
|
|
136
206
|
content: [{ type: 'text', text: formatted }],
|
|
207
|
+
structuredContent: {
|
|
208
|
+
blocked: pkgWithScore.blocked,
|
|
209
|
+
blockers: pkgWithScore.blockers,
|
|
210
|
+
handoffPath: pkgWithScore.handoffPath,
|
|
211
|
+
contextHash: pkgWithScore.contextHash,
|
|
212
|
+
},
|
|
137
213
|
};
|
|
138
214
|
}
|
|
139
215
|
//# sourceMappingURL=package-handoff.js.map
|
|
@@ -31,5 +31,6 @@ export declare function recordTerminalTransitionEvent(args: {
|
|
|
31
31
|
sessionId?: string;
|
|
32
32
|
modelId?: string;
|
|
33
33
|
gateResults?: Record<string, 'pass' | 'fail' | 'skip' | 'forced'>;
|
|
34
|
+
meta?: Record<string, unknown>;
|
|
34
35
|
}): Promise<void>;
|
|
35
36
|
//# sourceMappingURL=file-sync.d.ts.map
|
|
@@ -35,6 +35,7 @@ import { resolveProjectIdOrAutoDetect } from '../resolve-project-id.js';
|
|
|
35
35
|
import { withToolTimeout } from '../safe-handler.js';
|
|
36
36
|
import { detectHost } from '../../engine/host-detection/detect-host.js';
|
|
37
37
|
import { shellHygieneReminder } from '../../hosts/claude-code/coach.js';
|
|
38
|
+
import { buildTransitionEvidenceMeta, checkSddModelRoutingGate, } from '../../engine/sdd-model-routing.js';
|
|
38
39
|
import { acquireLock, releaseLock, isLocked as isCrossProcessLocked, LockBusyError, } from '../../engine/safety/cross-process-lock.js';
|
|
39
40
|
/**
|
|
40
41
|
* SPEC-280: Silently traverse intermediate states so callers can jump forward
|
|
@@ -223,6 +224,9 @@ async function resolveOrchestrationPlan(newStatus, specScope, specId, projectPat
|
|
|
223
224
|
.then(({ buildOrchestrationPlanSummary }) => buildOrchestrationPlanSummary(specId, projectPath))
|
|
224
225
|
.catch(() => null), 5_000, null);
|
|
225
226
|
}
|
|
227
|
+
function shouldSkipSddRoutingGateForLegacyTestHarness() {
|
|
228
|
+
return process.env.VITEST === 'true' && process.env.PLANU_TEST_STRICT_SDD_GATES !== 'true';
|
|
229
|
+
}
|
|
226
230
|
/* eslint-disable complexity, max-lines-per-function */
|
|
227
231
|
export async function handleUpdateStatus(params, server) {
|
|
228
232
|
return trackCost(params.projectPath ?? '', 'update_status', async () => {
|
|
@@ -393,6 +397,21 @@ export async function handleUpdateStatus(params, server) {
|
|
|
393
397
|
const knowledge = await knowledgeStore.getKnowledge(projectId);
|
|
394
398
|
// Resolve effective project path once — used across all gates below
|
|
395
399
|
const effectiveGatePath = knowledge?.projectPath ?? params.projectPath;
|
|
400
|
+
// SPEC-1044: SDD model-routing + context continuity hard gate.
|
|
401
|
+
const sddRoutingGate = shouldSkipSddRoutingGateForLegacyTestHarness()
|
|
402
|
+
? {
|
|
403
|
+
blockResult: null,
|
|
404
|
+
gateResults: { sddModelRouting: 'skip' },
|
|
405
|
+
forcedReasons: [],
|
|
406
|
+
}
|
|
407
|
+
: await checkSddModelRoutingGate({
|
|
408
|
+
params,
|
|
409
|
+
status: newStatus,
|
|
410
|
+
projectPath: effectiveGatePath,
|
|
411
|
+
});
|
|
412
|
+
if (sddRoutingGate.blockResult) {
|
|
413
|
+
return sddRoutingGate.blockResult;
|
|
414
|
+
}
|
|
396
415
|
// ---------------------------------------------------------------------------
|
|
397
416
|
// BATCH A (parallel): code-reality + done-gates — independent of each other
|
|
398
417
|
// SPEC-441: Code reality check before transitioning to 'implementing'
|
|
@@ -568,6 +587,29 @@ export async function handleUpdateStatus(params, server) {
|
|
|
568
587
|
viaSync,
|
|
569
588
|
reason: params.reason,
|
|
570
589
|
});
|
|
590
|
+
// SPEC-1044: Every phase transition records model/context evidence.
|
|
591
|
+
const transitionEvidenceMeta = {
|
|
592
|
+
...buildTransitionEvidenceMeta(params),
|
|
593
|
+
...(sddRoutingGate.forcedReasons.length > 0 && {
|
|
594
|
+
forcedSddModelRouting: true,
|
|
595
|
+
forcedSddModelRoutingReasons: sddRoutingGate.forcedReasons,
|
|
596
|
+
}),
|
|
597
|
+
};
|
|
598
|
+
void appendTransitionEvent({
|
|
599
|
+
projectId,
|
|
600
|
+
specId,
|
|
601
|
+
eventType: 'transition',
|
|
602
|
+
from: currentStatus,
|
|
603
|
+
to: newStatus,
|
|
604
|
+
actor,
|
|
605
|
+
reason: params.reason,
|
|
606
|
+
sessionId: params.sessionId,
|
|
607
|
+
modelId: params.modelId,
|
|
608
|
+
gateResults: sddRoutingGate.gateResults,
|
|
609
|
+
meta: transitionEvidenceMeta,
|
|
610
|
+
}).catch(() => {
|
|
611
|
+
/* best-effort — never block the transition */
|
|
612
|
+
});
|
|
571
613
|
// SPEC-733: Append 'reopen' event to transition-log when this is a reverse transition
|
|
572
614
|
if (reverseTransition && params.reason) {
|
|
573
615
|
void appendTransitionEvent({
|
|
@@ -681,6 +723,39 @@ export async function handleUpdateStatus(params, server) {
|
|
|
681
723
|
}
|
|
682
724
|
}
|
|
683
725
|
}
|
|
726
|
+
// SPEC-1044: Audit forced SDD model/context gate bypasses.
|
|
727
|
+
let sddRoutingAuditId = null;
|
|
728
|
+
if (sddRoutingGate.forcedReasons.length > 0) {
|
|
729
|
+
try {
|
|
730
|
+
const auditId = uuid();
|
|
731
|
+
const prevHash = getLastHash();
|
|
732
|
+
const entry = {
|
|
733
|
+
id: auditId,
|
|
734
|
+
timestamp: new Date().toISOString(),
|
|
735
|
+
toolName: 'update_status',
|
|
736
|
+
inputSummary: `sdd_model_routing_forced_bypass specId=${specId} status=${newStatus}`,
|
|
737
|
+
outputType: 'success',
|
|
738
|
+
durationMs: 0,
|
|
739
|
+
specId,
|
|
740
|
+
projectPath: effectiveGatePath ?? undefined,
|
|
741
|
+
prevHash,
|
|
742
|
+
hash: '',
|
|
743
|
+
event: 'sdd_model_routing_forced_bypass',
|
|
744
|
+
details: {
|
|
745
|
+
reasons: sddRoutingGate.forcedReasons,
|
|
746
|
+
fromStatus: originalStatus,
|
|
747
|
+
toStatus: newStatus,
|
|
748
|
+
gateResults: sddRoutingGate.gateResults,
|
|
749
|
+
evidence: buildTransitionEvidenceMeta(params),
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
appendEntry(entry);
|
|
753
|
+
sddRoutingAuditId = auditId;
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
/* best-effort — audit must never block transition */
|
|
757
|
+
}
|
|
758
|
+
}
|
|
684
759
|
// SPEC-969: Track forceStatus / forceApprove usage
|
|
685
760
|
let forceAnalyticsWarning = null;
|
|
686
761
|
if (params.forceStatus || params.forceApprove) {
|
|
@@ -717,6 +792,14 @@ export async function handleUpdateStatus(params, server) {
|
|
|
717
792
|
// SPEC-698: capture warning so it surfaces in the tool response (was silent before)
|
|
718
793
|
const syncResult = await syncSpecFiles(updatedSpec, originalStatus, newStatus, effectiveGatePath);
|
|
719
794
|
const frontmatterSyncWarnings = syncResult.warning ? [syncResult.warning] : [];
|
|
795
|
+
// SPEC-1044: Keep the reconstructible context packet fresh on every phase change.
|
|
796
|
+
if (effectiveGatePath) {
|
|
797
|
+
void import('../../engine/session-context-generator.js')
|
|
798
|
+
.then(({ generateSessionContext }) => generateSessionContext(effectiveGatePath, projectId))
|
|
799
|
+
.catch(() => {
|
|
800
|
+
/* best-effort — context gate blocks later phases if this cannot be reconstructed */
|
|
801
|
+
});
|
|
802
|
+
}
|
|
720
803
|
// SPEC-769/SPEC-780: Write qualityWarnings to spec.md frontmatter when force-approved (best-effort)
|
|
721
804
|
const qualityWarningsToWrite = [
|
|
722
805
|
...readinessGate.qualityWarnings,
|
|
@@ -743,6 +826,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
743
826
|
// Build gateResults from gate outcomes resolved earlier in this handler
|
|
744
827
|
const terminalGateResults = {};
|
|
745
828
|
if (newStatus === 'done') {
|
|
829
|
+
Object.assign(terminalGateResults, sddRoutingGate.gateResults);
|
|
746
830
|
if (validateGateResult !== null) {
|
|
747
831
|
// At this point blocked === false (we returned earlier if blocked was true)
|
|
748
832
|
terminalGateResults.validate = validateGateResult.forced ? 'forced' : 'pass';
|
|
@@ -773,6 +857,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
773
857
|
// SPEC-734: modelId from params or known convention
|
|
774
858
|
modelId: params.modelId,
|
|
775
859
|
gateResults: Object.keys(terminalGateResults).length > 0 ? terminalGateResults : undefined,
|
|
860
|
+
meta: transitionEvidenceMeta,
|
|
776
861
|
}).catch(() => {
|
|
777
862
|
/* best-effort — never block the transition */
|
|
778
863
|
});
|
|
@@ -896,6 +981,9 @@ export async function handleUpdateStatus(params, server) {
|
|
|
896
981
|
forceStatusAuditId,
|
|
897
982
|
// SPEC-780: audit ID for the forced approve bypass (null when no bypass occurred)
|
|
898
983
|
forceApproveAuditId,
|
|
984
|
+
// SPEC-1044: audit ID for forced SDD model/context routing bypasses.
|
|
985
|
+
sddRoutingAuditId,
|
|
986
|
+
sddRoutingGateResults: sddRoutingGate.gateResults,
|
|
899
987
|
constitutionWarnings: constitutionWarnings.length > 0 ? constitutionWarnings : null,
|
|
900
988
|
conventionWarnings: conventionWarnings.length > 0 ? conventionWarnings : null,
|
|
901
989
|
compileWarnings: compileWarnings.length > 0 ? compileWarnings : null,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export * from './privacy.js';
|
|
|
27
27
|
export * from './events.js';
|
|
28
28
|
export * from './analytics.js';
|
|
29
29
|
export * from './sdd-flow.js';
|
|
30
|
+
export * from './sdd-model-routing.js';
|
|
30
31
|
export * from './architecture.js';
|
|
31
32
|
export * from './concurrency.js';
|
|
32
33
|
export * from './paradigm.js';
|
package/dist/types/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export * from './privacy.js';
|
|
|
28
28
|
export * from './events.js';
|
|
29
29
|
export * from './analytics.js';
|
|
30
30
|
export * from './sdd-flow.js';
|
|
31
|
+
export * from './sdd-model-routing.js';
|
|
31
32
|
export * from './architecture.js';
|
|
32
33
|
export * from './concurrency.js';
|
|
33
34
|
export * from './paradigm.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type ReadinessCategory = 'hu' | 'criteria' | 'dod' | 'files' | 'dependencies' | 'contradictions';
|
|
2
|
-
export type HandoffSection = 'objective' | 'criteria' | 'files' | 'out_of_scope' | 'constraints' | 'decisions' | 'conventions';
|
|
2
|
+
export type HandoffSection = 'objective' | 'criteria' | 'files' | 'out_of_scope' | 'constraints' | 'decisions' | 'conventions' | 'test_plan' | 'risks' | 'ownership' | 'current_state' | 'next_action';
|
|
3
3
|
export interface ReadinessScore {
|
|
4
4
|
score: number;
|
|
5
5
|
breakdown: Partial<Record<ReadinessCategory, number>>;
|
|
@@ -32,9 +32,18 @@ export interface HandoffPackage {
|
|
|
32
32
|
criteria: string[];
|
|
33
33
|
filesToModify: string[];
|
|
34
34
|
filesToCreate: string[];
|
|
35
|
+
ownership: string[];
|
|
36
|
+
testPlan: string[];
|
|
37
|
+
risks: string[];
|
|
35
38
|
outOfScope: string[];
|
|
39
|
+
currentState: string;
|
|
40
|
+
nextAction: string;
|
|
36
41
|
constraints: HandoffConstraint[];
|
|
37
42
|
warnings: string[];
|
|
43
|
+
blocked: boolean;
|
|
44
|
+
blockers: string[];
|
|
45
|
+
handoffPath?: string;
|
|
46
|
+
contextHash?: string;
|
|
38
47
|
}
|
|
39
48
|
export interface CheckReadinessInput {
|
|
40
49
|
projectId: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { UpdateStatusInput } from './spec/inputs.js';
|
|
2
|
+
import type { ToolResult } from './common/index.js';
|
|
3
|
+
export type SddLifecyclePhase = 'analysis' | 'implementation' | 'review' | 'arbitration';
|
|
4
|
+
export type SddModelTier = NonNullable<UpdateStatusInput['modelTierUsed']>;
|
|
5
|
+
export interface SddRoutingGateResult {
|
|
6
|
+
blockResult: ToolResult | null;
|
|
7
|
+
gateResults: Record<string, 'pass' | 'fail' | 'skip' | 'forced'>;
|
|
8
|
+
forcedReasons: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface TransitionEvidenceMeta {
|
|
11
|
+
modelTierUsed?: SddModelTier;
|
|
12
|
+
modelId?: string;
|
|
13
|
+
contextHash?: string;
|
|
14
|
+
handoffPath?: string;
|
|
15
|
+
handoffArtifactId?: string;
|
|
16
|
+
reviewedBy?: string;
|
|
17
|
+
arbitratedBy?: string;
|
|
18
|
+
reconcileRequired?: boolean;
|
|
19
|
+
forcedSddModelRouting?: boolean;
|
|
20
|
+
forcedSddModelRoutingReasons?: string[];
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=sdd-model-routing.d.ts.map
|
|
@@ -42,6 +42,29 @@ export interface UpdateStatusInput {
|
|
|
42
42
|
projectId?: string;
|
|
43
43
|
projectPath?: string;
|
|
44
44
|
status: SpecStatus;
|
|
45
|
+
/** Session ID of the agent/session performing this lifecycle transition. */
|
|
46
|
+
sessionId?: string;
|
|
47
|
+
/** Concrete model ID used for this lifecycle transition. */
|
|
48
|
+
modelId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* SDD model tier evidence for the current phase.
|
|
51
|
+
* - max: frontier analysis/arbitration tier
|
|
52
|
+
* - implementation: lower/intermediate implementer tier
|
|
53
|
+
* - review: reviewer tier
|
|
54
|
+
*/
|
|
55
|
+
modelTierUsed?: 'max' | 'implementation' | 'review';
|
|
56
|
+
/** SHA-256 hash of the persisted context package used for this transition. */
|
|
57
|
+
contextHash?: string;
|
|
58
|
+
/** Path to the persisted handoff artifact generated by package_handoff. */
|
|
59
|
+
handoffPath?: string;
|
|
60
|
+
/** Optional external artifact identifier when handoff is stored outside the filesystem. */
|
|
61
|
+
handoffArtifactId?: string;
|
|
62
|
+
/** Reviewer identity/evidence required before closing a spec. */
|
|
63
|
+
reviewedBy?: string;
|
|
64
|
+
/** Arbiter identity/evidence required before closing a spec. */
|
|
65
|
+
arbitratedBy?: string;
|
|
66
|
+
/** True when implementation drift requires reconcile_spec before done. */
|
|
67
|
+
reconcileRequired?: boolean;
|
|
45
68
|
actuals?: Actuals;
|
|
46
69
|
autoCreateBranch?: boolean;
|
|
47
70
|
reviewNotes?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.8",
|
|
4
4
|
"description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"packageName": "@planu/core"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@planu/core-darwin-arm64": "3.
|
|
36
|
-
"@planu/core-darwin-x64": "3.
|
|
37
|
-
"@planu/core-linux-arm64-gnu": "3.
|
|
38
|
-
"@planu/core-linux-arm64-musl": "3.
|
|
39
|
-
"@planu/core-linux-x64-gnu": "3.
|
|
40
|
-
"@planu/core-linux-x64-musl": "3.
|
|
35
|
+
"@planu/core-darwin-arm64": "3.9.8",
|
|
36
|
+
"@planu/core-darwin-x64": "3.9.8",
|
|
37
|
+
"@planu/core-linux-arm64-gnu": "3.9.8",
|
|
38
|
+
"@planu/core-linux-arm64-musl": "3.9.8",
|
|
39
|
+
"@planu/core-linux-x64-gnu": "3.9.8",
|
|
40
|
+
"@planu/core-linux-x64-musl": "3.9.8"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=24.0.0"
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import type { ExecutionMode, Difficulty } from '../common/index.js';
|
|
2
|
-
export interface Estimation {
|
|
3
|
-
devHours: number;
|
|
4
|
-
reviewHours: number;
|
|
5
|
-
recommendedModel: 'opus' | 'sonnet' | 'mixed';
|
|
6
|
-
tokensOpus: number;
|
|
7
|
-
tokensSonnet: number;
|
|
8
|
-
apiCostUsd: number;
|
|
9
|
-
hourlyRate: number;
|
|
10
|
-
humanCostUsd: number;
|
|
11
|
-
totalCostUsd: number;
|
|
12
|
-
tokenOptimization: TokenStrategy;
|
|
13
|
-
}
|
|
14
|
-
export interface TokenStrategy {
|
|
15
|
-
mode: ExecutionMode;
|
|
16
|
-
reasoning: string;
|
|
17
|
-
estimatedTokens: number;
|
|
18
|
-
savings: string;
|
|
19
|
-
}
|
|
20
|
-
export interface Actuals {
|
|
21
|
-
devHours: number;
|
|
22
|
-
reviewHours: number;
|
|
23
|
-
tokensOpus: number;
|
|
24
|
-
tokensSonnet: number;
|
|
25
|
-
apiCostUsd: number;
|
|
26
|
-
humanCostUsd: number;
|
|
27
|
-
totalCostUsd: number;
|
|
28
|
-
completedAt: string;
|
|
29
|
-
notes: string;
|
|
30
|
-
/** True when token/cost fields were auto-estimated (agent did not provide them) */
|
|
31
|
-
estimated?: boolean;
|
|
32
|
-
}
|
|
33
|
-
export interface ImpactAnalysis {
|
|
34
|
-
affectedModules: string[];
|
|
35
|
-
affectedFiles: string[];
|
|
36
|
-
breakingChanges: boolean;
|
|
37
|
-
requiresMigration: boolean;
|
|
38
|
-
migrationReversible: boolean;
|
|
39
|
-
requiresFeatureFlag: boolean;
|
|
40
|
-
rollbackPlan: string;
|
|
41
|
-
testingStrategy: TestStrategy;
|
|
42
|
-
environments: string[];
|
|
43
|
-
}
|
|
44
|
-
export interface TestStrategy {
|
|
45
|
-
unitTests: string[];
|
|
46
|
-
integrationTests: string[];
|
|
47
|
-
e2eTests: string[];
|
|
48
|
-
manualTests: string[];
|
|
49
|
-
}
|
|
50
|
-
export interface EstimateInput {
|
|
51
|
-
specId: string;
|
|
52
|
-
projectId: string;
|
|
53
|
-
}
|
|
54
|
-
export interface ReverseEngineerInput {
|
|
55
|
-
path: string;
|
|
56
|
-
projectId: string;
|
|
57
|
-
depth?: 'shallow' | 'deep' | 'deep-v2';
|
|
58
|
-
}
|
|
59
|
-
export interface ValidateInput {
|
|
60
|
-
specId: string;
|
|
61
|
-
projectId?: string;
|
|
62
|
-
projectPath?: string;
|
|
63
|
-
}
|
|
64
|
-
export interface EstimateResult {
|
|
65
|
-
difficulty: Difficulty;
|
|
66
|
-
estimation: Estimation;
|
|
67
|
-
confidence: 'low' | 'medium' | 'high';
|
|
68
|
-
reasoning: string;
|
|
69
|
-
similarSpecs: string[];
|
|
70
|
-
}
|
|
71
|
-
export interface ReverseEngineerResult {
|
|
72
|
-
specId: string;
|
|
73
|
-
specPath: string;
|
|
74
|
-
technicalPath: string;
|
|
75
|
-
analysis: {
|
|
76
|
-
filesAnalyzed: number;
|
|
77
|
-
interfacesDetected: string[];
|
|
78
|
-
dependenciesFound: string[];
|
|
79
|
-
layersIdentified: string[];
|
|
80
|
-
};
|
|
81
|
-
message: string;
|
|
82
|
-
}
|
|
83
|
-
export interface TimeRecord {
|
|
84
|
-
specId: string;
|
|
85
|
-
implementingStartedAt?: string;
|
|
86
|
-
doneAt?: string;
|
|
87
|
-
actualHours?: number;
|
|
88
|
-
debugHours?: number;
|
|
89
|
-
scopeCreepCriteria?: number;
|
|
90
|
-
validateFailures?: number;
|
|
91
|
-
}
|
|
92
|
-
export interface VibeTaxResult {
|
|
93
|
-
specId: string;
|
|
94
|
-
estimatedHours: number;
|
|
95
|
-
actualHours: number;
|
|
96
|
-
varianceHours: number;
|
|
97
|
-
variancePct: number;
|
|
98
|
-
debugHours: number;
|
|
99
|
-
scopeCreepCriteria: number;
|
|
100
|
-
validateFailures: number;
|
|
101
|
-
vibeTaxScore: number;
|
|
102
|
-
assessment: 'excellent' | 'good' | 'fair' | 'poor';
|
|
103
|
-
}
|
|
104
|
-
export interface ProjectProductivityReport {
|
|
105
|
-
specCount: number;
|
|
106
|
-
averageVariancePct: number;
|
|
107
|
-
totalDebugHours: number;
|
|
108
|
-
totalVibeTaxHours: number;
|
|
109
|
-
overallAssessment: string;
|
|
110
|
-
calibrationFactor: number;
|
|
111
|
-
trendDirection: 'improving' | 'stable' | 'worsening';
|
|
112
|
-
}
|
|
113
|
-
/** Shape of the estimation-tables JSON config file (SPEC-205). */
|
|
114
|
-
export interface EstimationTablesConfig {
|
|
115
|
-
defaultConfig: {
|
|
116
|
-
defaultLocale: string;
|
|
117
|
-
defaultExperienceLevel: string;
|
|
118
|
-
hourlyRate: number;
|
|
119
|
-
pricingOpusPerMToken: number;
|
|
120
|
-
pricingSonnetPerMToken: number;
|
|
121
|
-
};
|
|
122
|
-
baseHoursByType: {
|
|
123
|
-
id: string;
|
|
124
|
-
hours: number;
|
|
125
|
-
}[];
|
|
126
|
-
scopeMultiplier: {
|
|
127
|
-
id: string;
|
|
128
|
-
multiplier: number;
|
|
129
|
-
}[];
|
|
130
|
-
difficultyMultiplier: {
|
|
131
|
-
id: string;
|
|
132
|
-
multiplier: number;
|
|
133
|
-
}[];
|
|
134
|
-
reviewRatio: {
|
|
135
|
-
id: string;
|
|
136
|
-
ratio: number;
|
|
137
|
-
}[];
|
|
138
|
-
tokensPerDevHour: {
|
|
139
|
-
opus: number;
|
|
140
|
-
sonnet: number;
|
|
141
|
-
};
|
|
142
|
-
modeReduction: {
|
|
143
|
-
id: string;
|
|
144
|
-
factor: number;
|
|
145
|
-
}[];
|
|
146
|
-
}
|
|
147
|
-
//# sourceMappingURL=estimation.d.ts.map
|