klaude-code 1.2.22__py3-none-any.whl → 1.2.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. klaude_code/command/prompt-jj-describe.md +32 -0
  2. klaude_code/command/status_cmd.py +1 -1
  3. klaude_code/{const/__init__.py → const.py} +11 -2
  4. klaude_code/core/executor.py +1 -1
  5. klaude_code/core/manager/sub_agent_manager.py +1 -1
  6. klaude_code/core/reminders.py +51 -0
  7. klaude_code/core/task.py +37 -18
  8. klaude_code/core/tool/__init__.py +1 -4
  9. klaude_code/core/tool/file/read_tool.py +23 -1
  10. klaude_code/core/tool/file/write_tool.py +7 -3
  11. klaude_code/core/tool/skill/__init__.py +0 -0
  12. klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -39
  13. klaude_code/llm/openai_compatible/client.py +29 -102
  14. klaude_code/llm/openai_compatible/stream.py +272 -0
  15. klaude_code/llm/openrouter/client.py +29 -109
  16. klaude_code/llm/openrouter/{reasoning_handler.py → reasoning.py} +24 -2
  17. klaude_code/protocol/model.py +15 -2
  18. klaude_code/session/export.py +1 -1
  19. klaude_code/session/store.py +4 -2
  20. klaude_code/skill/__init__.py +27 -0
  21. klaude_code/skill/assets/deslop/SKILL.md +17 -0
  22. klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
  23. klaude_code/skill/assets/handoff/SKILL.md +39 -0
  24. klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
  25. klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
  26. klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +60 -24
  27. klaude_code/skill/manager.py +70 -0
  28. klaude_code/skill/system_skills.py +192 -0
  29. klaude_code/ui/core/stage_manager.py +0 -3
  30. klaude_code/ui/modes/repl/completers.py +103 -3
  31. klaude_code/ui/modes/repl/event_handler.py +101 -49
  32. klaude_code/ui/modes/repl/input_prompt_toolkit.py +55 -6
  33. klaude_code/ui/modes/repl/renderer.py +24 -17
  34. klaude_code/ui/renderers/assistant.py +7 -2
  35. klaude_code/ui/renderers/developer.py +12 -0
  36. klaude_code/ui/renderers/diffs.py +1 -1
  37. klaude_code/ui/renderers/metadata.py +6 -8
  38. klaude_code/ui/renderers/sub_agent.py +28 -5
  39. klaude_code/ui/renderers/thinking.py +16 -10
  40. klaude_code/ui/renderers/tools.py +83 -34
  41. klaude_code/ui/renderers/user_input.py +32 -2
  42. klaude_code/ui/rich/markdown.py +40 -20
  43. klaude_code/ui/rich/status.py +15 -19
  44. klaude_code/ui/rich/theme.py +70 -17
  45. {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/METADATA +18 -13
  46. {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/RECORD +49 -45
  47. klaude_code/command/prompt-deslop.md +0 -14
  48. klaude_code/command/prompt-dev-docs-update.md +0 -56
  49. klaude_code/command/prompt-dev-docs.md +0 -46
  50. klaude_code/command/prompt-handoff.md +0 -33
  51. klaude_code/command/prompt-jj-workspace.md +0 -18
  52. klaude_code/core/tool/memory/__init__.py +0 -5
  53. klaude_code/llm/openai_compatible/stream_processor.py +0 -83
  54. /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
  55. {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/WHEEL +0 -0
  56. {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: dev-docs
3
+ description: Manage development documentation for complex tasks. Use this skill when users want to create a strategic plan with structured task breakdown, or update development documentation before context compaction/reset. Triggers include "create a plan", "dev docs", "update docs before context reset", "context limit approaching".
4
+ metadata:
5
+ short-description: Create and update development documentation
6
+ ---
7
+
8
+ # Dev Docs
9
+
10
+ Manage development documentation for complex, multi-phase tasks. This skill supports two workflows:
11
+ 1. **Create**: Generate a comprehensive strategic plan with structured task breakdown
12
+ 2. **Update**: Update documentation before context compaction or reset
13
+
14
+ ## Create Development Plan
15
+
16
+ When creating a new plan:
17
+
18
+ 1. **Analyze the request** and determine the scope of planning needed
19
+ 2. **Examine relevant files** in the codebase to understand current state
20
+ 3. **Create a structured plan** with:
21
+ - Executive Summary
22
+ - Current State Analysis
23
+ - Proposed Future State
24
+ - Implementation Phases (broken into sections)
25
+ - Detailed Tasks (actionable items with clear acceptance criteria)
26
+ - Risk Assessment and Mitigation Strategies
27
+ - Success Metrics
28
+ - Required Resources and Dependencies
29
+
30
+ 4. **Task Breakdown Structure**:
31
+ - Each major section represents a phase or component
32
+ - Number and prioritize tasks within sections
33
+ - Include clear acceptance criteria for each task
34
+ - Specify dependencies between tasks
35
+ - Estimate effort levels (S/M/L/XL)
36
+
37
+ 5. **Create task management structure**:
38
+ - Create directory: `dev/active/[task-name]/` (relative to project root)
39
+ - Generate three files:
40
+ - `[task-name]-plan.md` - The comprehensive plan
41
+ - `[task-name]-context.md` - Key files, decisions, dependencies
42
+ - `[task-name]-tasks.md` - Checklist format for tracking progress
43
+ - Include "Last Updated: YYYY-MM-DD" in each file
44
+
45
+ 6. **Stop and Consult**: Pause and negotiate the plan with the user.
46
+
47
+ ### Quality Standards
48
+
49
+ - Plans must be self-contained with all necessary context
50
+ - Use clear, actionable language
51
+ - Include specific technical details where relevant
52
+ - Consider both technical and business perspectives
53
+ - Account for potential risks and edge cases
54
+
55
+ ## Update Development Documentation
56
+
57
+ When approaching context limits or before context reset:
58
+
59
+ ### 1. Update Active Task Documentation
60
+
61
+ For each task in `/dev/active/`:
62
+
63
+ Update `[task-name]-context.md` with:
64
+ - Current implementation state
65
+ - Key decisions made this session
66
+ - Files modified and why
67
+ - Any blockers or issues discovered
68
+ - Next immediate steps
69
+ - Last Updated timestamp
70
+
71
+ Update `[task-name]-tasks.md` with:
72
+ - Mark completed tasks with checkmark
73
+ - Add any new tasks discovered
74
+ - Update in-progress tasks with current status
75
+ - Reorder priorities if needed
76
+
77
+ ### 2. Capture Session Context
78
+
79
+ Include any relevant information about:
80
+ - Complex problems solved
81
+ - Architectural decisions made
82
+ - Tricky bugs found and fixed
83
+ - Integration points discovered
84
+ - Testing approaches used
85
+ - Performance optimizations made
86
+
87
+ ### 3. Update Memory (if applicable)
88
+
89
+ - Store any new patterns or solutions in project memory/documentation
90
+ - Update entity relationships discovered
91
+ - Add observations about system behavior
92
+
93
+ ### 4. Document Unfinished Work
94
+
95
+ - What was being worked on when context limit approached
96
+ - Exact state of any partially completed features
97
+ - Commands that need to be run on restart
98
+ - Any temporary workarounds that need permanent fixes
99
+
100
+ ### 5. Create Handoff Notes
101
+
102
+ If switching to a new conversation:
103
+ - Exact file and line being edited
104
+ - The goal of current changes
105
+ - Any uncommitted changes that need attention
106
+ - Test commands to verify work
107
+
108
+ **Priority**: Focus on capturing information that would be hard to rediscover or reconstruct from code alone.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: handoff
3
+ description: Write a HANDOFF.md file for another agent to continue the conversation. Use this skill when switching to a new conversation/session and need to pass context to the next agent. Triggers include "handoff", "write handoff", "create handoff", "pass to another agent".
4
+ metadata:
5
+ short-description: Write handoff document for agent continuation
6
+ ---
7
+
8
+ # Handoff
9
+
10
+ Write a HANDOFF.md file in the current working directory for another agent to continue this conversation.
11
+
12
+ Extract relevant context from the conversation to facilitate continuing this work. Write from the user's perspective (first person: "I did...", "I told you...").
13
+
14
+ ## Consider What Would Be Useful
15
+
16
+ - What did the user just do or implement?
17
+ - What instructions did the user give that are still relevant (e.g., follow patterns in the codebase)?
18
+ - Did the user provide a plan or spec that should be included?
19
+ - What important information did the user share (certain libraries, patterns, constraints, preferences)?
20
+ - What key technical details were discovered (APIs, methods, patterns)?
21
+ - What caveats, limitations, or open questions remain?
22
+
23
+ Extract only what matters for the specific goal. Skip irrelevant questions. Choose an appropriate length based on the complexity.
24
+
25
+ Focus on capabilities and behavior, not file-by-file changes. Avoid excessive implementation details (variable names, storage keys, constants) unless critical.
26
+
27
+ ## Format
28
+
29
+ - Plain text with bullets
30
+ - No markdown headers, no bold/italic, no code fences
31
+ - Use workspace-relative paths for files
32
+ - List relevant file or directory paths at the end:
33
+
34
+ ```
35
+ @src/project/main.py
36
+ @src/project/llm/
37
+ ```
38
+
39
+ If the user's goal is unclear, ask for clarification.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: jj-workspace
3
+ description: Create a jj workspace before starting work to enable parallel Claude sessions. Use this skill when starting a new task that should be isolated from other concurrent work. Triggers include "jj workspace", "parallel work", "create workspace", "isolated workspace".
4
+ metadata:
5
+ short-description: Create jj workspace for parallel work
6
+ ---
7
+
8
+ # JJ Workspace
9
+
10
+ Create a dedicated jj workspace before starting any task. This allows multiple Claude sessions to work in parallel without conflicts.
11
+
12
+ ## Steps
13
+
14
+ 1. Generate a short, descriptive workspace name based on the task (e.g., `workspace-add-login` or `workspace-fix-typo`)
15
+ 2. Run `jj workspace add <workspace-name>` to create the workspace
16
+ 3. Change into the workspace directory: `cd <workspace-name>`
17
+ 4. Describe the change: `jj describe -m '<brief task description>'`
18
+ 5. Continue all subsequent work within this workspace directory
19
+
20
+ If no task is provided, ask the user for a task description.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: skill-creator
3
+ description: Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
4
+ metadata:
5
+ short-description: Create or update a skill
6
+ ---
7
+
8
+ # Skill Creator
9
+
10
+ This skill provides guidance for creating effective skills.
11
+
12
+ ## About Skills
13
+
14
+ Skills are modular, self-contained packages that extend the agent's capabilities by providing
15
+ specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific
16
+ domains or tasks - they transform the agent from a general-purpose assistant into a specialized
17
+ agent equipped with procedural knowledge.
18
+
19
+ ### What Skills Provide
20
+
21
+ 1. Specialized workflows - Multi-step procedures for specific domains
22
+ 2. Tool integrations - Instructions for working with specific file formats or APIs
23
+ 3. Domain expertise - Company-specific knowledge, schemas, business logic
24
+ 4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks
25
+
26
+ ## Core Principles
27
+
28
+ ### Concise is Key
29
+
30
+ The context window is a public good. Skills share the context window with everything else:
31
+ system prompt, conversation history, other Skills' metadata, and the actual user request.
32
+
33
+ **Default assumption: The agent is already very smart.** Only add context the agent doesn't
34
+ already have. Challenge each piece of information: "Does the agent really need this explanation?"
35
+ and "Does this paragraph justify its token cost?"
36
+
37
+ Prefer concise examples over verbose explanations.
38
+
39
+ ### Anatomy of a Skill
40
+
41
+ Every skill consists of a required SKILL.md file and optional bundled resources:
42
+
43
+ ```
44
+ skill-name/
45
+ ├── SKILL.md (required)
46
+ │ ├── YAML frontmatter metadata (required)
47
+ │ │ ├── name: (required)
48
+ │ │ └── description: (required)
49
+ │ └── Markdown instructions (required)
50
+ └── Bundled Resources (optional)
51
+ ├── scripts/ - Executable code (Python/Bash/etc.)
52
+ ├── references/ - Documentation intended to be loaded into context as needed
53
+ └── assets/ - Files used in output (templates, icons, fonts, etc.)
54
+ ```
55
+
56
+ #### SKILL.md (required)
57
+
58
+ Every SKILL.md consists of:
59
+
60
+ - **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields
61
+ that determine when the skill gets used, thus it is very important to be clear and comprehensive
62
+ in describing what the skill is, and when it should be used.
63
+ - **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the
64
+ skill triggers (if at all).
65
+
66
+ #### Bundled Resources (optional)
67
+
68
+ ##### Scripts (`scripts/`)
69
+
70
+ Executable code (Python/Bash/etc.) for tasks that require deterministic reliability or are
71
+ repeatedly rewritten.
72
+
73
+ - **When to include**: When the same code is being rewritten repeatedly or deterministic
74
+ reliability is needed
75
+ - **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks
76
+ - **Benefits**: Token efficient, deterministic, may be executed without loading into context
77
+
78
+ ##### References (`references/`)
79
+
80
+ Documentation and reference material intended to be loaded as needed into context.
81
+
82
+ - **When to include**: For documentation that the agent should reference while working
83
+ - **Examples**: `references/schema.md` for database schemas, `references/api_docs.md` for
84
+ API specifications
85
+ - **Benefits**: Keeps SKILL.md lean, loaded only when needed
86
+
87
+ ##### Assets (`assets/`)
88
+
89
+ Files not intended to be loaded into context, but rather used within the output.
90
+
91
+ - **When to include**: When the skill needs files that will be used in the final output
92
+ - **Examples**: `assets/logo.png` for brand assets, `assets/template.html` for HTML templates
93
+ - **Benefits**: Separates output resources from documentation
94
+
95
+ ## Skill Creation Process
96
+
97
+ Skill creation involves these steps:
98
+
99
+ 1. Understand the skill with concrete examples
100
+ 2. Plan reusable skill contents (scripts, references, assets)
101
+ 3. Create the skill directory structure
102
+ 4. Write SKILL.md with proper frontmatter
103
+ 5. Add bundled resources as needed
104
+ 6. Test and iterate based on real usage
105
+
106
+ ### Skill Naming
107
+
108
+ - Use lowercase letters, digits, and hyphens only
109
+ - Prefer short, verb-led phrases that describe the action
110
+ - Name the skill folder exactly after the skill name
111
+
112
+ ### Writing Guidelines
113
+
114
+ Always use imperative/infinitive form.
115
+
116
+ #### Frontmatter
117
+
118
+ Write the YAML frontmatter with `name` and `description`:
119
+
120
+ - `name`: The skill name (required)
121
+ - `description`: This is the primary triggering mechanism for your skill. Include both what
122
+ the Skill does and specific triggers/contexts for when to use it. Include all "when to use"
123
+ information here - Not in the body.
124
+
125
+ #### Body
126
+
127
+ Write instructions for using the skill and its bundled resources. Keep SKILL.md body to the
128
+ essentials and under 500 lines to minimize context bloat.
129
+
130
+ ## Skill Storage Locations
131
+
132
+ Skills can be stored in multiple locations with the following priority (higher priority overrides lower):
133
+
134
+ | Priority | Scope | Path | Description |
135
+ |----------|---------|-----------------------------|-----------------------|
136
+ | 1 | Project | `.claude/skills/` | Current project only |
137
+ | 2 | User | `~/.klaude/skills/` | User-level |
138
+ | 3 | User | `~/.claude/skills/` | User-level (Claude) |
139
+ | 4 | System | `~/.klaude/skills/.system/` | Built-in system skills|
@@ -15,12 +15,22 @@ class Skill:
15
15
  name: str # Skill identifier (lowercase-hyphen)
16
16
  description: str # What the skill does and when to use it
17
17
  content: str # Full markdown instructions
18
- location: str # Skill location: 'user' or 'project'
18
+ location: str # Skill location: 'system', 'user', or 'project'
19
19
  license: str | None = None
20
20
  allowed_tools: list[str] | None = None
21
21
  metadata: dict[str, str] | None = None
22
22
  skill_path: Path | None = None
23
23
 
24
+ @property
25
+ def short_description(self) -> str:
26
+ """Get short description for display in completions.
27
+
28
+ Returns metadata['short-description'] if available, otherwise falls back to description.
29
+ """
30
+ if self.metadata and "short-description" in self.metadata:
31
+ return self.metadata["short-description"]
32
+ return self.description
33
+
24
34
  def to_prompt(self) -> str:
25
35
  """Convert skill to prompt format for agent consumption"""
26
36
  return f"""# Skill: {self.name}
@@ -36,13 +46,15 @@ class Skill:
36
46
  class SkillLoader:
37
47
  """Load and manage Claude Skills from SKILL.md files"""
38
48
 
49
+ # System-level skills directory (built-in, lowest priority)
50
+ SYSTEM_SKILLS_DIR: ClassVar[Path] = Path("~/.klaude/skills/.system")
51
+
39
52
  # User-level skills directories (checked in order, later ones override earlier ones with same name)
40
53
  USER_SKILLS_DIRS: ClassVar[list[Path]] = [
41
54
  Path("~/.claude/skills"),
42
55
  Path("~/.klaude/skills"),
43
- # Path("~/.claude/plugins/marketplaces"),
44
56
  ]
45
- # Project-level skills directory
57
+ # Project-level skills directory (highest priority)
46
58
  PROJECT_SKILLS_DIR: ClassVar[Path] = Path("./.claude/skills")
47
59
 
48
60
  def __init__(self) -> None:
@@ -54,7 +66,7 @@ class SkillLoader:
54
66
 
55
67
  Args:
56
68
  skill_path: Path to SKILL.md file
57
- location: Skill location ('user' or 'project')
69
+ location: Skill location ('system', 'user', or 'project')
58
70
 
59
71
  Returns:
60
72
  Skill object or None if loading failed
@@ -121,39 +133,57 @@ class SkillLoader:
121
133
  return None
122
134
 
123
135
  def discover_skills(self) -> list[Skill]:
124
- """Recursively find all SKILL.md files and load them from both user and project directories
136
+ """Recursively find all SKILL.md files and load them from system, user and project directories.
137
+
138
+ Loading order (lower priority first, higher priority overrides):
139
+ 1. System skills (~/.klaude/skills/.system/) - built-in, lowest priority
140
+ 2. User skills (~/.claude/skills/, ~/.klaude/skills/) - user-level
141
+ 3. Project skills (./.claude/skills/) - project-level, highest priority
125
142
 
126
143
  Returns:
127
144
  List of successfully loaded Skill objects
128
145
  """
129
146
  skills: list[Skill] = []
130
147
 
131
- # Load user-level skills from all directories
148
+ # Load system-level skills first (lowest priority, can be overridden)
149
+ system_dir = self.SYSTEM_SKILLS_DIR.expanduser()
150
+ if system_dir.exists():
151
+ for skill_file in system_dir.rglob("SKILL.md"):
152
+ skill = self.load_skill(skill_file, location="system")
153
+ if skill:
154
+ skills.append(skill)
155
+ self.loaded_skills[skill.name] = skill
156
+
157
+ # Load user-level skills (override system skills if same name)
132
158
  for user_dir in self.USER_SKILLS_DIRS:
133
159
  expanded_dir = user_dir.expanduser()
134
160
  if expanded_dir.exists():
135
- for pattern in ("SKILL.md", "skill.md"):
136
- for skill_file in expanded_dir.rglob(pattern):
137
- skill = self.load_skill(skill_file, location="user")
138
- if skill:
139
- skills.append(skill)
140
- self.loaded_skills[skill.name] = skill
161
+ for skill_file in expanded_dir.rglob("SKILL.md"):
162
+ # Skip files under .system directory (already loaded above)
163
+ if ".system" in skill_file.parts:
164
+ continue
165
+ skill = self.load_skill(skill_file, location="user")
166
+ if skill:
167
+ skills.append(skill)
168
+ self.loaded_skills[skill.name] = skill
141
169
 
142
170
  # Load project-level skills (override user skills if same name)
143
171
  project_dir = self.PROJECT_SKILLS_DIR.resolve()
144
172
  if project_dir.exists():
145
- for pattern in ("SKILL.md", "skill.md"):
146
- for skill_file in project_dir.rglob(pattern):
147
- skill = self.load_skill(skill_file, location="project")
148
- if skill:
149
- skills.append(skill)
150
- self.loaded_skills[skill.name] = skill
173
+ for skill_file in project_dir.rglob("SKILL.md"):
174
+ skill = self.load_skill(skill_file, location="project")
175
+ if skill:
176
+ skills.append(skill)
177
+ self.loaded_skills[skill.name] = skill
151
178
 
152
179
  # Log discovery summary
153
180
  if skills:
181
+ system_count = sum(1 for s in skills if s.location == "system")
154
182
  user_count = sum(1 for s in skills if s.location == "user")
155
183
  project_count = sum(1 for s in skills if s.location == "project")
156
184
  parts: list[str] = []
185
+ if system_count > 0:
186
+ parts.append(f"{system_count} system")
157
187
  if user_count > 0:
158
188
  parts.append(f"{user_count} user")
159
189
  if project_count > 0:
@@ -171,11 +201,17 @@ class SkillLoader:
171
201
  Returns:
172
202
  Skill object or None if not found
173
203
  """
204
+ # Prefer exact match first (supports namespaced skill names).
205
+ skill = self.loaded_skills.get(name)
206
+ if skill is not None:
207
+ return skill
208
+
174
209
  # Support both formats: 'pdf' and 'document-skills:pdf'
175
210
  if ":" in name:
176
- name = name.split(":")[-1]
211
+ short = name.split(":")[-1]
212
+ return self.loaded_skills.get(short)
177
213
 
178
- return self.loaded_skills.get(name)
214
+ return None
179
215
 
180
216
  def list_skills(self) -> list[str]:
181
217
  """Get list of all loaded skill names"""
@@ -224,25 +260,25 @@ class SkillLoader:
224
260
  content = re.sub(dir_pattern, replace_dir_path, content)
225
261
 
226
262
  # Pattern 2: Markdown links [text](./path or path)
227
- # e.g., "[Guide](./docs/guide.md)" -> "[Guide](`/abs/path/to/docs/guide.md`) (use read_file to access)"
263
+ # e.g., "[Guide](./docs/guide.md)" -> "[Guide](`/abs/path/to/docs/guide.md`) (use the Read tool to access)"
228
264
  link_pattern = r"\[([^\]]+)\]\((\./)?([^\)]+\.md)\)"
229
265
 
230
266
  def replace_link(match: re.Match[str]) -> str:
231
267
  text = match.group(1)
232
268
  filename = match.group(3)
233
269
  abs_path = skill_dir / filename
234
- return f"[{text}](`{abs_path}`) (use read_file to access)"
270
+ return f"[{text}](`{abs_path}`) (use the Read tool to access)"
235
271
 
236
272
  content = re.sub(link_pattern, replace_link, content)
237
273
 
238
274
  # Pattern 3: Standalone markdown references
239
- # e.g., "see reference.md" -> "see `/abs/path/to/reference.md` (use read_file to access)"
275
+ # e.g., "see reference.md" -> "see `/abs/path/to/reference.md` (use the Read tool to access)"
240
276
  standalone_pattern = r"(?<!\])\b(\w+\.md)\b(?!\))"
241
277
 
242
278
  def replace_standalone(match: re.Match[str]) -> str:
243
279
  filename = match.group(1)
244
280
  abs_path = skill_dir / filename
245
- return f"`{abs_path}` (use read_file to access)"
281
+ return f"`{abs_path}` (use the Read tool to access)"
246
282
 
247
283
  content = re.sub(standalone_pattern, replace_standalone, content)
248
284
 
@@ -0,0 +1,70 @@
1
+ """Global skill manager with lazy initialization.
2
+
3
+ This module provides a centralized interface for accessing skills throughout the application.
4
+ Skills are loaded lazily on first access to avoid unnecessary IO at startup.
5
+ """
6
+
7
+ from klaude_code.skill.loader import Skill, SkillLoader
8
+ from klaude_code.skill.system_skills import install_system_skills
9
+
10
+ _loader: SkillLoader | None = None
11
+ _initialized: bool = False
12
+
13
+
14
+ def _ensure_initialized() -> SkillLoader:
15
+ """Ensure the skill system is initialized and return the loader."""
16
+ global _loader, _initialized
17
+ if not _initialized:
18
+ install_system_skills()
19
+ _loader = SkillLoader()
20
+ _loader.discover_skills()
21
+ _initialized = True
22
+ assert _loader is not None
23
+ return _loader
24
+
25
+
26
+ def get_skill_loader() -> SkillLoader:
27
+ """Get the global skill loader instance.
28
+
29
+ Lazily initializes the skill system on first call.
30
+
31
+ Returns:
32
+ The global SkillLoader instance
33
+ """
34
+ return _ensure_initialized()
35
+
36
+
37
+ def get_skill(name: str) -> Skill | None:
38
+ """Get a skill by name.
39
+
40
+ Args:
41
+ name: Skill name (supports both 'skill-name' and 'namespace:skill-name')
42
+
43
+ Returns:
44
+ Skill object or None if not found
45
+ """
46
+ return _ensure_initialized().get_skill(name)
47
+
48
+
49
+ def get_available_skills() -> list[tuple[str, str, str]]:
50
+ """Get list of available skills for completion and display.
51
+
52
+ Returns:
53
+ List of (name, short_description, location) tuples.
54
+ Uses metadata['short-description'] if available, otherwise falls back to description.
55
+ Skills are ordered by priority: project > user > system.
56
+ """
57
+ loader = _ensure_initialized()
58
+ skills = [(s.name, s.short_description, s.location) for s in loader.loaded_skills.values()]
59
+ location_order = {"project": 0, "user": 1, "system": 2}
60
+ skills.sort(key=lambda x: location_order.get(x[2], 3))
61
+ return skills
62
+
63
+
64
+ def list_skill_names() -> list[str]:
65
+ """Get list of all loaded skill names.
66
+
67
+ Returns:
68
+ List of skill names
69
+ """
70
+ return _ensure_initialized().list_skills()