cngkit 1.1.22 → 1.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/{chunk-AS7FIJWP.js → chunk-2Y5FMKFS.js} +3 -4
- package/dist/chunk-2Y5FMKFS.js.map +1 -0
- package/dist/{chunk-VMTXY4KQ.js → chunk-3VDQDFHG.js} +14 -6
- package/dist/chunk-3VDQDFHG.js.map +1 -0
- package/dist/{chunk-TIY4RTXC.js → chunk-4Q2LNYVU.js} +12 -15
- package/dist/chunk-4Q2LNYVU.js.map +1 -0
- package/dist/{chunk-KURN5BJ6.js → chunk-EG3QD47O.js} +90 -15
- package/dist/chunk-EG3QD47O.js.map +1 -0
- package/dist/{chunk-DCW4RKLC.js → chunk-H5LPFVPJ.js} +10 -8
- package/dist/{chunk-DCW4RKLC.js.map → chunk-H5LPFVPJ.js.map} +1 -1
- package/dist/{chunk-BRFWVQI4.js → chunk-LC4CYVCT.js} +1 -1
- package/dist/chunk-LC4CYVCT.js.map +1 -0
- package/dist/{chunk-YALWTRIP.js → chunk-LNFZT3R3.js} +2 -2
- package/dist/{chunk-JNHW72SU.js → chunk-QCAHQE7Q.js} +14 -9
- package/dist/chunk-QCAHQE7Q.js.map +1 -0
- package/dist/{chunk-N255PYL7.js → chunk-SRZRHLRJ.js} +2 -2
- package/dist/{chunk-3BATDTKU.js → chunk-YPGDQ6P2.js} +2 -4
- package/dist/chunk-YPGDQ6P2.js.map +1 -0
- package/dist/{chunk-NLAANOBW.js → chunk-ZFBOTEAS.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +3 -3
- package/dist/commands/coderoom/join.js +14 -5
- package/dist/commands/coderoom/join.js.map +1 -1
- package/dist/commands/coderoom/share.js +4 -4
- package/dist/commands/hookify/index.js +3 -3
- package/dist/commands/hookify/ingest.js +3 -3
- package/dist/commands/hooks/index.js +3 -3
- package/dist/commands/hooks/install.js +3 -3
- package/dist/commands/hooks/uninstall.js +3 -3
- package/dist/commands/index.js +3 -3
- package/dist/commands/knowledges/audiences.js +6 -6
- package/dist/commands/knowledges/cat.js +6 -6
- package/dist/commands/knowledges/files.js +6 -6
- package/dist/commands/knowledges/find.js +6 -6
- package/dist/commands/knowledges/find.js.map +1 -1
- package/dist/commands/knowledges/glob.js +5 -5
- package/dist/commands/knowledges/grep.js +5 -5
- package/dist/commands/knowledges/grep.js.map +1 -1
- package/dist/commands/knowledges/head.js +6 -6
- package/dist/commands/knowledges/index.js +3 -3
- package/dist/commands/knowledges/list.js +6 -6
- package/dist/commands/knowledges/ls.js +6 -6
- package/dist/commands/knowledges/ls.js.map +1 -1
- package/dist/commands/knowledges/read.js +5 -5
- package/dist/commands/knowledges/read.js.map +1 -1
- package/dist/commands/knowledges/realpath.js +3 -3
- package/dist/commands/knowledges/realpath.js.map +1 -1
- package/dist/commands/knowledges/search.js +6 -6
- package/dist/commands/knowledges/stat.js +6 -6
- package/dist/commands/knowledges/status.js +6 -6
- package/dist/commands/knowledges/tail.js +6 -6
- package/dist/commands/knowledges/tree.js +6 -6
- package/dist/commands/login.js +2 -2
- package/dist/commands/scrub.js +17 -14
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +41 -31
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3BATDTKU.js.map +0 -1
- package/dist/chunk-AS7FIJWP.js.map +0 -1
- package/dist/chunk-BRFWVQI4.js.map +0 -1
- package/dist/chunk-JNHW72SU.js.map +0 -1
- package/dist/chunk-KURN5BJ6.js.map +0 -1
- package/dist/chunk-TIY4RTXC.js.map +0 -1
- package/dist/chunk-VMTXY4KQ.js.map +0 -1
- /package/dist/{chunk-YALWTRIP.js.map → chunk-LNFZT3R3.js.map} +0 -0
- /package/dist/{chunk-N255PYL7.js.map → chunk-SRZRHLRJ.js.map} +0 -0
- /package/dist/{chunk-NLAANOBW.js.map → chunk-ZFBOTEAS.js.map} +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
KnowCatalogSummary
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-3VDQDFHG.js";
|
|
4
4
|
import {
|
|
5
5
|
createKnowledgesApi
|
|
6
|
-
} from "../../chunk-
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-N255PYL7.js";
|
|
6
|
+
} from "../../chunk-ZFBOTEAS.js";
|
|
7
|
+
import "../../chunk-2Y5FMKFS.js";
|
|
9
8
|
import {
|
|
10
9
|
formatJson,
|
|
11
10
|
shouldPrintJson
|
|
12
11
|
} from "../../chunk-XQGLUQFM.js";
|
|
12
|
+
import "../../chunk-SRZRHLRJ.js";
|
|
13
13
|
import {
|
|
14
14
|
JsonOutputOptionsSchema
|
|
15
15
|
} from "../../chunk-25Q463MH.js";
|
|
16
16
|
import {
|
|
17
17
|
CommandRunner
|
|
18
|
-
} from "../../chunk-
|
|
19
|
-
import "../../chunk-
|
|
18
|
+
} from "../../chunk-4Q2LNYVU.js";
|
|
19
|
+
import "../../chunk-H5LPFVPJ.js";
|
|
20
20
|
import "../../chunk-PZ5AY32C.js";
|
|
21
21
|
|
|
22
22
|
// src/features/knowledges/handlers/status.ts
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
outputRawContent
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-LC4CYVCT.js";
|
|
4
4
|
import {
|
|
5
5
|
createKnowledgesApi
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-ZFBOTEAS.js";
|
|
7
7
|
import {
|
|
8
8
|
normalizeCatalogPath
|
|
9
|
-
} from "../../chunk-
|
|
10
|
-
import "../../chunk-N255PYL7.js";
|
|
9
|
+
} from "../../chunk-2Y5FMKFS.js";
|
|
11
10
|
import {
|
|
12
11
|
coerceLimit
|
|
13
12
|
} from "../../chunk-XQGLUQFM.js";
|
|
13
|
+
import "../../chunk-SRZRHLRJ.js";
|
|
14
14
|
import {
|
|
15
15
|
JsonOutputOptionsSchema,
|
|
16
16
|
RequiredFilePathArgsSchema
|
|
17
17
|
} from "../../chunk-25Q463MH.js";
|
|
18
18
|
import {
|
|
19
19
|
CommandRunner
|
|
20
|
-
} from "../../chunk-
|
|
21
|
-
import "../../chunk-
|
|
20
|
+
} from "../../chunk-4Q2LNYVU.js";
|
|
21
|
+
import "../../chunk-H5LPFVPJ.js";
|
|
22
22
|
import "../../chunk-PZ5AY32C.js";
|
|
23
23
|
|
|
24
24
|
// src/commands/knowledges/tail.tsx
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatFilesystemTree
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-LNFZT3R3.js";
|
|
4
4
|
import {
|
|
5
5
|
createKnowledgesApi
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-ZFBOTEAS.js";
|
|
7
7
|
import {
|
|
8
8
|
normalizeFilesystemCatalogPath
|
|
9
|
-
} from "../../chunk-
|
|
10
|
-
import "../../chunk-N255PYL7.js";
|
|
9
|
+
} from "../../chunk-2Y5FMKFS.js";
|
|
11
10
|
import {
|
|
12
11
|
coerceLimit,
|
|
13
12
|
formatJson,
|
|
14
13
|
shouldPrintJson
|
|
15
14
|
} from "../../chunk-XQGLUQFM.js";
|
|
15
|
+
import "../../chunk-SRZRHLRJ.js";
|
|
16
16
|
import {
|
|
17
17
|
JsonOutputOptionsSchema,
|
|
18
18
|
OptionalPathArgsSchema
|
|
19
19
|
} from "../../chunk-25Q463MH.js";
|
|
20
20
|
import {
|
|
21
21
|
CommandRunner
|
|
22
|
-
} from "../../chunk-
|
|
23
|
-
import "../../chunk-
|
|
22
|
+
} from "../../chunk-4Q2LNYVU.js";
|
|
23
|
+
import "../../chunk-H5LPFVPJ.js";
|
|
24
24
|
import "../../chunk-PZ5AY32C.js";
|
|
25
25
|
|
|
26
26
|
// src/commands/knowledges/tree.tsx
|
package/dist/commands/login.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
} from "../chunk-25Q463MH.js";
|
|
4
4
|
import {
|
|
5
5
|
CommandRunner
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-4Q2LNYVU.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveApiBaseUrl
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-H5LPFVPJ.js";
|
|
10
10
|
import "../chunk-PZ5AY32C.js";
|
|
11
11
|
|
|
12
12
|
// src/shared/browser.ts
|
package/dist/commands/scrub.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
} from "../chunk-25Q463MH.js";
|
|
5
5
|
import {
|
|
6
6
|
CommandRunner
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-4Q2LNYVU.js";
|
|
8
|
+
import "../chunk-H5LPFVPJ.js";
|
|
9
9
|
import "../chunk-PZ5AY32C.js";
|
|
10
10
|
|
|
11
11
|
// src/commands/scrub.tsx
|
|
@@ -115,18 +115,21 @@ function ScrubFindingsReport({ findings }) {
|
|
|
115
115
|
findings.map((finding, index) => {
|
|
116
116
|
const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;
|
|
117
117
|
const status = finding.verified ? "verified" : "unverified";
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
/* @__PURE__ */
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
return (
|
|
119
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: Findings can have duplicate filePaths; index is the simplest stable key
|
|
120
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
121
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "- " }),
|
|
122
|
+
/* @__PURE__ */ jsx(Text, { children: location }),
|
|
123
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
124
|
+
" ",
|
|
125
|
+
finding.detectorName
|
|
126
|
+
] }),
|
|
127
|
+
/* @__PURE__ */ jsxs(Text, { color: finding.verified ? "green" : "yellow", children: [
|
|
128
|
+
" ",
|
|
129
|
+
status
|
|
130
|
+
] })
|
|
131
|
+
] }, index)
|
|
132
|
+
);
|
|
130
133
|
})
|
|
131
134
|
] });
|
|
132
135
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/scrub.tsx","../../src/features/scrub/run-scrub-command.ts","../../src/features/scrub/masker.ts","../../src/features/scrub/scrub-output.tsx","../../src/features/scrub/trufflehog.ts","../../src/features/scrub/findings.ts"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, OptionalPathArgsSchema } from \"../cli/options.js\";\nimport { runScrubCommand, type ScrubCommandOptions } from \"../features/scrub/run-scrub-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"Scan for secrets with TruffleHog and optionally mask them inline\";\nexport const args = OptionalPathArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n mask: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Compatibility alias for inline scrubbing; --yes is still required\",\n })\n ),\n yes: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Confirm and run inline scrubbing\",\n })\n ),\n});\n\ntype ScrubCommandProps = {\n readonly args: z.infer<typeof args>;\n readonly options: ScrubCommandOptions;\n};\n\nexport default function ScrubCommand({ args, options }: ScrubCommandProps) {\n return <CommandRunner run={(output) => runScrubCommand(args[0], options, output)} />;\n}\n","import process from \"node:process\";\nimport { createElement } from \"react\";\n\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport type { ScrubFinding } from \"./findings.js\";\nimport { scrubFindingsInline } from \"./masker.js\";\nimport { ScrubFindingsReport } from \"./scrub-output.js\";\nimport { scanFilesystemWithTruffleHog } from \"./trufflehog.js\";\n\nexport type ScrubCommandOptions = GlobalCommandOptions & {\n mask?: boolean;\n yes?: boolean;\n};\n\nexport type ScrubCommandDependencies = {\n scanFilesystem(targetPath: string): Promise<ScrubFinding[]>;\n};\n\nexport async function runScrubCommand(\n targetPath: string | undefined,\n options: ScrubCommandOptions,\n output: CommandOutput,\n dependencies?: ScrubCommandDependencies\n): Promise<void> {\n const scrubTargetPath = targetPath ?? \".\";\n const scanFilesystem =\n dependencies?.scanFilesystem ??\n ((scanTargetPath: string) =>\n scanFilesystemWithTruffleHog(scanTargetPath, {\n cwd: process.cwd(),\n }));\n const findings = await scanFilesystem(scrubTargetPath);\n\n if (findings.length === 0) {\n output.success(\"No secrets found.\");\n return;\n }\n\n output.component(createElement(ScrubFindingsReport, { findings }));\n\n if (!options.yes) {\n if (options.mask) {\n throw new Error(\"Inline scrubbing rewrites files. Re-run with --yes to continue.\");\n }\n\n output.warning(\"Report only. Re-run with --yes to scrub files inline.\");\n return;\n }\n\n const result = await scrubFindingsInline(scrubTargetPath, findings);\n output.success(\n `Masked ${result.replacements} secret${result.replacements === 1 ? \"\" : \"s\"} in ${\n result.filesChanged\n } file${result.filesChanged === 1 ? \"\" : \"s\"}.`\n );\n\n if (result.skippedFindings > 0) {\n output.warning(`Skipped ${result.skippedFindings} finding(s) outside the scrub root.`);\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { ScrubFinding } from \"./findings.js\";\n\nexport type InlineScrubResult = {\n filesChanged: number;\n replacements: number;\n skippedFindings: number;\n};\n\nexport async function scrubFindingsInline(\n targetPath: string,\n findings: ScrubFinding[]\n): Promise<InlineScrubResult> {\n const target = await resolveScrubTarget(targetPath);\n const findingsByFile = groupFindingsBySafePath(target, findings);\n let filesChanged = 0;\n let replacements = 0;\n\n for (const [absolutePath, fileFindings] of findingsByFile.safeFindingsByPath) {\n const originalContent = await fs.readFile(absolutePath, \"utf8\");\n let scrubbedContent = originalContent;\n let fileReplacements = 0;\n\n for (const finding of fileFindings) {\n const placeholder = formatSecretPlaceholder(finding);\n const nextContent = scrubbedContent.split(finding.rawSecret).join(placeholder);\n if (nextContent !== scrubbedContent) {\n fileReplacements += countOccurrences(scrubbedContent, finding.rawSecret);\n scrubbedContent = nextContent;\n }\n }\n\n if (scrubbedContent !== originalContent) {\n await fs.writeFile(absolutePath, scrubbedContent);\n filesChanged += 1;\n replacements += fileReplacements;\n }\n }\n\n return {\n filesChanged,\n replacements,\n skippedFindings: findingsByFile.skippedFindings,\n };\n}\n\nexport function formatSecretPlaceholder(finding: ScrubFinding): string {\n return `[CNGKIT_SECRET:${sanitizePlaceholderPart(finding.detectorName)}:${\n finding.verified ? \"verified\" : \"unverified\"\n }]`;\n}\n\nfunction groupFindingsBySafePath(\n target: ScrubTarget,\n findings: ScrubFinding[]\n): {\n safeFindingsByPath: Map<string, ScrubFinding[]>;\n skippedFindings: number;\n} {\n const safeFindingsByPath = new Map<string, ScrubFinding[]>();\n let skippedFindings = 0;\n\n for (const finding of findings) {\n const absolutePath = resolveSafeFindingPath(target, finding.filePath);\n if (!absolutePath) {\n skippedFindings += 1;\n continue;\n }\n\n const fileFindings = safeFindingsByPath.get(absolutePath) ?? [];\n fileFindings.push(finding);\n safeFindingsByPath.set(absolutePath, fileFindings);\n }\n\n return { safeFindingsByPath, skippedFindings };\n}\n\ntype ScrubTarget = {\n rootDir: string;\n allowedFilePath?: string;\n};\n\nasync function resolveScrubTarget(targetPath: string): Promise<ScrubTarget> {\n const absoluteTargetPath = path.resolve(targetPath);\n const stat = await fs.stat(absoluteTargetPath);\n\n if (stat.isFile()) {\n return {\n rootDir: path.dirname(absoluteTargetPath),\n allowedFilePath: absoluteTargetPath,\n };\n }\n\n return {\n rootDir: absoluteTargetPath,\n };\n}\n\nfunction resolveSafeFindingPath(\n target: ScrubTarget,\n findingFilePath: string\n): string | undefined {\n const absolutePath = path.isAbsolute(findingFilePath)\n ? path.resolve(findingFilePath)\n : path.resolve(target.rootDir, findingFilePath);\n\n if (target.allowedFilePath) {\n return absolutePath === target.allowedFilePath ? absolutePath : undefined;\n }\n\n const normalizedRoot = `${path.resolve(target.rootDir)}${path.sep}`;\n\n if (absolutePath === path.resolve(target.rootDir) || absolutePath.startsWith(normalizedRoot)) {\n return absolutePath;\n }\n\n return undefined;\n}\n\nfunction countOccurrences(value: string, search: string): number {\n if (!search) {\n return 0;\n }\n\n return value.split(search).length - 1;\n}\n\nfunction sanitizePlaceholderPart(value: string): string {\n return value.replace(/[^A-Za-z0-9_-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"Unknown\";\n}\n","import { Box, Text } from \"ink\";\n\nimport type { ScrubFinding } from \"./findings.js\";\n\nexport function ScrubFindingsReport({ findings }: { readonly findings: readonly ScrubFinding[] }) {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"yellow\" bold>\n Found {findings.length} possible secret{findings.length === 1 ? \"\" : \"s\"}:\n </Text>\n {findings.map((finding, index) => {\n const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;\n const status = finding.verified ? \"verified\" : \"unverified\";\n\n return (\n <Text key={`${finding.filePath}-${finding.line ?? 0}-${finding.detectorName}-${index}`}>\n <Text color=\"cyan\">- </Text>\n <Text>{location}</Text>\n <Text dimColor> {finding.detectorName}</Text>\n <Text color={finding.verified ? \"green\" : \"yellow\"}> {status}</Text>\n </Text>\n );\n })}\n </Box>\n );\n}\n","import { spawn } from \"node:child_process\";\nimport path from \"node:path\";\n\nimport { parseTruffleHogJsonLines, type ScrubFinding } from \"./findings.js\";\n\nexport type TruffleHogScanOptions = {\n cwd: string;\n};\n\nexport async function scanFilesystemWithTruffleHog(\n targetPath: string,\n options: TruffleHogScanOptions\n): Promise<ScrubFinding[]> {\n const absoluteTargetPath = path.resolve(options.cwd, targetPath);\n const { stdout } = await runTruffleHog([\n \"filesystem\",\n absoluteTargetPath,\n \"--json\",\n \"--no-update\",\n \"--force-skip-binaries\",\n \"--force-skip-archives\",\n ]);\n\n return parseTruffleHogJsonLines(stdout);\n}\n\nfunction runTruffleHog(args: string[]): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(\"trufflehog\", args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n reject(\n new Error(\n \"TruffleHog is not installed. Install it with `brew install trufflehog` or see https://github.com/trufflesecurity/trufflehog.\"\n )\n );\n return;\n }\n\n reject(error);\n });\n child.on(\"close\", (exitCode) => {\n const stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n const stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\n if (exitCode && exitCode !== 0) {\n reject(new Error(`TruffleHog scan failed with exit code ${exitCode}: ${stderr.trim()}`));\n return;\n }\n\n resolve({ stdout, stderr });\n });\n });\n}\n","export type ScrubFinding = {\n detectorName: string;\n filePath: string;\n line?: number;\n rawSecret: string;\n redactedSecret: string;\n verified: boolean;\n};\n\ntype JsonRecord = Record<string, unknown>;\n\nexport function parseTruffleHogJsonLines(output: string): ScrubFinding[] {\n const findings: ScrubFinding[] = [];\n\n for (const line of output.split(/\\r?\\n/)) {\n const trimmedLine = line.trim();\n if (!trimmedLine.startsWith(\"{\")) {\n continue;\n }\n\n const parsedLine = parseJsonRecord(trimmedLine);\n if (!parsedLine) {\n continue;\n }\n\n const finding = parseTruffleHogFinding(parsedLine);\n if (finding) {\n findings.push(finding);\n }\n }\n\n return findings;\n}\n\nfunction parseTruffleHogFinding(finding: JsonRecord): ScrubFinding | undefined {\n const rawSecret = readString(finding.Raw) || readString(finding.RawV2);\n const filePath = readFilesystemFilePath(finding);\n\n if (!rawSecret || !filePath) {\n return undefined;\n }\n\n return {\n detectorName: readString(finding.DetectorName) || \"Unknown\",\n filePath,\n line: readFilesystemLine(finding),\n rawSecret,\n redactedSecret: readString(finding.Redacted) || \"\",\n verified: finding.Verified === true,\n };\n}\n\nfunction readFilesystemFilePath(finding: JsonRecord): string | undefined {\n const filesystem = readFilesystemMetadata(finding);\n if (!filesystem) {\n return undefined;\n }\n\n return readString(filesystem.file) || readString(filesystem.path);\n}\n\nfunction readFilesystemLine(finding: JsonRecord): number | undefined {\n const filesystem = readFilesystemMetadata(finding);\n if (!filesystem) {\n return undefined;\n }\n\n return typeof filesystem.line === \"number\" ? filesystem.line : undefined;\n}\n\nfunction readFilesystemMetadata(finding: JsonRecord): JsonRecord | undefined {\n const sourceMetadata = readRecord(finding.SourceMetadata);\n const data = readRecord(sourceMetadata?.Data);\n return readRecord(data?.Filesystem);\n}\n\nfunction parseJsonRecord(line: string): JsonRecord | undefined {\n try {\n return readRecord(JSON.parse(line));\n } catch {\n return undefined;\n }\n}\n\nfunction isJsonRecord(value: unknown): value is JsonRecord {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction readRecord(value: unknown): JsonRecord | undefined {\n return isJsonRecord(value) ? value : undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n"],"mappings":"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","names":["path","options","path","args","options","jsx","args","options"]}
|
|
1
|
+
{"version":3,"sources":["../../src/commands/scrub.tsx","../../src/features/scrub/run-scrub-command.ts","../../src/features/scrub/masker.ts","../../src/features/scrub/scrub-output.tsx","../../src/features/scrub/trufflehog.ts","../../src/features/scrub/findings.ts"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, OptionalPathArgsSchema } from \"../cli/options.js\";\nimport { runScrubCommand, type ScrubCommandOptions } from \"../features/scrub/run-scrub-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"Scan for secrets with TruffleHog and optionally mask them inline\";\nexport const args = OptionalPathArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n mask: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Compatibility alias for inline scrubbing; --yes is still required\",\n })\n ),\n yes: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Confirm and run inline scrubbing\",\n })\n ),\n});\n\ntype ScrubCommandProps = {\n readonly args: z.infer<typeof args>;\n readonly options: ScrubCommandOptions;\n};\n\nexport default function ScrubCommand({ args, options }: ScrubCommandProps) {\n return <CommandRunner run={(output) => runScrubCommand(args[0], options, output)} />;\n}\n","import process from \"node:process\";\nimport { createElement } from \"react\";\n\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport type { ScrubFinding } from \"./findings.js\";\nimport { scrubFindingsInline } from \"./masker.js\";\nimport { ScrubFindingsReport } from \"./scrub-output.js\";\nimport { scanFilesystemWithTruffleHog } from \"./trufflehog.js\";\n\nexport type ScrubCommandOptions = GlobalCommandOptions & {\n mask?: boolean;\n yes?: boolean;\n};\n\nexport type ScrubCommandDependencies = {\n scanFilesystem(targetPath: string): Promise<ScrubFinding[]>;\n};\n\nexport async function runScrubCommand(\n targetPath: string | undefined,\n options: ScrubCommandOptions,\n output: CommandOutput,\n dependencies?: ScrubCommandDependencies\n): Promise<void> {\n const scrubTargetPath = targetPath ?? \".\";\n const scanFilesystem =\n dependencies?.scanFilesystem ??\n ((scanTargetPath: string) =>\n scanFilesystemWithTruffleHog(scanTargetPath, {\n cwd: process.cwd(),\n }));\n const findings = await scanFilesystem(scrubTargetPath);\n\n if (findings.length === 0) {\n output.success(\"No secrets found.\");\n return;\n }\n\n output.component(createElement(ScrubFindingsReport, { findings }));\n\n if (!options.yes) {\n if (options.mask) {\n throw new Error(\"Inline scrubbing rewrites files. Re-run with --yes to continue.\");\n }\n\n output.warning(\"Report only. Re-run with --yes to scrub files inline.\");\n return;\n }\n\n const result = await scrubFindingsInline(scrubTargetPath, findings);\n output.success(\n `Masked ${result.replacements} secret${result.replacements === 1 ? \"\" : \"s\"} in ${\n result.filesChanged\n } file${result.filesChanged === 1 ? \"\" : \"s\"}.`\n );\n\n if (result.skippedFindings > 0) {\n output.warning(`Skipped ${result.skippedFindings} finding(s) outside the scrub root.`);\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { ScrubFinding } from \"./findings.js\";\n\nexport type InlineScrubResult = {\n filesChanged: number;\n replacements: number;\n skippedFindings: number;\n};\n\nexport async function scrubFindingsInline(\n targetPath: string,\n findings: ScrubFinding[]\n): Promise<InlineScrubResult> {\n const target = await resolveScrubTarget(targetPath);\n const findingsByFile = groupFindingsBySafePath(target, findings);\n let filesChanged = 0;\n let replacements = 0;\n\n for (const [absolutePath, fileFindings] of findingsByFile.safeFindingsByPath) {\n const originalContent = await fs.readFile(absolutePath, \"utf8\");\n let scrubbedContent = originalContent;\n let fileReplacements = 0;\n\n for (const finding of fileFindings) {\n const placeholder = formatSecretPlaceholder(finding);\n const nextContent = scrubbedContent.split(finding.rawSecret).join(placeholder);\n if (nextContent !== scrubbedContent) {\n fileReplacements += countOccurrences(scrubbedContent, finding.rawSecret);\n scrubbedContent = nextContent;\n }\n }\n\n if (scrubbedContent !== originalContent) {\n await fs.writeFile(absolutePath, scrubbedContent);\n filesChanged += 1;\n replacements += fileReplacements;\n }\n }\n\n return {\n filesChanged,\n replacements,\n skippedFindings: findingsByFile.skippedFindings,\n };\n}\n\nexport function formatSecretPlaceholder(finding: ScrubFinding): string {\n return `[CNGKIT_SECRET:${sanitizePlaceholderPart(finding.detectorName)}:${\n finding.verified ? \"verified\" : \"unverified\"\n }]`;\n}\n\nfunction groupFindingsBySafePath(\n target: ScrubTarget,\n findings: ScrubFinding[]\n): {\n safeFindingsByPath: Map<string, ScrubFinding[]>;\n skippedFindings: number;\n} {\n const safeFindingsByPath = new Map<string, ScrubFinding[]>();\n let skippedFindings = 0;\n\n for (const finding of findings) {\n const absolutePath = resolveSafeFindingPath(target, finding.filePath);\n if (!absolutePath) {\n skippedFindings += 1;\n continue;\n }\n\n const fileFindings = safeFindingsByPath.get(absolutePath) ?? [];\n fileFindings.push(finding);\n safeFindingsByPath.set(absolutePath, fileFindings);\n }\n\n return { safeFindingsByPath, skippedFindings };\n}\n\ntype ScrubTarget = {\n rootDir: string;\n allowedFilePath?: string;\n};\n\nasync function resolveScrubTarget(targetPath: string): Promise<ScrubTarget> {\n const absoluteTargetPath = path.resolve(targetPath);\n const stat = await fs.stat(absoluteTargetPath);\n\n if (stat.isFile()) {\n return {\n rootDir: path.dirname(absoluteTargetPath),\n allowedFilePath: absoluteTargetPath,\n };\n }\n\n return {\n rootDir: absoluteTargetPath,\n };\n}\n\nfunction resolveSafeFindingPath(target: ScrubTarget, findingFilePath: string): string | undefined {\n const absolutePath = path.isAbsolute(findingFilePath)\n ? path.resolve(findingFilePath)\n : path.resolve(target.rootDir, findingFilePath);\n\n if (target.allowedFilePath) {\n return absolutePath === target.allowedFilePath ? absolutePath : undefined;\n }\n\n const normalizedRoot = `${path.resolve(target.rootDir)}${path.sep}`;\n\n if (absolutePath === path.resolve(target.rootDir) || absolutePath.startsWith(normalizedRoot)) {\n return absolutePath;\n }\n\n return undefined;\n}\n\nfunction countOccurrences(value: string, search: string): number {\n if (!search) {\n return 0;\n }\n\n return value.split(search).length - 1;\n}\n\nfunction sanitizePlaceholderPart(value: string): string {\n return value.replace(/[^A-Za-z0-9_-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"Unknown\";\n}\n","import { Box, Text } from \"ink\";\n\nimport type { ScrubFinding } from \"./findings.js\";\n\nexport function ScrubFindingsReport({ findings }: { readonly findings: readonly ScrubFinding[] }) {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"yellow\" bold>\n Found {findings.length} possible secret{findings.length === 1 ? \"\" : \"s\"}:\n </Text>\n {findings.map((finding, index) => {\n const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;\n const status = finding.verified ? \"verified\" : \"unverified\";\n\n return (\n // biome-ignore lint/suspicious/noArrayIndexKey: Findings can have duplicate filePaths; index is the simplest stable key\n <Text key={index}>\n <Text color=\"cyan\">- </Text>\n <Text>{location}</Text>\n <Text dimColor> {finding.detectorName}</Text>\n <Text color={finding.verified ? \"green\" : \"yellow\"}> {status}</Text>\n </Text>\n );\n })}\n </Box>\n );\n}\n","import { spawn } from \"node:child_process\";\nimport path from \"node:path\";\n\nimport { parseTruffleHogJsonLines, type ScrubFinding } from \"./findings.js\";\n\nexport type TruffleHogScanOptions = {\n cwd: string;\n};\n\nexport async function scanFilesystemWithTruffleHog(\n targetPath: string,\n options: TruffleHogScanOptions\n): Promise<ScrubFinding[]> {\n const absoluteTargetPath = path.resolve(options.cwd, targetPath);\n const { stdout } = await runTruffleHog([\n \"filesystem\",\n absoluteTargetPath,\n \"--json\",\n \"--no-update\",\n \"--force-skip-binaries\",\n \"--force-skip-archives\",\n ]);\n\n return parseTruffleHogJsonLines(stdout);\n}\n\nfunction runTruffleHog(args: string[]): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(\"trufflehog\", args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n reject(\n new Error(\n \"TruffleHog is not installed. Install it with `brew install trufflehog` or see https://github.com/trufflesecurity/trufflehog.\"\n )\n );\n return;\n }\n\n reject(error);\n });\n child.on(\"close\", (exitCode) => {\n const stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n const stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\n if (exitCode && exitCode !== 0) {\n reject(new Error(`TruffleHog scan failed with exit code ${exitCode}: ${stderr.trim()}`));\n return;\n }\n\n resolve({ stdout, stderr });\n });\n });\n}\n","export type ScrubFinding = {\n detectorName: string;\n filePath: string;\n line?: number;\n rawSecret: string;\n redactedSecret: string;\n verified: boolean;\n};\n\ntype JsonRecord = Record<string, unknown>;\n\nexport function parseTruffleHogJsonLines(output: string): ScrubFinding[] {\n const findings: ScrubFinding[] = [];\n\n for (const line of output.split(/\\r?\\n/)) {\n const trimmedLine = line.trim();\n if (!trimmedLine.startsWith(\"{\")) {\n continue;\n }\n\n const parsedLine = parseJsonRecord(trimmedLine);\n if (!parsedLine) {\n continue;\n }\n\n const finding = parseTruffleHogFinding(parsedLine);\n if (finding) {\n findings.push(finding);\n }\n }\n\n return findings;\n}\n\nfunction parseTruffleHogFinding(finding: JsonRecord): ScrubFinding | undefined {\n const rawSecret = readString(finding.Raw) || readString(finding.RawV2);\n const filePath = readFilesystemFilePath(finding);\n\n if (!rawSecret || !filePath) {\n return undefined;\n }\n\n return {\n detectorName: readString(finding.DetectorName) || \"Unknown\",\n filePath,\n line: readFilesystemLine(finding),\n rawSecret,\n redactedSecret: readString(finding.Redacted) || \"\",\n verified: finding.Verified === true,\n };\n}\n\nfunction readFilesystemFilePath(finding: JsonRecord): string | undefined {\n const filesystem = readFilesystemMetadata(finding);\n if (!filesystem) {\n return undefined;\n }\n\n return readString(filesystem.file) || readString(filesystem.path);\n}\n\nfunction readFilesystemLine(finding: JsonRecord): number | undefined {\n const filesystem = readFilesystemMetadata(finding);\n if (!filesystem) {\n return undefined;\n }\n\n return typeof filesystem.line === \"number\" ? filesystem.line : undefined;\n}\n\nfunction readFilesystemMetadata(finding: JsonRecord): JsonRecord | undefined {\n const sourceMetadata = readRecord(finding.SourceMetadata);\n const data = readRecord(sourceMetadata?.Data);\n return readRecord(data?.Filesystem);\n}\n\nfunction parseJsonRecord(line: string): JsonRecord | undefined {\n try {\n return readRecord(JSON.parse(line));\n } catch {\n return undefined;\n }\n}\n\nfunction isJsonRecord(value: unknown): value is JsonRecord {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction readRecord(value: unknown): JsonRecord | undefined {\n return isJsonRecord(value) ? value : undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n"],"mappings":"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","names":["path","options","path","args","options","jsx","args","options"]}
|
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
} from "../chunk-XQGLUQFM.js";
|
|
8
8
|
import {
|
|
9
9
|
renderCngkitHelp
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-QCAHQE7Q.js";
|
|
11
11
|
import {
|
|
12
12
|
GlobalOptionsSchema,
|
|
13
13
|
TranscriptArgsSchema
|
|
14
14
|
} from "../chunk-25Q463MH.js";
|
|
15
15
|
import {
|
|
16
16
|
CommandRunner
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-4Q2LNYVU.js";
|
|
18
|
+
import "../chunk-H5LPFVPJ.js";
|
|
19
19
|
import "../chunk-PZ5AY32C.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/transcripts.tsx
|
|
@@ -79,11 +79,11 @@ function parseJsonObject(line) {
|
|
|
79
79
|
return void 0;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
function isRecord(value) {
|
|
83
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
84
|
+
}
|
|
82
85
|
function asObject(value) {
|
|
83
|
-
|
|
84
|
-
return value;
|
|
85
|
-
}
|
|
86
|
-
return void 0;
|
|
86
|
+
return isRecord(value) ? value : void 0;
|
|
87
87
|
}
|
|
88
88
|
function readString(value) {
|
|
89
89
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
@@ -311,33 +311,43 @@ function parseClaudeEntry(record, parsed, includeInternal) {
|
|
|
311
311
|
// src/features/transcripts/transcript-output.tsx
|
|
312
312
|
import { Box, Text } from "ink";
|
|
313
313
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
314
|
-
function TranscriptRecordList({
|
|
314
|
+
function TranscriptRecordList({
|
|
315
|
+
records
|
|
316
|
+
}) {
|
|
315
317
|
if (records.length === 0) {
|
|
316
318
|
return /* @__PURE__ */ jsx(Text, { color: "yellow", children: "No transcript files found." });
|
|
317
319
|
}
|
|
318
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: records.map((record, index) => /* @__PURE__ */ jsxs(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
320
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: records.map((record, index) => /* @__PURE__ */ jsxs(
|
|
321
|
+
Box,
|
|
322
|
+
{
|
|
323
|
+
flexDirection: "column",
|
|
324
|
+
marginBottom: 1,
|
|
325
|
+
children: [
|
|
326
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
327
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
328
|
+
index + 1,
|
|
329
|
+
". "
|
|
330
|
+
] }),
|
|
331
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: record.source }),
|
|
332
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
333
|
+
" ",
|
|
334
|
+
record.updatedAt
|
|
335
|
+
] })
|
|
336
|
+
] }),
|
|
337
|
+
/* @__PURE__ */ jsx(Text, { children: record.filePath }),
|
|
338
|
+
record.sessionId ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
339
|
+
"session ",
|
|
340
|
+
record.sessionId
|
|
341
|
+
] }) : null,
|
|
342
|
+
record.cwd ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
343
|
+
"cwd ",
|
|
344
|
+
record.cwd
|
|
345
|
+
] }) : null,
|
|
346
|
+
record.title ? /* @__PURE__ */ jsx(Text, { children: singleLine(record.title) }) : null
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
`${record.source}-${record.filePath}-${index}`
|
|
350
|
+
)) });
|
|
341
351
|
}
|
|
342
352
|
|
|
343
353
|
// src/features/transcripts/run-transcript-command.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/transcripts.tsx","../../src/features/transcripts/run-transcript-command.ts","../../src/features/transcripts/reader.ts","../../src/features/transcripts/jsonl.ts","../../src/features/transcripts/paths.ts","../../src/features/transcripts/discovery.ts","../../src/features/transcripts/types.ts","../../src/features/transcripts/transcript-output.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, TranscriptArgsSchema } from \"../cli/options.js\";\nimport { runTranscriptCommand, type TranscriptCommandOptions } from \"../features/transcripts/run-transcript-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"List, read, and grep local Claude/Codex transcript JSONL files\";\nexport const args = TranscriptArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n source: z\n .enum([\"all\", \"codex\", \"claude\"])\n .optional()\n .describe(\n option({\n description: \"Transcript source. Default: all\",\n valueDescription: \"all|codex|claude\",\n })\n ),\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum records or entries\",\n valueDescription: \"n\",\n })\n ),\n fileLimit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum recent files to scan for grep\",\n valueDescription: \"n\",\n })\n ),\n includeInternal: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Include developer/system/internal transcript entries\",\n })\n ),\n json: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Print raw JSON\",\n })\n ),\n});\n\ntype TranscriptsCommandProps = {\n readonly args: string[];\n readonly options: TranscriptCommandOptions;\n};\n\nexport default function TranscriptsCommand({ args, options }: TranscriptsCommandProps) {\n return <CommandRunner run={(output) => runTranscriptCommand(args, options, output)} />;\n}\n","import { createElement } from \"react\";\n\nimport { renderCngkitHelp } from \"../../cli/help-specs.js\";\nimport {\n coerceLimit,\n formatJson,\n optionalJoinedArgument,\n shouldPrintJson,\n type JsonOutputOptions,\n type NumberOption,\n} from \"../../shared/command-utils.js\";\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport {\n grepTranscriptEntries,\n listTranscriptRecords,\n readTranscriptEntries,\n type TranscriptEntry,\n type TranscriptScopeName,\n} from \"./reader.js\";\nimport { TranscriptRecordList } from \"./transcript-output.js\";\n\nexport type TranscriptCommandOptions = GlobalCommandOptions &\n JsonOutputOptions & {\n source?: string;\n limit?: NumberOption;\n fileLimit?: NumberOption;\n includeInternal?: boolean;\n };\n\nexport async function runTranscriptCommand(\n args: string[] | undefined,\n options: TranscriptCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [action = \"list\", ...actionArgs] = args ?? [];\n const source = normalizeTranscriptSource(options.source);\n const limit = coerceLimit(options.limit, action === \"list\" ? 12 : 80, 500);\n const includeInternal = options.includeInternal === true;\n\n switch (action) {\n case \"help\":\n output.component(renderCngkitHelp(\"transcripts\"));\n return;\n case \"list\": {\n const records = await listTranscriptRecords({ source, limit });\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ records }));\n return;\n }\n\n output.component(createElement(TranscriptRecordList, { records }));\n return;\n }\n case \"read\": {\n const target = actionArgs[0];\n if (!target) {\n throw new Error(\"Missing transcript target. Usage: cngkit transcripts read <path-or-id>\");\n }\n\n const entries = await readTranscriptEntries({\n source,\n target,\n limit,\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n case \"grep\": {\n const query = optionalJoinedArgument(actionArgs);\n if (!query) {\n throw new Error(\"Missing query. Usage: cngkit transcripts grep <query>\");\n }\n\n const entries = await grepTranscriptEntries({\n source,\n query,\n limit,\n fileLimit: coerceLimit(options.fileLimit, 60, 5000),\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n default:\n throw new Error(\"Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>\");\n }\n}\n\nfunction formatTranscriptEntries(entries: TranscriptEntry[]): string {\n if (entries.length === 0) {\n return \"No transcript entries found.\";\n }\n\n return entries\n .map((entry) => {\n const when = entry.timestamp ? ` ${entry.timestamp}` : \"\";\n return `[${entry.source}] ${entry.role}${when}\\n${entry.text.trim()}`;\n })\n .join(\"\\n\\n\");\n}\n\nfunction normalizeTranscriptSource(value: string | undefined): TranscriptScopeName {\n if (value === undefined) {\n return \"all\";\n }\n\n switch (value) {\n case \"all\":\n case \"codex\":\n case \"claude\":\n return value;\n default:\n throw new Error(\"Unknown transcript source. Use one of: all, codex, claude.\");\n }\n}\n","// Public API entrypoint for transcript reading. Internal helpers live in\n// `./types.ts`, `./paths.ts`, `./jsonl.ts`, and `./discovery.ts`; only the\n// consumer-facing functions are exported here.\n\nimport { promises as fs } from \"node:fs\";\n\nimport {\n asObject,\n extractContentText,\n parseJsonObject,\n readString,\n} from \"./jsonl.js\";\nimport {\n discoverTranscriptFiles,\n readTranscriptRecord,\n resolveTranscriptFile,\n} from \"./discovery.js\";\nimport type {\n TranscriptEntry,\n TranscriptGrepOptions,\n TranscriptListOptions,\n TranscriptRecord,\n TranscriptReadOptions,\n} from \"./types.js\";\n\nexport {\n TranscriptSourceNames,\n type TranscriptEntry,\n type TranscriptGrepOptions,\n type TranscriptListOptions,\n type TranscriptReadOptions,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function listTranscriptRecords(\n options: TranscriptListOptions\n): Promise<TranscriptRecord[]> {\n const files = await discoverTranscriptFiles(options.source);\n const records = await Promise.all(files.slice(0, options.limit).map(readTranscriptRecord));\n return records;\n}\n\nexport async function readTranscriptEntries(\n options: TranscriptReadOptions\n): Promise<TranscriptEntry[]> {\n const file = await resolveTranscriptFile(options.source, options.target);\n const entries = await readEntriesFromFile(file, options.includeInternal);\n return entries.slice(Math.max(0, entries.length - options.limit));\n}\n\nexport async function grepTranscriptEntries(\n options: TranscriptGrepOptions\n): Promise<TranscriptEntry[]> {\n const files = await discoverTranscriptFiles(options.source);\n const normalizedQuery = options.query.toLowerCase();\n const matches: TranscriptEntry[] = [];\n\n for (const file of files.slice(0, options.fileLimit)) {\n const entries = await readEntriesFromFile(file, options.includeInternal);\n\n for (const entry of entries) {\n if (entry.text.toLowerCase().includes(normalizedQuery)) {\n matches.push(entry);\n }\n\n if (matches.length >= options.limit) {\n return matches;\n }\n }\n }\n\n return matches;\n}\n\nasync function readEntriesFromFile(\n record: TranscriptRecord,\n includeInternal: boolean\n): Promise<TranscriptEntry[]> {\n const content = await fs.readFile(record.filePath, \"utf8\");\n const entries: TranscriptEntry[] = [];\n\n for (const line of content.split(\"\\n\")) {\n if (!line.trim()) {\n continue;\n }\n\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n const entry =\n record.source === \"codex\"\n ? parseCodexEntry(record, parsed, includeInternal)\n : parseClaudeEntry(record, parsed, includeInternal);\n\n if (entry) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n\nfunction parseCodexEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const payload = asObject(parsed.payload);\n if (!payload || parsed.type !== \"response_item\") {\n return undefined;\n }\n\n const role = readString(payload.role);\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = extractContentText(payload.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n\nfunction parseClaudeEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = message ? extractContentText(message.content) : readString(parsed.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n","import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptRecord } from \"./types.js\";\nimport { statIfExists } from \"./paths.js\";\n\nexport async function readJsonlLines(filePath: string, limit: number): Promise<string[]> {\n const content = await fs.readFile(filePath, \"utf8\");\n return content.split(\"\\n\").filter(Boolean).slice(0, limit);\n}\n\nexport function parseJsonObject(line: string): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nexport function asObject(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nexport function readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nexport function extractContentText(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const parts = value.flatMap((item) => {\n if (typeof item === \"string\") {\n return [item];\n }\n\n const objectItem = asObject(item);\n if (!objectItem) {\n return [];\n }\n\n return [readString(objectItem.text), readString(objectItem.content)].filter(\n (part): part is string => Boolean(part)\n );\n });\n\n return parts.length > 0 ? parts.join(\"\\n\") : undefined;\n}\n\nexport async function discoverJsonlFiles(\n source: TranscriptRecord[\"source\"],\n rootPath: string\n): Promise<TranscriptRecord[]> {\n const stat = await statIfExists(rootPath);\n if (!stat) {\n return [];\n }\n\n if (stat.isFile()) {\n return [await recordFromPath(source, rootPath, stat.mtime)];\n }\n\n const records: TranscriptRecord[] = [];\n const entries = await fs.readdir(rootPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const entryPath = path.join(rootPath, entry.name);\n if (entry.isDirectory()) {\n records.push(...(await discoverJsonlFiles(source, entryPath)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n const entryStat = await fs.stat(entryPath);\n records.push(await recordFromPath(source, entryPath, entryStat.mtime));\n }\n }\n\n return records;\n}\n\nexport async function recordFromPath(\n source: TranscriptRecord[\"source\"],\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n","import os from \"node:os\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptSourceName } from \"./types.js\";\n\nexport async function statIfExists(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nexport function expandHome(value: string): string {\n if (value === \"~\") {\n return os.homedir();\n }\n if (value.startsWith(\"~/\")) {\n return path.join(os.homedir(), value.slice(2));\n }\n return value;\n}\n\nexport function detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nexport function truncateSingleLine(value: string, maxLength: number): string {\n const line = value.replace(/\\s+/g, \" \").trim();\n return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;\n}\n","import path from \"node:path\";\n\nimport {\n asObject,\n discoverJsonlFiles,\n extractContentText,\n parseJsonObject,\n readJsonlLines,\n readString,\n recordFromPath,\n} from \"./jsonl.js\";\nimport {\n detectSourceFromPath,\n expandHome,\n statIfExists,\n truncateSingleLine,\n} from \"./paths.js\";\nimport {\n TranscriptSourceNames,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function discoverTranscriptFiles(\n source: TranscriptScopeName\n): Promise<TranscriptRecord[]> {\n const sources = source === \"all\" ? TranscriptSourceNames : [source];\n const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));\n return records\n .flat()\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));\n}\n\nfunction sourceRoots(source: TranscriptSourceName): Promise<TranscriptRecord[]>[] {\n if (source === \"codex\") {\n return [\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/sessions\")),\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/archived_sessions\")),\n ];\n }\n\n return [\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/projects\")),\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/history.jsonl\")),\n ];\n}\n\nexport async function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nexport async function readMetadataFromFile(\n source: TranscriptSourceName,\n filePath: string\n): Promise<Pick<TranscriptRecord, \"cwd\" | \"sessionId\" | \"title\">> {\n const lines = await readJsonlLines(filePath, 80);\n\n for (const line of lines) {\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n if (source === \"codex\") {\n const payload = asObject(parsed.payload);\n if (parsed.type === \"session_meta\" && payload) {\n return {\n cwd: readString(payload.cwd),\n sessionId: readString(payload.session_id) ?? readString(payload.id),\n title: readString(payload.cwd),\n };\n }\n }\n\n if (source === \"claude\") {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n const content = message ? extractContentText(message.content) : readString(parsed.content);\n if (content && (role === \"user\" || role === \"assistant\")) {\n return {\n cwd: readString(parsed.cwd) ?? readString(parsed.project),\n sessionId: readString(parsed.sessionId),\n title: truncateSingleLine(content, 120),\n };\n }\n }\n }\n\n return {\n sessionId: path.basename(filePath, \".jsonl\"),\n };\n}\n\nexport async function resolveTranscriptFile(\n source: TranscriptScopeName,\n target: string\n): Promise<TranscriptRecord> {\n const expandedTarget = expandHome(target);\n const directStat = await statIfExists(expandedTarget);\n if (directStat?.isFile()) {\n return recordFromPath(detectSourceFromPath(expandedTarget), expandedTarget, directStat.mtime);\n }\n\n const files = await discoverTranscriptFiles(source);\n const normalizedTarget = target.toLowerCase();\n const match = files.find((file) => {\n return (\n file.filePath.toLowerCase().includes(normalizedTarget) ||\n file.sessionId?.toLowerCase().includes(normalizedTarget)\n );\n });\n\n if (!match) {\n throw new Error(`No transcript matched \"${target}\". Run cngkit transcripts list first.`);\n }\n\n return match;\n}\n","export const TranscriptSourceNames = [\"codex\", \"claude\"] as const;\n\nexport type TranscriptSourceName = (typeof TranscriptSourceNames)[number];\nexport type TranscriptScopeName = TranscriptSourceName | \"all\";\n\nexport type TranscriptRecord = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly updatedAt: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n readonly title?: string;\n};\n\nexport type TranscriptEntry = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly timestamp?: string;\n readonly role: string;\n readonly text: string;\n};\n\nexport type TranscriptListOptions = {\n readonly source: TranscriptScopeName;\n readonly limit: number;\n};\n\nexport type TranscriptReadOptions = {\n readonly source: TranscriptScopeName;\n readonly target: string;\n readonly limit: number;\n readonly includeInternal: boolean;\n};\n\nexport type TranscriptGrepOptions = {\n readonly source: TranscriptScopeName;\n readonly query: string;\n readonly limit: number;\n readonly fileLimit: number;\n readonly includeInternal: boolean;\n};\n\nexport type JsonObject = Record<string, unknown>;\n","import { Box, Text } from \"ink\";\n\nimport { singleLine } from \"../../shared/command-utils.js\";\nimport type { TranscriptRecord } from \"./reader.js\";\n\nexport function TranscriptRecordList({ records }: { readonly records: readonly TranscriptRecord[] }) {\n if (records.length === 0) {\n return <Text color=\"yellow\">No transcript files found.</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {records.map((record, index) => (\n <Box flexDirection=\"column\" key={`${record.source}-${record.filePath}-${index}`} marginBottom={1}>\n <Text>\n <Text color=\"cyan\">{index + 1}. </Text>\n <Text bold>{record.source}</Text>\n <Text dimColor> {record.updatedAt}</Text>\n </Text>\n <Text>{record.filePath}</Text>\n {record.sessionId ? (\n <Text dimColor>\n session {record.sessionId}\n </Text>\n ) : null}\n {record.cwd ? <Text dimColor>cwd {record.cwd}</Text> : null}\n {record.title ? <Text>{singleLine(record.title)}</Text> : null}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,cAAc;AACvB,SAAS,SAAS;;;ACDlB,SAAS,qBAAqB;;;ACI9B,SAAS,YAAYA,WAAU;;;ACJ/B,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;;;ACDjB,OAAO,QAAQ;AACf,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AAIjB,eAAsB,aAAa,UAAkB;AACnD,MAAI;AACF,WAAO,MAAM,GAAG,KAAK,QAAQ;AAAA,EAC/B,SAAS,OAAO;AACd,QAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEO,SAAS,YAAY,OAAgD;AAC1E,SAAO,iBAAiB,SAAS,UAAU;AAC7C;AAEO,SAAS,WAAW,OAAuB;AAChD,MAAI,UAAU,KAAK;AACjB,WAAO,GAAG,QAAQ;AAAA,EACpB;AACA,MAAI,MAAM,WAAW,IAAI,GAAG;AAC1B,WAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,MAAM,MAAM,CAAC,CAAC;AAAA,EAC/C;AACA,SAAO;AACT;AAEO,SAAS,qBAAqB,UAAwC;AAC3E,SAAO,SAAS,SAAS,GAAG,KAAK,GAAG,UAAU,KAAK,GAAG,EAAE,IAAI,WAAW;AACzE;AAEO,SAAS,mBAAmB,OAAe,WAA2B;AAC3E,QAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC7C,SAAO,KAAK,SAAS,YAAY,GAAG,KAAK,MAAM,GAAG,YAAY,CAAC,CAAC,QAAQ;AAC1E;;;ADhCA,eAAsB,eAAe,UAAkB,OAAkC;AACvF,QAAM,UAAU,MAAMC,IAAG,SAAS,UAAU,MAAM;AAClD,SAAO,QAAQ,MAAM,IAAI,EAAE,OAAO,OAAO,EAAE,MAAM,GAAG,KAAK;AAC3D;AAEO,SAAS,gBAAgB,MAAmD;AACjF,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,WAAO,SAAS,MAAM;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAS,OAAqD;AAC5E,MAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,WAAW,OAAoC;AAC7D,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAEO,SAAS,mBAAmB,OAAoC;AACrE,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,QAAQ,CAAC,SAAS;AACpC,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO,CAAC,IAAI;AAAA,IACd;AAEA,UAAM,aAAa,SAAS,IAAI;AAChC,QAAI,CAAC,YAAY;AACf,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,CAAC,WAAW,WAAW,IAAI,GAAG,WAAW,WAAW,OAAO,CAAC,EAAE;AAAA,MACnE,CAAC,SAAyB,QAAQ,IAAI;AAAA,IACxC;AAAA,EACF,CAAC;AAED,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAC/C;AAEA,eAAsB,mBACpB,QACA,UAC6B;AAC7B,QAAM,OAAO,MAAM,aAAa,QAAQ;AACxC,MAAI,CAAC,MAAM;AACT,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,KAAK,OAAO,GAAG;AACjB,WAAO,CAAC,MAAM,eAAe,QAAQ,UAAU,KAAK,KAAK,CAAC;AAAA,EAC5D;AAEA,QAAM,UAA8B,CAAC;AACrC,QAAM,UAAU,MAAMA,IAAG,QAAQ,UAAU,EAAE,eAAe,KAAK,CAAC;AAElE,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAYC,MAAK,KAAK,UAAU,MAAM,IAAI;AAChD,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAI,MAAM,mBAAmB,QAAQ,SAAS,CAAE;AAC7D;AAAA,IACF;AAEA,QAAI,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,QAAQ,GAAG;AACnD,YAAM,YAAY,MAAMD,IAAG,KAAK,SAAS;AACzC,cAAQ,KAAK,MAAM,eAAe,QAAQ,WAAW,UAAU,KAAK,CAAC;AAAA,IACvE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,eACpB,QACA,UACA,OAC2B;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,MAAM,YAAY;AAAA,EAC/B;AACF;;;AEpGA,OAAOE,WAAU;;;ACAV,IAAM,wBAAwB,CAAC,SAAS,QAAQ;;;ADwBvD,eAAsB,wBACpB,QAC6B;AAC7B,QAAM,UAAU,WAAW,QAAQ,wBAAwB,CAAC,MAAM;AAClE,QAAM,UAAU,MAAM,QAAQ,IAAI,QAAQ,QAAQ,CAAC,eAAe,YAAY,UAAU,CAAC,CAAC;AAC1F,SAAO,QACJ,KAAK,EACL,KAAK,CAAC,MAAM,UAAU,MAAM,UAAU,cAAc,KAAK,SAAS,CAAC;AACxE;AAEA,SAAS,YAAY,QAA6D;AAChF,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,MACL,mBAAmB,SAAS,WAAW,mBAAmB,CAAC;AAAA,MAC3D,mBAAmB,SAAS,WAAW,4BAA4B,CAAC;AAAA,IACtE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,mBAAmB,UAAU,WAAW,oBAAoB,CAAC;AAAA,IAC7D,mBAAmB,UAAU,WAAW,yBAAyB,CAAC;AAAA,EACpE;AACF;AAEA,eAAsB,qBAAqB,QAAqD;AAC9F,QAAM,WAAW,MAAM,qBAAqB,OAAO,QAAQ,OAAO,QAAQ;AAC1E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;AAEA,eAAsB,qBACpB,QACA,UACgE;AAChE,QAAM,QAAQ,MAAM,eAAe,UAAU,EAAE;AAE/C,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,gBAAgB,IAAI;AACnC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,QAAI,WAAW,SAAS;AACtB,YAAM,UAAU,SAAS,OAAO,OAAO;AACvC,UAAI,OAAO,SAAS,kBAAkB,SAAS;AAC7C,eAAO;AAAA,UACL,KAAK,WAAW,QAAQ,GAAG;AAAA,UAC3B,WAAW,WAAW,QAAQ,UAAU,KAAK,WAAW,QAAQ,EAAE;AAAA,UAClE,OAAO,WAAW,QAAQ,GAAG;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,UAAU;AACvB,YAAM,UAAU,SAAS,OAAO,OAAO;AACvC,YAAM,OAAO,WAAW,SAAS,IAAI,KAAK,WAAW,OAAO,IAAI;AAChE,YAAM,UAAU,UAAU,mBAAmB,QAAQ,OAAO,IAAI,WAAW,OAAO,OAAO;AACzF,UAAI,YAAY,SAAS,UAAU,SAAS,cAAc;AACxD,eAAO;AAAA,UACL,KAAK,WAAW,OAAO,GAAG,KAAK,WAAW,OAAO,OAAO;AAAA,UACxD,WAAW,WAAW,OAAO,SAAS;AAAA,UACtC,OAAO,mBAAmB,SAAS,GAAG;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAWC,MAAK,SAAS,UAAU,QAAQ;AAAA,EAC7C;AACF;AAEA,eAAsB,sBACpB,QACA,QAC2B;AAC3B,QAAM,iBAAiB,WAAW,MAAM;AACxC,QAAM,aAAa,MAAM,aAAa,cAAc;AACpD,MAAI,YAAY,OAAO,GAAG;AACxB,WAAO,eAAe,qBAAqB,cAAc,GAAG,gBAAgB,WAAW,KAAK;AAAA,EAC9F;AAEA,QAAM,QAAQ,MAAM,wBAAwB,MAAM;AAClD,QAAM,mBAAmB,OAAO,YAAY;AAC5C,QAAM,QAAQ,MAAM,KAAK,CAAC,SAAS;AACjC,WACE,KAAK,SAAS,YAAY,EAAE,SAAS,gBAAgB,KACrD,KAAK,WAAW,YAAY,EAAE,SAAS,gBAAgB;AAAA,EAE3D,CAAC;AAED,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0BAA0B,MAAM,uCAAuC;AAAA,EACzF;AAEA,SAAO;AACT;;;AHtFA,eAAsB,sBACpBC,UAC6B;AAC7B,QAAM,QAAQ,MAAM,wBAAwBA,SAAQ,MAAM;AAC1D,QAAM,UAAU,MAAM,QAAQ,IAAI,MAAM,MAAM,GAAGA,SAAQ,KAAK,EAAE,IAAI,oBAAoB,CAAC;AACzF,SAAO;AACT;AAEA,eAAsB,sBACpBA,UAC4B;AAC5B,QAAM,OAAO,MAAM,sBAAsBA,SAAQ,QAAQA,SAAQ,MAAM;AACvE,QAAM,UAAU,MAAM,oBAAoB,MAAMA,SAAQ,eAAe;AACvE,SAAO,QAAQ,MAAM,KAAK,IAAI,GAAG,QAAQ,SAASA,SAAQ,KAAK,CAAC;AAClE;AAEA,eAAsB,sBACpBA,UAC4B;AAC5B,QAAM,QAAQ,MAAM,wBAAwBA,SAAQ,MAAM;AAC1D,QAAM,kBAAkBA,SAAQ,MAAM,YAAY;AAClD,QAAM,UAA6B,CAAC;AAEpC,aAAW,QAAQ,MAAM,MAAM,GAAGA,SAAQ,SAAS,GAAG;AACpD,UAAM,UAAU,MAAM,oBAAoB,MAAMA,SAAQ,eAAe;AAEvE,eAAW,SAAS,SAAS;AAC3B,UAAI,MAAM,KAAK,YAAY,EAAE,SAAS,eAAe,GAAG;AACtD,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAEA,UAAI,QAAQ,UAAUA,SAAQ,OAAO;AACnC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,oBACb,QACA,iBAC4B;AAC5B,QAAM,UAAU,MAAMC,IAAG,SAAS,OAAO,UAAU,MAAM;AACzD,QAAM,UAA6B,CAAC;AAEpC,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB;AAAA,IACF;AAEA,UAAM,SAAS,gBAAgB,IAAI;AACnC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,UAAM,QACJ,OAAO,WAAW,UACd,gBAAgB,QAAQ,QAAQ,eAAe,IAC/C,iBAAiB,QAAQ,QAAQ,eAAe;AAEtD,QAAI,OAAO;AACT,cAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,gBACP,QACA,QACA,iBAC6B;AAC7B,QAAM,UAAU,SAAS,OAAO,OAAO;AACvC,MAAI,CAAC,WAAW,OAAO,SAAS,iBAAiB;AAC/C,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,WAAW,QAAQ,IAAI;AACpC,MAAI,CAAC,QAAS,CAAC,mBAAmB,SAAS,UAAU,SAAS,aAAc;AAC1E,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,mBAAmB,QAAQ,OAAO;AAC/C,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,WAAW,WAAW,OAAO,SAAS;AAAA,IACtC;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,iBACP,QACA,QACA,iBAC6B;AAC7B,QAAM,UAAU,SAAS,OAAO,OAAO;AACvC,QAAM,OAAO,WAAW,SAAS,IAAI,KAAK,WAAW,OAAO,IAAI;AAEhE,MAAI,CAAC,QAAS,CAAC,mBAAmB,SAAS,UAAU,SAAS,aAAc;AAC1E,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,UAAU,mBAAmB,QAAQ,OAAO,IAAI,WAAW,OAAO,OAAO;AACtF,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,WAAW,WAAW,OAAO,SAAS;AAAA,IACtC;AAAA,IACA;AAAA,EACF;AACF;;;AK/JA,SAAS,KAAK,YAAY;AAOf,cAQC,YARD;AAFJ,SAAS,qBAAqB,EAAE,QAAQ,GAAsD;AACnG,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,oBAAC,QAAK,OAAM,UAAS,wCAA0B;AAAA,EACxD;AAEA,SACE,oBAAC,OAAI,eAAc,UAChB,kBAAQ,IAAI,CAAC,QAAQ,UACpB,qBAAC,OAAI,eAAc,UAA8D,cAAc,GAC7F;AAAA,yBAAC,QACC;AAAA,2BAAC,QAAK,OAAM,QAAQ;AAAA,gBAAQ;AAAA,QAAE;AAAA,SAAE;AAAA,MAChC,oBAAC,QAAK,MAAI,MAAE,iBAAO,QAAO;AAAA,MAC1B,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,QAAE,OAAO;AAAA,SAAU;AAAA,OACpC;AAAA,IACA,oBAAC,QAAM,iBAAO,UAAS;AAAA,IACtB,OAAO,YACN,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MACJ,OAAO;AAAA,OAClB,IACE;AAAA,IACH,OAAO,MAAM,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MAAK,OAAO;AAAA,OAAI,IAAU;AAAA,IACtD,OAAO,QAAQ,oBAAC,QAAM,qBAAW,OAAO,KAAK,GAAE,IAAU;AAAA,OAb3B,GAAG,OAAO,MAAM,IAAI,OAAO,QAAQ,IAAI,KAAK,EAc7E,CACD,GACH;AAEJ;;;ANDA,eAAsB,qBACpBC,OACAC,UACA,QACe;AACf,QAAM,CAAC,SAAS,QAAQ,GAAG,UAAU,IAAID,SAAQ,CAAC;AAClD,QAAM,SAAS,0BAA0BC,SAAQ,MAAM;AACvD,QAAM,QAAQ,YAAYA,SAAQ,OAAO,WAAW,SAAS,KAAK,IAAI,GAAG;AACzE,QAAM,kBAAkBA,SAAQ,oBAAoB;AAEpD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,UAAU,iBAAiB,aAAa,CAAC;AAChD;AAAA,IACF,KAAK,QAAQ;AACX,YAAM,UAAU,MAAM,sBAAsB,EAAE,QAAQ,MAAM,CAAC;AAC7D,UAAI,gBAAgBA,QAAO,GAAG;AAC5B,eAAO,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC;AAClC;AAAA,MACF;AAEA,aAAO,UAAU,cAAc,sBAAsB,EAAE,QAAQ,CAAC,CAAC;AACjE;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAS,WAAW,CAAC;AAC3B,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,wEAAwE;AAAA,MAC1F;AAEA,YAAM,UAAU,MAAM,sBAAsB;AAAA,QAC1C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,UAAI,gBAAgBA,QAAO,GAAG;AAC5B,eAAO,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC;AAClC;AAAA,MACF;AAEA,aAAO,IAAI,wBAAwB,OAAO,CAAC;AAC3C;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,QAAQ,uBAAuB,UAAU;AAC/C,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,uDAAuD;AAAA,MACzE;AAEA,YAAM,UAAU,MAAM,sBAAsB;AAAA,QAC1C;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,YAAYA,SAAQ,WAAW,IAAI,GAAI;AAAA,QAClD;AAAA,MACF,CAAC;AAED,UAAI,gBAAgBA,QAAO,GAAG;AAC5B,eAAO,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC;AAClC;AAAA,MACF;AAEA,aAAO,IAAI,wBAAwB,OAAO,CAAC;AAC3C;AAAA,IACF;AAAA,IACA;AACE,YAAM,IAAI,MAAM,yEAAyE;AAAA,EAC7F;AACF;AAEA,SAAS,wBAAwB,SAAoC;AACnE,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,SAAO,QACJ,IAAI,CAAC,UAAU;AACd,UAAM,OAAO,MAAM,YAAY,IAAI,MAAM,SAAS,KAAK;AACvD,WAAO,IAAI,MAAM,MAAM,KAAK,MAAM,IAAI,GAAG,IAAI;AAAA,EAAK,MAAM,KAAK,KAAK,CAAC;AAAA,EACrE,CAAC,EACA,KAAK,MAAM;AAChB;AAEA,SAAS,0BAA0B,OAAgD;AACjF,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,YAAM,IAAI,MAAM,4DAA4D;AAAA,EAChF;AACF;;;ADnES,gBAAAC,YAAA;AAtDF,IAAM,cAAc;AACpB,IAAM,OAAO;AACb,IAAM,UAAU,oBAAoB,OAAO;AAAA,EAChD,QAAQ,EACL,KAAK,CAAC,OAAO,SAAS,QAAQ,CAAC,EAC/B,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EACF,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EACF,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EACF,iBAAiB,EACd,QAAQ,EACR,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EACF,MAAM,EACH,QAAQ,EACR,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACJ,CAAC;AAOc,SAAR,mBAAoC,EAAE,MAAAC,OAAM,SAAAC,SAAQ,GAA4B;AACrF,SAAO,gBAAAF,KAAC,iBAAc,KAAK,CAAC,WAAW,qBAAqBC,OAAMC,UAAS,MAAM,GAAG;AACtF;","names":["fs","fs","path","fs","path","path","path","options","fs","args","options","jsx","args","options"]}
|
|
1
|
+
{"version":3,"sources":["../../src/commands/transcripts.tsx","../../src/features/transcripts/run-transcript-command.ts","../../src/features/transcripts/reader.ts","../../src/features/transcripts/jsonl.ts","../../src/features/transcripts/paths.ts","../../src/features/transcripts/discovery.ts","../../src/features/transcripts/types.ts","../../src/features/transcripts/transcript-output.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, TranscriptArgsSchema } from \"../cli/options.js\";\nimport {\n runTranscriptCommand,\n type TranscriptCommandOptions,\n} from \"../features/transcripts/run-transcript-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"List, read, and grep local Claude/Codex transcript JSONL files\";\nexport const args = TranscriptArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n source: z\n .enum([\"all\", \"codex\", \"claude\"])\n .optional()\n .describe(\n option({\n description: \"Transcript source. Default: all\",\n valueDescription: \"all|codex|claude\",\n })\n ),\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum records or entries\",\n valueDescription: \"n\",\n })\n ),\n fileLimit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum recent files to scan for grep\",\n valueDescription: \"n\",\n })\n ),\n includeInternal: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Include developer/system/internal transcript entries\",\n })\n ),\n json: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Print raw JSON\",\n })\n ),\n});\n\ntype TranscriptsCommandProps = {\n readonly args: string[];\n readonly options: TranscriptCommandOptions;\n};\n\nexport default function TranscriptsCommand({ args, options }: TranscriptsCommandProps) {\n return <CommandRunner run={(output) => runTranscriptCommand(args, options, output)} />;\n}\n","import { createElement } from \"react\";\n\nimport { renderCngkitHelp } from \"../../cli/help-specs.js\";\nimport {\n coerceLimit,\n formatJson,\n optionalJoinedArgument,\n shouldPrintJson,\n type JsonOutputOptions,\n type NumberOption,\n} from \"../../shared/command-utils.js\";\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport {\n grepTranscriptEntries,\n listTranscriptRecords,\n readTranscriptEntries,\n type TranscriptEntry,\n type TranscriptScopeName,\n} from \"./reader.js\";\nimport { TranscriptRecordList } from \"./transcript-output.js\";\n\nexport type TranscriptCommandOptions = GlobalCommandOptions &\n JsonOutputOptions & {\n source?: string;\n limit?: NumberOption;\n fileLimit?: NumberOption;\n includeInternal?: boolean;\n };\n\nexport async function runTranscriptCommand(\n args: string[] | undefined,\n options: TranscriptCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [action = \"list\", ...actionArgs] = args ?? [];\n const source = normalizeTranscriptSource(options.source);\n const limit = coerceLimit(options.limit, action === \"list\" ? 12 : 80, 500);\n const includeInternal = options.includeInternal === true;\n\n switch (action) {\n case \"help\":\n output.component(renderCngkitHelp(\"transcripts\"));\n return;\n case \"list\": {\n const records = await listTranscriptRecords({ source, limit });\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ records }));\n return;\n }\n\n output.component(createElement(TranscriptRecordList, { records }));\n return;\n }\n case \"read\": {\n const target = actionArgs[0];\n if (!target) {\n throw new Error(\"Missing transcript target. Usage: cngkit transcripts read <path-or-id>\");\n }\n\n const entries = await readTranscriptEntries({\n source,\n target,\n limit,\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n case \"grep\": {\n const query = optionalJoinedArgument(actionArgs);\n if (!query) {\n throw new Error(\"Missing query. Usage: cngkit transcripts grep <query>\");\n }\n\n const entries = await grepTranscriptEntries({\n source,\n query,\n limit,\n fileLimit: coerceLimit(options.fileLimit, 60, 5000),\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n default:\n throw new Error(\"Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>\");\n }\n}\n\nfunction formatTranscriptEntries(entries: TranscriptEntry[]): string {\n if (entries.length === 0) {\n return \"No transcript entries found.\";\n }\n\n return entries\n .map((entry) => {\n const when = entry.timestamp ? ` ${entry.timestamp}` : \"\";\n return `[${entry.source}] ${entry.role}${when}\\n${entry.text.trim()}`;\n })\n .join(\"\\n\\n\");\n}\n\nfunction normalizeTranscriptSource(value: string | undefined): TranscriptScopeName {\n if (value === undefined) {\n return \"all\";\n }\n\n switch (value) {\n case \"all\":\n case \"codex\":\n case \"claude\":\n return value;\n default:\n throw new Error(\"Unknown transcript source. Use one of: all, codex, claude.\");\n }\n}\n","// Public API entrypoint for transcript reading. Internal helpers live in\n// `./types.ts`, `./paths.ts`, `./jsonl.ts`, and `./discovery.ts`; only the\n// consumer-facing functions are exported here.\n\nimport { promises as fs } from \"node:fs\";\n\nimport { asObject, extractContentText, parseJsonObject, readString } from \"./jsonl.js\";\nimport {\n discoverTranscriptFiles,\n readTranscriptRecord,\n resolveTranscriptFile,\n} from \"./discovery.js\";\nimport type {\n TranscriptEntry,\n TranscriptGrepOptions,\n TranscriptListOptions,\n TranscriptRecord,\n TranscriptReadOptions,\n} from \"./types.js\";\n\nexport {\n TranscriptSourceNames,\n type TranscriptEntry,\n type TranscriptGrepOptions,\n type TranscriptListOptions,\n type TranscriptReadOptions,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function listTranscriptRecords(\n options: TranscriptListOptions\n): Promise<TranscriptRecord[]> {\n const files = await discoverTranscriptFiles(options.source);\n const records = await Promise.all(files.slice(0, options.limit).map(readTranscriptRecord));\n return records;\n}\n\nexport async function readTranscriptEntries(\n options: TranscriptReadOptions\n): Promise<TranscriptEntry[]> {\n const file = await resolveTranscriptFile(options.source, options.target);\n const entries = await readEntriesFromFile(file, options.includeInternal);\n return entries.slice(Math.max(0, entries.length - options.limit));\n}\n\nexport async function grepTranscriptEntries(\n options: TranscriptGrepOptions\n): Promise<TranscriptEntry[]> {\n const files = await discoverTranscriptFiles(options.source);\n const normalizedQuery = options.query.toLowerCase();\n const matches: TranscriptEntry[] = [];\n\n for (const file of files.slice(0, options.fileLimit)) {\n const entries = await readEntriesFromFile(file, options.includeInternal);\n\n for (const entry of entries) {\n if (entry.text.toLowerCase().includes(normalizedQuery)) {\n matches.push(entry);\n }\n\n if (matches.length >= options.limit) {\n return matches;\n }\n }\n }\n\n return matches;\n}\n\nasync function readEntriesFromFile(\n record: TranscriptRecord,\n includeInternal: boolean\n): Promise<TranscriptEntry[]> {\n const content = await fs.readFile(record.filePath, \"utf8\");\n const entries: TranscriptEntry[] = [];\n\n for (const line of content.split(\"\\n\")) {\n if (!line.trim()) {\n continue;\n }\n\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n const entry =\n record.source === \"codex\"\n ? parseCodexEntry(record, parsed, includeInternal)\n : parseClaudeEntry(record, parsed, includeInternal);\n\n if (entry) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n\nfunction parseCodexEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const payload = asObject(parsed.payload);\n if (!payload || parsed.type !== \"response_item\") {\n return undefined;\n }\n\n const role = readString(payload.role);\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = extractContentText(payload.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n\nfunction parseClaudeEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = message ? extractContentText(message.content) : readString(parsed.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n","import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptRecord } from \"./types.js\";\nimport { statIfExists } from \"./paths.js\";\n\nexport async function readJsonlLines(filePath: string, limit: number): Promise<string[]> {\n const content = await fs.readFile(filePath, \"utf8\");\n return content.split(\"\\n\").filter(Boolean).slice(0, limit);\n}\n\nexport function parseJsonObject(line: string): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function asObject(value: unknown): Record<string, unknown> | undefined {\n return isRecord(value) ? value : undefined;\n}\n\nexport function readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nexport function extractContentText(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const parts = value.flatMap((item) => {\n if (typeof item === \"string\") {\n return [item];\n }\n\n const objectItem = asObject(item);\n if (!objectItem) {\n return [];\n }\n\n return [readString(objectItem.text), readString(objectItem.content)].filter(\n (part): part is string => Boolean(part)\n );\n });\n\n return parts.length > 0 ? parts.join(\"\\n\") : undefined;\n}\n\nexport async function discoverJsonlFiles(\n source: TranscriptRecord[\"source\"],\n rootPath: string\n): Promise<TranscriptRecord[]> {\n const stat = await statIfExists(rootPath);\n if (!stat) {\n return [];\n }\n\n if (stat.isFile()) {\n return [await recordFromPath(source, rootPath, stat.mtime)];\n }\n\n const records: TranscriptRecord[] = [];\n const entries = await fs.readdir(rootPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const entryPath = path.join(rootPath, entry.name);\n if (entry.isDirectory()) {\n records.push(...(await discoverJsonlFiles(source, entryPath)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n const entryStat = await fs.stat(entryPath);\n records.push(await recordFromPath(source, entryPath, entryStat.mtime));\n }\n }\n\n return records;\n}\n\nexport async function recordFromPath(\n source: TranscriptRecord[\"source\"],\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n","import os from \"node:os\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptSourceName } from \"./types.js\";\n\nexport async function statIfExists(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nexport function expandHome(value: string): string {\n if (value === \"~\") {\n return os.homedir();\n }\n if (value.startsWith(\"~/\")) {\n return path.join(os.homedir(), value.slice(2));\n }\n return value;\n}\n\nexport function detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nexport function truncateSingleLine(value: string, maxLength: number): string {\n const line = value.replace(/\\s+/g, \" \").trim();\n return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;\n}\n","import path from \"node:path\";\n\nimport {\n asObject,\n discoverJsonlFiles,\n extractContentText,\n parseJsonObject,\n readJsonlLines,\n readString,\n recordFromPath,\n} from \"./jsonl.js\";\nimport { detectSourceFromPath, expandHome, statIfExists, truncateSingleLine } from \"./paths.js\";\nimport {\n TranscriptSourceNames,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function discoverTranscriptFiles(\n source: TranscriptScopeName\n): Promise<TranscriptRecord[]> {\n const sources = source === \"all\" ? TranscriptSourceNames : [source];\n const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));\n return records.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));\n}\n\nfunction sourceRoots(source: TranscriptSourceName): Promise<TranscriptRecord[]>[] {\n if (source === \"codex\") {\n return [\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/sessions\")),\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/archived_sessions\")),\n ];\n }\n\n return [\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/projects\")),\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/history.jsonl\")),\n ];\n}\n\nexport async function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nexport async function readMetadataFromFile(\n source: TranscriptSourceName,\n filePath: string\n): Promise<Pick<TranscriptRecord, \"cwd\" | \"sessionId\" | \"title\">> {\n const lines = await readJsonlLines(filePath, 80);\n\n for (const line of lines) {\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n if (source === \"codex\") {\n const payload = asObject(parsed.payload);\n if (parsed.type === \"session_meta\" && payload) {\n return {\n cwd: readString(payload.cwd),\n sessionId: readString(payload.session_id) ?? readString(payload.id),\n title: readString(payload.cwd),\n };\n }\n }\n\n if (source === \"claude\") {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n const content = message ? extractContentText(message.content) : readString(parsed.content);\n if (content && (role === \"user\" || role === \"assistant\")) {\n return {\n cwd: readString(parsed.cwd) ?? readString(parsed.project),\n sessionId: readString(parsed.sessionId),\n title: truncateSingleLine(content, 120),\n };\n }\n }\n }\n\n return {\n sessionId: path.basename(filePath, \".jsonl\"),\n };\n}\n\nexport async function resolveTranscriptFile(\n source: TranscriptScopeName,\n target: string\n): Promise<TranscriptRecord> {\n const expandedTarget = expandHome(target);\n const directStat = await statIfExists(expandedTarget);\n if (directStat?.isFile()) {\n return recordFromPath(detectSourceFromPath(expandedTarget), expandedTarget, directStat.mtime);\n }\n\n const files = await discoverTranscriptFiles(source);\n const normalizedTarget = target.toLowerCase();\n const match = files.find((file) => {\n return (\n file.filePath.toLowerCase().includes(normalizedTarget) ||\n file.sessionId?.toLowerCase().includes(normalizedTarget)\n );\n });\n\n if (!match) {\n throw new Error(`No transcript matched \"${target}\". Run cngkit transcripts list first.`);\n }\n\n return match;\n}\n","export const TranscriptSourceNames = [\"codex\", \"claude\"] as const;\n\nexport type TranscriptSourceName = (typeof TranscriptSourceNames)[number];\nexport type TranscriptScopeName = TranscriptSourceName | \"all\";\n\nexport type TranscriptRecord = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly updatedAt: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n readonly title?: string;\n};\n\nexport type TranscriptEntry = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly timestamp?: string;\n readonly role: string;\n readonly text: string;\n};\n\nexport type TranscriptListOptions = {\n readonly source: TranscriptScopeName;\n readonly limit: number;\n};\n\nexport type TranscriptReadOptions = {\n readonly source: TranscriptScopeName;\n readonly target: string;\n readonly limit: number;\n readonly includeInternal: boolean;\n};\n\nexport type TranscriptGrepOptions = {\n readonly source: TranscriptScopeName;\n readonly query: string;\n readonly limit: number;\n readonly fileLimit: number;\n readonly includeInternal: boolean;\n};\n\nexport type JsonObject = Record<string, unknown>;\n","import { Box, Text } from \"ink\";\n\nimport { singleLine } from \"../../shared/command-utils.js\";\nimport type { TranscriptRecord } from \"./reader.js\";\n\nexport function TranscriptRecordList({\n records,\n}: {\n readonly records: readonly TranscriptRecord[];\n}) {\n if (records.length === 0) {\n return <Text color=\"yellow\">No transcript files found.</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {records.map((record, index) => (\n <Box\n flexDirection=\"column\"\n key={`${record.source}-${record.filePath}-${index}`}\n marginBottom={1}\n >\n <Text>\n <Text color=\"cyan\">{index + 1}. </Text>\n <Text bold>{record.source}</Text>\n <Text dimColor> {record.updatedAt}</Text>\n </Text>\n <Text>{record.filePath}</Text>\n {record.sessionId ? <Text dimColor>session {record.sessionId}</Text> : null}\n {record.cwd ? <Text dimColor>cwd {record.cwd}</Text> : null}\n {record.title ? <Text>{singleLine(record.title)}</Text> : null}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":"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","names":["fs","fs","path","fs","path","path","path","options","fs","args","options","jsx","args","options"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/hooks/hooks-targets.ts","../src/features/hooks/run-hooks-install-command.ts","../src/features/hooks/hooks-output.tsx","../src/features/hooks/hooks-install-flow.ts","../src/features/hooks/hooks-handler.ts","../src/features/hooks/hooks-document-io.ts","../src/features/hooks/hooks-schemas.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\n\nexport const hookToolIds = [\"all\", \"claude\", \"codex\"] as const;\n\nexport type HookToolId = (typeof hookToolIds)[number];\n\nexport type InstallTarget = {\n readonly tool: Exclude<HookToolId, \"all\">;\n readonly label: string;\n readonly configPath: string;\n readonly events: readonly string[];\n readonly statusMessages: boolean;\n};\n\nexport const claudeHookEvents = [\n \"SessionStart\",\n \"Setup\",\n \"InstructionsLoaded\",\n \"UserPromptSubmit\",\n \"UserPromptExpansion\",\n \"MessageDisplay\",\n \"PreToolUse\",\n \"PermissionRequest\",\n \"PermissionDenied\",\n \"PostToolUse\",\n \"PostToolUseFailure\",\n \"PostToolBatch\",\n \"Notification\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"TaskCreated\",\n \"TaskCompleted\",\n \"Stop\",\n \"StopFailure\",\n \"TeammateIdle\",\n \"ConfigChange\",\n \"CwdChanged\",\n \"FileChanged\",\n \"WorktreeCreate\",\n \"WorktreeRemove\",\n \"PreCompact\",\n \"PostCompact\",\n \"SessionEnd\",\n \"Elicitation\",\n \"ElicitationResult\",\n] as const;\n\nexport const codexHookEvents = [\n \"SessionStart\",\n \"SubagentStart\",\n \"PreToolUse\",\n \"PermissionRequest\",\n \"PostToolUse\",\n \"PreCompact\",\n \"PostCompact\",\n \"UserPromptSubmit\",\n \"SubagentStop\",\n \"Stop\",\n] as const;\n\nexport function resolveInstallTargets(tool: HookToolId): InstallTarget[] {\n const homeDirectory = os.homedir();\n const targets: InstallTarget[] = [\n {\n tool: \"claude\",\n label: \"Claude Code\",\n configPath: path.join(homeDirectory, \".claude\", \"settings.json\"),\n events: claudeHookEvents,\n statusMessages: false,\n },\n {\n tool: \"codex\",\n label: \"Codex\",\n configPath: path.join(homeDirectory, \".codex\", \"hooks.json\"),\n events: codexHookEvents,\n statusMessages: true,\n },\n ];\n\n if (tool === \"all\") {\n return targets;\n }\n\n return targets.filter((target) => target.tool === tool);\n}\n","// Public API entrypoint for hooks install/uninstall. Implementation lives in\n// `./hooks-{schemas,targets,handler,document-io,install-flow}.ts`; this file\n// just dispatches and renders the result.\n\nimport { createElement } from \"react\";\n\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport { HookTargetResults } from \"./hooks-output.js\";\nimport {\n installHooksForTarget,\n uninstallHooksForTarget,\n} from \"./hooks-install-flow.js\";\nimport { hookToolIds, resolveInstallTargets, type HookToolId } from \"./hooks-targets.js\";\n\nexport { hookToolIds };\nexport type { HookToolId } from \"./hooks-targets.js\";\n\nexport type HooksInstallCommandOptions = {\n readonly dryRun?: boolean;\n readonly tool?: HookToolId;\n};\n\nexport type HooksUninstallCommandOptions = {\n readonly dryRun?: boolean;\n readonly tool?: HookToolId;\n};\n\nexport async function runHooksInstallCommand(\n options: HooksInstallCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const targets = resolveInstallTargets(options.tool ?? \"all\");\n const results = [];\n\n for (const target of targets) {\n const result = await installHooksForTarget(target, options.dryRun === true);\n results.push(result);\n }\n\n output.component(createElement(HookTargetResults, { results }));\n}\n\nexport async function runHooksUninstallCommand(\n options: HooksUninstallCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const targets = resolveInstallTargets(options.tool ?? \"all\");\n const results = [];\n\n for (const target of targets) {\n const result = await uninstallHooksForTarget(target, options.dryRun === true);\n results.push(result);\n }\n\n output.component(createElement(HookTargetResults, { results }));\n}\n","import { Box, Text } from \"ink\";\n\nexport type HookTargetResult = {\n readonly action: string;\n readonly label: string;\n readonly configPath: string;\n readonly changedCount: number;\n readonly replacedCount?: number;\n readonly events: readonly string[];\n};\n\nexport function HookTargetResults({\n results,\n}: {\n readonly results: readonly HookTargetResult[];\n}) {\n return (\n <Box flexDirection=\"column\">\n {results.map((result) => (\n <Box flexDirection=\"column\" key={`${result.label}-${result.configPath}`} marginBottom={1}>\n <Text color=\"green\" bold>\n {result.action} {result.changedCount} {result.label} hook\n {result.changedCount === 1 ? \"\" : \"s\"}\n </Text>\n <Text>{result.configPath}</Text>\n {result.replacedCount !== undefined ? (\n <Text dimColor>replaced {result.replacedCount} existing cngkit handler(s)</Text>\n ) : null}\n <Text dimColor>events: {result.events.length > 0 ? result.events.join(\", \") : \"none\"}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { HookTargetResult } from \"./hooks-output.js\";\nimport {\n createCngkitHookHandler,\n isCngkitHookHandler,\n} from \"./hooks-handler.js\";\nimport { parseHookConfig, parseHookGroups, readJsonDocument } from \"./hooks-document-io.js\";\nimport type { HookGroup } from \"./hooks-schemas.js\";\nimport type { InstallTarget } from \"./hooks-targets.js\";\n\nexport async function installHooksForTarget(\n target: InstallTarget,\n dryRun: boolean\n): Promise<HookTargetResult> {\n const document = await readJsonDocument(target.configPath);\n const hooks = parseHookConfig(document.hooks);\n let replacedHandlers = 0;\n\n for (const eventName of target.events) {\n const result = installHookEvent(hooks[eventName], eventName, target.statusMessages);\n hooks[eventName] = result.groups;\n replacedHandlers += result.removedHandlers;\n }\n\n document.hooks = hooks;\n\n if (!dryRun) {\n await fs.mkdir(path.dirname(target.configPath), { recursive: true });\n await fs.writeFile(target.configPath, `${JSON.stringify(document, null, 2)}\\n`);\n }\n\n const action = dryRun ? \"Would install\" : \"Installed\";\n return {\n action,\n label: target.label,\n configPath: target.configPath,\n changedCount: target.events.length,\n replacedCount: replacedHandlers,\n events: target.events,\n };\n}\n\nexport async function uninstallHooksForTarget(\n target: InstallTarget,\n dryRun: boolean\n): Promise<HookTargetResult> {\n const document = await readJsonDocument(target.configPath);\n const hooks = parseHookConfig(document.hooks);\n const removedEvents: string[] = [];\n let removedHandlers = 0;\n\n for (const [eventName, groups] of Object.entries(hooks)) {\n const result = uninstallHookEvent(groups);\n if (result.removedHandlers > 0) {\n removedEvents.push(eventName);\n removedHandlers += result.removedHandlers;\n }\n hooks[eventName] = result.groups;\n }\n\n document.hooks = Object.fromEntries(\n Object.entries(hooks).filter(([, groups]) => groups.length > 0)\n );\n\n if (!dryRun) {\n await fs.mkdir(path.dirname(target.configPath), { recursive: true });\n await fs.writeFile(target.configPath, `${JSON.stringify(document, null, 2)}\\n`);\n }\n\n const action = dryRun ? \"Would remove\" : \"Removed\";\n return {\n action,\n label: target.label,\n configPath: target.configPath,\n changedCount: removedHandlers,\n events: removedEvents,\n };\n}\n\nfunction installHookEvent(\n existingGroups: HookGroup[] | undefined,\n eventName: string,\n withStatusMessage: boolean\n): {\n readonly groups: HookGroup[];\n readonly removedHandlers: number;\n} {\n const result = uninstallHookEvent(existingGroups ?? []);\n\n return {\n groups: [\n ...result.groups,\n {\n hooks: [createCngkitHookHandler(eventName, withStatusMessage)],\n },\n ],\n removedHandlers: result.removedHandlers,\n };\n}\n\nfunction uninstallHookEvent(groups: HookGroup[] | undefined): {\n readonly groups: HookGroup[];\n readonly removedHandlers: number;\n} {\n let removedHandlers = 0;\n const cleanedGroups = parseHookGroups(groups)\n .map((group) => {\n if (group.hooks === undefined) {\n return group;\n }\n\n const hooks = group.hooks.filter((handler) => {\n const keep = !isCngkitHookHandler(handler);\n if (!keep) {\n removedHandlers += 1;\n }\n return keep;\n });\n\n return { ...group, hooks };\n })\n .filter((group) => {\n return group.hooks === undefined || group.hooks.length > 0;\n });\n\n return { groups: cleanedGroups, removedHandlers };\n}\n","import type { HookHandler } from \"./hooks-schemas.js\";\n\nexport const cngkitHookCommandPrefix = \"cngkit hookify ingest --event \";\n\nexport const legacyCngkitHookCommandPrefixes = [\n \"npx --yes cngkit@latest hookify ingest --event \",\n \"npm exec cngkit@latest hookify ingest --event \",\n \"npm exec --yes cngkit@latest hookify ingest --event \",\n] as const;\n\nexport function isCngkitHookHandler(handler: HookHandler): boolean {\n const command = handler.command?.trimStart();\n if (command === undefined) {\n return false;\n }\n\n return [cngkitHookCommandPrefix, ...legacyCngkitHookCommandPrefixes].some((prefix) =>\n command.startsWith(prefix)\n );\n}\n\nexport function createCngkitHookHandler(\n eventName: string,\n withStatusMessage: boolean\n): HookHandler {\n const handler: HookHandler = {\n type: \"command\",\n command: `${cngkitHookCommandPrefix}${eventName}`,\n };\n\n if (!withStatusMessage) {\n return handler;\n }\n\n return {\n ...handler,\n statusMessage: `Forwarding ${eventName} hook to cngkit`,\n };\n}\n","import fs from \"node:fs/promises\";\n\nimport { z } from \"zod\";\n\nimport {\n AgentSettingsDocumentSchema,\n HookGroupSchema,\n type AgentSettingsDocument,\n type HookConfig,\n type HookGroup,\n} from \"./hooks-schemas.js\";\n\nexport async function readJsonDocument(filePath: string): Promise<AgentSettingsDocument> {\n if (!(await fileExists(filePath))) {\n return {};\n }\n\n const content = await fs.readFile(filePath, \"utf8\");\n return AgentSettingsDocumentSchema.parse(JSON.parse(content));\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n return fs.access(filePath).then(\n () => true,\n () => false\n );\n}\n\nexport function parseHookConfig(value: AgentSettingsDocument[\"hooks\"]): HookConfig {\n // The runtime config may be malformed by other tools; fall back to an empty\n // hooks record so callers can still mutate and rewrite it without throwing.\n return z.record(z.string(), z.array(HookGroupSchema)).catch({}).parse(value ?? {});\n}\n\nexport function parseHookGroups(value: HookGroup[] | undefined): HookGroup[] {\n return z.array(HookGroupSchema).catch([]).parse(value ?? []);\n}\n","import { z } from \"zod\";\n\nexport const HookHandlerSchema = z\n .object({\n command: z.string().optional(),\n })\n .passthrough();\n\nexport const HookGroupSchema = z\n .object({\n hooks: z.array(HookHandlerSchema).optional(),\n })\n .passthrough();\n\nexport const HooksConfigSchema = z.record(z.string(), z.array(HookGroupSchema));\n\nexport const AgentSettingsDocumentSchema = z\n .object({\n hooks: HooksConfigSchema.optional(),\n })\n .passthrough();\n\nexport type HookHandler = z.infer<typeof HookHandlerSchema>;\nexport type HookGroup = z.infer<typeof HookGroupSchema>;\nexport type HookConfig = z.infer<typeof HooksConfigSchema>;\nexport type AgentSettingsDocument = z.infer<typeof AgentSettingsDocumentSchema>;\n"],"mappings":"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","names":["fs","path","z","z","fs","path"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/knowledges/knowledges-input.ts"],"sourcesContent":["import type { CngApi } from \"@cng/client\";\n\nconst storedTopicsRoot = \"skills/knowledges/topics\";\n\nexport function normalizeAudienceId(value: string | undefined): CngApi.HarnessAudienceId | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n switch (value) {\n case \"all\":\n case \"operators\":\n case \"builders\":\n case \"researchers\":\n case \"agent-makers\":\n return value;\n default:\n throw new Error(\n `Unknown audience \"${value}\". Run cngkit knowledges audiences to see supported values.`\n );\n }\n}\n\nexport function normalizeCatalogPath(value: string): string {\n const trimmed = value.trim();\n if (trimmed === \"/\" || trimmed === \"\") {\n return storedTopicsRoot;\n }\n\n const relativePath = trimmed.replace(/^\\/+/, \"\");\n if (relativePath === storedTopicsRoot || relativePath.startsWith(`${storedTopicsRoot}/`)) {\n return relativePath;\n }\n\n const topicRelativePath = relativePath.startsWith(\"topics/\")\n ? relativePath.slice(\"topics/\".length)\n : relativePath;\n\n const [firstSegment, ...restSegments] = relativePath.split(\"/\");\n if (!firstSegment) {\n return relativePath;\n }\n\n switch (firstSegment) {\n case \"concepts\":\n case \"domains\":\n case \"formats\":\n case \"languages\":\n case \"libraries\":\n case \"patterns\":\n case \"platforms\":\n case \"procedures\":\n case \"protocols\":\n case \"tools\":\n return `${storedTopicsRoot}/${firstSegment}${restSegments.length > 0 ? `/${restSegments.join(\"/\")}` : \"\"}`;\n default:\n return `${storedTopicsRoot}/${topicRelativePath}`;\n }\n}\n\nexport function normalizeFilesystemCatalogPath(value: string): string {\n const trimmed = value.trim();\n if (trimmed === \"/\" || trimmed === \"\") {\n return storedTopicsRoot;\n }\n\n return normalizeCatalogPath(trimmed);\n}\n\nexport function formatCatalogPathForDisplay(value: string): string {\n const normalized = value.replace(/\\/+$/, \"\");\n if (normalized === storedTopicsRoot) {\n return \"/\";\n }\n\n if (normalized.startsWith(`${storedTopicsRoot}/`)) {\n return `/${normalized.slice(storedTopicsRoot.length + 1)}`;\n }\n\n return value;\n}\n\nexport function normalizeGrepMode(value: string | undefined): CngApi.HarnessGrepOutputMode {\n if (value === undefined) {\n return \"content\";\n }\n\n switch (value) {\n case \"content\":\n case \"files_with_matches\":\n case \"count\":\n return value;\n default:\n throw new Error(\"Unknown grep mode. Use one of: content, files_with_matches, count.\");\n }\n}\n"],"mappings":";AAEA,IAAM,mBAAmB;AAElB,SAAS,oBAAoB,OAAiE;AACnG,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,YAAM,IAAI;AAAA,QACR,qBAAqB,KAAK;AAAA,MAC5B;AAAA,EACJ;AACF;AAEO,SAAS,qBAAqB,OAAuB;AAC1D,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,YAAY,OAAO,YAAY,IAAI;AACrC,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,QAAQ,QAAQ,QAAQ,EAAE;AAC/C,MAAI,iBAAiB,oBAAoB,aAAa,WAAW,GAAG,gBAAgB,GAAG,GAAG;AACxF,WAAO;AAAA,EACT;AAEA,QAAM,oBAAoB,aAAa,WAAW,SAAS,IACvD,aAAa,MAAM,UAAU,MAAM,IACnC;AAEJ,QAAM,CAAC,cAAc,GAAG,YAAY,IAAI,aAAa,MAAM,GAAG;AAC9D,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,UAAQ,cAAc;AAAA,IACpB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,GAAG,gBAAgB,IAAI,YAAY,GAAG,aAAa,SAAS,IAAI,IAAI,aAAa,KAAK,GAAG,CAAC,KAAK,EAAE;AAAA,IAC1G;AACE,aAAO,GAAG,gBAAgB,IAAI,iBAAiB;AAAA,EACnD;AACF;AAEO,SAAS,+BAA+B,OAAuB;AACpE,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,YAAY,OAAO,YAAY,IAAI;AACrC,WAAO;AAAA,EACT;AAEA,SAAO,qBAAqB,OAAO;AACrC;AAEO,SAAS,4BAA4B,OAAuB;AACjE,QAAM,aAAa,MAAM,QAAQ,QAAQ,EAAE;AAC3C,MAAI,eAAe,kBAAkB;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,WAAW,GAAG,gBAAgB,GAAG,GAAG;AACjD,WAAO,IAAI,WAAW,MAAM,iBAAiB,SAAS,CAAC,CAAC;AAAA,EAC1D;AAEA,SAAO;AACT;AAEO,SAAS,kBAAkB,OAAyD;AACzF,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,YAAM,IAAI,MAAM,oEAAoE;AAAA,EACxF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/knowledges/format/content-format.ts"],"sourcesContent":["import type { CngApi } from \"@cng/client\";\n\nimport { formatJson, shouldPrintJson, type JsonOutputOptions } from \"../../../shared/command-utils.js\";\nimport type { CommandOutput } from \"../../../shared/output/types.js\";\n\nexport function outputRawContent(\n data: CngApi.HarnessReadResponse,\n options: JsonOutputOptions,\n output: CommandOutput\n): void {\n if (shouldPrintJson(options)) {\n output.raw(formatJson(data));\n return;\n }\n\n output.raw(data.content);\n}\n"],"mappings":";;;;;;AAKO,SAAS,iBACd,MACA,SACA,QACM;AACN,MAAI,gBAAgB,OAAO,GAAG;AAC5B,WAAO,IAAI,WAAW,IAAI,CAAC;AAC3B;AAAA,EACF;AAEA,SAAO,IAAI,KAAK,OAAO;AACzB;","names":[]}
|