@sonnechasser/ntrp 1.5.3 → 1.5.7
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/index.js +1853 -283
- package/dist/mcp/server.js +1565 -70
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -194,6 +194,17 @@ function formatCurrency(value) {
|
|
|
194
194
|
if (value >= 1e3) return `$${(value / 1e3).toFixed(0)}K`;
|
|
195
195
|
return `$${value.toFixed(0)}`;
|
|
196
196
|
}
|
|
197
|
+
function formatConstraintLine(aggregate) {
|
|
198
|
+
if (!aggregate) return void 0;
|
|
199
|
+
const gating = aggregate.gating_vital_sign;
|
|
200
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
201
|
+
const vital = aggregate.vital_signs.find((v) => v.vital_sign === gating);
|
|
202
|
+
if (vital?.dollar_value != null && vital.dollar_value > 0) {
|
|
203
|
+
const dollars = `${formatCurrency(vital.dollar_value)} ${vital.dollar_label ?? ""}`.trim();
|
|
204
|
+
return `${label} (${dollars})`;
|
|
205
|
+
}
|
|
206
|
+
return label;
|
|
207
|
+
}
|
|
197
208
|
function extractPipelineMetrics(vitals) {
|
|
198
209
|
const flowRate = vitals.find((v) => v.vital_sign === "flow_rate");
|
|
199
210
|
if (!flowRate) return null;
|
|
@@ -286,6 +297,7 @@ __export(store_exports, {
|
|
|
286
297
|
getExportsDir: () => getExportsDir,
|
|
287
298
|
getKnowledgeDir: () => getKnowledgeDir,
|
|
288
299
|
getMemoryDir: () => getMemoryDir,
|
|
300
|
+
getRuminationsDir: () => getRuminationsDir,
|
|
289
301
|
getStrategiesDir: () => getStrategiesDir,
|
|
290
302
|
getWinsDir: () => getWinsDir,
|
|
291
303
|
loadConfig: () => loadConfig,
|
|
@@ -429,6 +441,22 @@ from work done outside this platform.
|
|
|
429
441
|
}
|
|
430
442
|
return dir;
|
|
431
443
|
}
|
|
444
|
+
function getRuminationsDir() {
|
|
445
|
+
const dir = join(NTRP_DIR, "ruminations");
|
|
446
|
+
if (!existsSync(dir)) {
|
|
447
|
+
mkdirSync(dir, { recursive: true });
|
|
448
|
+
writeFileSync(
|
|
449
|
+
join(dir, "README.md"),
|
|
450
|
+
`# Craft logs
|
|
451
|
+
|
|
452
|
+
This directory stores overnight strategy craft jobs. Each job has a machine file (\`<id>.json\`) and a short log (\`<id>.md\`).
|
|
453
|
+
|
|
454
|
+
Resume a job with \`ntrp strategy craft --resume <id>\`.
|
|
455
|
+
`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return dir;
|
|
459
|
+
}
|
|
432
460
|
function getWinsDir() {
|
|
433
461
|
const dir = join(NTRP_DIR, "wins");
|
|
434
462
|
if (!existsSync(dir)) {
|
|
@@ -1720,6 +1748,8 @@ function buildSessionContextDoc(file, opts = {}) {
|
|
|
1720
1748
|
lines.push(`- Constraints: ${file.strategist.constraintsNote}`);
|
|
1721
1749
|
}
|
|
1722
1750
|
if (file.strategist.origin) lines.push(`- Origin: ${file.strategist.origin}`);
|
|
1751
|
+
if (file.strategist.mode) lines.push(`- Mode: ${file.strategist.mode}`);
|
|
1752
|
+
if (file.strategist.ruminationId) lines.push(`- Craft job: ${file.strategist.ruminationId}`);
|
|
1723
1753
|
lines.push("");
|
|
1724
1754
|
}
|
|
1725
1755
|
if (file.think) {
|
|
@@ -5622,6 +5652,7 @@ var init_surfaces = __esm({
|
|
|
5622
5652
|
strategy: { defaultTier: "medium", allowUserTier: false },
|
|
5623
5653
|
strategist: { defaultTier: "high", allowUserTier: true },
|
|
5624
5654
|
strategist_stress: { defaultTier: "high", allowUserTier: false },
|
|
5655
|
+
strategist_critic: { defaultTier: "high", allowUserTier: false },
|
|
5625
5656
|
option_eval: { defaultTier: "low", allowUserTier: false }
|
|
5626
5657
|
};
|
|
5627
5658
|
}
|
|
@@ -7934,12 +7965,51 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
7934
7965
|
function strategyLibraryPath(slug) {
|
|
7935
7966
|
return join16(getStrategiesDir(), `${slug}.md`);
|
|
7936
7967
|
}
|
|
7937
|
-
function writeStrategyMarkdown(strategy) {
|
|
7968
|
+
function writeStrategyMarkdown(strategy, extras) {
|
|
7938
7969
|
const path = strategyLibraryPath(strategy.slug);
|
|
7939
|
-
writeFileSync11(path, renderStrategyMarkdown(strategy), "utf-8");
|
|
7970
|
+
writeFileSync11(path, renderStrategyMarkdown(strategy, extras), "utf-8");
|
|
7940
7971
|
return path;
|
|
7941
7972
|
}
|
|
7942
|
-
function
|
|
7973
|
+
function formatEffortSum(workstreams) {
|
|
7974
|
+
const sum = workstreams.reduce(
|
|
7975
|
+
(acc, ws) => acc + (Number.isFinite(ws.effort_hours) ? ws.effort_hours : 0),
|
|
7976
|
+
0
|
|
7977
|
+
);
|
|
7978
|
+
const count = workstreams.length;
|
|
7979
|
+
return `~${Math.round(sum)} team-hours across ${count} workstream${count === 1 ? "" : "s"}`;
|
|
7980
|
+
}
|
|
7981
|
+
function renderConstraintHeading(constraintLine) {
|
|
7982
|
+
return `## Constraint
|
|
7983
|
+
${constraintLine?.trim() || "none stated"}
|
|
7984
|
+
`;
|
|
7985
|
+
}
|
|
7986
|
+
function renderScopeHeading(constraints, outOfScope) {
|
|
7987
|
+
const inLines = constraints.length > 0 ? formatList(constraints) : "- none stated";
|
|
7988
|
+
const outItems = (outOfScope ?? []).map((s) => s.trim()).filter(Boolean);
|
|
7989
|
+
const outLines = outItems.length > 0 ? formatList(outItems) : "- none stated";
|
|
7990
|
+
return `## In scope / out of scope
|
|
7991
|
+
In scope:
|
|
7992
|
+
${inLines}
|
|
7993
|
+
|
|
7994
|
+
Out of scope:
|
|
7995
|
+
${outLines}
|
|
7996
|
+
`;
|
|
7997
|
+
}
|
|
7998
|
+
function renderKilledAlternativeLine(killedAlternative) {
|
|
7999
|
+
return `Killed alternative: ${killedAlternative?.trim() || "none stated"}`;
|
|
8000
|
+
}
|
|
8001
|
+
function renderEffortHeading(workstreams) {
|
|
8002
|
+
return `## Effort
|
|
8003
|
+
${formatEffortSum(workstreams)}
|
|
8004
|
+
`;
|
|
8005
|
+
}
|
|
8006
|
+
function renderReviewHeading(opts) {
|
|
8007
|
+
const cmd = opts.slug?.trim() ? `/strategy review ${opts.slug.trim()}` : "/strategy review";
|
|
8008
|
+
return `## Review
|
|
8009
|
+
Cadence: ${opts.cadence}. Check progress with ${cmd}.
|
|
8010
|
+
`;
|
|
8011
|
+
}
|
|
8012
|
+
function renderStrategyMarkdown(strategy, extras) {
|
|
7943
8013
|
const frontmatter = stringifyYaml({
|
|
7944
8014
|
id: strategy.id,
|
|
7945
8015
|
slug: strategy.slug,
|
|
@@ -7954,6 +8024,9 @@ function renderStrategyMarkdown(strategy) {
|
|
|
7954
8024
|
...strategy.baseline_batch_id ? { baseline_batch_id: strategy.baseline_batch_id } : {},
|
|
7955
8025
|
updated_at: strategy.updated_at
|
|
7956
8026
|
}).trim();
|
|
8027
|
+
if (strategy.origin === "strategist") {
|
|
8028
|
+
return renderStrategistLibraryMarkdown(strategy, extras, frontmatter);
|
|
8029
|
+
}
|
|
7957
8030
|
const objectiveSection = strategy.objective ? `
|
|
7958
8031
|
## Objective
|
|
7959
8032
|
${strategy.objective}
|
|
@@ -8000,6 +8073,67 @@ ${formatList(strategy.risks)}
|
|
|
8000
8073
|
## Experiment Design
|
|
8001
8074
|
${strategy.experiment_design}
|
|
8002
8075
|
|
|
8076
|
+
## Source Excerpt
|
|
8077
|
+
${strategy.raw_excerpt || "_No excerpt captured._"}
|
|
8078
|
+
`;
|
|
8079
|
+
}
|
|
8080
|
+
function renderStrategistLibraryMarkdown(strategy, extras, frontmatter) {
|
|
8081
|
+
const callSection = strategy.raw_excerpt ? `
|
|
8082
|
+
## The Call
|
|
8083
|
+
${strategy.raw_excerpt}
|
|
8084
|
+
` : "";
|
|
8085
|
+
const objectiveSection = strategy.objective ? `
|
|
8086
|
+
## Locked objective
|
|
8087
|
+
${strategy.objective}
|
|
8088
|
+
` : "";
|
|
8089
|
+
const workstreamSection = strategy.workstreams.length > 0 ? `
|
|
8090
|
+
## Workstreams
|
|
8091
|
+
${strategy.workstreams.map(formatWorkstream).join("\n")}
|
|
8092
|
+
` : "";
|
|
8093
|
+
const assumptionsSection = strategy.assumptions.length > 0 ? `
|
|
8094
|
+
## Assumptions (unverified)
|
|
8095
|
+
${formatList(strategy.assumptions)}
|
|
8096
|
+
` : "";
|
|
8097
|
+
const craftLogSection = extras?.craftLogPath ? `
|
|
8098
|
+
## Craft log
|
|
8099
|
+
See \`${extras.craftLogPath}\`.
|
|
8100
|
+
` : "";
|
|
8101
|
+
return `---
|
|
8102
|
+
${frontmatter}
|
|
8103
|
+
---
|
|
8104
|
+
|
|
8105
|
+
# ${strategy.title}
|
|
8106
|
+
${callSection}${objectiveSection}
|
|
8107
|
+
${renderConstraintHeading(extras?.constraintLine)}
|
|
8108
|
+
## Goal
|
|
8109
|
+
${strategy.goal}
|
|
8110
|
+
|
|
8111
|
+
${renderScopeHeading(strategy.constraints, extras?.outOfScope)}
|
|
8112
|
+
## Hypothesis
|
|
8113
|
+
${strategy.hypothesis}
|
|
8114
|
+
|
|
8115
|
+
${renderKilledAlternativeLine(extras?.killedAlternative)}
|
|
8116
|
+
|
|
8117
|
+
## Target Segment
|
|
8118
|
+
${strategy.target_segment}
|
|
8119
|
+
|
|
8120
|
+
${renderEffortHeading(strategy.workstreams)}${workstreamSection}
|
|
8121
|
+
## Success Metrics
|
|
8122
|
+
${formatMetrics(strategy.success_metrics)}
|
|
8123
|
+
|
|
8124
|
+
## Leading Indicators
|
|
8125
|
+
${formatMetrics(strategy.leading_indicators)}
|
|
8126
|
+
|
|
8127
|
+
## Recommended Actions
|
|
8128
|
+
${formatList(strategy.recommended_actions)}
|
|
8129
|
+
${assumptionsSection}
|
|
8130
|
+
## Risks
|
|
8131
|
+
${formatList(strategy.risks)}
|
|
8132
|
+
|
|
8133
|
+
## Experiment Design
|
|
8134
|
+
${strategy.experiment_design}
|
|
8135
|
+
|
|
8136
|
+
${renderReviewHeading({ cadence: strategy.review_cadence, slug: strategy.slug })}${craftLogSection}
|
|
8003
8137
|
## Source Excerpt
|
|
8004
8138
|
${strategy.raw_excerpt || "_No excerpt captured._"}
|
|
8005
8139
|
`;
|
|
@@ -8870,6 +9004,7 @@ function buildSessionFileSnapshot(ctx) {
|
|
|
8870
9004
|
if (ctx.strategistState) file.strategist = ctx.strategistState;
|
|
8871
9005
|
if (ctx.thinkState) file.think = ctx.thinkState;
|
|
8872
9006
|
if (ctx.pendingAsk) file.pending_ask = ctx.pendingAsk;
|
|
9007
|
+
if (ctx.lastCraftJobId) file.last_craft_job_id = ctx.lastCraftJobId;
|
|
8873
9008
|
return file;
|
|
8874
9009
|
}
|
|
8875
9010
|
function defaultSessionAnalysis(primary = "gtm_health") {
|
|
@@ -9224,6 +9359,9 @@ async function finalizeSession(ctx, stage) {
|
|
|
9224
9359
|
if (ctx.pendingAsk) {
|
|
9225
9360
|
file.pending_ask = ctx.pendingAsk;
|
|
9226
9361
|
}
|
|
9362
|
+
if (ctx.lastCraftJobId) {
|
|
9363
|
+
file.last_craft_job_id = ctx.lastCraftJobId;
|
|
9364
|
+
}
|
|
9227
9365
|
try {
|
|
9228
9366
|
writeFileSync13(ctx.sessionFile, JSON.stringify(file, null, 2) + "\n");
|
|
9229
9367
|
} catch {
|
|
@@ -9295,6 +9433,7 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
9295
9433
|
ctx.strategistState = opts.strategistState;
|
|
9296
9434
|
ctx.thinkState = opts.thinkState;
|
|
9297
9435
|
ctx.pendingAsk = opts.pendingAsk;
|
|
9436
|
+
ctx.lastCraftJobId = opts.lastCraftJobId;
|
|
9298
9437
|
ctx.gapAudit = void 0;
|
|
9299
9438
|
ctx.deliverIntent = false;
|
|
9300
9439
|
ctx.computeInProgress = false;
|
|
@@ -12340,7 +12479,9 @@ var init_guide_slides = __esm({
|
|
|
12340
12479
|
playbook: "loop",
|
|
12341
12480
|
remember: "loop",
|
|
12342
12481
|
sessions: "loop",
|
|
12343
|
-
progress: "loop"
|
|
12482
|
+
progress: "loop",
|
|
12483
|
+
"keep-going": "loop",
|
|
12484
|
+
keepgoing: "loop"
|
|
12344
12485
|
};
|
|
12345
12486
|
TALK = {
|
|
12346
12487
|
id: "talk",
|
|
@@ -12391,7 +12532,7 @@ var init_guide_slides = __esm({
|
|
|
12391
12532
|
"",
|
|
12392
12533
|
'"what is our ARR?" or "why is this red?" need a loaded dataset. Narrative findings and /ask need a stored key \u2014 /connect pastes any provider key and we detect it.',
|
|
12393
12534
|
"",
|
|
12394
|
-
"Replay this tour with /deepdive, jump to one slide with /deepdive freshness (or nrr, arr, \u2026), or /deepdive guide for this how-to section. When the prompt shows a dim \u23CE hint (yes, use demo data, go ahead, /connect), bare Enter submits it \u2014 and b / back steps back one confirm gate (or the previous tour slide while you're in /deepdive)."
|
|
12535
|
+
"Replay this tour with /deepdive, jump to one slide with /deepdive freshness (or nrr, arr, \u2026), or /deepdive guide for this how-to section. When the prompt shows a dim \u23CE hint (yes, use demo data, go ahead, /connect, keep going), bare Enter submits it \u2014 and b / back steps back one confirm gate (or the previous tour slide while you're in /deepdive)."
|
|
12395
12536
|
],
|
|
12396
12537
|
deepdive: [
|
|
12397
12538
|
"/connect ollama for a keyless local model. /connect --base-url <url> --id <name> for any OpenAI-compatible endpoint.",
|
|
@@ -12437,13 +12578,15 @@ var init_guide_slides = __esm({
|
|
|
12437
12578
|
caption: "Stethoscope, not hospital \u2014 we observe and recommend; you decide the surgery.",
|
|
12438
12579
|
layers: [
|
|
12439
12580
|
{ label: "Listen (/diagnose, /metrics)" },
|
|
12440
|
-
{ label: 'Plan ("how should we fix this?"
|
|
12581
|
+
{ label: 'Plan ("how should we fix this?" \xB7 keep going)', highlight: true },
|
|
12441
12582
|
{ label: "Review (/strategy review, /playbook)" },
|
|
12442
12583
|
{ label: "Remember (/remember, /rate, ANALYST.md)" }
|
|
12443
12584
|
]
|
|
12444
12585
|
},
|
|
12445
12586
|
lines: [
|
|
12446
|
-
|
|
12587
|
+
`After a diagnose we don't stop at the score. "how should we fix this?" or /strategy opens a strategy session: we work back from an objective into sequenced workstreams with dated milestones and dollar-anchored ranges.`,
|
|
12588
|
+
"",
|
|
12589
|
+
"At the confirm card, \u23CE yes builds that plan once. Type keep going to keep working until the plan is ready \u2014 the same armed-Enter pattern as use demo data and go ahead. If you see Best so far, the prompt returns to \u203A and \u23CE keep going continues. Plan ready? Type /strategy review later to check milestones against new vitals.",
|
|
12447
12590
|
"",
|
|
12448
12591
|
"When a vital is red, /playbook names a matching play \u2014 and outcomes from reviews annotate the catalog with what actually hit here. /remember stores a durable fact; /rate bad <reason> writes a calibration. Optional ~/.ntrp/ANALYST.md is standing voice and priorities (it never overrides safety rules).",
|
|
12449
12592
|
"",
|
|
@@ -12451,6 +12594,7 @@ var init_guide_slides = __esm({
|
|
|
12451
12594
|
],
|
|
12452
12595
|
deepdive: [
|
|
12453
12596
|
'"build me a game plan" before analysis queues the strategist and resumes after compute.',
|
|
12597
|
+
"An unfinished plan arms \u23CE keep going on \u203A. No plan in progress? Type how should we fix this? to start one.",
|
|
12454
12598
|
"Interactive sessions distill 5 or fewer durable facts on close when an LLM key is stored. One-shot commands do not bank hours or distill.",
|
|
12455
12599
|
"/scratch factory-resets data and config. progress.json and install.json survive unless you pass --include-progress.",
|
|
12456
12600
|
"Credits in /progress accrue in interactive ntrp only."
|
|
@@ -12880,7 +13024,7 @@ var init_metric_tour = __esm({
|
|
|
12880
13024
|
"/metrics \u2014 SaaS scorecard (ARR, NRR, coverage\u2026)",
|
|
12881
13025
|
"/deepdive \u2014 replay this tour \xB7 /deepdive guide how-to only",
|
|
12882
13026
|
"/handoff \u2014 ship a file; teach the inbox once (/inbox skill)",
|
|
12883
|
-
"/strategy \u2014 measurable plan \xB7 /help shortcuts",
|
|
13027
|
+
"/strategy \u2014 measurable plan \xB7 keep going until it's ready \xB7 /help shortcuts",
|
|
12884
13028
|
"",
|
|
12885
13029
|
"Fourteen more SaaS metrics sit behind /deepdive list \u2014 including unit economics that unlock when spend data lands.",
|
|
12886
13030
|
"",
|
|
@@ -15683,14 +15827,140 @@ var init_registry = __esm({
|
|
|
15683
15827
|
}
|
|
15684
15828
|
});
|
|
15685
15829
|
|
|
15830
|
+
// src/ruminations/store.ts
|
|
15831
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
|
|
15832
|
+
import { join as join23 } from "path";
|
|
15833
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
15834
|
+
function makeRuminationId(now2 = /* @__PURE__ */ new Date()) {
|
|
15835
|
+
const day = now2.toISOString().slice(0, 10).replace(/-/g, "");
|
|
15836
|
+
return `${day}-${randomUUID7().slice(0, 4)}`;
|
|
15837
|
+
}
|
|
15838
|
+
function ruminationJsonPath(id) {
|
|
15839
|
+
return join23(getRuminationsDir(), `${id}.json`);
|
|
15840
|
+
}
|
|
15841
|
+
function ruminationLogPath(id) {
|
|
15842
|
+
return join23(getRuminationsDir(), `${id}.md`);
|
|
15843
|
+
}
|
|
15844
|
+
function unfinishedCraftJob(id) {
|
|
15845
|
+
if (!id?.trim()) return null;
|
|
15846
|
+
const job = loadRuminationJob(id);
|
|
15847
|
+
if (!job || job.status === "ready") return null;
|
|
15848
|
+
return job;
|
|
15849
|
+
}
|
|
15850
|
+
function loadRuminationJob(id) {
|
|
15851
|
+
const path = ruminationJsonPath(id);
|
|
15852
|
+
if (!existsSync23(path)) return null;
|
|
15853
|
+
try {
|
|
15854
|
+
const parsed = JSON.parse(readFileSync19(path, "utf-8"));
|
|
15855
|
+
if (!parsed || typeof parsed !== "object" || parsed.id !== id) return null;
|
|
15856
|
+
if (parsed.best_plan === void 0) parsed.best_plan = parsed.plan ?? null;
|
|
15857
|
+
if (parsed.best_score === void 0) parsed.best_score = parsed.last_critic?.score ?? null;
|
|
15858
|
+
return parsed;
|
|
15859
|
+
} catch {
|
|
15860
|
+
return null;
|
|
15861
|
+
}
|
|
15862
|
+
}
|
|
15863
|
+
function createRuminationJob(opts) {
|
|
15864
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
15865
|
+
return {
|
|
15866
|
+
id: opts.id ?? makeRuminationId(),
|
|
15867
|
+
objective: opts.objective,
|
|
15868
|
+
constraints_note: opts.constraintsNote,
|
|
15869
|
+
baseline_batch_id: opts.baselineBatchId ?? null,
|
|
15870
|
+
snapshot_hash: opts.snapshotHash,
|
|
15871
|
+
plan: null,
|
|
15872
|
+
best_plan: null,
|
|
15873
|
+
best_score: null,
|
|
15874
|
+
last_critic: null,
|
|
15875
|
+
rubric_gaps: [],
|
|
15876
|
+
iterations: [],
|
|
15877
|
+
cumulative_input_tokens: 0,
|
|
15878
|
+
cumulative_output_tokens: 0,
|
|
15879
|
+
status: "running",
|
|
15880
|
+
from_fallback: false,
|
|
15881
|
+
revised_once: false,
|
|
15882
|
+
extra_ground_used: false,
|
|
15883
|
+
min_score: opts.minScore,
|
|
15884
|
+
max_rounds: opts.maxRounds,
|
|
15885
|
+
max_tokens: opts.maxTokens,
|
|
15886
|
+
created_at: now2,
|
|
15887
|
+
updated_at: now2,
|
|
15888
|
+
no_improve_streak: 0,
|
|
15889
|
+
last_critic_score: null
|
|
15890
|
+
};
|
|
15891
|
+
}
|
|
15892
|
+
function renderRuminationLog(job) {
|
|
15893
|
+
const lines = [];
|
|
15894
|
+
lines.push(`# Craft log \u2014 ${job.id}`);
|
|
15895
|
+
lines.push("");
|
|
15896
|
+
lines.push(`Objective: ${job.objective}`);
|
|
15897
|
+
lines.push(`Status: ${job.status}${job.stop_reason ? ` (${job.stop_reason})` : ""}`);
|
|
15898
|
+
lines.push(
|
|
15899
|
+
`Tokens: ${job.cumulative_input_tokens + job.cumulative_output_tokens} total (${job.cumulative_input_tokens} in / ${job.cumulative_output_tokens} out)`
|
|
15900
|
+
);
|
|
15901
|
+
if (job.library_path) lines.push(`Strategy: ${job.library_path}`);
|
|
15902
|
+
if (job.handoff_path) lines.push(`Handoff: ${job.handoff_path}`);
|
|
15903
|
+
lines.push("");
|
|
15904
|
+
lines.push("## Rounds");
|
|
15905
|
+
lines.push("");
|
|
15906
|
+
if (job.iterations.length === 0) {
|
|
15907
|
+
lines.push("_No rounds yet._");
|
|
15908
|
+
} else {
|
|
15909
|
+
for (const it of job.iterations) {
|
|
15910
|
+
const codes = it.blocking_codes.length > 0 ? it.blocking_codes.join(", ") : "none";
|
|
15911
|
+
lines.push(
|
|
15912
|
+
`- Round ${it.round}: score ${it.critic_score ?? "\u2014"} \xB7 ${it.decision} \xB7 blocking ${codes} \xB7 ${it.tokens_in + it.tokens_out} tok`
|
|
15913
|
+
);
|
|
15914
|
+
}
|
|
15915
|
+
}
|
|
15916
|
+
if (job.last_critic?.partner_pushback) {
|
|
15917
|
+
lines.push("");
|
|
15918
|
+
lines.push("## Partner pushback");
|
|
15919
|
+
lines.push("");
|
|
15920
|
+
lines.push(job.last_critic.partner_pushback);
|
|
15921
|
+
}
|
|
15922
|
+
if (job.last_critic?.killed_alternative) {
|
|
15923
|
+
lines.push("");
|
|
15924
|
+
lines.push("## Killed alternative");
|
|
15925
|
+
lines.push("");
|
|
15926
|
+
lines.push(job.last_critic.killed_alternative);
|
|
15927
|
+
}
|
|
15928
|
+
lines.push("");
|
|
15929
|
+
return lines.join("\n");
|
|
15930
|
+
}
|
|
15931
|
+
function saveRuminationJob(job) {
|
|
15932
|
+
job.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
15933
|
+
getRuminationsDir();
|
|
15934
|
+
writeFileSync15(ruminationJsonPath(job.id), JSON.stringify(job, null, 2) + "\n", "utf-8");
|
|
15935
|
+
writeFileSync15(ruminationLogPath(job.id), renderRuminationLog(job), "utf-8");
|
|
15936
|
+
}
|
|
15937
|
+
function addRuminationIteration(job, iteration) {
|
|
15938
|
+
job.iterations.push({ ...iteration, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
15939
|
+
job.cumulative_input_tokens += iteration.tokens_in;
|
|
15940
|
+
job.cumulative_output_tokens += iteration.tokens_out;
|
|
15941
|
+
}
|
|
15942
|
+
var init_store3 = __esm({
|
|
15943
|
+
"src/ruminations/store.ts"() {
|
|
15944
|
+
"use strict";
|
|
15945
|
+
init_store();
|
|
15946
|
+
}
|
|
15947
|
+
});
|
|
15948
|
+
|
|
15686
15949
|
// src/conversation/recommended-action.ts
|
|
15687
15950
|
function resolveRecommendedAction(ctx) {
|
|
15688
15951
|
if (!hasValidLicense()) return { submit: "/activate", hint: "/activate" };
|
|
15689
15952
|
const phase = resolveConversationPhase(ctx);
|
|
15690
15953
|
switch (phase) {
|
|
15691
15954
|
case "explore":
|
|
15955
|
+
if (!canUseReplAi(ctx)) {
|
|
15956
|
+
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
15957
|
+
return { submit: "/connect", hint: "/connect" };
|
|
15958
|
+
}
|
|
15959
|
+
if (unfinishedCraftJob(ctx.lastCraftJobId)) {
|
|
15960
|
+
return { submit: "keep going", hint: "keep going" };
|
|
15961
|
+
}
|
|
15692
15962
|
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
15693
|
-
return
|
|
15963
|
+
return null;
|
|
15694
15964
|
case "awaiting_data":
|
|
15695
15965
|
if (ctx.gapAudit?.can_compute) return { submit: "go ahead", hint: "go ahead" };
|
|
15696
15966
|
if (!sessionHasData(ctx)) return { submit: "use demo data", hint: "use demo data" };
|
|
@@ -15714,6 +15984,7 @@ var init_recommended_action = __esm({
|
|
|
15714
15984
|
init_activation();
|
|
15715
15985
|
init_registry();
|
|
15716
15986
|
init_phase();
|
|
15987
|
+
init_store3();
|
|
15717
15988
|
}
|
|
15718
15989
|
});
|
|
15719
15990
|
|
|
@@ -16511,17 +16782,17 @@ __export(play_outcomes_exports, {
|
|
|
16511
16782
|
listPlayOutcomes: () => listPlayOutcomes,
|
|
16512
16783
|
recordPlayOutcomes: () => recordPlayOutcomes
|
|
16513
16784
|
});
|
|
16514
|
-
import { existsSync as
|
|
16515
|
-
import { join as
|
|
16516
|
-
import { randomUUID as
|
|
16785
|
+
import { existsSync as existsSync24, readFileSync as readFileSync20, appendFileSync as appendFileSync6 } from "fs";
|
|
16786
|
+
import { join as join24 } from "path";
|
|
16787
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
16517
16788
|
function outcomesPath() {
|
|
16518
|
-
return
|
|
16789
|
+
return join24(getMemoryDir(), OUTCOMES_FILE);
|
|
16519
16790
|
}
|
|
16520
16791
|
function listPlayOutcomes() {
|
|
16521
16792
|
const path = outcomesPath();
|
|
16522
|
-
if (!
|
|
16793
|
+
if (!existsSync24(path)) return [];
|
|
16523
16794
|
const out = [];
|
|
16524
|
-
for (const line of
|
|
16795
|
+
for (const line of readFileSync20(path, "utf-8").split("\n")) {
|
|
16525
16796
|
const trimmed = line.trim();
|
|
16526
16797
|
if (!trimmed) continue;
|
|
16527
16798
|
try {
|
|
@@ -16551,7 +16822,7 @@ function recordPlayOutcomes(strategy, outcomes, batchId) {
|
|
|
16551
16822
|
if (seen.has(key)) continue;
|
|
16552
16823
|
seen.add(key);
|
|
16553
16824
|
const record = {
|
|
16554
|
-
id:
|
|
16825
|
+
id: randomUUID8(),
|
|
16555
16826
|
play_id: playId,
|
|
16556
16827
|
strategy_slug: strategy.slug,
|
|
16557
16828
|
workstream_order: outcome.workstream_order,
|
|
@@ -16614,11 +16885,11 @@ function jsonSafe(value) {
|
|
|
16614
16885
|
}
|
|
16615
16886
|
return value;
|
|
16616
16887
|
}
|
|
16617
|
-
function envelope(command, data, warnings) {
|
|
16888
|
+
function envelope(command, data, warnings, status = "ok") {
|
|
16618
16889
|
return {
|
|
16619
16890
|
schema_version: HEADLESS_SCHEMA_VERSION,
|
|
16620
16891
|
command,
|
|
16621
|
-
status
|
|
16892
|
+
status,
|
|
16622
16893
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16623
16894
|
data,
|
|
16624
16895
|
...warnings && warnings.length > 0 ? { warnings } : {}
|
|
@@ -16634,8 +16905,8 @@ function errorEnvelope(command, err) {
|
|
|
16634
16905
|
error: ntrpError.toHeadlessError()
|
|
16635
16906
|
};
|
|
16636
16907
|
}
|
|
16637
|
-
function emitResult(command, data, warnings) {
|
|
16638
|
-
console.log(JSON.stringify(jsonSafe(envelope(command, data, warnings)), null, 2));
|
|
16908
|
+
function emitResult(command, data, warnings, status = "ok") {
|
|
16909
|
+
console.log(JSON.stringify(jsonSafe(envelope(command, data, warnings, status)), null, 2));
|
|
16639
16910
|
}
|
|
16640
16911
|
function emitError(command, err) {
|
|
16641
16912
|
const ntrpError = err instanceof NtrpError ? err : toNtrpError(err);
|
|
@@ -19988,18 +20259,18 @@ var init_terminal = __esm({
|
|
|
19988
20259
|
});
|
|
19989
20260
|
|
|
19990
20261
|
// src/demo/taxonomy-cache.ts
|
|
19991
|
-
import { readFileSync as
|
|
20262
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync25, mkdirSync as mkdirSync13, unlinkSync as unlinkSync5 } from "fs";
|
|
19992
20263
|
import { homedir as homedir7 } from "os";
|
|
19993
|
-
import { join as
|
|
20264
|
+
import { join as join25 } from "path";
|
|
19994
20265
|
function ensureDir7() {
|
|
19995
|
-
if (!
|
|
20266
|
+
if (!existsSync25(NTRP_DIR4)) {
|
|
19996
20267
|
mkdirSync13(NTRP_DIR4, { recursive: true });
|
|
19997
20268
|
}
|
|
19998
20269
|
}
|
|
19999
20270
|
function loadCachedTaxonomy(profile) {
|
|
20000
|
-
if (!
|
|
20271
|
+
if (!existsSync25(TAXONOMY_PATH)) return null;
|
|
20001
20272
|
try {
|
|
20002
|
-
const parsed = JSON.parse(
|
|
20273
|
+
const parsed = JSON.parse(readFileSync21(TAXONOMY_PATH, "utf-8"));
|
|
20003
20274
|
if (!parsed || typeof parsed !== "object") return null;
|
|
20004
20275
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
20005
20276
|
return parsed;
|
|
@@ -20009,10 +20280,10 @@ function loadCachedTaxonomy(profile) {
|
|
|
20009
20280
|
}
|
|
20010
20281
|
function saveCachedTaxonomy(taxonomy) {
|
|
20011
20282
|
ensureDir7();
|
|
20012
|
-
|
|
20283
|
+
writeFileSync16(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
20013
20284
|
}
|
|
20014
20285
|
function invalidateTaxonomy() {
|
|
20015
|
-
if (
|
|
20286
|
+
if (existsSync25(TAXONOMY_PATH)) {
|
|
20016
20287
|
try {
|
|
20017
20288
|
unlinkSync5(TAXONOMY_PATH);
|
|
20018
20289
|
} catch {
|
|
@@ -20023,8 +20294,8 @@ var NTRP_DIR4, TAXONOMY_PATH;
|
|
|
20023
20294
|
var init_taxonomy_cache = __esm({
|
|
20024
20295
|
"src/demo/taxonomy-cache.ts"() {
|
|
20025
20296
|
"use strict";
|
|
20026
|
-
NTRP_DIR4 =
|
|
20027
|
-
TAXONOMY_PATH =
|
|
20297
|
+
NTRP_DIR4 = join25(homedir7(), ".ntrp");
|
|
20298
|
+
TAXONOMY_PATH = join25(NTRP_DIR4, "demo-taxonomy.json");
|
|
20028
20299
|
}
|
|
20029
20300
|
});
|
|
20030
20301
|
|
|
@@ -20482,7 +20753,7 @@ __export(inbox_setup_exports, {
|
|
|
20482
20753
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
20483
20754
|
});
|
|
20484
20755
|
import chalk19 from "chalk";
|
|
20485
|
-
import { existsSync as
|
|
20756
|
+
import { existsSync as existsSync26 } from "fs";
|
|
20486
20757
|
function markDemoOffered() {
|
|
20487
20758
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
20488
20759
|
}
|
|
@@ -20514,7 +20785,7 @@ function printSkipHint(beat) {
|
|
|
20514
20785
|
async function reuseInboxFolderIfPresent(session, beat, folderPath) {
|
|
20515
20786
|
if (getAiInboxDir()) return false;
|
|
20516
20787
|
const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
|
|
20517
|
-
const existing = candidates.find((p) =>
|
|
20788
|
+
const existing = candidates.find((p) => existsSync26(p));
|
|
20518
20789
|
if (!existing) return false;
|
|
20519
20790
|
console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
|
|
20520
20791
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
@@ -20620,7 +20891,7 @@ __export(ingest_exports, {
|
|
|
20620
20891
|
handler: () => handler2
|
|
20621
20892
|
});
|
|
20622
20893
|
import chalk20 from "chalk";
|
|
20623
|
-
import { readFileSync as
|
|
20894
|
+
import { readFileSync as readFileSync22, existsSync as existsSync27 } from "fs";
|
|
20624
20895
|
import { basename as basename6 } from "path";
|
|
20625
20896
|
async function handler2(args, ctx) {
|
|
20626
20897
|
const { positional, flags } = parseArgs2(args, [
|
|
@@ -20644,7 +20915,7 @@ async function handler2(args, ctx) {
|
|
|
20644
20915
|
console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
|
|
20645
20916
|
process.exit(1);
|
|
20646
20917
|
}
|
|
20647
|
-
if (!
|
|
20918
|
+
if (!existsSync27(file)) {
|
|
20648
20919
|
console.error(chalk20.red(` File not found: ${file}`));
|
|
20649
20920
|
process.exit(1);
|
|
20650
20921
|
}
|
|
@@ -20662,7 +20933,7 @@ async function handler2(args, ctx) {
|
|
|
20662
20933
|
try {
|
|
20663
20934
|
await initSchema();
|
|
20664
20935
|
spinner.text = "Parsing CSV\u2026";
|
|
20665
|
-
const content =
|
|
20936
|
+
const content = readFileSync22(file, "utf-8");
|
|
20666
20937
|
const { rows, headers } = parseCSV(content);
|
|
20667
20938
|
if (rows.length === 0) {
|
|
20668
20939
|
spinner.fail("CSV is empty");
|
|
@@ -23029,9 +23300,9 @@ async function handleGetSessionBrief(input) {
|
|
|
23029
23300
|
if (!target) {
|
|
23030
23301
|
return { error: `No session matching "${raw}".` };
|
|
23031
23302
|
}
|
|
23032
|
-
const { existsSync:
|
|
23303
|
+
const { existsSync: existsSync39, readFileSync: readFileSync25 } = await import("fs");
|
|
23033
23304
|
const briefPath = contextDocPathForSession2(target.id);
|
|
23034
|
-
if (!
|
|
23305
|
+
if (!existsSync39(briefPath)) {
|
|
23035
23306
|
return {
|
|
23036
23307
|
session_id: target.id,
|
|
23037
23308
|
error: "No context brief on disk for this session (created before brief storage existed).",
|
|
@@ -23042,7 +23313,7 @@ async function handleGetSessionBrief(input) {
|
|
|
23042
23313
|
return {
|
|
23043
23314
|
session_id: target.id,
|
|
23044
23315
|
security_notice: UNTRUSTED_CONTENT_NOTICE,
|
|
23045
|
-
brief: wrapUntrustedContent(
|
|
23316
|
+
brief: wrapUntrustedContent(readFileSync25(briefPath, "utf-8"))
|
|
23046
23317
|
};
|
|
23047
23318
|
}
|
|
23048
23319
|
function auditDenied(name, input, resultJson, start) {
|
|
@@ -25348,7 +25619,7 @@ __export(onboard_tiers_exports, {
|
|
|
25348
25619
|
resetOnboardTierProgress: () => resetOnboardTierProgress,
|
|
25349
25620
|
resolveNextOnboardTier: () => resolveNextOnboardTier
|
|
25350
25621
|
});
|
|
25351
|
-
import { existsSync as
|
|
25622
|
+
import { existsSync as existsSync28, statSync as statSync4 } from "fs";
|
|
25352
25623
|
function flagSet(tier) {
|
|
25353
25624
|
return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
|
|
25354
25625
|
}
|
|
@@ -25375,7 +25646,7 @@ function hasProductionDataset(ctx) {
|
|
|
25375
25646
|
if (source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source) || source.startsWith("~")) {
|
|
25376
25647
|
return true;
|
|
25377
25648
|
}
|
|
25378
|
-
if (!source.includes(":") &&
|
|
25649
|
+
if (!source.includes(":") && existsSync28(source)) return true;
|
|
25379
25650
|
if (source.startsWith("csv:") || source.startsWith("file:") || source.startsWith("folder:")) {
|
|
25380
25651
|
return true;
|
|
25381
25652
|
}
|
|
@@ -25434,7 +25705,7 @@ function markDemoDataSeen() {
|
|
|
25434
25705
|
}
|
|
25435
25706
|
function pathLooksPresent(raw) {
|
|
25436
25707
|
try {
|
|
25437
|
-
return
|
|
25708
|
+
return existsSync28(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
|
|
25438
25709
|
} catch {
|
|
25439
25710
|
return false;
|
|
25440
25711
|
}
|
|
@@ -25839,7 +26110,7 @@ __export(onboard_exports, {
|
|
|
25839
26110
|
profileExists: () => profileExists
|
|
25840
26111
|
});
|
|
25841
26112
|
import chalk25 from "chalk";
|
|
25842
|
-
import { existsSync as
|
|
26113
|
+
import { existsSync as existsSync29 } from "fs";
|
|
25843
26114
|
import { basename as basename7 } from "path";
|
|
25844
26115
|
async function handler5(args, ctx) {
|
|
25845
26116
|
const { flags } = parseArgs2(args, ["force", "skip-brand"]);
|
|
@@ -26114,7 +26385,7 @@ async function runProductionTier(session, ctx) {
|
|
|
26114
26385
|
return "Production data skipped";
|
|
26115
26386
|
}
|
|
26116
26387
|
const resolved = resolveUserPath(trimmed);
|
|
26117
|
-
if (!
|
|
26388
|
+
if (!existsSync29(resolved)) {
|
|
26118
26389
|
console.log(" " + chalk25.red(`Path not found: ${resolved}`));
|
|
26119
26390
|
console.log(" " + chalk25.dim("Try again with /onboard, or drop the path into the REPL."));
|
|
26120
26391
|
return "Production path not found";
|
|
@@ -26521,7 +26792,7 @@ __export(new_exports, {
|
|
|
26521
26792
|
handler: () => handler6
|
|
26522
26793
|
});
|
|
26523
26794
|
import chalk26 from "chalk";
|
|
26524
|
-
import { existsSync as
|
|
26795
|
+
import { existsSync as existsSync30 } from "fs";
|
|
26525
26796
|
import { basename as basename8 } from "path";
|
|
26526
26797
|
async function handler6(args, ctx) {
|
|
26527
26798
|
const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
|
|
@@ -26543,7 +26814,7 @@ async function handler6(args, ctx) {
|
|
|
26543
26814
|
console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
|
|
26544
26815
|
return;
|
|
26545
26816
|
}
|
|
26546
|
-
if (source.kind === "file" && !
|
|
26817
|
+
if (source.kind === "file" && !existsSync30(source.path)) {
|
|
26547
26818
|
console.error(chalk26.red(` File not found: ${source.path}`));
|
|
26548
26819
|
return;
|
|
26549
26820
|
}
|
|
@@ -26770,7 +27041,7 @@ __export(end_exports, {
|
|
|
26770
27041
|
handler: () => handler7
|
|
26771
27042
|
});
|
|
26772
27043
|
import chalk27 from "chalk";
|
|
26773
|
-
import { existsSync as
|
|
27044
|
+
import { existsSync as existsSync31 } from "fs";
|
|
26774
27045
|
async function handler7(args, ctx) {
|
|
26775
27046
|
if (args.length > 0) {
|
|
26776
27047
|
console.error(chalk27.red(" Usage: /end"));
|
|
@@ -26807,10 +27078,10 @@ async function handler7(args, ctx) {
|
|
|
26807
27078
|
if (summary) {
|
|
26808
27079
|
console.log(" " + chalk27.dim(summary));
|
|
26809
27080
|
}
|
|
26810
|
-
if (
|
|
27081
|
+
if (existsSync31(transcriptPathForSession(endedId))) {
|
|
26811
27082
|
console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
|
|
26812
27083
|
}
|
|
26813
|
-
if (
|
|
27084
|
+
if (existsSync31(contextDocPathForSession(endedId))) {
|
|
26814
27085
|
console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
|
|
26815
27086
|
}
|
|
26816
27087
|
console.log();
|
|
@@ -26831,8 +27102,8 @@ __export(session_exports, {
|
|
|
26831
27102
|
handler: () => handler8
|
|
26832
27103
|
});
|
|
26833
27104
|
import chalk28 from "chalk";
|
|
26834
|
-
import { join as
|
|
26835
|
-
import { existsSync as
|
|
27105
|
+
import { join as join26 } from "path";
|
|
27106
|
+
import { existsSync as existsSync32 } from "fs";
|
|
26836
27107
|
async function handler8(args, ctx) {
|
|
26837
27108
|
const sub = args[0];
|
|
26838
27109
|
if (!sub) return listSessionsView(ctx);
|
|
@@ -26935,7 +27206,7 @@ async function pickUp(idArg, ctx) {
|
|
|
26935
27206
|
}
|
|
26936
27207
|
resetContextForSwitch(ctx, {
|
|
26937
27208
|
sessionId: target.id,
|
|
26938
|
-
sessionFile:
|
|
27209
|
+
sessionFile: join26(getSessionsDir(), `${target.id}.json`),
|
|
26939
27210
|
sessionName: session.name,
|
|
26940
27211
|
messages: [...session.messages],
|
|
26941
27212
|
conversation: session.thread ? [...session.thread] : [],
|
|
@@ -26950,7 +27221,8 @@ async function pickUp(idArg, ctx) {
|
|
|
26950
27221
|
llm: session.llm ? { ...session.llm } : void 0,
|
|
26951
27222
|
strategistState: session.strategist,
|
|
26952
27223
|
thinkState: session.think,
|
|
26953
|
-
pendingAsk: session.pending_ask
|
|
27224
|
+
pendingAsk: session.pending_ask,
|
|
27225
|
+
lastCraftJobId: session.last_craft_job_id
|
|
26954
27226
|
});
|
|
26955
27227
|
ctx.datasetPath = datasetPathForSession(target.id);
|
|
26956
27228
|
await setActiveDbPath(ctx.datasetPath);
|
|
@@ -26968,7 +27240,11 @@ async function pickUp(idArg, ctx) {
|
|
|
26968
27240
|
if (session.strategist && session.strategist.step !== "awaiting_analysis") {
|
|
26969
27241
|
const objective = session.strategist.objective;
|
|
26970
27242
|
console.log(
|
|
26971
|
-
" " + chalk28.yellow("Resuming mid-strategy") + (objective ? chalk28.dim(`: "${objective}"`) : "") + chalk28.dim(" \u2014 Confirm? ") + chalk28.cyan("\u23CE yes") + chalk28.dim(" \xB7 ") + chalk28.cyan("cancel") + chalk28.dim(" to drop it.")
|
|
27243
|
+
" " + chalk28.yellow("Resuming mid-strategy") + (objective ? chalk28.dim(`: "${objective}"`) : "") + chalk28.dim(" \u2014 Confirm? ") + chalk28.cyan("\u23CE yes") + (session.strategist.mode === "craft" ? chalk28.dim(" \xB7 keep going") : "") + chalk28.dim(" \xB7 ") + chalk28.cyan("cancel") + chalk28.dim(" to drop it.")
|
|
27244
|
+
);
|
|
27245
|
+
} else if (unfinishedCraftJob(session.last_craft_job_id)) {
|
|
27246
|
+
console.log(
|
|
27247
|
+
" " + chalk28.dim("Plan in progress \u2014 type ") + chalk28.cyan("keep going") + chalk28.dim(" to continue.")
|
|
26972
27248
|
);
|
|
26973
27249
|
}
|
|
26974
27250
|
if (session.think && session.think.step === "active") {
|
|
@@ -26978,7 +27254,7 @@ async function pickUp(idArg, ctx) {
|
|
|
26978
27254
|
);
|
|
26979
27255
|
}
|
|
26980
27256
|
const contextPath = contextDocPathForSession(target.id);
|
|
26981
|
-
if (
|
|
27257
|
+
if (existsSync32(contextPath)) {
|
|
26982
27258
|
console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
|
|
26983
27259
|
}
|
|
26984
27260
|
console.log();
|
|
@@ -26993,6 +27269,7 @@ var init_session = __esm({
|
|
|
26993
27269
|
init_connection();
|
|
26994
27270
|
init_theme();
|
|
26995
27271
|
init_layout();
|
|
27272
|
+
init_store3();
|
|
26996
27273
|
GENERIC_DATASET_RE = /^(demo dataset|.*\bdemo)$/i;
|
|
26997
27274
|
}
|
|
26998
27275
|
});
|
|
@@ -27269,7 +27546,7 @@ __export(report_exports, {
|
|
|
27269
27546
|
handler: () => handler9
|
|
27270
27547
|
});
|
|
27271
27548
|
import chalk29 from "chalk";
|
|
27272
|
-
import { mkdirSync as mkdirSync14, writeFileSync as
|
|
27549
|
+
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync17 } from "fs";
|
|
27273
27550
|
import { dirname as dirname5 } from "path";
|
|
27274
27551
|
async function handler9(args, ctx) {
|
|
27275
27552
|
const { flags } = parseArgs2(args);
|
|
@@ -27366,7 +27643,7 @@ async function handler9(args, ctx) {
|
|
|
27366
27643
|
console.warn(chalk29.yellow(` Warning: writing report outside NTRP home (${dirname5(resolvedOutput)})`));
|
|
27367
27644
|
}
|
|
27368
27645
|
mkdirSync14(dirname5(resolvedOutput), { recursive: true });
|
|
27369
|
-
|
|
27646
|
+
writeFileSync17(resolvedOutput, rendered);
|
|
27370
27647
|
console.log(chalk29.green(` Report written to ${resolvedOutput}`));
|
|
27371
27648
|
} else if (rendered) {
|
|
27372
27649
|
console.log(rendered);
|
|
@@ -27398,7 +27675,7 @@ var init_report2 = __esm({
|
|
|
27398
27675
|
|
|
27399
27676
|
// src/output/notes-export.ts
|
|
27400
27677
|
import { mkdirSync as mkdirSync15 } from "fs";
|
|
27401
|
-
import { join as
|
|
27678
|
+
import { join as join27 } from "path";
|
|
27402
27679
|
function exportToNotes(data) {
|
|
27403
27680
|
const { computeResult, divergences, findings, exchanges } = data;
|
|
27404
27681
|
const { aggregate, segments } = computeResult;
|
|
@@ -27408,7 +27685,7 @@ function exportToNotes(data) {
|
|
|
27408
27685
|
const filename = `${dateStr}-${timeStr}-gtm-health.md`;
|
|
27409
27686
|
const dir = data.dir ?? getArchiveKindDir("notes");
|
|
27410
27687
|
mkdirSync15(dir, { recursive: true });
|
|
27411
|
-
const filepath =
|
|
27688
|
+
const filepath = join27(dir, filename);
|
|
27412
27689
|
const severityTags = /* @__PURE__ */ new Set();
|
|
27413
27690
|
for (const f of findings) severityTags.add(f.severity);
|
|
27414
27691
|
const tags = ["ntrp", "gtm-health", ...severityTags];
|
|
@@ -27653,8 +27930,8 @@ __export(backmeup_exports, {
|
|
|
27653
27930
|
});
|
|
27654
27931
|
import chalk31 from "chalk";
|
|
27655
27932
|
import Papa5 from "papaparse";
|
|
27656
|
-
import { mkdirSync as mkdirSync16, writeFileSync as
|
|
27657
|
-
import { join as
|
|
27933
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync18 } from "fs";
|
|
27934
|
+
import { join as join28 } from "path";
|
|
27658
27935
|
function sanitizeCsvValue(value) {
|
|
27659
27936
|
if (typeof value !== "string") return value;
|
|
27660
27937
|
return CSV_FORMULA_RE.test(value) ? `'${value}` : value;
|
|
@@ -27690,7 +27967,7 @@ async function handler11(args, _ctx) {
|
|
|
27690
27967
|
if (!isInsideNtrp(baseDir)) {
|
|
27691
27968
|
console.warn(chalk31.yellow(` Warning: writing backup outside NTRP home (${baseDir})`));
|
|
27692
27969
|
}
|
|
27693
|
-
const folder =
|
|
27970
|
+
const folder = join28(baseDir, folderName);
|
|
27694
27971
|
mkdirSync16(folder, { recursive: true });
|
|
27695
27972
|
const generatedAt = now2.toISOString();
|
|
27696
27973
|
let fileCount = 0;
|
|
@@ -27705,7 +27982,7 @@ async function handler11(args, _ctx) {
|
|
|
27705
27982
|
"Total At Risk": health.total_value_at_risk != null ? formatCurrency(health.total_value_at_risk) : "N/A",
|
|
27706
27983
|
"Generated At": generatedAt
|
|
27707
27984
|
}));
|
|
27708
|
-
|
|
27985
|
+
writeFileSync18(join28(folder, "cover-sheet.csv"), Papa5.unparse(sanitizeCsvRows(coverRows)), "utf-8");
|
|
27709
27986
|
fileCount++;
|
|
27710
27987
|
if (findings.length > 0) {
|
|
27711
27988
|
const findingsRows = findings.map((f) => ({
|
|
@@ -27715,7 +27992,7 @@ async function handler11(args, _ctx) {
|
|
|
27715
27992
|
Finding: f.finding,
|
|
27716
27993
|
"Recommended Plays": f.recommended_plays ? f.recommended_plays.map((p) => p.play_name).join("; ") : ""
|
|
27717
27994
|
}));
|
|
27718
|
-
|
|
27995
|
+
writeFileSync18(join28(folder, "findings.csv"), Papa5.unparse(sanitizeCsvRows(findingsRows)), "utf-8");
|
|
27719
27996
|
fileCount++;
|
|
27720
27997
|
}
|
|
27721
27998
|
for (const vs of health.vital_signs) {
|
|
@@ -27725,7 +28002,7 @@ async function handler11(args, _ctx) {
|
|
|
27725
28002
|
...detail
|
|
27726
28003
|
}));
|
|
27727
28004
|
const filename = EVIDENCE_FILENAMES[vs.vital_sign] ?? `${vs.vital_sign}.csv`;
|
|
27728
|
-
|
|
28005
|
+
writeFileSync18(join28(folder, filename), Papa5.unparse(sanitizeCsvRows(rows)), "utf-8");
|
|
27729
28006
|
fileCount++;
|
|
27730
28007
|
}
|
|
27731
28008
|
const event = recordExportWrite({
|
|
@@ -27929,8 +28206,8 @@ var init_bundle = __esm({
|
|
|
27929
28206
|
});
|
|
27930
28207
|
|
|
27931
28208
|
// src/repositories/markdown.ts
|
|
27932
|
-
import { mkdirSync as mkdirSync17, writeFileSync as
|
|
27933
|
-
import { basename as basename9, dirname as dirname6, join as
|
|
28209
|
+
import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync19 } from "fs";
|
|
28210
|
+
import { basename as basename9, dirname as dirname6, join as join29, resolve as resolve8 } from "path";
|
|
27934
28211
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
27935
28212
|
function renderMarkdownFiles(pkg) {
|
|
27936
28213
|
const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -28153,9 +28430,9 @@ var init_markdown3 = __esm({
|
|
|
28153
28430
|
mkdirSync17(root, { recursive: true });
|
|
28154
28431
|
const written = [];
|
|
28155
28432
|
for (const file of files) {
|
|
28156
|
-
const absolutePath =
|
|
28433
|
+
const absolutePath = join29(root, file.relativePath);
|
|
28157
28434
|
mkdirSync17(dirname6(absolutePath), { recursive: true });
|
|
28158
|
-
|
|
28435
|
+
writeFileSync19(absolutePath, file.contents, "utf-8");
|
|
28159
28436
|
written.push(absolutePath);
|
|
28160
28437
|
}
|
|
28161
28438
|
return {
|
|
@@ -28459,7 +28736,7 @@ __export(handoff_exports, {
|
|
|
28459
28736
|
handler: () => handler13
|
|
28460
28737
|
});
|
|
28461
28738
|
import chalk33 from "chalk";
|
|
28462
|
-
import { join as
|
|
28739
|
+
import { join as join30 } from "path";
|
|
28463
28740
|
async function handler13(args, ctx) {
|
|
28464
28741
|
const sub = args[0];
|
|
28465
28742
|
if (!sub) {
|
|
@@ -28559,7 +28836,7 @@ async function runPublish2(args, ctx) {
|
|
|
28559
28836
|
);
|
|
28560
28837
|
if (sub === "propose" && !hasDir) {
|
|
28561
28838
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
28562
|
-
const dir =
|
|
28839
|
+
const dir = join30(getArchiveKindDir("publish"), `ntrp-repository-${stamp}`);
|
|
28563
28840
|
publishArgs.push("--dir", dir);
|
|
28564
28841
|
}
|
|
28565
28842
|
const result = await publish(publishArgs, ctx);
|
|
@@ -29334,150 +29611,6 @@ var init_segment = __esm({
|
|
|
29334
29611
|
}
|
|
29335
29612
|
});
|
|
29336
29613
|
|
|
29337
|
-
// src/services/strategist.ts
|
|
29338
|
-
import { createHash as createHash2 } from "crypto";
|
|
29339
|
-
function serializeGapAudit(audit) {
|
|
29340
|
-
const lines = [`Can compute: ${audit.can_compute} (lens: ${audit.primary_lens})`];
|
|
29341
|
-
for (const item of audit.satisfied) {
|
|
29342
|
-
lines.push(`- HAVE ${item.label}: ${item.detail}`);
|
|
29343
|
-
}
|
|
29344
|
-
for (const item of audit.missing) {
|
|
29345
|
-
lines.push(`- MISSING ${item.label}: ${item.why}`);
|
|
29346
|
-
}
|
|
29347
|
-
for (const item of audit.optional) {
|
|
29348
|
-
lines.push(`- LIMITED ${item.label}: ${item.detail}`);
|
|
29349
|
-
}
|
|
29350
|
-
return lines.join("\n");
|
|
29351
|
-
}
|
|
29352
|
-
async function prepareStrategistInputs(ctx, objective) {
|
|
29353
|
-
let snapshot = ctx.snapshot.computeResult;
|
|
29354
|
-
if (!snapshot) {
|
|
29355
|
-
snapshot = await computeFullHealth();
|
|
29356
|
-
ctx.snapshot.computeResult = snapshot;
|
|
29357
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
29358
|
-
segmentId: s.segment.id,
|
|
29359
|
-
segmentName: s.segment.name,
|
|
29360
|
-
result: s.result
|
|
29361
|
-
}));
|
|
29362
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
29363
|
-
}
|
|
29364
|
-
const audit = ctx.gapAudit ?? await refreshGapAudit(ctx).catch(() => null);
|
|
29365
|
-
const gapAuditBlock = audit ? serializeGapAudit(audit) : "";
|
|
29366
|
-
const memoryBlock = await Promise.resolve().then(() => (init_store2(), store_exports2)).then((m) => m.buildMemoryBlock(objective)).catch(() => "");
|
|
29367
|
-
let baselineBatchId = null;
|
|
29368
|
-
try {
|
|
29369
|
-
const reading = await getLatestHealthReading();
|
|
29370
|
-
baselineBatchId = reading?.upload_batch_id ?? null;
|
|
29371
|
-
} catch {
|
|
29372
|
-
}
|
|
29373
|
-
return {
|
|
29374
|
-
snapshot,
|
|
29375
|
-
divergences: ctx.snapshot.divergences,
|
|
29376
|
-
gapAuditBlock,
|
|
29377
|
-
memoryBlock,
|
|
29378
|
-
baselineBatchId,
|
|
29379
|
-
includeMetrics: true
|
|
29380
|
-
};
|
|
29381
|
-
}
|
|
29382
|
-
function proposeObjectiveFromSnapshot(snapshot) {
|
|
29383
|
-
const { aggregate } = snapshot;
|
|
29384
|
-
const gating = aggregate.gating_vital_sign;
|
|
29385
|
-
if (!gating) return null;
|
|
29386
|
-
const vital = aggregate.vital_signs.find((v) => v.vital_sign === gating);
|
|
29387
|
-
if (!vital) return null;
|
|
29388
|
-
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
29389
|
-
const dollar = vital.dollar_value != null && vital.dollar_value > 0 ? ` and recover the ${formatCurrency(vital.dollar_value)} ${vital.dollar_label ?? "at stake"}` : "";
|
|
29390
|
-
return `Move ${label} from ${Math.round(vital.score)} to 60+${dollar} within 60 days`;
|
|
29391
|
-
}
|
|
29392
|
-
function slugify2(value) {
|
|
29393
|
-
const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
29394
|
-
return slug || `strategy-${Date.now()}`;
|
|
29395
|
-
}
|
|
29396
|
-
function outcomeToMetric(outcome) {
|
|
29397
|
-
return {
|
|
29398
|
-
name: outcome.metric,
|
|
29399
|
-
target: outcome.target_range,
|
|
29400
|
-
baseline: outcome.baseline,
|
|
29401
|
-
timeframe: `by ${outcome.check_date}`
|
|
29402
|
-
};
|
|
29403
|
-
}
|
|
29404
|
-
async function persistStrategistPlan(plan, opts = {}) {
|
|
29405
|
-
await initSchema();
|
|
29406
|
-
const slug = slugify2(plan.title);
|
|
29407
|
-
const libraryPath = strategyLibraryPath(slug);
|
|
29408
|
-
const linkedPlayIds = [...new Set(plan.workstreams.flatMap((ws) => ws.play_ids))];
|
|
29409
|
-
const successMetrics = plan.workstreams.map((ws) => outcomeToMetric(ws.expected_outcome));
|
|
29410
|
-
const leadingIndicators = plan.workstreams.flatMap((ws) => ws.leading_indicators.map(outcomeToMetric));
|
|
29411
|
-
const recommendedActions = plan.workstreams.flatMap((ws) => ws.actions.map((action) => `[WS${ws.order}] ${action}`)).slice(0, 15);
|
|
29412
|
-
const reviewProtocol = [
|
|
29413
|
-
`Review ${plan.review_cadence.toLowerCase()} with /strategy review ${slug}.`,
|
|
29414
|
-
`Check each milestone at its due date against the named verification method.`,
|
|
29415
|
-
`At each outcome check date, compare the measured value to its target range against baseline batch ${opts.baselineBatchId ?? "(latest)"}.`,
|
|
29416
|
-
`If a contingency trigger fires, activate the pre-agreed fallback.`
|
|
29417
|
-
].join(" ");
|
|
29418
|
-
const id = await upsertStrategy({
|
|
29419
|
-
slug,
|
|
29420
|
-
title: plan.title,
|
|
29421
|
-
status: opts.status ?? "active",
|
|
29422
|
-
source_type: "agent",
|
|
29423
|
-
source_path: null,
|
|
29424
|
-
goal: plan.objective,
|
|
29425
|
-
hypothesis: plan.hypothesis,
|
|
29426
|
-
target_segment: plan.target_segment,
|
|
29427
|
-
priority: plan.priority,
|
|
29428
|
-
linked_play_ids: linkedPlayIds,
|
|
29429
|
-
success_metrics: successMetrics,
|
|
29430
|
-
leading_indicators: leadingIndicators,
|
|
29431
|
-
risks: plan.risks,
|
|
29432
|
-
recommended_actions: recommendedActions,
|
|
29433
|
-
experiment_design: reviewProtocol,
|
|
29434
|
-
review_cadence: plan.review_cadence,
|
|
29435
|
-
confidence: plan.confidence,
|
|
29436
|
-
raw_excerpt: plan.summary_30k,
|
|
29437
|
-
library_path: libraryPath,
|
|
29438
|
-
origin: "strategist",
|
|
29439
|
-
objective: plan.objective,
|
|
29440
|
-
constraints: plan.constraints,
|
|
29441
|
-
workstreams: plan.workstreams,
|
|
29442
|
-
assumptions: plan.assumptions,
|
|
29443
|
-
baseline_batch_id: opts.baselineBatchId ?? null
|
|
29444
|
-
});
|
|
29445
|
-
const strategy = await getStrategyBySlugOrId(id);
|
|
29446
|
-
if (!strategy) {
|
|
29447
|
-
throw new NtrpError("strategy_persist_failed", "Strategy was not found after saving.", 1 /* RuntimeError */);
|
|
29448
|
-
}
|
|
29449
|
-
const writtenPath = writeStrategyMarkdown(strategy);
|
|
29450
|
-
await insertStrategySource({
|
|
29451
|
-
strategy_id: strategy.id,
|
|
29452
|
-
source_type: "agent",
|
|
29453
|
-
source_path: null,
|
|
29454
|
-
content_hash: createHash2("sha256").update(JSON.stringify(plan)).digest("hex"),
|
|
29455
|
-
extracted_text_excerpt: plan.summary_30k.slice(0, 800),
|
|
29456
|
-
metadata: {
|
|
29457
|
-
origin: "strategist",
|
|
29458
|
-
objective: plan.objective,
|
|
29459
|
-
workstream_count: plan.workstreams.length,
|
|
29460
|
-
baseline_batch_id: opts.baselineBatchId ?? null
|
|
29461
|
-
}
|
|
29462
|
-
});
|
|
29463
|
-
return { strategy: { ...strategy, library_path: writtenPath }, library_path: writtenPath };
|
|
29464
|
-
}
|
|
29465
|
-
var init_strategist = __esm({
|
|
29466
|
-
"src/services/strategist.ts"() {
|
|
29467
|
-
"use strict";
|
|
29468
|
-
init_schema();
|
|
29469
|
-
init_queries();
|
|
29470
|
-
init_health_score();
|
|
29471
|
-
init_divergence();
|
|
29472
|
-
init_gap_audit();
|
|
29473
|
-
init_library();
|
|
29474
|
-
init_errors2();
|
|
29475
|
-
init_types2();
|
|
29476
|
-
init_formatters();
|
|
29477
|
-
init_formatters();
|
|
29478
|
-
}
|
|
29479
|
-
});
|
|
29480
|
-
|
|
29481
29614
|
// src/ai/strategist-prompt.ts
|
|
29482
29615
|
function companyContextSection4() {
|
|
29483
29616
|
const block = buildCompanyProfileBlock();
|
|
@@ -29616,6 +29749,52 @@ ${AAR_BLOCK}
|
|
|
29616
29749
|
|
|
29617
29750
|
Then respond with the FINAL revised plan as strict JSON in the same schema \u2014 no markdown fences, no prose. Fold what you learned into constraints, assumptions, risks, and confidence. This version is the one that gets saved and reviewed against, so make every number one you are willing to be checked on.`;
|
|
29618
29751
|
}
|
|
29752
|
+
function buildCriticMessage(input) {
|
|
29753
|
+
return `CRAFT CRITIC. You are a skeptical partner reviewing a GTM plan before it ships to a client. The objective is locked and must be echoed verbatim.
|
|
29754
|
+
|
|
29755
|
+
ARMED OBJECTIVE (byte-identical; do not rewrite):
|
|
29756
|
+
"${input.objective}"
|
|
29757
|
+
|
|
29758
|
+
CODE RUBRIC GAPS (already blocking \u2014 you cannot ship until these are gone):
|
|
29759
|
+
${input.rubricGaps || "(none)"}
|
|
29760
|
+
|
|
29761
|
+
LIVE SNAPSHOT (cite these numbers only):
|
|
29762
|
+
${input.healthSnapshot}
|
|
29763
|
+
|
|
29764
|
+
CURRENT PLAN JSON:
|
|
29765
|
+
${input.planJson}
|
|
29766
|
+
|
|
29767
|
+
${HEILMEIER_GATE}
|
|
29768
|
+
|
|
29769
|
+
${AAR_BLOCK}
|
|
29770
|
+
|
|
29771
|
+
Decide whether a partner would ship this. Respond with STRICT JSON only:
|
|
29772
|
+
{
|
|
29773
|
+
"ship": false,
|
|
29774
|
+
"score": 0,
|
|
29775
|
+
"objective_echo": ${JSON.stringify(input.objective)},
|
|
29776
|
+
"gaps": [{ "code": "bottleneck", "severity": "blocking", "note": "...", "fix": "..." }],
|
|
29777
|
+
"killed_alternative": "one approach that failed newness/stake/exams",
|
|
29778
|
+
"partner_pushback": "one paragraph a partner would actually say"
|
|
29779
|
+
}
|
|
29780
|
+
|
|
29781
|
+
ship may be true only when objective_echo matches, blocking gaps are empty, and you would defend this plan in the room. Advisory gaps do not block. Score 0-100.`;
|
|
29782
|
+
}
|
|
29783
|
+
function buildReviseMessage(input) {
|
|
29784
|
+
return `CRAFT REVISE. Keep the objective verbatim. Patch only the listed gap codes. Copy baselines from the current plan / snapshot exactly \u2014 do not invent numbers.
|
|
29785
|
+
|
|
29786
|
+
ARMED OBJECTIVE (must appear unchanged as "objective"):
|
|
29787
|
+
"${input.objective}"
|
|
29788
|
+
|
|
29789
|
+
GAPS TO CLOSE:
|
|
29790
|
+
${input.gapsJson}
|
|
29791
|
+
|
|
29792
|
+
CURRENT PLAN:
|
|
29793
|
+
${input.planJson}
|
|
29794
|
+
|
|
29795
|
+
Respond with ONLY the full plan JSON object in this schema \u2014 no prose, no fences:
|
|
29796
|
+
${STRATEGIST_PLAN_SCHEMA_BLOCK}`;
|
|
29797
|
+
}
|
|
29619
29798
|
var STRATEGIST_PLAN_SCHEMA_BLOCK;
|
|
29620
29799
|
var init_strategist_prompt = __esm({
|
|
29621
29800
|
"src/ai/strategist-prompt.ts"() {
|
|
@@ -30143,6 +30322,297 @@ var init_strategist_validate = __esm({
|
|
|
30143
30322
|
}
|
|
30144
30323
|
});
|
|
30145
30324
|
|
|
30325
|
+
// src/ai/strategist-rubric.ts
|
|
30326
|
+
function numbersMatch2(a, b) {
|
|
30327
|
+
if (a === b) return true;
|
|
30328
|
+
if (a === 0 || b === 0) return Math.abs(a - b) < 0.5;
|
|
30329
|
+
return Math.abs(a - b) / Math.max(Math.abs(a), Math.abs(b)) <= 0.02;
|
|
30330
|
+
}
|
|
30331
|
+
function parseIsoDate2(value) {
|
|
30332
|
+
const match = value.trim().match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
30333
|
+
if (!match) return null;
|
|
30334
|
+
const date = /* @__PURE__ */ new Date(`${match[1]}-${match[2]}-${match[3]}T00:00:00Z`);
|
|
30335
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
30336
|
+
}
|
|
30337
|
+
function addDays2(date, days) {
|
|
30338
|
+
const out = new Date(date);
|
|
30339
|
+
out.setUTCDate(out.getUTCDate() + days);
|
|
30340
|
+
return out;
|
|
30341
|
+
}
|
|
30342
|
+
function workstreamText(ws) {
|
|
30343
|
+
return [ws.title, ws.problem, ws.rationale, ws.actions.join(" "), ws.play_ids.join(" ")].join(" ");
|
|
30344
|
+
}
|
|
30345
|
+
function hasOwnerShape(ws) {
|
|
30346
|
+
const blob = workstreamText(ws).toLowerCase();
|
|
30347
|
+
return OWNER_TOKENS.some((token) => blob.includes(token));
|
|
30348
|
+
}
|
|
30349
|
+
function firstWorkstreamMovesConstraint(ws, gating) {
|
|
30350
|
+
const plays = getPlaybook();
|
|
30351
|
+
if (ws.play_ids.some((id) => plays.find((p) => p.id === id)?.trigger_vital_sign === gating)) {
|
|
30352
|
+
return true;
|
|
30353
|
+
}
|
|
30354
|
+
const blob = workstreamText(ws).toLowerCase();
|
|
30355
|
+
return VITAL_ALIASES[gating].some((alias) => blob.includes(alias));
|
|
30356
|
+
}
|
|
30357
|
+
function citesStake(plan, snapshot) {
|
|
30358
|
+
const dollars = [];
|
|
30359
|
+
if (snapshot.aggregate.total_value_at_risk != null && snapshot.aggregate.total_value_at_risk > 0) {
|
|
30360
|
+
dollars.push(snapshot.aggregate.total_value_at_risk);
|
|
30361
|
+
}
|
|
30362
|
+
for (const vs of snapshot.aggregate.vital_signs) {
|
|
30363
|
+
if (vs.dollar_value != null && vs.dollar_value > 0) dollars.push(vs.dollar_value);
|
|
30364
|
+
}
|
|
30365
|
+
if (dollars.length === 0) return true;
|
|
30366
|
+
const blob = `${plan.summary_30k} ${plan.hypothesis} ${plan.workstreams[0]?.problem ?? ""}`;
|
|
30367
|
+
const found = extractNumbers(blob);
|
|
30368
|
+
if (found.some((n) => dollars.some((d) => numbersMatch2(n, d)))) return true;
|
|
30369
|
+
const formatted = dollars.map((d) => formatCurrency(d).toLowerCase());
|
|
30370
|
+
const lower = blob.toLowerCase();
|
|
30371
|
+
return formatted.some((f) => lower.includes(f.toLowerCase()));
|
|
30372
|
+
}
|
|
30373
|
+
function hasStartWithin48h(plan, todayIso) {
|
|
30374
|
+
const today = parseIsoDate2(todayIso) ?? /* @__PURE__ */ new Date(`${todayIso}T00:00:00Z`);
|
|
30375
|
+
const limit = addDays2(today, 2);
|
|
30376
|
+
const first = plan.workstreams[0];
|
|
30377
|
+
if (!first) return false;
|
|
30378
|
+
if (first.actions.some((action) => START_SOON_RE.test(action))) return true;
|
|
30379
|
+
const dated = [
|
|
30380
|
+
...first.milestones.map((m) => m.due),
|
|
30381
|
+
...first.deliverables.map((d) => d.due)
|
|
30382
|
+
];
|
|
30383
|
+
for (const iso of dated) {
|
|
30384
|
+
const parsed = parseIsoDate2(iso);
|
|
30385
|
+
if (parsed && parsed.getTime() <= limit.getTime()) return true;
|
|
30386
|
+
}
|
|
30387
|
+
return plan.workstreams.some(
|
|
30388
|
+
(ws) => ws.actions.some((action) => START_SOON_RE.test(action))
|
|
30389
|
+
);
|
|
30390
|
+
}
|
|
30391
|
+
function examsHold(ws) {
|
|
30392
|
+
const o = ws.expected_outcome;
|
|
30393
|
+
return extractNumbers(o.baseline).length > 0 && extractNumbers(o.target_range).length > 0 && isKnownInstrument(o.measured_by);
|
|
30394
|
+
}
|
|
30395
|
+
function inventedNumbers(plan, evidenceText) {
|
|
30396
|
+
const evidenceNums = extractNumbers(evidenceText);
|
|
30397
|
+
if (evidenceNums.length === 0) return false;
|
|
30398
|
+
for (const ws of plan.workstreams) {
|
|
30399
|
+
const baselines = extractNumbers(ws.expected_outcome.baseline);
|
|
30400
|
+
if (baselines.length === 0) continue;
|
|
30401
|
+
if (!baselines.some((b) => evidenceNums.some((e) => numbersMatch2(b, e)))) return true;
|
|
30402
|
+
}
|
|
30403
|
+
return false;
|
|
30404
|
+
}
|
|
30405
|
+
function scoreConsultantPlan(plan, opts) {
|
|
30406
|
+
const gaps = [];
|
|
30407
|
+
const gating = opts.snapshot.aggregate.gating_vital_sign;
|
|
30408
|
+
const first = plan.workstreams[0];
|
|
30409
|
+
const evidence = opts.evidenceText ?? JSON.stringify({
|
|
30410
|
+
aggregate: opts.snapshot.aggregate
|
|
30411
|
+
});
|
|
30412
|
+
if (opts.armedObjective && plan.objective !== opts.armedObjective) {
|
|
30413
|
+
gaps.push({
|
|
30414
|
+
code: "objective_drift",
|
|
30415
|
+
severity: "blocking",
|
|
30416
|
+
note: "Plan objective does not match the armed objective.",
|
|
30417
|
+
fix: "Copy the armed objective into plan.objective with no edits."
|
|
30418
|
+
});
|
|
30419
|
+
}
|
|
30420
|
+
if (!first || !firstWorkstreamMovesConstraint(first, gating)) {
|
|
30421
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
30422
|
+
gaps.push({
|
|
30423
|
+
code: "bottleneck",
|
|
30424
|
+
severity: "blocking",
|
|
30425
|
+
note: `First workstream does not move the gating vital (${label}).`,
|
|
30426
|
+
fix: `Lead with a play or problem that moves ${gating}. Non-constraint work waits.`
|
|
30427
|
+
});
|
|
30428
|
+
}
|
|
30429
|
+
if (!citesStake(plan, opts.snapshot)) {
|
|
30430
|
+
gaps.push({
|
|
30431
|
+
code: "stake",
|
|
30432
|
+
severity: "blocking",
|
|
30433
|
+
note: "30k or hypothesis does not cite snapshot dollars / cost of inaction.",
|
|
30434
|
+
fix: "Open with the gating dollar figure from the live snapshot."
|
|
30435
|
+
});
|
|
30436
|
+
}
|
|
30437
|
+
for (const ws of plan.workstreams) {
|
|
30438
|
+
if (!hasOwnerShape(ws)) {
|
|
30439
|
+
gaps.push({
|
|
30440
|
+
code: "owner",
|
|
30441
|
+
severity: "blocking",
|
|
30442
|
+
note: `Workstream "${ws.title}" has no function-shaped owner.`,
|
|
30443
|
+
fix: "Name RevOps, sales manager, CS lead, marketing ops, or AE lead in the actions."
|
|
30444
|
+
});
|
|
30445
|
+
break;
|
|
30446
|
+
}
|
|
30447
|
+
}
|
|
30448
|
+
if (!hasStartWithin48h(plan, opts.todayIso)) {
|
|
30449
|
+
gaps.push({
|
|
30450
|
+
code: "start_48h",
|
|
30451
|
+
severity: "blocking",
|
|
30452
|
+
note: "No action or milestone is startable within 48 hours.",
|
|
30453
|
+
fix: "Put a dated first action or milestone within two days of today."
|
|
30454
|
+
});
|
|
30455
|
+
}
|
|
30456
|
+
const missingEffort = plan.workstreams.filter(
|
|
30457
|
+
(ws) => !Number.isFinite(ws.effort_hours) || ws.effort_hours <= 0
|
|
30458
|
+
);
|
|
30459
|
+
if (missingEffort.length > 0 || plan.workstreams.length === 0) {
|
|
30460
|
+
gaps.push({
|
|
30461
|
+
code: "effort",
|
|
30462
|
+
severity: "blocking",
|
|
30463
|
+
note: "Each workstream needs a finite effort_hours greater than zero.",
|
|
30464
|
+
fix: "Set effort_hours to a real team-hour estimate per workstream."
|
|
30465
|
+
});
|
|
30466
|
+
}
|
|
30467
|
+
if (opts.constraintsNote?.trim() && plan.constraints.length === 0) {
|
|
30468
|
+
gaps.push({
|
|
30469
|
+
code: "scope",
|
|
30470
|
+
severity: "blocking",
|
|
30471
|
+
note: "Operator stated constraints, but the plan constraints list is empty.",
|
|
30472
|
+
fix: "Copy operator constraints into plan.constraints. Name what is out of scope."
|
|
30473
|
+
});
|
|
30474
|
+
}
|
|
30475
|
+
if (plan.workstreams.some((ws) => !examsHold(ws))) {
|
|
30476
|
+
gaps.push({
|
|
30477
|
+
code: "exams",
|
|
30478
|
+
severity: "blocking",
|
|
30479
|
+
note: "A workstream expected outcome is missing a numeric baseline, range, or known instrument.",
|
|
30480
|
+
fix: "Give every expected_outcome a baseline, target range, and measured_by instrument from live data."
|
|
30481
|
+
});
|
|
30482
|
+
}
|
|
30483
|
+
if (inventedNumbers(plan, evidence)) {
|
|
30484
|
+
gaps.push({
|
|
30485
|
+
code: "invented_numbers",
|
|
30486
|
+
severity: "blocking",
|
|
30487
|
+
note: "An outcome baseline is not in the grounded snapshot.",
|
|
30488
|
+
fix: "Copy baselines from the health snapshot exactly."
|
|
30489
|
+
});
|
|
30490
|
+
}
|
|
30491
|
+
const blocking = gaps.filter((g) => g.severity === "blocking");
|
|
30492
|
+
return { pass: blocking.length === 0, gaps, blocking };
|
|
30493
|
+
}
|
|
30494
|
+
function asGapCode(value) {
|
|
30495
|
+
return typeof value === "string" && GAP_CODES.includes(value) ? value : null;
|
|
30496
|
+
}
|
|
30497
|
+
function parseCriticResponse(raw, opts) {
|
|
30498
|
+
if (!raw) {
|
|
30499
|
+
return {
|
|
30500
|
+
ship: false,
|
|
30501
|
+
score: 0,
|
|
30502
|
+
objective_echo: "",
|
|
30503
|
+
gaps: [
|
|
30504
|
+
{
|
|
30505
|
+
code: "thin_evidence",
|
|
30506
|
+
severity: "blocking",
|
|
30507
|
+
note: "Critic reply was not valid JSON.",
|
|
30508
|
+
fix: "Respond with the critic JSON object only."
|
|
30509
|
+
}
|
|
30510
|
+
],
|
|
30511
|
+
killed_alternative: "",
|
|
30512
|
+
partner_pushback: ""
|
|
30513
|
+
};
|
|
30514
|
+
}
|
|
30515
|
+
const objectiveEcho = typeof raw.objective_echo === "string" ? raw.objective_echo : "";
|
|
30516
|
+
const killed = typeof raw.killed_alternative === "string" ? raw.killed_alternative.trim() : "";
|
|
30517
|
+
const pushback = typeof raw.partner_pushback === "string" ? raw.partner_pushback.trim() : "";
|
|
30518
|
+
const scoreRaw = typeof raw.score === "number" ? raw.score : Number(raw.score);
|
|
30519
|
+
const score = Number.isFinite(scoreRaw) ? Math.max(0, Math.min(100, scoreRaw)) : 0;
|
|
30520
|
+
const gaps = [];
|
|
30521
|
+
if (Array.isArray(raw.gaps)) {
|
|
30522
|
+
for (const item of raw.gaps) {
|
|
30523
|
+
if (!item || typeof item !== "object") continue;
|
|
30524
|
+
const rec = item;
|
|
30525
|
+
const code = asGapCode(rec.code);
|
|
30526
|
+
if (!code) continue;
|
|
30527
|
+
gaps.push({
|
|
30528
|
+
code,
|
|
30529
|
+
severity: rec.severity === "advisory" ? "advisory" : "blocking",
|
|
30530
|
+
note: typeof rec.note === "string" ? rec.note : "",
|
|
30531
|
+
fix: typeof rec.fix === "string" ? rec.fix : ""
|
|
30532
|
+
});
|
|
30533
|
+
}
|
|
30534
|
+
}
|
|
30535
|
+
if (objectiveEcho !== opts.armedObjective) {
|
|
30536
|
+
gaps.push({
|
|
30537
|
+
code: "objective_drift",
|
|
30538
|
+
severity: "blocking",
|
|
30539
|
+
note: "Critic objective_echo does not match the armed objective.",
|
|
30540
|
+
fix: "Echo the armed objective exactly."
|
|
30541
|
+
});
|
|
30542
|
+
}
|
|
30543
|
+
if (!killed) {
|
|
30544
|
+
gaps.push({
|
|
30545
|
+
code: "alternatives_killed",
|
|
30546
|
+
severity: "blocking",
|
|
30547
|
+
note: "Critic did not name an approach that failed newness, stake, or exams.",
|
|
30548
|
+
fix: "Name one killed alternative so the plan is not a restatement of the status quo."
|
|
30549
|
+
});
|
|
30550
|
+
}
|
|
30551
|
+
for (const gap of opts.rubricBlocking) {
|
|
30552
|
+
if (!gaps.some((g) => g.code === gap.code)) gaps.push(gap);
|
|
30553
|
+
}
|
|
30554
|
+
const blocking = gaps.filter((g) => g.severity === "blocking");
|
|
30555
|
+
const modelShip = raw.ship === true;
|
|
30556
|
+
const ship = modelShip && blocking.length === 0 && score >= opts.minScore && objectiveEcho === opts.armedObjective && killed.length > 0;
|
|
30557
|
+
return {
|
|
30558
|
+
ship,
|
|
30559
|
+
score,
|
|
30560
|
+
objective_echo: objectiveEcho,
|
|
30561
|
+
gaps,
|
|
30562
|
+
killed_alternative: killed,
|
|
30563
|
+
partner_pushback: pushback
|
|
30564
|
+
};
|
|
30565
|
+
}
|
|
30566
|
+
function lockPlanObjective(plan, armedObjective) {
|
|
30567
|
+
return { ...plan, objective: armedObjective };
|
|
30568
|
+
}
|
|
30569
|
+
var OWNER_TOKENS, VITAL_ALIASES, START_SOON_RE, GAP_CODES;
|
|
30570
|
+
var init_strategist_rubric = __esm({
|
|
30571
|
+
"src/ai/strategist-rubric.ts"() {
|
|
30572
|
+
"use strict";
|
|
30573
|
+
init_playbook();
|
|
30574
|
+
init_strategist_validate();
|
|
30575
|
+
init_formatters();
|
|
30576
|
+
OWNER_TOKENS = [
|
|
30577
|
+
"revops",
|
|
30578
|
+
"rev ops",
|
|
30579
|
+
"revenue ops",
|
|
30580
|
+
"sales manager",
|
|
30581
|
+
"sales ops",
|
|
30582
|
+
"cs lead",
|
|
30583
|
+
"customer success",
|
|
30584
|
+
"marketing ops",
|
|
30585
|
+
"ae lead",
|
|
30586
|
+
"account executive lead",
|
|
30587
|
+
"sdr lead",
|
|
30588
|
+
"bdr lead",
|
|
30589
|
+
"demand gen",
|
|
30590
|
+
"enablement"
|
|
30591
|
+
];
|
|
30592
|
+
VITAL_ALIASES = {
|
|
30593
|
+
freshness: ["freshness", "stale", "zombie", "dead pipeline"],
|
|
30594
|
+
flow_rate: ["flow_rate", "flow rate", "stuck", "velocity", "stage"],
|
|
30595
|
+
drop_rate: ["drop_rate", "drop rate", "handoff", "leak"],
|
|
30596
|
+
signal_to_noise: ["signal_to_noise", "signal-to-noise", "signal to noise", "noise", "misdirected"],
|
|
30597
|
+
thread_depth: ["thread_depth", "thread depth", "single-thread", "single threaded", "multi-thread"]
|
|
30598
|
+
};
|
|
30599
|
+
START_SOON_RE = /\b(48\s*-?hours?|48h|tomorrow|today|monday|this week|within two days|within 2 days)\b/i;
|
|
30600
|
+
GAP_CODES = [
|
|
30601
|
+
"bottleneck",
|
|
30602
|
+
"stake",
|
|
30603
|
+
"owner",
|
|
30604
|
+
"start_48h",
|
|
30605
|
+
"effort",
|
|
30606
|
+
"scope",
|
|
30607
|
+
"exams",
|
|
30608
|
+
"alternatives_killed",
|
|
30609
|
+
"thin_evidence",
|
|
30610
|
+
"invented_numbers",
|
|
30611
|
+
"objective_drift"
|
|
30612
|
+
];
|
|
30613
|
+
}
|
|
30614
|
+
});
|
|
30615
|
+
|
|
30146
30616
|
// src/ai/strategist.ts
|
|
30147
30617
|
function buildHealthSnapshot(computeResult, divergences) {
|
|
30148
30618
|
const { aggregate, segments } = computeResult;
|
|
@@ -30365,6 +30835,15 @@ Respond with ONLY the corrected plan JSON object in the required schema (title,
|
|
|
30365
30835
|
for (const issue of validated.issues) {
|
|
30366
30836
|
yield { type: "notice", text: issue };
|
|
30367
30837
|
}
|
|
30838
|
+
const rubric = scoreConsultantPlan(validated.plan, {
|
|
30839
|
+
snapshot: options.computeResult,
|
|
30840
|
+
constraintsNote: options.constraintsNote,
|
|
30841
|
+
todayIso,
|
|
30842
|
+
evidenceText
|
|
30843
|
+
});
|
|
30844
|
+
for (const gap of rubric.blocking) {
|
|
30845
|
+
yield { type: "notice", text: `Plan gap (${gap.code}): ${gap.note}` };
|
|
30846
|
+
}
|
|
30368
30847
|
yield {
|
|
30369
30848
|
type: "plan",
|
|
30370
30849
|
plan: validated.plan,
|
|
@@ -30401,7 +30880,7 @@ function describePlanValidationFailure(text, evidenceText, todayIso) {
|
|
|
30401
30880
|
return "unknown validation failure";
|
|
30402
30881
|
}
|
|
30403
30882
|
var GROUND_MAX_ROUNDS, BACKCAST_MAX_ROUNDS, STRESS_MAX_ROUNDS, STAGE_MAX_TOKENS, PLAN_JSON_MAX_TOKENS, STAGE_LABELS;
|
|
30404
|
-
var
|
|
30883
|
+
var init_strategist = __esm({
|
|
30405
30884
|
"src/ai/strategist.ts"() {
|
|
30406
30885
|
"use strict";
|
|
30407
30886
|
init_types();
|
|
@@ -30416,6 +30895,7 @@ var init_strategist2 = __esm({
|
|
|
30416
30895
|
init_thread();
|
|
30417
30896
|
init_strategist_prompt();
|
|
30418
30897
|
init_strategist_validate();
|
|
30898
|
+
init_strategist_rubric();
|
|
30419
30899
|
init_strategist_prompt();
|
|
30420
30900
|
GROUND_MAX_ROUNDS = 6;
|
|
30421
30901
|
BACKCAST_MAX_ROUNDS = 4;
|
|
@@ -30430,6 +30910,803 @@ var init_strategist2 = __esm({
|
|
|
30430
30910
|
}
|
|
30431
30911
|
});
|
|
30432
30912
|
|
|
30913
|
+
// src/ai/strategist-craft.ts
|
|
30914
|
+
import { createHash as createHash2 } from "crypto";
|
|
30915
|
+
function snapshotHash(snapshotJson) {
|
|
30916
|
+
return createHash2("sha256").update(snapshotJson).digest("hex").slice(0, 16);
|
|
30917
|
+
}
|
|
30918
|
+
function totalTokens(job) {
|
|
30919
|
+
return job.cumulative_input_tokens + job.cumulative_output_tokens;
|
|
30920
|
+
}
|
|
30921
|
+
function fallbackNotice(text) {
|
|
30922
|
+
return /grounded (playbook )?fallback/i.test(text);
|
|
30923
|
+
}
|
|
30924
|
+
function canShip(job, critic, rubricBlocking) {
|
|
30925
|
+
if (!critic.ship) return false;
|
|
30926
|
+
if (rubricBlocking.length > 0) return false;
|
|
30927
|
+
if (critic.score < job.min_score) return false;
|
|
30928
|
+
if (job.from_fallback && !job.revised_once) return false;
|
|
30929
|
+
return true;
|
|
30930
|
+
}
|
|
30931
|
+
function recordScoreStreak(job, score) {
|
|
30932
|
+
if (job.last_critic_score != null && score <= job.last_critic_score) {
|
|
30933
|
+
job.no_improve_streak += 1;
|
|
30934
|
+
} else {
|
|
30935
|
+
job.no_improve_streak = 0;
|
|
30936
|
+
}
|
|
30937
|
+
job.last_critic_score = score;
|
|
30938
|
+
}
|
|
30939
|
+
async function* strategistCraftSession(options) {
|
|
30940
|
+
assertReplAi(options.ctx);
|
|
30941
|
+
const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
30942
|
+
const healthSnapshot = buildHealthSnapshot(options.computeResult, options.divergences);
|
|
30943
|
+
const maxRounds = options.maxRounds ?? DEFAULT_CRAFT_MAX_ROUNDS;
|
|
30944
|
+
const minScore = options.minScore ?? DEFAULT_CRAFT_MIN_SCORE;
|
|
30945
|
+
const complete = options.complete ?? completeWithFailover;
|
|
30946
|
+
const runIteration0 = options.runIteration0 ?? strategistPlanSession;
|
|
30947
|
+
const job = options.job ?? createRuminationJob({
|
|
30948
|
+
objective: options.objective,
|
|
30949
|
+
constraintsNote: options.constraintsNote,
|
|
30950
|
+
baselineBatchId: options.baselineBatchId,
|
|
30951
|
+
snapshotHash: snapshotHash(healthSnapshot),
|
|
30952
|
+
minScore,
|
|
30953
|
+
maxRounds,
|
|
30954
|
+
maxTokens: options.maxTokens
|
|
30955
|
+
});
|
|
30956
|
+
if (!options.job) job.objective = options.objective;
|
|
30957
|
+
job.min_score = minScore;
|
|
30958
|
+
job.max_rounds = maxRounds;
|
|
30959
|
+
if (options.maxTokens != null) job.max_tokens = options.maxTokens;
|
|
30960
|
+
saveRuminationJob(job);
|
|
30961
|
+
options.ctx.lastCraftJobId = job.status === "ready" ? void 0 : job.id;
|
|
30962
|
+
saveSessionState(options.ctx);
|
|
30963
|
+
const evidenceText = healthSnapshot;
|
|
30964
|
+
const armedObjective = job.objective;
|
|
30965
|
+
let lastMeta = { provider_used: "unknown", model_used: "unknown" };
|
|
30966
|
+
let measurable = { measurable_targets: 0, total_targets: 0 };
|
|
30967
|
+
const scorePlan = (plan) => scoreConsultantPlan(plan, {
|
|
30968
|
+
snapshot: options.computeResult,
|
|
30969
|
+
constraintsNote: options.constraintsNote ?? job.constraints_note,
|
|
30970
|
+
armedObjective,
|
|
30971
|
+
todayIso,
|
|
30972
|
+
evidenceText
|
|
30973
|
+
});
|
|
30974
|
+
const rememberBest = (plan, score) => {
|
|
30975
|
+
if (score == null) {
|
|
30976
|
+
if (!job.best_plan) job.best_plan = plan;
|
|
30977
|
+
return;
|
|
30978
|
+
}
|
|
30979
|
+
if (job.best_score == null || score > job.best_score) {
|
|
30980
|
+
job.best_score = score;
|
|
30981
|
+
job.best_plan = plan;
|
|
30982
|
+
}
|
|
30983
|
+
};
|
|
30984
|
+
try {
|
|
30985
|
+
if (job.status === "ready" && job.plan) {
|
|
30986
|
+
const locked = lockPlanObjective(job.plan, armedObjective);
|
|
30987
|
+
job.plan = locked;
|
|
30988
|
+
saveRuminationJob(job);
|
|
30989
|
+
yield {
|
|
30990
|
+
type: "plan",
|
|
30991
|
+
plan: locked,
|
|
30992
|
+
issues: [],
|
|
30993
|
+
measurable_targets: measurable.measurable_targets,
|
|
30994
|
+
total_targets: measurable.total_targets,
|
|
30995
|
+
baseline_batch_id: options.baselineBatchId ?? null
|
|
30996
|
+
};
|
|
30997
|
+
yield {
|
|
30998
|
+
type: "done",
|
|
30999
|
+
model_used: lastMeta.model_used,
|
|
31000
|
+
provider_used: lastMeta.provider_used,
|
|
31001
|
+
usage: lastMeta
|
|
31002
|
+
};
|
|
31003
|
+
return { job, plan: locked, status: job.status, stop_reason: job.stop_reason };
|
|
31004
|
+
}
|
|
31005
|
+
if (!job.plan) {
|
|
31006
|
+
yield { type: "stage", stage: "ground", label: "Craft \u2014 grounding, sequencing, stress-testing" };
|
|
31007
|
+
for await (const event of runIteration0({ ...options, objective: armedObjective })) {
|
|
31008
|
+
if (options.interrupted?.()) {
|
|
31009
|
+
job.status = "unfinished";
|
|
31010
|
+
job.stop_reason = "interrupt";
|
|
31011
|
+
saveRuminationJob(job);
|
|
31012
|
+
break;
|
|
31013
|
+
}
|
|
31014
|
+
if (event.type === "notice" && fallbackNotice(event.text)) {
|
|
31015
|
+
job.from_fallback = true;
|
|
31016
|
+
}
|
|
31017
|
+
if (event.type === "plan") {
|
|
31018
|
+
const locked = lockPlanObjective(event.plan, armedObjective);
|
|
31019
|
+
job.plan = locked;
|
|
31020
|
+
rememberBest(locked, null);
|
|
31021
|
+
measurable = {
|
|
31022
|
+
measurable_targets: event.measurable_targets,
|
|
31023
|
+
total_targets: event.total_targets
|
|
31024
|
+
};
|
|
31025
|
+
const rubric = scorePlan(locked);
|
|
31026
|
+
job.rubric_gaps = rubric.gaps;
|
|
31027
|
+
yield { ...event, plan: locked, issues: [...event.issues, ...rubric.blocking.map((g) => `Plan gap (${g.code}): ${g.note}`)] };
|
|
31028
|
+
} else if (event.type === "done") {
|
|
31029
|
+
lastMeta = event.usage ?? lastMeta;
|
|
31030
|
+
job.cumulative_input_tokens += event.usage?.input_tokens ?? 0;
|
|
31031
|
+
job.cumulative_output_tokens += event.usage?.output_tokens ?? 0;
|
|
31032
|
+
yield event;
|
|
31033
|
+
} else {
|
|
31034
|
+
yield event;
|
|
31035
|
+
}
|
|
31036
|
+
}
|
|
31037
|
+
saveRuminationJob(job);
|
|
31038
|
+
}
|
|
31039
|
+
if (!job.plan) {
|
|
31040
|
+
job.status = "unfinished";
|
|
31041
|
+
job.stop_reason = job.stop_reason ?? "error";
|
|
31042
|
+
saveRuminationJob(job);
|
|
31043
|
+
yield {
|
|
31044
|
+
type: "done",
|
|
31045
|
+
model_used: lastMeta.model_used,
|
|
31046
|
+
provider_used: lastMeta.provider_used,
|
|
31047
|
+
usage: lastMeta
|
|
31048
|
+
};
|
|
31049
|
+
return { job, plan: null, status: job.status, stop_reason: job.stop_reason };
|
|
31050
|
+
}
|
|
31051
|
+
rememberBest(job.plan, job.best_score);
|
|
31052
|
+
const systemPrompt = buildStrategistSystemPrompt(todayIso);
|
|
31053
|
+
const callText = async (surface, userContent, maxTokens) => {
|
|
31054
|
+
const result = await complete(
|
|
31055
|
+
{
|
|
31056
|
+
surface,
|
|
31057
|
+
messages: [{ role: "user", content: userContent }],
|
|
31058
|
+
system: systemPrompt,
|
|
31059
|
+
max_tokens: maxTokens
|
|
31060
|
+
},
|
|
31061
|
+
{ ctx: options.ctx }
|
|
31062
|
+
);
|
|
31063
|
+
lastMeta = result.meta;
|
|
31064
|
+
return {
|
|
31065
|
+
text: result.response.text,
|
|
31066
|
+
input: result.response.token_usage?.input_tokens ?? result.meta.input_tokens ?? 0,
|
|
31067
|
+
output: result.response.token_usage?.output_tokens ?? result.meta.output_tokens ?? 0
|
|
31068
|
+
};
|
|
31069
|
+
};
|
|
31070
|
+
let stop;
|
|
31071
|
+
const startRound = (job.iterations.at(-1)?.round ?? 0) + 1;
|
|
31072
|
+
for (let round = startRound; round <= maxRounds; round++) {
|
|
31073
|
+
if (options.interrupted?.()) {
|
|
31074
|
+
stop = "interrupt";
|
|
31075
|
+
break;
|
|
31076
|
+
}
|
|
31077
|
+
if (job.max_tokens != null && totalTokens(job) >= job.max_tokens) {
|
|
31078
|
+
stop = "budget_tokens";
|
|
31079
|
+
break;
|
|
31080
|
+
}
|
|
31081
|
+
const plan = job.plan;
|
|
31082
|
+
if (!plan) {
|
|
31083
|
+
stop = "error";
|
|
31084
|
+
break;
|
|
31085
|
+
}
|
|
31086
|
+
const rubric = scorePlan(plan);
|
|
31087
|
+
job.rubric_gaps = rubric.gaps;
|
|
31088
|
+
const criticRaw = await callText(
|
|
31089
|
+
"strategist_critic",
|
|
31090
|
+
buildCriticMessage({
|
|
31091
|
+
objective: armedObjective,
|
|
31092
|
+
planJson: JSON.stringify(plan),
|
|
31093
|
+
rubricGaps: rubric.blocking.map((g) => `${g.code}: ${g.note}`).join("\n") || "(none)",
|
|
31094
|
+
healthSnapshot
|
|
31095
|
+
}),
|
|
31096
|
+
CRITIC_MAX_TOKENS
|
|
31097
|
+
);
|
|
31098
|
+
const critic = parseCriticResponse(parseJsonObjectFromText(criticRaw.text), {
|
|
31099
|
+
armedObjective,
|
|
31100
|
+
minScore,
|
|
31101
|
+
rubricBlocking: rubric.blocking
|
|
31102
|
+
});
|
|
31103
|
+
job.last_critic = critic;
|
|
31104
|
+
recordScoreStreak(job, critic.score);
|
|
31105
|
+
rememberBest(plan, critic.score);
|
|
31106
|
+
const tokensThisRound = criticRaw.input + criticRaw.output;
|
|
31107
|
+
let decision = "revise";
|
|
31108
|
+
if (canShip(job, critic, rubric.blocking)) {
|
|
31109
|
+
decision = "ship";
|
|
31110
|
+
} else if (job.no_improve_streak >= 2) {
|
|
31111
|
+
decision = "no-delta";
|
|
31112
|
+
}
|
|
31113
|
+
addRuminationIteration(job, {
|
|
31114
|
+
round,
|
|
31115
|
+
critic_score: critic.score,
|
|
31116
|
+
ship: critic.ship,
|
|
31117
|
+
blocking_codes: critic.gaps.filter((g) => g.severity === "blocking").map((g) => g.code),
|
|
31118
|
+
tokens_in: criticRaw.input,
|
|
31119
|
+
tokens_out: criticRaw.output,
|
|
31120
|
+
decision
|
|
31121
|
+
});
|
|
31122
|
+
yield {
|
|
31123
|
+
type: "craft_round",
|
|
31124
|
+
round,
|
|
31125
|
+
max: maxRounds,
|
|
31126
|
+
score: critic.score,
|
|
31127
|
+
tokensThisRound,
|
|
31128
|
+
tokensTotal: totalTokens(job),
|
|
31129
|
+
decision
|
|
31130
|
+
};
|
|
31131
|
+
yield {
|
|
31132
|
+
type: "notice",
|
|
31133
|
+
text: `craft ${round}/${maxRounds} \xB7 score ${critic.score} \xB7 ${tokensThisRound} tok this round \xB7 ${totalTokens(job)} total`
|
|
31134
|
+
};
|
|
31135
|
+
saveRuminationJob(job);
|
|
31136
|
+
if (decision === "ship") {
|
|
31137
|
+
stop = "ship";
|
|
31138
|
+
break;
|
|
31139
|
+
}
|
|
31140
|
+
if (decision === "no-delta") {
|
|
31141
|
+
stop = "no_delta";
|
|
31142
|
+
break;
|
|
31143
|
+
}
|
|
31144
|
+
const needsGround = critic.gaps.some((g) => g.code === "thin_evidence") && !job.extra_ground_used;
|
|
31145
|
+
if (needsGround) {
|
|
31146
|
+
job.extra_ground_used = true;
|
|
31147
|
+
yield { type: "notice", text: "Critic asked for thinner evidence \u2014 one extra ground pass." };
|
|
31148
|
+
for await (const event of runIteration0({ ...options, objective: armedObjective })) {
|
|
31149
|
+
if (event.type === "notice" && fallbackNotice(event.text)) {
|
|
31150
|
+
job.from_fallback = true;
|
|
31151
|
+
} else if (event.type === "plan") {
|
|
31152
|
+
job.plan = lockPlanObjective(event.plan, armedObjective);
|
|
31153
|
+
rememberBest(job.plan, null);
|
|
31154
|
+
measurable = {
|
|
31155
|
+
measurable_targets: event.measurable_targets,
|
|
31156
|
+
total_targets: event.total_targets
|
|
31157
|
+
};
|
|
31158
|
+
} else if (event.type !== "done") {
|
|
31159
|
+
yield event;
|
|
31160
|
+
}
|
|
31161
|
+
}
|
|
31162
|
+
saveRuminationJob(job);
|
|
31163
|
+
continue;
|
|
31164
|
+
}
|
|
31165
|
+
if (options.interrupted?.()) {
|
|
31166
|
+
stop = "interrupt";
|
|
31167
|
+
break;
|
|
31168
|
+
}
|
|
31169
|
+
if (job.max_tokens != null && totalTokens(job) >= job.max_tokens) {
|
|
31170
|
+
stop = "budget_tokens";
|
|
31171
|
+
break;
|
|
31172
|
+
}
|
|
31173
|
+
const reviseRaw = await callText(
|
|
31174
|
+
"strategist",
|
|
31175
|
+
buildReviseMessage({
|
|
31176
|
+
objective: armedObjective,
|
|
31177
|
+
planJson: JSON.stringify(plan),
|
|
31178
|
+
gapsJson: JSON.stringify(critic.gaps)
|
|
31179
|
+
}),
|
|
31180
|
+
REVISE_MAX_TOKENS
|
|
31181
|
+
);
|
|
31182
|
+
job.cumulative_input_tokens += reviseRaw.input;
|
|
31183
|
+
job.cumulative_output_tokens += reviseRaw.output;
|
|
31184
|
+
const parsed = parseJsonObjectFromText(reviseRaw.text);
|
|
31185
|
+
const validated = parsed ? validateStrategistPlan(parsed, { evidenceText, todayIso }) : null;
|
|
31186
|
+
if (validated) {
|
|
31187
|
+
job.plan = lockPlanObjective(validated.plan, armedObjective);
|
|
31188
|
+
job.revised_once = true;
|
|
31189
|
+
job.from_fallback = false;
|
|
31190
|
+
measurable = {
|
|
31191
|
+
measurable_targets: validated.measurableTargets,
|
|
31192
|
+
total_targets: validated.totalTargets
|
|
31193
|
+
};
|
|
31194
|
+
const nextRubric = scorePlan(job.plan);
|
|
31195
|
+
job.rubric_gaps = nextRubric.gaps;
|
|
31196
|
+
} else {
|
|
31197
|
+
yield { type: "notice", text: "Revise JSON invalid \u2014 keeping prior plan." };
|
|
31198
|
+
}
|
|
31199
|
+
saveRuminationJob(job);
|
|
31200
|
+
}
|
|
31201
|
+
if (!stop) stop = "budget_rounds";
|
|
31202
|
+
if (stop !== "ship" && job.best_plan) {
|
|
31203
|
+
job.plan = lockPlanObjective(job.best_plan, armedObjective);
|
|
31204
|
+
}
|
|
31205
|
+
job.stop_reason = stop;
|
|
31206
|
+
job.status = stop === "ship" ? "ready" : "unfinished";
|
|
31207
|
+
saveRuminationJob(job);
|
|
31208
|
+
if (job.plan) {
|
|
31209
|
+
const rubric = scorePlan(job.plan);
|
|
31210
|
+
yield {
|
|
31211
|
+
type: "plan",
|
|
31212
|
+
plan: job.plan,
|
|
31213
|
+
issues: rubric.blocking.map((g) => `Plan gap (${g.code}): ${g.note}`),
|
|
31214
|
+
measurable_targets: measurable.measurable_targets,
|
|
31215
|
+
total_targets: measurable.total_targets,
|
|
31216
|
+
baseline_batch_id: options.baselineBatchId ?? null
|
|
31217
|
+
};
|
|
31218
|
+
}
|
|
31219
|
+
yield {
|
|
31220
|
+
type: "done",
|
|
31221
|
+
model_used: lastMeta.model_used,
|
|
31222
|
+
provider_used: lastMeta.provider_used,
|
|
31223
|
+
failover: lastMeta.failover,
|
|
31224
|
+
usage: {
|
|
31225
|
+
...lastMeta,
|
|
31226
|
+
input_tokens: job.cumulative_input_tokens,
|
|
31227
|
+
output_tokens: job.cumulative_output_tokens
|
|
31228
|
+
}
|
|
31229
|
+
};
|
|
31230
|
+
return { job, plan: job.plan, status: job.status, stop_reason: job.stop_reason };
|
|
31231
|
+
} catch (err) {
|
|
31232
|
+
job.status = "unfinished";
|
|
31233
|
+
job.stop_reason = "error";
|
|
31234
|
+
if (job.best_plan) job.plan = lockPlanObjective(job.best_plan, armedObjective);
|
|
31235
|
+
saveRuminationJob(job);
|
|
31236
|
+
throw err;
|
|
31237
|
+
}
|
|
31238
|
+
}
|
|
31239
|
+
function formatCraftRoundLine(event) {
|
|
31240
|
+
const score = event.score == null ? "\u2014" : String(event.score);
|
|
31241
|
+
const tok = event.tokensThisRound >= 1e3 ? `${Math.round(event.tokensThisRound / 1e3)}k` : String(event.tokensThisRound);
|
|
31242
|
+
const total = event.tokensTotal >= 1e3 ? `${Math.round(event.tokensTotal / 1e3)}k` : String(event.tokensTotal);
|
|
31243
|
+
return `craft ${event.round}/${event.max} \xB7 score ${score} \xB7 ${tok} tok this round \xB7 ${total} total`;
|
|
31244
|
+
}
|
|
31245
|
+
var CRITIC_MAX_TOKENS, REVISE_MAX_TOKENS, DEFAULT_CRAFT_MAX_ROUNDS, DEFAULT_CRAFT_MIN_SCORE;
|
|
31246
|
+
var init_strategist_craft = __esm({
|
|
31247
|
+
"src/ai/strategist-craft.ts"() {
|
|
31248
|
+
"use strict";
|
|
31249
|
+
init_failover();
|
|
31250
|
+
init_repl_api();
|
|
31251
|
+
init_context2();
|
|
31252
|
+
init_strategist();
|
|
31253
|
+
init_strategist_validate();
|
|
31254
|
+
init_strategist_rubric();
|
|
31255
|
+
init_strategist_prompt();
|
|
31256
|
+
init_store3();
|
|
31257
|
+
CRITIC_MAX_TOKENS = 2048;
|
|
31258
|
+
REVISE_MAX_TOKENS = 8192;
|
|
31259
|
+
DEFAULT_CRAFT_MAX_ROUNDS = 5;
|
|
31260
|
+
DEFAULT_CRAFT_MIN_SCORE = 85;
|
|
31261
|
+
}
|
|
31262
|
+
});
|
|
31263
|
+
|
|
31264
|
+
// src/services/strategist.ts
|
|
31265
|
+
import { createHash as createHash3 } from "crypto";
|
|
31266
|
+
function serializeGapAudit(audit) {
|
|
31267
|
+
const lines = [`Can compute: ${audit.can_compute} (lens: ${audit.primary_lens})`];
|
|
31268
|
+
for (const item of audit.satisfied) {
|
|
31269
|
+
lines.push(`- HAVE ${item.label}: ${item.detail}`);
|
|
31270
|
+
}
|
|
31271
|
+
for (const item of audit.missing) {
|
|
31272
|
+
lines.push(`- MISSING ${item.label}: ${item.why}`);
|
|
31273
|
+
}
|
|
31274
|
+
for (const item of audit.optional) {
|
|
31275
|
+
lines.push(`- LIMITED ${item.label}: ${item.detail}`);
|
|
31276
|
+
}
|
|
31277
|
+
return lines.join("\n");
|
|
31278
|
+
}
|
|
31279
|
+
async function prepareStrategistInputs(ctx, objective) {
|
|
31280
|
+
let snapshot = ctx.snapshot.computeResult;
|
|
31281
|
+
if (!snapshot) {
|
|
31282
|
+
snapshot = await computeFullHealth();
|
|
31283
|
+
ctx.snapshot.computeResult = snapshot;
|
|
31284
|
+
const divInput = snapshot.segments.map((s) => ({
|
|
31285
|
+
segmentId: s.segment.id,
|
|
31286
|
+
segmentName: s.segment.name,
|
|
31287
|
+
result: s.result
|
|
31288
|
+
}));
|
|
31289
|
+
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
31290
|
+
}
|
|
31291
|
+
const audit = ctx.gapAudit ?? await refreshGapAudit(ctx).catch(() => null);
|
|
31292
|
+
const gapAuditBlock = audit ? serializeGapAudit(audit) : "";
|
|
31293
|
+
const memoryBlock = await Promise.resolve().then(() => (init_store2(), store_exports2)).then((m) => m.buildMemoryBlock(objective)).catch(() => "");
|
|
31294
|
+
let baselineBatchId = null;
|
|
31295
|
+
try {
|
|
31296
|
+
const reading = await getLatestHealthReading();
|
|
31297
|
+
baselineBatchId = reading?.upload_batch_id ?? null;
|
|
31298
|
+
} catch {
|
|
31299
|
+
}
|
|
31300
|
+
return {
|
|
31301
|
+
snapshot,
|
|
31302
|
+
divergences: ctx.snapshot.divergences,
|
|
31303
|
+
gapAuditBlock,
|
|
31304
|
+
memoryBlock,
|
|
31305
|
+
baselineBatchId,
|
|
31306
|
+
includeMetrics: true
|
|
31307
|
+
};
|
|
31308
|
+
}
|
|
31309
|
+
function proposeObjectiveFromSnapshot(snapshot) {
|
|
31310
|
+
const { aggregate } = snapshot;
|
|
31311
|
+
const gating = aggregate.gating_vital_sign;
|
|
31312
|
+
if (!gating) return null;
|
|
31313
|
+
const vital = aggregate.vital_signs.find((v) => v.vital_sign === gating);
|
|
31314
|
+
if (!vital) return null;
|
|
31315
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
31316
|
+
const dollar = vital.dollar_value != null && vital.dollar_value > 0 ? ` and recover the ${formatCurrency(vital.dollar_value)} ${vital.dollar_label ?? "at stake"}` : "";
|
|
31317
|
+
return `Move ${label} from ${Math.round(vital.score)} to 60+${dollar} within 60 days`;
|
|
31318
|
+
}
|
|
31319
|
+
function slugify2(value) {
|
|
31320
|
+
const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
31321
|
+
return slug || `strategy-${Date.now()}`;
|
|
31322
|
+
}
|
|
31323
|
+
function outcomeToMetric(outcome) {
|
|
31324
|
+
return {
|
|
31325
|
+
name: outcome.metric,
|
|
31326
|
+
target: outcome.target_range,
|
|
31327
|
+
baseline: outcome.baseline,
|
|
31328
|
+
timeframe: `by ${outcome.check_date}`
|
|
31329
|
+
};
|
|
31330
|
+
}
|
|
31331
|
+
async function persistStrategistPlan(plan, opts = {}) {
|
|
31332
|
+
await initSchema();
|
|
31333
|
+
const slug = slugify2(plan.title);
|
|
31334
|
+
const libraryPath = strategyLibraryPath(slug);
|
|
31335
|
+
const linkedPlayIds = [...new Set(plan.workstreams.flatMap((ws) => ws.play_ids))];
|
|
31336
|
+
const successMetrics = plan.workstreams.map((ws) => outcomeToMetric(ws.expected_outcome));
|
|
31337
|
+
const leadingIndicators = plan.workstreams.flatMap((ws) => ws.leading_indicators.map(outcomeToMetric));
|
|
31338
|
+
const recommendedActions = plan.workstreams.flatMap((ws) => ws.actions.map((action) => `[WS${ws.order}] ${action}`)).slice(0, 15);
|
|
31339
|
+
const reviewProtocol = [
|
|
31340
|
+
`Review ${plan.review_cadence.toLowerCase()} with /strategy review ${slug}.`,
|
|
31341
|
+
`Check each milestone at its due date against the named verification method.`,
|
|
31342
|
+
`At each outcome check date, compare the measured value to its target range against baseline batch ${opts.baselineBatchId ?? "(latest)"}.`,
|
|
31343
|
+
`If a contingency trigger fires, activate the pre-agreed fallback.`
|
|
31344
|
+
].join(" ");
|
|
31345
|
+
const id = await upsertStrategy({
|
|
31346
|
+
slug,
|
|
31347
|
+
title: plan.title,
|
|
31348
|
+
status: opts.status ?? "active",
|
|
31349
|
+
source_type: "agent",
|
|
31350
|
+
source_path: null,
|
|
31351
|
+
goal: plan.objective,
|
|
31352
|
+
hypothesis: plan.hypothesis,
|
|
31353
|
+
target_segment: plan.target_segment,
|
|
31354
|
+
priority: plan.priority,
|
|
31355
|
+
linked_play_ids: linkedPlayIds,
|
|
31356
|
+
success_metrics: successMetrics,
|
|
31357
|
+
leading_indicators: leadingIndicators,
|
|
31358
|
+
risks: plan.risks,
|
|
31359
|
+
recommended_actions: recommendedActions,
|
|
31360
|
+
experiment_design: reviewProtocol,
|
|
31361
|
+
review_cadence: plan.review_cadence,
|
|
31362
|
+
confidence: plan.confidence,
|
|
31363
|
+
raw_excerpt: plan.summary_30k,
|
|
31364
|
+
library_path: libraryPath,
|
|
31365
|
+
origin: "strategist",
|
|
31366
|
+
objective: plan.objective,
|
|
31367
|
+
constraints: plan.constraints,
|
|
31368
|
+
workstreams: plan.workstreams,
|
|
31369
|
+
assumptions: plan.assumptions,
|
|
31370
|
+
baseline_batch_id: opts.baselineBatchId ?? null
|
|
31371
|
+
});
|
|
31372
|
+
const strategy = await getStrategyBySlugOrId(id);
|
|
31373
|
+
if (!strategy) {
|
|
31374
|
+
throw new NtrpError("strategy_persist_failed", "Strategy was not found after saving.", 1 /* RuntimeError */);
|
|
31375
|
+
}
|
|
31376
|
+
const extras = {
|
|
31377
|
+
craftLogPath: opts.craftLogPath,
|
|
31378
|
+
constraintLine: opts.constraintLine,
|
|
31379
|
+
killedAlternative: opts.killedAlternative,
|
|
31380
|
+
outOfScope: opts.outOfScope
|
|
31381
|
+
};
|
|
31382
|
+
const writtenPath = writeStrategyMarkdown(strategy, extras);
|
|
31383
|
+
await insertStrategySource({
|
|
31384
|
+
strategy_id: strategy.id,
|
|
31385
|
+
source_type: "agent",
|
|
31386
|
+
source_path: null,
|
|
31387
|
+
content_hash: createHash3("sha256").update(JSON.stringify(plan)).digest("hex"),
|
|
31388
|
+
extracted_text_excerpt: plan.summary_30k.slice(0, 800),
|
|
31389
|
+
metadata: {
|
|
31390
|
+
origin: "strategist",
|
|
31391
|
+
objective: plan.objective,
|
|
31392
|
+
workstream_count: plan.workstreams.length,
|
|
31393
|
+
baseline_batch_id: opts.baselineBatchId ?? null
|
|
31394
|
+
}
|
|
31395
|
+
});
|
|
31396
|
+
return { strategy: { ...strategy, library_path: writtenPath }, library_path: writtenPath };
|
|
31397
|
+
}
|
|
31398
|
+
var init_strategist2 = __esm({
|
|
31399
|
+
"src/services/strategist.ts"() {
|
|
31400
|
+
"use strict";
|
|
31401
|
+
init_schema();
|
|
31402
|
+
init_queries();
|
|
31403
|
+
init_health_score();
|
|
31404
|
+
init_divergence();
|
|
31405
|
+
init_gap_audit();
|
|
31406
|
+
init_library();
|
|
31407
|
+
init_errors2();
|
|
31408
|
+
init_types2();
|
|
31409
|
+
init_formatters();
|
|
31410
|
+
}
|
|
31411
|
+
});
|
|
31412
|
+
|
|
31413
|
+
// src/ruminations/handoff.ts
|
|
31414
|
+
function renderCraftHandoffMarkdown(opts) {
|
|
31415
|
+
const { plan } = opts;
|
|
31416
|
+
const lines = [];
|
|
31417
|
+
lines.push("# Action plan \u2014 NTRP craft");
|
|
31418
|
+
lines.push("");
|
|
31419
|
+
lines.push("Use this file as a prompt for another agent or as a morning brief.");
|
|
31420
|
+
lines.push("");
|
|
31421
|
+
lines.push("## The Call");
|
|
31422
|
+
lines.push("");
|
|
31423
|
+
lines.push(plan.summary_30k);
|
|
31424
|
+
lines.push("");
|
|
31425
|
+
lines.push("## Locked objective");
|
|
31426
|
+
lines.push("");
|
|
31427
|
+
lines.push(plan.objective);
|
|
31428
|
+
lines.push("");
|
|
31429
|
+
lines.push(renderConstraintHeading(opts.constraintLine).trimEnd());
|
|
31430
|
+
lines.push("");
|
|
31431
|
+
lines.push(renderScopeHeading(plan.constraints, opts.outOfScope).trimEnd());
|
|
31432
|
+
lines.push("");
|
|
31433
|
+
lines.push("## Hypothesis");
|
|
31434
|
+
lines.push("");
|
|
31435
|
+
lines.push(plan.hypothesis);
|
|
31436
|
+
lines.push("");
|
|
31437
|
+
lines.push(renderKilledAlternativeLine(opts.killedAlternative));
|
|
31438
|
+
lines.push("");
|
|
31439
|
+
lines.push(renderEffortHeading(plan.workstreams).trimEnd());
|
|
31440
|
+
lines.push("");
|
|
31441
|
+
lines.push("## Workstreams");
|
|
31442
|
+
lines.push("");
|
|
31443
|
+
for (const ws of plan.workstreams) {
|
|
31444
|
+
lines.push(`### ${ws.order}. ${ws.title}`);
|
|
31445
|
+
lines.push("");
|
|
31446
|
+
lines.push(`- Problem: ${ws.problem}`);
|
|
31447
|
+
lines.push(`- Why this order: ${ws.rationale}`);
|
|
31448
|
+
if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
|
|
31449
|
+
lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
|
|
31450
|
+
lines.push(
|
|
31451
|
+
`- Exam: ${ws.expected_outcome.metric} ${ws.expected_outcome.baseline} -> ${ws.expected_outcome.target_range} by ${ws.expected_outcome.check_date} (${ws.expected_outcome.measured_by})`
|
|
31452
|
+
);
|
|
31453
|
+
lines.push(
|
|
31454
|
+
`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) -> ${ws.contingency.fallback}`
|
|
31455
|
+
);
|
|
31456
|
+
lines.push("");
|
|
31457
|
+
}
|
|
31458
|
+
if (plan.risks.length > 0) {
|
|
31459
|
+
lines.push("## Risks");
|
|
31460
|
+
lines.push("");
|
|
31461
|
+
for (const r of plan.risks) lines.push(`- ${r}`);
|
|
31462
|
+
lines.push("");
|
|
31463
|
+
}
|
|
31464
|
+
if (plan.assumptions.length > 0) {
|
|
31465
|
+
lines.push("## Assumptions");
|
|
31466
|
+
lines.push("");
|
|
31467
|
+
for (const a of plan.assumptions) lines.push(`- ${a}`);
|
|
31468
|
+
lines.push("");
|
|
31469
|
+
}
|
|
31470
|
+
lines.push(renderReviewHeading({ cadence: plan.review_cadence, slug: opts.slug }).trimEnd());
|
|
31471
|
+
lines.push("");
|
|
31472
|
+
if (opts.journalPath) {
|
|
31473
|
+
lines.push("## Craft log");
|
|
31474
|
+
lines.push("");
|
|
31475
|
+
lines.push(`Status: ${opts.status}`);
|
|
31476
|
+
lines.push(`Log: ${opts.journalPath}`);
|
|
31477
|
+
if (opts.libraryPath) lines.push(`Strategy library: ${opts.libraryPath}`);
|
|
31478
|
+
lines.push("");
|
|
31479
|
+
} else if (opts.libraryPath) {
|
|
31480
|
+
lines.push("## Strategy library");
|
|
31481
|
+
lines.push("");
|
|
31482
|
+
lines.push(opts.libraryPath);
|
|
31483
|
+
lines.push("");
|
|
31484
|
+
}
|
|
31485
|
+
return lines.join("\n");
|
|
31486
|
+
}
|
|
31487
|
+
function writeCraftPlanHandoff(opts) {
|
|
31488
|
+
const journalPath = opts.jobId ? ruminationLogPath(opts.jobId) : void 0;
|
|
31489
|
+
const markdown = renderCraftHandoffMarkdown({
|
|
31490
|
+
plan: opts.plan,
|
|
31491
|
+
journalPath,
|
|
31492
|
+
libraryPath: opts.libraryPath,
|
|
31493
|
+
status: opts.status,
|
|
31494
|
+
constraintLine: opts.constraintLine,
|
|
31495
|
+
killedAlternative: opts.killedAlternative,
|
|
31496
|
+
outOfScope: opts.outOfScope,
|
|
31497
|
+
slug: opts.slug
|
|
31498
|
+
});
|
|
31499
|
+
const path = resolveArchivePath("prompt:plan", `handoff-plan-${exportStamp()}.md`);
|
|
31500
|
+
writeRedactedText(path, markdown);
|
|
31501
|
+
const event = recordExportWrite({
|
|
31502
|
+
kind: "prompt:plan",
|
|
31503
|
+
path,
|
|
31504
|
+
sessionId: opts.sessionId,
|
|
31505
|
+
title: `${opts.plan.title} craft plan`
|
|
31506
|
+
});
|
|
31507
|
+
return { path, inboxPath: event.inbox_path };
|
|
31508
|
+
}
|
|
31509
|
+
var init_handoff2 = __esm({
|
|
31510
|
+
"src/ruminations/handoff.ts"() {
|
|
31511
|
+
"use strict";
|
|
31512
|
+
init_exports_registry();
|
|
31513
|
+
init_redact_write();
|
|
31514
|
+
init_library();
|
|
31515
|
+
init_store3();
|
|
31516
|
+
}
|
|
31517
|
+
});
|
|
31518
|
+
|
|
31519
|
+
// src/services/strategist-run.ts
|
|
31520
|
+
async function assertComputableData() {
|
|
31521
|
+
await initSchema();
|
|
31522
|
+
let counts = {};
|
|
31523
|
+
try {
|
|
31524
|
+
counts = await getEntityCounts();
|
|
31525
|
+
} catch {
|
|
31526
|
+
counts = {};
|
|
31527
|
+
}
|
|
31528
|
+
if (!Object.values(counts).some((n) => (n ?? 0) > 0)) {
|
|
31529
|
+
throw new NtrpError(
|
|
31530
|
+
"strategy_no_data",
|
|
31531
|
+
"No pipeline data loaded. Ingest a CSV, run demo data, then craft a plan.",
|
|
31532
|
+
4 /* NoData */
|
|
31533
|
+
);
|
|
31534
|
+
}
|
|
31535
|
+
}
|
|
31536
|
+
function rememberCraftJob(ctx, job) {
|
|
31537
|
+
if (!job) return;
|
|
31538
|
+
ctx.lastCraftJobId = job.status === "ready" ? void 0 : job.id;
|
|
31539
|
+
saveSessionState(ctx);
|
|
31540
|
+
}
|
|
31541
|
+
async function executeStrategistJob(req) {
|
|
31542
|
+
if (!canUseReplAi(req.ctx)) {
|
|
31543
|
+
throw new NtrpError(
|
|
31544
|
+
"strategy_no_key",
|
|
31545
|
+
"No LLM API key configured. Run /connect and paste a key, then retry craft.",
|
|
31546
|
+
3 /* Auth */
|
|
31547
|
+
);
|
|
31548
|
+
}
|
|
31549
|
+
const resumeJob = req.resumeId ? loadRuminationJob(req.resumeId) : null;
|
|
31550
|
+
if (req.resumeId && !resumeJob) {
|
|
31551
|
+
throw new NtrpError(
|
|
31552
|
+
"strategy_resume_missing",
|
|
31553
|
+
`No craft job "${req.resumeId}". Check ~/.ntrp/ruminations/.`,
|
|
31554
|
+
2 /* Usage */
|
|
31555
|
+
);
|
|
31556
|
+
}
|
|
31557
|
+
if (resumeJob) rememberCraftJob(req.ctx, resumeJob);
|
|
31558
|
+
const objective = resumeJob?.objective || req.objective;
|
|
31559
|
+
if (!objective.trim()) {
|
|
31560
|
+
throw new NtrpError(
|
|
31561
|
+
"strategy_objective_required",
|
|
31562
|
+
'strategy craft requires an objective. Example: ntrp strategy craft "cut stale pipeline before Q4"',
|
|
31563
|
+
2 /* Usage */
|
|
31564
|
+
);
|
|
31565
|
+
}
|
|
31566
|
+
await assertComputableData();
|
|
31567
|
+
const inputs = await prepareStrategistInputs(req.ctx, objective);
|
|
31568
|
+
const notices = [];
|
|
31569
|
+
let plan = resumeJob?.plan ?? null;
|
|
31570
|
+
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
31571
|
+
let meta = {};
|
|
31572
|
+
let job = resumeJob ?? void 0;
|
|
31573
|
+
const sessionOpts = {
|
|
31574
|
+
objective,
|
|
31575
|
+
computeResult: inputs.snapshot,
|
|
31576
|
+
divergences: inputs.divergences,
|
|
31577
|
+
includeMetrics: inputs.includeMetrics,
|
|
31578
|
+
memoryBlock: inputs.memoryBlock,
|
|
31579
|
+
gapAuditBlock: inputs.gapAuditBlock,
|
|
31580
|
+
constraintsNote: req.constraintsNote ?? resumeJob?.constraints_note,
|
|
31581
|
+
baselineBatchId: inputs.baselineBatchId,
|
|
31582
|
+
ctx: req.ctx
|
|
31583
|
+
};
|
|
31584
|
+
const useCraft = req.mode === "craft" && req.untilReady !== false;
|
|
31585
|
+
const handleEvent = (event) => {
|
|
31586
|
+
req.onEvent?.(event);
|
|
31587
|
+
if (event.type === "notice") notices.push(event.text);
|
|
31588
|
+
if (event.type === "plan") {
|
|
31589
|
+
plan = event.plan;
|
|
31590
|
+
stats = {
|
|
31591
|
+
measurable_targets: event.measurable_targets,
|
|
31592
|
+
total_targets: event.total_targets
|
|
31593
|
+
};
|
|
31594
|
+
}
|
|
31595
|
+
if (event.type === "done") {
|
|
31596
|
+
meta = event.usage ?? { model_used: event.model_used, provider_used: event.provider_used };
|
|
31597
|
+
}
|
|
31598
|
+
if (event.type === "craft_round") {
|
|
31599
|
+
notices.push(formatCraftRoundLine(event));
|
|
31600
|
+
}
|
|
31601
|
+
};
|
|
31602
|
+
if (useCraft) {
|
|
31603
|
+
const gen = strategistCraftSession({
|
|
31604
|
+
...sessionOpts,
|
|
31605
|
+
maxRounds: req.maxRounds ?? resumeJob?.max_rounds ?? DEFAULT_CRAFT_MAX_ROUNDS,
|
|
31606
|
+
maxTokens: req.maxTokens ?? resumeJob?.max_tokens,
|
|
31607
|
+
minScore: req.minScore ?? resumeJob?.min_score ?? DEFAULT_CRAFT_MIN_SCORE,
|
|
31608
|
+
job: resumeJob ?? void 0,
|
|
31609
|
+
interrupted: req.interrupted
|
|
31610
|
+
});
|
|
31611
|
+
let next = await gen.next();
|
|
31612
|
+
while (!next.done) {
|
|
31613
|
+
handleEvent(next.value);
|
|
31614
|
+
next = await gen.next();
|
|
31615
|
+
}
|
|
31616
|
+
job = next.value.job;
|
|
31617
|
+
plan = next.value.plan;
|
|
31618
|
+
} else {
|
|
31619
|
+
for await (const event of strategistPlanSession(sessionOpts)) {
|
|
31620
|
+
handleEvent(event);
|
|
31621
|
+
}
|
|
31622
|
+
}
|
|
31623
|
+
const blocking = job?.rubric_gaps.filter((g) => g.severity === "blocking").map((g) => g.code) ?? [];
|
|
31624
|
+
const status = job?.status === "unfinished" || !plan ? "unfinished" : "ready";
|
|
31625
|
+
const result = {
|
|
31626
|
+
plan,
|
|
31627
|
+
status,
|
|
31628
|
+
stop_reason: job?.stop_reason,
|
|
31629
|
+
objective,
|
|
31630
|
+
critic_score: job?.last_critic?.score ?? null,
|
|
31631
|
+
rounds: job?.iterations.length ?? 0,
|
|
31632
|
+
usage: meta,
|
|
31633
|
+
journal_path: job ? ruminationLogPath(job.id) : void 0,
|
|
31634
|
+
blocking_gaps: blocking,
|
|
31635
|
+
measurable_targets: stats.measurable_targets,
|
|
31636
|
+
total_targets: stats.total_targets,
|
|
31637
|
+
notices,
|
|
31638
|
+
job
|
|
31639
|
+
};
|
|
31640
|
+
const constraintLine = formatConstraintLine(inputs.snapshot.aggregate);
|
|
31641
|
+
const killedAlternative = job?.last_critic?.killed_alternative;
|
|
31642
|
+
if (plan && req.save) {
|
|
31643
|
+
const persisted = await persistStrategistPlan(plan, {
|
|
31644
|
+
baselineBatchId: inputs.baselineBatchId,
|
|
31645
|
+
craftLogPath: job ? ruminationLogPath(job.id) : void 0,
|
|
31646
|
+
constraintLine,
|
|
31647
|
+
killedAlternative
|
|
31648
|
+
});
|
|
31649
|
+
result.slug = persisted.strategy.slug;
|
|
31650
|
+
result.library_path = persisted.library_path;
|
|
31651
|
+
req.ctx.deliverables.push({
|
|
31652
|
+
kind: "strategy",
|
|
31653
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
31654
|
+
path: persisted.library_path,
|
|
31655
|
+
note: plan.title
|
|
31656
|
+
});
|
|
31657
|
+
if (job) {
|
|
31658
|
+
job.library_path = persisted.library_path;
|
|
31659
|
+
}
|
|
31660
|
+
}
|
|
31661
|
+
if (plan && req.handoff) {
|
|
31662
|
+
const written = writeCraftPlanHandoff({
|
|
31663
|
+
plan,
|
|
31664
|
+
jobId: job?.id,
|
|
31665
|
+
status: result.status,
|
|
31666
|
+
libraryPath: result.library_path,
|
|
31667
|
+
sessionId: req.ctx.sessionId,
|
|
31668
|
+
constraintLine,
|
|
31669
|
+
killedAlternative,
|
|
31670
|
+
slug: result.slug
|
|
31671
|
+
});
|
|
31672
|
+
result.handoff_path = written.path;
|
|
31673
|
+
result.inbox_path = written.inboxPath;
|
|
31674
|
+
if (job) {
|
|
31675
|
+
job.handoff_path = written.path;
|
|
31676
|
+
job.inbox_path = written.inboxPath;
|
|
31677
|
+
}
|
|
31678
|
+
req.ctx.deliverables.push({
|
|
31679
|
+
kind: "prompt:plan",
|
|
31680
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
31681
|
+
path: written.path,
|
|
31682
|
+
note: plan.title
|
|
31683
|
+
});
|
|
31684
|
+
}
|
|
31685
|
+
if (job) {
|
|
31686
|
+
if (req.ctx.strategistState) req.ctx.strategistState.ruminationId = job.id;
|
|
31687
|
+
saveRuminationJob(job);
|
|
31688
|
+
rememberCraftJob(req.ctx, job);
|
|
31689
|
+
}
|
|
31690
|
+
return result;
|
|
31691
|
+
}
|
|
31692
|
+
var init_strategist_run = __esm({
|
|
31693
|
+
"src/services/strategist-run.ts"() {
|
|
31694
|
+
"use strict";
|
|
31695
|
+
init_context2();
|
|
31696
|
+
init_errors2();
|
|
31697
|
+
init_types2();
|
|
31698
|
+
init_repl_api();
|
|
31699
|
+
init_strategist();
|
|
31700
|
+
init_strategist_craft();
|
|
31701
|
+
init_formatters();
|
|
31702
|
+
init_strategist2();
|
|
31703
|
+
init_queries();
|
|
31704
|
+
init_schema();
|
|
31705
|
+
init_store3();
|
|
31706
|
+
init_handoff2();
|
|
31707
|
+
}
|
|
31708
|
+
});
|
|
31709
|
+
|
|
30433
31710
|
// src/output/strategy-brief.ts
|
|
30434
31711
|
import chalk36 from "chalk";
|
|
30435
31712
|
function printWrapped(text, width, prefix = INDENT, style) {
|
|
@@ -30521,6 +31798,40 @@ function printStrategyBrief(plan, stats) {
|
|
|
30521
31798
|
console.log(`${INDENT}${coverageStyled}${chalk36.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
|
|
30522
31799
|
console.log();
|
|
30523
31800
|
}
|
|
31801
|
+
function printCraftWrapUp(opts) {
|
|
31802
|
+
console.log();
|
|
31803
|
+
if (opts.status === "ready") {
|
|
31804
|
+
console.log(INDENT + paint("accent", "Plan ready"));
|
|
31805
|
+
if (opts.library_path) console.log(INDENT + chalk36.dim(opts.library_path));
|
|
31806
|
+
if (opts.slug) {
|
|
31807
|
+
console.log(
|
|
31808
|
+
INDENT + chalk36.dim("Type ") + paint("accent", `/strategy review ${opts.slug}`) + chalk36.dim(" to check progress.")
|
|
31809
|
+
);
|
|
31810
|
+
}
|
|
31811
|
+
const handoff = opts.inbox_path ?? opts.handoff_path;
|
|
31812
|
+
if (handoff) console.log(INDENT + chalk36.dim(handoff));
|
|
31813
|
+
} else {
|
|
31814
|
+
console.log(INDENT + paint("accent", "Best so far."));
|
|
31815
|
+
const path = opts.library_path ?? opts.handoff_path ?? opts.inbox_path;
|
|
31816
|
+
if (path) console.log(INDENT + chalk36.dim(path));
|
|
31817
|
+
if (opts.oneShot && opts.jobId) {
|
|
31818
|
+
console.log(
|
|
31819
|
+
INDENT + chalk36.dim("Resume: ") + paint("accent", `ntrp strategy craft --resume ${opts.jobId}`)
|
|
31820
|
+
);
|
|
31821
|
+
} else {
|
|
31822
|
+
console.log(
|
|
31823
|
+
INDENT + chalk36.dim("Type ") + chalk36.cyan("keep going") + chalk36.dim(" to continue.")
|
|
31824
|
+
);
|
|
31825
|
+
}
|
|
31826
|
+
}
|
|
31827
|
+
if (opts.oneShot && opts.journal_path) {
|
|
31828
|
+
console.log(INDENT + chalk36.dim(`Craft log: ${opts.journal_path}`));
|
|
31829
|
+
}
|
|
31830
|
+
console.log();
|
|
31831
|
+
}
|
|
31832
|
+
function isCraftRoundNotice(text) {
|
|
31833
|
+
return /^craft \d+\/\d+/.test(text);
|
|
31834
|
+
}
|
|
30524
31835
|
var INDENT;
|
|
30525
31836
|
var init_strategy_brief = __esm({
|
|
30526
31837
|
"src/output/strategy-brief.ts"() {
|
|
@@ -30535,7 +31846,9 @@ var init_strategy_brief = __esm({
|
|
|
30535
31846
|
var strategist_flow_exports = {};
|
|
30536
31847
|
__export(strategist_flow_exports, {
|
|
30537
31848
|
extractObjectiveSeed: () => extractObjectiveSeed,
|
|
31849
|
+
handleKeepGoingLine: () => handleKeepGoingLine,
|
|
30538
31850
|
handleStrategizeFlow: () => handleStrategizeFlow,
|
|
31851
|
+
isKeepGoingIntent: () => isKeepGoingIntent,
|
|
30539
31852
|
isStrategistIntent: () => isStrategistIntent,
|
|
30540
31853
|
promptQueuedAiStrategist: () => promptQueuedAiStrategist,
|
|
30541
31854
|
queueStrategistForAnalysis: () => queueStrategistForAnalysis,
|
|
@@ -30558,7 +31871,8 @@ function queueStrategistForAnalysis(ctx, opts) {
|
|
|
30558
31871
|
ctx.strategistState = {
|
|
30559
31872
|
step: "awaiting_analysis",
|
|
30560
31873
|
objective: opts.seed,
|
|
30561
|
-
origin: opts.origin
|
|
31874
|
+
origin: opts.origin,
|
|
31875
|
+
mode: opts.mode
|
|
30562
31876
|
};
|
|
30563
31877
|
saveSessionState(ctx);
|
|
30564
31878
|
console.log();
|
|
@@ -30583,7 +31897,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
30583
31897
|
objective = (snapshot ? proposeObjectiveFromSnapshot(snapshot) : null) ?? "";
|
|
30584
31898
|
}
|
|
30585
31899
|
if (!objective) {
|
|
30586
|
-
ctx.strategistState = { step: "objective_input", origin: opts.origin };
|
|
31900
|
+
ctx.strategistState = { step: "objective_input", origin: opts.origin, mode: opts.mode };
|
|
30587
31901
|
saveSessionState(ctx);
|
|
30588
31902
|
console.log();
|
|
30589
31903
|
console.log(" " + chalk37.dim('What is the objective? State a finish line. Example: "cut stale pipeline in half before Q4".'));
|
|
@@ -30591,7 +31905,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
30591
31905
|
recordMessage(ctx, "agent", "Strategist: asked for objective");
|
|
30592
31906
|
return "Awaiting objective";
|
|
30593
31907
|
}
|
|
30594
|
-
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin };
|
|
31908
|
+
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin, mode: opts.mode };
|
|
30595
31909
|
saveSessionState(ctx);
|
|
30596
31910
|
printObjectiveCard(ctx, objective, !opts.seed);
|
|
30597
31911
|
recordMessage(ctx, "agent", `Strategist objective proposed: ${objective}`);
|
|
@@ -30604,7 +31918,8 @@ async function resumeStrategistAfterCompute(ctx) {
|
|
|
30604
31918
|
console.log(" " + paint("accent", "Analysis is ready. The strategy session continues."));
|
|
30605
31919
|
await startStrategistFlow(ctx, {
|
|
30606
31920
|
seed: state2.objective,
|
|
30607
|
-
origin: state2.origin ?? "nl"
|
|
31921
|
+
origin: state2.origin ?? "nl",
|
|
31922
|
+
mode: state2.mode
|
|
30608
31923
|
});
|
|
30609
31924
|
}
|
|
30610
31925
|
function promptQueuedAiStrategist(ctx) {
|
|
@@ -30614,6 +31929,64 @@ function promptQueuedAiStrategist(ctx) {
|
|
|
30614
31929
|
}
|
|
30615
31930
|
printObjectiveCard(ctx, state2.objective, true);
|
|
30616
31931
|
}
|
|
31932
|
+
function isKeepGoingIntent(input) {
|
|
31933
|
+
return CRAFT_RE.test(input.trim());
|
|
31934
|
+
}
|
|
31935
|
+
async function handleKeepGoingLine(ctx, input) {
|
|
31936
|
+
const line = input.trim();
|
|
31937
|
+
recordMessage(ctx, "user", line);
|
|
31938
|
+
const job = unfinishedCraftJob(ctx.lastCraftJobId);
|
|
31939
|
+
if (!job) {
|
|
31940
|
+
console.log();
|
|
31941
|
+
console.log(
|
|
31942
|
+
" " + chalk37.dim("No plan in progress. Type ") + chalk37.cyan("how should we fix this?") + chalk37.dim(" to start one.")
|
|
31943
|
+
);
|
|
31944
|
+
console.log();
|
|
31945
|
+
recordMessage(ctx, "agent", "No plan in progress");
|
|
31946
|
+
return "No plan in progress";
|
|
31947
|
+
}
|
|
31948
|
+
if (!canUseReplAi(ctx)) {
|
|
31949
|
+
console.log();
|
|
31950
|
+
console.log(
|
|
31951
|
+
" " + chalk37.dim("Type ") + chalk37.cyan("/connect") + chalk37.dim(" to connect a key. Then type keep going.")
|
|
31952
|
+
);
|
|
31953
|
+
console.log();
|
|
31954
|
+
recordMessage(ctx, "agent", "Keep going needs a key");
|
|
31955
|
+
return "Keep going needs a key";
|
|
31956
|
+
}
|
|
31957
|
+
const spinner = makeSpinner("Working the plan\u2026");
|
|
31958
|
+
try {
|
|
31959
|
+
const result = await executeStrategistJob({
|
|
31960
|
+
ctx,
|
|
31961
|
+
objective: job.objective,
|
|
31962
|
+
constraintsNote: job.constraints_note,
|
|
31963
|
+
mode: "craft",
|
|
31964
|
+
untilReady: true,
|
|
31965
|
+
save: true,
|
|
31966
|
+
handoff: true,
|
|
31967
|
+
resumeId: job.id,
|
|
31968
|
+
onEvent: (event) => {
|
|
31969
|
+
if (event.type === "thinking") {
|
|
31970
|
+
spinner.stop();
|
|
31971
|
+
console.log(" " + chalk37.dim.italic(event.text));
|
|
31972
|
+
spinner.start();
|
|
31973
|
+
spinner.text = "Working the plan\u2026";
|
|
31974
|
+
}
|
|
31975
|
+
}
|
|
31976
|
+
});
|
|
31977
|
+
spinner.stop();
|
|
31978
|
+
printCraftReplResult(ctx, result, job.objective);
|
|
31979
|
+
ctx.strategistState = void 0;
|
|
31980
|
+
saveSessionState(ctx);
|
|
31981
|
+
return result.status === "ready" ? `Strategy crafted: ${result.plan?.title}` : "Strategy craft unfinished";
|
|
31982
|
+
} catch (err) {
|
|
31983
|
+
spinner.fail("Strategy craft failed");
|
|
31984
|
+
console.error(" " + chalk37.red(String(err.message ?? err)));
|
|
31985
|
+
ctx.strategistState = void 0;
|
|
31986
|
+
saveSessionState(ctx);
|
|
31987
|
+
return;
|
|
31988
|
+
}
|
|
31989
|
+
}
|
|
30617
31990
|
async function handleStrategizeFlow(input, ctx) {
|
|
30618
31991
|
const state2 = ctx.strategistState;
|
|
30619
31992
|
if (!state2) return;
|
|
@@ -30640,6 +32013,11 @@ async function handleStrategizeFlow(input, ctx) {
|
|
|
30640
32013
|
printObjectiveCard(ctx, state2.objective, false);
|
|
30641
32014
|
return "Objective proposed";
|
|
30642
32015
|
}
|
|
32016
|
+
if (CRAFT_RE.test(line)) {
|
|
32017
|
+
state2.mode = "craft";
|
|
32018
|
+
saveSessionState(ctx);
|
|
32019
|
+
return runStrategistSession(ctx);
|
|
32020
|
+
}
|
|
30643
32021
|
if (CONFIRM_RE.test(line)) {
|
|
30644
32022
|
return runStrategistSession(ctx);
|
|
30645
32023
|
}
|
|
@@ -30690,7 +32068,7 @@ async function runStrategistSession(ctx) {
|
|
|
30690
32068
|
saveSessionState(ctx);
|
|
30691
32069
|
return "Skeleton plan (awaiting connect)";
|
|
30692
32070
|
}
|
|
30693
|
-
if (ctx.rl && !state2.constraintsNote) {
|
|
32071
|
+
if (ctx.rl && !state2.constraintsNote && state2.mode !== "craft") {
|
|
30694
32072
|
const prompts = createPromptSession(ctx.rl, ctx);
|
|
30695
32073
|
try {
|
|
30696
32074
|
const note = await prompts.ask(
|
|
@@ -30704,7 +32082,39 @@ async function runStrategistSession(ctx) {
|
|
|
30704
32082
|
}
|
|
30705
32083
|
}
|
|
30706
32084
|
console.log();
|
|
30707
|
-
const spinner = makeSpinner("Reading live data\u2026");
|
|
32085
|
+
const spinner = makeSpinner(state2.mode === "craft" ? "Working the plan\u2026" : "Reading live data\u2026");
|
|
32086
|
+
if (state2.mode === "craft") {
|
|
32087
|
+
try {
|
|
32088
|
+
const result = await executeStrategistJob({
|
|
32089
|
+
ctx,
|
|
32090
|
+
objective,
|
|
32091
|
+
constraintsNote: state2.constraintsNote,
|
|
32092
|
+
mode: "craft",
|
|
32093
|
+
untilReady: true,
|
|
32094
|
+
save: true,
|
|
32095
|
+
handoff: true,
|
|
32096
|
+
onEvent: (event) => {
|
|
32097
|
+
if (event.type === "thinking") {
|
|
32098
|
+
spinner.stop();
|
|
32099
|
+
console.log(" " + chalk37.dim.italic(event.text));
|
|
32100
|
+
spinner.start();
|
|
32101
|
+
spinner.text = "Working the plan\u2026";
|
|
32102
|
+
}
|
|
32103
|
+
}
|
|
32104
|
+
});
|
|
32105
|
+
spinner.stop();
|
|
32106
|
+
printCraftReplResult(ctx, result, objective);
|
|
32107
|
+
ctx.strategistState = void 0;
|
|
32108
|
+
saveSessionState(ctx);
|
|
32109
|
+
return result.status === "ready" ? `Strategy crafted: ${result.plan?.title}` : "Strategy craft unfinished";
|
|
32110
|
+
} catch (err) {
|
|
32111
|
+
spinner.fail("Strategy craft failed");
|
|
32112
|
+
console.error(" " + chalk37.red(String(err.message ?? err)));
|
|
32113
|
+
ctx.strategistState = void 0;
|
|
32114
|
+
saveSessionState(ctx);
|
|
32115
|
+
return;
|
|
32116
|
+
}
|
|
32117
|
+
}
|
|
30708
32118
|
let plan = null;
|
|
30709
32119
|
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
30710
32120
|
let baselineBatchId = null;
|
|
@@ -30788,7 +32198,10 @@ async function runStrategistSession(ctx) {
|
|
|
30788
32198
|
}
|
|
30789
32199
|
if (saved) {
|
|
30790
32200
|
try {
|
|
30791
|
-
const persisted = await persistStrategistPlan(plan, {
|
|
32201
|
+
const persisted = await persistStrategistPlan(plan, {
|
|
32202
|
+
baselineBatchId,
|
|
32203
|
+
constraintLine: formatConstraintLine(ctx.snapshot.computeResult?.aggregate)
|
|
32204
|
+
});
|
|
30792
32205
|
ctx.deliverables.push({
|
|
30793
32206
|
kind: "strategy",
|
|
30794
32207
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -30836,19 +32249,54 @@ async function ensureSnapshot(ctx) {
|
|
|
30836
32249
|
return null;
|
|
30837
32250
|
}
|
|
30838
32251
|
}
|
|
32252
|
+
function printCraftReplResult(ctx, result, objective) {
|
|
32253
|
+
if (result.plan) {
|
|
32254
|
+
printStrategyBrief(result.plan, {
|
|
32255
|
+
measurable_targets: result.measurable_targets,
|
|
32256
|
+
total_targets: result.total_targets
|
|
32257
|
+
});
|
|
32258
|
+
} else {
|
|
32259
|
+
console.log(" " + chalk37.dim("(No plan produced)"));
|
|
32260
|
+
}
|
|
32261
|
+
for (const notice of result.notices.slice(0, 8)) {
|
|
32262
|
+
if (!isCraftRoundNotice(notice)) console.log(" " + chalk37.dim(notice));
|
|
32263
|
+
}
|
|
32264
|
+
printLlmAttribution(result.usage);
|
|
32265
|
+
if (result.library_path) creditStrategySession(ctx);
|
|
32266
|
+
printCraftWrapUp({
|
|
32267
|
+
status: result.status,
|
|
32268
|
+
library_path: result.library_path,
|
|
32269
|
+
inbox_path: result.inbox_path,
|
|
32270
|
+
handoff_path: result.handoff_path,
|
|
32271
|
+
slug: result.slug,
|
|
32272
|
+
jobId: result.job?.id,
|
|
32273
|
+
oneShot: false
|
|
32274
|
+
});
|
|
32275
|
+
if (result.library_path || result.handoff_path) {
|
|
32276
|
+
recordMessage(ctx, "agent", `Strategy crafted: ${result.plan?.title ?? objective} (${result.status})`);
|
|
32277
|
+
}
|
|
32278
|
+
}
|
|
30839
32279
|
function printObjectiveCard(ctx, objective, proposed) {
|
|
32280
|
+
const craftMode = ctx.strategistState?.mode === "craft";
|
|
30840
32281
|
console.log();
|
|
30841
32282
|
console.log(" " + chalk37.bold("Strategy session"));
|
|
30842
32283
|
console.log(
|
|
30843
32284
|
" " + chalk37.dim(proposed ? "Proposed objective: " : "Objective: ") + paint("accent", objective)
|
|
30844
32285
|
);
|
|
30845
32286
|
console.log(
|
|
30846
|
-
" " + chalk37.dim(
|
|
32287
|
+
" " + chalk37.dim(
|
|
32288
|
+
craftMode ? "NTRP uses your live data. It keeps working until the plan is ready, then saves it and writes a handoff." : "NTRP uses your live data. It sequences the work and sets measured milestones. It then stress-tests the plan."
|
|
32289
|
+
)
|
|
30847
32290
|
);
|
|
30848
32291
|
console.log();
|
|
30849
32292
|
console.log(
|
|
30850
32293
|
" " + chalk37.dim("Confirm? ") + chalk37.cyan("\u23CE yes") + chalk37.dim(" \xB7 ") + chalk37.cyan("b back") + chalk37.dim(" \xB7 ") + chalk37.cyan("adjust") + chalk37.dim(" \xB7 ") + chalk37.cyan("cancel")
|
|
30851
32294
|
);
|
|
32295
|
+
if (!craftMode) {
|
|
32296
|
+
console.log(
|
|
32297
|
+
" " + chalk37.dim("or type ") + chalk37.cyan("keep going") + chalk37.dim(" to keep working until the plan is ready")
|
|
32298
|
+
);
|
|
32299
|
+
}
|
|
30852
32300
|
console.log();
|
|
30853
32301
|
}
|
|
30854
32302
|
async function printKeylessSkeletonPlan(ctx, objective) {
|
|
@@ -30910,7 +32358,7 @@ async function resumeStrategistAfterConnect(ctx) {
|
|
|
30910
32358
|
printObjectiveCard(ctx, state2.objective, true);
|
|
30911
32359
|
return true;
|
|
30912
32360
|
}
|
|
30913
|
-
var STRATEGIST_INTENT_RE, CANCEL_RE, CONFIRM_RE, ADJUST_RE, QUESTION_RE;
|
|
32361
|
+
var STRATEGIST_INTENT_RE, CANCEL_RE, CONFIRM_RE, ADJUST_RE, QUESTION_RE, CRAFT_RE;
|
|
30914
32362
|
var init_strategist_flow = __esm({
|
|
30915
32363
|
"src/conversation/strategist-flow.ts"() {
|
|
30916
32364
|
"use strict";
|
|
@@ -30922,17 +32370,20 @@ var init_strategist_flow = __esm({
|
|
|
30922
32370
|
init_prompts();
|
|
30923
32371
|
init_health_score();
|
|
30924
32372
|
init_divergence();
|
|
30925
|
-
init_strategist();
|
|
30926
32373
|
init_strategist2();
|
|
32374
|
+
init_strategist();
|
|
32375
|
+
init_strategist_run();
|
|
30927
32376
|
init_strategy_brief();
|
|
30928
32377
|
init_llm_attribution();
|
|
30929
32378
|
init_time_bank();
|
|
30930
32379
|
init_formatters();
|
|
32380
|
+
init_store3();
|
|
30931
32381
|
STRATEGIST_INTENT_RE = /\b(strateg(y|ize|ic)|game\s?plan|battle\s?plan|roadmap|(build|draft|make|create|put together)\s+(me\s+)?(a\s+|the\s+)?plan\b|plan\s+(to|for)\s+(fix|improv|reduc|recover|hit|reach|get|grow|turn)|how\s+(should|do|can)\s+we\s+(fix|approach|tackle|attack|prioritize|sequence|turn\s+(this|it)\s+around)|what\s+should\s+we\s+(do|fix|tackle|prioritize|focus\s+on)\s+(first|next)|what\s+order\s+should|where\s+(do|should)\s+we\s+start)\b/i;
|
|
30932
32382
|
CANCEL_RE = /^(cancel|stop|quit|abort|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
|
|
30933
32383
|
CONFIRM_RE = /^(y|yes|yep|yeah|confirm|go|go ahead|do it|proceed|sounds good|looks good|lgtm|ok|okay)\b/i;
|
|
30934
32384
|
ADJUST_RE = /^(n|no|adjust|change|edit|different|not quite|refine)\b/i;
|
|
30935
32385
|
QUESTION_RE = /(\?\s*$)|^(what|why|how|when|which|where|who)\b/i;
|
|
32386
|
+
CRAFT_RE = /^(craft|keep going|until tight|keep working)\b/i;
|
|
30936
32387
|
}
|
|
30937
32388
|
});
|
|
30938
32389
|
|
|
@@ -30945,8 +32396,8 @@ __export(strategy_review_exports, {
|
|
|
30945
32396
|
persistStrategyReview: () => persistStrategyReview,
|
|
30946
32397
|
resolveReviewableStrategy: () => resolveReviewableStrategy
|
|
30947
32398
|
});
|
|
30948
|
-
import { writeFileSync as
|
|
30949
|
-
import { join as
|
|
32399
|
+
import { writeFileSync as writeFileSync20 } from "fs";
|
|
32400
|
+
import { join as join31 } from "path";
|
|
30950
32401
|
function groupByBatch(vitals, metrics) {
|
|
30951
32402
|
const map = /* @__PURE__ */ new Map();
|
|
30952
32403
|
const order = [];
|
|
@@ -31236,7 +32687,7 @@ async function persistStrategyReview(report, milestoneVerdicts, notes) {
|
|
|
31236
32687
|
}
|
|
31237
32688
|
function logWin(strategy, hits, milestoneWins) {
|
|
31238
32689
|
const date = isoToday();
|
|
31239
|
-
const path =
|
|
32690
|
+
const path = join31(getWinsDir(), `${strategy.slug}-${date}.md`);
|
|
31240
32691
|
const lines = [
|
|
31241
32692
|
`# Win \u2014 ${strategy.title}`,
|
|
31242
32693
|
"",
|
|
@@ -31253,7 +32704,7 @@ function logWin(strategy, hits, milestoneWins) {
|
|
|
31253
32704
|
for (const label of milestoneWins) lines.push(`- ${label}`);
|
|
31254
32705
|
}
|
|
31255
32706
|
lines.push("", "_Logged by /strategy review._", "");
|
|
31256
|
-
|
|
32707
|
+
writeFileSync20(path, lines.join("\n"));
|
|
31257
32708
|
return path;
|
|
31258
32709
|
}
|
|
31259
32710
|
var VITAL_TOKENS, COMPONENT_TOKENS;
|
|
@@ -31485,9 +32936,15 @@ __export(strategy_exports2, {
|
|
|
31485
32936
|
});
|
|
31486
32937
|
import chalk39 from "chalk";
|
|
31487
32938
|
async function handler16(args, ctx) {
|
|
31488
|
-
const { positional, flags } = parseArgs2(args, ["no-ai"]);
|
|
32939
|
+
const { positional, flags } = parseArgs2(args, ["no-ai", "save", "handoff"]);
|
|
31489
32940
|
const first = positional[0];
|
|
31490
32941
|
const interactive = !ctx.oneShot && !isStructuredOutput(ctx.execution);
|
|
32942
|
+
if (interactive && first === "craft") {
|
|
32943
|
+
const seed = positional.slice(1).join(" ").trim() || void 0;
|
|
32944
|
+
const { startStrategistFlow: startStrategistFlow2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
32945
|
+
await startStrategistFlow2(ctx, { seed, origin: "command", mode: "craft" });
|
|
32946
|
+
return;
|
|
32947
|
+
}
|
|
31491
32948
|
if (interactive && (!first || !RESERVED_SUBCOMMANDS.has(first))) {
|
|
31492
32949
|
const seed = positional.join(" ").trim() || void 0;
|
|
31493
32950
|
const { startStrategistFlow: startStrategistFlow2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
@@ -31498,14 +32955,18 @@ async function handler16(args, ctx) {
|
|
|
31498
32955
|
try {
|
|
31499
32956
|
const result = await runStrategy(sub, positional, flags, ctx);
|
|
31500
32957
|
if (isStructuredOutput(ctx.execution)) {
|
|
32958
|
+
if (result.action === "craft") {
|
|
32959
|
+
emitResult("strategy.craft", craftJsonData(result), void 0, result.status === "unfinished" ? "unfinished" : "ok");
|
|
32960
|
+
return;
|
|
32961
|
+
}
|
|
31501
32962
|
emitResult("strategy", result);
|
|
31502
32963
|
return;
|
|
31503
32964
|
}
|
|
31504
32965
|
renderStrategyResult(result);
|
|
31505
32966
|
} catch (err) {
|
|
31506
|
-
if (isStructuredOutput(ctx.execution)) emitError("strategy", err);
|
|
32967
|
+
if (isStructuredOutput(ctx.execution)) emitError(first === "craft" ? "strategy.craft" : "strategy", err);
|
|
31507
32968
|
console.error(chalk39.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
31508
|
-
process.exit(1);
|
|
32969
|
+
process.exit(err instanceof NtrpError ? err.exitCode : 1);
|
|
31509
32970
|
}
|
|
31510
32971
|
}
|
|
31511
32972
|
async function runStrategy(sub, positional, flags, ctx) {
|
|
@@ -31580,6 +33041,8 @@ async function runStrategy(sub, positional, flags, ctx) {
|
|
|
31580
33041
|
const { runStrategyReview: runStrategyReview2 } = await Promise.resolve().then(() => (init_strategy_review_cmd(), strategy_review_cmd_exports));
|
|
31581
33042
|
return { action: "review", ...await runStrategyReview2(ctx, positional[1]) };
|
|
31582
33043
|
}
|
|
33044
|
+
case "craft":
|
|
33045
|
+
return runCraft(positional, flags, ctx);
|
|
31583
33046
|
default:
|
|
31584
33047
|
throw new NtrpError("unknown_strategy_subcommand", `Unknown strategy subcommand: ${sub}`, 2 /* Usage */);
|
|
31585
33048
|
}
|
|
@@ -31587,8 +33050,79 @@ async function runStrategy(sub, positional, flags, ctx) {
|
|
|
31587
33050
|
function shouldSpin(ctx) {
|
|
31588
33051
|
return !isStructuredOutput(ctx.execution) && ctx.execution.progress;
|
|
31589
33052
|
}
|
|
33053
|
+
async function runCraft(positional, flags, ctx) {
|
|
33054
|
+
if (getFalse(flags, "ai")) {
|
|
33055
|
+
throw new NtrpError(
|
|
33056
|
+
"strategy_craft_requires_ai",
|
|
33057
|
+
"strategy craft needs an LLM key. Drop --no-ai, run /connect, then retry.",
|
|
33058
|
+
3 /* Auth */
|
|
33059
|
+
);
|
|
33060
|
+
}
|
|
33061
|
+
const resumeId = getString(flags, "resume");
|
|
33062
|
+
const objective = positional.slice(1).join(" ").trim();
|
|
33063
|
+
const untilReady = !getFalse(flags, "until");
|
|
33064
|
+
const save = !getFalse(flags, "save");
|
|
33065
|
+
const handoff = !getFalse(flags, "handoff");
|
|
33066
|
+
let interrupted = false;
|
|
33067
|
+
const onInt = () => {
|
|
33068
|
+
interrupted = true;
|
|
33069
|
+
};
|
|
33070
|
+
process.on("SIGINT", onInt);
|
|
33071
|
+
const spinner = shouldSpin(ctx) ? makeSpinner("Working the plan\u2026") : null;
|
|
33072
|
+
try {
|
|
33073
|
+
const job = await executeStrategistJob({
|
|
33074
|
+
ctx,
|
|
33075
|
+
objective,
|
|
33076
|
+
mode: "craft",
|
|
33077
|
+
untilReady,
|
|
33078
|
+
save,
|
|
33079
|
+
handoff,
|
|
33080
|
+
maxRounds: getNumber(flags, "max-rounds"),
|
|
33081
|
+
maxTokens: getNumber(flags, "max-tokens"),
|
|
33082
|
+
minScore: getNumber(flags, "min-score"),
|
|
33083
|
+
resumeId,
|
|
33084
|
+
interrupted: () => interrupted,
|
|
33085
|
+
onEvent: (event) => {
|
|
33086
|
+
if (!spinner) return;
|
|
33087
|
+
if (event.type === "thinking") {
|
|
33088
|
+
spinner.stop();
|
|
33089
|
+
console.log(" " + chalk39.dim.italic(event.text));
|
|
33090
|
+
spinner.start();
|
|
33091
|
+
spinner.text = "Working the plan\u2026";
|
|
33092
|
+
}
|
|
33093
|
+
}
|
|
33094
|
+
});
|
|
33095
|
+
spinner?.stop();
|
|
33096
|
+
return { action: "craft", ...job };
|
|
33097
|
+
} catch (err) {
|
|
33098
|
+
spinner?.fail("Strategy craft failed");
|
|
33099
|
+
throw err;
|
|
33100
|
+
} finally {
|
|
33101
|
+
process.off("SIGINT", onInt);
|
|
33102
|
+
}
|
|
33103
|
+
}
|
|
33104
|
+
function craftJsonData(result) {
|
|
33105
|
+
return {
|
|
33106
|
+
action: "craft",
|
|
33107
|
+
status: result.status,
|
|
33108
|
+
slug: result.slug ?? null,
|
|
33109
|
+
objective: result.objective,
|
|
33110
|
+
critic_score: result.critic_score,
|
|
33111
|
+
rounds: result.rounds,
|
|
33112
|
+
usage: result.usage,
|
|
33113
|
+
library_path: result.library_path ?? null,
|
|
33114
|
+
journal_path: result.journal_path ?? null,
|
|
33115
|
+
inbox_path: result.inbox_path ?? null,
|
|
33116
|
+
handoff_path: result.handoff_path ?? null,
|
|
33117
|
+
blocking_gaps: result.blocking_gaps,
|
|
33118
|
+
stop_reason: result.stop_reason ?? null
|
|
33119
|
+
};
|
|
33120
|
+
}
|
|
31590
33121
|
function renderStrategyResult(result) {
|
|
31591
33122
|
switch (result.action) {
|
|
33123
|
+
case "craft":
|
|
33124
|
+
renderCraftResult(result);
|
|
33125
|
+
return;
|
|
31592
33126
|
case "review":
|
|
31593
33127
|
return;
|
|
31594
33128
|
case "ingest":
|
|
@@ -31621,6 +33155,31 @@ function renderStrategyResult(result) {
|
|
|
31621
33155
|
return;
|
|
31622
33156
|
}
|
|
31623
33157
|
}
|
|
33158
|
+
function renderCraftResult(result) {
|
|
33159
|
+
if (result.plan) {
|
|
33160
|
+
printStrategyBrief(result.plan, {
|
|
33161
|
+
measurable_targets: result.measurable_targets,
|
|
33162
|
+
total_targets: result.total_targets
|
|
33163
|
+
});
|
|
33164
|
+
} else {
|
|
33165
|
+
console.log();
|
|
33166
|
+
console.log(" " + chalk39.dim("(No plan produced)"));
|
|
33167
|
+
}
|
|
33168
|
+
for (const notice of result.notices.slice(0, 8)) {
|
|
33169
|
+
console.log(" " + chalk39.dim(notice));
|
|
33170
|
+
}
|
|
33171
|
+
printLlmAttribution(result.usage);
|
|
33172
|
+
printCraftWrapUp({
|
|
33173
|
+
status: result.status,
|
|
33174
|
+
library_path: result.library_path,
|
|
33175
|
+
inbox_path: result.inbox_path,
|
|
33176
|
+
handoff_path: result.handoff_path,
|
|
33177
|
+
journal_path: result.journal_path,
|
|
33178
|
+
slug: result.slug,
|
|
33179
|
+
jobId: result.job?.id,
|
|
33180
|
+
oneShot: true
|
|
33181
|
+
});
|
|
33182
|
+
}
|
|
31624
33183
|
function printStrategyList(strategies) {
|
|
31625
33184
|
console.log(chalk39.bold("\n Strategies\n"));
|
|
31626
33185
|
if (strategies.length === 0) {
|
|
@@ -31731,7 +33290,10 @@ var init_strategy2 = __esm({
|
|
|
31731
33290
|
init_types2();
|
|
31732
33291
|
init_strategy();
|
|
31733
33292
|
init_repl_api();
|
|
31734
|
-
|
|
33293
|
+
init_strategist_run();
|
|
33294
|
+
init_strategy_brief();
|
|
33295
|
+
init_llm_attribution();
|
|
33296
|
+
RESERVED_SUBCOMMANDS = /* @__PURE__ */ new Set(["ingest", "add", "list", "show", "sync", "sources", "review", "craft"]);
|
|
31735
33297
|
}
|
|
31736
33298
|
});
|
|
31737
33299
|
|
|
@@ -32650,15 +34212,15 @@ var init_checkout = __esm({
|
|
|
32650
34212
|
});
|
|
32651
34213
|
|
|
32652
34214
|
// src/services/setup.ts
|
|
32653
|
-
import { existsSync as
|
|
32654
|
-
import { join as
|
|
34215
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync18, readFileSync as readFileSync23, writeFileSync as writeFileSync21 } from "fs";
|
|
34216
|
+
import { join as join32 } from "path";
|
|
32655
34217
|
function setupCheck() {
|
|
32656
34218
|
const home = ntrpHome();
|
|
32657
34219
|
let writable = false;
|
|
32658
34220
|
try {
|
|
32659
34221
|
mkdirSync18(home, { recursive: true });
|
|
32660
|
-
const probe =
|
|
32661
|
-
|
|
34222
|
+
const probe = join32(home, ".write-check");
|
|
34223
|
+
writeFileSync21(probe, "ok\n");
|
|
32662
34224
|
writable = true;
|
|
32663
34225
|
} catch {
|
|
32664
34226
|
writable = false;
|
|
@@ -32697,7 +34259,7 @@ function setupCheck() {
|
|
|
32697
34259
|
};
|
|
32698
34260
|
}
|
|
32699
34261
|
function readProfileInput(pathOrDash) {
|
|
32700
|
-
const raw = pathOrDash === "-" ?
|
|
34262
|
+
const raw = pathOrDash === "-" ? readFileSync23(0, "utf-8") : readFileSync23(pathOrDash, "utf-8");
|
|
32701
34263
|
return JSON.parse(raw);
|
|
32702
34264
|
}
|
|
32703
34265
|
function writeAgentProfile(input) {
|
|
@@ -33487,7 +35049,7 @@ var init_orchestrator = __esm({
|
|
|
33487
35049
|
});
|
|
33488
35050
|
|
|
33489
35051
|
// src/services/smoke-protocol.ts
|
|
33490
|
-
import { join as
|
|
35052
|
+
import { join as join33 } from "path";
|
|
33491
35053
|
function isSmokeProtocolTrigger(input) {
|
|
33492
35054
|
return normalize2(input).includes(SMOKE_TRIGGER_PHRASE);
|
|
33493
35055
|
}
|
|
@@ -33523,7 +35085,7 @@ async function runSmokeProtocol(_input, ctx) {
|
|
|
33523
35085
|
});
|
|
33524
35086
|
const proposalResult = await proposeRepositoryExport({
|
|
33525
35087
|
target: "markdown",
|
|
33526
|
-
directory:
|
|
35088
|
+
directory: join33(getExportsDir(), "repository-smoke"),
|
|
33527
35089
|
source: "smoke_protocol",
|
|
33528
35090
|
modelOrFixture: "smoke-protocol-v1"
|
|
33529
35091
|
});
|
|
@@ -34455,8 +36017,8 @@ var init_recall = __esm({
|
|
|
34455
36017
|
|
|
34456
36018
|
// src/memory/feedback.ts
|
|
34457
36019
|
import { appendFileSync as appendFileSync7 } from "fs";
|
|
34458
|
-
import { join as
|
|
34459
|
-
import { randomUUID as
|
|
36020
|
+
import { join as join34 } from "path";
|
|
36021
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
34460
36022
|
function summarize(text) {
|
|
34461
36023
|
return text.replace(/[#*`>_]/g, "").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
34462
36024
|
}
|
|
@@ -34478,7 +36040,7 @@ function findCalibrationToSupersede(question, note) {
|
|
|
34478
36040
|
}
|
|
34479
36041
|
function recordFeedback(input) {
|
|
34480
36042
|
const entry = {
|
|
34481
|
-
id:
|
|
36043
|
+
id: randomUUID9(),
|
|
34482
36044
|
rating: input.rating,
|
|
34483
36045
|
question: scrubText(input.question).slice(0, 300),
|
|
34484
36046
|
answer_summary: scrubText(summarize(input.answer)),
|
|
@@ -34487,7 +36049,7 @@ function recordFeedback(input) {
|
|
|
34487
36049
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
34488
36050
|
};
|
|
34489
36051
|
try {
|
|
34490
|
-
appendFileSync7(
|
|
36052
|
+
appendFileSync7(join34(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
|
|
34491
36053
|
} catch {
|
|
34492
36054
|
}
|
|
34493
36055
|
if (input.rating === "positive") {
|
|
@@ -34657,7 +36219,7 @@ __export(sessions_exports, {
|
|
|
34657
36219
|
handler: () => handler35
|
|
34658
36220
|
});
|
|
34659
36221
|
import chalk62 from "chalk";
|
|
34660
|
-
import { existsSync as
|
|
36222
|
+
import { existsSync as existsSync34 } from "fs";
|
|
34661
36223
|
async function handler35(args, _ctx) {
|
|
34662
36224
|
const sub = args[0] ?? "list";
|
|
34663
36225
|
if (sub === "list" || !args[0]) {
|
|
@@ -34764,12 +36326,12 @@ function showSession(idArg) {
|
|
|
34764
36326
|
console.log();
|
|
34765
36327
|
const transcriptPath = transcriptPathForSession(session.id);
|
|
34766
36328
|
const contextPath = contextDocPathForSession(session.id);
|
|
34767
|
-
if (
|
|
36329
|
+
if (existsSync34(transcriptPath) || existsSync34(contextPath)) {
|
|
34768
36330
|
console.log(" " + chalk62.dim("\u2500".repeat(40)));
|
|
34769
|
-
if (
|
|
36331
|
+
if (existsSync34(contextPath)) {
|
|
34770
36332
|
console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
|
|
34771
36333
|
}
|
|
34772
|
-
if (
|
|
36334
|
+
if (existsSync34(transcriptPath)) {
|
|
34773
36335
|
console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
|
|
34774
36336
|
}
|
|
34775
36337
|
console.log();
|
|
@@ -34922,7 +36484,7 @@ var switch_exports = {};
|
|
|
34922
36484
|
__export(switch_exports, {
|
|
34923
36485
|
handler: () => handler38
|
|
34924
36486
|
});
|
|
34925
|
-
import { join as
|
|
36487
|
+
import { join as join35 } from "path";
|
|
34926
36488
|
import chalk65 from "chalk";
|
|
34927
36489
|
async function handler38(args, ctx) {
|
|
34928
36490
|
if (args.length === 0) {
|
|
@@ -34952,7 +36514,7 @@ async function handler38(args, ctx) {
|
|
|
34952
36514
|
}
|
|
34953
36515
|
const context = buildSwitchContext(session);
|
|
34954
36516
|
const newId = makeSessionId();
|
|
34955
|
-
const newFile =
|
|
36517
|
+
const newFile = join35(getSessionsDir(), `${newId}.json`);
|
|
34956
36518
|
resetContextForSwitch(ctx, {
|
|
34957
36519
|
sessionId: newId,
|
|
34958
36520
|
sessionFile: newFile,
|
|
@@ -34965,7 +36527,8 @@ async function handler38(args, ctx) {
|
|
|
34965
36527
|
stage: session.stage,
|
|
34966
36528
|
dataset: session.dataset,
|
|
34967
36529
|
deliverables: session.deliverables,
|
|
34968
|
-
llm: session.llm ? { ...session.llm } : void 0
|
|
36530
|
+
llm: session.llm ? { ...session.llm } : void 0,
|
|
36531
|
+
lastCraftJobId: session.last_craft_job_id
|
|
34969
36532
|
});
|
|
34970
36533
|
console.log();
|
|
34971
36534
|
console.log(" " + paint("accent", `Switched to "${targetName}"`));
|
|
@@ -34978,7 +36541,7 @@ async function handler38(args, ctx) {
|
|
|
34978
36541
|
return `Switched to "${targetName}"`;
|
|
34979
36542
|
} else {
|
|
34980
36543
|
const newId = makeSessionId();
|
|
34981
|
-
const newFile =
|
|
36544
|
+
const newFile = join35(getSessionsDir(), `${newId}.json`);
|
|
34982
36545
|
resetContextForSwitch(ctx, {
|
|
34983
36546
|
sessionId: newId,
|
|
34984
36547
|
sessionFile: newFile,
|
|
@@ -36118,8 +37681,8 @@ __export(relaunch_exports, {
|
|
|
36118
37681
|
resolveRelaunchEntry: () => resolveRelaunchEntry,
|
|
36119
37682
|
updateRestartSummary: () => updateRestartSummary
|
|
36120
37683
|
});
|
|
36121
|
-
import { existsSync as
|
|
36122
|
-
import { join as
|
|
37684
|
+
import { existsSync as existsSync35 } from "fs";
|
|
37685
|
+
import { join as join36 } from "path";
|
|
36123
37686
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
36124
37687
|
import { spawnSync } from "child_process";
|
|
36125
37688
|
function encodeJustUpdated(fromVersion, toVersion) {
|
|
@@ -36143,8 +37706,8 @@ function updateRestartSummary(toVersion) {
|
|
|
36143
37706
|
function npmGlobalEntry() {
|
|
36144
37707
|
const listed = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
36145
37708
|
if (listed.status !== 0) return null;
|
|
36146
|
-
const entry =
|
|
36147
|
-
return
|
|
37709
|
+
const entry = join36(listed.stdout.trim(), NPM_PACKAGE, "dist/index.js");
|
|
37710
|
+
return existsSync35(entry) ? entry : null;
|
|
36148
37711
|
}
|
|
36149
37712
|
function thisBundleEntry() {
|
|
36150
37713
|
return fileURLToPath2(import.meta.url);
|
|
@@ -36154,10 +37717,10 @@ function resolveRelaunchEntry(toVersion) {
|
|
|
36154
37717
|
(p) => Boolean(p)
|
|
36155
37718
|
);
|
|
36156
37719
|
for (const entry of candidates) {
|
|
36157
|
-
if (!
|
|
37720
|
+
if (!existsSync35(entry)) continue;
|
|
36158
37721
|
if (readVersionNearEntry(entry) === toVersion) return entry;
|
|
36159
37722
|
}
|
|
36160
|
-
return candidates.find((p) =>
|
|
37723
|
+
return candidates.find((p) => existsSync35(p)) ?? thisBundleEntry();
|
|
36161
37724
|
}
|
|
36162
37725
|
function relaunchArgv(toVersion) {
|
|
36163
37726
|
return [resolveRelaunchEntry(toVersion)];
|
|
@@ -36388,10 +37951,10 @@ function renderProgressReport() {
|
|
|
36388
37951
|
`${chalk74.dim("Deliverables")} ${chalk74.bold(String(usage5.deliverables))}`,
|
|
36389
37952
|
`${chalk74.dim("AI exchanges")} ${chalk74.bold(String(usage5.nl_exchanges))}`
|
|
36390
37953
|
]);
|
|
36391
|
-
const
|
|
37954
|
+
const totalTokens2 = usage5.input_tokens + usage5.output_tokens;
|
|
36392
37955
|
printCard("AI usage", [
|
|
36393
37956
|
`${chalk74.dim("LLM calls")} ${chalk74.bold(String(usage5.llm_calls))}`,
|
|
36394
|
-
`${chalk74.dim("Tokens")} ${chalk74.bold(formatTokens(
|
|
37957
|
+
`${chalk74.dim("Tokens")} ${chalk74.bold(formatTokens(totalTokens2))} in+out (${formatTokens(usage5.input_tokens)} in \xB7 ${formatTokens(usage5.output_tokens)} out)`
|
|
36395
37958
|
]);
|
|
36396
37959
|
const weeks = [...usage5.weekly].sort((a, b) => a.week.localeCompare(b.week)).slice(-8);
|
|
36397
37960
|
const weekHours = weeks.map((w) => w.minutes_saved / 60);
|
|
@@ -36697,8 +38260,8 @@ __export(exports_exports, {
|
|
|
36697
38260
|
handler: () => handler48
|
|
36698
38261
|
});
|
|
36699
38262
|
import chalk78 from "chalk";
|
|
36700
|
-
import { existsSync as
|
|
36701
|
-
import { join as
|
|
38263
|
+
import { existsSync as existsSync36 } from "fs";
|
|
38264
|
+
import { join as join37 } from "path";
|
|
36702
38265
|
function usage4() {
|
|
36703
38266
|
console.log(chalk78.dim(" Usage:"));
|
|
36704
38267
|
console.log(chalk78.dim(" /exports list [kind]"));
|
|
@@ -36755,7 +38318,7 @@ function printInboxShow() {
|
|
|
36755
38318
|
console.log(" " + paint("accent", "AI inbox: ") + inbox);
|
|
36756
38319
|
const latest = inboxLatestHandoffPath();
|
|
36757
38320
|
if (latest) console.log(" " + chalk78.dim("Latest handoff: ") + latest);
|
|
36758
|
-
console.log(" " + chalk78.dim("Finder skill: ") +
|
|
38321
|
+
console.log(" " + chalk78.dim("Finder skill: ") + join37(inbox, "SKILL.md"));
|
|
36759
38322
|
console.log(" " + chalk78.dim("Reprint: ") + paint("accent", "/inbox skill"));
|
|
36760
38323
|
} else {
|
|
36761
38324
|
console.log(" " + chalk78.dim("AI inbox: (not set). Type ") + paint("accent", "/inbox set <folder>"));
|
|
@@ -36777,8 +38340,8 @@ function printOpen() {
|
|
|
36777
38340
|
console.log(" " + chalk78.dim("AI inbox: ") + inbox);
|
|
36778
38341
|
const latest = inboxLatestHandoffPath();
|
|
36779
38342
|
if (latest) console.log(" " + chalk78.dim("Inbox latest: ") + latest);
|
|
36780
|
-
console.log(" " + chalk78.dim("Pickup skill: ") +
|
|
36781
|
-
console.log(" " + chalk78.dim("Finder skill: ") +
|
|
38343
|
+
console.log(" " + chalk78.dim("Pickup skill: ") + join37(inbox, "latest-pickup.md"));
|
|
38344
|
+
console.log(" " + chalk78.dim("Finder skill: ") + join37(inbox, "SKILL.md"));
|
|
36782
38345
|
} else {
|
|
36783
38346
|
console.log(" " + chalk78.dim("AI inbox is not set. Type ") + paint("accent", "/inbox set <folder>"));
|
|
36784
38347
|
const loc = handoffLocations();
|
|
@@ -36856,7 +38419,7 @@ function runMove(args, ctx) {
|
|
|
36856
38419
|
}
|
|
36857
38420
|
try {
|
|
36858
38421
|
const destDir = resolveUserPath(dest);
|
|
36859
|
-
if (!
|
|
38422
|
+
if (!existsSync36(destDir)) {
|
|
36860
38423
|
}
|
|
36861
38424
|
const event = moveExport(idOrName, destDir);
|
|
36862
38425
|
console.log();
|
|
@@ -37502,7 +39065,7 @@ This command is hidden. Type \`/ingest --demo\` instead. That command calls this
|
|
|
37502
39065
|
name: strategy
|
|
37503
39066
|
description: Make a measured strategy from your data
|
|
37504
39067
|
section: More
|
|
37505
|
-
args: [objective] | [list|show|review|ingest|add|sync|sources] [args]
|
|
39068
|
+
args: [objective] | [list|show|review|ingest|add|sync|sources|craft] [args]
|
|
37506
39069
|
handler: ../commands/strategy.ts
|
|
37507
39070
|
---
|
|
37508
39071
|
|
|
@@ -37511,6 +39074,8 @@ NTRP uses live data. It works back from the objective.
|
|
|
37511
39074
|
The result is sequenced workstreams with dated milestones, deliverables, outcome ranges, and a contingency per workstream.
|
|
37512
39075
|
Saved plans go to the strategy library. Later answers use them.
|
|
37513
39076
|
Type \`/strategy review [slug]\` to check the plan against live data when new batches arrive.
|
|
39077
|
+
Type \`keep going\` at the confirm card to keep working until the plan is ready.
|
|
39078
|
+
One-shot: \`ntrp strategy craft <objective>\`. Resume with \`ntrp strategy craft --resume <id>\`.
|
|
37514
39079
|
|
|
37515
39080
|
Library commands: \`/strategy list\`, \`/strategy show <slug>\`.
|
|
37516
39081
|
Type \`/strategy ingest <file>\` for markdown, YAML, PDF, text, or \`-\` for stdin.
|
|
@@ -37892,8 +39457,8 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
|
|
|
37892
39457
|
});
|
|
37893
39458
|
|
|
37894
39459
|
// src/ai/prompt-parts.ts
|
|
37895
|
-
import { existsSync as
|
|
37896
|
-
import { join as
|
|
39460
|
+
import { existsSync as existsSync37, readFileSync as readFileSync24 } from "fs";
|
|
39461
|
+
import { join as join38 } from "path";
|
|
37897
39462
|
function buildCompanyProfileBlock() {
|
|
37898
39463
|
const p = loadProfile();
|
|
37899
39464
|
if (!p) return "";
|
|
@@ -37911,10 +39476,10 @@ function buildCompanyProfileBlock() {
|
|
|
37911
39476
|
return lines.join("\n");
|
|
37912
39477
|
}
|
|
37913
39478
|
function loadAnalystFile() {
|
|
37914
|
-
const path =
|
|
39479
|
+
const path = join38(ntrpHome(), ANALYST_FILE_NAME);
|
|
37915
39480
|
try {
|
|
37916
|
-
if (!
|
|
37917
|
-
const raw = sanitizeExternalText(
|
|
39481
|
+
if (!existsSync37(path)) return null;
|
|
39482
|
+
const raw = sanitizeExternalText(readFileSync24(path, "utf-8").trim());
|
|
37918
39483
|
if (!raw) return null;
|
|
37919
39484
|
if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
|
|
37920
39485
|
const head = raw.slice(0, Math.floor(ANALYST_FILE_MAX_CHARS * 0.75));
|
|
@@ -38448,8 +40013,8 @@ __export(ingest_chat_exports, {
|
|
|
38448
40013
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
38449
40014
|
looksLikeFilePath: () => looksLikeFilePath
|
|
38450
40015
|
});
|
|
38451
|
-
import { existsSync as
|
|
38452
|
-
import { basename as basename10, join as
|
|
40016
|
+
import { existsSync as existsSync38, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
|
|
40017
|
+
import { basename as basename10, join as join39, resolve as resolve9 } from "path";
|
|
38453
40018
|
import { homedir as homedir8 } from "os";
|
|
38454
40019
|
import chalk80 from "chalk";
|
|
38455
40020
|
function extractFilePath(input) {
|
|
@@ -38471,7 +40036,7 @@ function extractFilePath(input) {
|
|
|
38471
40036
|
if (!candidate) continue;
|
|
38472
40037
|
if (!looksLikePathToken(candidate)) continue;
|
|
38473
40038
|
const p = expandPath(candidate);
|
|
38474
|
-
if (
|
|
40039
|
+
if (existsSync38(p)) {
|
|
38475
40040
|
try {
|
|
38476
40041
|
const st = statSync5(p);
|
|
38477
40042
|
if (st.isFile() || st.isDirectory()) return p;
|
|
@@ -38500,7 +40065,7 @@ function looksLikeFilePath(input) {
|
|
|
38500
40065
|
function listCsvsInFolder(dir) {
|
|
38501
40066
|
try {
|
|
38502
40067
|
if (!statSync5(dir).isDirectory()) return [];
|
|
38503
|
-
return readdirSync6(dir).filter((name) => name.toLowerCase().endsWith(".csv")).map((name) =>
|
|
40068
|
+
return readdirSync6(dir).filter((name) => name.toLowerCase().endsWith(".csv")).map((name) => join39(dir, name)).sort();
|
|
38504
40069
|
} catch {
|
|
38505
40070
|
return [];
|
|
38506
40071
|
}
|
|
@@ -38582,12 +40147,12 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
38582
40147
|
}
|
|
38583
40148
|
const { handler: ingest } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
|
|
38584
40149
|
const { detectEntityType: detectEntityType2 } = await Promise.resolve().then(() => (init_csv_detect(), csv_detect_exports));
|
|
38585
|
-
const { readFileSync:
|
|
40150
|
+
const { readFileSync: readFileSync25 } = await import("fs");
|
|
38586
40151
|
const { parseCSV: parseCSV2 } = await Promise.resolve().then(() => (init_csv_parse(), csv_parse_exports));
|
|
38587
40152
|
const { getStoredApiKey } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
38588
40153
|
let headerCheckFailed = false;
|
|
38589
40154
|
try {
|
|
38590
|
-
const raw =
|
|
40155
|
+
const raw = readFileSync25(filePath, "utf-8");
|
|
38591
40156
|
const { headers } = parseCSV2(raw);
|
|
38592
40157
|
const detected = detectEntityType2(headers, "unknown");
|
|
38593
40158
|
if (!detected) headerCheckFailed = true;
|
|
@@ -39585,6 +41150,10 @@ async function conversationRouter(input, ctx) {
|
|
|
39585
41150
|
const summary = await handleStrategizeFlow(line, ctx) ?? void 0;
|
|
39586
41151
|
return { handled: true, summary };
|
|
39587
41152
|
}
|
|
41153
|
+
if (isKeepGoingIntent(line) && (phase === "explore" || phase === "orient" && isAnalysisReady(ctx))) {
|
|
41154
|
+
const summary = await handleKeepGoingLine(ctx, line) ?? void 0;
|
|
41155
|
+
return { handled: true, summary };
|
|
41156
|
+
}
|
|
39588
41157
|
if (phase === "think") {
|
|
39589
41158
|
const summary = await handleThinkFlow(line, ctx) ?? void 0;
|
|
39590
41159
|
return { handled: true, summary };
|
|
@@ -39895,7 +41464,7 @@ var init_loop_guard2 = __esm({
|
|
|
39895
41464
|
},
|
|
39896
41465
|
strategize: {
|
|
39897
41466
|
mode: "strategy objective confirm",
|
|
39898
|
-
accepts: "\u23CE or yes (build the plan) \xB7 b / back or adjust (restate the objective) \xB7 cancel",
|
|
41467
|
+
accepts: "\u23CE or yes (build the plan) \xB7 keep going (keep working until the plan is ready) \xB7 b / back or adjust (restate the objective) \xB7 cancel",
|
|
39899
41468
|
leave: "cancel drops the strategy session and returns to Q&A"
|
|
39900
41469
|
}
|
|
39901
41470
|
};
|
|
@@ -40380,7 +41949,8 @@ var init_deepdive_complete = __esm({
|
|
|
40380
41949
|
"inbox",
|
|
40381
41950
|
"claude",
|
|
40382
41951
|
"skill",
|
|
40383
|
-
"strategy"
|
|
41952
|
+
"strategy",
|
|
41953
|
+
"keep-going"
|
|
40384
41954
|
];
|
|
40385
41955
|
}
|
|
40386
41956
|
});
|
|
@@ -40491,7 +42061,7 @@ __export(repl_exports, {
|
|
|
40491
42061
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
40492
42062
|
import { clearLine as clearLine2, cursorTo as cursorTo2 } from "readline";
|
|
40493
42063
|
import chalk88 from "chalk";
|
|
40494
|
-
import { join as
|
|
42064
|
+
import { join as join40 } from "path";
|
|
40495
42065
|
function buildPrompt(ctx) {
|
|
40496
42066
|
return buildConversationPrompt(ctx);
|
|
40497
42067
|
}
|
|
@@ -40819,14 +42389,14 @@ function printHelp() {
|
|
|
40819
42389
|
console.log(" " + chalk88.dim("Type the question. You do not need a slash command."));
|
|
40820
42390
|
console.log(" " + chalk88.dim("Paste a CSV path or type ") + paint("accent", '"use demo data"') + chalk88.dim(" to load data."));
|
|
40821
42391
|
console.log(" " + chalk88.dim("After analysis, type questions in English."));
|
|
40822
|
-
console.log(" " + chalk88.dim("Type ") + paint("accent", '"how should we fix this?"') + chalk88.dim(" to make a strategy."));
|
|
42392
|
+
console.log(" " + chalk88.dim("Type ") + paint("accent", '"how should we fix this?"') + chalk88.dim(" to make a strategy. Type ") + paint("accent", "keep going") + chalk88.dim(" at confirm to keep working until the plan is ready."));
|
|
40823
42393
|
console.log(" " + chalk88.dim("Type ") + paint("accent", '"ship a board deck"') + chalk88.dim(" to write a handoff."));
|
|
40824
42394
|
console.log(" " + chalk88.dim("The ") + paint("accent", "\u203A") + chalk88.dim(" prompt shows brief or deep after analysis. Brief is the default."));
|
|
40825
42395
|
console.log();
|
|
40826
42396
|
console.log(" " + sectionHeading("Keys"));
|
|
40827
42397
|
console.log(
|
|
40828
42398
|
" " + paint("accent", "\u23CE") + chalk88.dim(
|
|
40829
|
-
" Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect, /update)."
|
|
42399
|
+
" Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect, /update, keep going)."
|
|
40830
42400
|
)
|
|
40831
42401
|
);
|
|
40832
42402
|
console.log(
|
|
@@ -40877,7 +42447,7 @@ function printHelp() {
|
|
|
40877
42447
|
["/remember <fact>", "Store a fact, a decision, or a preference"],
|
|
40878
42448
|
["/recall [topic]", "Show what NTRP stores about your business"],
|
|
40879
42449
|
["/rate good|bad <note>", "Correct the last answer. A bad note becomes a calibration"],
|
|
40880
|
-
[`${
|
|
42450
|
+
[`${join40(ntrpHome(), ANALYST_FILE_NAME)}`, "Standing operator instructions (tone, priorities, house rules)"]
|
|
40881
42451
|
];
|
|
40882
42452
|
const teachMaxW = Math.max(...teach.map(([c]) => c.length)) + 2;
|
|
40883
42453
|
for (const [cmd, desc] of teach) {
|