@userreason/wizard 0.2.1 → 0.3.1

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.
Files changed (40) hide show
  1. package/README.md +95 -0
  2. package/dist/agent-note.js +7 -6
  3. package/dist/agent-skill.js +6 -5
  4. package/dist/analytics.js +9 -0
  5. package/dist/bin.js +222 -51
  6. package/dist/chunk-4INLEQE2.js +196 -0
  7. package/dist/{chunk-GGARVSPT.js → chunk-7AAH6C4L.js} +9198 -5670
  8. package/dist/{chunk-ULHKVBPD.js → chunk-7O7H3PIM.js} +7 -7
  9. package/dist/chunk-CSZW2ULJ.js +152 -0
  10. package/dist/{chunk-3RDJORRS.js → chunk-DMXUXEQI.js} +1 -1
  11. package/dist/{chunk-SJVLAYGG.js → chunk-EVNEX5N5.js} +8 -1
  12. package/dist/chunk-KF37D5WL.js +44 -0
  13. package/dist/{chunk-5SC2K42Q.js → chunk-LEJFE4U5.js} +2 -2
  14. package/dist/{chunk-OEGNGGZ2.js → chunk-NMHTFC5Q.js} +10 -3
  15. package/dist/{chunk-6CJ4ANMN.js → chunk-NNPYUATH.js} +1 -1
  16. package/dist/chunk-PJQOSDYS.js +50 -0
  17. package/dist/chunk-Q5U5UOQN.js +288 -0
  18. package/dist/{chunk-5Z5J6PY5.js → chunk-SB3AYQWG.js} +6 -6
  19. package/dist/{chunk-YMSGDC4B.js → chunk-TOID3NYP.js} +40 -11
  20. package/dist/{chunk-JD5U4U26.js → chunk-TRBSH5G7.js} +6 -6
  21. package/dist/{chunk-ZGZOBW2B.js → chunk-XASPGFYK.js} +3 -3
  22. package/dist/{chunk-HACTNBV3.js → chunk-XPFKXLIZ.js} +3 -3
  23. package/dist/chunk-ZHJV247H.js +31 -0
  24. package/dist/cli-access.js +3 -3
  25. package/dist/connection.js +2 -2
  26. package/dist/first-survey.js +11 -0
  27. package/dist/footer.js +17 -0
  28. package/dist/global-cli.js +6 -5
  29. package/dist/install-diagnostics.js +9 -0
  30. package/dist/integrate.js +5 -4
  31. package/dist/project.js +8 -3
  32. package/dist/registry.js +5 -3
  33. package/dist/setup-flow.js +25 -0
  34. package/dist/setup.js +33 -0
  35. package/dist/skills/userreason/SKILL.md +43 -0
  36. package/dist/skills/userreason/references/management.md +6 -0
  37. package/dist/startup.js +2 -2
  38. package/dist/state.js +6 -5
  39. package/dist/ui.js +2 -2
  40. package/package.json +1 -2
@@ -0,0 +1,288 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ planFooter
4
+ } from "./chunk-4INLEQE2.js";
5
+ import {
6
+ planAgentSkill
7
+ } from "./chunk-SB3AYQWG.js";
8
+ import {
9
+ detectInstallation,
10
+ initializationPath,
11
+ planIntegration
12
+ } from "./chunk-LEJFE4U5.js";
13
+ import {
14
+ compatibleProductionVersion,
15
+ inspectProject,
16
+ installCli,
17
+ installSdk,
18
+ installedVersion,
19
+ integrationSource
20
+ } from "./chunk-TOID3NYP.js";
21
+ import {
22
+ planPackageRegistry
23
+ } from "./chunk-NMHTFC5Q.js";
24
+ import {
25
+ SetupError,
26
+ apiUrl,
27
+ connectBrowserSetup,
28
+ detectSetupTerminal,
29
+ packageProfile,
30
+ readSetupFile,
31
+ resumeBrowserSetup,
32
+ runBrowserSetup,
33
+ runSetupExecutor,
34
+ setupDigest,
35
+ setupHash,
36
+ verifySetupFiles,
37
+ writeSetupFile
38
+ } from "./chunk-7AAH6C4L.js";
39
+
40
+ // src/setup.ts
41
+ import { basename } from "node:path";
42
+
43
+ // src/setup-plan.ts
44
+ import { randomUUID } from "node:crypto";
45
+ import { lstat, readdir, readFile } from "node:fs/promises";
46
+ import { join, relative } from "node:path";
47
+ var relativeName = (project, path) => relative(project.directory, path).replaceAll("\\", "/");
48
+ var metadata = (files) => files.map((file2) => ({ path: file2.path, before_hash: file2.before === null ? null : setupHash(file2.before), after_hash: setupHash(file2.after) }));
49
+ var exactVersion = (value) => {
50
+ const version = value.replace(/^[~^]/, "");
51
+ if (!/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?(?:\+[A-Za-z0-9.-]+)?$/.test(version)) throw new SetupError("setup_package_version", "The package profile must resolve to an exact version before review.");
52
+ return version;
53
+ };
54
+ async function alreadyCompatible(project, name, profile) {
55
+ try {
56
+ const manifest = JSON.parse(await readSetupFile(project.directory, "package.json"));
57
+ const declaration = (name === "@userreason/js" ? manifest.dependencies : manifest.devDependencies)?.[name];
58
+ if (!declaration) return false;
59
+ const installed = JSON.parse(await readSetupFile(project.directory, `node_modules/${name}/package.json`)).version;
60
+ if (typeof installed !== "string") return false;
61
+ return profile.kind === "local-development" ? installed === (name === "@userreason/js" ? profile.sdkVersion : profile.cliVersion) : compatibleProductionVersion(installed, name === "@userreason/js" ? profile.sdkVersion : profile.cliVersion);
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+ async function file(project, path, after) {
67
+ const name = relativeName(project, path);
68
+ return { path: name, before: await readSetupFile(project.directory, name), after };
69
+ }
70
+ async function installationFiles(project, config) {
71
+ const plan = await planIntegration(project, config);
72
+ if (plan.status === "manual") return null;
73
+ if (plan.status === "integrated") {
74
+ const found = await detectInstallation(project), path = await initializationPath(project);
75
+ if (!found || !path || found.siteId !== config.siteId || found.apiBaseUrl !== config.apiBaseUrl) return null;
76
+ const before = await readSetupFile(project.directory, relativeName(project, path));
77
+ return before === null ? null : [{ path: relativeName(project, path), before, after: before }];
78
+ }
79
+ if (plan.status !== "ready") return null;
80
+ const result = [await file(project, plan.path, plan.after)];
81
+ if (plan.legacyNext) {
82
+ const helper = integrationSource(project.framework, config, project.typescript, true);
83
+ const planned = await file(project, join(project.sourceDirectory, helper.filename), helper.content);
84
+ if (planned.before !== null && planned.before !== planned.after) return null;
85
+ result.unshift(planned);
86
+ }
87
+ return result;
88
+ }
89
+ async function prepareSetupPlan(project, session, apiOrigin, selectedProfile = packageProfile()) {
90
+ const profile = { ...selectedProfile, sdkVersion: exactVersion(selectedProfile.sdkVersion), cliVersion: exactVersion(selectedProfile.cliVersion) };
91
+ const operations = [], manual = [];
92
+ const config = session.site_id ? { siteId: session.site_id, apiBaseUrl: apiUrl(apiOrigin) } : null;
93
+ return prepare(project, session, profile, config, operations, manual);
94
+ }
95
+ async function prepare(project, session, profile, config, operations = [], manual = []) {
96
+ const add = (kind, files, extra = {}) => {
97
+ const effect = { effect_id: randomUUID(), kind, files: metadata(files), ...extra };
98
+ operations.push({ effect, files });
99
+ };
100
+ let initialized = false;
101
+ let initialization = null;
102
+ if (session.choices.install_sdk) {
103
+ initialization = config ? await installationFiles(project, config) : null;
104
+ initialized = initialization !== null;
105
+ if (!initialization) manual.push({ kind: "initialize_sdk", code: "review_required" });
106
+ } else if (config) {
107
+ const installed = await detectInstallation(project);
108
+ initialized = installed?.siteId === config.siteId && installed?.apiBaseUrl === config.apiBaseUrl;
109
+ }
110
+ const packages = [];
111
+ if (session.choices.install_sdk && !await alreadyCompatible(project, "@userreason/js", profile)) packages.push({ name: "@userreason/js", version: exactVersion(profile.sdkVersion) });
112
+ if (session.choices.install_skill && initialized && !await alreadyCompatible(project, "@userreason/cli", profile)) packages.push({ name: "@userreason/cli", version: exactVersion(profile.cliVersion) });
113
+ if (packages.length) {
114
+ const before = await readSetupFile(project.directory, "package.json");
115
+ if (!before) throw new SetupError("setup_manifest_invalid", "The project needs an ordinary package.json.");
116
+ const manifest = JSON.parse(before);
117
+ for (const item of packages) {
118
+ const section = item.name === "@userreason/js" ? "dependencies" : "devDependencies";
119
+ if (manifest[section] && (typeof manifest[section] !== "object" || Array.isArray(manifest[section]))) throw new SetupError("setup_manifest_invalid", "The dependency manifest needs review.");
120
+ manifest[section] = { ...manifest[section], [item.name]: item.version };
121
+ const other = section === "dependencies" ? "devDependencies" : "dependencies";
122
+ if (manifest[other]?.[item.name] !== void 0) {
123
+ delete manifest[other][item.name];
124
+ if (!Object.keys(manifest[other]).length) delete manifest[other];
125
+ }
126
+ }
127
+ const indent = /\n([ \t]+)"/.exec(before)?.[1] ?? " ";
128
+ const after = JSON.stringify(manifest, null, indent) + (before.endsWith("\n") ? "\n" : "");
129
+ const files = [{ path: "package.json", before, after }];
130
+ const registry = await planPackageRegistry(project, profile);
131
+ if (registry) files.push({ ...registry, path: relativeName(project, registry.path) });
132
+ const guards = [];
133
+ for (const path of [".npmrc", ".yarnrc", ".yarnrc.yml"]) {
134
+ const content = await readSetupFile(project.directory, path);
135
+ if (!files.some((file2) => file2.path === path) && content !== null) files.push({ path, before: content, after: content });
136
+ guards.push({ path, before_hash: content === null ? null : setupHash(content) });
137
+ }
138
+ const paths = project.packageManager === "npm" ? ["package-lock.json"] : project.packageManager === "pnpm" ? ["pnpm-lock.yaml"] : ["yarn.lock", ".pnp.cjs", ".pnp.loader.mjs"];
139
+ const managed_outputs = await Promise.all(paths.map(async (path) => {
140
+ const content = await readSetupFile(project.directory, path);
141
+ return { path, before_hash: content === null ? null : setupHash(content) };
142
+ }));
143
+ add("install_packages", files, { package_manager: project.packageManager, packages, managed_outputs });
144
+ operations.at(-1).guards = guards;
145
+ }
146
+ if (initialization) add("initialize_sdk", initialization, { site_id: config.siteId });
147
+ if (session.choices.install_skill) {
148
+ if (!initialized) manual.push({ kind: "install_skill", code: "dependency_failed" });
149
+ else {
150
+ const skill = await planAgentSkill(project);
151
+ if ("files" in skill && (!skill.update || Object.keys(skill.update.snapshot).every((path) => skill.files.some((file2) => file2.path === path)))) {
152
+ const files = await Promise.all(skill.files.map((item) => file(project, join(skill.path, item.path), item.content)));
153
+ add("install_skill", files);
154
+ } else if (!("files" in skill) && skill.reason === "up_to_date" && skill.path) {
155
+ const files = await Promise.all(["references/management.md", "SKILL.md"].map(async (path) => {
156
+ const name = `${skill.path}/${path}`;
157
+ const before = await readSetupFile(project.directory, name);
158
+ if (before === null) throw new Error("Missing installed skill");
159
+ return { path: name, before, after: before };
160
+ }));
161
+ add("install_skill", files);
162
+ } else manual.push({ kind: "install_skill", code: "review_required" });
163
+ }
164
+ }
165
+ if (session.choices.footer_badge) {
166
+ const overlay = new Map(operations.flatMap((operation) => operation.files.map((file2) => [join(project.directory, file2.path), file2.after])));
167
+ const footer = await planFooter(project, overlay);
168
+ if (footer.status === "manual") manual.push({ kind: "insert_footer", code: footer.code === "missing_footer" ? "unsupported" : "review_required" });
169
+ else add("insert_footer", [{ path: relativeName(project, footer.path), before: footer.before, after: footer.after }], { placement: { path: relativeName(project, footer.path), anchor_hash: footer.anchorHash } });
170
+ }
171
+ return { wire: { project_fingerprint: session.project_fingerprint, choices_digest: setupDigest(session.choices), effects: operations.map((item) => item.effect), ...manual.length ? { manual_steps: manual } : {} }, operations };
172
+ }
173
+ async function snapshot(project) {
174
+ const result = {}, stack = [""];
175
+ let entries = 0, bytes = 0;
176
+ while (stack.length) {
177
+ const folder = stack.pop();
178
+ for (const entry of await readdir(join(project.directory, folder), { withFileTypes: true })) {
179
+ if (++entries > 2e4) throw new SetupError("setup_files_changed", "Project scanning exceeds the bounded review budget.");
180
+ const path = folder ? `${folder}/${entry.name}` : entry.name;
181
+ if (path === "node_modules" || path === ".git" || /^\.userreason-npm-/.test(path) || /^\.yarn\/(cache|unplugged)(\/|$)/.test(path) || path === ".yarn/install-state.gz") continue;
182
+ const info = await lstat(join(project.directory, path));
183
+ if (info.isSymbolicLink()) {
184
+ result[path] = "link";
185
+ continue;
186
+ }
187
+ if (info.isDirectory()) stack.push(path);
188
+ else if (info.isFile()) {
189
+ bytes += info.size;
190
+ if (info.size > 2e7 || bytes > 64e6) throw new SetupError("setup_files_changed", "Project scanning exceeds the bounded byte budget.");
191
+ result[path] = setupHash(await readFile(join(project.directory, path)));
192
+ }
193
+ }
194
+ }
195
+ return result;
196
+ }
197
+ function setupRuntime(project, apiOrigin, selectedProfile = packageProfile()) {
198
+ apiOrigin = apiUrl(apiOrigin);
199
+ const profile = { ...selectedProfile, sdkVersion: exactVersion(selectedProfile.sdkVersion), cliVersion: exactVersion(selectedProfile.cliVersion) };
200
+ return {
201
+ prepare: (session) => prepare(project, session, profile, session.site_id ? { siteId: session.site_id, apiBaseUrl: apiOrigin } : null),
202
+ async apply(operation, signal) {
203
+ const current = await inspectProject(project.directory, project.framework);
204
+ if (current.packageManager !== project.packageManager) throw new SetupError("setup_files_changed", "The package manager changed after review.");
205
+ if (operation.effect.kind !== "install_packages") {
206
+ if (operation.effect.kind === "insert_footer") {
207
+ const footer = await planFooter(current);
208
+ if (footer.status === "manual" || relativeName(project, footer.path) !== operation.effect.placement.path || footer.anchorHash !== operation.effect.placement.anchor_hash || footer.after !== operation.files[0]?.after)
209
+ throw new SetupError("setup_files_changed", "The reviewed footer placement changed.");
210
+ }
211
+ if (operation.effect.kind === "install_skill") {
212
+ const skill = await planAgentSkill(current);
213
+ if ("files" in skill) {
214
+ if (skill.files.length !== operation.files.length || skill.files.some((item) => !operation.files.some((file2) => file2.path === relativeName(project, join(skill.path, item.path)) && file2.after === item.content)))
215
+ throw new SetupError("setup_files_changed", "The reviewed skill changed.");
216
+ } else if (skill.reason !== "up_to_date" || !operation.files.every((file2) => file2.before === file2.after)) throw new SetupError("setup_files_changed", "An existing skill needs review.");
217
+ }
218
+ const already = operation.files.every((file2) => file2.before === file2.after);
219
+ for (const file2 of operation.files) {
220
+ signal.throwIfAborted();
221
+ await writeSetupFile(project.directory, file2);
222
+ }
223
+ return already ? "already_present" : "verified";
224
+ }
225
+ if (operation.effect.package_manager !== current.packageManager) throw new SetupError("setup_command_mismatch", "The package manager differs from the reviewed command.");
226
+ for (const item of operation.effect.packages) if (item.version !== (item.name === "@userreason/js" ? profile.sdkVersion : profile.cliVersion)) throw new SetupError("setup_package_version", "The approved package version differs from this local package profile.");
227
+ const before = await snapshot(project);
228
+ for (const file2 of operation.files.filter((file3) => file3.path !== "package.json")) await writeSetupFile(project.directory, file2);
229
+ for (const item of operation.effect.packages) {
230
+ signal.throwIfAborted();
231
+ await (item.name === "@userreason/js" ? installSdk : installCli)(current, { profile, exact: true, signal, configureRegistry: false });
232
+ }
233
+ const manifest = operation.files.find((file2) => file2.path === "package.json");
234
+ const actual = await readSetupFile(project.directory, manifest.path);
235
+ if (!actual || setupDigest(JSON.parse(actual)) !== setupDigest(JSON.parse(manifest.after))) throw new Error("The package manager made unexpected manifest changes");
236
+ await writeSetupFile(project.directory, { ...manifest, before: actual });
237
+ const after = await snapshot(project), allowed = /* @__PURE__ */ new Set([...operation.files.map((file2) => file2.path), ...(operation.effect.managed_outputs ?? []).map((file2) => file2.path)]);
238
+ for (const path of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) if (!allowed.has(path) && before[path] !== after[path]) throw new Error("The package manager changed an unreviewed project file");
239
+ return "verified";
240
+ },
241
+ async verify(operation) {
242
+ if (!await verifySetupFiles(project.directory, operation.files, "after")) return false;
243
+ if (operation.effect.kind !== "install_packages") return true;
244
+ for (const item of operation.effect.packages) if (await installedVersion(project, item.name).catch(() => "") !== item.version) return false;
245
+ return true;
246
+ }
247
+ };
248
+ }
249
+
250
+ // src/setup.ts
251
+ function connectSetup(apiOrigin, project, onBrowser, options = {}) {
252
+ return connectBrowserSetup(apiOrigin, {
253
+ directory: project.directory,
254
+ browserOrigin: options.browserOrigin,
255
+ capabilities: {
256
+ framework: project.framework,
257
+ package_manager: project.packageManager,
258
+ install_packages: true,
259
+ initialize_sdk: true,
260
+ install_skill: true,
261
+ insert_footer: true,
262
+ source_review: true,
263
+ sample_read: true,
264
+ ...options.automaticSetup ? { onboarding_v2: true } : {},
265
+ ...options.launcher ? { launcher: options.launcher } : {},
266
+ project_name: basename(project.directory).replace(/[\u0000-\u001f\u007f\/\\]/g, "").trim().slice(0, 120) || "Application",
267
+ terminal: detectSetupTerminal()
268
+ }
269
+ }, onBrowser, options);
270
+ }
271
+ function resumeSetup(apiOrigin, setupId, project, options = {}) {
272
+ return resumeBrowserSetup(apiOrigin, setupId, project.directory, options);
273
+ }
274
+ function executeSetup(client, project, options = {}) {
275
+ return runSetupExecutor(client, project.directory, setupRuntime(project, client.identity.api_url), options);
276
+ }
277
+ function runSetup(client, project, options = {}) {
278
+ return runBrowserSetup(client, project.directory, setupRuntime(project, client.identity.api_url), options);
279
+ }
280
+
281
+ export {
282
+ prepareSetupPlan,
283
+ setupRuntime,
284
+ connectSetup,
285
+ resumeSetup,
286
+ executeSetup,
287
+ runSetup
288
+ };
@@ -1,13 +1,13 @@
1
1
  import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
- import {
3
- Cancelled
4
- } from "./chunk-SJVLAYGG.js";
5
2
  import {
6
3
  cliCommand
7
- } from "./chunk-YMSGDC4B.js";
4
+ } from "./chunk-TOID3NYP.js";
8
5
  import {
9
6
  require_dist
10
- } from "./chunk-OEGNGGZ2.js";
7
+ } from "./chunk-NMHTFC5Q.js";
8
+ import {
9
+ Cancelled
10
+ } from "./chunk-EVNEX5N5.js";
11
11
  import {
12
12
  __toESM
13
13
  } from "./chunk-WL3NOIM6.js";
@@ -245,7 +245,7 @@ Agents can propose feedback investigations, read the current documentation and u
245
245
  terminal.success("Feedback skill installed in .agents/skills/userreason");
246
246
  return { status: "added", path: ".agents/skills/userreason" };
247
247
  } catch (error) {
248
- if (error instanceof Cancelled) return { status: "declined" };
248
+ if (error instanceof Cancelled) return { status: "declined", reason: "user_cancelled" };
249
249
  terminal.warn("Installation is complete. The optional agent skill could not be installed; the skill directory needs review.");
250
250
  return { status: "failed", reason: "skill_directory_needs_review" };
251
251
  }
@@ -1,10 +1,13 @@
1
1
  import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ installFailureDetails
4
+ } from "./chunk-PJQOSDYS.js";
2
5
  import {
3
6
  configurePackageRegistry
4
- } from "./chunk-OEGNGGZ2.js";
7
+ } from "./chunk-NMHTFC5Q.js";
5
8
  import {
6
9
  packageProfile
7
- } from "./chunk-GGARVSPT.js";
10
+ } from "./chunk-7AAH6C4L.js";
8
11
 
9
12
  // src/project.ts
10
13
  import { lstat, readFile, writeFile, access, mkdtemp, realpath, rm } from "node:fs/promises";
@@ -140,7 +143,7 @@ async function hasCompatibleSdk(project, profile = packageProfile()) {
140
143
  try {
141
144
  const manifest = JSON.parse(await readFile(join(project.directory, "package.json"), "utf8"));
142
145
  const version = await installedVersion(project, "@userreason/js");
143
- return Boolean(manifest.dependencies?.["@userreason/js"] && (profile.kind === "local-development" ? version === profile.sdkVersion : /^0\.4\.\d+$/.test(version)));
146
+ return Boolean(manifest.dependencies?.["@userreason/js"] && (profile.kind === "local-development" ? version === profile.sdkVersion : compatibleProductionVersion(version, profile.sdkVersion)));
144
147
  } catch {
145
148
  return false;
146
149
  }
@@ -157,8 +160,13 @@ async function hasCompatibleCli(project, profile = packageProfile()) {
157
160
  return false;
158
161
  }
159
162
  }
163
+ function compatibleProductionVersion(version, requested) {
164
+ const installed = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
165
+ const target = /^(\d+)\.(\d+)\.(\d+)$/.exec(requested.replace(/^[~^]/, ""));
166
+ return Boolean(installed && target && installed[1] === target[1] && installed[2] === target[2] && Number(installed[3]) >= Number(target[3]));
167
+ }
160
168
  function compatibleCliVersion(version, profile) {
161
- return profile.kind === "local-development" ? version === profile.cliVersion : /^0\.1\.\d+$/.test(version) && Number(version.split(".")[2]) >= 1;
169
+ return profile.kind === "local-development" ? version === profile.cliVersion : compatibleProductionVersion(version, profile.cliVersion);
162
170
  }
163
171
  function cliInstallCommand(project, profile = packageProfile()) {
164
172
  return sdkInstallCommand(project, profile).replace("@userreason/js@" + profile.sdkVersion, "--save-dev @userreason/cli@" + profile.cliVersion).replace("yarn add --mode=skip-build --save-dev", "yarn add --mode=skip-build --dev").replace("yarn add --ignore-scripts --save-dev", "yarn add --ignore-scripts --dev");
@@ -177,10 +185,12 @@ async function installDependency(project, dependency, options) {
177
185
  const spec = dependency === "sdk" ? `@userreason/js@${profile.sdkVersion}` : `@userreason/cli@${profile.cliVersion}`;
178
186
  const args = project.packageManager === "npm" ? ["install", "--ignore-scripts", spec] : project.packageManager === "pnpm" ? ["add", "--ignore-scripts", spec] : (project.yarnMajor ?? 1) >= 2 ? ["add", "--mode=skip-build", spec] : ["add", "--ignore-scripts", spec];
179
187
  if (dependency === "cli") args.push(project.packageManager === "yarn" ? "--dev" : "--save-dev");
180
- if (profile.kind === "local-development") args.push(project.packageManager === "yarn" ? "--exact" : "--save-exact");
188
+ if (profile.kind === "local-development" || options.exact) args.push(project.packageManager === "yarn" ? "--exact" : "--save-exact");
181
189
  let cache;
190
+ let logs = "";
182
191
  try {
183
- await configurePackageRegistry(project, profile);
192
+ options.signal?.throwIfAborted();
193
+ if (options.configureRegistry !== false) await configurePackageRegistry(project, profile);
184
194
  if (project.packageManager === "npm") {
185
195
  cache = await mkdtemp(join(await realpath(project.directory), ".userreason-npm-"));
186
196
  args.push("--cache", cache);
@@ -197,16 +207,28 @@ async function installDependency(project, dependency, options) {
197
207
  ...cache ? { npm_config_cache: cache, NPM_CONFIG_CACHE: cache } : {}
198
208
  }
199
209
  });
200
- let logs = "";
210
+ let killTimer;
211
+ const abort = () => {
212
+ child.kill("SIGTERM");
213
+ killTimer = setTimeout(() => child.kill("SIGKILL"), 2e3);
214
+ killTimer.unref();
215
+ };
216
+ options.signal?.addEventListener("abort", abort, { once: true });
217
+ if (options.signal?.aborted) abort();
201
218
  const capture = (chunk) => {
202
219
  const text = chunk.toString();
203
220
  logs = (logs + text).slice(-64e3);
204
- if (options.verbose) process.stderr.write(text);
205
221
  };
206
222
  child.stdout?.on("data", capture);
207
223
  child.stderr?.on("data", capture);
208
224
  child.on("error", () => reject(new SdkInstallError(`Unable to start ${project.packageManager}.`)));
209
225
  child.on("close", (code) => {
226
+ options.signal?.removeEventListener("abort", abort);
227
+ if (killTimer) clearTimeout(killTimer);
228
+ if (options.signal?.aborted) {
229
+ reject(options.signal.reason);
230
+ return;
231
+ }
210
232
  if (code === 0) {
211
233
  if (/\b[1-9]\d*\s+vulnerabilit(?:y|ies)\b|\bWARN\b|\bwarning\b/i.test(logs))
212
234
  options.onWarning?.(
@@ -217,19 +239,24 @@ async function installDependency(project, dependency, options) {
217
239
  const restricted = /ERR_ACCESS_DENIED|Access to this API has been restricted/.test(logs);
218
240
  reject(
219
241
  new SdkInstallError(
220
- restricted ? `Your Node or package-manager permission policy blocked ${label} installation. Run the manual install command in your normal terminal.` : `${label} installation failed (${project.packageManager} exit ${code}). Rerun with --debug to inspect package-manager output.`
242
+ restricted ? `Your Node or package-manager permission policy blocked ${label} installation. Run the manual install command in your normal terminal.` : `${label} installation failed (${project.packageManager} exit ${code}). Package-manager details follow.`
221
243
  )
222
244
  );
223
245
  }
224
246
  });
225
247
  });
226
248
  } catch (error) {
227
- if (error instanceof SdkInstallError) throw error;
249
+ if (error instanceof SdkInstallError) {
250
+ const details = await installFailureDetails(project.directory, cache, logs);
251
+ if (details) error.message += "\n" + details;
252
+ throw error;
253
+ }
228
254
  throw new SdkInstallError(
229
- "Unable to prepare a writable SDK install cache inside the project. Run the manual install command in your normal terminal.",
255
+ "Unable to prepare or run package installation. " + await installFailureDetails(project.directory, cache, error instanceof Error ? error.message : "Unknown local error."),
230
256
  { cause: error }
231
257
  );
232
258
  } finally {
259
+ if (options.verbose && logs) process.stderr.write(await installFailureDetails(project.directory, cache, logs) + "\n");
233
260
  if (cache) {
234
261
  try {
235
262
  await rm(cache, { recursive: true, force: true });
@@ -266,10 +293,12 @@ export {
266
293
  initializationSource,
267
294
  integrationSource,
268
295
  writeIntegration,
296
+ installedVersion,
269
297
  cliCommand,
270
298
  hasCompatibleSdk,
271
299
  sdkInstallCommand,
272
300
  hasCompatibleCli,
301
+ compatibleProductionVersion,
273
302
  compatibleCliVersion,
274
303
  cliInstallCommand,
275
304
  SdkInstallError,
@@ -1,14 +1,14 @@
1
1
  import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
- import {
3
- Cancelled
4
- } from "./chunk-SJVLAYGG.js";
5
2
  import {
6
3
  cliCommand,
7
4
  compatibleCliVersion
8
- } from "./chunk-YMSGDC4B.js";
5
+ } from "./chunk-TOID3NYP.js";
6
+ import {
7
+ Cancelled
8
+ } from "./chunk-EVNEX5N5.js";
9
9
  import {
10
10
  packageProfile
11
- } from "./chunk-GGARVSPT.js";
11
+ } from "./chunk-7AAH6C4L.js";
12
12
 
13
13
  // src/global-cli.ts
14
14
  import { execFile } from "node:child_process";
@@ -102,7 +102,7 @@ This installs development CLI ${profile.cliVersion} from ${profile.registry}, re
102
102
  if (!available) terminal.warn(`Global CLI ${installed.version} is installed, but this terminal does not resolve userreason to it. Check npm's global bin directory in PATH and restart your terminal or agent. Use ${cliCommand(project)} in this project meanwhile.`);
103
103
  return { status: reuse ? "already_present" : "installed", version: installed.version, command_available: available };
104
104
  } catch (error) {
105
- if (error instanceof Cancelled) return { status: "declined" };
105
+ if (error instanceof Cancelled) return { status: "declined", reason: "user_cancelled" };
106
106
  terminal.warn(`The optional global CLI could not be set up. ${error instanceof Error ? error.message : ""}`);
107
107
  terminal.note(`${command}
108
108
 
@@ -1,15 +1,15 @@
1
1
  import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
2
  import {
3
3
  detectInstallation
4
- } from "./chunk-5SC2K42Q.js";
4
+ } from "./chunk-LEJFE4U5.js";
5
5
  import {
6
6
  DEFAULT_API
7
- } from "./chunk-YMSGDC4B.js";
7
+ } from "./chunk-TOID3NYP.js";
8
8
  import {
9
9
  apiUrl,
10
10
  configDirectory,
11
11
  uuid
12
- } from "./chunk-GGARVSPT.js";
12
+ } from "./chunk-7AAH6C4L.js";
13
13
 
14
14
  // src/state.ts
15
15
  import { mkdir, readFile, writeFile, rename, rm, realpath, lstat } from "node:fs/promises";
@@ -2,14 +2,14 @@ import { createRequire as createWizardRequire } from 'node:module'; import { fil
2
2
  import {
3
3
  Cancelled,
4
4
  openBrowser
5
- } from "./chunk-SJVLAYGG.js";
5
+ } from "./chunk-EVNEX5N5.js";
6
6
  import {
7
7
  authorizeCli,
8
8
  grantSummary,
9
9
  printable,
10
10
  resolveCredentials,
11
11
  saveConfig
12
- } from "./chunk-GGARVSPT.js";
12
+ } from "./chunk-7AAH6C4L.js";
13
13
 
14
14
  // src/cli-access.ts
15
15
  async function offerCliAccess(config, terminal, options) {
@@ -39,7 +39,7 @@ async function offerCliAccess(config, terminal, options) {
39
39
  return { status: "approved", grant };
40
40
  } catch (error) {
41
41
  waiting?.stop();
42
- if (error instanceof Cancelled) return { status: "declined" };
42
+ if (error instanceof Cancelled) return { status: "declined", reason: "user_cancelled" };
43
43
  if (error instanceof Error) terminal.warn(printable(error.message));
44
44
  terminal.warn("The application is installed, but ongoing CLI access was not connected. Run the project CLI with login --open to finish.");
45
45
  return { status: "failed" };
@@ -0,0 +1,31 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ firstSurvey,
4
+ firstSurveyHandoff
5
+ } from "./chunk-7AAH6C4L.js";
6
+
7
+ // src/first-survey.ts
8
+ var surveySuggestionsEnabled = false;
9
+ function beginFirstSurveyPreparation(_root, _mode, _prepare) {
10
+ return void 0;
11
+ }
12
+ async function offerFirstSurvey(client, root, workspace, site, ui, mode) {
13
+ if (!surveySuggestionsEnabled || mode.development || !mode.interactive || mode.preview) return { state: "skipped" };
14
+ const choice = await ui.choose(
15
+ "Find your first useful survey? Nothing changes or publishes automatically.",
16
+ [
17
+ { value: "guided", label: "Review suggestions" },
18
+ { value: "agent", label: "Continue with my coding agent" },
19
+ { value: "finish", label: "Finish setup" }
20
+ ],
21
+ "finish"
22
+ );
23
+ if (choice === "agent") ui.show(firstSurveyHandoff(root, client.config.apiUrl, workspace, site));
24
+ if (choice === "guided") return firstSurvey(client, root, workspace, site, ui);
25
+ return { state: "skipped" };
26
+ }
27
+
28
+ export {
29
+ beginFirstSurveyPreparation,
30
+ offerFirstSurvey
31
+ };
@@ -1,9 +1,9 @@
1
1
  import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
2
  import {
3
3
  offerCliAccess
4
- } from "./chunk-HACTNBV3.js";
5
- import "./chunk-SJVLAYGG.js";
6
- import "./chunk-GGARVSPT.js";
4
+ } from "./chunk-XPFKXLIZ.js";
5
+ import "./chunk-EVNEX5N5.js";
6
+ import "./chunk-7AAH6C4L.js";
7
7
  import "./chunk-WL3NOIM6.js";
8
8
  export {
9
9
  offerCliAccess
@@ -4,10 +4,10 @@ import {
4
4
  compatibilityCredentials,
5
5
  connectInstallation,
6
6
  memoryCredentials
7
- } from "./chunk-3RDJORRS.js";
7
+ } from "./chunk-DMXUXEQI.js";
8
8
  import {
9
9
  CredentialStoreError
10
- } from "./chunk-GGARVSPT.js";
10
+ } from "./chunk-7AAH6C4L.js";
11
11
  import "./chunk-WL3NOIM6.js";
12
12
  export {
13
13
  BrowserConnectionUnavailable,
@@ -0,0 +1,11 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ beginFirstSurveyPreparation,
4
+ offerFirstSurvey
5
+ } from "./chunk-ZHJV247H.js";
6
+ import "./chunk-7AAH6C4L.js";
7
+ import "./chunk-WL3NOIM6.js";
8
+ export {
9
+ beginFirstSurveyPreparation,
10
+ offerFirstSurvey
11
+ };
package/dist/footer.js ADDED
@@ -0,0 +1,17 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ FOOTER_LINK,
4
+ applyFooter,
5
+ planFooter
6
+ } from "./chunk-4INLEQE2.js";
7
+ import "./chunk-LEJFE4U5.js";
8
+ import "./chunk-TOID3NYP.js";
9
+ import "./chunk-PJQOSDYS.js";
10
+ import "./chunk-NMHTFC5Q.js";
11
+ import "./chunk-7AAH6C4L.js";
12
+ import "./chunk-WL3NOIM6.js";
13
+ export {
14
+ FOOTER_LINK,
15
+ applyFooter,
16
+ planFooter
17
+ };
@@ -2,11 +2,12 @@ import { createRequire as createWizardRequire } from 'node:module'; import { fil
2
2
  import {
3
3
  globalCliInstallCommand,
4
4
  offerGlobalCli
5
- } from "./chunk-JD5U4U26.js";
6
- import "./chunk-SJVLAYGG.js";
7
- import "./chunk-YMSGDC4B.js";
8
- import "./chunk-OEGNGGZ2.js";
9
- import "./chunk-GGARVSPT.js";
5
+ } from "./chunk-TRBSH5G7.js";
6
+ import "./chunk-TOID3NYP.js";
7
+ import "./chunk-PJQOSDYS.js";
8
+ import "./chunk-NMHTFC5Q.js";
9
+ import "./chunk-EVNEX5N5.js";
10
+ import "./chunk-7AAH6C4L.js";
10
11
  import "./chunk-WL3NOIM6.js";
11
12
  export {
12
13
  globalCliInstallCommand,
@@ -0,0 +1,9 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ installFailureDetails
4
+ } from "./chunk-PJQOSDYS.js";
5
+ import "./chunk-7AAH6C4L.js";
6
+ import "./chunk-WL3NOIM6.js";
7
+ export {
8
+ installFailureDetails
9
+ };