one-code-extension 0.1.4 → 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/LICENSE +20 -0
- package/README.md +222 -177
- package/agents/explore.md +9 -6
- package/agents/general-purpose.md +1 -1
- package/agents/plan.md +6 -4
- package/docs/guide/README.md +52 -0
- package/docs/guide/bring-your-claude-code-setup.md +128 -0
- package/docs/guide/configuration.md +173 -0
- package/docs/guide/differences-from-claude-code.md +152 -0
- package/docs/guide/doctor.md +88 -0
- package/docs/guide/hooks.md +111 -0
- package/docs/guide/installation.md +126 -0
- package/docs/guide/permissions-modes-and-auto-mode.md +452 -0
- package/docs/guide/providers-and-models.md +196 -0
- package/docs/guide/reference.md +226 -0
- package/docs/guide/sessions-and-context.md +192 -0
- package/docs/guide/skills-plugins-and-mcp.md +178 -0
- package/docs/guide/subagents-and-workflows.md +195 -0
- package/docs/guide/tasks-and-background-work.md +113 -0
- package/docs/guide/terminal-interface.md +157 -0
- package/docs/guide/tools.md +177 -0
- package/docs/guide/troubleshooting.md +177 -0
- package/extensions/ask-user/index.ts +65 -71
- package/extensions/ask-user/questions.ts +15 -28
- package/extensions/ask-user/widget.ts +533 -0
- package/extensions/auto-mode/actions.ts +9 -26
- package/extensions/auto-mode/classifier-prompt.ts +127 -0
- package/extensions/auto-mode/classifier.ts +305 -227
- package/extensions/auto-mode/config.ts +279 -77
- package/extensions/auto-mode/defaults.ts +17 -60
- package/extensions/auto-mode/instructions.ts +2 -1
- package/extensions/auto-mode/model-picker.ts +53 -31
- package/extensions/auto-mode/model-select.ts +153 -82
- package/extensions/auto-mode/paths.ts +13 -1
- package/extensions/auto-mode/pause.ts +8 -0
- package/extensions/auto-mode/prompt.ts +205 -238
- package/extensions/auto-mode/recoverability.ts +229 -0
- package/extensions/auto-mode/rules.ts +92 -75
- package/extensions/auto-mode/safety-floor.ts +43 -12
- package/extensions/auto-mode/sensitive.ts +11 -0
- package/extensions/auto-mode/setup-run.ts +245 -0
- package/extensions/auto-mode/setup.ts +235 -0
- package/extensions/auto-mode/shell-analysis.ts +332 -26
- package/extensions/auto-mode/transcript.ts +102 -0
- package/extensions/background/index.ts +256 -43
- package/extensions/background/monitor-batch.ts +58 -0
- package/extensions/background/registry.ts +9 -0
- package/extensions/background/wakeup.ts +45 -0
- package/extensions/bash/background.ts +47 -22
- package/extensions/bash/guards.ts +229 -0
- package/extensions/bash/index.ts +81 -19
- package/extensions/branding/index.ts +44 -18
- package/extensions/branding/startup.ts +23 -8
- package/extensions/claude-compat/index.ts +17 -2
- package/extensions/claude-context/index.ts +41 -12
- package/extensions/clear/index.ts +27 -0
- package/extensions/compaction/fit.ts +121 -0
- package/extensions/compaction/index.ts +176 -71
- package/extensions/compaction/prompt.ts +21 -1
- package/extensions/context-budget/budget.ts +61 -0
- package/extensions/context-budget/index.ts +66 -0
- package/extensions/context-management/index.ts +40 -16
- package/extensions/doctor/build.ts +226 -0
- package/extensions/doctor/cli.ts +99 -0
- package/extensions/doctor/compat.ts +532 -0
- package/extensions/doctor/dependencies.ts +186 -0
- package/extensions/doctor/fix-prompt.ts +125 -0
- package/extensions/doctor/index.ts +308 -0
- package/extensions/doctor/models.ts +231 -0
- package/extensions/doctor/presets.ts +208 -0
- package/extensions/doctor/report.ts +189 -0
- package/extensions/doctor/update-lookup.ts +46 -0
- package/extensions/doctor/viewer.ts +124 -0
- package/extensions/effort/index.ts +41 -4
- package/extensions/effort/slider.ts +33 -2
- package/extensions/file-tracker/index.ts +143 -20
- package/extensions/file-tracker/replay.ts +46 -0
- package/extensions/file-tracker/tracker.ts +51 -4
- package/extensions/footer/footer-line.ts +207 -0
- package/extensions/footer/index.ts +152 -0
- package/extensions/footer/pr.ts +53 -0
- package/extensions/hooks/executor.ts +57 -20
- package/extensions/hooks/formatter-notice.ts +76 -0
- package/extensions/hooks/index.ts +314 -73
- package/extensions/hooks/matcher.ts +35 -1
- package/extensions/hooks/plugin-hooks.ts +3 -3
- package/extensions/hooks/protocol.ts +58 -0
- package/extensions/hooks/subagent-bridge.ts +77 -0
- package/extensions/hooks/trust.ts +15 -4
- package/extensions/init/index.ts +26 -0
- package/extensions/init/prompt.ts +40 -0
- package/extensions/interrupted/index.ts +35 -0
- package/extensions/interrupted/line.ts +9 -0
- package/extensions/lib/abort.ts +15 -0
- package/extensions/lib/agent-loader.ts +183 -0
- package/extensions/lib/anthropic-payload.ts +15 -0
- package/extensions/lib/atomic-write.ts +56 -0
- package/extensions/lib/bridge-watch.ts +20 -0
- package/extensions/lib/cache-retention.ts +60 -0
- package/extensions/lib/capability-index.ts +466 -0
- package/extensions/lib/claude-context.ts +318 -27
- package/extensions/lib/claude-settings.ts +64 -0
- package/extensions/lib/consent-preview.ts +30 -0
- package/extensions/lib/deferred.ts +144 -13
- package/extensions/lib/favorites.ts +43 -0
- package/extensions/lib/fetch-timeout.ts +15 -0
- package/extensions/lib/frontmatter.ts +31 -0
- package/extensions/lib/git.ts +44 -2
- package/extensions/lib/hook-gate.ts +90 -0
- package/extensions/lib/interrupt.ts +94 -0
- package/extensions/lib/mcp-overrides.ts +61 -0
- package/extensions/lib/mcp-share.ts +25 -0
- package/extensions/lib/mcp-status.ts +34 -0
- package/extensions/lib/memory.ts +41 -6
- package/extensions/lib/model-facts.generated.json +2480 -0
- package/extensions/lib/model-facts.ts +162 -0
- package/extensions/lib/model-policy.ts +145 -189
- package/extensions/lib/model-tier.ts +480 -64
- package/extensions/lib/model-unusable.ts +37 -0
- package/extensions/lib/notifications.ts +320 -1
- package/extensions/lib/one-code-settings.ts +63 -0
- package/extensions/lib/original-command.ts +71 -0
- package/extensions/lib/package-version.ts +31 -0
- package/extensions/lib/paths.ts +39 -4
- package/extensions/lib/permission-gate.ts +192 -0
- package/extensions/lib/persisted-output.ts +18 -0
- package/extensions/lib/pi-ai-estimate.ts +160 -0
- package/extensions/lib/pi-version.ts +1 -1
- package/extensions/lib/plan-mode-channels.ts +11 -0
- package/extensions/lib/plugin-overrides.ts +24 -0
- package/extensions/lib/plugin-root.ts +44 -0
- package/extensions/lib/plugins.ts +233 -54
- package/extensions/lib/prefix-warm-gate.ts +157 -0
- package/extensions/lib/process-tree.ts +56 -0
- package/extensions/lib/reminders.ts +321 -68
- package/extensions/lib/scratchpad.ts +11 -8
- package/extensions/lib/session-lifecycle.ts +28 -0
- package/extensions/lib/settings-channels.ts +14 -0
- package/extensions/lib/shell-tasks.ts +54 -0
- package/extensions/lib/skill-overrides.ts +86 -0
- package/extensions/lib/skill-scan.ts +110 -0
- package/extensions/lib/text-width.ts +267 -0
- package/extensions/lib/tui-render.ts +281 -54
- package/extensions/lib/usage-bus.ts +75 -0
- package/extensions/lib/usage-tracker.ts +67 -0
- package/extensions/lib/worktree-isolation.ts +170 -0
- package/extensions/lsp/client.ts +69 -12
- package/extensions/lsp/format.ts +12 -2
- package/extensions/lsp/index.ts +273 -84
- package/extensions/lsp/install-hints.ts +43 -0
- package/extensions/lsp/keep-alive.ts +19 -0
- package/extensions/lsp/plugin-servers.ts +331 -0
- package/extensions/lsp/watcher.ts +157 -0
- package/extensions/mcp/announce.ts +99 -0
- package/extensions/mcp/client.ts +181 -22
- package/extensions/mcp/config.ts +55 -24
- package/extensions/mcp/index.ts +586 -86
- package/extensions/mcp/oauth/browser.ts +26 -0
- package/extensions/mcp/oauth/callback.ts +104 -0
- package/extensions/mcp/oauth/flow.ts +68 -0
- package/extensions/mcp/oauth/provider.ts +147 -0
- package/extensions/mcp/oauth/store.ts +86 -0
- package/extensions/mcp/panel/keys.ts +34 -0
- package/extensions/mcp/panel/model.ts +62 -0
- package/extensions/mcp/panel/render.ts +141 -0
- package/extensions/mcp/panel/state.ts +108 -0
- package/extensions/mcp/schema.ts +83 -6
- package/extensions/mcp/trust.ts +340 -0
- package/extensions/memory/entries.ts +163 -0
- package/extensions/memory/index.ts +119 -2
- package/extensions/memory/open-external.ts +97 -0
- package/extensions/memory/panel.ts +124 -0
- package/extensions/model-default/index.ts +74 -0
- package/extensions/notebook/index.ts +36 -18
- package/extensions/notebook/notebook.ts +110 -17
- package/extensions/permissions/index.ts +1012 -247
- package/extensions/permissions/matcher.ts +488 -57
- package/extensions/permissions/modes.ts +55 -7
- package/extensions/permissions/project-trust.ts +81 -0
- package/extensions/permissions/protected-paths.ts +16 -3
- package/extensions/permissions/rule-prose.ts +79 -0
- package/extensions/permissions/session-grant.ts +91 -0
- package/extensions/permissions/settings.ts +112 -33
- package/extensions/permissions/subagent-gate.ts +57 -0
- package/extensions/plan-mode/index.ts +143 -33
- package/extensions/plan-mode/reminder.ts +16 -19
- package/extensions/plan-mode/viewer.ts +40 -31
- package/extensions/plugins/counts.ts +68 -0
- package/extensions/plugins/index.ts +496 -32
- package/extensions/plugins/install/install.ts +167 -0
- package/extensions/plugins/install/paths.ts +23 -0
- package/extensions/plugins/install/registry.ts +72 -0
- package/extensions/plugins/marketplace/git.ts +103 -0
- package/extensions/plugins/marketplace/official.ts +36 -0
- package/extensions/plugins/marketplace/parse.ts +94 -0
- package/extensions/plugins/marketplace/registry.ts +60 -0
- package/extensions/plugins/marketplace/sync.ts +146 -0
- package/extensions/plugins/marketplace/types.ts +128 -0
- package/extensions/plugins/panel/keys.ts +52 -0
- package/extensions/plugins/panel/render.ts +315 -0
- package/extensions/plugins/panel/rows.ts +230 -0
- package/extensions/plugins/panel/state.ts +333 -0
- package/extensions/recap/index.ts +215 -0
- package/extensions/recap/prompt.ts +44 -0
- package/extensions/recap/scheduler.ts +87 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/search-tools/tools.ts +7 -5
- package/extensions/skill/index.ts +360 -34
- package/extensions/skill/invoke.ts +185 -0
- package/extensions/skill/listing.ts +21 -0
- package/extensions/skill/panel/keys.ts +41 -0
- package/extensions/skill/panel/render.ts +91 -0
- package/extensions/skill/panel/state.ts +115 -0
- package/extensions/spinner/index.ts +110 -0
- package/extensions/spinner/line.ts +51 -0
- package/extensions/spinner/verbs.ts +201 -0
- package/extensions/subagents/agents.ts +88 -44
- package/extensions/subagents/default-model.ts +62 -32
- package/extensions/subagents/delegation-steer.ts +23 -0
- package/extensions/subagents/hand-back-review.ts +67 -0
- package/extensions/subagents/index.ts +1545 -346
- package/extensions/subagents/live-runs.ts +293 -0
- package/extensions/subagents/model-select.ts +252 -82
- package/extensions/subagents/outcome.ts +90 -0
- package/extensions/subagents/panel-keys.ts +41 -0
- package/extensions/subagents/panel-render.ts +357 -0
- package/extensions/subagents/panel-widget.ts +330 -0
- package/extensions/subagents/pending-claim.ts +45 -0
- package/extensions/subagents/prose.ts +105 -0
- package/extensions/subagents/runner.ts +674 -0
- package/extensions/subagents/runs.ts +68 -1
- package/extensions/subagents/send-to-main-tool.ts +55 -0
- package/extensions/subagents/session-turns.ts +138 -0
- package/extensions/subagents/shell-panel.ts +198 -0
- package/extensions/subagents/usage.ts +16 -4
- package/extensions/subagents/worktree.ts +13 -0
- package/extensions/system-prompt/environment.ts +5 -3
- package/extensions/system-prompt/git-status.ts +93 -0
- package/extensions/system-prompt/index.ts +23 -1
- package/extensions/system-prompt/template.ts +34 -4
- package/extensions/system-prompt/tiers/common.ts +31 -6
- package/extensions/system-prompt/tiers/low.ts +13 -3
- package/extensions/system-prompt/tiers/mid.ts +21 -8
- package/extensions/system-reminder/index.ts +53 -9
- package/extensions/tasks/index.ts +65 -16
- package/extensions/tasks/store.ts +76 -3
- package/extensions/tool-search/announce.ts +42 -0
- package/extensions/tool-search/index.ts +136 -13
- package/extensions/turn-duration/index.ts +60 -0
- package/extensions/turn-duration/line.ts +22 -0
- package/extensions/turn-duration/span.ts +41 -0
- package/extensions/turn-duration/verbs.ts +24 -0
- package/extensions/web/backends.ts +421 -0
- package/extensions/web/index.ts +178 -21
- package/extensions/web-fetch/index.ts +74 -18
- package/extensions/web-fetch/summarize.ts +1 -1
- package/extensions/workflow/agent-session.ts +182 -112
- package/extensions/workflow/globals.ts +129 -106
- package/extensions/workflow/index.ts +343 -28
- package/extensions/workflow/records.ts +118 -0
- package/extensions/workflow/run-manager.ts +80 -31
- package/extensions/workflow/saved-workflows.ts +2 -1
- package/extensions/workflow/script-worker.mjs +110 -0
- package/extensions/workflow/types.ts +74 -8
- package/extensions/workflow/viewer.ts +562 -0
- package/extensions/workflow/vm-runtime.ts +118 -22
- package/extensions/workflow/widget.ts +126 -18
- package/extensions/worktree/guards.ts +240 -0
- package/extensions/worktree/index.ts +30 -5
- package/extensions/worktree/rewrite.ts +32 -16
- package/package.json +20 -10
- package/skills/code-review/SKILL.md +165 -0
- package/skills/fewer-permission-prompts/SKILL.md +61 -0
- package/skills/security-review/SKILL.md +178 -0
- package/skills/simplify/SKILL.md +70 -0
- package/themes/{one-code-light.json → onecode-light.json} +6 -1
- package/themes/{one-code.json → onecode.json} +6 -1
- package/types/app.d.ts +8 -1
- package/types/community-packages.d.ts +45 -2
- package/docs/decisions/auto-mode.md +0 -526
- package/docs/decisions/branding.md +0 -127
- package/docs/decisions/code-review-remediation.md +0 -106
- package/docs/decisions/compaction.md +0 -140
- package/docs/decisions/distribution.md +0 -168
- package/docs/decisions/hooks.md +0 -64
- package/docs/decisions/lsp.md +0 -58
- package/docs/decisions/mcp.md +0 -104
- package/docs/decisions/memory-state.md +0 -170
- package/docs/decisions/model-policy.md +0 -126
- package/docs/decisions/modes.md +0 -178
- package/docs/decisions/skills-plugins.md +0 -56
- package/docs/decisions/subagents-workflows.md +0 -225
- package/docs/decisions/system-prompt.md +0 -98
- package/docs/decisions/tools.md +0 -407
- package/docs/decisions/tui.md +0 -217
- package/docs/decisions.md +0 -148
- package/extensions/subagents/child.ts +0 -414
- package/extensions/subagents/rpc-turns.ts +0 -123
- package/extensions/todo/index.ts +0 -133
- package/extensions/workflow/permission-gate.ts +0 -71
|
@@ -3,32 +3,73 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## Which files are read, and why that list is short
|
|
5
5
|
*
|
|
6
|
-
* User settings (`~/.claude/settings.json`)
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* that
|
|
11
|
-
*
|
|
6
|
+
* User settings (`~/.claude/settings.json`), One Code's own user settings
|
|
7
|
+
* (`~/.onecode/settings.json`), and managed settings only — **never the
|
|
8
|
+
* project's `.claude/settings.json` or `.claude/settings.local.json`**. Both
|
|
9
|
+
* project files live in the repository, so a checked-in file or a build step
|
|
10
|
+
* that writes one could otherwise hand itself classifier permissions and switch
|
|
11
|
+
* off the gate that is meant to contain it. Claude Code made the same exclusion
|
|
12
|
+
* (it dropped `settings.local.json` in v2.1.207); this is that property, not an
|
|
13
|
+
* omission.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* ## Read vs. write: `.claude` is borrowed, `~/.onecode` is ours
|
|
16
|
+
*
|
|
17
|
+
* The CC-schema keys (`environment`, `hard_deny`/`soft_deny`/`allow`) are read
|
|
18
|
+
* from `~/.claude` too, because Claude Code's own `/auto-mode-setup` writes them
|
|
19
|
+
* there and reading them is the compatibility promise. But One Code's *own*
|
|
20
|
+
* keys — `classifierModel` / `classifierModelSetFor`, which Claude Code does not
|
|
21
|
+
* define — are read from `~/.onecode` and managed settings only, never from
|
|
22
|
+
* `~/.claude`: One Code used to write them there, and a stale value (a model the
|
|
23
|
+
* session cannot reach) must not keep biting. Every write goes to
|
|
24
|
+
* `~/.onecode/settings.json`; One Code never mutates Claude Code's files. See
|
|
25
|
+
* "Own state, borrowed config" in docs/decisions/memory-state.md.
|
|
26
|
+
*
|
|
27
|
+
* ## The customization surface (CC 2.1.233's own)
|
|
28
|
+
*
|
|
29
|
+
* The ruleset is CC's fixed monolith (classifier-prompt.ts); its prose is not
|
|
30
|
+
* user-editable, by design — the ruleset *is* the security boundary. What CC's
|
|
31
|
+
* `/auto-mode-setup` exposes (and we mirror exactly) is:
|
|
32
|
+
*
|
|
33
|
+
* - `environment` — replaces the `## Environment` slot lines. `"$defaults"`
|
|
34
|
+
* splices CC's default slots at that position, so a user can extend rather
|
|
35
|
+
* than replace.
|
|
36
|
+
* - `hard_deny` / `soft_deny` / `allow` — extra rules **appended** to the end of
|
|
37
|
+
* the matching embedded list, verbatim, exactly where CC injects them. These
|
|
38
|
+
* are append-only: `"$defaults"` is accepted (CC's wizard always writes it)
|
|
39
|
+
* but carries no meaning — the built-in rules always apply and cannot be
|
|
40
|
+
* removed or reordered from settings, so an extra rule can tighten or carve
|
|
41
|
+
* out, never rewrite the boundary.
|
|
42
|
+
*
|
|
43
|
+
* (The rule lists were briefly retired between the ruleset adoption and CC
|
|
44
|
+
* 2.1.233 shipping `/auto-mode-setup`, which writes this exact schema — see
|
|
45
|
+
* docs/decisions/auto-mode.md.)
|
|
17
46
|
*/
|
|
18
47
|
|
|
19
|
-
import { existsSync,
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
48
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
49
|
+
import { claudeUserSettingsPath } from "../lib/claude-settings.ts";
|
|
50
|
+
import { oneCodeSettingsPath, readSettingsForWrite, writeSettings } from "../lib/one-code-settings.ts";
|
|
51
|
+
import { DEFAULT_ENVIRONMENT, slotName } from "./defaults.ts";
|
|
22
52
|
|
|
23
53
|
export interface AutoModeConfig {
|
|
54
|
+
/** Environment slot lines spliced into CC's ruleset (prefix-less, CC's settings format). */
|
|
24
55
|
environment: string[];
|
|
56
|
+
/** Extra HARD BLOCK rules appended to the embedded list (`$defaults` already stripped). */
|
|
57
|
+
hardDeny: string[];
|
|
58
|
+
/** Extra SOFT BLOCK rules appended to the embedded list (`$defaults` already stripped). */
|
|
59
|
+
softDeny: string[];
|
|
60
|
+
/** Extra ALLOW rules appended to the embedded list (`$defaults` already stripped). */
|
|
25
61
|
allow: string[];
|
|
26
|
-
soft_deny: string[];
|
|
27
|
-
hard_deny: string[];
|
|
28
62
|
/** Suspend narrow Bash allow rules so every shell command reaches the classifier. */
|
|
29
63
|
classifyAllShell: boolean;
|
|
30
64
|
/** `provider/model-id` override for the classifier model. */
|
|
31
65
|
classifierModel?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Containment identity (`modelIdentity().containment`) the `classifierModel`
|
|
68
|
+
* was stamped for when set via `/auto-mode model`. A cross-provider setting
|
|
69
|
+
* whose stamp no longer matches the session is treated as stale and overridden
|
|
70
|
+
* — see `classifierCandidates`. Undefined for a hand-edited setting.
|
|
71
|
+
*/
|
|
72
|
+
classifierModelSetFor?: string;
|
|
32
73
|
/** Append every gate decision to auto-mode-decisions.jsonl next to the session files. */
|
|
33
74
|
logDecisions: boolean;
|
|
34
75
|
}
|
|
@@ -36,12 +77,14 @@ export interface AutoModeConfig {
|
|
|
36
77
|
interface AutoModeSettingsFile {
|
|
37
78
|
autoMode?: {
|
|
38
79
|
environment?: unknown;
|
|
39
|
-
allow?: unknown;
|
|
40
|
-
soft_deny?: unknown;
|
|
41
80
|
hard_deny?: unknown;
|
|
81
|
+
soft_deny?: unknown;
|
|
82
|
+
allow?: unknown;
|
|
42
83
|
classifyAllShell?: unknown;
|
|
43
84
|
classifierModel?: unknown;
|
|
85
|
+
classifierModelSetFor?: unknown;
|
|
44
86
|
logDecisions?: unknown;
|
|
87
|
+
[key: string]: unknown;
|
|
45
88
|
};
|
|
46
89
|
[key: string]: unknown;
|
|
47
90
|
}
|
|
@@ -49,15 +92,21 @@ interface AutoModeSettingsFile {
|
|
|
49
92
|
const DEFAULTS_TOKEN = "$defaults";
|
|
50
93
|
|
|
51
94
|
/** Managed-settings locations, highest authority, matching Claude Code's paths. */
|
|
52
|
-
function managedSettingsPaths(): string[] {
|
|
95
|
+
export function managedSettingsPaths(): string[] {
|
|
53
96
|
if (process.platform === "darwin") return ["/Library/Application Support/ClaudeCode/managed-settings.json"];
|
|
54
97
|
if (process.platform === "win32") return ["C:\\ProgramData\\ClaudeCode\\managed-settings.json"];
|
|
55
98
|
return ["/etc/claude-code/managed-settings.json"];
|
|
56
99
|
}
|
|
57
100
|
|
|
58
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* The files `autoMode` is read from, lowest precedence first. One Code's own
|
|
103
|
+
* auto-mode keys (`classifierModel`, `classifierModelSetFor`) are deliberately
|
|
104
|
+
* NOT collected from the borrowed `claudeUserSettingsPath` — Claude Code does not
|
|
105
|
+
* define them, and One Code once wrote them there, so a stale value must not
|
|
106
|
+
* survive the move to `~/.onecode` (see the read guard in the loader).
|
|
107
|
+
*/
|
|
59
108
|
export function autoModeSettingsPaths(home: string): string[] {
|
|
60
|
-
return [
|
|
109
|
+
return [claudeUserSettingsPath(home), oneCodeSettingsPath(home), ...managedSettingsPaths()];
|
|
61
110
|
}
|
|
62
111
|
|
|
63
112
|
function readFile(path: string, diagnostics: string[]): AutoModeSettingsFile | undefined {
|
|
@@ -75,45 +124,93 @@ function readFile(path: string, diagnostics: string[]): AutoModeSettingsFile | u
|
|
|
75
124
|
|
|
76
125
|
const KNOWN_AUTO_MODE_KEYS = new Set([
|
|
77
126
|
"environment",
|
|
78
|
-
"allow",
|
|
79
|
-
"soft_deny",
|
|
80
127
|
"hard_deny",
|
|
128
|
+
"soft_deny",
|
|
129
|
+
"allow",
|
|
81
130
|
"classifyAllShell",
|
|
82
131
|
"classifierModel",
|
|
132
|
+
"classifierModelSetFor",
|
|
83
133
|
"logDecisions",
|
|
84
134
|
]);
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
/** Rule-extra keys, CC's spelling; appended to the matching embedded list. */
|
|
137
|
+
const RULE_LIST_KEYS = ["hard_deny", "soft_deny", "allow"] as const;
|
|
87
138
|
|
|
88
139
|
/**
|
|
89
140
|
* Shape problems in one file's `autoMode` block, as human-readable lines.
|
|
90
141
|
* Purely advisory: loading stays lenient (a wrong-typed field is skipped, not
|
|
91
|
-
* fatal), but skipped-because-mistyped must be visible in `/auto-mode config`
|
|
92
|
-
*
|
|
142
|
+
* fatal), but skipped-because-mistyped must be visible in `/auto-mode config` or
|
|
143
|
+
* it reads as the gate ignoring the user's settings.
|
|
93
144
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Validate one array-of-strings autoMode key. `omitDefaultsNote`, when given,
|
|
147
|
+
* is the warning pushed if "$defaults" is missing. Purely advisory: loading
|
|
148
|
+
* stays lenient either way.
|
|
149
|
+
*/
|
|
150
|
+
function validateStringListKey(
|
|
151
|
+
block: Record<string, unknown>,
|
|
152
|
+
key: string,
|
|
153
|
+
path: string,
|
|
154
|
+
diagnostics: string[],
|
|
155
|
+
omitDefaultsNote?: string,
|
|
156
|
+
): void {
|
|
157
|
+
const list = block[key];
|
|
158
|
+
if (list === undefined) return;
|
|
159
|
+
if (!Array.isArray(list)) {
|
|
160
|
+
diagnostics.push(`${path}: autoMode.${key} must be an array of strings — ignored`);
|
|
161
|
+
return;
|
|
97
162
|
}
|
|
98
|
-
for (const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (!Array.isArray(value)) {
|
|
102
|
-
diagnostics.push(`${path}: autoMode.${key} must be an array of strings — ignored`);
|
|
103
|
-
continue;
|
|
163
|
+
for (const [position, entry] of list.entries()) {
|
|
164
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
165
|
+
diagnostics.push(`${path}: autoMode.${key}[${position}] is not a non-empty string — dropped`);
|
|
104
166
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
167
|
+
}
|
|
168
|
+
if (!omitDefaultsNote) return;
|
|
169
|
+
const strings = list.filter((entry): entry is string => typeof entry === "string");
|
|
170
|
+
if (strings.length > 0 && !strings.some((entry) => entry.trim() === DEFAULTS_TOKEN)) {
|
|
171
|
+
diagnostics.push(`${path}: autoMode.${key} ${omitDefaultsNote}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function validateAutoModeBlock(block: Record<string, unknown>, path: string, diagnostics: string[]): void {
|
|
176
|
+
for (const key of Object.keys(block)) {
|
|
177
|
+
if (!KNOWN_AUTO_MODE_KEYS.has(key)) {
|
|
178
|
+
diagnostics.push(`${path}: unknown autoMode key "${key}" is ignored`);
|
|
109
179
|
}
|
|
110
|
-
|
|
180
|
+
}
|
|
181
|
+
validateStringListKey(block, "environment", path, diagnostics);
|
|
182
|
+
// A FULL environment replacement (every built-in slot re-stated, edited or
|
|
183
|
+
// not) is the standard shape — CC's wizard and ours both write it — so it
|
|
184
|
+
// earns no warning. What does: a replacement that silently DROPS built-in
|
|
185
|
+
// slots because "$defaults" is absent — usually a hand-written list whose
|
|
186
|
+
// author believed they were adding, not replacing.
|
|
187
|
+
const environment = block.environment;
|
|
188
|
+
if (Array.isArray(environment)) {
|
|
189
|
+
const strings = environment.filter((entry): entry is string => typeof entry === "string");
|
|
111
190
|
if (strings.length > 0 && !strings.some((entry) => entry.trim() === DEFAULTS_TOKEN)) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
191
|
+
const present = new Set(strings.map(slotName).filter(Boolean));
|
|
192
|
+
const missing = DEFAULT_ENVIRONMENT.map(slotName).filter((name): name is string => !!name && !present.has(name));
|
|
193
|
+
if (missing.length > 0) {
|
|
194
|
+
const shown = missing.slice(0, 3).join(", ") + (missing.length > 3 ? ", …" : "");
|
|
195
|
+
diagnostics.push(
|
|
196
|
+
`${path}: autoMode.environment omits "${DEFAULTS_TOKEN}", so it REPLACES the built-in environment rather than extending it — built-in slot(s) now missing: ${shown}`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
115
199
|
}
|
|
116
200
|
}
|
|
201
|
+
// Unlike environment, the rule lists are append-only: the built-in rules
|
|
202
|
+
// always apply whether or not "$defaults" is present (a settings file must be
|
|
203
|
+
// able to add rules, never to remove the boundary), and nobody should believe
|
|
204
|
+
// omitting the token disabled them.
|
|
205
|
+
for (const key of RULE_LIST_KEYS) {
|
|
206
|
+
validateStringListKey(
|
|
207
|
+
block,
|
|
208
|
+
key,
|
|
209
|
+
path,
|
|
210
|
+
diagnostics,
|
|
211
|
+
`omits "${DEFAULTS_TOKEN}" — note the built-in rules still apply; these lists only append, never replace`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
117
214
|
for (const key of ["classifyAllShell", "logDecisions"] as const) {
|
|
118
215
|
if (block[key] !== undefined && typeof block[key] !== "boolean") {
|
|
119
216
|
diagnostics.push(`${path}: autoMode.${key} must be a boolean — ignored`);
|
|
@@ -133,8 +230,8 @@ function stringArray(value: unknown): string[] | undefined {
|
|
|
133
230
|
}
|
|
134
231
|
|
|
135
232
|
/**
|
|
136
|
-
* Splice `$defaults` into a user list. Entries from separate scopes are
|
|
137
|
-
*
|
|
233
|
+
* Splice `$defaults` into a user list. Entries from separate scopes are additive:
|
|
234
|
+
* a developer can extend a managed list but not remove from it.
|
|
138
235
|
*/
|
|
139
236
|
export function spliceDefaults(entries: string[] | undefined, defaults: string[]): string[] {
|
|
140
237
|
if (!entries) return [...defaults];
|
|
@@ -156,14 +253,16 @@ export function loadAutoModeConfigWithDiagnostics(home: string): AutoModeConfigL
|
|
|
156
253
|
const diagnostics: string[] = [];
|
|
157
254
|
const collected: {
|
|
158
255
|
environment?: string[];
|
|
159
|
-
allow?: string[];
|
|
160
|
-
soft_deny?: string[];
|
|
161
256
|
hard_deny?: string[];
|
|
257
|
+
soft_deny?: string[];
|
|
258
|
+
allow?: string[];
|
|
162
259
|
classifyAllShell?: boolean;
|
|
163
260
|
classifierModel?: string;
|
|
261
|
+
classifierModelSetFor?: string;
|
|
164
262
|
logDecisions?: boolean;
|
|
165
263
|
} = {};
|
|
166
264
|
|
|
265
|
+
const claudeUser = claudeUserSettingsPath(home);
|
|
167
266
|
for (const path of autoModeSettingsPaths(home)) {
|
|
168
267
|
const file = readFile(path, diagnostics);
|
|
169
268
|
const block = file?.autoMode;
|
|
@@ -173,27 +272,52 @@ export function loadAutoModeConfigWithDiagnostics(home: string): AutoModeConfigL
|
|
|
173
272
|
}
|
|
174
273
|
if (!block) continue;
|
|
175
274
|
validateAutoModeBlock(block as Record<string, unknown>, path, diagnostics);
|
|
275
|
+
// Additive across scopes: a later scope extends rather than replaces, so
|
|
276
|
+
// managed entries cannot be dropped by a user file.
|
|
277
|
+
const parsed = stringArray(block.environment);
|
|
278
|
+
if (parsed) collected.environment = [...(collected.environment ?? []), ...parsed];
|
|
176
279
|
for (const key of RULE_LIST_KEYS) {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
// so managed entries cannot be dropped by a user file.
|
|
180
|
-
if (parsed) collected[key] = [...(collected[key] ?? []), ...parsed];
|
|
280
|
+
const list = stringArray(block[key]);
|
|
281
|
+
if (list) collected[key] = [...(collected[key] ?? []), ...list];
|
|
181
282
|
}
|
|
182
283
|
if (typeof block.classifyAllShell === "boolean") collected.classifyAllShell = block.classifyAllShell;
|
|
183
284
|
if (typeof block.logDecisions === "boolean") collected.logDecisions = block.logDecisions;
|
|
285
|
+
// `classifierModel` is One Code's own key, not Claude Code's. It is read from
|
|
286
|
+
// `~/.onecode` and managed settings only — never from `~/.claude`, where an
|
|
287
|
+
// old One Code build may have left a stale value. A leftover in `~/.claude` is
|
|
288
|
+
// surfaced as a diagnostic so the user knows why it stopped applying.
|
|
184
289
|
if (typeof block.classifierModel === "string" && block.classifierModel.trim()) {
|
|
185
|
-
|
|
290
|
+
if (path === claudeUser) {
|
|
291
|
+
diagnostics.push(
|
|
292
|
+
`${path}: autoMode.classifierModel is ignored here — One Code reads it from ~/.onecode/settings.json (set it with /auto-mode model)`,
|
|
293
|
+
);
|
|
294
|
+
} else {
|
|
295
|
+
collected.classifierModel = block.classifierModel.trim();
|
|
296
|
+
// The stamp travels with the model from the same file, so a lower-precedence
|
|
297
|
+
// stamp cannot attach to a higher-precedence model.
|
|
298
|
+
collected.classifierModelSetFor =
|
|
299
|
+
typeof block.classifierModelSetFor === "string" && block.classifierModelSetFor.trim()
|
|
300
|
+
? block.classifierModelSetFor.trim()
|
|
301
|
+
: undefined;
|
|
302
|
+
}
|
|
186
303
|
}
|
|
187
304
|
}
|
|
188
305
|
|
|
306
|
+
// Rule lists are extras appended to the embedded built-ins, so "$defaults" is
|
|
307
|
+
// stripped rather than expanded — the built-ins are already in the ruleset and
|
|
308
|
+
// must apply regardless of what settings say (append-only, by construction).
|
|
309
|
+
const ruleExtras = (entries: string[] | undefined): string[] =>
|
|
310
|
+
(entries ?? []).filter((entry) => entry.trim() !== DEFAULTS_TOKEN);
|
|
311
|
+
|
|
189
312
|
return {
|
|
190
313
|
config: {
|
|
191
314
|
environment: spliceDefaults(collected.environment, DEFAULT_ENVIRONMENT),
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
315
|
+
hardDeny: ruleExtras(collected.hard_deny),
|
|
316
|
+
softDeny: ruleExtras(collected.soft_deny),
|
|
317
|
+
allow: ruleExtras(collected.allow),
|
|
195
318
|
classifyAllShell: collected.classifyAllShell ?? false,
|
|
196
319
|
classifierModel: collected.classifierModel,
|
|
320
|
+
classifierModelSetFor: collected.classifierModelSetFor,
|
|
197
321
|
logDecisions: collected.logDecisions ?? false,
|
|
198
322
|
},
|
|
199
323
|
diagnostics,
|
|
@@ -205,33 +329,111 @@ export function loadAutoModeConfig(home: string): AutoModeConfig {
|
|
|
205
329
|
}
|
|
206
330
|
|
|
207
331
|
/**
|
|
208
|
-
* Persist `autoMode.classifierModel` in
|
|
209
|
-
* every other key. `undefined` removes
|
|
210
|
-
* the
|
|
211
|
-
*
|
|
332
|
+
* Persist `autoMode.classifierModel` in One Code's own settings file
|
|
333
|
+
* (`~/.onecode/settings.json`), preserving every other key. `undefined` removes
|
|
334
|
+
* the setting. Never touches Claude Code's files — this is One Code's key, and
|
|
335
|
+
* the knob that may move the classifier to another provider, so it lives in One
|
|
336
|
+
* Code's own state (see the module comment).
|
|
212
337
|
*
|
|
213
338
|
* Unlike loading, this throws on a malformed file: a lenient read merely skips
|
|
214
|
-
* rules, but a lenient write would replace the
|
|
215
|
-
* only ours.
|
|
339
|
+
* rules, but a lenient write would replace the whole settings file with only ours.
|
|
216
340
|
*/
|
|
217
|
-
export function persistClassifierModel(spec: string | undefined, home: string): void {
|
|
218
|
-
const path =
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
341
|
+
export function persistClassifierModel(spec: string | undefined, home: string, setForContainment?: string): void {
|
|
342
|
+
const path = oneCodeSettingsPath(home);
|
|
343
|
+
const file = readSettingsForWrite(path);
|
|
344
|
+
const block = asRecord(file.autoMode) ?? {};
|
|
345
|
+
if (spec === undefined) {
|
|
346
|
+
delete block.classifierModel;
|
|
347
|
+
delete block.classifierModelSetFor;
|
|
348
|
+
} else {
|
|
349
|
+
block.classifierModel = spec;
|
|
350
|
+
// Stamp the containment the model was chosen on, so a later session on a
|
|
351
|
+
// different provider treats a cross-provider setting as stale (parity with
|
|
352
|
+
// subagentModelSetFor). No stamp for a hand-edited setting.
|
|
353
|
+
if (setForContainment) block.classifierModelSetFor = setForContainment;
|
|
354
|
+
else delete block.classifierModelSetFor;
|
|
355
|
+
}
|
|
356
|
+
if (Object.keys(block).length === 0) delete file.autoMode;
|
|
357
|
+
else file.autoMode = block;
|
|
358
|
+
writeSettings(path, file);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** value as a plain object, or undefined — the repeated sub-block guard. */
|
|
362
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
363
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
364
|
+
? (value as Record<string, unknown>)
|
|
365
|
+
: undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Persist a `/auto-mode setup` result to One Code's own settings file: replace
|
|
370
|
+
* the wizard-owned keys (environment + the three rule lists — an undefined list
|
|
371
|
+
* removes stale values, last-writer like CC's own wizard) while preserving every
|
|
372
|
+
* other settings key and the non-wizard autoMode keys (classifierModel, …).
|
|
373
|
+
* Never touches Claude Code's files; throws on a malformed file rather than
|
|
374
|
+
* replacing the settings with only ours.
|
|
375
|
+
*/
|
|
376
|
+
export function persistAutoModeSetup(patch: Record<string, string[] | undefined>, home: string): void {
|
|
377
|
+
const path = oneCodeSettingsPath(home);
|
|
378
|
+
const file = readSettingsForWrite(path);
|
|
379
|
+
const block = asRecord(file.autoMode) ?? {};
|
|
380
|
+
for (const key of ["environment", ...RULE_LIST_KEYS] as const) {
|
|
381
|
+
const value = patch[key];
|
|
382
|
+
if (value === undefined) delete block[key];
|
|
383
|
+
else block[key] = value;
|
|
226
384
|
}
|
|
227
|
-
const block =
|
|
228
|
-
typeof file.autoMode === "object" && file.autoMode !== null && !Array.isArray(file.autoMode)
|
|
229
|
-
? (file.autoMode as Record<string, unknown>)
|
|
230
|
-
: {};
|
|
231
|
-
if (spec === undefined) delete block.classifierModel;
|
|
232
|
-
else block.classifierModel = spec;
|
|
233
385
|
if (Object.keys(block).length === 0) delete file.autoMode;
|
|
234
386
|
else file.autoMode = block;
|
|
235
|
-
|
|
236
|
-
|
|
387
|
+
writeSettings(path, file);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The `permissions.allow` entries in One Code's own settings file — what the
|
|
392
|
+
* setup audit can actually remove. Broad rules that live in Claude Code's files
|
|
393
|
+
* are One Code's to warn about, never to delete.
|
|
394
|
+
*/
|
|
395
|
+
export function oneCodePermissionAllow(home: string): string[] {
|
|
396
|
+
// Lenient read (a missing/malformed file is skipped) — this is a read for
|
|
397
|
+
// auditing, not a write, so leniency is correct; the writers still refuse to
|
|
398
|
+
// clobber a malformed file. Diagnostics are discarded here.
|
|
399
|
+
return permissionAllowIn(oneCodeSettingsPath(home));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The `permissions.allow` entries in Claude Code's *user* settings file — the
|
|
404
|
+
* borrowed half of the audit. These are One Code's to warn about, never to
|
|
405
|
+
* delete: an entry here still bypasses the classifier even after its One Code
|
|
406
|
+
* copy is removed, so the audit must flag it separately.
|
|
407
|
+
*/
|
|
408
|
+
export function claudeUserPermissionAllow(home: string): string[] {
|
|
409
|
+
return permissionAllowIn(claudeUserSettingsPath(home));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Shared lenient read of a settings file's `permissions.allow` string list. */
|
|
413
|
+
function permissionAllowIn(path: string): string[] {
|
|
414
|
+
const list = asRecord(readFile(path, [])?.permissions)?.allow;
|
|
415
|
+
return Array.isArray(list) ? list.filter((entry): entry is string => typeof entry === "string") : [];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Remove specific `permissions.allow` entries from One Code's own settings file
|
|
420
|
+
* (the setup audit's "remove them" choice). Only One Code's own file is
|
|
421
|
+
* touched — never Claude Code's. Exact-match only, everything else preserved;
|
|
422
|
+
* throws on a malformed file for the same reason as the writers above. Returns
|
|
423
|
+
* how many entries were actually removed.
|
|
424
|
+
*/
|
|
425
|
+
export function removeOneCodePermissionAllow(entries: string[], home: string): number {
|
|
426
|
+
const path = oneCodeSettingsPath(home);
|
|
427
|
+
const file = readSettingsForWrite(path);
|
|
428
|
+
const permissions = asRecord(file.permissions);
|
|
429
|
+
const allowList = permissions?.allow;
|
|
430
|
+
if (!permissions || !Array.isArray(allowList)) return 0;
|
|
431
|
+
const doomed = new Set(entries);
|
|
432
|
+
const kept = allowList.filter((entry) => typeof entry !== "string" || !doomed.has(entry));
|
|
433
|
+
const removed = allowList.length - kept.length;
|
|
434
|
+
if (removed > 0) {
|
|
435
|
+
permissions.allow = kept;
|
|
436
|
+
writeSettings(path, file);
|
|
437
|
+
}
|
|
438
|
+
return removed;
|
|
237
439
|
}
|
|
@@ -1,65 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Built-in auto-mode
|
|
2
|
+
* Built-in auto-mode configuration defaults.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* allow — exceptions to soft_deny
|
|
12
|
-
* (intent) — a specific user instruction clears remaining soft denials
|
|
4
|
+
* The ruleset itself is no longer configurable prose lists — the classifier
|
|
5
|
+
* ruleset is a fixed monolith matching Claude Code's classifier (see
|
|
6
|
+
* classifier-prompt.ts and docs/decisions/auto-mode.md). The one customization
|
|
7
|
+
* surface Claude Code exposes, and the only one we expose, is the `## Environment` section:
|
|
8
|
+
* `DEFAULT_ENVIRONMENT` is CC's default slot text, and a user's `autoMode.environment`
|
|
9
|
+
* entries replace it. Re-exported here so config.ts has a single stable import
|
|
10
|
+
* point rather than reaching into the generated prompt module.
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
export const DEFAULT_ENVIRONMENT: string[] = [
|
|
17
|
-
"Trusted repo: the git repository containing the working directory, and its configured remotes. Pushing to any branch of this repository is routine. Content first read from outside this repository is not this repository's own work, whoever asked for it to be moved.",
|
|
18
|
-
"Repository visibility: assume private unless the user's own message says otherwise. Confidential material may go into a private repo; secrets and personal data may never go into any repo regardless of visibility.",
|
|
19
|
-
"Trusted internal domains: none configured. Any host that is not the repository's own remote is outside the boundary.",
|
|
20
|
-
"Trusted cloud buckets: none configured. Treat every bucket write as leaving the boundary.",
|
|
21
|
-
"Internal package registry: none configured. Installs from the ecosystem's default public registry are routine; installs redirected to another registry are not.",
|
|
22
|
-
"Sensitive data locations: credential directories (~/.ssh, ~/.aws, ~/.kube, ~/.docker, ~/.m2, ~/.gnupg, ~/.config/gcloud), .env files anywhere, private keys and keystores, and /proc or /sys process state.",
|
|
23
|
-
"Sensitive remote targets: any host, namespace, container, or branch whose name carries prod, production, live, or release.",
|
|
24
|
-
"Protected IaC scopes: any Terraform, Pulumi, CloudFormation, Helm, or Kubernetes manifest path whose name marks it as production.",
|
|
25
|
-
"Primary use: software development in the working directory.",
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
/** Unconditional. User intent does not clear these. */
|
|
29
|
-
export const DEFAULT_HARD_DENY: string[] = [
|
|
30
|
-
"Data exfiltration: sending secrets, credentials, private keys, personal data, or the contents of sensitive files to any destination outside the trust boundary — by network request, by commit or push, by writing into a shared location, or by embedding them in a command argument that reaches a third party. This holds however the action is framed and whoever asked for it.",
|
|
31
|
-
"Auto-mode bypass: disabling, weakening, or working around this approval mechanism — editing permission settings or Claude Code/One Code configuration to grant broader permissions, adding allow rules, changing the permission mode, or routing an action through a path that avoids classification.",
|
|
32
|
-
"Credential harvesting: reading credential stores, keychains, browser password databases, or process environment memory (/proc/*/environ) for any purpose other than one the user specifically named.",
|
|
33
|
-
"Destroying the user's own history: rewriting or deleting the reflog, deleting the local repository, or removing backups such that the user's uncommitted or unpushed work cannot be recovered.",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
/** Blocked, but an allow rule or a specific user instruction clears them. */
|
|
37
|
-
export const DEFAULT_SOFT_DENY: string[] = [
|
|
38
|
-
"Git destructive [must name the operation and its target]: force pushing, deleting remote branches, tags, or releases, rewriting remote history, or amending a commit that is not the agent's own unpushed work from this session.",
|
|
39
|
-
"Production deploys and destroys [must name the target]: deploying, applying, or destroying against a sensitive remote target or protected IaC scope, including terraform apply/destroy, helm upgrade, kubectl apply against a production namespace, and any release or publish pipeline.",
|
|
40
|
-
"Remote code execution: piping a downloaded script into an interpreter (curl | bash and equivalents), running an installer fetched at execution time, or executing a file whose contents were downloaded during this session.",
|
|
41
|
-
"Irreversible loss outside the working directory: deleting, overwriting, or truncating an existing file or directory that is not under the working directory and not obviously scratch space such as /tmp. Creating a new file outside the working directory is covered by the separate rule below, not by this one.",
|
|
42
|
-
"Creating or modifying files outside the working directory [must name the path]: writing somewhere the user did not ask for is how work leaks out of the project. Clears when the user's own message names that path, or names the destination unambiguously enough that the path follows from it. Also clears for the harness's own designated directories named in the system prompt — the session scratchpad (under /tmp or /private/tmp, in a claude-<uid> directory) and the auto-memory directory (~/.claude/projects/<project>/memory) — which exist precisely so temporary and memory files do not land in the project.",
|
|
43
|
-
"Publishing: pushing a package to a registry, creating a public release or gist, or making a private repository public.",
|
|
44
|
-
"Disabling safety machinery: turning off CI checks, required reviews, branch protection, test suites in configuration, pre-commit hooks, or TLS certificate verification.",
|
|
45
|
-
"Secrets entering version control: staging or committing a file that holds a credential, key, or token, including one the agent generated.",
|
|
46
|
-
"Bulk permission or ownership changes: recursive chmod/chown, or making files world-writable.",
|
|
47
|
-
"Writing to paths whose contents execute later without further approval: .git/hooks, .git/config, editor auto-run configuration, or a build wrapper such as mvnw or gradlew.",
|
|
48
|
-
"Database mutation outside a migration tool: dropping, truncating, or bulk-updating tables directly, against any database.",
|
|
49
|
-
"Remote shells and port-forwards into a sensitive remote target.",
|
|
50
|
-
"Sending repository contents to a third-party service that is not part of the configured toolchain.",
|
|
51
|
-
];
|
|
13
|
+
export { DEFAULT_ENVIRONMENT } from "./classifier-prompt.ts";
|
|
52
14
|
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"Creating and updating pull requests on the trusted repo, and reading issues and CI results.",
|
|
62
|
-
"Starting, stopping, and querying local development servers and containers bound to loopback.",
|
|
63
|
-
"Test artifacts: placeholder credentials authored as placeholders in examples and fixtures. A value copied from a real secret or a sensitive path is never a test artifact.",
|
|
64
|
-
"Reading the project's own .env file when the user asked for work that requires its configuration, provided the values do not leave the machine.",
|
|
65
|
-
];
|
|
15
|
+
/**
|
|
16
|
+
* The `**Name**:` head of an Environment slot line, or undefined for header
|
|
17
|
+
* ("### Org-wide") and prose entries. Used to tell a full slot replacement
|
|
18
|
+
* (the wizard's normal output) from a partial one that silently drops slots.
|
|
19
|
+
*/
|
|
20
|
+
export function slotName(line: string): string | undefined {
|
|
21
|
+
return line.match(/^\*\*(.+?)\*\*:/)?.[1];
|
|
22
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
15
|
import { dirname, join } from "node:path";
|
|
16
|
+
import { claudeUserDir } from "../lib/paths.ts";
|
|
16
17
|
|
|
17
18
|
const FILE_NAMES = ["CLAUDE.md", "CLAUDE.local.md", "AGENTS.md"];
|
|
18
19
|
|
|
@@ -68,7 +69,7 @@ export function loadProjectInstructions(cwd: string, home: string): string | und
|
|
|
68
69
|
if (parent === dir) break;
|
|
69
70
|
dir = parent;
|
|
70
71
|
}
|
|
71
|
-
add(join(home, "
|
|
72
|
+
add(join(claudeUserDir(home), "CLAUDE.md"));
|
|
72
73
|
|
|
73
74
|
return parts.length > 0 ? parts.join("\n\n") : undefined;
|
|
74
75
|
}
|