@sonnechasser/ntrp 1.5.2 → 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 +2077 -479
- package/dist/mcp/server.js +1678 -141
- 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
|
"",
|
|
@@ -15499,14 +15643,324 @@ var init_explore_mode = __esm({
|
|
|
15499
15643
|
}
|
|
15500
15644
|
});
|
|
15501
15645
|
|
|
15646
|
+
// src/config/update-check.ts
|
|
15647
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync17, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "fs";
|
|
15648
|
+
import { join as join21 } from "path";
|
|
15649
|
+
function cachePath2() {
|
|
15650
|
+
return join21(ntrpHome(), "update-check.json");
|
|
15651
|
+
}
|
|
15652
|
+
function ensureDir6() {
|
|
15653
|
+
const dir = ntrpHome();
|
|
15654
|
+
if (!existsSync21(dir)) {
|
|
15655
|
+
mkdirSync12(dir, { recursive: true });
|
|
15656
|
+
}
|
|
15657
|
+
}
|
|
15658
|
+
function loadUpdateCheckCache() {
|
|
15659
|
+
const path = cachePath2();
|
|
15660
|
+
if (!existsSync21(path)) return null;
|
|
15661
|
+
try {
|
|
15662
|
+
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
15663
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
|
|
15664
|
+
return null;
|
|
15665
|
+
}
|
|
15666
|
+
return parsed;
|
|
15667
|
+
} catch {
|
|
15668
|
+
return null;
|
|
15669
|
+
}
|
|
15670
|
+
}
|
|
15671
|
+
function saveUpdateCheckCache(cache2) {
|
|
15672
|
+
ensureDir6();
|
|
15673
|
+
writeFileSync14(cachePath2(), JSON.stringify(cache2, null, 2) + "\n");
|
|
15674
|
+
}
|
|
15675
|
+
function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
|
|
15676
|
+
if (!cache2) return false;
|
|
15677
|
+
return Date.now() - cache2.lastCheck < ttlMs;
|
|
15678
|
+
}
|
|
15679
|
+
function invalidateUpdateCheckCache() {
|
|
15680
|
+
const path = cachePath2();
|
|
15681
|
+
if (existsSync21(path)) {
|
|
15682
|
+
unlinkSync4(path);
|
|
15683
|
+
}
|
|
15684
|
+
}
|
|
15685
|
+
var CACHE_TTL_MS2;
|
|
15686
|
+
var init_update_check = __esm({
|
|
15687
|
+
"src/config/update-check.ts"() {
|
|
15688
|
+
"use strict";
|
|
15689
|
+
init_store();
|
|
15690
|
+
CACHE_TTL_MS2 = 864e5;
|
|
15691
|
+
}
|
|
15692
|
+
});
|
|
15693
|
+
|
|
15694
|
+
// src/version.ts
|
|
15695
|
+
import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
|
|
15696
|
+
import { dirname as dirname4, join as join22 } from "path";
|
|
15697
|
+
import { fileURLToPath } from "url";
|
|
15698
|
+
function readVersionFromPackageJson(packageJsonPath) {
|
|
15699
|
+
if (!existsSync22(packageJsonPath)) return null;
|
|
15700
|
+
try {
|
|
15701
|
+
const pkg = JSON.parse(readFileSync18(packageJsonPath, "utf-8"));
|
|
15702
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
15703
|
+
} catch {
|
|
15704
|
+
}
|
|
15705
|
+
return null;
|
|
15706
|
+
}
|
|
15707
|
+
function readVersionNearEntry(entryPath) {
|
|
15708
|
+
const start = dirname4(entryPath);
|
|
15709
|
+
for (const rel of [join22(start, "..", "package.json"), join22(start, "../..", "package.json")]) {
|
|
15710
|
+
const version = readVersionFromPackageJson(rel);
|
|
15711
|
+
if (version) return version;
|
|
15712
|
+
}
|
|
15713
|
+
return null;
|
|
15714
|
+
}
|
|
15715
|
+
function readInstalledVersionFromDisk() {
|
|
15716
|
+
return readVersionNearEntry(fileURLToPath(import.meta.url));
|
|
15717
|
+
}
|
|
15718
|
+
function getInstalledVersion() {
|
|
15719
|
+
if (cachedVersion) return cachedVersion;
|
|
15720
|
+
cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
|
|
15721
|
+
return cachedVersion;
|
|
15722
|
+
}
|
|
15723
|
+
var cachedVersion;
|
|
15724
|
+
var init_version = __esm({
|
|
15725
|
+
"src/version.ts"() {
|
|
15726
|
+
"use strict";
|
|
15727
|
+
}
|
|
15728
|
+
});
|
|
15729
|
+
|
|
15730
|
+
// src/update/registry.ts
|
|
15731
|
+
var registry_exports = {};
|
|
15732
|
+
__export(registry_exports, {
|
|
15733
|
+
NPM_PACKAGE: () => NPM_PACKAGE,
|
|
15734
|
+
applyUpdateCheckResult: () => applyUpdateCheckResult,
|
|
15735
|
+
checkForUpdate: () => checkForUpdate,
|
|
15736
|
+
fetchLatestVersion: () => fetchLatestVersion,
|
|
15737
|
+
formatUpdateNudge: () => formatUpdateNudge,
|
|
15738
|
+
hasAvailableUpdate: () => hasAvailableUpdate,
|
|
15739
|
+
hydrateUpdateAvailableFromCache: () => hydrateUpdateAvailableFromCache,
|
|
15740
|
+
isNewerVersion: () => isNewerVersion,
|
|
15741
|
+
startBackgroundUpdateCheck: () => startBackgroundUpdateCheck
|
|
15742
|
+
});
|
|
15743
|
+
function registryUrl() {
|
|
15744
|
+
return process.env.NTRP_REGISTRY_URL ?? "https://registry.npmjs.org/@sonnechasser/ntrp/latest";
|
|
15745
|
+
}
|
|
15746
|
+
function parseVersionParts(version) {
|
|
15747
|
+
const cleaned = version.trim().replace(/^v/i, "");
|
|
15748
|
+
const core = cleaned.split("-")[0] ?? cleaned;
|
|
15749
|
+
const parts = core.split(".").map((p) => parseInt(p, 10));
|
|
15750
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
15751
|
+
}
|
|
15752
|
+
function isNewerVersion(latest, current) {
|
|
15753
|
+
const [lMaj, lMin, lPatch] = parseVersionParts(latest);
|
|
15754
|
+
const [cMaj, cMin, cPatch] = parseVersionParts(current);
|
|
15755
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
15756
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
15757
|
+
return lPatch > cPatch;
|
|
15758
|
+
}
|
|
15759
|
+
function formatUpdateNudge(current, latest) {
|
|
15760
|
+
return `\u26A1 NTRP v${latest} available (running v${current}) \u2014 \u23CE /update`;
|
|
15761
|
+
}
|
|
15762
|
+
function hasAvailableUpdate(update) {
|
|
15763
|
+
return Boolean(update && isNewerVersion(update.latest, update.current));
|
|
15764
|
+
}
|
|
15765
|
+
function hydrateUpdateAvailableFromCache(current) {
|
|
15766
|
+
const cached2 = loadUpdateCheckCache();
|
|
15767
|
+
if (!cached2?.latestVersion) return void 0;
|
|
15768
|
+
if (!isNewerVersion(cached2.latestVersion, current)) return void 0;
|
|
15769
|
+
return { current, latest: cached2.latestVersion };
|
|
15770
|
+
}
|
|
15771
|
+
function applyUpdateCheckResult(ctx, result) {
|
|
15772
|
+
if (result?.updateAvailable) {
|
|
15773
|
+
ctx.updateAvailable = { current: result.current, latest: result.latest };
|
|
15774
|
+
return;
|
|
15775
|
+
}
|
|
15776
|
+
if (result && !result.updateAvailable) {
|
|
15777
|
+
ctx.updateAvailable = void 0;
|
|
15778
|
+
}
|
|
15779
|
+
}
|
|
15780
|
+
function startBackgroundUpdateCheck(ctx) {
|
|
15781
|
+
const pending = checkForUpdate({ force: true, timeoutMs: 5e3 });
|
|
15782
|
+
ctx.pendingUpdateCheck = pending;
|
|
15783
|
+
void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch(() => void 0);
|
|
15784
|
+
}
|
|
15785
|
+
async function fetchLatestVersion(timeoutMs = 5e3) {
|
|
15786
|
+
try {
|
|
15787
|
+
const res = await fetch(registryUrl(), { signal: AbortSignal.timeout(timeoutMs) });
|
|
15788
|
+
if (!res.ok) return null;
|
|
15789
|
+
const data = await res.json();
|
|
15790
|
+
return typeof data.version === "string" && data.version.length > 0 ? data.version : null;
|
|
15791
|
+
} catch {
|
|
15792
|
+
return null;
|
|
15793
|
+
}
|
|
15794
|
+
}
|
|
15795
|
+
function buildResult(current, latest) {
|
|
15796
|
+
return {
|
|
15797
|
+
current,
|
|
15798
|
+
latest,
|
|
15799
|
+
updateAvailable: isNewerVersion(latest, current)
|
|
15800
|
+
};
|
|
15801
|
+
}
|
|
15802
|
+
async function checkForUpdate(options) {
|
|
15803
|
+
const current = getInstalledVersion();
|
|
15804
|
+
const timeoutMs = options?.timeoutMs ?? 5e3;
|
|
15805
|
+
const cached2 = loadUpdateCheckCache();
|
|
15806
|
+
if (!options?.force && isCacheFresh(cached2)) {
|
|
15807
|
+
return buildResult(current, cached2.latestVersion);
|
|
15808
|
+
}
|
|
15809
|
+
const latest = await fetchLatestVersion(timeoutMs);
|
|
15810
|
+
if (!latest) {
|
|
15811
|
+
if (cached2?.latestVersion) {
|
|
15812
|
+
return buildResult(current, cached2.latestVersion);
|
|
15813
|
+
}
|
|
15814
|
+
return null;
|
|
15815
|
+
}
|
|
15816
|
+
const nextCache = { lastCheck: Date.now(), latestVersion: latest };
|
|
15817
|
+
saveUpdateCheckCache(nextCache);
|
|
15818
|
+
return buildResult(current, latest);
|
|
15819
|
+
}
|
|
15820
|
+
var NPM_PACKAGE;
|
|
15821
|
+
var init_registry = __esm({
|
|
15822
|
+
"src/update/registry.ts"() {
|
|
15823
|
+
"use strict";
|
|
15824
|
+
init_update_check();
|
|
15825
|
+
init_version();
|
|
15826
|
+
NPM_PACKAGE = "@sonnechasser/ntrp";
|
|
15827
|
+
}
|
|
15828
|
+
});
|
|
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
|
+
|
|
15502
15949
|
// src/conversation/recommended-action.ts
|
|
15503
15950
|
function resolveRecommendedAction(ctx) {
|
|
15504
15951
|
if (!hasValidLicense()) return { submit: "/activate", hint: "/activate" };
|
|
15505
15952
|
const phase = resolveConversationPhase(ctx);
|
|
15506
15953
|
switch (phase) {
|
|
15507
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
|
+
}
|
|
15508
15962
|
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
15509
|
-
return
|
|
15963
|
+
return null;
|
|
15510
15964
|
case "awaiting_data":
|
|
15511
15965
|
if (ctx.gapAudit?.can_compute) return { submit: "go ahead", hint: "go ahead" };
|
|
15512
15966
|
if (!sessionHasData(ctx)) return { submit: "use demo data", hint: "use demo data" };
|
|
@@ -15517,6 +15971,8 @@ function resolveRecommendedAction(ctx) {
|
|
|
15517
15971
|
return ctx.strategistState?.step === "objective_confirm" ? { submit: "yes", hint: "yes" } : null;
|
|
15518
15972
|
case "think":
|
|
15519
15973
|
return null;
|
|
15974
|
+
case "orient":
|
|
15975
|
+
return hasAvailableUpdate(ctx.updateAvailable) ? { submit: "/update", hint: "/update" } : null;
|
|
15520
15976
|
default:
|
|
15521
15977
|
return null;
|
|
15522
15978
|
}
|
|
@@ -15526,7 +15982,9 @@ var init_recommended_action = __esm({
|
|
|
15526
15982
|
"use strict";
|
|
15527
15983
|
init_repl_api();
|
|
15528
15984
|
init_activation();
|
|
15985
|
+
init_registry();
|
|
15529
15986
|
init_phase();
|
|
15987
|
+
init_store3();
|
|
15530
15988
|
}
|
|
15531
15989
|
});
|
|
15532
15990
|
|
|
@@ -16324,17 +16782,17 @@ __export(play_outcomes_exports, {
|
|
|
16324
16782
|
listPlayOutcomes: () => listPlayOutcomes,
|
|
16325
16783
|
recordPlayOutcomes: () => recordPlayOutcomes
|
|
16326
16784
|
});
|
|
16327
|
-
import { existsSync as
|
|
16328
|
-
import { join as
|
|
16329
|
-
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";
|
|
16330
16788
|
function outcomesPath() {
|
|
16331
|
-
return
|
|
16789
|
+
return join24(getMemoryDir(), OUTCOMES_FILE);
|
|
16332
16790
|
}
|
|
16333
16791
|
function listPlayOutcomes() {
|
|
16334
16792
|
const path = outcomesPath();
|
|
16335
|
-
if (!
|
|
16793
|
+
if (!existsSync24(path)) return [];
|
|
16336
16794
|
const out = [];
|
|
16337
|
-
for (const line of
|
|
16795
|
+
for (const line of readFileSync20(path, "utf-8").split("\n")) {
|
|
16338
16796
|
const trimmed = line.trim();
|
|
16339
16797
|
if (!trimmed) continue;
|
|
16340
16798
|
try {
|
|
@@ -16364,7 +16822,7 @@ function recordPlayOutcomes(strategy, outcomes, batchId) {
|
|
|
16364
16822
|
if (seen.has(key)) continue;
|
|
16365
16823
|
seen.add(key);
|
|
16366
16824
|
const record = {
|
|
16367
|
-
id:
|
|
16825
|
+
id: randomUUID8(),
|
|
16368
16826
|
play_id: playId,
|
|
16369
16827
|
strategy_slug: strategy.slug,
|
|
16370
16828
|
workstream_order: outcome.workstream_order,
|
|
@@ -16427,11 +16885,11 @@ function jsonSafe(value) {
|
|
|
16427
16885
|
}
|
|
16428
16886
|
return value;
|
|
16429
16887
|
}
|
|
16430
|
-
function envelope(command, data, warnings) {
|
|
16888
|
+
function envelope(command, data, warnings, status = "ok") {
|
|
16431
16889
|
return {
|
|
16432
16890
|
schema_version: HEADLESS_SCHEMA_VERSION,
|
|
16433
16891
|
command,
|
|
16434
|
-
status
|
|
16892
|
+
status,
|
|
16435
16893
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16436
16894
|
data,
|
|
16437
16895
|
...warnings && warnings.length > 0 ? { warnings } : {}
|
|
@@ -16447,8 +16905,8 @@ function errorEnvelope(command, err) {
|
|
|
16447
16905
|
error: ntrpError.toHeadlessError()
|
|
16448
16906
|
};
|
|
16449
16907
|
}
|
|
16450
|
-
function emitResult(command, data, warnings) {
|
|
16451
|
-
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));
|
|
16452
16910
|
}
|
|
16453
16911
|
function emitError(command, err) {
|
|
16454
16912
|
const ntrpError = err instanceof NtrpError ? err : toNtrpError(err);
|
|
@@ -19801,18 +20259,18 @@ var init_terminal = __esm({
|
|
|
19801
20259
|
});
|
|
19802
20260
|
|
|
19803
20261
|
// src/demo/taxonomy-cache.ts
|
|
19804
|
-
import { readFileSync as
|
|
20262
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync25, mkdirSync as mkdirSync13, unlinkSync as unlinkSync5 } from "fs";
|
|
19805
20263
|
import { homedir as homedir7 } from "os";
|
|
19806
|
-
import { join as
|
|
19807
|
-
function
|
|
19808
|
-
if (!
|
|
19809
|
-
|
|
20264
|
+
import { join as join25 } from "path";
|
|
20265
|
+
function ensureDir7() {
|
|
20266
|
+
if (!existsSync25(NTRP_DIR4)) {
|
|
20267
|
+
mkdirSync13(NTRP_DIR4, { recursive: true });
|
|
19810
20268
|
}
|
|
19811
20269
|
}
|
|
19812
20270
|
function loadCachedTaxonomy(profile) {
|
|
19813
|
-
if (!
|
|
20271
|
+
if (!existsSync25(TAXONOMY_PATH)) return null;
|
|
19814
20272
|
try {
|
|
19815
|
-
const parsed = JSON.parse(
|
|
20273
|
+
const parsed = JSON.parse(readFileSync21(TAXONOMY_PATH, "utf-8"));
|
|
19816
20274
|
if (!parsed || typeof parsed !== "object") return null;
|
|
19817
20275
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
19818
20276
|
return parsed;
|
|
@@ -19821,13 +20279,13 @@ function loadCachedTaxonomy(profile) {
|
|
|
19821
20279
|
}
|
|
19822
20280
|
}
|
|
19823
20281
|
function saveCachedTaxonomy(taxonomy) {
|
|
19824
|
-
|
|
19825
|
-
|
|
20282
|
+
ensureDir7();
|
|
20283
|
+
writeFileSync16(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
19826
20284
|
}
|
|
19827
20285
|
function invalidateTaxonomy() {
|
|
19828
|
-
if (
|
|
20286
|
+
if (existsSync25(TAXONOMY_PATH)) {
|
|
19829
20287
|
try {
|
|
19830
|
-
|
|
20288
|
+
unlinkSync5(TAXONOMY_PATH);
|
|
19831
20289
|
} catch {
|
|
19832
20290
|
}
|
|
19833
20291
|
}
|
|
@@ -19836,8 +20294,8 @@ var NTRP_DIR4, TAXONOMY_PATH;
|
|
|
19836
20294
|
var init_taxonomy_cache = __esm({
|
|
19837
20295
|
"src/demo/taxonomy-cache.ts"() {
|
|
19838
20296
|
"use strict";
|
|
19839
|
-
NTRP_DIR4 =
|
|
19840
|
-
TAXONOMY_PATH =
|
|
20297
|
+
NTRP_DIR4 = join25(homedir7(), ".ntrp");
|
|
20298
|
+
TAXONOMY_PATH = join25(NTRP_DIR4, "demo-taxonomy.json");
|
|
19841
20299
|
}
|
|
19842
20300
|
});
|
|
19843
20301
|
|
|
@@ -20295,7 +20753,7 @@ __export(inbox_setup_exports, {
|
|
|
20295
20753
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
20296
20754
|
});
|
|
20297
20755
|
import chalk19 from "chalk";
|
|
20298
|
-
import { existsSync as
|
|
20756
|
+
import { existsSync as existsSync26 } from "fs";
|
|
20299
20757
|
function markDemoOffered() {
|
|
20300
20758
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
20301
20759
|
}
|
|
@@ -20327,7 +20785,7 @@ function printSkipHint(beat) {
|
|
|
20327
20785
|
async function reuseInboxFolderIfPresent(session, beat, folderPath) {
|
|
20328
20786
|
if (getAiInboxDir()) return false;
|
|
20329
20787
|
const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
|
|
20330
|
-
const existing = candidates.find((p) =>
|
|
20788
|
+
const existing = candidates.find((p) => existsSync26(p));
|
|
20331
20789
|
if (!existing) return false;
|
|
20332
20790
|
console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
|
|
20333
20791
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
@@ -20433,7 +20891,7 @@ __export(ingest_exports, {
|
|
|
20433
20891
|
handler: () => handler2
|
|
20434
20892
|
});
|
|
20435
20893
|
import chalk20 from "chalk";
|
|
20436
|
-
import { readFileSync as
|
|
20894
|
+
import { readFileSync as readFileSync22, existsSync as existsSync27 } from "fs";
|
|
20437
20895
|
import { basename as basename6 } from "path";
|
|
20438
20896
|
async function handler2(args, ctx) {
|
|
20439
20897
|
const { positional, flags } = parseArgs2(args, [
|
|
@@ -20457,7 +20915,7 @@ async function handler2(args, ctx) {
|
|
|
20457
20915
|
console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
|
|
20458
20916
|
process.exit(1);
|
|
20459
20917
|
}
|
|
20460
|
-
if (!
|
|
20918
|
+
if (!existsSync27(file)) {
|
|
20461
20919
|
console.error(chalk20.red(` File not found: ${file}`));
|
|
20462
20920
|
process.exit(1);
|
|
20463
20921
|
}
|
|
@@ -20475,7 +20933,7 @@ async function handler2(args, ctx) {
|
|
|
20475
20933
|
try {
|
|
20476
20934
|
await initSchema();
|
|
20477
20935
|
spinner.text = "Parsing CSV\u2026";
|
|
20478
|
-
const content =
|
|
20936
|
+
const content = readFileSync22(file, "utf-8");
|
|
20479
20937
|
const { rows, headers } = parseCSV(content);
|
|
20480
20938
|
if (rows.length === 0) {
|
|
20481
20939
|
spinner.fail("CSV is empty");
|
|
@@ -22842,9 +23300,9 @@ async function handleGetSessionBrief(input) {
|
|
|
22842
23300
|
if (!target) {
|
|
22843
23301
|
return { error: `No session matching "${raw}".` };
|
|
22844
23302
|
}
|
|
22845
|
-
const { existsSync:
|
|
23303
|
+
const { existsSync: existsSync39, readFileSync: readFileSync25 } = await import("fs");
|
|
22846
23304
|
const briefPath = contextDocPathForSession2(target.id);
|
|
22847
|
-
if (!
|
|
23305
|
+
if (!existsSync39(briefPath)) {
|
|
22848
23306
|
return {
|
|
22849
23307
|
session_id: target.id,
|
|
22850
23308
|
error: "No context brief on disk for this session (created before brief storage existed).",
|
|
@@ -22855,7 +23313,7 @@ async function handleGetSessionBrief(input) {
|
|
|
22855
23313
|
return {
|
|
22856
23314
|
session_id: target.id,
|
|
22857
23315
|
security_notice: UNTRUSTED_CONTENT_NOTICE,
|
|
22858
|
-
brief: wrapUntrustedContent(
|
|
23316
|
+
brief: wrapUntrustedContent(readFileSync25(briefPath, "utf-8"))
|
|
22859
23317
|
};
|
|
22860
23318
|
}
|
|
22861
23319
|
function auditDenied(name, input, resultJson, start) {
|
|
@@ -23388,7 +23846,9 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
|
|
|
23388
23846
|
);
|
|
23389
23847
|
} else if (phase === "orient") {
|
|
23390
23848
|
lines.push("Already done: nothing locked yet");
|
|
23391
|
-
lines.push(
|
|
23849
|
+
lines.push(
|
|
23850
|
+
"Available now: help the user name a focus; CLI will propose scope from their words. If \u23CE /update is armed, the CLI owns the install confirm \u2014 do not re-ask about updating"
|
|
23851
|
+
);
|
|
23392
23852
|
} else if (phase === "think") {
|
|
23393
23853
|
lines.push("Already done: analysis complete; think channel open");
|
|
23394
23854
|
lines.push("Available now: socratic exploration; draft_strategy / draft_handoff when ready to graduate");
|
|
@@ -25159,7 +25619,7 @@ __export(onboard_tiers_exports, {
|
|
|
25159
25619
|
resetOnboardTierProgress: () => resetOnboardTierProgress,
|
|
25160
25620
|
resolveNextOnboardTier: () => resolveNextOnboardTier
|
|
25161
25621
|
});
|
|
25162
|
-
import { existsSync as
|
|
25622
|
+
import { existsSync as existsSync28, statSync as statSync4 } from "fs";
|
|
25163
25623
|
function flagSet(tier) {
|
|
25164
25624
|
return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
|
|
25165
25625
|
}
|
|
@@ -25186,7 +25646,7 @@ function hasProductionDataset(ctx) {
|
|
|
25186
25646
|
if (source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source) || source.startsWith("~")) {
|
|
25187
25647
|
return true;
|
|
25188
25648
|
}
|
|
25189
|
-
if (!source.includes(":") &&
|
|
25649
|
+
if (!source.includes(":") && existsSync28(source)) return true;
|
|
25190
25650
|
if (source.startsWith("csv:") || source.startsWith("file:") || source.startsWith("folder:")) {
|
|
25191
25651
|
return true;
|
|
25192
25652
|
}
|
|
@@ -25245,7 +25705,7 @@ function markDemoDataSeen() {
|
|
|
25245
25705
|
}
|
|
25246
25706
|
function pathLooksPresent(raw) {
|
|
25247
25707
|
try {
|
|
25248
|
-
return
|
|
25708
|
+
return existsSync28(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
|
|
25249
25709
|
} catch {
|
|
25250
25710
|
return false;
|
|
25251
25711
|
}
|
|
@@ -25650,7 +26110,7 @@ __export(onboard_exports, {
|
|
|
25650
26110
|
profileExists: () => profileExists
|
|
25651
26111
|
});
|
|
25652
26112
|
import chalk25 from "chalk";
|
|
25653
|
-
import { existsSync as
|
|
26113
|
+
import { existsSync as existsSync29 } from "fs";
|
|
25654
26114
|
import { basename as basename7 } from "path";
|
|
25655
26115
|
async function handler5(args, ctx) {
|
|
25656
26116
|
const { flags } = parseArgs2(args, ["force", "skip-brand"]);
|
|
@@ -25925,7 +26385,7 @@ async function runProductionTier(session, ctx) {
|
|
|
25925
26385
|
return "Production data skipped";
|
|
25926
26386
|
}
|
|
25927
26387
|
const resolved = resolveUserPath(trimmed);
|
|
25928
|
-
if (!
|
|
26388
|
+
if (!existsSync29(resolved)) {
|
|
25929
26389
|
console.log(" " + chalk25.red(`Path not found: ${resolved}`));
|
|
25930
26390
|
console.log(" " + chalk25.dim("Try again with /onboard, or drop the path into the REPL."));
|
|
25931
26391
|
return "Production path not found";
|
|
@@ -26332,7 +26792,7 @@ __export(new_exports, {
|
|
|
26332
26792
|
handler: () => handler6
|
|
26333
26793
|
});
|
|
26334
26794
|
import chalk26 from "chalk";
|
|
26335
|
-
import { existsSync as
|
|
26795
|
+
import { existsSync as existsSync30 } from "fs";
|
|
26336
26796
|
import { basename as basename8 } from "path";
|
|
26337
26797
|
async function handler6(args, ctx) {
|
|
26338
26798
|
const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
|
|
@@ -26354,7 +26814,7 @@ async function handler6(args, ctx) {
|
|
|
26354
26814
|
console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
|
|
26355
26815
|
return;
|
|
26356
26816
|
}
|
|
26357
|
-
if (source.kind === "file" && !
|
|
26817
|
+
if (source.kind === "file" && !existsSync30(source.path)) {
|
|
26358
26818
|
console.error(chalk26.red(` File not found: ${source.path}`));
|
|
26359
26819
|
return;
|
|
26360
26820
|
}
|
|
@@ -26581,7 +27041,7 @@ __export(end_exports, {
|
|
|
26581
27041
|
handler: () => handler7
|
|
26582
27042
|
});
|
|
26583
27043
|
import chalk27 from "chalk";
|
|
26584
|
-
import { existsSync as
|
|
27044
|
+
import { existsSync as existsSync31 } from "fs";
|
|
26585
27045
|
async function handler7(args, ctx) {
|
|
26586
27046
|
if (args.length > 0) {
|
|
26587
27047
|
console.error(chalk27.red(" Usage: /end"));
|
|
@@ -26618,10 +27078,10 @@ async function handler7(args, ctx) {
|
|
|
26618
27078
|
if (summary) {
|
|
26619
27079
|
console.log(" " + chalk27.dim(summary));
|
|
26620
27080
|
}
|
|
26621
|
-
if (
|
|
27081
|
+
if (existsSync31(transcriptPathForSession(endedId))) {
|
|
26622
27082
|
console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
|
|
26623
27083
|
}
|
|
26624
|
-
if (
|
|
27084
|
+
if (existsSync31(contextDocPathForSession(endedId))) {
|
|
26625
27085
|
console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
|
|
26626
27086
|
}
|
|
26627
27087
|
console.log();
|
|
@@ -26642,8 +27102,8 @@ __export(session_exports, {
|
|
|
26642
27102
|
handler: () => handler8
|
|
26643
27103
|
});
|
|
26644
27104
|
import chalk28 from "chalk";
|
|
26645
|
-
import { join as
|
|
26646
|
-
import { existsSync as
|
|
27105
|
+
import { join as join26 } from "path";
|
|
27106
|
+
import { existsSync as existsSync32 } from "fs";
|
|
26647
27107
|
async function handler8(args, ctx) {
|
|
26648
27108
|
const sub = args[0];
|
|
26649
27109
|
if (!sub) return listSessionsView(ctx);
|
|
@@ -26746,7 +27206,7 @@ async function pickUp(idArg, ctx) {
|
|
|
26746
27206
|
}
|
|
26747
27207
|
resetContextForSwitch(ctx, {
|
|
26748
27208
|
sessionId: target.id,
|
|
26749
|
-
sessionFile:
|
|
27209
|
+
sessionFile: join26(getSessionsDir(), `${target.id}.json`),
|
|
26750
27210
|
sessionName: session.name,
|
|
26751
27211
|
messages: [...session.messages],
|
|
26752
27212
|
conversation: session.thread ? [...session.thread] : [],
|
|
@@ -26761,7 +27221,8 @@ async function pickUp(idArg, ctx) {
|
|
|
26761
27221
|
llm: session.llm ? { ...session.llm } : void 0,
|
|
26762
27222
|
strategistState: session.strategist,
|
|
26763
27223
|
thinkState: session.think,
|
|
26764
|
-
pendingAsk: session.pending_ask
|
|
27224
|
+
pendingAsk: session.pending_ask,
|
|
27225
|
+
lastCraftJobId: session.last_craft_job_id
|
|
26765
27226
|
});
|
|
26766
27227
|
ctx.datasetPath = datasetPathForSession(target.id);
|
|
26767
27228
|
await setActiveDbPath(ctx.datasetPath);
|
|
@@ -26779,7 +27240,11 @@ async function pickUp(idArg, ctx) {
|
|
|
26779
27240
|
if (session.strategist && session.strategist.step !== "awaiting_analysis") {
|
|
26780
27241
|
const objective = session.strategist.objective;
|
|
26781
27242
|
console.log(
|
|
26782
|
-
" " + 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.")
|
|
26783
27248
|
);
|
|
26784
27249
|
}
|
|
26785
27250
|
if (session.think && session.think.step === "active") {
|
|
@@ -26789,7 +27254,7 @@ async function pickUp(idArg, ctx) {
|
|
|
26789
27254
|
);
|
|
26790
27255
|
}
|
|
26791
27256
|
const contextPath = contextDocPathForSession(target.id);
|
|
26792
|
-
if (
|
|
27257
|
+
if (existsSync32(contextPath)) {
|
|
26793
27258
|
console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
|
|
26794
27259
|
}
|
|
26795
27260
|
console.log();
|
|
@@ -26804,6 +27269,7 @@ var init_session = __esm({
|
|
|
26804
27269
|
init_connection();
|
|
26805
27270
|
init_theme();
|
|
26806
27271
|
init_layout();
|
|
27272
|
+
init_store3();
|
|
26807
27273
|
GENERIC_DATASET_RE = /^(demo dataset|.*\bdemo)$/i;
|
|
26808
27274
|
}
|
|
26809
27275
|
});
|
|
@@ -27080,8 +27546,8 @@ __export(report_exports, {
|
|
|
27080
27546
|
handler: () => handler9
|
|
27081
27547
|
});
|
|
27082
27548
|
import chalk29 from "chalk";
|
|
27083
|
-
import { mkdirSync as
|
|
27084
|
-
import { dirname as
|
|
27549
|
+
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync17 } from "fs";
|
|
27550
|
+
import { dirname as dirname5 } from "path";
|
|
27085
27551
|
async function handler9(args, ctx) {
|
|
27086
27552
|
const { flags } = parseArgs2(args);
|
|
27087
27553
|
const format = getString(flags, "format", "f") ?? getConfigValue("default-format") ?? "terminal";
|
|
@@ -27174,10 +27640,10 @@ async function handler9(args, ctx) {
|
|
|
27174
27640
|
if (output) {
|
|
27175
27641
|
const resolvedOutput = resolveUserPath(output);
|
|
27176
27642
|
if (!isInsideNtrp(resolvedOutput)) {
|
|
27177
|
-
console.warn(chalk29.yellow(` Warning: writing report outside NTRP home (${
|
|
27643
|
+
console.warn(chalk29.yellow(` Warning: writing report outside NTRP home (${dirname5(resolvedOutput)})`));
|
|
27178
27644
|
}
|
|
27179
|
-
|
|
27180
|
-
|
|
27645
|
+
mkdirSync14(dirname5(resolvedOutput), { recursive: true });
|
|
27646
|
+
writeFileSync17(resolvedOutput, rendered);
|
|
27181
27647
|
console.log(chalk29.green(` Report written to ${resolvedOutput}`));
|
|
27182
27648
|
} else if (rendered) {
|
|
27183
27649
|
console.log(rendered);
|
|
@@ -27208,8 +27674,8 @@ var init_report2 = __esm({
|
|
|
27208
27674
|
});
|
|
27209
27675
|
|
|
27210
27676
|
// src/output/notes-export.ts
|
|
27211
|
-
import { mkdirSync as
|
|
27212
|
-
import { join as
|
|
27677
|
+
import { mkdirSync as mkdirSync15 } from "fs";
|
|
27678
|
+
import { join as join27 } from "path";
|
|
27213
27679
|
function exportToNotes(data) {
|
|
27214
27680
|
const { computeResult, divergences, findings, exchanges } = data;
|
|
27215
27681
|
const { aggregate, segments } = computeResult;
|
|
@@ -27218,8 +27684,8 @@ function exportToNotes(data) {
|
|
|
27218
27684
|
const timeStr = formatTime(now2);
|
|
27219
27685
|
const filename = `${dateStr}-${timeStr}-gtm-health.md`;
|
|
27220
27686
|
const dir = data.dir ?? getArchiveKindDir("notes");
|
|
27221
|
-
|
|
27222
|
-
const filepath =
|
|
27687
|
+
mkdirSync15(dir, { recursive: true });
|
|
27688
|
+
const filepath = join27(dir, filename);
|
|
27223
27689
|
const severityTags = /* @__PURE__ */ new Set();
|
|
27224
27690
|
for (const f of findings) severityTags.add(f.severity);
|
|
27225
27691
|
const tags = ["ntrp", "gtm-health", ...severityTags];
|
|
@@ -27464,8 +27930,8 @@ __export(backmeup_exports, {
|
|
|
27464
27930
|
});
|
|
27465
27931
|
import chalk31 from "chalk";
|
|
27466
27932
|
import Papa5 from "papaparse";
|
|
27467
|
-
import { mkdirSync as
|
|
27468
|
-
import { join as
|
|
27933
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync18 } from "fs";
|
|
27934
|
+
import { join as join28 } from "path";
|
|
27469
27935
|
function sanitizeCsvValue(value) {
|
|
27470
27936
|
if (typeof value !== "string") return value;
|
|
27471
27937
|
return CSV_FORMULA_RE.test(value) ? `'${value}` : value;
|
|
@@ -27501,8 +27967,8 @@ async function handler11(args, _ctx) {
|
|
|
27501
27967
|
if (!isInsideNtrp(baseDir)) {
|
|
27502
27968
|
console.warn(chalk31.yellow(` Warning: writing backup outside NTRP home (${baseDir})`));
|
|
27503
27969
|
}
|
|
27504
|
-
const folder =
|
|
27505
|
-
|
|
27970
|
+
const folder = join28(baseDir, folderName);
|
|
27971
|
+
mkdirSync16(folder, { recursive: true });
|
|
27506
27972
|
const generatedAt = now2.toISOString();
|
|
27507
27973
|
let fileCount = 0;
|
|
27508
27974
|
const coverRows = health.vital_signs.map((vs) => ({
|
|
@@ -27516,7 +27982,7 @@ async function handler11(args, _ctx) {
|
|
|
27516
27982
|
"Total At Risk": health.total_value_at_risk != null ? formatCurrency(health.total_value_at_risk) : "N/A",
|
|
27517
27983
|
"Generated At": generatedAt
|
|
27518
27984
|
}));
|
|
27519
|
-
|
|
27985
|
+
writeFileSync18(join28(folder, "cover-sheet.csv"), Papa5.unparse(sanitizeCsvRows(coverRows)), "utf-8");
|
|
27520
27986
|
fileCount++;
|
|
27521
27987
|
if (findings.length > 0) {
|
|
27522
27988
|
const findingsRows = findings.map((f) => ({
|
|
@@ -27526,7 +27992,7 @@ async function handler11(args, _ctx) {
|
|
|
27526
27992
|
Finding: f.finding,
|
|
27527
27993
|
"Recommended Plays": f.recommended_plays ? f.recommended_plays.map((p) => p.play_name).join("; ") : ""
|
|
27528
27994
|
}));
|
|
27529
|
-
|
|
27995
|
+
writeFileSync18(join28(folder, "findings.csv"), Papa5.unparse(sanitizeCsvRows(findingsRows)), "utf-8");
|
|
27530
27996
|
fileCount++;
|
|
27531
27997
|
}
|
|
27532
27998
|
for (const vs of health.vital_signs) {
|
|
@@ -27536,7 +28002,7 @@ async function handler11(args, _ctx) {
|
|
|
27536
28002
|
...detail
|
|
27537
28003
|
}));
|
|
27538
28004
|
const filename = EVIDENCE_FILENAMES[vs.vital_sign] ?? `${vs.vital_sign}.csv`;
|
|
27539
|
-
|
|
28005
|
+
writeFileSync18(join28(folder, filename), Papa5.unparse(sanitizeCsvRows(rows)), "utf-8");
|
|
27540
28006
|
fileCount++;
|
|
27541
28007
|
}
|
|
27542
28008
|
const event = recordExportWrite({
|
|
@@ -27740,8 +28206,8 @@ var init_bundle = __esm({
|
|
|
27740
28206
|
});
|
|
27741
28207
|
|
|
27742
28208
|
// src/repositories/markdown.ts
|
|
27743
|
-
import { mkdirSync as
|
|
27744
|
-
import { basename as basename9, dirname 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";
|
|
27745
28211
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
27746
28212
|
function renderMarkdownFiles(pkg) {
|
|
27747
28213
|
const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -27961,12 +28427,12 @@ var init_markdown3 = __esm({
|
|
|
27961
28427
|
write(pkg) {
|
|
27962
28428
|
const root = getRootPath(pkg.target);
|
|
27963
28429
|
const files = renderMarkdownFiles(pkg);
|
|
27964
|
-
|
|
28430
|
+
mkdirSync17(root, { recursive: true });
|
|
27965
28431
|
const written = [];
|
|
27966
28432
|
for (const file of files) {
|
|
27967
|
-
const absolutePath =
|
|
27968
|
-
|
|
27969
|
-
|
|
28433
|
+
const absolutePath = join29(root, file.relativePath);
|
|
28434
|
+
mkdirSync17(dirname6(absolutePath), { recursive: true });
|
|
28435
|
+
writeFileSync19(absolutePath, file.contents, "utf-8");
|
|
27970
28436
|
written.push(absolutePath);
|
|
27971
28437
|
}
|
|
27972
28438
|
return {
|
|
@@ -28270,7 +28736,7 @@ __export(handoff_exports, {
|
|
|
28270
28736
|
handler: () => handler13
|
|
28271
28737
|
});
|
|
28272
28738
|
import chalk33 from "chalk";
|
|
28273
|
-
import { join as
|
|
28739
|
+
import { join as join30 } from "path";
|
|
28274
28740
|
async function handler13(args, ctx) {
|
|
28275
28741
|
const sub = args[0];
|
|
28276
28742
|
if (!sub) {
|
|
@@ -28370,7 +28836,7 @@ async function runPublish2(args, ctx) {
|
|
|
28370
28836
|
);
|
|
28371
28837
|
if (sub === "propose" && !hasDir) {
|
|
28372
28838
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
28373
|
-
const dir =
|
|
28839
|
+
const dir = join30(getArchiveKindDir("publish"), `ntrp-repository-${stamp}`);
|
|
28374
28840
|
publishArgs.push("--dir", dir);
|
|
28375
28841
|
}
|
|
28376
28842
|
const result = await publish(publishArgs, ctx);
|
|
@@ -29145,150 +29611,6 @@ var init_segment = __esm({
|
|
|
29145
29611
|
}
|
|
29146
29612
|
});
|
|
29147
29613
|
|
|
29148
|
-
// src/services/strategist.ts
|
|
29149
|
-
import { createHash as createHash2 } from "crypto";
|
|
29150
|
-
function serializeGapAudit(audit) {
|
|
29151
|
-
const lines = [`Can compute: ${audit.can_compute} (lens: ${audit.primary_lens})`];
|
|
29152
|
-
for (const item of audit.satisfied) {
|
|
29153
|
-
lines.push(`- HAVE ${item.label}: ${item.detail}`);
|
|
29154
|
-
}
|
|
29155
|
-
for (const item of audit.missing) {
|
|
29156
|
-
lines.push(`- MISSING ${item.label}: ${item.why}`);
|
|
29157
|
-
}
|
|
29158
|
-
for (const item of audit.optional) {
|
|
29159
|
-
lines.push(`- LIMITED ${item.label}: ${item.detail}`);
|
|
29160
|
-
}
|
|
29161
|
-
return lines.join("\n");
|
|
29162
|
-
}
|
|
29163
|
-
async function prepareStrategistInputs(ctx, objective) {
|
|
29164
|
-
let snapshot = ctx.snapshot.computeResult;
|
|
29165
|
-
if (!snapshot) {
|
|
29166
|
-
snapshot = await computeFullHealth();
|
|
29167
|
-
ctx.snapshot.computeResult = snapshot;
|
|
29168
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
29169
|
-
segmentId: s.segment.id,
|
|
29170
|
-
segmentName: s.segment.name,
|
|
29171
|
-
result: s.result
|
|
29172
|
-
}));
|
|
29173
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
29174
|
-
}
|
|
29175
|
-
const audit = ctx.gapAudit ?? await refreshGapAudit(ctx).catch(() => null);
|
|
29176
|
-
const gapAuditBlock = audit ? serializeGapAudit(audit) : "";
|
|
29177
|
-
const memoryBlock = await Promise.resolve().then(() => (init_store2(), store_exports2)).then((m) => m.buildMemoryBlock(objective)).catch(() => "");
|
|
29178
|
-
let baselineBatchId = null;
|
|
29179
|
-
try {
|
|
29180
|
-
const reading = await getLatestHealthReading();
|
|
29181
|
-
baselineBatchId = reading?.upload_batch_id ?? null;
|
|
29182
|
-
} catch {
|
|
29183
|
-
}
|
|
29184
|
-
return {
|
|
29185
|
-
snapshot,
|
|
29186
|
-
divergences: ctx.snapshot.divergences,
|
|
29187
|
-
gapAuditBlock,
|
|
29188
|
-
memoryBlock,
|
|
29189
|
-
baselineBatchId,
|
|
29190
|
-
includeMetrics: true
|
|
29191
|
-
};
|
|
29192
|
-
}
|
|
29193
|
-
function proposeObjectiveFromSnapshot(snapshot) {
|
|
29194
|
-
const { aggregate } = snapshot;
|
|
29195
|
-
const gating = aggregate.gating_vital_sign;
|
|
29196
|
-
if (!gating) return null;
|
|
29197
|
-
const vital = aggregate.vital_signs.find((v) => v.vital_sign === gating);
|
|
29198
|
-
if (!vital) return null;
|
|
29199
|
-
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
29200
|
-
const dollar = vital.dollar_value != null && vital.dollar_value > 0 ? ` and recover the ${formatCurrency(vital.dollar_value)} ${vital.dollar_label ?? "at stake"}` : "";
|
|
29201
|
-
return `Move ${label} from ${Math.round(vital.score)} to 60+${dollar} within 60 days`;
|
|
29202
|
-
}
|
|
29203
|
-
function slugify2(value) {
|
|
29204
|
-
const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
29205
|
-
return slug || `strategy-${Date.now()}`;
|
|
29206
|
-
}
|
|
29207
|
-
function outcomeToMetric(outcome) {
|
|
29208
|
-
return {
|
|
29209
|
-
name: outcome.metric,
|
|
29210
|
-
target: outcome.target_range,
|
|
29211
|
-
baseline: outcome.baseline,
|
|
29212
|
-
timeframe: `by ${outcome.check_date}`
|
|
29213
|
-
};
|
|
29214
|
-
}
|
|
29215
|
-
async function persistStrategistPlan(plan, opts = {}) {
|
|
29216
|
-
await initSchema();
|
|
29217
|
-
const slug = slugify2(plan.title);
|
|
29218
|
-
const libraryPath = strategyLibraryPath(slug);
|
|
29219
|
-
const linkedPlayIds = [...new Set(plan.workstreams.flatMap((ws) => ws.play_ids))];
|
|
29220
|
-
const successMetrics = plan.workstreams.map((ws) => outcomeToMetric(ws.expected_outcome));
|
|
29221
|
-
const leadingIndicators = plan.workstreams.flatMap((ws) => ws.leading_indicators.map(outcomeToMetric));
|
|
29222
|
-
const recommendedActions = plan.workstreams.flatMap((ws) => ws.actions.map((action) => `[WS${ws.order}] ${action}`)).slice(0, 15);
|
|
29223
|
-
const reviewProtocol = [
|
|
29224
|
-
`Review ${plan.review_cadence.toLowerCase()} with /strategy review ${slug}.`,
|
|
29225
|
-
`Check each milestone at its due date against the named verification method.`,
|
|
29226
|
-
`At each outcome check date, compare the measured value to its target range against baseline batch ${opts.baselineBatchId ?? "(latest)"}.`,
|
|
29227
|
-
`If a contingency trigger fires, activate the pre-agreed fallback.`
|
|
29228
|
-
].join(" ");
|
|
29229
|
-
const id = await upsertStrategy({
|
|
29230
|
-
slug,
|
|
29231
|
-
title: plan.title,
|
|
29232
|
-
status: opts.status ?? "active",
|
|
29233
|
-
source_type: "agent",
|
|
29234
|
-
source_path: null,
|
|
29235
|
-
goal: plan.objective,
|
|
29236
|
-
hypothesis: plan.hypothesis,
|
|
29237
|
-
target_segment: plan.target_segment,
|
|
29238
|
-
priority: plan.priority,
|
|
29239
|
-
linked_play_ids: linkedPlayIds,
|
|
29240
|
-
success_metrics: successMetrics,
|
|
29241
|
-
leading_indicators: leadingIndicators,
|
|
29242
|
-
risks: plan.risks,
|
|
29243
|
-
recommended_actions: recommendedActions,
|
|
29244
|
-
experiment_design: reviewProtocol,
|
|
29245
|
-
review_cadence: plan.review_cadence,
|
|
29246
|
-
confidence: plan.confidence,
|
|
29247
|
-
raw_excerpt: plan.summary_30k,
|
|
29248
|
-
library_path: libraryPath,
|
|
29249
|
-
origin: "strategist",
|
|
29250
|
-
objective: plan.objective,
|
|
29251
|
-
constraints: plan.constraints,
|
|
29252
|
-
workstreams: plan.workstreams,
|
|
29253
|
-
assumptions: plan.assumptions,
|
|
29254
|
-
baseline_batch_id: opts.baselineBatchId ?? null
|
|
29255
|
-
});
|
|
29256
|
-
const strategy = await getStrategyBySlugOrId(id);
|
|
29257
|
-
if (!strategy) {
|
|
29258
|
-
throw new NtrpError("strategy_persist_failed", "Strategy was not found after saving.", 1 /* RuntimeError */);
|
|
29259
|
-
}
|
|
29260
|
-
const writtenPath = writeStrategyMarkdown(strategy);
|
|
29261
|
-
await insertStrategySource({
|
|
29262
|
-
strategy_id: strategy.id,
|
|
29263
|
-
source_type: "agent",
|
|
29264
|
-
source_path: null,
|
|
29265
|
-
content_hash: createHash2("sha256").update(JSON.stringify(plan)).digest("hex"),
|
|
29266
|
-
extracted_text_excerpt: plan.summary_30k.slice(0, 800),
|
|
29267
|
-
metadata: {
|
|
29268
|
-
origin: "strategist",
|
|
29269
|
-
objective: plan.objective,
|
|
29270
|
-
workstream_count: plan.workstreams.length,
|
|
29271
|
-
baseline_batch_id: opts.baselineBatchId ?? null
|
|
29272
|
-
}
|
|
29273
|
-
});
|
|
29274
|
-
return { strategy: { ...strategy, library_path: writtenPath }, library_path: writtenPath };
|
|
29275
|
-
}
|
|
29276
|
-
var init_strategist = __esm({
|
|
29277
|
-
"src/services/strategist.ts"() {
|
|
29278
|
-
"use strict";
|
|
29279
|
-
init_schema();
|
|
29280
|
-
init_queries();
|
|
29281
|
-
init_health_score();
|
|
29282
|
-
init_divergence();
|
|
29283
|
-
init_gap_audit();
|
|
29284
|
-
init_library();
|
|
29285
|
-
init_errors2();
|
|
29286
|
-
init_types2();
|
|
29287
|
-
init_formatters();
|
|
29288
|
-
init_formatters();
|
|
29289
|
-
}
|
|
29290
|
-
});
|
|
29291
|
-
|
|
29292
29614
|
// src/ai/strategist-prompt.ts
|
|
29293
29615
|
function companyContextSection4() {
|
|
29294
29616
|
const block = buildCompanyProfileBlock();
|
|
@@ -29427,6 +29749,52 @@ ${AAR_BLOCK}
|
|
|
29427
29749
|
|
|
29428
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.`;
|
|
29429
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
|
+
}
|
|
29430
29798
|
var STRATEGIST_PLAN_SCHEMA_BLOCK;
|
|
29431
29799
|
var init_strategist_prompt = __esm({
|
|
29432
29800
|
"src/ai/strategist-prompt.ts"() {
|
|
@@ -29954,6 +30322,297 @@ var init_strategist_validate = __esm({
|
|
|
29954
30322
|
}
|
|
29955
30323
|
});
|
|
29956
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
|
+
|
|
29957
30616
|
// src/ai/strategist.ts
|
|
29958
30617
|
function buildHealthSnapshot(computeResult, divergences) {
|
|
29959
30618
|
const { aggregate, segments } = computeResult;
|
|
@@ -30176,6 +30835,15 @@ Respond with ONLY the corrected plan JSON object in the required schema (title,
|
|
|
30176
30835
|
for (const issue of validated.issues) {
|
|
30177
30836
|
yield { type: "notice", text: issue };
|
|
30178
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
|
+
}
|
|
30179
30847
|
yield {
|
|
30180
30848
|
type: "plan",
|
|
30181
30849
|
plan: validated.plan,
|
|
@@ -30212,7 +30880,7 @@ function describePlanValidationFailure(text, evidenceText, todayIso) {
|
|
|
30212
30880
|
return "unknown validation failure";
|
|
30213
30881
|
}
|
|
30214
30882
|
var GROUND_MAX_ROUNDS, BACKCAST_MAX_ROUNDS, STRESS_MAX_ROUNDS, STAGE_MAX_TOKENS, PLAN_JSON_MAX_TOKENS, STAGE_LABELS;
|
|
30215
|
-
var
|
|
30883
|
+
var init_strategist = __esm({
|
|
30216
30884
|
"src/ai/strategist.ts"() {
|
|
30217
30885
|
"use strict";
|
|
30218
30886
|
init_types();
|
|
@@ -30227,6 +30895,7 @@ var init_strategist2 = __esm({
|
|
|
30227
30895
|
init_thread();
|
|
30228
30896
|
init_strategist_prompt();
|
|
30229
30897
|
init_strategist_validate();
|
|
30898
|
+
init_strategist_rubric();
|
|
30230
30899
|
init_strategist_prompt();
|
|
30231
30900
|
GROUND_MAX_ROUNDS = 6;
|
|
30232
30901
|
BACKCAST_MAX_ROUNDS = 4;
|
|
@@ -30241,6 +30910,803 @@ var init_strategist2 = __esm({
|
|
|
30241
30910
|
}
|
|
30242
30911
|
});
|
|
30243
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
|
+
|
|
30244
31710
|
// src/output/strategy-brief.ts
|
|
30245
31711
|
import chalk36 from "chalk";
|
|
30246
31712
|
function printWrapped(text, width, prefix = INDENT, style) {
|
|
@@ -30332,6 +31798,40 @@ function printStrategyBrief(plan, stats) {
|
|
|
30332
31798
|
console.log(`${INDENT}${coverageStyled}${chalk36.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
|
|
30333
31799
|
console.log();
|
|
30334
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
|
+
}
|
|
30335
31835
|
var INDENT;
|
|
30336
31836
|
var init_strategy_brief = __esm({
|
|
30337
31837
|
"src/output/strategy-brief.ts"() {
|
|
@@ -30346,7 +31846,9 @@ var init_strategy_brief = __esm({
|
|
|
30346
31846
|
var strategist_flow_exports = {};
|
|
30347
31847
|
__export(strategist_flow_exports, {
|
|
30348
31848
|
extractObjectiveSeed: () => extractObjectiveSeed,
|
|
31849
|
+
handleKeepGoingLine: () => handleKeepGoingLine,
|
|
30349
31850
|
handleStrategizeFlow: () => handleStrategizeFlow,
|
|
31851
|
+
isKeepGoingIntent: () => isKeepGoingIntent,
|
|
30350
31852
|
isStrategistIntent: () => isStrategistIntent,
|
|
30351
31853
|
promptQueuedAiStrategist: () => promptQueuedAiStrategist,
|
|
30352
31854
|
queueStrategistForAnalysis: () => queueStrategistForAnalysis,
|
|
@@ -30369,7 +31871,8 @@ function queueStrategistForAnalysis(ctx, opts) {
|
|
|
30369
31871
|
ctx.strategistState = {
|
|
30370
31872
|
step: "awaiting_analysis",
|
|
30371
31873
|
objective: opts.seed,
|
|
30372
|
-
origin: opts.origin
|
|
31874
|
+
origin: opts.origin,
|
|
31875
|
+
mode: opts.mode
|
|
30373
31876
|
};
|
|
30374
31877
|
saveSessionState(ctx);
|
|
30375
31878
|
console.log();
|
|
@@ -30394,7 +31897,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
30394
31897
|
objective = (snapshot ? proposeObjectiveFromSnapshot(snapshot) : null) ?? "";
|
|
30395
31898
|
}
|
|
30396
31899
|
if (!objective) {
|
|
30397
|
-
ctx.strategistState = { step: "objective_input", origin: opts.origin };
|
|
31900
|
+
ctx.strategistState = { step: "objective_input", origin: opts.origin, mode: opts.mode };
|
|
30398
31901
|
saveSessionState(ctx);
|
|
30399
31902
|
console.log();
|
|
30400
31903
|
console.log(" " + chalk37.dim('What is the objective? State a finish line. Example: "cut stale pipeline in half before Q4".'));
|
|
@@ -30402,7 +31905,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
30402
31905
|
recordMessage(ctx, "agent", "Strategist: asked for objective");
|
|
30403
31906
|
return "Awaiting objective";
|
|
30404
31907
|
}
|
|
30405
|
-
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin };
|
|
31908
|
+
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin, mode: opts.mode };
|
|
30406
31909
|
saveSessionState(ctx);
|
|
30407
31910
|
printObjectiveCard(ctx, objective, !opts.seed);
|
|
30408
31911
|
recordMessage(ctx, "agent", `Strategist objective proposed: ${objective}`);
|
|
@@ -30415,7 +31918,8 @@ async function resumeStrategistAfterCompute(ctx) {
|
|
|
30415
31918
|
console.log(" " + paint("accent", "Analysis is ready. The strategy session continues."));
|
|
30416
31919
|
await startStrategistFlow(ctx, {
|
|
30417
31920
|
seed: state2.objective,
|
|
30418
|
-
origin: state2.origin ?? "nl"
|
|
31921
|
+
origin: state2.origin ?? "nl",
|
|
31922
|
+
mode: state2.mode
|
|
30419
31923
|
});
|
|
30420
31924
|
}
|
|
30421
31925
|
function promptQueuedAiStrategist(ctx) {
|
|
@@ -30425,6 +31929,64 @@ function promptQueuedAiStrategist(ctx) {
|
|
|
30425
31929
|
}
|
|
30426
31930
|
printObjectiveCard(ctx, state2.objective, true);
|
|
30427
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
|
+
}
|
|
30428
31990
|
async function handleStrategizeFlow(input, ctx) {
|
|
30429
31991
|
const state2 = ctx.strategistState;
|
|
30430
31992
|
if (!state2) return;
|
|
@@ -30451,6 +32013,11 @@ async function handleStrategizeFlow(input, ctx) {
|
|
|
30451
32013
|
printObjectiveCard(ctx, state2.objective, false);
|
|
30452
32014
|
return "Objective proposed";
|
|
30453
32015
|
}
|
|
32016
|
+
if (CRAFT_RE.test(line)) {
|
|
32017
|
+
state2.mode = "craft";
|
|
32018
|
+
saveSessionState(ctx);
|
|
32019
|
+
return runStrategistSession(ctx);
|
|
32020
|
+
}
|
|
30454
32021
|
if (CONFIRM_RE.test(line)) {
|
|
30455
32022
|
return runStrategistSession(ctx);
|
|
30456
32023
|
}
|
|
@@ -30501,7 +32068,7 @@ async function runStrategistSession(ctx) {
|
|
|
30501
32068
|
saveSessionState(ctx);
|
|
30502
32069
|
return "Skeleton plan (awaiting connect)";
|
|
30503
32070
|
}
|
|
30504
|
-
if (ctx.rl && !state2.constraintsNote) {
|
|
32071
|
+
if (ctx.rl && !state2.constraintsNote && state2.mode !== "craft") {
|
|
30505
32072
|
const prompts = createPromptSession(ctx.rl, ctx);
|
|
30506
32073
|
try {
|
|
30507
32074
|
const note = await prompts.ask(
|
|
@@ -30515,7 +32082,39 @@ async function runStrategistSession(ctx) {
|
|
|
30515
32082
|
}
|
|
30516
32083
|
}
|
|
30517
32084
|
console.log();
|
|
30518
|
-
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
|
+
}
|
|
30519
32118
|
let plan = null;
|
|
30520
32119
|
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
30521
32120
|
let baselineBatchId = null;
|
|
@@ -30599,7 +32198,10 @@ async function runStrategistSession(ctx) {
|
|
|
30599
32198
|
}
|
|
30600
32199
|
if (saved) {
|
|
30601
32200
|
try {
|
|
30602
|
-
const persisted = await persistStrategistPlan(plan, {
|
|
32201
|
+
const persisted = await persistStrategistPlan(plan, {
|
|
32202
|
+
baselineBatchId,
|
|
32203
|
+
constraintLine: formatConstraintLine(ctx.snapshot.computeResult?.aggregate)
|
|
32204
|
+
});
|
|
30603
32205
|
ctx.deliverables.push({
|
|
30604
32206
|
kind: "strategy",
|
|
30605
32207
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -30647,19 +32249,54 @@ async function ensureSnapshot(ctx) {
|
|
|
30647
32249
|
return null;
|
|
30648
32250
|
}
|
|
30649
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
|
+
}
|
|
30650
32279
|
function printObjectiveCard(ctx, objective, proposed) {
|
|
32280
|
+
const craftMode = ctx.strategistState?.mode === "craft";
|
|
30651
32281
|
console.log();
|
|
30652
32282
|
console.log(" " + chalk37.bold("Strategy session"));
|
|
30653
32283
|
console.log(
|
|
30654
32284
|
" " + chalk37.dim(proposed ? "Proposed objective: " : "Objective: ") + paint("accent", objective)
|
|
30655
32285
|
);
|
|
30656
32286
|
console.log(
|
|
30657
|
-
" " + 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
|
+
)
|
|
30658
32290
|
);
|
|
30659
32291
|
console.log();
|
|
30660
32292
|
console.log(
|
|
30661
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")
|
|
30662
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
|
+
}
|
|
30663
32300
|
console.log();
|
|
30664
32301
|
}
|
|
30665
32302
|
async function printKeylessSkeletonPlan(ctx, objective) {
|
|
@@ -30721,7 +32358,7 @@ async function resumeStrategistAfterConnect(ctx) {
|
|
|
30721
32358
|
printObjectiveCard(ctx, state2.objective, true);
|
|
30722
32359
|
return true;
|
|
30723
32360
|
}
|
|
30724
|
-
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;
|
|
30725
32362
|
var init_strategist_flow = __esm({
|
|
30726
32363
|
"src/conversation/strategist-flow.ts"() {
|
|
30727
32364
|
"use strict";
|
|
@@ -30733,17 +32370,20 @@ var init_strategist_flow = __esm({
|
|
|
30733
32370
|
init_prompts();
|
|
30734
32371
|
init_health_score();
|
|
30735
32372
|
init_divergence();
|
|
30736
|
-
init_strategist();
|
|
30737
32373
|
init_strategist2();
|
|
32374
|
+
init_strategist();
|
|
32375
|
+
init_strategist_run();
|
|
30738
32376
|
init_strategy_brief();
|
|
30739
32377
|
init_llm_attribution();
|
|
30740
32378
|
init_time_bank();
|
|
30741
32379
|
init_formatters();
|
|
32380
|
+
init_store3();
|
|
30742
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;
|
|
30743
32382
|
CANCEL_RE = /^(cancel|stop|quit|abort|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
|
|
30744
32383
|
CONFIRM_RE = /^(y|yes|yep|yeah|confirm|go|go ahead|do it|proceed|sounds good|looks good|lgtm|ok|okay)\b/i;
|
|
30745
32384
|
ADJUST_RE = /^(n|no|adjust|change|edit|different|not quite|refine)\b/i;
|
|
30746
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;
|
|
30747
32387
|
}
|
|
30748
32388
|
});
|
|
30749
32389
|
|
|
@@ -30756,8 +32396,8 @@ __export(strategy_review_exports, {
|
|
|
30756
32396
|
persistStrategyReview: () => persistStrategyReview,
|
|
30757
32397
|
resolveReviewableStrategy: () => resolveReviewableStrategy
|
|
30758
32398
|
});
|
|
30759
|
-
import { writeFileSync as
|
|
30760
|
-
import { join as
|
|
32399
|
+
import { writeFileSync as writeFileSync20 } from "fs";
|
|
32400
|
+
import { join as join31 } from "path";
|
|
30761
32401
|
function groupByBatch(vitals, metrics) {
|
|
30762
32402
|
const map = /* @__PURE__ */ new Map();
|
|
30763
32403
|
const order = [];
|
|
@@ -31047,7 +32687,7 @@ async function persistStrategyReview(report, milestoneVerdicts, notes) {
|
|
|
31047
32687
|
}
|
|
31048
32688
|
function logWin(strategy, hits, milestoneWins) {
|
|
31049
32689
|
const date = isoToday();
|
|
31050
|
-
const path =
|
|
32690
|
+
const path = join31(getWinsDir(), `${strategy.slug}-${date}.md`);
|
|
31051
32691
|
const lines = [
|
|
31052
32692
|
`# Win \u2014 ${strategy.title}`,
|
|
31053
32693
|
"",
|
|
@@ -31064,7 +32704,7 @@ function logWin(strategy, hits, milestoneWins) {
|
|
|
31064
32704
|
for (const label of milestoneWins) lines.push(`- ${label}`);
|
|
31065
32705
|
}
|
|
31066
32706
|
lines.push("", "_Logged by /strategy review._", "");
|
|
31067
|
-
|
|
32707
|
+
writeFileSync20(path, lines.join("\n"));
|
|
31068
32708
|
return path;
|
|
31069
32709
|
}
|
|
31070
32710
|
var VITAL_TOKENS, COMPONENT_TOKENS;
|
|
@@ -31296,9 +32936,15 @@ __export(strategy_exports2, {
|
|
|
31296
32936
|
});
|
|
31297
32937
|
import chalk39 from "chalk";
|
|
31298
32938
|
async function handler16(args, ctx) {
|
|
31299
|
-
const { positional, flags } = parseArgs2(args, ["no-ai"]);
|
|
32939
|
+
const { positional, flags } = parseArgs2(args, ["no-ai", "save", "handoff"]);
|
|
31300
32940
|
const first = positional[0];
|
|
31301
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
|
+
}
|
|
31302
32948
|
if (interactive && (!first || !RESERVED_SUBCOMMANDS.has(first))) {
|
|
31303
32949
|
const seed = positional.join(" ").trim() || void 0;
|
|
31304
32950
|
const { startStrategistFlow: startStrategistFlow2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
@@ -31309,14 +32955,18 @@ async function handler16(args, ctx) {
|
|
|
31309
32955
|
try {
|
|
31310
32956
|
const result = await runStrategy(sub, positional, flags, ctx);
|
|
31311
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
|
+
}
|
|
31312
32962
|
emitResult("strategy", result);
|
|
31313
32963
|
return;
|
|
31314
32964
|
}
|
|
31315
32965
|
renderStrategyResult(result);
|
|
31316
32966
|
} catch (err) {
|
|
31317
|
-
if (isStructuredOutput(ctx.execution)) emitError("strategy", err);
|
|
32967
|
+
if (isStructuredOutput(ctx.execution)) emitError(first === "craft" ? "strategy.craft" : "strategy", err);
|
|
31318
32968
|
console.error(chalk39.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
31319
|
-
process.exit(1);
|
|
32969
|
+
process.exit(err instanceof NtrpError ? err.exitCode : 1);
|
|
31320
32970
|
}
|
|
31321
32971
|
}
|
|
31322
32972
|
async function runStrategy(sub, positional, flags, ctx) {
|
|
@@ -31391,6 +33041,8 @@ async function runStrategy(sub, positional, flags, ctx) {
|
|
|
31391
33041
|
const { runStrategyReview: runStrategyReview2 } = await Promise.resolve().then(() => (init_strategy_review_cmd(), strategy_review_cmd_exports));
|
|
31392
33042
|
return { action: "review", ...await runStrategyReview2(ctx, positional[1]) };
|
|
31393
33043
|
}
|
|
33044
|
+
case "craft":
|
|
33045
|
+
return runCraft(positional, flags, ctx);
|
|
31394
33046
|
default:
|
|
31395
33047
|
throw new NtrpError("unknown_strategy_subcommand", `Unknown strategy subcommand: ${sub}`, 2 /* Usage */);
|
|
31396
33048
|
}
|
|
@@ -31398,8 +33050,79 @@ async function runStrategy(sub, positional, flags, ctx) {
|
|
|
31398
33050
|
function shouldSpin(ctx) {
|
|
31399
33051
|
return !isStructuredOutput(ctx.execution) && ctx.execution.progress;
|
|
31400
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
|
+
}
|
|
31401
33121
|
function renderStrategyResult(result) {
|
|
31402
33122
|
switch (result.action) {
|
|
33123
|
+
case "craft":
|
|
33124
|
+
renderCraftResult(result);
|
|
33125
|
+
return;
|
|
31403
33126
|
case "review":
|
|
31404
33127
|
return;
|
|
31405
33128
|
case "ingest":
|
|
@@ -31432,6 +33155,31 @@ function renderStrategyResult(result) {
|
|
|
31432
33155
|
return;
|
|
31433
33156
|
}
|
|
31434
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
|
+
}
|
|
31435
33183
|
function printStrategyList(strategies) {
|
|
31436
33184
|
console.log(chalk39.bold("\n Strategies\n"));
|
|
31437
33185
|
if (strategies.length === 0) {
|
|
@@ -31542,7 +33290,10 @@ var init_strategy2 = __esm({
|
|
|
31542
33290
|
init_types2();
|
|
31543
33291
|
init_strategy();
|
|
31544
33292
|
init_repl_api();
|
|
31545
|
-
|
|
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"]);
|
|
31546
33297
|
}
|
|
31547
33298
|
});
|
|
31548
33299
|
|
|
@@ -32461,15 +34212,15 @@ var init_checkout = __esm({
|
|
|
32461
34212
|
});
|
|
32462
34213
|
|
|
32463
34214
|
// src/services/setup.ts
|
|
32464
|
-
import { existsSync as
|
|
32465
|
-
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";
|
|
32466
34217
|
function setupCheck() {
|
|
32467
34218
|
const home = ntrpHome();
|
|
32468
34219
|
let writable = false;
|
|
32469
34220
|
try {
|
|
32470
|
-
|
|
32471
|
-
const probe =
|
|
32472
|
-
|
|
34221
|
+
mkdirSync18(home, { recursive: true });
|
|
34222
|
+
const probe = join32(home, ".write-check");
|
|
34223
|
+
writeFileSync21(probe, "ok\n");
|
|
32473
34224
|
writable = true;
|
|
32474
34225
|
} catch {
|
|
32475
34226
|
writable = false;
|
|
@@ -32508,7 +34259,7 @@ function setupCheck() {
|
|
|
32508
34259
|
};
|
|
32509
34260
|
}
|
|
32510
34261
|
function readProfileInput(pathOrDash) {
|
|
32511
|
-
const raw = pathOrDash === "-" ?
|
|
34262
|
+
const raw = pathOrDash === "-" ? readFileSync23(0, "utf-8") : readFileSync23(pathOrDash, "utf-8");
|
|
32512
34263
|
return JSON.parse(raw);
|
|
32513
34264
|
}
|
|
32514
34265
|
function writeAgentProfile(input) {
|
|
@@ -33298,7 +35049,7 @@ var init_orchestrator = __esm({
|
|
|
33298
35049
|
});
|
|
33299
35050
|
|
|
33300
35051
|
// src/services/smoke-protocol.ts
|
|
33301
|
-
import { join as
|
|
35052
|
+
import { join as join33 } from "path";
|
|
33302
35053
|
function isSmokeProtocolTrigger(input) {
|
|
33303
35054
|
return normalize2(input).includes(SMOKE_TRIGGER_PHRASE);
|
|
33304
35055
|
}
|
|
@@ -33334,7 +35085,7 @@ async function runSmokeProtocol(_input, ctx) {
|
|
|
33334
35085
|
});
|
|
33335
35086
|
const proposalResult = await proposeRepositoryExport({
|
|
33336
35087
|
target: "markdown",
|
|
33337
|
-
directory:
|
|
35088
|
+
directory: join33(getExportsDir(), "repository-smoke"),
|
|
33338
35089
|
source: "smoke_protocol",
|
|
33339
35090
|
modelOrFixture: "smoke-protocol-v1"
|
|
33340
35091
|
});
|
|
@@ -34266,8 +36017,8 @@ var init_recall = __esm({
|
|
|
34266
36017
|
|
|
34267
36018
|
// src/memory/feedback.ts
|
|
34268
36019
|
import { appendFileSync as appendFileSync7 } from "fs";
|
|
34269
|
-
import { join as
|
|
34270
|
-
import { randomUUID as
|
|
36020
|
+
import { join as join34 } from "path";
|
|
36021
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
34271
36022
|
function summarize(text) {
|
|
34272
36023
|
return text.replace(/[#*`>_]/g, "").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
34273
36024
|
}
|
|
@@ -34289,7 +36040,7 @@ function findCalibrationToSupersede(question, note) {
|
|
|
34289
36040
|
}
|
|
34290
36041
|
function recordFeedback(input) {
|
|
34291
36042
|
const entry = {
|
|
34292
|
-
id:
|
|
36043
|
+
id: randomUUID9(),
|
|
34293
36044
|
rating: input.rating,
|
|
34294
36045
|
question: scrubText(input.question).slice(0, 300),
|
|
34295
36046
|
answer_summary: scrubText(summarize(input.answer)),
|
|
@@ -34298,7 +36049,7 @@ function recordFeedback(input) {
|
|
|
34298
36049
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
34299
36050
|
};
|
|
34300
36051
|
try {
|
|
34301
|
-
appendFileSync7(
|
|
36052
|
+
appendFileSync7(join34(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
|
|
34302
36053
|
} catch {
|
|
34303
36054
|
}
|
|
34304
36055
|
if (input.rating === "positive") {
|
|
@@ -34468,7 +36219,7 @@ __export(sessions_exports, {
|
|
|
34468
36219
|
handler: () => handler35
|
|
34469
36220
|
});
|
|
34470
36221
|
import chalk62 from "chalk";
|
|
34471
|
-
import { existsSync as
|
|
36222
|
+
import { existsSync as existsSync34 } from "fs";
|
|
34472
36223
|
async function handler35(args, _ctx) {
|
|
34473
36224
|
const sub = args[0] ?? "list";
|
|
34474
36225
|
if (sub === "list" || !args[0]) {
|
|
@@ -34575,12 +36326,12 @@ function showSession(idArg) {
|
|
|
34575
36326
|
console.log();
|
|
34576
36327
|
const transcriptPath = transcriptPathForSession(session.id);
|
|
34577
36328
|
const contextPath = contextDocPathForSession(session.id);
|
|
34578
|
-
if (
|
|
36329
|
+
if (existsSync34(transcriptPath) || existsSync34(contextPath)) {
|
|
34579
36330
|
console.log(" " + chalk62.dim("\u2500".repeat(40)));
|
|
34580
|
-
if (
|
|
36331
|
+
if (existsSync34(contextPath)) {
|
|
34581
36332
|
console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
|
|
34582
36333
|
}
|
|
34583
|
-
if (
|
|
36334
|
+
if (existsSync34(transcriptPath)) {
|
|
34584
36335
|
console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
|
|
34585
36336
|
}
|
|
34586
36337
|
console.log();
|
|
@@ -34733,7 +36484,7 @@ var switch_exports = {};
|
|
|
34733
36484
|
__export(switch_exports, {
|
|
34734
36485
|
handler: () => handler38
|
|
34735
36486
|
});
|
|
34736
|
-
import { join as
|
|
36487
|
+
import { join as join35 } from "path";
|
|
34737
36488
|
import chalk65 from "chalk";
|
|
34738
36489
|
async function handler38(args, ctx) {
|
|
34739
36490
|
if (args.length === 0) {
|
|
@@ -34763,7 +36514,7 @@ async function handler38(args, ctx) {
|
|
|
34763
36514
|
}
|
|
34764
36515
|
const context = buildSwitchContext(session);
|
|
34765
36516
|
const newId = makeSessionId();
|
|
34766
|
-
const newFile =
|
|
36517
|
+
const newFile = join35(getSessionsDir(), `${newId}.json`);
|
|
34767
36518
|
resetContextForSwitch(ctx, {
|
|
34768
36519
|
sessionId: newId,
|
|
34769
36520
|
sessionFile: newFile,
|
|
@@ -34776,7 +36527,8 @@ async function handler38(args, ctx) {
|
|
|
34776
36527
|
stage: session.stage,
|
|
34777
36528
|
dataset: session.dataset,
|
|
34778
36529
|
deliverables: session.deliverables,
|
|
34779
|
-
llm: session.llm ? { ...session.llm } : void 0
|
|
36530
|
+
llm: session.llm ? { ...session.llm } : void 0,
|
|
36531
|
+
lastCraftJobId: session.last_craft_job_id
|
|
34780
36532
|
});
|
|
34781
36533
|
console.log();
|
|
34782
36534
|
console.log(" " + paint("accent", `Switched to "${targetName}"`));
|
|
@@ -34789,7 +36541,7 @@ async function handler38(args, ctx) {
|
|
|
34789
36541
|
return `Switched to "${targetName}"`;
|
|
34790
36542
|
} else {
|
|
34791
36543
|
const newId = makeSessionId();
|
|
34792
|
-
const newFile =
|
|
36544
|
+
const newFile = join35(getSessionsDir(), `${newId}.json`);
|
|
34793
36545
|
resetContextForSwitch(ctx, {
|
|
34794
36546
|
sessionId: newId,
|
|
34795
36547
|
sessionFile: newFile,
|
|
@@ -35916,186 +37668,6 @@ var init_model = __esm({
|
|
|
35916
37668
|
}
|
|
35917
37669
|
});
|
|
35918
37670
|
|
|
35919
|
-
// src/config/update-check.ts
|
|
35920
|
-
import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
35921
|
-
import { join as join33 } from "path";
|
|
35922
|
-
function cachePath2() {
|
|
35923
|
-
return join33(ntrpHome(), "update-check.json");
|
|
35924
|
-
}
|
|
35925
|
-
function ensureDir7() {
|
|
35926
|
-
const dir = ntrpHome();
|
|
35927
|
-
if (!existsSync32(dir)) {
|
|
35928
|
-
mkdirSync18(dir, { recursive: true });
|
|
35929
|
-
}
|
|
35930
|
-
}
|
|
35931
|
-
function loadUpdateCheckCache() {
|
|
35932
|
-
const path = cachePath2();
|
|
35933
|
-
if (!existsSync32(path)) return null;
|
|
35934
|
-
try {
|
|
35935
|
-
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
35936
|
-
if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
|
|
35937
|
-
return null;
|
|
35938
|
-
}
|
|
35939
|
-
return parsed;
|
|
35940
|
-
} catch {
|
|
35941
|
-
return null;
|
|
35942
|
-
}
|
|
35943
|
-
}
|
|
35944
|
-
function saveUpdateCheckCache(cache2) {
|
|
35945
|
-
ensureDir7();
|
|
35946
|
-
writeFileSync20(cachePath2(), JSON.stringify(cache2, null, 2) + "\n");
|
|
35947
|
-
}
|
|
35948
|
-
function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
|
|
35949
|
-
if (!cache2) return false;
|
|
35950
|
-
return Date.now() - cache2.lastCheck < ttlMs;
|
|
35951
|
-
}
|
|
35952
|
-
function invalidateUpdateCheckCache() {
|
|
35953
|
-
const path = cachePath2();
|
|
35954
|
-
if (existsSync32(path)) {
|
|
35955
|
-
unlinkSync5(path);
|
|
35956
|
-
}
|
|
35957
|
-
}
|
|
35958
|
-
var CACHE_TTL_MS2;
|
|
35959
|
-
var init_update_check = __esm({
|
|
35960
|
-
"src/config/update-check.ts"() {
|
|
35961
|
-
"use strict";
|
|
35962
|
-
init_store();
|
|
35963
|
-
CACHE_TTL_MS2 = 864e5;
|
|
35964
|
-
}
|
|
35965
|
-
});
|
|
35966
|
-
|
|
35967
|
-
// src/version.ts
|
|
35968
|
-
import { existsSync as existsSync33, readFileSync as readFileSync22 } from "fs";
|
|
35969
|
-
import { dirname as dirname6, join as join34 } from "path";
|
|
35970
|
-
import { fileURLToPath } from "url";
|
|
35971
|
-
function readVersionFromPackageJson(packageJsonPath) {
|
|
35972
|
-
if (!existsSync33(packageJsonPath)) return null;
|
|
35973
|
-
try {
|
|
35974
|
-
const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
|
|
35975
|
-
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
35976
|
-
} catch {
|
|
35977
|
-
}
|
|
35978
|
-
return null;
|
|
35979
|
-
}
|
|
35980
|
-
function readVersionNearEntry(entryPath) {
|
|
35981
|
-
const start = dirname6(entryPath);
|
|
35982
|
-
for (const rel of [join34(start, "..", "package.json"), join34(start, "../..", "package.json")]) {
|
|
35983
|
-
const version = readVersionFromPackageJson(rel);
|
|
35984
|
-
if (version) return version;
|
|
35985
|
-
}
|
|
35986
|
-
return null;
|
|
35987
|
-
}
|
|
35988
|
-
function readInstalledVersionFromDisk() {
|
|
35989
|
-
return readVersionNearEntry(fileURLToPath(import.meta.url));
|
|
35990
|
-
}
|
|
35991
|
-
function getInstalledVersion() {
|
|
35992
|
-
if (cachedVersion) return cachedVersion;
|
|
35993
|
-
cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
|
|
35994
|
-
return cachedVersion;
|
|
35995
|
-
}
|
|
35996
|
-
var cachedVersion;
|
|
35997
|
-
var init_version = __esm({
|
|
35998
|
-
"src/version.ts"() {
|
|
35999
|
-
"use strict";
|
|
36000
|
-
}
|
|
36001
|
-
});
|
|
36002
|
-
|
|
36003
|
-
// src/update/registry.ts
|
|
36004
|
-
var registry_exports = {};
|
|
36005
|
-
__export(registry_exports, {
|
|
36006
|
-
NPM_PACKAGE: () => NPM_PACKAGE,
|
|
36007
|
-
applyUpdateCheckResult: () => applyUpdateCheckResult,
|
|
36008
|
-
checkForUpdate: () => checkForUpdate,
|
|
36009
|
-
fetchLatestVersion: () => fetchLatestVersion,
|
|
36010
|
-
formatUpdateNudge: () => formatUpdateNudge,
|
|
36011
|
-
hydrateUpdateAvailableFromCache: () => hydrateUpdateAvailableFromCache,
|
|
36012
|
-
isNewerVersion: () => isNewerVersion,
|
|
36013
|
-
startBackgroundUpdateCheck: () => startBackgroundUpdateCheck
|
|
36014
|
-
});
|
|
36015
|
-
function registryUrl() {
|
|
36016
|
-
return process.env.NTRP_REGISTRY_URL ?? "https://registry.npmjs.org/@sonnechasser/ntrp/latest";
|
|
36017
|
-
}
|
|
36018
|
-
function parseVersionParts(version) {
|
|
36019
|
-
const cleaned = version.trim().replace(/^v/i, "");
|
|
36020
|
-
const core = cleaned.split("-")[0] ?? cleaned;
|
|
36021
|
-
const parts = core.split(".").map((p) => parseInt(p, 10));
|
|
36022
|
-
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
36023
|
-
}
|
|
36024
|
-
function isNewerVersion(latest, current) {
|
|
36025
|
-
const [lMaj, lMin, lPatch] = parseVersionParts(latest);
|
|
36026
|
-
const [cMaj, cMin, cPatch] = parseVersionParts(current);
|
|
36027
|
-
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
36028
|
-
if (lMin !== cMin) return lMin > cMin;
|
|
36029
|
-
return lPatch > cPatch;
|
|
36030
|
-
}
|
|
36031
|
-
function formatUpdateNudge(current, latest) {
|
|
36032
|
-
return `\u26A1 NTRP v${latest} available (running v${current}) \u2014 type /update`;
|
|
36033
|
-
}
|
|
36034
|
-
function hydrateUpdateAvailableFromCache(current) {
|
|
36035
|
-
const cached2 = loadUpdateCheckCache();
|
|
36036
|
-
if (!cached2?.latestVersion) return void 0;
|
|
36037
|
-
if (!isNewerVersion(cached2.latestVersion, current)) return void 0;
|
|
36038
|
-
return { current, latest: cached2.latestVersion };
|
|
36039
|
-
}
|
|
36040
|
-
function applyUpdateCheckResult(ctx, result) {
|
|
36041
|
-
if (result?.updateAvailable) {
|
|
36042
|
-
ctx.updateAvailable = { current: result.current, latest: result.latest };
|
|
36043
|
-
return;
|
|
36044
|
-
}
|
|
36045
|
-
if (result && !result.updateAvailable) {
|
|
36046
|
-
ctx.updateAvailable = void 0;
|
|
36047
|
-
}
|
|
36048
|
-
}
|
|
36049
|
-
function startBackgroundUpdateCheck(ctx) {
|
|
36050
|
-
const pending = checkForUpdate({ force: true, timeoutMs: 5e3 });
|
|
36051
|
-
ctx.pendingUpdateCheck = pending;
|
|
36052
|
-
void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch(() => void 0);
|
|
36053
|
-
}
|
|
36054
|
-
async function fetchLatestVersion(timeoutMs = 5e3) {
|
|
36055
|
-
try {
|
|
36056
|
-
const res = await fetch(registryUrl(), { signal: AbortSignal.timeout(timeoutMs) });
|
|
36057
|
-
if (!res.ok) return null;
|
|
36058
|
-
const data = await res.json();
|
|
36059
|
-
return typeof data.version === "string" && data.version.length > 0 ? data.version : null;
|
|
36060
|
-
} catch {
|
|
36061
|
-
return null;
|
|
36062
|
-
}
|
|
36063
|
-
}
|
|
36064
|
-
function buildResult(current, latest) {
|
|
36065
|
-
return {
|
|
36066
|
-
current,
|
|
36067
|
-
latest,
|
|
36068
|
-
updateAvailable: isNewerVersion(latest, current)
|
|
36069
|
-
};
|
|
36070
|
-
}
|
|
36071
|
-
async function checkForUpdate(options) {
|
|
36072
|
-
const current = getInstalledVersion();
|
|
36073
|
-
const timeoutMs = options?.timeoutMs ?? 5e3;
|
|
36074
|
-
const cached2 = loadUpdateCheckCache();
|
|
36075
|
-
if (!options?.force && isCacheFresh(cached2)) {
|
|
36076
|
-
return buildResult(current, cached2.latestVersion);
|
|
36077
|
-
}
|
|
36078
|
-
const latest = await fetchLatestVersion(timeoutMs);
|
|
36079
|
-
if (!latest) {
|
|
36080
|
-
if (cached2?.latestVersion) {
|
|
36081
|
-
return buildResult(current, cached2.latestVersion);
|
|
36082
|
-
}
|
|
36083
|
-
return null;
|
|
36084
|
-
}
|
|
36085
|
-
const nextCache = { lastCheck: Date.now(), latestVersion: latest };
|
|
36086
|
-
saveUpdateCheckCache(nextCache);
|
|
36087
|
-
return buildResult(current, latest);
|
|
36088
|
-
}
|
|
36089
|
-
var NPM_PACKAGE;
|
|
36090
|
-
var init_registry = __esm({
|
|
36091
|
-
"src/update/registry.ts"() {
|
|
36092
|
-
"use strict";
|
|
36093
|
-
init_update_check();
|
|
36094
|
-
init_version();
|
|
36095
|
-
NPM_PACKAGE = "@sonnechasser/ntrp";
|
|
36096
|
-
}
|
|
36097
|
-
});
|
|
36098
|
-
|
|
36099
37671
|
// src/update/relaunch.ts
|
|
36100
37672
|
var relaunch_exports = {};
|
|
36101
37673
|
__export(relaunch_exports, {
|
|
@@ -36109,8 +37681,8 @@ __export(relaunch_exports, {
|
|
|
36109
37681
|
resolveRelaunchEntry: () => resolveRelaunchEntry,
|
|
36110
37682
|
updateRestartSummary: () => updateRestartSummary
|
|
36111
37683
|
});
|
|
36112
|
-
import { existsSync as
|
|
36113
|
-
import { join as
|
|
37684
|
+
import { existsSync as existsSync35 } from "fs";
|
|
37685
|
+
import { join as join36 } from "path";
|
|
36114
37686
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
36115
37687
|
import { spawnSync } from "child_process";
|
|
36116
37688
|
function encodeJustUpdated(fromVersion, toVersion) {
|
|
@@ -36134,8 +37706,8 @@ function updateRestartSummary(toVersion) {
|
|
|
36134
37706
|
function npmGlobalEntry() {
|
|
36135
37707
|
const listed = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
36136
37708
|
if (listed.status !== 0) return null;
|
|
36137
|
-
const entry =
|
|
36138
|
-
return
|
|
37709
|
+
const entry = join36(listed.stdout.trim(), NPM_PACKAGE, "dist/index.js");
|
|
37710
|
+
return existsSync35(entry) ? entry : null;
|
|
36139
37711
|
}
|
|
36140
37712
|
function thisBundleEntry() {
|
|
36141
37713
|
return fileURLToPath2(import.meta.url);
|
|
@@ -36145,10 +37717,10 @@ function resolveRelaunchEntry(toVersion) {
|
|
|
36145
37717
|
(p) => Boolean(p)
|
|
36146
37718
|
);
|
|
36147
37719
|
for (const entry of candidates) {
|
|
36148
|
-
if (!
|
|
37720
|
+
if (!existsSync35(entry)) continue;
|
|
36149
37721
|
if (readVersionNearEntry(entry) === toVersion) return entry;
|
|
36150
37722
|
}
|
|
36151
|
-
return candidates.find((p) =>
|
|
37723
|
+
return candidates.find((p) => existsSync35(p)) ?? thisBundleEntry();
|
|
36152
37724
|
}
|
|
36153
37725
|
function relaunchArgv(toVersion) {
|
|
36154
37726
|
return [resolveRelaunchEntry(toVersion)];
|
|
@@ -36242,6 +37814,22 @@ async function handler44(_args, ctx) {
|
|
|
36242
37814
|
console.log();
|
|
36243
37815
|
return;
|
|
36244
37816
|
}
|
|
37817
|
+
if (!ctx.oneShot) {
|
|
37818
|
+
const prompts = createPromptSession(ctx.rl, ctx);
|
|
37819
|
+
let ok2 = false;
|
|
37820
|
+
try {
|
|
37821
|
+
console.log();
|
|
37822
|
+
ok2 = await prompts.confirm(`Install NTRP v${current} \u2192 v${latest}?`, true);
|
|
37823
|
+
} finally {
|
|
37824
|
+
prompts.close();
|
|
37825
|
+
}
|
|
37826
|
+
if (!ok2) {
|
|
37827
|
+
console.log();
|
|
37828
|
+
console.log(chalk73.dim(" Update cancelled."));
|
|
37829
|
+
console.log();
|
|
37830
|
+
return "Update cancelled";
|
|
37831
|
+
}
|
|
37832
|
+
}
|
|
36245
37833
|
console.log();
|
|
36246
37834
|
console.log(` Updating NTRP v${current} \u2192 v${latest}...`);
|
|
36247
37835
|
const { ok, output } = runGlobalInstall();
|
|
@@ -36288,6 +37876,7 @@ var PERMISSIONS_URL;
|
|
|
36288
37876
|
var init_update = __esm({
|
|
36289
37877
|
"src/commands/update.ts"() {
|
|
36290
37878
|
"use strict";
|
|
37879
|
+
init_prompts();
|
|
36291
37880
|
init_update_check();
|
|
36292
37881
|
init_registry();
|
|
36293
37882
|
init_relaunch();
|
|
@@ -36362,10 +37951,10 @@ function renderProgressReport() {
|
|
|
36362
37951
|
`${chalk74.dim("Deliverables")} ${chalk74.bold(String(usage5.deliverables))}`,
|
|
36363
37952
|
`${chalk74.dim("AI exchanges")} ${chalk74.bold(String(usage5.nl_exchanges))}`
|
|
36364
37953
|
]);
|
|
36365
|
-
const
|
|
37954
|
+
const totalTokens2 = usage5.input_tokens + usage5.output_tokens;
|
|
36366
37955
|
printCard("AI usage", [
|
|
36367
37956
|
`${chalk74.dim("LLM calls")} ${chalk74.bold(String(usage5.llm_calls))}`,
|
|
36368
|
-
`${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)`
|
|
36369
37958
|
]);
|
|
36370
37959
|
const weeks = [...usage5.weekly].sort((a, b) => a.week.localeCompare(b.week)).slice(-8);
|
|
36371
37960
|
const weekHours = weeks.map((w) => w.minutes_saved / 60);
|
|
@@ -36671,8 +38260,8 @@ __export(exports_exports, {
|
|
|
36671
38260
|
handler: () => handler48
|
|
36672
38261
|
});
|
|
36673
38262
|
import chalk78 from "chalk";
|
|
36674
|
-
import { existsSync as
|
|
36675
|
-
import { join as
|
|
38263
|
+
import { existsSync as existsSync36 } from "fs";
|
|
38264
|
+
import { join as join37 } from "path";
|
|
36676
38265
|
function usage4() {
|
|
36677
38266
|
console.log(chalk78.dim(" Usage:"));
|
|
36678
38267
|
console.log(chalk78.dim(" /exports list [kind]"));
|
|
@@ -36729,7 +38318,7 @@ function printInboxShow() {
|
|
|
36729
38318
|
console.log(" " + paint("accent", "AI inbox: ") + inbox);
|
|
36730
38319
|
const latest = inboxLatestHandoffPath();
|
|
36731
38320
|
if (latest) console.log(" " + chalk78.dim("Latest handoff: ") + latest);
|
|
36732
|
-
console.log(" " + chalk78.dim("Finder skill: ") +
|
|
38321
|
+
console.log(" " + chalk78.dim("Finder skill: ") + join37(inbox, "SKILL.md"));
|
|
36733
38322
|
console.log(" " + chalk78.dim("Reprint: ") + paint("accent", "/inbox skill"));
|
|
36734
38323
|
} else {
|
|
36735
38324
|
console.log(" " + chalk78.dim("AI inbox: (not set). Type ") + paint("accent", "/inbox set <folder>"));
|
|
@@ -36751,8 +38340,8 @@ function printOpen() {
|
|
|
36751
38340
|
console.log(" " + chalk78.dim("AI inbox: ") + inbox);
|
|
36752
38341
|
const latest = inboxLatestHandoffPath();
|
|
36753
38342
|
if (latest) console.log(" " + chalk78.dim("Inbox latest: ") + latest);
|
|
36754
|
-
console.log(" " + chalk78.dim("Pickup skill: ") +
|
|
36755
|
-
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"));
|
|
36756
38345
|
} else {
|
|
36757
38346
|
console.log(" " + chalk78.dim("AI inbox is not set. Type ") + paint("accent", "/inbox set <folder>"));
|
|
36758
38347
|
const loc = handoffLocations();
|
|
@@ -36830,7 +38419,7 @@ function runMove(args, ctx) {
|
|
|
36830
38419
|
}
|
|
36831
38420
|
try {
|
|
36832
38421
|
const destDir = resolveUserPath(dest);
|
|
36833
|
-
if (!
|
|
38422
|
+
if (!existsSync36(destDir)) {
|
|
36834
38423
|
}
|
|
36835
38424
|
const event = moveExport(idOrName, destDir);
|
|
36836
38425
|
console.log();
|
|
@@ -37247,7 +38836,9 @@ section: Settings
|
|
|
37247
38836
|
handler: ../commands/update.ts
|
|
37248
38837
|
---
|
|
37249
38838
|
|
|
37250
|
-
Install the latest global NTRP package via npm
|
|
38839
|
+
Install the latest global NTRP package via npm.
|
|
38840
|
+
Interactive confirms with \u23CE yes, then re-execs onto home.
|
|
38841
|
+
One-shot installs immediately and prints a restart hint.`
|
|
37251
38842
|
},
|
|
37252
38843
|
{
|
|
37253
38844
|
name: "resume",
|
|
@@ -37474,7 +39065,7 @@ This command is hidden. Type \`/ingest --demo\` instead. That command calls this
|
|
|
37474
39065
|
name: strategy
|
|
37475
39066
|
description: Make a measured strategy from your data
|
|
37476
39067
|
section: More
|
|
37477
|
-
args: [objective] | [list|show|review|ingest|add|sync|sources] [args]
|
|
39068
|
+
args: [objective] | [list|show|review|ingest|add|sync|sources|craft] [args]
|
|
37478
39069
|
handler: ../commands/strategy.ts
|
|
37479
39070
|
---
|
|
37480
39071
|
|
|
@@ -37483,6 +39074,8 @@ NTRP uses live data. It works back from the objective.
|
|
|
37483
39074
|
The result is sequenced workstreams with dated milestones, deliverables, outcome ranges, and a contingency per workstream.
|
|
37484
39075
|
Saved plans go to the strategy library. Later answers use them.
|
|
37485
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>\`.
|
|
37486
39079
|
|
|
37487
39080
|
Library commands: \`/strategy list\`, \`/strategy show <slug>\`.
|
|
37488
39081
|
Type \`/strategy ingest <file>\` for markdown, YAML, PDF, text, or \`-\` for stdin.
|
|
@@ -37864,8 +39457,8 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
|
|
|
37864
39457
|
});
|
|
37865
39458
|
|
|
37866
39459
|
// src/ai/prompt-parts.ts
|
|
37867
|
-
import { existsSync as
|
|
37868
|
-
import { join as
|
|
39460
|
+
import { existsSync as existsSync37, readFileSync as readFileSync24 } from "fs";
|
|
39461
|
+
import { join as join38 } from "path";
|
|
37869
39462
|
function buildCompanyProfileBlock() {
|
|
37870
39463
|
const p = loadProfile();
|
|
37871
39464
|
if (!p) return "";
|
|
@@ -37883,10 +39476,10 @@ function buildCompanyProfileBlock() {
|
|
|
37883
39476
|
return lines.join("\n");
|
|
37884
39477
|
}
|
|
37885
39478
|
function loadAnalystFile() {
|
|
37886
|
-
const path =
|
|
39479
|
+
const path = join38(ntrpHome(), ANALYST_FILE_NAME);
|
|
37887
39480
|
try {
|
|
37888
|
-
if (!
|
|
37889
|
-
const raw = sanitizeExternalText(
|
|
39481
|
+
if (!existsSync37(path)) return null;
|
|
39482
|
+
const raw = sanitizeExternalText(readFileSync24(path, "utf-8").trim());
|
|
37890
39483
|
if (!raw) return null;
|
|
37891
39484
|
if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
|
|
37892
39485
|
const head = raw.slice(0, Math.floor(ANALYST_FILE_MAX_CHARS * 0.75));
|
|
@@ -38420,8 +40013,8 @@ __export(ingest_chat_exports, {
|
|
|
38420
40013
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
38421
40014
|
looksLikeFilePath: () => looksLikeFilePath
|
|
38422
40015
|
});
|
|
38423
|
-
import { existsSync as
|
|
38424
|
-
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";
|
|
38425
40018
|
import { homedir as homedir8 } from "os";
|
|
38426
40019
|
import chalk80 from "chalk";
|
|
38427
40020
|
function extractFilePath(input) {
|
|
@@ -38443,7 +40036,7 @@ function extractFilePath(input) {
|
|
|
38443
40036
|
if (!candidate) continue;
|
|
38444
40037
|
if (!looksLikePathToken(candidate)) continue;
|
|
38445
40038
|
const p = expandPath(candidate);
|
|
38446
|
-
if (
|
|
40039
|
+
if (existsSync38(p)) {
|
|
38447
40040
|
try {
|
|
38448
40041
|
const st = statSync5(p);
|
|
38449
40042
|
if (st.isFile() || st.isDirectory()) return p;
|
|
@@ -38472,7 +40065,7 @@ function looksLikeFilePath(input) {
|
|
|
38472
40065
|
function listCsvsInFolder(dir) {
|
|
38473
40066
|
try {
|
|
38474
40067
|
if (!statSync5(dir).isDirectory()) return [];
|
|
38475
|
-
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();
|
|
38476
40069
|
} catch {
|
|
38477
40070
|
return [];
|
|
38478
40071
|
}
|
|
@@ -38554,12 +40147,12 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
38554
40147
|
}
|
|
38555
40148
|
const { handler: ingest } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
|
|
38556
40149
|
const { detectEntityType: detectEntityType2 } = await Promise.resolve().then(() => (init_csv_detect(), csv_detect_exports));
|
|
38557
|
-
const { readFileSync:
|
|
40150
|
+
const { readFileSync: readFileSync25 } = await import("fs");
|
|
38558
40151
|
const { parseCSV: parseCSV2 } = await Promise.resolve().then(() => (init_csv_parse(), csv_parse_exports));
|
|
38559
40152
|
const { getStoredApiKey } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
38560
40153
|
let headerCheckFailed = false;
|
|
38561
40154
|
try {
|
|
38562
|
-
const raw =
|
|
40155
|
+
const raw = readFileSync25(filePath, "utf-8");
|
|
38563
40156
|
const { headers } = parseCSV2(raw);
|
|
38564
40157
|
const detected = detectEntityType2(headers, "unknown");
|
|
38565
40158
|
if (!detected) headerCheckFailed = true;
|
|
@@ -39557,6 +41150,10 @@ async function conversationRouter(input, ctx) {
|
|
|
39557
41150
|
const summary = await handleStrategizeFlow(line, ctx) ?? void 0;
|
|
39558
41151
|
return { handled: true, summary };
|
|
39559
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
|
+
}
|
|
39560
41157
|
if (phase === "think") {
|
|
39561
41158
|
const summary = await handleThinkFlow(line, ctx) ?? void 0;
|
|
39562
41159
|
return { handled: true, summary };
|
|
@@ -39867,7 +41464,7 @@ var init_loop_guard2 = __esm({
|
|
|
39867
41464
|
},
|
|
39868
41465
|
strategize: {
|
|
39869
41466
|
mode: "strategy objective confirm",
|
|
39870
|
-
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",
|
|
39871
41468
|
leave: "cancel drops the strategy session and returns to Q&A"
|
|
39872
41469
|
}
|
|
39873
41470
|
};
|
|
@@ -39917,7 +41514,7 @@ function ntrpStatusRow(version, update) {
|
|
|
39917
41514
|
return {
|
|
39918
41515
|
label: "ntrp",
|
|
39919
41516
|
state: badge("UPDATE", "warning"),
|
|
39920
|
-
detail: `v${update.latest} \xB7
|
|
41517
|
+
detail: `v${update.latest} \xB7 \u23CE /update`
|
|
39921
41518
|
};
|
|
39922
41519
|
}
|
|
39923
41520
|
if (disk && isNewerVersion(disk, getInstalledVersion())) {
|
|
@@ -40352,7 +41949,8 @@ var init_deepdive_complete = __esm({
|
|
|
40352
41949
|
"inbox",
|
|
40353
41950
|
"claude",
|
|
40354
41951
|
"skill",
|
|
40355
|
-
"strategy"
|
|
41952
|
+
"strategy",
|
|
41953
|
+
"keep-going"
|
|
40356
41954
|
];
|
|
40357
41955
|
}
|
|
40358
41956
|
});
|
|
@@ -40463,7 +42061,7 @@ __export(repl_exports, {
|
|
|
40463
42061
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
40464
42062
|
import { clearLine as clearLine2, cursorTo as cursorTo2 } from "readline";
|
|
40465
42063
|
import chalk88 from "chalk";
|
|
40466
|
-
import { join as
|
|
42064
|
+
import { join as join40 } from "path";
|
|
40467
42065
|
function buildPrompt(ctx) {
|
|
40468
42066
|
return buildConversationPrompt(ctx);
|
|
40469
42067
|
}
|
|
@@ -40791,14 +42389,14 @@ function printHelp() {
|
|
|
40791
42389
|
console.log(" " + chalk88.dim("Type the question. You do not need a slash command."));
|
|
40792
42390
|
console.log(" " + chalk88.dim("Paste a CSV path or type ") + paint("accent", '"use demo data"') + chalk88.dim(" to load data."));
|
|
40793
42391
|
console.log(" " + chalk88.dim("After analysis, type questions in English."));
|
|
40794
|
-
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."));
|
|
40795
42393
|
console.log(" " + chalk88.dim("Type ") + paint("accent", '"ship a board deck"') + chalk88.dim(" to write a handoff."));
|
|
40796
42394
|
console.log(" " + chalk88.dim("The ") + paint("accent", "\u203A") + chalk88.dim(" prompt shows brief or deep after analysis. Brief is the default."));
|
|
40797
42395
|
console.log();
|
|
40798
42396
|
console.log(" " + sectionHeading("Keys"));
|
|
40799
42397
|
console.log(
|
|
40800
42398
|
" " + paint("accent", "\u23CE") + chalk88.dim(
|
|
40801
|
-
" Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect)."
|
|
42399
|
+
" Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect, /update, keep going)."
|
|
40802
42400
|
)
|
|
40803
42401
|
);
|
|
40804
42402
|
console.log(
|
|
@@ -40849,7 +42447,7 @@ function printHelp() {
|
|
|
40849
42447
|
["/remember <fact>", "Store a fact, a decision, or a preference"],
|
|
40850
42448
|
["/recall [topic]", "Show what NTRP stores about your business"],
|
|
40851
42449
|
["/rate good|bad <note>", "Correct the last answer. A bad note becomes a calibration"],
|
|
40852
|
-
[`${
|
|
42450
|
+
[`${join40(ntrpHome(), ANALYST_FILE_NAME)}`, "Standing operator instructions (tone, priorities, house rules)"]
|
|
40853
42451
|
];
|
|
40854
42452
|
const teachMaxW = Math.max(...teach.map(([c]) => c.length)) + 2;
|
|
40855
42453
|
for (const [cmd, desc] of teach) {
|