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.
- package/bundled-skills/.antigravity-install-manifest.json +24 -1
- package/bundled-skills/007/SKILL.md +2 -2
- package/bundled-skills/10-andruia-skill-smith/SKILL.md +1 -1
- package/bundled-skills/agent-squad/SKILL.md +5 -0
- package/bundled-skills/agent-squad/alex/SKILL.md +3 -0
- package/bundled-skills/agent-squad/aria/SKILL.md +3 -0
- package/bundled-skills/agent-squad/dep/SKILL.md +3 -0
- package/bundled-skills/agent-squad/luna/SKILL.md +4 -1
- package/bundled-skills/agent-squad/mason/SKILL.md +3 -0
- package/bundled-skills/agent-squad/max/SKILL.md +3 -0
- package/bundled-skills/agent-squad/quinn/SKILL.md +3 -0
- package/bundled-skills/agent-squad/rex/SKILL.md +3 -0
- package/bundled-skills/angular/metadata.json +1 -1
- package/bundled-skills/angular-best-practices/metadata.json +1 -1
- package/bundled-skills/angular-state-management/metadata.json +1 -1
- package/bundled-skills/angular-ui-patterns/metadata.json +1 -1
- package/bundled-skills/antigravity-skill-orchestrator/SKILL.md +2 -2
- package/bundled-skills/apify-actor-development/SKILL.md +2 -2
- package/bundled-skills/apple-container/SKILL.md +165 -0
- package/bundled-skills/apple-container/references/commands.md +1593 -0
- package/bundled-skills/apple-container/references/concepts.md +294 -0
- package/bundled-skills/apple-container/references/configuration.md +499 -0
- package/bundled-skills/apple-container/references/workflows.md +498 -0
- package/bundled-skills/ask-copilot/SKILL.md +112 -0
- package/bundled-skills/auto-research/SKILL.md +126 -0
- package/bundled-skills/brooks-lint/SKILL.md +1 -1
- package/bundled-skills/browser-harness/references/install.md +3 -1
- package/bundled-skills/chrome-extension-developer/SKILL.md +1 -1
- package/bundled-skills/clean-code-guard/SKILL.md +184 -0
- package/bundled-skills/clean-code-guard/references/ai-failure-modes.md +345 -0
- package/bundled-skills/clean-code-guard/references/comments-and-formatting.md +115 -0
- package/bundled-skills/clean-code-guard/references/dry-kiss-yagni.md +133 -0
- package/bundled-skills/clean-code-guard/references/naming-and-functions.md +187 -0
- package/bundled-skills/clean-code-guard/references/review-checklist.md +150 -0
- package/bundled-skills/clean-code-guard/references/solid.md +280 -0
- package/bundled-skills/clean-code-guard/references/sources.md +47 -0
- package/bundled-skills/cloudflare-security-audit/SKILL.md +137 -0
- package/bundled-skills/cloudflare-security-audit/references/AI-AND-LLM.md +67 -0
- package/bundled-skills/cloudflare-security-audit/references/ATTACK-CLASSES.md +116 -0
- package/bundled-skills/cloudflare-security-audit/references/CLIENT-SIDE.md +67 -0
- package/bundled-skills/cloudflare-security-audit/references/HUNTING.md +110 -0
- package/bundled-skills/cloudflare-security-audit/references/MEMORY-SAFETY-AND-BINARY.md +58 -0
- package/bundled-skills/cloudflare-security-audit/references/RECONNAISSANCE.md +46 -0
- package/bundled-skills/cloudflare-security-audit/references/VALIDATION-AND-REPORTING.md +109 -0
- package/bundled-skills/cloudflare-security-audit/references/WEB-PROTOCOL-AND-AUTH.md +85 -0
- package/bundled-skills/cloudflare-security-audit/resources/report-schema.json +210 -0
- package/bundled-skills/comfyui-gateway/references/troubleshooting.md +1 -1
- package/bundled-skills/commit/SKILL.md +2 -1
- package/bundled-skills/deepapi/SKILL.md +6 -6
- package/bundled-skills/docs/README.md +1 -1
- package/bundled-skills/docs/contributors/quality-bar.md +1 -1
- package/bundled-skills/docs/contributors/security-guardrails.md +1 -1
- package/bundled-skills/docs/integrations/jetski-cortex.md +5 -5
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +3 -3
- package/bundled-skills/docs/maintainers/ci-drift-fix.md +7 -5
- package/bundled-skills/docs/maintainers/merge-batch.md +24 -7
- package/bundled-skills/docs/maintainers/pr-autonomy.md +89 -0
- package/bundled-skills/docs/maintainers/release-notes-7.2.0.md +1 -1
- package/bundled-skills/docs/maintainers/release-process.md +9 -9
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +5 -5
- package/bundled-skills/docs/maintainers/skills-date-tracking.md +1 -1
- package/bundled-skills/docs/maintainers/skills-update-guide.md +2 -2
- package/bundled-skills/docs/users/{antigravity-awesome-skills-vs-awesome-claude-skills.md → agentic-awesome-skills-vs-awesome-claude-skills.md} +5 -5
- package/bundled-skills/docs/users/ai-agent-skills.md +4 -4
- package/bundled-skills/docs/users/best-claude-code-skills-github.md +3 -3
- package/bundled-skills/docs/users/best-cursor-skills-github.md +3 -3
- package/bundled-skills/docs/users/bundles.md +56 -57
- package/bundled-skills/docs/users/claude-code-skills.md +6 -6
- package/bundled-skills/docs/users/codex-cli-skills.md +5 -5
- package/bundled-skills/docs/users/cursor-skills.md +3 -3
- package/bundled-skills/docs/users/faq.md +27 -15
- package/bundled-skills/docs/users/gemini-cli-skills.md +4 -4
- package/bundled-skills/docs/users/getting-started.md +22 -12
- package/bundled-skills/docs/users/kiro-integration.md +7 -7
- package/bundled-skills/docs/users/plugins.md +8 -9
- package/bundled-skills/docs/users/security-skills.md +1 -1
- package/bundled-skills/docs/users/skills-vs-mcp-tools.md +1 -1
- package/bundled-skills/docs/users/specialized-plugin-roadmap.md +3 -4
- package/bundled-skills/docs/users/usage.md +9 -9
- package/bundled-skills/docs/users/visual-guide.md +7 -7
- package/bundled-skills/docs/users/windows-truncation-recovery.md +1 -1
- package/bundled-skills/docs/users/workflows.md +1 -1
- package/bundled-skills/docs/vietnamese/BUNDLES.vi.md +1 -1
- package/bundled-skills/docs/vietnamese/FAQ.vi.md +15 -8
- package/bundled-skills/docs/vietnamese/GETTING_STARTED.vi.md +12 -5
- package/bundled-skills/docs/vietnamese/QUALITY_BAR.vi.md +1 -1
- package/bundled-skills/docs/vietnamese/README.vi.md +21 -17
- package/bundled-skills/docs/vietnamese/SECURITY_GUARDRAILS.vi.md +1 -1
- package/bundled-skills/docs/vietnamese/SKILLS_README.vi.md +2 -2
- package/bundled-skills/docs/vietnamese/TRANSLATION_PLAN.vi.md +1 -1
- package/bundled-skills/docs/vietnamese/VISUAL_GUIDE.vi.md +8 -8
- package/bundled-skills/docs-guard/SKILL.md +109 -0
- package/bundled-skills/docs-guard/references/code-samples.md +46 -0
- package/bundled-skills/docs-guard/references/docstrings.md +60 -0
- package/bundled-skills/docs-guard/references/review-checklist.md +45 -0
- package/bundled-skills/docs-guard/references/sources.md +22 -0
- package/bundled-skills/docs-guard/references/verification.md +51 -0
- package/bundled-skills/efficient-web-research/SKILL.md +4 -0
- package/bundled-skills/electron-development/SKILL.md +1 -1
- package/bundled-skills/event-staffing-compliance/SKILL.md +3 -0
- package/bundled-skills/event-staffing-ordering/SKILL.md +3 -0
- package/bundled-skills/feature-tracking/SKILL.md +222 -0
- package/bundled-skills/flutter-expert/SKILL.md +6 -1
- package/bundled-skills/flutter-expert/resources/implementation-playbook.md +214 -0
- package/bundled-skills/gemini-deep-research/README.md +246 -0
- package/bundled-skills/gemini-deep-research/SKILL.md +136 -0
- package/bundled-skills/gemini-deep-research/requirements.txt +2 -0
- package/bundled-skills/gemini-deep-research/scripts/research.py +692 -0
- package/bundled-skills/git-pushing/SKILL.md +21 -4
- package/bundled-skills/git-pushing/scripts/smart_commit.sh +206 -11
- package/bundled-skills/go-in-depth/scripts/workflow-script.js +4 -1
- package/bundled-skills/grok-build/SKILL.md +167 -0
- package/bundled-skills/grok-build/references/cli.md +99 -0
- package/bundled-skills/huggingface-tool-builder/references/find_models_by_paper.sh +1 -1
- package/bundled-skills/idea-autopsy/SKILL.md +147 -0
- package/bundled-skills/it-manager-hospital/README.md +1 -1
- package/bundled-skills/it-manager-pro/README.md +1 -1
- package/bundled-skills/itil-expert/README.md +1 -1
- package/bundled-skills/langgraph/SKILL.md +1 -1
- package/bundled-skills/linkedin-content-generator/SKILL.md +1 -1
- package/bundled-skills/lint-and-validate/scripts/lint_runner.py +23 -23
- package/bundled-skills/lint-and-validate/scripts/type_coverage.py +30 -30
- package/bundled-skills/logic-lens/SKILL.md +1 -1
- package/bundled-skills/lore/LICENSE +21 -0
- package/bundled-skills/lore/README.md +386 -0
- package/bundled-skills/lore/README.zh-CN.md +386 -0
- package/bundled-skills/lore/SKILL.md +453 -0
- package/bundled-skills/lore/WORKFLOWS.md +216 -0
- package/bundled-skills/lore/WORKFLOWS.zh-CN.md +216 -0
- package/bundled-skills/lore/references/audit-template.md +60 -0
- package/bundled-skills/lore/references/compatibility.md +228 -0
- package/bundled-skills/lore/references/config.md +125 -0
- package/bundled-skills/lore/references/entry-format.md +70 -0
- package/bundled-skills/lore/references/history-command.md +106 -0
- package/bundled-skills/lore/references/monorepo-detection.md +78 -0
- package/bundled-skills/lore/references/platform-mirrors.md +377 -0
- package/bundled-skills/lore/references/stale-new-markers.md +62 -0
- package/bundled-skills/lore/references/summary-template.md +98 -0
- package/bundled-skills/lore/scripts/README.md +54 -0
- package/bundled-skills/lore/scripts/README.zh-CN.md +54 -0
- package/bundled-skills/lore/scripts/find_duplicates.py +211 -0
- package/bundled-skills/lore/scripts/find_stale.py +116 -0
- package/bundled-skills/lore/scripts/history.py +527 -0
- package/bundled-skills/lore/scripts/id_hash.py +32 -0
- package/bundled-skills/lore/scripts/list_entries.py +183 -0
- package/bundled-skills/lovable-cleanup/SKILL.md +3 -3
- package/bundled-skills/makepad-splash/SKILL.md +1 -1
- package/bundled-skills/mmx-cli/SKILL.md +14 -1
- package/bundled-skills/monopoly/SKILL.md +5 -0
- package/bundled-skills/monopoly/patterns/SKILL.md +4 -0
- package/bundled-skills/monopoly/scale-benchmarks/SKILL.md +4 -0
- package/bundled-skills/monopoly/security-checklist/SKILL.md +4 -0
- package/bundled-skills/monopoly/tech-matrix/SKILL.md +4 -0
- package/bundled-skills/mtls-configuration/SKILL.md +1 -1
- package/bundled-skills/network-101/SKILL.md +4 -5
- package/bundled-skills/nika/SKILL.md +270 -0
- package/bundled-skills/not-a-vibe-coder/SKILL.md +4 -0
- package/bundled-skills/ontoly-software-graph/SKILL.md +160 -0
- package/bundled-skills/openclaw-github-repo-commander/SKILL.md +1 -1
- package/bundled-skills/pilot-protocol/SKILL.md +6 -3
- package/bundled-skills/postgres-readonly-queries/README.md +77 -0
- package/bundled-skills/postgres-readonly-queries/SKILL.md +152 -0
- package/bundled-skills/postgres-readonly-queries/connections.example.json +34 -0
- package/bundled-skills/postgres-readonly-queries/requirements.txt +1 -0
- package/bundled-skills/postgres-readonly-queries/scripts/query.py +262 -0
- package/bundled-skills/pptx-deck-creation/SKILL.md +275 -0
- package/bundled-skills/pptx-deck-creation/references/audit-checklist.md +156 -0
- package/bundled-skills/pptx-deck-creation/references/design-profiles.md +96 -0
- package/bundled-skills/pptx-deck-creation/references/python-snippets.md +150 -0
- package/bundled-skills/pptx-deck-creation/references/reference-deck-analysis.md +91 -0
- package/bundled-skills/pptx-deck-creation/references/visual-asset-adapters.md +158 -0
- package/bundled-skills/privilege-escalation-methods/SKILL.md +1 -1
- package/bundled-skills/product-decision-agent/SKILL.md +94 -0
- package/bundled-skills/product-decision-agent/agents/openai.yaml +7 -0
- package/bundled-skills/product-decision-agent/references/methodology-basis.md +232 -0
- package/bundled-skills/product-decision-agent/references/product-playbooks.md +813 -0
- package/bundled-skills/product-decision-agent/references/reasoning-engine.md +289 -0
- package/bundled-skills/product-decision-agent/references/response-examples.md +279 -0
- package/bundled-skills/product-decision-agent/scripts/quality_gate.py +221 -0
- package/bundled-skills/product-decision-agent/scripts/test_quality_gate.py +94 -0
- package/bundled-skills/pytest-skill/reference/playbook.md +358 -0
- package/bundled-skills/quit-sponsor/SKILL.md +96 -0
- package/bundled-skills/rclone-cli/references/providers/s3.md +1 -1
- package/bundled-skills/security-and-hardening/SKILL.md +1 -1
- package/bundled-skills/semgrep-rule-creator/SKILL.md +6 -6
- package/bundled-skills/skill-creator/README.md +3 -3
- package/bundled-skills/skill-creator/SKILL.md +8 -4
- package/bundled-skills/skill-creator/scripts/quick_validate.py +1 -1
- package/bundled-skills/sql-injection-testing/SKILL.md +4 -4
- package/bundled-skills/subagent-orchestrator/README.md +1 -1
- package/bundled-skills/super-code/bash/SKILL.md +3 -0
- package/bundled-skills/super-code/c/SKILL.md +3 -0
- package/bundled-skills/super-code/cpp/SKILL.md +3 -0
- package/bundled-skills/super-code/csharp/SKILL.md +3 -0
- package/bundled-skills/super-code/dart/SKILL.md +3 -0
- package/bundled-skills/super-code/elixir/SKILL.md +3 -0
- package/bundled-skills/super-code/go/SKILL.md +3 -0
- package/bundled-skills/super-code/java/SKILL.md +3 -0
- package/bundled-skills/super-code/kotlin/SKILL.md +3 -0
- package/bundled-skills/super-code/php/SKILL.md +3 -0
- package/bundled-skills/super-code/python/SKILL.md +3 -0
- package/bundled-skills/super-code/ruby/SKILL.md +3 -0
- package/bundled-skills/super-code/rust/SKILL.md +3 -0
- package/bundled-skills/super-code/scala/SKILL.md +3 -0
- package/bundled-skills/super-code/swift/SKILL.md +3 -0
- package/bundled-skills/super-code/typescript/SKILL.md +3 -0
- package/bundled-skills/telegram-bot-messaging/README.md +185 -0
- package/bundled-skills/telegram-bot-messaging/SKILL.md +101 -0
- package/bundled-skills/telegram-bot-messaging/scripts/telegram.sh +420 -0
- package/bundled-skills/test-guard/SKILL.md +132 -0
- package/bundled-skills/test-guard/references/jest.md +50 -0
- package/bundled-skills/test-guard/references/llm-app-testing.md +40 -0
- package/bundled-skills/test-guard/references/phpunit.md +56 -0
- package/bundled-skills/test-guard/references/pytest.md +65 -0
- package/bundled-skills/trading-ledger/SKILL.md +11 -3
- package/bundled-skills/uizze-ui-research/SKILL.md +93 -0
- package/bundled-skills/vibe-code-auditor/SKILL.md +1 -1
- package/bundled-skills/vibecode-production-qa-validator/SKILL.md +1 -1
- package/bundled-skills/videodb/scripts/ws_listener.py +29 -15
- package/bundled-skills/voice-ai-engine-development/README.md +1 -1
- package/bundled-skills/vulnerability-scanner/SKILL.md +1 -1
- package/bundled-skills/warehouse/SKILL.md +154 -0
- package/bundled-skills/woo-guard/SKILL.md +113 -0
- package/bundled-skills/woo-guard/references/checkout-and-money.md +64 -0
- package/bundled-skills/woo-guard/references/hpos-and-crud.md +87 -0
- package/bundled-skills/woo-guard/references/review-checklist.md +44 -0
- package/bundled-skills/woo-guard/references/sources.md +17 -0
- package/bundled-skills/wp-guard/SKILL.md +119 -0
- package/bundled-skills/wp-guard/references/i18n.md +88 -0
- package/bundled-skills/wp-guard/references/performance.md +70 -0
- package/bundled-skills/wp-guard/references/review-checklist.md +57 -0
- package/bundled-skills/wp-guard/references/security.md +100 -0
- package/bundled-skills/wp-guard/references/sources.md +27 -0
- package/bundled-skills/writing-skills/gotchas.md +1 -1
- package/bundled-skills/writing-skills/references/standards/metadata-standard.md +1 -1
- package/bundled-skills/x-twitter-scraper/SKILL.md +14 -32
- package/bundled-skills/xss-html-injection/SKILL.md +3 -3
- package/bundled-skills/youtube-seo-optimizer/SKILL.md +1 -1
- package/bundled-skills/youtube-summarizer/CHANGELOG.md +2 -2
- package/package.json +1 -1
- 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()
|