opencode-skills-collection 3.0.33 → 3.0.35
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 +2 -1
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +3 -3
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/mmx-cli/SKILL.md +5 -2
- package/bundled-skills/nextjs-seo-indexing/SKILL.md +3 -3
- package/bundled-skills/schema-markup-generator/SKILL.md +1 -1
- package/bundled-skills/social-metadata-hardening/SKILL.md +4 -3
- package/bundled-skills/social-post-writer-seo/SKILL.md +19 -0
- package/bundled-skills/user-thoughts/SKILL.md +236 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/README.ai.md +13 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/define.ini +3 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/README.ai.md +25 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/backlog.md +19 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/dev-stack.md +7 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/general.md +7 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/plans.md +7 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/rules.md +7 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/details.md +7 -0
- package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/outline.md +7 -0
- package/bundled-skills/user-thoughts/references/commands.md +54 -0
- package/bundled-skills/user-thoughts/references/edge-cases.md +84 -0
- package/bundled-skills/user-thoughts/references/safety.md +65 -0
- package/bundled-skills/user-thoughts/references/sortin.md +76 -0
- package/bundled-skills/user-thoughts/scripts/common.py +62 -0
- package/bundled-skills/user-thoughts/scripts/ignore_ops.py +125 -0
- package/bundled-skills/user-thoughts/scripts/init.py +63 -0
- package/bundled-skills/user-thoughts/scripts/show_mdbase.py +93 -0
- package/bundled-skills/user-thoughts/scripts/show_raw.py +42 -0
- package/bundled-skills/user-thoughts/scripts/sortin.py +211 -0
- package/bundled-skills/user-thoughts/scripts/status.py +56 -0
- package/bundled-skills/user-thoughts/scripts/toggle.py +68 -0
- package/bundled-skills/user-thoughts/scripts/write_raw.py +106 -0
- package/bundled-skills/vibe-code-cleanup/SKILL.md +4 -4
- package/bundled-skills/vibecode-production-qa-validator/SKILL.md +3 -2
- package/package.json +1 -1
- package/skills_index.json +26 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Show unprocessed raw files."""
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from common import find_ustht, is_processed
|
|
6
|
+
|
|
7
|
+
HELP = """Usage: python show_raw.py [--help]
|
|
8
|
+
|
|
9
|
+
Show unprocessed #raw/ files, including filenames, entry counts, and content.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
15
|
+
print(HELP)
|
|
16
|
+
sys.exit(0)
|
|
17
|
+
|
|
18
|
+
ustht = find_ustht()
|
|
19
|
+
if ustht is None:
|
|
20
|
+
print("Error: .ustht/ was not found. Run /ustht init first.")
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
raw_dir = ustht / "raw"
|
|
24
|
+
if not raw_dir.exists():
|
|
25
|
+
print("No unprocessed records.")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
files = [f for f in sorted(raw_dir.glob("*.md"), reverse=True) if not is_processed(f)]
|
|
29
|
+
if not files:
|
|
30
|
+
print("No unprocessed records. All raw files are marked processed.")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
for f in files:
|
|
34
|
+
content = f.read_text(encoding="utf-8").strip()
|
|
35
|
+
entry_count = sum(1 for line in content.splitlines() if line.strip().startswith("- ["))
|
|
36
|
+
print(f"#{f.name} ({entry_count} unprocessed entries):")
|
|
37
|
+
print(content)
|
|
38
|
+
print()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
main()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Soft-maintain raw user-thought entries into mdbase."""
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from common import find_ustht, read_define_ini, write_define_ini, is_processed, validate_dim_name
|
|
9
|
+
|
|
10
|
+
HELP = """Usage: python sortin.py [--dry] [--help]
|
|
11
|
+
|
|
12
|
+
Soft maintenance: parse unprocessed #raw/*.md files, append entries to matching
|
|
13
|
+
mdbase dimensions, mark raw files as processed, and update LAST_SORTIN.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--dry Preview changes without writing
|
|
17
|
+
--help Show this help text
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_raw_file(filepath: Path):
|
|
22
|
+
"""Parse raw entries from one file."""
|
|
23
|
+
entries = []
|
|
24
|
+
date = filepath.stem.split("-", 3)
|
|
25
|
+
if len(date) >= 3:
|
|
26
|
+
date = "-".join(date[:3])
|
|
27
|
+
else:
|
|
28
|
+
date = datetime.now().strftime("%Y-%m-%d")
|
|
29
|
+
|
|
30
|
+
for line in filepath.read_text(encoding="utf-8").splitlines():
|
|
31
|
+
line = line.strip()
|
|
32
|
+
match = re.match(r"^- \[(\d{2}:\d{2})\] (.*)$", line)
|
|
33
|
+
if not match:
|
|
34
|
+
continue
|
|
35
|
+
time, content = match.groups()
|
|
36
|
+
dim = "general"
|
|
37
|
+
text = content
|
|
38
|
+
if " | suggested-dim:" in content:
|
|
39
|
+
text, dim = content.rsplit(" | suggested-dim:", 1)
|
|
40
|
+
dim = dim.strip()
|
|
41
|
+
if not validate_dim_name(dim):
|
|
42
|
+
dim = "general"
|
|
43
|
+
entries.append({"time": time, "text": text.strip(), "dimension": dim, "date": date})
|
|
44
|
+
return entries
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def dim_path(mdbase: Path, dim: str) -> Path:
|
|
48
|
+
"""Return the target file path for a dimension."""
|
|
49
|
+
if dim == "backlog":
|
|
50
|
+
return mdbase / "backlog.md"
|
|
51
|
+
return mdbase / "details" / f"{dim}.md"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def count_entries(path: Path) -> int:
|
|
55
|
+
if not path.exists():
|
|
56
|
+
return 0
|
|
57
|
+
return sum(1 for line in path.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- "))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def append_entries(path: Path, entries):
|
|
61
|
+
"""Append entries grouped by date to one dimension file."""
|
|
62
|
+
by_date = defaultdict(list)
|
|
63
|
+
for entry in entries:
|
|
64
|
+
by_date[entry["date"]].append(entry)
|
|
65
|
+
|
|
66
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
if not path.exists():
|
|
68
|
+
title = path.stem.replace("-", " ").title()
|
|
69
|
+
path.write_text(f"# {title}\n\n> Project memory for `{path.stem}`.\n\n", encoding="utf-8")
|
|
70
|
+
|
|
71
|
+
content = path.read_text(encoding="utf-8").rstrip()
|
|
72
|
+
for date, date_entries in sorted(by_date.items()):
|
|
73
|
+
lines = [f"- {entry['text']}" for entry in date_entries]
|
|
74
|
+
block = "\n".join(lines)
|
|
75
|
+
heading = f"## {date}"
|
|
76
|
+
if heading in content:
|
|
77
|
+
content_lines = content.splitlines()
|
|
78
|
+
heading_idx = next(i for i, line in enumerate(content_lines) if line.strip() == heading)
|
|
79
|
+
insert_idx = len(content_lines)
|
|
80
|
+
for i in range(heading_idx + 1, len(content_lines)):
|
|
81
|
+
if content_lines[i].startswith("## "):
|
|
82
|
+
insert_idx = i
|
|
83
|
+
break
|
|
84
|
+
before = content_lines[:insert_idx]
|
|
85
|
+
after = content_lines[insert_idx:]
|
|
86
|
+
if before and before[-1].strip():
|
|
87
|
+
before.append("")
|
|
88
|
+
before.extend(lines)
|
|
89
|
+
if after:
|
|
90
|
+
before.append("")
|
|
91
|
+
before.extend(after)
|
|
92
|
+
content = "\n".join(before).rstrip()
|
|
93
|
+
else:
|
|
94
|
+
content = f"{content}\n\n{heading}\n\n{block}".rstrip()
|
|
95
|
+
path.write_text(content + "\n", encoding="utf-8")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def mark_processed(filepath: Path):
|
|
99
|
+
"""Insert the processed marker at the top of a raw file."""
|
|
100
|
+
content = filepath.read_text(encoding="utf-8")
|
|
101
|
+
if content.split("\n", 1)[0].strip() != "<!-- processed -->":
|
|
102
|
+
filepath.write_text("<!-- processed -->\n" + content, encoding="utf-8")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def update_index(mdbase: Path):
|
|
106
|
+
"""Rebuild mdbase/README.ai.md with dimension counts."""
|
|
107
|
+
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
108
|
+
details = mdbase / "details"
|
|
109
|
+
dims = []
|
|
110
|
+
if details.exists():
|
|
111
|
+
dims = sorted(p.relative_to(details).with_suffix("").as_posix() for p in details.rglob("*.md"))
|
|
112
|
+
|
|
113
|
+
rows = ["| File | Dimension | Entries |", "|------|-----------|---------|"]
|
|
114
|
+
backlog = mdbase / "backlog.md"
|
|
115
|
+
if backlog.exists():
|
|
116
|
+
rows.append(f"| [backlog.md](backlog.md) | backlog | {count_entries(backlog)} |")
|
|
117
|
+
for dim in dims:
|
|
118
|
+
path = details / f"{dim}.md"
|
|
119
|
+
rows.append(f"| [details/{dim}.md](details/{dim}.md) | {dim} | {count_entries(path)} |")
|
|
120
|
+
|
|
121
|
+
content = "\n".join([
|
|
122
|
+
"# user-thoughts mdbase Index",
|
|
123
|
+
"",
|
|
124
|
+
"This directory stores user-provided project decisions, constraints, preferences, and plans.",
|
|
125
|
+
"",
|
|
126
|
+
f"Last updated: {now}",
|
|
127
|
+
"",
|
|
128
|
+
"## Maintenance Rules",
|
|
129
|
+
"",
|
|
130
|
+
"- Preserve user wording and constraints.",
|
|
131
|
+
"- Append entries by date under `## yyyy-mm-dd` headings.",
|
|
132
|
+
"- Prefer existing dimensions before creating new ones.",
|
|
133
|
+
"- Mark deprecated content instead of silently deleting history.",
|
|
134
|
+
"",
|
|
135
|
+
"## Document Index",
|
|
136
|
+
"",
|
|
137
|
+
*rows,
|
|
138
|
+
"",
|
|
139
|
+
])
|
|
140
|
+
(mdbase / "README.ai.md").write_text(content, encoding="utf-8")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def main():
|
|
144
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
145
|
+
print(HELP)
|
|
146
|
+
sys.exit(0)
|
|
147
|
+
|
|
148
|
+
dry = "--dry" in sys.argv
|
|
149
|
+
ustht = find_ustht()
|
|
150
|
+
if ustht is None:
|
|
151
|
+
print("Error: .ustht/ was not found. Run /ustht init first.")
|
|
152
|
+
sys.exit(1)
|
|
153
|
+
|
|
154
|
+
cfg = read_define_ini(ustht)
|
|
155
|
+
if cfg.get("SKILL_STATUS") == "off":
|
|
156
|
+
print("SKILL is off; write ignored. Run /ustht skill on to enable it.")
|
|
157
|
+
sys.exit(0)
|
|
158
|
+
|
|
159
|
+
raw_dir = ustht / "raw"
|
|
160
|
+
if not raw_dir.exists():
|
|
161
|
+
print("No unprocessed records.")
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
raw_files = [f for f in sorted(raw_dir.glob("*.md")) if not is_processed(f)]
|
|
165
|
+
if not raw_files:
|
|
166
|
+
print("No unprocessed records. All raw files are marked processed.")
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
all_entries = []
|
|
170
|
+
entries_by_file = {}
|
|
171
|
+
for f in raw_files:
|
|
172
|
+
entries = parse_raw_file(f)
|
|
173
|
+
entries_by_file[f] = entries
|
|
174
|
+
all_entries.extend(entries)
|
|
175
|
+
|
|
176
|
+
if not all_entries:
|
|
177
|
+
print("No valid entries found in raw files.")
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
grouped = defaultdict(list)
|
|
181
|
+
for entry in all_entries:
|
|
182
|
+
grouped[entry["dimension"]].append(entry)
|
|
183
|
+
|
|
184
|
+
print("Preview mode:" if dry else f"Soft maintenance complete. Processed {len(all_entries)} thoughts:")
|
|
185
|
+
mdbase = ustht / "mdbase"
|
|
186
|
+
for dim, entries in sorted(grouped.items()):
|
|
187
|
+
target = dim_path(mdbase, dim)
|
|
188
|
+
label = f"{dim}.md" if target.exists() else f"{dim}.md [new dimension]"
|
|
189
|
+
sample = entries[0]["text"][:60]
|
|
190
|
+
print(f" -> {label}: +{len(entries)} ({sample})")
|
|
191
|
+
|
|
192
|
+
if dry:
|
|
193
|
+
print(f" {len(all_entries)} total entries; no files were changed.")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
for dim, entries in grouped.items():
|
|
197
|
+
append_entries(dim_path(mdbase, dim), entries)
|
|
198
|
+
|
|
199
|
+
for f in raw_files:
|
|
200
|
+
if entries_by_file.get(f):
|
|
201
|
+
mark_processed(f)
|
|
202
|
+
|
|
203
|
+
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
204
|
+
cfg["LAST_SORTIN"] = now
|
|
205
|
+
write_define_ini(ustht, cfg)
|
|
206
|
+
update_index(mdbase)
|
|
207
|
+
print(f" LAST_SORTIN updated to {now}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Show current user-thoughts runtime status."""
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from common import find_ustht, read_define_ini, is_processed
|
|
6
|
+
|
|
7
|
+
HELP = """Usage: python status.py [--help]
|
|
8
|
+
|
|
9
|
+
Show SKILL_STATUS, INSTANT_STATUS, LAST_SORTIN, raw file counts, and mdbase
|
|
10
|
+
dimension counts.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def count_raw(raw_dir: Path):
|
|
15
|
+
"""Return total and unprocessed raw file counts."""
|
|
16
|
+
if not raw_dir.exists():
|
|
17
|
+
return 0, 0
|
|
18
|
+
files = list(raw_dir.glob("*.md"))
|
|
19
|
+
unprocessed = sum(1 for f in files if not is_processed(f))
|
|
20
|
+
return len(files), unprocessed
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def count_dims(mdbase: Path):
|
|
24
|
+
"""Count dimension files under mdbase/details/."""
|
|
25
|
+
details = mdbase / "details"
|
|
26
|
+
if not details.exists():
|
|
27
|
+
return 0
|
|
28
|
+
return len(list(details.rglob("*.md")))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main():
|
|
32
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
33
|
+
print(HELP)
|
|
34
|
+
sys.exit(0)
|
|
35
|
+
|
|
36
|
+
ustht = find_ustht()
|
|
37
|
+
if ustht is None:
|
|
38
|
+
print("Error: .ustht/ was not found. Run /ustht init first.")
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
cfg = read_define_ini(ustht)
|
|
42
|
+
skill_status = cfg.get("SKILL_STATUS", "unknown")
|
|
43
|
+
instant_status = cfg.get("INSTANT_STATUS", "unknown")
|
|
44
|
+
last_sortin = cfg.get("LAST_SORTIN", "never") or "never"
|
|
45
|
+
total_raw, unprocessed_raw = count_raw(ustht / "raw")
|
|
46
|
+
dims = count_dims(ustht / "mdbase")
|
|
47
|
+
|
|
48
|
+
print(f"SKILL_STATUS={skill_status}")
|
|
49
|
+
print(f"INSTANT_STATUS={instant_status}")
|
|
50
|
+
print(f"LAST_SORTIN={last_sortin}")
|
|
51
|
+
print(f"raw={unprocessed_raw} unprocessed / {total_raw} total")
|
|
52
|
+
print(f"dims={dims}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
main()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Toggle SKILL_STATUS and INSTANT_STATUS."""
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from common import find_ustht, read_define_ini, write_define_ini
|
|
5
|
+
|
|
6
|
+
HELP = """Usage: python toggle.py skill|instant [on|off] [--help]
|
|
7
|
+
|
|
8
|
+
Subcommands:
|
|
9
|
+
skill Show SKILL_STATUS
|
|
10
|
+
skill on|off Set SKILL_STATUS
|
|
11
|
+
instant Show INSTANT_STATUS
|
|
12
|
+
instant on|off Set INSTANT_STATUS
|
|
13
|
+
|
|
14
|
+
Note: instant on requires SKILL_STATUS=on.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
20
|
+
print(HELP)
|
|
21
|
+
sys.exit(0)
|
|
22
|
+
|
|
23
|
+
ustht = find_ustht()
|
|
24
|
+
if ustht is None:
|
|
25
|
+
print("Error: .ustht/ was not found. Run /ustht init first.")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
if len(sys.argv) < 2:
|
|
29
|
+
print(f"Usage: {sys.argv[0]} skill|instant [on|off]")
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
cmd = sys.argv[1]
|
|
33
|
+
if cmd not in {"skill", "instant"}:
|
|
34
|
+
print(f"Unknown command: {cmd}. Available: skill, instant")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
|
|
37
|
+
cfg = read_define_ini(ustht)
|
|
38
|
+
ini_key = "SKILL_STATUS" if cmd == "skill" else "INSTANT_STATUS"
|
|
39
|
+
|
|
40
|
+
if len(sys.argv) == 2:
|
|
41
|
+
print(f"{ini_key}={cfg.get(ini_key, 'unknown')}")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
val = sys.argv[2]
|
|
45
|
+
if val not in {"on", "off"}:
|
|
46
|
+
print(f"Invalid value: {val}. Available values: on | off")
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
|
|
49
|
+
if cmd == "instant" and val == "on" and cfg.get("SKILL_STATUS") == "off":
|
|
50
|
+
print("SKILL is off; instant capture cannot be enabled. Run /ustht skill on first.")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
cfg[ini_key] = val
|
|
54
|
+
if cmd == "skill" and val == "off":
|
|
55
|
+
cfg["INSTANT_STATUS"] = "off"
|
|
56
|
+
write_define_ini(ustht, cfg)
|
|
57
|
+
|
|
58
|
+
if cmd == "skill":
|
|
59
|
+
if val == "off":
|
|
60
|
+
print("SKILL is off. Instant capture has been paused.")
|
|
61
|
+
else:
|
|
62
|
+
print("SKILL is on.")
|
|
63
|
+
else:
|
|
64
|
+
print("Instant capture is on." if val == "on" else "Instant capture is off.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Append one thought to today's raw file."""
|
|
2
|
+
import sys
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from common import find_ustht, read_define_ini, validate_dim_name
|
|
7
|
+
|
|
8
|
+
HELP = """Usage: python write_raw.py "thought text" [--dim dimension] [--help]
|
|
9
|
+
|
|
10
|
+
Append one thought to today's #raw/ markdown file.
|
|
11
|
+
|
|
12
|
+
Arguments:
|
|
13
|
+
"thought text" Thought text to record (required)
|
|
14
|
+
--dim dimension Suggested dimension, such as rules or ui/outline
|
|
15
|
+
--help Show this help text
|
|
16
|
+
|
|
17
|
+
Behavior:
|
|
18
|
+
- If today's raw file is already processed, creates a numbered file such as 2026-06-01-2.md.
|
|
19
|
+
- If the day has more than five raw entries, suggests /ustht sortin.
|
|
20
|
+
- If SKILL_STATUS=off, exits without writing.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def count_today_raw(raw_dir: Path) -> int:
|
|
25
|
+
"""Count unprocessed entries across today's raw files."""
|
|
26
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
27
|
+
count = 0
|
|
28
|
+
for f in sorted(raw_dir.glob(f"{today}*.md")):
|
|
29
|
+
content = f.read_text(encoding="utf-8")
|
|
30
|
+
first_line = content.split("\n", 1)[0].strip()
|
|
31
|
+
if first_line == "<!-- processed -->":
|
|
32
|
+
continue
|
|
33
|
+
count += sum(1 for line in content.splitlines() if line.strip().startswith("- ["))
|
|
34
|
+
return count
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main():
|
|
38
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
39
|
+
print(HELP)
|
|
40
|
+
sys.exit(0)
|
|
41
|
+
|
|
42
|
+
ustht = find_ustht()
|
|
43
|
+
if ustht is None:
|
|
44
|
+
print("Error: .ustht/ was not found. Run /ustht init first.")
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
cfg = read_define_ini(ustht)
|
|
48
|
+
if cfg.get("SKILL_STATUS") == "off":
|
|
49
|
+
print("SKILL is off; write ignored.")
|
|
50
|
+
sys.exit(0)
|
|
51
|
+
|
|
52
|
+
thought = None
|
|
53
|
+
dim = None
|
|
54
|
+
args = sys.argv[1:]
|
|
55
|
+
i = 0
|
|
56
|
+
while i < len(args):
|
|
57
|
+
if args[i] == "--dim" and i + 1 < len(args):
|
|
58
|
+
dim = args[i + 1]
|
|
59
|
+
i += 2
|
|
60
|
+
elif thought is None:
|
|
61
|
+
thought = args[i]
|
|
62
|
+
i += 1
|
|
63
|
+
else:
|
|
64
|
+
i += 1
|
|
65
|
+
|
|
66
|
+
if not thought:
|
|
67
|
+
print("Error: missing thought text.")
|
|
68
|
+
print(f"Usage: {sys.argv[0]} \"thought text\" [--dim dimension]")
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
if dim and not validate_dim_name(dim):
|
|
72
|
+
print(f"Invalid dimension name: {dim}. Use lowercase letters, digits, hyphens, and optional / subdirectories.")
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
|
|
75
|
+
raw_dir = ustht / "raw"
|
|
76
|
+
raw_dir.mkdir(exist_ok=True)
|
|
77
|
+
|
|
78
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
79
|
+
now = datetime.now().strftime("%H:%M")
|
|
80
|
+
raw_file = raw_dir / f"{today}.md"
|
|
81
|
+
|
|
82
|
+
if raw_file.exists():
|
|
83
|
+
first_line = raw_file.read_text(encoding="utf-8").split("\n", 1)[0].strip()
|
|
84
|
+
if first_line == "<!-- processed -->":
|
|
85
|
+
seq = 2
|
|
86
|
+
while (raw_dir / f"{today}-{seq}.md").exists():
|
|
87
|
+
seq += 1
|
|
88
|
+
raw_file = raw_dir / f"{today}-{seq}.md"
|
|
89
|
+
|
|
90
|
+
thought_clean = thought.replace("\n", " ").replace("\r", "")
|
|
91
|
+
suffix = f" | suggested-dim:{dim}" if dim else ""
|
|
92
|
+
entry = f"- [{now}] {thought_clean}{suffix}"
|
|
93
|
+
|
|
94
|
+
if raw_file.exists():
|
|
95
|
+
content = raw_file.read_text(encoding="utf-8").rstrip()
|
|
96
|
+
raw_file.write_text(f"{content}\n{entry}\n", encoding="utf-8")
|
|
97
|
+
else:
|
|
98
|
+
raw_file.write_text(f"{entry}\n", encoding="utf-8")
|
|
99
|
+
|
|
100
|
+
count = count_today_raw(raw_dir)
|
|
101
|
+
if count > 5:
|
|
102
|
+
print(f"Today has {count} recorded thoughts. Consider running /ustht sortin.")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vibe-code-cleanup
|
|
3
|
-
description: "Safe production cleanup and hardening for vibe-coded fullstack apps (Next.js, React, Node.js, etc.). Removes dead imports, unused files, broken references
|
|
3
|
+
description: "Safe production cleanup and hardening for vibe-coded fullstack apps (Next.js, React, Node.js, etc.). Removes dead imports, unused files, and broken references without breaking routes or APIs."
|
|
4
4
|
category: fullstack
|
|
5
5
|
risk: safe
|
|
6
6
|
source: self
|
|
@@ -48,8 +48,8 @@ Before changing anything, map the codebase:
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# List all pages/routes
|
|
51
|
-
find . -
|
|
52
|
-
find
|
|
51
|
+
find . -type f \( -name 'page.js' -o -name 'page.jsx' -o -name 'page.ts' -o -name 'page.tsx' \)
|
|
52
|
+
find pages -type f \( -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \) | rg -v '/_' | sort
|
|
53
53
|
|
|
54
54
|
# Find broken imports (TS projects)
|
|
55
55
|
npx tsc --noEmit 2>&1 | head -80
|
|
@@ -185,7 +185,7 @@ If build or typecheck breaks → **revert the last batch** before continuing.
|
|
|
185
185
|
|
|
186
186
|
Each commit should be a single logical unit:
|
|
187
187
|
|
|
188
|
-
```
|
|
188
|
+
```text
|
|
189
189
|
fix: remove broken import in app/blog/page.js
|
|
190
190
|
refactor: consolidate social metadata into lib/socialMetadata.js
|
|
191
191
|
chore: remove verified-unused utils/oldHelper.js
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vibecode-production-qa-validator
|
|
3
|
-
description: "End-to-end production QA, build verification, and launch-readiness checklist for fullstack Next.js apps
|
|
3
|
+
description: "End-to-end production QA, build verification, and launch-readiness checklist for fullstack Next.js apps. Covers TypeScript, linting, tests, build, SEO tags, route regression, and sitemap validation."
|
|
4
4
|
category: devops
|
|
5
5
|
risk: safe
|
|
6
6
|
source: self
|
|
@@ -69,6 +69,7 @@ grep "Static pages\|○\|●" build.log | tail -5
|
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
### Route symbols explained
|
|
72
|
+
|
|
72
73
|
| Symbol | Meaning | Expected? |
|
|
73
74
|
|--------|---------|-----------|
|
|
74
75
|
| `○` | Static (rendered at build time) | ✓ Good for most pages |
|
|
@@ -179,7 +180,7 @@ git status | grep -E "\.next|node_modules"
|
|
|
179
180
|
```
|
|
180
181
|
|
|
181
182
|
Good commit message format:
|
|
182
|
-
```
|
|
183
|
+
```text
|
|
183
184
|
type(scope): brief description
|
|
184
185
|
|
|
185
186
|
fix(seo): add canonical tags to all blog pages
|
package/package.json
CHANGED
package/skills_index.json
CHANGED
|
@@ -25211,7 +25211,7 @@
|
|
|
25211
25211
|
"path": "skills/schema-markup-generator",
|
|
25212
25212
|
"category": "seo",
|
|
25213
25213
|
"name": "schema-markup-generator",
|
|
25214
|
-
"description": "Generate and implement JSON-LD structured data for web apps,
|
|
25214
|
+
"description": "Generate and implement JSON-LD structured data for web apps, blogs, FAQs, and SaaS sites. Supports WebSite, SoftwareApplication, BlogPosting, FAQPage, HowTo, and more.",
|
|
25215
25215
|
"risk": "safe",
|
|
25216
25216
|
"source": "self",
|
|
25217
25217
|
"date_added": "2026-05-31",
|
|
@@ -27561,7 +27561,7 @@
|
|
|
27561
27561
|
"path": "skills/social-metadata-hardening",
|
|
27562
27562
|
"category": "seo",
|
|
27563
27563
|
"name": "social-metadata-hardening",
|
|
27564
|
-
"description": "Fix social sharing previews so URLs render as rich cards on Facebook, LinkedIn, X/Twitter, WhatsApp, Telegram,
|
|
27564
|
+
"description": "Fix social sharing previews so URLs render as rich cards on Facebook, LinkedIn, X/Twitter, WhatsApp, Telegram, and more. Covers OG tags, Twitter cards, absolute image URLs, and debugging.",
|
|
27565
27565
|
"risk": "safe",
|
|
27566
27566
|
"source": "self",
|
|
27567
27567
|
"date_added": "2026-05-31",
|
|
@@ -30732,6 +30732,28 @@
|
|
|
30732
30732
|
"reasons": []
|
|
30733
30733
|
}
|
|
30734
30734
|
},
|
|
30735
|
+
{
|
|
30736
|
+
"id": "user-thoughts",
|
|
30737
|
+
"path": "skills/user-thoughts",
|
|
30738
|
+
"category": "web-development",
|
|
30739
|
+
"name": "user-thoughts",
|
|
30740
|
+
"description": "Persist user decisions and project constraints to mdbase across sessions. Trigger on /user-thoughts or /ustht, or when the user discusses architecture, tech stack, rules, UI/UX, or project memory.",
|
|
30741
|
+
"risk": "safe",
|
|
30742
|
+
"source": "https://github.com/JularDepick/user-thoughts.SKILL",
|
|
30743
|
+
"date_added": "2026-05-31",
|
|
30744
|
+
"plugin": {
|
|
30745
|
+
"targets": {
|
|
30746
|
+
"codex": "supported",
|
|
30747
|
+
"claude": "supported"
|
|
30748
|
+
},
|
|
30749
|
+
"setup": {
|
|
30750
|
+
"type": "none",
|
|
30751
|
+
"summary": "",
|
|
30752
|
+
"docs": null
|
|
30753
|
+
},
|
|
30754
|
+
"reasons": []
|
|
30755
|
+
}
|
|
30756
|
+
},
|
|
30735
30757
|
{
|
|
30736
30758
|
"id": "using-git-worktrees",
|
|
30737
30759
|
"path": "skills/using-git-worktrees",
|
|
@@ -31225,7 +31247,7 @@
|
|
|
31225
31247
|
"path": "skills/vibe-code-cleanup",
|
|
31226
31248
|
"category": "fullstack",
|
|
31227
31249
|
"name": "vibe-code-cleanup",
|
|
31228
|
-
"description": "Safe production cleanup and hardening for vibe-coded fullstack apps (Next.js, React, Node.js, etc.). Removes dead imports, unused files, broken references
|
|
31250
|
+
"description": "Safe production cleanup and hardening for vibe-coded fullstack apps (Next.js, React, Node.js, etc.). Removes dead imports, unused files, and broken references without breaking routes or APIs.",
|
|
31229
31251
|
"risk": "safe",
|
|
31230
31252
|
"source": "self",
|
|
31231
31253
|
"date_added": "2026-05-31",
|
|
@@ -31247,7 +31269,7 @@
|
|
|
31247
31269
|
"path": "skills/vibecode-production-qa-validator",
|
|
31248
31270
|
"category": "devops",
|
|
31249
31271
|
"name": "vibecode-production-qa-validator",
|
|
31250
|
-
"description": "End-to-end production QA, build verification, and launch-readiness checklist for fullstack Next.js apps
|
|
31272
|
+
"description": "End-to-end production QA, build verification, and launch-readiness checklist for fullstack Next.js apps. Covers TypeScript, linting, tests, build, SEO tags, route regression, and sitemap validation.",
|
|
31251
31273
|
"risk": "safe",
|
|
31252
31274
|
"source": "self",
|
|
31253
31275
|
"date_added": "2026-05-31",
|