@whisperr/wizard 0.7.2 → 0.7.3
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/dist/index.js +69 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import open2 from "open";
|
|
|
11
11
|
|
|
12
12
|
// src/core/config.ts
|
|
13
13
|
var DEFAULT_API_BASE = "https://api.whisperr.net";
|
|
14
|
-
var DEFAULT_MODEL = "claude-
|
|
14
|
+
var DEFAULT_MODEL = "claude-opus-5";
|
|
15
15
|
var DEFAULT_PLANNER_MODEL = "claude-opus-5";
|
|
16
16
|
var DEFAULT_EFFORT = "high";
|
|
17
17
|
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -2063,11 +2063,14 @@ var SURVEY_SYSTEM_PROMPT = [
|
|
|
2063
2063
|
"",
|
|
2064
2064
|
"Report, in plain markdown:",
|
|
2065
2065
|
"1. Stack: frameworks, package manager, app entry point file.",
|
|
2066
|
-
"2. Feature areas: name, root directory, and
|
|
2066
|
+
"2. Feature areas: name, root directory, and every relevant file containing",
|
|
2067
|
+
" committed end-user actions or meaningful lifecycle transitions.",
|
|
2067
2068
|
"3. The end-user identify() anchor: the login/signup success and session-restore call sites for the",
|
|
2068
2069
|
" PAYING end user \u2014 never admin, staff, operator, or seller paths.",
|
|
2069
2070
|
"4. Existing analytics calls, if any.",
|
|
2070
|
-
"5.
|
|
2071
|
+
"5. Exhaustive inventory of integratable call sites: file, function/symbol,",
|
|
2072
|
+
" confirmed outcome or state transition, available non-PII payload fields,",
|
|
2073
|
+
" and whether similar paths exist elsewhere.",
|
|
2071
2074
|
"Cite concrete file paths for every claim. Do not propose event names yet."
|
|
2072
2075
|
].join("\n");
|
|
2073
2076
|
function surveyUserPrompt(repoPath, onboardingContext) {
|
|
@@ -2090,9 +2093,20 @@ var SELECTION_SYSTEM_PROMPT = [
|
|
|
2090
2093
|
" streak_broken) \u2014 the server derives those later.",
|
|
2091
2094
|
"- Emit after confirmed outcomes, not intent: a purchase event fires on confirmed success, not on",
|
|
2092
2095
|
" button tap; denied permissions are not grants.",
|
|
2093
|
-
"-
|
|
2094
|
-
"
|
|
2095
|
-
"
|
|
2096
|
+
"- Aim for comprehensive coverage of the product\u2019s observable end-user lifecycle.",
|
|
2097
|
+
" Inspect every meaningful feature area and propose every distinct, concretely",
|
|
2098
|
+
" integratable event that could contribute to activation, engagement, retention,",
|
|
2099
|
+
" monetization, or churn analysis.",
|
|
2100
|
+
"- Prefer complete coverage over a short curated list. Include distinct successful",
|
|
2101
|
+
" outcomes, failures, cancellations, retries, state transitions, and meaningful",
|
|
2102
|
+
" feature usage when each has its own concrete call site and analytical value.",
|
|
2103
|
+
"- Let the product\u2019s actual complexity determine the count. A feature-rich product",
|
|
2104
|
+
" may legitimately produce roughly 100\u2013200 events, while a smaller product may",
|
|
2105
|
+
" produce fewer. Do not stop after finding only the most obvious events, and do",
|
|
2106
|
+
" not invent events merely to reach a number.",
|
|
2107
|
+
"- Before finishing, systematically revisit every surveyed feature area and confirm",
|
|
2108
|
+
" that all meaningful committed actions and lifecycle transitions have either",
|
|
2109
|
+
" been proposed or deliberately excluded with a reason.",
|
|
2096
2110
|
"- Codes are lowercase snake_case, named for what happened (checkout_completed, not do_checkout).",
|
|
2097
2111
|
"- payloadSchema lists the fields worth capturing with a short description of each \u2014 the important",
|
|
2098
2112
|
" info only. NEVER include personal identifiers: no email, phone, names, addresses, birth dates,",
|
|
@@ -3183,7 +3197,7 @@ function coverageNote(coverage) {
|
|
|
3183
3197
|
|
|
3184
3198
|
// src/core/toolPolicy.ts
|
|
3185
3199
|
import { isAbsolute, relative, resolve } from "path";
|
|
3186
|
-
var SECRET_MATERIAL_DENIAL = "blocked: secret material
|
|
3200
|
+
var SECRET_MATERIAL_DENIAL = "blocked: secret material must not be accessed";
|
|
3187
3201
|
var WRITE_OUTSIDE_REPO_DENIAL = "blocked: writes must stay inside the target repository";
|
|
3188
3202
|
var SENSITIVE_WRITE_DENIAL = "blocked: refusing to write CI/git configuration";
|
|
3189
3203
|
var BASH_ALLOWLIST_DENIAL = "blocked: command is outside the Bash allowlist - use package install/add commands, mkdir, or git status/diff/log only";
|
|
@@ -3194,9 +3208,24 @@ function engineMcpToolName(toolName) {
|
|
|
3194
3208
|
function trustedEngineMcpTools(tools) {
|
|
3195
3209
|
return new Set(tools.map((tool2) => engineMcpToolName(tool2.name)));
|
|
3196
3210
|
}
|
|
3197
|
-
var
|
|
3198
|
-
|
|
3199
|
-
|
|
3211
|
+
var SECRET_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
3212
|
+
".aws",
|
|
3213
|
+
".ssh",
|
|
3214
|
+
".gnupg",
|
|
3215
|
+
".azure",
|
|
3216
|
+
".docker",
|
|
3217
|
+
".kube",
|
|
3218
|
+
".secrets",
|
|
3219
|
+
"secrets"
|
|
3220
|
+
]);
|
|
3221
|
+
var SECRET_EXACT_FILES = /* @__PURE__ */ new Set([
|
|
3222
|
+
".netrc",
|
|
3223
|
+
".npmrc",
|
|
3224
|
+
".pypirc",
|
|
3225
|
+
"auth.json",
|
|
3226
|
+
"google-services.json",
|
|
3227
|
+
"googleservice-info.plist"
|
|
3228
|
+
]);
|
|
3200
3229
|
var SENSITIVE_WRITE_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".circleci", ".buildkite"]);
|
|
3201
3230
|
var SENSITIVE_WRITE_EXACT_FILES = /* @__PURE__ */ new Set([
|
|
3202
3231
|
".gitlab-ci.yml",
|
|
@@ -3212,6 +3241,7 @@ var SECRET_SUFFIXES = [
|
|
|
3212
3241
|
".pfx",
|
|
3213
3242
|
".jks",
|
|
3214
3243
|
".keystore",
|
|
3244
|
+
".kdbx",
|
|
3215
3245
|
".tfvars"
|
|
3216
3246
|
];
|
|
3217
3247
|
var JS_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
@@ -3245,7 +3275,6 @@ function isSecretPathLike(value) {
|
|
|
3245
3275
|
for (const rawPart of parts) {
|
|
3246
3276
|
const part = stripOuterQuotes(rawPart.trim().toLowerCase());
|
|
3247
3277
|
if (!part || part === "." || part === "**") continue;
|
|
3248
|
-
if (ENV_EXAMPLES.has(part)) continue;
|
|
3249
3278
|
if (SECRET_DIRECTORIES.has(part)) return true;
|
|
3250
3279
|
if (SECRET_EXACT_FILES.has(part)) return true;
|
|
3251
3280
|
if (part === ".env" || part === ".envrc" || part.startsWith(".env.") || part.startsWith(".env*")) {
|
|
@@ -3256,6 +3285,7 @@ function isSecretPathLike(value) {
|
|
|
3256
3285
|
}
|
|
3257
3286
|
if (part.includes("credentials")) return true;
|
|
3258
3287
|
if (part.startsWith("secrets.")) return true;
|
|
3288
|
+
if (part.startsWith("service-account") && part.endsWith(".json")) return true;
|
|
3259
3289
|
if (SECRET_SUFFIXES.some((suffix) => part.endsWith(suffix))) return true;
|
|
3260
3290
|
}
|
|
3261
3291
|
return false;
|
|
@@ -3291,6 +3321,9 @@ function evaluateWrite(input, context) {
|
|
|
3291
3321
|
if (!isPathInsideRepo(pathValue, context.repoPath)) {
|
|
3292
3322
|
return { behavior: "deny", message: WRITE_OUTSIDE_REPO_DENIAL };
|
|
3293
3323
|
}
|
|
3324
|
+
if (isSecretPathLike(pathValue)) {
|
|
3325
|
+
return { behavior: "deny", message: SECRET_MATERIAL_DENIAL };
|
|
3326
|
+
}
|
|
3294
3327
|
if (isSensitiveWritePath(pathValue, context.repoPath)) {
|
|
3295
3328
|
return { behavior: "deny", message: SENSITIVE_WRITE_DENIAL };
|
|
3296
3329
|
}
|
|
@@ -4349,6 +4382,10 @@ function short(s, max = 60) {
|
|
|
4349
4382
|
// src/engine/providers/claude.ts
|
|
4350
4383
|
var READ_ONLY_TOOLS2 = ["Read", "Glob", "Grep"];
|
|
4351
4384
|
var EDIT_TOOLS = ["Read", "Edit", "Write", "Bash", "Glob", "Grep"];
|
|
4385
|
+
var FAST_MODE_MODELS = /* @__PURE__ */ new Set(["claude-opus-5", "claude-opus-4-8"]);
|
|
4386
|
+
function supportsClaudeFastMode(model) {
|
|
4387
|
+
return FAST_MODE_MODELS.has(model);
|
|
4388
|
+
}
|
|
4352
4389
|
function createClaudeProvider(config, session) {
|
|
4353
4390
|
return {
|
|
4354
4391
|
name: "claude",
|
|
@@ -4384,6 +4421,7 @@ function createClaudeProvider(config, session) {
|
|
|
4384
4421
|
hooks: buildToolPolicyHooks(invocation.cwd, trustedTools),
|
|
4385
4422
|
canUseTool: createToolPermissionCallback(invocation.cwd, trustedTools),
|
|
4386
4423
|
maxTurns: roleConfig.maxTurns,
|
|
4424
|
+
...supportsClaudeFastMode(roleConfig.model) ? { settings: { fastMode: true } } : {},
|
|
4387
4425
|
...invocation.resumeSessionId ? { resume: invocation.resumeSessionId } : {},
|
|
4388
4426
|
settingSources: []
|
|
4389
4427
|
}
|
|
@@ -4444,6 +4482,18 @@ function jailedPath(cwd, candidate) {
|
|
|
4444
4482
|
}
|
|
4445
4483
|
return resolved;
|
|
4446
4484
|
}
|
|
4485
|
+
function guardedPath(cwd, toolName, candidate) {
|
|
4486
|
+
const path = jailedPath(cwd, candidate);
|
|
4487
|
+
const decision = evaluateToolUse(
|
|
4488
|
+
toolName,
|
|
4489
|
+
toolName === "Glob" ? { path: candidate, pattern: "*" } : { file_path: candidate },
|
|
4490
|
+
{ repoPath: cwd }
|
|
4491
|
+
);
|
|
4492
|
+
if (decision.behavior === "deny") {
|
|
4493
|
+
throw new Error(decision.message);
|
|
4494
|
+
}
|
|
4495
|
+
return path;
|
|
4496
|
+
}
|
|
4447
4497
|
function fileTools(cwd, allowWrite) {
|
|
4448
4498
|
const tools = [
|
|
4449
4499
|
{
|
|
@@ -4456,7 +4506,8 @@ function fileTools(cwd, allowWrite) {
|
|
|
4456
4506
|
required: ["path"]
|
|
4457
4507
|
},
|
|
4458
4508
|
handler: async (input) => {
|
|
4459
|
-
const
|
|
4509
|
+
const path = guardedPath(cwd, "Read", String(input.path ?? ""));
|
|
4510
|
+
const content = readFileSync3(path, "utf8");
|
|
4460
4511
|
return { content: content.slice(0, 4e4) };
|
|
4461
4512
|
}
|
|
4462
4513
|
},
|
|
@@ -4470,8 +4521,8 @@ function fileTools(cwd, allowWrite) {
|
|
|
4470
4521
|
required: ["path"]
|
|
4471
4522
|
},
|
|
4472
4523
|
handler: async (input) => {
|
|
4473
|
-
const target9 =
|
|
4474
|
-
const entries = readdirSync(target9).map((entry) => {
|
|
4524
|
+
const target9 = guardedPath(cwd, "Glob", String(input.path ?? "."));
|
|
4525
|
+
const entries = readdirSync(target9).filter((entry) => !isSecretPathLike(entry)).map((entry) => {
|
|
4475
4526
|
const kind = statSync(join8(target9, entry)).isDirectory() ? "dir" : "file";
|
|
4476
4527
|
return `${kind}:${entry}`;
|
|
4477
4528
|
});
|
|
@@ -4491,7 +4542,8 @@ function fileTools(cwd, allowWrite) {
|
|
|
4491
4542
|
required: ["path", "content"]
|
|
4492
4543
|
},
|
|
4493
4544
|
handler: async (input) => {
|
|
4494
|
-
|
|
4545
|
+
const path = guardedPath(cwd, "Write", String(input.path ?? ""));
|
|
4546
|
+
writeFileSync2(path, String(input.content ?? ""));
|
|
4495
4547
|
return { ok: true };
|
|
4496
4548
|
}
|
|
4497
4549
|
},
|
|
@@ -4509,7 +4561,7 @@ function fileTools(cwd, allowWrite) {
|
|
|
4509
4561
|
required: ["path", "oldText", "newText"]
|
|
4510
4562
|
},
|
|
4511
4563
|
handler: async (input) => {
|
|
4512
|
-
const path =
|
|
4564
|
+
const path = guardedPath(cwd, "Edit", String(input.path ?? ""));
|
|
4513
4565
|
const content = readFileSync3(path, "utf8");
|
|
4514
4566
|
const oldText = String(input.oldText ?? "");
|
|
4515
4567
|
if (!content.includes(oldText)) {
|
|
@@ -4679,7 +4731,7 @@ var MINUTE = 6e4;
|
|
|
4679
4731
|
function engineModelConfig(config) {
|
|
4680
4732
|
const model = (role, fallback) => process.env[`WHISPERR_WIZARD_ENGINE_${role.toUpperCase()}_MODEL`]?.trim() || fallback;
|
|
4681
4733
|
return {
|
|
4682
|
-
survey: { model: model("survey",
|
|
4734
|
+
survey: { model: model("survey", config.model), effort: "medium", maxTurns: 40, maxMs: 12 * MINUTE },
|
|
4683
4735
|
design: { model: model("design", config.plannerModel), effort: "high", maxTurns: 40, maxMs: 15 * MINUTE },
|
|
4684
4736
|
edit: { model: model("edit", config.model), effort: config.effort, maxTurns: 50, maxMs: 15 * MINUTE },
|
|
4685
4737
|
review: { model: model("review", config.model), effort: "medium", maxTurns: 15, maxMs: 8 * MINUTE }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whisperr/wizard",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Whisperr Wizard \u2014 one command to integrate the Whisperr SDK into your app. Authenticates with your onboarded account and uses an AI coding agent to wire up identify() and your business events automatically.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|