Install
$ agentstack add skill-j-256-claude-code-skills-dsc-scenario ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
DSC Scenario Composer
Produce a plan of SCAPI / OCAPI calls – in order, with scope union, ID threading, and a runnable cURL block – to reach a target state. Every claim backed by a public developer.salesforce.com URL.
When to use
The user is trying to reproduce a customer flow on an instance and needs to know:
- Which API calls must happen before the target operation can succeed.
- Which scopes the instance's SLAS/OAuth client must be configured with.
- Where each input to the target (basket IDs, customer IDs, line-item IDs) comes from.
Or the user pastes a cURL command and asks "what else do I need to call to make this work."
Inputs
Ask for missing bits only when the skill can't proceed:
- Target – one of:
- An operationId (
createOrder,shopper-baskets.addItemToBasket). - A natural-language goal ("registered shopper adds a coupon and checks out"). You resolve this to an operationId by matching against
_index.json.title+ Summary prose across cached references; ask the user to confirm before proceeding. - A sample request (cURL, raw HTTP). Use
lib/parse-request.js+lib/resolve-slug.jsto map it to a slug. - Reference URL – the developer.salesforce.com URL of the reference containing the target. Usually inferrable from the request path or operationId's reference prefix.
Resolving an OCAPI target you can't name the reference for. OCAPI reference slugs are not guessable (ocapi-shop-orders, ocapi-data-code-versions – there is no ocapi-shop-api; guessing 404s). When the user gives a METHOD + path ("OCAPI shop, POST /orders", "submit a basket via POST /orders") but not the reference slug, resolve it deterministically with resolve-target.js – pass the area-landing URL (https://developer.salesforce.com/docs/commerce/b2c-commerce/references) plus the method and path; it scans the landing and returns the exact {reference, slug, referenceUrl} to hand to scenario.js:
node ~/.claude/skills/dsc-scenario/scripts/resolve-target.js /.cache/dsc-scrape"
}
EOF
It returns {area, candidates:[{reference, slug, referenceUrl, ...}]}. Use candidates[0] (most-specific path wins). Empty candidates means no match – ask the user for the reference URL; do NOT guess a slug (see "Decline, don't fabricate" below). The area-landing URL itself comes from the OCAPI hint in the user's prompt via the alias map (_shared/scrape/aliases.js maps "OCAPI", "/dw/shop", "/dw/data", etc. to the b2c-commerce landing).
Flow
- Resolve target to
{reference, targetSlug}. For natural-language goals, match titles + Summary prose and confirm with the user. - Invoke
scenario.js– it scrapes/refreshes the cache for you (via the accessor) and runs the type-graph walk locally; you do not warm the cache or read its files first:
``bash node ~/.claude/skills/dsc-scenario/scripts/scenario.js /.cache/dsc-scrape" } EOF ``
Omitting graph is the default: scenario.js runs walkTypes itself against the cache it just warmed. For an unusually large or cross-reference-heavy target where you want a sub-agent to do the walk instead, read scripts/walk-via-agent.md, pass that prompt to the Agent tool, and put its returned {nodes, edges, externalInputs} in the graph field – but that is the exception, not the routine path, and even then the sub-agent reads the cache scenario.js warmed, it does not scrape.
Producer choice point (two-pass). If scenario.js returns a bridgeCandidates array, the target depends on a resource that has more than one operation producing it from nothing, and the skill needs you to pick which one belongs in the plan. The candidates are those from-nothing producers. Pick the single canonical create – the one that builds the resource fresh, not the ones that derive it from an existing instance (mergeBasket/transferBasket presuppose an existing basket; createBasket does not). Re-run scenario.js with bridgeProducer: "" added to the stdin JSON. The second call composes the full plan deterministically – you do not hand-write the producer step. Pick exactly one; do not list the alternatives in the plan.
This fires in two structurally identical situations, distinguished only by where the producer lives – you handle both the same way (pick one, re-invoke with bridgeProducer):
- Cross-reference: the target's body is a named resource produced by another reference (e.g.
createOrdertakes a preparedBasket, produced byshopper-baskets-v2.createBasket). - In-reference: the target needs an id (e.g.
addPaymentInstrumentToBasketneeds abasketId) that several ops in the target's own reference produce from nothing (createBasket,transferBasket,mergeBasket). Without a pick the plan would chain all of them as bogus mandatory prerequisites; the candidate list lets you keep only the canonical create.
- Layer business-logic ordering. The structural plan from Step 2 may need reordering based on rules stated in the Summary or endpoint
descriptionprose. Apply constraints only when they're quoted from the docs; otherwise leave the structural order as-is and annotate as "no explicit ordering constraint found – structural only." Never invent constraints. - Honor the submittability advisory (if present). When
scenario.jsreturns asubmittabilityobject, the produced resource (e.g. aBasket) must be populated beyond the structural FK-threading minimum for the target to accept it – curated runtime knowledge the spec does not state. Render it per "Submittability registry" below: a populated producer body + the curated business-rule framing. Absent that object, the structural plan is the whole story. - Compose the output per the template below. Cite only the URLs in
sources[]; never cite local paths.
Output composition
scenario.js emits {plan, runnable, sources, staleness}. Wrap it for the user like this (and if staleness is non-empty, prepend the stale-data warning from "Key invariants" above the ## Scenario: line):
## Scenario:
Target: (.)
References involved:
Combined scopes required: (mandatory -- always emit this line with the scope names from plan.combinedScopes; never omit or summarize it away, even in a terse answer)
## Plan
1. **.** .
- Method/path:
- Spec:
- Produces:
- Why:
2. ... (one block per step.)
## Run it
## Sources
-
-
The auth step(s) appear at the top of the plan list (steps 1, optionally 1a/1b for the two-leg PKCE flow), driven by plan.authBranch and plan.authFlow from composePlan. When authBranch === 'unknown', omit the auth-step block entirely; the plan starts with the target reference's first operation. The "References involved" line includes auth (Shopper Login / SLAS) when authBranch === 'shopper-slas'; AM auth steps cite the canonical account.demandware.com/dwsso/oauth2/access_token URL with a one-line note (see "Account Manager (AM) auth framing" below) -- never a developer.salesforce.com URL.
The ## Run it block is mandatory. The runnable's URL prefix is emitted deterministically by scenario.js from each reference's basePath (SCAPI /checkout//v/..., OCAPI Shop /s/${SITEID}/dw/shop/v/..., OCAPI Data /s/-/dw/data/v/...). OCAPI calls also carry a ?client_id=${CLIENT_ID} query param (the auth floor); scenario.js emits that too. You do not reconstruct any of it.
PKCE in the runnable. When the auth flow uses PKCE (any SLAS shopper flow, AM 'public-pkce'), don't hand-write the CODE_VERIFIER / CODE_CHALLENGE lines. Run node ~/.claude/skills/dsc-scenario/scripts/pkce-snippet.js and paste its stdout into the bash block before the /oauth2/authorize (or /oauth2/login) call. Hand-rolled snippets drift toward the 32-byte / 43-char minimum-length form; the helper emits the 96-byte / 128-char form (still RFC 7636 compliant, more entropy) consistently.
Extracting JSON response fields in the runnable -- use jq -r. When a later call needs a field from an earlier call's JSON response body (access_token from getAccessToken, basketId from createBasket, applicableShippingMethods[0].id from getShippingMethodsForShipment), extract it with jq -r -- e.g. ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token). This is the idiom the deterministic renderer (scripts/curl-block.js) emits, so a hand-composed multi-reference runnable must match it. Do NOT hand-roll node -e "...JSON.parse(d)..." or python3 -c "...json.load(...)..." for this -- it's the right answer the renderer already produces, and a 200-character Node stdin-reader is not what a support engineer pastes into a terminal. (This is the inverse case of the redirect rule below: the authorization code comes from the 303 Location header and must NOT be parsed as JSON; genuine JSON response bodies like the token or basket are parsed, with jq.) Begin the runnable with a one-line preflight so a missing jq fails loud rather than silently mis-capturing -- command -v jq >/dev/null || { echo "this script needs jq (brew install jq, or apt-get install jq)"; exit 1; }. Note: jq ships with macOS only since Sequoia (15); Linux and older-macOS users may not have it, which is why the preflight earns its line. Do NOT use the display-context fallback ... || _jq() { cat; } (as in stepped-demo-script): here the value is captured into a shell variable, and falling back to cat would stuff the entire JSON blob into ACCESS_TOKEN/BASKET_ID and break every downstream call.
Capturing the authorization code in the runnable. The SLAS authorization-code legs answer with a 303 whose Location header carries the code – authorizeCustomer ("The authorization code was successfully added to the redirect_uri") and authenticateCustomer (the code and usid are added to the location header and returned as query params). The guest leg (authorizeCustomer + hint=guest + public client) and the registered-B2C leg (authenticateCustomer + a Basic-auth shopperUserID:shopperPassword header) are both non-interactive: a single curl returns the 303, and the runnable must capture the code straight from the redirect. Either idiom is correct: curl -sS -o /dev/null -w '%{redirect_url}' then parse code= out of the captured URL, or curl -sS -D - (dump headers) then grep -i '^location:' and sed the code= value out. Do NOT emit a manual "open this URL in a browser and paste the code" step for these flows (and never a read/read -rp prompt mid-script); a human hand-off in the middle of the script breaks the paste-and-run contract exactly like a bare spec path does.
The code is in the Location header, never a JSON response body. Both legs are declared with 303 (redirect) responses only – there is no 200 with a JSON token-less body, and no authorizationCode field anywhere in either operation's response schema. Do NOT write ... | node -e "...JSON.parse(d).authorizationCode" or python3 -c "...json.load(...)['authorizationCode']" against the authorize/login response – that field is fabricated; the spec returns the code via the redirect Location, so the runnable must read it from there. Parsing a JSON body off the authorize/login call is a spec-fidelity bug, not a style choice.
The lone genuine exception to the non-interactive rule is the registered-federated leg (authorizeCustomer + hint=): there the shopper authenticates at an external IDP (Okta, Auth0, …), so that one call is inherently interactive – keep the browser step and say why, rather than pretend the code can be scraped headlessly.
authenticateCustomer (POST /oauth2/login) request contract. The registered-B2C login leg is easy to get subtly wrong because the spec states its parameters in prose, not in a formal parameters/requestBody schema (the schema only lists organizationId, Authorization, x-slas-client-auth). Take the contract from the description verbatim:
Authorization: Basic base64(:)– required header, the shopper's own username:password (NOT the client ID/secret; the client goes in the optionalx-slas-client-authheader when strict client auth is enabled).- Required parameters:
code_challenge,channel_id,client_id,redirect_uri. Optional:usid. - There is no
grant_typeon/login(that belongs on the/tokenexchange ingetAccessToken), and noresponse_type,channel_type,login_id,login_password, orlocale– those are fabrications. In particularchannel_idis required and easy to drop; do not omit it, and do not inventchannel_typein its place.
When you compose the registered-B2C runnable, the /login curl carries the shopper Basic-auth header plus exactly those four required params (and usid only if you have one to thread). Don't add a grant_type to it; don't substitute login_id/login_password form fields for the Basic header.
When a step's only evidence is {kind: 'structural', ...}, the "Why" line should read: " requires in the request; this step's response provides it." When you add a business-logic constraint from prose, quote the relevant sentence and cite the Summary or endpoint URL inline.
Cross-reference steps go in the same Plan list, not in a separate section. If externalInputs[] includes {reference: "auth", ...}, the "References involved" line names auth (the SLAS reference's URL slug; the title on DSC is "Shopper Login (SLAS)") alongside the others, and the SLAS step (or steps – usually authorizeCustomer + getAccessToken) appears as numbered step(s) in the main Plan with their own Spec: URL. Don't write "external input – not part of either reference"; that prose is wrong (the input is part of a DSC reference). Auth steps in particular are mandatory expansions – see "Cross-reference walks" below. The "References involved" line and the Plan must include the auth steps even when the rest of the scenario is scoped to one reference.
Submittability registry – populating the produced resource
The structural walk plans the FK-threading minimum: enough to make the type graph resolve. For some targets that minimum is not enough for the target to accept the produced resource. The canonical case: createOrder's only structural input is basketId, so the walk emits a createBasket with an empty {} body – but createOrder rejects an unpopulated basket at submit (400). The set of fields the basket must carry to be submittable is in neither the machine-readable spec (Basket.required is null) nor the basket-prep prose (it states no hard required-set). It is curated runtime knowledge, encoded in scripts/submittability.json and folded in deterministically by scenario.js – the same category of encoded fact as the SLAS auth-routing table, not model fabrication.
When scenario.js returns a submittability object, it means the target's body type has a registry entry. The object carries {typeName, note, submittableVia, needed, bodyContents:[{field, why}], provenance, confidence:"curated", producerSlug}. note is a one-line plain-language summary of the rule (surface it verbatim if you want a quick framing sentence); submittableVia is "producer-body" when the minimum is populated in the producer's request body (today's only shape); needed is the list of separate ops required (empty for body-content entries like Basket). Render it like this:
- Populate the producer step's body (the
producerSlugstep – e.g.createBasket) with thebodyContentsfields, showing a realistic example body (a line item, a shipping method + address, a billing address, a payment instrument), not the empty{}and not opaque `placeholders. The runnable fromscenario.js` carries these fields and a banner; flesh the body out to something a support engineer can actually paste, keeping the field set to exactly w
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: j-256
- Source: j-256/claude-code-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.