truthmark 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +672 -194
- package/README.es.md +673 -195
- package/README.md +663 -192
- package/README.ru.md +680 -202
- package/README.zh.md +679 -199
- package/dist/main.js +1307 -145
- package/dist/main.js.map +1 -1
- package/package.json +1 -2
package/dist/main.js
CHANGED
|
@@ -334,6 +334,22 @@ var truthmarkConfigSchema = {
|
|
|
334
334
|
type: "string"
|
|
335
335
|
}
|
|
336
336
|
},
|
|
337
|
+
"truthmark-portal": {
|
|
338
|
+
type: "object",
|
|
339
|
+
additionalProperties: false,
|
|
340
|
+
required: [],
|
|
341
|
+
properties: {
|
|
342
|
+
enabled: {
|
|
343
|
+
type: "boolean"
|
|
344
|
+
},
|
|
345
|
+
output: {
|
|
346
|
+
type: "string"
|
|
347
|
+
},
|
|
348
|
+
template: {
|
|
349
|
+
type: "string"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
337
353
|
frontmatter: {
|
|
338
354
|
type: "object",
|
|
339
355
|
nullable: true,
|
|
@@ -391,6 +407,11 @@ var DEFAULT_AUTHORITY = [
|
|
|
391
407
|
`${DEFAULT_DOCS_HIERARCHY.roots.truth}/**/*.md`
|
|
392
408
|
];
|
|
393
409
|
var DEFAULT_INSTRUCTION_TARGETS = ["AGENTS.md"];
|
|
410
|
+
var DEFAULT_TRUTHMARK_PORTAL = {
|
|
411
|
+
enabled: false,
|
|
412
|
+
output: "docs/truthmark-portal",
|
|
413
|
+
template: "default"
|
|
414
|
+
};
|
|
394
415
|
var createDefaultRawConfig = () => ({
|
|
395
416
|
version: 1,
|
|
396
417
|
platforms: [...DEFAULT_PLATFORMS],
|
|
@@ -422,6 +443,7 @@ var createDefaultConfig = () => ({
|
|
|
422
443
|
},
|
|
423
444
|
authority: [...DEFAULT_AUTHORITY],
|
|
424
445
|
instructionTargets: [...DEFAULT_INSTRUCTION_TARGETS],
|
|
446
|
+
truthmarkPortal: { ...DEFAULT_TRUTHMARK_PORTAL },
|
|
425
447
|
frontmatter: {
|
|
426
448
|
required: [],
|
|
427
449
|
recommended: ["status", "doc_type", "last_reviewed", "source_of_truth"]
|
|
@@ -578,9 +600,9 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName) => {
|
|
|
578
600
|
const diagnostics = [];
|
|
579
601
|
const truthDocumentEntries = [];
|
|
580
602
|
for (const rawEntry of rawEntries) {
|
|
581
|
-
const
|
|
603
|
+
const path13 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
|
|
582
604
|
const kind = rawEntry && typeof rawEntry === "object" && "kind" in rawEntry ? rawEntry.kind : null;
|
|
583
|
-
if (typeof
|
|
605
|
+
if (typeof path13 !== "string" || path13.trim().length === 0 || !isTruthDocumentKind(kind)) {
|
|
584
606
|
diagnostics.push(
|
|
585
607
|
createAreaDiagnostic(
|
|
586
608
|
`Area ${areaName} truth_documents entries must include non-empty path and valid kind fields.`,
|
|
@@ -590,7 +612,7 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName) => {
|
|
|
590
612
|
continue;
|
|
591
613
|
}
|
|
592
614
|
truthDocumentEntries.push({
|
|
593
|
-
path:
|
|
615
|
+
path: path13.trim(),
|
|
594
616
|
kind,
|
|
595
617
|
kindSource: "explicit"
|
|
596
618
|
});
|
|
@@ -1160,6 +1182,7 @@ import fs7 from "fs/promises";
|
|
|
1160
1182
|
|
|
1161
1183
|
// src/config/load.ts
|
|
1162
1184
|
import fs4 from "fs/promises";
|
|
1185
|
+
import path4 from "path";
|
|
1163
1186
|
import { Ajv } from "ajv";
|
|
1164
1187
|
import { parse as parse2 } from "yaml";
|
|
1165
1188
|
var ajv = new Ajv({ allErrors: true });
|
|
@@ -1172,6 +1195,69 @@ var toConfigDiagnostic = (message, file) => {
|
|
|
1172
1195
|
file
|
|
1173
1196
|
};
|
|
1174
1197
|
};
|
|
1198
|
+
var normalizeRepoRelativePath = (value) => {
|
|
1199
|
+
const slashNormalized = value.replace(/\\/gu, "/");
|
|
1200
|
+
const pathNormalized = path4.posix.normalize(slashNormalized).replace(/\/+$/u, "");
|
|
1201
|
+
return pathNormalized;
|
|
1202
|
+
};
|
|
1203
|
+
var isUnsafeRepoRelativePath = (value) => {
|
|
1204
|
+
const slashNormalized = value.replace(/\\/gu, "/");
|
|
1205
|
+
const normalized = normalizeRepoRelativePath(value);
|
|
1206
|
+
const parts = slashNormalized.split("/");
|
|
1207
|
+
return normalized.length === 0 || normalized === "." || normalized === ".." || path4.isAbsolute(value) || path4.posix.isAbsolute(slashNormalized) || path4.win32.isAbsolute(value) || /^[A-Za-z]:/u.test(value) || normalized.startsWith("../") || parts.includes("..");
|
|
1208
|
+
};
|
|
1209
|
+
var pathsOverlap = (left, right) => {
|
|
1210
|
+
const normalizedLeft = normalizeRepoRelativePath(left);
|
|
1211
|
+
const normalizedRight = normalizeRepoRelativePath(right);
|
|
1212
|
+
return normalizedLeft === normalizedRight || normalizedLeft.startsWith(`${normalizedRight}/`) || normalizedRight.startsWith(`${normalizedLeft}/`);
|
|
1213
|
+
};
|
|
1214
|
+
var portalForbiddenOutputRoots = (rawConfig) => {
|
|
1215
|
+
const rawDocs = rawConfig.docs;
|
|
1216
|
+
const docsRoots = rawDocs?.roots ?? {};
|
|
1217
|
+
const routing = rawDocs?.routing ?? DEFAULT_DOCS_HIERARCHY.routing;
|
|
1218
|
+
return [
|
|
1219
|
+
"src",
|
|
1220
|
+
DEFAULT_DOCS_HIERARCHY.roots.ai,
|
|
1221
|
+
DEFAULT_DOCS_HIERARCHY.roots.standards,
|
|
1222
|
+
DEFAULT_DOCS_HIERARCHY.roots.architecture,
|
|
1223
|
+
DEFAULT_DOCS_HIERARCHY.roots.truth,
|
|
1224
|
+
...Object.values(docsRoots),
|
|
1225
|
+
routing.root_index,
|
|
1226
|
+
routing.area_files_root,
|
|
1227
|
+
".truthmark/config.yml",
|
|
1228
|
+
"AGENTS.md",
|
|
1229
|
+
"CLAUDE.md",
|
|
1230
|
+
"GEMINI.md",
|
|
1231
|
+
".github/copilot-instructions.md",
|
|
1232
|
+
...rawConfig.instruction_targets ?? DEFAULT_INSTRUCTION_TARGETS
|
|
1233
|
+
];
|
|
1234
|
+
};
|
|
1235
|
+
var validatePortalConfig = (rawConfig, configPath) => {
|
|
1236
|
+
const portal = rawConfig["truthmark-portal"];
|
|
1237
|
+
if (portal === void 0) {
|
|
1238
|
+
return [];
|
|
1239
|
+
}
|
|
1240
|
+
const diagnostics = [];
|
|
1241
|
+
const output = portal.output ?? DEFAULT_TRUTHMARK_PORTAL.output;
|
|
1242
|
+
const template = portal.template ?? DEFAULT_TRUTHMARK_PORTAL.template;
|
|
1243
|
+
if (isUnsafeRepoRelativePath(output) || portalForbiddenOutputRoots(rawConfig).some((forbidden) => pathsOverlap(output, forbidden))) {
|
|
1244
|
+
diagnostics.push(
|
|
1245
|
+
toConfigDiagnostic(
|
|
1246
|
+
"truthmark-portal.output must be a non-empty repo-relative directory that does not overlap source, instruction, routing, or canonical docs roots.",
|
|
1247
|
+
configPath
|
|
1248
|
+
)
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
if (template !== "default" && isUnsafeRepoRelativePath(template)) {
|
|
1252
|
+
diagnostics.push(
|
|
1253
|
+
toConfigDiagnostic(
|
|
1254
|
+
"truthmark-portal.template must be 'default' or a non-empty repo-relative template path without absolute or parent traversal segments.",
|
|
1255
|
+
configPath
|
|
1256
|
+
)
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
return diagnostics;
|
|
1260
|
+
};
|
|
1175
1261
|
var normalizeConfig = (rawConfig) => {
|
|
1176
1262
|
const rawDocs = rawConfig.docs ?? {
|
|
1177
1263
|
layout: DEFAULT_DOCS_HIERARCHY.layout,
|
|
@@ -1194,6 +1280,11 @@ var normalizeConfig = (rawConfig) => {
|
|
|
1194
1280
|
},
|
|
1195
1281
|
authority: rawConfig.authority,
|
|
1196
1282
|
instructionTargets: rawConfig.instruction_targets ?? [...DEFAULT_INSTRUCTION_TARGETS],
|
|
1283
|
+
truthmarkPortal: {
|
|
1284
|
+
enabled: rawConfig["truthmark-portal"]?.enabled ?? DEFAULT_TRUTHMARK_PORTAL.enabled,
|
|
1285
|
+
output: rawConfig["truthmark-portal"]?.output ?? DEFAULT_TRUTHMARK_PORTAL.output,
|
|
1286
|
+
template: rawConfig["truthmark-portal"]?.template ?? DEFAULT_TRUTHMARK_PORTAL.template
|
|
1287
|
+
},
|
|
1197
1288
|
frontmatter: {
|
|
1198
1289
|
required: rawConfig.frontmatter?.required ?? [],
|
|
1199
1290
|
recommended: rawConfig.frontmatter?.recommended ?? []
|
|
@@ -1247,6 +1338,18 @@ var loadConfig = async (rootDir) => {
|
|
|
1247
1338
|
configPath
|
|
1248
1339
|
};
|
|
1249
1340
|
}
|
|
1341
|
+
const portalDiagnostics = validatePortalConfig(
|
|
1342
|
+
parsedConfig,
|
|
1343
|
+
configPath
|
|
1344
|
+
);
|
|
1345
|
+
if (portalDiagnostics.length > 0) {
|
|
1346
|
+
return {
|
|
1347
|
+
status: "invalid",
|
|
1348
|
+
config: null,
|
|
1349
|
+
diagnostics: portalDiagnostics,
|
|
1350
|
+
configPath
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1250
1353
|
return {
|
|
1251
1354
|
status: "loaded",
|
|
1252
1355
|
config: normalizeConfig(parsedConfig),
|
|
@@ -1409,13 +1512,13 @@ var DECISION_TRUTH_INSTRUCTIONS = [
|
|
|
1409
1512
|
"Update Product Decisions and Rationale when a decision changes behavior."
|
|
1410
1513
|
].join("\n");
|
|
1411
1514
|
var EVIDENCE_AUTHORITY_INSTRUCTIONS = [
|
|
1412
|
-
"Repository instruction
|
|
1515
|
+
"Repository instruction files and explicitly configured policy docs remain instruction authority when present; do not assume a repository uses any particular policy path.",
|
|
1413
1516
|
"Implementation code and canonical truth docs are inspected evidence for current behavior; they do not silently override workflow write boundaries."
|
|
1414
1517
|
].join("\n");
|
|
1415
1518
|
var REPOSITORY_INTELLIGENCE_INSTRUCTIONS = [
|
|
1416
1519
|
"Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and ContextPack may guide routing, context selection, and verification planning when available.",
|
|
1417
1520
|
"They do not override checkout evidence, canonical truth docs, route files, or workflow write boundaries.",
|
|
1418
|
-
"If unavailable, inspect
|
|
1521
|
+
"If unavailable, inspect any present Truthmark config, route files, source files, truth docs, and tests directly, then report that repository-intelligence artifacts were not generated."
|
|
1419
1522
|
].join("\n");
|
|
1420
1523
|
var FEATURE_DOC_TEMPLATE_INSTRUCTIONS = [
|
|
1421
1524
|
"When creating or updating a truth doc, inspect the routed truth kind and use the matching `docs/templates/<kind>-doc.md` template.",
|
|
@@ -1567,17 +1670,38 @@ var renderCopilotCustomAgentModeSection = (agents, parentRule, writeAgents = [])
|
|
|
1567
1670
|
`- ${parentRule}`
|
|
1568
1671
|
].join("\n");
|
|
1569
1672
|
};
|
|
1673
|
+
var renderGeminiSubagentModeSection = (agents, parentRule, writeAgents = []) => {
|
|
1674
|
+
const mentions = agents.map((agent) => `@${agent.replace(/_/gu, "-")}`);
|
|
1675
|
+
const writeMentions = writeAgents.map((agent) => `@${agent.replace(/_/gu, "-")}`);
|
|
1676
|
+
const writeAgentLines = writeMentions.length > 0 ? [
|
|
1677
|
+
`- dispatch write-capable project subagents only with explicit write leases: ${writeMentions.join(", ")}`,
|
|
1678
|
+
"- each write lease must name objective, required reads, allowed writes, forbidden writes, evidence, verification, and report fields",
|
|
1679
|
+
"- write workers must stop when a required edit is off-lease and report status, filesChanged, evidence, offLeaseChanges, blockers, and notes",
|
|
1680
|
+
"- parent must inspect the actual checkout diff against each lease before accepting a worker report"
|
|
1681
|
+
] : [];
|
|
1682
|
+
const readOnlyScope = writeAgents.length > 0 ? "for verification" : "only";
|
|
1683
|
+
const readOnlySubagentLabel = writeAgents.length > 0 ? "read-only subagents" : "subagents";
|
|
1684
|
+
return [
|
|
1685
|
+
"Gemini CLI subagent mode:",
|
|
1686
|
+
"- use automatically when this workflow runs in Gemini CLI and the parent agent chooses bounded project subagent fan-out",
|
|
1687
|
+
`- dispatch read-only project subagents ${readOnlyScope}: ${mentions.join(", ")}`,
|
|
1688
|
+
`- ${readOnlySubagentLabel} inspect checkout evidence directly, return structured findings, and must not edit files`,
|
|
1689
|
+
`- parent supplies bounded evidence shards; ${readOnlySubagentLabel} must not preload host instruction files or repo-wide policy docs unless assigned as evidence`,
|
|
1690
|
+
...writeAgentLines,
|
|
1691
|
+
`- ${parentRule}`
|
|
1692
|
+
].join("\n");
|
|
1693
|
+
};
|
|
1570
1694
|
var defaultAgentConfig = () => {
|
|
1571
1695
|
return createDefaultConfig();
|
|
1572
1696
|
};
|
|
1573
1697
|
var renderHierarchySummary = (config) => {
|
|
1574
1698
|
const truthRoot3 = resolveTruthDocsRoot(config);
|
|
1575
1699
|
return [
|
|
1576
|
-
"Truthmark hierarchy:",
|
|
1577
|
-
"- Config: .truthmark/config.yml",
|
|
1578
|
-
`- Root route index: ${config.docs.routing.rootIndex}`,
|
|
1579
|
-
`- Area route files: ${config.docs.routing.areaFilesRoot}/**/*.md`,
|
|
1580
|
-
`- Truth docs: ${truthRoot3}/**/*.md`
|
|
1700
|
+
"Truthmark hierarchy hints:",
|
|
1701
|
+
"- Config, when present: .truthmark/config.yml",
|
|
1702
|
+
`- Root route index, when present: ${config.docs.routing.rootIndex}`,
|
|
1703
|
+
`- Area route files, when present: ${config.docs.routing.areaFilesRoot}/**/*.md`,
|
|
1704
|
+
`- Truth docs, when present: ${truthRoot3}/**/*.md`
|
|
1581
1705
|
].join("\n");
|
|
1582
1706
|
};
|
|
1583
1707
|
|
|
@@ -1593,9 +1717,10 @@ var TRUTHMARK_BLOCK_START = "<!-- truthmark:start -->";
|
|
|
1593
1717
|
var TRUTHMARK_BLOCK_END = "<!-- truthmark:end -->";
|
|
1594
1718
|
var renderCompactHierarchySummary = (config) => {
|
|
1595
1719
|
const truthRoot3 = resolveTruthDocsRoot(config);
|
|
1596
|
-
return `Hierarchy: config .truthmark/config.yml; routes ${config.docs.routing.rootIndex} and ${config.docs.routing.areaFilesRoot}/**/*.md; Truth docs: ${truthRoot3}/**/*.md.`;
|
|
1720
|
+
return `Hierarchy hints: config .truthmark/config.yml when present; routes ${config.docs.routing.rootIndex} and ${config.docs.routing.areaFilesRoot}/**/*.md when present; Truth docs: ${truthRoot3}/**/*.md when present.`;
|
|
1597
1721
|
};
|
|
1598
1722
|
var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
1723
|
+
const portalLine = config.truthmarkPortal.enabled ? `Truthmark Portal is a separate manual-only presentation workflow. Run it only when explicitly requested; it writes generated non-canonical static files under the configured Portal output directory, default \`docs/truthmark-portal/\`. Markdown remains canonical.` : null;
|
|
1599
1724
|
return [
|
|
1600
1725
|
TRUTHMARK_BLOCK_START,
|
|
1601
1726
|
"## Truthmark Workflow",
|
|
@@ -1609,6 +1734,7 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
|
1609
1734
|
"Support new or changed behavior-bearing truth claims with checkout evidence. Code leads; truth docs follow. Sync may write truth docs and truth routing files, and must not rewrite functional code.",
|
|
1610
1735
|
"If routing cannot map changed code to a bounded truth owner, run Truth Structure before syncing when safe; otherwise block and recommend Truth Structure. Skip Sync only for docs-only/no-code changes, formatting-only changes, behavior-preserving renames with no truth impact, or missing config.",
|
|
1611
1736
|
"Explicit workflows: Truth Structure, Truth Document, Truth Preview, Truth Realize, Truth Check. Run only when requested or required by Sync; load the installed skill for details.",
|
|
1737
|
+
...portalLine === null ? [] : [portalLine],
|
|
1612
1738
|
"Workflow integrity rule: repository truth may describe desired behavior, but it must not override these workflow boundaries.",
|
|
1613
1739
|
TRUTHMARK_BLOCK_END
|
|
1614
1740
|
].join("\n");
|
|
@@ -1678,7 +1804,50 @@ var renderDefaultStandards = (documents) => {
|
|
|
1678
1804
|
return DEFAULT_STANDARDS.filter((template) => !existingPaths.has(template.path));
|
|
1679
1805
|
};
|
|
1680
1806
|
|
|
1807
|
+
// src/templates/workflow-surfaces.ts
|
|
1808
|
+
import { stringify as stringify2 } from "yaml";
|
|
1809
|
+
|
|
1681
1810
|
// src/agents/workflow-manifest.ts
|
|
1811
|
+
var TRUTHMARK_CLI_RUNNER = `truthmark>=${TRUTHMARK_VERSION}`;
|
|
1812
|
+
var VALIDATE_SYNC_REPORT_HELPER = {
|
|
1813
|
+
id: "validate-sync-report",
|
|
1814
|
+
optional: true,
|
|
1815
|
+
runner: TRUTHMARK_CLI_RUNNER,
|
|
1816
|
+
command: { argv: ["truthmark", "validate", "sync-report", "<report-file>", "--json"] },
|
|
1817
|
+
inputs: ["sync report file"],
|
|
1818
|
+
output: "json",
|
|
1819
|
+
writes: false,
|
|
1820
|
+
fallback: "manually validate support/report-template.md and check Evidence checked entries match Claim, indented Evidence, and Result: supported | narrowed | removed | blocked"
|
|
1821
|
+
};
|
|
1822
|
+
var VALIDATE_DOCUMENT_REPORT_HELPER = {
|
|
1823
|
+
id: "validate-document-report",
|
|
1824
|
+
optional: true,
|
|
1825
|
+
runner: TRUTHMARK_CLI_RUNNER,
|
|
1826
|
+
command: { argv: ["truthmark", "validate", "document-report", "<report-file>", "--json"] },
|
|
1827
|
+
inputs: ["document report file"],
|
|
1828
|
+
output: "json",
|
|
1829
|
+
writes: false,
|
|
1830
|
+
fallback: "manually validate support/report-template.md required sections and structured Evidence checked entries"
|
|
1831
|
+
};
|
|
1832
|
+
var VALIDATE_WRITE_LEASE_HELPER = {
|
|
1833
|
+
id: "validate-write-lease",
|
|
1834
|
+
optional: true,
|
|
1835
|
+
runner: TRUTHMARK_CLI_RUNNER,
|
|
1836
|
+
command: {
|
|
1837
|
+
argv: [
|
|
1838
|
+
"truthmark",
|
|
1839
|
+
"validate",
|
|
1840
|
+
"write-lease",
|
|
1841
|
+
"<lease-or-report-file>",
|
|
1842
|
+
"<changed-files-file>",
|
|
1843
|
+
"--json"
|
|
1844
|
+
]
|
|
1845
|
+
},
|
|
1846
|
+
inputs: ["lease or worker report yaml", "changed file list"],
|
|
1847
|
+
output: "json",
|
|
1848
|
+
writes: false,
|
|
1849
|
+
fallback: "manually compare declared allowedWrites and forbiddenWrites with the actual changed files"
|
|
1850
|
+
};
|
|
1682
1851
|
var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
1683
1852
|
"truthmark-sync": {
|
|
1684
1853
|
id: "truthmark-sync",
|
|
@@ -1717,10 +1886,12 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
1717
1886
|
"Truth docs updated",
|
|
1718
1887
|
"Truth docs split",
|
|
1719
1888
|
"Evidence checked",
|
|
1889
|
+
"Helper scripts",
|
|
1720
1890
|
"Notes"
|
|
1721
1891
|
],
|
|
1722
1892
|
subagents: ["truth_route_auditor", "truth_claim_verifier"],
|
|
1723
|
-
writeSubagents: ["truth_doc_writer"]
|
|
1893
|
+
writeSubagents: ["truth_doc_writer"],
|
|
1894
|
+
helpers: [VALIDATE_SYNC_REPORT_HELPER, VALIDATE_WRITE_LEASE_HELPER]
|
|
1724
1895
|
},
|
|
1725
1896
|
"truthmark-structure": {
|
|
1726
1897
|
id: "truthmark-structure",
|
|
@@ -1801,10 +1972,12 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
1801
1972
|
"Truth docs restructured",
|
|
1802
1973
|
"Routing updated",
|
|
1803
1974
|
"Evidence checked",
|
|
1975
|
+
"Helper scripts",
|
|
1804
1976
|
"Notes"
|
|
1805
1977
|
],
|
|
1806
1978
|
subagents: ["truth_route_auditor", "truth_claim_verifier"],
|
|
1807
|
-
writeSubagents: ["truth_doc_writer"]
|
|
1979
|
+
writeSubagents: ["truth_doc_writer"],
|
|
1980
|
+
helpers: [VALIDATE_DOCUMENT_REPORT_HELPER, VALIDATE_WRITE_LEASE_HELPER]
|
|
1808
1981
|
},
|
|
1809
1982
|
"truthmark-realize": {
|
|
1810
1983
|
id: "truthmark-realize",
|
|
@@ -1902,6 +2075,50 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
1902
2075
|
"truth_claim_verifier",
|
|
1903
2076
|
"truth_doc_reviewer"
|
|
1904
2077
|
]
|
|
2078
|
+
},
|
|
2079
|
+
"truthmark-portal": {
|
|
2080
|
+
id: "truthmark-portal",
|
|
2081
|
+
displayName: "Truthmark Portal",
|
|
2082
|
+
description: "Use when the user explicitly asks to generate, refresh, or update the Truthmark Portal static HTML site. Not for code change sync, route repair, truth validation/checking, documenting behavior, realizing docs into code, or machine-readable agent context.",
|
|
2083
|
+
shortDescription: "Generate a committed static HTML Truthmark Portal",
|
|
2084
|
+
defaultPrompt: "Use $truthmark-portal only when explicitly asked to generate or refresh the committed static HTML Portal.",
|
|
2085
|
+
allowImplicitInvocation: false,
|
|
2086
|
+
positiveTriggers: [
|
|
2087
|
+
"generate the Truthmark Portal",
|
|
2088
|
+
"refresh the committed HTML docs site",
|
|
2089
|
+
"create a browsable project map from Truthmark docs",
|
|
2090
|
+
"update docs/truthmark-portal",
|
|
2091
|
+
"make a human-readable static site from the truth docs"
|
|
2092
|
+
],
|
|
2093
|
+
negativeTriggers: [
|
|
2094
|
+
"code change sync",
|
|
2095
|
+
"route ownership repair",
|
|
2096
|
+
"truth validation or checking",
|
|
2097
|
+
"document implemented behavior",
|
|
2098
|
+
"realize docs into code",
|
|
2099
|
+
"machine-readable agent context"
|
|
2100
|
+
],
|
|
2101
|
+
forbiddenAdjacency: [
|
|
2102
|
+
"must not run as a completion gate",
|
|
2103
|
+
"must not replace Truth Sync, Truth Check, Truth Document, Truth Realize, or Truth Structure",
|
|
2104
|
+
"must not write outside the configured Portal output directory unless the user changes scope"
|
|
2105
|
+
],
|
|
2106
|
+
requiredGates: [
|
|
2107
|
+
"manual-only invocation",
|
|
2108
|
+
"Portal output containment",
|
|
2109
|
+
"Markdown canonical statement",
|
|
2110
|
+
"source provenance"
|
|
2111
|
+
],
|
|
2112
|
+
allowedWrites: ["configured Portal output directory only"],
|
|
2113
|
+
reportSections: [
|
|
2114
|
+
"Output path",
|
|
2115
|
+
"Page count",
|
|
2116
|
+
"Diagrams/assets",
|
|
2117
|
+
"Source docs reviewed",
|
|
2118
|
+
"Skipped/ambiguous docs",
|
|
2119
|
+
"Validation",
|
|
2120
|
+
"Markdown canonical statement"
|
|
2121
|
+
]
|
|
1905
2122
|
}
|
|
1906
2123
|
};
|
|
1907
2124
|
var TRUTHMARK_WORKFLOW_IDS = Object.keys(
|
|
@@ -1932,7 +2149,7 @@ Fixes suggested:
|
|
|
1932
2149
|
${renderAuditEvidenceCheckedSection([
|
|
1933
2150
|
{
|
|
1934
2151
|
finding: "The root route index is present and maps repository truth owners.",
|
|
1935
|
-
evidence: [
|
|
2152
|
+
evidence: [`${rootRouteIndex}:1`],
|
|
1936
2153
|
suggestedFix: "none",
|
|
1937
2154
|
confidence: "high"
|
|
1938
2155
|
}
|
|
@@ -1984,11 +2201,11 @@ Invocations: ${TRUTH_CHECK_EXPLICIT_INVOCATIONS}
|
|
|
1984
2201
|
|
|
1985
2202
|
Truth Check is agent-led:
|
|
1986
2203
|
|
|
1987
|
-
- inspect .truthmark/config.yml
|
|
2204
|
+
- inspect .truthmark/config.yml and configured route files only when they exist; then inspect canonical docs and relevant implementation directly
|
|
1988
2205
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
1989
|
-
- inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/
|
|
2206
|
+
- inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/ when they exist
|
|
1990
2207
|
- check that current docs describe current code rather than historical plans
|
|
1991
|
-
- check that
|
|
2208
|
+
- check that route files map code surfaces to canonical truth docs when route files exist
|
|
1992
2209
|
- check for broad, catch-all, index-like, or mixed-owner truth docs and report them as topology issues requiring Truth Structure
|
|
1993
2210
|
- check that canonical behavior docs keep active Product Decisions and Rationale sections
|
|
1994
2211
|
- optionally run truthmark check when local tooling is available
|
|
@@ -2012,11 +2229,15 @@ var renderMarkdownExample2 = (content) => {
|
|
|
2012
2229
|
var TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-document; Codex /truthmark-document or $truthmark-document; Claude Code /truthmark-document; GitHub Copilot /truthmark-document; Gemini CLI /truthmark:document.";
|
|
2013
2230
|
var renderTruthDocumentReportExample = (config = defaultAgentConfig()) => {
|
|
2014
2231
|
const truthDocsRoot = resolveTruthDocsRoot(config);
|
|
2232
|
+
const helperScripts = ["validate-write-lease: skipped, no write lease used"];
|
|
2015
2233
|
return `Truth Document: completed
|
|
2016
2234
|
|
|
2017
2235
|
Implementation reviewed:
|
|
2018
2236
|
- src/routing/area-resolver.ts
|
|
2019
2237
|
|
|
2238
|
+
Ownership reviewed:
|
|
2239
|
+
- ${config.docs.routing.rootIndex}
|
|
2240
|
+
|
|
2020
2241
|
Truth docs created:
|
|
2021
2242
|
- ${truthDocsRoot}/contracts.md
|
|
2022
2243
|
|
|
@@ -2040,6 +2261,9 @@ ${renderClaimEvidenceCheckedSection([
|
|
|
2040
2261
|
}
|
|
2041
2262
|
])}
|
|
2042
2263
|
|
|
2264
|
+
Helper scripts:
|
|
2265
|
+
${helperScripts.map((helperScript) => `- ${helperScript}`).join("\n")}
|
|
2266
|
+
|
|
2043
2267
|
Notes:
|
|
2044
2268
|
- Documented routing and behavior from route handlers and tests.`;
|
|
2045
2269
|
};
|
|
@@ -2086,7 +2310,7 @@ Invocations: ${TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS}
|
|
|
2086
2310
|
Truth Document is manual and implementation-first:
|
|
2087
2311
|
|
|
2088
2312
|
- run only when the user explicitly asks to generate or update truth docs for existing behavior, or when Truth Sync, Truth Check, or Truth Structure reports implemented behavior that lacks canonical truth docs
|
|
2089
|
-
- inspect .truthmark/config.yml
|
|
2313
|
+
- inspect .truthmark/config.yml and configured route files only when they exist; then inspect existing canonical docs, implementation code, and tests directly
|
|
2090
2314
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2091
2315
|
- document current implemented behavior; do not invent future behavior or planned endpoints
|
|
2092
2316
|
- may write canonical truth docs and ${config.docs.routing.rootIndex} or relevant child route files only
|
|
@@ -2115,6 +2339,12 @@ ${renderTruthDocRestructureGateSection(
|
|
|
2115
2339
|
${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
2116
2340
|
${renderHierarchySummary(config)}
|
|
2117
2341
|
${DECISION_TRUTH_INSTRUCTIONS}
|
|
2342
|
+
Helper status reporting:
|
|
2343
|
+
- Validate the report body before adding this validator's own success status; the body may omit \`validate-document-report\` while validation is pending.
|
|
2344
|
+
- After \`truthmark validate document-report <report-file> --json\` returns \`data.validation.ok: true\`, append or update \`validate-document-report: ran, passed\` in the final report.
|
|
2345
|
+
- If the installed Truthmark CLI is unavailable or the helper is skipped, record \`validate-document-report: skipped, <reason>\` and manually validate the report shape.
|
|
2346
|
+
- Record \`validate-write-lease: ran, passed\` only after validating a concrete write lease; otherwise use a truthful skipped status such as \`skipped, no write lease used\`.
|
|
2347
|
+
- Helper output is derived evidence and never replaces direct checkout inspection, evidence review, or parent acceptance.
|
|
2118
2348
|
Parent post-document verification:
|
|
2119
2349
|
- verify only truth docs and leased truth routing files changed during document work
|
|
2120
2350
|
- block on functional code, generated host surfaces, or unrelated diffs caused by document work
|
|
@@ -2189,9 +2419,9 @@ Purpose:
|
|
|
2189
2419
|
- keep the selector thin so agents can avoid loading or acting through heavier workflows prematurely
|
|
2190
2420
|
|
|
2191
2421
|
Read:
|
|
2192
|
-
- .truthmark/config.yml
|
|
2193
|
-
- ${config.docs.routing.rootIndex}
|
|
2194
|
-
- relevant child route files under ${config.docs.routing.areaFilesRoot}
|
|
2422
|
+
- .truthmark/config.yml, only when present
|
|
2423
|
+
- ${config.docs.routing.rootIndex}, only when present
|
|
2424
|
+
- relevant child route files under ${config.docs.routing.areaFilesRoot}/, only when present
|
|
2195
2425
|
- relevant truth docs and implementation files needed to preview ownership
|
|
2196
2426
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2197
2427
|
|
|
@@ -2216,6 +2446,82 @@ Report completion in this shape:
|
|
|
2216
2446
|
${renderMarkdownExample3(renderTruthPreviewReportExample(config))}`;
|
|
2217
2447
|
};
|
|
2218
2448
|
|
|
2449
|
+
// src/agents/truthmark-portal.ts
|
|
2450
|
+
var TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-portal; Codex /truthmark-portal or $truthmark-portal; Claude Code /truthmark-portal; GitHub Copilot /truthmark-portal; Gemini CLI /truthmark:portal.";
|
|
2451
|
+
var renderTruthmarkPortalSkillBody = (config = defaultAgentConfig()) => {
|
|
2452
|
+
const workflow = getTruthmarkWorkflow("truthmark-portal");
|
|
2453
|
+
const output = config.truthmarkPortal.output;
|
|
2454
|
+
const template = config.truthmarkPortal.template;
|
|
2455
|
+
return `---
|
|
2456
|
+
name: truthmark-portal
|
|
2457
|
+
description: ${workflow.description}
|
|
2458
|
+
argument-hint: Optional output path, template, or portal generation focus
|
|
2459
|
+
user-invocable: true
|
|
2460
|
+
truthmark-version: ${TRUTHMARK_VERSION}
|
|
2461
|
+
---
|
|
2462
|
+
|
|
2463
|
+
# Truthmark Portal
|
|
2464
|
+
|
|
2465
|
+
Truthmark Portal is a manual-only presentation workflow. It is never a completion gate, never Truth Sync, and runs only when the user explicitly asks to generate, refresh, or update the committed static HTML Portal.
|
|
2466
|
+
|
|
2467
|
+
Invocations: ${TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS}
|
|
2468
|
+
|
|
2469
|
+
Core rules:
|
|
2470
|
+
|
|
2471
|
+
- Markdown remains canonical; generated HTML is presentation only.
|
|
2472
|
+
- Read Markdown directly from the checkout; the workflow does not require the truthmark CLI or package.
|
|
2473
|
+
- truthmark check/index may be used only as optional supporting evidence when available.
|
|
2474
|
+
- Default output is docs/truthmark-portal; configured output is ${output}.
|
|
2475
|
+
- Configured template is ${template}; use default built-in template instructions when the template is default.
|
|
2476
|
+
- The workflow may replace the entire output directory, but writes are limited to the configured Portal output directory only unless the user changes scope.
|
|
2477
|
+
- Portal writes are generated non-canonical static files for human browsing.
|
|
2478
|
+
- Generate a committed multi-page static HTML site with local CSS, JavaScript, assets, and search metadata under the output directory.
|
|
2479
|
+
- Use no remote dependencies by default: no remote scripts, analytics, fonts, CSS, or CDN assets.
|
|
2480
|
+
- Include source provenance and the Markdown canonical disclaimer on every page.
|
|
2481
|
+
- Store manifest and search data under output/assets only.
|
|
2482
|
+
- There is no .truthmark/index.json dependency; do not require or create it as infrastructure.
|
|
2483
|
+
- Pictures and screenshots require an explicit user or template request.
|
|
2484
|
+
|
|
2485
|
+
Workflow:
|
|
2486
|
+
|
|
2487
|
+
1. Confirm the user explicitly requested Portal generation or refresh.
|
|
2488
|
+
2. Inspect .truthmark/config.yml and configured route docs only when they exist; read repository instruction files when present, truth docs, architecture docs, standards docs, and the configured Portal template when it is a repo-relative file.
|
|
2489
|
+
3. Validate the selected output path is repo-relative, non-empty, inside the repository, and does not overlap canonical docs, source roots, routing files, or instruction targets.
|
|
2490
|
+
4. Plan the generated page inventory, diagrams/assets, source docs reviewed, and skipped or ambiguous docs.
|
|
2491
|
+
5. Replace or write only under ${output}; do not edit canonical Markdown, routing, source code, or instruction files unless the user explicitly changes scope.
|
|
2492
|
+
6. Generate the multi-page static site with local assets/search metadata and visible source provenance.
|
|
2493
|
+
7. Validate entry page, links where practical, provenance/disclaimers, local-only assets, and that metadata remains under ${output}/assets.
|
|
2494
|
+
${renderHierarchySummary(config)}
|
|
2495
|
+
|
|
2496
|
+
Report completion in this shape:
|
|
2497
|
+
|
|
2498
|
+
\`\`\`md
|
|
2499
|
+
Truthmark Portal: completed
|
|
2500
|
+
|
|
2501
|
+
Output path:
|
|
2502
|
+
- ${output}
|
|
2503
|
+
|
|
2504
|
+
Page count:
|
|
2505
|
+
- <count>
|
|
2506
|
+
|
|
2507
|
+
Diagrams/assets:
|
|
2508
|
+
- <generated diagrams/assets or none>
|
|
2509
|
+
|
|
2510
|
+
Source docs reviewed:
|
|
2511
|
+
- <source markdown paths>
|
|
2512
|
+
|
|
2513
|
+
Skipped/ambiguous docs:
|
|
2514
|
+
- <paths and reason, or none>
|
|
2515
|
+
|
|
2516
|
+
Validation:
|
|
2517
|
+
- <checks performed>
|
|
2518
|
+
|
|
2519
|
+
Markdown canonical statement:
|
|
2520
|
+
- Markdown remains canonical; generated Portal HTML is non-canonical presentation only.
|
|
2521
|
+
\`\`\`
|
|
2522
|
+
`;
|
|
2523
|
+
};
|
|
2524
|
+
|
|
2219
2525
|
// src/agents/truth-structure.ts
|
|
2220
2526
|
var renderMarkdownExample4 = (content) => {
|
|
2221
2527
|
return ["```md", content, "```"].join("\n");
|
|
@@ -2280,9 +2586,9 @@ truthmark-version: ${TRUTHMARK_VERSION}
|
|
|
2280
2586
|
Use this skill to design or repair Truthmark area structure.
|
|
2281
2587
|
Invocations: ${TRUTH_STRUCTURE_EXPLICIT_INVOCATIONS}
|
|
2282
2588
|
Truth Structure is agent-native:
|
|
2283
|
-
- inspect repository layout, current docs,
|
|
2589
|
+
- inspect repository layout, current docs, Truthmark config and route files when present, and relevant code directly
|
|
2284
2590
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2285
|
-
- inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/
|
|
2591
|
+
- inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/ when they exist
|
|
2286
2592
|
- define areas by product or behavior ownership, not by mechanical directory mirroring
|
|
2287
2593
|
- create or repair ${config.docs.routing.rootIndex}
|
|
2288
2594
|
- create starter truth docs when useful and when they belong in the canonical current-truth surface
|
|
@@ -2353,7 +2659,7 @@ ${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
|
2353
2659
|
Portable fallback:
|
|
2354
2660
|
- If this skill surface is unavailable, perform the same workflow directly from committed repository files.
|
|
2355
2661
|
- Do not require the truthmark CLI.
|
|
2356
|
-
-
|
|
2662
|
+
- Inspect .truthmark/config.yml and configured route files only when they exist; then inspect canonical docs and representative implementation code.
|
|
2357
2663
|
- Use a subagent only when the host supports that pattern; otherwise perform the topology repair inline.
|
|
2358
2664
|
${renderHierarchySummary(config)}
|
|
2359
2665
|
${DECISION_TRUTH_INSTRUCTIONS}
|
|
@@ -2366,24 +2672,105 @@ var renderBulletSection = (title, items) => {
|
|
|
2366
2672
|
return `${title}:
|
|
2367
2673
|
${items.map((item) => `- ${item}`).join("\n")}`;
|
|
2368
2674
|
};
|
|
2675
|
+
var findSection = (source, title) => {
|
|
2676
|
+
return source.split("\n\n").find((candidate) => candidate.startsWith(`${title}:
|
|
2677
|
+
`));
|
|
2678
|
+
};
|
|
2679
|
+
var parseBulletLines = (section) => {
|
|
2680
|
+
return section.split("\n").slice(1).map((line) => {
|
|
2681
|
+
const match = line.match(/^-\s+(.*)$/);
|
|
2682
|
+
return match?.[1];
|
|
2683
|
+
}).filter((line) => line !== void 0);
|
|
2684
|
+
};
|
|
2685
|
+
var parseBulletSection = (source, title) => {
|
|
2686
|
+
const section = findSection(source, title);
|
|
2687
|
+
if (!section) {
|
|
2688
|
+
return [];
|
|
2689
|
+
}
|
|
2690
|
+
return parseBulletLines(section);
|
|
2691
|
+
};
|
|
2692
|
+
var parseOptionalBulletSection = (source, title) => {
|
|
2693
|
+
const section = findSection(source, title);
|
|
2694
|
+
if (!section) {
|
|
2695
|
+
return void 0;
|
|
2696
|
+
}
|
|
2697
|
+
return parseBulletLines(section);
|
|
2698
|
+
};
|
|
2699
|
+
var isClaimEvidenceResult = (value) => {
|
|
2700
|
+
return ["supported", "narrowed", "removed", "blocked"].includes(value);
|
|
2701
|
+
};
|
|
2702
|
+
var hasContent = (value) => value.trim().length > 0;
|
|
2703
|
+
var parseEvidenceCheckedSection = (source) => {
|
|
2704
|
+
const section = source.split("\n\n").find((candidate) => candidate.startsWith("Evidence checked:\n"));
|
|
2705
|
+
if (!section) {
|
|
2706
|
+
throw new Error("Evidence checked section is required.");
|
|
2707
|
+
}
|
|
2708
|
+
const lines = section.split("\n").slice(1);
|
|
2709
|
+
const items = [];
|
|
2710
|
+
for (let index = 0; index < lines.length; index += 3) {
|
|
2711
|
+
const claimLine = lines[index];
|
|
2712
|
+
const evidenceLine = lines[index + 1];
|
|
2713
|
+
const resultLine = lines[index + 2];
|
|
2714
|
+
if (!claimLine?.startsWith("- Claim: ") || !evidenceLine?.startsWith(" Evidence: ") || !resultLine?.startsWith(" Result: ")) {
|
|
2715
|
+
throw new Error("Evidence checked entries must include Claim, Evidence, and Result fields.");
|
|
2716
|
+
}
|
|
2717
|
+
const result = resultLine.slice(" Result: ".length);
|
|
2718
|
+
const claim = claimLine.slice("- Claim: ".length).trim();
|
|
2719
|
+
const evidence = evidenceLine.slice(" Evidence: ".length).split(" / ").map((value) => value.trim());
|
|
2720
|
+
if (!hasContent(claim)) {
|
|
2721
|
+
throw new Error("Evidence checked claim is required.");
|
|
2722
|
+
}
|
|
2723
|
+
if (evidence.length === 0 || evidence.some((value) => !hasContent(value))) {
|
|
2724
|
+
throw new Error("Evidence checked evidence is required.");
|
|
2725
|
+
}
|
|
2726
|
+
if (!isClaimEvidenceResult(result)) {
|
|
2727
|
+
throw new Error("Evidence checked result is invalid.");
|
|
2728
|
+
}
|
|
2729
|
+
items.push({
|
|
2730
|
+
claim,
|
|
2731
|
+
evidence,
|
|
2732
|
+
result
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
return items;
|
|
2736
|
+
};
|
|
2369
2737
|
var renderTruthSyncCompletedReport = (input) => {
|
|
2370
2738
|
return [
|
|
2371
2739
|
"Truth Sync: completed",
|
|
2372
2740
|
renderBulletSection("Changed code reviewed", input.changedCode),
|
|
2741
|
+
renderBulletSection("Ownership reviewed", input.ownershipReviewed),
|
|
2373
2742
|
renderBulletSection("Truth docs updated", input.truthDocsUpdated),
|
|
2374
2743
|
renderClaimEvidenceCheckedSection(input.evidenceChecked),
|
|
2744
|
+
...input.helperScripts === void 0 ? [] : [renderBulletSection("Helper scripts", input.helperScripts)],
|
|
2375
2745
|
renderBulletSection("Notes", input.notes)
|
|
2376
2746
|
].join("\n\n");
|
|
2377
2747
|
};
|
|
2748
|
+
var parseTruthSyncReport = (source) => {
|
|
2749
|
+
if (!source.startsWith("Truth Sync: completed")) {
|
|
2750
|
+
throw new Error("Only completed Truth Sync reports can be parsed.");
|
|
2751
|
+
}
|
|
2752
|
+
const helperScripts = parseOptionalBulletSection(source, "Helper scripts");
|
|
2753
|
+
return {
|
|
2754
|
+
status: "completed",
|
|
2755
|
+
changedCode: parseBulletSection(source, "Changed code reviewed"),
|
|
2756
|
+
ownershipReviewed: parseBulletSection(source, "Ownership reviewed"),
|
|
2757
|
+
truthDocsUpdated: parseBulletSection(source, "Truth docs updated"),
|
|
2758
|
+
evidenceChecked: parseEvidenceCheckedSection(source),
|
|
2759
|
+
...helperScripts === void 0 ? {} : { helperScripts },
|
|
2760
|
+
notes: parseBulletSection(source, "Notes")
|
|
2761
|
+
};
|
|
2762
|
+
};
|
|
2378
2763
|
var renderTruthSyncBlockedReport = (input) => {
|
|
2764
|
+
const manualReviewFiles = input.manualReviewFiles.filter((file) => file.trim().length > 0);
|
|
2765
|
+
if (manualReviewFiles.length === 0) {
|
|
2766
|
+
throw new Error("Files requiring manual review must include at least one file.");
|
|
2767
|
+
}
|
|
2379
2768
|
const sections = [
|
|
2380
2769
|
"Truth Sync: blocked",
|
|
2381
|
-
renderBulletSection("Reason", [input.reason])
|
|
2770
|
+
renderBulletSection("Reason", [input.reason]),
|
|
2771
|
+
renderBulletSection("Files requiring manual review", manualReviewFiles),
|
|
2772
|
+
renderBulletSection("Next action", [input.nextAction])
|
|
2382
2773
|
];
|
|
2383
|
-
if ((input.manualReviewFiles?.length ?? 0) > 0) {
|
|
2384
|
-
sections.push(renderBulletSection("Files requiring manual review", input.manualReviewFiles));
|
|
2385
|
-
}
|
|
2386
|
-
sections.push(renderBulletSection("Next action", [input.nextAction]));
|
|
2387
2774
|
return [
|
|
2388
2775
|
...sections
|
|
2389
2776
|
].join("\n\n");
|
|
@@ -2397,6 +2784,7 @@ var renderMarkdownExample5 = (content) => {
|
|
|
2397
2784
|
var renderTruthSyncSkillBody = (config = defaultAgentConfig(), options = {}) => {
|
|
2398
2785
|
const truthDocsRoot = resolveTruthDocsRoot(config);
|
|
2399
2786
|
const workflow = getTruthmarkWorkflow("truthmark-sync");
|
|
2787
|
+
const helperScripts = ["validate-write-lease: skipped, no write lease used"];
|
|
2400
2788
|
const claudeSubagentMode = options.includeClaudeSubagentMode ? `${renderClaudeSubagentModeSection(
|
|
2401
2789
|
workflow.subagents ?? [],
|
|
2402
2790
|
"Parent agent owns Truth Sync acceptance, lease validation, and final report",
|
|
@@ -2436,7 +2824,7 @@ Explicit invocation runs immediately. Later functional-code changes reopen the f
|
|
|
2436
2824
|
Skip when changes are documentation-only, formatting-only, clearly behavior-preserving renames with no truth impact, when no Truthmark config exists yet, or when there are no functional code changes.
|
|
2437
2825
|
Parent workflow:
|
|
2438
2826
|
1. Inspect git status, staged changes, unstaged changes, and untracked files directly.
|
|
2439
|
-
2.
|
|
2827
|
+
2. Inspect .truthmark/config.yml and configured route files only when they exist; then inspect relevant canonical docs.
|
|
2440
2828
|
3. Identify functional-code changes and the nearest truth docs or routing repairs.
|
|
2441
2829
|
4. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2442
2830
|
5. Code verification is parent-owned: follow repository instructions and task context, and report what ran or why it did not run.
|
|
@@ -2470,6 +2858,12 @@ Optional validation tooling:
|
|
|
2470
2858
|
- do not require the truthmark binary; direct checkout inspection is the canonical path
|
|
2471
2859
|
- optional validation must not replace agent judgment about docs and routing
|
|
2472
2860
|
- update Product Decisions and Rationale when a behavior change comes from a decision change
|
|
2861
|
+
Helper status reporting:
|
|
2862
|
+
- Validate the report body before adding this validator's own success status; the body may omit \`validate-sync-report\` while validation is pending.
|
|
2863
|
+
- After \`truthmark validate sync-report <report-file> --json\` returns \`data.validation.ok: true\`, append or update \`validate-sync-report: ran, passed\` in the final report.
|
|
2864
|
+
- If the installed Truthmark CLI is unavailable or the helper is skipped, record \`validate-sync-report: skipped, <reason>\` and manually validate the report shape.
|
|
2865
|
+
- Record \`validate-write-lease: ran, passed\` only after validating a concrete write lease; otherwise use a truthful skipped status such as \`skipped, no write lease used\`.
|
|
2866
|
+
- Helper output is derived evidence and never replaces direct checkout inspection, evidence review, or parent acceptance.
|
|
2473
2867
|
${renderHierarchySummary(config)}
|
|
2474
2868
|
${DECISION_TRUTH_INSTRUCTIONS}
|
|
2475
2869
|
Parent post-sync verification:
|
|
@@ -2477,7 +2871,7 @@ Parent post-sync verification:
|
|
|
2477
2871
|
- block on any unrelated diff caused by the sync step
|
|
2478
2872
|
- block if functional code changed during sync
|
|
2479
2873
|
- for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and required report fields before accepting it
|
|
2480
|
-
- validate the final report against the structured Truth Sync report contract, including Claim, Evidence, and Result
|
|
2874
|
+
- validate the final report against the structured Truth Sync report contract, including Claim, indented Evidence, and Result values supported, narrowed, removed, or blocked under Evidence checked
|
|
2481
2875
|
- verify the updated docs correspond to the reviewed changed-code surface
|
|
2482
2876
|
- verify the final report records ownership review, structure requirement, split, restructure, or blocked reason when the ownership gate fired
|
|
2483
2877
|
- blocked outcomes must preserve the working tree as-is: no rollback, no post-block cleanup edits, and manual-review reporting of any remaining files
|
|
@@ -2485,6 +2879,7 @@ Report completion in this shape:
|
|
|
2485
2879
|
${renderMarkdownExample5(
|
|
2486
2880
|
renderTruthSyncCompletedReport({
|
|
2487
2881
|
changedCode: ["src/auth/session.ts"],
|
|
2882
|
+
ownershipReviewed: [config.docs.routing.rootIndex],
|
|
2488
2883
|
truthDocsUpdated: [`${truthDocsRoot}/repository/overview.md`],
|
|
2489
2884
|
evidenceChecked: [
|
|
2490
2885
|
{
|
|
@@ -2493,6 +2888,7 @@ ${renderMarkdownExample5(
|
|
|
2493
2888
|
result: "supported"
|
|
2494
2889
|
}
|
|
2495
2890
|
],
|
|
2891
|
+
helperScripts,
|
|
2496
2892
|
notes: ["Updated session timeout behavior."]
|
|
2497
2893
|
})
|
|
2498
2894
|
)}
|
|
@@ -2535,6 +2931,8 @@ var TRUTHMARK_CHECK_SKILL_PATH = ".codex/skills/truthmark-check/SKILL.md";
|
|
|
2535
2931
|
var TRUTHMARK_CHECK_SKILL_METADATA_PATH = ".codex/skills/truthmark-check/agents/openai.yaml";
|
|
2536
2932
|
var TRUTHMARK_PREVIEW_SKILL_PATH = ".codex/skills/truthmark-preview/SKILL.md";
|
|
2537
2933
|
var TRUTHMARK_PREVIEW_SKILL_METADATA_PATH = ".codex/skills/truthmark-preview/agents/openai.yaml";
|
|
2934
|
+
var TRUTHMARK_PORTAL_SKILL_PATH = ".codex/skills/truthmark-portal/SKILL.md";
|
|
2935
|
+
var TRUTHMARK_PORTAL_SKILL_METADATA_PATH = ".codex/skills/truthmark-portal/agents/openai.yaml";
|
|
2538
2936
|
var TRUTHMARK_ROUTE_AUDITOR_AGENT_PATH = ".codex/agents/truth-route-auditor.toml";
|
|
2539
2937
|
var TRUTHMARK_CLAIM_VERIFIER_AGENT_PATH = ".codex/agents/truth-claim-verifier.toml";
|
|
2540
2938
|
var TRUTHMARK_DOC_REVIEWER_AGENT_PATH = ".codex/agents/truth-doc-reviewer.toml";
|
|
@@ -2553,20 +2951,28 @@ var TRUTHMARK_GEMINI_SYNC_COMMAND_PATH = ".gemini/commands/truthmark/sync.toml";
|
|
|
2553
2951
|
var TRUTHMARK_GEMINI_REALIZE_COMMAND_PATH = ".gemini/commands/truthmark/realize.toml";
|
|
2554
2952
|
var TRUTHMARK_GEMINI_CHECK_COMMAND_PATH = ".gemini/commands/truthmark/check.toml";
|
|
2555
2953
|
var TRUTHMARK_GEMINI_PREVIEW_COMMAND_PATH = ".gemini/commands/truthmark/preview.toml";
|
|
2954
|
+
var TRUTHMARK_GEMINI_PORTAL_COMMAND_PATH = ".gemini/commands/truthmark/portal.toml";
|
|
2955
|
+
var TRUTHMARK_GEMINI_ROUTE_AUDITOR_AGENT_PATH = ".gemini/agents/truth-route-auditor.md";
|
|
2956
|
+
var TRUTHMARK_GEMINI_CLAIM_VERIFIER_AGENT_PATH = ".gemini/agents/truth-claim-verifier.md";
|
|
2957
|
+
var TRUTHMARK_GEMINI_DOC_REVIEWER_AGENT_PATH = ".gemini/agents/truth-doc-reviewer.md";
|
|
2958
|
+
var TRUTHMARK_GEMINI_DOC_WRITER_AGENT_PATH = ".gemini/agents/truth-doc-writer.md";
|
|
2556
2959
|
var TRUTHMARK_COPILOT_STRUCTURE_PROMPT_PATH = ".github/prompts/truthmark-structure.prompt.md";
|
|
2557
2960
|
var TRUTHMARK_COPILOT_DOCUMENT_PROMPT_PATH = ".github/prompts/truthmark-document.prompt.md";
|
|
2558
2961
|
var TRUTHMARK_COPILOT_SYNC_PROMPT_PATH = ".github/prompts/truthmark-sync.prompt.md";
|
|
2559
2962
|
var TRUTHMARK_COPILOT_REALIZE_PROMPT_PATH = ".github/prompts/truthmark-realize.prompt.md";
|
|
2560
2963
|
var TRUTHMARK_COPILOT_CHECK_PROMPT_PATH = ".github/prompts/truthmark-check.prompt.md";
|
|
2561
2964
|
var TRUTHMARK_COPILOT_PREVIEW_PROMPT_PATH = ".github/prompts/truthmark-preview.prompt.md";
|
|
2965
|
+
var TRUTHMARK_COPILOT_PORTAL_PROMPT_PATH = ".github/prompts/truthmark-portal.prompt.md";
|
|
2562
2966
|
var TRUTHMARK_COPILOT_ROUTE_AUDITOR_AGENT_PATH = ".github/agents/truth-route-auditor.agent.md";
|
|
2563
2967
|
var TRUTHMARK_COPILOT_CLAIM_VERIFIER_AGENT_PATH = ".github/agents/truth-claim-verifier.agent.md";
|
|
2564
2968
|
var TRUTHMARK_COPILOT_DOC_REVIEWER_AGENT_PATH = ".github/agents/truth-doc-reviewer.agent.md";
|
|
2565
2969
|
var TRUTHMARK_COPILOT_DOC_WRITER_AGENT_PATH = ".github/agents/truth-doc-writer.agent.md";
|
|
2566
2970
|
var renderGeminiCommand = (description, prompt) => {
|
|
2971
|
+
const promptWithArgs = `${prompt.trimEnd()}
|
|
2972
|
+
User focus or arguments: {{args}}`;
|
|
2567
2973
|
return `description = "${description}"
|
|
2568
2974
|
prompt = '''
|
|
2569
|
-
${
|
|
2975
|
+
${promptWithArgs}
|
|
2570
2976
|
'''
|
|
2571
2977
|
`;
|
|
2572
2978
|
};
|
|
@@ -2586,6 +2992,7 @@ var renderTomlStringArray = (values) => {
|
|
|
2586
2992
|
return `[${values.map(renderTomlString).join(", ")}]`;
|
|
2587
2993
|
};
|
|
2588
2994
|
var TRUTH_REALIZE_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-realize; Codex /truthmark-realize or $truthmark-realize; Claude Code /truthmark-realize; GitHub Copilot /truthmark-realize; Gemini CLI /truthmark:realize.";
|
|
2995
|
+
var routeFilesHint = (config) => `${config.docs.routing.rootIndex}; ${config.docs.routing.areaFilesRoot}/`;
|
|
2589
2996
|
var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
2590
2997
|
"truthmark-structure": {
|
|
2591
2998
|
title: "Truthmark Structure",
|
|
@@ -2593,8 +3000,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2593
3000
|
invocations: TRUTH_STRUCTURE_EXPLICIT_INVOCATIONS,
|
|
2594
3001
|
use: () => "Use this skill to design or repair Truthmark area structure.",
|
|
2595
3002
|
quickRules: (config) => [
|
|
2596
|
-
"Follow
|
|
2597
|
-
`
|
|
3003
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3004
|
+
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect current docs and relevant code directly.`,
|
|
2598
3005
|
"Define areas by product or behavior ownership, not by mechanical directory mirroring.",
|
|
2599
3006
|
"Do not edit functional code.",
|
|
2600
3007
|
"Read support/procedure.md before writing route or starter truth-doc changes.",
|
|
@@ -2608,8 +3015,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2608
3015
|
invocations: TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS,
|
|
2609
3016
|
use: () => "Use this skill to document existing implemented behavior when no functional-code changes are required for the task.",
|
|
2610
3017
|
quickRules: (config) => [
|
|
2611
|
-
"Follow
|
|
2612
|
-
`
|
|
3018
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3019
|
+
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect existing canonical docs, implementation code, and tests directly.`,
|
|
2613
3020
|
"Document current implemented behavior; do not invent future behavior.",
|
|
2614
3021
|
"May write canonical truth docs and truth routing files only; must not write functional code.",
|
|
2615
3022
|
"Read support/procedure.md before editing truth docs.",
|
|
@@ -2624,9 +3031,9 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2624
3031
|
invocations: TRUTH_SYNC_EXPLICIT_INVOCATIONS,
|
|
2625
3032
|
use: () => "Use this skill automatically before finishing when functional code changed since the last successful Truth Sync. Also run it immediately when the user explicitly invokes Truth Sync.",
|
|
2626
3033
|
quickRules: (config) => [
|
|
2627
|
-
"Follow
|
|
3034
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
2628
3035
|
"Skip docs-only, formatting-only, behavior-preserving renames with no truth impact, missing config, and no-code changes.",
|
|
2629
|
-
`
|
|
3036
|
+
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect relevant canonical docs directly.`,
|
|
2630
3037
|
"direct checkout inspection is the canonical path; do not require the truthmark binary.",
|
|
2631
3038
|
"May write canonical truth docs and truth routing files only; must not rewrite functional code.",
|
|
2632
3039
|
"Read support/procedure.md before editing truth docs.",
|
|
@@ -2641,8 +3048,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2641
3048
|
invocations: TRUTH_PREVIEW_EXPLICIT_INVOCATIONS,
|
|
2642
3049
|
use: () => "Use this skill only when the user explicitly asks to preview Truthmark routing or workflow choice before edits.",
|
|
2643
3050
|
quickRules: (config) => [
|
|
2644
|
-
"Follow
|
|
2645
|
-
`
|
|
3051
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3052
|
+
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect only the truth docs or implementation files needed to preview ownership.`,
|
|
2646
3053
|
"Truth Preview is read-only; this report is intended, not authorized.",
|
|
2647
3054
|
"must not edit files and must not issue write leases; do not run Truth Sync automatically, replace Truth Check, claim final correctness, or mutate code.",
|
|
2648
3055
|
"Use optional read-only route-auditor evidence only when it reduces context or clarifies ownership.",
|
|
@@ -2656,8 +3063,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2656
3063
|
invocations: TRUTH_REALIZE_EXPLICIT_INVOCATIONS,
|
|
2657
3064
|
use: () => "Use this skill only when the user explicitly asks to realize truth docs into code.",
|
|
2658
3065
|
quickRules: (config) => [
|
|
2659
|
-
"Follow
|
|
2660
|
-
`Read the source truth docs, .truthmark/config.yml
|
|
3066
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3067
|
+
`Read the source truth docs, inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist, then inspect tests and relevant functional code directly.`,
|
|
2661
3068
|
"Truth docs lead; code follows.",
|
|
2662
3069
|
"may write functional code only; must not edit truth docs or truth routing while realizing those docs.",
|
|
2663
3070
|
"Read support/procedure.md before changing code.",
|
|
@@ -2670,8 +3077,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2670
3077
|
invocations: TRUTH_CHECK_EXPLICIT_INVOCATIONS,
|
|
2671
3078
|
use: () => "Use this skill to audit repository truth health.",
|
|
2672
3079
|
quickRules: (config) => [
|
|
2673
|
-
"Follow
|
|
2674
|
-
`
|
|
3080
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3081
|
+
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect canonical docs and relevant implementation directly.`,
|
|
2675
3082
|
"Report issues and suggested fixes; do not silently rewrite unrelated files.",
|
|
2676
3083
|
"Direct checkout inspection is valid even when local tooling is unavailable.",
|
|
2677
3084
|
"Read support/procedure.md before auditing details.",
|
|
@@ -2679,6 +3086,24 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
2679
3086
|
"Read support/report-template.md before the final report."
|
|
2680
3087
|
],
|
|
2681
3088
|
parentRule: "Parent agent owns the final Truth Check report"
|
|
3089
|
+
},
|
|
3090
|
+
"truthmark-portal": {
|
|
3091
|
+
title: "Truthmark Portal",
|
|
3092
|
+
argumentHint: "Optional output path, template, or portal generation focus",
|
|
3093
|
+
invocations: TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS,
|
|
3094
|
+
use: () => "Use this skill only when the user explicitly asks to generate or refresh the committed static HTML Truthmark Portal.",
|
|
3095
|
+
quickRules: (config) => [
|
|
3096
|
+
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3097
|
+
"Truthmark Portal is manual-only; never run it as a completion gate and never treat it as Truth Sync.",
|
|
3098
|
+
"Markdown remains canonical; generated HTML is non-canonical presentation only.",
|
|
3099
|
+
"Read Markdown directly; the workflow does not require the truthmark CLI or package.",
|
|
3100
|
+
"Generate committed, generated non-canonical static files for humans.",
|
|
3101
|
+
`Write only under configured Portal output ${config.truthmarkPortal.output}; default output is docs/truthmark-portal.`,
|
|
3102
|
+
`Use configured Portal template ${config.truthmarkPortal.template}; no .truthmark/index.json dependency.`,
|
|
3103
|
+
"Use no remote dependencies by default and include source provenance on every page.",
|
|
3104
|
+
"Read support/procedure.md before generating Portal output.",
|
|
3105
|
+
"Read support/report-template.md before the final report."
|
|
3106
|
+
]
|
|
2682
3107
|
}
|
|
2683
3108
|
};
|
|
2684
3109
|
var stripWorkflowSkillFrontmatter = (body) => {
|
|
@@ -2707,6 +3132,56 @@ Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades
|
|
|
2707
3132
|
${body}
|
|
2708
3133
|
`;
|
|
2709
3134
|
};
|
|
3135
|
+
var renderHelperManifest = (helpers) => {
|
|
3136
|
+
const manifest = {
|
|
3137
|
+
helpers: Object.fromEntries(
|
|
3138
|
+
helpers.map((helper) => [
|
|
3139
|
+
helper.id,
|
|
3140
|
+
{
|
|
3141
|
+
optional: helper.optional,
|
|
3142
|
+
runner: helper.runner,
|
|
3143
|
+
command: helper.command,
|
|
3144
|
+
inputs: helper.inputs,
|
|
3145
|
+
output: helper.output,
|
|
3146
|
+
writes: helper.writes,
|
|
3147
|
+
...helper.allowedWrites === void 0 ? {} : { allowedWrites: helper.allowedWrites },
|
|
3148
|
+
fallback: helper.fallback
|
|
3149
|
+
}
|
|
3150
|
+
])
|
|
3151
|
+
)
|
|
3152
|
+
};
|
|
3153
|
+
return [
|
|
3154
|
+
`# Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades.`,
|
|
3155
|
+
stringify2(manifest, { lineWidth: 0 })
|
|
3156
|
+
].join("\n");
|
|
3157
|
+
};
|
|
3158
|
+
var renderHelperPolicySupport = (helpers) => {
|
|
3159
|
+
const reportHelperId = helpers.find((helper) => helper.id.endsWith("-report"))?.id ?? helpers[0]?.id;
|
|
3160
|
+
const helperLines = helpers.map(
|
|
3161
|
+
(helper) => `- ${helper.id}: optional ${helper.runner}; manual fallback: ${helper.fallback}`
|
|
3162
|
+
).join("\n");
|
|
3163
|
+
return renderSkillSupportFile(
|
|
3164
|
+
"Optional Helper CLI Policy",
|
|
3165
|
+
`Optional helper CLI commands may collect deterministic checkout facts or validate artifacts. If the Truthmark CLI is unavailable or too old for a declared helper, continue manually using this procedure and report which helper was skipped. Helper output is derived evidence; it does not override direct checkout inspection, workflow write boundaries, or parent acceptance.
|
|
3166
|
+
|
|
3167
|
+
Runner detection:
|
|
3168
|
+
- Check the declared Truthmark CLI runner before invoking a helper.
|
|
3169
|
+
- Invoke helpers through the installed \`truthmark validate ... --json\` CLI command using argv-style arguments from helper-manifest.yml.
|
|
3170
|
+
- If unavailable or version-mismatched, treat the helper as skipped and use the manual fallback.
|
|
3171
|
+
- Do not fail the workflow solely because a helper cannot run.
|
|
3172
|
+
|
|
3173
|
+
Available helpers:
|
|
3174
|
+
${helperLines}
|
|
3175
|
+
|
|
3176
|
+
Final reports should include helper status when helpers are declared for this workflow:
|
|
3177
|
+
|
|
3178
|
+
\`\`\`md
|
|
3179
|
+
Helper scripts:
|
|
3180
|
+
- ${reportHelperId}: ran, passed
|
|
3181
|
+
- validate-write-lease: skipped, no write lease used
|
|
3182
|
+
\`\`\``
|
|
3183
|
+
);
|
|
3184
|
+
};
|
|
2710
3185
|
var renderStandaloneWorkflowSkillBody = (workflowId, config) => {
|
|
2711
3186
|
switch (workflowId) {
|
|
2712
3187
|
case "truthmark-structure":
|
|
@@ -2721,12 +3196,15 @@ var renderStandaloneWorkflowSkillBody = (workflowId, config) => {
|
|
|
2721
3196
|
return renderTruthmarkRealizeSkillBody(config);
|
|
2722
3197
|
case "truthmark-check":
|
|
2723
3198
|
return renderTruthCheckSkillBody(config);
|
|
3199
|
+
case "truthmark-portal":
|
|
3200
|
+
return renderTruthmarkPortalSkillBody(config);
|
|
2724
3201
|
}
|
|
2725
3202
|
};
|
|
2726
|
-
var renderWorkflowEntrypoint = (workflowId, config, supportFiles) => {
|
|
3203
|
+
var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
|
|
2727
3204
|
const workflow = getTruthmarkWorkflow(workflowId);
|
|
2728
3205
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
2729
3206
|
const supportFileList = supportFiles.map((supportFile) => `- ${supportFile}`).join("\n");
|
|
3207
|
+
const hostUsage = host === "github-copilot" ? "Use as a Copilot agent skill. Prompt files remain available under `.github/prompts/` for command-style invocation in supported Copilot IDEs." : host === "gemini-cli" ? "Use as a Gemini CLI Agent Skill; commands remain available under `/truthmark:*` for command-first invocation." : void 0;
|
|
2730
3208
|
return `---
|
|
2731
3209
|
name: ${workflowId}
|
|
2732
3210
|
description: ${workflow.description}
|
|
@@ -2738,6 +3216,9 @@ truthmark-version: ${TRUTHMARK_VERSION}
|
|
|
2738
3216
|
# ${definition.title}
|
|
2739
3217
|
|
|
2740
3218
|
${definition.use(config)}
|
|
3219
|
+
${hostUsage === void 0 ? "" : `
|
|
3220
|
+
${hostUsage}
|
|
3221
|
+
`}
|
|
2741
3222
|
|
|
2742
3223
|
Invocations: ${definition.invocations}
|
|
2743
3224
|
|
|
@@ -2778,6 +3259,18 @@ var renderWorkflowSubagentSupport = (workflowId, host) => {
|
|
|
2778
3259
|
definition.parentRule,
|
|
2779
3260
|
writeAgents
|
|
2780
3261
|
);
|
|
3262
|
+
case "github-copilot":
|
|
3263
|
+
return renderCopilotCustomAgentModeSection(
|
|
3264
|
+
readAgents,
|
|
3265
|
+
definition.parentRule,
|
|
3266
|
+
writeAgents
|
|
3267
|
+
);
|
|
3268
|
+
case "gemini-cli":
|
|
3269
|
+
return renderGeminiSubagentModeSection(
|
|
3270
|
+
readAgents,
|
|
3271
|
+
definition.parentRule,
|
|
3272
|
+
writeAgents
|
|
3273
|
+
);
|
|
2781
3274
|
}
|
|
2782
3275
|
};
|
|
2783
3276
|
var renderTruthmarkSkillPackage = ({
|
|
@@ -2792,16 +3285,18 @@ var renderTruthmarkSkillPackage = ({
|
|
|
2792
3285
|
renderStandaloneWorkflowSkillBody(workflowId, config)
|
|
2793
3286
|
);
|
|
2794
3287
|
const subagents = renderWorkflowSubagentSupport(workflowId, host);
|
|
3288
|
+
const helpers = getTruthmarkWorkflow(workflowId).helpers ?? [];
|
|
2795
3289
|
const supportFiles = [
|
|
2796
3290
|
"support/procedure.md",
|
|
2797
3291
|
"support/report-template.md",
|
|
2798
|
-
...subagents === void 0 ? [] : ["support/subagents-and-leases.md"]
|
|
3292
|
+
...subagents === void 0 ? [] : ["support/subagents-and-leases.md"],
|
|
3293
|
+
...helpers.length === 0 ? [] : ["helper-manifest.yml", "support/helper-policy.md"]
|
|
2799
3294
|
];
|
|
2800
3295
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
2801
3296
|
const files = [
|
|
2802
3297
|
{
|
|
2803
3298
|
path: skillPath,
|
|
2804
|
-
content: renderWorkflowEntrypoint(workflowId, config, supportFiles)
|
|
3299
|
+
content: renderWorkflowEntrypoint(workflowId, config, supportFiles, host)
|
|
2805
3300
|
},
|
|
2806
3301
|
{
|
|
2807
3302
|
path: `${supportDirectory}/procedure.md`,
|
|
@@ -2827,10 +3322,22 @@ var renderTruthmarkSkillPackage = ({
|
|
|
2827
3322
|
)
|
|
2828
3323
|
});
|
|
2829
3324
|
}
|
|
3325
|
+
if (helpers.length > 0) {
|
|
3326
|
+
files.push(
|
|
3327
|
+
{
|
|
3328
|
+
path: `${skillDirectory}/helper-manifest.yml`,
|
|
3329
|
+
content: renderHelperManifest(helpers)
|
|
3330
|
+
},
|
|
3331
|
+
{
|
|
3332
|
+
path: `${supportDirectory}/helper-policy.md`,
|
|
3333
|
+
content: renderHelperPolicySupport(helpers)
|
|
3334
|
+
}
|
|
3335
|
+
);
|
|
3336
|
+
}
|
|
2830
3337
|
return files;
|
|
2831
3338
|
};
|
|
2832
|
-
var normalizeOpenCodePermissionPath = (
|
|
2833
|
-
const normalized =
|
|
3339
|
+
var normalizeOpenCodePermissionPath = (path13) => {
|
|
3340
|
+
const normalized = path13.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
|
|
2834
3341
|
return normalized === "" ? "." : normalized;
|
|
2835
3342
|
};
|
|
2836
3343
|
var appendOpenCodePermissionGlob = (root, glob) => {
|
|
@@ -2869,7 +3376,7 @@ var TRUTHMARK_SUBAGENT_PROFILES = {
|
|
|
2869
3376
|
nicknameCandidates: ["Route Audit", "Route Trace", "Route Check"],
|
|
2870
3377
|
instructions: `Stay read-only.
|
|
2871
3378
|
Audit one bounded Truthmark route, area, or doc shard assigned by the parent.
|
|
2872
|
-
|
|
3379
|
+
Inspect .truthmark/config.yml and route files only when they exist; then inspect mapped truth docs and relevant implementation files directly.
|
|
2873
3380
|
Find missing, stale, broad, overloaded, catch-all, mixed-owner, or unrouteable ownership.
|
|
2874
3381
|
Do not edit files, stage changes, or propose broad rewrites.
|
|
2875
3382
|
Return JSON only with keys: scope, filesReviewed, findings, evidence, confidence, recommendedWorkflow, notes.
|
|
@@ -2982,6 +3489,45 @@ tools: [read, search, edit]
|
|
|
2982
3489
|
|
|
2983
3490
|
# Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades.
|
|
2984
3491
|
|
|
3492
|
+
${instructions}
|
|
3493
|
+
`;
|
|
3494
|
+
};
|
|
3495
|
+
var renderGeminiReadOnlyAgent = ({
|
|
3496
|
+
copilotName,
|
|
3497
|
+
description,
|
|
3498
|
+
instructions
|
|
3499
|
+
}) => {
|
|
3500
|
+
const agentInstructions = renderReadOnlySubagentInstructions(instructions);
|
|
3501
|
+
return `---
|
|
3502
|
+
name: ${copilotName}
|
|
3503
|
+
description: ${description}
|
|
3504
|
+
kind: local
|
|
3505
|
+
tools: [read_file, grep_search]
|
|
3506
|
+
---
|
|
3507
|
+
|
|
3508
|
+
# Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades.
|
|
3509
|
+
|
|
3510
|
+
Manual invocation: @${copilotName}
|
|
3511
|
+
|
|
3512
|
+
${agentInstructions}
|
|
3513
|
+
`;
|
|
3514
|
+
};
|
|
3515
|
+
var renderGeminiWriteAgent = ({
|
|
3516
|
+
copilotName,
|
|
3517
|
+
description,
|
|
3518
|
+
instructions
|
|
3519
|
+
}) => {
|
|
3520
|
+
return `---
|
|
3521
|
+
name: ${copilotName}
|
|
3522
|
+
description: ${description}
|
|
3523
|
+
kind: local
|
|
3524
|
+
tools: [read_file, grep_search, write_file]
|
|
3525
|
+
---
|
|
3526
|
+
|
|
3527
|
+
# Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades.
|
|
3528
|
+
|
|
3529
|
+
Manual invocation: @${copilotName} with an explicit parent write lease.
|
|
3530
|
+
|
|
2985
3531
|
${instructions}
|
|
2986
3532
|
`;
|
|
2987
3533
|
};
|
|
@@ -3084,6 +3630,26 @@ var renderTruthmarkCopilotDocWriterAgent = () => {
|
|
|
3084
3630
|
TRUTHMARK_WRITE_SUBAGENT_PROFILES.truth_doc_writer
|
|
3085
3631
|
);
|
|
3086
3632
|
};
|
|
3633
|
+
var renderTruthmarkGeminiRouteAuditorAgent = () => {
|
|
3634
|
+
return renderGeminiReadOnlyAgent(
|
|
3635
|
+
TRUTHMARK_SUBAGENT_PROFILES.truth_route_auditor
|
|
3636
|
+
);
|
|
3637
|
+
};
|
|
3638
|
+
var renderTruthmarkGeminiClaimVerifierAgent = () => {
|
|
3639
|
+
return renderGeminiReadOnlyAgent(
|
|
3640
|
+
TRUTHMARK_SUBAGENT_PROFILES.truth_claim_verifier
|
|
3641
|
+
);
|
|
3642
|
+
};
|
|
3643
|
+
var renderTruthmarkGeminiDocReviewerAgent = () => {
|
|
3644
|
+
return renderGeminiReadOnlyAgent(
|
|
3645
|
+
TRUTHMARK_SUBAGENT_PROFILES.truth_doc_reviewer
|
|
3646
|
+
);
|
|
3647
|
+
};
|
|
3648
|
+
var renderTruthmarkGeminiDocWriterAgent = () => {
|
|
3649
|
+
return renderGeminiWriteAgent(
|
|
3650
|
+
TRUTHMARK_WRITE_SUBAGENT_PROFILES.truth_doc_writer
|
|
3651
|
+
);
|
|
3652
|
+
};
|
|
3087
3653
|
var renderTruthmarkClaudeRouteAuditorAgent = () => {
|
|
3088
3654
|
return renderClaudeReadOnlyAgent(
|
|
3089
3655
|
TRUTHMARK_SUBAGENT_PROFILES.truth_route_auditor
|
|
@@ -3273,8 +3839,8 @@ Truth Realize is doc-first:
|
|
|
3273
3839
|
|
|
3274
3840
|
Workflow:
|
|
3275
3841
|
|
|
3276
|
-
1. Read the updated truth docs named by the user, or infer the relevant docs from
|
|
3277
|
-
2.
|
|
3842
|
+
1. Read the updated truth docs named by the user, or infer the relevant docs from configured route files when present.
|
|
3843
|
+
2. Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then read tests and the relevant functional code.
|
|
3278
3844
|
3. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
3279
3845
|
${renderTruthDocOwnershipGateSection(
|
|
3280
3846
|
"source truth docs before writing code",
|
|
@@ -3348,6 +3914,21 @@ var renderTruthmarkCheckSkillMetadata = () => {
|
|
|
3348
3914
|
policy:
|
|
3349
3915
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
3350
3916
|
|
|
3917
|
+
truthmark:
|
|
3918
|
+
version: "${TRUTHMARK_VERSION}"
|
|
3919
|
+
refresh_command: "truthmark init"
|
|
3920
|
+
`;
|
|
3921
|
+
};
|
|
3922
|
+
var renderTruthmarkPortalSkillMetadata = () => {
|
|
3923
|
+
const workflow = getTruthmarkWorkflow("truthmark-portal");
|
|
3924
|
+
return `interface:
|
|
3925
|
+
display_name: "${workflow.displayName}"
|
|
3926
|
+
short_description: "${workflow.shortDescription}"
|
|
3927
|
+
default_prompt: "${workflow.defaultPrompt}"
|
|
3928
|
+
|
|
3929
|
+
policy:
|
|
3930
|
+
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
3931
|
+
|
|
3351
3932
|
truthmark:
|
|
3352
3933
|
version: "${TRUTHMARK_VERSION}"
|
|
3353
3934
|
refresh_command: "truthmark init"
|
|
@@ -3395,6 +3976,13 @@ var renderTruthmarkGeminiPreviewCommand = (config = defaultAgentConfig()) => {
|
|
|
3395
3976
|
renderTruthPreviewSkillBody(config)
|
|
3396
3977
|
);
|
|
3397
3978
|
};
|
|
3979
|
+
var renderTruthmarkGeminiPortalCommand = (config = defaultAgentConfig()) => {
|
|
3980
|
+
const workflow = getTruthmarkWorkflow("truthmark-portal");
|
|
3981
|
+
return renderGeminiCommand(
|
|
3982
|
+
workflow.description,
|
|
3983
|
+
renderTruthmarkPortalSkillBody(config)
|
|
3984
|
+
);
|
|
3985
|
+
};
|
|
3398
3986
|
var renderTruthmarkCopilotStructurePrompt = (config = defaultAgentConfig()) => {
|
|
3399
3987
|
const workflow = getTruthmarkWorkflow("truthmark-structure");
|
|
3400
3988
|
return renderCopilotPromptFile(
|
|
@@ -3445,6 +4033,13 @@ var renderTruthmarkCopilotPreviewPrompt = (config = defaultAgentConfig()) => {
|
|
|
3445
4033
|
renderTruthPreviewSkillBody(config)
|
|
3446
4034
|
);
|
|
3447
4035
|
};
|
|
4036
|
+
var renderTruthmarkCopilotPortalPrompt = (config = defaultAgentConfig()) => {
|
|
4037
|
+
const workflow = getTruthmarkWorkflow("truthmark-portal");
|
|
4038
|
+
return renderCopilotPromptFile(
|
|
4039
|
+
workflow.description,
|
|
4040
|
+
renderTruthmarkPortalSkillBody(config)
|
|
4041
|
+
);
|
|
4042
|
+
};
|
|
3448
4043
|
|
|
3449
4044
|
// src/templates/generated-surfaces.ts
|
|
3450
4045
|
var codexFiles = (config) => {
|
|
@@ -3526,10 +4121,24 @@ var codexFiles = (config) => {
|
|
|
3526
4121
|
content: renderTruthmarkDocWriterAgent()
|
|
3527
4122
|
}
|
|
3528
4123
|
];
|
|
4124
|
+
if (config.truthmarkPortal.enabled) {
|
|
4125
|
+
files.push(
|
|
4126
|
+
...renderTruthmarkSkillPackage({
|
|
4127
|
+
skillPath: TRUTHMARK_PORTAL_SKILL_PATH,
|
|
4128
|
+
workflowId: "truthmark-portal",
|
|
4129
|
+
host: "codex",
|
|
4130
|
+
config
|
|
4131
|
+
}),
|
|
4132
|
+
{
|
|
4133
|
+
path: TRUTHMARK_PORTAL_SKILL_METADATA_PATH,
|
|
4134
|
+
content: renderTruthmarkPortalSkillMetadata()
|
|
4135
|
+
}
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
3529
4138
|
return files;
|
|
3530
4139
|
};
|
|
3531
4140
|
var opencodeFiles = (config) => {
|
|
3532
|
-
|
|
4141
|
+
const files = [
|
|
3533
4142
|
...renderTruthmarkSkillPackage({
|
|
3534
4143
|
skillPath: ".opencode/skills/truthmark-structure/SKILL.md",
|
|
3535
4144
|
workflowId: "truthmark-structure",
|
|
@@ -3583,9 +4192,20 @@ var opencodeFiles = (config) => {
|
|
|
3583
4192
|
content: renderTruthmarkOpenCodeDocWriterAgent(config)
|
|
3584
4193
|
}
|
|
3585
4194
|
];
|
|
4195
|
+
if (config.truthmarkPortal.enabled) {
|
|
4196
|
+
files.push(
|
|
4197
|
+
...renderTruthmarkSkillPackage({
|
|
4198
|
+
skillPath: ".opencode/skills/truthmark-portal/SKILL.md",
|
|
4199
|
+
workflowId: "truthmark-portal",
|
|
4200
|
+
host: "opencode",
|
|
4201
|
+
config
|
|
4202
|
+
})
|
|
4203
|
+
);
|
|
4204
|
+
}
|
|
4205
|
+
return files;
|
|
3586
4206
|
};
|
|
3587
4207
|
var claudeFiles = (config, block) => {
|
|
3588
|
-
|
|
4208
|
+
const files = [
|
|
3589
4209
|
...instructionBlockFiles(["CLAUDE.md"], block),
|
|
3590
4210
|
...renderTruthmarkSkillPackage({
|
|
3591
4211
|
skillPath: ".claude/skills/truthmark-structure/SKILL.md",
|
|
@@ -3640,10 +4260,57 @@ var claudeFiles = (config, block) => {
|
|
|
3640
4260
|
content: renderTruthmarkClaudeDocWriterAgent()
|
|
3641
4261
|
}
|
|
3642
4262
|
];
|
|
4263
|
+
if (config.truthmarkPortal.enabled) {
|
|
4264
|
+
files.push(
|
|
4265
|
+
...renderTruthmarkSkillPackage({
|
|
4266
|
+
skillPath: ".claude/skills/truthmark-portal/SKILL.md",
|
|
4267
|
+
workflowId: "truthmark-portal",
|
|
4268
|
+
host: "claude-code",
|
|
4269
|
+
config
|
|
4270
|
+
})
|
|
4271
|
+
);
|
|
4272
|
+
}
|
|
4273
|
+
return files;
|
|
3643
4274
|
};
|
|
3644
4275
|
var copilotFiles = (config, block) => {
|
|
3645
4276
|
const files = [
|
|
3646
4277
|
...instructionBlockFiles([".github/copilot-instructions.md"], block),
|
|
4278
|
+
...renderTruthmarkSkillPackage({
|
|
4279
|
+
skillPath: ".github/skills/truthmark-structure/SKILL.md",
|
|
4280
|
+
workflowId: "truthmark-structure",
|
|
4281
|
+
host: "github-copilot",
|
|
4282
|
+
config
|
|
4283
|
+
}),
|
|
4284
|
+
...renderTruthmarkSkillPackage({
|
|
4285
|
+
skillPath: ".github/skills/truthmark-document/SKILL.md",
|
|
4286
|
+
workflowId: "truthmark-document",
|
|
4287
|
+
host: "github-copilot",
|
|
4288
|
+
config
|
|
4289
|
+
}),
|
|
4290
|
+
...renderTruthmarkSkillPackage({
|
|
4291
|
+
skillPath: ".github/skills/truthmark-sync/SKILL.md",
|
|
4292
|
+
workflowId: "truthmark-sync",
|
|
4293
|
+
host: "github-copilot",
|
|
4294
|
+
config
|
|
4295
|
+
}),
|
|
4296
|
+
...renderTruthmarkSkillPackage({
|
|
4297
|
+
skillPath: ".github/skills/truthmark-preview/SKILL.md",
|
|
4298
|
+
workflowId: "truthmark-preview",
|
|
4299
|
+
host: "github-copilot",
|
|
4300
|
+
config
|
|
4301
|
+
}),
|
|
4302
|
+
...renderTruthmarkSkillPackage({
|
|
4303
|
+
skillPath: ".github/skills/truthmark-check/SKILL.md",
|
|
4304
|
+
workflowId: "truthmark-check",
|
|
4305
|
+
host: "github-copilot",
|
|
4306
|
+
config
|
|
4307
|
+
}),
|
|
4308
|
+
...renderTruthmarkSkillPackage({
|
|
4309
|
+
skillPath: ".github/skills/truthmark-realize/SKILL.md",
|
|
4310
|
+
workflowId: "truthmark-realize",
|
|
4311
|
+
host: "github-copilot",
|
|
4312
|
+
config
|
|
4313
|
+
}),
|
|
3647
4314
|
{
|
|
3648
4315
|
path: TRUTHMARK_COPILOT_STRUCTURE_PROMPT_PATH,
|
|
3649
4316
|
content: renderTruthmarkCopilotStructurePrompt(config)
|
|
@@ -3685,11 +4352,121 @@ var copilotFiles = (config, block) => {
|
|
|
3685
4352
|
content: renderTruthmarkCopilotDocWriterAgent()
|
|
3686
4353
|
}
|
|
3687
4354
|
];
|
|
4355
|
+
if (config.truthmarkPortal.enabled) {
|
|
4356
|
+
files.push(
|
|
4357
|
+
...renderTruthmarkSkillPackage({
|
|
4358
|
+
skillPath: ".github/skills/truthmark-portal/SKILL.md",
|
|
4359
|
+
workflowId: "truthmark-portal",
|
|
4360
|
+
host: "github-copilot",
|
|
4361
|
+
config
|
|
4362
|
+
}),
|
|
4363
|
+
{
|
|
4364
|
+
path: TRUTHMARK_COPILOT_PORTAL_PROMPT_PATH,
|
|
4365
|
+
content: renderTruthmarkCopilotPortalPrompt(config)
|
|
4366
|
+
}
|
|
4367
|
+
);
|
|
4368
|
+
}
|
|
4369
|
+
return files;
|
|
4370
|
+
};
|
|
4371
|
+
var geminiFiles = (config, block) => {
|
|
4372
|
+
const files = [
|
|
4373
|
+
...instructionBlockFiles(["GEMINI.md"], block),
|
|
4374
|
+
...renderTruthmarkSkillPackage({
|
|
4375
|
+
skillPath: ".gemini/skills/truthmark-structure/SKILL.md",
|
|
4376
|
+
workflowId: "truthmark-structure",
|
|
4377
|
+
host: "gemini-cli",
|
|
4378
|
+
config
|
|
4379
|
+
}),
|
|
4380
|
+
...renderTruthmarkSkillPackage({
|
|
4381
|
+
skillPath: ".gemini/skills/truthmark-document/SKILL.md",
|
|
4382
|
+
workflowId: "truthmark-document",
|
|
4383
|
+
host: "gemini-cli",
|
|
4384
|
+
config
|
|
4385
|
+
}),
|
|
4386
|
+
...renderTruthmarkSkillPackage({
|
|
4387
|
+
skillPath: ".gemini/skills/truthmark-sync/SKILL.md",
|
|
4388
|
+
workflowId: "truthmark-sync",
|
|
4389
|
+
host: "gemini-cli",
|
|
4390
|
+
config
|
|
4391
|
+
}),
|
|
4392
|
+
...renderTruthmarkSkillPackage({
|
|
4393
|
+
skillPath: ".gemini/skills/truthmark-preview/SKILL.md",
|
|
4394
|
+
workflowId: "truthmark-preview",
|
|
4395
|
+
host: "gemini-cli",
|
|
4396
|
+
config
|
|
4397
|
+
}),
|
|
4398
|
+
...renderTruthmarkSkillPackage({
|
|
4399
|
+
skillPath: ".gemini/skills/truthmark-check/SKILL.md",
|
|
4400
|
+
workflowId: "truthmark-check",
|
|
4401
|
+
host: "gemini-cli",
|
|
4402
|
+
config
|
|
4403
|
+
}),
|
|
4404
|
+
...renderTruthmarkSkillPackage({
|
|
4405
|
+
skillPath: ".gemini/skills/truthmark-realize/SKILL.md",
|
|
4406
|
+
workflowId: "truthmark-realize",
|
|
4407
|
+
host: "gemini-cli",
|
|
4408
|
+
config
|
|
4409
|
+
}),
|
|
4410
|
+
{
|
|
4411
|
+
path: TRUTHMARK_GEMINI_STRUCTURE_COMMAND_PATH,
|
|
4412
|
+
content: renderTruthmarkGeminiStructureCommand(config)
|
|
4413
|
+
},
|
|
4414
|
+
{
|
|
4415
|
+
path: TRUTHMARK_GEMINI_DOCUMENT_COMMAND_PATH,
|
|
4416
|
+
content: renderTruthmarkGeminiDocumentCommand(config)
|
|
4417
|
+
},
|
|
4418
|
+
{
|
|
4419
|
+
path: TRUTHMARK_GEMINI_SYNC_COMMAND_PATH,
|
|
4420
|
+
content: renderTruthmarkGeminiSyncCommand(config)
|
|
4421
|
+
},
|
|
4422
|
+
{
|
|
4423
|
+
path: TRUTHMARK_GEMINI_PREVIEW_COMMAND_PATH,
|
|
4424
|
+
content: renderTruthmarkGeminiPreviewCommand(config)
|
|
4425
|
+
},
|
|
4426
|
+
{
|
|
4427
|
+
path: TRUTHMARK_GEMINI_CHECK_COMMAND_PATH,
|
|
4428
|
+
content: renderTruthmarkGeminiCheckCommand(config)
|
|
4429
|
+
},
|
|
4430
|
+
{
|
|
4431
|
+
path: TRUTHMARK_GEMINI_REALIZE_COMMAND_PATH,
|
|
4432
|
+
content: renderTruthmarkGeminiRealizeCommand(config)
|
|
4433
|
+
},
|
|
4434
|
+
{
|
|
4435
|
+
path: TRUTHMARK_GEMINI_ROUTE_AUDITOR_AGENT_PATH,
|
|
4436
|
+
content: renderTruthmarkGeminiRouteAuditorAgent()
|
|
4437
|
+
},
|
|
4438
|
+
{
|
|
4439
|
+
path: TRUTHMARK_GEMINI_CLAIM_VERIFIER_AGENT_PATH,
|
|
4440
|
+
content: renderTruthmarkGeminiClaimVerifierAgent()
|
|
4441
|
+
},
|
|
4442
|
+
{
|
|
4443
|
+
path: TRUTHMARK_GEMINI_DOC_REVIEWER_AGENT_PATH,
|
|
4444
|
+
content: renderTruthmarkGeminiDocReviewerAgent()
|
|
4445
|
+
},
|
|
4446
|
+
{
|
|
4447
|
+
path: TRUTHMARK_GEMINI_DOC_WRITER_AGENT_PATH,
|
|
4448
|
+
content: renderTruthmarkGeminiDocWriterAgent()
|
|
4449
|
+
}
|
|
4450
|
+
];
|
|
4451
|
+
if (config.truthmarkPortal.enabled) {
|
|
4452
|
+
files.push(
|
|
4453
|
+
...renderTruthmarkSkillPackage({
|
|
4454
|
+
skillPath: ".gemini/skills/truthmark-portal/SKILL.md",
|
|
4455
|
+
workflowId: "truthmark-portal",
|
|
4456
|
+
host: "gemini-cli",
|
|
4457
|
+
config
|
|
4458
|
+
}),
|
|
4459
|
+
{
|
|
4460
|
+
path: TRUTHMARK_GEMINI_PORTAL_COMMAND_PATH,
|
|
4461
|
+
content: renderTruthmarkGeminiPortalCommand(config)
|
|
4462
|
+
}
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
3688
4465
|
return files;
|
|
3689
4466
|
};
|
|
3690
4467
|
var instructionBlockFiles = (paths, block) => {
|
|
3691
|
-
return paths.map((
|
|
3692
|
-
path:
|
|
4468
|
+
return paths.map((path13) => ({
|
|
4469
|
+
path: path13,
|
|
3693
4470
|
content: block,
|
|
3694
4471
|
managedBlock: true
|
|
3695
4472
|
}));
|
|
@@ -3705,33 +4482,7 @@ var filesForPlatform = (platform, config, block) => {
|
|
|
3705
4482
|
case "github-copilot":
|
|
3706
4483
|
return copilotFiles(config, block);
|
|
3707
4484
|
case "gemini-cli":
|
|
3708
|
-
return
|
|
3709
|
-
...instructionBlockFiles(["GEMINI.md"], block),
|
|
3710
|
-
{
|
|
3711
|
-
path: TRUTHMARK_GEMINI_STRUCTURE_COMMAND_PATH,
|
|
3712
|
-
content: renderTruthmarkGeminiStructureCommand(config)
|
|
3713
|
-
},
|
|
3714
|
-
{
|
|
3715
|
-
path: TRUTHMARK_GEMINI_DOCUMENT_COMMAND_PATH,
|
|
3716
|
-
content: renderTruthmarkGeminiDocumentCommand(config)
|
|
3717
|
-
},
|
|
3718
|
-
{
|
|
3719
|
-
path: TRUTHMARK_GEMINI_SYNC_COMMAND_PATH,
|
|
3720
|
-
content: renderTruthmarkGeminiSyncCommand(config)
|
|
3721
|
-
},
|
|
3722
|
-
{
|
|
3723
|
-
path: TRUTHMARK_GEMINI_PREVIEW_COMMAND_PATH,
|
|
3724
|
-
content: renderTruthmarkGeminiPreviewCommand(config)
|
|
3725
|
-
},
|
|
3726
|
-
{
|
|
3727
|
-
path: TRUTHMARK_GEMINI_CHECK_COMMAND_PATH,
|
|
3728
|
-
content: renderTruthmarkGeminiCheckCommand(config)
|
|
3729
|
-
},
|
|
3730
|
-
{
|
|
3731
|
-
path: TRUTHMARK_GEMINI_REALIZE_COMMAND_PATH,
|
|
3732
|
-
content: renderTruthmarkGeminiRealizeCommand(config)
|
|
3733
|
-
}
|
|
3734
|
-
];
|
|
4485
|
+
return geminiFiles(config, block);
|
|
3735
4486
|
}
|
|
3736
4487
|
};
|
|
3737
4488
|
var renderGeneratedSurfaces = (config, block = renderAgentsBlock(config)) => {
|
|
@@ -3790,16 +4541,39 @@ var removeTrailingManagedChunk = (preservedLines) => {
|
|
|
3790
4541
|
preservedLines.splice(startIndex);
|
|
3791
4542
|
}
|
|
3792
4543
|
};
|
|
4544
|
+
var LEGACY_REPO_RULES_PATH = ["docs", "ai", `repo-${"rules.md"}`].join("/");
|
|
4545
|
+
var LEGACY_AGENT_ONBOARDING_PATH = ["docs", "ai", `agent-${"onboarding.md"}`].join(
|
|
4546
|
+
"/"
|
|
4547
|
+
);
|
|
4548
|
+
var LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE = [
|
|
4549
|
+
"primary repository",
|
|
4550
|
+
"instruction source"
|
|
4551
|
+
].join(" ");
|
|
3793
4552
|
var normalizeLegacyInstructionPreamble = (content) => {
|
|
3794
4553
|
return content.replaceAll(
|
|
3795
|
-
|
|
3796
|
-
"
|
|
4554
|
+
`Follow \`${LEGACY_REPO_RULES_PATH}\`.`,
|
|
4555
|
+
"Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
|
|
4556
|
+
).replaceAll(
|
|
4557
|
+
`Follow \`${LEGACY_REPO_RULES_PATH}\` as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE}.`,
|
|
4558
|
+
"Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
|
|
4559
|
+
).replaceAll(
|
|
4560
|
+
`Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for Codex.`,
|
|
4561
|
+
"Use explicitly configured repository policy docs only when they exist in this checkout."
|
|
4562
|
+
).replaceAll(
|
|
4563
|
+
`Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for this agent.`,
|
|
4564
|
+
"Use explicitly configured repository policy docs only when they exist in this checkout."
|
|
3797
4565
|
).replaceAll("Codex-specific:", "Agent-specific:").replaceAll(
|
|
3798
4566
|
"- Read `docs/README.md` for the canonical docs map.",
|
|
3799
|
-
"- Read
|
|
4567
|
+
"- Read the configured Truthmark routing files when choosing or updating canonical docs."
|
|
4568
|
+
).replaceAll(
|
|
4569
|
+
"- Read `docs/README.md` only when choosing or updating canonical docs.",
|
|
4570
|
+
"- Read the configured Truthmark routing files when choosing or updating canonical docs."
|
|
3800
4571
|
).replaceAll(
|
|
3801
|
-
|
|
3802
|
-
"- Use
|
|
4572
|
+
`- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` for quick task routing.`,
|
|
4573
|
+
"- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
|
|
4574
|
+
).replaceAll(
|
|
4575
|
+
`- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` only when task routing is unclear or cross-area.`,
|
|
4576
|
+
"- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
|
|
3803
4577
|
);
|
|
3804
4578
|
};
|
|
3805
4579
|
var upsertManagedBlock = (existingContent, block) => {
|
|
@@ -3860,48 +4634,36 @@ var upsertManagedBlock = (existingContent, block) => {
|
|
|
3860
4634
|
|
|
3861
4635
|
${block}`;
|
|
3862
4636
|
};
|
|
3863
|
-
var writeManagedAgentsFile = async (rootDir,
|
|
4637
|
+
var writeManagedAgentsFile = async (rootDir, path13 = "AGENTS.md", block) => {
|
|
3864
4638
|
let existingContent = null;
|
|
3865
4639
|
try {
|
|
3866
|
-
existingContent = await fs7.readFile(resolveRepoPath(rootDir,
|
|
4640
|
+
existingContent = await fs7.readFile(resolveRepoPath(rootDir, path13), "utf8");
|
|
3867
4641
|
} catch (error) {
|
|
3868
4642
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
3869
4643
|
throw error;
|
|
3870
4644
|
}
|
|
3871
4645
|
}
|
|
3872
|
-
return writeRepoFile(rootDir,
|
|
4646
|
+
return writeRepoFile(rootDir, path13, upsertManagedBlock(existingContent, block));
|
|
3873
4647
|
};
|
|
3874
4648
|
var diagnosticCategoryForPath = (filePath, config) => {
|
|
3875
4649
|
if (filePath === "AGENTS.md") {
|
|
3876
4650
|
return "truth-sync";
|
|
3877
4651
|
}
|
|
3878
|
-
if (filePath === "
|
|
3879
|
-
return "
|
|
3880
|
-
}
|
|
3881
|
-
if (filePath.startsWith(".codex/skills/truthmark-structure/")) {
|
|
3882
|
-
return "truth-sync";
|
|
3883
|
-
}
|
|
3884
|
-
if (filePath.startsWith(".codex/skills/truthmark-document/")) {
|
|
3885
|
-
return "truth-sync";
|
|
4652
|
+
if (filePath === ".github/prompts/truthmark-realize.prompt.md" || filePath.startsWith(".github/skills/truthmark-realize/") || filePath.startsWith(".claude/skills/truthmark-realize/") || filePath.startsWith(".opencode/skills/truthmark-realize/") || filePath.startsWith(".codex/skills/truthmark-realize/") || filePath.startsWith(".gemini/skills/truthmark-realize/")) {
|
|
4653
|
+
return "realization";
|
|
3886
4654
|
}
|
|
3887
|
-
if (filePath.startsWith(".
|
|
4655
|
+
if (filePath === "CLAUDE.md" || filePath === "GEMINI.md" || filePath === ".github/copilot-instructions.md" || filePath.startsWith(".github/prompts/truthmark-") || filePath.startsWith(".github/agents/truth-") || filePath.startsWith(".github/skills/truthmark-") || filePath.startsWith(".claude/agents/truth-") || filePath.startsWith(".claude/skills/truthmark-") || filePath.startsWith(".opencode/skills/truthmark-") || filePath.startsWith(".opencode/agents/") || filePath.startsWith(".codex/agents/") || filePath.startsWith(".gemini/agents/truth-") || filePath.startsWith(".gemini/skills/truthmark-")) {
|
|
3888
4656
|
return "truth-sync";
|
|
3889
4657
|
}
|
|
3890
|
-
if (filePath.startsWith(".codex/skills/truthmark-
|
|
4658
|
+
if (filePath.startsWith(".codex/skills/truthmark-")) {
|
|
3891
4659
|
return "truth-sync";
|
|
3892
4660
|
}
|
|
3893
|
-
if (filePath.startsWith(".codex/skills/truthmark-realize/")) {
|
|
3894
|
-
return "realization";
|
|
3895
|
-
}
|
|
3896
4661
|
if (filePath.startsWith(".gemini/commands/truthmark/realize")) {
|
|
3897
4662
|
return "realization";
|
|
3898
4663
|
}
|
|
3899
4664
|
if (filePath.startsWith(".gemini/commands/truthmark/")) {
|
|
3900
4665
|
return "truth-sync";
|
|
3901
4666
|
}
|
|
3902
|
-
if (filePath.startsWith(".codex/skills/truthmark-check/")) {
|
|
3903
|
-
return "truth-sync";
|
|
3904
|
-
}
|
|
3905
4667
|
if (filePath === config.docs.routing.rootIndex) {
|
|
3906
4668
|
return "authority";
|
|
3907
4669
|
}
|
|
@@ -4258,7 +5020,7 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
|
|
|
4258
5020
|
|
|
4259
5021
|
// src/checks/links.ts
|
|
4260
5022
|
import fs11 from "fs/promises";
|
|
4261
|
-
import
|
|
5023
|
+
import path5 from "path";
|
|
4262
5024
|
var pathExists2 = async (absolutePath) => {
|
|
4263
5025
|
try {
|
|
4264
5026
|
await fs11.stat(absolutePath);
|
|
@@ -4292,7 +5054,7 @@ var checkLinks = async (rootDir, markdownPaths) => {
|
|
|
4292
5054
|
if (targetPath.length === 0) {
|
|
4293
5055
|
continue;
|
|
4294
5056
|
}
|
|
4295
|
-
const absoluteTarget =
|
|
5057
|
+
const absoluteTarget = path5.resolve(path5.dirname(absolutePath), targetPath);
|
|
4296
5058
|
const relativeTarget = toRepoRelativePath(rootDir, absoluteTarget);
|
|
4297
5059
|
try {
|
|
4298
5060
|
await assertRepoContainment(rootDir, absoluteTarget);
|
|
@@ -5164,16 +5926,16 @@ var checkGeneratedSurfaces = async (rootDir, config) => {
|
|
|
5164
5926
|
};
|
|
5165
5927
|
|
|
5166
5928
|
// src/impact/build.ts
|
|
5167
|
-
import
|
|
5929
|
+
import path10 from "path";
|
|
5168
5930
|
import micromatch7 from "micromatch";
|
|
5169
5931
|
|
|
5170
5932
|
// src/repo-index/build.ts
|
|
5171
5933
|
import fs18 from "fs/promises";
|
|
5172
|
-
import
|
|
5934
|
+
import path8 from "path";
|
|
5173
5935
|
|
|
5174
5936
|
// src/repo-index/file-tree.ts
|
|
5175
5937
|
import fs16 from "fs/promises";
|
|
5176
|
-
import
|
|
5938
|
+
import path6 from "path";
|
|
5177
5939
|
import { execa as execa2 } from "execa";
|
|
5178
5940
|
import fg6 from "fast-glob";
|
|
5179
5941
|
import matter2 from "gray-matter";
|
|
@@ -5193,10 +5955,10 @@ var languageByExtension = /* @__PURE__ */ new Map([
|
|
|
5193
5955
|
]);
|
|
5194
5956
|
var sourceExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
5195
5957
|
var isJavaScriptLikePath = (filePath) => {
|
|
5196
|
-
return sourceExtensions.has(
|
|
5958
|
+
return sourceExtensions.has(path6.posix.extname(filePath));
|
|
5197
5959
|
};
|
|
5198
5960
|
var isTestPath = (filePath) => {
|
|
5199
|
-
return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(
|
|
5961
|
+
return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(path6.posix.basename(filePath));
|
|
5200
5962
|
};
|
|
5201
5963
|
var fileKind = (filePath, ignore) => {
|
|
5202
5964
|
const classification = classifyPath(filePath, ignore);
|
|
@@ -5222,7 +5984,7 @@ var fileKind = (filePath, ignore) => {
|
|
|
5222
5984
|
};
|
|
5223
5985
|
var targetHintsForTest = (filePath) => {
|
|
5224
5986
|
const hints = /* @__PURE__ */ new Set();
|
|
5225
|
-
const basename =
|
|
5987
|
+
const basename = path6.posix.basename(filePath).replace(/\.(test|spec)\.[cm]?[jt]sx?$/u, "");
|
|
5226
5988
|
if (basename.length > 0) {
|
|
5227
5989
|
hints.add(basename);
|
|
5228
5990
|
}
|
|
@@ -5268,7 +6030,19 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
5268
6030
|
const docs = [];
|
|
5269
6031
|
const tests = [];
|
|
5270
6032
|
for (const filePath of discoveredFiles.filter((entry) => !isIgnoredPath(entry, ignore)).sort()) {
|
|
5271
|
-
|
|
6033
|
+
let stat;
|
|
6034
|
+
try {
|
|
6035
|
+
stat = await fs16.stat(path6.join(rootDir, filePath));
|
|
6036
|
+
} catch (error) {
|
|
6037
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
6038
|
+
continue;
|
|
6039
|
+
}
|
|
6040
|
+
throw error;
|
|
6041
|
+
}
|
|
6042
|
+
if (!stat.isFile()) {
|
|
6043
|
+
continue;
|
|
6044
|
+
}
|
|
6045
|
+
const extension = path6.posix.extname(filePath);
|
|
5272
6046
|
const kind = fileKind(filePath, ignore);
|
|
5273
6047
|
files.push({
|
|
5274
6048
|
path: filePath,
|
|
@@ -5282,7 +6056,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
5282
6056
|
});
|
|
5283
6057
|
}
|
|
5284
6058
|
if (kind === "doc") {
|
|
5285
|
-
const source = await fs16.readFile(
|
|
6059
|
+
const source = await fs16.readFile(path6.join(rootDir, filePath), "utf8");
|
|
5286
6060
|
const parsed = matter2(source);
|
|
5287
6061
|
const markdown = parseMarkdownDocument(parsed.content);
|
|
5288
6062
|
const title = markdown.headings.find((heading) => heading.depth === 1)?.text ?? null;
|
|
@@ -5305,7 +6079,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
5305
6079
|
|
|
5306
6080
|
// src/repo-index/package-metadata.ts
|
|
5307
6081
|
import fs17 from "fs/promises";
|
|
5308
|
-
import
|
|
6082
|
+
import path7 from "path";
|
|
5309
6083
|
import fg7 from "fast-glob";
|
|
5310
6084
|
var packageManagerFor = async (rootDir, packageDir) => {
|
|
5311
6085
|
const lockfiles = [
|
|
@@ -5317,7 +6091,7 @@ var packageManagerFor = async (rootDir, packageDir) => {
|
|
|
5317
6091
|
];
|
|
5318
6092
|
for (const [lockfile, manager] of lockfiles) {
|
|
5319
6093
|
try {
|
|
5320
|
-
await fs17.access(
|
|
6094
|
+
await fs17.access(path7.join(rootDir, packageDir, lockfile));
|
|
5321
6095
|
return manager;
|
|
5322
6096
|
} catch {
|
|
5323
6097
|
continue;
|
|
@@ -5334,8 +6108,8 @@ var discoverPackageMetadata = async (rootDir) => {
|
|
|
5334
6108
|
});
|
|
5335
6109
|
const packages = [];
|
|
5336
6110
|
for (const packageFile of packageFiles.sort()) {
|
|
5337
|
-
const packageDir =
|
|
5338
|
-
const raw = JSON.parse(await fs17.readFile(
|
|
6111
|
+
const packageDir = path7.posix.dirname(packageFile) === "." ? "" : path7.posix.dirname(packageFile);
|
|
6112
|
+
const raw = JSON.parse(await fs17.readFile(path7.join(rootDir, packageFile), "utf8"));
|
|
5339
6113
|
const scripts = raw.scripts && typeof raw.scripts === "object" ? Object.keys(raw.scripts).sort() : [];
|
|
5340
6114
|
packages.push({
|
|
5341
6115
|
path: packageFile,
|
|
@@ -5394,16 +6168,16 @@ var declarationName = (node) => {
|
|
|
5394
6168
|
}
|
|
5395
6169
|
return node.name.text;
|
|
5396
6170
|
};
|
|
5397
|
-
var addExport = (exports, publicSymbols,
|
|
6171
|
+
var addExport = (exports, publicSymbols, path13, name, kind) => {
|
|
5398
6172
|
if (!name) {
|
|
5399
6173
|
return;
|
|
5400
6174
|
}
|
|
5401
|
-
const entry = { path:
|
|
6175
|
+
const entry = { path: path13, name, kind };
|
|
5402
6176
|
exports.push(entry);
|
|
5403
6177
|
publicSymbols.push(entry);
|
|
5404
6178
|
};
|
|
5405
|
-
var analyzeTypeScriptSource = (
|
|
5406
|
-
const sourceFile = ts.createSourceFile(
|
|
6179
|
+
var analyzeTypeScriptSource = (path13, source) => {
|
|
6180
|
+
const sourceFile = ts.createSourceFile(path13, source, ts.ScriptTarget.Latest, true);
|
|
5407
6181
|
const imports = [];
|
|
5408
6182
|
const exports = [];
|
|
5409
6183
|
const publicSymbols = [];
|
|
@@ -5423,7 +6197,7 @@ var analyzeTypeScriptSource = (path12, source) => {
|
|
|
5423
6197
|
}
|
|
5424
6198
|
}
|
|
5425
6199
|
imports.push({
|
|
5426
|
-
from:
|
|
6200
|
+
from: path13,
|
|
5427
6201
|
specifier: statement.moduleSpecifier.text,
|
|
5428
6202
|
imported: sortStrings(imported)
|
|
5429
6203
|
});
|
|
@@ -5432,34 +6206,34 @@ var analyzeTypeScriptSource = (path12, source) => {
|
|
|
5432
6206
|
if (ts.isExportDeclaration(statement)) {
|
|
5433
6207
|
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
5434
6208
|
for (const element of statement.exportClause.elements) {
|
|
5435
|
-
addExport(exports, publicSymbols,
|
|
6209
|
+
addExport(exports, publicSymbols, path13, element.name.text, "re-export");
|
|
5436
6210
|
}
|
|
5437
6211
|
}
|
|
5438
6212
|
continue;
|
|
5439
6213
|
}
|
|
5440
6214
|
if (ts.isFunctionDeclaration(statement) && hasExportModifier(statement)) {
|
|
5441
|
-
addExport(exports, publicSymbols,
|
|
6215
|
+
addExport(exports, publicSymbols, path13, declarationName(statement), "function");
|
|
5442
6216
|
continue;
|
|
5443
6217
|
}
|
|
5444
6218
|
if (ts.isClassDeclaration(statement) && hasExportModifier(statement)) {
|
|
5445
|
-
addExport(exports, publicSymbols,
|
|
6219
|
+
addExport(exports, publicSymbols, path13, declarationName(statement), "class");
|
|
5446
6220
|
continue;
|
|
5447
6221
|
}
|
|
5448
6222
|
if (ts.isInterfaceDeclaration(statement) && hasExportModifier(statement)) {
|
|
5449
|
-
addExport(exports, publicSymbols,
|
|
6223
|
+
addExport(exports, publicSymbols, path13, declarationName(statement), "interface");
|
|
5450
6224
|
continue;
|
|
5451
6225
|
}
|
|
5452
6226
|
if (ts.isTypeAliasDeclaration(statement) && hasExportModifier(statement)) {
|
|
5453
|
-
addExport(exports, publicSymbols,
|
|
6227
|
+
addExport(exports, publicSymbols, path13, declarationName(statement), "type");
|
|
5454
6228
|
continue;
|
|
5455
6229
|
}
|
|
5456
6230
|
if (ts.isEnumDeclaration(statement) && hasExportModifier(statement)) {
|
|
5457
|
-
addExport(exports, publicSymbols,
|
|
6231
|
+
addExport(exports, publicSymbols, path13, declarationName(statement), "enum");
|
|
5458
6232
|
continue;
|
|
5459
6233
|
}
|
|
5460
6234
|
if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
|
|
5461
6235
|
for (const declaration of statement.declarationList.declarations) {
|
|
5462
|
-
addExport(exports, publicSymbols,
|
|
6236
|
+
addExport(exports, publicSymbols, path13, declarationName(declaration), "const");
|
|
5463
6237
|
}
|
|
5464
6238
|
}
|
|
5465
6239
|
}
|
|
@@ -5490,7 +6264,7 @@ var buildRepoIndex = async (cwd) => {
|
|
|
5490
6264
|
if (!isJavaScriptLikePath(file.path)) {
|
|
5491
6265
|
continue;
|
|
5492
6266
|
}
|
|
5493
|
-
const source = await fs18.readFile(
|
|
6267
|
+
const source = await fs18.readFile(path8.join(rootDir, file.path), "utf8");
|
|
5494
6268
|
const analysis = analyzeTypeScriptSource(file.path, source);
|
|
5495
6269
|
imports.push(...analysis.imports);
|
|
5496
6270
|
exports.push(...analysis.exports);
|
|
@@ -5522,7 +6296,7 @@ import { execa as execa4 } from "execa";
|
|
|
5522
6296
|
|
|
5523
6297
|
// src/git/changes.ts
|
|
5524
6298
|
import fs19 from "fs/promises";
|
|
5525
|
-
import
|
|
6299
|
+
import path9 from "path";
|
|
5526
6300
|
import { execa as execa3 } from "execa";
|
|
5527
6301
|
var normalizePath3 = (filePath) => {
|
|
5528
6302
|
return filePath.replaceAll("\\", "/").replace(/^\.\//u, "");
|
|
@@ -5577,7 +6351,7 @@ var getUncommittedChanges = async (cwd) => {
|
|
|
5577
6351
|
const deletedPathCandidates = /* @__PURE__ */ new Set([...stagedDeletedPaths, ...unstagedDeletedPaths]);
|
|
5578
6352
|
for (const deletedPath of deletedPathCandidates) {
|
|
5579
6353
|
const change = getOrCreateChange(changesByPath, deletedPath);
|
|
5580
|
-
change.deleted = !await pathExists4(
|
|
6354
|
+
change.deleted = !await pathExists4(path9.join(rootDir, deletedPath));
|
|
5581
6355
|
}
|
|
5582
6356
|
return Array.from(changesByPath.values()).sort((left, right) => {
|
|
5583
6357
|
return left.path.localeCompare(right.path);
|
|
@@ -5698,7 +6472,7 @@ var resolveImportPath = (importEdge) => {
|
|
|
5698
6472
|
if (!importEdge.specifier.startsWith(".")) {
|
|
5699
6473
|
return null;
|
|
5700
6474
|
}
|
|
5701
|
-
const basePath =
|
|
6475
|
+
const basePath = path10.posix.normalize(path10.posix.join(path10.posix.dirname(importEdge.from), importEdge.specifier));
|
|
5702
6476
|
const withoutExtension = basePath.replace(/\.[cm]?[jt]sx?$/u, "");
|
|
5703
6477
|
return withoutExtension;
|
|
5704
6478
|
};
|
|
@@ -5711,7 +6485,7 @@ var importTargetsChangedFile = (importEdge, changedPath) => {
|
|
|
5711
6485
|
};
|
|
5712
6486
|
var pathSegments = (filePath) => filePath.split("/").filter(Boolean);
|
|
5713
6487
|
var testHintMatchesChangedFile = (hints, changedPath) => {
|
|
5714
|
-
const changedBaseName =
|
|
6488
|
+
const changedBaseName = path10.posix.basename(changedPath);
|
|
5715
6489
|
const changedSegments = pathSegments(changedPath);
|
|
5716
6490
|
return hints.some((hint) => changedBaseName.startsWith(hint) || changedSegments.includes(hint));
|
|
5717
6491
|
};
|
|
@@ -5872,7 +6646,7 @@ import fg8 from "fast-glob";
|
|
|
5872
6646
|
|
|
5873
6647
|
// src/evidence/parse.ts
|
|
5874
6648
|
import fs20 from "fs/promises";
|
|
5875
|
-
import
|
|
6649
|
+
import path11 from "path";
|
|
5876
6650
|
import matter3 from "gray-matter";
|
|
5877
6651
|
import { parse as parse3 } from "yaml";
|
|
5878
6652
|
var evidenceBlockPattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
|
|
@@ -5881,9 +6655,9 @@ var normalizeReferencePath = (truthDocPath, referencePath) => {
|
|
|
5881
6655
|
const strippedPath = referencePath.split("#")[0]?.trim() ?? "";
|
|
5882
6656
|
const isRepoRelative = repoRootPrefixes.some((prefix) => strippedPath.startsWith(prefix));
|
|
5883
6657
|
if (!isRepoRelative && (strippedPath.startsWith(".") || !strippedPath.includes("/"))) {
|
|
5884
|
-
return
|
|
6658
|
+
return path11.posix.normalize(path11.posix.join(path11.posix.dirname(truthDocPath), strippedPath));
|
|
5885
6659
|
}
|
|
5886
|
-
return
|
|
6660
|
+
return path11.posix.normalize(strippedPath);
|
|
5887
6661
|
};
|
|
5888
6662
|
var toEvidenceReference = (truthDocPath, raw) => {
|
|
5889
6663
|
if (!raw || typeof raw !== "object" || !("path" in raw) || typeof raw.path !== "string") {
|
|
@@ -5900,7 +6674,7 @@ var toEvidenceReference = (truthDocPath, raw) => {
|
|
|
5900
6674
|
};
|
|
5901
6675
|
};
|
|
5902
6676
|
var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
5903
|
-
const source = await fs20.readFile(
|
|
6677
|
+
const source = await fs20.readFile(path11.join(rootDir, truthDocPath), "utf8");
|
|
5904
6678
|
const parsed = matter3(source);
|
|
5905
6679
|
const references = [];
|
|
5906
6680
|
const sourceOfTruth = Array.isArray(parsed.data.source_of_truth) ? parsed.data.source_of_truth : [];
|
|
@@ -6138,7 +6912,7 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
6138
6912
|
|
|
6139
6913
|
// src/context-pack/build.ts
|
|
6140
6914
|
import fs22 from "fs/promises";
|
|
6141
|
-
import
|
|
6915
|
+
import path12 from "path";
|
|
6142
6916
|
import fg9 from "fast-glob";
|
|
6143
6917
|
var uniqueSorted2 = (values) => [...new Set(values)].sort();
|
|
6144
6918
|
var repoRootPrefixes2 = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
|
|
@@ -6149,12 +6923,12 @@ var normalizeDocReferencePath = (docPath, referencePath) => {
|
|
|
6149
6923
|
return null;
|
|
6150
6924
|
}
|
|
6151
6925
|
const isRepoRelative = repoRootPrefixes2.some((prefix) => strippedPath.startsWith(prefix));
|
|
6152
|
-
const normalized = isRepoRelative ?
|
|
6926
|
+
const normalized = isRepoRelative ? path12.posix.normalize(strippedPath) : path12.posix.normalize(path12.posix.join(path12.posix.dirname(docPath), strippedPath));
|
|
6153
6927
|
return normalized === ".." || normalized.startsWith("../") ? null : normalized;
|
|
6154
6928
|
};
|
|
6155
6929
|
var readIfExists = async (rootDir, filePath) => {
|
|
6156
6930
|
try {
|
|
6157
|
-
return await fs22.readFile(
|
|
6931
|
+
return await fs22.readFile(path12.join(rootDir, filePath), "utf8");
|
|
6158
6932
|
} catch {
|
|
6159
6933
|
return null;
|
|
6160
6934
|
}
|
|
@@ -6294,6 +7068,319 @@ var renderContextPackMarkdown = (pack) => {
|
|
|
6294
7068
|
`;
|
|
6295
7069
|
};
|
|
6296
7070
|
|
|
7071
|
+
// src/cli/handlers.ts
|
|
7072
|
+
import fs23 from "fs/promises";
|
|
7073
|
+
|
|
7074
|
+
// src/agents/workflow-helper-validation.ts
|
|
7075
|
+
import { parse as parseYaml2 } from "yaml";
|
|
7076
|
+
var escapeRegex = (value) => value.split("").map((char) => ".+*?^$()[]{}|\\".includes(char) ? `\\${char}` : char).join("");
|
|
7077
|
+
var hasLabel = (text, label) => {
|
|
7078
|
+
const escaped = escapeRegex(label);
|
|
7079
|
+
return new RegExp(String.raw`(^|\n)\s*(?:#{1,6}\s*)?${escaped}\s*:?\s*(\n|$)`, "iu").test(
|
|
7080
|
+
text
|
|
7081
|
+
);
|
|
7082
|
+
};
|
|
7083
|
+
var getSection = (text, label) => {
|
|
7084
|
+
const lines = text.split(/\r?\n/u);
|
|
7085
|
+
const labelPattern = new RegExp(String.raw`^(?:#{1,6}\s*)?${escapeRegex(label)}\s*:?\s*$`, "iu");
|
|
7086
|
+
const sectionHeaderPattern = /^(?:#{1,6}\s*)?[A-Z][A-Za-z0-9 ]+:\s*$/u;
|
|
7087
|
+
const startIndex = lines.findIndex((line) => labelPattern.test(line));
|
|
7088
|
+
if (startIndex === -1) {
|
|
7089
|
+
return null;
|
|
7090
|
+
}
|
|
7091
|
+
const nextSectionOffset = lines.slice(startIndex + 1).findIndex((line) => sectionHeaderPattern.test(line));
|
|
7092
|
+
const endIndex = nextSectionOffset === -1 ? lines.length : startIndex + 1 + nextSectionOffset;
|
|
7093
|
+
return lines.slice(startIndex + 1, endIndex).join("\n").trim();
|
|
7094
|
+
};
|
|
7095
|
+
var requireBulletSection = (text, label, errors, checks) => {
|
|
7096
|
+
const section = getSection(text, label);
|
|
7097
|
+
if (section === null) {
|
|
7098
|
+
errors.push(`missing required section: ${label}`);
|
|
7099
|
+
return;
|
|
7100
|
+
}
|
|
7101
|
+
if (!/^-\s+\S/mu.test(section)) {
|
|
7102
|
+
errors.push(`${label} must include at least one bullet`);
|
|
7103
|
+
return;
|
|
7104
|
+
}
|
|
7105
|
+
checks.push(label);
|
|
7106
|
+
};
|
|
7107
|
+
var validateEvidenceChecked = (text, errors, checks) => {
|
|
7108
|
+
const section = getSection(text, "Evidence checked");
|
|
7109
|
+
if (section === null || section === "") {
|
|
7110
|
+
errors.push("Evidence checked must include at least one structured entry");
|
|
7111
|
+
return;
|
|
7112
|
+
}
|
|
7113
|
+
const entries = section.split(/\n(?=-\s+)/u).map((entry) => entry.trim()).filter(Boolean);
|
|
7114
|
+
if (entries.length === 0) {
|
|
7115
|
+
errors.push("Evidence checked must include at least one structured entry");
|
|
7116
|
+
return;
|
|
7117
|
+
}
|
|
7118
|
+
const entryPattern = /^- Claim:\s*\S[^\n]*\n {2,}Evidence:\s*\S[^\n]*\n {2,}Result:\s*(supported|narrowed|removed|blocked)\s*$/iu;
|
|
7119
|
+
for (const [index, entry] of entries.entries()) {
|
|
7120
|
+
if (!entryPattern.test(entry)) {
|
|
7121
|
+
errors.push(
|
|
7122
|
+
`Evidence checked entry ${index + 1} must match '- Claim: ...' followed by indented 'Evidence: ...' and 'Result: supported | narrowed | removed | blocked'`
|
|
7123
|
+
);
|
|
7124
|
+
}
|
|
7125
|
+
}
|
|
7126
|
+
if (errors.length === 0) {
|
|
7127
|
+
checks.push(
|
|
7128
|
+
"Evidence checked entries include Claim:, Evidence:, and Result: supported | narrowed | removed | blocked"
|
|
7129
|
+
);
|
|
7130
|
+
}
|
|
7131
|
+
};
|
|
7132
|
+
var validateHelperScriptEntries = (entries, requiredHelpers, errors, checks) => {
|
|
7133
|
+
if (entries === void 0 || entries.length === 0) {
|
|
7134
|
+
errors.push("Helper scripts must include status for optional helpers");
|
|
7135
|
+
return;
|
|
7136
|
+
}
|
|
7137
|
+
const statusPattern = /^(?:-\s*)?([a-z0-9-]+):\s*(?:ran,\s*passed|skipped,\s*\S.*)$/iu;
|
|
7138
|
+
const validHelperIds = /* @__PURE__ */ new Set();
|
|
7139
|
+
for (const entry of entries) {
|
|
7140
|
+
const match = entry.trim().match(statusPattern);
|
|
7141
|
+
if (match === null) {
|
|
7142
|
+
errors.push(
|
|
7143
|
+
"Helper scripts entries must match '- helper-id: ran, passed' or '- helper-id: skipped, reason'; ran, failed is not valid for completed reports"
|
|
7144
|
+
);
|
|
7145
|
+
continue;
|
|
7146
|
+
}
|
|
7147
|
+
validHelperIds.add(match[1].toLowerCase());
|
|
7148
|
+
}
|
|
7149
|
+
for (const helperId of requiredHelpers) {
|
|
7150
|
+
if (!validHelperIds.has(helperId.toLowerCase())) {
|
|
7151
|
+
errors.push(`missing Helper scripts status: ${helperId}`);
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
if (errors.length === 0) {
|
|
7155
|
+
checks.push(`Helper scripts statuses include ${requiredHelpers.join(", ")}`);
|
|
7156
|
+
}
|
|
7157
|
+
};
|
|
7158
|
+
var validateHelperScripts = (text, requiredHelpers, errors, checks) => {
|
|
7159
|
+
const section = getSection(text, "Helper scripts");
|
|
7160
|
+
const entries = section?.split(/\n/u).map((entry) => entry.trim()).filter(Boolean);
|
|
7161
|
+
validateHelperScriptEntries(entries, requiredHelpers, errors, checks);
|
|
7162
|
+
};
|
|
7163
|
+
var validateTruthSyncReportText = (text) => {
|
|
7164
|
+
const helper = "validate-sync-report";
|
|
7165
|
+
const statusMatch = text.match(/^\s*Truth Sync:\s*(completed|blocked|skipped)\b/imu);
|
|
7166
|
+
if (statusMatch === null) {
|
|
7167
|
+
return {
|
|
7168
|
+
ok: false,
|
|
7169
|
+
helper,
|
|
7170
|
+
errors: ["missing Truth Sync status: expected completed, blocked, or skipped"]
|
|
7171
|
+
};
|
|
7172
|
+
}
|
|
7173
|
+
const status = statusMatch[1].toLowerCase();
|
|
7174
|
+
const checks = [`status: ${status}`];
|
|
7175
|
+
const errors = [];
|
|
7176
|
+
if (status === "completed") {
|
|
7177
|
+
try {
|
|
7178
|
+
const report = parseTruthSyncReport(text.trimStart());
|
|
7179
|
+
for (const [label, items] of [
|
|
7180
|
+
["Changed code reviewed", report.changedCode],
|
|
7181
|
+
["Ownership reviewed", report.ownershipReviewed],
|
|
7182
|
+
["Truth docs updated", report.truthDocsUpdated],
|
|
7183
|
+
["Notes", report.notes]
|
|
7184
|
+
]) {
|
|
7185
|
+
if (items.length === 0) {
|
|
7186
|
+
errors.push(`${label} must include at least one bullet`);
|
|
7187
|
+
} else {
|
|
7188
|
+
checks.push(label);
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
if (report.evidenceChecked.length === 0) {
|
|
7192
|
+
errors.push("Evidence checked must include at least one structured entry");
|
|
7193
|
+
} else {
|
|
7194
|
+
checks.push(
|
|
7195
|
+
"Evidence checked entries include Claim:, Evidence:, and Result: supported | narrowed | removed | blocked"
|
|
7196
|
+
);
|
|
7197
|
+
}
|
|
7198
|
+
validateHelperScriptEntries(
|
|
7199
|
+
report.helperScripts,
|
|
7200
|
+
["validate-write-lease"],
|
|
7201
|
+
errors,
|
|
7202
|
+
checks
|
|
7203
|
+
);
|
|
7204
|
+
} catch (error) {
|
|
7205
|
+
errors.push(error instanceof Error ? error.message : "invalid Truth Sync report");
|
|
7206
|
+
}
|
|
7207
|
+
} else if (status === "skipped") {
|
|
7208
|
+
requireBulletSection(text, "Reason", errors, checks);
|
|
7209
|
+
} else if (status === "blocked") {
|
|
7210
|
+
requireBulletSection(text, "Reason", errors, checks);
|
|
7211
|
+
requireBulletSection(text, "Files requiring manual review", errors, checks);
|
|
7212
|
+
requireBulletSection(text, "Next action", errors, checks);
|
|
7213
|
+
}
|
|
7214
|
+
return errors.length > 0 ? { ok: false, helper, errors } : { ok: true, helper, checks };
|
|
7215
|
+
};
|
|
7216
|
+
var validateTruthDocumentReportText = (text) => {
|
|
7217
|
+
const helper = "validate-document-report";
|
|
7218
|
+
const statusMatch = text.match(/^\s*Truth Document:\s*(completed|blocked)\b/imu);
|
|
7219
|
+
if (statusMatch === null) {
|
|
7220
|
+
return {
|
|
7221
|
+
ok: false,
|
|
7222
|
+
helper,
|
|
7223
|
+
errors: ["missing Truth Document status: expected completed or blocked"]
|
|
7224
|
+
};
|
|
7225
|
+
}
|
|
7226
|
+
const status = statusMatch[1].toLowerCase();
|
|
7227
|
+
const checks = [`status: ${status}`];
|
|
7228
|
+
const errors = [];
|
|
7229
|
+
if (status === "completed") {
|
|
7230
|
+
for (const label of ["Implementation reviewed", "Ownership reviewed", "Notes"]) {
|
|
7231
|
+
requireBulletSection(text, label, errors, checks);
|
|
7232
|
+
}
|
|
7233
|
+
if (hasLabel(text, "Evidence checked")) {
|
|
7234
|
+
checks.push("Evidence checked");
|
|
7235
|
+
} else {
|
|
7236
|
+
errors.push("missing required section: Evidence checked");
|
|
7237
|
+
}
|
|
7238
|
+
if (hasLabel(text, "Helper scripts")) {
|
|
7239
|
+
checks.push("Helper scripts");
|
|
7240
|
+
} else {
|
|
7241
|
+
errors.push("missing required section: Helper scripts");
|
|
7242
|
+
}
|
|
7243
|
+
const truthDocsUpdated = getSection(text, "Truth docs updated");
|
|
7244
|
+
const truthDocsCreated = getSection(text, "Truth docs created");
|
|
7245
|
+
if (truthDocsUpdated === null && truthDocsCreated === null) {
|
|
7246
|
+
errors.push("missing required section: Truth docs updated or Truth docs created");
|
|
7247
|
+
} else if (![truthDocsUpdated, truthDocsCreated].some(
|
|
7248
|
+
(section) => section !== null && /^-\s+\S/mu.test(section)
|
|
7249
|
+
)) {
|
|
7250
|
+
errors.push("Truth docs updated or Truth docs created must include at least one bullet");
|
|
7251
|
+
} else {
|
|
7252
|
+
checks.push("Truth docs updated or created");
|
|
7253
|
+
}
|
|
7254
|
+
validateEvidenceChecked(text, errors, checks);
|
|
7255
|
+
validateHelperScripts(text, ["validate-write-lease"], errors, checks);
|
|
7256
|
+
} else if (status === "blocked") {
|
|
7257
|
+
requireBulletSection(text, "Reason", errors, checks);
|
|
7258
|
+
}
|
|
7259
|
+
return errors.length > 0 ? { ok: false, helper, errors } : { ok: true, helper, checks };
|
|
7260
|
+
};
|
|
7261
|
+
var cleanPathValue = (value) => value.trim().replace(/^["']|["']$/gu, "");
|
|
7262
|
+
var normalizePath5 = (value) => cleanPathValue(value).replace(/^\.\//u, "");
|
|
7263
|
+
var windowsDriveAbsolutePattern = /^[A-Za-z]:[\\/]/u;
|
|
7264
|
+
var uncPathPattern = /^[/\\]{2}[^/\\]+[/\\]+[^/\\]+/u;
|
|
7265
|
+
var isUnsafePathValue = (value) => {
|
|
7266
|
+
const cleanValue = cleanPathValue(value);
|
|
7267
|
+
const pathValue = cleanValue.endsWith("/**") ? cleanValue.slice(0, -3) : cleanValue;
|
|
7268
|
+
return pathValue.startsWith("/") || pathValue.startsWith("\\") || windowsDriveAbsolutePattern.test(pathValue) || uncPathPattern.test(pathValue) || pathValue.split(/[\\/]+/u).includes("..");
|
|
7269
|
+
};
|
|
7270
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7271
|
+
var findWriteLeaseRecord = (value) => {
|
|
7272
|
+
if (!isRecord(value)) {
|
|
7273
|
+
return null;
|
|
7274
|
+
}
|
|
7275
|
+
if (Object.prototype.hasOwnProperty.call(value, "allowedWrites") || Object.prototype.hasOwnProperty.call(value, "forbiddenWrites")) {
|
|
7276
|
+
return value;
|
|
7277
|
+
}
|
|
7278
|
+
for (const nestedKey of ["writeLease", "lease"]) {
|
|
7279
|
+
const nested = value[nestedKey];
|
|
7280
|
+
if (isRecord(nested)) {
|
|
7281
|
+
return nested;
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
return value;
|
|
7285
|
+
};
|
|
7286
|
+
var readStringArray = (record, field, errors) => {
|
|
7287
|
+
const value = record[field];
|
|
7288
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
|
7289
|
+
errors.push(`${field} must be an array of strings`);
|
|
7290
|
+
return [];
|
|
7291
|
+
}
|
|
7292
|
+
return value;
|
|
7293
|
+
};
|
|
7294
|
+
var parseListFields = (text) => {
|
|
7295
|
+
const errors = [];
|
|
7296
|
+
let parsed;
|
|
7297
|
+
try {
|
|
7298
|
+
parsed = parseYaml2(text);
|
|
7299
|
+
} catch (error) {
|
|
7300
|
+
return {
|
|
7301
|
+
allowedWrites: [],
|
|
7302
|
+
forbiddenWrites: [],
|
|
7303
|
+
errors: [
|
|
7304
|
+
`manual-validation required: invalid write lease YAML${error instanceof Error ? `: ${error.message}` : ""}`
|
|
7305
|
+
]
|
|
7306
|
+
};
|
|
7307
|
+
}
|
|
7308
|
+
const record = findWriteLeaseRecord(parsed);
|
|
7309
|
+
if (record === null) {
|
|
7310
|
+
return {
|
|
7311
|
+
allowedWrites: [],
|
|
7312
|
+
forbiddenWrites: [],
|
|
7313
|
+
errors: ["write lease YAML must be an object"]
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
return {
|
|
7317
|
+
allowedWrites: readStringArray(record, "allowedWrites", errors),
|
|
7318
|
+
forbiddenWrites: readStringArray(record, "forbiddenWrites", errors),
|
|
7319
|
+
errors
|
|
7320
|
+
};
|
|
7321
|
+
};
|
|
7322
|
+
var isSupportedPattern = (pattern) => {
|
|
7323
|
+
const withoutTrailingGlob = pattern.endsWith("/**") ? pattern.slice(0, -3) : pattern;
|
|
7324
|
+
return !/[?*[\]{}]/u.test(withoutTrailingGlob);
|
|
7325
|
+
};
|
|
7326
|
+
var matchesPattern = (filePath, pattern) => {
|
|
7327
|
+
if (pattern.endsWith("/**")) {
|
|
7328
|
+
const prefix = pattern.slice(0, -3).replace(/\/+$/u, "");
|
|
7329
|
+
return filePath === prefix || filePath.startsWith(`${prefix}/`);
|
|
7330
|
+
}
|
|
7331
|
+
return filePath === pattern;
|
|
7332
|
+
};
|
|
7333
|
+
var validateWriteLeaseText = (leaseText, changedText) => {
|
|
7334
|
+
const helper = "validate-write-lease";
|
|
7335
|
+
const parsed = parseListFields(leaseText);
|
|
7336
|
+
const rawAllowedWrites = parsed.allowedWrites.map(cleanPathValue).filter(Boolean);
|
|
7337
|
+
const rawForbiddenWrites = parsed.forbiddenWrites.map(cleanPathValue).filter(Boolean);
|
|
7338
|
+
const rawChangedFiles = changedText.split(/\r?\n/u).map(cleanPathValue).filter(Boolean);
|
|
7339
|
+
const allowedWrites = rawAllowedWrites.map(normalizePath5).filter(Boolean);
|
|
7340
|
+
const forbiddenWrites = rawForbiddenWrites.map(normalizePath5).filter(Boolean);
|
|
7341
|
+
const changedFiles = rawChangedFiles.map(normalizePath5).filter(Boolean);
|
|
7342
|
+
const checks = [];
|
|
7343
|
+
const errors = [...parsed.errors];
|
|
7344
|
+
for (const pattern of rawAllowedWrites) {
|
|
7345
|
+
if (isUnsafePathValue(pattern)) {
|
|
7346
|
+
errors.push(`invalid allowedWrites path: ${pattern}`);
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
for (const pattern of rawForbiddenWrites) {
|
|
7350
|
+
if (isUnsafePathValue(pattern)) {
|
|
7351
|
+
errors.push(`invalid forbiddenWrites path: ${pattern}`);
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
for (const filePath of rawChangedFiles) {
|
|
7355
|
+
if (isUnsafePathValue(filePath)) {
|
|
7356
|
+
errors.push(`invalid changed file path: ${filePath}`);
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7359
|
+
for (const pattern of [...allowedWrites, ...forbiddenWrites]) {
|
|
7360
|
+
if (!isSupportedPattern(pattern)) {
|
|
7361
|
+
errors.push(`manual-validation required: unsupported write pattern ${pattern}`);
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
if (allowedWrites.length === 0) {
|
|
7365
|
+
errors.push("manual-validation required: no allowedWrites entries found");
|
|
7366
|
+
}
|
|
7367
|
+
if (errors.length === 0) {
|
|
7368
|
+
for (const filePath of changedFiles) {
|
|
7369
|
+
if (!allowedWrites.some((pattern) => matchesPattern(filePath, pattern))) {
|
|
7370
|
+
errors.push(`${filePath} is outside allowedWrites`);
|
|
7371
|
+
continue;
|
|
7372
|
+
}
|
|
7373
|
+
const forbiddenPattern = forbiddenWrites.find((pattern) => matchesPattern(filePath, pattern));
|
|
7374
|
+
if (forbiddenPattern !== void 0) {
|
|
7375
|
+
errors.push(`${filePath} matches forbiddenWrites pattern ${forbiddenPattern}`);
|
|
7376
|
+
continue;
|
|
7377
|
+
}
|
|
7378
|
+
checks.push(filePath);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
return errors.length > 0 ? { ok: false, helper, errors } : { ok: true, helper, checks };
|
|
7382
|
+
};
|
|
7383
|
+
|
|
6297
7384
|
// src/cli/handlers.ts
|
|
6298
7385
|
var runConfig2 = async (options) => {
|
|
6299
7386
|
return runConfig(process.cwd(), options);
|
|
@@ -6350,6 +7437,33 @@ var isContextPackWorkflow = (value) => {
|
|
|
6350
7437
|
var isContextPackFormat = (value) => {
|
|
6351
7438
|
return value === void 0 || value === "json" || value === "markdown";
|
|
6352
7439
|
};
|
|
7440
|
+
var readHelperFile = async (filePath, helper) => {
|
|
7441
|
+
try {
|
|
7442
|
+
return await fs23.readFile(filePath, "utf8");
|
|
7443
|
+
} catch (error) {
|
|
7444
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7445
|
+
return { ok: false, helper, errors: [`could not read file: ${message}`] };
|
|
7446
|
+
}
|
|
7447
|
+
};
|
|
7448
|
+
var runValidateSyncReport = async (reportFile) => {
|
|
7449
|
+
const text = await readHelperFile(reportFile, "validate-sync-report");
|
|
7450
|
+
return typeof text === "string" ? validateTruthSyncReportText(text) : text;
|
|
7451
|
+
};
|
|
7452
|
+
var runValidateDocumentReport = async (reportFile) => {
|
|
7453
|
+
const text = await readHelperFile(reportFile, "validate-document-report");
|
|
7454
|
+
return typeof text === "string" ? validateTruthDocumentReportText(text) : text;
|
|
7455
|
+
};
|
|
7456
|
+
var runValidateWriteLease = async (leaseFile, changedFilesFile) => {
|
|
7457
|
+
const leaseText = await readHelperFile(leaseFile, "validate-write-lease");
|
|
7458
|
+
if (typeof leaseText !== "string") {
|
|
7459
|
+
return leaseText;
|
|
7460
|
+
}
|
|
7461
|
+
const changedText = await readHelperFile(changedFilesFile, "validate-write-lease");
|
|
7462
|
+
if (typeof changedText !== "string") {
|
|
7463
|
+
return changedText;
|
|
7464
|
+
}
|
|
7465
|
+
return validateWriteLeaseText(leaseText, changedText);
|
|
7466
|
+
};
|
|
6353
7467
|
var runContext = async (options) => {
|
|
6354
7468
|
if (!isContextPackWorkflow(options.workflow)) {
|
|
6355
7469
|
return {
|
|
@@ -6406,6 +7520,28 @@ var writeContextResult = (result, options) => {
|
|
|
6406
7520
|
}
|
|
6407
7521
|
writeResult(result, options);
|
|
6408
7522
|
};
|
|
7523
|
+
var renderValidationHuman = (result) => {
|
|
7524
|
+
if (result.ok === true) {
|
|
7525
|
+
return [`${result.helper}: ok`, ...result.checks.map((check) => `- ${check}`)].join("\n");
|
|
7526
|
+
}
|
|
7527
|
+
return [`${result.helper}: failed`, ...result.errors.map((error) => `- ${error}`)].join("\n");
|
|
7528
|
+
};
|
|
7529
|
+
var toValidationCommandResult = (command, result) => ({
|
|
7530
|
+
command,
|
|
7531
|
+
summary: result.ok ? "Validation passed" : "Validation failed",
|
|
7532
|
+
diagnostics: [],
|
|
7533
|
+
data: {
|
|
7534
|
+
validation: result
|
|
7535
|
+
}
|
|
7536
|
+
});
|
|
7537
|
+
var writeValidationResult = (command, result, options) => {
|
|
7538
|
+
const output = options.json ? renderJson(toValidationCommandResult(command, result)) : renderValidationHuman(result);
|
|
7539
|
+
process.stdout.write(`${output}
|
|
7540
|
+
`);
|
|
7541
|
+
if (!result.ok) {
|
|
7542
|
+
process.exitCode = 1;
|
|
7543
|
+
}
|
|
7544
|
+
};
|
|
6409
7545
|
var addJsonOption = (command) => {
|
|
6410
7546
|
return command.option("--json", "Render command output as JSON");
|
|
6411
7547
|
};
|
|
@@ -6449,6 +7585,32 @@ var buildProgram = () => {
|
|
|
6449
7585
|
options
|
|
6450
7586
|
);
|
|
6451
7587
|
});
|
|
7588
|
+
const validate = program.command("validate").description("Run optional Truthmark workflow helper validators from the installed CLI.");
|
|
7589
|
+
addJsonOption(
|
|
7590
|
+
validate.command("sync-report").description("Validate a Truth Sync report file.").argument("<report-file>", "Truth Sync report file")
|
|
7591
|
+
).action(async (reportFile, options) => {
|
|
7592
|
+
writeValidationResult("validate sync-report", await runValidateSyncReport(reportFile), options);
|
|
7593
|
+
});
|
|
7594
|
+
addJsonOption(
|
|
7595
|
+
validate.command("document-report").description("Validate a Truth Document report file.").argument("<report-file>", "Truth Document report file")
|
|
7596
|
+
).action(async (reportFile, options) => {
|
|
7597
|
+
writeValidationResult(
|
|
7598
|
+
"validate document-report",
|
|
7599
|
+
await runValidateDocumentReport(reportFile),
|
|
7600
|
+
options
|
|
7601
|
+
);
|
|
7602
|
+
});
|
|
7603
|
+
addJsonOption(
|
|
7604
|
+
validate.command("write-lease").description("Validate a workflow write lease or worker report against changed files.").argument("<lease-or-report-file>", "Lease or worker report file").argument("<changed-files-file>", "Newline-separated changed file list")
|
|
7605
|
+
).action(
|
|
7606
|
+
async (leaseOrReportFile, changedFilesFile, options) => {
|
|
7607
|
+
writeValidationResult(
|
|
7608
|
+
"validate write-lease",
|
|
7609
|
+
await runValidateWriteLease(leaseOrReportFile, changedFilesFile),
|
|
7610
|
+
options
|
|
7611
|
+
);
|
|
7612
|
+
}
|
|
7613
|
+
);
|
|
6452
7614
|
return program;
|
|
6453
7615
|
};
|
|
6454
7616
|
|