@chat-js/cli 0.2.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -3914,17 +3914,17 @@ var {
3914
3914
  // package.json
3915
3915
  var package_default = {
3916
3916
  name: "@chat-js/cli",
3917
- version: "0.2.0",
3917
+ version: "0.3.0",
3918
3918
  description: "CLI for creating and extending ChatJS apps",
3919
3919
  license: "Apache-2.0",
3920
3920
  repository: {
3921
3921
  type: "git",
3922
- url: "https://github.com/franciscomoretti/chat.js.git",
3922
+ url: "https://github.com/franciscomoretti/chat-js.git",
3923
3923
  directory: "packages/cli"
3924
3924
  },
3925
- homepage: "https://github.com/franciscomoretti/chat.js/tree/main/packages/cli",
3925
+ homepage: "https://github.com/franciscomoretti/chat-js/tree/main/packages/cli",
3926
3926
  bugs: {
3927
- url: "https://github.com/franciscomoretti/chat.js/issues"
3927
+ url: "https://github.com/franciscomoretti/chat-js/issues"
3928
3928
  },
3929
3929
  keywords: [
3930
3930
  "chatjs",
@@ -17611,6 +17611,71 @@ var add = new Command().name("add").description("add a component or feature to a
17611
17611
  }
17612
17612
  });
17613
17613
 
17614
+ // src/commands/config.ts
17615
+ import { spawn } from "node:child_process";
17616
+ import path from "node:path";
17617
+
17618
+ // src/utils/get-package-manager.ts
17619
+ function inferPackageManager() {
17620
+ const ua = process.env.npm_config_user_agent ?? "";
17621
+ if (ua.startsWith("pnpm/"))
17622
+ return "pnpm";
17623
+ if (ua.startsWith("yarn/"))
17624
+ return "yarn";
17625
+ if (ua.startsWith("npm/"))
17626
+ return "npm";
17627
+ if (ua.startsWith("bun/"))
17628
+ return "bun";
17629
+ return "bun";
17630
+ }
17631
+
17632
+ // src/commands/config.ts
17633
+ var EVAL_SCRIPT = `
17634
+ import userConfig from "./chat.config.ts";
17635
+ import { applyDefaults } from "./lib/config-schema";
17636
+ console.log(JSON.stringify(applyDefaults(userConfig), null, 2));
17637
+ `;
17638
+ function getTsEvalCommand(pm) {
17639
+ switch (pm) {
17640
+ case "bun":
17641
+ return ["bun", ["--eval", EVAL_SCRIPT]];
17642
+ case "pnpm":
17643
+ return ["pnpm", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
17644
+ case "yarn":
17645
+ return ["yarn", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
17646
+ default:
17647
+ return ["npx", ["tsx", "--eval", EVAL_SCRIPT]];
17648
+ }
17649
+ }
17650
+ var config2 = new Command().name("config").description("print the resolved configuration for the current ChatJS project").option("-c, --cwd <cwd>", "the working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
17651
+ try {
17652
+ const cwd = path.resolve(opts.cwd);
17653
+ const pm = inferPackageManager();
17654
+ const [cmd, args] = getTsEvalCommand(pm);
17655
+ await new Promise((resolve, reject) => {
17656
+ const child = spawn(cmd, args, {
17657
+ cwd,
17658
+ stdio: ["ignore", "inherit", "pipe"]
17659
+ });
17660
+ const stderr = [];
17661
+ child.stderr?.on("data", (data) => stderr.push(String(data)));
17662
+ child.on("error", (err) => {
17663
+ reject(new Error(`Could not spawn ${cmd}. Make sure ${pm} is installed.
17664
+ ${err.message}`));
17665
+ });
17666
+ child.on("close", (code) => {
17667
+ if (code === 0)
17668
+ resolve();
17669
+ else
17670
+ reject(new Error(`Failed to resolve config:
17671
+ ${stderr.join("").trim()}`));
17672
+ });
17673
+ });
17674
+ } catch (error48) {
17675
+ handleError(error48);
17676
+ }
17677
+ });
17678
+
17614
17679
  // src/commands/create.ts
17615
17680
  import { readFile, writeFile } from "node:fs/promises";
17616
17681
  import { join as join2, resolve as resolve2 } from "node:path";
@@ -18661,7 +18726,7 @@ ${l}
18661
18726
  } }).prompt();
18662
18727
 
18663
18728
  // ../../apps/chat/lib/ai/gateway-model-defaults.ts
18664
- var multiProviderDefaults = {
18729
+ var vercelDefaults = {
18665
18730
  providerOrder: ["openai", "google", "anthropic"],
18666
18731
  disabledModels: [],
18667
18732
  curatedDefaults: [
@@ -18684,36 +18749,66 @@ var multiProviderDefaults = {
18684
18749
  chatImageCompatible: "openai/gpt-4o-mini"
18685
18750
  },
18686
18751
  tools: {
18687
- webSearch: {
18688
- enabled: false
18689
- },
18690
- urlRetrieval: {
18691
- enabled: false
18692
- },
18693
- codeExecution: {
18694
- enabled: false
18695
- },
18696
- mcp: {
18697
- enabled: false
18698
- },
18752
+ webSearch: { enabled: false },
18753
+ urlRetrieval: { enabled: false },
18754
+ codeExecution: { enabled: false },
18755
+ mcp: { enabled: false },
18699
18756
  followupSuggestions: {
18700
18757
  enabled: false,
18701
18758
  default: "google/gemini-2.5-flash-lite"
18702
18759
  },
18703
- text: {
18704
- polish: "openai/gpt-5-mini"
18705
- },
18706
- sheet: {
18707
- format: "openai/gpt-5-mini",
18708
- analyze: "openai/gpt-5-mini"
18709
- },
18710
- code: {
18711
- edits: "openai/gpt-5-mini"
18712
- },
18713
- image: {
18760
+ text: { polish: "openai/gpt-5-mini" },
18761
+ sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
18762
+ code: { edits: "openai/gpt-5-mini" },
18763
+ image: { enabled: false, default: "google/gemini-3-pro-image" },
18764
+ video: { enabled: false, default: "xai/grok-imagine-video" },
18765
+ deepResearch: {
18714
18766
  enabled: false,
18715
- default: "google/gemini-3-pro-image"
18767
+ defaultModel: "google/gemini-2.5-flash-lite",
18768
+ finalReportModel: "google/gemini-3-flash",
18769
+ allowClarification: true,
18770
+ maxResearcherIterations: 1,
18771
+ maxConcurrentResearchUnits: 2,
18772
+ maxSearchQueries: 2
18773
+ }
18774
+ }
18775
+ };
18776
+ var openrouterDefaults = {
18777
+ providerOrder: ["openai", "google", "anthropic"],
18778
+ disabledModels: [],
18779
+ curatedDefaults: [
18780
+ "openai/gpt-5-nano",
18781
+ "openai/gpt-5-mini",
18782
+ "openai/gpt-5.2",
18783
+ "openai/gpt-5.2-chat",
18784
+ "google/gemini-2.5-flash-lite",
18785
+ "google/gemini-3-flash",
18786
+ "google/gemini-3-pro-preview",
18787
+ "anthropic/claude-sonnet-4.5",
18788
+ "anthropic/claude-opus-4.5",
18789
+ "xai/grok-4"
18790
+ ],
18791
+ anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
18792
+ workflows: {
18793
+ chat: "openai/gpt-5-mini",
18794
+ title: "openai/gpt-5-nano",
18795
+ pdf: "openai/gpt-5-mini",
18796
+ chatImageCompatible: "openai/gpt-4o-mini"
18797
+ },
18798
+ tools: {
18799
+ webSearch: { enabled: false },
18800
+ urlRetrieval: { enabled: false },
18801
+ codeExecution: { enabled: false },
18802
+ mcp: { enabled: false },
18803
+ followupSuggestions: {
18804
+ enabled: false,
18805
+ default: "google/gemini-2.5-flash-lite"
18716
18806
  },
18807
+ text: { polish: "openai/gpt-5-mini" },
18808
+ sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
18809
+ code: { edits: "openai/gpt-5-mini" },
18810
+ image: { enabled: false },
18811
+ video: { enabled: false },
18717
18812
  deepResearch: {
18718
18813
  enabled: false,
18719
18814
  defaultModel: "google/gemini-2.5-flash-lite",
@@ -18725,7 +18820,7 @@ var multiProviderDefaults = {
18725
18820
  }
18726
18821
  }
18727
18822
  };
18728
- var openaiOnlyDefaults = {
18823
+ var openaiDefaults = {
18729
18824
  providerOrder: ["openai"],
18730
18825
  disabledModels: [],
18731
18826
  curatedDefaults: [
@@ -18742,36 +18837,54 @@ var openaiOnlyDefaults = {
18742
18837
  chatImageCompatible: "gpt-4o-mini"
18743
18838
  },
18744
18839
  tools: {
18745
- webSearch: {
18746
- enabled: false
18747
- },
18748
- urlRetrieval: {
18749
- enabled: false
18750
- },
18751
- codeExecution: {
18752
- enabled: false
18753
- },
18754
- mcp: {
18755
- enabled: false
18756
- },
18757
- followupSuggestions: {
18758
- enabled: false,
18759
- default: "gpt-5-nano"
18760
- },
18761
- text: {
18762
- polish: "gpt-5-mini"
18763
- },
18764
- sheet: {
18765
- format: "gpt-5-mini",
18766
- analyze: "gpt-5-mini"
18767
- },
18768
- code: {
18769
- edits: "gpt-5-mini"
18770
- },
18771
- image: {
18840
+ webSearch: { enabled: false },
18841
+ urlRetrieval: { enabled: false },
18842
+ codeExecution: { enabled: false },
18843
+ mcp: { enabled: false },
18844
+ followupSuggestions: { enabled: false, default: "gpt-5-nano" },
18845
+ text: { polish: "gpt-5-mini" },
18846
+ sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
18847
+ code: { edits: "gpt-5-mini" },
18848
+ image: { enabled: false },
18849
+ video: { enabled: false },
18850
+ deepResearch: {
18772
18851
  enabled: false,
18773
- default: "gpt-image-1"
18774
- },
18852
+ defaultModel: "gpt-5-nano",
18853
+ finalReportModel: "gpt-5-mini",
18854
+ allowClarification: true,
18855
+ maxResearcherIterations: 1,
18856
+ maxConcurrentResearchUnits: 2,
18857
+ maxSearchQueries: 2
18858
+ }
18859
+ }
18860
+ };
18861
+ var openaiCompatibleDefaults = {
18862
+ providerOrder: ["openai"],
18863
+ disabledModels: [],
18864
+ curatedDefaults: [
18865
+ "gpt-5-nano",
18866
+ "gpt-5-mini",
18867
+ "gpt-5.2",
18868
+ "gpt-5.2-chat-latest"
18869
+ ],
18870
+ anonymousModels: ["gpt-5-nano"],
18871
+ workflows: {
18872
+ chat: "gpt-5-mini",
18873
+ title: "gpt-5-nano",
18874
+ pdf: "gpt-5-mini",
18875
+ chatImageCompatible: "gpt-4o-mini"
18876
+ },
18877
+ tools: {
18878
+ webSearch: { enabled: false },
18879
+ urlRetrieval: { enabled: false },
18880
+ codeExecution: { enabled: false },
18881
+ mcp: { enabled: false },
18882
+ followupSuggestions: { enabled: false, default: "gpt-5-nano" },
18883
+ text: { polish: "gpt-5-mini" },
18884
+ sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
18885
+ code: { edits: "gpt-5-mini" },
18886
+ image: { enabled: false },
18887
+ video: { enabled: false },
18775
18888
  deepResearch: {
18776
18889
  enabled: false,
18777
18890
  defaultModel: "gpt-5-nano",
@@ -18784,10 +18897,10 @@ var openaiOnlyDefaults = {
18784
18897
  }
18785
18898
  };
18786
18899
  var GATEWAY_MODEL_DEFAULTS = {
18787
- vercel: multiProviderDefaults,
18788
- openrouter: multiProviderDefaults,
18789
- openai: openaiOnlyDefaults,
18790
- "openai-compatible": openaiOnlyDefaults
18900
+ vercel: vercelDefaults,
18901
+ openrouter: openrouterDefaults,
18902
+ openai: openaiDefaults,
18903
+ "openai-compatible": openaiCompatibleDefaults
18791
18904
  };
18792
18905
 
18793
18906
  // ../../apps/chat/lib/config-schema.ts
@@ -18850,14 +18963,26 @@ function createAiSchema(g) {
18850
18963
  code: exports_external.object({
18851
18964
  edits: gatewayModelId()
18852
18965
  }),
18853
- image: exports_external.object({
18854
- enabled: exports_external.boolean(),
18855
- default: gatewayImageModelId()
18856
- }),
18857
- video: exports_external.object({
18858
- enabled: exports_external.boolean(),
18859
- default: gatewayVideoModelId()
18860
- }),
18966
+ image: exports_external.discriminatedUnion("enabled", [
18967
+ exports_external.object({
18968
+ enabled: exports_external.literal(true),
18969
+ default: gatewayImageModelId()
18970
+ }),
18971
+ exports_external.object({
18972
+ enabled: exports_external.literal(false),
18973
+ default: gatewayImageModelId().optional()
18974
+ })
18975
+ ]),
18976
+ video: exports_external.discriminatedUnion("enabled", [
18977
+ exports_external.object({
18978
+ enabled: exports_external.literal(true),
18979
+ default: gatewayVideoModelId()
18980
+ }),
18981
+ exports_external.object({
18982
+ enabled: exports_external.literal(false),
18983
+ default: gatewayVideoModelId().optional()
18984
+ })
18985
+ ]),
18861
18986
  deepResearch: deepResearchToolConfigSchema.extend({
18862
18987
  enabled: exports_external.boolean(),
18863
18988
  defaultModel: gatewayModelId(),
@@ -18879,72 +19004,7 @@ var aiConfigSchema = exports_external.discriminatedUnion("gateway", [
18879
19004
  gatewaySchemaMap["openai-compatible"]
18880
19005
  ]).default({
18881
19006
  gateway: DEFAULT_GATEWAY,
18882
- providerOrder: ["openai", "google", "anthropic"],
18883
- disabledModels: [],
18884
- curatedDefaults: [
18885
- "openai/gpt-5-nano",
18886
- "openai/gpt-5-mini",
18887
- "openai/gpt-5.2",
18888
- "openai/gpt-5.2-chat",
18889
- "google/gemini-2.5-flash-lite",
18890
- "google/gemini-3-flash",
18891
- "google/gemini-3-pro-preview",
18892
- "anthropic/claude-sonnet-4.5",
18893
- "anthropic/claude-opus-4.5",
18894
- "xai/grok-4"
18895
- ],
18896
- anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
18897
- workflows: {
18898
- chat: "openai/gpt-5-mini",
18899
- title: "openai/gpt-5-nano",
18900
- pdf: "openai/gpt-5-mini",
18901
- chatImageCompatible: "openai/gpt-4o-mini"
18902
- },
18903
- tools: {
18904
- webSearch: {
18905
- enabled: false
18906
- },
18907
- urlRetrieval: {
18908
- enabled: false
18909
- },
18910
- codeExecution: {
18911
- enabled: false
18912
- },
18913
- mcp: {
18914
- enabled: false
18915
- },
18916
- followupSuggestions: {
18917
- enabled: false,
18918
- default: "google/gemini-2.5-flash-lite"
18919
- },
18920
- text: {
18921
- polish: "openai/gpt-5-mini"
18922
- },
18923
- sheet: {
18924
- format: "openai/gpt-5-mini",
18925
- analyze: "openai/gpt-5-mini"
18926
- },
18927
- code: {
18928
- edits: "openai/gpt-5-mini"
18929
- },
18930
- image: {
18931
- enabled: false,
18932
- default: "google/gemini-3-pro-image"
18933
- },
18934
- video: {
18935
- enabled: false,
18936
- default: "xai/grok-imagine-video"
18937
- },
18938
- deepResearch: {
18939
- enabled: false,
18940
- defaultModel: "google/gemini-2.5-flash-lite",
18941
- finalReportModel: "google/gemini-3-flash",
18942
- allowClarification: true,
18943
- maxResearcherIterations: 1,
18944
- maxConcurrentResearchUnits: 2,
18945
- maxSearchQueries: 2
18946
- }
18947
- }
19007
+ ...GATEWAY_MODEL_DEFAULTS[DEFAULT_GATEWAY]
18948
19008
  });
18949
19009
  var pricingConfigSchema = exports_external.object({
18950
19010
  currency: exports_external.string().optional(),
@@ -19074,8 +19134,8 @@ function extractDescriptions(schema, prefix = "", result = new Map) {
19074
19134
  if (unwrapped instanceof exports_external.ZodObject) {
19075
19135
  const shape = unwrapped._zod.def.shape;
19076
19136
  for (const [key, propSchema] of Object.entries(shape)) {
19077
- const path = prefix ? `${prefix}.${key}` : key;
19078
- extractDescriptions(propSchema, path, result);
19137
+ const path2 = prefix ? `${prefix}.${key}` : key;
19138
+ extractDescriptions(propSchema, path2, result);
19079
19139
  }
19080
19140
  }
19081
19141
  return result;
@@ -19117,11 +19177,11 @@ ${spaces}}`;
19117
19177
  function generateConfig(obj, indent, pathPrefix, descs) {
19118
19178
  const spaces = " ".repeat(indent);
19119
19179
  return Object.entries(obj).map(([key, value]) => {
19120
- const path = pathPrefix ? `${pathPrefix}.${key}` : key;
19121
- const desc = descs.get(path);
19180
+ const path2 = pathPrefix ? `${pathPrefix}.${key}` : key;
19181
+ const desc = descs.get(path2);
19122
19182
  const comment = desc ? ` // ${desc}` : "";
19123
19183
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
19124
- const nested = generateConfig(value, indent + 1, path, descs);
19184
+ const nested = generateConfig(value, indent + 1, path2, descs);
19125
19185
  return `${spaces}${formatKey(key)}: {
19126
19186
  ${nested}
19127
19187
  ${spaces}},`;
@@ -19131,7 +19191,6 @@ ${spaces}},`;
19131
19191
  `);
19132
19192
  }
19133
19193
  function buildConfigTs(input) {
19134
- const modelDefaults = GATEWAY_MODEL_DEFAULTS[input.gateway];
19135
19194
  const fullConfig = {
19136
19195
  appPrefix: input.appPrefix,
19137
19196
  appName: input.appName,
@@ -19160,7 +19219,7 @@ function buildConfigTs(input) {
19160
19219
  terms: { title: "Terms of Service" }
19161
19220
  },
19162
19221
  authentication: input.auth,
19163
- models: { gateway: input.gateway, ...modelDefaults },
19222
+ ai: { gateway: input.gateway },
19164
19223
  anonymous: {
19165
19224
  credits: 10,
19166
19225
  availableTools: [],
@@ -19177,15 +19236,9 @@ function buildConfigTs(input) {
19177
19236
  "image/jpeg": [".jpg", ".jpeg"],
19178
19237
  "application/pdf": [".pdf"]
19179
19238
  }
19180
- },
19181
- deepResearch: {
19182
- allowClarification: true,
19183
- maxResearcherIterations: 1,
19184
- maxConcurrentResearchUnits: 2,
19185
- maxSearchQueries: 2
19186
19239
  }
19187
19240
  };
19188
- return `import type { ConfigInput } from "@/lib/config-schema";
19241
+ return `import { defineConfig } from "@/lib/config-schema";
19189
19242
 
19190
19243
  /**
19191
19244
  * ChatJS Configuration
@@ -19193,9 +19246,9 @@ function buildConfigTs(input) {
19193
19246
  * Edit this file to customize your app.
19194
19247
  * @see https://chatjs.dev/docs/reference/config
19195
19248
  */
19196
- const config: ConfigInput = {
19249
+ const config = defineConfig({
19197
19250
  ${generateConfig(fullConfig, 1, "", descriptions)}
19198
- };
19251
+ });
19199
19252
 
19200
19253
  export default config;
19201
19254
  `;
@@ -19514,10 +19567,10 @@ import { dirname, join, resolve } from "node:path";
19514
19567
  import { fileURLToPath } from "node:url";
19515
19568
 
19516
19569
  // src/utils/run-command.ts
19517
- import { spawn } from "node:child_process";
19570
+ import { spawn as spawn2 } from "node:child_process";
19518
19571
  async function runCommand(command, args, cwd) {
19519
19572
  await new Promise((resolvePromise, rejectPromise) => {
19520
- const child = spawn(command, args, { cwd, stdio: "pipe" });
19573
+ const child = spawn2(command, args, { cwd, stdio: "pipe" });
19521
19574
  const stderr = [];
19522
19575
  child.stderr?.on("data", (data) => {
19523
19576
  stderr.push(String(data));
@@ -19552,20 +19605,6 @@ async function scaffoldFromGit(url2, destination) {
19552
19605
  await rm(join(destination, ".git"), { recursive: true, force: true });
19553
19606
  }
19554
19607
 
19555
- // src/utils/get-package-manager.ts
19556
- function inferPackageManager() {
19557
- const ua = process.env.npm_config_user_agent ?? "";
19558
- if (ua.startsWith("pnpm/"))
19559
- return "pnpm";
19560
- if (ua.startsWith("yarn/"))
19561
- return "yarn";
19562
- if (ua.startsWith("npm/"))
19563
- return "npm";
19564
- if (ua.startsWith("bun/"))
19565
- return "bun";
19566
- return "bun";
19567
- }
19568
-
19569
19608
  // ../../node_modules/ora/index.js
19570
19609
  import process10 from "node:process";
19571
19610
 
@@ -21056,4 +21095,5 @@ process.on("SIGTERM", () => process.exit(0));
21056
21095
  var program2 = new Command().name("chat-js").description("ChatJS CLI").version(package_default.version, "-v, --version", "display the version number");
21057
21096
  program2.addCommand(create, { isDefault: true });
21058
21097
  program2.addCommand(add);
21098
+ program2.addCommand(config2);
21059
21099
  program2.parse();
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
1
  {
2
- "name": "@chat-js/cli",
3
- "version": "0.2.0",
4
- "description": "CLI for creating and extending ChatJS apps",
5
- "license": "Apache-2.0",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/franciscomoretti/chat.js.git",
9
- "directory": "packages/cli"
10
- },
11
- "homepage": "https://github.com/franciscomoretti/chat.js/tree/main/packages/cli",
12
- "bugs": {
13
- "url": "https://github.com/franciscomoretti/chat.js/issues"
14
- },
15
- "keywords": [
16
- "chatjs",
17
- "ai",
18
- "cli",
19
- "scaffold",
20
- "nextjs"
21
- ],
22
- "dependencies": {
23
- "@clack/prompts": "^1.0.1",
24
- "commander": "^14.0.0",
25
- "kleur": "^4.1.5",
26
- "ora": "^8.2.0",
27
- "zod": "^4.3.6"
28
- },
29
- "bin": {
30
- "chat-js": "./dist/index.js"
31
- },
32
- "files": [
33
- "dist",
34
- "templates"
35
- ],
36
- "private": false,
37
- "publishConfig": {
38
- "access": "public"
39
- },
40
- "scripts": {
41
- "start": "bun src/index.ts",
42
- "build": "bun build ./src/index.ts --target=node --format=esm --outfile ./dist/index.js",
43
- "template:sync": "bun ../../scripts/sync-template.ts",
44
- "prepublishOnly": "bun run template:sync && bun run build && node ./dist/index.js --help >/dev/null"
45
- },
46
- "type": "module",
47
- "devDependencies": {
48
- "@types/bun": "latest"
49
- }
2
+ "name": "@chat-js/cli",
3
+ "version": "0.3.0",
4
+ "description": "CLI for creating and extending ChatJS apps",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/franciscomoretti/chat-js.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "homepage": "https://github.com/franciscomoretti/chat-js/tree/main/packages/cli",
12
+ "bugs": {
13
+ "url": "https://github.com/franciscomoretti/chat-js/issues"
14
+ },
15
+ "keywords": [
16
+ "chatjs",
17
+ "ai",
18
+ "cli",
19
+ "scaffold",
20
+ "nextjs"
21
+ ],
22
+ "dependencies": {
23
+ "@clack/prompts": "^1.0.1",
24
+ "commander": "^14.0.0",
25
+ "kleur": "^4.1.5",
26
+ "ora": "^8.2.0",
27
+ "zod": "^4.3.6"
28
+ },
29
+ "bin": {
30
+ "chat-js": "./dist/index.js"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "templates"
35
+ ],
36
+ "private": false,
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "start": "bun src/index.ts",
42
+ "build": "bun build ./src/index.ts --target=node --format=esm --outfile ./dist/index.js",
43
+ "template:sync": "bun ../../scripts/sync-template.ts",
44
+ "prepublishOnly": "bun run template:sync && bun run build && node ./dist/index.js --help >/dev/null"
45
+ },
46
+ "type": "module",
47
+ "devDependencies": {
48
+ "@types/bun": "latest"
49
+ }
50
50
  }
@@ -0,0 +1,19 @@
1
+ # @chatjs/chat
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#94](https://github.com/FranciscoMoretti/chat-js/pull/94) [`2a8a7cc`](https://github.com/FranciscoMoretti/chat-js/commit/2a8a7cc2b0649bd73e41999dbf0528a21e8065be) Thanks [@FranciscoMoretti](https://github.com/FranciscoMoretti)! - ## Config defaults & `defineConfig` helper
8
+
9
+ ### New features
10
+
11
+ - **`defineConfig()` helper** — new type-safe wrapper for `chat.config.ts`. The gateway type is inferred from `ai.gateway`, so autocomplete and type errors are scoped to the model IDs available in the chosen gateway. Replace `satisfies ConfigInput` with `defineConfig({...})`.
12
+ - **Gateway-specific defaults** — all AI config fields (models, tools, workflows) are now optional. Omitted fields are automatically filled from per-gateway defaults at runtime via `applyDefaults()`. Only `ai.gateway` is required.
13
+ - **`chatjs config` CLI command** — new command that prints the fully-resolved configuration for the current project, applying all defaults. Useful for debugging and verifying your setup.
14
+ - **Separate defaults per gateway** — `vercel`, `openrouter`, `openai`, and `openai-compatible` each have their own typed defaults (`ModelDefaultsFor<G>`), ensuring model IDs are validated against the correct gateway's model registry.
15
+ - **Stricter image/video tool schemas** — `tools.image` and `tools.video` now use a discriminated union: `enabled: true` requires a `default` model, while `enabled: false` makes it optional.
16
+
17
+ ### Breaking changes
18
+
19
+ None — existing configs using `satisfies ConfigInput` continue to work. Migrating to `defineConfig()` is recommended for better DX but not required.
@@ -12,11 +12,15 @@ export async function generateTitleFromUserMessage({
12
12
  }) {
13
13
  const { text: title } = await generateText({
14
14
  model: await getLanguageModel(config.ai.workflows.title),
15
- system: `\n
16
- - you will generate a short title based on the first message a user begins a conversation with
17
- - ensure it is not more than 40 characters long
18
- - the title should be a summary of the user's message
19
- - do not use quotes or colons`,
15
+ system: `Generate a concise title for a chat conversation based on the user's first message.
16
+
17
+ Rules (strictly follow all):
18
+ - Maximum 40 characters hard limit, never exceed this
19
+ - 3-6 words is ideal
20
+ - No quotes, colons, or punctuation at the end
21
+ - No filler words like "How to" or "Question about"
22
+ - Use title case
23
+ - Return ONLY the title, nothing else`,
20
24
  prompt: JSON.stringify(message),
21
25
  experimental_telemetry: { isEnabled: true },
22
26
  });
@@ -1,7 +1,7 @@
1
1
  @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
3
  @plugin "@tailwindcss/typography";
4
- @source "../node_modules/streamdown/dist/index.js";
4
+ @source "../node_modules/streamdown/dist/*.js";
5
5
 
6
6
  @custom-variant dark (&:is(.dark *));
7
7