fss-link 1.9.1 → 1.9.3
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/README.md +10 -0
- package/bundle/edit-link-skill/SKILL.md +121 -0
- package/bundle/fss-link-main.js +1562 -1505
- package/bundle/worktrees/SKILL.md +102 -0
- package/docs/README.md +1 -0
- package/docs/SLASH-COMMANDS.md +186 -0
- package/package.json +3 -2
- package/scripts/copy_bundle_assets.js +18 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktrees
|
|
3
|
+
description: >
|
|
4
|
+
Git worktrees for isolated parallel development across agents.
|
|
5
|
+
Use when starting feature work that needs isolation, before executing
|
|
6
|
+
plans that risk breaking the main branch, or when another agent is
|
|
7
|
+
already working in the project.
|
|
8
|
+
category: workflow
|
|
9
|
+
triggers: [worktree, isolated, parallel, branch workspace]
|
|
10
|
+
version: 1
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Git Worktrees — Isolated Parallel Development
|
|
14
|
+
|
|
15
|
+
**Use when:** Multiple agents need to work on different features simultaneously without interfering with each other.
|
|
16
|
+
|
|
17
|
+
## When to Use
|
|
18
|
+
|
|
19
|
+
- Starting feature work that needs isolation from the current workspace
|
|
20
|
+
- Before executing implementation plans that risk breaking the main branch
|
|
21
|
+
- When another agent is already working in the project
|
|
22
|
+
|
|
23
|
+
## When NOT to Use
|
|
24
|
+
|
|
25
|
+
- The user simply asks to fix a bug or implement a feature — those belong to the regular working directory
|
|
26
|
+
- You are already inside a worktree
|
|
27
|
+
- The project is not a git repository
|
|
28
|
+
|
|
29
|
+
## Creating a Worktree
|
|
30
|
+
|
|
31
|
+
Use the `enter_worktree` tool:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
enter_worktree({ name: "fix-auth-bug" })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
- `worktreePath` — absolute path (e.g., `/path/to/project/.fss-link/worktrees/fix-auth-bug`)
|
|
39
|
+
- `branch` — git branch name (e.g., `fss-link-fix-auth-bug`)
|
|
40
|
+
|
|
41
|
+
**CRITICAL:** Every tool call must use the full absolute `worktreePath`. Never use relative paths.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# ✅ CORRECT — absolute path
|
|
45
|
+
write_file({ path: "/path/to/project/.fss-link/worktrees/fix-auth-bug/src/auth.ts", ... })
|
|
46
|
+
|
|
47
|
+
# ❌ WRONG — relative path modifies the MAIN checkout
|
|
48
|
+
write_file({ path: "src/auth.ts", ... })
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Verifying You Are in the Worktree
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pwd # Should show .../.fss-link/worktrees/<slug>/
|
|
55
|
+
git branch --show-current # Should show fss-link-<slug>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Project Setup
|
|
59
|
+
|
|
60
|
+
Auto-detect and run appropriate setup:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd /path/to/project/.fss-link/worktrees/fix-auth-bug && npm install
|
|
64
|
+
cd /path/to/project/.fss-link/worktrees/fix-auth-bug && cargo build
|
|
65
|
+
cd /path/to/project/.fss-link/worktrees/fix-auth-bug && pip install -r requirements.txt
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Exiting a Worktree
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
exit_worktree({ name: "fix-auth-bug", action: "keep" }) # Work in progress
|
|
72
|
+
exit_worktree({ name: "fix-auth-bug", action: "remove" }) # Work committed
|
|
73
|
+
exit_worktree({ name: "fix-auth-bug", action: "remove", discard_changes: true }) # Discard everything
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quick Reference
|
|
77
|
+
|
|
78
|
+
| Situation | Action |
|
|
79
|
+
|-----------|--------|
|
|
80
|
+
| Need isolated workspace | `enter_worktree({ name: "feature-name" })` |
|
|
81
|
+
| Already in a worktree | Do NOT call enter_worktree again |
|
|
82
|
+
| Done, work in progress | `exit_worktree({ name, action: "keep" })` |
|
|
83
|
+
| Done, work committed | `exit_worktree({ name, action: "remove" })` |
|
|
84
|
+
| Done, discard everything | `exit_worktree({ name, action: "remove", discard_changes: true })` |
|
|
85
|
+
|
|
86
|
+
## Common Mistakes
|
|
87
|
+
|
|
88
|
+
1. **Using relative paths** — Always use full `worktreePath` for every tool call
|
|
89
|
+
2. **Creating nested worktrees** — Check path first; if it contains `.fss-link/worktrees/`, you're already in one
|
|
90
|
+
3. **Running `git worktree add` manually** — Always use the `enter_worktree` tool
|
|
91
|
+
4. **Removing with uncommitted changes** — Commit or stash first, or use `discard_changes: true`
|
|
92
|
+
|
|
93
|
+
## Global Flags
|
|
94
|
+
|
|
95
|
+
N/A — this is a workflow skill, not a CLI tool.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
*This skill is part of the FSS Parsers collection.*
|
|
100
|
+
|
|
101
|
+
**References:**
|
|
102
|
+
- Full procedural guide → `references/capabilities.md`
|
package/docs/README.md
CHANGED
|
@@ -183,6 +183,7 @@ npx vitest run packages/cli/src/config/database.test.ts
|
|
|
183
183
|
|
|
184
184
|
- [Configuration Guide](CONFIGURATION.md) — providers, models, database, context settings
|
|
185
185
|
- [Tools Reference](TOOLS.md) — all available tools and their parameters
|
|
186
|
+
- [Slash Commands](SLASH-COMMANDS.md) — `/resume`, `/rewind`, `/sessions`, and the Welcome Back dialog
|
|
186
187
|
- [Tool Flow Diagrams](../fss-docs/tool-flow-diagrams/) — visual documentation with Mermaid diagrams
|
|
187
188
|
|
|
188
189
|
## License
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Slash Commands — Session Management
|
|
2
|
+
|
|
3
|
+
## `/resume` — List and Resume Saved Sessions
|
|
4
|
+
|
|
5
|
+
Resume a previous conversation session by number, title, or session ID.
|
|
6
|
+
|
|
7
|
+
### Usage
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
/resume # List recent sessions
|
|
11
|
+
/resume <number> # Resume by list position
|
|
12
|
+
/resume <title substring> # Resume by title match
|
|
13
|
+
/resume <session-id> # Resume by exact session ID
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Alternatives
|
|
17
|
+
|
|
18
|
+
`/sessions`, `/continue` — all three names invoke the same command.
|
|
19
|
+
|
|
20
|
+
### Listing Sessions
|
|
21
|
+
|
|
22
|
+
Running `/resume` with no arguments displays a numbered list of up to 50 recent sessions for the current project, sorted by most recently updated:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Recent sessions:
|
|
26
|
+
|
|
27
|
+
1. Implement auth middleware for API endpoints 2 hours ago 14 turns
|
|
28
|
+
2. Fix login bug on password reset flow 1 day ago 8 turns
|
|
29
|
+
3. Set up CI pipeline with GitHub Actions 3 days ago 22 turns
|
|
30
|
+
|
|
31
|
+
Type a number to resume, or /resume <partial title> to match by name.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Titles are truncated at 55 characters. The list is scoped to the current project (identified by project hash).
|
|
35
|
+
|
|
36
|
+
### Selection Methods
|
|
37
|
+
|
|
38
|
+
| Method | Example | Behavior |
|
|
39
|
+
|--------|---------|----------|
|
|
40
|
+
| **Numeric index** | `/resume 2` | Selects the second item in the listed order (1-based) |
|
|
41
|
+
| **Title substring** | `/resume auth` | First session whose title contains "auth" (case-insensitive) |
|
|
42
|
+
| **Exact session ID** | `/resume abc123def` | Matches the session's unique ID exactly |
|
|
43
|
+
|
|
44
|
+
### Matching Rules
|
|
45
|
+
|
|
46
|
+
- **Numeric index** is checked first. If the number is out of range (fewer sessions than the number given), it falls through to title matching.
|
|
47
|
+
- **Title substring** uses `String.prototype.includes()` — any part of the title matching the input selects the first such session. There is no ranking or scoring; the first match wins.
|
|
48
|
+
- **Exact session ID** is the final fallback if neither numeric nor title matching succeeds.
|
|
49
|
+
- If no match is found at all, an error message is shown: `No session matched "foo". Use /resume to list sessions.`
|
|
50
|
+
|
|
51
|
+
### Tab Completion
|
|
52
|
+
|
|
53
|
+
Pressing Tab while typing `/resume <partial>` shows completions for session titles that **start with** the partial text (`startsWith`). This is intentionally stricter than the action's own matching (`includes`) — tab completion may show fewer options than what `/resume <text>` would actually match.
|
|
54
|
+
|
|
55
|
+
### What Happens When You Resume
|
|
56
|
+
|
|
57
|
+
The selected session's full conversation history is restored:
|
|
58
|
+
- The Gemini client's history is replaced with the saved history from the session
|
|
59
|
+
- The session ID is set so subsequent exchanges update the same session record
|
|
60
|
+
- Turn count is restored from the session record
|
|
61
|
+
- The UI displays the restored history as a scrollable message list
|
|
62
|
+
|
|
63
|
+
### Session Storage
|
|
64
|
+
|
|
65
|
+
Sessions are stored in `~/.fss-link/sessions.db` (a dedicated SQLite database). Each session record includes:
|
|
66
|
+
- `id` — unique session identifier
|
|
67
|
+
- `title` — auto-generated from the first user message (truncated to 80 chars)
|
|
68
|
+
- `project_path`, `project_hash` — project identification
|
|
69
|
+
- `provider`, `model_name` — which model was used
|
|
70
|
+
- `created_at`, `updated_at` — timestamps
|
|
71
|
+
- `turn_count`, `total_input_tokens`, `total_output_tokens` — usage metrics
|
|
72
|
+
- `history_json` — full conversation history as JSON
|
|
73
|
+
|
|
74
|
+
Sessions are created on the first exchange in a conversation and updated after each subsequent exchange. An auto-save timer flushes changes every 30 seconds.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## `/rewind` — Rewind to a Previous Turn
|
|
79
|
+
|
|
80
|
+
Rewind the conversation to a selected past turn, discarding everything after it.
|
|
81
|
+
|
|
82
|
+
### Usage
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
/rewind # Open rewind dialog
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
No arguments are accepted — `/rewind foo` behaves identically to `/rewind`.
|
|
89
|
+
|
|
90
|
+
### How It Works
|
|
91
|
+
|
|
92
|
+
Running `/rewind` opens an interactive dialog that shows all user-initiated turns in the current conversation. Selecting a turn discards everything after it, effectively rewinding the conversation to that point.
|
|
93
|
+
|
|
94
|
+
### Turn Extraction
|
|
95
|
+
|
|
96
|
+
The dialog filters the conversation history to show only meaningful user turns:
|
|
97
|
+
- **Included**: User messages with text content
|
|
98
|
+
- **Excluded**: Tool-response turns (all parts are `functionResponse`), system notes (`[System note:`), steering messages (`[Steering message`)
|
|
99
|
+
|
|
100
|
+
Turns are displayed in reverse-chronological order (most recent first), numbered T1, T2, T3… from oldest to newest.
|
|
101
|
+
|
|
102
|
+
### Navigation & Search
|
|
103
|
+
|
|
104
|
+
| Key | Action |
|
|
105
|
+
|-----|--------|
|
|
106
|
+
| `↑` / `↓` | Move selection up/down |
|
|
107
|
+
| `Enter` | Confirm selection and rewind |
|
|
108
|
+
| `Esc` (first press) | Clear the search filter |
|
|
109
|
+
| `Esc` (second press) | Close the dialog without rewinding |
|
|
110
|
+
| `Backspace` | Delete one character from the search filter |
|
|
111
|
+
| Any printable character | Type into the search filter box |
|
|
112
|
+
|
|
113
|
+
### Search
|
|
114
|
+
|
|
115
|
+
The dialog has a working text filter. Type any characters to narrow the visible turns:
|
|
116
|
+
|
|
117
|
+
- **Case-insensitive substring match** on turn text
|
|
118
|
+
- Selection resets to the top (index 0) when the filter changes
|
|
119
|
+
- Shows "No turns match" if the filter produces zero results
|
|
120
|
+
- First `Esc` clears the filter and restores the full list; second `Esc` closes the dialog
|
|
121
|
+
|
|
122
|
+
**Limitations:**
|
|
123
|
+
- Single-term only — multi-word queries like "auth middleware" won't work as two separate terms
|
|
124
|
+
- Searches only user message text, not tool calls, results, or delta events
|
|
125
|
+
- No ranking or scoring — just `includes` substring match
|
|
126
|
+
|
|
127
|
+
### What Happens When You Rewind
|
|
128
|
+
|
|
129
|
+
1. The conversation history is cleared
|
|
130
|
+
2. A preview message is built from the selected turn's content
|
|
131
|
+
3. All turns after the selected one are discarded
|
|
132
|
+
4. The model can then continue from the rewound point
|
|
133
|
+
|
|
134
|
+
### Known Behavior
|
|
135
|
+
|
|
136
|
+
The rewind point uses `fullHistory.slice(0, contentIndex)` — the selected turn is **included** in the rewound history. The dialog help text says "conversation after selected turn will be discarded," which is accurate: the selected turn itself is preserved, everything after it is removed.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## `/sessions` — Alias for `/resume`
|
|
141
|
+
|
|
142
|
+
`/sessions` and `/continue` are aliases for `/resume`. All three names invoke the same command with identical behavior. See `/resume` above for full details.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Welcome Back Dialog — Project-Level Resume
|
|
147
|
+
|
|
148
|
+
The **Welcome Back dialog** is a separate feature from `/resume`. It appears automatically when you launch FSS Link in a project that has prior session history, offering a high-level choice to continue from where you left off or start fresh.
|
|
149
|
+
|
|
150
|
+
### When It Appears
|
|
151
|
+
|
|
152
|
+
- On startup, when `enableWelcomeBack` is enabled in settings (default: on)
|
|
153
|
+
- Only when the project has existing conversation history
|
|
154
|
+
- Uses `ProjectSummaryParser` to analyze project state — not `sessions.db` directly
|
|
155
|
+
|
|
156
|
+
### Options
|
|
157
|
+
|
|
158
|
+
| Option | Keyboard Shortcut | Behavior |
|
|
159
|
+
|--------|-------------------|----------|
|
|
160
|
+
| **Continue Project** | `C` | Resumes from the most recent session with contextual summary |
|
|
161
|
+
| **Start Fresh Session** | `R` | Begins a new conversation with no history |
|
|
162
|
+
| **Cancel** | `Q` / `Escape` | Exits without starting a session |
|
|
163
|
+
|
|
164
|
+
### What It Shows
|
|
165
|
+
|
|
166
|
+
The dialog displays project-level information including:
|
|
167
|
+
- Project goal (if available)
|
|
168
|
+
- Time since last activity
|
|
169
|
+
- Session count and active sessions
|
|
170
|
+
- Momentum indicator (high / building / stale / none)
|
|
171
|
+
- Git status (branch, staged/unstaged changes, last commit)
|
|
172
|
+
- Efficiency metrics and recent activity summary
|
|
173
|
+
- Next suggestion (if available)
|
|
174
|
+
|
|
175
|
+
### Key Differences from `/resume`
|
|
176
|
+
|
|
177
|
+
| Aspect | Welcome Back Dialog | `/resume` |
|
|
178
|
+
|--------|---------------------|-----------|
|
|
179
|
+
| **Trigger** | Automatic on project launch | Manual slash command |
|
|
180
|
+
| **Scope** | Project-level (one choice) | Per-session picker (up to 50) |
|
|
181
|
+
| **Data source** | `ProjectSummaryParser` (analysis of project state) | `sessions.db` (direct session records) |
|
|
182
|
+
| **Selection** | Continue / Restart / Cancel | Number, title substring, or session ID |
|
|
183
|
+
| **Context** | Smart summary built from project analysis | Full conversation history restored verbatim |
|
|
184
|
+
| **Search** | None — single decision point | Substring search across session titles |
|
|
185
|
+
|
|
186
|
+
The Welcome Back dialog is best for quick decisions at project launch. `/resume` is for browsing and selecting from multiple saved sessions within an active conversation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fss-link",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.0.0"
|
|
6
6
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "git+https://github.com/FSSCoding/fss-link.git"
|
|
14
14
|
},
|
|
15
15
|
"config": {
|
|
16
|
-
"sandboxImageUri": "ghcr.io/fsscoding/fss-link:1.9.
|
|
16
|
+
"sandboxImageUri": "ghcr.io/fsscoding/fss-link:1.9.3"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "node scripts/start.js",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"LICENSE"
|
|
83
83
|
],
|
|
84
84
|
"devDependencies": {
|
|
85
|
+
"@testing-library/dom": "^10.4.1",
|
|
85
86
|
"@types/ink-testing-library": "^1.0.4",
|
|
86
87
|
"@vitest/coverage-v8": "^4.1.8",
|
|
87
88
|
"concurrently": "^9.2.0",
|
|
@@ -46,4 +46,22 @@ for (const file of sbFiles) {
|
|
|
46
46
|
copyFileSync(join(root, file), join(bundleDir, basename(file)));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Copy bundled default skills (SKILL.md files) alongside the bundled JS.
|
|
50
|
+
// bundledSkills.ts reads these via `path.join(__dirname, './<dir>/SKILL.md')`
|
|
51
|
+
// relative to the running bundle/fss-link-main.js, so the source tree layout
|
|
52
|
+
// (packages/core/src/bundled-skills/<dir>/SKILL.md) must be mirrored here as
|
|
53
|
+
// bundle/<dir>/SKILL.md — esbuild only bundles JS, it never copies these.
|
|
54
|
+
const skillFiles = glob.sync('packages/core/src/bundled-skills/**/SKILL.md', {
|
|
55
|
+
cwd: root,
|
|
56
|
+
});
|
|
57
|
+
for (const file of skillFiles) {
|
|
58
|
+
const rel = file.replace('packages/core/src/bundled-skills/', '');
|
|
59
|
+
const dest = join(bundleDir, rel);
|
|
60
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
61
|
+
copyFileSync(join(root, file), dest);
|
|
62
|
+
}
|
|
63
|
+
if (skillFiles.length > 0) {
|
|
64
|
+
console.log(`Bundled skills copied to bundle/ (${skillFiles.length} file(s))`);
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
console.log('Assets copied to bundle/');
|