opencode-skills-collection 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +24 -1
  2. package/bundled-skills/007/SKILL.md +2 -2
  3. package/bundled-skills/10-andruia-skill-smith/SKILL.md +1 -1
  4. package/bundled-skills/agent-squad/SKILL.md +5 -0
  5. package/bundled-skills/agent-squad/alex/SKILL.md +3 -0
  6. package/bundled-skills/agent-squad/aria/SKILL.md +3 -0
  7. package/bundled-skills/agent-squad/dep/SKILL.md +3 -0
  8. package/bundled-skills/agent-squad/luna/SKILL.md +4 -1
  9. package/bundled-skills/agent-squad/mason/SKILL.md +3 -0
  10. package/bundled-skills/agent-squad/max/SKILL.md +3 -0
  11. package/bundled-skills/agent-squad/quinn/SKILL.md +3 -0
  12. package/bundled-skills/agent-squad/rex/SKILL.md +3 -0
  13. package/bundled-skills/angular/metadata.json +1 -1
  14. package/bundled-skills/angular-best-practices/metadata.json +1 -1
  15. package/bundled-skills/angular-state-management/metadata.json +1 -1
  16. package/bundled-skills/angular-ui-patterns/metadata.json +1 -1
  17. package/bundled-skills/antigravity-skill-orchestrator/SKILL.md +2 -2
  18. package/bundled-skills/apify-actor-development/SKILL.md +2 -2
  19. package/bundled-skills/apple-container/SKILL.md +165 -0
  20. package/bundled-skills/apple-container/references/commands.md +1593 -0
  21. package/bundled-skills/apple-container/references/concepts.md +294 -0
  22. package/bundled-skills/apple-container/references/configuration.md +499 -0
  23. package/bundled-skills/apple-container/references/workflows.md +498 -0
  24. package/bundled-skills/ask-copilot/SKILL.md +112 -0
  25. package/bundled-skills/auto-research/SKILL.md +126 -0
  26. package/bundled-skills/brooks-lint/SKILL.md +1 -1
  27. package/bundled-skills/browser-harness/references/install.md +3 -1
  28. package/bundled-skills/chrome-extension-developer/SKILL.md +1 -1
  29. package/bundled-skills/clean-code-guard/SKILL.md +184 -0
  30. package/bundled-skills/clean-code-guard/references/ai-failure-modes.md +345 -0
  31. package/bundled-skills/clean-code-guard/references/comments-and-formatting.md +115 -0
  32. package/bundled-skills/clean-code-guard/references/dry-kiss-yagni.md +133 -0
  33. package/bundled-skills/clean-code-guard/references/naming-and-functions.md +187 -0
  34. package/bundled-skills/clean-code-guard/references/review-checklist.md +150 -0
  35. package/bundled-skills/clean-code-guard/references/solid.md +280 -0
  36. package/bundled-skills/clean-code-guard/references/sources.md +47 -0
  37. package/bundled-skills/cloudflare-security-audit/SKILL.md +137 -0
  38. package/bundled-skills/cloudflare-security-audit/references/AI-AND-LLM.md +67 -0
  39. package/bundled-skills/cloudflare-security-audit/references/ATTACK-CLASSES.md +116 -0
  40. package/bundled-skills/cloudflare-security-audit/references/CLIENT-SIDE.md +67 -0
  41. package/bundled-skills/cloudflare-security-audit/references/HUNTING.md +110 -0
  42. package/bundled-skills/cloudflare-security-audit/references/MEMORY-SAFETY-AND-BINARY.md +58 -0
  43. package/bundled-skills/cloudflare-security-audit/references/RECONNAISSANCE.md +46 -0
  44. package/bundled-skills/cloudflare-security-audit/references/VALIDATION-AND-REPORTING.md +109 -0
  45. package/bundled-skills/cloudflare-security-audit/references/WEB-PROTOCOL-AND-AUTH.md +85 -0
  46. package/bundled-skills/cloudflare-security-audit/resources/report-schema.json +210 -0
  47. package/bundled-skills/comfyui-gateway/references/troubleshooting.md +1 -1
  48. package/bundled-skills/commit/SKILL.md +2 -1
  49. package/bundled-skills/deepapi/SKILL.md +6 -6
  50. package/bundled-skills/docs/README.md +1 -1
  51. package/bundled-skills/docs/contributors/quality-bar.md +1 -1
  52. package/bundled-skills/docs/contributors/security-guardrails.md +1 -1
  53. package/bundled-skills/docs/integrations/jetski-cortex.md +5 -5
  54. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +3 -3
  55. package/bundled-skills/docs/maintainers/ci-drift-fix.md +7 -5
  56. package/bundled-skills/docs/maintainers/merge-batch.md +24 -7
  57. package/bundled-skills/docs/maintainers/pr-autonomy.md +89 -0
  58. package/bundled-skills/docs/maintainers/release-notes-7.2.0.md +1 -1
  59. package/bundled-skills/docs/maintainers/release-process.md +9 -9
  60. package/bundled-skills/docs/maintainers/repo-growth-seo.md +5 -5
  61. package/bundled-skills/docs/maintainers/skills-date-tracking.md +1 -1
  62. package/bundled-skills/docs/maintainers/skills-update-guide.md +2 -2
  63. package/bundled-skills/docs/users/{antigravity-awesome-skills-vs-awesome-claude-skills.md → agentic-awesome-skills-vs-awesome-claude-skills.md} +5 -5
  64. package/bundled-skills/docs/users/ai-agent-skills.md +4 -4
  65. package/bundled-skills/docs/users/best-claude-code-skills-github.md +3 -3
  66. package/bundled-skills/docs/users/best-cursor-skills-github.md +3 -3
  67. package/bundled-skills/docs/users/bundles.md +56 -57
  68. package/bundled-skills/docs/users/claude-code-skills.md +6 -6
  69. package/bundled-skills/docs/users/codex-cli-skills.md +5 -5
  70. package/bundled-skills/docs/users/cursor-skills.md +3 -3
  71. package/bundled-skills/docs/users/faq.md +27 -15
  72. package/bundled-skills/docs/users/gemini-cli-skills.md +4 -4
  73. package/bundled-skills/docs/users/getting-started.md +22 -12
  74. package/bundled-skills/docs/users/kiro-integration.md +7 -7
  75. package/bundled-skills/docs/users/plugins.md +8 -9
  76. package/bundled-skills/docs/users/security-skills.md +1 -1
  77. package/bundled-skills/docs/users/skills-vs-mcp-tools.md +1 -1
  78. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +3 -4
  79. package/bundled-skills/docs/users/usage.md +9 -9
  80. package/bundled-skills/docs/users/visual-guide.md +7 -7
  81. package/bundled-skills/docs/users/windows-truncation-recovery.md +1 -1
  82. package/bundled-skills/docs/users/workflows.md +1 -1
  83. package/bundled-skills/docs/vietnamese/BUNDLES.vi.md +1 -1
  84. package/bundled-skills/docs/vietnamese/FAQ.vi.md +15 -8
  85. package/bundled-skills/docs/vietnamese/GETTING_STARTED.vi.md +12 -5
  86. package/bundled-skills/docs/vietnamese/QUALITY_BAR.vi.md +1 -1
  87. package/bundled-skills/docs/vietnamese/README.vi.md +21 -17
  88. package/bundled-skills/docs/vietnamese/SECURITY_GUARDRAILS.vi.md +1 -1
  89. package/bundled-skills/docs/vietnamese/SKILLS_README.vi.md +2 -2
  90. package/bundled-skills/docs/vietnamese/TRANSLATION_PLAN.vi.md +1 -1
  91. package/bundled-skills/docs/vietnamese/VISUAL_GUIDE.vi.md +8 -8
  92. package/bundled-skills/docs-guard/SKILL.md +109 -0
  93. package/bundled-skills/docs-guard/references/code-samples.md +46 -0
  94. package/bundled-skills/docs-guard/references/docstrings.md +60 -0
  95. package/bundled-skills/docs-guard/references/review-checklist.md +45 -0
  96. package/bundled-skills/docs-guard/references/sources.md +22 -0
  97. package/bundled-skills/docs-guard/references/verification.md +51 -0
  98. package/bundled-skills/efficient-web-research/SKILL.md +4 -0
  99. package/bundled-skills/electron-development/SKILL.md +1 -1
  100. package/bundled-skills/event-staffing-compliance/SKILL.md +3 -0
  101. package/bundled-skills/event-staffing-ordering/SKILL.md +3 -0
  102. package/bundled-skills/feature-tracking/SKILL.md +222 -0
  103. package/bundled-skills/flutter-expert/SKILL.md +6 -1
  104. package/bundled-skills/flutter-expert/resources/implementation-playbook.md +214 -0
  105. package/bundled-skills/gemini-deep-research/README.md +246 -0
  106. package/bundled-skills/gemini-deep-research/SKILL.md +136 -0
  107. package/bundled-skills/gemini-deep-research/requirements.txt +2 -0
  108. package/bundled-skills/gemini-deep-research/scripts/research.py +692 -0
  109. package/bundled-skills/git-pushing/SKILL.md +21 -4
  110. package/bundled-skills/git-pushing/scripts/smart_commit.sh +206 -11
  111. package/bundled-skills/go-in-depth/scripts/workflow-script.js +4 -1
  112. package/bundled-skills/grok-build/SKILL.md +167 -0
  113. package/bundled-skills/grok-build/references/cli.md +99 -0
  114. package/bundled-skills/huggingface-tool-builder/references/find_models_by_paper.sh +1 -1
  115. package/bundled-skills/idea-autopsy/SKILL.md +147 -0
  116. package/bundled-skills/it-manager-hospital/README.md +1 -1
  117. package/bundled-skills/it-manager-pro/README.md +1 -1
  118. package/bundled-skills/itil-expert/README.md +1 -1
  119. package/bundled-skills/langgraph/SKILL.md +1 -1
  120. package/bundled-skills/linkedin-content-generator/SKILL.md +1 -1
  121. package/bundled-skills/lint-and-validate/scripts/lint_runner.py +23 -23
  122. package/bundled-skills/lint-and-validate/scripts/type_coverage.py +30 -30
  123. package/bundled-skills/logic-lens/SKILL.md +1 -1
  124. package/bundled-skills/lore/LICENSE +21 -0
  125. package/bundled-skills/lore/README.md +386 -0
  126. package/bundled-skills/lore/README.zh-CN.md +386 -0
  127. package/bundled-skills/lore/SKILL.md +453 -0
  128. package/bundled-skills/lore/WORKFLOWS.md +216 -0
  129. package/bundled-skills/lore/WORKFLOWS.zh-CN.md +216 -0
  130. package/bundled-skills/lore/references/audit-template.md +60 -0
  131. package/bundled-skills/lore/references/compatibility.md +228 -0
  132. package/bundled-skills/lore/references/config.md +125 -0
  133. package/bundled-skills/lore/references/entry-format.md +70 -0
  134. package/bundled-skills/lore/references/history-command.md +106 -0
  135. package/bundled-skills/lore/references/monorepo-detection.md +78 -0
  136. package/bundled-skills/lore/references/platform-mirrors.md +377 -0
  137. package/bundled-skills/lore/references/stale-new-markers.md +62 -0
  138. package/bundled-skills/lore/references/summary-template.md +98 -0
  139. package/bundled-skills/lore/scripts/README.md +54 -0
  140. package/bundled-skills/lore/scripts/README.zh-CN.md +54 -0
  141. package/bundled-skills/lore/scripts/find_duplicates.py +211 -0
  142. package/bundled-skills/lore/scripts/find_stale.py +116 -0
  143. package/bundled-skills/lore/scripts/history.py +527 -0
  144. package/bundled-skills/lore/scripts/id_hash.py +32 -0
  145. package/bundled-skills/lore/scripts/list_entries.py +183 -0
  146. package/bundled-skills/lovable-cleanup/SKILL.md +3 -3
  147. package/bundled-skills/makepad-splash/SKILL.md +1 -1
  148. package/bundled-skills/mmx-cli/SKILL.md +14 -1
  149. package/bundled-skills/monopoly/SKILL.md +5 -0
  150. package/bundled-skills/monopoly/patterns/SKILL.md +4 -0
  151. package/bundled-skills/monopoly/scale-benchmarks/SKILL.md +4 -0
  152. package/bundled-skills/monopoly/security-checklist/SKILL.md +4 -0
  153. package/bundled-skills/monopoly/tech-matrix/SKILL.md +4 -0
  154. package/bundled-skills/mtls-configuration/SKILL.md +1 -1
  155. package/bundled-skills/network-101/SKILL.md +4 -5
  156. package/bundled-skills/nika/SKILL.md +270 -0
  157. package/bundled-skills/not-a-vibe-coder/SKILL.md +4 -0
  158. package/bundled-skills/ontoly-software-graph/SKILL.md +160 -0
  159. package/bundled-skills/openclaw-github-repo-commander/SKILL.md +1 -1
  160. package/bundled-skills/pilot-protocol/SKILL.md +6 -3
  161. package/bundled-skills/postgres-readonly-queries/README.md +77 -0
  162. package/bundled-skills/postgres-readonly-queries/SKILL.md +152 -0
  163. package/bundled-skills/postgres-readonly-queries/connections.example.json +34 -0
  164. package/bundled-skills/postgres-readonly-queries/requirements.txt +1 -0
  165. package/bundled-skills/postgres-readonly-queries/scripts/query.py +262 -0
  166. package/bundled-skills/pptx-deck-creation/SKILL.md +275 -0
  167. package/bundled-skills/pptx-deck-creation/references/audit-checklist.md +156 -0
  168. package/bundled-skills/pptx-deck-creation/references/design-profiles.md +96 -0
  169. package/bundled-skills/pptx-deck-creation/references/python-snippets.md +150 -0
  170. package/bundled-skills/pptx-deck-creation/references/reference-deck-analysis.md +91 -0
  171. package/bundled-skills/pptx-deck-creation/references/visual-asset-adapters.md +158 -0
  172. package/bundled-skills/privilege-escalation-methods/SKILL.md +1 -1
  173. package/bundled-skills/product-decision-agent/SKILL.md +94 -0
  174. package/bundled-skills/product-decision-agent/agents/openai.yaml +7 -0
  175. package/bundled-skills/product-decision-agent/references/methodology-basis.md +232 -0
  176. package/bundled-skills/product-decision-agent/references/product-playbooks.md +813 -0
  177. package/bundled-skills/product-decision-agent/references/reasoning-engine.md +289 -0
  178. package/bundled-skills/product-decision-agent/references/response-examples.md +279 -0
  179. package/bundled-skills/product-decision-agent/scripts/quality_gate.py +221 -0
  180. package/bundled-skills/product-decision-agent/scripts/test_quality_gate.py +94 -0
  181. package/bundled-skills/pytest-skill/reference/playbook.md +358 -0
  182. package/bundled-skills/quit-sponsor/SKILL.md +96 -0
  183. package/bundled-skills/rclone-cli/references/providers/s3.md +1 -1
  184. package/bundled-skills/security-and-hardening/SKILL.md +1 -1
  185. package/bundled-skills/semgrep-rule-creator/SKILL.md +6 -6
  186. package/bundled-skills/skill-creator/README.md +3 -3
  187. package/bundled-skills/skill-creator/SKILL.md +8 -4
  188. package/bundled-skills/skill-creator/scripts/quick_validate.py +1 -1
  189. package/bundled-skills/sql-injection-testing/SKILL.md +4 -4
  190. package/bundled-skills/subagent-orchestrator/README.md +1 -1
  191. package/bundled-skills/super-code/bash/SKILL.md +3 -0
  192. package/bundled-skills/super-code/c/SKILL.md +3 -0
  193. package/bundled-skills/super-code/cpp/SKILL.md +3 -0
  194. package/bundled-skills/super-code/csharp/SKILL.md +3 -0
  195. package/bundled-skills/super-code/dart/SKILL.md +3 -0
  196. package/bundled-skills/super-code/elixir/SKILL.md +3 -0
  197. package/bundled-skills/super-code/go/SKILL.md +3 -0
  198. package/bundled-skills/super-code/java/SKILL.md +3 -0
  199. package/bundled-skills/super-code/kotlin/SKILL.md +3 -0
  200. package/bundled-skills/super-code/php/SKILL.md +3 -0
  201. package/bundled-skills/super-code/python/SKILL.md +3 -0
  202. package/bundled-skills/super-code/ruby/SKILL.md +3 -0
  203. package/bundled-skills/super-code/rust/SKILL.md +3 -0
  204. package/bundled-skills/super-code/scala/SKILL.md +3 -0
  205. package/bundled-skills/super-code/swift/SKILL.md +3 -0
  206. package/bundled-skills/super-code/typescript/SKILL.md +3 -0
  207. package/bundled-skills/telegram-bot-messaging/README.md +185 -0
  208. package/bundled-skills/telegram-bot-messaging/SKILL.md +101 -0
  209. package/bundled-skills/telegram-bot-messaging/scripts/telegram.sh +420 -0
  210. package/bundled-skills/test-guard/SKILL.md +132 -0
  211. package/bundled-skills/test-guard/references/jest.md +50 -0
  212. package/bundled-skills/test-guard/references/llm-app-testing.md +40 -0
  213. package/bundled-skills/test-guard/references/phpunit.md +56 -0
  214. package/bundled-skills/test-guard/references/pytest.md +65 -0
  215. package/bundled-skills/trading-ledger/SKILL.md +11 -3
  216. package/bundled-skills/uizze-ui-research/SKILL.md +93 -0
  217. package/bundled-skills/vibe-code-auditor/SKILL.md +1 -1
  218. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +1 -1
  219. package/bundled-skills/videodb/scripts/ws_listener.py +29 -15
  220. package/bundled-skills/voice-ai-engine-development/README.md +1 -1
  221. package/bundled-skills/vulnerability-scanner/SKILL.md +1 -1
  222. package/bundled-skills/warehouse/SKILL.md +154 -0
  223. package/bundled-skills/woo-guard/SKILL.md +113 -0
  224. package/bundled-skills/woo-guard/references/checkout-and-money.md +64 -0
  225. package/bundled-skills/woo-guard/references/hpos-and-crud.md +87 -0
  226. package/bundled-skills/woo-guard/references/review-checklist.md +44 -0
  227. package/bundled-skills/woo-guard/references/sources.md +17 -0
  228. package/bundled-skills/wp-guard/SKILL.md +119 -0
  229. package/bundled-skills/wp-guard/references/i18n.md +88 -0
  230. package/bundled-skills/wp-guard/references/performance.md +70 -0
  231. package/bundled-skills/wp-guard/references/review-checklist.md +57 -0
  232. package/bundled-skills/wp-guard/references/security.md +100 -0
  233. package/bundled-skills/wp-guard/references/sources.md +27 -0
  234. package/bundled-skills/writing-skills/gotchas.md +1 -1
  235. package/bundled-skills/writing-skills/references/standards/metadata-standard.md +1 -1
  236. package/bundled-skills/x-twitter-scraper/SKILL.md +14 -32
  237. package/bundled-skills/xss-html-injection/SKILL.md +3 -3
  238. package/bundled-skills/youtube-seo-optimizer/SKILL.md +1 -1
  239. package/bundled-skills/youtube-summarizer/CHANGELOG.md +2 -2
  240. package/package.json +1 -1
  241. package/skills_index.json +273 -0
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env python3
2
+ """Find potential duplicate entries in .lore/.
3
+
4
+ Usage:
5
+ python find_duplicates.py # default threshold 0.7
6
+ python find_duplicates.py --threshold=0.85
7
+ python find_duplicates.py --json
8
+ python find_duplicates.py --candidate "<text>"
9
+ python find_duplicates.py --candidate-file path/to/candidate.txt
10
+ echo '<text>' | python find_duplicates.py --candidate-stdin
11
+
12
+ Detection strategies:
13
+ 1. Identical hash suffix (4 chars after the date) — these are exact
14
+ text matches and indicate either a real duplicate or a hash
15
+ collision. Always reported.
16
+ 2. Token-based Jaccard similarity above `--threshold` on the entry
17
+ text. Catches rewrites that mean the same thing but produce a
18
+ different hash (e.g. "use Zustand" vs "we chose Zustand").
19
+
20
+ Output is sorted by similarity (descending). Run from the project root.
21
+
22
+ This script is the mechanical part of `sync` step 5 (de-duplication).
23
+ The agent still decides what to do with each pair.
24
+
25
+ When a candidate is supplied (via --candidate, --candidate-file, or
26
+ --candidate-stdin), the candidate is also included in the comparison
27
+ set so sync step 5 can detect "this proposed entry duplicates an
28
+ existing one" before appending. Without a candidate, only
29
+ already-appended entries are compared.
30
+ """
31
+ import json
32
+ import re
33
+ import subprocess
34
+ import sys
35
+ from pathlib import Path
36
+
37
+
38
+ def get_entries():
39
+ """Invoke list_entries.py --json to get parsed entries."""
40
+ script = Path(__file__).parent / "list_entries.py"
41
+ r = subprocess.run(
42
+ [sys.executable, str(script), "--json"],
43
+ capture_output=True,
44
+ text=True,
45
+ )
46
+ if r.returncode != 0:
47
+ print(r.stderr, file=sys.stderr)
48
+ sys.exit(1)
49
+ return json.loads(r.stdout)
50
+
51
+
52
+ def read_candidate(args):
53
+ """Return the candidate text or None.
54
+
55
+ Sources, in priority order:
56
+ 1. --candidate "<text>"
57
+ 2. --candidate-file <path>
58
+ 3. --candidate-stdin (reads entire stdin)
59
+ """
60
+ inline = None
61
+ file_path = None
62
+ use_stdin = False
63
+ i = 0
64
+ while i < len(args):
65
+ a = args[i]
66
+ if a.startswith("--candidate="):
67
+ inline = a.split("=", 1)[1]
68
+ elif a.startswith("--candidate-file="):
69
+ file_path = a.split("=", 1)[1]
70
+ elif a in ("--candidate", "--candidate-file"):
71
+ if i + 1 >= len(args) or args[i + 1].startswith("--"):
72
+ die(2, f"{a} requires a value")
73
+ i += 1
74
+ if a == "--candidate":
75
+ inline = args[i]
76
+ else:
77
+ file_path = args[i]
78
+ elif a == "--candidate-stdin":
79
+ use_stdin = True
80
+ i += 1
81
+ if inline is not None:
82
+ return inline
83
+ if file_path is not None:
84
+ try:
85
+ return Path(file_path).read_text(encoding="utf-8")
86
+ except OSError as exc:
87
+ die(2, f"failed to read candidate file {file_path}: {exc}")
88
+ if use_stdin:
89
+ if sys.stdin.isatty():
90
+ die(2, "--candidate-stdin given but stdin is a TTY")
91
+ return sys.stdin.read()
92
+ return None
93
+
94
+
95
+ def die(code, message):
96
+ print(f"error: {message}", file=sys.stderr)
97
+ sys.exit(code)
98
+
99
+
100
+ def synthetic_candidate_entry(text):
101
+ """Build a candidate entry dict shaped like list_entries.py output.
102
+
103
+ The synthetic entry has layer "CANDIDATE" so it compares only against
104
+ existing entries on the same layer when the agent supplies --layer.
105
+ """
106
+ return {
107
+ "id": "CANDIDATE-unsaved",
108
+ "layer": "CANDIDATE",
109
+ "scope": "_candidate",
110
+ "file": "<candidate>",
111
+ "text": text.strip(),
112
+ "tags": {},
113
+ }
114
+
115
+
116
+ def tokenize(text: str):
117
+ return set(re.findall(r"\w+", text.lower()))
118
+
119
+
120
+ def jaccard(a: set, b: set):
121
+ if not a or not b:
122
+ return 0.0
123
+ return len(a & b) / len(a | b)
124
+
125
+
126
+ def hash_suffix(eid: str):
127
+ return eid.split("-")[-1]
128
+
129
+
130
+ def main():
131
+ args = sys.argv[1:]
132
+ threshold = 0.7
133
+ json_output = "--json" in args
134
+ layer_filter = None
135
+
136
+ for arg in args:
137
+ if arg.startswith("--threshold="):
138
+ threshold = float(arg.split("=", 1)[1])
139
+ elif arg.startswith("--layer="):
140
+ layer_filter = arg.split("=", 1)[1]
141
+
142
+ candidate_text = read_candidate(args)
143
+
144
+ entries = get_entries()
145
+ if layer_filter is not None:
146
+ entries = [e for e in entries if e.get("layer") == layer_filter]
147
+
148
+ candidates = []
149
+ if candidate_text:
150
+ candidates.append(synthetic_candidate_entry(candidate_text))
151
+
152
+ pairs = []
153
+
154
+ # existing-vs-existing pairs (unchanged behavior)
155
+ for i, a in enumerate(entries):
156
+ for b in entries[i + 1:]:
157
+ if a["layer"] != b["layer"]:
158
+ continue
159
+ if hash_suffix(a["id"]) == hash_suffix(b["id"]):
160
+ pairs.append((a, b, 1.0, "identical hash"))
161
+ continue
162
+ sim = jaccard(tokenize(a["text"]), tokenize(b["text"]))
163
+ if sim >= threshold:
164
+ pairs.append((a, b, sim, f"similar text (≥{threshold})"))
165
+
166
+ # candidate-vs-existing pairs
167
+ if candidates:
168
+ # --layer narrows entries above; without it, compare the proposed
169
+ # entry with every layer because the candidate has not been assigned
170
+ # a canonical layer yet.
171
+ compare_set = entries
172
+ for a in compare_set:
173
+ sim = jaccard(
174
+ tokenize(candidates[0]["text"]),
175
+ tokenize(a["text"]),
176
+ )
177
+ if sim >= threshold:
178
+ pairs.append((candidates[0], a, sim,
179
+ f"candidate similar to existing (≥{threshold})"))
180
+
181
+ pairs.sort(key=lambda x: -x[2])
182
+
183
+ if json_output:
184
+ out = [
185
+ {
186
+ "similarity": round(sim, 3),
187
+ "reason": reason,
188
+ "a": a,
189
+ "b": b,
190
+ }
191
+ for a, b, sim, reason in pairs
192
+ ]
193
+ print(json.dumps(out, indent=2, ensure_ascii=False))
194
+ return
195
+
196
+ if not pairs:
197
+ if candidate_text:
198
+ print("No potential duplicates found for the candidate.")
199
+ else:
200
+ print("No potential duplicates found.")
201
+ return
202
+
203
+ for a, b, sim, reason in pairs:
204
+ print(f"[{sim:.2f}] {reason}")
205
+ print(f" A: [{a['file']}] {a['id']} {a['text']}")
206
+ print(f" B: [{b['file']}] {b['id']} {b['text']}")
207
+ print()
208
+
209
+
210
+ if __name__ == "__main__":
211
+ main()
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env python3
2
+ """Find stale entries in .lore/.
3
+
4
+ Usage:
5
+ python find_stale.py # default: 90-day threshold
6
+ python find_stale.py --days=180
7
+ python find_stale.py --json
8
+
9
+ Reports two categories:
10
+
11
+ Stale : entry has not been `#verified` within the threshold
12
+ (or has no #verified at all, and was added > threshold
13
+ days ago).
14
+ Pending arch : entry already carries a `#stale:` tag and is waiting
15
+ to be moved into .lore/archive/.
16
+
17
+ Output is plain text by default, JSON with --json.
18
+
19
+ Used by:
20
+ - `audit` workflow (read-only)
21
+ - `compress` workflow (advisory)
22
+ - `lore mirror` (sanity check before regenerating)
23
+ """
24
+ import json
25
+ import subprocess
26
+ import sys
27
+ from datetime import date, datetime, timedelta
28
+ from pathlib import Path
29
+
30
+
31
+ def get_entries():
32
+ script = Path(__file__).parent / "list_entries.py"
33
+ r = subprocess.run(
34
+ [sys.executable, str(script), "--json"],
35
+ capture_output=True,
36
+ text=True,
37
+ )
38
+ if r.returncode != 0:
39
+ print(r.stderr.strip(), file=sys.stderr)
40
+ sys.exit(1)
41
+ try:
42
+ return json.loads(r.stdout)
43
+ except json.JSONDecodeError as exc:
44
+ print(f"error: list_entries.py returned invalid JSON: {exc}",
45
+ file=sys.stderr)
46
+ sys.exit(1)
47
+
48
+
49
+ def parse_date(s: str):
50
+ try:
51
+ return datetime.strptime(s, "%Y-%m-%d").date()
52
+ except (ValueError, TypeError):
53
+ return None
54
+
55
+
56
+ def main():
57
+ days = 90
58
+ json_output = "--json" in sys.argv[1:]
59
+
60
+ for arg in sys.argv[1:]:
61
+ if arg.startswith("--days="):
62
+ days = int(arg.split("=", 1)[1])
63
+
64
+ today = date.today()
65
+ cutoff = today - timedelta(days=days)
66
+
67
+ entries = get_entries()
68
+ stale = []
69
+ pending_arch = []
70
+
71
+ for e in entries:
72
+ # Already marked stale → pending archive
73
+ if "stale" in e["tags"]:
74
+ pending_arch.append(e)
75
+ continue
76
+
77
+ # Determine the entry's freshness date
78
+ last_v = parse_date(e["last_verified"])
79
+ added = parse_date(e["tags"].get("added"))
80
+ ref_date = last_v or added
81
+
82
+ if ref_date is None:
83
+ continue # no date info, can't decide
84
+
85
+ if ref_date < cutoff:
86
+ stale.append(e)
87
+
88
+ if json_output:
89
+ out = {
90
+ "threshold_days": days,
91
+ "as_of": today.isoformat(),
92
+ "stale": stale,
93
+ "pending_archive": pending_arch,
94
+ }
95
+ print(json.dumps(out, indent=2, ensure_ascii=False))
96
+ return
97
+
98
+ print(f"=== Stale (unverified > {days} days, as of {today}) ===")
99
+ if not stale:
100
+ print(" (none)")
101
+ for e in stale:
102
+ ref = e["last_verified"] or e["tags"].get("added", "unknown")
103
+ print(f" [{e['file']}] {e['id']} {e['text']}")
104
+ print(f" ref date: {ref}")
105
+
106
+ print()
107
+ print("=== Pending archive (tagged #stale) ===")
108
+ if not pending_arch:
109
+ print(" (none)")
110
+ for e in pending_arch:
111
+ print(f" [{e['file']}] {e['id']} {e['text']}")
112
+ print(f" marked stale: {e['tags']['stale']}")
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()