@mccaskie-labs/workbench-config 0.2.5 → 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.
@@ -26,6 +26,7 @@ export const COMPONENTS = [
26
26
  "ui",
27
27
  "forms",
28
28
  "sign-in",
29
+ "availability",
29
30
  ];
30
31
  const id = z.string().regex(/^[a-z0-9][a-z0-9.-]*$/, "lower case, digits, dots and hyphens");
31
32
  const code = z.string().regex(/^[a-z][a-z0-9_]*$/, "a snake_case error code");
@@ -203,8 +204,6 @@ const delivery = z.strictObject({
203
204
  choice: z.enum(["built_in", "optional"]),
204
205
  /** One plain sentence for the owner, shown when Claude introduces the tools. */
205
206
  intro: z.string().min(1).max(160),
206
- /** When the tool applies, in the client's words. Only for a component with no `offer`, whose `offer.when` says it. */
207
- when: z.string().min(1).optional(),
208
207
  });
209
208
  const baseShape = {
210
209
  $schema: z.literal("./schema.json"),
@@ -262,12 +261,6 @@ export const capabilitySchema = z.strictObject(baseShape).superRefine((m, ctx) =
262
261
  }
263
262
  }
264
263
  });
265
- /**
266
- * Components whose client questions are still being written (Robert,
267
- * 11 November 2026: every shared tool ships its questions). The list only
268
- * shrinks; a new component can't join it.
269
- */
270
- export const OFFER_PENDING = ["database", "email", "email-inbound", "strings"];
271
264
  function checkOffer(file, component, o, seen) {
272
265
  const problems = [];
273
266
  const named = new Map();
@@ -381,21 +374,14 @@ export function checkManifests(files) {
381
374
  problems.push(...checkOffer(file, m.component, m.offer, seen));
382
375
  if (m.offer && m.no_offer)
383
376
  problems.push({ file, check: "schema", message: "has both offer and no_offer" });
384
- const pending = OFFER_PENDING.includes(m.component);
385
- if (!m.offer && !m.no_offer && !pending) {
377
+ if (!m.offer && !m.no_offer) {
386
378
  problems.push({ file, check: "schema", message: "every component ships its client questions: add an offer, or no_offer with the reason a client has nothing to answer" });
387
379
  }
388
- if ((m.offer || m.no_offer) && pending)
389
- problems.push({ file, check: "schema", message: `remove ${m.component} from OFFER_PENDING` });
390
380
  // §9.6: every component a client's Claude Code works with says how it reaches the project.
391
381
  if (m.no_offer && m.delivery)
392
382
  problems.push({ file, check: "schema", message: "has both no_offer and delivery: plumbing gets no skill of its own" });
393
383
  if (!m.no_offer && !m.delivery)
394
384
  problems.push({ file, check: "schema", message: "add delivery: whether the tool is built in or optional, and one sentence that introduces it to the owner" });
395
- if (m.delivery && m.offer && m.delivery.when)
396
- problems.push({ file, check: "schema", message: "delivery.when repeats offer.when: remove it" });
397
- if (m.delivery && !m.offer && !m.delivery.when)
398
- problems.push({ file, check: "schema", message: "delivery needs when, because there is no offer to say when the tool applies" });
399
385
  }
400
386
  const byComponent = new Map(manifests.map((m) => [m.component, m]));
401
387
  for (const { file, m, own } of deferred) {
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { WORKBENCH_ENVS, workbenchEnv, type WorkbenchEnv } from "./env.ts";
2
2
  export { defineConfig, type Config } from "./define-config.ts";
3
3
  export { sharedSecret, workerSecret, redact, redactAll, type SecretsStoreSecretLike } from "./secrets.ts";
4
4
  export { WorkbenchConfigError, type ConfigErrorCode, type ConfigProblem } from "./errors.ts";
5
- export { capabilitySchema, checkManifests, allowedSchemas, COMPONENTS, type Capability, type Component, type ManifestProblem, type Offer, OFFER_PATTERNS, OFFER_PENDING, } from "./capability-schema.ts";
5
+ export { capabilitySchema, checkManifests, allowedSchemas, COMPONENTS, type Capability, type Component, type ManifestProblem, type Offer, OFFER_PATTERNS, } from "./capability-schema.ts";
6
6
  export { projectNotes, renderSkill, renderSkills, renderStart, skillName, SKILLS_DIR, START_SKILL, TOOLS_FILE } from "./skills.ts";
7
7
  export { toolsState } from "./tools.ts";
8
8
  export type { ToolChoices, ToolsState } from "./tools.ts";
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export { WORKBENCH_ENVS, workbenchEnv } from "./env.js";
2
2
  export { defineConfig } from "./define-config.js";
3
3
  export { sharedSecret, workerSecret, redact, redactAll } from "./secrets.js";
4
4
  export { WorkbenchConfigError } from "./errors.js";
5
- export { capabilitySchema, checkManifests, allowedSchemas, COMPONENTS, OFFER_PATTERNS, OFFER_PENDING, } from "./capability-schema.js";
5
+ export { capabilitySchema, checkManifests, allowedSchemas, COMPONENTS, OFFER_PATTERNS, } from "./capability-schema.js";
6
6
  export { projectNotes, renderSkill, renderSkills, renderStart, skillName, SKILLS_DIR, START_SKILL, TOOLS_FILE } from "./skills.js";
7
7
  export { toolsState } from "./tools.js";
8
8
  export { capabilities, MANIFEST_VERSION } from "./capabilities.gen.js";
package/dist/skills.d.ts CHANGED
@@ -6,7 +6,7 @@ export declare const TOOLS_FILE = "workbench/tools.json";
6
6
  export declare const START_SKILL = "workbench-start";
7
7
  /** The project's own notes on a tool, beside its generated skill: where this project wires it in. Never generated. */
8
8
  export declare const projectNotes: (component: string) => string;
9
- /** The SKILL.md for one manifest, or undefined when it has no `delivery` (plumbing). `guide` is its `capabilities/<component>.md`, when there is one. */
9
+ /** The SKILL.md for one manifest, or undefined for plumbing (no `delivery` or no `offer`). `guide` is its `capabilities/<component>.md`, when there is one. */
10
10
  export declare function renderSkill(m: Capability, guide?: string): string | undefined;
11
11
  /** The `workbench-start` skill: every tool introduced to the owner, and how their choices are recorded (§9.6). */
12
12
  export declare function renderStart(manifests: readonly Capability[]): string;
package/dist/skills.js CHANGED
@@ -120,14 +120,14 @@ function useBlock(m, guide) {
120
120
  out.push(`Read \`${projectNotes(m.component)}\` next, if it is there: it is this project's own note on where the tool is already wired in and how to call it here. It is the project's to edit; add to it when the wiring changes. Its limits, rules and error codes are in ${manifest}, and each package's API is in its \`README.md\` under \`node_modules/@mccaskie-labs/\`.`);
121
121
  return out;
122
122
  }
123
- /** The SKILL.md for one manifest, or undefined when it has no `delivery` (plumbing). `guide` is its `capabilities/<component>.md`, when there is one. */
123
+ /** The SKILL.md for one manifest, or undefined for plumbing (no `delivery` or no `offer`). `guide` is its `capabilities/<component>.md`, when there is one. */
124
124
  export function renderSkill(m, guide) {
125
125
  const o = m.offer;
126
126
  const d = m.delivery;
127
- if (!d)
127
+ if (!d || !o)
128
128
  return undefined;
129
- const when = o?.when ?? d.when ?? m.summary;
130
- const description = (o ? `${when} Offers Workbench ${m.title} and asks the client for its settings.` : `${when} Use Workbench ${m.title} for it.`).replaceAll("\n", " ");
129
+ const when = o.when;
130
+ const description = `${when} Offers Workbench ${m.title} and asks the client for its settings.`.replaceAll("\n", " ");
131
131
  const out = [
132
132
  "---",
133
133
  `name: ${skillName(m.component)}`,
@@ -146,10 +146,6 @@ export function renderSkill(m, guide) {
146
146
  "",
147
147
  ...beforeBlock(m),
148
148
  ];
149
- if (!o) {
150
- out.push("", "## Questions", "", "This tool's questions for the client aren't written yet. Use it with its defaults, tell the client that is what you did, and invent no setting. A choice only the client can make goes to them as a plain question.", ...(m.styles ? ["", "## Restyling", "", restyleLine(m.styles.prefix)] : []), "", ...useBlock(m, guide));
151
- return `${out.join("\n")}\n`;
152
- }
153
149
  out.push("", "## What to say", "", d.choice === "optional"
154
150
  ? `Once the owner's choice is yes, say: "${o.say}"`
155
151
  : `Say: "${o.say}" Don't ask whether to use Workbench or something of the client's own. If the client says they don't want it, stop and say what they give up.`, "", "## How to ask", "", ...(o.needs?.length ? [`- First check the project setup this needs: ${o.needs.map((n) => `\`workbench/${n}.json\``).join(", ")}. If one is missing, use that component's \`workbench-<component>\` skill first.`] : []), ...PATTERN_STEPS[o.pattern].map((step) => `- ${step}`), "- Ask with the AskUserQuestion tool, up to four questions per call. Give each question's answers in the order below; the first is recommended, so label it \"(Recommended)\", unless the question says none is. The tool adds \"Other\" itself.", "- Where the client accepts every recommendation, still ask each \"Must ask\" question.", "- An \"Other\" answer must meet the rule under its question. When it doesn't, say why and ask again. Never store a value outside it.", "- Ask nothing that isn't listed here, and invent no setting. A need the questions don't cover goes back to the client as a plain question, and isn't written to the answers file.", `- Write the answers to \`${o.answers_file}\`${o.per ? `, one entry per ${o.per}` : ""}: ${o.answers_format ?? `for each question its id, the value, and the client's own words when they typed "Other" or changed a recommendation.`}${o.generator ? ` Then run \`${o.generator}\`, which writes the code and refuses anything the package can't apply (it doesn't check the "Other" rules: you do). It builds nothing while an answers file is a draft. Never edit what it writes; change the answers file and run it again.` : ""}`, "", "## Questions", "", o.questions.map((q, i) => questionBlock(q, i + 1)).join("\n\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mccaskie-labs/workbench-config",
3
- "version": "0.2.5",
3
+ "version": "0.3.1",
4
4
  "description": "Workbench app configuration: WORKBENCH_ENV, typed variables that fail by name, secrets that are never printed, and the capabilities manifest.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",