@lannguyensi/harness 0.6.0 → 0.8.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.
Files changed (139) hide show
  1. package/CHANGELOG.md +255 -0
  2. package/README.md +189 -148
  3. package/dist/cli/apply/apply.d.ts +13 -0
  4. package/dist/cli/apply/apply.js +59 -3
  5. package/dist/cli/apply/apply.js.map +1 -1
  6. package/dist/cli/apply/generate-codex-config.d.ts +6 -0
  7. package/dist/cli/apply/generate-codex-config.js +149 -0
  8. package/dist/cli/apply/generate-codex-config.js.map +1 -0
  9. package/dist/cli/apply/generate-settings.d.ts +15 -1
  10. package/dist/cli/apply/generate-settings.js +16 -1
  11. package/dist/cli/apply/generate-settings.js.map +1 -1
  12. package/dist/cli/apply/index.d.ts +2 -1
  13. package/dist/cli/apply/index.js +2 -1
  14. package/dist/cli/apply/index.js.map +1 -1
  15. package/dist/cli/approve/understanding.d.ts +39 -0
  16. package/dist/cli/approve/understanding.js +122 -0
  17. package/dist/cli/approve/understanding.js.map +1 -0
  18. package/dist/cli/describe.d.ts +1 -1
  19. package/dist/cli/describe.js +2 -0
  20. package/dist/cli/describe.js.map +1 -1
  21. package/dist/cli/doctor/codex.d.ts +34 -0
  22. package/dist/cli/doctor/codex.js +331 -0
  23. package/dist/cli/doctor/codex.js.map +1 -0
  24. package/dist/cli/doctor/format.js +29 -1
  25. package/dist/cli/doctor/format.js.map +1 -1
  26. package/dist/cli/doctor/index.d.ts +13 -1
  27. package/dist/cli/doctor/index.js +49 -1
  28. package/dist/cli/doctor/index.js.map +1 -1
  29. package/dist/cli/doctor/types.d.ts +35 -1
  30. package/dist/cli/doctor/types.js +12 -1
  31. package/dist/cli/doctor/types.js.map +1 -1
  32. package/dist/cli/explain.d.ts +10 -1
  33. package/dist/cli/explain.js +44 -18
  34. package/dist/cli/explain.js.map +1 -1
  35. package/dist/cli/index.js +315 -8
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/cli/list.d.ts +1 -1
  38. package/dist/cli/list.js +17 -0
  39. package/dist/cli/list.js.map +1 -1
  40. package/dist/cli/pack/add.d.ts +13 -0
  41. package/dist/cli/pack/add.js +71 -0
  42. package/dist/cli/pack/add.js.map +1 -0
  43. package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +30 -0
  44. package/dist/cli/pack/hook-codex-pre-tool-use.js +149 -0
  45. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -0
  46. package/dist/cli/pack/hook-codex-stop.d.ts +31 -0
  47. package/dist/cli/pack/hook-codex-stop.js +332 -0
  48. package/dist/cli/pack/hook-codex-stop.js.map +1 -0
  49. package/dist/cli/pack/hook-codex-user-prompt-submit.d.ts +18 -0
  50. package/dist/cli/pack/hook-codex-user-prompt-submit.js +92 -0
  51. package/dist/cli/pack/hook-codex-user-prompt-submit.js.map +1 -0
  52. package/dist/cli/pack/hook-pre-tool-use.d.ts +32 -0
  53. package/dist/cli/pack/hook-pre-tool-use.js +181 -0
  54. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -0
  55. package/dist/cli/pack/index.d.ts +4 -0
  56. package/dist/cli/pack/index.js +5 -0
  57. package/dist/cli/pack/index.js.map +1 -0
  58. package/dist/cli/pack/list.d.ts +10 -0
  59. package/dist/cli/pack/list.js +43 -0
  60. package/dist/cli/pack/list.js.map +1 -0
  61. package/dist/cli/pack/mutate.d.ts +14 -0
  62. package/dist/cli/pack/mutate.js +76 -0
  63. package/dist/cli/pack/mutate.js.map +1 -0
  64. package/dist/cli/pack/remove.d.ts +15 -0
  65. package/dist/cli/pack/remove.js +153 -0
  66. package/dist/cli/pack/remove.js.map +1 -0
  67. package/dist/cli/session-export/index.d.ts +46 -0
  68. package/dist/cli/session-export/index.js +169 -0
  69. package/dist/cli/session-export/index.js.map +1 -0
  70. package/dist/cli/session-export/redact.d.ts +22 -0
  71. package/dist/cli/session-export/redact.js +47 -0
  72. package/dist/cli/session-export/redact.js.map +1 -0
  73. package/dist/cli/session-export/transcript.d.ts +24 -0
  74. package/dist/cli/session-export/transcript.js +162 -0
  75. package/dist/cli/session-export/transcript.js.map +1 -0
  76. package/dist/cli/validate/checks.js +32 -0
  77. package/dist/cli/validate/checks.js.map +1 -1
  78. package/dist/policies/ledger-client.js +2 -1
  79. package/dist/policies/ledger-client.js.map +1 -1
  80. package/dist/policy-packs/builtin/permission-profiles.d.ts +11 -0
  81. package/dist/policy-packs/builtin/permission-profiles.js +74 -0
  82. package/dist/policy-packs/builtin/permission-profiles.js.map +1 -0
  83. package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +56 -0
  84. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +186 -0
  85. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -0
  86. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +15 -0
  87. package/dist/policy-packs/builtin/understanding-before-execution.js +254 -0
  88. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -0
  89. package/dist/policy-packs/expand.d.ts +4 -0
  90. package/dist/policy-packs/expand.js +90 -0
  91. package/dist/policy-packs/expand.js.map +1 -0
  92. package/dist/policy-packs/index.d.ts +5 -0
  93. package/dist/policy-packs/index.js +5 -0
  94. package/dist/policy-packs/index.js.map +1 -0
  95. package/dist/policy-packs/permission-translator.d.ts +9 -0
  96. package/dist/policy-packs/permission-translator.js +76 -0
  97. package/dist/policy-packs/permission-translator.js.map +1 -0
  98. package/dist/policy-packs/registry.d.ts +11 -0
  99. package/dist/policy-packs/registry.js +20 -0
  100. package/dist/policy-packs/registry.js.map +1 -0
  101. package/dist/policy-packs/runtime.d.ts +8 -0
  102. package/dist/policy-packs/runtime.js +30 -0
  103. package/dist/policy-packs/runtime.js.map +1 -0
  104. package/dist/policy-packs/source.d.ts +6 -0
  105. package/dist/policy-packs/source.js +10 -0
  106. package/dist/policy-packs/source.js.map +1 -0
  107. package/dist/policy-packs/types.d.ts +41 -0
  108. package/dist/policy-packs/types.js +11 -0
  109. package/dist/policy-packs/types.js.map +1 -0
  110. package/dist/probes/mcp.js +2 -1
  111. package/dist/probes/mcp.js.map +1 -1
  112. package/dist/runtime/index.d.ts +1 -0
  113. package/dist/runtime/index.js +1 -0
  114. package/dist/runtime/index.js.map +1 -1
  115. package/dist/runtime/ledger-add.d.ts +16 -0
  116. package/dist/runtime/ledger-add.js +139 -0
  117. package/dist/runtime/ledger-add.js.map +1 -0
  118. package/dist/runtime/ledger-record.js +2 -1
  119. package/dist/runtime/ledger-record.js.map +1 -1
  120. package/dist/schema/audit.d.ts +71 -0
  121. package/dist/schema/audit.js +32 -0
  122. package/dist/schema/audit.js.map +1 -0
  123. package/dist/schema/index.d.ts +1893 -10
  124. package/dist/schema/index.js +27 -0
  125. package/dist/schema/index.js.map +1 -1
  126. package/dist/schema/permission-profiles.d.ts +2161 -0
  127. package/dist/schema/permission-profiles.js +60 -0
  128. package/dist/schema/permission-profiles.js.map +1 -0
  129. package/dist/schema/policy-packs.d.ts +52 -0
  130. package/dist/schema/policy-packs.js +35 -0
  131. package/dist/schema/policy-packs.js.map +1 -0
  132. package/dist/schema/tools.d.ts +8 -8
  133. package/dist/schema/workflows.d.ts +519 -0
  134. package/dist/schema/workflows.js +81 -0
  135. package/dist/schema/workflows.js.map +1 -0
  136. package/dist/version.d.ts +1 -0
  137. package/dist/version.js +3 -0
  138. package/dist/version.js.map +1 -0
  139. package/package.json +1 -1
@@ -0,0 +1,153 @@
1
+ // `harness pack remove <name>` — managed remove from policy_packs[].
2
+ //
3
+ // Reference-checked: refuses (without --force) when `.last-apply` records
4
+ // files under `policy-packs/<name>/`, on the theory that `harness apply`
5
+ // has run with this pack present and removing it without telling apply
6
+ // would leave orphan files in `harness.generated/`. With --force, the
7
+ // manifest entry is removed AND the orphan files are deleted AND the
8
+ // `.last-apply` file entries are pruned, so the next `harness apply` is
9
+ // a clean no-op.
10
+ import * as fs from "node:fs";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import { parse as parseYaml } from "yaml";
14
+ import { atomicWriteFile } from "../../io/atomic-write.js";
15
+ import { writeLastApply } from "../../io/last-apply.js";
16
+ import { LAST_APPLY_BASENAME, readLastApply, } from "../../io/last-apply.js";
17
+ import { withFileLock } from "../../io/lock.js";
18
+ import { unifiedDiff } from "../../io/patch.js";
19
+ import { formatValidationErrors, validateBeforeWrite, } from "../../io/validate-before-write.js";
20
+ import { EX_FAIL, EX_NOINPUT, HarnessExitError } from "../exit-codes.js";
21
+ import { applyPackRemove, planPackRemove } from "./mutate.js";
22
+ const DEFAULT_BASENAME = "harness.yaml";
23
+ const LOCK_BASENAME = ".harness.lock";
24
+ const GENERATED_DIRNAME = "harness.generated";
25
+ function resolveTargetPath(opts) {
26
+ if (opts.configPath)
27
+ return path.resolve(opts.configPath);
28
+ return path.join(opts.homeDir ?? path.join(os.homedir(), ".claude"), DEFAULT_BASENAME);
29
+ }
30
+ function resolveGeneratedDir(opts, manifestPath) {
31
+ if (opts.homeDir !== undefined)
32
+ return path.join(opts.homeDir, GENERATED_DIRNAME);
33
+ return path.join(path.dirname(manifestPath), GENERATED_DIRNAME);
34
+ }
35
+ function packFileKeys(record, packName) {
36
+ if (!record)
37
+ return [];
38
+ const prefix = `policy-packs/${packName}/`;
39
+ return Object.keys(record.files)
40
+ .filter((k) => k.startsWith(prefix))
41
+ .sort();
42
+ }
43
+ function pruneRecord(record, packName) {
44
+ const out = {
45
+ files: {},
46
+ ...(record.manifest !== undefined ? { manifest: record.manifest } : {}),
47
+ ...(record.memoryDirs !== undefined ? { memoryDirs: record.memoryDirs } : {}),
48
+ };
49
+ const prefix = `policy-packs/${packName}/`;
50
+ for (const [key, entry] of Object.entries(record.files)) {
51
+ if (!key.startsWith(prefix))
52
+ out.files[key] = entry;
53
+ }
54
+ return out;
55
+ }
56
+ function formatNameList(names) {
57
+ if (names.length === 0)
58
+ return "(none declared)";
59
+ return names.map((n) => ` - ${n}`).join("\n");
60
+ }
61
+ // Belt-and-braces defense against a path-traversal `name` reaching the
62
+ // filesystem cleanup. The schema regex on PolicyPackSchema.name catches
63
+ // this at parseManifest time, but planPackRemove reads the YAML
64
+ // directly (not via the schema), so a malformed manifest could still
65
+ // surface a bad name here. Refuse rather than rmSync into the void.
66
+ const SAFE_PACK_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
67
+ export async function packRemove(name, opts = {}) {
68
+ const target = resolveTargetPath(opts);
69
+ if (!fs.existsSync(target)) {
70
+ throw new HarnessExitError(`harness manifest not found at ${target}; run \`harness init\` first`, EX_NOINPUT);
71
+ }
72
+ if (!SAFE_PACK_NAME_RE.test(name)) {
73
+ throw new HarnessExitError(`policy_pack name ${JSON.stringify(name)} contains path separators or other unsafe characters; refusing to operate on it. Allowed: [A-Za-z0-9._-], leading char alphanumeric.`, EX_FAIL);
74
+ }
75
+ const original = fs.readFileSync(target, "utf8");
76
+ const plan = planPackRemove(original, name);
77
+ if (!plan.found) {
78
+ throw new HarnessExitError(`policy_packs entry "${name}" not found. Available entries:\n${formatNameList(plan.availableNames)}`, EX_FAIL);
79
+ }
80
+ const generatedDir = resolveGeneratedDir(opts, target);
81
+ const lastApply = readLastApply(generatedDir);
82
+ const trackedFiles = packFileKeys(lastApply, name);
83
+ if (trackedFiles.length > 0 && !opts.force) {
84
+ const list = trackedFiles.map((f) => ` - ${f}`).join("\n");
85
+ throw new HarnessExitError(`pack "${name}" has applied state present in ${LAST_APPLY_BASENAME}:\n${list}\n` +
86
+ `Pass --force to remove the manifest entry and clean up these generated files. ` +
87
+ `(Without cleanup, a subsequent \`harness apply\` would not delete them.)`, EX_FAIL);
88
+ }
89
+ const proposed = applyPackRemove(original, name);
90
+ const diff = unifiedDiff({
91
+ fileName: path.basename(target),
92
+ oldText: original,
93
+ newText: proposed,
94
+ oldHeader: "current",
95
+ newHeader: "proposed",
96
+ });
97
+ const schemaResult = validateBeforeWrite(parseYaml(proposed));
98
+ if (!schemaResult.ok) {
99
+ throw new HarnessExitError(`proposed manifest fails schema validation:\n${formatValidationErrors(schemaResult.errors)}`, EX_FAIL);
100
+ }
101
+ if (opts.dryRun) {
102
+ // On dry-run, surface what --force WOULD clean up so the user can
103
+ // sanity-check the blast radius before committing.
104
+ return {
105
+ path: target,
106
+ name,
107
+ diff,
108
+ applied: false,
109
+ cleanedFiles: opts.force ? trackedFiles : [],
110
+ };
111
+ }
112
+ const lockPath = path.join(path.dirname(target), LOCK_BASENAME);
113
+ const cleanedFiles = [];
114
+ await withFileLock(lockPath, () => {
115
+ const current = fs.readFileSync(target, "utf8");
116
+ const next = applyPackRemove(current, name);
117
+ const recheck = validateBeforeWrite(parseYaml(next));
118
+ if (!recheck.ok) {
119
+ throw new HarnessExitError(`proposed manifest fails schema validation after lock acquisition:\n${formatValidationErrors(recheck.errors)}`, EX_FAIL);
120
+ }
121
+ atomicWriteFile(target, next);
122
+ // Best-effort cleanup under --force. We do this AFTER the manifest
123
+ // write has succeeded so a partial cleanup never leaves the manifest
124
+ // in a confusing half-state. Each fs operation is wrapped: a missing
125
+ // file is a no-op, not a failure (the user may have deleted it
126
+ // manually since the last apply).
127
+ if (opts.force && trackedFiles.length > 0) {
128
+ const packDir = path.join(generatedDir, "policy-packs", name);
129
+ try {
130
+ fs.rmSync(packDir, { recursive: true, force: true });
131
+ }
132
+ catch {
133
+ // ignore — best-effort
134
+ }
135
+ // Update .last-apply so the next `harness apply` no-ops cleanly
136
+ // instead of detecting the now-missing files as drift.
137
+ const fresh = readLastApply(generatedDir);
138
+ if (fresh) {
139
+ const pruned = pruneRecord(fresh, name);
140
+ writeLastApply(generatedDir, pruned);
141
+ }
142
+ cleanedFiles.push(...trackedFiles);
143
+ }
144
+ });
145
+ return {
146
+ path: target,
147
+ name,
148
+ diff,
149
+ applied: true,
150
+ cleanedFiles,
151
+ };
152
+ }
153
+ //# sourceMappingURL=remove.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../src/cli/pack/remove.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type LedgerEntry, type LedgerQueryResult } from "../../policies/index.js";
2
+ import { type LoaderOptions } from "../loader.js";
3
+ import { type TranscriptEvent, type TranscriptParseResult } from "./transcript.js";
4
+ export type ExportFormat = "json" | "jsonl";
5
+ export interface SessionExportOptions extends LoaderOptions {
6
+ sessionId?: string;
7
+ format?: ExportFormat;
8
+ outFile?: string;
9
+ homeDir?: string;
10
+ projectsRoot?: string;
11
+ fetchLedger?: (sessionId: string) => Promise<LedgerQueryResult>;
12
+ /** Override transcript locator (tests). */
13
+ locateTranscript?: (sessionId: string) => string | null;
14
+ /** Read a transcript file (tests). */
15
+ readTranscript?: (file: string) => TranscriptParseResult;
16
+ /** Override env for env_var redaction (tests). */
17
+ env?: NodeJS.ProcessEnv;
18
+ }
19
+ export interface SessionExportEvent {
20
+ source: "transcript" | "ledger";
21
+ kind: string;
22
+ timestamp: string | null;
23
+ /** Stable per-source ordering tiebreaker. */
24
+ seq: number;
25
+ data: Record<string, unknown>;
26
+ }
27
+ export interface SessionExportHeader {
28
+ id: string;
29
+ cwd: string | null;
30
+ startedAt: string | null;
31
+ endedAt: string | null;
32
+ transcriptPath: string | null;
33
+ transcriptEvents: number;
34
+ ledgerEntries: number;
35
+ ledgerStatus: "ok" | "degraded" | "missing";
36
+ ledgerNote: string | null;
37
+ redactionRules: number;
38
+ }
39
+ export interface SessionExportResult {
40
+ output: string;
41
+ header: SessionExportHeader;
42
+ events: SessionExportEvent[];
43
+ }
44
+ export declare function sessionExport(opts: SessionExportOptions): Promise<SessionExportResult>;
45
+ declare function mergeEvents(transcript: TranscriptEvent[], ledger: LedgerEntry[]): SessionExportEvent[];
46
+ export { mergeEvents as mergeEventsForTesting };
@@ -0,0 +1,169 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { parseLedgerTimestamp, queryLedgerByTag, } from "../../policies/index.js";
4
+ import { resolveSessionId } from "../../runtime/session-id.js";
5
+ import { EX_FAIL, EX_USAGE, HarnessExitError } from "../exit-codes.js";
6
+ import { loadManifest } from "../loader.js";
7
+ import { redactString, resolveRedactionRules } from "./redact.js";
8
+ import { locateTranscript, readTranscript, } from "./transcript.js";
9
+ export async function sessionExport(opts) {
10
+ const format = opts.format ?? "json";
11
+ if (format !== "json" && format !== "jsonl") {
12
+ throw new HarnessExitError(`unknown --format "${format}"; expected json or jsonl`, EX_USAGE);
13
+ }
14
+ const sessionId = resolveSessionId(opts.sessionId);
15
+ const { manifest } = loadManifest(opts);
16
+ const rules = resolveRedactionRules(manifest.audit.redact, {
17
+ ...(opts.env !== undefined ? { env: opts.env } : {}),
18
+ });
19
+ const transcriptResult = await loadTranscript(sessionId, opts);
20
+ const ledgerOutcome = await loadLedger(sessionId, manifest, opts);
21
+ if (!transcriptResult.path &&
22
+ transcriptResult.events.length === 0 &&
23
+ ledgerOutcome.entries.length === 0 &&
24
+ ledgerOutcome.status === "ok") {
25
+ throw new HarnessExitError(`no transcript or ledger entries for session "${sessionId}"`, EX_FAIL);
26
+ }
27
+ const events = mergeEvents(transcriptResult.events, ledgerOutcome.entries);
28
+ const header = {
29
+ id: sessionId,
30
+ cwd: transcriptResult.cwd,
31
+ startedAt: transcriptResult.startedAt,
32
+ endedAt: transcriptResult.endedAt,
33
+ transcriptPath: transcriptResult.path,
34
+ transcriptEvents: transcriptResult.events.length,
35
+ ledgerEntries: ledgerOutcome.entries.length,
36
+ ledgerStatus: ledgerOutcome.status,
37
+ ledgerNote: ledgerOutcome.note,
38
+ redactionRules: rules.length,
39
+ };
40
+ const rawOutput = format === "json"
41
+ ? `${JSON.stringify({ session: header, events }, null, 2)}\n`
42
+ : `${[
43
+ JSON.stringify({ kind: "session", ...header }),
44
+ ...events.map((e) => JSON.stringify(e)),
45
+ ].join("\n")}\n`;
46
+ const output = redactString(rawOutput, rules);
47
+ if (opts.outFile) {
48
+ fs.mkdirSync(path.dirname(path.resolve(opts.outFile)), { recursive: true });
49
+ fs.writeFileSync(opts.outFile, output, "utf8");
50
+ }
51
+ return { output, header, events };
52
+ }
53
+ async function loadTranscript(sessionId, opts) {
54
+ const locator = opts.locateTranscript ?? ((id) => {
55
+ const args = {};
56
+ if (opts.homeDir !== undefined)
57
+ args.homeDir = opts.homeDir;
58
+ if (opts.projectsRoot !== undefined)
59
+ args.projectsRoot = opts.projectsRoot;
60
+ return locateTranscript(id, args);
61
+ });
62
+ const reader = opts.readTranscript ?? readTranscript;
63
+ const file = locator(sessionId);
64
+ if (file === null) {
65
+ return { events: [], startedAt: null, endedAt: null, cwd: null, path: null };
66
+ }
67
+ const r = reader(file);
68
+ return {
69
+ events: r.events,
70
+ startedAt: r.startedAt,
71
+ endedAt: r.endedAt,
72
+ cwd: r.cwd,
73
+ path: file,
74
+ };
75
+ }
76
+ async function loadLedger(sessionId, manifest, opts) {
77
+ const fetcher = opts.fetchLedger ?? defaultLedgerFetcher(manifest);
78
+ if (fetcher === null) {
79
+ return {
80
+ entries: [],
81
+ status: "missing",
82
+ note: "grounding-mcp not declared in manifest; ledger join skipped",
83
+ };
84
+ }
85
+ const result = await fetcher(sessionId);
86
+ if (result.kind === "ok") {
87
+ return { entries: result.entries, status: "ok", note: null };
88
+ }
89
+ return { entries: [], status: "degraded", note: result.reason };
90
+ }
91
+ function defaultLedgerFetcher(manifest) {
92
+ const server = manifest.tools.mcp.find((m) => m.name === "grounding-mcp");
93
+ if (!server)
94
+ return null;
95
+ const command = Array.isArray(server.command)
96
+ ? server.command
97
+ : server.command.trim().split(/\s+/);
98
+ return async (sid) => queryLedgerByTag({
99
+ mcpCommand: command,
100
+ ...(server.env && { mcpEnv: server.env }),
101
+ sessionId: sid,
102
+ timeoutMs: server.health?.timeout_ms ?? 5_000,
103
+ });
104
+ }
105
+ function mergeEvents(transcript, ledger) {
106
+ const out = [];
107
+ transcript.forEach((e, i) => {
108
+ const data = { ...e.data };
109
+ if (e.uuid !== undefined)
110
+ data.uuid = e.uuid;
111
+ if (e.parentUuid !== undefined)
112
+ data.parentUuid = e.parentUuid;
113
+ if (e.cwd !== undefined)
114
+ data.cwd = e.cwd;
115
+ out.push({
116
+ source: "transcript",
117
+ kind: e.kind,
118
+ timestamp: e.timestamp,
119
+ seq: i,
120
+ data,
121
+ });
122
+ });
123
+ ledger.forEach((entry, i) => {
124
+ const ts = ledgerEntryIso(entry);
125
+ const data = {
126
+ id: entry.id,
127
+ content: entry.content,
128
+ };
129
+ if (entry.type !== undefined)
130
+ data.type = entry.type;
131
+ if (entry.source !== undefined)
132
+ data.tagSource = entry.source;
133
+ out.push({
134
+ source: "ledger",
135
+ kind: entry.type ?? "ledger_entry",
136
+ timestamp: ts,
137
+ seq: i,
138
+ data,
139
+ });
140
+ });
141
+ out.sort((a, b) => {
142
+ const at = a.timestamp ?? "";
143
+ const bt = b.timestamp ?? "";
144
+ if (at === bt) {
145
+ if (a.source !== b.source)
146
+ return a.source === "transcript" ? -1 : 1;
147
+ return a.seq - b.seq;
148
+ }
149
+ if (at === "")
150
+ return 1;
151
+ if (bt === "")
152
+ return -1;
153
+ return at < bt ? -1 : 1;
154
+ });
155
+ return out;
156
+ }
157
+ function ledgerEntryIso(entry) {
158
+ if (entry.createdAt instanceof Date)
159
+ return entry.createdAt.toISOString();
160
+ if (typeof entry.createdAt === "string") {
161
+ const parsed = parseLedgerTimestamp(entry.createdAt);
162
+ if (parsed === null)
163
+ return null;
164
+ return new Date(parsed * 1000).toISOString();
165
+ }
166
+ return null;
167
+ }
168
+ export { mergeEvents as mergeEventsForTesting };
169
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/session-export/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { RedactRule } from "../../schema/audit.js";
2
+ /**
3
+ * Default regex denylist — applied even when the manifest declares no
4
+ * `audit.redact[]`. Catches the four obvious patterns called out in the
5
+ * design discussion: token, secret, password, api_key. Conservative on
6
+ * purpose: matches `key: value` / `key=value` and replaces the value
7
+ * with `<REDACTED>`.
8
+ */
9
+ export declare const DEFAULT_REGEX_RULES: {
10
+ regex: string;
11
+ flags: string;
12
+ replacement: string;
13
+ }[];
14
+ export interface ResolvedRule {
15
+ pattern: RegExp;
16
+ replacement: string;
17
+ }
18
+ export interface ResolveRedactionOptions {
19
+ env?: NodeJS.ProcessEnv;
20
+ }
21
+ export declare function resolveRedactionRules(manifestRules: RedactRule[], opts?: ResolveRedactionOptions): ResolvedRule[];
22
+ export declare function redactString(input: string, rules: ResolvedRule[]): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Default regex denylist — applied even when the manifest declares no
3
+ * `audit.redact[]`. Catches the four obvious patterns called out in the
4
+ * design discussion: token, secret, password, api_key. Conservative on
5
+ * purpose: matches `key: value` / `key=value` and replaces the value
6
+ * with `<REDACTED>`.
7
+ */
8
+ export const DEFAULT_REGEX_RULES = [
9
+ {
10
+ regex: "(token|secret|password|api[_-]?key)([\"']?\\s*[:=]\\s*[\"']?)[^\\s\"',}]+",
11
+ flags: "gi",
12
+ replacement: "$1$2<REDACTED>",
13
+ },
14
+ ];
15
+ export function resolveRedactionRules(manifestRules, opts = {}) {
16
+ const env = opts.env ?? process.env;
17
+ const out = [];
18
+ for (const def of DEFAULT_REGEX_RULES) {
19
+ out.push({ pattern: new RegExp(def.regex, def.flags), replacement: def.replacement });
20
+ }
21
+ for (const rule of manifestRules) {
22
+ if ("regex" in rule) {
23
+ out.push({ pattern: new RegExp(rule.regex, "g"), replacement: rule.replacement });
24
+ }
25
+ else {
26
+ const value = env[rule.env_var];
27
+ if (typeof value !== "string" || value.length === 0)
28
+ continue;
29
+ out.push({
30
+ pattern: new RegExp(escapeRegex(value), "g"),
31
+ replacement: rule.replacement,
32
+ });
33
+ }
34
+ }
35
+ return out;
36
+ }
37
+ function escapeRegex(literal) {
38
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
39
+ }
40
+ export function redactString(input, rules) {
41
+ let out = input;
42
+ for (const r of rules) {
43
+ out = out.replace(r.pattern, r.replacement);
44
+ }
45
+ return out;
46
+ }
47
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/cli/session-export/redact.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA4D;IAC1F;QACE,KAAK,EAAE,2EAA2E;QAClF,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,gBAAgB;KAC9B;CACF,CAAC;AAWF,MAAM,UAAU,qBAAqB,CACnC,aAA2B,EAC3B,OAAgC,EAAE;IAElC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACpB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACpF,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC9D,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;gBAC5C,WAAW,EAAE,IAAI,CAAC,WAAW;aAC9B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,KAAqB;IAC/D,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,24 @@
1
+ export type TranscriptEventKind = "user_prompt" | "user_tool_result" | "assistant_text" | "assistant_thinking" | "assistant_tool_use" | "attachment" | "permission_mode" | "file_history_snapshot";
2
+ export interface TranscriptEvent {
3
+ source: "transcript";
4
+ kind: TranscriptEventKind;
5
+ timestamp: string | null;
6
+ uuid?: string | undefined;
7
+ parentUuid?: string | undefined;
8
+ cwd?: string | undefined;
9
+ data: Record<string, unknown>;
10
+ }
11
+ export interface TranscriptParseResult {
12
+ events: TranscriptEvent[];
13
+ startedAt: string | null;
14
+ endedAt: string | null;
15
+ cwd: string | null;
16
+ malformedLines: number;
17
+ }
18
+ export declare function parseTranscript(jsonl: string): TranscriptParseResult;
19
+ export interface LocateTranscriptOptions {
20
+ homeDir?: string;
21
+ projectsRoot?: string;
22
+ }
23
+ export declare function locateTranscript(sessionId: string, opts?: LocateTranscriptOptions): string | null;
24
+ export declare function readTranscript(filePath: string): TranscriptParseResult;
@@ -0,0 +1,162 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ export function parseTranscript(jsonl) {
5
+ const events = [];
6
+ let startedAt = null;
7
+ let endedAt = null;
8
+ let cwd = null;
9
+ let malformedLines = 0;
10
+ for (const line of jsonl.split("\n")) {
11
+ if (!line.trim())
12
+ continue;
13
+ let raw;
14
+ try {
15
+ raw = JSON.parse(line);
16
+ }
17
+ catch {
18
+ malformedLines += 1;
19
+ continue;
20
+ }
21
+ const ts = typeof raw.timestamp === "string" ? raw.timestamp : null;
22
+ if (ts !== null) {
23
+ if (startedAt === null || ts < startedAt)
24
+ startedAt = ts;
25
+ if (endedAt === null || ts > endedAt)
26
+ endedAt = ts;
27
+ }
28
+ if (typeof raw.cwd === "string" && cwd === null)
29
+ cwd = raw.cwd;
30
+ const baseFields = {
31
+ ...(typeof raw.uuid === "string" ? { uuid: raw.uuid } : {}),
32
+ ...(typeof raw.parentUuid === "string" ? { parentUuid: raw.parentUuid } : {}),
33
+ ...(typeof raw.cwd === "string" ? { cwd: raw.cwd } : {}),
34
+ };
35
+ const base = {
36
+ source: "transcript",
37
+ timestamp: ts,
38
+ ...baseFields,
39
+ };
40
+ switch (raw.type) {
41
+ case "user": {
42
+ const content = raw.message?.content;
43
+ if (typeof content === "string") {
44
+ events.push({ ...base, kind: "user_prompt", data: { text: content } });
45
+ }
46
+ else if (Array.isArray(content)) {
47
+ for (const block of content) {
48
+ if (!block || typeof block !== "object")
49
+ continue;
50
+ const b = block;
51
+ if (b.type === "tool_result") {
52
+ events.push({
53
+ ...base,
54
+ kind: "user_tool_result",
55
+ data: {
56
+ tool_use_id: b.tool_use_id,
57
+ is_error: b.is_error,
58
+ content: b.content,
59
+ },
60
+ });
61
+ }
62
+ else if (b.type === "text") {
63
+ events.push({
64
+ ...base,
65
+ kind: "user_prompt",
66
+ data: { text: b.text },
67
+ });
68
+ }
69
+ }
70
+ }
71
+ break;
72
+ }
73
+ case "assistant": {
74
+ const content = raw.message?.content;
75
+ if (Array.isArray(content)) {
76
+ for (const block of content) {
77
+ if (!block || typeof block !== "object")
78
+ continue;
79
+ const b = block;
80
+ if (b.type === "text") {
81
+ events.push({
82
+ ...base,
83
+ kind: "assistant_text",
84
+ data: { text: b.text },
85
+ });
86
+ }
87
+ else if (b.type === "thinking") {
88
+ events.push({
89
+ ...base,
90
+ kind: "assistant_thinking",
91
+ data: { text: b.thinking ?? b.text },
92
+ });
93
+ }
94
+ else if (b.type === "tool_use") {
95
+ events.push({
96
+ ...base,
97
+ kind: "assistant_tool_use",
98
+ data: { id: b.id, name: b.name, input: b.input },
99
+ });
100
+ }
101
+ }
102
+ }
103
+ break;
104
+ }
105
+ case "attachment": {
106
+ events.push({
107
+ ...base,
108
+ kind: "attachment",
109
+ data: { attachment: raw.attachment },
110
+ });
111
+ break;
112
+ }
113
+ case "permission-mode": {
114
+ events.push({
115
+ ...base,
116
+ kind: "permission_mode",
117
+ data: { mode: raw.permissionMode },
118
+ });
119
+ break;
120
+ }
121
+ case "file-history-snapshot": {
122
+ events.push({
123
+ ...base,
124
+ kind: "file_history_snapshot",
125
+ data: {
126
+ messageId: raw.messageId,
127
+ isSnapshotUpdate: raw.isSnapshotUpdate,
128
+ snapshot: raw.snapshot,
129
+ },
130
+ });
131
+ break;
132
+ }
133
+ default:
134
+ // Ignore unknown record types; future Claude Code versions may add new ones.
135
+ break;
136
+ }
137
+ }
138
+ return { events, startedAt, endedAt, cwd, malformedLines };
139
+ }
140
+ export function locateTranscript(sessionId, opts = {}) {
141
+ const projectsRoot = opts.projectsRoot ?? path.join(opts.homeDir ?? os.homedir(), ".claude", "projects");
142
+ if (!fs.existsSync(projectsRoot))
143
+ return null;
144
+ let dirs;
145
+ try {
146
+ dirs = fs.readdirSync(projectsRoot);
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ for (const dir of dirs) {
152
+ const candidate = path.join(projectsRoot, dir, `${sessionId}.jsonl`);
153
+ if (fs.existsSync(candidate))
154
+ return candidate;
155
+ }
156
+ return null;
157
+ }
158
+ export function readTranscript(filePath) {
159
+ const raw = fs.readFileSync(filePath, "utf8");
160
+ return parseTranscript(raw);
161
+ }
162
+ //# sourceMappingURL=transcript.js.map