pythonclaw 0.2.0__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 (112) hide show
  1. pythonclaw/__init__.py +17 -0
  2. pythonclaw/__main__.py +6 -0
  3. pythonclaw/channels/discord_bot.py +231 -0
  4. pythonclaw/channels/telegram_bot.py +236 -0
  5. pythonclaw/config.py +190 -0
  6. pythonclaw/core/__init__.py +25 -0
  7. pythonclaw/core/agent.py +773 -0
  8. pythonclaw/core/compaction.py +220 -0
  9. pythonclaw/core/knowledge/rag.py +93 -0
  10. pythonclaw/core/llm/anthropic_client.py +107 -0
  11. pythonclaw/core/llm/base.py +26 -0
  12. pythonclaw/core/llm/gemini_client.py +139 -0
  13. pythonclaw/core/llm/openai_compatible.py +39 -0
  14. pythonclaw/core/llm/response.py +57 -0
  15. pythonclaw/core/memory/manager.py +120 -0
  16. pythonclaw/core/memory/storage.py +164 -0
  17. pythonclaw/core/persistent_agent.py +103 -0
  18. pythonclaw/core/retrieval/__init__.py +6 -0
  19. pythonclaw/core/retrieval/chunker.py +78 -0
  20. pythonclaw/core/retrieval/dense.py +152 -0
  21. pythonclaw/core/retrieval/fusion.py +51 -0
  22. pythonclaw/core/retrieval/reranker.py +112 -0
  23. pythonclaw/core/retrieval/retriever.py +166 -0
  24. pythonclaw/core/retrieval/sparse.py +69 -0
  25. pythonclaw/core/session_store.py +269 -0
  26. pythonclaw/core/skill_loader.py +322 -0
  27. pythonclaw/core/skillhub.py +290 -0
  28. pythonclaw/core/tools.py +622 -0
  29. pythonclaw/core/utils.py +64 -0
  30. pythonclaw/daemon.py +221 -0
  31. pythonclaw/init.py +61 -0
  32. pythonclaw/main.py +489 -0
  33. pythonclaw/onboard.py +290 -0
  34. pythonclaw/scheduler/cron.py +310 -0
  35. pythonclaw/scheduler/heartbeat.py +178 -0
  36. pythonclaw/server.py +145 -0
  37. pythonclaw/session_manager.py +104 -0
  38. pythonclaw/templates/persona/demo_persona.md +2 -0
  39. pythonclaw/templates/skills/communication/CATEGORY.md +4 -0
  40. pythonclaw/templates/skills/communication/email/SKILL.md +54 -0
  41. pythonclaw/templates/skills/communication/email/__pycache__/send_email.cpython-311.pyc +0 -0
  42. pythonclaw/templates/skills/communication/email/send_email.py +88 -0
  43. pythonclaw/templates/skills/data/CATEGORY.md +4 -0
  44. pythonclaw/templates/skills/data/csv_analyzer/SKILL.md +51 -0
  45. pythonclaw/templates/skills/data/csv_analyzer/__pycache__/analyze.cpython-311.pyc +0 -0
  46. pythonclaw/templates/skills/data/csv_analyzer/analyze.py +138 -0
  47. pythonclaw/templates/skills/data/finance/SKILL.md +41 -0
  48. pythonclaw/templates/skills/data/finance/__pycache__/fetch_quote.cpython-311.pyc +0 -0
  49. pythonclaw/templates/skills/data/finance/fetch_quote.py +118 -0
  50. pythonclaw/templates/skills/data/news/SKILL.md +39 -0
  51. pythonclaw/templates/skills/data/news/__pycache__/search_news.cpython-311.pyc +0 -0
  52. pythonclaw/templates/skills/data/news/search_news.py +57 -0
  53. pythonclaw/templates/skills/data/pdf_reader/SKILL.md +40 -0
  54. pythonclaw/templates/skills/data/pdf_reader/__pycache__/read_pdf.cpython-311.pyc +0 -0
  55. pythonclaw/templates/skills/data/pdf_reader/read_pdf.py +113 -0
  56. pythonclaw/templates/skills/data/scraper/SKILL.md +39 -0
  57. pythonclaw/templates/skills/data/scraper/__pycache__/scrape.cpython-311.pyc +0 -0
  58. pythonclaw/templates/skills/data/scraper/scrape.py +92 -0
  59. pythonclaw/templates/skills/data/weather/SKILL.md +42 -0
  60. pythonclaw/templates/skills/data/weather/__pycache__/weather.cpython-311.pyc +0 -0
  61. pythonclaw/templates/skills/data/weather/weather.py +142 -0
  62. pythonclaw/templates/skills/data/youtube/SKILL.md +43 -0
  63. pythonclaw/templates/skills/data/youtube/__pycache__/youtube_info.cpython-311.pyc +0 -0
  64. pythonclaw/templates/skills/data/youtube/youtube_info.py +167 -0
  65. pythonclaw/templates/skills/dev/CATEGORY.md +4 -0
  66. pythonclaw/templates/skills/dev/code_runner/SKILL.md +46 -0
  67. pythonclaw/templates/skills/dev/code_runner/__pycache__/run_code.cpython-311.pyc +0 -0
  68. pythonclaw/templates/skills/dev/code_runner/run_code.py +117 -0
  69. pythonclaw/templates/skills/dev/github/SKILL.md +52 -0
  70. pythonclaw/templates/skills/dev/github/__pycache__/gh.cpython-311.pyc +0 -0
  71. pythonclaw/templates/skills/dev/github/gh.py +165 -0
  72. pythonclaw/templates/skills/dev/http_request/SKILL.md +40 -0
  73. pythonclaw/templates/skills/dev/http_request/__pycache__/request.cpython-311.pyc +0 -0
  74. pythonclaw/templates/skills/dev/http_request/request.py +90 -0
  75. pythonclaw/templates/skills/google/CATEGORY.md +4 -0
  76. pythonclaw/templates/skills/google/workspace/SKILL.md +98 -0
  77. pythonclaw/templates/skills/google/workspace/check_setup.sh +52 -0
  78. pythonclaw/templates/skills/meta/CATEGORY.md +4 -0
  79. pythonclaw/templates/skills/meta/skill_creator/SKILL.md +151 -0
  80. pythonclaw/templates/skills/system/CATEGORY.md +4 -0
  81. pythonclaw/templates/skills/system/change_persona/SKILL.md +41 -0
  82. pythonclaw/templates/skills/system/change_setting/SKILL.md +65 -0
  83. pythonclaw/templates/skills/system/change_setting/__pycache__/update_config.cpython-311.pyc +0 -0
  84. pythonclaw/templates/skills/system/change_setting/update_config.py +129 -0
  85. pythonclaw/templates/skills/system/change_soul/SKILL.md +41 -0
  86. pythonclaw/templates/skills/system/onboarding/SKILL.md +63 -0
  87. pythonclaw/templates/skills/system/onboarding/__pycache__/write_identity.cpython-311.pyc +0 -0
  88. pythonclaw/templates/skills/system/onboarding/write_identity.py +218 -0
  89. pythonclaw/templates/skills/system/random/SKILL.md +33 -0
  90. pythonclaw/templates/skills/system/random/__pycache__/random_util.cpython-311.pyc +0 -0
  91. pythonclaw/templates/skills/system/random/random_util.py +45 -0
  92. pythonclaw/templates/skills/system/time/SKILL.md +33 -0
  93. pythonclaw/templates/skills/system/time/__pycache__/time_util.cpython-311.pyc +0 -0
  94. pythonclaw/templates/skills/system/time/time_util.py +81 -0
  95. pythonclaw/templates/skills/text/CATEGORY.md +4 -0
  96. pythonclaw/templates/skills/text/translator/SKILL.md +47 -0
  97. pythonclaw/templates/skills/text/translator/__pycache__/translate.cpython-311.pyc +0 -0
  98. pythonclaw/templates/skills/text/translator/translate.py +66 -0
  99. pythonclaw/templates/skills/web/CATEGORY.md +4 -0
  100. pythonclaw/templates/skills/web/tavily/SKILL.md +61 -0
  101. pythonclaw/templates/soul/SOUL.md +54 -0
  102. pythonclaw/web/__init__.py +1 -0
  103. pythonclaw/web/app.py +585 -0
  104. pythonclaw/web/static/favicon.png +0 -0
  105. pythonclaw/web/static/index.html +1318 -0
  106. pythonclaw/web/static/logo.png +0 -0
  107. pythonclaw-0.2.0.dist-info/METADATA +410 -0
  108. pythonclaw-0.2.0.dist-info/RECORD +112 -0
  109. pythonclaw-0.2.0.dist-info/WHEEL +5 -0
  110. pythonclaw-0.2.0.dist-info/entry_points.txt +2 -0
  111. pythonclaw-0.2.0.dist-info/licenses/LICENSE +21 -0
  112. pythonclaw-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: change_setting
3
+ description: >
4
+ Modify pythonclaw.json configuration at runtime. Use when the user wants
5
+ to set API keys, tokens, change LLM provider, adjust web port, or
6
+ update any configuration value.
7
+ ---
8
+
9
+ ## Instructions
10
+
11
+ Read and modify the project configuration file `pythonclaw.json`.
12
+
13
+ ### When to Use
14
+
15
+ - User says "set my API key to ...", "change provider to ...",
16
+ "update my token", "configure email", etc.
17
+ - User wants to change any runtime setting without editing files manually
18
+
19
+ ### How to Use
20
+
21
+ 1. Read the current config:
22
+ ```bash
23
+ python {skill_path}/update_config.py --show
24
+ ```
25
+
26
+ 2. Update a specific value using dot-notation for the key path:
27
+ ```bash
28
+ python {skill_path}/update_config.py --set "llm.deepseek.apiKey" "sk-xxx"
29
+ ```
30
+
31
+ Examples:
32
+ ```bash
33
+ # Change LLM provider
34
+ python {skill_path}/update_config.py --set "llm.provider" "claude"
35
+
36
+ # Set Tavily API key
37
+ python {skill_path}/update_config.py --set "tavily.apiKey" "tvly-xxx"
38
+
39
+ # Set Telegram token
40
+ python {skill_path}/update_config.py --set "channels.telegram.token" "123:ABC"
41
+
42
+ # Set GitHub token
43
+ python {skill_path}/update_config.py --set "skills.github.token" "ghp_xxx"
44
+
45
+ # Change web port
46
+ python {skill_path}/update_config.py --set "web.port" "8080"
47
+
48
+ # Set email credentials
49
+ python {skill_path}/update_config.py --set "skills.email.senderEmail" "me@gmail.com"
50
+ python {skill_path}/update_config.py --set "skills.email.senderPassword" "app-password"
51
+ ```
52
+
53
+ 3. After updating, tell the user the change was saved and whether
54
+ a restart is needed to take effect.
55
+
56
+ ### Security Notes
57
+
58
+ - When displaying config, API keys / passwords / tokens are masked
59
+ - Only `pythonclaw.json` is modified — no other files
60
+
61
+ ## Resources
62
+
63
+ | File | Description |
64
+ |------|-------------|
65
+ | `update_config.py` | CLI tool to read and update pythonclaw.json |
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env python3
2
+ """Read and update pythonclaw.json configuration values."""
3
+
4
+ import argparse
5
+ import json
6
+ import os
7
+ import re
8
+ import sys
9
+
10
+ CONFIG_FILE = "pythonclaw.json"
11
+
12
+ SENSITIVE_PATTERNS = re.compile(
13
+ r"(apikey|api_key|token|password|secret)", re.IGNORECASE
14
+ )
15
+
16
+
17
+ def _load_config() -> dict:
18
+ if not os.path.exists(CONFIG_FILE):
19
+ print(f"Error: {CONFIG_FILE} not found in {os.getcwd()}", file=sys.stderr)
20
+ sys.exit(1)
21
+ with open(CONFIG_FILE, "r", encoding="utf-8") as f:
22
+ return json.load(f)
23
+
24
+
25
+ def _save_config(cfg: dict) -> None:
26
+ with open(CONFIG_FILE, "w", encoding="utf-8") as f:
27
+ json.dump(cfg, f, indent=2, ensure_ascii=False)
28
+ f.write("\n")
29
+
30
+
31
+ def _mask_value(key: str, value) -> str:
32
+ if isinstance(value, str) and SENSITIVE_PATTERNS.search(key) and value:
33
+ if len(value) <= 8:
34
+ return "****"
35
+ return value[:4] + "****" + value[-4:]
36
+ return value
37
+
38
+
39
+ def _mask_dict(d: dict, parent_key: str = "") -> dict:
40
+ masked = {}
41
+ for k, v in d.items():
42
+ full_key = f"{parent_key}.{k}" if parent_key else k
43
+ if isinstance(v, dict):
44
+ masked[k] = _mask_dict(v, full_key)
45
+ else:
46
+ masked[k] = _mask_value(full_key, v)
47
+ return masked
48
+
49
+
50
+ def _get_nested(d: dict, key_path: str):
51
+ keys = key_path.split(".")
52
+ current = d
53
+ for k in keys:
54
+ if not isinstance(current, dict) or k not in current:
55
+ return None
56
+ current = current[k]
57
+ return current
58
+
59
+
60
+ def _set_nested(d: dict, key_path: str, value) -> None:
61
+ keys = key_path.split(".")
62
+ current = d
63
+ for k in keys[:-1]:
64
+ if k not in current or not isinstance(current[k], dict):
65
+ current[k] = {}
66
+ current = current[k]
67
+
68
+ old_value = current.get(keys[-1])
69
+ if isinstance(old_value, int):
70
+ try:
71
+ value = int(value)
72
+ except (ValueError, TypeError):
73
+ pass
74
+ elif isinstance(old_value, bool) or (isinstance(value, str) and value.lower() in ("true", "false")):
75
+ value = value.lower() in ("true", "1", "yes") if isinstance(value, str) else value
76
+ elif isinstance(old_value, list) and isinstance(value, str):
77
+ try:
78
+ value = json.loads(value)
79
+ except json.JSONDecodeError:
80
+ value = [v.strip() for v in value.split(",") if v.strip()]
81
+
82
+ current[keys[-1]] = value
83
+
84
+
85
+ def show_config():
86
+ cfg = _load_config()
87
+ masked = _mask_dict(cfg)
88
+ print(json.dumps(masked, indent=2, ensure_ascii=False))
89
+
90
+
91
+ def get_value(key_path: str):
92
+ cfg = _load_config()
93
+ val = _get_nested(cfg, key_path)
94
+ if val is None:
95
+ print(f"Key '{key_path}' not found")
96
+ sys.exit(1)
97
+ masked = _mask_value(key_path, val) if isinstance(val, str) else val
98
+ if isinstance(masked, dict):
99
+ masked = _mask_dict(masked, key_path)
100
+ print(json.dumps(masked, indent=2, ensure_ascii=False) if isinstance(masked, (dict, list)) else masked)
101
+
102
+
103
+ def set_value(key_path: str, value: str):
104
+ cfg = _load_config()
105
+ _set_nested(cfg, key_path, value)
106
+ _save_config(cfg)
107
+ display_val = _mask_value(key_path, value)
108
+ print(f"Updated {key_path} = {display_val}")
109
+
110
+
111
+ def main():
112
+ parser = argparse.ArgumentParser(description="Manage pythonclaw.json")
113
+ parser.add_argument("--show", action="store_true", help="Show all config (masked)")
114
+ parser.add_argument("--get", metavar="KEY", help="Get a config value by dot-path")
115
+ parser.add_argument("--set", nargs=2, metavar=("KEY", "VALUE"), help="Set a config value")
116
+ args = parser.parse_args()
117
+
118
+ if args.show:
119
+ show_config()
120
+ elif args.get:
121
+ get_value(args.get)
122
+ elif args.set:
123
+ set_value(args.set[0], args.set[1])
124
+ else:
125
+ parser.print_help()
126
+
127
+
128
+ if __name__ == "__main__":
129
+ main()
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: change_soul
3
+ description: >
4
+ Modify the agent's core identity (soul.md). Use when the user wants to
5
+ change their name, the agent's core values, language preference, or
6
+ fundamental behavior.
7
+ ---
8
+
9
+ ## Instructions
10
+
11
+ Modify the agent's soul (core identity) file at `context/soul/SOUL.md`.
12
+
13
+ ### When to Use
14
+
15
+ - User says "change my name to ...", "call me ...", "change language to ..."
16
+ - User wants to modify core values or ethical boundaries
17
+ - User asks to update fundamental agent behavior
18
+
19
+ ### How to Use
20
+
21
+ 1. Ask the user what they want to change
22
+ 2. Read the current soul file:
23
+ ```
24
+ read_file("context/soul/SOUL.md")
25
+ ```
26
+ 3. Modify the relevant section and write it back:
27
+ ```
28
+ write_file("context/soul/SOUL.md", "...updated content...")
29
+ ```
30
+ 4. Tell the user: "Soul updated. Use `/clear` to apply the changes in
31
+ a fresh conversation, or they will take effect on next restart."
32
+
33
+ ### Important
34
+
35
+ - Preserve the overall structure of SOUL.md
36
+ - Only change the specific section the user asked about
37
+ - Keep core ethical boundaries intact — never remove safety guidelines
38
+
39
+ ## Resources
40
+
41
+ This skill uses the built-in `read_file` and `write_file` tools directly.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: onboarding
3
+ description: >
4
+ First-time setup wizard. Asks the user their preferred name, desired
5
+ agent personality, and focus area, then writes soul.md and persona.md.
6
+ Use when the agent starts for the first time with no soul/persona
7
+ configured, or when the user asks to reconfigure their agent identity.
8
+ ---
9
+
10
+ ## Instructions
11
+
12
+ Guide the user through initial agent setup with a friendly conversation.
13
+
14
+ ### When to Use
15
+
16
+ - Agent starts with empty/default soul.md and persona.md
17
+ - User says "reconfigure", "setup", "change my agent", etc.
18
+
19
+ ### Onboarding Flow
20
+
21
+ Ask these questions **one at a time** in a friendly, conversational tone:
22
+
23
+ 1. **Name**: "What should I call you?"
24
+ 2. **Personality**: "What kind of personality would you like me to have?
25
+ For example: professional & concise, friendly & casual, humorous,
26
+ formal, encouraging, etc."
27
+ 3. **Focus area**: "What area would you like me to focus on?
28
+ For example: software development, finance & investing, research,
29
+ daily assistant, creative writing, etc."
30
+ 4. **Language preference**: "What language do you prefer I respond in?
31
+ (English, Chinese, etc.)"
32
+
33
+ ### After Collecting Answers
34
+
35
+ Use `run_command` to write the files:
36
+
37
+ **Write soul.md:**
38
+ ```bash
39
+ python {skill_path}/write_identity.py --type soul \
40
+ --user-name "NAME" \
41
+ --personality "PERSONALITY" \
42
+ --focus "FOCUS" \
43
+ --language "LANGUAGE"
44
+ ```
45
+
46
+ **Write persona.md:**
47
+ ```bash
48
+ python {skill_path}/write_identity.py --type persona \
49
+ --user-name "NAME" \
50
+ --personality "PERSONALITY" \
51
+ --focus "FOCUS" \
52
+ --language "LANGUAGE"
53
+ ```
54
+
55
+ After writing, tell the user: "Setup complete! Your preferences have been
56
+ saved. Use `/clear` to start a fresh conversation with your new identity,
57
+ or just keep chatting."
58
+
59
+ ## Resources
60
+
61
+ | File | Description |
62
+ |------|-------------|
63
+ | `write_identity.py` | Generates soul.md and persona.md files |
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env python3
2
+ """Generate soul.md or persona.md from user preferences."""
3
+
4
+ import argparse
5
+ import os
6
+
7
+ SOUL_TEMPLATE = """# PythonClaw — Soul
8
+
9
+ You are a PythonClaw agent — an autonomous AI assistant.
10
+
11
+ This document defines your core identity — the values, principles, and character
12
+ that remain constant regardless of which persona or role you are playing.
13
+
14
+ ## User
15
+
16
+ - The user's name is **{user_name}**.
17
+ - Always address them by name when appropriate.
18
+ - Preferred language: **{language}**. Always respond in this language unless
19
+ the user explicitly switches.
20
+
21
+ ## Core Values
22
+
23
+ - **Honesty**: You never fabricate facts. When uncertain, say so clearly.
24
+ - **Helpfulness**: Your primary purpose is to genuinely help {user_name}.
25
+ Look for the real need behind a request.
26
+ - **Respect**: Treat {user_name} with dignity. Adapt your communication
27
+ style to their preferences.
28
+ - **Curiosity**: You are genuinely interested in problems. Ask clarifying
29
+ questions when needed.
30
+ - **Responsibility**: Think before you act. Consider side-effects and
31
+ prefer reversible actions.
32
+
33
+ ## Ethical Boundaries
34
+
35
+ - Never help with anything that could cause serious harm.
36
+ - Never deceive or manipulate.
37
+ - If asked to do something unethical, explain why and offer alternatives.
38
+
39
+ ## Emotional Character
40
+
41
+ {personality_description}
42
+
43
+ ## Relationship with {user_name}
44
+
45
+ Remember that {user_name} has goals and context beyond this conversation.
46
+ Treat their time as valuable. Keep responses concise, expanding only when
47
+ depth is genuinely needed.
48
+
49
+ ---
50
+ *This soul file is loaded at agent startup.*
51
+ """
52
+
53
+ PERSONA_TEMPLATE = """# PythonClaw — Persona
54
+
55
+ ## Role
56
+
57
+ You are {user_name}'s personal AI assistant, specializing in **{focus}**.
58
+
59
+ ## Personality
60
+
61
+ {personality_traits}
62
+
63
+ ## Focus Area
64
+
65
+ Your primary expertise and focus is: **{focus}**.
66
+
67
+ When {user_name} asks questions in this area, provide deep, detailed,
68
+ expert-level answers. For topics outside your focus, still help but
69
+ mention when something is outside your specialty.
70
+
71
+ ## Communication Style
72
+
73
+ - Respond in **{language}**
74
+ - {style_notes}
75
+ - Use examples and analogies when explaining complex topics
76
+ - Be proactive — suggest relevant follow-ups and related insights
77
+
78
+ ## Specialization Guidelines
79
+
80
+ {focus_guidelines}
81
+ """
82
+
83
+
84
+ def _personality_description(personality: str) -> str:
85
+ p = personality.lower()
86
+ if any(w in p for w in ("professional", "formal", "concise")):
87
+ return (
88
+ "You are professional, measured, and precise. You get straight "
89
+ "to the point without unnecessary preamble. Your tone is "
90
+ "respectful and business-like."
91
+ )
92
+ if any(w in p for w in ("friendly", "casual", "warm")):
93
+ return (
94
+ "You are warm, approachable, and conversational. You use a "
95
+ "natural, relaxed tone while remaining helpful and accurate. "
96
+ "You occasionally use light humor when appropriate."
97
+ )
98
+ if any(w in p for w in ("humor", "funny", "witty")):
99
+ return (
100
+ "You have a sharp wit and enjoy making conversations engaging "
101
+ "with well-timed humor. You balance entertainment with "
102
+ "genuinely useful information."
103
+ )
104
+ if any(w in p for w in ("encouraging", "supportive", "coach")):
105
+ return (
106
+ "You are encouraging and supportive, like a patient mentor. "
107
+ "You celebrate progress, provide constructive feedback, and "
108
+ "help build confidence."
109
+ )
110
+ return (
111
+ f"Your personality is: {personality}. "
112
+ "You embody these traits consistently in every interaction."
113
+ )
114
+
115
+
116
+ def _personality_traits(personality: str) -> str:
117
+ return f"- Core trait: **{personality}**\n- Consistent across all interactions"
118
+
119
+
120
+ def _style_notes(personality: str) -> str:
121
+ p = personality.lower()
122
+ if "concise" in p or "professional" in p:
123
+ return "Keep answers brief and structured with bullet points"
124
+ if "casual" in p or "friendly" in p:
125
+ return "Use a conversational, natural tone"
126
+ if "humor" in p or "funny" in p:
127
+ return "Include wit and humor while staying informative"
128
+ return "Adapt your tone to the context of each conversation"
129
+
130
+
131
+ def _focus_guidelines(focus: str) -> str:
132
+ f = focus.lower()
133
+ if any(w in f for w in ("software", "dev", "coding", "programming")):
134
+ return (
135
+ "- Provide clean, production-quality code examples\n"
136
+ "- Explain architectural decisions and trade-offs\n"
137
+ "- Follow best practices and current industry standards\n"
138
+ "- Consider security, performance, and maintainability"
139
+ )
140
+ if any(w in f for w in ("finance", "invest", "stock", "trading")):
141
+ return (
142
+ "- Always include disclaimers that you are not a financial advisor\n"
143
+ "- Use real data from web searches when available\n"
144
+ "- Consider risk factors and diversification\n"
145
+ "- Present bull and bear cases for balanced analysis"
146
+ )
147
+ if any(w in f for w in ("research", "academic", "science")):
148
+ return (
149
+ "- Cite sources and provide references when possible\n"
150
+ "- Distinguish between established facts and hypotheses\n"
151
+ "- Use precise, academic language when appropriate\n"
152
+ "- Encourage critical thinking and methodology"
153
+ )
154
+ if any(w in f for w in ("daily", "assistant", "productivity")):
155
+ return (
156
+ "- Be proactive with reminders and suggestions\n"
157
+ "- Help organize tasks and priorities\n"
158
+ "- Provide practical, actionable advice\n"
159
+ "- Learn user patterns and preferences over time"
160
+ )
161
+ if any(w in f for w in ("creative", "writing", "content")):
162
+ return (
163
+ "- Offer diverse creative perspectives and styles\n"
164
+ "- Provide constructive feedback on creative work\n"
165
+ "- Help brainstorm and develop ideas\n"
166
+ "- Balance originality with user's vision"
167
+ )
168
+ return f"- Focus deeply on: {focus}\n- Provide expert-level guidance in this area"
169
+
170
+
171
+ def write_soul(user_name: str, personality: str, focus: str, language: str) -> str:
172
+ content = SOUL_TEMPLATE.format(
173
+ user_name=user_name,
174
+ language=language,
175
+ personality_description=_personality_description(personality),
176
+ )
177
+ path = os.path.join("context", "soul", "SOUL.md")
178
+ os.makedirs(os.path.dirname(path), exist_ok=True)
179
+ with open(path, "w", encoding="utf-8") as f:
180
+ f.write(content.strip() + "\n")
181
+ return path
182
+
183
+
184
+ def write_persona(user_name: str, personality: str, focus: str, language: str) -> str:
185
+ content = PERSONA_TEMPLATE.format(
186
+ user_name=user_name,
187
+ focus=focus,
188
+ language=language,
189
+ personality_traits=_personality_traits(personality),
190
+ style_notes=_style_notes(personality),
191
+ focus_guidelines=_focus_guidelines(focus),
192
+ )
193
+ path = os.path.join("context", "persona", "persona.md")
194
+ os.makedirs(os.path.dirname(path), exist_ok=True)
195
+ with open(path, "w", encoding="utf-8") as f:
196
+ f.write(content.strip() + "\n")
197
+ return path
198
+
199
+
200
+ def main():
201
+ parser = argparse.ArgumentParser(description="Generate soul.md or persona.md")
202
+ parser.add_argument("--type", required=True, choices=["soul", "persona"])
203
+ parser.add_argument("--user-name", required=True)
204
+ parser.add_argument("--personality", required=True)
205
+ parser.add_argument("--focus", required=True)
206
+ parser.add_argument("--language", default="English")
207
+ args = parser.parse_args()
208
+
209
+ if args.type == "soul":
210
+ path = write_soul(args.user_name, args.personality, args.focus, args.language)
211
+ else:
212
+ path = write_persona(args.user_name, args.personality, args.focus, args.language)
213
+
214
+ print(f"Written: {path}")
215
+
216
+
217
+ if __name__ == "__main__":
218
+ main()
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: system_random
3
+ description: >
4
+ Generate random numbers, UUIDs, passwords, or pick random items from a list.
5
+ Use when the user needs any kind of randomness.
6
+ ---
7
+
8
+ ## Instructions
9
+
10
+ Generate random values using the bundled script.
11
+
12
+ ```bash
13
+ # Random integer in range
14
+ python {skill_path}/random_util.py --int 1 100
15
+
16
+ # Random float in range
17
+ python {skill_path}/random_util.py --float 0.0 1.0
18
+
19
+ # UUID
20
+ python {skill_path}/random_util.py --uuid
21
+
22
+ # Random password (default 16 chars)
23
+ python {skill_path}/random_util.py --password 20
24
+
25
+ # Pick N random items from a comma-separated list
26
+ python {skill_path}/random_util.py --choice "apple,banana,cherry,date" --count 2
27
+ ```
28
+
29
+ ## Resources
30
+
31
+ | File | Description |
32
+ |------|-------------|
33
+ | `random_util.py` | CLI tool for generating random values |
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python3
2
+ """Generate random numbers, UUIDs, passwords, or pick random items."""
3
+
4
+ import argparse
5
+ import random
6
+ import string
7
+ import uuid
8
+
9
+
10
+ def main():
11
+ parser = argparse.ArgumentParser(description="Random value generator")
12
+ parser.add_argument("--int", nargs=2, type=int, metavar=("MIN", "MAX"),
13
+ help="Random integer in [MIN, MAX]")
14
+ parser.add_argument("--float", nargs=2, type=float, metavar=("MIN", "MAX"),
15
+ help="Random float in [MIN, MAX]")
16
+ parser.add_argument("--uuid", action="store_true", help="Generate a UUID4")
17
+ parser.add_argument("--password", type=int, metavar="LENGTH",
18
+ help="Generate a random password of given length")
19
+ parser.add_argument("--choice", type=str,
20
+ help="Comma-separated list to pick from")
21
+ parser.add_argument("--count", type=int, default=1,
22
+ help="Number of items to pick (for --choice)")
23
+ args = parser.parse_args()
24
+
25
+ if args.int:
26
+ print(random.randint(args.int[0], args.int[1]))
27
+ elif args.float:
28
+ print(round(random.uniform(args.float[0], args.float[1]), 6))
29
+ elif args.uuid:
30
+ print(uuid.uuid4())
31
+ elif args.password is not None:
32
+ length = max(args.password, 4)
33
+ chars = string.ascii_letters + string.digits + string.punctuation
34
+ print("".join(random.choices(chars, k=length)))
35
+ elif args.choice:
36
+ items = [x.strip() for x in args.choice.split(",") if x.strip()]
37
+ count = min(args.count, len(items))
38
+ picks = random.sample(items, count)
39
+ print(", ".join(picks))
40
+ else:
41
+ parser.print_help()
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: system_time
3
+ description: >
4
+ Get the current date, time, timezone, or convert between timezones.
5
+ Use when the user asks what time it is, needs a date, or timezone conversion.
6
+ ---
7
+
8
+ ## Instructions
9
+
10
+ Get time information using the bundled script.
11
+
12
+ ```bash
13
+ # Current local time
14
+ python {skill_path}/time_util.py
15
+
16
+ # Time in a specific timezone
17
+ python {skill_path}/time_util.py --tz "America/New_York"
18
+
19
+ # List common timezone names
20
+ python {skill_path}/time_util.py --list-tz
21
+
22
+ # Unix timestamp
23
+ python {skill_path}/time_util.py --unix
24
+
25
+ # Convert a time between timezones
26
+ python {skill_path}/time_util.py --convert "2026-03-01 14:30" --from-tz "Asia/Shanghai" --to-tz "America/New_York"
27
+ ```
28
+
29
+ ## Resources
30
+
31
+ | File | Description |
32
+ |------|-------------|
33
+ | `time_util.py` | CLI tool for time queries and conversions |