@retasc/cli 1.13.1 → 1.14.0
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 +33 -0
- package/dist/commands/bind.js +68 -3
- package/dist/config.js +4 -0
- package/dist/index.js +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,39 @@ release commits and the issues they reference.
|
|
|
6
6
|
|
|
7
7
|
Dates are the npm publish date. Each entry names the RTSC issue behind it.
|
|
8
8
|
|
|
9
|
+
## 1.14.0 (2026-08-02)
|
|
10
|
+
|
|
11
|
+
- **RTSC-523** — setup asks before installing anything on your machine.
|
|
12
|
+
|
|
13
|
+
`retasc bind` and `retasc join` are one command you paste and answer, and somewhere in the
|
|
14
|
+
middle they ran `npm install -g @retasc/cli`. It said so, but it never asked. That is the
|
|
15
|
+
only step in a folder-scoped command that changes the machine rather than the folder, and
|
|
16
|
+
a shared npm prefix is exactly what plenty of developers keep clean.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Install the retasc command on this machine?
|
|
20
|
+
|
|
21
|
+
1) Yes, globally A `retasc` command you can use anywhere.
|
|
22
|
+
Best if you'll use Retasc in more than one project.
|
|
23
|
+
2) No, run it on demand Nothing is installed. Your agent fetches it when it
|
|
24
|
+
starts, which adds a couple of seconds and needs a
|
|
25
|
+
network connection.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Both answers leave the folder bound identically. Option 2 is not a per-project install:
|
|
29
|
+
nothing is written to the folder, so it needs no `package.json` and cannot fail for want
|
|
30
|
+
of an npm project. Your answer is remembered, because it is a question about the machine
|
|
31
|
+
and the machine has not changed by the time you bind a second folder.
|
|
32
|
+
|
|
33
|
+
You are only asked when it would actually happen. If `retasc` already works, there is
|
|
34
|
+
nothing to decide and nothing is said.
|
|
35
|
+
|
|
36
|
+
**Without a TTY it installs, exactly as before.** That is deliberate rather than a
|
|
37
|
+
leftover: when an agent runs setup on someone's behalf, that person never types `retasc`,
|
|
38
|
+
but their agent starts the MCP server every session, and the on-demand route would cost
|
|
39
|
+
them seconds and a network dependency every single time. `--no-install` on `bind` and
|
|
40
|
+
`join` declines without a TTY, for a developer whose own agent is doing the setup.
|
|
41
|
+
|
|
9
42
|
## 1.13.1 (2026-08-02)
|
|
10
43
|
|
|
11
44
|
- **RTSC-522** — the changelog is public, and five releases that were never written down now
|
package/dist/commands/bind.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { api, cliError } from "../api.js";
|
|
2
2
|
import { deviceLogin } from "../auth.js";
|
|
3
|
-
import { loadConfig } from "../config.js";
|
|
3
|
+
import { loadConfig, patchConfig } from "../config.js";
|
|
4
4
|
import { installMarker } from "./mcp.js";
|
|
5
5
|
import { readLocalBinding, resolveBinding } from "../lib/binding.js";
|
|
6
6
|
import { getBinding, setBinding, newWorkspaceId } from "../lib/keystore.js";
|
|
7
|
-
import { resolveLauncher, launcherNote, selfCommand } from "../lib/launcher.js";
|
|
7
|
+
import { resolveLauncher, launcherNote, runsOk, selfCommand } from "../lib/launcher.js";
|
|
8
8
|
import { ask, confirm, isInteractive } from "../lib/prompt.js";
|
|
9
9
|
import { clean } from "../lib/text.js";
|
|
10
10
|
import { VERSION } from "../version.js";
|
|
@@ -151,6 +151,63 @@ export async function rebindGuard(args) {
|
|
|
151
151
|
}
|
|
152
152
|
return { proceed: false, existing };
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* May we install `retasc` on this machine? (RTSC-523)
|
|
156
|
+
*
|
|
157
|
+
* Asked only when it would actually happen. `runsOk` is the same probe
|
|
158
|
+
* `resolveLauncher` opens with, so if `retasc` already works there is nothing to decide and
|
|
159
|
+
* a question would have exactly one possible outcome.
|
|
160
|
+
*
|
|
161
|
+
* A DEFAULT is right here, unlike the sign-in door (RTSC-508), which deliberately has none.
|
|
162
|
+
* A wrong answer costs a couple of seconds per agent start, not a second identity that
|
|
163
|
+
* cannot be merged back. The two questions look alike and are not.
|
|
164
|
+
*
|
|
165
|
+
* NO TTY ⇒ install, silently, exactly as before. That is not laziness about the default: it
|
|
166
|
+
* is the right answer for the person it affects most. The non-technical member of RTSC-497
|
|
167
|
+
* never opens a terminal — her AGENT runs this (RTSC-495/496), with no TTY. She will never
|
|
168
|
+
* type `retasc` herself, but her agent spawns the MCP server on every start, and the npx
|
|
169
|
+
* route would cost her seconds and a network dependency every single time, forever.
|
|
170
|
+
* `--no-install` exists for the developer who wants to decline without a TTY.
|
|
171
|
+
*/
|
|
172
|
+
export async function chooseInstall(
|
|
173
|
+
/** `--no-install` ⇒ false. Undefined means "not stated". */
|
|
174
|
+
flag, deps = {}) {
|
|
175
|
+
if (flag === false)
|
|
176
|
+
return false;
|
|
177
|
+
const onPath = deps.onPath ?? (() => runsOk("retasc") !== null);
|
|
178
|
+
// Nothing to install, so nothing to ask.
|
|
179
|
+
if (onPath())
|
|
180
|
+
return true;
|
|
181
|
+
const remembered = (deps.remembered ?? (() => loadConfig().globalInstall))();
|
|
182
|
+
if (remembered !== undefined)
|
|
183
|
+
return remembered;
|
|
184
|
+
const interactive = deps.interactive ?? isInteractive;
|
|
185
|
+
if (!interactive())
|
|
186
|
+
return true;
|
|
187
|
+
console.log("\nInstall the retasc command on this machine?");
|
|
188
|
+
console.log(" 1) Yes, globally A `retasc` command you can use anywhere.");
|
|
189
|
+
console.log(" Best if you'll use Retasc in more than one project.");
|
|
190
|
+
console.log(" 2) No, run it on demand Nothing is installed. Your agent fetches it when it");
|
|
191
|
+
console.log(" starts, which adds a couple of seconds and needs a");
|
|
192
|
+
console.log(" network connection.");
|
|
193
|
+
const askFn = deps.askFn ?? ask;
|
|
194
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
195
|
+
const a = await askFn("Choose a number [1]: ");
|
|
196
|
+
// Either answer leaves this folder bound identically — only the machine differs — so
|
|
197
|
+
// an empty answer taking the default is safe here in a way it is not for the door.
|
|
198
|
+
if (a === "" || a === "1")
|
|
199
|
+
return remember(true);
|
|
200
|
+
if (a === "2")
|
|
201
|
+
return remember(false);
|
|
202
|
+
console.log("Please enter 1 or 2.");
|
|
203
|
+
}
|
|
204
|
+
throw new Error("no valid choice — aborting");
|
|
205
|
+
}
|
|
206
|
+
/** Record the answer, so binding a second folder does not re-ask about the same machine. */
|
|
207
|
+
function remember(globalInstall) {
|
|
208
|
+
patchConfig({ globalInstall });
|
|
209
|
+
return globalInstall;
|
|
210
|
+
}
|
|
154
211
|
/**
|
|
155
212
|
* Everything from "which project" to a working folder: pick the project, make `retasc`
|
|
156
213
|
* durable, mint a key for that (org, project), write the binding, and wire the marker.
|
|
@@ -237,7 +294,15 @@ export async function completeWorkspaceSetup(args) {
|
|
|
237
294
|
// name one proved to run on this machine. Resolved (and announced) here so the install
|
|
238
295
|
// can't land after a key exists, and so every later message knows what to tell the user
|
|
239
296
|
// to type.
|
|
240
|
-
|
|
297
|
+
//
|
|
298
|
+
// RTSC-523 — and ASK first, because this is the one step in a folder-scoped command that
|
|
299
|
+
// changes the MACHINE. It used to just run `npm install -g`. The project already holds
|
|
300
|
+
// the opposite position everywhere else: `resolveLauncher` leaves a working `retasc`
|
|
301
|
+
// alone ("no surprise installs for someone who already manages their own"), and RTSC-520
|
|
302
|
+
// decided an update must ask. The first command a new developer runs was the one place
|
|
303
|
+
// we did it anyway.
|
|
304
|
+
const install = await chooseInstall(opts.install);
|
|
305
|
+
const launcher = resolveLauncher({ version: VERSION, install });
|
|
241
306
|
const note = launcherNote(launcher);
|
|
242
307
|
if (note)
|
|
243
308
|
console.log(note);
|
package/dist/config.js
CHANGED
|
@@ -61,6 +61,10 @@ export function loadConfig() {
|
|
|
61
61
|
loginProvider: stored.loginProvider === "github" || stored.loginProvider === "google"
|
|
62
62
|
? stored.loginProvider
|
|
63
63
|
: undefined,
|
|
64
|
+
// Same rule (RTSC-523): only a real boolean counts. Anything else reads as
|
|
65
|
+
// "never asked", so a mangled file leads to a question rather than to an
|
|
66
|
+
// install nobody agreed to.
|
|
67
|
+
globalInstall: typeof stored.globalInstall === "boolean" ? stored.globalInstall : undefined,
|
|
64
68
|
};
|
|
65
69
|
}
|
|
66
70
|
/** Move a corrupt config aside to a unique sibling so a human can recover any
|
package/dist/index.js
CHANGED
|
@@ -178,6 +178,10 @@ program
|
|
|
178
178
|
.option("--agent <name>", "Agent member name (default: auto)")
|
|
179
179
|
.option("--runtime <runtime>", "Agent runtime", "claude-code")
|
|
180
180
|
.option("-y, --yes", "Don't prompt to confirm replacing an existing binding")
|
|
181
|
+
// RTSC-523 — decline the global install without a TTY. `bind`/`join` ask when a human
|
|
182
|
+
// is present; this is how a scripted run, or a developer whose own agent runs setup,
|
|
183
|
+
// says no up front instead of being installed onto.
|
|
184
|
+
.option("--no-install", "Don't install `retasc` on this machine; wire the pinned npx launcher instead")
|
|
181
185
|
.action(async (opts) => {
|
|
182
186
|
requireLogin();
|
|
183
187
|
await bindAction(opts).catch(fail);
|
|
@@ -400,6 +404,10 @@ program
|
|
|
400
404
|
// RTSC-477 — name the way back. `--yes` skips the identity question and must never answer
|
|
401
405
|
// it, so the flag that causes the gap is the right place to say how to close it.
|
|
402
406
|
.option("-y, --yes", "Don't prompt to replace an existing binding, and skip the identity question (ask it later with `retasc identity`)")
|
|
407
|
+
// RTSC-523 — decline the global install without a TTY. `bind`/`join` ask when a human
|
|
408
|
+
// is present; this is how a scripted run, or a developer whose own agent runs setup,
|
|
409
|
+
// says no up front instead of being installed onto.
|
|
410
|
+
.option("--no-install", "Don't install `retasc` on this machine; wire the pinned npx launcher instead")
|
|
403
411
|
.allowExcessArguments(false)
|
|
404
412
|
.action(async (link, opts) => {
|
|
405
413
|
await joinAction(link, opts).catch(fail);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retasc/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Retasc CLI \u2014 the issue tracker AI agents pull work from. Sign in with GitHub or Google, create projects, mint agent API keys, and wire your agent to the Retasc MCP server in one command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|