mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 08:23:59 +01:00
refactor(learn): diet /learn onto the levels: tool flags replace the inline stats program, one mode table, prune rules kept
Skill-Diet: learn Skill-Size: 5797→2586 bytes (SKILL.md.tmpl) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
cbb49a59d7
commit
b286989895
2 files changed
+44
-330
No files matched your search
+22
-165
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: learn
|
||||
preamble-tier: 2
|
||||
version: 1.0.0
|
||||
version: 1.1.0
|
||||
description: Manage project learnings.
|
||||
triggers:
|
||||
- show learnings
|
||||
@@ -97,174 +97,31 @@ Replace `OUTCOME` and `USED_BROWSE` (yes/no) before running; substitute
|
||||
are "" unless outcome is error. If the command is missing (stale install), skip
|
||||
telemetry — it never blocks the workflow.
|
||||
|
||||
# Project Learnings Manager
|
||||
# /learn: project learnings
|
||||
|
||||
You are a **Staff Engineer who maintains the team wiki**. Your job is to help the user
|
||||
see what gstack has learned across sessions on this project, search for relevant
|
||||
knowledge, and prune stale or contradictory entries.
|
||||
**Hard gate:** manage learnings only. Make no code changes, even when a learning names a fix.
|
||||
|
||||
**HARD GATE:** Do NOT implement code changes. This skill manages learnings only.
|
||||
`~/.claude/skills/gstack/bin/gstack-learnings-search` reads this project's learnings (latest entry per key+type);
|
||||
`~/.claude/skills/gstack/bin/gstack-learnings-log` appends one. Both take `--help`. Pick the mode from the request:
|
||||
|
||||
---
|
||||
|
||||
## Detect command
|
||||
|
||||
Parse the user's input to determine which command to run:
|
||||
|
||||
- `/learn` (no arguments) → **Show recent**
|
||||
- `/learn search <query>` → **Search**
|
||||
- `/learn prune` → **Prune**
|
||||
- `/learn export` → **Export**
|
||||
- `/learn stats` → **Stats**
|
||||
- `/learn add` → **Manual add**
|
||||
|
||||
---
|
||||
|
||||
## Show recent (default)
|
||||
|
||||
Show the most recent 20 learnings, grouped by type.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 20 2>/dev/null || echo "No learnings yet."
|
||||
```
|
||||
|
||||
Present the output in a readable format. If no learnings exist, tell the user:
|
||||
"No learnings recorded yet. As you use /review, /ship, /investigate, and other skills,
|
||||
gstack will automatically capture patterns, pitfalls, and insights it discovers."
|
||||
|
||||
---
|
||||
|
||||
## Search
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --query "USER_QUERY" --limit 20 2>/dev/null || echo "No matches."
|
||||
```
|
||||
|
||||
Replace USER_QUERY with the user's search terms. Present results clearly.
|
||||
|
||||
---
|
||||
| Request | Run | Then |
|
||||
|---|---|---|
|
||||
| show (default) | `gstack-learnings-search --limit 20` | group by type; if empty, say none are recorded yet and that /review, /ship, /investigate and other skills record them as they work |
|
||||
| search | `gstack-learnings-search --query "<words>" --limit 20` | show the matches |
|
||||
| other projects | add `--cross-project` | only user-stated learnings from other projects appear |
|
||||
| stats | `gstack-learnings-search --stats` | show the counts as a table |
|
||||
| export | `gstack-learnings-search --limit 50` | a `## Project Learnings` section, `### <Type>s` per type, lines `- **key**: insight (confidence: N/10)`; show it, and write it into CLAUDE.md or a file only if the user asks |
|
||||
| add | `gstack-learnings-log '<json>'` | ask only for what is missing: type, kebab-case key, one-sentence insight, confidence 1-10, files; `"source":"user-stated"` |
|
||||
| prune | `gstack-learnings-search --limit 100` | see Prune |
|
||||
|
||||
## Prune
|
||||
|
||||
Check learnings for staleness and contradictions.
|
||||
Flag each learning that is:
|
||||
- **STALE**: a path in its `files` no longer exists in the repo.
|
||||
- **CONFLICT**: the same key appears under two types with opposing insights (within one type the latest entry already wins).
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 100 2>/dev/null
|
||||
```
|
||||
|
||||
For each learning in the output:
|
||||
|
||||
1. **File existence check:** If the learning has a `files` field, check whether those
|
||||
files still exist in the repo using Glob. If any referenced files are deleted, flag:
|
||||
"STALE: [key] references deleted file [path]"
|
||||
|
||||
2. **Contradiction check:** Look for learnings with the same `key` but different or
|
||||
opposite `insight` values. Flag: "CONFLICT: [key] has contradicting entries —
|
||||
[insight A] vs [insight B]"
|
||||
|
||||
Present each flagged entry via AskUserQuestion:
|
||||
- A) Remove this learning
|
||||
- B) Keep it
|
||||
- C) Update it (I'll tell you what to change)
|
||||
|
||||
For removals, read the learnings.jsonl file and remove the matching line, then write
|
||||
back. For updates, append a new entry with the corrected insight (append-only, the
|
||||
latest entry wins).
|
||||
|
||||
---
|
||||
|
||||
## Export
|
||||
|
||||
Export learnings as markdown suitable for adding to CLAUDE.md or project documentation.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 50 2>/dev/null
|
||||
```
|
||||
|
||||
Format the output as a markdown section:
|
||||
|
||||
```markdown
|
||||
## Project Learnings
|
||||
|
||||
### Patterns
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
|
||||
### Pitfalls
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
|
||||
### Preferences
|
||||
- **[key]**: [insight]
|
||||
|
||||
### Architecture
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
```
|
||||
|
||||
Present the formatted output to the user. Ask if they want to append it to CLAUDE.md
|
||||
or save it as a separate file.
|
||||
|
||||
---
|
||||
|
||||
## Stats
|
||||
|
||||
Show summary statistics about the project's learnings.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
LEARN_FILE="$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl"
|
||||
if [ -f "$LEARN_FILE" ]; then
|
||||
TOTAL=$(wc -l < "$LEARN_FILE" | tr -d ' ')
|
||||
echo "TOTAL: $TOTAL entries"
|
||||
# Count by type (after dedup)
|
||||
cat "$LEARN_FILE" | bun --no-env-file --config=/dev/null -e "
|
||||
const lines = (await Bun.stdin.text()).trim().split('\n').filter(Boolean);
|
||||
const seen = new Map();
|
||||
for (const line of lines) {
|
||||
try {
|
||||
const e = JSON.parse(line);
|
||||
const dk = (e.key||'') + '|' + (e.type||'');
|
||||
const existing = seen.get(dk);
|
||||
if (!existing || new Date(e.ts) > new Date(existing.ts)) seen.set(dk, e);
|
||||
} catch {}
|
||||
}
|
||||
const byType = {};
|
||||
const bySource = {};
|
||||
let totalConf = 0;
|
||||
for (const e of seen.values()) {
|
||||
byType[e.type] = (byType[e.type]||0) + 1;
|
||||
bySource[e.source] = (bySource[e.source]||0) + 1;
|
||||
totalConf += e.confidence || 0;
|
||||
}
|
||||
console.log('UNIQUE: ' + seen.size + ' (after dedup)');
|
||||
console.log('RAW_ENTRIES: ' + lines.length);
|
||||
console.log('BY_TYPE: ' + JSON.stringify(byType));
|
||||
console.log('BY_SOURCE: ' + JSON.stringify(bySource));
|
||||
console.log('AVG_CONFIDENCE: ' + (totalConf / seen.size).toFixed(1));
|
||||
" 2>/dev/null
|
||||
else
|
||||
echo "NO_LEARNINGS"
|
||||
fi
|
||||
```
|
||||
|
||||
Present the stats in a readable table format.
|
||||
|
||||
---
|
||||
|
||||
## Manual add
|
||||
|
||||
The user wants to manually add a learning. Use AskUserQuestion to gather:
|
||||
1. Type (pattern / pitfall / preference / architecture / tool)
|
||||
2. A short key (2-5 words, kebab-case)
|
||||
3. The insight (one sentence)
|
||||
4. Confidence (1-10)
|
||||
5. Related files (optional)
|
||||
|
||||
Then log it:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"learn","type":"TYPE","key":"KEY","insight":"INSIGHT","confidence":N,"source":"user-stated","files":["FILE1"]}'
|
||||
```
|
||||
For each flagged entry, unless the user already approved the action, ask: remove, keep, or update.
|
||||
- Remove: delete its lines (that key and type) from `$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl`, where
|
||||
`SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG)` and
|
||||
`GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT)`. Leave every other line as it is.
|
||||
- Update: log a corrected entry with the same key and type; the latest one wins.
|
||||
+22
-165
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: learn
|
||||
preamble-tier: 2
|
||||
version: 1.0.0
|
||||
version: 1.1.0
|
||||
description: |
|
||||
Manage project learnings. Review, search, prune, and export what gstack
|
||||
has learned across sessions. Use when asked to "what have we learned",
|
||||
@@ -24,174 +24,31 @@ allowed-tools:
|
||||
|
||||
{{PREAMBLE}}
|
||||
|
||||
# Project Learnings Manager
|
||||
# /learn: project learnings
|
||||
|
||||
You are a **Staff Engineer who maintains the team wiki**. Your job is to help the user
|
||||
see what gstack has learned across sessions on this project, search for relevant
|
||||
knowledge, and prune stale or contradictory entries.
|
||||
**Hard gate:** manage learnings only. Make no code changes, even when a learning names a fix.
|
||||
|
||||
**HARD GATE:** Do NOT implement code changes. This skill manages learnings only.
|
||||
`~/.claude/skills/gstack/bin/gstack-learnings-search` reads this project's learnings (latest entry per key+type);
|
||||
`~/.claude/skills/gstack/bin/gstack-learnings-log` appends one. Both take `--help`. Pick the mode from the request:
|
||||
|
||||
---
|
||||
|
||||
## Detect command
|
||||
|
||||
Parse the user's input to determine which command to run:
|
||||
|
||||
- `/learn` (no arguments) → **Show recent**
|
||||
- `/learn search <query>` → **Search**
|
||||
- `/learn prune` → **Prune**
|
||||
- `/learn export` → **Export**
|
||||
- `/learn stats` → **Stats**
|
||||
- `/learn add` → **Manual add**
|
||||
|
||||
---
|
||||
|
||||
## Show recent (default)
|
||||
|
||||
Show the most recent 20 learnings, grouped by type.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 20 2>/dev/null || echo "No learnings yet."
|
||||
```
|
||||
|
||||
Present the output in a readable format. If no learnings exist, tell the user:
|
||||
"No learnings recorded yet. As you use /review, /ship, /investigate, and other skills,
|
||||
gstack will automatically capture patterns, pitfalls, and insights it discovers."
|
||||
|
||||
---
|
||||
|
||||
## Search
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --query "USER_QUERY" --limit 20 2>/dev/null || echo "No matches."
|
||||
```
|
||||
|
||||
Replace USER_QUERY with the user's search terms. Present results clearly.
|
||||
|
||||
---
|
||||
| Request | Run | Then |
|
||||
|---|---|---|
|
||||
| show (default) | `gstack-learnings-search --limit 20` | group by type; if empty, say none are recorded yet and that /review, /ship, /investigate and other skills record them as they work |
|
||||
| search | `gstack-learnings-search --query "<words>" --limit 20` | show the matches |
|
||||
| other projects | add `--cross-project` | only user-stated learnings from other projects appear |
|
||||
| stats | `gstack-learnings-search --stats` | show the counts as a table |
|
||||
| export | `gstack-learnings-search --limit 50` | a `## Project Learnings` section, `### <Type>s` per type, lines `- **key**: insight (confidence: N/10)`; show it, and write it into CLAUDE.md or a file only if the user asks |
|
||||
| add | `gstack-learnings-log '<json>'` | ask only for what is missing: type, kebab-case key, one-sentence insight, confidence 1-10, files; `"source":"user-stated"` |
|
||||
| prune | `gstack-learnings-search --limit 100` | see Prune |
|
||||
|
||||
## Prune
|
||||
|
||||
Check learnings for staleness and contradictions.
|
||||
Flag each learning that is:
|
||||
- **STALE**: a path in its `files` no longer exists in the repo.
|
||||
- **CONFLICT**: the same key appears under two types with opposing insights (within one type the latest entry already wins).
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 100 2>/dev/null
|
||||
```
|
||||
|
||||
For each learning in the output:
|
||||
|
||||
1. **File existence check:** If the learning has a `files` field, check whether those
|
||||
files still exist in the repo using Glob. If any referenced files are deleted, flag:
|
||||
"STALE: [key] references deleted file [path]"
|
||||
|
||||
2. **Contradiction check:** Look for learnings with the same `key` but different or
|
||||
opposite `insight` values. Flag: "CONFLICT: [key] has contradicting entries —
|
||||
[insight A] vs [insight B]"
|
||||
|
||||
Present each flagged entry via AskUserQuestion:
|
||||
- A) Remove this learning
|
||||
- B) Keep it
|
||||
- C) Update it (I'll tell you what to change)
|
||||
|
||||
For removals, read the learnings.jsonl file and remove the matching line, then write
|
||||
back. For updates, append a new entry with the corrected insight (append-only, the
|
||||
latest entry wins).
|
||||
|
||||
---
|
||||
|
||||
## Export
|
||||
|
||||
Export learnings as markdown suitable for adding to CLAUDE.md or project documentation.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 50 2>/dev/null
|
||||
```
|
||||
|
||||
Format the output as a markdown section:
|
||||
|
||||
```markdown
|
||||
## Project Learnings
|
||||
|
||||
### Patterns
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
|
||||
### Pitfalls
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
|
||||
### Preferences
|
||||
- **[key]**: [insight]
|
||||
|
||||
### Architecture
|
||||
- **[key]**: [insight] (confidence: N/10)
|
||||
```
|
||||
|
||||
Present the formatted output to the user. Ask if they want to append it to CLAUDE.md
|
||||
or save it as a separate file.
|
||||
|
||||
---
|
||||
|
||||
## Stats
|
||||
|
||||
Show summary statistics about the project's learnings.
|
||||
|
||||
```bash
|
||||
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
|
||||
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
LEARN_FILE="$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl"
|
||||
if [ -f "$LEARN_FILE" ]; then
|
||||
TOTAL=$(wc -l < "$LEARN_FILE" | tr -d ' ')
|
||||
echo "TOTAL: $TOTAL entries"
|
||||
# Count by type (after dedup)
|
||||
cat "$LEARN_FILE" | bun --no-env-file --config=/dev/null -e "
|
||||
const lines = (await Bun.stdin.text()).trim().split('\n').filter(Boolean);
|
||||
const seen = new Map();
|
||||
for (const line of lines) {
|
||||
try {
|
||||
const e = JSON.parse(line);
|
||||
const dk = (e.key||'') + '|' + (e.type||'');
|
||||
const existing = seen.get(dk);
|
||||
if (!existing || new Date(e.ts) > new Date(existing.ts)) seen.set(dk, e);
|
||||
} catch {}
|
||||
}
|
||||
const byType = {};
|
||||
const bySource = {};
|
||||
let totalConf = 0;
|
||||
for (const e of seen.values()) {
|
||||
byType[e.type] = (byType[e.type]||0) + 1;
|
||||
bySource[e.source] = (bySource[e.source]||0) + 1;
|
||||
totalConf += e.confidence || 0;
|
||||
}
|
||||
console.log('UNIQUE: ' + seen.size + ' (after dedup)');
|
||||
console.log('RAW_ENTRIES: ' + lines.length);
|
||||
console.log('BY_TYPE: ' + JSON.stringify(byType));
|
||||
console.log('BY_SOURCE: ' + JSON.stringify(bySource));
|
||||
console.log('AVG_CONFIDENCE: ' + (totalConf / seen.size).toFixed(1));
|
||||
" 2>/dev/null
|
||||
else
|
||||
echo "NO_LEARNINGS"
|
||||
fi
|
||||
```
|
||||
|
||||
Present the stats in a readable table format.
|
||||
|
||||
---
|
||||
|
||||
## Manual add
|
||||
|
||||
The user wants to manually add a learning. Use AskUserQuestion to gather:
|
||||
1. Type (pattern / pitfall / preference / architecture / tool)
|
||||
2. A short key (2-5 words, kebab-case)
|
||||
3. The insight (one sentence)
|
||||
4. Confidence (1-10)
|
||||
5. Related files (optional)
|
||||
|
||||
Then log it:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"learn","type":"TYPE","key":"KEY","insight":"INSIGHT","confidence":N,"source":"user-stated","files":["FILE1"]}'
|
||||
```
|
||||
For each flagged entry, unless the user already approved the action, ask: remove, keep, or update.
|
||||
- Remove: delete its lines (that key and type) from `$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl`, where
|
||||
`SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG)` and
|
||||
`GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT)`. Leave every other line as it is.
|
||||
- Update: log a corrected entry with the same key and type; the latest one wins.
|
||||
Reference in new issue
Block a user