martin-loop 0.1.5 → 1.3.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.
- package/CODE_OF_CONDUCT.md +32 -0
- package/LICENSE +21 -21
- package/README.md +307 -398
- package/demo/seeded-workspace/README.md +35 -35
- package/demo/seeded-workspace/TASKS.md +29 -29
- package/demo/seeded-workspace/martin.config.yaml +11 -11
- package/demo/seeded-workspace/package.json +8 -8
- package/demo/seeded-workspace/src/invoice-summary.js +11 -11
- package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
- package/dist/bin/martin-loop.js +0 -0
- package/dist/vendor/adapters/counter.d.ts +1 -0
- package/dist/vendor/adapters/counter.js +4 -0
- package/dist/vendor/adapters/git-baseline.d.ts +50 -0
- package/dist/vendor/adapters/git-baseline.js +233 -0
- package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
- package/dist/vendor/adapters/openrouter-adapter.js +302 -0
- package/dist/vendor/adapters/usage.d.ts +48 -0
- package/dist/vendor/adapters/usage.js +66 -0
- package/dist/vendor/cli/bin/exit.d.ts +12 -0
- package/dist/vendor/cli/bin/exit.js +28 -0
- package/dist/vendor/cli/commands/analyze.d.ts +5 -0
- package/dist/vendor/cli/commands/analyze.js +58 -0
- package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
- package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
- package/dist/vendor/cli/commands/audit.d.ts +8 -0
- package/dist/vendor/cli/commands/audit.js +199 -0
- package/dist/vendor/cli/commands/corpus.d.ts +5 -0
- package/dist/vendor/cli/commands/corpus.js +60 -0
- package/dist/vendor/cli/commands/doctor.d.ts +8 -0
- package/dist/vendor/cli/commands/doctor.js +219 -0
- package/dist/vendor/cli/commands/explain.d.ts +17 -0
- package/dist/vendor/cli/commands/explain.js +176 -0
- package/dist/vendor/cli/commands/export.d.ts +5 -0
- package/dist/vendor/cli/commands/export.js +60 -0
- package/dist/vendor/cli/commands/governance.d.ts +8 -0
- package/dist/vendor/cli/commands/governance.js +95 -0
- package/dist/vendor/cli/commands/improve.d.ts +18 -0
- package/dist/vendor/cli/commands/improve.js +396 -0
- package/dist/vendor/cli/commands/init.d.ts +8 -0
- package/dist/vendor/cli/commands/init.js +281 -0
- package/dist/vendor/cli/commands/migration.d.ts +8 -0
- package/dist/vendor/cli/commands/migration.js +67 -0
- package/dist/vendor/cli/commands/prior.d.ts +23 -0
- package/dist/vendor/cli/commands/prior.js +145 -0
- package/dist/vendor/cli/commands/resume.d.ts +21 -0
- package/dist/vendor/cli/commands/resume.js +73 -0
- package/dist/vendor/cli/commands/verify.d.ts +6 -0
- package/dist/vendor/cli/commands/verify.js +43 -0
- package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
- package/dist/vendor/cli/research/public-corpus.js +151 -0
- package/dist/vendor/cli/ui/error-card.d.ts +38 -0
- package/dist/vendor/cli/ui/error-card.js +103 -0
- package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
- package/dist/vendor/cli/ui/mission-brief.js +173 -0
- package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
- package/dist/vendor/cli/ui/summary-card.js +102 -0
- package/dist/vendor/contracts/audit.d.ts +46 -0
- package/dist/vendor/contracts/audit.js +360 -0
- package/dist/vendor/contracts/post-phase15.d.ts +240 -0
- package/dist/vendor/contracts/post-phase15.js +166 -0
- package/dist/vendor/core/agent/mandates.d.ts +46 -0
- package/dist/vendor/core/agent/mandates.js +178 -0
- package/dist/vendor/core/agent/receipts.d.ts +38 -0
- package/dist/vendor/core/agent/receipts.js +131 -0
- package/dist/vendor/core/agent/signing.d.ts +17 -0
- package/dist/vendor/core/agent/signing.js +91 -0
- package/dist/vendor/core/attestation/sign.d.ts +25 -0
- package/dist/vendor/core/attestation/sign.js +216 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
- package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
- package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
- package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
- package/dist/vendor/core/autonomy/envelope.js +27 -0
- package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
- package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
- package/dist/vendor/core/autonomy/resume.d.ts +15 -0
- package/dist/vendor/core/autonomy/resume.js +23 -0
- package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
- package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
- package/dist/vendor/core/context-distillation.d.ts +3 -0
- package/dist/vendor/core/context-distillation.js +44 -0
- package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
- package/dist/vendor/core/context-flow/compile-context.js +111 -0
- package/dist/vendor/core/context-flow/entities.d.ts +2 -0
- package/dist/vendor/core/context-flow/entities.js +44 -0
- package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
- package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
- package/dist/vendor/core/context-flow/index.d.ts +11 -0
- package/dist/vendor/core/context-flow/index.js +24 -0
- package/dist/vendor/core/context-flow/labels.d.ts +3 -0
- package/dist/vendor/core/context-flow/labels.js +17 -0
- package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
- package/dist/vendor/core/context-flow/normalizer.js +69 -0
- package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
- package/dist/vendor/core/context-flow/profiles.js +36 -0
- package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
- package/dist/vendor/core/context-flow/redaction.js +6 -0
- package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
- package/dist/vendor/core/context-flow/sensitivity.js +27 -0
- package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
- package/dist/vendor/core/context-flow/sync-preview.js +22 -0
- package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
- package/dist/vendor/core/context-flow/token-estimator.js +13 -0
- package/dist/vendor/core/context-flow/types.d.ts +91 -0
- package/dist/vendor/core/context-flow/types.js +2 -0
- package/dist/vendor/core/context-utility.d.ts +47 -0
- package/dist/vendor/core/context-utility.js +405 -0
- package/dist/vendor/core/cost/pipeline.d.ts +92 -0
- package/dist/vendor/core/cost/pipeline.js +141 -0
- package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
- package/dist/vendor/core/cost/tagged-cost.js +55 -0
- package/dist/vendor/core/cost-governor.d.ts +2 -0
- package/dist/vendor/core/cost-governor.js +50 -0
- package/dist/vendor/core/cve/cve-check.d.ts +80 -0
- package/dist/vendor/core/cve/cve-check.js +172 -0
- package/dist/vendor/core/digital-twin/index.d.ts +27 -0
- package/dist/vendor/core/digital-twin/index.js +90 -0
- package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
- package/dist/vendor/core/drift/drift-graph.js +100 -0
- package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
- package/dist/vendor/core/drift/objective-lock.js +88 -0
- package/dist/vendor/core/drift/scope.d.ts +46 -0
- package/dist/vendor/core/drift/scope.js +102 -0
- package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
- package/dist/vendor/core/drift/signature-lock.js +202 -0
- package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
- package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
- package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
- package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
- package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
- package/dist/vendor/core/evidence/claim-audit.js +89 -0
- package/dist/vendor/core/exit-intelligence.d.ts +2 -0
- package/dist/vendor/core/exit-intelligence.js +58 -0
- package/dist/vendor/core/explain/formatter.d.ts +42 -0
- package/dist/vendor/core/explain/formatter.js +171 -0
- package/dist/vendor/core/explain/timeline.d.ts +29 -0
- package/dist/vendor/core/explain/timeline.js +213 -0
- package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
- package/dist/vendor/core/failure-taxonomy.js +76 -0
- package/dist/vendor/core/gateway/index.d.ts +10 -0
- package/dist/vendor/core/gateway/index.js +12 -0
- package/dist/vendor/core/gateway/registry.d.ts +40 -0
- package/dist/vendor/core/gateway/registry.js +97 -0
- package/dist/vendor/core/gateway/transport.d.ts +31 -0
- package/dist/vendor/core/gateway/transport.js +82 -0
- package/dist/vendor/core/gateway/vault.d.ts +19 -0
- package/dist/vendor/core/gateway/vault.js +29 -0
- package/dist/vendor/core/graph/adapters.d.ts +43 -0
- package/dist/vendor/core/graph/adapters.js +91 -0
- package/dist/vendor/core/graph/hotspots.d.ts +22 -0
- package/dist/vendor/core/graph/hotspots.js +30 -0
- package/dist/vendor/core/graph/index.d.ts +1 -0
- package/dist/vendor/core/graph/index.js +2 -0
- package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
- package/dist/vendor/core/honey/honey-tokens.js +44 -0
- package/dist/vendor/core/index.d.ts +2 -2
- package/dist/vendor/core/index.js +38 -12
- package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
- package/dist/vendor/core/learning/bayesian-update.js +60 -0
- package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
- package/dist/vendor/core/learning/prior-sets.js +111 -0
- package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
- package/dist/vendor/core/learning/promotion-gate.js +23 -0
- package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
- package/dist/vendor/core/leash/blast-radius.js +156 -0
- package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
- package/dist/vendor/core/leash/policy-leash.js +117 -0
- package/dist/vendor/core/memo/memo.d.ts +63 -0
- package/dist/vendor/core/memo/memo.js +97 -0
- package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
- package/dist/vendor/core/memory/learning-pipeline.js +391 -0
- package/dist/vendor/core/memory/palace.d.ts +84 -0
- package/dist/vendor/core/memory/palace.js +379 -0
- package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
- package/dist/vendor/core/merge/ast-merge.js +350 -0
- package/dist/vendor/core/merge/text-merge.d.ts +12 -0
- package/dist/vendor/core/merge/text-merge.js +182 -0
- package/dist/vendor/core/otel/tracer.d.ts +45 -0
- package/dist/vendor/core/otel/tracer.js +116 -0
- package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
- package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
- package/dist/vendor/core/parallel/scorer.d.ts +24 -0
- package/dist/vendor/core/parallel/scorer.js +65 -0
- package/dist/vendor/core/pattern-detection.d.ts +64 -0
- package/dist/vendor/core/pattern-detection.js +108 -0
- package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
- package/dist/vendor/core/persistence/checkpoint.js +156 -0
- package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
- package/dist/vendor/core/persistence/cleanup.js +131 -0
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
- package/dist/vendor/core/persistence/runs-reader.js +84 -0
- package/dist/vendor/core/persistence/store.d.ts +6 -1
- package/dist/vendor/core/persistence/store.js +5 -0
- package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
- package/dist/vendor/core/policy/file-touch-quota.js +105 -0
- package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
- package/dist/vendor/core/policy/policy-loader.js +170 -0
- package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
- package/dist/vendor/core/policy/policy-schema.js +78 -0
- package/dist/vendor/core/probe/probe.d.ts +49 -0
- package/dist/vendor/core/probe/probe.js +115 -0
- package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
- package/dist/vendor/core/proof/patch-proof.js +84 -0
- package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
- package/dist/vendor/core/proof/semantic-probe.js +82 -0
- package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
- package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
- package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
- package/dist/vendor/core/red-blue/red-phase.js +141 -0
- package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
- package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
- package/dist/vendor/core/replay/replay.d.ts +85 -0
- package/dist/vendor/core/replay/replay.js +109 -0
- package/dist/vendor/core/router/engine.d.ts +54 -0
- package/dist/vendor/core/router/engine.js +131 -0
- package/dist/vendor/core/router/index.d.ts +1 -0
- package/dist/vendor/core/router/index.js +2 -0
- package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
- package/dist/vendor/core/router/trust-calibration.js +127 -0
- package/dist/vendor/core/run-martin.d.ts +2 -0
- package/dist/vendor/core/run-martin.js +287 -0
- package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
- package/dist/vendor/core/security/cve-scanner.js +178 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
- package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
- package/dist/vendor/core/sentinel/progress-guard.js +46 -0
- package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
- package/dist/vendor/core/siem/siem-emitter.js +157 -0
- package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
- package/dist/vendor/core/strategy/attempt-brief.js +89 -0
- package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
- package/dist/vendor/core/summarize/diff-summary.js +204 -0
- package/dist/vendor/core/surface-signals.d.ts +21 -0
- package/dist/vendor/core/surface-signals.js +139 -0
- package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
- package/dist/vendor/core/truth/truth-wall.js +69 -0
- package/dist/vendor/core/truth-spine.d.ts +26 -0
- package/dist/vendor/core/truth-spine.js +62 -0
- package/dist/vendor/core/types.d.ts +115 -0
- package/dist/vendor/core/types.js +2 -0
- package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
- package/dist/vendor/core/verification/tiered-verify.js +29 -0
- package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
- package/dist/vendor/core/verifier-pyramid.js +111 -0
- package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
- package/dist/vendor/core/workflow-artifacts.js +668 -0
- package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
- package/dist/vendor/core/wrap/supervised-run.js +178 -0
- package/docs/assets/cli-animated.svg +139 -0
- package/docs/assets/cli-static.svg +34 -0
- package/docs/assets/github-hero-v2.svg +23 -0
- package/docs/assets/martin-raplph.png.jpg +0 -0
- package/docs/assets/martinloop-logo.png +0 -0
- package/docs/assets/nvidia-inception-program-light.png +0 -0
- package/docs/assets/nvidia-inception-program.png +0 -0
- package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
- package/docs/assets/side-by-side.svg +134 -0
- package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
- package/docs/oss/EXAMPLES.md +134 -134
- package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
- package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
- package/docs/oss/QUICKSTART.md +170 -165
- package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
- package/docs/oss/README.md +96 -96
- package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
- package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
- package/package.json +130 -58
- package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
- package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
- package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type PublicResearchSource = "github" | "reddit" | "hackernews" | "docs" | "forum" | "blog" | "social";
|
|
2
|
+
export type PublicResearchSeverity = "low" | "medium" | "high" | "critical";
|
|
3
|
+
export type PublicResearchActionability = "low" | "medium" | "high";
|
|
4
|
+
export type PublicResearchFrequencyBucket = "isolated" | "recurring" | "saturated";
|
|
5
|
+
export interface PublicResearchItemInput {
|
|
6
|
+
source: PublicResearchSource;
|
|
7
|
+
url: string;
|
|
8
|
+
date: string;
|
|
9
|
+
ecosystem: string;
|
|
10
|
+
persona: string;
|
|
11
|
+
problem: string;
|
|
12
|
+
severity: PublicResearchSeverity;
|
|
13
|
+
excerpt: string;
|
|
14
|
+
actionability: PublicResearchActionability;
|
|
15
|
+
martinLoopFit: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface PublicResearchItem extends PublicResearchItemInput {
|
|
18
|
+
canonicalUrl: string;
|
|
19
|
+
fingerprint: string;
|
|
20
|
+
normalizedProblem: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PublicResearchProblemSummary {
|
|
23
|
+
problem: string;
|
|
24
|
+
normalizedProblem: string;
|
|
25
|
+
count: number;
|
|
26
|
+
ecosystems: string[];
|
|
27
|
+
recommendedFit: string[];
|
|
28
|
+
frequencyBucket: PublicResearchFrequencyBucket;
|
|
29
|
+
}
|
|
30
|
+
export interface PublicResearchCorpusSummary {
|
|
31
|
+
totalItems: number;
|
|
32
|
+
bySource: Record<PublicResearchSource, number>;
|
|
33
|
+
byProblem: PublicResearchProblemSummary[];
|
|
34
|
+
topFits: Array<{
|
|
35
|
+
fit: string;
|
|
36
|
+
count: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export declare function normalizePublicResearchItem(input: PublicResearchItemInput): PublicResearchItem;
|
|
40
|
+
export declare function dedupePublicResearchItems(items: PublicResearchItemInput[]): PublicResearchItem[];
|
|
41
|
+
export declare function summarizePublicResearchCorpus(items: PublicResearchItem[]): PublicResearchCorpusSummary;
|
|
42
|
+
export declare function renderPublicResearchCorpusReport(summary: PublicResearchCorpusSummary): string;
|
|
43
|
+
export declare function bucketFrequency(count: number): PublicResearchFrequencyBucket;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function normalizePublicResearchItem(input) {
|
|
3
|
+
const canonicalUrl = normalizeUrl(input.url);
|
|
4
|
+
const normalizedProblem = normalizeText(input.problem);
|
|
5
|
+
const normalizedExcerpt = normalizeText(input.excerpt);
|
|
6
|
+
return {
|
|
7
|
+
...input,
|
|
8
|
+
ecosystem: input.ecosystem.trim(),
|
|
9
|
+
persona: input.persona.trim(),
|
|
10
|
+
problem: input.problem.trim(),
|
|
11
|
+
excerpt: input.excerpt.trim(),
|
|
12
|
+
martinLoopFit: uniqueStrings(input.martinLoopFit),
|
|
13
|
+
canonicalUrl,
|
|
14
|
+
normalizedProblem,
|
|
15
|
+
fingerprint: createFingerprint(canonicalUrl, normalizedProblem, normalizeText(input.ecosystem), normalizedExcerpt.slice(0, 140))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function dedupePublicResearchItems(items) {
|
|
19
|
+
const deduped = new Map();
|
|
20
|
+
for (const item of items.map(normalizePublicResearchItem)) {
|
|
21
|
+
const duplicateKey = `${item.canonicalUrl}::${item.normalizedProblem}`;
|
|
22
|
+
const existing = deduped.get(duplicateKey);
|
|
23
|
+
if (!existing) {
|
|
24
|
+
deduped.set(duplicateKey, item);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
deduped.set(duplicateKey, choosePreferredItem(existing, item));
|
|
28
|
+
}
|
|
29
|
+
return [...deduped.values()].sort((left, right) => right.date.localeCompare(left.date));
|
|
30
|
+
}
|
|
31
|
+
export function summarizePublicResearchCorpus(items) {
|
|
32
|
+
const bySource = {
|
|
33
|
+
github: 0,
|
|
34
|
+
reddit: 0,
|
|
35
|
+
hackernews: 0,
|
|
36
|
+
docs: 0,
|
|
37
|
+
forum: 0,
|
|
38
|
+
blog: 0,
|
|
39
|
+
social: 0
|
|
40
|
+
};
|
|
41
|
+
const problems = new Map();
|
|
42
|
+
const fits = new Map();
|
|
43
|
+
for (const item of items) {
|
|
44
|
+
bySource[item.source] += 1;
|
|
45
|
+
const existingProblem = problems.get(item.normalizedProblem);
|
|
46
|
+
if (!existingProblem) {
|
|
47
|
+
problems.set(item.normalizedProblem, {
|
|
48
|
+
problem: item.problem,
|
|
49
|
+
normalizedProblem: item.normalizedProblem,
|
|
50
|
+
count: 1,
|
|
51
|
+
ecosystems: [item.ecosystem],
|
|
52
|
+
recommendedFit: [...item.martinLoopFit],
|
|
53
|
+
frequencyBucket: "isolated"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
existingProblem.count += 1;
|
|
58
|
+
existingProblem.ecosystems = uniqueStrings([...existingProblem.ecosystems, item.ecosystem]);
|
|
59
|
+
existingProblem.recommendedFit = uniqueStrings([
|
|
60
|
+
...existingProblem.recommendedFit,
|
|
61
|
+
...item.martinLoopFit
|
|
62
|
+
]);
|
|
63
|
+
existingProblem.frequencyBucket = bucketFrequency(existingProblem.count);
|
|
64
|
+
}
|
|
65
|
+
for (const fit of item.martinLoopFit) {
|
|
66
|
+
fits.set(fit, (fits.get(fit) ?? 0) + 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
totalItems: items.length,
|
|
71
|
+
bySource,
|
|
72
|
+
byProblem: [...problems.values()].sort((left, right) => right.count - left.count),
|
|
73
|
+
topFits: [...fits.entries()]
|
|
74
|
+
.map(([fit, count]) => ({ fit, count }))
|
|
75
|
+
.sort((left, right) => right.count - left.count)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function renderPublicResearchCorpusReport(summary) {
|
|
79
|
+
const sourceLines = Object.entries(summary.bySource)
|
|
80
|
+
.filter(([, count]) => count > 0)
|
|
81
|
+
.map(([source, count]) => `- ${source}: ${String(count)}`);
|
|
82
|
+
const problemLines = summary.byProblem.slice(0, 10).map((problem) => `- ${problem.problem} — ${String(problem.count)} items (${problem.frequencyBucket})`);
|
|
83
|
+
const fitLines = summary.topFits.slice(0, 7).map((fit) => `- ${fit.fit}: ${String(fit.count)} linked sources`);
|
|
84
|
+
return [
|
|
85
|
+
"# Public Research Corpus Summary",
|
|
86
|
+
"",
|
|
87
|
+
`Total deduped items: ${String(summary.totalItems)}`,
|
|
88
|
+
"",
|
|
89
|
+
"## Sources",
|
|
90
|
+
...(sourceLines.length > 0 ? sourceLines : ["- none"]),
|
|
91
|
+
"",
|
|
92
|
+
"## Top Problems",
|
|
93
|
+
...(problemLines.length > 0 ? problemLines : ["- none"]),
|
|
94
|
+
"",
|
|
95
|
+
"## Martin Loop Fits",
|
|
96
|
+
...(fitLines.length > 0 ? fitLines : ["- none"]),
|
|
97
|
+
""
|
|
98
|
+
].join("\n");
|
|
99
|
+
}
|
|
100
|
+
export function bucketFrequency(count) {
|
|
101
|
+
if (count >= 10)
|
|
102
|
+
return "saturated";
|
|
103
|
+
if (count >= 3)
|
|
104
|
+
return "recurring";
|
|
105
|
+
return "isolated";
|
|
106
|
+
}
|
|
107
|
+
function choosePreferredItem(left, right) {
|
|
108
|
+
const leftScore = scoreItem(left);
|
|
109
|
+
const rightScore = scoreItem(right);
|
|
110
|
+
return rightScore > leftScore ? right : left;
|
|
111
|
+
}
|
|
112
|
+
function scoreItem(item) {
|
|
113
|
+
const severityScore = {
|
|
114
|
+
low: 1,
|
|
115
|
+
medium: 2,
|
|
116
|
+
high: 3,
|
|
117
|
+
critical: 4
|
|
118
|
+
};
|
|
119
|
+
const actionabilityScore = {
|
|
120
|
+
low: 1,
|
|
121
|
+
medium: 2,
|
|
122
|
+
high: 3
|
|
123
|
+
};
|
|
124
|
+
return (severityScore[item.severity] * 10 +
|
|
125
|
+
actionabilityScore[item.actionability] * 5 +
|
|
126
|
+
item.martinLoopFit.length);
|
|
127
|
+
}
|
|
128
|
+
function normalizeUrl(url) {
|
|
129
|
+
const trimmed = url.trim();
|
|
130
|
+
const parsed = new URL(trimmed);
|
|
131
|
+
parsed.hash = "";
|
|
132
|
+
parsed.searchParams.sort();
|
|
133
|
+
if ((parsed.hostname === "www.reddit.com" || parsed.hostname === "reddit.com") &&
|
|
134
|
+
parsed.pathname.endsWith("/")) {
|
|
135
|
+
parsed.pathname = parsed.pathname.slice(0, -1);
|
|
136
|
+
}
|
|
137
|
+
return parsed.toString();
|
|
138
|
+
}
|
|
139
|
+
function normalizeText(value) {
|
|
140
|
+
return value
|
|
141
|
+
.toLowerCase()
|
|
142
|
+
.replace(/[^a-z0-9]+/gu, " ")
|
|
143
|
+
.trim();
|
|
144
|
+
}
|
|
145
|
+
function uniqueStrings(values) {
|
|
146
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
147
|
+
}
|
|
148
|
+
function createFingerprint(...parts) {
|
|
149
|
+
return createHash("sha256").update(parts.join("::"), "utf8").digest("hex");
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=public-corpus.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* error-card.ts
|
|
3
|
+
*
|
|
4
|
+
* Renders a structured error card to stderr after a fatal CLI failure.
|
|
5
|
+
* Mirrors the visual language of summary-card.ts (CARD_WIDTH=56, box-drawing).
|
|
6
|
+
* Respects MARTIN_NO_SUMMARY env var and --json flag.
|
|
7
|
+
*/
|
|
8
|
+
export interface ErrorCardInput {
|
|
9
|
+
/** Short all-caps machine code, e.g. "BUDGET_EXCEEDED", "CONFIG_NOT_FOUND" */
|
|
10
|
+
code: string;
|
|
11
|
+
/** Human-readable one-liner describing what went wrong */
|
|
12
|
+
message: string;
|
|
13
|
+
/** Optional recovery suggestion shown below the message */
|
|
14
|
+
hint?: string;
|
|
15
|
+
/** Optional key-value detail rows (e.g. { file: "martin.config.yaml", limit: "$0.05" }) */
|
|
16
|
+
context?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns true when the error card should be rendered.
|
|
20
|
+
* Mirrors shouldRenderSummaryCard — checks TTY, MARTIN_NO_SUMMARY, --json.
|
|
21
|
+
*/
|
|
22
|
+
export declare function shouldRenderErrorCard(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Renders a box-drawing error card string ready to write to stderr.
|
|
25
|
+
*
|
|
26
|
+
* Example output:
|
|
27
|
+
* ╔══════════════════════════════════════════════════════╗
|
|
28
|
+
* ║ MARTIN ERROR [BUDGET_EXCEEDED] ║
|
|
29
|
+
* ╠══════════════════════════════════════════════════════╣
|
|
30
|
+
* ║ Task cost exceeded configured budget limit. ║
|
|
31
|
+
* ║ ║
|
|
32
|
+
* ║ hint: increase budget_limit in martin.config.yaml ║
|
|
33
|
+
* ╠══════════════════════════════════════════════════════╣
|
|
34
|
+
* ║ limit $0.05 ║
|
|
35
|
+
* ║ spent $0.08 ║
|
|
36
|
+
* ╚══════════════════════════════════════════════════════╝
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderErrorCard(input: ErrorCardInput): string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* error-card.ts
|
|
3
|
+
*
|
|
4
|
+
* Renders a structured error card to stderr after a fatal CLI failure.
|
|
5
|
+
* Mirrors the visual language of summary-card.ts (CARD_WIDTH=56, box-drawing).
|
|
6
|
+
* Respects MARTIN_NO_SUMMARY env var and --json flag.
|
|
7
|
+
*/
|
|
8
|
+
const CARD_WIDTH = 56;
|
|
9
|
+
const INNER_WIDTH = CARD_WIDTH - 4; // 2 border chars + 2 padding spaces
|
|
10
|
+
/**
|
|
11
|
+
* Returns true when the error card should be rendered.
|
|
12
|
+
* Mirrors shouldRenderSummaryCard — checks TTY, MARTIN_NO_SUMMARY, --json.
|
|
13
|
+
*/
|
|
14
|
+
export function shouldRenderErrorCard() {
|
|
15
|
+
if (process.env.MARTIN_NO_SUMMARY === "1")
|
|
16
|
+
return false;
|
|
17
|
+
if (!process.stderr.isTTY && process.env.MARTIN_FORCE_SUMMARY !== "1")
|
|
18
|
+
return false;
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Renders a box-drawing error card string ready to write to stderr.
|
|
23
|
+
*
|
|
24
|
+
* Example output:
|
|
25
|
+
* ╔══════════════════════════════════════════════════════╗
|
|
26
|
+
* ║ MARTIN ERROR [BUDGET_EXCEEDED] ║
|
|
27
|
+
* ╠══════════════════════════════════════════════════════╣
|
|
28
|
+
* ║ Task cost exceeded configured budget limit. ║
|
|
29
|
+
* ║ ║
|
|
30
|
+
* ║ hint: increase budget_limit in martin.config.yaml ║
|
|
31
|
+
* ╠══════════════════════════════════════════════════════╣
|
|
32
|
+
* ║ limit $0.05 ║
|
|
33
|
+
* ║ spent $0.08 ║
|
|
34
|
+
* ╚══════════════════════════════════════════════════════╝
|
|
35
|
+
*/
|
|
36
|
+
export function renderErrorCard(input) {
|
|
37
|
+
const lines = [];
|
|
38
|
+
const top = `╔${"═".repeat(CARD_WIDTH - 2)}╗`;
|
|
39
|
+
const bottom = `╚${"═".repeat(CARD_WIDTH - 2)}╝`;
|
|
40
|
+
const divider = `╠${"═".repeat(CARD_WIDTH - 2)}╣`;
|
|
41
|
+
// Header row: "MARTIN ERROR" left, "[CODE]" right
|
|
42
|
+
const codeTag = `[${truncate(input.code, 20)}]`;
|
|
43
|
+
const headerLabel = "MARTIN ERROR";
|
|
44
|
+
const headerGap = INNER_WIDTH - headerLabel.length - codeTag.length;
|
|
45
|
+
const headerPad = headerGap > 0 ? " ".repeat(headerGap) : " ";
|
|
46
|
+
lines.push(top);
|
|
47
|
+
lines.push(`║ ${headerLabel}${headerPad}${codeTag} ║`);
|
|
48
|
+
lines.push(divider);
|
|
49
|
+
// Message (word-wrapped to INNER_WIDTH)
|
|
50
|
+
for (const segment of wrapText(input.message, INNER_WIDTH)) {
|
|
51
|
+
lines.push(row(segment));
|
|
52
|
+
}
|
|
53
|
+
// Hint row
|
|
54
|
+
if (input.hint !== undefined) {
|
|
55
|
+
lines.push(row(""));
|
|
56
|
+
lines.push(row(truncate(`hint: ${input.hint}`, INNER_WIDTH)));
|
|
57
|
+
}
|
|
58
|
+
// Context key-value rows
|
|
59
|
+
const entries = Object.entries(input.context ?? {});
|
|
60
|
+
if (entries.length > 0) {
|
|
61
|
+
lines.push(divider);
|
|
62
|
+
const keyWidth = Math.min(16, Math.max(...entries.map(([k]) => k.length)) + 2);
|
|
63
|
+
for (const [key, value] of entries) {
|
|
64
|
+
const paddedKey = key.padEnd(keyWidth);
|
|
65
|
+
lines.push(row(truncate(`${paddedKey}${value}`, INNER_WIDTH)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
lines.push(bottom);
|
|
69
|
+
return lines.join("\n") + "\n";
|
|
70
|
+
}
|
|
71
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
72
|
+
function row(content) {
|
|
73
|
+
const remaining = INNER_WIDTH - content.length;
|
|
74
|
+
const padding = remaining > 0 ? " ".repeat(remaining) : "";
|
|
75
|
+
return `║ ${content}${padding} ║`;
|
|
76
|
+
}
|
|
77
|
+
function truncate(str, maxLen) {
|
|
78
|
+
if (str.length <= maxLen)
|
|
79
|
+
return str;
|
|
80
|
+
return `${str.slice(0, maxLen - 3)}...`;
|
|
81
|
+
}
|
|
82
|
+
function wrapText(text, maxWidth) {
|
|
83
|
+
if (text.length <= maxWidth)
|
|
84
|
+
return [text];
|
|
85
|
+
const words = text.split(" ");
|
|
86
|
+
const wrapped = [];
|
|
87
|
+
let current = "";
|
|
88
|
+
for (const word of words) {
|
|
89
|
+
const candidate = current.length === 0 ? word : `${current} ${word}`;
|
|
90
|
+
if (candidate.length <= maxWidth) {
|
|
91
|
+
current = candidate;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (current.length > 0)
|
|
95
|
+
wrapped.push(current);
|
|
96
|
+
current = truncate(word, maxWidth);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (current.length > 0)
|
|
100
|
+
wrapped.push(current);
|
|
101
|
+
return wrapped.length > 0 ? wrapped : [""];
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=error-card.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mission-brief.ts
|
|
3
|
+
*
|
|
4
|
+
* Pre-run gate rendered before any attempt loop starts.
|
|
5
|
+
*
|
|
6
|
+
* Displays blast radius, trust calibration, and budget so the user can
|
|
7
|
+
* abort before spending a single token. High-risk runs require explicit
|
|
8
|
+
* confirmation ("proceed") to prevent accidental expensive jobs.
|
|
9
|
+
*
|
|
10
|
+
* Skip conditions:
|
|
11
|
+
* MARTIN_NO_BRIEF=1 env var — always skip (CI/automation)
|
|
12
|
+
* --yes / -y flag — skip interactive prompt, still print the brief
|
|
13
|
+
* --json flag — suppress entirely (piped usage)
|
|
14
|
+
* non-TTY stdin — suppress unless MARTIN_FORCE_BRIEF=1; prompt never waits
|
|
15
|
+
*
|
|
16
|
+
* The brief is written to stderr so stdout stays JSON-clean. Interactivity is
|
|
17
|
+
* based on stdin for input safety and stderr for display capability.
|
|
18
|
+
*/
|
|
19
|
+
import type { BlastRadiusResult } from "../../core/index.js";
|
|
20
|
+
import type { ModelTrustProfile } from "../../core/index.js";
|
|
21
|
+
import type { LoopBudget } from "../../contracts/index.js";
|
|
22
|
+
export interface MissionBriefInput {
|
|
23
|
+
objective: string;
|
|
24
|
+
budget: LoopBudget;
|
|
25
|
+
blast: BlastRadiusResult;
|
|
26
|
+
/** Best trust profile for the likely model, if calibration has enough data */
|
|
27
|
+
trustProfile?: ModelTrustProfile | null;
|
|
28
|
+
/** Model selected by the router (may differ from trust profile) */
|
|
29
|
+
selectedModel?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function shouldRenderMissionBrief(args: string[]): boolean;
|
|
32
|
+
export declare function renderMissionBrief(input: MissionBriefInput): string;
|
|
33
|
+
/**
|
|
34
|
+
* Awaits user confirmation before a run starts.
|
|
35
|
+
*
|
|
36
|
+
* - High-risk runs require typing "proceed" exactly.
|
|
37
|
+
* - Normal runs accept any input (Enter key).
|
|
38
|
+
* - Returns false if the user aborts or times out (30s).
|
|
39
|
+
* - In non-TTY or when skip=true, returns true immediately.
|
|
40
|
+
*/
|
|
41
|
+
export declare function awaitMissionBriefConfirmation(blast: BlastRadiusResult, skip: boolean): Promise<boolean>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mission-brief.ts
|
|
3
|
+
*
|
|
4
|
+
* Pre-run gate rendered before any attempt loop starts.
|
|
5
|
+
*
|
|
6
|
+
* Displays blast radius, trust calibration, and budget so the user can
|
|
7
|
+
* abort before spending a single token. High-risk runs require explicit
|
|
8
|
+
* confirmation ("proceed") to prevent accidental expensive jobs.
|
|
9
|
+
*
|
|
10
|
+
* Skip conditions:
|
|
11
|
+
* MARTIN_NO_BRIEF=1 env var — always skip (CI/automation)
|
|
12
|
+
* --yes / -y flag — skip interactive prompt, still print the brief
|
|
13
|
+
* --json flag — suppress entirely (piped usage)
|
|
14
|
+
* non-TTY stdin — suppress unless MARTIN_FORCE_BRIEF=1; prompt never waits
|
|
15
|
+
*
|
|
16
|
+
* The brief is written to stderr so stdout stays JSON-clean. Interactivity is
|
|
17
|
+
* based on stdin for input safety and stderr for display capability.
|
|
18
|
+
*/
|
|
19
|
+
const CARD_WIDTH = 58;
|
|
20
|
+
const INNER_WIDTH = CARD_WIDTH - 4;
|
|
21
|
+
export function shouldRenderMissionBrief(args) {
|
|
22
|
+
if (process.env.MARTIN_NO_BRIEF === "1")
|
|
23
|
+
return false;
|
|
24
|
+
if (args.includes("--json"))
|
|
25
|
+
return false;
|
|
26
|
+
if (process.env.MARTIN_FORCE_BRIEF === "1")
|
|
27
|
+
return true;
|
|
28
|
+
if (!process.stdin.isTTY)
|
|
29
|
+
return false;
|
|
30
|
+
if (!process.stderr.isTTY)
|
|
31
|
+
return false;
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
export function renderMissionBrief(input) {
|
|
35
|
+
const { objective, budget, blast, trustProfile, selectedModel } = input;
|
|
36
|
+
const top = `╔${"═".repeat(CARD_WIDTH - 2)}╗`;
|
|
37
|
+
const bottom = `╚${"═".repeat(CARD_WIDTH - 2)}╝`;
|
|
38
|
+
const divider = `╠${"═".repeat(CARD_WIDTH - 2)}╣`;
|
|
39
|
+
const riskLabel = blast.regressionRisk === "high"
|
|
40
|
+
? "HIGH RISK"
|
|
41
|
+
: blast.regressionRisk === "medium"
|
|
42
|
+
? "Medium risk"
|
|
43
|
+
: "Low risk";
|
|
44
|
+
const blastStr = `${blast.score}/100 · ${riskLabel}`;
|
|
45
|
+
const budgetStr = `$${budget.maxUsd.toFixed(2)} · ${budget.maxIterations ?? 5} iterations max`;
|
|
46
|
+
const lines = [];
|
|
47
|
+
lines.push(top);
|
|
48
|
+
lines.push(row("Martin Loop — Mission Brief"));
|
|
49
|
+
lines.push(divider);
|
|
50
|
+
lines.push(row(`Task ${truncate(objective, INNER_WIDTH - 10)}`));
|
|
51
|
+
const modelLabel = selectedModel ?? trustProfile?.model ?? "auto-selected";
|
|
52
|
+
const modelSuffix = trustProfile
|
|
53
|
+
? ` (${Math.round(trustProfile.efficiencyScore * 100)}% efficiency · ${trustProfile.runsObserved} runs)`
|
|
54
|
+
: "";
|
|
55
|
+
lines.push(row(`Engine ${truncate(modelLabel + modelSuffix, INNER_WIDTH - 10)}`));
|
|
56
|
+
lines.push(row(`Budget ${budgetStr}`));
|
|
57
|
+
lines.push(row(`Blast ${blastStr}`));
|
|
58
|
+
if (blast.regressionRisk === "high" && blast.hotspotsInPath.length > 0) {
|
|
59
|
+
lines.push(divider);
|
|
60
|
+
for (const file of blast.hotspotsInPath.slice(0, 3)) {
|
|
61
|
+
lines.push(row(` Hotspot: ${truncate(file, INNER_WIDTH - 12)}`));
|
|
62
|
+
}
|
|
63
|
+
if (blast.hotspotsInPath.length > 3) {
|
|
64
|
+
lines.push(row(` + ${blast.hotspotsInPath.length - 3} more hotspot files`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (blast.rationale) {
|
|
68
|
+
lines.push(divider);
|
|
69
|
+
const parts = blast.rationale.split("; ");
|
|
70
|
+
for (const part of parts.slice(0, 2)) {
|
|
71
|
+
lines.push(row(` ${truncate(part, INNER_WIDTH - 2)}`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
lines.push(divider);
|
|
75
|
+
if (blast.regressionRisk === "high") {
|
|
76
|
+
lines.push(row(` Blast score ${blast.score}/100. Type "proceed" to continue.`));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
lines.push(row(` Press Enter to proceed or Ctrl+C to abort.`));
|
|
80
|
+
}
|
|
81
|
+
lines.push(bottom);
|
|
82
|
+
return lines.join("\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Awaits user confirmation before a run starts.
|
|
86
|
+
*
|
|
87
|
+
* - High-risk runs require typing "proceed" exactly.
|
|
88
|
+
* - Normal runs accept any input (Enter key).
|
|
89
|
+
* - Returns false if the user aborts or times out (30s).
|
|
90
|
+
* - In non-TTY or when skip=true, returns true immediately.
|
|
91
|
+
*/
|
|
92
|
+
export async function awaitMissionBriefConfirmation(blast, skip) {
|
|
93
|
+
if (skip || !process.stdin.isTTY)
|
|
94
|
+
return true;
|
|
95
|
+
const isHighRisk = blast.regressionRisk === "high";
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
const timeout = setTimeout(() => {
|
|
98
|
+
cleanup();
|
|
99
|
+
process.stderr.write("\nNo response — aborting run to protect budget.\n");
|
|
100
|
+
resolve(false);
|
|
101
|
+
}, 30_000);
|
|
102
|
+
function cleanup() {
|
|
103
|
+
clearTimeout(timeout);
|
|
104
|
+
process.stdin.setRawMode?.(false);
|
|
105
|
+
process.stdin.pause();
|
|
106
|
+
process.stdin.removeListener("data", onData);
|
|
107
|
+
}
|
|
108
|
+
let buffer = "";
|
|
109
|
+
function onData(chunk) {
|
|
110
|
+
const text = chunk.toString();
|
|
111
|
+
// Ctrl+C — abort
|
|
112
|
+
if (text === "\x03") {
|
|
113
|
+
process.stderr.write("\nAborted.\n");
|
|
114
|
+
cleanup();
|
|
115
|
+
resolve(false);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (isHighRisk) {
|
|
119
|
+
// Accumulate typed characters, echo them, check for Enter
|
|
120
|
+
for (const ch of text) {
|
|
121
|
+
if (ch === "\r" || ch === "\n") {
|
|
122
|
+
process.stderr.write("\n");
|
|
123
|
+
const confirmed = buffer.trim().toLowerCase() === "proceed";
|
|
124
|
+
if (!confirmed) {
|
|
125
|
+
process.stderr.write(`Type "proceed" to confirm, or Ctrl+C to abort.\n`);
|
|
126
|
+
buffer = "";
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
cleanup();
|
|
130
|
+
resolve(true);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (ch === "\x7f" && buffer.length > 0) {
|
|
134
|
+
buffer = buffer.slice(0, -1);
|
|
135
|
+
process.stderr.write("\b \b");
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
buffer += ch;
|
|
139
|
+
process.stderr.write(ch);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Any key (Enter) proceeds
|
|
145
|
+
cleanup();
|
|
146
|
+
resolve(true);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
process.stdin.setRawMode?.(true);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// setRawMode not available (e.g. Windows without a pty)
|
|
154
|
+
}
|
|
155
|
+
process.stdin.resume();
|
|
156
|
+
process.stdin.on("data", onData);
|
|
157
|
+
if (isHighRisk) {
|
|
158
|
+
process.stderr.write('\n> ');
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
163
|
+
function row(content) {
|
|
164
|
+
const remaining = CARD_WIDTH - 4 - content.length;
|
|
165
|
+
const padding = remaining > 0 ? " ".repeat(remaining) : "";
|
|
166
|
+
return `║ ${content}${padding} ║`;
|
|
167
|
+
}
|
|
168
|
+
function truncate(str, maxLen) {
|
|
169
|
+
if (str.length <= maxLen)
|
|
170
|
+
return str;
|
|
171
|
+
return `${str.slice(0, maxLen - 3)}...`;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=mission-brief.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summary-card.ts
|
|
3
|
+
*
|
|
4
|
+
* Renders a human-readable run summary card to stdout after every martin run.
|
|
5
|
+
* Respects --json flag and MARTIN_NO_SUMMARY env var.
|
|
6
|
+
* Width: 56 chars to fit any terminal >= 60 cols.
|
|
7
|
+
*/
|
|
8
|
+
export interface RunSummaryInput {
|
|
9
|
+
succeeded: boolean;
|
|
10
|
+
loopId: string;
|
|
11
|
+
objective: string;
|
|
12
|
+
attemptCount: number;
|
|
13
|
+
maxAttempts: number;
|
|
14
|
+
grade?: string;
|
|
15
|
+
groundingScore?: number;
|
|
16
|
+
modelName?: string;
|
|
17
|
+
probeTier?: string;
|
|
18
|
+
costUsd: number;
|
|
19
|
+
probeCostUsd?: number;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
lastBlockReason?: string;
|
|
22
|
+
rollbackComplete?: boolean;
|
|
23
|
+
/** Router cost telemetry — populated when MartinRouter is active */
|
|
24
|
+
routerSelectedModel?: string;
|
|
25
|
+
routerCostPer1kTcs?: number;
|
|
26
|
+
routerTrustTier?: "high" | "medium" | "low";
|
|
27
|
+
/**
|
|
28
|
+
* Estimated blast radius of the executed action on a 0–100 scale.
|
|
29
|
+
* Display + telemetry only in Phase 32 — no gate enforcement until Phase 33.
|
|
30
|
+
*/
|
|
31
|
+
blastRadius?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function shouldRenderSummaryCard(args: string[]): boolean;
|
|
34
|
+
export declare function renderSummaryCard(input: RunSummaryInput): string;
|