cngkit 1.1.19 → 1.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -9
  3. package/dist/chunk-52PGDSFU.js +42 -0
  4. package/dist/chunk-52PGDSFU.js.map +1 -0
  5. package/dist/{chunk-L6ZVQRSY.js → chunk-5WTRGYAO.js} +141 -52
  6. package/dist/chunk-5WTRGYAO.js.map +1 -0
  7. package/dist/{chunk-GBONV6XP.js → chunk-CVF2ODLP.js} +3 -3
  8. package/dist/{chunk-7SO75QXJ.js → chunk-DIJEVOVN.js} +109 -89
  9. package/dist/chunk-DIJEVOVN.js.map +1 -0
  10. package/dist/{chunk-3A6GRNEV.js → chunk-SKK2XLRZ.js} +21 -21
  11. package/dist/chunk-SKK2XLRZ.js.map +1 -0
  12. package/dist/{chunk-DBA3BZXP.js → chunk-SMTQ3W3F.js} +50 -10
  13. package/dist/chunk-SMTQ3W3F.js.map +1 -0
  14. package/dist/{chunk-TYDIBWZV.js → chunk-YJXAH7D5.js} +2 -2
  15. package/dist/cli.js +63 -24
  16. package/dist/cli.js.map +1 -1
  17. package/dist/commands/coderoom/index.js +3 -3
  18. package/dist/commands/coderoom/join.js +4 -4
  19. package/dist/commands/coderoom/share.js +4 -4
  20. package/dist/commands/hookify/index.js +3 -3
  21. package/dist/commands/hookify/ingest.js +3 -3
  22. package/dist/commands/hooks/index.js +3 -3
  23. package/dist/commands/hooks/install.js +3 -3
  24. package/dist/commands/hooks/uninstall.js +3 -3
  25. package/dist/commands/index.js +3 -3
  26. package/dist/commands/knowledges/audiences.js +5 -5
  27. package/dist/commands/knowledges/cat.js +5 -5
  28. package/dist/commands/knowledges/files.js +5 -5
  29. package/dist/commands/knowledges/find.js +5 -5
  30. package/dist/commands/knowledges/glob.js +5 -5
  31. package/dist/commands/knowledges/grep.js +5 -5
  32. package/dist/commands/knowledges/head.js +5 -5
  33. package/dist/commands/knowledges/index.js +3 -3
  34. package/dist/commands/knowledges/list.js +5 -5
  35. package/dist/commands/knowledges/ls.js +5 -5
  36. package/dist/commands/knowledges/read.js +5 -5
  37. package/dist/commands/knowledges/realpath.js +6 -6
  38. package/dist/commands/knowledges/realpath.js.map +1 -1
  39. package/dist/commands/knowledges/search.js +5 -5
  40. package/dist/commands/knowledges/stat.js +5 -5
  41. package/dist/commands/knowledges/status.js +5 -5
  42. package/dist/commands/knowledges/tail.js +5 -5
  43. package/dist/commands/knowledges/tree.js +5 -5
  44. package/dist/commands/login.js +3 -3
  45. package/dist/commands/login.js.map +1 -1
  46. package/dist/commands/scrub.js +37 -14
  47. package/dist/commands/scrub.js.map +1 -1
  48. package/dist/commands/transcripts.js +41 -21
  49. package/dist/commands/transcripts.js.map +1 -1
  50. package/package.json +2 -3
  51. package/dist/chunk-3A6GRNEV.js.map +0 -1
  52. package/dist/chunk-7SO75QXJ.js.map +0 -1
  53. package/dist/chunk-DBA3BZXP.js.map +0 -1
  54. package/dist/chunk-L6ZVQRSY.js.map +0 -1
  55. package/dist/chunk-X4E7NAN4.js +0 -26
  56. package/dist/chunk-X4E7NAN4.js.map +0 -1
  57. /package/dist/{chunk-GBONV6XP.js.map → chunk-CVF2ODLP.js.map} +0 -0
  58. /package/dist/{chunk-TYDIBWZV.js.map → chunk-YJXAH7D5.js.map} +0 -0
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runKnowReadCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema,
9
9
  RequiredFilePathArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/read.tsx
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  runKnowRealpathCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema,
9
9
  RequiredCatalogPathArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/realpath.tsx
18
18
  import { jsx } from "react/jsx-runtime";
19
- var description = "Resolve a catalog path shortcut";
19
+ var description = "Normalize a topics-root catalog path";
20
20
  var args = RequiredCatalogPathArgsSchema;
21
21
  var options = JsonOutputOptionsSchema;
22
22
  function RealpathCommand({ args: args2, options: options2 }) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/knowledges/realpath.tsx"],"sourcesContent":["import { JsonOutputOptionsSchema, RequiredCatalogPathArgsSchema } from \"../../cli/options.js\";\nimport { runKnowRealpathCommand, type KnowRealpathCommandOptions } from \"../../features/knowledges/run-knowledges-command.js\";\nimport { CommandRunner } from \"../../cli/command-runner.js\";\n\nexport const description = \"Resolve a catalog path shortcut\";\nexport const args = RequiredCatalogPathArgsSchema;\nexport const options = JsonOutputOptionsSchema;\n\ntype RealpathCommandProps = {\n readonly args: [string];\n readonly options: KnowRealpathCommandOptions;\n};\n\nexport default function RealpathCommand({ args, options }: RealpathCommandProps) {\n return <CommandRunner run={(output) => runKnowRealpathCommand(args[0], options, output)} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAcS;AAVF,IAAM,cAAc;AACpB,IAAM,OAAO;AACb,IAAM,UAAU;AAOR,SAAR,gBAAiC,EAAE,MAAAA,OAAM,SAAAC,SAAQ,GAAyB;AAC/E,SAAO,oBAAC,iBAAc,KAAK,CAAC,WAAW,uBAAuBD,MAAK,CAAC,GAAGC,UAAS,MAAM,GAAG;AAC3F;","names":["args","options"]}
1
+ {"version":3,"sources":["../../../src/commands/knowledges/realpath.tsx"],"sourcesContent":["import { JsonOutputOptionsSchema, RequiredCatalogPathArgsSchema } from \"../../cli/options.js\";\nimport { runKnowRealpathCommand, type KnowRealpathCommandOptions } from \"../../features/knowledges/run-knowledges-command.js\";\nimport { CommandRunner } from \"../../cli/command-runner.js\";\n\nexport const description = \"Normalize a topics-root catalog path\";\nexport const args = RequiredCatalogPathArgsSchema;\nexport const options = JsonOutputOptionsSchema;\n\ntype RealpathCommandProps = {\n readonly args: [string];\n readonly options: KnowRealpathCommandOptions;\n};\n\nexport default function RealpathCommand({ args, options }: RealpathCommandProps) {\n return <CommandRunner run={(output) => runKnowRealpathCommand(args[0], options, output)} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAcS;AAVF,IAAM,cAAc;AACpB,IAAM,OAAO;AACb,IAAM,UAAU;AAOR,SAAR,gBAAiC,EAAE,MAAAA,OAAM,SAAAC,SAAQ,GAAyB;AAC/E,SAAO,oBAAC,iBAAc,KAAK,CAAC,WAAW,uBAAuBD,MAAK,CAAC,GAAGC,UAAS,MAAM,GAAG;AAC3F;","names":["args","options"]}
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runKnowSearchCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  LimitOptionsSchema,
9
9
  RequiredQueryArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/search.tsx
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runKnowStatCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema,
9
9
  RequiredCatalogPathArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/stat.tsx
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  runKnowStatusCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema
9
9
  } from "../../chunk-NGEWD4BW.js";
10
10
  import {
11
11
  CommandRunner
12
- } from "../../chunk-X4E7NAN4.js";
13
- import "../../chunk-7SO75QXJ.js";
12
+ } from "../../chunk-52PGDSFU.js";
13
+ import "../../chunk-DIJEVOVN.js";
14
14
  import "../../chunk-PZ5AY32C.js";
15
15
 
16
16
  // src/commands/knowledges/status.tsx
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runKnowTailCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema,
9
9
  RequiredFilePathArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/tail.tsx
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runKnowTreeCommand
3
- } from "../../chunk-L6ZVQRSY.js";
3
+ } from "../../chunk-5WTRGYAO.js";
4
4
  import "../../chunk-XQGLUQFM.js";
5
- import "../../chunk-3A6GRNEV.js";
6
- import "../../chunk-TYDIBWZV.js";
5
+ import "../../chunk-SKK2XLRZ.js";
6
+ import "../../chunk-YJXAH7D5.js";
7
7
  import {
8
8
  JsonOutputOptionsSchema,
9
9
  OptionalPathArgsSchema
10
10
  } from "../../chunk-NGEWD4BW.js";
11
11
  import {
12
12
  CommandRunner
13
- } from "../../chunk-X4E7NAN4.js";
14
- import "../../chunk-7SO75QXJ.js";
13
+ } from "../../chunk-52PGDSFU.js";
14
+ import "../../chunk-DIJEVOVN.js";
15
15
  import "../../chunk-PZ5AY32C.js";
16
16
 
17
17
  // src/commands/knowledges/tree.tsx
@@ -3,10 +3,10 @@ import {
3
3
  } from "../chunk-NGEWD4BW.js";
4
4
  import {
5
5
  CommandRunner
6
- } from "../chunk-X4E7NAN4.js";
6
+ } from "../chunk-52PGDSFU.js";
7
7
  import {
8
8
  resolveApiBaseUrl
9
- } from "../chunk-7SO75QXJ.js";
9
+ } from "../chunk-DIJEVOVN.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 Curly login in your browser";
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 Curly login in your browser\";\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"]}
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"]}
@@ -4,8 +4,8 @@ import {
4
4
  } from "../chunk-NGEWD4BW.js";
5
5
  import {
6
6
  CommandRunner
7
- } from "../chunk-X4E7NAN4.js";
8
- import "../chunk-7SO75QXJ.js";
7
+ } from "../chunk-52PGDSFU.js";
8
+ import "../chunk-DIJEVOVN.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.warning(formatScrubReport(findings));
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__ */ jsx(CommandRunner, { run: (output) => runScrubCommand(args2[0], options2, output) });
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"]}
@@ -7,21 +7,24 @@ import {
7
7
  } from "../chunk-XQGLUQFM.js";
8
8
  import {
9
9
  renderCngkitHelp
10
- } from "../chunk-3A6GRNEV.js";
10
+ } from "../chunk-SKK2XLRZ.js";
11
11
  import {
12
12
  GlobalOptionsSchema,
13
13
  TranscriptArgsSchema
14
14
  } from "../chunk-NGEWD4BW.js";
15
15
  import {
16
16
  CommandRunner
17
- } from "../chunk-X4E7NAN4.js";
18
- import "../chunk-7SO75QXJ.js";
17
+ } from "../chunk-52PGDSFU.js";
18
+ import "../chunk-DIJEVOVN.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 ?? [];
@@ -303,7 +338,7 @@ async function runTranscriptCommand(args2, options2, output) {
303
338
  output.raw(formatJson({ records }));
304
339
  return;
305
340
  }
306
- output.info(formatTranscriptRecords(records));
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__ */ jsx(CommandRunner, { run: (output) => runTranscriptCommand(args2, options2, output) });
444
+ return /* @__PURE__ */ jsx2(CommandRunner, { run: (output) => runTranscriptCommand(args2, options2, output) });
425
445
  }
426
446
  export {
427
447
  args,