@chatcops/cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -2
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +4 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/config.d.ts +53 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +55 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/index.d.ts +8 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +7 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/main.d.ts +53 -0
  16. package/dist/main.d.ts.map +1 -0
  17. package/dist/main.js +247 -0
  18. package/dist/main.js.map +1 -0
  19. package/dist/test/assertions/index.d.ts +18 -0
  20. package/dist/test/assertions/index.d.ts.map +1 -0
  21. package/dist/test/assertions/index.js +50 -0
  22. package/dist/test/assertions/index.js.map +1 -0
  23. package/dist/test/assertions/javascript.d.ts +10 -0
  24. package/dist/test/assertions/javascript.d.ts.map +1 -0
  25. package/dist/test/assertions/javascript.js +51 -0
  26. package/dist/test/assertions/javascript.js.map +1 -0
  27. package/dist/test/assertions/judge.d.ts +8 -0
  28. package/dist/test/assertions/judge.d.ts.map +1 -0
  29. package/dist/test/assertions/judge.js +33 -0
  30. package/dist/test/assertions/judge.js.map +1 -0
  31. package/dist/test/assertions/parts.d.ts +12 -0
  32. package/dist/test/assertions/parts.d.ts.map +1 -0
  33. package/dist/test/assertions/parts.js +39 -0
  34. package/dist/test/assertions/parts.js.map +1 -0
  35. package/dist/test/assertions/text.d.ts +4 -0
  36. package/dist/test/assertions/text.d.ts.map +1 -0
  37. package/dist/test/assertions/text.js +10 -0
  38. package/dist/test/assertions/text.js.map +1 -0
  39. package/dist/test/assertions/tools.d.ts +13 -0
  40. package/dist/test/assertions/tools.d.ts.map +1 -0
  41. package/dist/test/assertions/tools.js +82 -0
  42. package/dist/test/assertions/tools.js.map +1 -0
  43. package/dist/test/assertions/usage.d.ts +8 -0
  44. package/dist/test/assertions/usage.d.ts.map +1 -0
  45. package/dist/test/assertions/usage.js +52 -0
  46. package/dist/test/assertions/usage.js.map +1 -0
  47. package/dist/test/assertions/values.d.ts +8 -0
  48. package/dist/test/assertions/values.d.ts.map +1 -0
  49. package/dist/test/assertions/values.js +19 -0
  50. package/dist/test/assertions/values.js.map +1 -0
  51. package/dist/test/cassette.d.ts +77 -0
  52. package/dist/test/cassette.d.ts.map +1 -0
  53. package/dist/test/cassette.js +236 -0
  54. package/dist/test/cassette.js.map +1 -0
  55. package/dist/test/command.d.ts +9 -0
  56. package/dist/test/command.d.ts.map +1 -0
  57. package/dist/test/command.js +139 -0
  58. package/dist/test/command.js.map +1 -0
  59. package/dist/test/cost.d.ts +26 -0
  60. package/dist/test/cost.d.ts.map +1 -0
  61. package/dist/test/cost.js +33 -0
  62. package/dist/test/cost.js.map +1 -0
  63. package/dist/test/judge.d.ts +32 -0
  64. package/dist/test/judge.d.ts.map +1 -0
  65. package/dist/test/judge.js +109 -0
  66. package/dist/test/judge.js.map +1 -0
  67. package/dist/test/load.d.ts +9 -0
  68. package/dist/test/load.d.ts.map +1 -0
  69. package/dist/test/load.js +98 -0
  70. package/dist/test/load.js.map +1 -0
  71. package/dist/test/reporters/index.d.ts +35 -0
  72. package/dist/test/reporters/index.d.ts.map +1 -0
  73. package/dist/test/reporters/index.js +11 -0
  74. package/dist/test/reporters/index.js.map +1 -0
  75. package/dist/test/reporters/json.d.ts +4 -0
  76. package/dist/test/reporters/json.d.ts.map +1 -0
  77. package/dist/test/reporters/json.js +8 -0
  78. package/dist/test/reporters/json.js.map +1 -0
  79. package/dist/test/reporters/pretty.d.ts +3 -0
  80. package/dist/test/reporters/pretty.d.ts.map +1 -0
  81. package/dist/test/reporters/pretty.js +119 -0
  82. package/dist/test/reporters/pretty.js.map +1 -0
  83. package/dist/test/run.d.ts +46 -0
  84. package/dist/test/run.d.ts.map +1 -0
  85. package/dist/test/run.js +157 -0
  86. package/dist/test/run.js.map +1 -0
  87. package/dist/test/types.d.ts +116 -0
  88. package/dist/test/types.d.ts.map +1 -0
  89. package/dist/test/types.js +2 -0
  90. package/dist/test/types.js.map +1 -0
  91. package/package.json +60 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CoderCops
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,57 @@
1
- # Temporary Holding Version
1
+ # @chatcops/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The `chatcops` command: build a knowledge index from your site, search it to check what your bot will find, and test your bot against conversations it has to get right.
4
+
5
+ ```bash
6
+ npx chatcops ingest --source https://example.com/sitemap.xml
7
+ npx chatcops search "how much is the Team plan?"
8
+ ```
9
+
10
+ Both read `chatcops.config.ts` (or `.mts`, `.js`, `.mjs`) in the current folder:
11
+
12
+ ```ts
13
+ import { openaiEmbedder } from '@chatcops/core';
14
+ import { defineConfig } from '@chatcops/cli';
15
+ import { postgresStorage, fromPostgresJs } from '@chatcops/store-postgres';
16
+ import postgres from 'postgres';
17
+
18
+ export default defineConfig({
19
+ embedder: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }),
20
+ storage: postgresStorage({ db: fromPostgresJs(postgres(process.env.DATABASE_URL!)), dimensions: 1024 }),
21
+ ingest: {
22
+ sources: [{ type: 'sitemap', url: 'https://example.com/sitemap.xml' }],
23
+ exclude: ['**/blog/**'],
24
+ },
25
+ });
26
+ ```
27
+
28
+ `chatcops ingest` options: `--source` (repeatable; a sitemap, a page URL, or a folder with `--base-url`), `--index`, `--dry-run`, `--max-pages`, `--concurrency`, `--prune`, `--force`, `--json`. `chatcops search` options: `--index`, `--top-k`, `--json`. Run `chatcops --help` for details.
29
+
30
+ `chatcops test` runs eval files (`evals/**/*.chat.yaml` by default) against the bot in the config's `server` section:
31
+
32
+ ```yaml
33
+ version: 1
34
+ name: Pricing
35
+ tests:
36
+ - name: quotes the Team price
37
+ turns:
38
+ - user: How much is the Team plan?
39
+ expect:
40
+ - contains: "$19"
41
+ - not-contains: "$29"
42
+ ```
43
+
44
+ Options: `--filter` (a name, or `tag:<name>`), `--repeat`, `--concurrency`, `--verbose`, `--reporter pretty|json` (repeatable), `--json`. The pretty report groups tests by file and includes run duration, assertion counts, available bot-token usage and total elapsed time. By default, assertion details are shown for failed tests; `--verbose` shows them for every test. `--record` saves every API call the tests make to `__cassettes__/` next to the eval files, and `--replay` answers them from there. Replay is the default when `CI` is set, so CI needs no API keys. `judge` assertions have a model from another provider grade a reply against a rubric (`--judge-model` to choose it), and with `prices` in the config, `--max-cost` stops a run once it has spent that much. It exits 0 when all tests passed, 1 when some failed, 2 when it couldn't run. See [Testing your bot](https://chat.codercops.com/core/testing/).
45
+
46
+ To inspect failure formatting without calling a live AI provider, build the CLI and run its deterministic fake-provider demo from the repository root:
47
+
48
+ ```bash
49
+ pnpm --filter @chatcops/cli build
50
+ node packages/cli/dist/bin.js test packages/cli/tests/fixtures/reporter-failure.chat.yaml --config packages/cli/tests/fixtures/reporter-failure.config.mjs --live --verbose
51
+ ```
52
+
53
+ The demo intentionally fails an assertion and exits with code 1; that is expected.
54
+
55
+ Exit codes for ingest and search: 0 when everything worked, 1 when it finished with failed pages or discovery errors, 2 when it couldn't run (bad options, config problems, a different embedder than the index was built with).
56
+
57
+ TypeScript config files need Node 22.18 or later; on older Node, use `chatcops.config.mjs`.
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from './main.js';
3
+ process.exitCode = await main(process.argv.slice(2), { stdout: process.stdout, stderr: process.stderr, cwd: process.cwd() });
4
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { AIProvider, ChatCopsStorage, Embedder, KeyValueStore, ProviderConfig, VectorStore } from '@chatcops/core';
2
+ import type { IngestConfig } from '@chatcops/ingest';
3
+ import type { ChatCopsServerConfig } from '@chatcops/server';
4
+ import type { Prices } from './test/cost.js';
5
+ /** `chatcops.config.ts`: what the commands index into and search. */
6
+ export interface ChatCopsCliConfig {
7
+ /** The embedder the index is built and searched with. Needed by `ingest` and `search`. */
8
+ embedder?: Embedder;
9
+ /** Storage with `vectors` and `kv` (the manifest), such as `postgresStorage(...)`. */
10
+ storage?: ChatCopsStorage;
11
+ /** Or the vector store and manifest separately. */
12
+ store?: VectorStore;
13
+ manifest?: KeyValueStore;
14
+ /** What `chatcops ingest` reads, unless `--source` says otherwise. */
15
+ ingest?: Omit<IngestConfig, 'embedder' | 'store' | 'manifest' | 'onEvent' | 'dryRun' | 'force' | 'prune'>;
16
+ /**
17
+ * The bot `chatcops test` talks to: the same config you give the server
18
+ * (provider, system prompt, tools, knowledge). Each test gets its own
19
+ * in-memory storage, so `storage` here is ignored.
20
+ */
21
+ server?: ChatCopsServerConfig;
22
+ /**
23
+ * The model that grades `judge` assertions. Defaults to the cheap model of
24
+ * a provider other than the bot's, with its key from the environment.
25
+ * `--judge-model` overrides it.
26
+ */
27
+ judge?: ProviderConfig | AIProvider;
28
+ /** US dollars per million tokens, by model id, for costs and `--max-cost`. */
29
+ prices?: Prices;
30
+ }
31
+ /** Types a config file: `export default defineConfig({ ... })`. */
32
+ export declare function defineConfig(config: ChatCopsCliConfig): ChatCopsCliConfig;
33
+ export declare const CONFIG_FILES: string[];
34
+ export declare class ConfigError extends Error {
35
+ constructor(message: string);
36
+ }
37
+ export interface LoadedConfig {
38
+ file: string;
39
+ config: ChatCopsCliConfig;
40
+ close(): Promise<void>;
41
+ }
42
+ export interface IndexConfig {
43
+ embedder: Embedder;
44
+ store: VectorStore;
45
+ manifest: KeyValueStore;
46
+ }
47
+ /** What `ingest` and `search` need from the config, or a message saying what's missing. */
48
+ export declare function indexConfig({ file, config }: LoadedConfig): IndexConfig;
49
+ /** What `test` needs from the config. */
50
+ export declare function serverConfig({ file, config }: LoadedConfig): ChatCopsServerConfig;
51
+ /** The config at `path`, or the first `chatcops.config.*` in `cwd`, checked. */
52
+ export declare function loadConfig(cwd: string, path?: string): Promise<LoadedConfig>;
53
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,sFAAsF;IACtF,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,mDAAmD;IACnD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,sEAAsE;IACtE,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC;IAC1G;;;;OAIG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC;IACpC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAEzE;AAED,eAAO,MAAM,YAAY,UAA6F,CAAC;AAEvH,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,GAAG,WAAW,CAOvE;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,GAAG,oBAAoB,CAGjF;AAED,gFAAgF;AAChF,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAsBlF"}
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ /** Types a config file: `export default defineConfig({ ... })`. */
5
+ export function defineConfig(config) {
6
+ return config;
7
+ }
8
+ export const CONFIG_FILES = ['chatcops.config.ts', 'chatcops.config.mts', 'chatcops.config.js', 'chatcops.config.mjs'];
9
+ export class ConfigError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'ConfigError';
13
+ }
14
+ }
15
+ /** What `ingest` and `search` need from the config, or a message saying what's missing. */
16
+ export function indexConfig({ file, config }) {
17
+ if (!config.embedder)
18
+ throw new ConfigError(`${file} needs an \`embedder\`, such as openaiEmbedder({ apiKey }).`);
19
+ const store = config.store ?? config.storage?.vectors;
20
+ const manifest = config.manifest ?? config.storage?.kv;
21
+ if (!store)
22
+ throw new ConfigError(`${file} needs a vector store: \`storage\` with vectors, or \`store\`.`);
23
+ if (!manifest)
24
+ throw new ConfigError(`${file} needs a manifest: \`storage\` with a key-value store, or \`manifest\`.`);
25
+ return { embedder: config.embedder, store, manifest };
26
+ }
27
+ /** What `test` needs from the config. */
28
+ export function serverConfig({ file, config }) {
29
+ if (!config.server)
30
+ throw new ConfigError(`${file} needs a \`server\` config for chatcops test: the provider, system prompt and tools of your bot.`);
31
+ return config.server;
32
+ }
33
+ /** The config at `path`, or the first `chatcops.config.*` in `cwd`, checked. */
34
+ export async function loadConfig(cwd, path) {
35
+ const file = path ? resolve(cwd, path) : CONFIG_FILES.map((name) => resolve(cwd, name)).find((candidate) => existsSync(candidate));
36
+ if (!file || !existsSync(file)) {
37
+ throw new ConfigError(path ? `No config file at ${file}.` : `No config file found. Create chatcops.config.ts (or .mjs) in ${cwd}, or pass --config.`);
38
+ }
39
+ let config;
40
+ try {
41
+ const module = (await import(pathToFileURL(file).href));
42
+ config = module.default;
43
+ }
44
+ catch (error) {
45
+ const code = error.code;
46
+ if (code === 'ERR_UNKNOWN_FILE_EXTENSION' && /\.m?ts$/.test(file)) {
47
+ throw new ConfigError(`This Node.js can't load TypeScript config files. Use Node 22.18 or later, or rename ${file} to chatcops.config.mjs.`);
48
+ }
49
+ throw new ConfigError(`Couldn't load ${file}: ${error instanceof Error ? error.message : String(error)}`);
50
+ }
51
+ if (!config || typeof config !== 'object')
52
+ throw new ConfigError(`${file} has no default export. Export the config with \`export default defineConfig({ ... })\`.`);
53
+ return { file, config, close: async () => void (await config.storage?.close?.()) };
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { defineConfig, loadConfig, ConfigError, CONFIG_FILES, type ChatCopsCliConfig, type LoadedConfig } from './config.js';
2
+ export { runFixtures, type RunOptions } from './test/run.js';
3
+ export { loadFixture, findFixtures } from './test/load.js';
4
+ export { ASSERTIONS } from './test/assertions/index.js';
5
+ export { REPORTERS, type Reporter, type ReporterFactory, type ReporterOutput } from './test/reporters/index.js';
6
+ export type { Assertion, AssertionCheck, AssertionResult, Fixture, TestCase, TestResult, TestRunResult, Turn, TurnResult } from './test/types.js';
7
+ export { main, toSource, EXIT, type Io } from './main.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7H,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChH,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClJ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { defineConfig, loadConfig, ConfigError, CONFIG_FILES } from './config.js';
2
+ export { runFixtures } from './test/run.js';
3
+ export { loadFixture, findFixtures } from './test/load.js';
4
+ export { ASSERTIONS } from './test/assertions/index.js';
5
+ export { REPORTERS } from './test/reporters/index.js';
6
+ export { main, toSource, EXIT } from './main.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAA6C,MAAM,aAAa,CAAC;AAC7H,OAAO,EAAE,WAAW,EAAmB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,SAAS,EAA4D,MAAM,2BAA2B,CAAC;AAEhH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAW,MAAM,WAAW,CAAC"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { type IngestSource } from '@chatcops/ingest';
2
+ export interface Io {
3
+ stdout: {
4
+ write(text: string): unknown;
5
+ };
6
+ stderr: {
7
+ write(text: string): unknown;
8
+ };
9
+ cwd: string;
10
+ /** Defaults to `process.env`. `chatcops test` reads `CI`. */
11
+ env?: Record<string, string | undefined>;
12
+ }
13
+ /** Exit codes: 0 done, 1 finished with problems (failed pages, discovery errors), 2 couldn't run. */
14
+ export declare const EXIT: {
15
+ readonly ok: 0;
16
+ readonly problems: 1;
17
+ readonly unusable: 2;
18
+ };
19
+ /** Runs the command line and returns the exit code. */
20
+ export declare function main(argv: string[], io: Io): Promise<number>;
21
+ declare function parse(argv: string[]): {
22
+ values: {
23
+ config?: string | undefined;
24
+ source?: string[] | undefined;
25
+ 'base-url'?: string | undefined;
26
+ index?: string | undefined;
27
+ 'dry-run'?: boolean | undefined;
28
+ 'max-pages'?: string | undefined;
29
+ concurrency?: string | undefined;
30
+ prune?: boolean | undefined;
31
+ force?: boolean | undefined;
32
+ json?: boolean | undefined;
33
+ 'top-k'?: string | undefined;
34
+ filter?: string | undefined;
35
+ repeat?: string | undefined;
36
+ verbose?: boolean | undefined;
37
+ reporter?: string[] | undefined;
38
+ record?: boolean | undefined;
39
+ live?: boolean | undefined;
40
+ 'judge-model'?: string | undefined;
41
+ 'max-cost'?: string | undefined;
42
+ replay?: boolean | undefined;
43
+ help?: boolean | undefined;
44
+ version?: boolean | undefined;
45
+ };
46
+ positionals: string[];
47
+ };
48
+ export type Values = ReturnType<typeof parse>['values'];
49
+ /** A `--source` value: a sitemap URL, a page URL, or a folder (with `--base-url`). */
50
+ export declare function toSource(value: string, baseUrl: string | undefined, cwd: string): IngestSource;
51
+ export declare function wholeNumber(value: string | undefined, flag: string): number | undefined;
52
+ export {};
53
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAIA,OAAO,EAA0C,KAAK,YAAY,EAAoB,MAAM,kBAAkB,CAAC;AAK/G,MAAM,WAAW,EAAE;IACjB,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACzC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,qGAAqG;AACrG,eAAO,MAAM,IAAI;;;;CAA+C,CAAC;AAsDjE,uDAAuD;AACvD,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ClE;AAED,iBAAS,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B5B;AAED,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;AA6DxD,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAM9F;AA2BD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIvF"}
package/dist/main.js ADDED
@@ -0,0 +1,247 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { parseArgs } from 'node:util';
4
+ import { hybridRetriever, vectorRetriever } from '@chatcops/core';
5
+ import { ingest, manifestKeys } from '@chatcops/ingest';
6
+ import { ConfigError, indexConfig, loadConfig, serverConfig } from './config.js';
7
+ import { Cassettes } from './test/cassette.js';
8
+ import { cassetteMode, runTests } from './test/command.js';
9
+ /** Exit codes: 0 done, 1 finished with problems (failed pages, discovery errors), 2 couldn't run. */
10
+ export const EXIT = { ok: 0, problems: 1, unusable: 2 };
11
+ const VERSION = (() => {
12
+ try {
13
+ return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
14
+ }
15
+ catch {
16
+ return '0.0.0';
17
+ }
18
+ })();
19
+ const HELP = `chatcops ${VERSION}
20
+
21
+ Usage:
22
+ chatcops ingest [options] Build or update the knowledge index from your site
23
+ chatcops search "<question>" [options] Show what retrieval finds for a question
24
+ chatcops test [files...] [options] Run eval files against your bot
25
+
26
+ Ingest options:
27
+ --source <url|path> A sitemap (.xml, .xml.gz), a page URL, or a folder of .md/.mdx/.html
28
+ files (with --base-url). Repeatable. Replaces the config's sources.
29
+ --base-url <url> Where a folder source is published
30
+ --index <name> Index name (default "default")
31
+ --dry-run Discover, extract and chunk; write and embed nothing
32
+ --max-pages <n> Index at most n pages
33
+ --concurrency <n> Pages fetched at once (default 4)
34
+ --prune Remove pages that are gone (never after a discovery error)
35
+ --force Re-embed every page, even unchanged ones
36
+ --json One JSON object per line, for CI
37
+
38
+ Search options:
39
+ --index <name> Index name (default "default")
40
+ --top-k <n> Chunks to show (default 5)
41
+ --json JSON output
42
+
43
+ Test options:
44
+ [files...] Globs of eval files (default "evals/**/*.chat.yaml")
45
+ --filter <text> Only tests whose name contains text, or tag:<name>
46
+ --repeat <n> Run each test n times
47
+ --concurrency <n> Tests run at once (default 4)
48
+ --verbose Show assertion details for every test
49
+ --reporter <name> pretty (default) or json. Repeatable.
50
+ --json Same as --reporter json
51
+ --record Call the APIs and save what they answer to __cassettes__
52
+ --replay Answer every API call from __cassettes__ (the default when CI is set)
53
+ --live Call the APIs, even when CI is set
54
+ --judge-model <p:m> The judge for judge assertions, as provider:model (gemini, openai, claude)
55
+ --max-cost <usd> Stop starting turns once the run has spent this (needs prices in the config)
56
+
57
+ All commands:
58
+ --config <file> Config file (default: chatcops.config.ts, .mts, .js or .mjs here)
59
+ -h, --help Show this help
60
+ -v, --version Show the version
61
+ `;
62
+ /** Runs the command line and returns the exit code. */
63
+ export async function main(argv, io) {
64
+ let parsed;
65
+ try {
66
+ parsed = parse(argv);
67
+ }
68
+ catch (error) {
69
+ io.stderr.write(`${message(error)}\n\nRun chatcops --help for usage.\n`);
70
+ return EXIT.unusable;
71
+ }
72
+ const { values, positionals } = parsed;
73
+ if (values.version) {
74
+ io.stdout.write(`${VERSION}\n`);
75
+ return EXIT.ok;
76
+ }
77
+ const [command, ...rest] = positionals;
78
+ if (values.help || !command) {
79
+ io.stdout.write(HELP);
80
+ return command || values.help ? EXIT.ok : EXIT.unusable;
81
+ }
82
+ try {
83
+ if (command === 'ingest')
84
+ return await runIngest(values, io);
85
+ if (command === 'search')
86
+ return await runSearch(rest.join(' '), values, io);
87
+ if (command === 'test') {
88
+ const cassettes = new Cassettes(cassetteMode(values, io.env ?? process.env));
89
+ // Before the config loads, so the providers and embedders it creates use it.
90
+ const uninstall = cassettes.mode === 'live' ? () => { } : cassettes.install();
91
+ try {
92
+ const loaded = await loadConfig(io.cwd, values.config);
93
+ try {
94
+ return await runTests(serverConfig(loaded), rest, values, io, cassettes, loaded.config);
95
+ }
96
+ finally {
97
+ await loaded.close();
98
+ }
99
+ }
100
+ finally {
101
+ uninstall();
102
+ }
103
+ }
104
+ io.stderr.write(`Unknown command "${command}". Run chatcops --help for usage.\n`);
105
+ return EXIT.unusable;
106
+ }
107
+ catch (error) {
108
+ io.stderr.write(`${error instanceof ConfigError ? '' : 'Error: '}${message(error)}\n`);
109
+ return EXIT.unusable;
110
+ }
111
+ }
112
+ function parse(argv) {
113
+ return parseArgs({
114
+ args: argv,
115
+ allowPositionals: true,
116
+ options: {
117
+ config: { type: 'string' },
118
+ source: { type: 'string', multiple: true },
119
+ 'base-url': { type: 'string' },
120
+ index: { type: 'string' },
121
+ 'dry-run': { type: 'boolean' },
122
+ 'max-pages': { type: 'string' },
123
+ concurrency: { type: 'string' },
124
+ prune: { type: 'boolean' },
125
+ force: { type: 'boolean' },
126
+ json: { type: 'boolean' },
127
+ 'top-k': { type: 'string' },
128
+ filter: { type: 'string' },
129
+ repeat: { type: 'string' },
130
+ verbose: { type: 'boolean' },
131
+ reporter: { type: 'string', multiple: true },
132
+ record: { type: 'boolean' },
133
+ live: { type: 'boolean' },
134
+ 'judge-model': { type: 'string' },
135
+ 'max-cost': { type: 'string' },
136
+ replay: { type: 'boolean' },
137
+ help: { type: 'boolean', short: 'h' },
138
+ version: { type: 'boolean', short: 'v' },
139
+ },
140
+ });
141
+ }
142
+ async function runIngest(values, io) {
143
+ const maxPages = wholeNumber(values['max-pages'], '--max-pages');
144
+ const concurrency = wholeNumber(values.concurrency, '--concurrency');
145
+ const loaded = await loadConfig(io.cwd, values.config);
146
+ try {
147
+ const target = indexConfig(loaded);
148
+ const sources = values.source?.length ? values.source.map((source) => toSource(source, values['base-url'], io.cwd)) : loaded.config.ingest?.sources;
149
+ if (!sources?.length)
150
+ throw new ConfigError('Nothing to ingest: pass --source, or set ingest.sources in the config.');
151
+ const json = !!values.json;
152
+ const stats = await ingest({
153
+ ...loaded.config.ingest,
154
+ sources,
155
+ ...(values.index ? { index: values.index } : {}),
156
+ ...(maxPages !== undefined ? { maxPages } : {}),
157
+ ...(concurrency !== undefined ? { concurrency } : {}),
158
+ embedder: target.embedder,
159
+ store: target.store,
160
+ manifest: target.manifest,
161
+ dryRun: !!values['dry-run'],
162
+ force: !!values.force,
163
+ prune: !!values.prune,
164
+ onEvent: (event) => (json ? io.stdout.write(`${JSON.stringify(event)}\n`) : io.stderr.write(describe(event))),
165
+ });
166
+ if (json)
167
+ io.stdout.write(`${JSON.stringify({ type: 'done', dryRun: !!values['dry-run'], stats })}\n`);
168
+ else
169
+ io.stdout.write(summary(stats, !!values['dry-run']));
170
+ return stats.failed || stats.errors.length ? EXIT.problems : EXIT.ok;
171
+ }
172
+ finally {
173
+ await loaded.close();
174
+ }
175
+ }
176
+ async function runSearch(question, values, io) {
177
+ if (!question.trim())
178
+ throw new ConfigError('What should I search for? chatcops search "how much is the Team plan?"');
179
+ const topK = wholeNumber(values['top-k'], '--top-k') ?? 5;
180
+ const loaded = await loadConfig(io.cwd, values.config);
181
+ try {
182
+ const index = values.index ?? 'default';
183
+ const { embedder, store, manifest } = indexConfig(loaded);
184
+ const meta = await manifest.get(manifestKeys(index).meta);
185
+ if (meta && meta.embedder !== embedder.id) {
186
+ throw new ConfigError(`Index "${index}" was built with ${meta.embedder}, but the config's embedder is ${embedder.id}. Search it with the embedder that built it.`);
187
+ }
188
+ if (!meta)
189
+ io.stderr.write(`Index "${index}" hasn't been built yet. Run chatcops ingest first.\n`);
190
+ const options = { store, embedder };
191
+ const retriever = store.keywordQuery ? hybridRetriever(options) : vectorRetriever(options);
192
+ const chunks = await retriever.retrieve({ text: question, topK });
193
+ if (values.json)
194
+ io.stdout.write(`${JSON.stringify({ question, index, chunks })}\n`);
195
+ else
196
+ io.stdout.write(chunks.length ? chunks.map(formatChunk).join('\n') : 'Nothing found.\n');
197
+ return EXIT.ok;
198
+ }
199
+ finally {
200
+ await loaded.close();
201
+ }
202
+ }
203
+ /** A `--source` value: a sitemap URL, a page URL, or a folder (with `--base-url`). */
204
+ export function toSource(value, baseUrl, cwd) {
205
+ if (/^https?:\/\//i.test(value)) {
206
+ return /\.xml(\.gz)?(\?.*)?$/i.test(value) ? { type: 'sitemap', url: value } : { type: 'urls', urls: [value] };
207
+ }
208
+ if (!baseUrl)
209
+ throw new ConfigError(`--source ${value} is a folder, so it needs --base-url (where those pages are published).`);
210
+ return { type: 'directory', path: resolve(cwd, value), baseUrl };
211
+ }
212
+ function describe(event) {
213
+ if (event.type === 'discovered') {
214
+ return `Found ${event.pages} page${event.pages === 1 ? '' : 's'}.${event.errors.map((error) => `\n ! ${error}`).join('')}\n`;
215
+ }
216
+ if (event.type === 'pruned')
217
+ return ` removed ${event.url}\n`;
218
+ const detail = event.chunks !== undefined ? ` (${event.chunks} chunk${event.chunks === 1 ? '' : 's'})` : event.reason ? ` (${event.reason})` : '';
219
+ return ` ${event.status.padEnd(10)} ${event.url}${detail}\n`;
220
+ }
221
+ function summary(stats, dryRun) {
222
+ const lines = [
223
+ `${dryRun ? 'Would index' : 'Indexed'} ${stats.indexed} of ${stats.discovered} pages: ${stats.chunks} chunks, ${stats.embedded} embedded.`,
224
+ `${stats.unchanged} unchanged, ${stats.skipped} skipped, ${stats.failed} failed, ${stats.pruned} ${dryRun ? 'to remove' : 'removed'}.`,
225
+ ];
226
+ if (stats.pruneSkipped)
227
+ lines.push(`Didn't remove anything: ${stats.pruneSkipped}.`);
228
+ if (stats.errors.length)
229
+ lines.push(...stats.errors.map((error) => `! ${error}`));
230
+ return `${lines.join('\n')}\n`;
231
+ }
232
+ function formatChunk(chunk, i) {
233
+ const title = [chunk.source.title, chunk.source.section].filter(Boolean).join(' > ') || chunk.source.url;
234
+ const text = chunk.text.replace(/\s+/g, ' ').trim();
235
+ return `${i + 1}. ${title} (${chunk.score.toFixed(4)})\n ${chunk.source.url}\n ${text.length > 240 ? `${text.slice(0, 239)}…` : text}\n`;
236
+ }
237
+ export function wholeNumber(value, flag) {
238
+ if (value === undefined)
239
+ return undefined;
240
+ if (!/^\d+$/.test(value))
241
+ throw new ConfigError(`${flag} takes a whole number, not "${value}".`);
242
+ return Number(value);
243
+ }
244
+ function message(error) {
245
+ return error instanceof Error ? error.message : String(error);
246
+ }
247
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Assertion, AssertionCheck, AssertionContext, AssertionResult, TurnResult } from '../types.js';
2
+ /**
3
+ * The checks an eval file can use, by key. Every key also works with a
4
+ * `not-` prefix, which passes when the check fails (`not-contains`).
5
+ *
6
+ * To add one, write it in its own file in this folder and add it here.
7
+ */
8
+ export declare const ASSERTIONS: Map<string, AssertionCheck>;
9
+ /** The base name of an assertion and whether it's negated: `not-contains` is `contains`, negated. */
10
+ export declare function assertionName(assertion: Assertion): {
11
+ key: string;
12
+ name: string;
13
+ negated: boolean;
14
+ };
15
+ /** Assertion keys a fixture uses that no check exists for. */
16
+ export declare function unknownAssertions(assertions: Assertion[]): string[];
17
+ export declare function check(assertion: Assertion, turn: TurnResult, conversation: TurnResult[], context?: AssertionContext): Promise<AssertionResult>;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/assertions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ5G;;;;;GAKG;AACH,eAAO,MAAM,UAAU,6BAWrB,CAAC;AAEH,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAInG;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAEnE;AAED,wBAAsB,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,gBAAkC,GAAG,OAAO,CAAC,eAAe,CAAC,CAWrK"}