claude-dev-env 1.26.1 → 1.26.3

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.
@@ -13,28 +13,12 @@ import os
13
13
  import stat
14
14
  import sys
15
15
  from pathlib import Path
16
- from typing import Any, NoReturn
16
+ from typing import NoReturn
17
17
 
18
18
 
19
19
  TEXT_FILE_ENCODING: str = "utf-8"
20
- GLOB_METACHARACTERS_IN_PATH: tuple[str, ...] = (
21
- "*",
22
- "?",
23
- "[",
24
- "]",
25
- "(",
26
- ")",
27
- "{",
28
- "}",
29
- ",",
30
- )
31
-
32
- JSON_INDENT_SPACES: int = 2
33
20
  PERMISSION_ALLOW_TOOLS: tuple[str, ...] = ("Edit", "Write", "Read")
34
21
 
35
- DEFAULT_SETTINGS_FILE_MODE: int = 0o600
36
- ATOMIC_WRITE_TEMPORARY_SUFFIX: str = ".tmp"
37
-
38
22
  AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE: str = (
39
23
  "Trusted local workspace: {project_path}/.claude/** is the user's "
40
24
  "project Claude Code config tree; edits inside are routine"
@@ -47,9 +31,20 @@ def exit_with_error(message: str) -> NoReturn:
47
31
 
48
32
 
49
33
  def path_contains_glob_metacharacters(candidate_path: str) -> bool:
34
+ glob_metacharacters_in_path: tuple[str, ...] = (
35
+ "*",
36
+ "?",
37
+ "[",
38
+ "]",
39
+ "(",
40
+ ")",
41
+ "{",
42
+ "}",
43
+ ",",
44
+ )
50
45
  return any(
51
46
  each_character in candidate_path
52
- for each_character in GLOB_METACHARACTERS_IN_PATH
47
+ for each_character in glob_metacharacters_in_path
53
48
  )
54
49
 
55
50
 
@@ -76,10 +71,10 @@ def build_permission_rules(
76
71
  ]
77
72
 
78
73
 
79
- def load_settings(settings_path: Path) -> dict[str, Any]:
74
+ def load_settings(settings_path: Path) -> dict[str, object]:
80
75
  if not settings_path.exists():
81
76
  return {}
82
- parsed_settings: dict[str, Any] = {}
77
+ parsed_settings: dict[str, object] = {}
83
78
  try:
84
79
  raw_text = settings_path.read_text(encoding=TEXT_FILE_ENCODING)
85
80
  except OSError as read_error:
@@ -100,11 +95,21 @@ def load_settings(settings_path: Path) -> dict[str, Any]:
100
95
  return parsed_settings
101
96
 
102
97
 
98
+ def serialize_settings_to_json_text(settings: dict[str, object]) -> str:
99
+ json_indent_width_columns: int = len(" ")
100
+ return json.dumps(
101
+ settings,
102
+ indent=json_indent_width_columns,
103
+ sort_keys=True,
104
+ )
105
+
106
+
103
107
  def get_mode_to_preserve(settings_path: Path) -> int:
108
+ default_settings_file_mode: int = 0o600
104
109
  try:
105
110
  stat_result = os.stat(settings_path)
106
111
  except FileNotFoundError:
107
- return DEFAULT_SETTINGS_FILE_MODE
112
+ return default_settings_file_mode
108
113
  except OSError as stat_error:
109
114
  exit_with_error(f"Failed to stat {settings_path}: {stat_error}")
110
115
  return stat.S_IMODE(stat_result.st_mode)
@@ -122,13 +127,12 @@ def write_atomically_with_mode(
122
127
  writer.write(serialized_content)
123
128
 
124
129
 
125
- def save_settings(settings_path: Path, settings: dict[str, Any]) -> None:
130
+ def save_settings(settings_path: Path, settings: dict[str, object]) -> None:
131
+ atomic_write_temporary_suffix: str = ".tmp"
126
132
  settings_path.parent.mkdir(parents=True, exist_ok=True)
127
- serialized_settings = json.dumps(
128
- settings, indent=JSON_INDENT_SPACES, sort_keys=True
129
- )
133
+ serialized_settings = serialize_settings_to_json_text(settings)
130
134
  temporary_path = settings_path.with_suffix(
131
- settings_path.suffix + ATOMIC_WRITE_TEMPORARY_SUFFIX
135
+ settings_path.suffix + atomic_write_temporary_suffix
132
136
  )
133
137
  mode_to_preserve = get_mode_to_preserve(settings_path)
134
138
  try:
@@ -149,7 +153,7 @@ def save_settings(settings_path: Path, settings: dict[str, Any]) -> None:
149
153
  pass
150
154
 
151
155
 
152
- def append_if_missing(target_list: list[str], new_value: str) -> bool:
156
+ def append_if_missing(target_list: list[object], new_value: str) -> bool:
153
157
  if new_value in target_list:
154
158
  return False
155
159
  target_list.append(new_value)
@@ -157,8 +161,8 @@ def append_if_missing(target_list: list[str], new_value: str) -> bool:
157
161
 
158
162
 
159
163
  def ensure_dict_section(
160
- settings: dict[str, Any], section_name: str
161
- ) -> dict[str, Any]:
164
+ settings: dict[str, object], section_name: str
165
+ ) -> dict[str, object]:
162
166
  """Return an existing dict section or create an empty one if absent.
163
167
 
164
168
  A missing key and an explicit JSON null are treated identically: both
@@ -168,7 +172,7 @@ def ensure_dict_section(
168
172
  """
169
173
  existing_section = settings.get(section_name)
170
174
  if existing_section is None:
171
- replacement_section: dict[str, Any] = {}
175
+ replacement_section: dict[str, object] = {}
172
176
  settings[section_name] = replacement_section
173
177
  return replacement_section
174
178
  if not isinstance(existing_section, dict):
@@ -180,7 +184,7 @@ def ensure_dict_section(
180
184
  return existing_section
181
185
 
182
186
 
183
- def ensure_list_entry(section: dict[str, Any], entry_name: str) -> list[Any]:
187
+ def ensure_list_entry(section: dict[str, object], entry_name: str) -> list[object]:
184
188
  """Return an existing list entry or create an empty one if absent.
185
189
 
186
190
  A missing key and an explicit JSON null are treated identically: both
@@ -190,7 +194,7 @@ def ensure_list_entry(section: dict[str, Any], entry_name: str) -> list[Any]:
190
194
  """
191
195
  existing_entry = section.get(entry_name)
192
196
  if existing_entry is None:
193
- replacement_entry: list[Any] = []
197
+ replacement_entry: list[object] = []
194
198
  section[entry_name] = replacement_entry
195
199
  return replacement_entry
196
200
  if not isinstance(existing_entry, list):
@@ -203,7 +207,7 @@ def ensure_list_entry(section: dict[str, Any], entry_name: str) -> list[Any]:
203
207
 
204
208
 
205
209
  def prune_empty_list_then_empty_section(
206
- settings: dict[str, Any], section_key: str, list_key: str
210
+ settings: dict[str, object], section_key: str, list_key: str
207
211
  ) -> None:
208
212
  section = settings.get(section_key)
209
213
  if not isinstance(section, dict):
@@ -46,6 +46,8 @@ def resolve_merge_base(repository_root: Path, base_reference: str) -> str:
46
46
  cwd=str(repository_root),
47
47
  capture_output=True,
48
48
  text=True,
49
+ encoding="utf-8",
50
+ errors="replace",
49
51
  check=False,
50
52
  )
51
53
  if merge_result.returncode != 0:
@@ -58,6 +60,35 @@ def resolve_merge_base(repository_root: Path, base_reference: str) -> str:
58
60
  return merge_result.stdout.strip()
59
61
 
60
62
 
63
+ def filter_paths_under_prefixes(
64
+ file_paths: list[Path],
65
+ repository_root: Path,
66
+ prefix_list: list[str],
67
+ ) -> list[Path]:
68
+ if not prefix_list:
69
+ return file_paths
70
+ normalized_prefixes = [
71
+ each.strip().replace("\\", "/").rstrip("/")
72
+ for each in prefix_list
73
+ if each.strip()
74
+ ]
75
+ if not normalized_prefixes:
76
+ return file_paths
77
+ resolved_root = repository_root.resolve()
78
+ filtered: list[Path] = []
79
+ for each_path in file_paths:
80
+ try:
81
+ relative_posix = each_path.resolve().relative_to(resolved_root).as_posix()
82
+ except ValueError:
83
+ continue
84
+ if any(
85
+ relative_posix == prefix or relative_posix.startswith(prefix + "/")
86
+ for prefix in normalized_prefixes
87
+ ):
88
+ filtered.append(each_path)
89
+ return filtered
90
+
91
+
61
92
  def paths_from_git_diff(repository_root: Path, base_reference: str) -> list[Path]:
62
93
  merge_base = resolve_merge_base(repository_root, base_reference)
63
94
  name_result = subprocess.run(
@@ -65,6 +96,8 @@ def paths_from_git_diff(repository_root: Path, base_reference: str) -> list[Path
65
96
  cwd=str(repository_root),
66
97
  capture_output=True,
67
98
  text=True,
99
+ encoding="utf-8",
100
+ errors="replace",
68
101
  check=False,
69
102
  )
70
103
  if name_result.returncode != 0:
@@ -109,6 +142,8 @@ def is_file_new_at_base(
109
142
  cwd=str(repository_root),
110
143
  capture_output=True,
111
144
  text=True,
145
+ encoding="utf-8",
146
+ errors="replace",
112
147
  check=False,
113
148
  )
114
149
  return cat_result.returncode != 0
@@ -124,6 +159,8 @@ def added_lines_for_file(
124
159
  cwd=str(repository_root),
125
160
  capture_output=True,
126
161
  text=True,
162
+ encoding="utf-8",
163
+ errors="replace",
127
164
  check=False,
128
165
  )
129
166
  if diff_result.returncode != 0:
@@ -300,6 +337,17 @@ def parse_arguments(argv: list[str]) -> argparse.Namespace:
300
337
  default="origin/main",
301
338
  help="Merge-base ref for git diff (default: origin/main).",
302
339
  )
340
+ parser.add_argument(
341
+ "--only-under",
342
+ action="append",
343
+ default=[],
344
+ dest="only_under",
345
+ metavar="PREFIX",
346
+ help=(
347
+ "After resolving the merge-base diff, keep only files whose repo-relative path "
348
+ "uses POSIX slashes and starts with PREFIX or equals PREFIX (repeatable)."
349
+ ),
350
+ )
303
351
  parser.add_argument(
304
352
  "paths",
305
353
  nargs="*",
@@ -321,6 +369,13 @@ def main(argv: list[str] | None = None) -> int:
321
369
  file_paths = [repository_root / path for path in arguments.paths]
322
370
  return run_gate(validate_content, file_paths, repository_root, added_lines_map=None)
323
371
  file_paths = paths_from_git_diff(repository_root, arguments.base)
372
+ file_paths = filter_paths_under_prefixes(
373
+ file_paths,
374
+ repository_root,
375
+ arguments.only_under,
376
+ )
377
+ if not file_paths:
378
+ return 0
324
379
  scoped_added_lines = added_lines_by_file(repository_root, arguments.base, file_paths)
325
380
  return run_gate(
326
381
  validate_content,
@@ -8,7 +8,6 @@ the changes applied. No-op when the entries already exist.
8
8
 
9
9
  import sys
10
10
  from pathlib import Path
11
- from typing import Any
12
11
 
13
12
  sys.path.insert(0, str(Path(__file__).resolve().parent))
14
13
 
@@ -26,16 +25,13 @@ from _claude_permissions_common import ( # noqa: E402
26
25
  )
27
26
 
28
27
 
29
- CLAUDE_USER_SETTINGS_PATH: Path = Path.home() / ".claude" / "settings.json"
30
-
31
-
32
28
  def is_valid_project_root(candidate_path: Path) -> bool:
33
29
  git_marker_path = candidate_path / ".git"
34
30
  claude_marker_path = candidate_path / ".claude"
35
31
  return git_marker_path.exists() or claude_marker_path.exists()
36
32
 
37
33
 
38
- def add_rules_to_allow_list(settings: dict[str, Any], rules_to_add: list[str]) -> int:
34
+ def add_rules_to_allow_list(settings: dict[str, object], rules_to_add: list[str]) -> int:
39
35
  permissions_section = ensure_dict_section(settings, "permissions")
40
36
  existing_allow_list = ensure_list_entry(permissions_section, "allow")
41
37
  return sum(
@@ -46,7 +42,7 @@ def add_rules_to_allow_list(settings: dict[str, Any], rules_to_add: list[str]) -
46
42
 
47
43
 
48
44
  def add_directory_to_additional_directories(
49
- settings: dict[str, Any], directory_path: str
45
+ settings: dict[str, object], directory_path: str
50
46
  ) -> int:
51
47
  permissions_section = ensure_dict_section(settings, "permissions")
52
48
  existing_directories = ensure_list_entry(
@@ -57,7 +53,9 @@ def add_directory_to_additional_directories(
57
53
  return 0
58
54
 
59
55
 
60
- def add_auto_mode_environment_entry(settings: dict[str, Any], entry_text: str) -> int:
56
+ def add_auto_mode_environment_entry(
57
+ settings: dict[str, object], entry_text: str
58
+ ) -> int:
61
59
  auto_mode_section = ensure_dict_section(settings, "autoMode")
62
60
  existing_environment = ensure_list_entry(auto_mode_section, "environment")
63
61
  if append_if_missing(existing_environment, entry_text):
@@ -66,6 +64,7 @@ def add_auto_mode_environment_entry(settings: dict[str, Any], entry_text: str) -
66
64
 
67
65
 
68
66
  def grant_permissions_for_current_directory() -> None:
67
+ claude_user_settings_path: Path = Path.home() / ".claude" / "settings.json"
69
68
  project_root_path = Path.cwd()
70
69
  if not is_valid_project_root(project_root_path):
71
70
  print(
@@ -79,7 +78,7 @@ def grant_permissions_for_current_directory() -> None:
79
78
  environment_entry = AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE.format(
80
79
  project_path=project_path
81
80
  )
82
- settings = load_settings(CLAUDE_USER_SETTINGS_PATH)
81
+ settings = load_settings(claude_user_settings_path)
83
82
  rules_added_count = add_rules_to_allow_list(settings, permission_rules)
84
83
  directories_added_count = add_directory_to_additional_directories(
85
84
  settings, project_path
@@ -92,12 +91,12 @@ def grant_permissions_for_current_directory() -> None:
92
91
  )
93
92
  if total_changes_count == 0:
94
93
  print(f"Project path: {project_path}")
95
- print(f"Settings file: {CLAUDE_USER_SETTINGS_PATH}")
94
+ print(f"Settings file: {claude_user_settings_path}")
96
95
  print("No changes needed; settings file left untouched.")
97
96
  return
98
- save_settings(CLAUDE_USER_SETTINGS_PATH, settings)
97
+ save_settings(claude_user_settings_path, settings)
99
98
  print(f"Project path: {project_path}")
100
- print(f"Settings file: {CLAUDE_USER_SETTINGS_PATH}")
99
+ print(f"Settings file: {claude_user_settings_path}")
101
100
  print(f"Allow rules added: {rules_added_count} of {len(permission_rules)}")
102
101
  print(f"Additional directories added: {directories_added_count}")
103
102
  print(f"Auto-mode environment entries added: {environment_entries_added_count}")
@@ -9,7 +9,6 @@ autoMode sections so repeated grant/revoke cycles leave no dead structure.
9
9
 
10
10
  import sys
11
11
  from pathlib import Path
12
- from typing import Any
13
12
 
14
13
  sys.path.insert(0, str(Path(__file__).resolve().parent))
15
14
 
@@ -25,25 +24,24 @@ from _claude_permissions_common import ( # noqa: E402
25
24
  )
26
25
 
27
26
 
28
- CLAUDE_USER_SETTINGS_PATH: Path = Path.home() / ".claude" / "settings.json"
29
-
30
-
31
27
  def is_valid_project_root(candidate_path: Path) -> bool:
32
28
  git_marker_path = candidate_path / ".git"
33
29
  claude_marker_path = candidate_path / ".claude"
34
30
  return git_marker_path.exists() or claude_marker_path.exists()
35
31
 
36
32
 
37
- def remove_values_from_list(target_list: list[str], values_to_remove: set[str]) -> int:
33
+ def remove_values_from_list(target_list: list[object], values_to_remove: set[str]) -> int:
38
34
  original_length = len(target_list)
39
35
  target_list[:] = [
40
- each_value for each_value in target_list if each_value not in values_to_remove
36
+ each_value
37
+ for each_value in target_list
38
+ if not (isinstance(each_value, str) and each_value in values_to_remove)
41
39
  ]
42
40
  return original_length - len(target_list)
43
41
 
44
42
 
45
43
  def remove_rules_from_allow_list(
46
- settings: dict[str, Any], rules_to_remove: list[str]
44
+ settings: dict[str, object], rules_to_remove: list[str]
47
45
  ) -> int:
48
46
  permissions_section = settings.get("permissions")
49
47
  if not isinstance(permissions_section, dict):
@@ -55,7 +53,7 @@ def remove_rules_from_allow_list(
55
53
 
56
54
 
57
55
  def remove_directory_from_additional_directories(
58
- settings: dict[str, Any], directory_path: str
56
+ settings: dict[str, object], directory_path: str
59
57
  ) -> int:
60
58
  permissions_section = settings.get("permissions")
61
59
  if not isinstance(permissions_section, dict):
@@ -67,7 +65,7 @@ def remove_directory_from_additional_directories(
67
65
 
68
66
 
69
67
  def remove_auto_mode_environment_entry(
70
- settings: dict[str, Any], entry_text: str
68
+ settings: dict[str, object], entry_text: str
71
69
  ) -> int:
72
70
  auto_mode_section = settings.get("autoMode")
73
71
  if not isinstance(auto_mode_section, dict):
@@ -78,7 +76,7 @@ def remove_auto_mode_environment_entry(
78
76
  return remove_values_from_list(existing_environment, {entry_text})
79
77
 
80
78
 
81
- def prune_settings_after_revoke(settings: dict[str, Any]) -> None:
79
+ def prune_settings_after_revoke(settings: dict[str, object]) -> None:
82
80
  prune_empty_list_then_empty_section(settings, "permissions", "allow")
83
81
  prune_empty_list_then_empty_section(
84
82
  settings, "permissions", "additionalDirectories"
@@ -87,6 +85,7 @@ def prune_settings_after_revoke(settings: dict[str, Any]) -> None:
87
85
 
88
86
 
89
87
  def revoke_permissions_for_current_directory() -> None:
88
+ claude_user_settings_path: Path = Path.home() / ".claude" / "settings.json"
90
89
  project_root_path = Path.cwd()
91
90
  if not is_valid_project_root(project_root_path):
92
91
  print(
@@ -100,7 +99,7 @@ def revoke_permissions_for_current_directory() -> None:
100
99
  environment_entry = AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE.format(
101
100
  project_path=project_path
102
101
  )
103
- settings = load_settings(CLAUDE_USER_SETTINGS_PATH)
102
+ settings = load_settings(claude_user_settings_path)
104
103
  rules_removed_count = remove_rules_from_allow_list(settings, permission_rules)
105
104
  directories_removed_count = remove_directory_from_additional_directories(
106
105
  settings, project_path
@@ -115,13 +114,13 @@ def revoke_permissions_for_current_directory() -> None:
115
114
  )
116
115
  if total_changes_count == 0:
117
116
  print(f"Project path: {project_path}")
118
- print(f"Settings file: {CLAUDE_USER_SETTINGS_PATH}")
117
+ print(f"Settings file: {claude_user_settings_path}")
119
118
  print("No changes to revoke; settings file left untouched.")
120
119
  return
121
120
  prune_settings_after_revoke(settings)
122
- save_settings(CLAUDE_USER_SETTINGS_PATH, settings)
121
+ save_settings(claude_user_settings_path, settings)
123
122
  print(f"Project path: {project_path}")
124
- print(f"Settings file: {CLAUDE_USER_SETTINGS_PATH}")
123
+ print(f"Settings file: {claude_user_settings_path}")
125
124
  print(f"Allow rules removed: {rules_removed_count} of {len(permission_rules)}")
126
125
  print(f"Additional directories removed: {directories_removed_count}")
127
126
  print(
@@ -0,0 +1,93 @@
1
+ # Bugteam skill — sources and citations
2
+
3
+ Canonical URLs and verbatim quotes that `SKILL.md` relies on. Load this file when verifying wording against upstream documentation or when expanding citations. Domain-oriented narrative (without duplicating these quotes) lives under [`reference/README.md`](reference/README.md).
4
+
5
+ ---
6
+
7
+ ## Claude Code — Agent teams
8
+
9
+ **URL:** [Orchestrate teams of Claude Code sessions](https://code.claude.com/docs/en/agent-teams)
10
+
11
+ **Enable flag (operational link used in refusal copy):** [Enable agent teams](https://code.claude.com/docs/en/agent-teams#enable-agent-teams)
12
+
13
+ ### Teammate context isolation (clean-room basis)
14
+
15
+ Direct quote:
16
+
17
+ > "Each teammate has its own context window. When spawned, a teammate loads the same project context as a regular session: CLAUDE.md, MCP servers, and skills. It also receives the spawn prompt from the lead. The lead's conversation history does not carry over."
18
+
19
+ **Skill use:** Independent context per teammate enforces the clean-room audit property; the same sentence is cited again where per-loop `Agent` spawns are justified.
20
+
21
+ ### Subagents vs agent teams
22
+
23
+ Direct quote:
24
+
25
+ > "Use subagents when you need quick, focused workers that report back. Use agent teams when teammates need to share findings, challenge each other, and coordinate on their own."
26
+
27
+ **Skill use:** Subagents return into the lead context (accumulates across loops); agent-team teammates do not pollute the lead. This skill needs the independent-context property.
28
+
29
+ ### Team creation in natural language
30
+
31
+ Direct quote:
32
+
33
+ > "tell Claude to create an agent team and describe the task and the team structure you want in natural language. Claude creates the team, spawns teammates, and coordinates work based on your prompt."
34
+
35
+ **Skill use:** Maps to the `TeamCreate` tool step in the process section.
36
+
37
+ ### Referencing subagent types when spawning teammates
38
+
39
+ Direct quote:
40
+
41
+ > "When spawning a teammate, you can reference a subagent type from any subagent scope: project, user, plugin, or CLI-defined. This lets you define a role once... and reuse it both as a delegated subagent and as an agent team teammate."
42
+
43
+ **Skill use:** Bugfind / bugfix roles reference `code-quality-agent` and `clean-coder` by subagent type name.
44
+
45
+ ### Lead cleanup and active teammates
46
+
47
+ Direct quote:
48
+
49
+ > "When the lead runs cleanup, it checks for active teammates and fails if any are still running, so shut them down first."
50
+
51
+ **Skill use:** Step 4 shutdown sequence before `TeamDelete`.
52
+
53
+ ### Ending the team
54
+
55
+ Direct quote:
56
+
57
+ > "When you're done, ask the lead to clean up: 'Clean up the team'."
58
+
59
+ **Skill use:** Maps to calling `TeamDelete()` after shutdown messages.
60
+
61
+ ---
62
+
63
+ ## Claude — Agent skills best practices
64
+
65
+ **Base URL:** [Agent Skills best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices)
66
+
67
+ ### Table of contents for long skills
68
+
69
+ **URL:** [Structure longer reference files with table of contents](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#structure-longer-reference-files-with-table-of-contents)
70
+
71
+ **Skill use:** Justifies the top-of-file Contents section so partial reads still expose scope.
72
+
73
+ ### Progressive disclosure and utility scripts
74
+
75
+ **URL:** [Progressive disclosure patterns](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#progressive-disclosure-patterns)
76
+
77
+ **Skill use:** Shell helpers live under `scripts/` and are executed, not loaded as primary context.
78
+
79
+ ### Concise is key
80
+
81
+ **URL:** [Concise is key](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#concise-is-key)
82
+
83
+ Direct quotes:
84
+
85
+ > "However, being concise in SKILL.md still matters: once Claude loads it, every token competes with conversation history and other context."
86
+
87
+ Heading and following line (section uses the heading as its own emphasis):
88
+
89
+ > Default assumption: Claude is already very smart
90
+
91
+ > "Only add context Claude doesn't already have. Challenge each piece of information:"
92
+
93
+ **Skill use:** `SKILL.md` revisions that drop redundant narration and trust the reader’s prior knowledge.