markform 0.1.17 → 0.1.19
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/README.md +27 -2
- package/dist/ai-sdk.d.mts +1 -2
- package/dist/ai-sdk.mjs +2 -2
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/{apply-DgDJBscb.mjs → apply-Dalpt-D6.mjs} +422 -31
- package/dist/apply-Dalpt-D6.mjs.map +1 -0
- package/dist/bin.mjs +20 -2
- package/dist/bin.mjs.map +1 -1
- package/dist/{cli-DAl8LQzI.mjs → cli-tpvFNqFY.mjs} +325 -73
- package/dist/cli-tpvFNqFY.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-DiCddBKu.mjs → coreTypes-CPKXf2dc.mjs} +9 -4
- package/dist/coreTypes-CPKXf2dc.mjs.map +1 -0
- package/dist/{coreTypes-CnEea7Kh.d.mts → coreTypes-CkxML8g2.d.mts} +128 -10
- package/dist/index.d.mts +642 -22
- package/dist/index.mjs +5 -5
- package/dist/{session-XDrocA3j.mjs → session-CK0x28RO.mjs} +2 -2
- package/dist/session-CK0x28RO.mjs.map +1 -0
- package/dist/{session-B7aR6hno.mjs → session-ZHBi3LVQ.mjs} +1 -1
- package/dist/{shared-fUKfJ1UA.mjs → shared-BTR35aMz.mjs} +1 -1
- package/dist/{shared-CCq4haEV.mjs → shared-DwdyWmvE.mjs} +1 -3
- package/dist/shared-DwdyWmvE.mjs.map +1 -0
- package/dist/{src-CHVJLGKt.mjs → src-BTyz-wS6.mjs} +2009 -584
- package/dist/src-BTyz-wS6.mjs.map +1 -0
- package/docs/markform-apis.md +112 -0
- package/docs/markform-reference.md +27 -0
- package/docs/markform-spec.md +115 -0
- package/examples/movie-research/movie-deep-research-mock-filled.form.md +1 -1
- package/examples/movie-research/movie-deep-research.form.md +1 -1
- package/examples/parallel/parallel-research.form.md +57 -0
- package/examples/startup-deep-research/startup-deep-research.form.md +1 -1
- package/package.json +16 -14
- package/dist/apply-DgDJBscb.mjs.map +0 -1
- package/dist/cli-DAl8LQzI.mjs.map +0 -1
- package/dist/coreTypes-DiCddBKu.mjs.map +0 -1
- package/dist/session-XDrocA3j.mjs.map +0 -1
- package/dist/shared-CCq4haEV.mjs.map +0 -1
- package/dist/src-CHVJLGKt.mjs.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
import { L as PatchSchema } from "./coreTypes-
|
|
3
|
-
import {
|
|
4
|
-
import { D as
|
|
5
|
-
import { n as serializeSession } from "./session-
|
|
6
|
-
import { _ as writeFile, a as formatPath, c as logError, d as logTiming, f as logVerbose, g as stripHtmlComments, h as shouldUseColors, i as formatOutput, l as logInfo, m as readFile$1, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-
|
|
2
|
+
import { L as PatchSchema } from "./coreTypes-CPKXf2dc.mjs";
|
|
3
|
+
import { $ as WEB_SEARCH_CONFIG, B as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, F as DEFAULT_MAX_PATCHES_PER_TURN, G as REPORT_EXTENSION, J as deriveReportPath, K as USER_ROLE, L as DEFAULT_MAX_TURNS, M as DEFAULT_FORMS_DIR, N as DEFAULT_MAX_ISSUES_PER_TURN, Q as SUGGESTED_LLMS, R as DEFAULT_PORT, V as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, W as MAX_FORMS_IN_MENU, X as detectFileType, Y as deriveSchemaPath, Z as parseRolesFlag, c as computeProgressSummary, d as serializeForm, et as formatSuggestedLlms, f as serializeRawMarkdown, h as friendlyUrlAbbrev, i as inspect, j as AGENT_ROLE, l as computeStructureSummary, m as formatBareUrlsAsHtmlLinks, n as getAllFields, nt as hasWebSearchSupport, p as serializeReport, q as deriveExportPath, rt as parseModelIdForDisplay, t as applyPatches, v as validateSyntaxConsistency } from "./apply-Dalpt-D6.mjs";
|
|
4
|
+
import { C as resolveModel, D as computeExecutionPlan, E as FillRecordCollector, H as formToJsonSchema, S as getProviderNames, T as createLiveAgent, U as parseForm, _ as fillForm, g as resolveHarnessConfig, h as formatFillRecordSummary, i as runResearch, j as createHarness, k as createMockAgent, m as stripUnstableFillRecordFields, n as isResearchForm, t as VERSION, w as buildMockWireFormat, x as getProviderInfo } from "./src-BTyz-wS6.mjs";
|
|
5
|
+
import { n as serializeSession } from "./session-CK0x28RO.mjs";
|
|
6
|
+
import { _ as writeFile, a as formatPath, c as logError, d as logTiming, f as logVerbose, g as stripHtmlComments, h as shouldUseColors, i as formatOutput, l as logInfo, m as readFile$1, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-DwdyWmvE.mjs";
|
|
7
7
|
import Markdoc from "@markdoc/markdoc";
|
|
8
8
|
import YAML from "yaml";
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
import pc from "picocolors";
|
|
11
11
|
import { exec, execSync, spawn } from "node:child_process";
|
|
12
12
|
import { basename, dirname, join, resolve } from "node:path";
|
|
13
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
13
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { readFile } from "node:fs/promises";
|
|
16
16
|
import * as p from "@clack/prompts";
|
|
@@ -253,12 +253,12 @@ function registerApplyCommand(program) {
|
|
|
253
253
|
const patches = parsedJson;
|
|
254
254
|
const validatedPatches = [];
|
|
255
255
|
for (let i = 0; i < patches.length; i++) {
|
|
256
|
-
const result
|
|
257
|
-
if (!result
|
|
258
|
-
logError(`Invalid patch at index ${i}: ${result
|
|
256
|
+
const result = PatchSchema.safeParse(patches[i]);
|
|
257
|
+
if (!result.success) {
|
|
258
|
+
logError(`Invalid patch at index ${i}: ${result.error.issues[0]?.message ?? "Unknown error"}`);
|
|
259
259
|
process.exit(1);
|
|
260
260
|
}
|
|
261
|
-
validatedPatches.push(result
|
|
261
|
+
validatedPatches.push(result.data);
|
|
262
262
|
}
|
|
263
263
|
if (ctx.dryRun) {
|
|
264
264
|
logDryRun(`Would apply ${validatedPatches.length} patches to ${file}`, { patches: validatedPatches });
|
|
@@ -418,7 +418,7 @@ async function displayWithPager(content, title) {
|
|
|
418
418
|
return;
|
|
419
419
|
}
|
|
420
420
|
const header = `${pc.bgCyan(pc.black(` ${title} `))}`;
|
|
421
|
-
return new Promise((resolve
|
|
421
|
+
return new Promise((resolve) => {
|
|
422
422
|
const pager = spawn("less", [
|
|
423
423
|
"-R",
|
|
424
424
|
"-S",
|
|
@@ -435,10 +435,10 @@ async function displayWithPager(content, title) {
|
|
|
435
435
|
console.log("");
|
|
436
436
|
console.log(content);
|
|
437
437
|
console.log("");
|
|
438
|
-
resolve
|
|
438
|
+
resolve();
|
|
439
439
|
});
|
|
440
440
|
pager.on("close", () => {
|
|
441
|
-
resolve
|
|
441
|
+
resolve();
|
|
442
442
|
});
|
|
443
443
|
pager.stdin.write(header + "\n\n");
|
|
444
444
|
pager.stdin.write(content);
|
|
@@ -1353,13 +1353,13 @@ function parseVersionedPath(filePath) {
|
|
|
1353
1353
|
function generateVersionedPath(filePath) {
|
|
1354
1354
|
const parsed = parseVersionedPath(filePath);
|
|
1355
1355
|
if (!parsed) {
|
|
1356
|
-
let candidate
|
|
1357
|
-
let version
|
|
1358
|
-
while (existsSync(candidate
|
|
1359
|
-
version
|
|
1360
|
-
candidate
|
|
1356
|
+
let candidate = `${filePath}-filled1`;
|
|
1357
|
+
let version = 1;
|
|
1358
|
+
while (existsSync(candidate)) {
|
|
1359
|
+
version++;
|
|
1360
|
+
candidate = `${filePath}-filled${version}`;
|
|
1361
1361
|
}
|
|
1362
|
-
return candidate
|
|
1362
|
+
return candidate;
|
|
1363
1363
|
}
|
|
1364
1364
|
let version = parsed.version !== null ? parsed.version + 1 : 1;
|
|
1365
1365
|
let candidate = `${parsed.base}-filled${version}${parsed.extension}`;
|
|
@@ -1469,10 +1469,10 @@ async function promptForString(ctx) {
|
|
|
1469
1469
|
placeholder: placeholderText,
|
|
1470
1470
|
initialValue: currentVal ?? "",
|
|
1471
1471
|
validate: (value) => {
|
|
1472
|
-
if (field.required && !value
|
|
1473
|
-
if (field.minLength && value
|
|
1474
|
-
if (field.maxLength && value
|
|
1475
|
-
if (field.pattern && !new RegExp(field.pattern).test(value)) return `Must match pattern: ${field.pattern}`;
|
|
1472
|
+
if (field.required && !value?.trim()) return "This field is required";
|
|
1473
|
+
if (field.minLength && (value?.length ?? 0) < field.minLength) return `Minimum ${field.minLength} characters required`;
|
|
1474
|
+
if (field.maxLength && (value?.length ?? 0) > field.maxLength) return `Maximum ${field.maxLength} characters allowed`;
|
|
1475
|
+
if (field.pattern && value && !new RegExp(field.pattern).test(value)) return `Must match pattern: ${field.pattern}`;
|
|
1476
1476
|
}
|
|
1477
1477
|
});
|
|
1478
1478
|
if (p.isCancel(result)) return null;
|
|
@@ -1480,7 +1480,7 @@ async function promptForString(ctx) {
|
|
|
1480
1480
|
return {
|
|
1481
1481
|
op: "set_string",
|
|
1482
1482
|
fieldId: field.id,
|
|
1483
|
-
value: result
|
|
1483
|
+
value: result ?? null
|
|
1484
1484
|
};
|
|
1485
1485
|
}
|
|
1486
1486
|
/**
|
|
@@ -1495,8 +1495,8 @@ async function promptForNumber(ctx) {
|
|
|
1495
1495
|
placeholder: placeholderText,
|
|
1496
1496
|
initialValue: currentVal !== null ? String(currentVal) : "",
|
|
1497
1497
|
validate: (value) => {
|
|
1498
|
-
if (field.required && !value
|
|
1499
|
-
if (!value
|
|
1498
|
+
if (field.required && !value?.trim()) return "This field is required";
|
|
1499
|
+
if (!value?.trim()) return;
|
|
1500
1500
|
const num = Number(value);
|
|
1501
1501
|
if (isNaN(num)) return "Please enter a valid number";
|
|
1502
1502
|
if (field.integer && !Number.isInteger(num)) return "Please enter a whole number";
|
|
@@ -1527,14 +1527,14 @@ async function promptForStringList(ctx) {
|
|
|
1527
1527
|
placeholder: hint,
|
|
1528
1528
|
initialValue: currentItems.join("\n"),
|
|
1529
1529
|
validate: (value) => {
|
|
1530
|
-
const items
|
|
1531
|
-
if (field.required && items
|
|
1532
|
-
if (field.minItems && items
|
|
1533
|
-
if (field.maxItems && items
|
|
1530
|
+
const items = (value ?? "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1531
|
+
if (field.required && items.length === 0) return "At least one item is required";
|
|
1532
|
+
if (field.minItems && items.length < field.minItems) return `Minimum ${field.minItems} items required`;
|
|
1533
|
+
if (field.maxItems && items.length > field.maxItems) return `Maximum ${field.maxItems} items allowed`;
|
|
1534
1534
|
}
|
|
1535
1535
|
});
|
|
1536
1536
|
if (p.isCancel(result)) return null;
|
|
1537
|
-
const items = result.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1537
|
+
const items = (result ?? "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1538
1538
|
if (items.length === 0 && !field.required) return null;
|
|
1539
1539
|
return {
|
|
1540
1540
|
op: "set_string_list",
|
|
@@ -1612,16 +1612,16 @@ async function promptForCheckboxes(ctx) {
|
|
|
1612
1612
|
});
|
|
1613
1613
|
if (p.isCancel(result)) return null;
|
|
1614
1614
|
const selected = result;
|
|
1615
|
-
const values
|
|
1616
|
-
for (const opt of field.options) values
|
|
1615
|
+
const values = {};
|
|
1616
|
+
for (const opt of field.options) values[opt.id] = selected.includes(opt.id) ? "done" : "todo";
|
|
1617
1617
|
return {
|
|
1618
1618
|
op: "set_checkboxes",
|
|
1619
1619
|
fieldId: field.id,
|
|
1620
|
-
value: values
|
|
1620
|
+
value: values
|
|
1621
1621
|
};
|
|
1622
1622
|
}
|
|
1623
1623
|
if (field.checkboxMode === "explicit") {
|
|
1624
|
-
const values
|
|
1624
|
+
const values = {};
|
|
1625
1625
|
for (const opt of field.options) {
|
|
1626
1626
|
const current = currentValues[opt.id];
|
|
1627
1627
|
const result = await p.select({
|
|
@@ -1643,12 +1643,12 @@ async function promptForCheckboxes(ctx) {
|
|
|
1643
1643
|
initialValue: current === "yes" || current === "no" ? current : "unfilled"
|
|
1644
1644
|
});
|
|
1645
1645
|
if (p.isCancel(result)) return null;
|
|
1646
|
-
values
|
|
1646
|
+
values[opt.id] = result;
|
|
1647
1647
|
}
|
|
1648
1648
|
return {
|
|
1649
1649
|
op: "set_checkboxes",
|
|
1650
1650
|
fieldId: field.id,
|
|
1651
|
-
value: values
|
|
1651
|
+
value: values
|
|
1652
1652
|
};
|
|
1653
1653
|
}
|
|
1654
1654
|
const values = {};
|
|
@@ -1701,8 +1701,8 @@ async function promptForUrl(ctx) {
|
|
|
1701
1701
|
placeholder: placeholderText,
|
|
1702
1702
|
initialValue: currentVal ?? "",
|
|
1703
1703
|
validate: (value) => {
|
|
1704
|
-
if (field.required && !value
|
|
1705
|
-
if (!value
|
|
1704
|
+
if (field.required && !value?.trim()) return "This field is required";
|
|
1705
|
+
if (!value?.trim()) return;
|
|
1706
1706
|
try {
|
|
1707
1707
|
new URL(value);
|
|
1708
1708
|
} catch {
|
|
@@ -1715,7 +1715,7 @@ async function promptForUrl(ctx) {
|
|
|
1715
1715
|
return {
|
|
1716
1716
|
op: "set_url",
|
|
1717
1717
|
fieldId: field.id,
|
|
1718
|
-
value: result
|
|
1718
|
+
value: result ?? null
|
|
1719
1719
|
};
|
|
1720
1720
|
}
|
|
1721
1721
|
/**
|
|
@@ -1743,8 +1743,8 @@ async function promptForDate(ctx) {
|
|
|
1743
1743
|
placeholder: currentVal ?? `YYYY-MM-DD${formatHint}`,
|
|
1744
1744
|
initialValue: currentVal ?? "",
|
|
1745
1745
|
validate: (value) => {
|
|
1746
|
-
if (field.required && !value
|
|
1747
|
-
if (!value
|
|
1746
|
+
if (field.required && !value?.trim()) return "This field is required";
|
|
1747
|
+
if (!value?.trim()) return;
|
|
1748
1748
|
if (!isValidDate(value)) return "Please enter a valid date in YYYY-MM-DD format";
|
|
1749
1749
|
if (field.min && value < field.min) return `Date must be on or after ${field.min}`;
|
|
1750
1750
|
if (field.max && value > field.max) return `Date must be on or before ${field.max}`;
|
|
@@ -1755,7 +1755,7 @@ async function promptForDate(ctx) {
|
|
|
1755
1755
|
return {
|
|
1756
1756
|
op: "set_date",
|
|
1757
1757
|
fieldId: field.id,
|
|
1758
|
-
value: result
|
|
1758
|
+
value: result ?? null
|
|
1759
1759
|
};
|
|
1760
1760
|
}
|
|
1761
1761
|
/** Default year range for validation */
|
|
@@ -1774,8 +1774,8 @@ async function promptForYear(ctx) {
|
|
|
1774
1774
|
placeholder: currentVal !== null ? String(currentVal) : `Year (${minYear}-${maxYear})`,
|
|
1775
1775
|
initialValue: currentVal !== null ? String(currentVal) : "",
|
|
1776
1776
|
validate: (value) => {
|
|
1777
|
-
if (field.required && !value
|
|
1778
|
-
if (!value
|
|
1777
|
+
if (field.required && !value?.trim()) return "This field is required";
|
|
1778
|
+
if (!value?.trim()) return;
|
|
1779
1779
|
const num = Number(value);
|
|
1780
1780
|
if (isNaN(num) || !Number.isInteger(num)) return "Please enter a valid year (e.g., 2025)";
|
|
1781
1781
|
if (num < minYear) return `Year must be ${minYear} or later`;
|
|
@@ -1805,11 +1805,11 @@ async function promptForUrlList(ctx) {
|
|
|
1805
1805
|
placeholder: hint,
|
|
1806
1806
|
initialValue: currentItems.join("\n"),
|
|
1807
1807
|
validate: (value) => {
|
|
1808
|
-
const items
|
|
1809
|
-
if (field.required && items
|
|
1810
|
-
if (field.minItems && items
|
|
1811
|
-
if (field.maxItems && items
|
|
1812
|
-
for (const item of items
|
|
1808
|
+
const items = (value ?? "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1809
|
+
if (field.required && items.length === 0) return "At least one URL is required";
|
|
1810
|
+
if (field.minItems && items.length < field.minItems) return `Minimum ${field.minItems} URLs required`;
|
|
1811
|
+
if (field.maxItems && items.length > field.maxItems) return `Maximum ${field.maxItems} URLs allowed`;
|
|
1812
|
+
for (const item of items) try {
|
|
1813
1813
|
new URL(item);
|
|
1814
1814
|
} catch {
|
|
1815
1815
|
return `Invalid URL: ${item}`;
|
|
@@ -1817,7 +1817,7 @@ async function promptForUrlList(ctx) {
|
|
|
1817
1817
|
}
|
|
1818
1818
|
});
|
|
1819
1819
|
if (p.isCancel(result)) return null;
|
|
1820
|
-
const items = result.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1820
|
+
const items = (result ?? "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1821
1821
|
if (items.length === 0 && !field.required) return null;
|
|
1822
1822
|
return {
|
|
1823
1823
|
op: "set_url_list",
|
|
@@ -2188,7 +2188,7 @@ async function promptForModel(webSearchRequired) {
|
|
|
2188
2188
|
message: "Model ID (provider/model-id):",
|
|
2189
2189
|
placeholder: "anthropic/claude-sonnet-4-20250514",
|
|
2190
2190
|
validate: (value) => {
|
|
2191
|
-
if (!value
|
|
2191
|
+
if (!value?.includes("/")) return "Format: provider/model-id (e.g., anthropic/claude-sonnet-4-20250514)";
|
|
2192
2192
|
}
|
|
2193
2193
|
});
|
|
2194
2194
|
if (p.isCancel(customModel)) return null;
|
|
@@ -2278,6 +2278,7 @@ async function runAgentFillWorkflow(form, modelId, formsDir, filePath, isResearc
|
|
|
2278
2278
|
fillMode: overwrite ? "overwrite" : "continue",
|
|
2279
2279
|
enableWebSearch: isResearch,
|
|
2280
2280
|
captureWireFormat: false,
|
|
2281
|
+
recordFill: false,
|
|
2281
2282
|
callbacks
|
|
2282
2283
|
});
|
|
2283
2284
|
if (result.status.ok) p.log.success(pc.green(`Form completed in ${result.turns} turn(s)`));
|
|
@@ -2768,7 +2769,7 @@ function formatConsoleSession(transcript, useColors) {
|
|
|
2768
2769
|
* Register the fill command.
|
|
2769
2770
|
*/
|
|
2770
2771
|
function registerFillCommand(program) {
|
|
2771
|
-
program.command("fill <file>").description("Run an agent to autonomously fill a form").option("--mock", "Use mock agent (requires --mock-source)").option("--model <id>", "Model ID for live agent (format: provider/model-id, e.g. openai/gpt-5-mini)").option("--mock-source <file>", "Path to completed form for mock agent").option("--record <file>", "Record session transcript to file").option("--max-turns <n>", `Maximum turns (default: ${DEFAULT_MAX_TURNS})`, String(DEFAULT_MAX_TURNS)).option("--max-patches <n>", `Maximum patches per turn (default: ${DEFAULT_MAX_PATCHES_PER_TURN})`, String(DEFAULT_MAX_PATCHES_PER_TURN)).option("--max-issues <n>", `Maximum issues shown per turn (default: ${DEFAULT_MAX_ISSUES_PER_TURN})`, String(DEFAULT_MAX_ISSUES_PER_TURN)).option("--max-fields <n>", "Maximum unique fields per turn (applied before --max-issues)").option("--max-groups <n>", "Maximum unique groups per turn (applied before --max-issues)").option("--roles <roles>", "Target roles to fill (comma-separated, or '*' for all; default: 'agent', or 'user' in --interactive mode)").option("--mode <mode>", "Fill mode: continue (skip filled fields) or overwrite (re-fill; default: continue)").option("-o, --output <file>", "Write final form to file").option("--prompt <file>", "Path to custom system prompt file (appends to default)").option("--instructions <text>", "Inline system prompt (appends to default; takes precedence over --prompt)").option("-i, --interactive", "Interactive mode: prompt user for field values (defaults to user role)").option("--normalize", "Regenerate form without preserving external content").action(async (file, options, cmd) => {
|
|
2772
|
+
program.command("fill <file>").description("Run an agent to autonomously fill a form").option("--mock", "Use mock agent (requires --mock-source)").option("--model <id>", "Model ID for live agent (format: provider/model-id, e.g. openai/gpt-5-mini)").option("--mock-source <file>", "Path to completed form for mock agent").option("--record <file>", "Record session transcript to file").option("--max-turns <n>", `Maximum turns (default: ${DEFAULT_MAX_TURNS})`, String(DEFAULT_MAX_TURNS)).option("--max-patches <n>", `Maximum patches per turn (default: ${DEFAULT_MAX_PATCHES_PER_TURN})`, String(DEFAULT_MAX_PATCHES_PER_TURN)).option("--max-issues <n>", `Maximum issues shown per turn (default: ${DEFAULT_MAX_ISSUES_PER_TURN})`, String(DEFAULT_MAX_ISSUES_PER_TURN)).option("--max-fields <n>", "Maximum unique fields per turn (applied before --max-issues)").option("--max-groups <n>", "Maximum unique groups per turn (applied before --max-issues)").option("--roles <roles>", "Target roles to fill (comma-separated, or '*' for all; default: 'agent', or 'user' in --interactive mode)").option("--mode <mode>", "Fill mode: continue (skip filled fields) or overwrite (re-fill; default: continue)").option("-o, --output <file>", "Write final form to file").option("--prompt <file>", "Path to custom system prompt file (appends to default)").option("--instructions <text>", "Inline system prompt (appends to default; takes precedence over --prompt)").option("-i, --interactive", "Interactive mode: prompt user for field values (defaults to user role)").option("--normalize", "Regenerate form without preserving external content").option("--record-fill", "Write fill record to sidecar .fill.json file").option("--record-fill-stable", "Write fill record without timestamps/durations (for golden tests)").action(async (file, options, cmd) => {
|
|
2772
2773
|
const ctx = getCommandContext(cmd);
|
|
2773
2774
|
const filePath = resolve(file);
|
|
2774
2775
|
try {
|
|
@@ -2817,19 +2818,19 @@ function registerFillCommand(program) {
|
|
|
2817
2818
|
process.exit(1);
|
|
2818
2819
|
}
|
|
2819
2820
|
if (patches.length > 0) applyPatches(form, patches);
|
|
2820
|
-
const durationMs
|
|
2821
|
-
let outputPath
|
|
2822
|
-
if (options.output) outputPath
|
|
2821
|
+
const durationMs = Date.now() - startTime;
|
|
2822
|
+
let outputPath;
|
|
2823
|
+
if (options.output) outputPath = resolve(options.output);
|
|
2823
2824
|
else {
|
|
2824
2825
|
const formsDir = getFormsDir(ctx.formsDir);
|
|
2825
2826
|
await ensureFormsDir(formsDir);
|
|
2826
|
-
outputPath
|
|
2827
|
+
outputPath = generateVersionedPathInFormsDir(filePath, formsDir);
|
|
2827
2828
|
}
|
|
2828
2829
|
if (ctx.dryRun) {
|
|
2829
|
-
logInfo(ctx, `[DRY RUN] Would write form to: ${outputPath
|
|
2830
|
+
logInfo(ctx, `[DRY RUN] Would write form to: ${outputPath}`);
|
|
2830
2831
|
showInteractiveOutro(patches.length, false);
|
|
2831
2832
|
} else {
|
|
2832
|
-
const { reportPath, yamlPath, formPath, schemaPath } = await exportMultiFormat(form, outputPath
|
|
2833
|
+
const { reportPath, yamlPath, formPath, schemaPath } = await exportMultiFormat(form, outputPath);
|
|
2833
2834
|
showInteractiveOutro(patches.length, false);
|
|
2834
2835
|
console.log("");
|
|
2835
2836
|
p.log.success("Outputs:");
|
|
@@ -2838,11 +2839,11 @@ function registerFillCommand(program) {
|
|
|
2838
2839
|
console.log(` ${formatPath(formPath)} ${pc.dim("(filled markform source)")}`);
|
|
2839
2840
|
console.log(` ${formatPath(schemaPath)} ${pc.dim("(JSON Schema)")}`);
|
|
2840
2841
|
}
|
|
2841
|
-
logTiming(ctx, "Fill time", durationMs
|
|
2842
|
+
logTiming(ctx, "Fill time", durationMs);
|
|
2842
2843
|
if (patches.length > 0) {
|
|
2843
2844
|
console.log("");
|
|
2844
2845
|
console.log("Next step: fill remaining fields with agent");
|
|
2845
|
-
console.log(` markform fill ${formatPath(outputPath
|
|
2846
|
+
console.log(` markform fill ${formatPath(outputPath)} --model=<provider/model>`);
|
|
2846
2847
|
}
|
|
2847
2848
|
process.exit(0);
|
|
2848
2849
|
}
|
|
@@ -2867,6 +2868,7 @@ function registerFillCommand(program) {
|
|
|
2867
2868
|
fillMode
|
|
2868
2869
|
});
|
|
2869
2870
|
const harness = createHarness(form, harnessConfig);
|
|
2871
|
+
let collector;
|
|
2870
2872
|
let agent;
|
|
2871
2873
|
let mockPath;
|
|
2872
2874
|
let agentProvider;
|
|
@@ -2877,12 +2879,36 @@ function registerFillCommand(program) {
|
|
|
2877
2879
|
mockPath = resolve(options.mockSource);
|
|
2878
2880
|
logVerbose(ctx, `Reading mock source: ${mockPath}`);
|
|
2879
2881
|
agent = createMockAgent(parseForm(await readFile$1(mockPath)));
|
|
2882
|
+
const structureSummary = computeStructureSummary(form.schema);
|
|
2883
|
+
collector = new FillRecordCollector({
|
|
2884
|
+
form: {
|
|
2885
|
+
id: form.schema.id,
|
|
2886
|
+
title: form.schema.title,
|
|
2887
|
+
description: form.schema.description,
|
|
2888
|
+
structure: structureSummary
|
|
2889
|
+
},
|
|
2890
|
+
provider: "mock",
|
|
2891
|
+
model: "mock",
|
|
2892
|
+
parallelEnabled: false
|
|
2893
|
+
});
|
|
2880
2894
|
} else {
|
|
2881
2895
|
const modelIdString = options.model;
|
|
2882
2896
|
logVerbose(ctx, `Resolving model: ${modelIdString}`);
|
|
2883
2897
|
const { model, provider, modelId } = await resolveModel(modelIdString);
|
|
2884
2898
|
agentProvider = provider;
|
|
2885
2899
|
agentModelName = modelId;
|
|
2900
|
+
const structureSummary = computeStructureSummary(form.schema);
|
|
2901
|
+
collector = new FillRecordCollector({
|
|
2902
|
+
form: {
|
|
2903
|
+
id: form.schema.id,
|
|
2904
|
+
title: form.schema.title,
|
|
2905
|
+
description: form.schema.description,
|
|
2906
|
+
structure: structureSummary
|
|
2907
|
+
},
|
|
2908
|
+
provider,
|
|
2909
|
+
model: modelIdString,
|
|
2910
|
+
parallelEnabled: false
|
|
2911
|
+
});
|
|
2886
2912
|
let systemPrompt;
|
|
2887
2913
|
if (options.instructions) {
|
|
2888
2914
|
systemPrompt = options.instructions;
|
|
@@ -2892,13 +2918,30 @@ function registerFillCommand(program) {
|
|
|
2892
2918
|
logVerbose(ctx, `Reading system prompt from: ${promptPath}`);
|
|
2893
2919
|
systemPrompt = await readFile$1(promptPath);
|
|
2894
2920
|
}
|
|
2895
|
-
const
|
|
2921
|
+
const cliCallbacks = createCliToolCallbacks({
|
|
2896
2922
|
message: (msg) => currentSpinner?.message(msg),
|
|
2897
2923
|
update: (context) => currentSpinner?.update(context),
|
|
2898
2924
|
stop: (msg) => currentSpinner?.stop(msg),
|
|
2899
2925
|
error: (msg) => currentSpinner?.error(msg),
|
|
2900
2926
|
getElapsedMs: () => currentSpinner?.getElapsedMs() ?? 0
|
|
2901
2927
|
}, ctx);
|
|
2928
|
+
const liveCollector = collector;
|
|
2929
|
+
const callbacks = {
|
|
2930
|
+
onToolStart: (call) => {
|
|
2931
|
+
cliCallbacks.onToolStart?.(call);
|
|
2932
|
+
liveCollector.onToolStart(call);
|
|
2933
|
+
},
|
|
2934
|
+
onToolEnd: (call) => {
|
|
2935
|
+
cliCallbacks.onToolEnd?.(call);
|
|
2936
|
+
liveCollector.onToolEnd(call);
|
|
2937
|
+
},
|
|
2938
|
+
onLlmCallStart: (call) => {
|
|
2939
|
+
liveCollector.onLlmCallStart(call);
|
|
2940
|
+
},
|
|
2941
|
+
onLlmCallEnd: (call) => {
|
|
2942
|
+
liveCollector.onLlmCallEnd(call);
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2902
2945
|
targetRole = targetRoles[0] === "*" ? AGENT_ROLE : targetRoles[0] ?? AGENT_ROLE;
|
|
2903
2946
|
const liveAgent = createLiveAgent({
|
|
2904
2947
|
model,
|
|
@@ -3012,6 +3055,24 @@ function registerFillCommand(program) {
|
|
|
3012
3055
|
await writeFile(outputPath, formMarkdown);
|
|
3013
3056
|
logSuccess(ctx, `Form written to: ${outputPath}`);
|
|
3014
3057
|
}
|
|
3058
|
+
const finalInspect = inspect(harness.getForm(), { targetRoles });
|
|
3059
|
+
const progressSummary = computeProgressSummary(form.schema, harness.getForm().responsesByFieldId, harness.getForm().notes, finalInspect.issues);
|
|
3060
|
+
collector.setStatus(stepResult.isComplete ? "completed" : "partial", stepResult.isComplete ? void 0 : "max_turns");
|
|
3061
|
+
const fillRecord = collector.getRecord(progressSummary.counts);
|
|
3062
|
+
if (!ctx.quiet) {
|
|
3063
|
+
console.log("");
|
|
3064
|
+
const summary = formatFillRecordSummary(fillRecord, { verbose: ctx.verbose });
|
|
3065
|
+
console.error(summary);
|
|
3066
|
+
}
|
|
3067
|
+
if (options.recordFill || options.recordFillStable) {
|
|
3068
|
+
const sidecarPath = outputPath.replace(/\.(form\.)?md$/, ".fill.json");
|
|
3069
|
+
const recordToWrite = options.recordFillStable ? stripUnstableFillRecordFields(fillRecord) : fillRecord;
|
|
3070
|
+
if (ctx.dryRun) logInfo(ctx, `[DRY RUN] Would write fill record to: ${sidecarPath}`);
|
|
3071
|
+
else {
|
|
3072
|
+
writeFileSync(sidecarPath, JSON.stringify(recordToWrite, null, 2));
|
|
3073
|
+
logSuccess(ctx, `Fill record written to: ${sidecarPath}`);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3015
3076
|
const transcript = buildSessionTranscript(filePath, options.mock ? "mock" : "live", mockPath, options.model, harnessConfig, harness.getTurns(), stepResult.isComplete, outputPath);
|
|
3016
3077
|
if (options.record) {
|
|
3017
3078
|
const recordPath = resolve(options.record);
|
|
@@ -3098,7 +3159,6 @@ function formatPriority$1(priority, useColors) {
|
|
|
3098
3159
|
case 2: return pc.yellow(label);
|
|
3099
3160
|
case 3: return pc.cyan(label);
|
|
3100
3161
|
case 4: return pc.blue(label);
|
|
3101
|
-
case 5:
|
|
3102
3162
|
default: return pc.dim(label);
|
|
3103
3163
|
}
|
|
3104
3164
|
}
|
|
@@ -3535,6 +3595,191 @@ function registerModelsCommand(program) {
|
|
|
3535
3595
|
});
|
|
3536
3596
|
}
|
|
3537
3597
|
|
|
3598
|
+
//#endregion
|
|
3599
|
+
//#region src/cli/commands/plan.ts
|
|
3600
|
+
/**
|
|
3601
|
+
* Get the response status string for a field.
|
|
3602
|
+
*/
|
|
3603
|
+
function getFieldStatus(form, fieldId) {
|
|
3604
|
+
const response = form.responsesByFieldId[fieldId];
|
|
3605
|
+
if (!response) return "unanswered";
|
|
3606
|
+
return response.state;
|
|
3607
|
+
}
|
|
3608
|
+
/**
|
|
3609
|
+
* Check if a field still needs work (not yet answered).
|
|
3610
|
+
*/
|
|
3611
|
+
function fieldNeedsWork(form, fieldId) {
|
|
3612
|
+
const response = form.responsesByFieldId[fieldId];
|
|
3613
|
+
if (!response) return true;
|
|
3614
|
+
return response.state !== "answered";
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Get all field IDs for an execution plan item.
|
|
3618
|
+
*/
|
|
3619
|
+
function getFieldIdsForItem(form, item) {
|
|
3620
|
+
if (item.itemType === "field") return [item.itemId];
|
|
3621
|
+
const group = form.schema.groups.find((g) => g.id === item.itemId);
|
|
3622
|
+
if (!group) return [];
|
|
3623
|
+
return group.children.map((f) => f.id);
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Get field metadata from the form schema.
|
|
3627
|
+
*/
|
|
3628
|
+
function getFieldMeta(form, fieldId) {
|
|
3629
|
+
for (const group of form.schema.groups) {
|
|
3630
|
+
const field = group.children.find((f) => f.id === fieldId);
|
|
3631
|
+
if (field) return field;
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Build a PlanItemJson, including only fields that need work.
|
|
3636
|
+
* Returns undefined if no fields need work.
|
|
3637
|
+
*/
|
|
3638
|
+
function buildPlanItem(form, item) {
|
|
3639
|
+
const remainingFieldIds = getFieldIdsForItem(form, item).filter((id) => fieldNeedsWork(form, id));
|
|
3640
|
+
if (remainingFieldIds.length === 0) return;
|
|
3641
|
+
const planItem = {
|
|
3642
|
+
itemId: item.itemId,
|
|
3643
|
+
itemType: item.itemType
|
|
3644
|
+
};
|
|
3645
|
+
if (item.itemType === "group") planItem.fields = remainingFieldIds.map((id) => {
|
|
3646
|
+
const meta = getFieldMeta(form, id);
|
|
3647
|
+
return {
|
|
3648
|
+
fieldId: id,
|
|
3649
|
+
label: meta?.label,
|
|
3650
|
+
status: getFieldStatus(form, id),
|
|
3651
|
+
required: meta?.required ?? false
|
|
3652
|
+
};
|
|
3653
|
+
});
|
|
3654
|
+
return planItem;
|
|
3655
|
+
}
|
|
3656
|
+
function formatConsolePlan(report, useColors) {
|
|
3657
|
+
const lines = [];
|
|
3658
|
+
const bold = useColors ? pc.bold : (s) => s;
|
|
3659
|
+
const dim = useColors ? pc.dim : (s) => s;
|
|
3660
|
+
const cyan = useColors ? pc.cyan : (s) => s;
|
|
3661
|
+
const yellow = useColors ? pc.yellow : (s) => s;
|
|
3662
|
+
const titlePart = report.title ? ` (${report.title})` : "";
|
|
3663
|
+
lines.push(bold(cyan(`Plan: ${report.formId}${titlePart}`)));
|
|
3664
|
+
lines.push("");
|
|
3665
|
+
if (report.orderLevels.length === 0) {
|
|
3666
|
+
lines.push(dim("No remaining work — all fields are complete."));
|
|
3667
|
+
return lines.join("\n");
|
|
3668
|
+
}
|
|
3669
|
+
for (const level of report.orderLevels) {
|
|
3670
|
+
const itemCount = level.looseSerial.length + level.parallelBatches.reduce((sum, b) => sum + b.items.length, 0);
|
|
3671
|
+
lines.push(bold(`Order level ${level.order} (${itemCount} items):`));
|
|
3672
|
+
if (level.looseSerial.length > 0) {
|
|
3673
|
+
lines.push(` Loose serial (primary agent):`);
|
|
3674
|
+
for (const item of level.looseSerial) formatItem(lines, item, dim, yellow, " ");
|
|
3675
|
+
}
|
|
3676
|
+
for (const batch of level.parallelBatches) {
|
|
3677
|
+
lines.push(` Parallel batch "${batch.batchId}" (${batch.items.length} items, ${batch.items.length} agents):`);
|
|
3678
|
+
for (const item of batch.items) formatItem(lines, item, dim, yellow, " ");
|
|
3679
|
+
}
|
|
3680
|
+
lines.push("");
|
|
3681
|
+
}
|
|
3682
|
+
const s = report.summary;
|
|
3683
|
+
lines.push(dim(`Summary: ${s.orderLevelCount} order level${s.orderLevelCount !== 1 ? "s" : ""}, ${s.parallelBatchCount} parallel batch${s.parallelBatchCount !== 1 ? "es" : ""}, ${s.remainingFields} remaining field${s.remainingFields !== 1 ? "s" : ""}`));
|
|
3684
|
+
return lines.join("\n");
|
|
3685
|
+
}
|
|
3686
|
+
function formatItem(lines, item, dim, yellow, indent) {
|
|
3687
|
+
if (item.itemType === "group" && item.fields) {
|
|
3688
|
+
lines.push(`${indent}- ${item.itemId} [group]`);
|
|
3689
|
+
for (const f of item.fields) {
|
|
3690
|
+
const label = f.label ? ` (${f.label})` : "";
|
|
3691
|
+
const req = f.required ? yellow("required") + ", " : "";
|
|
3692
|
+
lines.push(`${indent} ${f.fieldId}${label} — ${req}${dim(f.status)}`);
|
|
3693
|
+
}
|
|
3694
|
+
} else {
|
|
3695
|
+
const meta = item;
|
|
3696
|
+
lines.push(`${indent}- ${meta.itemId}`);
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
/**
|
|
3700
|
+
* Register the plan command.
|
|
3701
|
+
*/
|
|
3702
|
+
function registerPlanCommand(program) {
|
|
3703
|
+
program.command("plan <file>").description("Show the idealized execution plan for a form (parallel batches, order levels)").action(async (file, _options, cmd) => {
|
|
3704
|
+
const ctx = getCommandContext(cmd);
|
|
3705
|
+
try {
|
|
3706
|
+
logVerbose(ctx, `Reading file: ${file}`);
|
|
3707
|
+
const content = await readFile$1(file);
|
|
3708
|
+
logVerbose(ctx, "Parsing and validating form...");
|
|
3709
|
+
const form = parseForm(content);
|
|
3710
|
+
const parseErrors = inspect(form).issues.filter((i) => i.reason === "validation_error");
|
|
3711
|
+
if (parseErrors.length > 0) {
|
|
3712
|
+
logError(`Form has validation errors. Fix them before planning:\n` + parseErrors.map((e) => ` - ${e.message}`).join("\n"));
|
|
3713
|
+
process.exit(1);
|
|
3714
|
+
}
|
|
3715
|
+
logVerbose(ctx, "Computing execution plan...");
|
|
3716
|
+
const executionPlan = computeExecutionPlan(form);
|
|
3717
|
+
const orderLevels = [];
|
|
3718
|
+
let totalItems = 0;
|
|
3719
|
+
let totalRemainingFields = 0;
|
|
3720
|
+
let totalBatches = 0;
|
|
3721
|
+
for (const order of executionPlan.orderLevels) {
|
|
3722
|
+
const looseItems = [];
|
|
3723
|
+
for (const item of executionPlan.looseSerial) {
|
|
3724
|
+
if (item.order !== order) continue;
|
|
3725
|
+
const planItem = buildPlanItem(form, item);
|
|
3726
|
+
if (planItem) {
|
|
3727
|
+
looseItems.push(planItem);
|
|
3728
|
+
totalItems++;
|
|
3729
|
+
totalRemainingFields += countRemainingFields(form, item);
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
const batches = [];
|
|
3733
|
+
for (const batch of executionPlan.parallelBatches) {
|
|
3734
|
+
const batchItems = [];
|
|
3735
|
+
for (const item of batch.items) {
|
|
3736
|
+
if (item.order !== order) continue;
|
|
3737
|
+
const planItem = buildPlanItem(form, item);
|
|
3738
|
+
if (planItem) {
|
|
3739
|
+
batchItems.push(planItem);
|
|
3740
|
+
totalItems++;
|
|
3741
|
+
totalRemainingFields += countRemainingFields(form, item);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
if (batchItems.length > 0) {
|
|
3745
|
+
batches.push({
|
|
3746
|
+
batchId: batch.batchId,
|
|
3747
|
+
items: batchItems
|
|
3748
|
+
});
|
|
3749
|
+
totalBatches++;
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
if (looseItems.length > 0 || batches.length > 0) orderLevels.push({
|
|
3753
|
+
order,
|
|
3754
|
+
looseSerial: looseItems,
|
|
3755
|
+
parallelBatches: batches
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3758
|
+
const output = formatOutput(ctx, {
|
|
3759
|
+
formId: form.schema.id,
|
|
3760
|
+
title: form.schema.title,
|
|
3761
|
+
orderLevels,
|
|
3762
|
+
summary: {
|
|
3763
|
+
orderLevelCount: orderLevels.length,
|
|
3764
|
+
parallelBatchCount: totalBatches,
|
|
3765
|
+
totalItems,
|
|
3766
|
+
remainingFields: totalRemainingFields
|
|
3767
|
+
}
|
|
3768
|
+
}, (data, useColors) => formatConsolePlan(data, useColors));
|
|
3769
|
+
console.log(output);
|
|
3770
|
+
} catch (error) {
|
|
3771
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
3772
|
+
process.exit(1);
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
/**
|
|
3777
|
+
* Count remaining fields for an execution plan item.
|
|
3778
|
+
*/
|
|
3779
|
+
function countRemainingFields(form, item) {
|
|
3780
|
+
return getFieldIdsForItem(form, item).filter((id) => fieldNeedsWork(form, id)).length;
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3538
3783
|
//#endregion
|
|
3539
3784
|
//#region src/cli/commands/serve.ts
|
|
3540
3785
|
/**
|
|
@@ -5130,7 +5375,7 @@ function renderViewFieldValue(field, value, isSkipped) {
|
|
|
5130
5375
|
case "string": {
|
|
5131
5376
|
const v = value.kind === "string" ? value.value : null;
|
|
5132
5377
|
if (v === null || v === "") return "<div class=\"view-field-empty\">(not filled)</div>";
|
|
5133
|
-
return `<div class="view-field-value">${
|
|
5378
|
+
return `<div class="view-field-value">${formatBareUrlsAsHtmlLinks(v, escapeHtml)}</div>`;
|
|
5134
5379
|
}
|
|
5135
5380
|
case "number": {
|
|
5136
5381
|
const v = value.kind === "number" ? value.value : null;
|
|
@@ -5140,7 +5385,7 @@ function renderViewFieldValue(field, value, isSkipped) {
|
|
|
5140
5385
|
case "string_list": {
|
|
5141
5386
|
const items = value.kind === "string_list" ? value.items : [];
|
|
5142
5387
|
if (items.length === 0) return "<div class=\"view-field-empty\">(not filled)</div>";
|
|
5143
|
-
return `<div class="view-field-value"><ul>${items.map((i) => `<li>${
|
|
5388
|
+
return `<div class="view-field-value"><ul>${items.map((i) => `<li>${formatBareUrlsAsHtmlLinks(i, escapeHtml)}</li>`).join("")}</ul></div>`;
|
|
5144
5389
|
}
|
|
5145
5390
|
case "single_select": {
|
|
5146
5391
|
const selected = value.kind === "single_select" ? value.selected : null;
|
|
@@ -5204,7 +5449,7 @@ function renderViewFieldValue(field, value, isSkipped) {
|
|
|
5204
5449
|
if (col.type === "url" && cellValue) {
|
|
5205
5450
|
const domain = friendlyUrlAbbrev(cellValue);
|
|
5206
5451
|
cellHtml = `<a href="${escapeHtml(cellValue)}" target="_blank" class="url-link" data-url="${escapeHtml(cellValue)}">${escapeHtml(domain)}</a>`;
|
|
5207
|
-
} else cellHtml =
|
|
5452
|
+
} else cellHtml = formatBareUrlsAsHtmlLinks(cellValue, escapeHtml);
|
|
5208
5453
|
}
|
|
5209
5454
|
tableHtml += `<td>${cellHtml}</td>`;
|
|
5210
5455
|
}
|
|
@@ -5415,6 +5660,7 @@ function renderMarkdownContent(content) {
|
|
|
5415
5660
|
}
|
|
5416
5661
|
/**
|
|
5417
5662
|
* Format inline markdown (bold, italic, code, links, checkboxes).
|
|
5663
|
+
* Also auto-links bare URLs for consistency.
|
|
5418
5664
|
*/
|
|
5419
5665
|
function formatInlineMarkdown(text) {
|
|
5420
5666
|
let result = escapeHtml(text);
|
|
@@ -5427,6 +5673,12 @@ function formatInlineMarkdown(text) {
|
|
|
5427
5673
|
const cleanUrl = url.replace(/&/g, "&");
|
|
5428
5674
|
return `<a href="${cleanUrl}" target="_blank" class="url-link" data-url="${cleanUrl}">${linkText}</a>`;
|
|
5429
5675
|
});
|
|
5676
|
+
result = result.replace(/(?<!href="|data-url="|">|\]\()(?:https?:\/\/|www\.)[^\s<>"]+(?<![.,;:!?'")])/g, (url) => {
|
|
5677
|
+
const cleanUrl = url.replace(/&/g, "&");
|
|
5678
|
+
const fullUrl = cleanUrl.startsWith("www.") ? `https://${cleanUrl}` : cleanUrl;
|
|
5679
|
+
const display = friendlyUrlAbbrev(fullUrl);
|
|
5680
|
+
return `<a href="${escapeHtml(fullUrl)}" target="_blank" class="url-link" data-url="${escapeHtml(fullUrl)}">${escapeHtml(display)}</a>`;
|
|
5681
|
+
});
|
|
5430
5682
|
return result;
|
|
5431
5683
|
}
|
|
5432
5684
|
/**
|
|
@@ -5594,7 +5846,7 @@ function registerResearchCommand(program) {
|
|
|
5594
5846
|
const modelId = options.model;
|
|
5595
5847
|
const { provider, model: modelName } = parseModelIdForDisplay(modelId);
|
|
5596
5848
|
if (!hasWebSearchSupport(provider)) {
|
|
5597
|
-
const webSearchProviders = Object.entries(WEB_SEARCH_CONFIG).filter(([, config]) => config.supported).map(([p
|
|
5849
|
+
const webSearchProviders = Object.entries(WEB_SEARCH_CONFIG).filter(([, config]) => config.supported).map(([p]) => p);
|
|
5598
5850
|
logError(`Model "${modelId}" does not support web search.`);
|
|
5599
5851
|
console.log("");
|
|
5600
5852
|
console.log(pc.yellow("Research forms require web search capabilities."));
|
|
@@ -5642,6 +5894,7 @@ function registerResearchCommand(program) {
|
|
|
5642
5894
|
model: modelId,
|
|
5643
5895
|
enableWebSearch: true,
|
|
5644
5896
|
captureWireFormat: false,
|
|
5897
|
+
recordFill: false,
|
|
5645
5898
|
maxTurnsTotal: maxTurns,
|
|
5646
5899
|
maxPatchesPerTurn,
|
|
5647
5900
|
maxIssuesPerTurn,
|
|
@@ -5664,10 +5917,10 @@ function registerResearchCommand(program) {
|
|
|
5664
5917
|
console.log(` ${formPath} ${pc.dim("(filled markform source)")}`);
|
|
5665
5918
|
console.log(` ${schemaPath} ${pc.dim("(JSON Schema)")}`);
|
|
5666
5919
|
if (options.transcript && result.transcript) {
|
|
5667
|
-
const { serializeSession
|
|
5920
|
+
const { serializeSession } = await import("./session-ZHBi3LVQ.mjs");
|
|
5668
5921
|
const transcriptPath = outputPath.replace(/\.form\.md$/, ".session.yaml");
|
|
5669
|
-
const { writeFile
|
|
5670
|
-
await writeFile
|
|
5922
|
+
const { writeFile } = await import("./shared-BTR35aMz.mjs");
|
|
5923
|
+
await writeFile(transcriptPath, serializeSession(result.transcript));
|
|
5671
5924
|
logInfo(ctx, `Transcript: ${transcriptPath}`);
|
|
5672
5925
|
}
|
|
5673
5926
|
logTiming(ctx, "Research fill", Date.now() - startTime);
|
|
@@ -5742,7 +5995,6 @@ function computeFieldStats(form, fields) {
|
|
|
5742
5995
|
case "aborted":
|
|
5743
5996
|
aborted++;
|
|
5744
5997
|
break;
|
|
5745
|
-
case "unanswered":
|
|
5746
5998
|
default:
|
|
5747
5999
|
unanswered++;
|
|
5748
6000
|
break;
|
|
@@ -5909,7 +6161,6 @@ function formatPriority(priority, useColors) {
|
|
|
5909
6161
|
case 2: return pc.yellow(label);
|
|
5910
6162
|
case 3: return pc.cyan(label);
|
|
5911
6163
|
case 4: return pc.blue(label);
|
|
5912
|
-
case 5:
|
|
5913
6164
|
default: return pc.dim(label);
|
|
5914
6165
|
}
|
|
5915
6166
|
}
|
|
@@ -6053,6 +6304,7 @@ function createProgram() {
|
|
|
6053
6304
|
registerFillCommand(program);
|
|
6054
6305
|
registerInspectCommand(program);
|
|
6055
6306
|
registerModelsCommand(program);
|
|
6307
|
+
registerPlanCommand(program);
|
|
6056
6308
|
registerRenderCommand(program);
|
|
6057
6309
|
registerReportCommand(program);
|
|
6058
6310
|
registerResearchCommand(program);
|
|
@@ -6072,4 +6324,4 @@ async function runCli() {
|
|
|
6072
6324
|
|
|
6073
6325
|
//#endregion
|
|
6074
6326
|
export { runCli as t };
|
|
6075
|
-
//# sourceMappingURL=cli-
|
|
6327
|
+
//# sourceMappingURL=cli-tpvFNqFY.mjs.map
|