@userreason/wizard 0.1.0 → 0.2.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/README.md +116 -28
- package/dist/THIRD_PARTY_NOTICES.txt +488 -0
- package/dist/agent-note.js +17 -0
- package/dist/agent-skill.js +16 -0
- package/dist/bin.js +720 -689
- package/dist/chunk-3RDJORRS.js +153 -0
- package/dist/chunk-5Z5J6PY5.js +258 -0
- package/dist/chunk-6CJ4ANMN.js +70 -0
- package/dist/chunk-6DCCEVPD.js +877 -0
- package/dist/chunk-FC7PZNYW.js +115 -0
- package/dist/chunk-GGARVSPT.js +211893 -0
- package/dist/chunk-HACTNBV3.js +51 -0
- package/dist/chunk-JD5U4U26.js +125 -0
- package/dist/chunk-KV6GSBTQ.js +165 -0
- package/dist/chunk-OEGNGGZ2.js +7403 -0
- package/dist/chunk-SJVLAYGG.js +1477 -0
- package/dist/chunk-WL3NOIM6.js +42 -0
- package/dist/chunk-YMSGDC4B.js +280 -0
- package/dist/cli-access.js +10 -0
- package/dist/connection.js +18 -0
- package/dist/global-cli.js +14 -0
- package/dist/integrate.js +21 -0
- package/dist/project.js +35 -101
- package/dist/registry.js +8 -0
- package/dist/skills/userreason/SKILL.md +55 -0
- package/dist/skills/userreason/references/management.md +61 -0
- package/dist/startup.js +11 -0
- package/dist/state.js +18 -0
- package/dist/ui.js +23 -0
- package/package.json +7 -17
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
openBrowser
|
|
5
|
+
} from "./chunk-SJVLAYGG.js";
|
|
6
|
+
import {
|
|
7
|
+
authorizeCli,
|
|
8
|
+
grantSummary,
|
|
9
|
+
printable,
|
|
10
|
+
resolveCredentials,
|
|
11
|
+
saveConfig
|
|
12
|
+
} from "./chunk-GGARVSPT.js";
|
|
13
|
+
|
|
14
|
+
// src/cli-access.ts
|
|
15
|
+
async function offerCliAccess(config, terminal, options) {
|
|
16
|
+
if (!options.installed || options.skip || !terminal.interactive && !options.requested) return { status: "skipped" };
|
|
17
|
+
if (options.preview !== false) terminal.note("Connect the project CLI for ongoing work. The browser lets you choose workspaces and permissions; all current and future workspaces and available permissions are selected initially. Your workspace role still limits every action. This is separate from the short installation approval.", "Ongoing CLI access");
|
|
18
|
+
let waiting;
|
|
19
|
+
try {
|
|
20
|
+
if (!options.requested && !await terminal.confirm("Connect the CLI for ongoing use?", true, { recommended: true })) return { status: "declined" };
|
|
21
|
+
let signedIn = false;
|
|
22
|
+
const show = async (url, label, previous) => {
|
|
23
|
+
const opened = !options.noOpen && await openBrowser(url);
|
|
24
|
+
waiting?.stop(previous);
|
|
25
|
+
waiting = terminal.waiting(label, opened ? {} : { link: url });
|
|
26
|
+
};
|
|
27
|
+
const result = await authorizeCli(config, resolveCredentials(config), {
|
|
28
|
+
onDevice: (device) => {
|
|
29
|
+
signedIn = true;
|
|
30
|
+
return show(device.verificationUriComplete, `Sign in for the CLI ${terminal.kit.S.dot} ${terminal.kit.c.muted("code")} ${terminal.kit.c.code(device.userCode)}`);
|
|
31
|
+
},
|
|
32
|
+
onApproval: (request) => show(request.approval_url, "Review and approve CLI access in the browser", signedIn ? "Signed in" : void 0)
|
|
33
|
+
});
|
|
34
|
+
waiting?.stop();
|
|
35
|
+
await saveConfig(config, options.development);
|
|
36
|
+
const grant = "grant" in result ? result.grant : void 0;
|
|
37
|
+
const summary = grantSummary(grant);
|
|
38
|
+
terminal.success(`CLI access approved${summary ? ` ${terminal.kit.c.muted(`(${summary})`)}` : ""}`);
|
|
39
|
+
return { status: "approved", grant };
|
|
40
|
+
} catch (error) {
|
|
41
|
+
waiting?.stop();
|
|
42
|
+
if (error instanceof Cancelled) return { status: "declined" };
|
|
43
|
+
if (error instanceof Error) terminal.warn(printable(error.message));
|
|
44
|
+
terminal.warn("The application is installed, but ongoing CLI access was not connected. Run the project CLI with login --open to finish.");
|
|
45
|
+
return { status: "failed" };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
offerCliAccess
|
|
51
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
import {
|
|
6
|
+
cliCommand,
|
|
7
|
+
compatibleCliVersion
|
|
8
|
+
} from "./chunk-YMSGDC4B.js";
|
|
9
|
+
import {
|
|
10
|
+
packageProfile
|
|
11
|
+
} from "./chunk-GGARVSPT.js";
|
|
12
|
+
|
|
13
|
+
// src/global-cli.ts
|
|
14
|
+
import { execFile } from "node:child_process";
|
|
15
|
+
import { constants } from "node:fs";
|
|
16
|
+
import { access, mkdtemp, readFile, realpath, rm } from "node:fs/promises";
|
|
17
|
+
import { delimiter, dirname, join, resolve } from "node:path";
|
|
18
|
+
import { promisify } from "node:util";
|
|
19
|
+
var run = promisify(execFile);
|
|
20
|
+
var registry = (profile) => profile.kind === "local-development" ? profile.registry : "https://registry.npmjs.org";
|
|
21
|
+
var registryArgs = (profile) => [`--registry=${registry(profile)}`, `--@userreason:registry=${registry(profile)}`];
|
|
22
|
+
var installArgs = (profile) => ["install", "--global", "--ignore-scripts", `@userreason/cli@${profile.cliVersion}`, ...registryArgs(profile)];
|
|
23
|
+
function globalCliInstallCommand(profile = packageProfile()) {
|
|
24
|
+
return ["npm", ...installArgs(profile)].join(" ");
|
|
25
|
+
}
|
|
26
|
+
async function installedCli(directory) {
|
|
27
|
+
const { stdout } = await run("npm", ["root", "--global"], { cwd: directory, timeout: 15e3, maxBuffer: 64e3 });
|
|
28
|
+
const root = stdout.trim();
|
|
29
|
+
if (!root) throw new Error("npm did not report its global package directory.");
|
|
30
|
+
try {
|
|
31
|
+
const manifest = JSON.parse(await readFile(join(root, "@userreason/cli/package.json"), "utf8"));
|
|
32
|
+
if (manifest.name !== "@userreason/cli" || typeof manifest.version !== "string") throw new Error("Invalid global CLI package.");
|
|
33
|
+
return { root, version: manifest.version };
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error.code === "ENOENT") return void 0;
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function commandAvailable(root, projectDirectory) {
|
|
40
|
+
const windows = process.platform === "win32";
|
|
41
|
+
const expected = await realpath(windows ? join(dirname(root), "userreason.cmd") : join(root, "@userreason/cli/dist/bin.js"));
|
|
42
|
+
let projectCommand;
|
|
43
|
+
try {
|
|
44
|
+
projectCommand = await realpath(join(projectDirectory, windows ? "node_modules/.bin/userreason.cmd" : "node_modules/@userreason/cli/dist/bin.js"));
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
const names = windows ? ["userreason.com", "userreason.exe", "userreason.bat", "userreason.cmd"] : ["userreason"];
|
|
48
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
49
|
+
if (!directory || resolve(directory) !== directory) continue;
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
const candidate = join(directory, name);
|
|
52
|
+
try {
|
|
53
|
+
await access(candidate, windows ? constants.F_OK : constants.X_OK);
|
|
54
|
+
const target = await realpath(candidate);
|
|
55
|
+
if (target === projectCommand) continue;
|
|
56
|
+
return target === expected;
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
async function offerGlobalCli(project, terminal, options) {
|
|
64
|
+
if (!options.installed || options.skip || !terminal.interactive && !options.requested) return { status: "skipped" };
|
|
65
|
+
const profile = options.profile ?? packageProfile();
|
|
66
|
+
const command = globalCliInstallCommand(profile);
|
|
67
|
+
if (options.preview !== false) terminal.note(`Make the userreason command available across your projects. The project keeps its own CLI for teammates and agents.${profile.kind === "local-development" ? `
|
|
68
|
+
This installs development CLI ${profile.cliVersion} from ${profile.registry}, replacing the CLI in npm's current global prefix. Its default API and documentation are development.` : ""}`, "Optional global CLI");
|
|
69
|
+
let cache;
|
|
70
|
+
try {
|
|
71
|
+
if (!options.requested && !await terminal.confirm("Make userreason available across your projects?", true)) return { status: "declined" };
|
|
72
|
+
let installed = await installedCli(project.directory);
|
|
73
|
+
const reuse = installed && compatibleCliVersion(installed.version, profile) && !options.force;
|
|
74
|
+
if (!reuse) {
|
|
75
|
+
cache = await mkdtemp(join(await realpath(project.directory), ".userreason-npm-"));
|
|
76
|
+
await terminal.task("Installing the global UserReason CLI", async () => {
|
|
77
|
+
try {
|
|
78
|
+
const child = run("npm", [...installArgs(profile), "--cache", cache], {
|
|
79
|
+
cwd: project.directory,
|
|
80
|
+
timeout: 12e4,
|
|
81
|
+
maxBuffer: 4e6,
|
|
82
|
+
env: { ...process.env, npm_config_cache: cache, NPM_CONFIG_CACHE: cache, npm_config_ignore_scripts: "true" }
|
|
83
|
+
});
|
|
84
|
+
if (options.verbose) {
|
|
85
|
+
child.child.stdout?.on("data", (chunk) => process.stderr.write(chunk));
|
|
86
|
+
child.child.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
|
87
|
+
}
|
|
88
|
+
await child;
|
|
89
|
+
} catch {
|
|
90
|
+
throw new Error("Global CLI installation failed. Check npm permissions or rerun with --debug; project setup is complete.");
|
|
91
|
+
}
|
|
92
|
+
}, "Global CLI installed");
|
|
93
|
+
installed = await installedCli(project.directory);
|
|
94
|
+
}
|
|
95
|
+
if (!installed || !compatibleCliVersion(installed.version, profile)) throw new Error("npm did not install the required global CLI version.");
|
|
96
|
+
let available = false;
|
|
97
|
+
try {
|
|
98
|
+
available = await commandAvailable(installed.root, project.directory);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
if (reuse) terminal.success(`Global CLI ${installed.version} is already installed`);
|
|
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
|
+
return { status: reuse ? "already_present" : "installed", version: installed.version, command_available: available };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof Cancelled) return { status: "declined" };
|
|
106
|
+
terminal.warn(`The optional global CLI could not be set up. ${error instanceof Error ? error.message : ""}`);
|
|
107
|
+
terminal.note(`${command}
|
|
108
|
+
|
|
109
|
+
The project CLI remains available: ${cliCommand(project)}`, "Install globally later");
|
|
110
|
+
return { status: "failed", install_command: command };
|
|
111
|
+
} finally {
|
|
112
|
+
if (cache) {
|
|
113
|
+
try {
|
|
114
|
+
await rm(cache, { recursive: true, force: true });
|
|
115
|
+
} catch {
|
|
116
|
+
terminal.warn("Unable to remove the temporary .userreason-npm-* cache inside your project.");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
globalCliInstallCommand,
|
|
124
|
+
offerGlobalCli
|
|
125
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
import {
|
|
6
|
+
initializationPath
|
|
7
|
+
} from "./chunk-6DCCEVPD.js";
|
|
8
|
+
import {
|
|
9
|
+
cliCommand
|
|
10
|
+
} from "./chunk-YMSGDC4B.js";
|
|
11
|
+
import {
|
|
12
|
+
packageProfile
|
|
13
|
+
} from "./chunk-GGARVSPT.js";
|
|
14
|
+
|
|
15
|
+
// src/agent-note.ts
|
|
16
|
+
import { constants } from "node:fs";
|
|
17
|
+
import { lstat, stat, readFile, open } from "node:fs/promises";
|
|
18
|
+
import { join, relative } from "node:path";
|
|
19
|
+
var start = "<!-- userreason:feedback:start -->";
|
|
20
|
+
var end = "<!-- userreason:feedback:end -->";
|
|
21
|
+
function note(entry, docs) {
|
|
22
|
+
return `## User feedback
|
|
23
|
+
|
|
24
|
+
When investigating low conversion, drop-off, adoption or unclear user needs,
|
|
25
|
+
consider targeted in-app feedback alongside analytics to understand users' reasons.
|
|
26
|
+
This project collects feedback through \`@userreason/js\`, initialized in \`${entry}\`.
|
|
27
|
+
See [feedback documentation](${docs}).`;
|
|
28
|
+
}
|
|
29
|
+
function earlierNotes(entry) {
|
|
30
|
+
const previousReference = '\nFor SDK methods, prompt settings and CLI commands, read the current documentation instead of relying on memory:\n`npx -y @userreason/cli@latest docs search "<question>"`, or https://docs.userreason.com/llms.txt.';
|
|
31
|
+
return [
|
|
32
|
+
`## User feedback
|
|
33
|
+
|
|
34
|
+
This project uses \`@userreason/js\` to collect in-app user feedback,
|
|
35
|
+
initialized in \`${entry}\`. See [SDK documentation](node_modules/@userreason/js/README.md).`,
|
|
36
|
+
note(entry, "node_modules/@userreason/js/FEEDBACK.md"),
|
|
37
|
+
note(entry, "node_modules/@userreason/js/README.md"),
|
|
38
|
+
...["FEEDBACK.md", "README.md"].map((file) => note(entry, `node_modules/@userreason/js/${file}`) + previousReference)
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
async function planAgentNote(project, options = {}) {
|
|
42
|
+
let path = "", file = "", metadata;
|
|
43
|
+
for (const candidate of ["AGENTS.md", "CLAUDE.md"]) {
|
|
44
|
+
try {
|
|
45
|
+
metadata = await lstat(join(project.directory, candidate));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code === "ENOENT") continue;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
path = join(project.directory, candidate);
|
|
51
|
+
file = candidate;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (!metadata) return { status: "skipped", reason: "no_guidance_file" };
|
|
55
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.nlink !== 1 || metadata.size > 1e6)
|
|
56
|
+
return { status: "skipped", file, reason: "guidance_file_needs_review" };
|
|
57
|
+
const before = await readFile(path, "utf8");
|
|
58
|
+
const headings = [...before.matchAll(/^## User feedback[ \t]*\r?$/gm)];
|
|
59
|
+
if (headings.length > 1) return { status: "skipped", file, reason: "existing_feedback_section" };
|
|
60
|
+
const heading = headings[0];
|
|
61
|
+
let replacement;
|
|
62
|
+
if (heading) {
|
|
63
|
+
let offset = heading.index;
|
|
64
|
+
const remainder = before.slice(offset + heading[0].length);
|
|
65
|
+
const nextHeading = /^#{1,2}[ \t]/m.exec(remainder);
|
|
66
|
+
const until = nextHeading ? offset + heading[0].length + nextHeading.index : before.length;
|
|
67
|
+
const section = before.slice(offset, until).replaceAll("\r\n", "\n");
|
|
68
|
+
const marked = before.slice(0, offset).endsWith(start + (before.includes("\r\n") ? "\r\n" : "\n"));
|
|
69
|
+
const body = marked ? section.replace(new RegExp(`\\n${end}\\n*$`), "") : section;
|
|
70
|
+
const oldEntry = /initialized in `([^`\n]+)`\./.exec(body)?.[1];
|
|
71
|
+
if (!oldEntry || !earlierNotes(oldEntry).includes(body.trimEnd()) || marked && (before.split(start).length !== 2 || before.split(end).length !== 2) || !marked && (before.includes(start) || before.includes(end)))
|
|
72
|
+
return { status: section.includes("@userreason/js") ? "already_present" : "skipped", file, reason: "existing_feedback_section" };
|
|
73
|
+
if (marked) offset -= start.length + (before.includes("\r\n") ? 2 : 1);
|
|
74
|
+
replacement = { offset, previous: before.slice(offset, until), after: "" };
|
|
75
|
+
} else if (before.includes(start) || before.includes(end)) return { status: "already_present", file };
|
|
76
|
+
if (options.beforeInstall) return { path, file, before, snippet: "", addition: "", dev: metadata.dev, ino: metadata.ino, ...replacement ? { replacement } : {} };
|
|
77
|
+
const source = await initializationPath(project);
|
|
78
|
+
if (!source) return { status: "skipped", file, reason: "no_initialization" };
|
|
79
|
+
const entry = relative(project.directory, source).replaceAll("\\", "/");
|
|
80
|
+
if (!entry || entry.startsWith("../") || /[\r\n`]/.test(entry))
|
|
81
|
+
return { status: "skipped", file, reason: "initialization_path_needs_review" };
|
|
82
|
+
let docs = "node_modules/@userreason/js/FEEDBACK.md";
|
|
83
|
+
try {
|
|
84
|
+
if (!(await stat(join(project.directory, docs))).isFile()) docs = "node_modules/@userreason/js/README.md";
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (error.code === "ENOENT") docs = "node_modules/@userreason/js/README.md";
|
|
87
|
+
else throw error;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
if (!(await stat(join(project.directory, docs))).isFile()) return { status: "skipped", file, reason: "docs_unavailable" };
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error.code === "ENOENT") return { status: "skipped", file, reason: "docs_unavailable" };
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
const snippet = note(entry, docs) + `
|
|
96
|
+
For SDK methods, survey settings and CLI commands, read the current documentation instead of relying on memory:
|
|
97
|
+
\`${cliCommand(project)} docs search "<question>"\`, or ${packageProfile().docsUrl}/llms.txt.`;
|
|
98
|
+
const newline = before.includes("\r\n") ? "\r\n" : "\n";
|
|
99
|
+
const separator = before.length ? before.endsWith(newline + newline) ? "" : before.endsWith(newline) ? newline : newline + newline : "";
|
|
100
|
+
const addition = separator + `${snippet}
|
|
101
|
+
`.replaceAll("\n", newline);
|
|
102
|
+
if (replacement) {
|
|
103
|
+
const trailing = /(?:\r?\n)*$/.exec(replacement.previous)[0];
|
|
104
|
+
replacement.after = snippet.replaceAll("\n", newline) + (trailing || newline);
|
|
105
|
+
}
|
|
106
|
+
return { path, file, before, snippet, addition, dev: metadata.dev, ino: metadata.ino, ...replacement ? { replacement } : {} };
|
|
107
|
+
}
|
|
108
|
+
async function applyAgentNote(plan) {
|
|
109
|
+
if ((await lstat(plan.path)).isSymbolicLink()) throw new Error("The guidance file became a symlink.");
|
|
110
|
+
const handle = await open(plan.path, constants.O_RDWR | (plan.replacement ? 0 : constants.O_APPEND) | (constants.O_NOFOLLOW ?? 0));
|
|
111
|
+
try {
|
|
112
|
+
const metadata = await handle.stat();
|
|
113
|
+
if (!metadata.isFile() || metadata.nlink !== 1 || metadata.dev !== plan.dev || metadata.ino !== plan.ino || await handle.readFile("utf8") !== plan.before)
|
|
114
|
+
throw new Error("The guidance file changed after the preview.");
|
|
115
|
+
if (plan.replacement) {
|
|
116
|
+
const { offset, previous, after } = plan.replacement;
|
|
117
|
+
const position = Buffer.byteLength(plan.before.slice(0, offset));
|
|
118
|
+
const tail = Buffer.from(after + plan.before.slice(offset + previous.length));
|
|
119
|
+
let written = 0;
|
|
120
|
+
while (written < tail.length) {
|
|
121
|
+
const result = await handle.write(tail, written, tail.length - written, position + written);
|
|
122
|
+
if (!result.bytesWritten) throw new Error("Unable to update the feedback note.");
|
|
123
|
+
written += result.bytesWritten;
|
|
124
|
+
}
|
|
125
|
+
await handle.truncate(position + tail.length);
|
|
126
|
+
} else await handle.appendFile(plan.addition, "utf8");
|
|
127
|
+
} finally {
|
|
128
|
+
await handle.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function offerAgentNote(project, terminal, options) {
|
|
132
|
+
if (!options.installed || options.skip || !terminal.interactive && !options.requested)
|
|
133
|
+
return { status: "skipped", reason: options.installed ? "not_requested" : "installation_pending" };
|
|
134
|
+
try {
|
|
135
|
+
const plan = await planAgentNote(project);
|
|
136
|
+
if (!("path" in plan)) {
|
|
137
|
+
if (plan.status === "already_present") terminal.info(`User feedback note already present in ${plan.file}`);
|
|
138
|
+
else if (plan.file || options.requested) terminal.info("No user feedback note added. An existing guidance file, SDK initialization and local SDK documentation are required; existing feedback sections are preserved.");
|
|
139
|
+
return plan;
|
|
140
|
+
}
|
|
141
|
+
if (options.preview !== false) terminal.note(
|
|
142
|
+
plan.replacement ? `Current note:
|
|
143
|
+
${plan.replacement.previous.trim()}
|
|
144
|
+
|
|
145
|
+
Proposed note:
|
|
146
|
+
${plan.snippet}` : plan.snippet,
|
|
147
|
+
`Optional ${plan.replacement ? "update" : "addition"} to ${plan.file}`
|
|
148
|
+
);
|
|
149
|
+
if (!options.requested && !await terminal.confirm(`${plan.replacement ? "Update" : "Add"} this user feedback note ${plan.replacement ? "in" : "to"} ${plan.file}?`, true, { recommended: true }))
|
|
150
|
+
return { status: "declined", file: plan.file };
|
|
151
|
+
await applyAgentNote(plan);
|
|
152
|
+
terminal.success(`User feedback note ${plan.replacement ? "updated in" : "added to"} ${plan.file}`);
|
|
153
|
+
return { status: plan.replacement ? "updated" : "added", file: plan.file };
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof Cancelled) return { status: "declined" };
|
|
156
|
+
terminal.warn("Installation is complete. The optional user feedback note could not be added; the guidance file needs review.");
|
|
157
|
+
return { status: "failed", reason: "guidance_file_needs_review" };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
planAgentNote,
|
|
163
|
+
applyAgentNote,
|
|
164
|
+
offerAgentNote
|
|
165
|
+
};
|