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.
@@ -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.1",
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.0"
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/');