@kendoo.agentdesk/agentdesk 0.19.5 → 0.19.6

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 CHANGED
@@ -13,6 +13,11 @@ Internal refactors, infrastructure changes, and architectural notes are not list
13
13
  ### Added
14
14
  - `[UI]` Private-session sharing flow. When someone visits a session URL they don't own, they now see a friendly "Shh… this one's private" page with a one-click "Request access" button instead of a blank error. The session owner sees pending requests in a new header inbox and can grant viewer access for just that session or the whole project. Viewer-granted sessions show up in the teammate's sidebar tagged as a viewer.
15
15
 
16
+ ## [0.19.6] — 2026-04-19
17
+
18
+ ### Changed
19
+ - `[CLI]` `agentdesk bootstrap` now auto-selects the project silently when the git remote's repo name matches the project's id or name (case-insensitive, scope-stripped), in addition to the existing exact `github.repo` match. No confirmation prompt is shown for a confident single match — the clone just picks up the right project and continues. The numbered picker is only shown when auto-match finds zero or multiple candidates.
20
+
16
21
  ## [0.19.5] — 2026-04-19
17
22
 
18
23
  ### Changed
package/cli/bootstrap.mjs CHANGED
@@ -57,15 +57,20 @@ async function fetchProjectSettings(apiKey, projectId) {
57
57
  } catch { return {}; }
58
58
  }
59
59
 
60
+ // Normalize a name for fuzzy comparison: lowercase, strip npm scope (@scope/)
61
+ // and everything up to the last `/`, then drop non-alphanumerics.
62
+ function normalizeName(s) {
63
+ if (!s) return "";
64
+ const tail = s.includes("/") ? s.slice(s.lastIndexOf("/") + 1) : s;
65
+ return tail.toLowerCase().replace(/[^a-z0-9]/g, "");
66
+ }
67
+
60
68
  // Discover the server-side project for this clone when .agentdesk.json is
61
- // absent. Strategy:
62
- // 1. Fetch the user's projects list.
63
- // 2. For each, fetch settings so we can see github.repo.
64
- // 3. If we can match the local git remote to exactly one project's repo,
65
- // auto-select (confirmation prompt).
66
- // 4. Otherwise show a numbered picker of all the user's projects.
67
- // 5. Write a minimal `.agentdesk.json` locally so loadConfig takes over
68
- // from here.
69
+ // absent. Strategy (each tier that yields exactly one match auto-selects):
70
+ // Tier 1 — server-side github.repo equals the local git remote (owner/repo).
71
+ // Tier 2 the repo name from the remote matches the project id or name
72
+ // (case-insensitive, scope-stripped).
73
+ // When 0 or 2+ projects match, fall back to a numbered picker.
69
74
  // Returns the chosen projectKey, or null if the user aborts.
70
75
  async function discoverProject(rl, cwd, apiKey) {
71
76
  const remote = detectGitRemote(cwd);
@@ -83,35 +88,44 @@ async function discoverProject(rl, cwd, apiKey) {
83
88
 
84
89
  // Fetch settings in parallel so we can display/match the configured repo.
85
90
  const settings = await Promise.all(projects.map(p => fetchProjectSettings(apiKey, p.id)));
91
+ const remoteRepoName = remote ? remote.split("/").pop() : null;
92
+ const remoteKey = normalizeName(remoteRepoName);
86
93
  const rows = projects.map((p, i) => {
87
94
  const repo = settings[i]?.github?.repo || null;
88
- const matches = remote && repo && repo.toLowerCase() === remote.toLowerCase();
89
- return { project: p, settings: settings[i] || {}, repo, matches };
95
+ const tier1 = remote && repo && repo.toLowerCase() === remote.toLowerCase();
96
+ const tier2 = !tier1 && remoteKey && (
97
+ normalizeName(p.id) === remoteKey || normalizeName(p.name) === remoteKey
98
+ );
99
+ return { project: p, settings: settings[i] || {}, repo, tier1, tier2 };
90
100
  });
91
101
 
92
- const exactMatches = rows.filter(r => r.matches);
102
+ const tier1Matches = rows.filter(r => r.tier1);
103
+ const tier2Matches = rows.filter(r => r.tier2);
93
104
  let chosen = null;
94
-
95
- if (exactMatches.length === 1) {
96
- const r = exactMatches[0];
97
- console.log(` ✓ Match: ${r.project.name} (${r.project.id}) — repo ${r.repo}`);
98
- console.log("");
99
- const ans = (await askInline(rl, ` Use this project? [Y/n]: `)).trim().toLowerCase();
100
- if (ans === "" || ans === "y" || ans === "yes") chosen = r;
105
+ let matchReason = null;
106
+
107
+ if (tier1Matches.length === 1) {
108
+ chosen = tier1Matches[0];
109
+ matchReason = `github.repo matches ${chosen.repo}`;
110
+ } else if (tier1Matches.length === 0 && tier2Matches.length === 1) {
111
+ chosen = tier2Matches[0];
112
+ matchReason = `project name matches repo "${remoteRepoName}"`;
101
113
  }
102
114
 
103
- if (!chosen) {
115
+ if (chosen) {
116
+ console.log(` ✓ Auto-matched: ${chosen.project.name} (${chosen.project.id}) — ${matchReason}`);
117
+ } else {
104
118
  console.log("");
105
- console.log(" Pick the project this repo maps to:");
119
+ console.log(" Couldn't auto-match this clone to one of your projects.");
106
120
  console.log("");
107
121
  rows.forEach((r, i) => {
108
- const tag = r.matches ? " ← matches git remote" : "";
122
+ const tag = r.tier1 ? " ← repo match" : r.tier2 ? " ← name match" : "";
109
123
  const repoStr = r.repo ? ` [${r.repo}]` : "";
110
124
  console.log(` ${i + 1}) ${r.project.name} (${r.project.id})${repoStr}${tag}`);
111
125
  });
112
126
  console.log("");
113
127
  while (true) {
114
- const answer = await askInline(rl, ` Choose (1-${rows.length}, or 'c' to cancel): `);
128
+ const answer = await askInline(rl, ` Pick one (1-${rows.length}, or 'c' to cancel): `);
115
129
  const trimmed = answer.trim().toLowerCase();
116
130
  if (trimmed === "c" || trimmed === "cancel") return null;
117
131
  const num = parseInt(trimmed, 10);
@@ -124,9 +138,8 @@ async function discoverProject(rl, cwd, apiKey) {
124
138
  // into it on the next call, producing the full cached view.
125
139
  const minimal = { projectKey: chosen.project.id };
126
140
  writeFileSync(join(cwd, ".agentdesk.json"), JSON.stringify(minimal, null, 2) + "\n");
127
- console.log("");
128
141
  console.log(` ✓ Wrote .agentdesk.json (projectKey: ${chosen.project.id})`);
129
- console.log(" (commit this file so future clones skip the picker)");
142
+ console.log(" (commit this file so future clones skip discovery)");
130
143
  return chosen.project.id;
131
144
  }
132
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.19.5",
3
+ "version": "0.19.6",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {