scream-code 0.7.7 → 0.7.9

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.
@@ -121994,7 +121994,7 @@ function optionalBuildString(value) {
121994
121994
  return typeof value === "string" && value.length > 0 ? value : void 0;
121995
121995
  }
121996
121996
  const SCREAM_BUILD_INFO = {
121997
- version: optionalBuildString("0.7.7"),
121997
+ version: optionalBuildString("0.7.9"),
121998
121998
  channel: optionalBuildString(""),
121999
121999
  commit: optionalBuildString(""),
122000
122000
  buildTarget: optionalBuildString("darwin-arm64")
@@ -123815,11 +123815,16 @@ function effectiveThinkingLevel(model, thinkingDraft) {
123815
123815
  if (levels.includes(thinkingDraft)) return thinkingDraft;
123816
123816
  return levels[0] ?? "off";
123817
123817
  }
123818
+ function modelHasImageIn(model) {
123819
+ return model.capabilities?.includes("image_in") === true;
123820
+ }
123818
123821
  var ModelSelectorComponent = class extends Container {
123819
123822
  focused = false;
123820
123823
  opts;
123821
123824
  list;
123822
123825
  thinkingDraft;
123826
+ imageDraft;
123827
+ lastSelectedAlias;
123823
123828
  constructor(opts) {
123824
123829
  super();
123825
123830
  this.opts = opts;
@@ -123835,6 +123840,8 @@ var ModelSelectorComponent = class extends Container {
123835
123840
  });
123836
123841
  const initialChoice = choices[selectedIdx];
123837
123842
  this.thinkingDraft = initialChoice !== void 0 ? effectiveThinkingLevel(initialChoice.model, opts.currentThinkingLevel) : opts.currentThinkingLevel;
123843
+ this.imageDraft = initialChoice !== void 0 ? modelHasImageIn(initialChoice.model) : false;
123844
+ this.lastSelectedAlias = initialChoice?.alias;
123838
123845
  }
123839
123846
  handleInput(data) {
123840
123847
  if (matchesKey(data, Key.escape)) {
@@ -123843,6 +123850,10 @@ var ModelSelectorComponent = class extends Container {
123843
123850
  return;
123844
123851
  }
123845
123852
  const selected = this.list.selected();
123853
+ if (selected !== void 0 && selected.alias !== this.lastSelectedAlias) {
123854
+ this.imageDraft = modelHasImageIn(selected.model);
123855
+ this.lastSelectedAlias = selected.alias;
123856
+ }
123846
123857
  if (selected !== void 0 && thinkingAvailability(selected.model) !== "unsupported") {
123847
123858
  const levels = getThinkingLevels(selected.model);
123848
123859
  const idx = levels.indexOf(this.thinkingDraft);
@@ -123857,11 +123868,16 @@ var ModelSelectorComponent = class extends Container {
123857
123868
  return;
123858
123869
  }
123859
123870
  }
123871
+ if (selected !== void 0 && !modelHasImageIn(selected.model) && matchesKey(data, Key.space)) {
123872
+ this.imageDraft = !this.imageDraft;
123873
+ return;
123874
+ }
123860
123875
  if (matchesKey(data, Key.enter)) {
123861
123876
  if (selected === void 0) return;
123862
123877
  this.opts.onSelect({
123863
123878
  alias: selected.alias,
123864
- thinkingLevel: effectiveThinkingLevel(selected.model, this.thinkingDraft)
123879
+ thinkingLevel: effectiveThinkingLevel(selected.model, this.thinkingDraft),
123880
+ imageEnabled: this.imageDraft
123865
123881
  });
123866
123882
  return;
123867
123883
  }
@@ -123872,7 +123888,11 @@ var ModelSelectorComponent = class extends Container {
123872
123888
  const searchable = this.opts.searchable === true;
123873
123889
  const view = this.list.view();
123874
123890
  const choices = view.items;
123875
- const navParts = ["↑↓ 模型", "←→ 思考等级"];
123891
+ const navParts = [
123892
+ "↑↓ 模型",
123893
+ "←→ 思考等级",
123894
+ "Space(空格) vision识图"
123895
+ ];
123876
123896
  if (view.page.pageCount > 1) navParts.push("PgUp/PgDn 翻页");
123877
123897
  navParts.push("Enter 应用", "Esc 取消");
123878
123898
  const titleSuffix = searchable && view.query.length === 0 ? chalk.hex(colors.textMuted)(" (输入搜索)") : "";
@@ -123897,6 +123917,9 @@ var ModelSelectorComponent = class extends Container {
123897
123917
  const selected = choices[view.selectedIndex];
123898
123918
  if (selected !== void 0) lines.push(this.renderThinkingControl(selected.model));
123899
123919
  lines.push("");
123920
+ lines.push(chalk.hex(colors.textMuted)(" Vision 识图"));
123921
+ if (selected !== void 0) lines.push(this.renderImageControl(selected.model));
123922
+ lines.push("");
123900
123923
  if (view.page.pageCount > 1) lines.push(chalk.hex(colors.textMuted)(` Page ${String(view.page.page + 1)}/${String(view.page.pageCount)}`));
123901
123924
  lines.push(chalk.hex(colors.primary)("─".repeat(width)));
123902
123925
  return lines.map((line) => truncateToWidth(line, width));
@@ -123912,6 +123935,11 @@ var ModelSelectorComponent = class extends Container {
123912
123935
  if (availability === "unsupported") return `${line} ${chalk.hex(colors.textMuted)("unsupported")}`;
123913
123936
  return line;
123914
123937
  }
123938
+ renderImageControl(model) {
123939
+ const { colors } = this.opts;
123940
+ if (modelHasImageIn(model)) return ` ${chalk.hex(colors.success).bold("✓ 默认开启")}${chalk.hex(colors.textMuted)(" (catalog)")}`;
123941
+ return ` ${this.imageDraft ? chalk.hex(colors.success).bold("✓ 开启") : chalk.hex(colors.textDim)("✗ 关闭")}${this.imageDraft ? chalk.hex(colors.textMuted)(" (手动开启)") : ""}`;
123942
+ }
123915
123943
  };
123916
123944
  //#endregion
123917
123945
  //#region src/tui/components/dialogs/text-input-dialog.ts
@@ -125625,23 +125653,24 @@ function showModelPicker(host, selectedValue = host.state.appState.model) {
125625
125653
  currentThinkingLevel: host.state.appState.thinkingLevel,
125626
125654
  colors: host.state.theme.colors,
125627
125655
  searchable: true,
125628
- onSelect: ({ alias, thinkingLevel }) => {
125656
+ onSelect: ({ alias, thinkingLevel, imageEnabled }) => {
125629
125657
  host.restoreEditor();
125630
- performModelSwitch(host, alias, thinkingLevel);
125658
+ performModelSwitch(host, alias, thinkingLevel, imageEnabled);
125631
125659
  },
125632
125660
  onCancel: () => {
125633
125661
  host.restoreEditor();
125634
125662
  }
125635
125663
  }));
125636
125664
  }
125637
- async function performModelSwitch(host, alias, thinkingLevel) {
125665
+ async function performModelSwitch(host, alias, thinkingLevel, imageEnabled) {
125638
125666
  if (isBusy(host.state.appState)) {
125639
125667
  host.showError("Cannot switch models while streaming — press Esc or Ctrl-C first.");
125640
125668
  return;
125641
125669
  }
125642
125670
  const prevModel = host.state.appState.model;
125643
125671
  const prevThinkingLevel = host.state.appState.thinkingLevel;
125644
- const runtimeChanged = alias !== prevModel || thinkingLevel !== prevThinkingLevel;
125672
+ const prevImageEnabled = host.state.appState.availableModels[alias]?.capabilities?.includes("image_in") === true;
125673
+ const runtimeChanged = alias !== prevModel || thinkingLevel !== prevThinkingLevel || imageEnabled !== prevImageEnabled;
125645
125674
  const overflow = alias !== prevModel ? contextOverflowForModel(host.state.appState, alias) : null;
125646
125675
  if (overflow !== null) {
125647
125676
  host.showNotice("Storm Breaker(风暴守护者)", `无法切换到模型「${alias}」:当前会话上下文 ${formatTokenCount$1(overflow.currentTokens)} 已超出该模型上限 ${formatTokenCount$1(overflow.maxContextTokens)}。建议先执行 /compact 压缩上下文,或选择上下文窗口更大的模型。`);
@@ -125665,21 +125694,38 @@ async function performModelSwitch(host, alias, thinkingLevel) {
125665
125694
  });
125666
125695
  let persisted = false;
125667
125696
  try {
125668
- persisted = await persistModelSelection(host, alias, thinkingLevel);
125697
+ persisted = await persistModelSelection(host, alias, thinkingLevel, imageEnabled);
125669
125698
  } catch (error) {
125670
125699
  const msg = formatErrorMessage(error);
125671
125700
  host.showError(`Switched to ${alias}, but failed to save default: ${msg}`);
125672
125701
  return;
125673
125702
  }
125703
+ if (persisted) {
125704
+ const prevAlias = host.state.appState.availableModels[alias];
125705
+ if (prevAlias !== void 0) {
125706
+ const nextCaps = computeCapabilities(prevAlias.capabilities, imageEnabled);
125707
+ host.setAppState({ availableModels: {
125708
+ ...host.state.appState.availableModels,
125709
+ [alias]: {
125710
+ ...prevAlias,
125711
+ capabilities: nextCaps
125712
+ }
125713
+ } });
125714
+ }
125715
+ }
125674
125716
  const status = runtimeChanged ? `Switched to ${alias} with thinking ${thinkingLevel}.` : persisted ? `Saved ${alias} with thinking ${thinkingLevel} as default.` : `Already using ${alias} with thinking ${thinkingLevel}.`;
125675
125717
  host.showStatus(status, host.state.theme.colors.success);
125676
125718
  }
125677
- async function persistModelSelection(host, alias, thinkingLevel) {
125719
+ async function persistModelSelection(host, alias, thinkingLevel, imageEnabled) {
125678
125720
  const config = await host.harness.getConfig({ reload: true });
125679
125721
  const effectiveThinking = thinkingLevel !== "off";
125680
125722
  const existingEffort = config.thinking?.effort;
125681
125723
  const newEffort = effectiveThinking ? thinkingLevel : existingEffort;
125682
- if (config.defaultModel === alias && config.defaultThinking === effectiveThinking && existingEffort === newEffort) return false;
125724
+ const aliasCfg = config.models?.[alias];
125725
+ const prevCaps = aliasCfg?.capabilities;
125726
+ const nextCaps = computeCapabilities(prevCaps, imageEnabled);
125727
+ const capsChanged = prevCaps?.includes("image_in") === true !== imageEnabled;
125728
+ if (config.defaultModel === alias && config.defaultThinking === effectiveThinking && existingEffort === newEffort && !capsChanged) return false;
125683
125729
  await host.harness.setConfig({
125684
125730
  defaultModel: alias,
125685
125731
  defaultThinking: effectiveThinking,
@@ -125687,10 +125733,28 @@ async function persistModelSelection(host, alias, thinkingLevel) {
125687
125733
  ...config.thinking,
125688
125734
  mode: effectiveThinking ? "on" : "off",
125689
125735
  effort: newEffort
125690
- }
125736
+ },
125737
+ models: capsChanged && aliasCfg !== void 0 ? { [alias]: {
125738
+ ...aliasCfg,
125739
+ capabilities: nextCaps
125740
+ } } : void 0
125691
125741
  });
125692
125742
  return true;
125693
125743
  }
125744
+ /**
125745
+ * Returns the new capabilities array after toggling 'image_in'. Returns
125746
+ * `undefined` when the result would be empty, so legacy aliases without a
125747
+ * capabilities declaration stay clean (no `capabilities = []` in toml).
125748
+ */
125749
+ function computeCapabilities(prev, imageEnabled) {
125750
+ if (imageEnabled) {
125751
+ if (prev === void 0) return ["image_in"];
125752
+ return prev.includes("image_in") ? [...prev] : [...prev, "image_in"];
125753
+ }
125754
+ if (prev === void 0) return void 0;
125755
+ const filtered = prev.filter((c) => c !== "image_in");
125756
+ return filtered.length === 0 ? void 0 : filtered;
125757
+ }
125694
125758
  function showThemePicker(host) {
125695
125759
  host.mountEditorReplacement(new ThemeSelectorComponent({
125696
125760
  currentValue: host.state.appState.theme,
@@ -133020,6 +133084,15 @@ function readImagePath(path) {
133020
133084
  mimeType: meta.mime
133021
133085
  };
133022
133086
  }
133087
+ /**
133088
+ * Read an image file from disk into a ClipboardImage. Used when the user
133089
+ * pastes a file URL that the terminal has translated into a text path —
133090
+ * the path points at a real image file, so we load it directly rather
133091
+ * than going through the clipboard native binding.
133092
+ */
133093
+ function readImageFromPath(path) {
133094
+ return readImagePath(path);
133095
+ }
133023
133096
  function readVideoPath(path) {
133024
133097
  const mimeType = videoMimeFromPath(path);
133025
133098
  if (mimeType === null) return null;
@@ -133471,6 +133544,16 @@ var EditorKeyboardController = class {
133471
133544
  return false;
133472
133545
  };
133473
133546
  editor.onPasteImage = async () => this.handleClipboardImagePaste();
133547
+ editor.onPasteImagePath = async (path) => this.handleImagePathPaste(path);
133548
+ }
133549
+ async handleImagePathPaste(path) {
133550
+ const media = readImageFromPath(path);
133551
+ if (media === null) return;
133552
+ const meta = parseImageMeta(media.bytes);
133553
+ if (meta === null) return;
133554
+ const attachment = this.imageStore.addImage(media.bytes, meta.mime, meta.width, meta.height);
133555
+ this.host.state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
133556
+ this.host.state.ui.requestRender();
133474
133557
  }
133475
133558
  clearPendingExit() {
133476
133559
  if (!this.pendingExit) return;
@@ -134413,8 +134496,6 @@ var SessionEventHandler = class {
134413
134496
  if (event.maxContextTokens !== void 0) patch.maxContextTokens = event.maxContextTokens;
134414
134497
  if (event.planMode !== void 0) patch.planMode = event.planMode ? event.planStrategy === "fusion" ? "fusionplan" : this.host.state.appState.planMode === "fusionplan" ? "fusionplan" : "plan" : "off";
134415
134498
  if (event.permission !== void 0) patch.permissionMode = event.permission;
134416
- if (event.model !== void 0) patch.model = event.model;
134417
- if (event.thinkingLevel !== void 0) patch.thinkingLevel = event.thinkingLevel;
134418
134499
  if (Object.keys(patch).length > 0) this.host.setAppState(patch);
134419
134500
  }
134420
134501
  handleSessionMetaChanged(event) {
@@ -140734,11 +140815,62 @@ function styleTitle(title, status, colors) {
140734
140815
  /**
140735
140816
  * Custom editor extending pi-tui Editor with app-level keybindings.
140736
140817
  */
140737
- const ANSI_SGR = /\u001B\[[0-9;]*m/g;
140818
+ const ANSI_SGR = /\[[0-9;]*m/g;
140738
140819
  const PASTE_MARKER_RE = /\[paste #(\d+)(?: (?:\+\d+ lines|\d+ chars))?\]/g;
140739
140820
  const BRACKET_PASTE_START = "\x1B[200~";
140740
140821
  const BRACKET_PASTE_END = "\x1B[201~";
140741
- const KITTY_CSI_U = /^\u001B\[(\d+);(\d+)((?::\d+)*)u$/;
140822
+ const BRACKETED_IMAGE_PATH_REGEX = /\.(?:png|jpe?g|gif|webp)$/i;
140823
+ const BRACKETED_IMAGE_PATH_BOUNDARY_REGEX = /\.(?:png|jpe?g|gif|webp)(?=$|["']?\s)/gi;
140824
+ const SHELL_ESCAPED_PATH_CHAR_REGEX = /\\([\\\s'"()[\]{}&;<>|?*!$`])/g;
140825
+ function isPastedPathSeparator(char) {
140826
+ return char === void 0 || char === " " || char === " " || char === "\r" || char === "\n";
140827
+ }
140828
+ function imagePathBoundaryEnd(payload, segmentStart, extensionEnd) {
140829
+ const quote = payload[segmentStart];
140830
+ const afterExtension = payload[extensionEnd];
140831
+ if (quote === "\"" || quote === "'") return afterExtension === quote && isPastedPathSeparator(payload[extensionEnd + 1]) ? extensionEnd + 1 : void 0;
140832
+ if (isPastedPathSeparator(afterExtension)) return extensionEnd;
140833
+ }
140834
+ function normalizePastedImagePath(path) {
140835
+ const trimmed = path.trim();
140836
+ const first = trimmed[0];
140837
+ const last = trimmed[trimmed.length - 1];
140838
+ return (trimmed.length > 1 && (first === "\"" || first === "'") && last === first ? trimmed.slice(1, -1) : trimmed).replace(SHELL_ESCAPED_PATH_CHAR_REGEX, "$1");
140839
+ }
140840
+ /**
140841
+ * If a bracketed paste contains only image file paths (one or more,
140842
+ * space/quote separated, all with image extensions), return them. Returns
140843
+ * `undefined` when the paste is mixed text + paths or contains non-image
140844
+ * content — in that case the caller should fall through to normal text
140845
+ * paste. Mirrors oh-my-pi's approach so pasting a Finder-copied image
140846
+ * file becomes a multimodal image attachment instead of a text path the
140847
+ * agent would have to Read manually.
140848
+ */
140849
+ function extractBracketedImagePastePaths(data) {
140850
+ if (!data.includes(BRACKET_PASTE_START)) return void 0;
140851
+ const startIndex = data.indexOf(BRACKET_PASTE_START);
140852
+ const endIndex = data.indexOf(BRACKET_PASTE_END, startIndex + 6);
140853
+ if (endIndex === -1) return void 0;
140854
+ const pasted = data.slice(startIndex + 6, endIndex).trim();
140855
+ if (pasted.length === 0) return void 0;
140856
+ const paths = [];
140857
+ let segmentStart = 0;
140858
+ BRACKETED_IMAGE_PATH_BOUNDARY_REGEX.lastIndex = 0;
140859
+ for (let match = BRACKETED_IMAGE_PATH_BOUNDARY_REGEX.exec(pasted); match !== null; match = BRACKETED_IMAGE_PATH_BOUNDARY_REGEX.exec(pasted)) {
140860
+ const extensionEnd = match.index + match[0].length;
140861
+ const boundaryEnd = imagePathBoundaryEnd(pasted, segmentStart, extensionEnd);
140862
+ if (boundaryEnd === void 0) continue;
140863
+ const path = normalizePastedImagePath(pasted.slice(segmentStart, boundaryEnd));
140864
+ if (path.length === 0 || !BRACKETED_IMAGE_PATH_REGEX.test(path)) return void 0;
140865
+ paths.push(path);
140866
+ segmentStart = boundaryEnd;
140867
+ while (segmentStart < pasted.length && isPastedPathSeparator(pasted[segmentStart])) segmentStart += 1;
140868
+ BRACKETED_IMAGE_PATH_BOUNDARY_REGEX.lastIndex = segmentStart;
140869
+ }
140870
+ if (paths.length === 0 || segmentStart !== pasted.length) return void 0;
140871
+ return paths;
140872
+ }
140873
+ const KITTY_CSI_U = /^\[(\d+);(\d+)((?::\d+)*)u$/;
140742
140874
  const CAPS_LOCK_BIT = 64;
140743
140875
  const CTRL_BIT = 4;
140744
140876
  const SHIFT_BIT = 1;
@@ -140770,7 +140902,7 @@ function normalizeCapsLockedCtrl(data) {
140770
140902
  if (codepoint < 65 || codepoint > 90) return data;
140771
140903
  const loweredCodepoint = codepoint + 32;
140772
140904
  const strippedModifier = (modifier & -65) + 1;
140773
- return `\u001B[${String(loweredCodepoint)};${String(strippedModifier)}${tail}u`;
140905
+ return `[${String(loweredCodepoint)};${String(strippedModifier)}${tail}u`;
140774
140906
  }
140775
140907
  /** Convert a visible-char index (ANSI-stripped) back to an index into the raw ANSI-bearing string. */
140776
140908
  function mapVisibleIdxToRaw(line, visibleIdx) {
@@ -140821,6 +140953,15 @@ var CustomEditor = class extends Editor {
140821
140953
  * the next keystroke.
140822
140954
  */
140823
140955
  onPasteImage;
140956
+ /**
140957
+ * Called when a bracketed paste contains only image file paths (e.g.
140958
+ * the user copied an image file in Finder and pasted — the terminal
140959
+ * translates the file URL into a text path). The host loads each path
140960
+ * from disk and registers it as an image attachment, so the paste
140961
+ * becomes a multimodal image input instead of a text path the agent
140962
+ * would have to Read manually.
140963
+ */
140964
+ onPasteImagePath;
140824
140965
  /** Fires exactly once when the user first types anything into the editor. */
140825
140966
  onFirstInput;
140826
140967
  firstInputFired = false;
@@ -140912,6 +141053,15 @@ var CustomEditor = class extends Editor {
140912
141053
  if (!normalized.includes(BRACKET_PASTE_END)) this.consumingPaste = true;
140913
141054
  return;
140914
141055
  }
141056
+ const pastedImagePaths = extractBracketedImagePastePaths(normalized);
141057
+ if (pastedImagePaths !== void 0 && this.onPasteImagePath !== void 0) {
141058
+ const handler = this.onPasteImagePath;
141059
+ (async () => {
141060
+ for (const path of pastedImagePaths) await handler(path);
141061
+ })();
141062
+ if (!normalized.includes(BRACKET_PASTE_END)) this.consumingPaste = true;
141063
+ return;
141064
+ }
140915
141065
  if (matchesKey(normalized, process.platform === "win32" ? "alt+v" : Key.ctrl("v"))) {
140916
141066
  if (this.expandPasteMarkerAtCursor()) return;
140917
141067
  if (this.onPasteImage !== void 0) {
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-AVBOJrWs.mjs")).main();
9
+ (await import("./app-DxQ8R4AP.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -41,20 +41,6 @@
41
41
  "publishConfig": {
42
42
  "access": "public"
43
43
  },
44
- "scripts": {
45
- "build": "tsdown",
46
- "dev": "node scripts/dev.mjs",
47
- "dev:cli-only": "tsx --import ../../build/register-raw-text-loader.mjs ./src/main.ts",
48
- "dev:prod": "node dist/main.mjs",
49
- "clean": "rm -rf dist",
50
- "typecheck": "tsc -p tsconfig.json --noEmit",
51
- "test": "pnpm -w run build:packages && vitest run",
52
- "e2e": "pnpm -w run build:packages && SCREAM_E2E=1 vitest run test/e2e",
53
- "e2e:real": "pnpm -w run build:packages && SCREAM_E2E_REAL=1 vitest run test/e2e/real-llm-smoke.e2e.test.ts",
54
- "preinstall": "node -e \"console.log('\\n📦 正在安装 scream-code,请稍候...\\n')\"",
55
- "postinstall": "node scripts/postinstall.mjs",
56
- "smoke": "node dist/main.mjs --version"
57
- },
58
44
  "dependencies": {
59
45
  "@earendil-works/pi-tui": "^0.80.2",
60
46
  "@mariozechner/clipboard": "^0.3.2",
@@ -68,15 +54,29 @@
68
54
  },
69
55
  "devDependencies": {
70
56
  "@modelcontextprotocol/sdk": "^1.29.0",
71
- "@scream-code/agent-core": "workspace:^",
72
- "@scream-code/config": "workspace:^",
73
- "@scream-code/memory": "workspace:*",
74
- "@scream-code/migration-legacy": "workspace:^",
75
- "@scream-code/scream-code-sdk": "workspace:^",
76
57
  "@types/semver": "^7.7.0",
77
- "tsx": "^4.21.0"
58
+ "tsx": "^4.21.0",
59
+ "@scream-code/agent-core": "^0.7.0",
60
+ "@scream-code/config": "^0.7.0",
61
+ "@scream-code/memory": "0.7.0",
62
+ "@scream-code/migration-legacy": "^0.7.0",
63
+ "@scream-code/scream-code-sdk": "^0.7.0"
78
64
  },
79
65
  "engines": {
80
66
  "node": ">=22.19.0"
67
+ },
68
+ "scripts": {
69
+ "build": "tsdown",
70
+ "dev": "node scripts/dev.mjs",
71
+ "dev:cli-only": "tsx --import ../../build/register-raw-text-loader.mjs ./src/main.ts",
72
+ "dev:prod": "node dist/main.mjs",
73
+ "clean": "rm -rf dist",
74
+ "typecheck": "tsc -p tsconfig.json --noEmit",
75
+ "test": "pnpm -w run build:packages && vitest run",
76
+ "e2e": "pnpm -w run build:packages && SCREAM_E2E=1 vitest run test/e2e",
77
+ "e2e:real": "pnpm -w run build:packages && SCREAM_E2E_REAL=1 vitest run test/e2e/real-llm-smoke.e2e.test.ts",
78
+ "preinstall": "node -e \"console.log('\\n📦 正在安装 scream-code,请稍候...\\n')\"",
79
+ "postinstall": "node scripts/postinstall.mjs",
80
+ "smoke": "node dist/main.mjs --version"
81
81
  }
82
- }
82
+ }