cngkit 1.1.18 → 1.1.20
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/LICENSE +1 -1
- package/README.md +94 -19
- package/dist/chunk-CIZBVLN5.js +35 -0
- package/dist/chunk-CIZBVLN5.js.map +1 -0
- package/dist/{chunk-IB5B3BLY.js → chunk-E2GLGGKO.js} +16 -4
- package/dist/chunk-E2GLGGKO.js.map +1 -0
- package/dist/chunk-MRXGD6TC.js +42 -0
- package/dist/chunk-MRXGD6TC.js.map +1 -0
- package/dist/{chunk-ZA4YOWPB.js → chunk-NGEWD4BW.js} +2 -1
- package/dist/chunk-NODJM6SH.js +658 -0
- package/dist/chunk-NODJM6SH.js.map +1 -0
- package/dist/chunk-SKK2XLRZ.js +1590 -0
- package/dist/chunk-SKK2XLRZ.js.map +1 -0
- package/dist/chunk-SMTQ3W3F.js +271 -0
- package/dist/chunk-SMTQ3W3F.js.map +1 -0
- package/dist/{chunk-FJ34NVQ4.js → chunk-WDI43VPW.js} +578 -88
- package/dist/chunk-WDI43VPW.js.map +1 -0
- package/dist/cli.js +107 -27
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +6 -6
- package/dist/commands/coderoom/index.js.map +1 -1
- package/dist/commands/coderoom/join.js +5 -5
- package/dist/commands/coderoom/share.js +5 -5
- package/dist/commands/hookify/index.js +6 -6
- package/dist/commands/hookify/index.js.map +1 -1
- package/dist/commands/hookify/ingest.js +52 -13
- package/dist/commands/hookify/ingest.js.map +1 -1
- package/dist/commands/hooks/index.js +25 -0
- package/dist/commands/hooks/index.js.map +1 -0
- package/dist/commands/hooks/install.js +40 -0
- package/dist/commands/hooks/install.js.map +1 -0
- package/dist/commands/hooks/uninstall.js +40 -0
- package/dist/commands/hooks/uninstall.js.map +1 -0
- package/dist/commands/index.js +5 -5
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/knowledges/audiences.js +6 -6
- package/dist/commands/knowledges/cat.js +31 -0
- package/dist/commands/knowledges/cat.js.map +1 -0
- package/dist/commands/knowledges/files.js +6 -6
- package/dist/commands/knowledges/find.js +66 -0
- package/dist/commands/knowledges/find.js.map +1 -0
- package/dist/commands/knowledges/glob.js +6 -6
- package/dist/commands/knowledges/grep.js +6 -6
- package/dist/commands/knowledges/head.js +41 -0
- package/dist/commands/knowledges/head.js.map +1 -0
- package/dist/commands/knowledges/index.js +6 -6
- package/dist/commands/knowledges/index.js.map +1 -1
- package/dist/commands/knowledges/list.js +7 -7
- package/dist/commands/knowledges/list.js.map +1 -1
- package/dist/commands/knowledges/ls.js +16 -7
- package/dist/commands/knowledges/ls.js.map +1 -1
- package/dist/commands/knowledges/read.js +6 -6
- package/dist/commands/knowledges/realpath.js +31 -0
- package/dist/commands/knowledges/realpath.js.map +1 -0
- package/dist/commands/knowledges/search.js +6 -6
- package/dist/commands/knowledges/stat.js +31 -0
- package/dist/commands/knowledges/stat.js.map +1 -0
- package/dist/commands/knowledges/status.js +6 -6
- package/dist/commands/knowledges/tail.js +41 -0
- package/dist/commands/knowledges/tail.js.map +1 -0
- package/dist/commands/knowledges/tree.js +46 -0
- package/dist/commands/knowledges/tree.js.map +1 -0
- package/dist/commands/login.js +4 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scrub.js +38 -15
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +44 -24
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +3 -4
- package/dist/chunk-C7HFDK4S.js +0 -393
- package/dist/chunk-C7HFDK4S.js.map +0 -1
- package/dist/chunk-CBIVTEZP.js +0 -222
- package/dist/chunk-CBIVTEZP.js.map +0 -1
- package/dist/chunk-FJ34NVQ4.js.map +0 -1
- package/dist/chunk-IB5B3BLY.js.map +0 -1
- package/dist/chunk-KSW6QT5Q.js +0 -628
- package/dist/chunk-KSW6QT5Q.js.map +0 -1
- package/dist/chunk-TWQDLZ6F.js +0 -26
- package/dist/chunk-TWQDLZ6F.js.map +0 -1
- /package/dist/{chunk-ZA4YOWPB.js.map → chunk-NGEWD4BW.js.map} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runKnowTailCommand
|
|
3
|
+
} from "../../chunk-NODJM6SH.js";
|
|
4
|
+
import "../../chunk-XQGLUQFM.js";
|
|
5
|
+
import "../../chunk-SKK2XLRZ.js";
|
|
6
|
+
import "../../chunk-CIZBVLN5.js";
|
|
7
|
+
import {
|
|
8
|
+
JsonOutputOptionsSchema,
|
|
9
|
+
RequiredFilePathArgsSchema
|
|
10
|
+
} from "../../chunk-NGEWD4BW.js";
|
|
11
|
+
import {
|
|
12
|
+
CommandRunner
|
|
13
|
+
} from "../../chunk-MRXGD6TC.js";
|
|
14
|
+
import "../../chunk-WDI43VPW.js";
|
|
15
|
+
import "../../chunk-PZ5AY32C.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/knowledges/tail.tsx
|
|
18
|
+
import { option } from "pastel";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { jsx } from "react/jsx-runtime";
|
|
21
|
+
var description = "Print the last lines of a remote catalog file";
|
|
22
|
+
var args = RequiredFilePathArgsSchema;
|
|
23
|
+
var options = JsonOutputOptionsSchema.extend({
|
|
24
|
+
lines: z.number().optional().describe(
|
|
25
|
+
option({
|
|
26
|
+
alias: "n",
|
|
27
|
+
description: "Number of lines",
|
|
28
|
+
valueDescription: "n"
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
});
|
|
32
|
+
function TailCommand({ args: args2, options: options2 }) {
|
|
33
|
+
return /* @__PURE__ */ jsx(CommandRunner, { run: (output) => runKnowTailCommand(args2[0], options2, output) });
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
args,
|
|
37
|
+
TailCommand as default,
|
|
38
|
+
description,
|
|
39
|
+
options
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=tail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/knowledges/tail.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { JsonOutputOptionsSchema, RequiredFilePathArgsSchema } from \"../../cli/options.js\";\nimport { runKnowTailCommand, type KnowTailCommandOptions } from \"../../features/knowledges/run-knowledges-command.js\";\nimport { CommandRunner } from \"../../cli/command-runner.js\";\n\nexport const description = \"Print the last lines of a remote catalog file\";\nexport const args = RequiredFilePathArgsSchema;\nexport const options = JsonOutputOptionsSchema.extend({\n lines: z\n .number()\n .optional()\n .describe(\n option({\n alias: \"n\",\n description: \"Number of lines\",\n valueDescription: \"n\",\n })\n ),\n});\n\ntype TailCommandProps = {\n readonly args: [string];\n readonly options: KnowTailCommandOptions;\n};\n\nexport default function TailCommand({ args, options }: TailCommandProps) {\n return <CommandRunner run={(output) => runKnowTailCommand(args[0], options, output)} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAS,cAAc;AACvB,SAAS,SAAS;AA2BT;AArBF,IAAM,cAAc;AACpB,IAAM,OAAO;AACb,IAAM,UAAU,wBAAwB,OAAO;AAAA,EACpD,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,OAAO;AAAA,MACP,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AACJ,CAAC;AAOc,SAAR,YAA6B,EAAE,MAAAA,OAAM,SAAAC,SAAQ,GAAqB;AACvE,SAAO,oBAAC,iBAAc,KAAK,CAAC,WAAW,mBAAmBD,MAAK,CAAC,GAAGC,UAAS,MAAM,GAAG;AACvF;","names":["args","options"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runKnowTreeCommand
|
|
3
|
+
} from "../../chunk-NODJM6SH.js";
|
|
4
|
+
import "../../chunk-XQGLUQFM.js";
|
|
5
|
+
import "../../chunk-SKK2XLRZ.js";
|
|
6
|
+
import "../../chunk-CIZBVLN5.js";
|
|
7
|
+
import {
|
|
8
|
+
JsonOutputOptionsSchema,
|
|
9
|
+
OptionalPathArgsSchema
|
|
10
|
+
} from "../../chunk-NGEWD4BW.js";
|
|
11
|
+
import {
|
|
12
|
+
CommandRunner
|
|
13
|
+
} from "../../chunk-MRXGD6TC.js";
|
|
14
|
+
import "../../chunk-WDI43VPW.js";
|
|
15
|
+
import "../../chunk-PZ5AY32C.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/knowledges/tree.tsx
|
|
18
|
+
import { option } from "pastel";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { jsx } from "react/jsx-runtime";
|
|
21
|
+
var description = "Print a remote catalog directory tree";
|
|
22
|
+
var args = OptionalPathArgsSchema;
|
|
23
|
+
var options = JsonOutputOptionsSchema.extend({
|
|
24
|
+
depth: z.number().optional().describe(
|
|
25
|
+
option({
|
|
26
|
+
description: "Maximum tree depth",
|
|
27
|
+
valueDescription: "n"
|
|
28
|
+
})
|
|
29
|
+
),
|
|
30
|
+
limit: z.number().optional().describe(
|
|
31
|
+
option({
|
|
32
|
+
description: "Maximum entries",
|
|
33
|
+
valueDescription: "n"
|
|
34
|
+
})
|
|
35
|
+
)
|
|
36
|
+
});
|
|
37
|
+
function TreeCommand({ args: args2, options: options2 }) {
|
|
38
|
+
return /* @__PURE__ */ jsx(CommandRunner, { run: (output) => runKnowTreeCommand(args2[0], options2, output) });
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
args,
|
|
42
|
+
TreeCommand as default,
|
|
43
|
+
description,
|
|
44
|
+
options
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/knowledges/tree.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { JsonOutputOptionsSchema, OptionalPathArgsSchema } from \"../../cli/options.js\";\nimport { runKnowTreeCommand, type KnowTreeCommandOptions } from \"../../features/knowledges/run-knowledges-command.js\";\nimport { CommandRunner } from \"../../cli/command-runner.js\";\n\nexport const description = \"Print a remote catalog directory tree\";\nexport const args = OptionalPathArgsSchema;\nexport const options = JsonOutputOptionsSchema.extend({\n depth: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum tree depth\",\n valueDescription: \"n\",\n })\n ),\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum entries\",\n valueDescription: \"n\",\n })\n ),\n});\n\ntype TreeCommandProps = {\n readonly args: [string?];\n readonly options: KnowTreeCommandOptions;\n};\n\nexport default function TreeCommand({ args, options }: TreeCommandProps) {\n return <CommandRunner run={(output) => runKnowTreeCommand(args[0], options, output)} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAS,cAAc;AACvB,SAAS,SAAS;AAmCT;AA7BF,IAAM,cAAc;AACpB,IAAM,OAAO;AACb,IAAM,UAAU,wBAAwB,OAAO;AAAA,EACpD,OAAO,EACJ,OAAO,EACP,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;AACJ,CAAC;AAOc,SAAR,YAA6B,EAAE,MAAAA,OAAM,SAAAC,SAAQ,GAAqB;AACvE,SAAO,oBAAC,iBAAc,KAAK,CAAC,WAAW,mBAAmBD,MAAK,CAAC,GAAGC,UAAS,MAAM,GAAG;AACvF;","names":["args","options"]}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GlobalOptionsSchema
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-NGEWD4BW.js";
|
|
4
4
|
import {
|
|
5
5
|
CommandRunner
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-MRXGD6TC.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveApiBaseUrl
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-WDI43VPW.js";
|
|
10
10
|
import "../chunk-PZ5AY32C.js";
|
|
11
11
|
|
|
12
12
|
// src/shared/browser.ts
|
|
@@ -55,7 +55,7 @@ async function runLoginCommand(options2, output) {
|
|
|
55
55
|
|
|
56
56
|
// src/commands/login.tsx
|
|
57
57
|
import { jsx } from "react/jsx-runtime";
|
|
58
|
-
var description = "Open
|
|
58
|
+
var description = "Open browser login";
|
|
59
59
|
var options = GlobalOptionsSchema;
|
|
60
60
|
function LoginCommand({ options: options2 }) {
|
|
61
61
|
return /* @__PURE__ */ jsx(CommandRunner, { run: (output) => runLoginCommand(options2, output) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/shared/browser.ts","../../src/features/login/run-login-command.ts","../../src/commands/login.tsx"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport process from \"node:process\";\n\nexport async function openBrowserUrl(url: string): Promise<boolean> {\n const command = browserOpenCommand(url);\n\n return await new Promise<boolean>((resolve, reject) => {\n const childProcess = spawn(command.command, command.args, {\n detached: true,\n stdio: \"ignore\",\n });\n\n childProcess.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n resolve(false);\n return;\n }\n\n reject(error);\n });\n childProcess.on(\"spawn\", () => {\n childProcess.unref();\n resolve(true);\n });\n });\n}\n\nfunction browserOpenCommand(url: string): { command: string; args: string[] } {\n if (process.platform === \"darwin\") {\n return { command: \"open\", args: [url] };\n }\n\n if (process.platform === \"win32\") {\n return { command: \"cmd\", args: [\"/c\", \"start\", \"\", url] };\n }\n\n return { command: \"xdg-open\", args: [url] };\n}\n","import { openBrowserUrl } from \"../../shared/browser.js\";\nimport { resolveApiBaseUrl, type GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\n\nexport type LoginCommandOptions = GlobalCommandOptions;\n\nexport async function runLoginCommand(\n options: LoginCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const loginUrl = new URL(\"/login\", resolveApiBaseUrl(options));\n const loginUrlString = loginUrl.toString();\n output.success(`Opening ${loginUrlString}`);\n\n const didOpenBrowser = await openBrowserUrl(loginUrlString);\n if (!didOpenBrowser) {\n output.warning(`No browser opener found. Open this URL manually: ${loginUrlString}`);\n }\n}\n","import { GlobalOptionsSchema } from \"../cli/options.js\";\nimport { runLoginCommand, type LoginCommandOptions } from \"../features/login/run-login-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"Open
|
|
1
|
+
{"version":3,"sources":["../../src/shared/browser.ts","../../src/features/login/run-login-command.ts","../../src/commands/login.tsx"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport process from \"node:process\";\n\nexport async function openBrowserUrl(url: string): Promise<boolean> {\n const command = browserOpenCommand(url);\n\n return await new Promise<boolean>((resolve, reject) => {\n const childProcess = spawn(command.command, command.args, {\n detached: true,\n stdio: \"ignore\",\n });\n\n childProcess.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n resolve(false);\n return;\n }\n\n reject(error);\n });\n childProcess.on(\"spawn\", () => {\n childProcess.unref();\n resolve(true);\n });\n });\n}\n\nfunction browserOpenCommand(url: string): { command: string; args: string[] } {\n if (process.platform === \"darwin\") {\n return { command: \"open\", args: [url] };\n }\n\n if (process.platform === \"win32\") {\n return { command: \"cmd\", args: [\"/c\", \"start\", \"\", url] };\n }\n\n return { command: \"xdg-open\", args: [url] };\n}\n","import { openBrowserUrl } from \"../../shared/browser.js\";\nimport { resolveApiBaseUrl, type GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\n\nexport type LoginCommandOptions = GlobalCommandOptions;\n\nexport async function runLoginCommand(\n options: LoginCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const loginUrl = new URL(\"/login\", resolveApiBaseUrl(options));\n const loginUrlString = loginUrl.toString();\n output.success(`Opening ${loginUrlString}`);\n\n const didOpenBrowser = await openBrowserUrl(loginUrlString);\n if (!didOpenBrowser) {\n output.warning(`No browser opener found. Open this URL manually: ${loginUrlString}`);\n }\n}\n","import { GlobalOptionsSchema } from \"../cli/options.js\";\nimport { runLoginCommand, type LoginCommandOptions } from \"../features/login/run-login-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"Open browser login\";\nexport const options = GlobalOptionsSchema;\n\ntype LoginCommandProps = {\n readonly options: LoginCommandOptions;\n};\n\nexport default function LoginCommand({ options }: LoginCommandProps) {\n return <CommandRunner run={(output) => runLoginCommand(options, output)} />;\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,OAAO,aAAa;AAEpB,eAAsB,eAAe,KAA+B;AAClE,QAAM,UAAU,mBAAmB,GAAG;AAEtC,SAAO,MAAM,IAAI,QAAiB,CAAC,SAAS,WAAW;AACrD,UAAM,eAAe,MAAM,QAAQ,SAAS,QAAQ,MAAM;AAAA,MACxD,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAED,iBAAa,GAAG,SAAS,CAAC,UAAiC;AACzD,UAAI,MAAM,SAAS,UAAU;AAC3B,gBAAQ,KAAK;AACb;AAAA,MACF;AAEA,aAAO,KAAK;AAAA,IACd,CAAC;AACD,iBAAa,GAAG,SAAS,MAAM;AAC7B,mBAAa,MAAM;AACnB,cAAQ,IAAI;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,mBAAmB,KAAkD;AAC5E,MAAI,QAAQ,aAAa,UAAU;AACjC,WAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,GAAG,EAAE;AAAA,EACxC;AAEA,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,EAAE,SAAS,OAAO,MAAM,CAAC,MAAM,SAAS,IAAI,GAAG,EAAE;AAAA,EAC1D;AAEA,SAAO,EAAE,SAAS,YAAY,MAAM,CAAC,GAAG,EAAE;AAC5C;;;AC/BA,eAAsB,gBACpBA,UACA,QACe;AACf,QAAM,WAAW,IAAI,IAAI,UAAU,kBAAkBA,QAAO,CAAC;AAC7D,QAAM,iBAAiB,SAAS,SAAS;AACzC,SAAO,QAAQ,WAAW,cAAc,EAAE;AAE1C,QAAM,iBAAiB,MAAM,eAAe,cAAc;AAC1D,MAAI,CAAC,gBAAgB;AACnB,WAAO,QAAQ,oDAAoD,cAAc,EAAE;AAAA,EACrF;AACF;;;ACNS;AARF,IAAM,cAAc;AACpB,IAAM,UAAU;AAMR,SAAR,aAA8B,EAAE,SAAAC,SAAQ,GAAsB;AACnE,SAAO,oBAAC,iBAAc,KAAK,CAAC,WAAW,gBAAgBA,UAAS,MAAM,GAAG;AAC3E;","names":["options","options"]}
|
package/dist/commands/scrub.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GlobalOptionsSchema,
|
|
3
3
|
OptionalPathArgsSchema
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-NGEWD4BW.js";
|
|
5
5
|
import {
|
|
6
6
|
CommandRunner
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-MRXGD6TC.js";
|
|
8
|
+
import "../chunk-WDI43VPW.js";
|
|
9
9
|
import "../chunk-PZ5AY32C.js";
|
|
10
10
|
|
|
11
11
|
// src/commands/scrub.tsx
|
|
@@ -14,6 +14,7 @@ import { z } from "zod";
|
|
|
14
14
|
|
|
15
15
|
// src/features/scrub/run-scrub-command.ts
|
|
16
16
|
import process from "process";
|
|
17
|
+
import { createElement } from "react";
|
|
17
18
|
|
|
18
19
|
// src/features/scrub/masker.ts
|
|
19
20
|
import fs from "fs/promises";
|
|
@@ -99,6 +100,37 @@ function sanitizePlaceholderPart(value) {
|
|
|
99
100
|
return value.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "Unknown";
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
// src/features/scrub/scrub-output.tsx
|
|
104
|
+
import { Box, Text } from "ink";
|
|
105
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
106
|
+
function ScrubFindingsReport({ findings }) {
|
|
107
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
108
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
|
|
109
|
+
"Found ",
|
|
110
|
+
findings.length,
|
|
111
|
+
" possible secret",
|
|
112
|
+
findings.length === 1 ? "" : "s",
|
|
113
|
+
":"
|
|
114
|
+
] }),
|
|
115
|
+
findings.map((finding, index) => {
|
|
116
|
+
const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;
|
|
117
|
+
const status = finding.verified ? "verified" : "unverified";
|
|
118
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
119
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "- " }),
|
|
120
|
+
/* @__PURE__ */ jsx(Text, { children: location }),
|
|
121
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
122
|
+
" ",
|
|
123
|
+
finding.detectorName
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsxs(Text, { color: finding.verified ? "green" : "yellow", children: [
|
|
126
|
+
" ",
|
|
127
|
+
status
|
|
128
|
+
] })
|
|
129
|
+
] }, `${finding.filePath}-${finding.line ?? 0}-${finding.detectorName}-${index}`);
|
|
130
|
+
})
|
|
131
|
+
] });
|
|
132
|
+
}
|
|
133
|
+
|
|
102
134
|
// src/features/scrub/trufflehog.ts
|
|
103
135
|
import { spawn } from "child_process";
|
|
104
136
|
import path2 from "path";
|
|
@@ -237,7 +269,7 @@ async function runScrubCommand(targetPath, options2, output, dependencies) {
|
|
|
237
269
|
output.success("No secrets found.");
|
|
238
270
|
return;
|
|
239
271
|
}
|
|
240
|
-
output.
|
|
272
|
+
output.component(createElement(ScrubFindingsReport, { findings }));
|
|
241
273
|
if (!options2.yes) {
|
|
242
274
|
if (options2.mask) {
|
|
243
275
|
throw new Error("Inline scrubbing rewrites files. Re-run with --yes to continue.");
|
|
@@ -253,18 +285,9 @@ async function runScrubCommand(targetPath, options2, output, dependencies) {
|
|
|
253
285
|
output.warning(`Skipped ${result.skippedFindings} finding(s) outside the scrub root.`);
|
|
254
286
|
}
|
|
255
287
|
}
|
|
256
|
-
function formatScrubReport(findings) {
|
|
257
|
-
const lines = [`Found ${findings.length} possible secret${findings.length === 1 ? "" : "s"}:`];
|
|
258
|
-
for (const finding of findings) {
|
|
259
|
-
const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;
|
|
260
|
-
const status = finding.verified ? "verified" : "unverified";
|
|
261
|
-
lines.push(`- ${location} ${finding.detectorName} ${status}`);
|
|
262
|
-
}
|
|
263
|
-
return lines.join("\n");
|
|
264
|
-
}
|
|
265
288
|
|
|
266
289
|
// src/commands/scrub.tsx
|
|
267
|
-
import { jsx } from "react/jsx-runtime";
|
|
290
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
268
291
|
var description = "Scan for secrets with TruffleHog and optionally mask them inline";
|
|
269
292
|
var args = OptionalPathArgsSchema;
|
|
270
293
|
var options = GlobalOptionsSchema.extend({
|
|
@@ -280,7 +303,7 @@ var options = GlobalOptionsSchema.extend({
|
|
|
280
303
|
)
|
|
281
304
|
});
|
|
282
305
|
function ScrubCommand({ args: args2, options: options2 }) {
|
|
283
|
-
return /* @__PURE__ */
|
|
306
|
+
return /* @__PURE__ */ jsx2(CommandRunner, { run: (output) => runScrubCommand(args2[0], options2, output) });
|
|
284
307
|
}
|
|
285
308
|
export {
|
|
286
309
|
args,
|
|
@@ -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/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\";\n\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\nimport type { ScrubFinding } from \"./findings.js\";\nimport { scrubFindingsInline } from \"./masker.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.warning(formatScrubReport(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\nfunction formatScrubReport(findings: ScrubFinding[]): string {\n const lines = [`Found ${findings.length} possible secret${findings.length === 1 ? \"\" : \"s\"}:`];\n\n for (const finding of findings) {\n const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;\n const status = finding.verified ? \"verified\" : \"unverified\";\n lines.push(`- ${location} ${finding.detectorName} ${status}`);\n }\n\n return lines.join(\"\\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 { 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","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.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"]}
|
|
@@ -6,22 +6,25 @@ import {
|
|
|
6
6
|
singleLine
|
|
7
7
|
} from "../chunk-XQGLUQFM.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
} from "../chunk-
|
|
9
|
+
renderCngkitHelp
|
|
10
|
+
} from "../chunk-SKK2XLRZ.js";
|
|
11
11
|
import {
|
|
12
12
|
GlobalOptionsSchema,
|
|
13
13
|
TranscriptArgsSchema
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-NGEWD4BW.js";
|
|
15
15
|
import {
|
|
16
16
|
CommandRunner
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-MRXGD6TC.js";
|
|
18
|
+
import "../chunk-WDI43VPW.js";
|
|
19
19
|
import "../chunk-PZ5AY32C.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/transcripts.tsx
|
|
22
22
|
import { option } from "pastel";
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
|
|
25
|
+
// src/features/transcripts/run-transcript-command.ts
|
|
26
|
+
import { createElement } from "react";
|
|
27
|
+
|
|
25
28
|
// src/features/transcripts/reader.ts
|
|
26
29
|
import { promises as fs } from "fs";
|
|
27
30
|
import os from "os";
|
|
@@ -287,6 +290,38 @@ function truncateSingleLine(value, maxLength) {
|
|
|
287
290
|
return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;
|
|
288
291
|
}
|
|
289
292
|
|
|
293
|
+
// src/features/transcripts/transcript-output.tsx
|
|
294
|
+
import { Box, Text } from "ink";
|
|
295
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
296
|
+
function TranscriptRecordList({ records }) {
|
|
297
|
+
if (records.length === 0) {
|
|
298
|
+
return /* @__PURE__ */ jsx(Text, { color: "yellow", children: "No transcript files found." });
|
|
299
|
+
}
|
|
300
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: records.map((record, index) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
301
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
302
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
303
|
+
index + 1,
|
|
304
|
+
". "
|
|
305
|
+
] }),
|
|
306
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: record.source }),
|
|
307
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
308
|
+
" ",
|
|
309
|
+
record.updatedAt
|
|
310
|
+
] })
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsx(Text, { children: record.filePath }),
|
|
313
|
+
record.sessionId ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
314
|
+
"session ",
|
|
315
|
+
record.sessionId
|
|
316
|
+
] }) : null,
|
|
317
|
+
record.cwd ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
318
|
+
"cwd ",
|
|
319
|
+
record.cwd
|
|
320
|
+
] }) : null,
|
|
321
|
+
record.title ? /* @__PURE__ */ jsx(Text, { children: singleLine(record.title) }) : null
|
|
322
|
+
] }, `${record.source}-${record.filePath}-${index}`)) });
|
|
323
|
+
}
|
|
324
|
+
|
|
290
325
|
// src/features/transcripts/run-transcript-command.ts
|
|
291
326
|
async function runTranscriptCommand(args2, options2, output) {
|
|
292
327
|
const [action = "list", ...actionArgs] = args2 ?? [];
|
|
@@ -295,7 +330,7 @@ async function runTranscriptCommand(args2, options2, output) {
|
|
|
295
330
|
const includeInternal = options2.includeInternal === true;
|
|
296
331
|
switch (action) {
|
|
297
332
|
case "help":
|
|
298
|
-
output.
|
|
333
|
+
output.component(renderCngkitHelp("transcripts"));
|
|
299
334
|
return;
|
|
300
335
|
case "list": {
|
|
301
336
|
const records = await listTranscriptRecords({ source, limit });
|
|
@@ -303,7 +338,7 @@ async function runTranscriptCommand(args2, options2, output) {
|
|
|
303
338
|
output.raw(formatJson({ records }));
|
|
304
339
|
return;
|
|
305
340
|
}
|
|
306
|
-
output.
|
|
341
|
+
output.component(createElement(TranscriptRecordList, { records }));
|
|
307
342
|
return;
|
|
308
343
|
}
|
|
309
344
|
case "read": {
|
|
@@ -347,21 +382,6 @@ async function runTranscriptCommand(args2, options2, output) {
|
|
|
347
382
|
throw new Error("Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>");
|
|
348
383
|
}
|
|
349
384
|
}
|
|
350
|
-
function formatTranscriptRecords(records) {
|
|
351
|
-
if (records.length === 0) {
|
|
352
|
-
return "No transcript files found.";
|
|
353
|
-
}
|
|
354
|
-
return records.map((record, index) => {
|
|
355
|
-
const title = record.title ? `
|
|
356
|
-
${singleLine(record.title)}` : "";
|
|
357
|
-
const cwd = record.cwd ? `
|
|
358
|
-
cwd ${record.cwd}` : "";
|
|
359
|
-
const session = record.sessionId ? `
|
|
360
|
-
session ${record.sessionId}` : "";
|
|
361
|
-
return `${index + 1}. ${record.source} ${record.updatedAt}
|
|
362
|
-
${record.filePath}${session}${cwd}${title}`;
|
|
363
|
-
}).join("\n");
|
|
364
|
-
}
|
|
365
385
|
function formatTranscriptEntries(entries) {
|
|
366
386
|
if (entries.length === 0) {
|
|
367
387
|
return "No transcript entries found.";
|
|
@@ -387,7 +407,7 @@ function normalizeTranscriptSource(value) {
|
|
|
387
407
|
}
|
|
388
408
|
|
|
389
409
|
// src/commands/transcripts.tsx
|
|
390
|
-
import { jsx } from "react/jsx-runtime";
|
|
410
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
391
411
|
var description = "List, read, and grep local Claude/Codex transcript JSONL files";
|
|
392
412
|
var args = TranscriptArgsSchema;
|
|
393
413
|
var options = GlobalOptionsSchema.extend({
|
|
@@ -421,7 +441,7 @@ var options = GlobalOptionsSchema.extend({
|
|
|
421
441
|
)
|
|
422
442
|
});
|
|
423
443
|
function TranscriptsCommand({ args: args2, options: options2 }) {
|
|
424
|
-
return /* @__PURE__ */
|
|
444
|
+
return /* @__PURE__ */ jsx2(CommandRunner, { run: (output) => runTranscriptCommand(args2, options2, output) });
|
|
425
445
|
}
|
|
426
446
|
export {
|
|
427
447
|
args,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/transcripts.tsx","../../src/features/transcripts/reader.ts","../../src/features/transcripts/run-transcript-command.ts"],"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 { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport 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\ntype JsonObject = Record<string, unknown>;\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 discoverTranscriptFiles(source: TranscriptScopeName): 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\nasync function discoverJsonlFiles(\n source: TranscriptSourceName,\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\nasync function recordFromPath(\n source: TranscriptSourceName,\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n\nasync function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nasync 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\nasync 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\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: JsonObject,\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: JsonObject,\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\nfunction 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\nasync 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\nasync 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\nfunction parseJsonObject(line: string): JsonObject | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nfunction asObject(value: unknown): JsonObject | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as JsonObject;\n }\n return undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nfunction 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\nfunction detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nfunction 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 { formatCngkitHelp } from \"../../cli/help-specs.js\";\nimport {\n coerceLimit,\n formatJson,\n optionalJoinedArgument,\n shouldPrintJson,\n singleLine,\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.js\";\nimport {\n grepTranscriptEntries,\n listTranscriptRecords,\n readTranscriptEntries,\n type TranscriptEntry,\n type TranscriptRecord,\n type TranscriptScopeName,\n} from \"./reader.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.markdown(formatCngkitHelp(\"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.info(formatTranscriptRecords(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 formatTranscriptRecords(records: TranscriptRecord[]): string {\n if (records.length === 0) {\n return \"No transcript files found.\";\n }\n\n return records\n .map((record, index) => {\n const title = record.title ? `\\n ${singleLine(record.title)}` : \"\";\n const cwd = record.cwd ? `\\n cwd ${record.cwd}` : \"\";\n const session = record.sessionId ? `\\n session ${record.sessionId}` : \"\";\n return `${index + 1}. ${record.source} ${record.updatedAt}\\n ${record.filePath}${session}${cwd}${title}`;\n })\n .join(\"\\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"],"mappings":"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","names":["options","args","options","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/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.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","import { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport 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\ntype JsonObject = Record<string, unknown>;\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 discoverTranscriptFiles(source: TranscriptScopeName): 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\nasync function discoverJsonlFiles(\n source: TranscriptSourceName,\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\nasync function recordFromPath(\n source: TranscriptSourceName,\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n\nasync function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nasync 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\nasync 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\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: JsonObject,\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: JsonObject,\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\nfunction 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\nasync 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\nasync 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\nfunction parseJsonObject(line: string): JsonObject | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nfunction asObject(value: unknown): JsonObject | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as JsonObject;\n }\n return undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nfunction 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\nfunction detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nfunction 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 { 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;;;ACA9B,SAAS,YAAY,UAAU;AAC/B,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,IAAM,wBAAwB,CAAC,SAAS,QAAQ;AA4CvD,eAAsB,sBACpBA,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,wBAAwB,QAA0D;AAC/F,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,eAAe,mBACb,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,MAAM,GAAG,QAAQ,UAAU,EAAE,eAAe,KAAK,CAAC;AAElE,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAY,KAAK,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,MAAM,GAAG,KAAK,SAAS;AACzC,cAAQ,KAAK,MAAM,eAAe,QAAQ,WAAW,UAAU,KAAK,CAAC;AAAA,IACvE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,eACb,QACA,UACA,OAC2B;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,MAAM,YAAY;AAAA,EAC/B;AACF;AAEA,eAAe,qBAAqB,QAAqD;AACvF,QAAM,WAAW,MAAM,qBAAqB,OAAO,QAAQ,OAAO,QAAQ;AAC1E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;AAEA,eAAe,qBACb,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,WAAW,KAAK,SAAS,UAAU,QAAQ;AAAA,EAC7C;AACF;AAEA,eAAe,sBACb,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;AAEA,eAAe,oBACb,QACA,iBAC4B;AAC5B,QAAM,UAAU,MAAM,GAAG,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;AAEA,SAAS,mBAAmB,OAAoC;AAC9D,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,eAAe,eAAe,UAAkB,OAAkC;AAChF,QAAM,UAAU,MAAM,GAAG,SAAS,UAAU,MAAM;AAClD,SAAO,QAAQ,MAAM,IAAI,EAAE,OAAO,OAAO,EAAE,MAAM,GAAG,KAAK;AAC3D;AAEA,eAAe,aAAa,UAAkB;AAC5C,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;AAEA,SAAS,gBAAgB,MAAsC;AAC7D,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,WAAO,SAAS,MAAM;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,OAAwC;AACxD,MAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAoC;AACtD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAEA,SAAS,YAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;AAEA,SAAS,WAAW,OAAuB;AACzC,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;AAEA,SAAS,qBAAqB,UAAwC;AACpE,SAAO,SAAS,SAAS,GAAG,KAAK,GAAG,UAAU,KAAK,GAAG,EAAE,IAAI,WAAW;AACzE;AAEA,SAAS,mBAAmB,OAAe,WAA2B;AACpE,QAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC7C,SAAO,KAAK,SAAS,YAAY,GAAG,KAAK,MAAM,GAAG,YAAY,CAAC,CAAC,QAAQ;AAC1E;;;AC/YA,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;;;AFDA,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":["options","args","options","jsx","args","options"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cngkit",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Opinionated
|
|
3
|
+
"version": "1.1.20",
|
|
4
|
+
"description": "Opinionated CNG CLI kit for Coderoom collaboration and operator workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cngkit": "dist/cli.js"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"format": "prettier --write README.md src tsconfig.json tsup.config.ts package.json",
|
|
26
26
|
"lint": "eslint src",
|
|
27
27
|
"prepublishOnly": "pnpm run build && pnpm run smoke",
|
|
28
|
-
"smoke": "node dist/cli.js --help && node dist/cli.js --version && node dist/cli.js help && node dist/cli.js help knowledges && node dist/cli.js help coderoom && node dist/cli.js help transcripts && node dist/cli.js help hookify && node dist/cli.js login --help && node dist/cli.js coderoom --help && node dist/cli.js coderoom share --help && node dist/cli.js coderoom join --help && node dist/cli.js scrub --help && node dist/cli.js transcripts --help && node dist/cli.js hookify --help && node dist/cli.js hookify ingest --help && node dist/cli.js knowledges --help && node dist/cli.js knowledges audiences --help && node dist/cli.js knowledges search --help && node dist/cli.js knowledges read --help && node dist/cli.js knowledges grep --help && node dist/cli.js knowledges glob --help && node dist/cli.js knowledges list --help && node dist/cli.js knowledges files --help && node dist/cli.js knowledges status --help",
|
|
28
|
+
"smoke": "node dist/cli.js --help && node dist/cli.js --version && node dist/cli.js help && node dist/cli.js help knowledges && node dist/cli.js help coderoom && node dist/cli.js help transcripts && node dist/cli.js help hooks && node dist/cli.js help hookify && node dist/cli.js login --help && node dist/cli.js coderoom --help && node dist/cli.js coderoom share --help && node dist/cli.js coderoom join --help && node dist/cli.js scrub --help && node dist/cli.js transcripts --help && node dist/cli.js hooks --help && node dist/cli.js hooks install --help && node dist/cli.js hooks uninstall --help && node dist/cli.js hookify --help && node dist/cli.js hookify ingest --help && node dist/cli.js knowledges --help && node dist/cli.js knowledges audiences --help && node dist/cli.js knowledges search --help && node dist/cli.js knowledges ls --help && node dist/cli.js knowledges tree --help && node dist/cli.js knowledges cat --help && node dist/cli.js knowledges head --help && node dist/cli.js knowledges tail --help && node dist/cli.js knowledges find --help && node dist/cli.js knowledges stat --help && node dist/cli.js knowledges realpath --help && node dist/cli.js knowledges read --help && node dist/cli.js knowledges grep --help && node dist/cli.js knowledges glob --help && node dist/cli.js knowledges list --help && node dist/cli.js knowledges files --help && node dist/cli.js knowledges status --help",
|
|
29
29
|
"smoke:docker": "bash scripts/smoke-docker.sh",
|
|
30
30
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
31
31
|
},
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"pastel": "^4.0.1",
|
|
45
45
|
"react": "^19.2.7",
|
|
46
46
|
"react-devtools-core": "^6.1.5",
|
|
47
|
-
"strip-ansi": "7.1.2",
|
|
48
47
|
"ws": "^8.20.1",
|
|
49
48
|
"zod": "^4.4.3"
|
|
50
49
|
},
|