cngkit 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -2
- package/dist/cli.cjs +762 -1
- package/dist/cli.cjs.map +1 -1
- package/package.json +15 -16
package/dist/cli.cjs
CHANGED
|
@@ -7443,7 +7443,7 @@ var logging;
|
|
|
7443
7443
|
// src/config.ts
|
|
7444
7444
|
var import_node_crypto = require("crypto");
|
|
7445
7445
|
var import_node_process = __toESM(require("process"), 1);
|
|
7446
|
-
var packageVersion = "1.
|
|
7446
|
+
var packageVersion = "1.1.1";
|
|
7447
7447
|
var defaultApiBaseUrl = "https://curly.ng";
|
|
7448
7448
|
function resolveApiBaseUrl(options) {
|
|
7449
7449
|
return options.apiBaseUrl ?? import_node_process.default.env.CNGKIT_API_BASE_URL ?? defaultApiBaseUrl;
|
|
@@ -7512,6 +7512,426 @@ function browserOpenCommand(url2) {
|
|
|
7512
7512
|
return { command: "xdg-open", args: [url2] };
|
|
7513
7513
|
}
|
|
7514
7514
|
|
|
7515
|
+
// src/help-specs.ts
|
|
7516
|
+
var commandList = [
|
|
7517
|
+
"`cngkit login` - open Curly.ng login.",
|
|
7518
|
+
"`cngkit share [room-code]` - start a live repo sync room.",
|
|
7519
|
+
"`cngkit join <room-code>` - join a live repo sync room.",
|
|
7520
|
+
"`cngkit scrub [path]` - scan for secrets and optionally mask them inline.",
|
|
7521
|
+
"`cngkit knowledges ...` - read the hosted Harness catalog."
|
|
7522
|
+
].join("\n");
|
|
7523
|
+
var knowledgesCommandList = [
|
|
7524
|
+
"`status` - print remote catalog state.",
|
|
7525
|
+
"`audiences` - list valid audience filters.",
|
|
7526
|
+
"`search <query>` - semantic search over Cloudflare Vectorize-backed records.",
|
|
7527
|
+
"`list [query]` - list known subskills.",
|
|
7528
|
+
"`files [query]` - list uploaded catalog files.",
|
|
7529
|
+
"`read <file-path>` - Claude-style file read.",
|
|
7530
|
+
"`grep <pattern>` - Claude-style content search.",
|
|
7531
|
+
"`glob [pattern]` - Claude-style file discovery."
|
|
7532
|
+
].join("\n");
|
|
7533
|
+
var helpTopics = [
|
|
7534
|
+
{
|
|
7535
|
+
title: "cngkit",
|
|
7536
|
+
aliases: ["", "overview", "help"],
|
|
7537
|
+
body: `# cngkit
|
|
7538
|
+
|
|
7539
|
+
Curly.ng operator CLI for backend-connected repo sync, secret scrubbing, and Harness knowledges access.
|
|
7540
|
+
|
|
7541
|
+
## Usage
|
|
7542
|
+
|
|
7543
|
+
\`\`\`bash
|
|
7544
|
+
cngkit <command> [options]
|
|
7545
|
+
\`\`\`
|
|
7546
|
+
|
|
7547
|
+
## Commands
|
|
7548
|
+
|
|
7549
|
+
${commandList}
|
|
7550
|
+
|
|
7551
|
+
## Progressive Help
|
|
7552
|
+
|
|
7553
|
+
\`\`\`bash
|
|
7554
|
+
cngkit <command> --help
|
|
7555
|
+
cngkit knowledges --help
|
|
7556
|
+
cngkit knowledges <subcommand> --help
|
|
7557
|
+
\`\`\`
|
|
7558
|
+
|
|
7559
|
+
The CLI intentionally prints plain Markdown or line-oriented text with no color-only state, tables that require terminal width, or hidden interactive prompts. Add \`--json\` to read-only knowledges commands when another agent or tool needs structured backend data.
|
|
7560
|
+
|
|
7561
|
+
## Backend
|
|
7562
|
+
|
|
7563
|
+
Default API: \`https://curly.ng\`
|
|
7564
|
+
|
|
7565
|
+
Override with:
|
|
7566
|
+
|
|
7567
|
+
\`\`\`bash
|
|
7568
|
+
cngkit --api-base-url <url> ...
|
|
7569
|
+
CNGKIT_API_BASE_URL=<url> cngkit ...
|
|
7570
|
+
\`\`\`
|
|
7571
|
+
`
|
|
7572
|
+
},
|
|
7573
|
+
{
|
|
7574
|
+
title: "login",
|
|
7575
|
+
aliases: ["login"],
|
|
7576
|
+
body: `# cngkit login
|
|
7577
|
+
|
|
7578
|
+
Open Curly.ng login in a browser. In headless environments, print the URL so an agent can surface it to the operator.
|
|
7579
|
+
|
|
7580
|
+
## Usage
|
|
7581
|
+
|
|
7582
|
+
\`\`\`bash
|
|
7583
|
+
cngkit login
|
|
7584
|
+
\`\`\`
|
|
7585
|
+
|
|
7586
|
+
## Output Contract
|
|
7587
|
+
|
|
7588
|
+
- First line names the login URL being opened.
|
|
7589
|
+
- If no local browser opener exists, the CLI prints a direct URL.
|
|
7590
|
+
- No credentials are printed or persisted by this command.
|
|
7591
|
+
`
|
|
7592
|
+
},
|
|
7593
|
+
{
|
|
7594
|
+
title: "share",
|
|
7595
|
+
aliases: ["share", "sync", "repo-sync"],
|
|
7596
|
+
body: `# cngkit share
|
|
7597
|
+
|
|
7598
|
+
Start a real-time repository sync room from the current directory.
|
|
7599
|
+
|
|
7600
|
+
## Usage
|
|
7601
|
+
|
|
7602
|
+
\`\`\`bash
|
|
7603
|
+
cngkit share [room-code]
|
|
7604
|
+
\`\`\`
|
|
7605
|
+
|
|
7606
|
+
## Backend Contract
|
|
7607
|
+
|
|
7608
|
+
- Connects to \`/api/cng/sync/:roomCode\` on the configured Curly backend.
|
|
7609
|
+
- Uses WebSocket transport backed by the \`CngSyncRoom\` Durable Object.
|
|
7610
|
+
- Sends an initial snapshot, then file and delete events.
|
|
7611
|
+
- Last received change wins.
|
|
7612
|
+
|
|
7613
|
+
## Filesystem Contract
|
|
7614
|
+
|
|
7615
|
+
- Preserves \`.git/\`.
|
|
7616
|
+
- Preserves files ignored by the repo's \`.gitignore\`.
|
|
7617
|
+
- Sends regular files as base64 payloads in the sync protocol.
|
|
7618
|
+
|
|
7619
|
+
## Output Contract
|
|
7620
|
+
|
|
7621
|
+
- Prints room code, repo root, peer id, backend health, and concise sync events.
|
|
7622
|
+
- Keeps running until the socket closes or the process receives a termination signal.
|
|
7623
|
+
`
|
|
7624
|
+
},
|
|
7625
|
+
{
|
|
7626
|
+
title: "join",
|
|
7627
|
+
aliases: ["join"],
|
|
7628
|
+
body: `# cngkit join
|
|
7629
|
+
|
|
7630
|
+
Join an existing real-time repository sync room in the current directory.
|
|
7631
|
+
|
|
7632
|
+
## Usage
|
|
7633
|
+
|
|
7634
|
+
\`\`\`bash
|
|
7635
|
+
cngkit join <room-code>
|
|
7636
|
+
\`\`\`
|
|
7637
|
+
|
|
7638
|
+
## Backend Contract
|
|
7639
|
+
|
|
7640
|
+
Same transport and filesystem contract as \`cngkit share\`. The supplied room code selects the Durable Object room.
|
|
7641
|
+
|
|
7642
|
+
## Output Contract
|
|
7643
|
+
|
|
7644
|
+
- Prints backend health, room code, repo root, peer id, and concise sync events.
|
|
7645
|
+
- Missing room code exits with a usage error.
|
|
7646
|
+
`
|
|
7647
|
+
},
|
|
7648
|
+
{
|
|
7649
|
+
title: "scrub",
|
|
7650
|
+
aliases: ["scrub"],
|
|
7651
|
+
body: `# cngkit scrub
|
|
7652
|
+
|
|
7653
|
+
Scan a file or directory for secrets with TruffleHog. Report-only is the default. Inline masking requires \`--yes\`.
|
|
7654
|
+
|
|
7655
|
+
## Usage
|
|
7656
|
+
|
|
7657
|
+
\`\`\`bash
|
|
7658
|
+
cngkit scrub [path]
|
|
7659
|
+
cngkit scrub [path] --yes
|
|
7660
|
+
\`\`\`
|
|
7661
|
+
|
|
7662
|
+
## Requirements
|
|
7663
|
+
|
|
7664
|
+
- \`trufflehog\` must be available on \`PATH\`.
|
|
7665
|
+
|
|
7666
|
+
## Safety Contract
|
|
7667
|
+
|
|
7668
|
+
- Default mode does not rewrite files.
|
|
7669
|
+
- \`--yes\` rewrites detected secret values inline with \`[CNGKIT_SECRET:<detector>:<verified|unverified>]\` placeholders.
|
|
7670
|
+
- \`--mask\` is accepted as a compatibility alias, but still requires \`--yes\`.
|
|
7671
|
+
- Raw and redacted secret values are never printed in the report.
|
|
7672
|
+
`
|
|
7673
|
+
},
|
|
7674
|
+
{
|
|
7675
|
+
title: "knowledges",
|
|
7676
|
+
aliases: ["knowledges", "knowledge"],
|
|
7677
|
+
body: `# cngkit knowledges
|
|
7678
|
+
|
|
7679
|
+
Read the hosted Harness knowledges catalog from the Curly backend. These commands are read-only and use the generated \`@cng/client\` SDK against public backend routes.
|
|
7680
|
+
|
|
7681
|
+
## Usage
|
|
7682
|
+
|
|
7683
|
+
\`\`\`bash
|
|
7684
|
+
cngkit knowledges <subcommand> [options]
|
|
7685
|
+
\`\`\`
|
|
7686
|
+
|
|
7687
|
+
## Subcommands
|
|
7688
|
+
|
|
7689
|
+
${knowledgesCommandList}
|
|
7690
|
+
|
|
7691
|
+
## Progressive Help
|
|
7692
|
+
|
|
7693
|
+
\`\`\`bash
|
|
7694
|
+
cngkit knowledges read --help
|
|
7695
|
+
cngkit knowledges grep --help
|
|
7696
|
+
cngkit knowledges glob --help
|
|
7697
|
+
\`\`\`
|
|
7698
|
+
|
|
7699
|
+
## AI-Friendly Output
|
|
7700
|
+
|
|
7701
|
+
- Default output is line-oriented and intended to be directly usable by agents.
|
|
7702
|
+
- \`read\` prints file content with no wrapper before any truncation note.
|
|
7703
|
+
- \`grep --output-mode content\` prints \`path:line\` blocks.
|
|
7704
|
+
- \`grep --output-mode files_with_matches\` and \`glob\` print one path per line.
|
|
7705
|
+
- \`--json\` prints the typed backend data payload for machine consumption.
|
|
7706
|
+
`
|
|
7707
|
+
},
|
|
7708
|
+
{
|
|
7709
|
+
title: "knowledges status",
|
|
7710
|
+
aliases: ["knowledges-status", "status"],
|
|
7711
|
+
body: `# cngkit knowledges status
|
|
7712
|
+
|
|
7713
|
+
Print remote Harness catalog state from \`GET /api/harness/knowledges/catalog\`.
|
|
7714
|
+
|
|
7715
|
+
## Usage
|
|
7716
|
+
|
|
7717
|
+
\`\`\`bash
|
|
7718
|
+
cngkit knowledges status [--json]
|
|
7719
|
+
\`\`\`
|
|
7720
|
+
|
|
7721
|
+
## Output Contract
|
|
7722
|
+
|
|
7723
|
+
- Text mode prints catalog name, file count, blob count, Vectorize binding, and latest run when present.
|
|
7724
|
+
- \`--json\` prints the backend data payload.
|
|
7725
|
+
`
|
|
7726
|
+
},
|
|
7727
|
+
{
|
|
7728
|
+
title: "knowledges audiences",
|
|
7729
|
+
aliases: ["knowledges-audiences", "audiences"],
|
|
7730
|
+
body: `# cngkit knowledges audiences
|
|
7731
|
+
|
|
7732
|
+
List valid audience filters from \`GET /api/harness/knowledges/audiences\`.
|
|
7733
|
+
|
|
7734
|
+
## Usage
|
|
7735
|
+
|
|
7736
|
+
\`\`\`bash
|
|
7737
|
+
cngkit knowledges audiences [--json]
|
|
7738
|
+
\`\`\`
|
|
7739
|
+
|
|
7740
|
+
Use this before \`cngkit knowledges files --audience <id>\`.
|
|
7741
|
+
`
|
|
7742
|
+
},
|
|
7743
|
+
{
|
|
7744
|
+
title: "knowledges search",
|
|
7745
|
+
aliases: ["knowledges-search", "search"],
|
|
7746
|
+
body: `# cngkit knowledges search
|
|
7747
|
+
|
|
7748
|
+
Run semantic search against the Cloudflare Vectorize-backed Harness index.
|
|
7749
|
+
|
|
7750
|
+
## Usage
|
|
7751
|
+
|
|
7752
|
+
\`\`\`bash
|
|
7753
|
+
cngkit knowledges search <query> [--limit <n>] [--json]
|
|
7754
|
+
\`\`\`
|
|
7755
|
+
|
|
7756
|
+
## Defaults
|
|
7757
|
+
|
|
7758
|
+
- \`--limit\`: \`5\`
|
|
7759
|
+
|
|
7760
|
+
## Example
|
|
7761
|
+
|
|
7762
|
+
\`\`\`bash
|
|
7763
|
+
cngkit knowledges search "cloudflare backend" --limit 3
|
|
7764
|
+
\`\`\`
|
|
7765
|
+
`
|
|
7766
|
+
},
|
|
7767
|
+
{
|
|
7768
|
+
title: "knowledges list",
|
|
7769
|
+
aliases: ["knowledges-list", "list"],
|
|
7770
|
+
body: `# cngkit knowledges list
|
|
7771
|
+
|
|
7772
|
+
List known subskills from \`GET /api/harness/knowledges/subskills\`, optionally filtered locally by query.
|
|
7773
|
+
|
|
7774
|
+
## Usage
|
|
7775
|
+
|
|
7776
|
+
\`\`\`bash
|
|
7777
|
+
cngkit knowledges list [query] [--limit <n>] [--json]
|
|
7778
|
+
\`\`\`
|
|
7779
|
+
|
|
7780
|
+
## Defaults
|
|
7781
|
+
|
|
7782
|
+
- \`--limit\`: \`25\`
|
|
7783
|
+
`
|
|
7784
|
+
},
|
|
7785
|
+
{
|
|
7786
|
+
title: "knowledges files",
|
|
7787
|
+
aliases: ["knowledges-files", "files"],
|
|
7788
|
+
body: `# cngkit knowledges files
|
|
7789
|
+
|
|
7790
|
+
List uploaded catalog files from \`GET /api/harness/knowledges/files\`.
|
|
7791
|
+
|
|
7792
|
+
## Usage
|
|
7793
|
+
|
|
7794
|
+
\`\`\`bash
|
|
7795
|
+
cngkit knowledges files [query] [--audience <id>] [--limit <n>] [--json]
|
|
7796
|
+
\`\`\`
|
|
7797
|
+
|
|
7798
|
+
## Defaults
|
|
7799
|
+
|
|
7800
|
+
- \`--limit\`: \`25\`
|
|
7801
|
+
- \`--audience\`: omitted
|
|
7802
|
+
|
|
7803
|
+
Run \`cngkit knowledges audiences\` to discover supported audience ids.
|
|
7804
|
+
`
|
|
7805
|
+
},
|
|
7806
|
+
{
|
|
7807
|
+
title: "knowledges read",
|
|
7808
|
+
aliases: ["knowledges-read", "read"],
|
|
7809
|
+
body: `# cngkit knowledges read
|
|
7810
|
+
|
|
7811
|
+
Claude-style read tool for hosted Harness catalog files. Backed by \`GET /api/harness/filesystem/read\` and \`HarnessReadQuerySchema\`.
|
|
7812
|
+
|
|
7813
|
+
## Usage
|
|
7814
|
+
|
|
7815
|
+
\`\`\`bash
|
|
7816
|
+
cngkit knowledges read <file-path> [--offset <n>] [--limit <n>] [--json]
|
|
7817
|
+
\`\`\`
|
|
7818
|
+
|
|
7819
|
+
## Inputs
|
|
7820
|
+
|
|
7821
|
+
- \`file-path\`: normalized catalog path. Shorthand paths such as \`/libraries/lib-cloudflare/SUBSKILL.md\` are expanded to \`skills/knowledges/subskills/libraries/lib-cloudflare/SUBSKILL.md\`.
|
|
7822
|
+
- \`--offset\`: zero-based starting line. Default: \`0\`.
|
|
7823
|
+
- \`--limit\`: maximum lines. Default in CLI: \`200\`. Backend max: \`2000\`.
|
|
7824
|
+
|
|
7825
|
+
## Output Contract
|
|
7826
|
+
|
|
7827
|
+
- Text mode prints only the returned file content first.
|
|
7828
|
+
- If truncated, a final bracketed truncation note is printed after the content.
|
|
7829
|
+
- \`--json\` prints \`{ file_path, content, total_lines, offset, limit, truncated }\`.
|
|
7830
|
+
|
|
7831
|
+
## Example
|
|
7832
|
+
|
|
7833
|
+
\`\`\`bash
|
|
7834
|
+
cngkit knowledges read /libraries/lib-cloudflare/SUBSKILL.md --limit 80
|
|
7835
|
+
\`\`\`
|
|
7836
|
+
`
|
|
7837
|
+
},
|
|
7838
|
+
{
|
|
7839
|
+
title: "knowledges grep",
|
|
7840
|
+
aliases: ["knowledges-grep", "grep"],
|
|
7841
|
+
body: `# cngkit knowledges grep
|
|
7842
|
+
|
|
7843
|
+
Claude-style grep tool for hosted Harness catalog files. Backed by \`GET /api/harness/filesystem/grep\` and \`HarnessGrepQuerySchema\`.
|
|
7844
|
+
|
|
7845
|
+
## Usage
|
|
7846
|
+
|
|
7847
|
+
\`\`\`bash
|
|
7848
|
+
cngkit knowledges grep <pattern> [--path <path>] [--include <glob>] [--output-mode <mode>] [--context <n>] [--case-insensitive] [--json]
|
|
7849
|
+
\`\`\`
|
|
7850
|
+
|
|
7851
|
+
## Inputs
|
|
7852
|
+
|
|
7853
|
+
- \`pattern\`: JavaScript regular expression source.
|
|
7854
|
+
- \`--path\`: catalog path prefix. Default: \`/\`.
|
|
7855
|
+
- \`--include\`: filename include filter. Default: \`*\`.
|
|
7856
|
+
- \`--output-mode\`: \`content\`, \`files_with_matches\`, or \`count\`. Default: \`content\`.
|
|
7857
|
+
- \`--context\`: context lines around content matches. Default: \`0\`. Backend max: \`20\`.
|
|
7858
|
+
- \`--case-insensitive\`: maps to backend \`case_insensitive=true\`.
|
|
7859
|
+
|
|
7860
|
+
## Output Contract
|
|
7861
|
+
|
|
7862
|
+
- \`content\`: prints blocks as \`file_path:line_number\`, optional context lines, then \`> matching line\`.
|
|
7863
|
+
- \`files_with_matches\`: prints one matching file path per line.
|
|
7864
|
+
- \`count\`: prints \`file_path: match_count\` lines.
|
|
7865
|
+
- \`--json\` prints the typed backend response union.
|
|
7866
|
+
|
|
7867
|
+
## Examples
|
|
7868
|
+
|
|
7869
|
+
\`\`\`bash
|
|
7870
|
+
cngkit knowledges grep Cloudflare --path /libraries/lib-cloudflare --output-mode files_with_matches
|
|
7871
|
+
cngkit knowledges grep "Vectorize|D1" --path /libraries/lib-cloudflare --context 2
|
|
7872
|
+
\`\`\`
|
|
7873
|
+
`
|
|
7874
|
+
},
|
|
7875
|
+
{
|
|
7876
|
+
title: "knowledges glob",
|
|
7877
|
+
aliases: ["knowledges-glob", "glob"],
|
|
7878
|
+
body: `# cngkit knowledges glob
|
|
7879
|
+
|
|
7880
|
+
Claude-style glob tool for hosted Harness catalog files. Backed by \`GET /api/harness/filesystem/glob\` and \`HarnessGlobQuerySchema\`.
|
|
7881
|
+
|
|
7882
|
+
## Usage
|
|
7883
|
+
|
|
7884
|
+
\`\`\`bash
|
|
7885
|
+
cngkit knowledges glob [pattern] [--path <path>] [--json]
|
|
7886
|
+
\`\`\`
|
|
7887
|
+
|
|
7888
|
+
## Inputs
|
|
7889
|
+
|
|
7890
|
+
- \`pattern\`: supported glob pattern. CLI default: \`**/*.md\`.
|
|
7891
|
+
- \`--path\`: catalog path prefix. Default: \`/\`.
|
|
7892
|
+
|
|
7893
|
+
## Supported Patterns
|
|
7894
|
+
|
|
7895
|
+
- \`**/*.md\`
|
|
7896
|
+
- \`**/*.SUBSKILL.md\`
|
|
7897
|
+
- \`**/SKILL.md\`
|
|
7898
|
+
- \`*.md\`
|
|
7899
|
+
- \`*.SUBSKILL.md\`
|
|
7900
|
+
- \`SKILL.md\`
|
|
7901
|
+
|
|
7902
|
+
## Output Contract
|
|
7903
|
+
|
|
7904
|
+
- Text mode prints one file path per line.
|
|
7905
|
+
- If truncated, a final bracketed truncation note is printed.
|
|
7906
|
+
- \`--json\` prints \`{ files, total_files, truncated }\`.
|
|
7907
|
+
|
|
7908
|
+
## Example
|
|
7909
|
+
|
|
7910
|
+
\`\`\`bash
|
|
7911
|
+
cngkit knowledges glob "**/*.md" --path /libraries/lib-cloudflare
|
|
7912
|
+
\`\`\`
|
|
7913
|
+
`
|
|
7914
|
+
}
|
|
7915
|
+
];
|
|
7916
|
+
var helpTopicByAlias = /* @__PURE__ */ new Map();
|
|
7917
|
+
for (const topic of helpTopics) {
|
|
7918
|
+
for (const alias of topic.aliases) {
|
|
7919
|
+
helpTopicByAlias.set(alias, topic);
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
function formatCngkitHelp(topicName) {
|
|
7923
|
+
const normalizedTopicName = normalizeHelpTopicName(topicName);
|
|
7924
|
+
return (helpTopicByAlias.get(normalizedTopicName) ?? helpTopicByAlias.get(""))?.body.trim() ?? "";
|
|
7925
|
+
}
|
|
7926
|
+
function formatKnowledgesHelp(topicName) {
|
|
7927
|
+
const normalizedTopicName = normalizeHelpTopicName(topicName);
|
|
7928
|
+
const topicKey = normalizedTopicName ? `knowledges-${normalizedTopicName}` : "knowledges";
|
|
7929
|
+
return formatCngkitHelp(topicKey);
|
|
7930
|
+
}
|
|
7931
|
+
function normalizeHelpTopicName(value) {
|
|
7932
|
+
return value?.trim().toLowerCase().replace(/\s+/g, "-") ?? "";
|
|
7933
|
+
}
|
|
7934
|
+
|
|
7515
7935
|
// src/scrub/masker.ts
|
|
7516
7936
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
7517
7937
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -24296,6 +24716,35 @@ async function waitForSessionClose(socket, watcher) {
|
|
|
24296
24716
|
}
|
|
24297
24717
|
|
|
24298
24718
|
// src/commands.ts
|
|
24719
|
+
async function runKnowledgesCommand(args, options, output, dependencies) {
|
|
24720
|
+
const [subcommand, ...subcommandArgs] = args ?? [];
|
|
24721
|
+
if (subcommand === void 0 || subcommand === "help") {
|
|
24722
|
+
output.info(formatKnowledgesHelp(subcommandArgs[0]));
|
|
24723
|
+
return;
|
|
24724
|
+
}
|
|
24725
|
+
switch (subcommand) {
|
|
24726
|
+
case "status":
|
|
24727
|
+
return runKnowStatusCommand(options, output, dependencies);
|
|
24728
|
+
case "audiences":
|
|
24729
|
+
return runKnowAudiencesCommand(options, output, dependencies);
|
|
24730
|
+
case "search":
|
|
24731
|
+
return runKnowSearchCommand(optionalJoinedArgument(subcommandArgs), options, output, dependencies);
|
|
24732
|
+
case "list":
|
|
24733
|
+
return runKnowListCommand(optionalJoinedArgument(subcommandArgs), options, output, dependencies);
|
|
24734
|
+
case "files":
|
|
24735
|
+
return runKnowFilesCommand(optionalJoinedArgument(subcommandArgs), options, output, dependencies);
|
|
24736
|
+
case "read":
|
|
24737
|
+
return runKnowReadCommand(subcommandArgs[0], options, output, dependencies);
|
|
24738
|
+
case "grep":
|
|
24739
|
+
return runKnowGrepCommand(optionalJoinedArgument(subcommandArgs), options, output, dependencies);
|
|
24740
|
+
case "glob":
|
|
24741
|
+
return runKnowGlobCommand(optionalJoinedArgument(subcommandArgs), options, output, dependencies);
|
|
24742
|
+
default:
|
|
24743
|
+
throw new Error(
|
|
24744
|
+
"Missing knowledges command. Usage: cngkit knowledges <status|audiences|search|list|files|read|grep|glob>"
|
|
24745
|
+
);
|
|
24746
|
+
}
|
|
24747
|
+
}
|
|
24299
24748
|
async function runLoginCommand(options, output) {
|
|
24300
24749
|
const loginUrl = new URL("/login", resolveApiBaseUrl(options));
|
|
24301
24750
|
const loginUrlString = loginUrl.toString();
|
|
@@ -24344,6 +24793,172 @@ async function runScrubCommand(targetPath, options, output, dependencies) {
|
|
|
24344
24793
|
output.info(`Skipped ${result.skippedFindings} finding(s) outside the scrub root.`);
|
|
24345
24794
|
}
|
|
24346
24795
|
}
|
|
24796
|
+
async function runKnowStatusCommand(options, output, dependencies) {
|
|
24797
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24798
|
+
const response = await api.getCatalog();
|
|
24799
|
+
if (options.json) {
|
|
24800
|
+
output.info(formatJson(response.data));
|
|
24801
|
+
return;
|
|
24802
|
+
}
|
|
24803
|
+
const latestRun = response.data.latestRun;
|
|
24804
|
+
const lines = [
|
|
24805
|
+
`Catalog: ${response.data.name}`,
|
|
24806
|
+
`Files: ${response.data.files}`,
|
|
24807
|
+
`Blobs: ${response.data.blobs}`,
|
|
24808
|
+
`Vectorize: ${response.data.vectorize.index} (${response.data.vectorize.binding})`
|
|
24809
|
+
];
|
|
24810
|
+
if (latestRun) {
|
|
24811
|
+
lines.push(`Latest run: ${latestRun.status} (${latestRun.id})`);
|
|
24812
|
+
if (latestRun.updated_at) {
|
|
24813
|
+
lines.push(`Updated: ${latestRun.updated_at}`);
|
|
24814
|
+
}
|
|
24815
|
+
} else {
|
|
24816
|
+
lines.push("Latest run: none");
|
|
24817
|
+
}
|
|
24818
|
+
output.info(lines.join("\n"));
|
|
24819
|
+
}
|
|
24820
|
+
async function runKnowAudiencesCommand(options, output, dependencies) {
|
|
24821
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24822
|
+
const response = await api.listAudiences();
|
|
24823
|
+
if (options.json) {
|
|
24824
|
+
output.info(formatJson(response.data));
|
|
24825
|
+
return;
|
|
24826
|
+
}
|
|
24827
|
+
const lines = response.data.audiences.flatMap((audience) => [
|
|
24828
|
+
`${audience.id} - ${audience.label}`,
|
|
24829
|
+
` ${singleLine(audience.help)}`
|
|
24830
|
+
]);
|
|
24831
|
+
output.info(lines.length > 0 ? lines.join("\n") : "No audiences available.");
|
|
24832
|
+
}
|
|
24833
|
+
async function runKnowSearchCommand(query, options, output, dependencies) {
|
|
24834
|
+
if (!query) {
|
|
24835
|
+
throw new Error("Missing search query. Usage: cngkit knowledges search <query>");
|
|
24836
|
+
}
|
|
24837
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24838
|
+
const response = await api.search(query, coerceLimit(options.limit, 5));
|
|
24839
|
+
if (options.json) {
|
|
24840
|
+
output.info(formatJson(response.data));
|
|
24841
|
+
return;
|
|
24842
|
+
}
|
|
24843
|
+
const lines = response.data.results.flatMap((result, index) => [
|
|
24844
|
+
`${index + 1}. ${result.title} (${result.subskillName})`,
|
|
24845
|
+
` score ${result.score.toFixed(3)} | ${result.path}`,
|
|
24846
|
+
` ${singleLine(result.description || result.contentPreview)}`
|
|
24847
|
+
]);
|
|
24848
|
+
output.info(lines.length > 0 ? lines.join("\n") : `No results for "${query}".`);
|
|
24849
|
+
}
|
|
24850
|
+
async function runKnowListCommand(query, options, output, dependencies) {
|
|
24851
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24852
|
+
const response = await api.listSubskills();
|
|
24853
|
+
const limit = coerceLimit(options.limit, 25);
|
|
24854
|
+
const normalizedQuery = query?.toLowerCase();
|
|
24855
|
+
const subskills = response.data.subskills.filter((subskill) => {
|
|
24856
|
+
if (!normalizedQuery) {
|
|
24857
|
+
return true;
|
|
24858
|
+
}
|
|
24859
|
+
return [subskill.name, subskill.title, subskill.description, subskill.type].join(" ").toLowerCase().includes(normalizedQuery);
|
|
24860
|
+
}).slice(0, limit);
|
|
24861
|
+
if (options.json) {
|
|
24862
|
+
output.info(formatJson({ subskills, total: subskills.length }));
|
|
24863
|
+
return;
|
|
24864
|
+
}
|
|
24865
|
+
const lines = subskills.flatMap((subskill) => [
|
|
24866
|
+
`${subskill.name} [${subskill.type}]`,
|
|
24867
|
+
` ${subskill.title} | files ${subskill.fileCount} | rating ${subskill.rating}`,
|
|
24868
|
+
` ${singleLine(subskill.description)}`
|
|
24869
|
+
]);
|
|
24870
|
+
output.info(lines.length > 0 ? lines.join("\n") : "No matching subskills.");
|
|
24871
|
+
}
|
|
24872
|
+
async function runKnowFilesCommand(query, options, output, dependencies) {
|
|
24873
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24874
|
+
const response = await api.listFiles({
|
|
24875
|
+
query,
|
|
24876
|
+
audience: normalizeAudienceId(options.audience),
|
|
24877
|
+
limit: coerceLimit(options.limit, 25)
|
|
24878
|
+
});
|
|
24879
|
+
if (options.json) {
|
|
24880
|
+
output.info(formatJson(response.data));
|
|
24881
|
+
return;
|
|
24882
|
+
}
|
|
24883
|
+
const lines = response.data.files.map((file2) => {
|
|
24884
|
+
const title = file2.display_title ?? file2.title ?? file2.subskill_name ?? "Untitled";
|
|
24885
|
+
return `${file2.path}
|
|
24886
|
+
${title}`;
|
|
24887
|
+
});
|
|
24888
|
+
output.info(lines.length > 0 ? lines.join("\n") : "No matching files.");
|
|
24889
|
+
}
|
|
24890
|
+
async function runKnowReadCommand(filePath, options, output, dependencies) {
|
|
24891
|
+
if (!filePath) {
|
|
24892
|
+
throw new Error("Missing file_path. Usage: cngkit knowledges read <file_path>");
|
|
24893
|
+
}
|
|
24894
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24895
|
+
const response = await api.read({
|
|
24896
|
+
filePath: normalizeCatalogPath(filePath),
|
|
24897
|
+
offset: coerceOptionalNumber(options.offset),
|
|
24898
|
+
limit: coerceLimit(options.limit, 200, 2e3)
|
|
24899
|
+
});
|
|
24900
|
+
if (options.json) {
|
|
24901
|
+
output.info(formatJson(response.data));
|
|
24902
|
+
return;
|
|
24903
|
+
}
|
|
24904
|
+
output.info(response.data.content);
|
|
24905
|
+
if (response.data.truncated) {
|
|
24906
|
+
output.info(
|
|
24907
|
+
`[truncated: showing ${response.data.limit} lines from offset ${response.data.offset} of ${response.data.total_lines}]`
|
|
24908
|
+
);
|
|
24909
|
+
}
|
|
24910
|
+
}
|
|
24911
|
+
async function runKnowGrepCommand(pattern, options, output, dependencies) {
|
|
24912
|
+
if (!pattern) {
|
|
24913
|
+
throw new Error("Missing pattern. Usage: cngkit knowledges grep <pattern>");
|
|
24914
|
+
}
|
|
24915
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24916
|
+
const response = await api.grep({
|
|
24917
|
+
pattern,
|
|
24918
|
+
path: normalizeCatalogPath(options.path ?? "/"),
|
|
24919
|
+
include: options.include ?? "*",
|
|
24920
|
+
mode: normalizeGrepMode(options.outputMode),
|
|
24921
|
+
context: coerceLimit(options.context, 0, 20),
|
|
24922
|
+
ignoreCase: options.caseInsensitive === true
|
|
24923
|
+
});
|
|
24924
|
+
if (options.json) {
|
|
24925
|
+
output.info(formatJson(response.data));
|
|
24926
|
+
return;
|
|
24927
|
+
}
|
|
24928
|
+
if (response.data.mode === "files_with_matches") {
|
|
24929
|
+
output.info(
|
|
24930
|
+
response.data.files.length > 0 ? response.data.files.join("\n") : `No files matched "${pattern}".`
|
|
24931
|
+
);
|
|
24932
|
+
return;
|
|
24933
|
+
}
|
|
24934
|
+
if (response.data.mode === "count") {
|
|
24935
|
+
const lines2 = response.data.counts.map((count) => `${count.file_path}: ${count.match_count}`);
|
|
24936
|
+
output.info(lines2.length > 0 ? lines2.join("\n") : `No matches for "${pattern}".`);
|
|
24937
|
+
return;
|
|
24938
|
+
}
|
|
24939
|
+
const lines = response.data.matches.flatMap((match) => [
|
|
24940
|
+
`${match.file_path}:${match.line_number}`,
|
|
24941
|
+
...match.context_before.map((line) => ` ${line}`),
|
|
24942
|
+
`> ${match.line}`,
|
|
24943
|
+
...match.context_after.map((line) => ` ${line}`)
|
|
24944
|
+
]);
|
|
24945
|
+
output.info(lines.length > 0 ? lines.join("\n") : `No matches for "${pattern}".`);
|
|
24946
|
+
}
|
|
24947
|
+
async function runKnowGlobCommand(pattern, options, output, dependencies) {
|
|
24948
|
+
const api = dependencies?.api ?? createKnowledgesApi(options);
|
|
24949
|
+
const response = await api.glob({
|
|
24950
|
+
pattern: pattern ?? "**/*.md",
|
|
24951
|
+
path: normalizeCatalogPath(options.path ?? "/")
|
|
24952
|
+
});
|
|
24953
|
+
if (options.json) {
|
|
24954
|
+
output.info(formatJson(response.data));
|
|
24955
|
+
return;
|
|
24956
|
+
}
|
|
24957
|
+
output.info(response.data.files.length > 0 ? response.data.files.join("\n") : "No matching files.");
|
|
24958
|
+
if (response.data.truncated) {
|
|
24959
|
+
output.info(`[truncated: ${response.data.total_files} total files]`);
|
|
24960
|
+
}
|
|
24961
|
+
}
|
|
24347
24962
|
async function printBackendStatus(options, output) {
|
|
24348
24963
|
const health = await readBackendHealth(options);
|
|
24349
24964
|
output.info(health.ok ? `API: ${health.service} ready` : `API: unavailable (${health.message})`);
|
|
@@ -24366,6 +24981,121 @@ function formatScrubReport(findings) {
|
|
|
24366
24981
|
}
|
|
24367
24982
|
return lines.join("\n");
|
|
24368
24983
|
}
|
|
24984
|
+
function createKnowledgesApi(options) {
|
|
24985
|
+
const client = createCngApiClient(options);
|
|
24986
|
+
return {
|
|
24987
|
+
getCatalog: () => client.harnessKnowledges.getHarnessKnowledgesCatalog(),
|
|
24988
|
+
listAudiences: () => client.harnessKnowledges.listHarnessKnowledgesAudiences(),
|
|
24989
|
+
search: (query, limit) => client.harnessKnowledges.searchHarnessKnowledges({
|
|
24990
|
+
q: query,
|
|
24991
|
+
limit
|
|
24992
|
+
}),
|
|
24993
|
+
listSubskills: () => client.harnessKnowledges.listHarnessKnowledgesSubskills(),
|
|
24994
|
+
listFiles: ({ query, audience, limit }) => client.harnessKnowledges.listHarnessSubskillAssets({
|
|
24995
|
+
...query ? { q: query } : {},
|
|
24996
|
+
...audience ? { audience } : {},
|
|
24997
|
+
limit
|
|
24998
|
+
}),
|
|
24999
|
+
read: ({ filePath, offset, limit }) => client.harnessFilesystem.getHarnessFilesystemRead({
|
|
25000
|
+
file_path: filePath,
|
|
25001
|
+
offset,
|
|
25002
|
+
limit
|
|
25003
|
+
}),
|
|
25004
|
+
grep: ({ pattern, path: path5, include, mode, context, ignoreCase }) => client.harnessFilesystem.getHarnessFilesystemGrep({
|
|
25005
|
+
pattern,
|
|
25006
|
+
path: path5,
|
|
25007
|
+
include,
|
|
25008
|
+
output_mode: mode,
|
|
25009
|
+
context,
|
|
25010
|
+
case_insensitive: ignoreCase ? "true" : void 0
|
|
25011
|
+
}),
|
|
25012
|
+
glob: ({ pattern, path: path5 }) => client.harnessFilesystem.getHarnessFilesystemGlob({
|
|
25013
|
+
pattern,
|
|
25014
|
+
path: path5
|
|
25015
|
+
})
|
|
25016
|
+
};
|
|
25017
|
+
}
|
|
25018
|
+
function coerceLimit(value, defaultValue, maxValue = 100) {
|
|
25019
|
+
const normalizedValue = coerceOptionalNumber(value);
|
|
25020
|
+
if (normalizedValue === void 0 || Number.isNaN(normalizedValue)) {
|
|
25021
|
+
return defaultValue;
|
|
25022
|
+
}
|
|
25023
|
+
return Math.max(defaultValue === 0 ? 0 : 1, Math.min(maxValue, Math.trunc(normalizedValue)));
|
|
25024
|
+
}
|
|
25025
|
+
function coerceOptionalNumber(value) {
|
|
25026
|
+
if (value === void 0 || value === "") {
|
|
25027
|
+
return void 0;
|
|
25028
|
+
}
|
|
25029
|
+
return Number(value);
|
|
25030
|
+
}
|
|
25031
|
+
function normalizeAudienceId(value) {
|
|
25032
|
+
if (value === void 0) {
|
|
25033
|
+
return void 0;
|
|
25034
|
+
}
|
|
25035
|
+
switch (value) {
|
|
25036
|
+
case "all":
|
|
25037
|
+
case "operators":
|
|
25038
|
+
case "builders":
|
|
25039
|
+
case "researchers":
|
|
25040
|
+
case "agent-makers":
|
|
25041
|
+
return value;
|
|
25042
|
+
default:
|
|
25043
|
+
throw new Error(
|
|
25044
|
+
`Unknown audience "${value}". Run cngkit knowledges audiences to see supported values.`
|
|
25045
|
+
);
|
|
25046
|
+
}
|
|
25047
|
+
}
|
|
25048
|
+
function normalizeGrepMode(value) {
|
|
25049
|
+
if (value === void 0) {
|
|
25050
|
+
return "content";
|
|
25051
|
+
}
|
|
25052
|
+
switch (value) {
|
|
25053
|
+
case "content":
|
|
25054
|
+
case "files_with_matches":
|
|
25055
|
+
case "count":
|
|
25056
|
+
return value;
|
|
25057
|
+
default:
|
|
25058
|
+
throw new Error("Unknown grep mode. Use one of: content, files_with_matches, count.");
|
|
25059
|
+
}
|
|
25060
|
+
}
|
|
25061
|
+
function singleLine(value) {
|
|
25062
|
+
return value.replace(/\s+/g, " ").trim();
|
|
25063
|
+
}
|
|
25064
|
+
function formatJson(value) {
|
|
25065
|
+
return JSON.stringify(value, null, 2);
|
|
25066
|
+
}
|
|
25067
|
+
function optionalJoinedArgument(values) {
|
|
25068
|
+
return values.length > 0 ? values.join(" ") : void 0;
|
|
25069
|
+
}
|
|
25070
|
+
function normalizeCatalogPath(value) {
|
|
25071
|
+
const trimmed = value.trim();
|
|
25072
|
+
if (trimmed === "/" || trimmed === "") {
|
|
25073
|
+
return "/";
|
|
25074
|
+
}
|
|
25075
|
+
const relativePath = trimmed.replace(/^\/+/, "");
|
|
25076
|
+
if (relativePath.startsWith("skills/knowledges/")) {
|
|
25077
|
+
return relativePath;
|
|
25078
|
+
}
|
|
25079
|
+
const [firstSegment, ...restSegments] = relativePath.split("/");
|
|
25080
|
+
if (!firstSegment || restSegments.length === 0) {
|
|
25081
|
+
return relativePath;
|
|
25082
|
+
}
|
|
25083
|
+
switch (firstSegment) {
|
|
25084
|
+
case "concepts":
|
|
25085
|
+
case "domains":
|
|
25086
|
+
case "formats":
|
|
25087
|
+
case "languages":
|
|
25088
|
+
case "libraries":
|
|
25089
|
+
case "patterns":
|
|
25090
|
+
case "platforms":
|
|
25091
|
+
case "procedures":
|
|
25092
|
+
case "protocols":
|
|
25093
|
+
case "tools":
|
|
25094
|
+
return `skills/knowledges/subskills/${firstSegment}/${restSegments.join("/")}`;
|
|
25095
|
+
default:
|
|
25096
|
+
return relativePath;
|
|
25097
|
+
}
|
|
25098
|
+
}
|
|
24369
25099
|
|
|
24370
25100
|
// src/output.ts
|
|
24371
25101
|
var consoleOutput = {
|
|
@@ -24386,6 +25116,34 @@ function handleFatalError(error51) {
|
|
|
24386
25116
|
consoleOutput.error(formatError2(error51));
|
|
24387
25117
|
import_node_process5.default.exitCode = 1;
|
|
24388
25118
|
}
|
|
25119
|
+
function printMarkdownHelpIfRequested(argv) {
|
|
25120
|
+
const commandName = argv[2];
|
|
25121
|
+
if (commandName === void 0 || commandName === "--help" || commandName === "-h") {
|
|
25122
|
+
consoleOutput.info(formatCngkitHelp());
|
|
25123
|
+
return true;
|
|
25124
|
+
}
|
|
25125
|
+
if (commandName === "help") {
|
|
25126
|
+
const [topicName, subtopicName] = argv.slice(3);
|
|
25127
|
+
const topic = topicName === "knowledges" && subtopicName ? `knowledges-${subtopicName}` : topicName;
|
|
25128
|
+
consoleOutput.info(formatCngkitHelp(topic));
|
|
25129
|
+
return true;
|
|
25130
|
+
}
|
|
25131
|
+
const commandArgs = argv.slice(3);
|
|
25132
|
+
const helpIndex = commandArgs.findIndex((argument) => argument === "--help" || argument === "-h");
|
|
25133
|
+
if (helpIndex < 0) {
|
|
25134
|
+
return false;
|
|
25135
|
+
}
|
|
25136
|
+
if (commandName === "knowledges") {
|
|
25137
|
+
const topic = commandArgs.find((argument, index) => index !== helpIndex && !argument.startsWith("-"));
|
|
25138
|
+
consoleOutput.info(formatKnowledgesHelp(topic));
|
|
25139
|
+
return true;
|
|
25140
|
+
}
|
|
25141
|
+
consoleOutput.info(formatCngkitHelp(commandName));
|
|
25142
|
+
return true;
|
|
25143
|
+
}
|
|
25144
|
+
if (printMarkdownHelpIfRequested(import_node_process5.default.argv)) {
|
|
25145
|
+
import_node_process5.default.exit(0);
|
|
25146
|
+
}
|
|
24389
25147
|
cli.option(
|
|
24390
25148
|
"--api-base-url <url>",
|
|
24391
25149
|
"Curly API base URL. Default: CNGKIT_API_BASE_URL or https://curly.ng"
|
|
@@ -24402,6 +25160,9 @@ cli.command("join <room-code>", "Join a real-time repo sync room in the current
|
|
|
24402
25160
|
cli.command("scrub [path]", "Scan for secrets with TruffleHog and optionally mask them inline").option("--mask", "Compatibility alias for inline scrubbing; --yes is still required").option("--yes", "Confirm and run inline scrubbing").action((targetPath, options) => {
|
|
24403
25161
|
void runScrubCommand(targetPath, options, consoleOutput).catch(handleFatalError);
|
|
24404
25162
|
});
|
|
25163
|
+
cli.command("knowledges [...args]", "Use the Cloudflare-backed Harness knowledges catalog").option("--limit <n>", "Maximum results").option("--audience <id>", "Audience filter").option("--offset <n>", "Starting line offset").option("--path <path>", "Catalog path prefix").option("--include <glob>", "Filename include filter").option("--output-mode <mode>", "Output mode: content, files_with_matches, count").option("--context <n>", "Context lines around content matches").option("--case-insensitive", "Case-insensitive search").option("--json", "Print raw JSON").action((args, options) => {
|
|
25164
|
+
void runKnowledgesCommand(args, options, consoleOutput).catch(handleFatalError);
|
|
25165
|
+
});
|
|
24405
25166
|
try {
|
|
24406
25167
|
cli.parse(import_node_process5.default.argv);
|
|
24407
25168
|
} catch (error51) {
|