Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93fbed2705 | |||
| 65be6196c1 | |||
| 7dc530b4c9 | |||
| b159f8b1ff | |||
| cff005b038 | |||
| 56cabf33c6 | |||
| 13dcea781d | |||
| 8b2cf41f13 | |||
| 3d57db9644 |
@@ -11,6 +11,5 @@
|
||||
"repository": "https://github.com/mvanhorn/last30days-skill",
|
||||
"license": "MIT",
|
||||
"keywords": ["research", "reddit", "twitter", "youtube", "tiktok", "instagram", "trends", "prompts", "polymarket", "github", "perplexity", "threads", "pinterest", "eli5", "hacker-news"],
|
||||
"skills": ["./"],
|
||||
"hooks": {}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,25 @@ claude plugin update last30days@last30days-skill
|
||||
clawhub install last30days-official
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
Gemini CLI supports installing extensions from GitHub repositories, but as of Gemini CLI v0.9.0 there is an upstream installer bug that can fail with:
|
||||
|
||||
`Configuration file not found at /tmp/gemini-extensionXXXXXX/gemini-extension.json`
|
||||
|
||||
even when `gemini-extension.json` exists at the repo root.
|
||||
|
||||
Upstream bug:
|
||||
- https://github.com/google-gemini/gemini-cli/issues/11452
|
||||
|
||||
Workarounds:
|
||||
1) Clone locally, then install from the local path
|
||||
```bash
|
||||
git clone https://github.com/mvanhorn/last30days-skill
|
||||
gemini extensions install ./last30days-skill
|
||||
```
|
||||
2) If GitHub install fails, use the OpenClaw or Claude Code install paths above.
|
||||
|
||||
### Manual
|
||||
```bash
|
||||
git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days
|
||||
|
||||
@@ -12,7 +12,11 @@ check_perms() {
|
||||
local file="$1"
|
||||
if [[ ! -f "$file" ]]; then return; fi
|
||||
local perms
|
||||
perms=$(stat -f '%Lp' "$file" 2>/dev/null || stat -c '%a' "$file" 2>/dev/null || echo "")
|
||||
# Try GNU stat first (Linux), fall back to BSD stat (macOS).
|
||||
# On Linux, `stat -f` prints filesystem info (not permissions) and exits 0,
|
||||
# so the previous BSD-first ordering left $perms as multi-line garbage on
|
||||
# every Linux session start and printed a false WARNING.
|
||||
perms=$(stat -c '%a' "$file" 2>/dev/null || stat -f '%Lp' "$file" 2>/dev/null || echo "")
|
||||
if [[ -n "$perms" && "$perms" != "600" && "$perms" != "400" ]]; then
|
||||
echo "/last30days: WARNING — $file has permissions $perms (should be 600)."
|
||||
echo " Fix: chmod 600 $file"
|
||||
|
||||
@@ -103,7 +103,7 @@ def save_output(report: schema.Report, emit: str, save_dir: str, suffix: str = "
|
||||
content = emit_output(report, emit)
|
||||
else:
|
||||
content = render.render_full(report)
|
||||
out_path.write_text(content)
|
||||
out_path.write_text(content, encoding="utf-8")
|
||||
return out_path
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ COMMON_TARGETS=(
|
||||
# but local development needs the cache kept in sync with the repo.
|
||||
# Do NOT add ~/.claude/skills/last30days - it creates a duplicate
|
||||
# /last30days-3 in the slash command menu alongside the plugin version.
|
||||
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3/3.0.0-alpha"
|
||||
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3/3.0.0"
|
||||
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3-nogem/3.0.0-nogem"
|
||||
"$HOME/.agents/skills/last30days"
|
||||
"$HOME/.codex/skills/last30days"
|
||||
|
||||
@@ -135,6 +135,14 @@ class CliV3Tests(unittest.TestCase):
|
||||
payload = json.loads(path.read_text())
|
||||
self.assertEqual("OpenClaw vs NanoClaw", payload["topic"])
|
||||
|
||||
def test_save_output_writes_utf8_encoded_markdown(self):
|
||||
report = self.make_report()
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with mock.patch("pathlib.Path.write_text", autospec=True, return_value=1) as write_text:
|
||||
cli.save_output(report, "md", tmp)
|
||||
_, kwargs = write_text.call_args
|
||||
self.assertEqual("utf-8", kwargs.get("encoding"))
|
||||
|
||||
def test_persist_report_updates_run_status_on_success_and_failure(self):
|
||||
report = self.make_report()
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _skill_version() -> str:
|
||||
text = (ROOT / "SKILL.md").read_text(encoding="utf-8")
|
||||
match = re.search(r'^version:\s*"([^"]+)"\s*$', text, re.MULTILINE)
|
||||
if not match:
|
||||
raise AssertionError("SKILL.md version frontmatter not found")
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class TestVersionConsistency(unittest.TestCase):
|
||||
def test_root_skill_header_matches_frontmatter_version(self) -> None:
|
||||
text = (ROOT / "SKILL.md").read_text(encoding="utf-8")
|
||||
version = _skill_version()
|
||||
self.assertIn(f"# last30days v{version}:", text)
|
||||
|
||||
def test_sync_cache_path_uses_skill_version(self) -> None:
|
||||
sync_text = (ROOT / "scripts" / "sync.sh").read_text(encoding="utf-8")
|
||||
version = _skill_version()
|
||||
self.assertIn(f'last30days-3/{version}"', sync_text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user