opencode-skills-collection 3.0.34 → 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.
Files changed (45) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +2 -1
  2. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  3. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  4. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  5. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  6. package/bundled-skills/docs/users/bundles.md +1 -1
  7. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  8. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  9. package/bundled-skills/docs/users/getting-started.md +3 -3
  10. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  11. package/bundled-skills/docs/users/usage.md +4 -4
  12. package/bundled-skills/docs/users/visual-guide.md +4 -4
  13. package/bundled-skills/mmx-cli/SKILL.md +5 -2
  14. package/bundled-skills/nextjs-seo-indexing/SKILL.md +3 -3
  15. package/bundled-skills/schema-markup-generator/SKILL.md +1 -1
  16. package/bundled-skills/social-metadata-hardening/SKILL.md +4 -3
  17. package/bundled-skills/social-post-writer-seo/SKILL.md +19 -0
  18. package/bundled-skills/user-thoughts/SKILL.md +236 -0
  19. package/bundled-skills/user-thoughts/assets/Runtime-Template/README.ai.md +13 -0
  20. package/bundled-skills/user-thoughts/assets/Runtime-Template/define.ini +3 -0
  21. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/README.ai.md +25 -0
  22. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/backlog.md +19 -0
  23. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/dev-stack.md +7 -0
  24. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/general.md +7 -0
  25. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/plans.md +7 -0
  26. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/rules.md +7 -0
  27. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/details.md +7 -0
  28. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/outline.md +7 -0
  29. package/bundled-skills/user-thoughts/references/commands.md +54 -0
  30. package/bundled-skills/user-thoughts/references/edge-cases.md +84 -0
  31. package/bundled-skills/user-thoughts/references/safety.md +65 -0
  32. package/bundled-skills/user-thoughts/references/sortin.md +76 -0
  33. package/bundled-skills/user-thoughts/scripts/common.py +62 -0
  34. package/bundled-skills/user-thoughts/scripts/ignore_ops.py +125 -0
  35. package/bundled-skills/user-thoughts/scripts/init.py +63 -0
  36. package/bundled-skills/user-thoughts/scripts/show_mdbase.py +93 -0
  37. package/bundled-skills/user-thoughts/scripts/show_raw.py +42 -0
  38. package/bundled-skills/user-thoughts/scripts/sortin.py +211 -0
  39. package/bundled-skills/user-thoughts/scripts/status.py +56 -0
  40. package/bundled-skills/user-thoughts/scripts/toggle.py +68 -0
  41. package/bundled-skills/user-thoughts/scripts/write_raw.py +106 -0
  42. package/bundled-skills/vibe-code-cleanup/SKILL.md +4 -4
  43. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +3 -2
  44. package/package.json +1 -1
  45. package/skills_index.json +26 -4
@@ -0,0 +1,7 @@
1
+ # General Project Notes
2
+
3
+ > Project-relevant thoughts that do not fit another dimension yet.
4
+
5
+ ## Current Notes
6
+
7
+ <!-- Append dated user thoughts here. -->
@@ -0,0 +1,7 @@
1
+ # Project Plans
2
+
3
+ > Directional plans, milestones, priorities, sequencing, and strategic project ideas.
4
+
5
+ ## Current Plans
6
+
7
+ <!-- Append dated user plans here. -->
@@ -0,0 +1,7 @@
1
+ # Project Rules and Constraints
2
+
3
+ > User-defined rules, constraints, conventions, preferences, and non-negotiables.
4
+
5
+ ## Current Rules
6
+
7
+ <!-- Append dated user rules here. -->
@@ -0,0 +1,7 @@
1
+ # UI Details
2
+
3
+ > Component-level UI preferences, interaction details, copy details, spacing, states, and visual refinements.
4
+
5
+ ## Current Details
6
+
7
+ <!-- Append dated UI detail decisions here. -->
@@ -0,0 +1,7 @@
1
+ # UI Outline
2
+
3
+ > Product-level UI direction, layout concepts, design principles, themes, and screen-level decisions.
4
+
5
+ ## Current Direction
6
+
7
+ <!-- Append dated UI outline decisions here. -->
@@ -0,0 +1,54 @@
1
+ # Command Reference
2
+
3
+ `user-thoughts` accepts `/user-thoughts` and `/ustht`. They are equivalent.
4
+
5
+ ## Command Summary
6
+
7
+ | Command | Meaning |
8
+ |---|---|
9
+ | `/ustht init` | Initialize `.ustht/` in the current project. |
10
+ | `/ustht status` | Show skill state, instant state, raw count, and dimension count. |
11
+ | `/ustht skill` | Show `SKILL_STATUS`. |
12
+ | `/ustht skill on|off` | Enable or disable write operations. |
13
+ | `/ustht instant` | Show `INSTANT_STATUS`. |
14
+ | `/ustht instant on|off` | Enable or disable instant capture. |
15
+ | `/ustht sortin [--dry]` | Append raw entries into mdbase. |
16
+ | `/ustht resort [--dry]` | Reorganize all mdbase content semantically. |
17
+ | `/ustht raw` | Show unprocessed raw entries. |
18
+ | `/ustht mdbase show [--all|--dimension]` | Show the index, all dimensions, or one dimension. |
19
+ | `/ustht mdbase export [--all|--dimension]` | Export mdbase content. |
20
+ | `/ustht import <path>` | Import project-relevant decisions from markdown files. |
21
+ | `/ustht ignore start|end` | Start or stop a temporary ignore interval. |
22
+ | `/ustht ignore --last` | Remove the last raw entry and record it as ignored. |
23
+ | `/ustht ignore show` | Show ignored entries. |
24
+
25
+ ## Natural-Language Mapping
26
+
27
+ Agents may map clear user intent to commands:
28
+
29
+ - "turn on project memory" -> `/ustht skill on && instant on`
30
+ - "stop recording this" -> `/ustht ignore start`
31
+ - "start recording again" -> `/ustht ignore end`
32
+ - "organize what I said" -> `/ustht sortin`
33
+ - "show what you remember" -> `/ustht mdbase show`
34
+ - "ignore the last note" -> `/ustht ignore --last`
35
+
36
+ When intent is ambiguous, ask a short clarification instead of guessing.
37
+
38
+ ## Chained Commands
39
+
40
+ Commands can be chained with `&&` and should run left to right. Stop only if a command fails in a way that makes the following command unsafe.
41
+
42
+ Example:
43
+
44
+ ```text
45
+ /ustht skill on && instant on && status
46
+ ```
47
+
48
+ ## Dimension Arguments
49
+
50
+ Dimension names must pass validation:
51
+
52
+ - lowercase letters, digits, and hyphens;
53
+ - `/` allowed for subdirectories, such as `ui/outline`;
54
+ - no spaces, `..`, backslashes, absolute paths, or reserved names.
@@ -0,0 +1,84 @@
1
+ # Edge Cases
2
+
3
+ Use these examples to keep behavior predictable.
4
+
5
+ ## No Runtime Directory
6
+
7
+ User: `/ustht status`
8
+
9
+ Agent: `.ustht/ was not found. Run /ustht init first.`
10
+
11
+ ## Skill Disabled
12
+
13
+ If `SKILL_STATUS=off`, write commands should not modify files. Read commands such as `status`, `raw`, and `mdbase show` may still run.
14
+
15
+ ## Instant Mode Disabled
16
+
17
+ When `INSTANT_STATUS=off`, do not capture natural-language thoughts automatically. Explicit commands still run.
18
+
19
+ ## Command Plus Thought
20
+
21
+ User: `Make buttons use 8px radius, and /ustht status`
22
+
23
+ Agent: run the command and record the UI preference if instant capture is enabled. Do not record the command text itself.
24
+
25
+ ## Message Suffix Ignore
26
+
27
+ User: `This color experiment is temporary /ustht ignore`
28
+
29
+ Agent: do not write it to raw. Record it in `ignored/` as a suffix-ignored entry if ignore tracking is available.
30
+
31
+ ## Ignore Interval
32
+
33
+ User: `/ustht ignore start`
34
+
35
+ Agent: enter ignore mode for the current context.
36
+
37
+ User: `Try three throwaway layouts.`
38
+
39
+ Agent: do not record the thought.
40
+
41
+ User: `/ustht ignore end`
42
+
43
+ Agent: exit ignore mode.
44
+
45
+ ## Last Entry Ignore
46
+
47
+ User: `/ustht ignore --last`
48
+
49
+ Agent: remove the last unprocessed raw entry and append it to `ignored/`. If no entry exists, say so without failing.
50
+
51
+ ## Processed Marker Mentioned by User
52
+
53
+ User: `Maybe we should use <!-- processed --> as a completion marker in docs.`
54
+
55
+ Agent: preserve that text as ordinary user content. `sortin` checks only the first line of raw files.
56
+
57
+ ## Illegal Dimension Names
58
+
59
+ Reject dimensions containing spaces, `..`, backslashes, absolute paths, or unsafe characters.
60
+
61
+ Examples:
62
+
63
+ - Reject `../../../etc/passwd`.
64
+ - Reject `my file`.
65
+ - Accept `ui/details`.
66
+ - Accept `dev-stack`.
67
+
68
+ ## Chained Commands
69
+
70
+ User: `/ustht skill on && instant on && status`
71
+
72
+ Agent: run commands left to right and report a compact summary.
73
+
74
+ ## Import With No Relevant Content
75
+
76
+ If `/ustht import README.md` finds no project decisions, report that no entries were extracted and do not write empty dimension sections.
77
+
78
+ ## Multi-Agent Writes
79
+
80
+ No file locks are provided. If multiple agents are active, coordinate before `sortin` or `resort` to avoid conflicting writes.
81
+
82
+ ## Sensitive Content
83
+
84
+ If the user says a thought contains secrets or personal data, prefer ignore behavior and remind them that `.ustht/` is not automatically redacted.
@@ -0,0 +1,65 @@
1
+ # Safety and Data Integrity
2
+
3
+ This document defines path safety, input validation, and data-integrity rules for `user-thoughts`.
4
+
5
+ ## Path Safety
6
+
7
+ All runtime file operations must stay inside `#ustht/` unless an import command reads project-local markdown files.
8
+
9
+ Dimension names are used to construct paths, so validate them strictly:
10
+
11
+ | Rule | Reason |
12
+ |---|---|
13
+ | Each path segment uses `[a-z0-9-]` only | Prevents shell and path surprises. |
14
+ | Each segment starts and ends with `[a-z0-9]` | Avoids hidden or malformed files. |
15
+ | `/` is allowed only as a dimension subdirectory separator | Supports `ui/outline`. |
16
+ | `..`, backslashes, spaces, and absolute paths are forbidden | Prevents path traversal. |
17
+ | Reserved names are forbidden | Avoids collisions with runtime folders. |
18
+
19
+ Reserved names: `backlog`, `readme-ai`, `export`, `raw`, `ignored`, `define`, `general`.
20
+
21
+ ## Content Safety
22
+
23
+ Raw entries use this format:
24
+
25
+ ```text
26
+ - [HH:MM] original user text | suggested-dim:dimension
27
+ ```
28
+
29
+ The suffix is agent-generated metadata. User text may contain markdown and should be preserved as written. Parse the last ` | suggested-dim:` separator only.
30
+
31
+ `<!-- processed -->` is meaningful only as the first line of a raw file. If the user mentions that string inside a thought, treat it as normal content.
32
+
33
+ ## define.ini Safety
34
+
35
+ Allowed keys and values:
36
+
37
+ | Key | Allowed value |
38
+ |---|---|
39
+ | `SKILL_STATUS` | `on` or `off` |
40
+ | `INSTANT_STATUS` | `on` or `off` |
41
+ | `LAST_SORTIN` | empty or `yyyy-mm-dd HH:MM` |
42
+
43
+ Values must not contain newlines or `=`. Write the whole file rather than appending partial fragments.
44
+
45
+ ## Shell Safety
46
+
47
+ - Do not execute user-provided shell commands.
48
+ - Do not use `eval` or dynamic execution.
49
+ - Construct file paths only from validated dimensions or fixed template paths.
50
+ - During initialization, copy known template files safely instead of recursively shell-copying arbitrary directories.
51
+
52
+ ## Data Integrity
53
+
54
+ `sortin` is not fully atomic. To reduce partial-write risk:
55
+
56
+ 1. Parse raw entries first.
57
+ 2. Write dimension files.
58
+ 3. Mark raw files as processed only after writes succeed.
59
+ 4. Update `LAST_SORTIN` last.
60
+
61
+ Processed raw files are retained for traceability. Dimension files should be appended or marked deprecated; do not silently delete user history.
62
+
63
+ ## Sensitive Data
64
+
65
+ The skill preserves original wording and does not redact secrets or personal data. Users should use ignore commands before sensitive content is captured, and teams should protect `.ustht/` with normal repository and filesystem hygiene.
@@ -0,0 +1,76 @@
1
+ # Sortin and Resort Algorithms
2
+
3
+ This document describes how raw thoughts become organized mdbase records.
4
+
5
+ ## Commands
6
+
7
+ | Command | Behavior |
8
+ |---|---|
9
+ | `/ustht sortin` | Soft maintenance: append new raw entries into mdbase without restructuring existing content. |
10
+ | `/ustht resort` | Hard maintenance: review all mdbase content, deduplicate, reclassify, merge, and update indexes. |
11
+ | `--dry` | Preview intended changes without writing. |
12
+
13
+ ## Raw Format
14
+
15
+ Before processing:
16
+
17
+ ```text
18
+ - [14:30] Make buttons use 8px radius | suggested-dim:ui/details
19
+ - [14:45] Login should use a dark theme | suggested-dim:ui/outline
20
+ - [15:10] Use REST APIs, not GraphQL | suggested-dim:dev-stack
21
+ ```
22
+
23
+ After processing, the first line of the file becomes:
24
+
25
+ ```text
26
+ <!-- processed -->
27
+ ```
28
+
29
+ ## Soft Append Format
30
+
31
+ A raw entry is appended under a date heading in the selected dimension file:
32
+
33
+ ```markdown
34
+ ## 2026-06-01
35
+
36
+ - Make buttons use 8px radius
37
+ ```
38
+
39
+ Rules:
40
+
41
+ - Preserve original wording.
42
+ - Remove only the timestamp and `suggested-dim` suffix.
43
+ - Group entries by raw-file date.
44
+ - Append to an existing date section when present.
45
+ - Create a new date section when needed.
46
+
47
+ ## Dimension Management
48
+
49
+ Create a new dimension only when the thought does not fit an existing dimension. Dimension names must be kebab-case path segments and must pass safety validation.
50
+
51
+ When `resort` finds overlapping dimensions, merge them into the clearest target and preserve provenance. When a dimension is no longer useful, mark it with `<!-- deprecated -->` instead of deleting it.
52
+
53
+ ## Classification Priority
54
+
55
+ 1. User-specified dimension.
56
+ 2. Exact existing dimension match.
57
+ 3. Closest semantic existing dimension, with a note if the fit is weak.
58
+ 4. `general.md` fallback.
59
+
60
+ ## Import Algorithm
61
+
62
+ `/ustht import <path>` scans markdown files under a safe project-local path and extracts project-relevant user decisions, constraints, and requirements. It should not modify source files. Imported entries should include source provenance such as `[source:docs/design.md]`.
63
+
64
+ Skip ordinary technical docs, generated docs, API reference text, and code comments unless they clearly encode a user decision.
65
+
66
+ ## Summary Output
67
+
68
+ After `sortin`, report the number of processed entries and destination dimensions, for example:
69
+
70
+ ```text
71
+ Soft maintenance complete. Processed 3 thoughts:
72
+ -> ui/details.md: +1
73
+ -> ui/outline.md: +1
74
+ -> dev-stack.md: +1
75
+ LAST_SORTIN updated to 2026-06-01 15:30
76
+ ```
@@ -0,0 +1,62 @@
1
+ """Shared helpers for user-thoughts scripts."""
2
+ import re
3
+ from pathlib import Path
4
+
5
+
6
+ def find_ustht() -> Path | None:
7
+ """Find .ustht/ in the current directory or one of its parents."""
8
+ cwd = Path.cwd()
9
+ for d in [cwd, *cwd.parents]:
10
+ ustht = d / ".ustht"
11
+ if ustht.is_dir():
12
+ return ustht
13
+ return None
14
+
15
+
16
+ def find_skill_dir() -> Path | None:
17
+ """Find the installed user-thoughts skill directory."""
18
+ script_dir = Path(__file__).resolve().parent
19
+ skill_dir = script_dir.parent
20
+ if (skill_dir / "SKILL.md").exists():
21
+ return skill_dir
22
+ return None
23
+
24
+
25
+ def read_define_ini(ustht: Path) -> dict:
26
+ """Read define.ini and return key/value pairs."""
27
+ ini = ustht / "define.ini"
28
+ if not ini.exists():
29
+ return {}
30
+ result = {}
31
+ for line in ini.read_text(encoding="utf-8").splitlines():
32
+ line = line.strip()
33
+ if "=" in line and not line.startswith("#"):
34
+ k, v = line.split("=", 1)
35
+ result[k.strip()] = v.strip()
36
+ return result
37
+
38
+
39
+ def write_define_ini(ustht: Path, cfg: dict):
40
+ """Replace define.ini with the provided key/value pairs."""
41
+ ini = ustht / "define.ini"
42
+ lines = [f"{k}={v}" for k, v in cfg.items()]
43
+ ini.write_text("\n".join(lines) + "\n", encoding="utf-8")
44
+
45
+
46
+ def is_processed(filepath: Path) -> bool:
47
+ """Return true when the first raw-file line is the processed marker."""
48
+ first_line = filepath.read_text(encoding="utf-8").split("\n", 1)[0].strip()
49
+ return first_line == "<!-- processed -->"
50
+
51
+
52
+ def validate_dim_name(dim: str) -> bool:
53
+ """Validate a dimension path made of safe kebab-case segments."""
54
+ reserved = {"raw", "ignored", "export", "define", "readme-ai"}
55
+ if not dim or len(dim) > 64 or ".." in dim or "\\" in dim or " " in dim:
56
+ return False
57
+ for part in dim.split("/"):
58
+ if part in reserved:
59
+ return False
60
+ if not part or not re.match(r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", part):
61
+ return False
62
+ return True
@@ -0,0 +1,125 @@
1
+ """Manage ignored user-thought entries."""
2
+ import sys
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ from common import find_ustht
7
+
8
+ HELP = """Usage: python ignore_ops.py show|remove_last|add_suffix "text" [--help]
9
+
10
+ Subcommands:
11
+ show List entries under #ignored/
12
+ remove_last Remove the latest raw entry and move it to #ignored/
13
+ add_suffix "text" Add a suffix-ignored entry to #ignored/
14
+ """
15
+
16
+
17
+ def find_last_raw_entry(raw_dir: Path):
18
+ """Return (file path, line index, entry text) for the latest raw entry."""
19
+ files = sorted(raw_dir.glob("*.md"), reverse=True)
20
+ for f in files:
21
+ lines = f.read_text(encoding="utf-8").splitlines()
22
+ if lines and lines[0].strip() == "<!-- processed -->":
23
+ continue
24
+ for idx in range(len(lines) - 1, -1, -1):
25
+ if lines[idx].strip().startswith("- ["):
26
+ return f, idx, lines[idx]
27
+ return None, None, None
28
+
29
+
30
+ def remove_line(filepath: Path, idx: int):
31
+ """Remove one line from a file."""
32
+ lines = filepath.read_text(encoding="utf-8").splitlines()
33
+ del lines[idx]
34
+ filepath.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
35
+
36
+
37
+ def append_to_ignored(ignored_dir: Path, text: str, reason: str):
38
+ """Append one ignored entry to today's ignored file."""
39
+ ignored_dir.mkdir(exist_ok=True)
40
+ today = datetime.now().strftime("%Y-%m-%d")
41
+ now = datetime.now().strftime("%H:%M")
42
+ f = ignored_dir / f"{today}.md"
43
+ clean = text.strip()
44
+ if " | suggested-dim:" in clean:
45
+ clean = clean.rsplit(" | suggested-dim:", 1)[0]
46
+ entry = f"- [{now}] {clean} ({reason})"
47
+ if f.exists():
48
+ content = f.read_text(encoding="utf-8").rstrip()
49
+ f.write_text(f"{content}\n{entry}\n", encoding="utf-8")
50
+ else:
51
+ f.write_text(f"{entry}\n", encoding="utf-8")
52
+
53
+
54
+ def show_ignored(ignored_dir: Path):
55
+ """Print all ignored entries."""
56
+ if not ignored_dir.exists():
57
+ print("No ignored entries.")
58
+ return
59
+ files = sorted(ignored_dir.glob("*.md"), reverse=True)
60
+ if not files:
61
+ print("No ignored entries.")
62
+ return
63
+ for f in files:
64
+ entries = [line for line in f.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- [")]
65
+ if entries:
66
+ print(f"#{f.name} ({len(entries)} entries):")
67
+ for entry in entries:
68
+ print(entry)
69
+
70
+
71
+ def remove_last(ustht: Path):
72
+ raw_dir = ustht / "raw"
73
+ if not raw_dir.exists():
74
+ print("No previous thought to ignore.")
75
+ return
76
+ filepath, idx, entry = find_last_raw_entry(raw_dir)
77
+ if filepath is None:
78
+ print("No previous thought to ignore.")
79
+ return
80
+ remove_line(filepath, idx)
81
+ append_to_ignored(ustht / "ignored", entry, "ignored with --last")
82
+ display = entry
83
+ if "] " in display:
84
+ display = display.split("] ", 1)[1]
85
+ if " | suggested-dim:" in display:
86
+ display = display.rsplit(" | suggested-dim:", 1)[0]
87
+ print(f"Ignored previous thought: {display}")
88
+
89
+
90
+ def add_suffix(ustht: Path, text: str):
91
+ append_to_ignored(ustht / "ignored", text, "ignored by suffix")
92
+ print("Ignored current message.")
93
+
94
+
95
+ def main():
96
+ if "--help" in sys.argv or "-h" in sys.argv:
97
+ print(HELP)
98
+ sys.exit(0)
99
+
100
+ ustht = find_ustht()
101
+ if ustht is None:
102
+ print("Error: .ustht/ was not found. Run /ustht init first.")
103
+ sys.exit(1)
104
+
105
+ if len(sys.argv) < 2:
106
+ print(f"Usage: {sys.argv[0]} show|remove_last|add_suffix \"text\"")
107
+ sys.exit(1)
108
+
109
+ cmd = sys.argv[1]
110
+ if cmd == "show":
111
+ show_ignored(ustht / "ignored")
112
+ elif cmd == "remove_last":
113
+ remove_last(ustht)
114
+ elif cmd == "add_suffix":
115
+ if len(sys.argv) < 3:
116
+ print("Error: add_suffix requires text.")
117
+ sys.exit(1)
118
+ add_suffix(ustht, sys.argv[2])
119
+ else:
120
+ print(f"Unknown command: {cmd}. Available: show, remove_last, add_suffix")
121
+ sys.exit(1)
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()
@@ -0,0 +1,63 @@
1
+ """Initialize the .ustht/ runtime directory from templates."""
2
+ import shutil
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from common import find_skill_dir
7
+
8
+ HELP = """Usage: python init.py [--help]
9
+
10
+ Create .ustht/ in the current working directory, copy the runtime templates,
11
+ and create raw/, ignored/, and export/ directories. Existing .ustht/ content is
12
+ not overwritten.
13
+ """
14
+
15
+
16
+ def copy_template(src: Path, dst: Path):
17
+ """Copy template files while skipping symlinks."""
18
+ for item in src.rglob("*"):
19
+ rel = item.relative_to(src)
20
+ target = dst / rel
21
+ if item.is_symlink():
22
+ continue
23
+ if item.is_dir():
24
+ target.mkdir(parents=True, exist_ok=True)
25
+ else:
26
+ target.parent.mkdir(parents=True, exist_ok=True)
27
+ shutil.copy2(item, target)
28
+
29
+
30
+ def main():
31
+ if "--help" in sys.argv or "-h" in sys.argv:
32
+ print(HELP)
33
+ sys.exit(0)
34
+
35
+ target = Path.cwd() / ".ustht"
36
+ if target.exists():
37
+ print("Already initialized; .ustht/ exists, skipping creation.")
38
+ sys.exit(0)
39
+
40
+ skill_dir = find_skill_dir()
41
+ if skill_dir is None:
42
+ print("Error: SKILL.md was not found. Ensure this script is inside user-thoughts/scripts/.")
43
+ sys.exit(1)
44
+
45
+ template = skill_dir / "assets" / "Runtime-Template"
46
+ if not template.exists():
47
+ print(f"Error: template directory does not exist: {template}")
48
+ sys.exit(1)
49
+
50
+ target.mkdir()
51
+ copy_template(template, target)
52
+ for name in ["raw", "ignored", "export"]:
53
+ (target / name).mkdir(exist_ok=True)
54
+
55
+ define = target / "define.ini"
56
+ if not define.exists():
57
+ define.write_text("SKILL_STATUS=on\nINSTANT_STATUS=off\nLAST_SORTIN=\n", encoding="utf-8")
58
+
59
+ print("Initialized .ustht/.")
60
+
61
+
62
+ if __name__ == "__main__":
63
+ main()
@@ -0,0 +1,93 @@
1
+ """Show the mdbase index or dimension content."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ from common import find_ustht, validate_dim_name
6
+
7
+ HELP = """Usage: python show_mdbase.py show [--all|--dimension] [--help]
8
+
9
+ Subcommands:
10
+ show Show README.ai.md index
11
+ show --all List all dimensions and entry counts
12
+ show <dimension> Show one dimension file
13
+ """
14
+
15
+
16
+ def show_index(mdbase: Path):
17
+ index = mdbase / "README.ai.md"
18
+ if not index.exists():
19
+ print("mdbase/README.ai.md does not exist.")
20
+ return
21
+ print(index.read_text(encoding="utf-8"))
22
+
23
+
24
+ def list_dims(mdbase: Path):
25
+ details = mdbase / "details"
26
+ if not details.exists():
27
+ return []
28
+ return sorted(p.relative_to(details).with_suffix("").as_posix() for p in details.rglob("*.md"))
29
+
30
+
31
+ def show_dim(mdbase: Path, dim: str):
32
+ if not validate_dim_name(dim):
33
+ print(f"Invalid dimension name: {dim}. Use lowercase letters, digits, hyphens, and optional / subdirectories.")
34
+ return
35
+ if dim == "backlog":
36
+ path = mdbase / "backlog.md"
37
+ else:
38
+ path = mdbase / "details" / f"{dim}.md"
39
+ if not path.exists():
40
+ print(f"mdbase/details/{dim}.md does not exist yet.")
41
+ return
42
+ print(path.read_text(encoding="utf-8"))
43
+
44
+
45
+ def show_all(mdbase: Path):
46
+ details = mdbase / "details"
47
+ if not details.exists():
48
+ print("mdbase/details/ does not exist.")
49
+ return
50
+ dims = list_dims(mdbase)
51
+ if not dims:
52
+ print("mdbase has no dimension files.")
53
+ return
54
+ print(f"mdbase has {len(dims)} dimensions:")
55
+ for dim in dims:
56
+ path = details / f"{dim}.md"
57
+ lines = [line for line in path.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- ")]
58
+ print(f" {dim}.md: {len(lines)} entries")
59
+
60
+
61
+ def main():
62
+ if "--help" in sys.argv or "-h" in sys.argv:
63
+ print(HELP)
64
+ sys.exit(0)
65
+
66
+ ustht = find_ustht()
67
+ if ustht is None:
68
+ print("Error: .ustht/ was not found. Run /ustht init first.")
69
+ sys.exit(1)
70
+
71
+ mdbase = ustht / "mdbase"
72
+ if not mdbase.exists():
73
+ print("mdbase is not initialized. Run /ustht init first.")
74
+ return
75
+
76
+ args = sys.argv[1:]
77
+ if not args or args[0] != "show":
78
+ print(f"Usage: {sys.argv[0]} show [--all|--dimension]")
79
+ sys.exit(1)
80
+
81
+ rest = args[1:]
82
+ if not rest:
83
+ show_index(mdbase)
84
+ elif rest[0] == "--all":
85
+ show_all(mdbase)
86
+ elif rest[0].startswith("--"):
87
+ show_dim(mdbase, rest[0][2:])
88
+ else:
89
+ show_dim(mdbase, rest[0])
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main()