@caddis/cli 0.4.2 → 0.4.4
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/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_glossary.py +268 -0
- package/bundles/antigravity-plugin/scripts/setup_project_ai.py +43 -0
- package/bundles/antigravity-plugin/skills/_registry.md +2 -0
- package/bundles/antigravity-plugin/skills/catchup/SKILL.md +57 -0
- package/bundles/antigravity-plugin/skills/glossary/SKILL.md +85 -0
- package/bundles/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""caddis_glossary — propose the words a project INVENTED, so they can be defined once.
|
|
2
|
+
|
|
3
|
+
WHY. Every project coins vocabulary out of ordinary English, and those are the worst words in
|
|
4
|
+
the codebase: a reader who guesses from the plain word gets them wrong and never knows. caddis
|
|
5
|
+
itself coined `pool`, `relay`, `drift`, `parking-lot`, `live-fire` — and its own glossary went
|
|
6
|
+
five and a half months without mentioning any of them, because nothing measured the gap.
|
|
7
|
+
|
|
8
|
+
WHAT THIS DOES AND DOES NOT DO. It PROPOSES candidates by measurement. It never writes a
|
|
9
|
+
definition, because a definition is domain knowledge that only a person or a model reading the
|
|
10
|
+
code can supply. A file of correct words with empty meanings is worse than no file: it looks
|
|
11
|
+
authoritative and teaches nothing. `/caddis:glossary` is the half that writes the meanings.
|
|
12
|
+
|
|
13
|
+
python caddis_glossary.py # propose candidates, ranked
|
|
14
|
+
python caddis_glossary.py --check # exit 1 if a proposed term has no entry
|
|
15
|
+
python caddis_glossary.py --scaffold # create the file if it is missing
|
|
16
|
+
|
|
17
|
+
HOW A CANDIDATE IS CHOSEN. Frequency alone is useless — "the file" beats every real term. A
|
|
18
|
+
word is a candidate when it is COMMON IN THIS REPO but is not ordinary technical English. The
|
|
19
|
+
stop-list below is the load-bearing part, and it is deliberately generous: a false positive
|
|
20
|
+
costs a human three seconds to ignore, while a false negative leaves a coined word undefined
|
|
21
|
+
forever, which is the failure being fixed.
|
|
22
|
+
|
|
23
|
+
ACCURACY, MEASURED. On a normal application repo it is good: run against `docket` it proposed
|
|
24
|
+
`board`, `lane`, `card`, `gate` — that project's actual coined vocabulary, top of the list. On a
|
|
25
|
+
HARNESS repo full of documentation ABOUT agents it is noisier, because words like `protocol` and
|
|
26
|
+
`intent` are both ordinary English and genuinely overloaded here. It is a suggester, not an
|
|
27
|
+
oracle; a human or the model decides what earns an entry.
|
|
28
|
+
|
|
29
|
+
Stdlib only, cross-platform, and it never raises on a bad file — it is run by a command and by
|
|
30
|
+
a gate, and a traceback from either is worse than a missing suggestion.
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import collections
|
|
36
|
+
import os
|
|
37
|
+
import re
|
|
38
|
+
import sys
|
|
39
|
+
|
|
40
|
+
# ── what a project's own vocabulary lives in ────────────────────────────────────────────
|
|
41
|
+
DOC_GLOBS = (".md",)
|
|
42
|
+
SKIP_DIRS = {
|
|
43
|
+
".git", "node_modules", ".venv", "venv", "dist", "build", "__pycache__",
|
|
44
|
+
".mypy_cache", ".pytest_cache", "vendor", "site-packages", ".next", "target",
|
|
45
|
+
# Vendored and reference material is someone ELSE'S vocabulary. Without these, caddis's
|
|
46
|
+
# own run proposed `aws4` (33 files) and `mxgraph` (11) out of bundled AWS and draw-io
|
|
47
|
+
# reference docs — words no caddis reader will ever meet.
|
|
48
|
+
"references", "assets", "fixtures", ".archive", "archive", "examples", "sample",
|
|
49
|
+
"samples", "third_party", "vendored", "node_modules", "bundles", "runtime-resources",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Ordinary technical English. A word here is never proposed, however often it appears.
|
|
53
|
+
# Longer than it looks necessary on purpose: the cost of a false positive is a human ignoring
|
|
54
|
+
# one line; the cost of a false negative is a coined word nobody ever defines.
|
|
55
|
+
COMMON = {
|
|
56
|
+
# english scaffolding
|
|
57
|
+
"the", "and", "for", "with", "that", "this", "from", "into", "than", "then", "when",
|
|
58
|
+
"where", "which", "what", "who", "why", "how", "not", "but", "all", "any", "each",
|
|
59
|
+
"one", "two", "only", "also", "more", "most", "less", "some", "such", "same", "other",
|
|
60
|
+
"its", "it's", "you", "your", "our", "their", "they", "them", "has", "have", "had",
|
|
61
|
+
"was", "were", "been", "being", "are", "can", "will", "would", "should", "must", "may",
|
|
62
|
+
"does", "did", "done", "doing", "use", "used", "using", "make", "made", "get", "got",
|
|
63
|
+
"see", "read", "write", "run", "runs", "running", "ran", "set", "put", "take", "give",
|
|
64
|
+
"new", "old", "first", "last", "next", "now", "here", "there", "before", "after",
|
|
65
|
+
"over", "under", "out", "off", "own", "per", "via", "yes", "no", "if", "in", "on", "of",
|
|
66
|
+
"to", "at", "by", "as", "is", "be", "or", "so", "up", "do", "an", "a",
|
|
67
|
+
# ordinary software vocabulary — predictable from the word itself
|
|
68
|
+
"file", "files", "path", "paths", "dir", "directory", "folder", "code", "line", "lines",
|
|
69
|
+
"test", "tests", "testing", "error", "errors", "bug", "fix", "fixed", "build", "built",
|
|
70
|
+
"commit", "commits", "branch", "merge", "push", "pull", "repo", "repository", "git",
|
|
71
|
+
"api", "cli", "url", "http", "https", "json", "yaml", "toml", "html", "css", "sql",
|
|
72
|
+
"user", "users", "name", "names", "value", "values", "key", "keys", "type", "types",
|
|
73
|
+
"list", "lists", "table", "tables", "row", "rows", "column", "field", "fields",
|
|
74
|
+
"function", "method", "class", "module", "package", "library", "script", "scripts",
|
|
75
|
+
"server", "client", "request", "response", "data", "input", "output", "config",
|
|
76
|
+
"version", "release", "install", "update", "delete", "create", "add", "remove",
|
|
77
|
+
"return", "returns", "call", "calls", "check", "checks", "log", "logs", "note", "notes",
|
|
78
|
+
"step", "steps", "case", "cases", "example", "default", "option", "options", "flag",
|
|
79
|
+
"flags", "command", "commands", "tool", "tools", "agent", "agents", "model", "models",
|
|
80
|
+
"session", "sessions", "context", "prompt", "prompts", "token", "tokens", "cache",
|
|
81
|
+
"state", "status", "result", "results", "change", "changes", "work", "works", "time",
|
|
82
|
+
"does", "doc", "docs", "document", "section", "page", "text", "content", "format",
|
|
83
|
+
"project", "projects", "team", "user", "system", "service", "process", "task", "tasks",
|
|
84
|
+
"issue", "issues", "problem", "solution", "reason", "cause", "point", "part", "way",
|
|
85
|
+
"thing", "things", "need", "needs", "want", "know", "like", "just", "even", "still",
|
|
86
|
+
"never", "always", "every", "both", "because", "while", "since", "about", "against",
|
|
87
|
+
# added after the first real run proposed all of these — ordinary in any codebase
|
|
88
|
+
"skill", "skills", "plan", "plans", "phase", "phases", "design", "designs", "review",
|
|
89
|
+
"reviews", "mode", "modes", "pattern", "patterns", "implementation", "reference",
|
|
90
|
+
"description", "descriptions", "workflow", "workflows", "feature", "features",
|
|
91
|
+
"component", "components", "interface", "structure", "approach", "detail", "details",
|
|
92
|
+
"requirement", "requirements", "specification", "architecture", "instructions",
|
|
93
|
+
"guidelines", "standard", "standards", "best", "practice", "practices", "overview",
|
|
94
|
+
"summary", "report", "reports", "action", "actions", "level", "levels", "order",
|
|
95
|
+
"number", "count", "size", "start", "stop", "begin", "end", "open", "close", "load",
|
|
96
|
+
"save", "store", "fetch", "send", "receive", "handle", "handles", "support", "supports",
|
|
97
|
+
"references", "security", "scope", "verification", "react", "python", "typescript",
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_WORD = re.compile(r"[a-z][a-z0-9]*(?:-[a-z0-9]+)*", re.I)
|
|
101
|
+
|
|
102
|
+
# A term must appear at least this often, in at least this many files, to be proposed.
|
|
103
|
+
# One mention is a passing usage; a coined term earns its definition by recurring.
|
|
104
|
+
MIN_USES = 12
|
|
105
|
+
MIN_FILES = 3
|
|
106
|
+
MAX_SUGGESTIONS = 25
|
|
107
|
+
# A coined term is specific, so in a LARGE corpus it shows up in a minority of files. Measured:
|
|
108
|
+
# `description` appeared in 2112 of ~2500 caddis docs and outranked every real term.
|
|
109
|
+
#
|
|
110
|
+
# But this rule INVERTS on a small repo. Applied unconditionally it deleted docket's four best
|
|
111
|
+
# candidates — `board`, `lane`, `card`, `docket` — because in a 50-file repo the core vocabulary
|
|
112
|
+
# genuinely is in most files. So it only engages once there are enough files for "share" to
|
|
113
|
+
# mean anything.
|
|
114
|
+
MAX_FILE_SHARE = 0.25
|
|
115
|
+
SHARE_RULE_MIN_CORPUS = 200
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _doc_files(root: str) -> list[str]:
|
|
119
|
+
found = []
|
|
120
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
121
|
+
dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS and not d.startswith(".venv")]
|
|
122
|
+
for n in filenames:
|
|
123
|
+
if n.endswith(DOC_GLOBS):
|
|
124
|
+
found.append(os.path.join(dirpath, n))
|
|
125
|
+
return found
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def glossary_path(root: str) -> str:
|
|
129
|
+
"""`.caddis/kb/GLOSSARY.md` — beside the KB, which is where reference docs live."""
|
|
130
|
+
return os.path.join(root, ".caddis", "kb", "GLOSSARY.md")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def defined_terms(path: str) -> set[str]:
|
|
134
|
+
"""Canonical terms are the first cell of each markdown table row."""
|
|
135
|
+
if not os.path.isfile(path):
|
|
136
|
+
return set()
|
|
137
|
+
terms = set()
|
|
138
|
+
try:
|
|
139
|
+
with open(path, encoding="utf-8", errors="replace") as fh:
|
|
140
|
+
for line in fh:
|
|
141
|
+
if not line.startswith("|"):
|
|
142
|
+
continue
|
|
143
|
+
cells = [c.strip() for c in line.strip().strip("|").split("|")]
|
|
144
|
+
if len(cells) < 2 or not cells[0] or set(cells[0]) <= {"-", ":", " "}:
|
|
145
|
+
continue
|
|
146
|
+
head = cells[0].strip("`*").lower()
|
|
147
|
+
if head in {"term", "canonical term", "word"}:
|
|
148
|
+
continue
|
|
149
|
+
terms.add(head)
|
|
150
|
+
except OSError:
|
|
151
|
+
return set()
|
|
152
|
+
return terms
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def propose(root: str) -> list[tuple[str, int, int]]:
|
|
156
|
+
"""Return [(term, uses, files)] — words this repo says often that plain English does not."""
|
|
157
|
+
uses: collections.Counter = collections.Counter()
|
|
158
|
+
files: dict[str, set] = collections.defaultdict(set)
|
|
159
|
+
for f in _doc_files(root):
|
|
160
|
+
try:
|
|
161
|
+
text = open(f, encoding="utf-8", errors="replace").read().lower()
|
|
162
|
+
except OSError:
|
|
163
|
+
continue
|
|
164
|
+
# Strip fenced code and inline code: identifiers are not project vocabulary.
|
|
165
|
+
text = re.sub(r"```.*?```", " ", text, flags=re.S)
|
|
166
|
+
text = re.sub(r"`[^`]*`", " ", text)
|
|
167
|
+
for w in _WORD.findall(text):
|
|
168
|
+
if len(w) < 4 or w in COMMON:
|
|
169
|
+
continue
|
|
170
|
+
uses[w] += 1
|
|
171
|
+
files[w].add(f)
|
|
172
|
+
total = max(1, len(_doc_files(root)))
|
|
173
|
+
out = [
|
|
174
|
+
(w, n, len(files[w]))
|
|
175
|
+
for w, n in uses.items()
|
|
176
|
+
if n >= MIN_USES
|
|
177
|
+
and len(files[w]) >= MIN_FILES
|
|
178
|
+
and (total < SHARE_RULE_MIN_CORPUS or (len(files[w]) / total) <= MAX_FILE_SHARE)
|
|
179
|
+
]
|
|
180
|
+
out.sort(key=lambda r: (-r[1], r[0]))
|
|
181
|
+
return out
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
TEMPLATE = """# Glossary
|
|
185
|
+
|
|
186
|
+
The words this project invented out of ordinary English, defined once.
|
|
187
|
+
|
|
188
|
+
**Admission test:** a term belongs here when its meaning in this project is *distinct enough
|
|
189
|
+
from its ordinary technical sense that a newcomer would misread it*. `commit` does not belong.
|
|
190
|
+
A word this team uses in its own way does.
|
|
191
|
+
|
|
192
|
+
Proposed by `caddis_glossary.py`, which measures which words this repo says often that plain
|
|
193
|
+
English does not. It cannot write the definitions — run `/caddis:glossary` and the model will
|
|
194
|
+
draft them from the actual code, or write them yourself.
|
|
195
|
+
|
|
196
|
+
| Term | Definition | DO NOT USE |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| _example_ | Replace this row. A definition should say what the word means HERE, and why the ordinary reading is wrong. | the synonyms that cause confusion |
|
|
199
|
+
|
|
200
|
+
## Flagged ambiguities
|
|
201
|
+
|
|
202
|
+
Name the words that are still overloaded rather than pretending the vocabulary is clean. A
|
|
203
|
+
glossary that hides its own dirt starts lying. Delete this section only when there is genuinely
|
|
204
|
+
nothing to flag.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def main(argv: list[str] | None = None) -> int:
|
|
209
|
+
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
210
|
+
ap.add_argument("--root", default=os.getcwd())
|
|
211
|
+
ap.add_argument("--check", action="store_true",
|
|
212
|
+
help="exit 1 if a proposed term has no entry")
|
|
213
|
+
ap.add_argument("--scaffold", action="store_true",
|
|
214
|
+
help="write the glossary template if it does not exist")
|
|
215
|
+
args = ap.parse_args(argv)
|
|
216
|
+
|
|
217
|
+
root = os.path.abspath(args.root)
|
|
218
|
+
path = glossary_path(root)
|
|
219
|
+
|
|
220
|
+
if args.scaffold:
|
|
221
|
+
if os.path.isfile(path):
|
|
222
|
+
print("[glossary] present — kept: %s" % os.path.relpath(path, root))
|
|
223
|
+
else:
|
|
224
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
225
|
+
with open(path, "w", encoding="utf-8", newline="\n") as fh:
|
|
226
|
+
fh.write(TEMPLATE)
|
|
227
|
+
print("[glossary] wrote %s" % os.path.relpath(path, root))
|
|
228
|
+
return 0
|
|
229
|
+
|
|
230
|
+
candidates = propose(root)
|
|
231
|
+
defined = defined_terms(path)
|
|
232
|
+
missing = [(w, n, f) for w, n, f in candidates if w not in defined]
|
|
233
|
+
|
|
234
|
+
if args.check:
|
|
235
|
+
# Advisory by design. A project may legitimately decide a frequent word needs no
|
|
236
|
+
# definition, and a gate that cannot be satisfied gets bypassed rather than obeyed.
|
|
237
|
+
if not os.path.isfile(path):
|
|
238
|
+
print("[glossary] no %s — run with --scaffold" % os.path.relpath(path, root))
|
|
239
|
+
return 0
|
|
240
|
+
if missing:
|
|
241
|
+
print("[glossary] %d frequent term(s) with no entry:" % len(missing))
|
|
242
|
+
for w, n, f in missing[:MAX_SUGGESTIONS]:
|
|
243
|
+
print(" %-24s %4d uses in %d files" % (w, n, f))
|
|
244
|
+
print(" Define them, or leave them — this is advisory. `/caddis:glossary` drafts them.")
|
|
245
|
+
return 1
|
|
246
|
+
print("[glossary] every frequent term is defined (%d entries)" % len(defined))
|
|
247
|
+
return 0
|
|
248
|
+
|
|
249
|
+
if not candidates:
|
|
250
|
+
print("[glossary] no candidates — too few docs, or nothing recurs enough.")
|
|
251
|
+
return 0
|
|
252
|
+
print("Candidate vocabulary for %s" % root)
|
|
253
|
+
print(" (>= %d uses across >= %d files, ordinary English excluded)\n" % (MIN_USES, MIN_FILES))
|
|
254
|
+
print(" %-26s %6s %6s %s" % ("TERM", "USES", "FILES", "DEFINED?"))
|
|
255
|
+
for w, n, f in candidates[:MAX_SUGGESTIONS]:
|
|
256
|
+
print(" %-26s %6d %6d %s" % (w, n, f, "yes" if w in defined else "-- no --"))
|
|
257
|
+
if missing:
|
|
258
|
+
print("\n %d undefined. Run `/caddis:glossary` to draft definitions from the code."
|
|
259
|
+
% len(missing))
|
|
260
|
+
return 0
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
if __name__ == "__main__":
|
|
264
|
+
try:
|
|
265
|
+
sys.exit(main())
|
|
266
|
+
except Exception as exc: # pragma: no cover — a suggester must never break a caller
|
|
267
|
+
print("[glossary] skipped: %s" % exc)
|
|
268
|
+
sys.exit(0)
|
|
@@ -902,6 +902,35 @@ memory.jsonl
|
|
|
902
902
|
"""
|
|
903
903
|
|
|
904
904
|
|
|
905
|
+
GLOSSARY_TEMPLATE = """\
|
|
906
|
+
# Glossary
|
|
907
|
+
|
|
908
|
+
The words this project invented out of ordinary English, defined once.
|
|
909
|
+
|
|
910
|
+
**Admission test:** a term belongs here when its meaning in this project is *distinct
|
|
911
|
+
enough from its ordinary technical sense that a newcomer would misread it*. `commit` does
|
|
912
|
+
not belong. A word this team uses in its own way does.
|
|
913
|
+
|
|
914
|
+
To find candidates, run the measurer. It ranks the words this repo says often that plain
|
|
915
|
+
English does not, and marks which already have an entry:
|
|
916
|
+
|
|
917
|
+
python <caddis>/scripts/caddis_glossary.py
|
|
918
|
+
|
|
919
|
+
It cannot write the definitions - only a person or the model reading the code can say what
|
|
920
|
+
a coined word MEANS here. Run `/caddis:glossary` to have them drafted, or write them.
|
|
921
|
+
|
|
922
|
+
| Term | Definition | DO NOT USE |
|
|
923
|
+
|---|---|---|
|
|
924
|
+
| _example_ | Replace this row. Say what the word means HERE, and why the ordinary reading is wrong. | the synonyms that cause confusion |
|
|
925
|
+
|
|
926
|
+
## Flagged ambiguities
|
|
927
|
+
|
|
928
|
+
Name the words that are still overloaded rather than pretending the vocabulary is clean. A
|
|
929
|
+
glossary that hides its own dirt starts lying. Delete this section only when there is
|
|
930
|
+
genuinely nothing to flag.
|
|
931
|
+
"""
|
|
932
|
+
|
|
933
|
+
|
|
905
934
|
ARTIFACT_CONFIG_EXAMPLE = """\
|
|
906
935
|
# caddis per-repo configuration — a documented template.
|
|
907
936
|
#
|
|
@@ -1198,6 +1227,20 @@ def scaffold_artifact_dir(target: Path, dry: bool) -> list[str]:
|
|
|
1198
1227
|
em.parent.mkdir(parents=True, exist_ok=True)
|
|
1199
1228
|
em.write_text(ENVIRONMENT_MAP_TEMPLATE, encoding="utf-8")
|
|
1200
1229
|
notes.append(f"scaffold: wrote {label}/kb/environment-map.md")
|
|
1230
|
+
# A vocabulary stub, seeded but never auto-filled. `caddis_glossary.py` can measure
|
|
1231
|
+
# which words a repo coined, but only a person or the model reading the code can say
|
|
1232
|
+
# what they MEAN - and a file of correct words with empty meanings looks authoritative
|
|
1233
|
+
# while teaching nothing. The stub carries the admission test so whoever fills it in
|
|
1234
|
+
# knows what qualifies.
|
|
1235
|
+
gl = root / "kb" / "GLOSSARY.md"
|
|
1236
|
+
if gl.exists():
|
|
1237
|
+
notes.append(f"scaffold: {label}/kb/GLOSSARY.md present - kept")
|
|
1238
|
+
else:
|
|
1239
|
+
if not dry:
|
|
1240
|
+
gl.parent.mkdir(parents=True, exist_ok=True)
|
|
1241
|
+
gl.write_text(GLOSSARY_TEMPLATE, encoding="utf-8")
|
|
1242
|
+
notes.append(f"scaffold: wrote {label}/kb/GLOSSARY.md (run /caddis:glossary to fill it)")
|
|
1243
|
+
|
|
1201
1244
|
gi = root / ".gitignore"
|
|
1202
1245
|
if gi.exists():
|
|
1203
1246
|
# APPEND what is missing rather than keeping the file frozen. The old behaviour was
|
|
@@ -70,11 +70,13 @@
|
|
|
70
70
|
| Skill | Path | When to Use |
|
|
71
71
|
|-------|------|-------------|
|
|
72
72
|
| Add Rules | `add-rules/` | Create canonical rules for ANY folder — write <folder>/AGENTS.md (from the folder template) + a <folder>/CLAUDE.md @import shim. For folders the stack-map generator doesn't cover. |
|
|
73
|
+
| Catchup | `catchup/` | Where were we? A quick list of what this session was doing and what is still open |
|
|
73
74
|
| Digress | `digress/` | Park the current workstream on the stack and switch to a new task — without losing the original |
|
|
74
75
|
| Excalidraw Db | `excalidraw-db/` | Turn a SQL artifact into an Excalidraw diagram for a design review / ARB pack / slide — higher-level, drag-the-boxes format |
|
|
75
76
|
| Feature Plan | `feature-plan/` | Create a phased, TDD-structured implementation plan that acts as the durable spine for multi-session work |
|
|
76
77
|
| Gate Preflight | `gate-preflight/` | Deterministic headless preflight — validates a plan against the actual codebase, self-contained (never depends on a skill activating) |
|
|
77
78
|
| Gate Review | `gate-review/` | Deterministic headless code review — adversarial diff review, self-contained (never depends on a skill activating) |
|
|
79
|
+
| Glossary | `glossary/` | Define the words this project invented, in one place — .caddis/kb/GLOSSARY.md |
|
|
78
80
|
| Handoff | `handoff/` | End-of-session handoff — capture exact state so the next session resumes with zero re-discovery |
|
|
79
81
|
| Implement | `implement/` | Headless plan executor — implement an approved plan phase-by-phase on the current feature branch, TDD-first, committing per phase |
|
|
80
82
|
| Kb | `kb/` | Rebuild the KB index (.caddis/kb/DOC-MAP.md) — create it if missing, index un-indexed notes, report dangling links |
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catchup
|
|
3
|
+
description: Where were we? A quick list of what this session was doing and what is still open
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /catchup — where are we?
|
|
7
|
+
|
|
8
|
+
Answer one question: **what were we doing, and what is still open?**
|
|
9
|
+
|
|
10
|
+
Not named `/recap` on purpose — Claude Code has a built-in `/recap` that produces a one-line
|
|
11
|
+
session summary. This is the list version, and two commands whose names differ by nothing would
|
|
12
|
+
be a coin toss every time.
|
|
13
|
+
|
|
14
|
+
## Read these, in this order, and stop as soon as you can answer
|
|
15
|
+
|
|
16
|
+
1. **`.caddis/session-state.md`** — the primary source. The `Stop` hook rewrites it at the end of
|
|
17
|
+
every turn, so it is current even when the session ended abruptly and nobody ran `/handoff`.
|
|
18
|
+
It holds the last real request, the task list with statuses, and the files touched.
|
|
19
|
+
2. **`.caddis/relay.md`** — only refreshed by `/handoff`, so it may be days behind, but it carries
|
|
20
|
+
what the state file cannot: the agreed **next step** and the open blockers.
|
|
21
|
+
3. **The current turn's own context**, if this session has already done work. What you have done
|
|
22
|
+
since the last Stop is newer than both files, and neither knows about it.
|
|
23
|
+
|
|
24
|
+
Compare the timestamps rather than assuming. If `relay.md` is newer, a handoff ran and its "Next
|
|
25
|
+
step" is the authority. If `session-state.md` is newer, work happened after the last handoff and
|
|
26
|
+
the relay's next step may already be done.
|
|
27
|
+
|
|
28
|
+
**A missing file is not an error.** A repo with neither has simply not run a caddis session yet —
|
|
29
|
+
say that in one line and stop.
|
|
30
|
+
|
|
31
|
+
## What to report
|
|
32
|
+
|
|
33
|
+
Keep it short enough to read at a glance. Roughly:
|
|
34
|
+
|
|
35
|
+
- **One line on where we are** — the workstream, and whether it is mid-flight or at a clean stop.
|
|
36
|
+
- **In-progress and pending items**, as a short list. Mark anything blocked, and on what.
|
|
37
|
+
- **The next step**, if one is recorded. Say which file it came from.
|
|
38
|
+
- **Uncommitted work**, if `git status` is dirty — this is the thing most likely to be lost, and
|
|
39
|
+
the state file does not track it. Check it; do not assume clean.
|
|
40
|
+
|
|
41
|
+
Leave out anything already done unless the user asks. "What is finished" is a different question
|
|
42
|
+
from "where are we", and the completed list is usually the longest part of both files.
|
|
43
|
+
|
|
44
|
+
## Say plainly when the answer is thin
|
|
45
|
+
|
|
46
|
+
If the state file has no tasks and no last request, the honest answer is "the last session did not
|
|
47
|
+
record anything specific" — not a paragraph assembled from file names. A recap the user cannot act
|
|
48
|
+
on is worse than one sentence admitting there is nothing to recap.
|
|
49
|
+
|
|
50
|
+
## When the user wants more than a list
|
|
51
|
+
|
|
52
|
+
- To **reopen** the actual conversation rather than read about it: `claude --continue` in this
|
|
53
|
+
folder, or `claude --resume` to pick an older session. Mention this only if the recap is thin,
|
|
54
|
+
or if they ask for detail the files do not hold.
|
|
55
|
+
- To **write** a durable resume doc rather than read one: `/caddis:handoff`.
|
|
56
|
+
- To **pop a parked workstream** off the digress stack: `/caddis:resume`. That is a different
|
|
57
|
+
mechanism — it restores work deliberately set aside, not the last session's state.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: glossary
|
|
3
|
+
description: Define the words this project invented, in one place — .caddis/kb/GLOSSARY.md
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /glossary — define what this project coined
|
|
7
|
+
|
|
8
|
+
Every project invents vocabulary out of ordinary English, and those are the most dangerous words
|
|
9
|
+
in the codebase: a reader who guesses from the plain word gets it wrong and never finds out.
|
|
10
|
+
caddis itself coined `pool`, `relay`, `drift`, `parking-lot` — and its own glossary went five and
|
|
11
|
+
a half months without mentioning any of them, because nothing measured the gap.
|
|
12
|
+
|
|
13
|
+
Your job is the half a script cannot do: **write what the words mean here.**
|
|
14
|
+
|
|
15
|
+
## Step 1 — get the candidates
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
python <caddis-plugin-root>/scripts/caddis_glossary.py
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It ranks the words this repo says often that plain English does not, and marks which already
|
|
22
|
+
have an entry. `--scaffold` creates `.caddis/kb/GLOSSARY.md` if it is missing.
|
|
23
|
+
|
|
24
|
+
It is a **suggester, not an oracle**. It is good on an application repo — run against a kanban
|
|
25
|
+
tool it proposed `board`, `lane`, `card` at the top — and noisier on a repo whose docs are ABOUT
|
|
26
|
+
software, where words like `protocol` are both ordinary and genuinely overloaded. Expect to
|
|
27
|
+
discard some suggestions. That is the intended cost: a false positive wastes three seconds, a
|
|
28
|
+
missing term stays undefined forever.
|
|
29
|
+
|
|
30
|
+
If the script is not available, read the repo yourself and look for the same thing.
|
|
31
|
+
|
|
32
|
+
## Step 2 — apply the admission test to each candidate
|
|
33
|
+
|
|
34
|
+
A term belongs **only if its meaning here is distinct enough from its ordinary technical sense
|
|
35
|
+
that a newcomer would misread it.**
|
|
36
|
+
|
|
37
|
+
- `commit`, `branch`, `endpoint` — **no**. The ordinary meaning is correct.
|
|
38
|
+
- A word the team bent to its own purpose — **yes**. That is the whole point.
|
|
39
|
+
|
|
40
|
+
Discard the rest without comment. A padded glossary is skimmed and then ignored.
|
|
41
|
+
|
|
42
|
+
## Step 3 — write each definition from the CODE, not from the word
|
|
43
|
+
|
|
44
|
+
Read where the term is actually used before defining it. A definition that restates the word
|
|
45
|
+
teaches nothing, and a wrong one is worse than a gap because it is believed.
|
|
46
|
+
|
|
47
|
+
Each row gets three things:
|
|
48
|
+
|
|
49
|
+
| Column | What goes in it |
|
|
50
|
+
|---|---|
|
|
51
|
+
| **Term** | The canonical spelling. Pick one and stop using the others. |
|
|
52
|
+
| **Definition** | What it means HERE, and — where it helps — why the ordinary reading misleads. Say the thing a newcomer would get wrong. |
|
|
53
|
+
| **DO NOT USE** | The synonyms actually causing confusion in this repo. Leave blank rather than inventing some. |
|
|
54
|
+
|
|
55
|
+
Good: *"pool — the single source of every shipped resource, in `.github/`. Everything installed is EXPORTED from it; nothing is authored in a bundle."*
|
|
56
|
+
|
|
57
|
+
Bad: *"pool — a pool of resources."*
|
|
58
|
+
|
|
59
|
+
## Step 4 — flag what is still dirty
|
|
60
|
+
|
|
61
|
+
Fill the **Flagged ambiguities** section with the words that remain overloaded. This is the part
|
|
62
|
+
people skip and it is the most valuable part, because a glossary that hides its own dirt starts
|
|
63
|
+
lying.
|
|
64
|
+
|
|
65
|
+
Look for a term used in several incompatible senses. In caddis, `gate` is defined as one thing
|
|
66
|
+
and used as at least twelve — exit gate, quality gate, evidence gate, privacy gate. The honest
|
|
67
|
+
move is to name the overload and recommend the two-word form, not to pick a winner by fiat.
|
|
68
|
+
|
|
69
|
+
Also flag a term whose canonical spelling the code itself contradicts. Do not "fix" either side
|
|
70
|
+
when both are load-bearing; document the split and where each applies.
|
|
71
|
+
|
|
72
|
+
## Step 5 — report
|
|
73
|
+
|
|
74
|
+
Say how many terms you added, how many candidates you rejected and roughly why, and what you
|
|
75
|
+
flagged as ambiguous. If you found nothing worth adding, say that — it is a real answer, and
|
|
76
|
+
better than padding the file to look productive.
|
|
77
|
+
|
|
78
|
+
## Keeping it honest
|
|
79
|
+
|
|
80
|
+
Re-run `caddis_glossary.py --check` later. It exits 1 when a frequent term has no entry. It is
|
|
81
|
+
**advisory**: a project may reasonably decide a common word needs no definition, and a gate that
|
|
82
|
+
cannot be satisfied gets bypassed rather than obeyed.
|
|
83
|
+
|
|
84
|
+
Do not enforce the DO-NOT-USE column by grepping. Most banned synonyms are ordinary English —
|
|
85
|
+
"step", "block", "refuse" — and a check that cries wolf gets deleted, leaving no check at all.
|
package/bundles/manifest.json
CHANGED
package/package.json
CHANGED