@sayknow-cli/coding-agent 0.3.10 → 0.3.12

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 (81) hide show
  1. package/CHANGELOG.md +0 -13
  2. package/dist/types/build-metadata.d.ts +7 -0
  3. package/dist/types/capability/mcp.d.ts +3 -1
  4. package/dist/types/commands/mcp.d.ts +1 -1
  5. package/dist/types/config/keybindings.d.ts +2 -2
  6. package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
  7. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  8. package/dist/types/modes/components/status-line/types.d.ts +6 -0
  9. package/dist/types/modes/components/status-line.d.ts +1 -0
  10. package/dist/types/modes/components/welcome.d.ts +1 -0
  11. package/dist/types/modes/rpc/rpc-client.d.ts +7 -2
  12. package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
  13. package/dist/types/notifications/telegram-daemon.d.ts +2 -0
  14. package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
  15. package/dist/types/runtime-mcp/types.d.ts +4 -3
  16. package/dist/types/session/agent-session.d.ts +16 -0
  17. package/dist/types/session/session-manager.d.ts +1 -0
  18. package/dist/types/tools/skill.d.ts +9 -8
  19. package/dist/types/utils/linkedom.d.ts +4 -0
  20. package/dist/types/utils/pasted-image-path.d.ts +5 -3
  21. package/package.json +7 -7
  22. package/scripts/build-binary.ts +2 -43
  23. package/scripts/compile-args.ts +106 -0
  24. package/scripts/generate-docs-index.ts +1 -0
  25. package/src/build-metadata.ts +104 -0
  26. package/src/capability/mcp.ts +3 -1
  27. package/src/cli/mcp-cli.ts +108 -26
  28. package/src/cli.ts +21 -0
  29. package/src/commands/mcp.ts +8 -8
  30. package/src/commands/stats.ts +6 -2
  31. package/src/config/keybindings.ts +2 -2
  32. package/src/config/mcp-schema.json +21 -17
  33. package/src/config/model-profiles.ts +23 -23
  34. package/src/discovery/builtin.ts +14 -0
  35. package/src/discovery/mcp-json.ts +14 -0
  36. package/src/eval/js/executor.ts +7 -1
  37. package/src/internal-urls/docs-index.generated.ts +3 -2
  38. package/src/internal-urls/skc-protocol.ts +14 -1
  39. package/src/modes/acp/acp-agent.ts +72 -16
  40. package/src/modes/acp/acp-client-bridge.ts +27 -5
  41. package/src/modes/acp/acp-event-mapper.ts +39 -1
  42. package/src/modes/components/assistant-message.ts +1 -1
  43. package/src/modes/components/hook-selector.ts +4 -0
  44. package/src/modes/components/model-selector.ts +17 -6
  45. package/src/modes/components/session-selector.ts +2 -2
  46. package/src/modes/components/status-line/presets.ts +5 -19
  47. package/src/modes/components/status-line/segments.ts +14 -0
  48. package/src/modes/components/status-line/types.ts +6 -0
  49. package/src/modes/components/status-line.ts +13 -6
  50. package/src/modes/components/tool-execution.ts +1 -1
  51. package/src/modes/components/welcome.ts +4 -1
  52. package/src/modes/controllers/command-controller.ts +1 -1
  53. package/src/modes/controllers/event-controller.ts +9 -9
  54. package/src/modes/controllers/input-controller.ts +8 -2
  55. package/src/modes/controllers/selector-controller.ts +7 -3
  56. package/src/modes/interactive-mode.ts +12 -4
  57. package/src/modes/rpc/rpc-client.ts +30 -6
  58. package/src/modes/utils/ui-helpers.ts +154 -32
  59. package/src/notifications/telegram-daemon.ts +28 -2
  60. package/src/prompts/tools/skill.md +2 -2
  61. package/src/runtime-mcp/config-writer.ts +4 -3
  62. package/src/runtime-mcp/config.ts +1 -0
  63. package/src/runtime-mcp/types.ts +4 -3
  64. package/src/session/agent-session.ts +117 -3
  65. package/src/session/session-manager.ts +68 -40
  66. package/src/skc-runtime/state-runtime.ts +105 -12
  67. package/src/skc-runtime/workflow-manifest.generated.json +4 -28
  68. package/src/skc-runtime/workflow-manifest.ts +1 -7
  69. package/src/slash-commands/builtin-registry.ts +9 -4
  70. package/src/tools/bash-interactive.ts +9 -4
  71. package/src/tools/browser/readable.ts +2 -2
  72. package/src/tools/fetch.ts +5 -5
  73. package/src/tools/skill.ts +14 -11
  74. package/src/utils/linkedom.ts +14 -0
  75. package/src/utils/pasted-image-path.ts +23 -9
  76. package/src/web/scrapers/arxiv.ts +2 -2
  77. package/src/web/scrapers/go-pkg.ts +2 -2
  78. package/src/web/scrapers/iacr.ts +2 -2
  79. package/src/web/scrapers/readthedocs.ts +2 -2
  80. package/src/web/scrapers/twitter.ts +3 -2
  81. package/src/web/scrapers/wikipedia.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -4,19 +4,6 @@ Sayknow-CLI is a rebranded fork of [gajae-code](https://github.com/Yeachan-Heo/g
4
4
  This file tracks the **fork's own releases**; upstream's full feature history lives
5
5
  in that project. Each release notes the upstream version it is built on.
6
6
 
7
- ## [0.3.10] — 2026-07-09
8
-
9
- ### Changed
10
-
11
- - Synced onto upstream **gajae-code v0.9.2**, carrying the 0.9.2 patch set into
12
- Sayknow-CLI while preserving the SKC fork layer and release surface.
13
-
14
- ### Fixed
15
-
16
- - Updated the release version surfaces so `skc --version`, package metadata,
17
- native sentinel exports, plugin metadata, schemas, and public sync checks report
18
- **0.3.10** consistently.
19
-
20
7
  ## [0.3.0] — 2026-06-23
21
8
 
22
9
  ### Changed
@@ -0,0 +1,7 @@
1
+ export type BuildChannel = "release" | "dev" | "local-source" | "package-install" | "compiled" | "unknown";
2
+ export interface BuildMetadata {
3
+ channel: BuildChannel;
4
+ label: string;
5
+ }
6
+ export declare function resolveBuildMetadata(moduleDir?: string): BuildMetadata;
7
+ export declare function formatBuildLabel(metadata?: BuildMetadata): string;
@@ -7,7 +7,7 @@ export interface MCPServer {
7
7
  name: string;
8
8
  /** Whether this server is enabled (default: true) */
9
9
  enabled?: boolean;
10
- /** Whether to connect automatically at session startup (default: true) */
10
+ /** Whether explicit runtime MCP consumers should connect automatically (default: true) */
11
11
  autoload?: boolean;
12
12
  /** Connection timeout in milliseconds */
13
13
  timeout?: number;
@@ -17,6 +17,8 @@ export interface MCPServer {
17
17
  args?: string[];
18
18
  /** Environment variables */
19
19
  env?: Record<string, string>;
20
+ /** Whether explicit stdio runtime consumers should avoid inheriting host environment */
21
+ noInheritEnv?: boolean;
20
22
  /** Working directory for stdio transport */
21
23
  cwd?: string;
22
24
  /** URL (for HTTP/SSE transport) */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Direct MCP server registration for standalone SKC.
2
+ * Explicit MCP server config storage for standalone SKC.
3
3
  */
4
4
  import { Command } from "@sayknow-cli/utils/cli";
5
5
  import { type MCPAction } from "../cli/mcp-cli";
@@ -297,11 +297,11 @@ export declare const KEYBINDINGS: {
297
297
  };
298
298
  readonly "app.session.delete": {
299
299
  readonly defaultKeys: "ctrl+d";
300
- readonly description: "Delete session";
300
+ readonly description: "Delete current session transcript/artifacts";
301
301
  };
302
302
  readonly "app.session.deleteNoninvasive": {
303
303
  readonly defaultKeys: "ctrl+backspace";
304
- readonly description: "Delete session (non-invasive)";
304
+ readonly description: "Delete selected session transcript/artifacts (non-invasive)";
305
305
  };
306
306
  readonly "app.tree.foldOrUp": {
307
307
  readonly defaultKeys: ["ctrl+left", "alt+left"];
@@ -16,6 +16,8 @@ interface AcpEventMapperOptions {
16
16
  * before emitting `ToolCallLocation` entries.
17
17
  */
18
18
  cwd?: string;
19
+ /** Phase to expose after compaction ends. Prompt-bound compaction resumes responding; idle maintenance returns idle. */
20
+ compactionEndPhase?: "responding" | "idle";
19
21
  }
20
22
  export declare function mapToolKind(toolName: string): ToolKind;
21
23
  export declare function mapAgentWireEventPayloadToAcpSessionUpdates(payload: AgentWireEventPayload, sessionId: string, options?: AcpEventMapperOptions): SessionNotification[];
@@ -48,6 +48,7 @@ export interface HookSelectorOptions {
48
48
  export declare class HookSelectorComponent extends Container {
49
49
  #private;
50
50
  constructor(title: string, options: string[], onSelect: (option: string) => void, onCancel: () => void, opts?: HookSelectorOptions);
51
+ hasActiveInlineInput(): boolean;
51
52
  handleInput(keyData: string): void;
52
53
  dispose(): void;
53
54
  }
@@ -27,6 +27,12 @@ export interface SegmentContext {
27
27
  };
28
28
  contextPercent: number;
29
29
  contextWindow: number;
30
+ /**
31
+ * True when a standalone `context_pct` segment is also part of the active
32
+ * layout. The model segment suppresses its inline percentage in that case to
33
+ * avoid showing the same value twice.
34
+ */
35
+ contextPctSegmentActive?: boolean;
30
36
  autoCompactEnabled: boolean;
31
37
  subagentCount: number;
32
38
  jobs: JobsSnapshot;
@@ -6,6 +6,7 @@ import { type JobsSnapshot } from "../jobs-observer";
6
6
  export interface StatusLineSegmentOptions {
7
7
  model?: {
8
8
  showThinkingLevel?: boolean;
9
+ showContextPercent?: boolean;
9
10
  };
10
11
  path?: {
11
12
  abbreviate?: boolean;
@@ -14,6 +14,7 @@ export interface WelcomeComponentOptions {
14
14
  getReservedBottomRows?: (termWidth: number) => number;
15
15
  changelogMarkdown?: string;
16
16
  collapseChangelog?: boolean;
17
+ buildLabel?: string;
17
18
  }
18
19
  /**
19
20
  * Sayknow-CLI launch surface: a blue-gradient SAYKNOW wordmark, compact
@@ -7,7 +7,7 @@ import type { AgentEvent, AgentMessage, AgentToolResult, ThinkingLevel } from "@
7
7
  import type { CompactionResult } from "@sayknow-cli/agent-core/compaction";
8
8
  import type { ImageContent, Model } from "@sayknow-cli/ai";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
- import type { SessionStats } from "../../session/agent-session";
10
+ import type { AgentSessionEvent, SessionStats } from "../../session/agent-session";
11
11
  import type { RpcExtensionUIRequest, RpcGetStateInclude, RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcUnattendedAccepted, RpcUnattendedDeclaration, RpcWorkflowGate, RpcWorkflowGateResolution } from "./rpc-types";
12
12
  export interface RpcClientOptions {
13
13
  /** Dial an existing Unix-domain socket instead of spawning a stdio child. */
@@ -37,6 +37,7 @@ export interface RpcClientOptions {
37
37
  }
38
38
  export type ModelInfo = Pick<Model, "provider" | "id" | "contextWindow" | "reasoning" | "thinking">;
39
39
  export type RpcEventListener = (event: AgentEvent) => void;
40
+ export type RpcSessionEventListener = (event: AgentSessionEvent) => void;
40
41
  export interface RpcClientToolContext<TDetails = unknown> {
41
42
  toolCallId: string;
42
43
  signal: AbortSignal;
@@ -72,9 +73,13 @@ export declare class RpcClient {
72
73
  */
73
74
  [Symbol.dispose](): void;
74
75
  /**
75
- * Subscribe to agent events.
76
+ * Subscribe to the legacy core AgentEvent subset.
76
77
  */
77
78
  onEvent(listener: RpcEventListener): () => void;
79
+ /**
80
+ * Subscribe to all renderer-facing agent session events.
81
+ */
82
+ onSessionEvent(listener: RpcSessionEventListener): () => void;
78
83
  /**
79
84
  * Subscribe to workflow lifecycle gates emitted by RPC mode.
80
85
  */
@@ -7,6 +7,18 @@ interface RenderInitialMessagesOptions {
7
7
  preserveExistingChat?: boolean;
8
8
  }
9
9
  export declare function argsWithPartialJson(args: unknown, partialJson: unknown): unknown;
10
+ export declare class CollapsedChatHistoryComponent implements Component {
11
+ readonly isCollapsedChatHistory = true;
12
+ count: number;
13
+ startTime: number;
14
+ endTime: number;
15
+ constructor(count: number, startTime: number, endTime: number);
16
+ merge(count: number, startTime: number, endTime: number): void;
17
+ render(_width: number): string[];
18
+ invalidate(): void;
19
+ }
20
+ export declare function addChatChild(ctx: InteractiveModeContext, component: Component): void;
21
+ export declare function trimChatChildren(ctx: InteractiveModeContext): void;
10
22
  export declare class UiHelpers {
11
23
  #private;
12
24
  private ctx;
@@ -375,6 +375,8 @@ export declare class TelegramNotificationDaemon {
375
375
  private rememberPendingThreadedFrame;
376
376
  private flushPendingThreadedFrames;
377
377
  private ensureTopic;
378
+ private topicPastOrphanGrace;
379
+ private deleteOrphanedTopic;
378
380
  private deleteTopic;
379
381
  private persistTopics;
380
382
  loadTopics(): Promise<void>;
@@ -70,10 +70,11 @@ export declare function getMCPServer(filePath: string, name: string): Promise<MC
70
70
  */
71
71
  export declare function listMCPServers(filePath: string): Promise<string[]>;
72
72
  /**
73
- * Set a server's autoload flag (connect automatically at session startup).
73
+ * Set a server's autoload flag for explicit runtime MCP consumers.
74
74
  *
75
- * Autoload defaults to true, so turning it on removes the key to keep the
76
- * config file free of redundant fields; turning it off writes `autoload: false`.
75
+ * Autoload defaults to true for consumers that honor it, so turning it on
76
+ * removes the key to keep the config file free of redundant fields; turning it
77
+ * off writes `autoload: false`.
77
78
  *
78
79
  * @throws Error if the server doesn't exist
79
80
  */
@@ -46,9 +46,10 @@ interface MCPServerConfigBase {
46
46
  /** Whether this server is enabled (default: true) */
47
47
  enabled?: boolean;
48
48
  /**
49
- * Whether to connect this server automatically at session startup (default: true).
50
- * When false the server stays configured-but-disconnected until connected
51
- * explicitly (e.g. `/mcp connect <name>`).
49
+ * Whether an explicit runtime MCP consumer should connect this server
50
+ * automatically when that consumer starts (default: true). Normal standalone
51
+ * `skc`, `skc --tmux`, and print-mode sessions do not consume `skc mcp`
52
+ * registrations today.
52
53
  */
53
54
  autoload?: boolean;
54
55
  /** Connection timeout in milliseconds (default: 30000) */
@@ -156,6 +156,10 @@ export interface AsyncJobSnapshot {
156
156
  recent: AsyncJobSnapshotItem[];
157
157
  delivery: AsyncJobDeliveryState;
158
158
  }
159
+ export interface RetainedMemorySample {
160
+ tuiChatChildren?: number;
161
+ tuiCachedRenderBytes?: number;
162
+ }
159
163
  export interface AgentSessionConfig {
160
164
  agent: Agent;
161
165
  sessionManager: SessionManager;
@@ -237,6 +241,8 @@ export interface AgentSessionConfig {
237
241
  * **MUST NOT** dispose it on their own teardown.
238
242
  */
239
243
  ownedAsyncJobManager?: AsyncJobManager;
244
+ /** Cheap TUI retained-memory counters; absent for headless sessions. */
245
+ retainedMemorySampler?: () => RetainedMemorySample;
240
246
  /**
241
247
  * MCPManager whose lifecycle this session owns (top-level sessions that
242
248
  * connected plugin-bundle MCP servers). Only the owned manager is
@@ -368,6 +374,15 @@ export declare const __agentSessionPerfCounters: {
368
374
  reset(): void;
369
375
  };
370
376
  export declare function buildContextInjectionSignature(kind: string, parts: readonly string[]): string;
377
+ export declare class StreamingEditFileCache {
378
+ #private;
379
+ get(path: string): string | undefined;
380
+ set(path: string, content: string): void;
381
+ delete(path: string): void;
382
+ clear(): void;
383
+ has(path: string): boolean;
384
+ get totalBytes(): number;
385
+ }
371
386
  export declare class AgentSession {
372
387
  #private;
373
388
  readonly agent: Agent;
@@ -935,6 +950,7 @@ export declare class AgentSession {
935
950
  prepareContributionPrep(options?: ContributionPrepOptions): Promise<ContributionPrepResult>;
936
951
  /** Test seam: override the emergency-compaction resource sampler so tests never read real RSS. */
937
952
  setResourceSampler(sampler: () => EmergencyCompactionSample): void;
953
+ setRetainedMemorySampler(sampler: (() => RetainedMemorySample) | undefined): void;
938
954
  /**
939
955
  * Toggle auto-compaction setting.
940
956
  */
@@ -535,6 +535,7 @@ export declare class SessionManager {
535
535
  getInjectedTtsrRules(): string[];
536
536
  getLeafId(): string | null;
537
537
  getLeafEntry(): SessionEntry | undefined;
538
+ getResidentImageBytes(): number;
538
539
  /**
539
540
  * Get the most recent model role from the current session path.
540
541
  * Returns undefined if no model change has been recorded.
@@ -4,15 +4,16 @@
4
4
  * Lets the agent hand off to another available skill in the current turn. The
5
5
  * callee's SKILL.md is dispatched through the same custom-message path used by
6
6
  * `/skill:<name>` typing, as a user-attribution message delivered same-turn
7
- * (without `deliverAs: "nextTurn"`). Before dispatch, the tool calls
8
- * `skc state <caller> handoff --to <callee>` in-process via the state-runtime
9
- * function so caller and callee mode-states plus `skill-active-state.json`
10
- * transition atomically.
7
+ * (without `deliverAs: "nextTurn"`). When the caller is one of the canonical
8
+ * workflow skills, the tool first runs the native state handoff so workflow
9
+ * mode-state and `skill-active-state.json` transition atomically. Runtime
10
+ * project/user skills have no native workflow mode-state, so they are dispatched
11
+ * directly and tracked by the prompt observer instead.
11
12
  *
12
- * Chaining is refused unless the caller's `current_phase` is in
13
- * `{complete, completed, handoff, failed, cancelled, canceled, inactive}`. The
14
- * agent declares readiness either by writing `current_phase: "handoff"` to its
15
- * mode-state or by running the handoff verb directly.
13
+ * Canonical workflow chaining is refused unless the caller's `current_phase` is
14
+ * in `{complete, completed, handoff, failed, cancelled, canceled, inactive}`.
15
+ * The agent declares readiness either by writing `current_phase: "handoff"` to
16
+ * its mode-state or by running the handoff verb directly.
16
17
  */
17
18
  import type { AgentTool, AgentToolResult } from "@sayknow-cli/agent-core";
18
19
  import * as z from "zod/v4";
@@ -0,0 +1,4 @@
1
+ type LinkedomModule = typeof import("linkedom");
2
+ type ParseHtml = LinkedomModule["parseHTML"];
3
+ export declare function parseHtmlLazy(markup: string): Promise<ReturnType<ParseHtml>>;
4
+ export {};
@@ -23,8 +23,10 @@ export interface ResolvePastedImagePathOptions extends DecodePastedPathOptions {
23
23
  */
24
24
  export declare function decodePastedPathCandidate(text: string, options?: DecodePastedPathOptions): string | undefined;
25
25
  /**
26
- * Returns the resolved path when the whole pasted text is a single path to an
27
- * existing image file (verified by extension AND content signature),
28
- * otherwise `undefined` (the paste is inserted as text).
26
+ * Returns the resolved path when the whole pasted text is a recognized clipboard
27
+ * temp path to an existing image file, otherwise `undefined` (the paste is
28
+ * inserted as text). Arbitrary saved image paths are not auto-attached here;
29
+ * users attach them explicitly with `@path/to/image.png`.
29
30
  */
30
31
  export declare function resolvePastedImagePath(text: string, options?: ResolvePastedImagePathOptions): string | undefined;
32
+ export declare function formatPastedImageReference(placeholder: string, imagePath: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/coding-agent",
4
- "version": "0.3.10",
4
+ "version": "0.3.12",
5
5
  "description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://sayknow-cli.com",
7
7
  "author": "jaybeyond",
@@ -52,12 +52,12 @@
52
52
  "@agentclientprotocol/sdk": "0.21.0",
53
53
  "@babel/parser": "^7.29.3",
54
54
  "@mozilla/readability": "^0.6.0",
55
- "@sayknow-cli/stats": "0.3.10",
56
- "@sayknow-cli/agent-core": "0.3.10",
57
- "@sayknow-cli/ai": "0.3.10",
58
- "@sayknow-cli/natives": "0.3.10",
59
- "@sayknow-cli/tui": "0.3.10",
60
- "@sayknow-cli/utils": "0.3.10",
55
+ "@sayknow-cli/stats": "0.3.12",
56
+ "@sayknow-cli/agent-core": "0.3.12",
57
+ "@sayknow-cli/ai": "0.3.12",
58
+ "@sayknow-cli/natives": "0.3.12",
59
+ "@sayknow-cli/tui": "0.3.12",
60
+ "@sayknow-cli/utils": "0.3.12",
61
61
  "@puppeteer/browsers": "^2.13.0",
62
62
  "@types/turndown": "5.0.6",
63
63
  "@xterm/headless": "^6.0.0",
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import * as path from "node:path";
4
+ import { buildDevCompileArgs } from "./compile-args";
4
5
 
5
6
  const packageDir = path.join(import.meta.dir, "..");
6
7
  const outputPath = path.join(packageDir, "dist", "skc");
@@ -34,49 +35,7 @@ async function main(): Promise<void> {
34
35
  await runCommand(["bun", "--cwd=../natives", "run", "embed:native"]);
35
36
  try {
36
37
  const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
37
- await runCommand(
38
- [
39
- "bun",
40
- "build",
41
- "--compile",
42
- // Minify shrinks the bundled JS the compiled binary must parse at
43
- // startup (302MB → ~114MB --help RSS measured on darwin-arm64).
44
- // --keep-names below preserves identifiers for error reports.
45
- "--minify",
46
- "--no-compile-autoload-bunfig",
47
- "--no-compile-autoload-dotenv",
48
- "--no-compile-autoload-tsconfig",
49
- "--no-compile-autoload-package-json",
50
- "--keep-names",
51
- "--define",
52
- 'process.env.PI_COMPILED="true"',
53
- "--external",
54
- "mupdf",
55
- "--root",
56
- "../..",
57
- "./src/cli.ts",
58
- // Worker entrypoints. Bun's `--compile` discovers the literal in
59
- // `new Worker("…", …)` at each spawn site, but only actually
60
- // emits the worker into the bunfs root when it is listed here as
61
- // an explicit additional entry. Paths are relative to this
62
- // script's cwd (packages/coding-agent) and the `--root` above
63
- // (../..) makes them appear inside the binary at
64
- // `/$bunfs/root/packages/<pkg>/src/<worker>.js`, which is
65
- // exactly what the literals at the spawn sites resolve to.
66
- "../stats/src/sync-worker.ts",
67
- "./src/tools/browser/tab-worker-entry.ts",
68
- "./src/eval/js/worker-entry.ts",
69
- // Hidden notify daemon CLI (loaded via dynamic import from notify-cli); list it
70
- // explicitly so Bun standalone keeps `skc notify daemon-internal` usable.
71
- "./src/notifications/telegram-daemon-cli.ts",
72
- // Hidden Telegram Remote gateway entrypoint. `skc telegram start` self-spawns
73
- // `skc telegram __gateway`, which dynamic-imports this bundled entry.
74
- "../telegram-remote/src/index.ts",
75
- "--outfile",
76
- "dist/skc",
77
- ],
78
- buildEnv,
79
- );
38
+ await runCommand(buildDevCompileArgs(), buildEnv);
80
39
 
81
40
  await stageWorkspaceNativeAddons();
82
41
  // Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
@@ -0,0 +1,106 @@
1
+ export interface CompileArgOptions {
2
+ root: string;
3
+ entrypoints: string[];
4
+ outfile: string;
5
+ target?: string;
6
+ defines?: string[];
7
+ externals?: string[];
8
+ }
9
+
10
+ export const compileAutoloadDisableFlags = [
11
+ "--no-compile-autoload-bunfig",
12
+ "--no-compile-autoload-dotenv",
13
+ "--no-compile-autoload-tsconfig",
14
+ "--no-compile-autoload-package-json",
15
+ ];
16
+
17
+ const compiledDefineFlags = ['process.env.PI_COMPILED="true"'];
18
+ const releaseDefineFlags = [...compiledDefineFlags, 'process.env.SKC_BUILD_CHANNEL="release"'];
19
+ const devDefineFlags = [...compiledDefineFlags, 'process.env.SKC_BUILD_CHANNEL="dev"'];
20
+
21
+ export const compiledExternalPackages = ["mupdf"];
22
+
23
+ export const releaseEntrypoints = [
24
+ "./packages/coding-agent/src/cli.ts",
25
+ "./packages/stats/src/sync-worker.ts",
26
+ "./packages/coding-agent/src/tools/browser/tab-worker-entry.ts",
27
+ "./packages/coding-agent/src/eval/js/worker-entry.ts",
28
+ "./packages/natives/native/index.js",
29
+ "./packages/coding-agent/src/notifications/telegram-daemon-cli.ts",
30
+ // Hidden Telegram Remote gateway entrypoint. `skc telegram start` self-spawns
31
+ // `skc telegram __gateway`, which dynamic-imports this bundled entry.
32
+ "./packages/telegram-remote/src/index.ts",
33
+ // NOTE: models.json must NOT be listed here — `bun build --compile` does not
34
+ // emit `.json` extra entrypoints into the bunfs. It is embedded via the
35
+ // `with { type: "file" }` import in packages/ai/src/models.ts instead.
36
+ // NOTE: handlebars must NOT be listed here either — the extra entrypoint
37
+ // silently vanished from minified bundles and crashed v0.9.3–v0.9.6
38
+ // releases at startup (#1939). It is bundled via the statically-traceable
39
+ // `require("handlebars")` in packages/utils/src/prompt.ts instead.
40
+ ];
41
+
42
+ export const devEntrypoints = [
43
+ "./src/cli.ts",
44
+ "../stats/src/sync-worker.ts",
45
+ "./src/tools/browser/tab-worker-entry.ts",
46
+ "./src/eval/js/worker-entry.ts",
47
+ "./src/notifications/telegram-daemon-cli.ts",
48
+ "../telegram-remote/src/index.ts",
49
+ ];
50
+
51
+ export function buildReleaseCompileArgs(target: string, outfile: string): string[] {
52
+ return buildCompileArgs({
53
+ root: ".",
54
+ entrypoints: releaseEntrypoints,
55
+ outfile,
56
+ target,
57
+ defines: releaseDefineFlags,
58
+
59
+ externals: compiledExternalPackages,
60
+ });
61
+ }
62
+
63
+ export function buildDevCompileArgs(outfile = "dist/skc"): string[] {
64
+ return buildCompileArgs({
65
+ root: "../..",
66
+ entrypoints: devEntrypoints,
67
+ outfile,
68
+ defines: devDefineFlags,
69
+ externals: compiledExternalPackages,
70
+ });
71
+ }
72
+
73
+ export function buildCompileArgs(options: CompileArgOptions): string[] {
74
+ const args = [
75
+ "bun",
76
+ "build",
77
+ "--compile",
78
+ // Minify shrinks the bundled JS the compiled binary must parse at
79
+ // startup (302MB → ~114MB --help RSS measured on darwin-arm64).
80
+ // --keep-names below preserves identifiers for error reports.
81
+ // SAFE with handlebars since #1939: the module is bundled via a
82
+ // statically-traceable require in packages/utils/src/prompt.ts, not a
83
+ // bunfs extra entrypoint (which --minify silently dropped, crashing
84
+ // v0.9.3–v0.9.6 releases at startup).
85
+ "--minify",
86
+ ...compileAutoloadDisableFlags,
87
+ "--keep-names",
88
+ ];
89
+
90
+ for (const define of options.defines ?? []) {
91
+ args.push("--define", define);
92
+ }
93
+
94
+ args.push("--root", options.root);
95
+
96
+ for (const external of options.externals ?? []) {
97
+ args.push("--external", external);
98
+ }
99
+
100
+ if (options.target) {
101
+ args.push("--target", options.target);
102
+ }
103
+
104
+ args.push(...options.entrypoints, "--outfile", options.outfile);
105
+ return args;
106
+ }
@@ -58,6 +58,7 @@ const mapEntries = docsWithContent
58
58
  .join("\n");
59
59
  const output = [
60
60
  "// Auto-generated by scripts/generate-docs-index.ts - DO NOT EDIT",
61
+ `Reflect.set(globalThis, Symbol.for("skc.docs-index.generated.loaded"), true);`,
61
62
  "",
62
63
  `export const EMBEDDED_DOC_FILENAMES: readonly string[] = ${filenamesLiteral};`,
63
64
  "",
@@ -0,0 +1,104 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { isCompiledBinary } from "@sayknow-cli/utils/env";
4
+
5
+ export type BuildChannel = "release" | "dev" | "local-source" | "package-install" | "compiled" | "unknown";
6
+
7
+ export interface BuildMetadata {
8
+ channel: BuildChannel;
9
+ label: string;
10
+ }
11
+
12
+ const REPO_MARKERS = [".git", "bun.lock"];
13
+ const SOURCE_REPO_PACKAGE_NAME = "sayknow-cli";
14
+
15
+ export function resolveBuildMetadata(moduleDir: string = import.meta.dir): BuildMetadata {
16
+ const explicitChannel = normalizeBuildChannel(process.env.SKC_BUILD_CHANNEL);
17
+ if (explicitChannel) {
18
+ return metadataForChannel(explicitChannel);
19
+ }
20
+
21
+ if (isCompiledBinary()) {
22
+ return metadataForChannel("compiled");
23
+ }
24
+
25
+ if (isLocalSourceTree(moduleDir)) {
26
+ return metadataForChannel("local-source");
27
+ }
28
+
29
+ return metadataForChannel("package-install");
30
+ }
31
+
32
+ export function formatBuildLabel(metadata: BuildMetadata = resolveBuildMetadata()): string {
33
+ return metadata.label;
34
+ }
35
+
36
+ function normalizeBuildChannel(value: string | undefined): BuildChannel | undefined {
37
+ const normalized = value?.trim().toLowerCase();
38
+ if (!normalized) return undefined;
39
+ switch (normalized) {
40
+ case "release":
41
+ case "stable":
42
+ return "release";
43
+ case "dev":
44
+ case "development":
45
+ return "dev";
46
+ case "local-source":
47
+ case "source":
48
+ case "local":
49
+ return "local-source";
50
+ case "package-install":
51
+ case "package":
52
+ case "npm":
53
+ return "package-install";
54
+ case "compiled":
55
+ return "compiled";
56
+ case "unknown":
57
+ return "unknown";
58
+ default:
59
+ return "unknown";
60
+ }
61
+ }
62
+
63
+ function metadataForChannel(channel: BuildChannel): BuildMetadata {
64
+ switch (channel) {
65
+ case "release":
66
+ return { channel, label: "release build" };
67
+ case "dev":
68
+ return { channel, label: "dev build" };
69
+ case "local-source":
70
+ return { channel, label: "local source" };
71
+ case "package-install":
72
+ return { channel, label: "package install" };
73
+ case "compiled":
74
+ return { channel, label: "compiled build" };
75
+ case "unknown":
76
+ return { channel, label: "build unknown" };
77
+ }
78
+ }
79
+
80
+ function isLocalSourceTree(startDir: string): boolean {
81
+ let current = path.resolve(startDir);
82
+ while (true) {
83
+ if (hasRepoMarkers(current)) return true;
84
+ const parent = path.dirname(current);
85
+ if (parent === current) return false;
86
+ current = parent;
87
+ }
88
+ }
89
+
90
+ function hasRepoMarkers(dir: string): boolean {
91
+ if (!REPO_MARKERS.every(marker => fs.existsSync(path.join(dir, marker)))) return false;
92
+ return readPackageName(path.join(dir, "package.json")) === SOURCE_REPO_PACKAGE_NAME;
93
+ }
94
+
95
+ function readPackageName(packageJsonPath: string): string | undefined {
96
+ try {
97
+ const parsed: unknown = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
98
+ if (typeof parsed !== "object" || parsed === null || !("name" in parsed)) return undefined;
99
+ const name = parsed.name;
100
+ return typeof name === "string" ? name : undefined;
101
+ } catch {
102
+ return undefined;
103
+ }
104
+ }
@@ -15,7 +15,7 @@ export interface MCPServer {
15
15
  name: string;
16
16
  /** Whether this server is enabled (default: true) */
17
17
  enabled?: boolean;
18
- /** Whether to connect automatically at session startup (default: true) */
18
+ /** Whether explicit runtime MCP consumers should connect automatically (default: true) */
19
19
  autoload?: boolean;
20
20
  /** Connection timeout in milliseconds */
21
21
  timeout?: number;
@@ -25,6 +25,8 @@ export interface MCPServer {
25
25
  args?: string[];
26
26
  /** Environment variables */
27
27
  env?: Record<string, string>;
28
+ /** Whether explicit stdio runtime consumers should avoid inheriting host environment */
29
+ noInheritEnv?: boolean;
28
30
  /** Working directory for stdio transport */
29
31
  cwd?: string;
30
32
  /** URL (for HTTP/SSE transport) */