flavor-code 1.2.17 → 1.2.20

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 CHANGED
@@ -247,9 +247,9 @@ flavor mcp disable docs
247
247
 
248
248
  </details>
249
249
 
250
- A Skill is a `SKILL.md` with YAML frontmatter, placed in `.flavor/skills/<name>/` or `~/.flavor-code/skills/<name>/`. Flavor loads skills progressively based on the task, and you can invoke one explicitly with `/<skill-name>`.
250
+ A Skill is a `SKILL.md` with YAML frontmatter, placed in `.flavor/skills/<name>/` or `~/.flavor-code/skills/<name>/`. Flavor loads skills progressively based on the task, and you can invoke one explicitly with `/<skill-name>`. Skill bodies support `$ARGUMENTS`, `$ARGUMENTS[N]`, and `$N` substitutions. A running composite Skill can load a dependency through the read-only `Skill` tool; plugin-qualified names such as `superharness:test-driven-development` resolve to discovered skills.
251
251
 
252
- Plugins live in `.flavor/plugins/` and can register commands, tools, hooks, Skill roots, and model adapters.
252
+ Plugins live in `.flavor/plugins/` and can register commands, tools, hooks, Skill roots, and model adapters. `additionalContext` returned by `SessionStart` and `UserPromptSubmit` hooks is added to the current task context, enabling reliable project-level engineering policy injection.
253
253
 
254
254
  > [!WARNING]
255
255
  > Plugins and agent self-registered tools are in-process JavaScript, not a security sandbox. Only install, enable, and approve code you trust.
package/README.zh-CN.md CHANGED
@@ -247,9 +247,9 @@ flavor mcp disable docs
247
247
 
248
248
  </details>
249
249
 
250
- Skill 是带有 YAML 头信息的 `SKILL.md`,放在 `.flavor/skills/<name>/` 或 `~/.flavor-code/skills/<name>/`。Flavor 会按任务渐进加载,也支持通过 `/<skill-name>` 显式调用。
250
+ Skill 是带有 YAML 头信息的 `SKILL.md`,放在 `.flavor/skills/<name>/` 或 `~/.flavor-code/skills/<name>/`。Flavor 会按任务渐进加载,也支持通过 `/<skill-name>` 显式调用。Skill 正文支持 `$ARGUMENTS`、`$ARGUMENTS[N]` 和 `$N` 参数占位符;运行中的组合 Skill 可以使用只读 `Skill` 工具继续加载依赖 Skill,插件限定名称(如 `superharness:test-driven-development`)会安全解析到已发现的 Skill。
251
251
 
252
- 插件放在 `.flavor/plugins/`,可以注册命令、工具、Hook、Skill 根目录和模型适配器。
252
+ 插件放在 `.flavor/plugins/`,可以注册命令、工具、Hook、Skill 根目录和模型适配器。`SessionStart` 与 `UserPromptSubmit` Hook 返回的 `additionalContext` 会进入当前任务上下文,可用于注入项目级工程规则。
253
253
 
254
254
  > [!WARNING]
255
255
  > 插件和 Agent 自注册工具是进程内执行的 JavaScript,不是安全沙箱。只安装、启用和批准你信任的代码。
@@ -14,8 +14,9 @@ import {
14
14
  modelContentTranscriptText,
15
15
  packageVersion,
16
16
  redactErrorText,
17
- transcriptReducer
18
- } from "./chunk-QQCXJC4U.js";
17
+ transcriptReducer,
18
+ withTimeout
19
+ } from "./chunk-AXLUVJKC.js";
19
20
  import "./chunk-IG4Z23CL.js";
20
21
  import {
21
22
  Box_default,
@@ -874,21 +875,27 @@ function CodeBlock({ token }) {
874
875
  const langLabel = token.lang?.trim();
875
876
  const label = langLabel && langLabel.length > 0 ? langLabel : "code";
876
877
  const topBarWidth = Math.max(0, columns - 4 - label.length - 4);
877
- return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", paddingX: 1, children: [
878
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
879
- "\u256D\u2500 ",
880
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: label }),
881
- " ",
882
- "\u2500".repeat(topBarWidth),
883
- "\u256E"
884
- ] }),
885
- /* @__PURE__ */ jsx(Box_default, { flexDirection: "column", paddingX: 1, children: truncated.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "gray", children: line }, i)) }),
886
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
887
- "\u2570",
888
- "\u2500".repeat(Math.max(0, columns - 6)),
889
- "\u256F"
878
+ return (
879
+ // Code blocks are self-contained painted regions. Marking the region
880
+ // opaque prevents ScrollBox's incremental renderer from blitting cached
881
+ // blank cells over clean, single-line code bodies during transcript
882
+ // scrolls/redraws.
883
+ /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", paddingX: 1, opaque: true, children: [
884
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
885
+ "\u256D\u2500 ",
886
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: label }),
887
+ " ",
888
+ "\u2500".repeat(topBarWidth),
889
+ "\u256E"
890
+ ] }),
891
+ /* @__PURE__ */ jsx(Box_default, { flexDirection: "column", paddingX: 1, children: truncated.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "gray", children: line }, i)) }),
892
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
893
+ "\u2570",
894
+ "\u2500".repeat(Math.max(0, columns - 6)),
895
+ "\u256F"
896
+ ] })
890
897
  ] })
891
- ] });
898
+ );
892
899
  }
893
900
  function BlockquoteView({ token }) {
894
901
  return /* @__PURE__ */ jsx(Box_default, { flexDirection: "column", children: token.tokens.map((child, i) => {
@@ -1098,9 +1105,15 @@ function AssistantTextInner({ text }) {
1098
1105
  var AssistantText = React2.memo(AssistantTextInner);
1099
1106
 
1100
1107
  // src/ui/signals.ts
1101
- function createSessionInterruptHandler(getSession, shutdown) {
1108
+ function createSessionInterruptHandler(getSession, shutdown, options) {
1109
+ let shuttingDown = false;
1102
1110
  return () => {
1111
+ if (shuttingDown) {
1112
+ options.forceExit();
1113
+ return;
1114
+ }
1103
1115
  if (getSession()?.interrupt() === "cancelled") return;
1116
+ shuttingDown = true;
1104
1117
  void shutdown();
1105
1118
  };
1106
1119
  }
@@ -1901,7 +1914,8 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1901
1914
  const interruptRef = useRef4(void 0);
1902
1915
  interruptRef.current ??= createSessionInterruptHandler(
1903
1916
  () => runtimeRef.current?.session,
1904
- () => shutdownRef.current(runtimeRef.current)
1917
+ () => shutdownRef.current(runtimeRef.current),
1918
+ { forceExit: () => process.exit(1) }
1905
1919
  );
1906
1920
  const interrupt = interruptRef.current;
1907
1921
  useEffect3(() => {
@@ -3529,11 +3543,23 @@ async function submitSafely(session, prompt, report) {
3529
3543
  safeReport(report, safeUiError(error));
3530
3544
  }
3531
3545
  }
3532
- async function shutdownRuntime(runtime, exit, report) {
3546
+ var SHUTDOWN_TIMEOUT_MS = 8e3;
3547
+ var FORCE_EXIT_GRACE_MS = 250;
3548
+ async function shutdownRuntime(runtime, exit, report, options = {}) {
3549
+ const timeoutMs = options.shutdownTimeoutMs ?? SHUTDOWN_TIMEOUT_MS;
3550
+ let timedOut = false;
3533
3551
  try {
3534
- await closeAndDisposeRuntime(runtime, report);
3552
+ const outcome = await withTimeout(closeAndDisposeRuntime(runtime, report), timeoutMs);
3553
+ timedOut = outcome.timedOut;
3554
+ if (timedOut) report(`Shutdown timed out after ${timeoutMs}ms; forcing exit.`);
3535
3555
  } finally {
3536
3556
  exit();
3557
+ if (timedOut) {
3558
+ const forceExit = options.forceExit ?? (() => {
3559
+ process.exit(1);
3560
+ });
3561
+ setTimeout(forceExit, FORCE_EXIT_GRACE_MS);
3562
+ }
3537
3563
  }
3538
3564
  }
3539
3565
  async function closeAndDisposeRuntime(runtime, report) {
@@ -3565,6 +3591,7 @@ export {
3565
3591
  HISTORY_CAP,
3566
3592
  MentionMenu,
3567
3593
  PromptLine,
3594
+ SHUTDOWN_TIMEOUT_MS,
3568
3595
  SinglePendingPrompt,
3569
3596
  TerminalLayout,
3570
3597
  appRuntimeOptions,
@@ -4,7 +4,7 @@ import {
4
4
  message,
5
5
  redactErrorText,
6
6
  redactSecrets
7
- } from "./chunk-QQCXJC4U.js";
7
+ } from "./chunk-AXLUVJKC.js";
8
8
 
9
9
  // src/rpc/server.ts
10
10
  import { createInterface } from "readline";