@amityco/social-plus-vise 1.1.0 → 1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `@amityco/social-plus-vise` are documented in this file.
4
4
 
5
5
  The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.2.0 — 2026-06-18
8
+
9
+ Adds advisory **solution-path / UIKit routing** and a fail-closed fix to the SwiftPM manifest sensor. No change to `vise check` exit codes, compliance rules, or sidecar **formats**; the one behavior change is the SwiftPM-timeout outcome noted under Changed.
10
+
11
+ ### Added
12
+ - **Solution-path & UIKit routing (advisory).** `vise plan` emits `solutionPath` (`sdk` · `uikit` · `hybrid` · `needs-decision`) so an agent pauses for `--answer solution_path=…` before building the wrong layer, cleanly separating the two integrator postures (SDK/custom-UI vs UIKit/prebuilt-UI). When UIKit is in play it also emits `uikitCustomization`, recommending the lowest-effort tier that meets the goal (Dynamic UI → Component Styling → Localization → Behavior Overrides → Fork and Extend → SDK Custom UI). Both are **advisory only** — they never change outcome classification, compliance rules, the sidecar schema, or `vise check` exit codes. Documented under "Solution path & UIKit routing" in the README; the two postures are described in `docs/PERSONAS.md`.
13
+ - **Real-build readiness checks** surfaced alongside the routing signals.
14
+
15
+ ### Changed
16
+ - **SwiftPM manifest sensor: timeout is now RED (fail-closed), not a skip.** A manifest-parse timeout previously read as a clean green; for a pure-SwiftPM iOS project the manifest sensor is the only sensor, so a stall masqueraded as success. It now reports `timed-out` and the CLI exits non-zero. **Behavior note for consumers:** a network-restricted CI runner that cannot resolve a remote SwiftPM dependency will now go RED on this sensor rather than silently passing — intended fail-closed behavior, not a regression.
17
+ - **Advisory routing engines hardened (two adversarial-verification batches, 28 fixes, every fix mutation-verified).** `recommendSolutionPath` / `recommendUIKitCustomization` precision: UIKit-rejection coverage now spans modal/contracted negators (won't/can't/cannot), intervening verbs ("don't want to use UIKit", "without using UIKit"), and "anything but UIKit" — an explicit rejection routes SDK-first instead of (previously) recommending UIKit; prebuilt idioms are anchored to a UI/social noun; `off-the-shelf` is anchored (so "off-the-shelf analytics" stays SDK-first); localization no longer fires on a programming/design "language"; backend feature capabilities (e.g. livestreaming) surface as a decision rather than a Dynamic-UI default. Advisory-only quality improvements; no gate behavior changes.
18
+ - **Personas split into two integrator postures** (AI-Assisted SDK/Custom-UI Integrator and AI-Assisted UIKit/Prebuilt-UI Adopter), tied to a deterministic behavior contract in the test suite.
19
+
20
+ ## 1.1.1 — 2026-06-16
21
+
22
+ Docs-only patch; no CLI, MCP, rule, or sidecar behavior changes.
23
+
24
+ ### Changed
25
+ - **README Evidence section reworked.** Dropped the pre-1.0 "like-for-like" feed-completeness result (it was measured against an earlier dev build on early-June agent versions); presented the two genuine matched comparisons (SDK compliance, design conformance) as a scannable table; relabeled the former "best case" as **"Feed completeness (capabilities selected)"** and reframed it as *absolute* opt-in completeness rather than a baseline-beating lift; led design conformance with the robust by-name-token signal instead of the fragile one-seed hex anecdote (and dropped the model attribution, given a cross-temporal confound now noted under Boundaries); tightened the limitations and lifted the "stopping condition" framing into the section intro. No benchmark numbers were invented; the SDK-compliance benchmark stays described generically (its internal codename remains denylisted from the shipped surface).
26
+
7
27
  ## 1.1.0 — 2026-06-16
8
28
 
9
29
  Public GA-readiness pass. No change to `vise check` exit codes or sidecar **formats**. The rule corpus gains three corrected rationales (stale/fictional SDK method names) — a deliberate pre-GA contract reset, with the `test:sidecar-compat` baseline re-frozen accordingly (no published 0.14.x consumers to protect; see below).
package/README.md CHANGED
@@ -47,7 +47,7 @@ It turns the request into a grounded plan, records a local contract under `sp-vi
47
47
 
48
48
  > 🔒 **Your source code never leaves your machine.** Vise fetches only the public social.plus docs and the SDK's published version on npm — never your code, file contents, or search queries. `VISE_DOCS_OFFLINE=1` runs fully offline.
49
49
 
50
- Vise can also run **ahead** of that loop: an advisory [Engagement Intelligence](#engagement-intelligence) layer turns a product goal into a candidate engagement design — *what to build*, not only *whether you built it right*.
50
+ Vise can also run **ahead** of that loop: an advisory [Engagement Intelligence](#engagement-intelligence) layer turns a product goal into multiple candidate engagement strategies — *what you might build*, not only *whether you built it right*.
51
51
 
52
52
  > **Why "Vise"?** A bench vise holds the workpiece steady so the craftsman's hands are free to shape it. Vise clamps the integration to the real docs, the real project structure, and the real compliance rules so the agent can focus on building instead of guessing.
53
53
 
@@ -98,12 +98,18 @@ Prefer a per-project install? `npm install -D @amityco/social-plus-vise`, then c
98
98
  ## How it works
99
99
 
100
100
  1. **Inspect** — `vise inspect` detects the platform, app surfaces, available sensors, and design signals from the local repo.
101
- 2. **Plan** — `vise plan --request "..."` classifies the outcome, cites docs, and raises blocking intake and design-preview questions. The agent surfaces them; you answer.
101
+ 2. **Plan** — `vise plan --request "..."` classifies the outcome, cites docs, and raises blocking intake and design-preview questions. Use `--summary` when you only need the route/intake readout before implementation. The agent surfaces questions; you answer.
102
102
  3. **Initialize** — `vise init` writes the `sp-vise/` compliance contract. While blocking questions are unanswered it refuses, returns `status: "needs-clarification"`, and exits 7 — the agent must ask instead of guessing.
103
103
  4. **Build** — the agent edits your code, grounded by `vise search-docs` and `vise get-doc-page`.
104
104
  5. **Check & repair** — `vise check` reports deterministic findings, completeness gaps, and attestation needs. The agent fixes findings or records attestations with evidence, looping until green.
105
105
  6. **Sense** — `vise run-sensors` runs your project's own typecheck/build/lint/SDK smokes. Done means the contract and evidence are committed, not just that the agent stopped.
106
106
 
107
+ ### Solution path & UIKit routing (advisory)
108
+
109
+ Some requests are better served by **social.plus UIKit** (prebuilt social surfaces) than by hand-rolling standard UI directly from SDK primitives. `vise plan` emits an advisory **`solutionPath`** — `sdk`, `uikit`, `hybrid`, or `needs-decision` — so an agent can pause for `--answer solution_path=uikit|sdk|hybrid` before it builds the wrong layer. This separates the two integrator postures cleanly: an **SDK / custom-UI** build (differentiated, direct-SDK) stays SDK-first, a **UIKit / prebuilt-UI** adoption (standard surfaces, fast launch) routes to UIKit, and a genuinely mixed request resolves to `needs-decision` rather than silently picking one. A request that *rejects* UIKit ("don't use UIKit", "we can't use UIKit") routes SDK-first, and a request that names a backend feature capability (e.g. livestreaming) is surfaced as a decision rather than a UIKit-customization default.
110
+
111
+ When UIKit is in play, `vise plan` can also emit **`uikitCustomization`**, recommending the lowest-effort customization tier that meets the goal — Dynamic UI → Component Styling → Localization → Behavior Overrides → Fork and Extend → SDK Custom UI. Both signals are **advisory only**: they never change outcome classification, compliance rules, the sidecar schema, or `vise check` exit codes.
112
+
107
113
  ### Three validation layers
108
114
 
109
115
  The layer is set by the *kind of claim*, which is how Vise is designed to avoid false positives where it gates:
@@ -120,9 +126,9 @@ Correctness is gated by deterministic rules or attestations; completeness is gat
120
126
 
121
127
  ### Engagement Intelligence
122
128
 
123
- **Advisory, in preview.** The three layers above answer *whether you built it right*. Ahead of the build, Vise can also help decide *what to build*: an **Engagement Intelligence** layer turns a product goal into a candidate **engagement design** — archetypes, UX patterns, and solution variants drawn from a social.plus experience catalog — then compiles the chosen variant into an implementation plan (`vise creative` → `vise creative accept` → `vise experience compile`), optionally bridging to installable social.plus blocks, plus advisory UX expectations and a multi-dimension experience review.
129
+ **Advisory, in preview.** The three layers above answer *whether you built it right*. Ahead of the build, Vise can also help reason about *what to build*: an **Engagement Intelligence** layer turns a product goal into multiple candidate **engagement strategies** — archetypes, UX patterns, and solution variants drawn from a social.plus experience catalog — with rationale, tradeoffs, no-fit guidance, availability boundaries, and review gaps. The human or driving agent still chooses the direction, then Vise compiles that selected variant into an implementation plan (`vise creative` → `vise creative accept` → `vise experience compile`), optionally bridging to installable social.plus blocks, plus advisory UX expectations and a multi-dimension experience review.
124
130
 
125
- It is **local-only, never uploads, carries no calibrated score** (a calibration program is in progress), and **never changes `vise check`'s exit codes**. Use it to shape the work; the validation layers still decide when it's done.
131
+ It is **local-only, never uploads, carries no calibrated score**, and **never changes `vise check`'s exit codes**. The opt-in ranking preview is review context, not a top-1 confidence claim or autonomous product-strategy selector. Use it to shape the work; the validation layers still decide when it's done.
126
132
 
127
133
  ### Design contracts
128
134
 
@@ -130,13 +136,22 @@ Vise can ingest your aesthetic from an HTML/CSS prototype (`vise design extract`
130
136
 
131
137
  ## Evidence
132
138
 
133
- Vise's measured effect is on **feed-feature completeness** — does the agent build the whole outcome (pagination, empty/error states, the optional capabilities you asked for) or just the happy path?
139
+ Vise's measured effect is on whether an agent builds the *whole* outcome pagination, empty and error states, the optional capabilities you asked for or just the happy path. The durable finding across benchmarks is the **mechanism**, not any single percentage: a checked verification loop beats the same rules pasted into a prompt. What Vise reliably changes is the **stopping condition** — the agent isn't done when the code compiles; it's done when the local contract is green, attested, or blocked on an explicit customer decision.
140
+
141
+ **Matched comparisons** — Vise's verification loop vs a docs-only baseline, same task and agent:
142
+
143
+ | Benchmark | What's measured | Docs-only | With Vise |
144
+ |---|---|---|---|
145
+ | **SDK compliance** | correctness slices passed (greenfield; 9 independent slices, single run per cell, two agents) | 6/9 (67%) | **9/9 (100%)** |
146
+ | **Design conformance** | the brand's *named* design tokens used vs hardcoded values (ambiguous brief, n=3) | ~72% | **~90%** |
147
+
148
+ The mechanism is the durable claim: a checked loop beats the same rules in the prompt.
149
+
150
+ **Feed completeness (capabilities selected):** when the optional capabilities — image, poll, edit — are selected up front, the Vise arm completed **97–100%** of an 11-item feed checklist across three agents (Cursor/Composer, Claude/Sonnet, Codex/GPT). This is *absolute* completeness from answering Vise's capability questions — **not** a lift over a baseline.
151
+
152
+ **Engagement Intelligence advisory quality:** a 24-case held-out dogfood set supports the advisory positioning: expected available strategies surfaced in **17/17** cases, multiple strategy options were visible in **20/20** strategy-offer cases, rationale/tradeoff evidence was visible in **24/24**, and no-fit plus availability-gated cases preserved their boundaries. The same run retained **4 ranking-calibration concerns**, so this is **not** a claim of top-1 ranking confidence, autonomous strategy selection, business-outcome lift, or a calibrated score.
134
153
 
135
- - **Like-for-like** (pre-registered, K=3 seeds, one firewalled ground truth): the Vise workflow **roughly doubled** feed-feature completeness over the docs-only baseline to **58–64%** from **21–30%** and the effect replicated across all three agents (Cursor/Composer 2.5 30→64%, Claude/Sonnet 4.6 27→61%, Codex/GPT-5.4 21→58%), evidence it's a property of the verification loop rather than one model. (Grading was deterministic-only in two of the three cells, which if anything understates the gap.)
136
- - **Best case** (the Vise arm explicitly opted into the optional capabilities up front; later build): with image/poll/edit selected, the Vise arm reached **97–100%** of the 11-item feed checklist across Cursor/Composer 2.5, Claude/Sonnet 4.6, and Codex/GPT-5.4. This is an opt-in best case, **not** the like-for-like delta above — the baseline arm was not offered the same selection and ran an earlier build.
137
- - **SDK compliance:** in a greenfield SDK-compliance benchmark (9 slices, single run per cell) the Vise verification-loop arm scored **9/9 (100%)** vs a docs-only baseline at **6/9 (67%)**. The durable finding is the mechanism — a checked loop beats the same rules pasted into the prompt.
138
- - **Design drift:** under an ambiguous brief (Sonnet, n=3), Vise design runs held hex-literal variance to **0** while the docs-only arm spiked to 15 on one stochastic seed — variance reduction on bad runs, not pixel perfection (a later pre-registered run saw both arms at 0; the robust signal is by-name token usage).
139
- - **Boundaries:** these are best-case/opt-in comparisons for greenfield social.plus work, self-reported (no third-party audit), not universal claims. Negative results travel with the claims — e.g. no measured advantage on day-2 bug fixing. What Vise reliably changes is the **stopping condition**: the agent isn't done when code compiles; it's done when the local contract is green, attested, or blocked on explicit customer input.
154
+ **Boundaries:** these are social.plus's own measurements on greenfield work self-reported, no third-party audit, measured on earlier builds, and not universal claims. Negative results travel with them: no measured advantage on day-2 bug fixing, and the design-conformance arms were measured at different times (the figure is the robust by-name-token signal, not pixel perfection).
140
155
 
141
156
  <sub>Cursor, Claude, Codex, GitHub Copilot, VS Code, and other product names are trademarks of their respective owners; social.plus is not affiliated with or endorsed by them. Benchmark figures are from social.plus's own measurements.</sub>
142
157
 
@@ -148,7 +163,7 @@ Vise's measured effect is on **feed-feature completeness** — does the agent bu
148
163
  | **React Native** | ✅ Full | `tsc`, `npm lint`, SDK import smoke |
149
164
  | **Flutter / Dart** | ✅ Full | `flutter analyze`, `flutter test` |
150
165
  | **Android (Kotlin)** | ✅ Full | Gradle assemble, unit tests |
151
- | **iOS (Swift)** | ✅ Full | Static rules fully operational (tree-sitter AST for highest-risk rules); build sensor is guarded best-effort — runs only when `xcodebuild` is available, reports environment issues as skipped-with-reason, and still fails on real build errors |
166
+ | **iOS (Swift)** | ✅ Full | Static rules fully operational (tree-sitter AST for highest-risk rules); `Package.swift` enables a SwiftPM manifest sensor, and `.xcodeproj`/`.xcworkspace` enables a guarded `xcodebuild` sensor when available |
152
167
 
153
168
  Each platform has dozens of rules across 10 compliance domains (feed, comments, moderation, chat, secrets, session & auth, notifications, live objects, logging hygiene, design tokens).
154
169
 
@@ -162,7 +177,8 @@ Run `vise <command> --help` for full flags. JSON output is the default for agent
162
177
  |---|---|
163
178
  | `vise doctor` | Verify install; print version, install path, docs source |
164
179
  | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors |
165
- | `vise plan [path] --request "..."` | Grounded implementation plan with intake questions and docs citations |
180
+ | `vise plan [path] --request "..." [--summary]` | Grounded implementation plan with intake questions and docs citations; `--summary` prints a compact route/intake view |
181
+ | `vise plan --summary "..."` | Shortcut for quick routing dogfood or discovery when the current directory is the repo |
166
182
  | `vise plan-harness [path] --request "..."` | Pre-planning step: build the harness around the request |
167
183
  | `vise init [path] --request "..." [--answer key=value]` | Write the `sp-vise/` compliance contract once blocking intake is answered; exits 7 (`needs-clarification`) otherwise |
168
184
  | `vise workplan next [path] --request "..."` | For broad social requests: print the next uncompleted surface and its focused commands |
@@ -173,7 +189,7 @@ Run `vise <command> --help` for full flags. JSON output is the default for agent
173
189
 
174
190
  | Command | Purpose |
175
191
  |---|---|
176
- | `vise creative [path] --request "..." [--requirements <path\|none>] [--prototype <html>] [--ranking-preview]` | Write an advisory Engagement Intelligence brief with candidate solution variants; `--ranking-preview` adds an opt-in, local-only ranking preview that never reorders the default candidates |
192
+ | `vise creative [path] --request "..." [--requirements <path\|none>] [--prototype <html>] [--ranking-preview]` | Write an advisory Engagement Intelligence brief with multiple candidate solution variants, rationale, tradeoffs, and review gaps; `--ranking-preview` adds opt-in, local-only review context that never reorders the default candidates |
177
193
  | `vise creative accept [path] --variant <id>` | Record the selected variant so `plan`/`init`/`workplan` carry it forward; `--variant none --rationale "..."` records a catalog-gap signal instead |
178
194
  | `vise ux-harness [path]` | Generate advisory UX expectations from the accepted selection |
179
195
  | `vise experience compile [path]` | Compile the accepted variant into an implementation artifact plan |
@@ -182,7 +198,7 @@ Run `vise <command> --help` for full flags. JSON output is the default for agent
182
198
  | `vise learning record [path]` | Append a local-only learning event; refreshes the learning summary |
183
199
  | `vise learning show [path]` | Read the local learning summary (never changes recommendations) |
184
200
 
185
- Everything in this group is local and advisory: no uploads, no `vise check` exit-code changes, no auto-accepted variants, and no calibrated score until the calibration program graduates one.
201
+ Everything in this group is local and advisory: no uploads, no `vise check` exit-code changes, no auto-accepted variants, no top-1 confidence claim, and no calibrated score.
186
202
 
187
203
  ### Design contract
188
204
 
@@ -292,7 +308,7 @@ Vise writes local planning, compliance, design, and evidence artifacts under `sp
292
308
  | `sp-vise/inspection.json` | `vise init` | Platform, surface, and design signals detected at init |
293
309
  | `sp-vise/attestations/*.json` | `vise sync` / `vise attest` | Per-rule evidence: signer, confidence, rationale, source fingerprints for drift detection |
294
310
  | `sp-vise/creative-brief.json` + `creative-brief.md` | `vise creative` | Advisory brief: goals, archetypes, candidate variants (JSON + human-readable) |
295
- | `sp-vise/candidate-ranking-preview.json` | `vise creative --ranking-preview` | Opt-in local ranking preview; `experience_score: null`, no uploads, no default-order change |
311
+ | `sp-vise/candidate-ranking-preview.json` | `vise creative --ranking-preview` | Opt-in local ranking preview for review context; `experience_score: null`, no uploads, no default-order change, no top-1 confidence claim |
296
312
  | `sp-vise/creative-selection.json` | `vise creative accept` | Accepted variant and plan/workplan feed-forward context |
297
313
  | `sp-vise/catalog-gap.json` | `vise creative accept --variant none` | Local-only no-fit signal for human catalog review |
298
314
  | `sp-vise/ux-harness.json` | `vise creative accept` or `vise ux-harness` | Advisory UX pattern expectations and anti-patterns |
package/dist/server.js CHANGED
@@ -285,12 +285,16 @@ async function handleCli(args) {
285
285
  return "exit";
286
286
  }
287
287
  if (command === "plan" || command === "plan-integration") {
288
- await printToolResult(planIntegrationTool, {
289
- repoPath: positionalRepoPath(args.slice(1)),
290
- request: requiredFlagValue(args, "request", "plan requires --request."),
291
- surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
292
- answers: keyValueFlag(args, "answer"),
293
- });
288
+ const input = await planCliInput(args);
289
+ if (hasFlag(args, "summary")) {
290
+ const result = await planIntegrationTool.call(input);
291
+ const text = result.content.map((item) => item.text).join("\n");
292
+ const payload = JSON.parse(text);
293
+ console.log(JSON.stringify(planSummary(payload), null, 2));
294
+ }
295
+ else {
296
+ await printToolResult(planIntegrationTool, input);
297
+ }
294
298
  return "exit";
295
299
  }
296
300
  if (command === "plan-harness") {
@@ -583,7 +587,9 @@ Create the evidence-backed implementation packet before editing code.
583
587
 
584
588
  Usage:
585
589
  vise plan [repoPath] --request "Add a social feed"
590
+ vise plan --summary "Use UIKit for a standard feed"
586
591
  vise plan apps/web --request "Create posts" --surface apps/web
592
+ vise plan . --request "Use UIKit for a standard feed" --summary
587
593
 
588
594
  Re-plan with collected answers (repeat --answer for each intake question):
589
595
  vise plan . --request "Add a social feed" \\
@@ -993,6 +999,33 @@ async function printToolResult(tool, input) {
993
999
  console.log(text);
994
1000
  return { result, text };
995
1001
  }
1002
+ async function planCliInput(args) {
1003
+ const subArgs = args.slice(1);
1004
+ const requestFromFlag = flagValue(args, "request");
1005
+ if (requestFromFlag) {
1006
+ return {
1007
+ repoPath: positionalRepoPath(subArgs),
1008
+ request: requestFromFlag,
1009
+ surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
1010
+ answers: keyValueFlag(args, "answer"),
1011
+ };
1012
+ }
1013
+ const positional = positionalValues(subArgs);
1014
+ const [first, ...rest] = positional;
1015
+ if (!first) {
1016
+ throw new Error("plan requires --request or a quoted request argument.");
1017
+ }
1018
+ const firstLooksLikePath = await pathExists(first);
1019
+ if (firstLooksLikePath && rest.length === 0) {
1020
+ throw new Error("plan requires --request when the only positional argument is a repository path.");
1021
+ }
1022
+ return {
1023
+ repoPath: firstLooksLikePath ? first : ".",
1024
+ request: firstLooksLikePath ? rest.join(" ").trim() : positional.join(" ").trim(),
1025
+ surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
1026
+ answers: keyValueFlag(args, "answer"),
1027
+ };
1028
+ }
996
1029
  function toolResultStatus(printed) {
997
1030
  try {
998
1031
  const payload = JSON.parse(printed.text);
@@ -1002,6 +1035,125 @@ function toolResultStatus(printed) {
1002
1035
  return undefined;
1003
1036
  }
1004
1037
  }
1038
+ function planSummary(payload) {
1039
+ const solutionPath = objectProp(payload, "solutionPath");
1040
+ const uikitCustomization = objectProp(payload, "uikitCustomization");
1041
+ const intake = objectProp(payload, "intake");
1042
+ const answers = objectProp(intake, "answers") ?? {};
1043
+ const workplan = objectProp(payload, "socialWorkplan");
1044
+ const questions = arrayProp(intake, "questions").map(questionSummary);
1045
+ const decisionsRequired = stringArrayProp(payload, "decisionsRequired");
1046
+ const solutionPathAnswerId = stringProp(solutionPath, "answerId");
1047
+ const uikitCustomizationAnswerId = stringProp(uikitCustomization, "answerId");
1048
+ return stripUndefined({
1049
+ kind: "plan-summary",
1050
+ outcome: stringProp(payload, "outcome"),
1051
+ platform: stringProp(payload, "platform"),
1052
+ supportLevel: stringProp(payload, "supportLevel"),
1053
+ solutionPath: solutionPath
1054
+ ? {
1055
+ recommendation: stringProp(solutionPath, "recommendation"),
1056
+ confidence: stringProp(solutionPath, "confidence"),
1057
+ summary: stringProp(solutionPath, "summary"),
1058
+ answerId: solutionPathAnswerId,
1059
+ decisionRequired: booleanProp(objectProp(solutionPath, "decision"), "requiredBeforeHandRolledUi") === true
1060
+ && !hasAnswerValue(answers, solutionPathAnswerId),
1061
+ }
1062
+ : undefined,
1063
+ uikitCustomization: uikitCustomization
1064
+ ? {
1065
+ status: stringProp(uikitCustomization, "status"),
1066
+ recommendedLevel: stringProp(uikitCustomization, "recommendedLevel"),
1067
+ confidence: stringProp(uikitCustomization, "confidence"),
1068
+ summary: stringProp(uikitCustomization, "summary"),
1069
+ answerId: uikitCustomizationAnswerId,
1070
+ decisionRequired: booleanProp(objectProp(uikitCustomization, "decision"), "requiredBeforeCustomization") === true
1071
+ && !hasAnswerValue(answers, uikitCustomizationAnswerId),
1072
+ }
1073
+ : undefined,
1074
+ intake: intake
1075
+ ? {
1076
+ status: stringProp(intake, "status"),
1077
+ remainingBlocking: numberProp(intake, "remainingBlocking"),
1078
+ blockingQuestions: questions.filter((question) => question.blocksImplementationWhenMissing),
1079
+ openQuestions: questions,
1080
+ answers,
1081
+ }
1082
+ : undefined,
1083
+ decisionsRequired,
1084
+ workplan: workplan ? workplanSummary(workplan) : undefined,
1085
+ nextStep: stringProp(payload, "nextStep"),
1086
+ });
1087
+ }
1088
+ function hasAnswerValue(answers, answerId) {
1089
+ if (!answerId) {
1090
+ return false;
1091
+ }
1092
+ const answer = answers[answerId];
1093
+ return typeof answer === "string" && answer.trim() !== "";
1094
+ }
1095
+ function workplanSummary(workplan) {
1096
+ return stripUndefined({
1097
+ kind: stringProp(workplan, "kind"),
1098
+ status: stringProp(workplan, "status"),
1099
+ nextStep: stringProp(workplan, "nextStep"),
1100
+ surfaces: arrayProp(workplan, "sequence").map((surface) => {
1101
+ const surfaceObject = asObject(surface);
1102
+ const intake = objectProp(surfaceObject, "intake");
1103
+ return stripUndefined({
1104
+ id: stringProp(surfaceObject, "id"),
1105
+ order: numberProp(surfaceObject, "order"),
1106
+ outcome: stringProp(surfaceObject, "outcome"),
1107
+ label: stringProp(surfaceObject, "label"),
1108
+ remainingBlocking: numberProp(intake, "remainingBlocking"),
1109
+ intakeStatus: stringProp(intake, "status"),
1110
+ });
1111
+ }),
1112
+ });
1113
+ }
1114
+ function questionSummary(value) {
1115
+ const question = asObject(value);
1116
+ return {
1117
+ id: stringProp(question, "id"),
1118
+ question: stringProp(question, "question"),
1119
+ required: booleanProp(question, "required"),
1120
+ blocksImplementationWhenMissing: booleanProp(question, "blocksImplementationWhenMissing"),
1121
+ };
1122
+ }
1123
+ function objectProp(value, key) {
1124
+ if (!value) {
1125
+ return undefined;
1126
+ }
1127
+ return asObject(value[key]);
1128
+ }
1129
+ function asObject(value) {
1130
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
1131
+ }
1132
+ function arrayProp(value, key) {
1133
+ if (!value) {
1134
+ return [];
1135
+ }
1136
+ const item = value[key];
1137
+ return Array.isArray(item) ? item : [];
1138
+ }
1139
+ function stringArrayProp(value, key) {
1140
+ return arrayProp(value, key).filter((item) => typeof item === "string");
1141
+ }
1142
+ function stringProp(value, key) {
1143
+ const item = value?.[key];
1144
+ return typeof item === "string" ? item : undefined;
1145
+ }
1146
+ function numberProp(value, key) {
1147
+ const item = value?.[key];
1148
+ return typeof item === "number" ? item : undefined;
1149
+ }
1150
+ function booleanProp(value, key) {
1151
+ const item = value?.[key];
1152
+ return typeof item === "boolean" ? item : undefined;
1153
+ }
1154
+ function stripUndefined(value) {
1155
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
1156
+ }
1005
1157
  async function installSkill(args) {
1006
1158
  const source = skillSourceDir();
1007
1159
  const instructionInstall = instructionInstallDestination(args);
@@ -1438,7 +1590,12 @@ function ciCheckResult(result) {
1438
1590
  };
1439
1591
  }
1440
1592
  function positionalRepoPath(args) {
1593
+ const values = positionalValues(args);
1594
+ return values[0] ?? ".";
1595
+ }
1596
+ function positionalValues(args) {
1441
1597
  const flagsWithValues = new Set(["request", "requirements", "prototype", "variant", "variant-id", "brief", "brief-path", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source", "note", "kind", "sentiment", "metric"]);
1598
+ const values = [];
1442
1599
  for (let index = 0; index < args.length; index += 1) {
1443
1600
  const arg = args[index];
1444
1601
  if (!arg) {
@@ -1454,9 +1611,18 @@ function positionalRepoPath(args) {
1454
1611
  if (arg.startsWith("-")) {
1455
1612
  continue;
1456
1613
  }
1457
- return arg;
1614
+ values.push(arg);
1615
+ }
1616
+ return values;
1617
+ }
1618
+ async function pathExists(input) {
1619
+ try {
1620
+ await stat(path.resolve(expandHome(input)));
1621
+ return true;
1622
+ }
1623
+ catch {
1624
+ return false;
1458
1625
  }
1459
- return ".";
1460
1626
  }
1461
1627
  function requiredPositionalText(args, message) {
1462
1628
  const values = [];