@xiaotianxt/skills 0.1.0

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 (120) hide show
  1. package/EXCLUDED.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -0
  4. package/SECURITY.md +23 -0
  5. package/SOURCES.md +45 -0
  6. package/bin/skills.mjs +241 -0
  7. package/package.json +38 -0
  8. package/skills/1password/SKILL.md +94 -0
  9. package/skills/1password/agents/openai.yaml +4 -0
  10. package/skills/1password/references/item-management.md +80 -0
  11. package/skills/1password/references/op-cli.md +107 -0
  12. package/skills/apple-calendar-event/SKILL.md +81 -0
  13. package/skills/apple-calendar-event/agents/openai.yaml +4 -0
  14. package/skills/apple-calendar-event/scripts/calendar_audit.py +201 -0
  15. package/skills/apple-calendar-event/scripts/calendar_event.py +164 -0
  16. package/skills/bro-browser/SKILL.md +118 -0
  17. package/skills/bro-browser/agents/openai.yaml +4 -0
  18. package/skills/bro-browser/references/tool-map.md +102 -0
  19. package/skills/bro-browser/references/workflows.md +146 -0
  20. package/skills/bro-browser/scripts/bro-call.mjs +189 -0
  21. package/skills/calendar/SKILL.md +182 -0
  22. package/skills/calendar/agents/openai.yaml +4 -0
  23. package/skills/calendar/references/operations.md +255 -0
  24. package/skills/calendar/scripts/calendar_list_review.py +157 -0
  25. package/skills/calendar/scripts/event_dedupe_preview.py +155 -0
  26. package/skills/canvas/SKILL.md +70 -0
  27. package/skills/canvas/agents/openai.yaml +4 -0
  28. package/skills/canvas/references/canvas-api.md +76 -0
  29. package/skills/course-exam-review-planner/SKILL.md +127 -0
  30. package/skills/cx/SKILL.md +25 -0
  31. package/skills/gh-fix-ci/LICENSE.txt +201 -0
  32. package/skills/gh-fix-ci/SKILL.md +81 -0
  33. package/skills/gh-fix-ci/agents/openai.yaml +6 -0
  34. package/skills/gh-fix-ci/assets/github-small.svg +3 -0
  35. package/skills/gh-fix-ci/assets/github.png +0 -0
  36. package/skills/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
  37. package/skills/gh-review-workflow/SKILL.md +61 -0
  38. package/skills/gh-review-workflow/agents/openai.yaml +4 -0
  39. package/skills/gh-review-workflow/references/workflow.md +48 -0
  40. package/skills/gh-review-workflow/scripts/fetch_review_state.py +222 -0
  41. package/skills/gh-review-workflow/scripts/resolve_review_threads.py +83 -0
  42. package/skills/github/SKILL.md +74 -0
  43. package/skills/github/agents/openai.yaml +6 -0
  44. package/skills/github/assets/github-small.svg +3 -0
  45. package/skills/github/assets/github.png +0 -0
  46. package/skills/gws-calendar/SKILL.md +126 -0
  47. package/skills/gws-calendar-agenda/SKILL.md +52 -0
  48. package/skills/gws-calendar-insert/SKILL.md +66 -0
  49. package/skills/gws-docs/SKILL.md +48 -0
  50. package/skills/gws-docs-write/SKILL.md +49 -0
  51. package/skills/gws-drive/SKILL.md +137 -0
  52. package/skills/gws-drive-upload/SKILL.md +52 -0
  53. package/skills/gws-gmail/SKILL.md +62 -0
  54. package/skills/gws-gmail-forward/SKILL.md +55 -0
  55. package/skills/gws-gmail-reply/SKILL.md +58 -0
  56. package/skills/gws-gmail-reply-all/SKILL.md +62 -0
  57. package/skills/gws-gmail-send/SKILL.md +57 -0
  58. package/skills/gws-gmail-triage/SKILL.md +50 -0
  59. package/skills/gws-gmail-watch/SKILL.md +58 -0
  60. package/skills/gws-shared/SKILL.md +27 -0
  61. package/skills/helium-browser-mcp/SKILL.md +137 -0
  62. package/skills/helium-browser-mcp/agents/openai.yaml +4 -0
  63. package/skills/helium-browser-mcp/scripts/obmcp.mjs +92 -0
  64. package/skills/helium-browser-mcp/scripts/openbrowsermcp-stdio-proxy.mjs +170 -0
  65. package/skills/learn/SKILL.md +122 -0
  66. package/skills/learn/agents/openai.yaml +7 -0
  67. package/skills/learn/assets/AGENTS.template.md +33 -0
  68. package/skills/learn/assets/errorlog.template.typ +61 -0
  69. package/skills/learn/assets/reading-sequence.template.md +23 -0
  70. package/skills/learn/assets/source-index.template.md +17 -0
  71. package/skills/learn/assets/tasklog.template.typ +57 -0
  72. package/skills/learn/assets/workbook.template.typ +60 -0
  73. package/skills/learn/references/learning-science.md +103 -0
  74. package/skills/learn/scripts/init_learning_workspace.py +70 -0
  75. package/skills/macos-messages/SKILL.md +258 -0
  76. package/skills/memory/SKILL.md +33 -0
  77. package/skills/memory/codex.md +186 -0
  78. package/skills/memory/opencode.md +164 -0
  79. package/skills/mimestreamctl/SKILL.md +170 -0
  80. package/skills/mimestreamctl/agents/openai.yaml +4 -0
  81. package/skills/mimestreamctl/scripts/mimestreamctl +33 -0
  82. package/skills/mon/SKILL.md +51 -0
  83. package/skills/mon/scripts/mon_spend_review.py +458 -0
  84. package/skills/ocr/SKILL.md +136 -0
  85. package/skills/ocr/agents/openai.yaml +4 -0
  86. package/skills/ocr/references/local-ocr-best-practices.md +297 -0
  87. package/skills/ocr/references/mineru-api.md +159 -0
  88. package/skills/ocr/scripts/ocr-router +22 -0
  89. package/skills/ocr/scripts/ocr_router.py +741 -0
  90. package/skills/panopto-mp4-bulk-download/SKILL.md +57 -0
  91. package/skills/panopto-mp4-bulk-download/agents/openai.yaml +4 -0
  92. package/skills/panopto-mp4-bulk-download/references/url-patterns.md +26 -0
  93. package/skills/panopto-mp4-bulk-download/scripts/panopto_bulk_mp4.sh +213 -0
  94. package/skills/rust-systems-style/SKILL.md +109 -0
  95. package/skills/rust-systems-style/agents/openai.yaml +4 -0
  96. package/skills/rust-systems-style/references/rust-review-checklist.md +77 -0
  97. package/skills/rust-systems-style/references/style-sources.md +68 -0
  98. package/skills/ship-ai-native-cli/SKILL.md +76 -0
  99. package/skills/ship-ai-native-cli/agents/openai.yaml +4 -0
  100. package/skills/ship-ai-native-cli/references/case-notes.md +83 -0
  101. package/skills/ship-ai-native-cli/references/product-method.md +82 -0
  102. package/skills/ship-ai-native-cli/references/release-checklist.md +147 -0
  103. package/skills/ship-ai-native-cli/references/rust-cli-shape.md +111 -0
  104. package/skills/telegram-mtproto-session/SKILL.md +125 -0
  105. package/skills/telegram-mtproto-session/agents/openai.yaml +4 -0
  106. package/skills/telegram-mtproto-session/scripts/telegram_session.py +687 -0
  107. package/skills/tg/SKILL.md +173 -0
  108. package/skills/things3-manager/SKILL.md +116 -0
  109. package/skills/things3-manager/scripts/things +42 -0
  110. package/skills/things3-manager/scripts/things_cli.py +514 -0
  111. package/skills/web-artifacts-builder/LICENSE.txt +202 -0
  112. package/skills/web-artifacts-builder/SKILL.md +74 -0
  113. package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  114. package/skills/web-artifacts-builder/scripts/init-artifact.sh +379 -0
  115. package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  116. package/skills/yeet/LICENSE.txt +201 -0
  117. package/skills/yeet/SKILL.md +71 -0
  118. package/skills/yeet/agents/openai.yaml +6 -0
  119. package/skills/yeet/assets/yeet-small.svg +3 -0
  120. package/skills/yeet/assets/yeet.png +0 -0
@@ -0,0 +1,61 @@
1
+ #set page(paper: "a4", margin: 1.8cm)
2
+ #set text(font: ("PingFang SC", "Arial Unicode MS"), size: 10.5pt, lang: "zh")
3
+ #set par(justify: true, leading: 0.62em)
4
+
5
+ = {TOPIC} 错题与误区日志
6
+
7
+ 用途:记录学习过程中暴露出的错误、模糊理解、概念混淆、迁移失败和关键盲点。这个文件决定后续复习顺序。
8
+
9
+ 记录规则:
10
+
11
+ - 只记录会影响后续理解或应用的错误,不记录每个小瑕疵。
12
+ - 保留学习者原始回答的意思,避免事后美化。
13
+ - 每条错误必须包含正确理解和下一次复习动作。
14
+ - 连续两次跨会话答对,才可以从 `review` 改为 `closed`。
15
+
16
+ == 错误分类
17
+
18
+ #table(
19
+ columns: (3.0cm, 1fr),
20
+ inset: 5pt,
21
+ align: left,
22
+ [类型], [说明],
23
+ [concept], [概念混淆。],
24
+ [source], [没有说清楚依据来自哪份资料,或脱离资料泛泛而谈。],
25
+ [application], [不能迁移到新场景。],
26
+ [boundary], [忽略边界条件或失败模式。],
27
+ [terminology], [术语不熟。],
28
+ [method], [解题方法、分析步骤或学习策略错误。],
29
+ [process], [学习流程问题,例如跳过提问或复盘。],
30
+ )
31
+
32
+ == 活跃错误表
33
+
34
+ #table(
35
+ columns: (1.1cm, 1.6cm, 1.7cm, 2.3cm, 1fr, 1fr, 2.0cm, 1.5cm),
36
+ inset: 3.6pt,
37
+ align: left,
38
+ [ID], [日期], [课程], [类型], [错误表现], [修正解释], [复习日期], [状态],
39
+ [E000], [{DATE}], [Plan], [process], [尚未开始正式问答。], [从 L001 开始,用验证问题暴露真实薄弱点。], [{DATE}], [open],
40
+ )
41
+
42
+ == 新错误记录模板
43
+
44
+ #table(
45
+ columns: (3.0cm, 1fr),
46
+ inset: 5pt,
47
+ align: left,
48
+ [字段], [内容],
49
+ [ID], [E001],
50
+ [日期], [YYYY-MM-DD],
51
+ [课程], [L???],
52
+ [类型], [concept / source / application / boundary / terminology / method / process],
53
+ [原问题], [助教提出的验证问题。],
54
+ [学习者回答], [保留学习者原意,避免事后美化。],
55
+ [错误表现], [具体指出错在哪里。],
56
+ [正确理解], [用简单语言修正。],
57
+ [为什么重要], [如果这个错误保留下来,会影响哪类理解、题目、项目或判断。],
58
+ [复习动作], [下一次如何验证是否真正掌握。],
59
+ [复习日期], [YYYY-MM-DD],
60
+ [状态], [open / review / closed],
61
+ )
@@ -0,0 +1,23 @@
1
+ # {TOPIC} Reading Sequence
2
+
3
+ Goal: {GOAL}
4
+
5
+ ## First Pass
6
+
7
+ ### L001: Establish the Goal and Map the Sources
8
+
9
+ Read:
10
+
11
+ - `materials/source-index.md`
12
+
13
+ Questions:
14
+
15
+ - What should the learner be able to do after this topic?
16
+ - Which sources are authoritative?
17
+ - What is the first small unit worth teaching?
18
+
19
+ Output:
20
+
21
+ - Update `tasklog.typ`.
22
+ - Ask verification questions before marking L001 done.
23
+
@@ -0,0 +1,17 @@
1
+ # {TOPIC} Source Index
2
+
3
+ Goal: {GOAL}
4
+
5
+ ## Source Table
6
+
7
+ | Priority | Source | Local Path / URL | Type | Use |
8
+ |---|---|---|---|---|
9
+ | must | TBD | TBD | book/course/doc/paper/video | Core source |
10
+
11
+ ## Collection Rules
12
+
13
+ - Prefer primary sources.
14
+ - Convert enough material into AI-readable text to teach accurately.
15
+ - Keep raw files and links; write original notes instead of copying long copyrighted text.
16
+ - Add sources as questions demand them; do not block learning on exhaustive collection.
17
+
@@ -0,0 +1,57 @@
1
+ #set page(paper: "a4", margin: 1.8cm)
2
+ #set text(font: ("PingFang SC", "Arial Unicode MS"), size: 10.5pt, lang: "zh")
3
+ #set par(justify: true, leading: 0.62em)
4
+
5
+ = {TOPIC} 学习任务日志
6
+
7
+ 目标:{GOAL}
8
+
9
+ 使用规则:
10
+
11
+ - 每次讲解后,助教必须提出验证问题。
12
+ - 学习者回答后,助教必须评分并更新本日志。
13
+ - 答错、含糊、只会背但不能应用的内容,必须同步写入 `errorlog.typ`。
14
+ - 状态使用 `todo`、`doing`、`review`、`done`。
15
+ - `done` 的条件是:能用自己的话解释,能应用到新场景,能指出常见误判。
16
+
17
+ == 评分规则
18
+
19
+ #table(
20
+ columns: (1.2cm, 3.2cm, 1fr),
21
+ inset: 5pt,
22
+ align: left,
23
+ [分数], [判定], [标准],
24
+ [0], [未掌握], [无法解释核心概念,或给出误导性答案。],
25
+ [1], [初步听懂], [能复述定义,但不能独立举例或应用。],
26
+ [2], [基本掌握], [能解释含义,并能应用到一个合理场景。],
27
+ [3], [可实战沟通], [能说明边界、误区、失败模式和下一步行动。],
28
+ )
29
+
30
+ == 学习计划
31
+
32
+ #table(
33
+ columns: (1.4cm, 4.2cm, 1.5cm, 1.4cm, 1fr),
34
+ inset: 4pt,
35
+ align: left,
36
+ [课], [主题], [优先级], [状态], [目标],
37
+ [L001], [建立问题和目标], [must], [doing], [明确学习目标、资料范围、输出标准和第一轮问题。],
38
+ )
39
+
40
+ == 讲解会话日志
41
+
42
+ #table(
43
+ columns: (2.2cm, 1.8cm, 1.8cm, 1.8cm, 1.7cm, 1fr),
44
+ inset: 4pt,
45
+ align: left,
46
+ [日期], [课程], [状态], [平均分], [错题数], [下一步],
47
+ [{DATE}], [Plan], [doing], [-], [0], [开始收集资料并建立第一轮学习顺序。],
48
+ )
49
+
50
+ == 验证问题模板
51
+
52
+ 1. 概念解释:请用自己的话解释本节最重要的概念。
53
+ 2. 来源依据:资料中哪一处支持这个说法?
54
+ 3. 应用场景:把概念应用到一个新例子。
55
+ 4. 误判识别:最容易犯的错误解释是什么?
56
+ 5. 下一步:如果证据不足,下一步应该读什么、问什么或做什么?
57
+
@@ -0,0 +1,60 @@
1
+ #set page(paper: "a4", margin: 1.8cm)
2
+ #set text(font: ("PingFang SC", "Arial Unicode MS"), size: 10.5pt, lang: "zh")
3
+ #set par(justify: true, leading: 0.62em)
4
+
5
+ = {TOPIC} 学习工作本
6
+
7
+ 目标:{GOAL}
8
+
9
+ 这个文件用于记录每节课的讲解结构、关键例子、验证问题和修正后的理解。错题本只记录错误;工作本记录学习过程。
10
+
11
+ == 使用方法
12
+
13
+ 每个小节按这个结构记录:
14
+
15
+ #enum[
16
+ Source:本节基于哪些资料。
17
+ ][
18
+ Map:本节知识地图。
19
+ ][
20
+ Explanation:完整讲解。
21
+ ][
22
+ Example:例题、业务场景、代码场景或案例。
23
+ ][
24
+ Questions:验证问题。
25
+ ][
26
+ Correction:回答后的修正说明。
27
+ ][
28
+ Drill:下一次复习或迁移练习。
29
+ ]
30
+
31
+ == L001:起步
32
+
33
+ === Source
34
+
35
+ 待补。
36
+
37
+ === Map
38
+
39
+ 待补。
40
+
41
+ === Explanation
42
+
43
+ 待补。
44
+
45
+ === Example
46
+
47
+ 待补。
48
+
49
+ === Questions
50
+
51
+ 待补。
52
+
53
+ === Correction
54
+
55
+ 待补。
56
+
57
+ === Drill
58
+
59
+ 待补。
60
+
@@ -0,0 +1,103 @@
1
+ # Learning Science Reference
2
+
3
+ Use these principles to design or adjust learning workflows.
4
+
5
+ ## Evidence-Backed Defaults
6
+
7
+ ### Retrieval Practice
8
+
9
+ Testing is not only assessment; it is a learning event. Prefer short-answer questions, explanation prompts, trace interpretation, and transfer questions over recognition-only checks.
10
+
11
+ Implementation:
12
+
13
+ - Teach a small unit.
14
+ - Ask the learner to explain or apply it without looking.
15
+ - Give corrective feedback.
16
+ - Log the exact misconception if the answer is weak.
17
+
18
+ Source anchors:
19
+
20
+ - Dunlosky et al. (2013): practice testing is high utility.
21
+ - Roediger & Karpicke (2006): retrieval after studying prose improved later retention.
22
+
23
+ ### Distributed Practice and Successive Relearning
24
+
25
+ Spacing matters. Review should happen after a gap, and practice should continue until the learner can retrieve to criterion.
26
+
27
+ Implementation:
28
+
29
+ - Keep error items `open`, `review`, or `closed`.
30
+ - Review open items after time has passed or when the user asks.
31
+ - Close only after repeated correct retrieval in separate sessions.
32
+ - Do not force a rigid schedule that annoys the user.
33
+
34
+ Source anchors:
35
+
36
+ - Cepeda et al. (2006): spacing effects are broadly supported, with optimal gaps depending on retention interval.
37
+ - Rawson & Dunlosky (2022): successive relearning combines spaced sessions and retrieval to criterion, but cost-benefit matters.
38
+
39
+ ### Formative Feedback
40
+
41
+ Feedback should answer: where is the learner going, how are they doing, and what should happen next. Feedback should explain the correction, not only mark right/wrong.
42
+
43
+ Implementation:
44
+
45
+ - Score answers with a transparent rubric.
46
+ - Write the corrected model.
47
+ - Add a next review action.
48
+ - Avoid generic praise or vague criticism.
49
+
50
+ Source anchors:
51
+
52
+ - Shute (2007): formative feedback varies by type and timing; effective feedback includes explanation, hints, or worked examples depending on task and learner.
53
+
54
+ ### Cognitive Load
55
+
56
+ Learning complex technical material fails when working memory is overloaded by disorganized sources, unnecessary notation, scattered files, or too many simultaneous objectives.
57
+
58
+ Implementation:
59
+
60
+ - Convert sources into readable text.
61
+ - Create a reading sequence.
62
+ - Teach in small units.
63
+ - Use worked examples before independent problems for novices.
64
+ - Fade support as competence rises.
65
+
66
+ Source anchors:
67
+
68
+ - Sweller, van Merrienboer, & Paas (1998): instructional design should account for working-memory limits, intrinsic load, extraneous load, and schema construction.
69
+ - Kalyuga (2007): learner prior knowledge changes which instructional support helps; too much guidance can hurt advanced learners.
70
+
71
+ ### Active Learning and ICAP
72
+
73
+ Do not confuse "interactive chat" with active learning. The learner must generate, explain, compare, debug, or transfer.
74
+
75
+ Implementation:
76
+
77
+ - Require the learner to explain in their own words.
78
+ - Ask transfer questions: "what tool next?", "what field/module does this become?", "what failure mode follows?"
79
+ - Prefer constructive and interactive activities over passive reading.
80
+
81
+ Source anchors:
82
+
83
+ - Freeman et al. (2014): active learning improved STEM outcomes in a large meta-analysis.
84
+ - Chi & Wylie (2014): ICAP orders engagement from passive to active to constructive to interactive.
85
+
86
+ ## Critique of the Folder + Sources + Logs Pipeline
87
+
88
+ The pipeline is strong because it creates source grounding, retrieval, feedback, and cumulative error memory. It should be adjusted in four ways:
89
+
90
+ 1. Do not over-collect. Start with a minimum viable corpus, then collect more when a question demands it.
91
+ 2. Do not over-convert. Convert enough for AI use; keep raw originals and indexes rather than rewriting entire books.
92
+ 3. Do not over-log. Log misconceptions that would cause future failure, not every stylistic weakness.
93
+ 4. Do not over-schedule review. Use spaced review as a tool, but let the user trigger heavy review unless a deadline requires a plan.
94
+
95
+ ## Practical Design Rule
96
+
97
+ A good learning workspace has exactly four persistent memories:
98
+
99
+ 1. Source memory: what materials exist and where.
100
+ 2. Goal memory: what the learner is trying to become able to do.
101
+ 3. Progress memory: what has been covered and scored.
102
+ 4. Error memory: what the learner got wrong and how to revisit it.
103
+
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ """Initialize a source-grounded learning workspace."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ from datetime import date
8
+ from pathlib import Path
9
+
10
+
11
+ def slugify(value: str) -> str:
12
+ out = []
13
+ last_dash = False
14
+ for ch in value.lower():
15
+ if ch.isalnum():
16
+ out.append(ch)
17
+ last_dash = False
18
+ elif not last_dash:
19
+ out.append("-")
20
+ last_dash = True
21
+ return "".join(out).strip("-") or "learning-topic"
22
+
23
+
24
+ def render(template: str, topic: str, goal: str) -> str:
25
+ return (
26
+ template.replace("{TOPIC}", topic)
27
+ .replace("{GOAL}", goal)
28
+ .replace("{DATE}", date.today().isoformat())
29
+ )
30
+
31
+
32
+ def main() -> int:
33
+ parser = argparse.ArgumentParser(description=__doc__)
34
+ parser.add_argument("topic", help="Human-readable topic name")
35
+ parser.add_argument("--path", default=".", help="Parent directory for workspace")
36
+ parser.add_argument("--name", help="Workspace folder name; defaults to topic slug")
37
+ parser.add_argument("--goal", default="Build practical, source-grounded mastery.")
38
+ parser.add_argument("--force", action="store_true", help="Overwrite existing template files")
39
+ args = parser.parse_args()
40
+
41
+ skill_dir = Path(__file__).resolve().parents[1]
42
+ assets = skill_dir / "assets"
43
+ root = Path(args.path).expanduser().resolve() / (args.name or slugify(args.topic))
44
+ root.mkdir(parents=True, exist_ok=True)
45
+
46
+ for subdir in ["materials", "notes", "cases", "outputs"]:
47
+ (root / subdir).mkdir(exist_ok=True)
48
+
49
+ files = {
50
+ "AGENTS.md": assets / "AGENTS.template.md",
51
+ "tasklog.typ": assets / "tasklog.template.typ",
52
+ "errorlog.typ": assets / "errorlog.template.typ",
53
+ "workbook.typ": assets / "workbook.template.typ",
54
+ "materials/source-index.md": assets / "source-index.template.md",
55
+ "notes/reading-sequence.md": assets / "reading-sequence.template.md",
56
+ }
57
+
58
+ for rel, template_path in files.items():
59
+ target = root / rel
60
+ if target.exists() and not args.force:
61
+ continue
62
+ text = render(template_path.read_text(), args.topic, args.goal)
63
+ target.write_text(text)
64
+
65
+ print(root)
66
+ return 0
67
+
68
+
69
+ if __name__ == "__main__":
70
+ raise SystemExit(main())
@@ -0,0 +1,258 @@
1
+ ---
2
+ name: macos-messages
3
+ description: Read and search local macOS Messages.app iMessage/SMS history directly through the chat.db SQLite database. Use when the user wants to read, search, or analyze local iPhone messages, SMS, iMessage conversations, or message attachments on this Mac.
4
+ ---
5
+
6
+ # macOS Messages
7
+
8
+ Direct read-only SQLite access to `~/Library/Messages/chat.db` for local iMessage and SMS history. No CLI wrapper — use `sqlite3` with the queries below.
9
+
10
+ ## Privacy
11
+
12
+ Chat data is private. Keep all work local. Print only the message content the user asked for. Never export `chat.db` to external services. Treat message bodies, sender identities, and attachment paths as sensitive.
13
+
14
+ ## Setup
15
+
16
+ The terminal needs **Full Disk Access** in System Settings → Privacy & Security → Full Disk Access. Without it, `~/Library/Messages/chat.db` is permission-denied.
17
+
18
+ Verify:
19
+
20
+ ```bash
21
+ sqlite3 ~/Library/Messages/chat.db "SELECT COUNT(*) FROM message"
22
+ ```
23
+
24
+ ## Schema
25
+
26
+ Four core tables, three join tables:
27
+
28
+ | Table | Role |
29
+ | --- | --- |
30
+ | `chat` | Conversation. `chat_identifier` is phone/email/group-id. `display_name` is the group name. |
31
+ | `message` | Individual message. `text` or `attributedBody` holds the body, `date` is timestamp, `is_from_me`=1 means sent. |
32
+ | `handle` | Contact. `id` is phone or email, `service` is iMessage/SMS/RCS. |
33
+ | `attachment` | Media file. `filename` is the on-disk path, `mime_type` is MIME type. |
34
+
35
+ Join tables: `chat_message_join`, `chat_handle_join`, `message_attachment_join`.
36
+
37
+ ## Text vs attributedBody
38
+
39
+ **This is the most common pitfall.** `message.text` is frequently NULL because Messages.app stores the body in `attributedBody` (NSKeyedArchiver binary blob) instead. This happens for most SMS, RCS, and rich iMessages. Plain-text iMessages are the main case where `text` is populated.
40
+
41
+ To extract readable text from `attributedBody`:
42
+
43
+ ```bash
44
+ sqlite3 ~/Library/Messages/chat.db \
45
+ "SELECT HEX(attributedBody) FROM message WHERE ROWID=<MSG_ID>" \
46
+ | xxd -r -p | strings | grep -vE '^(NS|__)' | grep -v streamtyped | head -20
47
+ ```
48
+
49
+ To run this in a single pipeline for a full chat read, see **Reading with extraction** below.
50
+
51
+ ## Date Conversion
52
+
53
+ All timestamps use Mac absolute time (since 2001-01-01). Convert:
54
+
55
+ ```sql
56
+ -- message.date: nanoseconds since 2001-01-01
57
+ datetime(date/1000000000 + 978307200, 'unixepoch', 'localtime')
58
+
59
+ -- attachment.created_date: seconds since 2001-01-01
60
+ datetime(created_date + 978307200, 'unixepoch', 'localtime')
61
+ ```
62
+
63
+ `978307200` = delta in seconds between Unix epoch (1970-01-01) and Mac epoch (2001-01-01).
64
+
65
+ ## Common Queries
66
+
67
+ ### List recent chats
68
+
69
+ ```sql
70
+ SELECT c.ROWID, c.chat_identifier, c.display_name, c.service_name,
71
+ datetime(MAX(m.date)/1000000000 + 978307200, 'unixepoch', 'localtime') AS last_msg
72
+ FROM chat c
73
+ JOIN chat_message_join cmj ON c.ROWID = cmj.chat_id
74
+ JOIN message m ON cmj.message_id = m.ROWID
75
+ GROUP BY c.ROWID
76
+ ORDER BY MAX(m.date) DESC
77
+ LIMIT 20
78
+ ```
79
+
80
+ ### Find a chat
81
+
82
+ ```sql
83
+ SELECT ROWID, chat_identifier, display_name, service_name
84
+ FROM chat
85
+ WHERE chat_identifier LIKE '%KEYWORD%'
86
+ OR display_name LIKE '%KEYWORD%'
87
+ LIMIT 10
88
+ ```
89
+
90
+ ### Read messages from a chat (with extraction)
91
+
92
+ Replace `<CHAT_ID>` with the chat ROWID. This extracts text from both `text` and `attributedBody`:
93
+
94
+ ```bash
95
+ sqlite3 ~/Library/Messages/chat.db -separator $'\t' \
96
+ "SELECT m.ROWID, datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') AS time,
97
+ CASE WHEN m.is_from_me THEN 'Me' ELSE COALESCE(h.id, m.service) END AS sender,
98
+ COALESCE(m.text, HEX(m.attributedBody)) AS body
99
+ FROM message m
100
+ JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
101
+ LEFT JOIN handle h ON m.handle_id = h.ROWID
102
+ WHERE cmj.chat_id = <CHAT_ID>
103
+ ORDER BY m.date DESC
104
+ LIMIT 50" \
105
+ | while IFS=$'\t' read rowid time sender body; do
106
+ text="$body"
107
+ if [[ "$body" =~ ^[0-9A-Fa-f]+$ ]] && [ ${#body} -gt 40 ]; then
108
+ text=$(echo "$body" | xxd -r -p | strings | grep -vE '^(NS|__)' | grep -v streamtyped | tr '\n' ' ' 2>/dev/null | head -c 500)
109
+ fi
110
+ echo "$time | $sender | ${text:0:300}"
111
+ done
112
+ ```
113
+
114
+ For ascending order (oldest first): change `DESC` to `ASC`.
115
+
116
+ For a quick peek (no extraction, just see which messages have body):
117
+
118
+ ```sql
119
+ SELECT datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') AS time,
120
+ CASE WHEN m.is_from_me THEN 'Me' ELSE COALESCE(h.id, m.service) END AS sender,
121
+ CASE WHEN m.text IS NOT NULL THEN SUBSTR(m.text, 1, 80)
122
+ WHEN m.attributedBody IS NOT NULL THEN '[attributedBody: ' || LENGTH(m.attributedBody) || ' bytes]'
123
+ ELSE '[empty]' END AS preview
124
+ FROM message m
125
+ JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
126
+ LEFT JOIN handle h ON m.handle_id = h.ROWID
127
+ WHERE cmj.chat_id = <CHAT_ID>
128
+ ORDER BY m.date DESC
129
+ LIMIT 50
130
+ ```
131
+
132
+ ### Read messages in a date range
133
+
134
+ ```sql
135
+ WHERE cmj.chat_id = <CHAT_ID>
136
+ AND m.date >= strftime('%s', '2026-04-01', 'localtime') * 1000000000 - 978307200000000000
137
+ AND m.date < strftime('%s', '2026-05-01', 'localtime') * 1000000000 - 978307200000000000
138
+ ```
139
+
140
+ ### Search all messages
141
+
142
+ Searches both `text` and `attributedBody`. Pipe through grep to handle binary extraction:
143
+
144
+ ```bash
145
+ sqlite3 ~/Library/Messages/chat.db -separator $'\t' \
146
+ "SELECT m.ROWID, datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') AS time,
147
+ CASE WHEN m.is_from_me THEN 'Me' ELSE COALESCE(h.id, m.service) END AS sender,
148
+ COALESCE(m.text, HEX(m.attributedBody)) AS body,
149
+ c.chat_identifier
150
+ FROM message m
151
+ JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
152
+ JOIN chat c ON cmj.chat_id = c.ROWID
153
+ LEFT JOIN handle h ON m.handle_id = h.ROWID
154
+ WHERE m.text LIKE '%KEYWORD%' ESCAPE '\'
155
+ OR HEX(m.attributedBody) LIKE '%' || REPLACE(HEX('KEYWORD'), '00', '') || '%'
156
+ ORDER BY m.date DESC
157
+ LIMIT 30" \
158
+ | while IFS=$'\t' read rowid time sender body chat; do
159
+ text="$body"
160
+ if [[ "$body" =~ ^[0-9A-Fa-f]+$ ]] && [ ${#body} -gt 40 ]; then
161
+ text=$(echo "$body" | xxd -r -p | strings | grep -vE '^(NS|__)' | grep -v streamtyped | tr '\n' ' ' 2>/dev/null | head -c 500)
162
+ fi
163
+ echo "$time | $sender | ${text:0:300} | [$chat]"
164
+ done
165
+ ```
166
+
167
+ The `REPLACE(HEX('KEYWORD'), '00', '')` accounts for UTF-16 encoding in the blob — hex matching handles ASCII text without needing full blob decode.
168
+
169
+ For a faster SQL-only search (returns matched rows, requires separate extraction):
170
+
171
+ ```sql
172
+ SELECT m.ROWID, datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') AS time,
173
+ CASE WHEN m.is_from_me THEN 'Me' ELSE COALESCE(h.id, m.service) END AS sender,
174
+ SUBSTR(COALESCE(m.text, ''), 1, 80) AS snippet,
175
+ c.chat_identifier
176
+ FROM message m
177
+ JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
178
+ JOIN chat c ON cmj.chat_id = c.ROWID
179
+ LEFT JOIN handle h ON m.handle_id = h.ROWID
180
+ WHERE m.text LIKE '%KEYWORD%' ESCAPE '\'
181
+ OR HEX(m.attributedBody) LIKE '%' || REPLACE(HEX('KEYWORD'), '00', '') || '%'
182
+ ORDER BY m.date DESC
183
+ LIMIT 30
184
+ ```
185
+
186
+ ### Search by contact
187
+
188
+ Find the handle first:
189
+
190
+ ```sql
191
+ SELECT ROWID, id, service FROM handle WHERE id LIKE '%PHONE_OR_EMAIL%' LIMIT 10
192
+ ```
193
+
194
+ Then get their messages with extraction (replace `<HANDLE_ID>`):
195
+
196
+ ```bash
197
+ sqlite3 ~/Library/Messages/chat.db -separator $'\t' \
198
+ "SELECT m.ROWID, datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') AS time,
199
+ COALESCE(m.text, HEX(m.attributedBody)) AS body, m.is_from_me
200
+ FROM message m
201
+ WHERE m.handle_id = <HANDLE_ID>
202
+ ORDER BY m.date DESC
203
+ LIMIT 50" \
204
+ | while IFS=$'\t' read rowid time body is_from_me; do
205
+ text="$body"
206
+ if [[ "$body" =~ ^[0-9A-Fa-f]+$ ]] && [ ${#body} -gt 40 ]; then
207
+ text=$(echo "$body" | xxd -r -p | strings | grep -vE '^(NS|__)' | grep -v streamtyped | tr '\n' ' ' 2>/dev/null | head -c 500)
208
+ fi
209
+ echo "$time | $([ "$is_from_me" = 1 ] && echo 'Me' || echo '→') | ${text:0:300}"
210
+ done
211
+ ```
212
+
213
+ ### Message counts per chat
214
+
215
+ ```sql
216
+ SELECT c.chat_identifier, c.display_name, COUNT(m.ROWID) AS msg_count
217
+ FROM chat c
218
+ JOIN chat_message_join cmj ON c.ROWID = cmj.chat_id
219
+ JOIN message m ON cmj.message_id = m.ROWID
220
+ GROUP BY c.ROWID
221
+ ORDER BY msg_count DESC
222
+ LIMIT 20
223
+ ```
224
+
225
+ ### Get attachments for a message
226
+
227
+ ```sql
228
+ SELECT a.filename, a.mime_type, a.total_bytes
229
+ FROM attachment a
230
+ JOIN message_attachment_join maj ON a.ROWID = maj.attachment_id
231
+ WHERE maj.message_id = <MESSAGE_ID>
232
+ ```
233
+
234
+ ### Attachments by date
235
+
236
+ ```sql
237
+ SELECT datetime(a.created_date + 978307200, 'unixepoch', 'localtime') AS time,
238
+ a.filename, a.mime_type, a.total_bytes
239
+ FROM attachment a
240
+ ORDER BY a.created_date DESC
241
+ LIMIT 30
242
+ ```
243
+
244
+ ## Safety
245
+
246
+ - **Read-only only.** Never run INSERT, UPDATE, DELETE, or DROP against `chat.db`.
247
+ - Always add `LIMIT`. This database can have tens of thousands of rows.
248
+ - When `text` is NULL, extract from `attributedBody` using the pipeline above — never assume a NULL `text` means an empty message.
249
+
250
+ ## Troubleshooting
251
+
252
+ | Symptom | Fix |
253
+ | --- | --- |
254
+ | `unable to open database file` | Grant Full Disk Access to Terminal in System Settings |
255
+ | `database is locked` | Normal when Messages.app is running; sqlite3 handles concurrent reads fine |
256
+ | No results for a known contact | Try both phone number and email in `handle.id`. iMessage uses email, SMS uses phone. |
257
+ | `text` IS NULL | Body is in `attributedBody`. Extract with: `HEX(attributedBody) \| xxd -r -p \| strings \| grep -vE '^(NS\|__)' \| grep -v streamtyped` |
258
+ | Search misses expected messages | Query only searched `text`. Add `OR HEX(m.attributedBody) LIKE '%' \|\| REPLACE(HEX('KEYWORD'), '00', '') \|\| '%'` to cover attributedBody. |
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: memory
3
+ description: Query and read past coding-agent session/conversation history, across both opencode and codex. Use when the user wants to recall something discussed in a previous session, search past conversations by keyword, review context from old sessions, or continue prior work. Triggers include "查一下之前的聊天记录", "还记得上次那个...", "look up a past session", "what did we talk about...", or any request to recall historical context.
4
+ ---
5
+
6
+ # Memory
7
+
8
+ This skill provides read-only access to past conversation history, supporting multiple coding agents.
9
+
10
+ ## Agent detection
11
+
12
+ Determine which agent you are running under, then load the corresponding reference file:
13
+
14
+ | Agent | How to detect | Reference file |
15
+ |---|---|---|
16
+ | **OpenCode** | System prompt mentions "opencode" OR you have the `opencode` CLI; DB exists at `~/.local/share/opencode/opencode.db` | [opencode.md](opencode.md) |
17
+ | **Codex (OpenAI)** | System prompt mentions "Codex" or "GPT-5"; sessions in `~/.codex/sessions/` | [codex.md](codex.md) |
18
+
19
+ **Detection priority:**
20
+ 1. Check your system prompt / identity (e.g. "You are Codex" vs "You are opencode").
21
+ 2. If ambiguous, check which session store exists on disk.
22
+
23
+ Once detected, read the corresponding file:
24
+
25
+ - For **OpenCode**: open `opencode.md` in the same directory as this file.
26
+ - For **Codex**: open `codex.md` in the same directory as this file.
27
+
28
+ ## Common safety rules (both agents)
29
+
30
+ - **Read-only**: Never modify session data (INSERT, UPDATE, DELETE, file write, etc.).
31
+ - **Limit output**: Always use `LIMIT` or piping (`head`) to avoid flooding context.
32
+ - **Privacy**: Avoid printing API keys, tokens, account emails, or raw error bodies.
33
+ - **WAL/Concurrency**: Both systems support concurrent reads; no need to stop the agent.