@sayknow-cli/coding-agent 0.2.7 → 0.3.1
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/CHANGELOG.md +20 -0
- package/dist/types/async/job-manager.d.ts +3 -1
- package/dist/types/cli/daemon-cli.d.ts +25 -0
- package/dist/types/cli/notify-cli.d.ts +25 -0
- package/dist/types/cli/setup-cli.d.ts +20 -1
- package/dist/types/commands/daemon.d.ts +41 -0
- package/dist/types/commands/notify.d.ts +41 -0
- package/dist/types/config/model-profile-activation.d.ts +12 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +136 -3
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/daemon/builtin.d.ts +20 -0
- package/dist/types/daemon/control-types.d.ts +57 -0
- package/dist/types/daemon/runtime.d.ts +25 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/i18n/messages/en.d.ts +1 -0
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/modes/components/oauth-selector.d.ts +2 -0
- package/dist/types/modes/components/settings-defs.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/notifications/attachment-registry.d.ts +17 -0
- package/dist/types/notifications/chat-adapters.d.ts +9 -0
- package/dist/types/notifications/config-commands.d.ts +26 -0
- package/dist/types/notifications/config.d.ts +69 -0
- package/dist/types/notifications/engine.d.ts +59 -0
- package/dist/types/notifications/helpers.d.ts +55 -0
- package/dist/types/notifications/html-format.d.ts +62 -0
- package/dist/types/notifications/index.d.ts +28 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
- package/dist/types/notifications/telegram-cli.d.ts +19 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
- package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
- package/dist/types/notifications/telegram-daemon.d.ts +295 -0
- package/dist/types/notifications/telegram-reference.d.ts +111 -0
- package/dist/types/notifications/threaded-inbound.d.ts +77 -0
- package/dist/types/notifications/threaded-render.d.ts +71 -0
- package/dist/types/notifications/topic-registry.d.ts +67 -0
- package/dist/types/rlm/index.d.ts +12 -0
- package/dist/types/session/agent-session.d.ts +41 -2
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/setup/credential-auto-import.d.ts +63 -0
- package/dist/types/setup/credential-import.d.ts +3 -0
- package/dist/types/setup/host-plugin-setup.d.ts +39 -0
- package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/state-writer.d.ts +2 -0
- package/dist/types/skc-runtime/tmux-common.d.ts +3 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +2 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +15 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +14 -0
- package/dist/types/tools/ask-answer-registry.d.ts +13 -0
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +19 -0
- package/dist/types/tools/subagent.d.ts +3 -0
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +22 -0
- package/dist/types/web/search/provider.d.ts +18 -1
- package/dist/types/web/search/providers/insane.d.ts +53 -0
- package/dist/types/web/search/providers/text-citations.d.ts +23 -0
- package/dist/types/web/search/types.d.ts +12 -4
- package/package.json +10 -8
- package/scripts/build-binary.ts +3 -0
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/async/job-manager.ts +5 -1
- package/src/cli/args.ts +1 -1
- package/src/cli/daemon-cli.ts +122 -0
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/notify-cli.ts +421 -0
- package/src/cli/setup-cli.ts +173 -84
- package/src/cli.ts +3 -3
- package/src/commands/daemon.ts +47 -0
- package/src/commands/notify.ts +61 -0
- package/src/commands/setup.ts +11 -1
- package/src/commands/team.ts +1 -1
- package/src/config/model-profile-activation.ts +74 -5
- package/src/config/model-profiles.ts +7 -4
- package/src/config/model-registry.ts +6 -3
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +143 -0
- package/src/coordinator/contract.ts +3 -0
- package/src/coordinator-mcp/server.ts +270 -1
- package/src/daemon/builtin.ts +46 -0
- package/src/daemon/control-types.ts +65 -0
- package/src/daemon/runtime.ts +51 -0
- package/src/defaults/skc/rules/ponytail.md +68 -0
- package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
- package/src/defaults/skc/skills/ultragoal/SKILL.md +16 -0
- package/src/edit/modes/replace.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +8 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/goals/tools/goal-tool.ts +11 -2
- package/src/hashline/hash.ts +1 -1
- package/src/i18n/messages/de.ts +1 -0
- package/src/i18n/messages/en.ts +1 -0
- package/src/i18n/messages/es.ts +1 -0
- package/src/i18n/messages/fr.ts +1 -0
- package/src/i18n/messages/ja.ts +1 -0
- package/src/i18n/messages/ko.ts +1 -0
- package/src/i18n/messages/zh.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +12 -10
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/main.ts +30 -0
- package/src/modes/acp/acp-event-mapper.ts +1 -0
- package/src/modes/components/hook-editor.ts +7 -2
- package/src/modes/components/oauth-selector.ts +19 -0
- package/src/modes/components/settings-defs.ts +2 -1
- package/src/modes/components/settings-selector.ts +7 -2
- package/src/modes/controllers/event-controller.ts +35 -0
- package/src/modes/controllers/selector-controller.ts +80 -17
- package/src/modes/interactive-mode.ts +52 -4
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/event-contract.ts +1 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
- package/src/modes/shared/agent-wire/event-observation.ts +16 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
- package/src/modes/theme/theme.ts +4 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +23 -0
- package/src/notifications/attachment-registry.ts +23 -0
- package/src/notifications/chat-adapters.ts +147 -0
- package/src/notifications/config-commands.ts +50 -0
- package/src/notifications/config.ts +128 -0
- package/src/notifications/engine.ts +100 -0
- package/src/notifications/helpers.ts +135 -0
- package/src/notifications/html-format.ts +389 -0
- package/src/notifications/index.ts +842 -0
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/rate-limit-pool.ts +179 -0
- package/src/notifications/telegram-cli.ts +194 -0
- package/src/notifications/telegram-daemon-cli.ts +74 -0
- package/src/notifications/telegram-daemon-control.ts +370 -0
- package/src/notifications/telegram-daemon.ts +1591 -0
- package/src/notifications/telegram-reference.ts +335 -0
- package/src/notifications/threaded-inbound.ts +136 -0
- package/src/notifications/threaded-render.ts +173 -0
- package/src/notifications/topic-registry.ts +133 -0
- package/src/rlm/index.ts +19 -0
- package/src/sdk.ts +16 -0
- package/src/session/agent-session.ts +195 -54
- package/src/session/auth-storage.ts +3 -0
- package/src/session/session-dump-format.ts +43 -2
- package/src/session/session-manager.ts +39 -5
- package/src/setup/credential-auto-import.ts +258 -0
- package/src/setup/credential-import.ts +17 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
- package/src/setup/host-plugin-setup.ts +142 -0
- package/src/skc-runtime/deep-interview-recorder.ts +2 -2
- package/src/skc-runtime/launch-tmux.ts +27 -5
- package/src/skc-runtime/ledger-event-renderer.ts +1 -0
- package/src/skc-runtime/ralplan-runtime.ts +2 -2
- package/src/skc-runtime/state-runtime.ts +18 -10
- package/src/skc-runtime/state-writer.ts +8 -8
- package/src/skc-runtime/tmux-common.ts +8 -0
- package/src/skc-runtime/tmux-sessions.ts +8 -1
- package/src/skc-runtime/ultragoal-guard.ts +57 -2
- package/src/skc-runtime/ultragoal-runtime.ts +105 -19
- package/src/skc-runtime/workflow-manifest.generated.json +56 -2
- package/src/skc-runtime/workflow-manifest.ts +18 -3
- package/src/slash-commands/builtin-registry.ts +4 -1
- package/src/task/executor.ts +5 -1
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +77 -6
- package/src/tools/fetch.ts +78 -1
- package/src/tools/image-gen.ts +5 -8
- package/src/tools/index.ts +22 -0
- package/src/tools/inspect-image.ts +16 -11
- package/src/tools/subagent-render.ts +7 -0
- package/src/tools/subagent.ts +38 -7
- package/src/tools/telegram-send.ts +137 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +155 -0
- package/src/web/search/provider.ts +77 -18
- package/src/web/search/providers/anthropic.ts +70 -3
- package/src/web/search/providers/codex.ts +1 -119
- package/src/web/search/providers/gemini.ts +99 -0
- package/src/web/search/providers/insane.ts +551 -0
- package/src/web/search/providers/openai-compatible.ts +66 -32
- package/src/web/search/providers/text-citations.ts +111 -0
- package/src/web/search/types.ts +13 -2
- package/vendor/insane-search/LICENSE +21 -0
- package/vendor/insane-search/MANIFEST.json +24 -0
- package/vendor/insane-search/engine/__init__.py +23 -0
- package/vendor/insane-search/engine/__main__.py +128 -0
- package/vendor/insane-search/engine/bias_check.py +183 -0
- package/vendor/insane-search/engine/executor.py +254 -0
- package/vendor/insane-search/engine/fetch_chain.py +725 -0
- package/vendor/insane-search/engine/learning.py +175 -0
- package/vendor/insane-search/engine/phase0.py +214 -0
- package/vendor/insane-search/engine/safety.py +91 -0
- package/vendor/insane-search/engine/templates/package.json +11 -0
- package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
- package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
- package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
- package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
- package/vendor/insane-search/engine/tests/test_u1.py +200 -0
- package/vendor/insane-search/engine/tests/test_u4.py +131 -0
- package/vendor/insane-search/engine/tests/test_u5.py +163 -0
- package/vendor/insane-search/engine/tests/test_u7.py +124 -0
- package/vendor/insane-search/engine/transport.py +211 -0
- package/vendor/insane-search/engine/url_transforms.py +98 -0
- package/vendor/insane-search/engine/validators.py +331 -0
- package/vendor/insane-search/engine/waf_detector.py +214 -0
- package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the `skc daemon` control plane.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately compact: a small result/status surface plus a built-in
|
|
5
|
+
* controller contract. There is exactly one daemon kind today (`telegram`);
|
|
6
|
+
* a richer registry is intentionally deferred until a second kind exists.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type DaemonKind = "telegram";
|
|
10
|
+
|
|
11
|
+
export type DaemonAction = "list" | "status" | "stop" | "reload";
|
|
12
|
+
|
|
13
|
+
export type DaemonHealth = "not_configured" | "stopped" | "running" | "stale" | "stopping" | "error";
|
|
14
|
+
|
|
15
|
+
export interface DaemonRuntimeInfo {
|
|
16
|
+
/** `source` when respawn goes through bun/node + the entry script; `compiled` for a single-file binary. */
|
|
17
|
+
mode: "source" | "compiled";
|
|
18
|
+
execPath: string;
|
|
19
|
+
/** True only in source/dev mode, where a respawn loads amended TypeScript directly. */
|
|
20
|
+
reloadPicksUpSourceEdits: boolean;
|
|
21
|
+
/** Present when the runtime mode constrains what reload can achieve (e.g. compiled binary). */
|
|
22
|
+
warning?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DaemonStatus {
|
|
26
|
+
kind: DaemonKind;
|
|
27
|
+
configured: boolean;
|
|
28
|
+
health: DaemonHealth;
|
|
29
|
+
pid?: number;
|
|
30
|
+
ownerId?: string;
|
|
31
|
+
startedAt?: number;
|
|
32
|
+
heartbeatAt?: number;
|
|
33
|
+
roots?: string[];
|
|
34
|
+
rootCount?: number;
|
|
35
|
+
runtime: DaemonRuntimeInfo;
|
|
36
|
+
detail?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DaemonOperationOptions {
|
|
40
|
+
/** How long to wait for cooperative release before escalating. */
|
|
41
|
+
gracefulTimeoutMs?: number;
|
|
42
|
+
/** How long to wait for the old pid to die after SIGKILL. */
|
|
43
|
+
killTimeoutMs?: number;
|
|
44
|
+
/** Allow hard-kill escalation / acting on a still-live owner. */
|
|
45
|
+
force?: boolean;
|
|
46
|
+
/** For reload: spawn a fresh owner even when none is currently running. */
|
|
47
|
+
spawnIfStopped?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DaemonOperationResult {
|
|
51
|
+
kind: DaemonKind;
|
|
52
|
+
action: Exclude<DaemonAction, "list">;
|
|
53
|
+
ok: boolean;
|
|
54
|
+
before?: DaemonStatus;
|
|
55
|
+
after?: DaemonStatus;
|
|
56
|
+
warnings: string[];
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface BuiltInDaemonController {
|
|
61
|
+
readonly kind: DaemonKind;
|
|
62
|
+
status(): Promise<DaemonStatus>;
|
|
63
|
+
stop(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
64
|
+
reload(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
65
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source-vs-compiled runtime detection for daemon spawning.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the logic previously embedded in `ensureTelegramDaemonRunning`
|
|
5
|
+
* so session autostart, reload, and status reporting agree on how a daemon
|
|
6
|
+
* process is launched and whether a reload can pick up amended source.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
|
|
11
|
+
export interface SkcRuntimeSpawnInfo {
|
|
12
|
+
execPath: string;
|
|
13
|
+
mode: "source" | "compiled";
|
|
14
|
+
/** Prefix prepended before the skc subcommand args; `[Bun.main]` in source mode, otherwise `[]`. */
|
|
15
|
+
argsPrefix: string[];
|
|
16
|
+
/** True only when respawn loads edited TypeScript directly (source/dev mode). */
|
|
17
|
+
reloadPicksUpSourceEdits: boolean;
|
|
18
|
+
/** Set in compiled mode to explain that a rebuild is required before reload picks up source edits. */
|
|
19
|
+
warning?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const COMPILED_RELOAD_WARNING =
|
|
23
|
+
"Compiled binary: reload respawns the same binary. Rebuild the binary first for amended source to take effect.";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve how to spawn a detached skc subcommand for the current runtime.
|
|
27
|
+
*
|
|
28
|
+
* Source/dev mode (bun/node) prepends the entry script (`Bun.main`) so the
|
|
29
|
+
* respawn loads edited source. A compiled single-file binary self-spawns its
|
|
30
|
+
* own subcommand directly and cannot pick up workspace source edits.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveSkcRuntimeSpawnInfo(execPath: string = process.execPath): SkcRuntimeSpawnInfo {
|
|
33
|
+
const base = path.basename(execPath).toLowerCase();
|
|
34
|
+
const fromSource = base === "bun" || base === "node" || base.startsWith("bun") || base.startsWith("node");
|
|
35
|
+
const mainScript = fromSource && typeof Bun !== "undefined" ? (Bun as unknown as { main?: string }).main : undefined;
|
|
36
|
+
if (fromSource) {
|
|
37
|
+
return {
|
|
38
|
+
execPath,
|
|
39
|
+
mode: "source",
|
|
40
|
+
argsPrefix: mainScript ? [mainScript] : [],
|
|
41
|
+
reloadPicksUpSourceEdits: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
execPath,
|
|
46
|
+
mode: "compiled",
|
|
47
|
+
argsPrefix: [],
|
|
48
|
+
reloadPicksUpSourceEdits: false,
|
|
49
|
+
warning: COMPILED_RELOAD_WARNING,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ponytail
|
|
3
|
+
description: "Lazy senior dev mode — always pick the simplest, shortest solution that works (YAGNI, reuse, stdlib/native first), never cutting validation, error handling, security, or accessibility."
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
license: MIT
|
|
6
|
+
source: https://github.com/DietrichGebert/ponytail
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Ponytail — lazy senior dev mode
|
|
10
|
+
|
|
11
|
+
You write code like the laziest senior dev in the room. Lazy means efficient,
|
|
12
|
+
not careless. The best code is the code you never wrote. This rule governs
|
|
13
|
+
*what* you build, not how you talk.
|
|
14
|
+
|
|
15
|
+
## The ladder
|
|
16
|
+
|
|
17
|
+
After you understand the problem, stop at the first rung that holds:
|
|
18
|
+
|
|
19
|
+
1. **Does this need to exist at all?** Speculative need → skip it, say so in one line (YAGNI).
|
|
20
|
+
2. **Already in this codebase?** A helper, util, type, or pattern that lives here → reuse it. Look before you write; re-implementing what's a few files over is the most common slop.
|
|
21
|
+
3. **Stdlib does it?** Use it.
|
|
22
|
+
4. **Native platform feature covers it?** `<input type="date">` over a picker lib, CSS over JS, DB constraint over app code.
|
|
23
|
+
5. **Already-installed dependency solves it?** Use it. Never add a new dependency for what a few lines can do.
|
|
24
|
+
6. **Can it be one line?** One line.
|
|
25
|
+
7. **Only then:** the minimum code that works.
|
|
26
|
+
|
|
27
|
+
The ladder runs *after* comprehension, not instead of it. Read the task and the
|
|
28
|
+
code it touches, trace the real flow end to end, then climb. Two rungs work →
|
|
29
|
+
take the higher one and move on.
|
|
30
|
+
|
|
31
|
+
**Bug fix = root cause, not symptom.** Grep every caller of the function you are
|
|
32
|
+
about to touch. One guard in the shared function is a smaller diff than a guard
|
|
33
|
+
in every caller — and patching only the path the ticket names leaves every
|
|
34
|
+
sibling caller broken.
|
|
35
|
+
|
|
36
|
+
## Rules
|
|
37
|
+
|
|
38
|
+
- No unrequested abstractions: no interface with one implementation, no factory for one product, no config for a value that never changes.
|
|
39
|
+
- No boilerplate or scaffolding "for later" — later can scaffold for itself.
|
|
40
|
+
- Deletion over addition. Boring over clever; clever is what someone decodes at 3am.
|
|
41
|
+
- Fewest files possible. Shortest working diff wins — but only once you understand the problem. The smallest change in the wrong place is a second bug, not laziness.
|
|
42
|
+
- Two stdlib options the same size? Take the one that is correct on edge cases.
|
|
43
|
+
- Mark deliberate simplifications with a `ponytail:` comment that names the ceiling and the upgrade path (e.g. `// ponytail: global lock, per-account locks if throughput matters`).
|
|
44
|
+
|
|
45
|
+
## Output
|
|
46
|
+
|
|
47
|
+
Code first. Then at most three short lines: what was skipped, when to add it.
|
|
48
|
+
No essays defending a simplification — every paragraph of defense is complexity
|
|
49
|
+
smuggled back in as prose. Explanation the user explicitly asked for (a report,
|
|
50
|
+
a walkthrough) is not debt; give it in full.
|
|
51
|
+
|
|
52
|
+
Pattern: `[code] → skipped: [X], add when [Y].`
|
|
53
|
+
|
|
54
|
+
## When NOT to be lazy
|
|
55
|
+
|
|
56
|
+
Never simplify away: input validation at trust boundaries, error handling that
|
|
57
|
+
prevents data loss, security measures, accessibility basics, or anything
|
|
58
|
+
explicitly requested. Never be lazy about *understanding* the problem — the
|
|
59
|
+
ladder shortens the solution, never the reading. A confident small diff in the
|
|
60
|
+
wrong place is the dangerous kind of lazy.
|
|
61
|
+
|
|
62
|
+
Non-trivial logic (a branch, loop, parser, money/security path) leaves ONE
|
|
63
|
+
runnable check behind — the smallest thing that fails if the logic breaks. No
|
|
64
|
+
frameworks or fixtures unless asked. Trivial one-liners need no test.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
Adapted from [ponytail](https://github.com/DietrichGebert/ponytail) (MIT) by DietrichGebert.
|
|
@@ -47,7 +47,7 @@ Planning artifacts and stage handoffs MUST be persisted through the ralplan CLI
|
|
|
47
47
|
skc ralplan --write --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.skc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.skc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.skc/_session-{sessionid}/specs`, `.skc/_session-{sessionid}/plans`, `.skc/_session-{sessionid}/state`, or any other `.skc/` path are forbidden unless an explicit force override is active.
|
|
50
|
+
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.skc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.skc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.skc/_session-{sessionid}/specs`, `.skc/_session-{sessionid}/plans`, `.skc/_session-{sessionid}/state`, or any other `.skc/` path are forbidden unless an explicit force override is active.
|
|
51
51
|
|
|
52
52
|
While ralplan is active it is a pre-approval planning phase: product-code mutation tools (`write`/`edit`/`ast_edit`) and product-mutating `bash` (e.g. `tee src/...`, redirects into the project tree) are blocked, exactly like deep-interview. Prefer passing the `--artifact` markdown **inline** (the content string) so no scratch file is needed; this is mandatory for restricted role agents (see below). Only the leader, and only when an artifact is too large to pass inline, may stage it as a file in a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree and pass that path — never write scratch files into the repo or `.skc/`. Product code is mutated only after the plan is approved and execution begins.
|
|
53
53
|
|
|
@@ -80,9 +80,16 @@ The consensus workflow:
|
|
|
80
80
|
d. Return to Critic evaluation
|
|
81
81
|
e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
|
|
82
82
|
f. If 5 iterations are reached without `APPROVE`, present the best version to the user
|
|
83
|
-
6.
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
6. **Post-ralplan interview** (intent reconciliation gate): After Critic returns `APPROVE` and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
84
|
+
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
85
|
+
b. **Cross-check prior context for conflicts**: glob `.skc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
86
|
+
c. **Reconcile with the user**:
|
|
87
|
+
- *(--interactive only)* Use the `ask` tool to confirm the open assumptions and conflicts **one at a time**, weakest/highest-impact first, polishing intent. If any confirmation reveals that the plan diverges from user intent, route the consolidated correction back into the re-review loop (step 5b Planner revision) and re-run Architect + Critic before returning here. Cap at the same 5-iteration ceiling.
|
|
88
|
+
- *(automated mode)* Do not ask. Embed every unconfirmed assumption and every detected prior-context conflict into the final plan under an **## Intent Reconciliation** section as explicit open confirmations the user must review at the `pending approval` gate, so nothing is silently assumed.
|
|
89
|
+
d. Persist the reconciliation with `skc ralplan --write --stage post-interview --stage_n <N> --artifact "..." --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
|
|
90
|
+
7. On reconciliation completion, mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `skc ralplan --write --stage final --stage_n <N> --artifact "..."`, and do not directly edit `.skc/_session-{sessionid}/plans`. *(--interactive only)* If `--interactive` is set, use the `ask` tool to present the plan with approval options (Approve execution via ultragoal (Recommended) / Approve execution via team (only when tmux-based interactive worker parallelization is required) / Compact then return for execution approval / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section. Otherwise, output the final plan and stop before any mutation or delegation.
|
|
91
|
+
8. *(--interactive only)* User chooses: Approve ultragoal execution (recommended), Approve team execution (tmux parallelization only), Request changes, or Reject
|
|
92
|
+
9. *(--interactive only)* On approval: invoke `/skill:ultragoal` for execution by default; invoke `/skill:team` only when the user explicitly needs tmux-based interactive worker parallelization -- never implement directly
|
|
86
93
|
|
|
87
94
|
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
88
95
|
|
|
@@ -107,6 +107,21 @@ Loop until `skc ultragoal status` reports all goals complete:
|
|
|
107
107
|
`skc ultragoal checkpoint --goal-id <id> --status blocked --evidence "<completed legacy SKC goal blocks goal create in this thread>" --skc-goal-json <goal-get-json-or-path>`
|
|
108
108
|
11. Resume failed goals with `skc ultragoal complete-goals --retry-failed`.
|
|
109
109
|
|
|
110
|
+
## Blocker triage and pause discipline
|
|
111
|
+
|
|
112
|
+
An active Ultragoal run must not give up on a blocker by pausing the goal and asking the user. Classify every blocker before deciding what to do, and default to `resolvable` when unsure:
|
|
113
|
+
|
|
114
|
+
- **`resolvable`** — anything the agent can act on: failing tests, missing implementation, a dependency to install, an ambiguous-but-inferable detail, investigation. **Never pause.** Exhaust autonomous resolution first: investigate, `skc ultragoal steer --kind add_subgoal --title "Investigate blocker" --objective "..." --evidence "..." --rationale "..."`, delegate an `executor`, or preserve the blocker durably with `skc ultragoal checkpoint --status blocked` / `skc ultragoal record-review-blockers` and keep scheduling the next goal.
|
|
115
|
+
- **`human_blocked`** — only the user can act: credentials/secrets, a manual or physical step, an external approval/decision, access the agent lacks. Pause is the last resort and is gated.
|
|
116
|
+
|
|
117
|
+
`goal({"op":"pause"})` is **blocked at runtime** while an Ultragoal run is active unless the latest durable ledger event classifies the current blocker as `human_blocked`. To pause, record the classification immediately before pausing and cite the human-only dependency as evidence:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
skc ultragoal classify-blocker --classification human_blocked --evidence "<the specific human-only dependency>" [--goal-id <id>]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Recording `--classification resolvable` is an audit note only; it never authorizes a pause. The `ask` tool stays blocked during active runs regardless of classification — record unresolved decisions as durable blockers instead of prompting.
|
|
124
|
+
|
|
110
125
|
## Dynamic steering
|
|
111
126
|
|
|
112
127
|
Use `skc ultragoal steer` when real findings or blockers prove the current story decomposition should change while the aggregate objective and constraints stay fixed. Steering is explicit-only and evidence-backed; broad natural-language requests are rejected instead of guessed.
|
|
@@ -199,6 +214,7 @@ An ultragoal story cannot be checkpointed `complete` until the active agent has
|
|
|
199
214
|
- CLI surfaces require runtime argv replay: `replaySafe: true`, an allowlisted argv `command`, and replayed normalized stdout matching `recordedStdout`; unsafe commands require audited `replayExempt` metadata with exact fields `reasonCode`, `reason`, `approvedBy`, and `fallbackArtifactRefs` plus a structurally valid fallback artifact. Allowed `reasonCode` values are exactly `unsafe_side_effect`, `requires_credentials`, `requires_network`, `non_deterministic_external`, `destructive`, `interactive_only`, and `platform_unavailable`.
|
|
200
215
|
- Native/desktop/tui surfaces require a structurally valid screenshot, PTY capture with terminal control codes, or app-automation transcript.
|
|
201
216
|
- API/package/algorithm/math surfaces require a real artifact file or typed receipt. Bare `inlineEvidence` text alone is not sufficient for any surface.
|
|
217
|
+
- The mandatory **computer-use** red-team suite (`kill-switch-bypass`, `suspended-enforcement`, `permission-revoked`, …) is conditional, not universal: require it only when computer/desktop control is genuinely part of the product surface being dogfooded. For every other product type, prove the change through the matching live surface instead — browser-use automation for web/GUI, bash/CLI live invocation or argv replay for CLI, and real artifacts or typed receipts for API/package/algorithm/math. Editing docs, prompts, or skills that merely mention computer-use does not by itself make the computer-use suite applicable; pick the red-team surface that matches what the change actually ships.
|
|
202
218
|
7. The executor QA/red-team lane must report a matrix using `executorQa.contractCoverage`, `executorQa.surfaceEvidence`, `executorQa.adversarialCases`, and `executorQa.artifactRefs`. Not-applicable rows are allowed only in `contractCoverage` and `surfaceEvidence`; each `status: "not_applicable"` row requires `contractRef` plus `reason`. `adversarialCases` rows cannot be not-applicable.
|
|
203
219
|
8. Run a final code review pass and fold it into the strict quality gate. Clean means `architectReview.architectureStatus`, `architectReview.productStatus`, and `architectReview.codeStatus` are all `"CLEAR"`, `architectReview.recommendation` is `"APPROVE"`, executor QA statuses are `"passed"`, iteration is `"passed"` with `fullRerun: true`, every evidence field is non-empty, every required matrix row is present, and every blockers array is empty. `COMMENT`, `WATCH`, `REQUEST CHANGES`, `BLOCK`, missing evidence, missing or shallow matrix rows, plan/code mismatches, or non-empty blockers are non-clean.
|
|
204
220
|
9. If any lane finds an issue, do **not** checkpoint `complete` and do **not** call `goal({"op":"complete"})`. Record durable blocker work instead:
|
|
@@ -42,7 +42,7 @@ let scoreSequenceFuzzyNative:
|
|
|
42
42
|
let findBestFuzzyMatchNative:
|
|
43
43
|
| ((content: string, target: string, threshold: number) => NativeBestFuzzyMatchResult)
|
|
44
44
|
| undefined;
|
|
45
|
-
void import("
|
|
45
|
+
void import("@sayknow-cli/natives")
|
|
46
46
|
.then(mod => {
|
|
47
47
|
if (typeof mod.h02ScoreSequenceFuzzy === "function") {
|
|
48
48
|
scoreSequenceFuzzyNative = mod.h02ScoreSequenceFuzzy;
|
|
@@ -6,6 +6,7 @@ import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMeta
|
|
|
6
6
|
import type { KeyId } from "@sayknow-cli/tui";
|
|
7
7
|
import { logger } from "@sayknow-cli/utils";
|
|
8
8
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
9
|
+
import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/unattended-session";
|
|
9
10
|
import { type Theme, theme } from "../../modes/theme/theme";
|
|
10
11
|
import type { SessionManager } from "../../session/session-manager";
|
|
11
12
|
import type {
|
|
@@ -180,6 +181,7 @@ export class ExtensionRunner {
|
|
|
180
181
|
#getContextUsageFn: () => ContextUsage | undefined = () => undefined;
|
|
181
182
|
#compactFn: (instructionsOrOptions?: string | CompactOptions) => Promise<void> = async () => {};
|
|
182
183
|
#getSystemPromptFn: () => string[] = () => [];
|
|
184
|
+
#getWorkflowGateFn: () => WorkflowGateEmitter | undefined = () => undefined;
|
|
183
185
|
#newSessionHandler: NewSessionHandler = async () => ({ cancelled: false });
|
|
184
186
|
#branchHandler: BranchHandler = async () => ({ cancelled: false });
|
|
185
187
|
#navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false });
|
|
@@ -234,6 +236,7 @@ export class ExtensionRunner {
|
|
|
234
236
|
this.#hasPendingMessagesFn = contextActions.hasPendingMessages;
|
|
235
237
|
this.#shutdownHandler = contextActions.shutdown;
|
|
236
238
|
this.#getSystemPromptFn = contextActions.getSystemPrompt;
|
|
239
|
+
this.#getWorkflowGateFn = contextActions.getWorkflowGate ?? (() => undefined);
|
|
237
240
|
|
|
238
241
|
// Command context actions (optional, only for interactive mode)
|
|
239
242
|
if (commandContextActions) {
|
|
@@ -463,6 +466,7 @@ export class ExtensionRunner {
|
|
|
463
466
|
shutdown: () => this.#shutdownHandler(),
|
|
464
467
|
getSystemPrompt: () => this.#getSystemPromptFn(),
|
|
465
468
|
hasQueuedMessages: () => this.#hasPendingMessagesFn(), // deprecated alias
|
|
469
|
+
workflowGate: this.#getWorkflowGateFn(),
|
|
466
470
|
};
|
|
467
471
|
}
|
|
468
472
|
|
|
@@ -32,6 +32,7 @@ import type { PythonResult } from "../../eval/py/executor";
|
|
|
32
32
|
import type { BashResult } from "../../exec/bash-executor";
|
|
33
33
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
34
34
|
import type { CustomEditor } from "../../modes/components/custom-editor";
|
|
35
|
+
import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/unattended-session";
|
|
35
36
|
import type { Theme } from "../../modes/theme/theme";
|
|
36
37
|
import type { CustomMessage } from "../../session/messages";
|
|
37
38
|
import type { ReadonlySessionManager, SessionManager } from "../../session/session-manager";
|
|
@@ -310,6 +311,11 @@ export interface ExtensionContext {
|
|
|
310
311
|
getSystemPrompt(): string[];
|
|
311
312
|
/** @deprecated Use hasPendingMessages() instead */
|
|
312
313
|
hasQueuedMessages(): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Unattended workflow-gate bridge. Present only when the session runs in
|
|
316
|
+
* unattended/RPC mode; `undefined` in interactive/TUI mode (notify-only).
|
|
317
|
+
*/
|
|
318
|
+
workflowGate?: WorkflowGateEmitter;
|
|
313
319
|
}
|
|
314
320
|
|
|
315
321
|
/**
|
|
@@ -1234,6 +1240,8 @@ export interface ExtensionContextActions {
|
|
|
1234
1240
|
getContextUsage: () => ContextUsage | undefined;
|
|
1235
1241
|
compact: (instructionsOrOptions?: string | CompactOptions) => Promise<void>;
|
|
1236
1242
|
getSystemPrompt: () => string[];
|
|
1243
|
+
/** Unattended workflow-gate bridge (present only in unattended/RPC mode). */
|
|
1244
|
+
getWorkflowGate?: () => WorkflowGateEmitter | undefined;
|
|
1237
1245
|
}
|
|
1238
1246
|
|
|
1239
1247
|
/** Actions for ExtensionCommandContext (ctx.* in command handlers). */
|
|
@@ -217,6 +217,7 @@ export interface AutoCompactionEndEvent {
|
|
|
217
217
|
errorMessage?: string;
|
|
218
218
|
/** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
|
|
219
219
|
skipped?: boolean;
|
|
220
|
+
continuationSkipReason?: "auto_continue_disabled_non_resumable_tail";
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
/** Fired when auto-retry starts */
|
|
@@ -6,7 +6,7 @@ import * as z from "zod/v4";
|
|
|
6
6
|
import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme, ThemeColor } from "../../modes/theme/theme";
|
|
8
8
|
import goalDescription from "../../prompts/tools/goal.md" with { type: "text" };
|
|
9
|
-
import { assertCanCompleteCurrentGoal } from "../../skc-runtime/ultragoal-guard";
|
|
9
|
+
import { assertCanCompleteCurrentGoal, assertUltragoalPauseAllowed } from "../../skc-runtime/ultragoal-guard";
|
|
10
10
|
import { formatDuration } from "../../slash-commands/helpers/format";
|
|
11
11
|
import type { ToolSession } from "../../tools";
|
|
12
12
|
import { formatErrorMessage, TRUNCATE_LENGTHS } from "../../tools/render-utils";
|
|
@@ -94,6 +94,11 @@ async function executeGoalOperation(session: ToolSession, params: GoalToolInput)
|
|
|
94
94
|
return buildGoalToolResponse(resumed.goal);
|
|
95
95
|
}
|
|
96
96
|
if (params.op === "pause") {
|
|
97
|
+
try {
|
|
98
|
+
await assertUltragoalPauseAllowed(session.cwd);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
101
|
+
}
|
|
97
102
|
const paused = await runtime.pauseGoal();
|
|
98
103
|
return buildGoalToolResponse(paused?.goal ?? null);
|
|
99
104
|
}
|
|
@@ -102,7 +107,11 @@ async function executeGoalOperation(session: ToolSession, params: GoalToolInput)
|
|
|
102
107
|
return buildGoalToolResponse(dropped ?? null);
|
|
103
108
|
}
|
|
104
109
|
try {
|
|
105
|
-
await assertCanCompleteCurrentGoal({
|
|
110
|
+
await assertCanCompleteCurrentGoal({
|
|
111
|
+
cwd: session.cwd,
|
|
112
|
+
currentGoal: session.getGoalModeState?.()?.goal ?? null,
|
|
113
|
+
sessionId: session.getSessionId?.(),
|
|
114
|
+
});
|
|
106
115
|
} catch (error) {
|
|
107
116
|
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
108
117
|
}
|
package/src/hashline/hash.ts
CHANGED
|
@@ -9,7 +9,7 @@ import bigrams from "./bigrams.json" with { type: "json" };
|
|
|
9
9
|
// module evaluation so this core module (and its re-exported helpers) stays
|
|
10
10
|
// usable and falls back to the TS loop if the native addon is unavailable.
|
|
11
11
|
let formatHashLinesNative: ((text: string, startLine?: number) => string) | undefined;
|
|
12
|
-
void import("
|
|
12
|
+
void import("@sayknow-cli/natives")
|
|
13
13
|
.then(mod => {
|
|
14
14
|
if (typeof mod.h06FormatHashLines === "function") {
|
|
15
15
|
formatHashLinesNative = mod.h06FormatHashLines;
|
package/src/i18n/messages/de.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const de: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Darstellung",
|
|
31
31
|
"settings.tab.model": "Modell",
|
|
32
32
|
"settings.tab.interaction": "Interaktion",
|
|
33
|
+
"settings.tab.notifications": "Benachrichtigungen",
|
|
33
34
|
"settings.tab.context": "Kontext",
|
|
34
35
|
"settings.tab.memory": "Speicher",
|
|
35
36
|
"settings.tab.editing": "Bearbeitung",
|
package/src/i18n/messages/en.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const en = {
|
|
|
37
37
|
"settings.tab.appearance": "Appearance",
|
|
38
38
|
"settings.tab.model": "Model",
|
|
39
39
|
"settings.tab.interaction": "Interaction",
|
|
40
|
+
"settings.tab.notifications": "Notifications",
|
|
40
41
|
"settings.tab.context": "Context",
|
|
41
42
|
"settings.tab.memory": "Memory",
|
|
42
43
|
"settings.tab.editing": "Editing",
|
package/src/i18n/messages/es.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const es: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Apariencia",
|
|
31
31
|
"settings.tab.model": "Modelo",
|
|
32
32
|
"settings.tab.interaction": "Interacción",
|
|
33
|
+
"settings.tab.notifications": "Notificaciones",
|
|
33
34
|
"settings.tab.context": "Contexto",
|
|
34
35
|
"settings.tab.memory": "Memoria",
|
|
35
36
|
"settings.tab.editing": "Edición",
|
package/src/i18n/messages/fr.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const fr: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Apparence",
|
|
31
31
|
"settings.tab.model": "Modèle",
|
|
32
32
|
"settings.tab.interaction": "Interaction",
|
|
33
|
+
"settings.tab.notifications": "Notifications",
|
|
33
34
|
"settings.tab.context": "Contexte",
|
|
34
35
|
"settings.tab.memory": "Mémoire",
|
|
35
36
|
"settings.tab.editing": "Édition",
|
package/src/i18n/messages/ja.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const ja: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "外観",
|
|
29
29
|
"settings.tab.model": "モデル",
|
|
30
30
|
"settings.tab.interaction": "操作",
|
|
31
|
+
"settings.tab.notifications": "通知",
|
|
31
32
|
"settings.tab.context": "コンテキスト",
|
|
32
33
|
"settings.tab.memory": "メモリ",
|
|
33
34
|
"settings.tab.editing": "編集",
|
package/src/i18n/messages/ko.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const ko: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "외관",
|
|
29
29
|
"settings.tab.model": "모델",
|
|
30
30
|
"settings.tab.interaction": "상호작용",
|
|
31
|
+
"settings.tab.notifications": "알림",
|
|
31
32
|
"settings.tab.context": "컨텍스트",
|
|
32
33
|
"settings.tab.memory": "메모리",
|
|
33
34
|
"settings.tab.editing": "편집",
|
package/src/i18n/messages/zh.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const zh: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "外观",
|
|
29
29
|
"settings.tab.model": "模型",
|
|
30
30
|
"settings.tab.interaction": "交互",
|
|
31
|
+
"settings.tab.notifications": "通知",
|
|
31
32
|
"settings.tab.context": "上下文",
|
|
32
33
|
"settings.tab.memory": "记忆",
|
|
33
34
|
"settings.tab.editing": "编辑",
|