@pellux/goodvibes-agent 0.1.60 → 0.1.61
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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/docs/getting-started.md +1 -1
- package/package.json +7 -3
- package/src/cli/help.ts +3 -17
- package/src/cli/package-verification.ts +1 -2
- package/src/input/commands/shell-core.ts +7 -0
- package/src/version.ts +1 -1
- package/.goodvibes/skills/add-provider/SKILL.md +0 -199
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.61 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- ff6766d Stop shipping repo-local skills
|
|
8
|
+
- 2eedcf5 Focus primary CLI help on Agent use
|
|
9
|
+
- 0ec6af0 Document Bun trust path for SDK native deps
|
|
10
|
+
- f9a6c76 Surface Agent actions in slash help
|
|
11
|
+
|
|
5
12
|
## 0.1.60 - 2026-05-31
|
|
6
13
|
|
|
7
14
|
- f3f2486 Stop shipping developer guidance in package
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ goodvibes-agent --help
|
|
|
29
29
|
If Bun reports untrusted lifecycle dependencies, trust only the package and dependencies required by this package:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
bun pm trust -g @pellux/goodvibes-agent @pellux/goodvibes-sdk core-js
|
|
32
|
+
bun pm trust -g @pellux/goodvibes-agent @pellux/goodvibes-sdk core-js tree-sitter-css tree-sitter-javascript tree-sitter-json tree-sitter-python tree-sitter-typescript
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## Source Usage
|
package/docs/getting-started.md
CHANGED
|
@@ -28,7 +28,7 @@ goodvibes-agent --help
|
|
|
28
28
|
If Bun requires lifecycle trust:
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
|
-
bun pm trust -g @pellux/goodvibes-agent @pellux/goodvibes-sdk core-js
|
|
31
|
+
bun pm trust -g @pellux/goodvibes-agent @pellux/goodvibes-sdk core-js tree-sitter-css tree-sitter-javascript tree-sitter-json tree-sitter-python tree-sitter-typescript
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Run From Source
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.61",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"bin",
|
|
14
14
|
"LICENSE",
|
|
15
|
-
".goodvibes/skills",
|
|
16
15
|
"src",
|
|
17
16
|
"!src/test",
|
|
18
17
|
"!src/**/*.test.ts",
|
|
@@ -107,7 +106,12 @@
|
|
|
107
106
|
},
|
|
108
107
|
"trustedDependencies": [
|
|
109
108
|
"@pellux/goodvibes-sdk",
|
|
110
|
-
"core-js"
|
|
109
|
+
"core-js",
|
|
110
|
+
"tree-sitter-css",
|
|
111
|
+
"tree-sitter-javascript",
|
|
112
|
+
"tree-sitter-json",
|
|
113
|
+
"tree-sitter-python",
|
|
114
|
+
"tree-sitter-typescript"
|
|
111
115
|
],
|
|
112
116
|
"overrides": {
|
|
113
117
|
"minimatch": "^10.2.5"
|
package/src/cli/help.ts
CHANGED
|
@@ -31,9 +31,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
31
31
|
'Commands:',
|
|
32
32
|
' tui [path] Start the interactive Agent terminal UI (default)',
|
|
33
33
|
' run|exec [prompt] Run non-interactively with text/json/stream-json output',
|
|
34
|
-
'
|
|
35
|
-
' service Inspect existing daemon service posture (read-only)',
|
|
36
|
-
' status Print config, provider, service, and onboarding posture',
|
|
34
|
+
' status Print config, provider, auth, and onboarding posture',
|
|
37
35
|
' doctor Print status plus setup warnings',
|
|
38
36
|
' onboarding [status] Open Agent onboarding, or print onboarding status',
|
|
39
37
|
' models [provider] List/use/pin selectable models and recent model history',
|
|
@@ -48,14 +46,9 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
48
46
|
' subscription Start/finish/logout provider subscription sessions',
|
|
49
47
|
' secrets List, set, link, delete, and test GoodVibes secret refs',
|
|
50
48
|
' sessions List, show, export, or resume saved sessions',
|
|
51
|
-
' tasks List/show in-process runtime tasks (read-only)',
|
|
52
49
|
' pair|qrcode Print companion pairing payload and QR code',
|
|
53
|
-
' surfaces Inspect/check browser/listener/external surfaces (read-only)',
|
|
54
|
-
' listener test Test HTTP listener/webhook readiness',
|
|
55
|
-
' control-plane status Inspect daemon auth, local admin, tokens, and ports',
|
|
56
50
|
' bundle export|inspect|import',
|
|
57
51
|
' Move setup/profile/trust/support bundles',
|
|
58
|
-
' remote|bridge Inspect remote runner/node posture',
|
|
59
52
|
' completion <shell> Generate shell completion script',
|
|
60
53
|
' help [command] Print this help or command-specific help',
|
|
61
54
|
' version Print version',
|
|
@@ -66,7 +59,6 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
66
59
|
' --agent-profile <name> Use an isolated Agent runtime profile home',
|
|
67
60
|
' -C, --cd <dir> Set working directory for this launch',
|
|
68
61
|
' --working-dir <dir> Alias for --cd',
|
|
69
|
-
' --daemon-home <dir> Override daemon home for daemon-backed commands',
|
|
70
62
|
' -c, --config <key=value> Override a config value for this launch',
|
|
71
63
|
' --enable <feature> Enable a feature flag for this launch',
|
|
72
64
|
' --disable <feature> Disable a feature flag for this launch',
|
|
@@ -76,9 +68,6 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
76
68
|
' --output-format <format> Alias for --output',
|
|
77
69
|
' --json Alias for --output-format json',
|
|
78
70
|
' --no-alt-screen Keep output in normal terminal scrollback',
|
|
79
|
-
' --port <port> Port for server/web commands',
|
|
80
|
-
' --hostname <host> Hostname for server/web commands',
|
|
81
|
-
' --open Open browser when supported',
|
|
82
71
|
' -r, --resume [id|latest] Resume saved session when supported',
|
|
83
72
|
' -s, --session <id> Use a specific session when supported',
|
|
84
73
|
' --continue Continue the latest session when supported',
|
|
@@ -105,13 +94,10 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
105
94
|
` ${binary} ask "What is GoodVibes Agent?"`,
|
|
106
95
|
` ${binary} search "release checklist"`,
|
|
107
96
|
` ${binary} delegate --wrfc "fix the failing tests in ~/work/project"`,
|
|
108
|
-
` ${binary} surfaces`,
|
|
109
|
-
` ${binary} surfaces check`,
|
|
110
|
-
` ${binary} service check`,
|
|
111
|
-
` ${binary} listener test`,
|
|
112
|
-
` ${binary} control-plane status`,
|
|
113
97
|
` ${binary} subscription providers`,
|
|
114
98
|
` ${binary} subscription login openai start --open`,
|
|
99
|
+
` ${binary} help service`,
|
|
100
|
+
` ${binary} help surfaces`,
|
|
115
101
|
].join('\n');
|
|
116
102
|
}
|
|
117
103
|
|
|
@@ -42,7 +42,7 @@ const REQUIRED_TARBALL_PATHS = [
|
|
|
42
42
|
'docs/deployment-and-services.md',
|
|
43
43
|
'docs/release-and-publishing.md',
|
|
44
44
|
] as const;
|
|
45
|
-
const FORBIDDEN_TARBALL_PREFIXES = ['.github/', 'src/test/', 'src/.test/', '.goodvibes/
|
|
45
|
+
const FORBIDDEN_TARBALL_PREFIXES = ['.github/', 'src/test/', 'src/.test/', '.goodvibes/', 'vendor/'] as const;
|
|
46
46
|
const FORBIDDEN_TARBALL_DOCS = [
|
|
47
47
|
['docs/cloud', 'flare-batch.md'].join(''),
|
|
48
48
|
['docs/home', 'assistant-surface.md'].join(''),
|
|
@@ -55,7 +55,6 @@ const PACKAGE_FACING_TEXT_PATHS = [
|
|
|
55
55
|
'docs/getting-started.md',
|
|
56
56
|
'docs/deployment-and-services.md',
|
|
57
57
|
'docs/release-and-publishing.md',
|
|
58
|
-
'.goodvibes/skills/add-provider/SKILL.md',
|
|
59
58
|
] as const;
|
|
60
59
|
const PACKAGE_FACING_FORBIDDEN_TEXT = [
|
|
61
60
|
['/api/', 'knowledge'].join(''),
|
|
@@ -116,6 +116,13 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
116
116
|
const items: SelectionItem[] = [
|
|
117
117
|
{ id: '/agent', label: '/agent', detail: 'Open the Agent operator workspace', category: 'Agent Operator' },
|
|
118
118
|
{ id: '/home', label: '/home', detail: 'Alias for the Agent operator workspace', category: 'Agent Operator' },
|
|
119
|
+
{ id: '/knowledge', label: '/knowledge', detail: 'Inspect isolated Agent Knowledge status, ask/search, and ingest flows', category: 'Agent Operator' },
|
|
120
|
+
{ id: '/memory', label: '/memory', detail: 'Review local Agent memory records', category: 'Agent Operator' },
|
|
121
|
+
{ id: '/personas', label: '/personas', detail: 'Create, review, and activate local Agent personas', category: 'Agent Operator' },
|
|
122
|
+
{ id: '/agent-skills', label: '/agent-skills', detail: 'Create, review, and enable reusable Agent skills', category: 'Agent Operator' },
|
|
123
|
+
{ id: '/routines', label: '/routines', detail: 'Create, review, start, and promote Agent routines explicitly', category: 'Agent Operator' },
|
|
124
|
+
{ id: '/delegate', label: '/delegate [task]', detail: 'Explicit build/fix/review handoff to GoodVibes TUI', category: 'Agent Operator' },
|
|
125
|
+
{ id: '/pair', label: '/pair', detail: 'Pair companion clients through the external GoodVibes service', category: 'Agent Operator' },
|
|
119
126
|
{ id: '/model', label: '/model [id]', detail: 'Select LLM model', category: 'Model & Provider' },
|
|
120
127
|
{ id: '/provider', label: '/provider [name]', detail: 'Switch provider', category: 'Model & Provider' },
|
|
121
128
|
{ id: '/effort', label: '/effort [level]', detail: 'Reasoning effort (instant/low/medium/high)', category: 'Model & Provider' },
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.61';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: add-provider
|
|
3
|
-
description: Adds custom LLM providers and models to GoodVibes Agent. Use when the user wants to add a provider, add a model, configure Ollama, Together AI, OpenRouter, Groq, LM Studio, Fireworks, vLLM, or any OpenAI-compatible endpoint.
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
triggers:
|
|
6
|
-
- /add-provider
|
|
7
|
-
- add provider
|
|
8
|
-
- add a provider
|
|
9
|
-
- new provider
|
|
10
|
-
- custom provider
|
|
11
|
-
- add model
|
|
12
|
-
- add a model
|
|
13
|
-
author: goodvibes
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# Add Custom Provider
|
|
17
|
-
|
|
18
|
-
Interactively collect provider and model details from the user, then write a JSON config to `~/.goodvibes/agent/providers/{name}.json`.
|
|
19
|
-
|
|
20
|
-
## Workflow
|
|
21
|
-
|
|
22
|
-
### Step 1: Check for Existing Provider
|
|
23
|
-
|
|
24
|
-
Before collecting info, check if `~/.goodvibes/agent/providers/` already has JSON files. If the user names a provider that already exists, ask:
|
|
25
|
-
- **Add models** to the existing provider, or
|
|
26
|
-
- **Overwrite** it entirely
|
|
27
|
-
|
|
28
|
-
If adding models, read the existing JSON, append new models, and write back.
|
|
29
|
-
|
|
30
|
-
### Step 2: Collect Provider Details
|
|
31
|
-
|
|
32
|
-
Ask the user for each field. Apply smart defaults when the provider name matches a known service.
|
|
33
|
-
|
|
34
|
-
#### Required Fields
|
|
35
|
-
|
|
36
|
-
| Field | Description | Validation |
|
|
37
|
-
|-------|-------------|------------|
|
|
38
|
-
| `name` | Internal ID | Lowercase alphanumeric + hyphens only, 1-64 chars |
|
|
39
|
-
| `displayName` | Human-readable name | Non-empty string |
|
|
40
|
-
| `type` | API compatibility | `openai-compat` (recommended) or `anthropic-compat` (not yet supported — use `openai-compat`) |
|
|
41
|
-
| `baseURL` | API endpoint | Must start with `http://` or `https://` |
|
|
42
|
-
|
|
43
|
-
> **Note:** `anthropic-compat` is accepted in the JSON schema for forward compatibility but is not yet functional at runtime. The loader will skip configs with this type and emit a warning. Use `openai-compat` for now — most Anthropic-compatible proxies (e.g., via LiteLLM) expose an OpenAI-compatible endpoint.
|
|
44
|
-
|
|
45
|
-
#### Optional Fields
|
|
46
|
-
|
|
47
|
-
| Field | Description | Default |
|
|
48
|
-
|-------|-------------|---------|
|
|
49
|
-
| `apiKeyEnv` | Environment variable for API key | None |
|
|
50
|
-
| `apiKey` | Explicit API key (not recommended) | None |
|
|
51
|
-
| `defaultHeaders` | Custom HTTP headers sent with every API request (e.g., for proxy authentication or routing) | None |
|
|
52
|
-
|
|
53
|
-
### Smart Defaults
|
|
54
|
-
|
|
55
|
-
When the user mentions a known provider, pre-fill these values and confirm:
|
|
56
|
-
|
|
57
|
-
```yaml
|
|
58
|
-
ollama:
|
|
59
|
-
displayName: Ollama
|
|
60
|
-
type: openai-compat
|
|
61
|
-
baseURL: http://localhost:11434/v1
|
|
62
|
-
apiKeyEnv: null
|
|
63
|
-
|
|
64
|
-
together:
|
|
65
|
-
displayName: Together AI
|
|
66
|
-
type: openai-compat
|
|
67
|
-
baseURL: https://api.together.xyz/v1
|
|
68
|
-
apiKeyEnv: TOGETHER_API_KEY
|
|
69
|
-
|
|
70
|
-
openrouter:
|
|
71
|
-
displayName: OpenRouter
|
|
72
|
-
type: openai-compat
|
|
73
|
-
baseURL: https://openrouter.ai/api/v1
|
|
74
|
-
apiKeyEnv: OPENROUTER_API_KEY
|
|
75
|
-
|
|
76
|
-
groq:
|
|
77
|
-
displayName: Groq
|
|
78
|
-
type: openai-compat
|
|
79
|
-
baseURL: https://api.groq.com/openai/v1
|
|
80
|
-
apiKeyEnv: GROQ_API_KEY
|
|
81
|
-
|
|
82
|
-
lm-studio:
|
|
83
|
-
displayName: LM Studio
|
|
84
|
-
type: openai-compat
|
|
85
|
-
baseURL: http://localhost:1234/v1
|
|
86
|
-
apiKeyEnv: null
|
|
87
|
-
|
|
88
|
-
fireworks:
|
|
89
|
-
displayName: Fireworks AI
|
|
90
|
-
type: openai-compat
|
|
91
|
-
baseURL: https://api.fireworks.ai/inference/v1
|
|
92
|
-
apiKeyEnv: FIREWORKS_API_KEY
|
|
93
|
-
|
|
94
|
-
vllm:
|
|
95
|
-
displayName: vLLM
|
|
96
|
-
type: openai-compat
|
|
97
|
-
baseURL: http://localhost:8000/v1
|
|
98
|
-
apiKeyEnv: null
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
If the name does not match a known provider, ask for all fields individually.
|
|
102
|
-
|
|
103
|
-
### Step 3: Collect Model Details
|
|
104
|
-
|
|
105
|
-
Collect at least one model. For each model:
|
|
106
|
-
|
|
107
|
-
| Field | Description | Validation |
|
|
108
|
-
|-------|-------------|------------|
|
|
109
|
-
| `id` | Model identifier sent to the API | Non-empty string |
|
|
110
|
-
| `displayName` | Human-readable name | Non-empty string |
|
|
111
|
-
| `description` | Short description | Optional, auto-generate if blank |
|
|
112
|
-
| `contextWindow` | Max tokens in context | Positive integer |
|
|
113
|
-
| `capabilities.toolCalling` | Supports function/tool calling | Boolean, default `true` |
|
|
114
|
-
| `capabilities.codeEditing` | Good at code editing tasks | Boolean, default `true` |
|
|
115
|
-
| `capabilities.reasoning` | Extended reasoning/chain-of-thought | Boolean, default `false` |
|
|
116
|
-
| `capabilities.multimodal` | Supports image input | Boolean, default `false` |
|
|
117
|
-
| `reasoningEffort` | Supported reasoning effort levels | Optional, e.g., `["low", "medium", "high"]` |
|
|
118
|
-
|
|
119
|
-
After each model, ask: "Add another model?" Loop until done.
|
|
120
|
-
|
|
121
|
-
### Step 4: Preview and Confirm
|
|
122
|
-
|
|
123
|
-
Show the complete JSON to the user and ask for confirmation before writing.
|
|
124
|
-
|
|
125
|
-
Example output:
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"name": "ollama",
|
|
130
|
-
"displayName": "Ollama",
|
|
131
|
-
"type": "openai-compat",
|
|
132
|
-
"baseURL": "http://localhost:11434/v1",
|
|
133
|
-
"models": [
|
|
134
|
-
{
|
|
135
|
-
"id": "llama3.3-70b",
|
|
136
|
-
"displayName": "Llama 3.3 70B",
|
|
137
|
-
"description": "Meta's Llama 3.3 70B parameter model",
|
|
138
|
-
"contextWindow": 131072,
|
|
139
|
-
"capabilities": {
|
|
140
|
-
"toolCalling": true,
|
|
141
|
-
"codeEditing": true,
|
|
142
|
-
"reasoning": true,
|
|
143
|
-
"multimodal": false
|
|
144
|
-
},
|
|
145
|
-
"reasoningEffort": ["low", "medium", "high"]
|
|
146
|
-
}
|
|
147
|
-
]
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Step 5: Write the File
|
|
152
|
-
|
|
153
|
-
Write to `~/.goodvibes/agent/providers/{name}.json`. Create the directory if it does not exist.
|
|
154
|
-
|
|
155
|
-
Use `precision_write` with `mode: "fail_if_exists"` for new providers. Use `mode: "overwrite"` when the user chose to overwrite an existing provider or when merging models into an existing file.
|
|
156
|
-
|
|
157
|
-
If the `apiKeyEnv` field is set, include it in the JSON. If the user provided an explicit `apiKey`, include it but warn that storing keys in plain text is not recommended -- suggest using an environment variable instead.
|
|
158
|
-
|
|
159
|
-
If `defaultHeaders` were provided, include them.
|
|
160
|
-
|
|
161
|
-
### Step 6: Confirm
|
|
162
|
-
|
|
163
|
-
Tell the user:
|
|
164
|
-
- The file was written to `~/.goodvibes/agent/providers/{name}.json`
|
|
165
|
-
- The provider should be available to the Agent runtime after provider reload or next Agent startup
|
|
166
|
-
- If an API key is needed, remind them to set the environment variable
|
|
167
|
-
|
|
168
|
-
## Validation Rules
|
|
169
|
-
|
|
170
|
-
Before writing, verify:
|
|
171
|
-
1. `name` matches `/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/` and is 1-64 characters (no trailing hyphens)
|
|
172
|
-
2. `baseURL` starts with `http://` or `https://`
|
|
173
|
-
3. `contextWindow` is a positive integer for every model
|
|
174
|
-
4. At least one model is defined
|
|
175
|
-
5. Every model has a non-empty `id` and `displayName`
|
|
176
|
-
|
|
177
|
-
If validation fails, tell the user which field is invalid and ask for correction.
|
|
178
|
-
|
|
179
|
-
## JSON Schema Reference
|
|
180
|
-
|
|
181
|
-
The provider JSON maps to the codebase types:
|
|
182
|
-
|
|
183
|
-
- Provider registration uses `OpenAICompatProvider` for `openai-compat` type. The `anthropic-compat` type is parsed and accepted in the JSON but is **not yet supported at runtime** — the loader skips it with a warning. Recommend `openai-compat` for all custom providers.
|
|
184
|
-
- `OpenAICompatOptions`: `{ name, baseURL, apiKey, defaultModel, models }`
|
|
185
|
-
- Model entries map to `ModelDefinition`: `{ id, provider, displayName, description, contextWindow, capabilities, reasoningEffort?, selectable }`
|
|
186
|
-
- `reasoningEffort?: string[]` — optional array of supported effort levels, e.g., `["low", "medium", "high"]`
|
|
187
|
-
- The `selectable` field defaults to `true` for custom models
|
|
188
|
-
- The `provider` field in `ModelDefinition` is auto-set to the provider `name`
|
|
189
|
-
|
|
190
|
-
## Conversational Style
|
|
191
|
-
|
|
192
|
-
Be natural and helpful. Guide users step-by-step but do not be overly verbose. If the user provides multiple details at once (e.g., "add ollama with llama3.3-70b"), extract what you can and only ask for missing fields.
|
|
193
|
-
|
|
194
|
-
## Edge Cases
|
|
195
|
-
|
|
196
|
-
- **Unknown context window**: Suggest 4096 as a safe default, note the user can update later
|
|
197
|
-
- **No API key needed**: Omit `apiKeyEnv` and `apiKey` from the JSON entirely
|
|
198
|
-
- **User provides a full JSON blob**: Validate it against the schema and write directly
|
|
199
|
-
- **Multiple providers in one session**: After completing one, ask if they want to add another
|