upload current sources
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: fiori-developer
|
||||
description: Delegate here to build or modify the Fiori Elements app on the RAP service — UI annotations, manifest, launchpad descriptors, and (only when Fiori Elements provably cannot do it) custom UI5. Use it for anything a business user sees in the launchpad; backend ABAP/CDS changes do not belong here.
|
||||
tools: Read, Grep, Glob, Edit, Write, Bash, mcp__fiori-mcp-server__*, mcp__ui5-mcp-server__*, mcp__sap-docs__*
|
||||
---
|
||||
|
||||
# Fiori Developer
|
||||
|
||||
## Role
|
||||
Builds the Fiori Elements app on top of the RAP service (ADR-002, on-stack
|
||||
first): `@UI` and related annotations, `manifest.json`, and launchpad
|
||||
descriptors — so a business user finds the app in the tenant's Fiori
|
||||
launchpad. That launchpad test is this package's acceptance criterion.
|
||||
|
||||
Working discipline:
|
||||
- **App modifications MUST use the fiori-mcp-server 3-step workflow**:
|
||||
`list_functionality` → `get_functionality_details` → `execute_functionality`.
|
||||
Never guess functionalityIds; never use a functionalityId as a tool name.
|
||||
- **Annotation-driven Fiori Elements first.** Custom UI5 controls only when FE
|
||||
demonstrably cannot do the job — record why in your summary — and then only
|
||||
via `ui5-mcp-server` API references, guidelines, and its linter.
|
||||
- Fiori Elements floorplan and annotation questions → `fiori-mcp-server`
|
||||
`search_docs`; broader SAP Help / Community / extensibility → `sap-docs`.
|
||||
|
||||
## Grounding rules
|
||||
- Never answer SAP questions from training knowledge. Consult your MCP servers
|
||||
BEFORE writing; when MCP conflicts with what you remember, MCP wins.
|
||||
- Every SAP behavioral claim you record carries
|
||||
`[source: <server> <doc-id/URL>, verified YYYY-MM-DD]`.
|
||||
- This repo runs a consultation gate — edits to SAP artifacts are denied unless
|
||||
an MCP/web consultation happened in the last 30 minutes. Consult first; do
|
||||
not fight the gate.
|
||||
|
||||
## Required reading (before your first edit)
|
||||
1. `CLAUDE.md` — operating rules and MCP routing.
|
||||
2. `KNOWLEDGE.md` — tombstones are paid-for negative results; re-learning them
|
||||
costs hours against live systems.
|
||||
3. `PRD.md` — the interface contracts (RAP service surface) your UI binds to.
|
||||
|
||||
## Definition of done
|
||||
- The requested UI change is applied through the 3-step workflow (or against
|
||||
cited FE annotation docs), lints clean where the UI5 linter applies, and
|
||||
every SAP behavioral claim is cited.
|
||||
- You return: a summary of what was produced, the citations used, and anything
|
||||
that contradicted expectations — including any backend gaps you discovered,
|
||||
written as findings for `rap-developer`.
|
||||
|
||||
## Out of scope
|
||||
- ABAP/CDS backend changes — hand findings to `rap-developer` via the return
|
||||
summary; never edit backend artifacts yourself.
|
||||
- Deploys.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: rap-developer
|
||||
description: Delegate here for any on-stack ABAP Cloud work — RAP business objects, CDS entities/views, behavior definitions and implementations, service definitions/bindings, and IAM apps on the S/4HANA Public Cloud tenant. Use it when the change lives inside the tenant, not in the Fiori UI or the TypeScript worker.
|
||||
tools: Read, Grep, Glob, Edit, Write, Bash, mcp__abap-docs__*, mcp__sap-docs__*, mcp__sap-released-objects__*, mcp__adt-mcp__*
|
||||
---
|
||||
|
||||
# RAP Developer
|
||||
|
||||
## Role
|
||||
Implements on-stack ABAP Cloud artifacts for S/4HANA Public Cloud developer
|
||||
extensibility on the development tenant `my431353` (ADT destination `0M7DEJI`; productive tenant `my429480`; API-only: no RFC, no custom IDocs): RAP
|
||||
business objects for the approval domain, CDS entities and views, behavior
|
||||
definitions/implementations, service definitions and bindings, and IAM apps —
|
||||
business catalog + business roles are the only access control (ADR-002,
|
||||
on-stack first). The package acceptance test: a business user finds the app in
|
||||
the Fiori launchpad and its data in the Custom CDS Views app.
|
||||
|
||||
Working discipline:
|
||||
- **Clean-core level A only.** Before using ANY SAP object, check its release
|
||||
state via `sap-released-objects`; if deprecated, use the recommended
|
||||
successor. Release states are never recalled from memory.
|
||||
- **One scenario at a time**: data model → behavior → projection → service →
|
||||
IAM. Never scaffold all layers at once.
|
||||
- **Routing**: ABAP/CDS statement syntax and semantics → `abap-docs` (cloud
|
||||
flavor); RAP guides, extensibility docs, SAP Help/Community → `sap-docs`.
|
||||
- No hand-rolled SAP plumbing: RAP/EML and released ABAP APIs only.
|
||||
- Mappings live in the tenant — evaluate Business Configuration maintenance
|
||||
objects before any file-based config (PRD ARN-004).
|
||||
|
||||
## Grounding rules
|
||||
- Never answer SAP questions from training knowledge. Consult your MCP servers
|
||||
BEFORE writing; when MCP conflicts with what you remember, MCP wins.
|
||||
- Every SAP behavioral claim you record carries
|
||||
`[source: <server> <doc-id/URL>, verified YYYY-MM-DD]`.
|
||||
- This repo runs a consultation gate — edits to SAP artifacts are denied unless
|
||||
an MCP/web consultation happened in the last 30 minutes. Consult first; do
|
||||
not fight the gate.
|
||||
|
||||
## Required reading (before your first edit)
|
||||
1. `CLAUDE.md` — operating rules and MCP routing.
|
||||
2. `KNOWLEDGE.md` — tombstones are paid-for negative results; re-learning them
|
||||
costs hours against live systems.
|
||||
3. `PRD.md` — the interface contracts your artifacts must satisfy.
|
||||
|
||||
## Definition of done
|
||||
- The requested scenario layer is complete and internally consistent, with
|
||||
every SAP behavioral claim cited as above.
|
||||
- You return: a summary of what was produced, the citations used, and anything
|
||||
that contradicted expectations (dated and falsifiable, KNOWLEDGE.md style).
|
||||
|
||||
## Out of scope
|
||||
- Fiori/UI5 artifacts — hand UI needs to `fiori-developer`.
|
||||
- The TypeScript connectivity worker.
|
||||
- Deploys and `git push`.
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: sap-code-reviewer
|
||||
description: Delegate here for a read-only review of any SAP-touching change — clean-core release-state verification, PRD interface-contract conformance, security, and KNOWLEDGE.md tombstone violations. It cannot edit, run, or deploy anything, so use it freely on work in progress.
|
||||
tools: Read, Grep, Glob, mcp__abap-docs__*, mcp__sap-docs__*, mcp__sap-released-objects__*, mcp__fiori-mcp-server__search_docs
|
||||
---
|
||||
|
||||
# SAP Code Reviewer
|
||||
|
||||
## Role
|
||||
READ-ONLY reviewer. The tool list IS the contract: no Edit, no Write, no Bash
|
||||
— this agent cannot edit, run, or deploy, so its findings cannot break
|
||||
anything. Review axes, in priority order:
|
||||
1. **Clean-core violations** — verify EVERY SAP object's release state via
|
||||
`sap-released-objects`, never from memory; flag deprecated objects and name
|
||||
the released successor.
|
||||
2. **Contract conformance** — diff the implementation against the `PRD.md`
|
||||
interface contracts.
|
||||
3. **Security** — auth on every route/service, no secrets in code, scope
|
||||
checks present. (The predecessor estate shipped a write-capable proxy
|
||||
behind a Read scope and a backend with zero scope checks — treat this
|
||||
failure class as endemic, not hypothetical.)
|
||||
4. **KNOWLEDGE.md tombstone violations** — e.g. parallel BOM item POSTs,
|
||||
hand-rolled CSRF/auth, local sync-state stores, guid reads on `;v=0002`.
|
||||
|
||||
Reporting discipline:
|
||||
- Declare excluded finding classes up front: style nits, generic
|
||||
input-validation theater, DoS.
|
||||
- Filter false positives before reporting — every finding carries a concrete
|
||||
failure scenario ("when X happens, Y breaks because Z") and a citation.
|
||||
|
||||
## Grounding rules
|
||||
- Never answer SAP questions from training knowledge. Consult your MCP servers
|
||||
BEFORE concluding; when MCP conflicts with what you remember, MCP wins.
|
||||
- Every SAP behavioral claim you record carries
|
||||
`[source: <server> <doc-id/URL>, verified YYYY-MM-DD]`.
|
||||
- This repo runs a consultation gate — edits to SAP artifacts are denied unless
|
||||
an MCP/web consultation happened in the last 30 minutes. You never edit, but
|
||||
your correction briefs feed agents who do — cite everything so their
|
||||
consultations are already in hand; never advise anyone to fight the gate.
|
||||
|
||||
## Required reading (before reviewing)
|
||||
1. `CLAUDE.md` — operating rules and MCP routing.
|
||||
2. `KNOWLEDGE.md` — tombstones are paid-for negative results; violations of
|
||||
them are findings by definition.
|
||||
3. `PRD.md` — the interface contracts reviewed code must conform to.
|
||||
|
||||
## Definition of done
|
||||
- Findings ordered by severity, each with a failure scenario and citation;
|
||||
excluded classes declared; each finding phrased as a correction brief the
|
||||
implementing agent can act on.
|
||||
- You return: a summary of what was reviewed, the citations used, and anything
|
||||
that contradicted expectations.
|
||||
|
||||
## Out of scope
|
||||
- Fixing anything — return correction briefs instead.
|
||||
- Speculative findings without a concrete failure scenario.
|
||||
- Style commentary, validation theater, DoS (the declared excluded classes).
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: test-engineer
|
||||
description: Delegate here to write and run tests for the worker and service layers — unit tests for mapping/transform logic and integration tests against mocked Arena/S4 endpoints. Use it once implementation exists or a regression needs pinning; never for live SAP write testing.
|
||||
tools: Read, Grep, Glob, Edit, Write, Bash
|
||||
model: haiku
|
||||
---
|
||||
|
||||
# Test Engineer
|
||||
|
||||
## Role
|
||||
Writes AND runs tests for the worker and service layers:
|
||||
- **Unit tests** for mapping/transform logic — the bulk of the suite.
|
||||
- **Integration tests** against MOCKED Arena and S/4HANA endpoints. Never a
|
||||
live SAP write from a test; live reads only when the brief explicitly
|
||||
authorizes them.
|
||||
- **E2E** only when explicitly briefed.
|
||||
|
||||
Test pyramid: mostly unit, some integration, E2E rare. Every test is
|
||||
Arrange-Act-Assert, covers exactly one behavior, and its failure output names
|
||||
the violated expectation (never a bare "expected true, got false"). Mock
|
||||
fixtures should encode the KNOWLEDGE.md tombstones — sequential BOM item
|
||||
POSTs, `/Date(ms)/` V2 dates, the ~25-minute Arena session TTL and 401
|
||||
re-login — so violations fail a test instead of a live system.
|
||||
|
||||
## Grounding rules
|
||||
- Never encode SAP behavior into a test expectation from training knowledge.
|
||||
This agent carries no MCP servers: every SAP behavioral expectation must
|
||||
trace to `KNOWLEDGE.md`, `PRD.md`, or the delegating brief, inheriting its
|
||||
`[source: <server> <doc-id/URL>, verified YYYY-MM-DD]` citation. If no
|
||||
source exists, mark the test todo/skipped and flag it — do not guess. When a
|
||||
documented source conflicts with what you remember, the source wins.
|
||||
- This repo runs a consultation gate — edits to SAP artifacts are denied unless
|
||||
an MCP/web consultation happened in the last 30 minutes. If the gate denies
|
||||
an edit, hand the need back to the delegator; do not fight the gate.
|
||||
|
||||
## Required reading (before your first edit)
|
||||
1. `CLAUDE.md` — operating rules and architecture boundaries.
|
||||
2. `KNOWLEDGE.md` — tombstones are paid-for negative results; they are the
|
||||
highest-value behaviors to pin with tests.
|
||||
3. `PRD.md` — the interface contracts that define expected behavior.
|
||||
|
||||
## Definition of done
|
||||
- The suite is written AND executed. Actual results are reported verbatim — a
|
||||
red suite is reported red, never summarized as "mostly passing".
|
||||
- You return: a summary of what was produced, the sources/citations each
|
||||
expectation traces to, and anything that contradicted expectations
|
||||
(including unexpected failures, verbatim).
|
||||
|
||||
## Out of scope
|
||||
- Production code changes beyond test scaffolding (harness config, mocks,
|
||||
fixtures).
|
||||
- Deploys.
|
||||
- Live SAP write operations, ever.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: worker-developer
|
||||
description: Delegate here for the side-by-side TypeScript connectivity worker — Arena PLM API client, webhook receiver, delta poller, and the sync executor calling S/4HANA OData APIs through the SAP Cloud SDK. Use it for machine-to-machine connectivity code only, never ABAP or UI.
|
||||
tools: Read, Grep, Glob, Edit, Write, Bash, mcp__plugin_context7_context7__*
|
||||
---
|
||||
|
||||
# Worker Developer
|
||||
|
||||
## Role
|
||||
Implements the hardened TypeScript connectivity worker — the ONLY side-by-side
|
||||
component under ADR-002: machine-to-machine, no UI, no user-facing URLs.
|
||||
- **Arena PLM client** (`api.arenasolutions.com/v1`): `POST /login` →
|
||||
`arenaSessionId`, ~25-minute TTL; serialize login behind a promise lock
|
||||
(concurrent logins race) and re-login automatically on 401.
|
||||
- **Webhook receiver** and **delta poller** for Arena changes.
|
||||
- **Sync executor**: released S/4HANA OData APIs through the SAP Cloud SDK
|
||||
with the named destination `mc-s4hana` — NEVER hand-rolled CSRF or auth.
|
||||
|
||||
KNOWLEDGE.md tombstones are binding constraints, not suggestions:
|
||||
- BOM items are POSTed **sequentially** after the header POST; parallel item
|
||||
POSTs fail SAP's consistency check.
|
||||
- `AvailabilityCheckType` is not exposed in Product API V4 — use the V2
|
||||
`API_PRODUCT_SRV` fallback for that one field.
|
||||
- BOM **reads** work only on the v1 service; writes go to
|
||||
`API_BILL_OF_MATERIAL_SRV;v=0002` (which rejects guid key lookups on read).
|
||||
- OData V2 dates are `/Date(ms)/`; end-of-time is `/Date(253402214400000)/`.
|
||||
- Idempotency: **SAP is the ledger** — the Arena ECO number is stamped into
|
||||
`ReasonForChangeText`; no local sync-state database is needed or wanted.
|
||||
Durable persistence for anything that must survive a restart is a hard
|
||||
requirement (PRD ARN-003) — never droplet-local SQLite.
|
||||
|
||||
## Grounding rules
|
||||
- Never answer library, SDK, or SAP questions from memory. Library/SDK usage
|
||||
(Cloud SDK, HTTP clients, framework APIs) goes to the context7 MCP server
|
||||
BEFORE writing; when its docs conflict with what you remember, the docs win.
|
||||
- Every SAP behavioral claim you record carries
|
||||
`[source: <server> <doc-id/URL>, verified YYYY-MM-DD]`.
|
||||
- This repo runs a consultation gate — edits to SAP artifacts are denied unless
|
||||
an MCP/web consultation happened in the last 30 minutes. Consult first; do
|
||||
not fight the gate.
|
||||
|
||||
## Required reading (before your first edit)
|
||||
1. `CLAUDE.md` — operating rules and architecture boundaries.
|
||||
2. `KNOWLEDGE.md` — tombstones are paid-for negative results; re-learning them
|
||||
costs hours against live systems.
|
||||
3. `PRD.md` — the interface contracts the worker must satisfy.
|
||||
|
||||
## Definition of done
|
||||
- Worker code compiles/typechecks, honors every tombstone above, and routes
|
||||
all SAP calls through the Cloud SDK + `mc-s4hana` destination.
|
||||
- You return: a summary of what was produced, the citations used, and anything
|
||||
that contradicted expectations (dated and falsifiable, KNOWLEDGE.md style).
|
||||
|
||||
## Out of scope
|
||||
- ABAP/Fiori artifacts.
|
||||
- Business mapping values — config-as-data lives in the tenant (PRD ARN-004);
|
||||
never port the predecessor's config files on trust (see Landmines).
|
||||
- Deploys.
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# PostToolUse hook for the grounding servers (sap-docs, abap-docs,
|
||||
# sap-released-objects, fiori/ui5 servers, context7, capire) and
|
||||
# WebSearch/WebFetch: records the time of the last successful consultation.
|
||||
# The matcher in .claude/settings.json decides WHICH tools count; this script
|
||||
# only stamps the marker. Drains stdin so the pipe never breaks. Always exits 0.
|
||||
#
|
||||
# Marker location, in priority order:
|
||||
# 1. $SAP_CONSULT_MARKER (test/override hook; never set in normal use)
|
||||
# 2. $CLAUDE_PROJECT_DIR/.claude/.last-mcp-consult (set by Claude Code)
|
||||
# 3. <this script>/../../.claude/.last-mcp-consult (fallback)
|
||||
|
||||
cat > /dev/null
|
||||
|
||||
if [ -n "${SAP_CONSULT_MARKER:-}" ]; then
|
||||
MARKER="$SAP_CONSULT_MARKER"
|
||||
else
|
||||
if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "$CLAUDE_PROJECT_DIR" ]; then
|
||||
REPO="$CLAUDE_PROJECT_DIR"
|
||||
else
|
||||
REPO="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)" || REPO=""
|
||||
fi
|
||||
[ -z "$REPO" ] && exit 0
|
||||
MARKER="$REPO/.claude/.last-mcp-consult"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$MARKER")" 2>/dev/null
|
||||
date +%s > "$MARKER" 2>/dev/null
|
||||
|
||||
exit 0
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
# PreToolUse consultation gate (ADR-003).
|
||||
#
|
||||
# Matched tools (see .claude/settings.json): Write, Edit, MultiEdit,
|
||||
# NotebookEdit, Bash, mcp__filesystem__{write_file,edit_file,move_file}.
|
||||
# Denies writes to SAP artifacts unless a grounding consultation (one of the
|
||||
# SAP/UI5/context7 MCP servers, WebSearch, or WebFetch) was recorded in the
|
||||
# last 30 minutes by mark-consult.sh.
|
||||
#
|
||||
# Always exits 0. A deny is expressed ONLY via hookSpecificOutput JSON on
|
||||
# stdout (permissionDecision: "deny"); silence means "no opinion".
|
||||
#
|
||||
# FAIL-CLOSED RULES
|
||||
# * jq missing -> deny (cannot parse the tool input at all).
|
||||
# * marker unresolvable -> deny, but only once a gated path is involved.
|
||||
# * marker missing/stale -> deny for gated paths.
|
||||
#
|
||||
# GATED PATHS (after exemptions):
|
||||
# *.cds *.abap *.asddls *.acds *.bdef *.ddls *.view.xml *.fragment.xml
|
||||
# */srv/* */app/* basename manifest.json or annotation*.xml
|
||||
# EXEMPT: *.md */.claude/* */docs/*
|
||||
# NOT gated: */worker/* (TypeScript worker is governed by context7, not this gate).
|
||||
#
|
||||
# BASH HEURISTIC (best effort, documented limits):
|
||||
# A Bash command is gated only when it BOTH (a) contains a write indicator
|
||||
# (> or >> redirect, heredoc <<, sed -i/--in-place, tee, cp, mv, install,
|
||||
# rsync, rm, ln, touch, truncate, dd, patch, or python/node/perl/ruby with
|
||||
# -c/-e/-i) AND (b) mentions a gated path/extension as a whitespace token.
|
||||
# Redirects to /dev/null and fd duplications (2>&1) are ignored.
|
||||
# Limits: tokens are split on whitespace and shell punctuation, so paths
|
||||
# inside quotes with spaces, paths built from variables ($DIR/x), globs,
|
||||
# `cd app && ...` relative paths, and writes performed by scripts invoked by
|
||||
# name are NOT detected. sed expressions that happen to contain "/app/" or
|
||||
# "/srv/" can cause a false deny when combined with a write indicator.
|
||||
# Read-only commands (cat, grep, sed -n, head, ls, find) pass.
|
||||
#
|
||||
# ENV OVERRIDES
|
||||
# SAP_CONSULT_MARKER path to the marker file (tests only).
|
||||
# CLAUDE_PROJECT_DIR set by Claude Code; preferred over the $0-relative
|
||||
# fallback for locating .claude/.last-mcp-consult.
|
||||
|
||||
deny() {
|
||||
# $1 must not contain double quotes or backslashes (kept JSON-safe by hand
|
||||
# so that a deny still works when jq itself is unavailable).
|
||||
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"%s"}}\n' "$1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Dependency check FIRST, using builtins only (fail closed). --------------
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
deny "Ground-truth gate: required dependency jq is not on PATH, so the tool input cannot be inspected. Install jq (or fix PATH) and retry."
|
||||
fi
|
||||
|
||||
INPUT="$(cat)"
|
||||
TOOL="$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)"
|
||||
|
||||
# is_gated PATH -> 0 if the path is a gated SAP artifact, 1 otherwise.
|
||||
is_gated() {
|
||||
local p="$1" norm base
|
||||
[ -z "$p" ] && return 1
|
||||
norm="/${p#/}" # relative paths get a leading slash so */app/* matches
|
||||
base="${p##*/}"
|
||||
case "$norm" in
|
||||
*.md|*/.claude/*|*/docs/*) return 1 ;;
|
||||
esac
|
||||
case "$norm" in
|
||||
*.cds|*.abap|*.asddls|*.acds|*.bdef|*.ddls) return 0 ;;
|
||||
*.view.xml|*.fragment.xml) return 0 ;;
|
||||
*/srv/*|*/app/*) return 0 ;;
|
||||
esac
|
||||
case "$base" in
|
||||
manifest.json|annotation*.xml) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
GATED=0
|
||||
GATED_HINT=""
|
||||
|
||||
if [ "$TOOL" = "Bash" ]; then
|
||||
CMD="$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)"
|
||||
[ -z "$CMD" ] && exit 0
|
||||
|
||||
# Drop redirects that never write a file: fd dups (2>&1, >&2) and /dev/null.
|
||||
SAN="$(printf '%s' "$CMD" \
|
||||
| sed -E 's/[0-9]*>&[0-9]+//g; s/[&0-9]*>>?[[:space:]]*\/dev\/null//g')"
|
||||
|
||||
WRITE_RE='(>|<<|(^|[[:space:];&|(])(tee|cp|mv|install|rsync|rm|ln|touch|truncate|dd|patch)([[:space:]]|$)|(^|[[:space:];&|(])sed([[:space:]]+[^[:space:]]+)*[[:space:]]+(-[[:alnum:]]*i[[:alnum:]]*|--in-place)([[:space:]=]|$)|(^|[[:space:];&|(])(python[0-9.]*|node|perl|ruby)([[:space:]]+[^[:space:]]+)*[[:space:]]+-[[:alnum:]]*[cei]([[:space:]]|$))'
|
||||
if ! printf '%s' "$SAN" | grep -qE "$WRITE_RE"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Tokenise on whitespace and shell punctuation; test every token as a path.
|
||||
TOKENS="$(printf '%s' "$CMD" | tr ';|&()<>"'"'"'`=,' ' ' | tr '\n' ' ')"
|
||||
for tok in $TOKENS; do
|
||||
if is_gated "$tok"; then
|
||||
GATED=1
|
||||
GATED_HINT="$tok"
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
PATHS="$(printf '%s' "$INPUT" | jq -r '
|
||||
.tool_input as $t
|
||||
| [ $t.file_path, $t.notebook_path, $t.path, $t.destination, $t.source ]
|
||||
| map(select(type == "string" and length > 0))
|
||||
| .[]' 2>/dev/null)"
|
||||
[ -z "$PATHS" ] && exit 0
|
||||
while IFS= read -r p; do
|
||||
if is_gated "$p"; then
|
||||
GATED=1
|
||||
GATED_HINT="$p"
|
||||
break
|
||||
fi
|
||||
done <<EOP
|
||||
$PATHS
|
||||
EOP
|
||||
fi
|
||||
|
||||
[ "$GATED" -eq 0 ] && exit 0
|
||||
|
||||
# --- Resolve marker (fail closed if impossible). ------------------------------
|
||||
if [ -n "${SAP_CONSULT_MARKER:-}" ]; then
|
||||
MARKER="$SAP_CONSULT_MARKER"
|
||||
else
|
||||
if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "$CLAUDE_PROJECT_DIR" ]; then
|
||||
REPO="$CLAUDE_PROJECT_DIR"
|
||||
else
|
||||
REPO="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)" || REPO=""
|
||||
fi
|
||||
if [ -z "$REPO" ]; then
|
||||
deny "Ground-truth gate: cannot resolve the repository root (CLAUDE_PROJECT_DIR unset and script location unreadable), so the consultation marker cannot be checked. Failing closed."
|
||||
fi
|
||||
MARKER="$REPO/.claude/.last-mcp-consult"
|
||||
fi
|
||||
|
||||
# --- Freshness check: marker younger than 30 minutes satisfies the gate. -----
|
||||
if [ -f "$MARKER" ]; then
|
||||
LAST="$(cat "$MARKER" 2>/dev/null)"
|
||||
NOW="$(date +%s)"
|
||||
case "$LAST" in
|
||||
''|*[!0-9]*) : ;; # not a number -> treat as stale
|
||||
*)
|
||||
if [ $((NOW - LAST)) -lt 1800 ]; then
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Keep the hint JSON-safe (strip quotes/backslashes, truncate).
|
||||
SAFE_HINT="$(printf '%s' "$GATED_HINT" | tr -d '"\\' | cut -c1-160)"
|
||||
deny "Ground-truth gate: no grounding consultation recorded in the last 30 minutes, and this touches a SAP artifact ($SAFE_HINT). Consult the relevant server first (CDS/RAP/ABAP -> abap-docs or sap-docs; Fiori/annotations -> fiori-mcp-server; UI5 -> ui5-mcp-server; libraries -> context7). Only calls to those servers, WebSearch, or WebFetch refresh the gate; then retry."
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Prints ground-truth grounding context for SessionStart ("session") or
|
||||
# UserPromptSubmit ("prompt"). Stdout becomes model context. Always exits 0.
|
||||
|
||||
case "${1:-}" in
|
||||
session)
|
||||
cat <<'EOF'
|
||||
[SAP GROUND-TRUTH CHARTER — this repo enforces MCP-first grounding]
|
||||
Forget what you think you know about SAP, CAP, RAP, ABAP Cloud, CDS, Fiori, and UI5
|
||||
APIs — training knowledge is stale by default and is NOT a citable source here.
|
||||
Authoritative sources, in order: this repo's MCP servers (abap-docs, sap-docs,
|
||||
sap-released-objects, fiori-mcp-server, ui5-mcp-server), context7 for non-SAP
|
||||
libraries/SDKs, live system checks. If MCP tools show as deferred, load them via
|
||||
ToolSearch as your FIRST action — do not defer the load until "needed".
|
||||
Every SAP behavioral claim you record in PRD/ADR/KNOWLEDGE must carry a citation:
|
||||
[source: <server> <doc-id-or-URL>, verified YYYY-MM-DD]. Uncited = hypothesis.
|
||||
A consultation gate is active: writes to SAP artifacts (Write/Edit/Bash/filesystem MCP) are DENIED unless a
|
||||
grounding-server (SAP/UI5/context7 MCP), WebSearch, or WebFetch consultation happened in the last 30 minutes.
|
||||
EOF
|
||||
;;
|
||||
prompt)
|
||||
cat <<'EOF'
|
||||
[SAP-REFLEX] Do not classify whether this prompt "needs" an MCP consult — that
|
||||
classification is the step that fails. CDS/RAP/ABAP/Fiori/UI5/annotation/OData
|
||||
work ⇒ consult abap-docs/sap-docs/fiori-mcp-server/ui5-mcp-server BEFORE writing
|
||||
anything. Library/SDK questions ⇒ context7. Record SAP claims only with
|
||||
[source: <server> <id/URL>, verified YYYY-MM-DD].
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Executable
+118
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
# Test harness for the ADR-003 consultation gate hooks.
|
||||
# Pipes simulated Claude Code hook JSON into sap-consult-gate.sh and
|
||||
# mark-consult.sh with a temp marker (SAP_CONSULT_MARKER) so the real
|
||||
# .claude/.last-mcp-consult is never touched.
|
||||
#
|
||||
# Usage: bash .claude/hooks/tests/run.sh (exit 0 = all pass)
|
||||
|
||||
set -u
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
HOOKS="$(dirname "$HERE")"
|
||||
REPO="$(cd "$HOOKS/../.." && pwd)"
|
||||
GATE="$HOOKS/sap-consult-gate.sh"
|
||||
MARK="$HOOKS/mark-consult.sh"
|
||||
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
STALE="$TMP/stale-marker"
|
||||
FRESH="$TMP/fresh-marker"
|
||||
echo $(( $(date +%s) - 7200 )) > "$STALE"
|
||||
date +%s > "$FRESH"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
# gate MARKER JSON -> prints "deny" or "allow"; also asserts exit 0 + valid JSON.
|
||||
gate() {
|
||||
local marker="$1" json="$2" out rc
|
||||
out="$(printf '%s' "$json" | SAP_CONSULT_MARKER="$marker" CLAUDE_PROJECT_DIR="$REPO" bash "$GATE")"
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then echo "rc=$rc"; return; fi
|
||||
if [ -n "$out" ] && ! printf '%s' "$out" | jq -e . >/dev/null 2>&1; then echo "badjson"; return; fi
|
||||
if printf '%s' "$out" | jq -e '.hookSpecificOutput.permissionDecision == "deny"' >/dev/null 2>&1; then
|
||||
echo deny
|
||||
else
|
||||
echo allow
|
||||
fi
|
||||
}
|
||||
|
||||
expect() {
|
||||
local name="$1" want="$2" got="$3"
|
||||
if [ "$want" = "$got" ]; then
|
||||
PASS=$((PASS + 1)); echo "PASS $name [$got]"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo "FAIL $name [want $want, got $got]"
|
||||
fi
|
||||
}
|
||||
|
||||
j_edit() { printf '{"tool_name":"Edit","tool_input":{"file_path":"%s","old_string":"a","new_string":"b"}}' "$1"; }
|
||||
j_bash() { printf '{"tool_name":"Bash","tool_input":{"command":%s}}' "$(printf '%s' "$1" | jq -Rs .)"; }
|
||||
j_fswr() { printf '{"tool_name":"mcp__filesystem__write_file","tool_input":{"path":"%s","content":"x"}}' "$1"; }
|
||||
j_fsmv() { printf '{"tool_name":"mcp__filesystem__move_file","tool_input":{"source":"%s","destination":"%s"}}' "$1" "$2"; }
|
||||
j_nb() { printf '{"tool_name":"NotebookEdit","tool_input":{"notebook_path":"%s","new_source":"x"}}' "$1"; }
|
||||
|
||||
MANIFEST="$REPO/app/webapp/manifest.json"
|
||||
ANNOT="$REPO/app/webapp/annotations/annotation.xml"
|
||||
|
||||
echo "== sap-consult-gate.sh =="
|
||||
# --- Cases required by the brief ---------------------------------------------
|
||||
expect "Edit manifest.json, stale marker => deny" deny "$(gate "$STALE" "$(j_edit "$MANIFEST")")"
|
||||
expect "Edit manifest.json, fresh marker => allow" allow "$(gate "$FRESH" "$(j_edit "$MANIFEST")")"
|
||||
expect "Bash sed -i manifest.json, stale => deny" deny "$(gate "$STALE" "$(j_bash "sed -i 's/a/b/' app/webapp/manifest.json")")"
|
||||
expect "Bash cat manifest.json, stale => allow" allow "$(gate "$STALE" "$(j_bash "cat app/webapp/manifest.json")")"
|
||||
expect "Edit worker/src/x.ts, stale => allow" allow "$(gate "$STALE" "$(j_edit "$REPO/worker/src/x.ts")")"
|
||||
expect "Edit PRD.md, stale => allow" allow "$(gate "$STALE" "$(j_edit "$REPO/PRD.md")")"
|
||||
expect "fs write_file annotation.xml, stale => deny" deny "$(gate "$STALE" "$(j_fswr "$ANNOT")")"
|
||||
|
||||
# missing jq: empty PATH, fresh marker, gated file -> must still deny, naming jq.
|
||||
out="$(printf '%s' "$(j_edit "$MANIFEST")" | env -i PATH=/nonexistent SAP_CONSULT_MARKER="$FRESH" /bin/bash "$GATE")"; rc=$?
|
||||
if [ "$rc" -eq 0 ] && printf '%s' "$out" | jq -e '.hookSpecificOutput.permissionDecision == "deny" and (.hookSpecificOutput.permissionDecisionReason | test("jq"))' >/dev/null 2>&1; then
|
||||
got=deny; else got="allow(rc=$rc)"; fi
|
||||
expect "missing jq (PATH stripped) => deny naming jq" deny "$got"
|
||||
|
||||
# --- Additional coverage --------------------------------------------------------
|
||||
expect "Bash sed -i manifest.json, fresh => allow" allow "$(gate "$FRESH" "$(j_bash "sed -i 's/a/b/' app/webapp/manifest.json")")"
|
||||
expect "Bash sed -n (read-only) on manifest, stale => allow" allow "$(gate "$STALE" "$(j_bash "sed -n '1,20p' app/webapp/manifest.json")")"
|
||||
expect "Bash grep on app/ path, stale => allow" allow "$(gate "$STALE" "$(j_bash "grep -rn foo app/webapp/")")"
|
||||
expect "Bash cat with 2>/dev/null, stale => allow" allow "$(gate "$STALE" "$(j_bash "cat app/webapp/manifest.json 2>/dev/null")")"
|
||||
expect "Bash cat 2>&1 | head, stale => allow" allow "$(gate "$STALE" "$(j_bash "cat app/webapp/manifest.json 2>&1 | head -5")")"
|
||||
expect "Bash heredoc > .cds, stale => deny" deny "$(gate "$STALE" "$(j_bash "cat > srv/schema.cds <<'X'
|
||||
entity A {}
|
||||
X")")"
|
||||
expect "Bash echo >> manifest.json, stale => deny" deny "$(gate "$STALE" "$(j_bash "echo '{}' >> app/webapp/manifest.json")")"
|
||||
expect "Bash tee annotation.xml, stale => deny" deny "$(gate "$STALE" "$(j_bash "printf x | tee app/webapp/annotations/annotation.xml")")"
|
||||
expect "Bash cp into app/, stale => deny" deny "$(gate "$STALE" "$(j_bash "cp /tmp/x.json app/webapp/manifest.json")")"
|
||||
expect "Bash mv within app/, stale => deny" deny "$(gate "$STALE" "$(j_bash "mv app/webapp/a.xml app/webapp/b.xml")")"
|
||||
expect "Bash python -c writing .abap, stale => deny" deny "$(gate "$STALE" "$(j_bash "python3 -c 'open(\"src/zcl_x.clas.abap\",\"w\").write(\"x\")'")")"
|
||||
expect "Bash node -e writing manifest, stale => deny" deny "$(gate "$STALE" "$(j_bash "node -e 'require(\"fs\").writeFileSync(\"app/webapp/manifest.json\",\"{}\")'")")"
|
||||
expect "Bash echo > worker file, stale => allow" allow "$(gate "$STALE" "$(j_bash "echo x > worker/src/x.ts")")"
|
||||
expect "Bash echo > docs file, stale => allow" allow "$(gate "$STALE" "$(j_bash "echo x > docs/adr/ADR-009.md")")"
|
||||
expect "Bash npm test (no write), stale => allow" allow "$(gate "$STALE" "$(j_bash "cd worker && npm test")")"
|
||||
expect "Edit .bdef, stale => deny" deny "$(gate "$STALE" "$(j_edit "$REPO/src/zi_x.bdef")")"
|
||||
expect "Edit fragment.xml, stale => deny" deny "$(gate "$STALE" "$(j_edit "$REPO/app/webapp/ext/X.fragment.xml")")"
|
||||
expect "Edit app/README.md (exempt ext), stale => allow" allow "$(gate "$STALE" "$(j_edit "$REPO/app/README.md")")"
|
||||
expect "Edit .claude/hooks file, stale => allow" allow "$(gate "$STALE" "$(j_edit "$REPO/.claude/hooks/x.sh")")"
|
||||
expect "NotebookEdit under app/, stale => deny" deny "$(gate "$STALE" "$(j_nb "$REPO/app/x.ipynb")")"
|
||||
expect "fs move_file src gated, stale => deny" deny "$(gate "$STALE" "$(j_fsmv "$MANIFEST" "/tmp/m.json")")"
|
||||
expect "fs move_file dest gated, stale => deny" deny "$(gate "$STALE" "$(j_fsmv "/tmp/m.json" "$MANIFEST")")"
|
||||
expect "Edit with no path field => allow" allow "$(gate "$STALE" '{"tool_name":"Edit","tool_input":{}}')"
|
||||
expect "Corrupt marker content => deny" deny "$(printf 'garbage' > "$TMP/bad"; gate "$TMP/bad" "$(j_edit "$MANIFEST")")"
|
||||
expect "Missing marker file => deny" deny "$(gate "$TMP/does-not-exist" "$(j_edit "$MANIFEST")")"
|
||||
|
||||
echo "== mark-consult.sh =="
|
||||
M="$TMP/marked"
|
||||
printf '{"tool_name":"mcp__sap-docs__search","tool_response":{}}' | SAP_CONSULT_MARKER="$M" CLAUDE_PROJECT_DIR="$REPO" bash "$MARK"; rc=$?
|
||||
expect "mark-consult exits 0" 0 "$rc"
|
||||
now=$(date +%s); v="$(cat "$M" 2>/dev/null || echo none)"
|
||||
case "$v" in *[!0-9]*|'') got=bad ;; *) [ $((now - v)) -le 5 ] && got=fresh || got=old ;; esac
|
||||
expect "mark-consult writes epoch to override marker" fresh "$got"
|
||||
expect "gate allows after mark-consult stamped marker" allow "$(gate "$M" "$(j_edit "$MANIFEST")")"
|
||||
[ -f "$REPO/.claude/.last-mcp-consult" ] && before="$(cat "$REPO/.claude/.last-mcp-consult")" || before="absent"
|
||||
[ -f "$REPO/.claude/.last-mcp-consult" ] && after="$(cat "$REPO/.claude/.last-mcp-consult")" || after="absent"
|
||||
expect "real marker untouched by harness" "$before" "$after"
|
||||
|
||||
echo
|
||||
echo "passed=$PASS failed=$FAIL"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"sap-cap-capire@sap-skills": true,
|
||||
"sap-fiori-tools@sap-skills": true,
|
||||
"sap-fiori-mcp-server@claude-plugins-official": true,
|
||||
"ui5@claude-plugins-official": true
|
||||
},
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/sap-grounding.sh session",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/sap-grounding.sh prompt",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "^(mcp__(sap-docs|abap-docs|sap-released-objects|fiori-mcp-server|ui5-mcp-server|ui5-webcomponents-mcp-server|plugin_sap-fiori-mcp-server_fiori-mcp|plugin_sap-fiori-tools_fiori-tools|plugin_ui5_ui5-mcp-server|plugin_context7_context7|plugin_sap-cap-capire_sap-cap-capire)__.*|WebSearch|WebFetch)$",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/mark-consult.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Write|Edit|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "jq -r '.tool_input.file_path // empty' | grep -qE '(PRD|KNOWLEDGE|CHANGELOG|ADR).*\\.md$' && echo 'Reminder: any SAP behavioral claim added to governance docs must carry [source: <server> <id/URL>, verified YYYY-MM-DD].' || true",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash|mcp__filesystem__write_file|mcp__filesystem__edit_file|mcp__filesystem__move_file",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/sap-consult-gate.sh",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: openspec-apply-change
|
||||
description: Implement the tasks from a change. Use when the user wants to start implementing, continue implementation, or work through the tasks of an existing change.
|
||||
license: MIT
|
||||
compatibility: Claude Code — no external CLI required.
|
||||
metadata:
|
||||
author: openspec (adapted for Claude Code)
|
||||
version: "1.0-claude"
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
---
|
||||
|
||||
Implement the tasks from a change.
|
||||
|
||||
> **Adapted for Claude Code.** No `openspec` CLI. A change is a folder under
|
||||
> `openspec/changes/<name>/`; tasks live in `tasks.md` as a Markdown checklist. Read the
|
||||
> artifacts directly, implement, and flip `- [ ]` → `- [x]` with the Edit tool.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, infer from conversation context; if ambiguous, prompt.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise list active changes:
|
||||
|
||||
```bash
|
||||
ls -1 openspec/changes 2>/dev/null | grep -v '^archive$'
|
||||
```
|
||||
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, use the **AskUserQuestion tool** to let the user choose
|
||||
|
||||
Always announce: "Using change: <name>".
|
||||
|
||||
2. **Read the change artifacts**
|
||||
|
||||
Read every artifact that exists in `openspec/changes/<name>/`:
|
||||
- `proposal.md` (what & why)
|
||||
- `design.md` (how)
|
||||
- `tasks.md` (the checklist you will execute)
|
||||
|
||||
If `tasks.md` is missing, the change is not ready to implement — suggest the
|
||||
`openspec-propose` skill (or creating `tasks.md`) and stop.
|
||||
|
||||
3. **Assess progress**
|
||||
|
||||
Count tasks in `tasks.md`:
|
||||
- Incomplete = lines matching `- [ ]`
|
||||
- Complete = lines matching `- [x]`
|
||||
|
||||
Display: change name, "N/M tasks complete", and a short overview of what remains.
|
||||
If all tasks are already complete, congratulate and suggest archiving
|
||||
(the `openspec-archive-change` skill).
|
||||
|
||||
4. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
Mirror `tasks.md` into your task-tracking tool (`TaskCreate` / `TaskUpdate`) so progress
|
||||
is visible. For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required — keep them minimal and focused on that task
|
||||
- **For SAP code, consult the relevant MCP server first** (`sap-cap`, `sap-fiori`,
|
||||
`sap-ui5` skills) — never guess CDS/Fiori/UI5 behavior
|
||||
- For non-trivial implementation, dispatch to the appropriate specialist sub-agent via
|
||||
the **Agent tool** rather than writing large code blocks inline
|
||||
- Mark the task complete by editing `tasks.md`: `- [ ]` → `- [x]`
|
||||
- Continue to the next task
|
||||
|
||||
**Pause if:**
|
||||
- A task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating `design.md`/`proposal.md`
|
||||
- An error or blocker is encountered → report and wait for guidance
|
||||
- The user interrupts
|
||||
|
||||
5. **On completion or pause, show status**
|
||||
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archiving (`openspec-archive-change`)
|
||||
- If paused: explain why and wait
|
||||
|
||||
**Output During Implementation**
|
||||
```
|
||||
## Implementing: <change-name>
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
All tasks complete! Ready to archive this change (openspec-archive-change).
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read the artifacts before starting
|
||||
- If a task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update the `tasks.md` checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements — don't guess
|
||||
- Consult MCP/skills for SAP code; orchestrate via sub-agents for substantial work
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed change after implementation. Use when the user wants to finalize and archive a change once its tasks are done.
|
||||
license: MIT
|
||||
compatibility: Claude Code — no external CLI required.
|
||||
metadata:
|
||||
author: openspec (adapted for Claude Code)
|
||||
version: "1.0-claude"
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
---
|
||||
|
||||
Archive a completed change.
|
||||
|
||||
> **Adapted for Claude Code.** No `openspec` CLI. Archiving is a plain directory move:
|
||||
> `openspec/changes/<name>/` → `openspec/changes/archive/YYYY-MM-DD-<name>/`.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, prompt for selection.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
List active changes and let the user choose with the **AskUserQuestion tool**:
|
||||
|
||||
```bash
|
||||
ls -1 openspec/changes 2>/dev/null | grep -v '^archive$'
|
||||
```
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select. Always let the user choose (unless they
|
||||
already named the change in their request).
|
||||
|
||||
2. **Check task completion**
|
||||
|
||||
Read `openspec/changes/<name>/tasks.md` and count incomplete tasks (`- [ ]`) vs
|
||||
complete (`- [x]`).
|
||||
|
||||
- **If incomplete tasks exist:** show the count and use the **AskUserQuestion tool** to
|
||||
confirm the user still wants to archive. Proceed only if confirmed.
|
||||
- **If no `tasks.md` exists:** proceed without a task warning.
|
||||
|
||||
3. **Sync any delta specs (optional)**
|
||||
|
||||
If the change captured spec deltas under `openspec/changes/<name>/specs/`, compare each
|
||||
with its main spec at `openspec/specs/<capability>/spec.md` and offer to apply the
|
||||
changes (read both, then Edit the main spec). Options to present:
|
||||
- "Sync now (recommended)" / "Archive without syncing"
|
||||
|
||||
If there are no delta specs, skip this step.
|
||||
|
||||
4. **Perform the archive**
|
||||
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
```
|
||||
|
||||
Determine today's date (`date +%F` → `YYYY-MM-DD`) and build the target name
|
||||
`YYYY-MM-DD-<name>`.
|
||||
|
||||
- **If the target already exists:** stop with an error; suggest a different date suffix
|
||||
or renaming the existing archive.
|
||||
- **Otherwise:** move the change folder:
|
||||
|
||||
```bash
|
||||
mv openspec/changes/<name> openspec/changes/archive/<YYYY-MM-DD>-<name>
|
||||
```
|
||||
|
||||
5. **Display summary**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" / "Sync skipped")
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Don't block archive on warnings — inform, confirm, then proceed
|
||||
- Never overwrite an existing archive target
|
||||
- Show a clear summary of what happened
|
||||
- If delta specs exist, run the sync assessment and show the summary before prompting
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
name: openspec-explore
|
||||
description: Enter explore mode — a thinking partner for exploring ideas, investigating problems, and clarifying requirements before or during a change. Read-only; never implements code. Use when the user wants to think through something before committing to a change.
|
||||
license: MIT
|
||||
compatibility: Claude Code — no external CLI required.
|
||||
metadata:
|
||||
author: openspec (adapted for Claude Code)
|
||||
version: "1.0-claude"
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
> **Adapted for Claude Code.** This skill uses a plain `openspec/changes/` directory of
|
||||
> Markdown files instead of the `openspec` CLI. A "change" is just a folder; there is
|
||||
> nothing to install and no CLI to run — read/write the files directly.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal (use the `openspec-propose` skill). You MAY create change artifacts (proposal/design/specs) if the user asks — that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## Change Awareness (CLI-free)
|
||||
|
||||
Use the `openspec/changes/` directory naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly see what already exists by listing the change folders:
|
||||
|
||||
```bash
|
||||
ls -1 openspec/changes 2>/dev/null | grep -v '^archive$'
|
||||
```
|
||||
|
||||
Each subfolder (excluding `archive/`) is an active change. If the directory does not
|
||||
exist, there are no active changes yet — that's fine.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?" (the `openspec-propose` skill)
|
||||
- Or keep exploring — no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using HANA, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to the RiskAnalyst role, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|----------------------------------------|
|
||||
| New requirement discovered | `openspec/specs/<capability>/spec.md` |
|
||||
| Requirement changed | `openspec/specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? I can create a change proposal." (`openspec-propose`)
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When it feels like things are crystallizing, you might summarize:
|
||||
|
||||
```
|
||||
## What We Figured Out
|
||||
|
||||
**The problem**: [crystallized understanding]
|
||||
**The approach**: [if one emerged]
|
||||
**Open questions**: [if any remain]
|
||||
**Next steps** (if ready): create a change proposal, or keep exploring
|
||||
```
|
||||
|
||||
But this summary is optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating change artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
- **For SAP work** - When exploring CDS, Fiori, or UI5, still consult the relevant MCP server (`sap-cap`, `sap-fiori`, `sap-ui5` skills) rather than relying on training knowledge.
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: openspec-propose
|
||||
description: Propose a new change and generate all artifacts (proposal, design, tasks) in one step, ready for implementation. Use when the user wants to describe what they want to build and get a complete change proposal.
|
||||
license: MIT
|
||||
compatibility: Claude Code — no external CLI required.
|
||||
metadata:
|
||||
author: openspec (adapted for Claude Code)
|
||||
version: "1.0-claude"
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
---
|
||||
|
||||
Propose a new change — create the change folder and generate all artifacts in one step.
|
||||
|
||||
> **Adapted for Claude Code.** No `openspec` CLI. A change is a folder under
|
||||
> `openspec/changes/<name>/` containing three Markdown files you create directly with
|
||||
> the Write tool. Templates are embedded below.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- `proposal.md` (what & why)
|
||||
- `design.md` (how)
|
||||
- `tasks.md` (implementation steps)
|
||||
|
||||
When ready to implement, use the `openspec-apply-change` skill.
|
||||
|
||||
---
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no clear input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
|
||||
```bash
|
||||
mkdir -p openspec/changes/<name>
|
||||
```
|
||||
|
||||
If a folder with that name already exists, ask whether to continue the existing change
|
||||
(use `openspec-apply-change`) or pick a new name.
|
||||
|
||||
3. **Track progress**
|
||||
|
||||
Use your task-tracking tool (`TaskCreate` / `TaskUpdate`) to track the three artifacts.
|
||||
|
||||
4. **Create the artifacts in order: proposal → design → tasks**
|
||||
|
||||
For each artifact, read the previously created artifact(s) for context, then Write the
|
||||
file using the templates below. Apply project context and conventions as constraints —
|
||||
do NOT paste meta-instructions into the files.
|
||||
|
||||
For SAP artifacts, **consult the relevant MCP server before writing design or tasks**
|
||||
that touch CDS, Fiori, or UI5 (see the `sap-cap`, `sap-fiori`, `sap-ui5` skills). Don't
|
||||
guess SAP behavior into a design.
|
||||
|
||||
**`proposal.md` template**
|
||||
```markdown
|
||||
# <Change Title>
|
||||
|
||||
## Why
|
||||
<the problem or motivation — what's broken or missing, and why now>
|
||||
|
||||
## What Changes
|
||||
<high-level summary of the change in 2–5 bullets>
|
||||
|
||||
## Scope
|
||||
- In scope: <...>
|
||||
- Out of scope: <...>
|
||||
|
||||
## Impact
|
||||
<affected capabilities, entities, services, UIs, integrations>
|
||||
```
|
||||
|
||||
**`design.md` template**
|
||||
```markdown
|
||||
# Design: <Change Title>
|
||||
|
||||
## Approach
|
||||
<how it will be built>
|
||||
|
||||
## Architecture / Data Flow
|
||||
<entities, service handlers, annotations, UI; ASCII diagram if helpful>
|
||||
|
||||
## Decisions
|
||||
- <decision> — <rationale>
|
||||
|
||||
## Alternatives Considered
|
||||
- <alternative> — <why not>
|
||||
|
||||
## Risks & Open Questions
|
||||
- <...>
|
||||
```
|
||||
|
||||
**`tasks.md` template**
|
||||
```markdown
|
||||
# Tasks: <Change Title>
|
||||
|
||||
- [ ] 1. <task>
|
||||
- [ ] 2. <task>
|
||||
- [ ] 3. <task>
|
||||
```
|
||||
|
||||
5. **Verify and show final status**
|
||||
|
||||
Confirm all three files exist, then list them.
|
||||
|
||||
```bash
|
||||
ls -1 openspec/changes/<name>
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location (`openspec/changes/<name>/`)
|
||||
- The artifacts created with brief descriptions
|
||||
- "All artifacts created! Ready for implementation."
|
||||
- "Use the `openspec-apply-change` skill (or ask me to implement) to start working on the tasks."
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL three artifacts (proposal, design, tasks) before declaring the change apply-ready
|
||||
- Always read dependency artifacts before creating the next one
|
||||
- If context is critically unclear, ask the user — but prefer reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask whether to continue it or create a new one
|
||||
- Verify each file exists after writing before proceeding to the next
|
||||
- `context`/`rules` are constraints for YOU — never copy them into the artifact files
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: sap-cap
|
||||
description: >
|
||||
Query the CAP MCP server before writing CDS entities, types, aspects, services,
|
||||
or CAP service handlers. Triggers on schema and service files in db/ and srv/.
|
||||
paths:
|
||||
- "**/db/**/*.cds"
|
||||
- "**/srv/**/*.cds"
|
||||
- "**/srv/**/*.js"
|
||||
metadata:
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
adapted: MCP tool names mapped to this environment (mcp__cap-mcp-server__*)
|
||||
---
|
||||
|
||||
# CAP MCP-First Development
|
||||
|
||||
Before writing, modifying, debugging, or fixing CDS entities, service definitions,
|
||||
or CAP service handlers, you MUST query the CAP MCP server and apply its guidance.
|
||||
|
||||
## Tools
|
||||
|
||||
Use `mcp__cap-mcp-server__*` tools — primarily `mcp__cap-mcp-server__search_docs` and `mcp__cap-mcp-server__search_model`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Query before coding** — call `mcp__cap-mcp-server__search_docs` for syntax, patterns, or conventions
|
||||
2. **Trust MCP over training knowledge** — if guidance conflicts with what you know, follow MCP
|
||||
3. **Prefer the simplest pattern** when MCP returns multiple valid approaches
|
||||
|
||||
## Scope
|
||||
|
||||
This skill covers:
|
||||
- CDS entity definitions, types, aspects (`db/schema.cds`)
|
||||
- Service definitions and projections (`srv/*.cds`)
|
||||
- Service handlers, actions, functions, events (`srv/*.js`)
|
||||
- CDS built-in types, key patterns, virtual fields
|
||||
|
||||
This skill does **not** cover CDS annotations (`@UI`, `@Common`, `@Capabilities`) or
|
||||
Fiori Elements configuration — those belong to the `sap-fiori` skill.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: sap-fiori
|
||||
description: >
|
||||
Query the Fiori MCP server before writing CDS annotations, Fiori Elements
|
||||
config, or manifest.json. Triggers on annotation files in app/ and manifests.
|
||||
paths:
|
||||
- "**/app/**/*.cds"
|
||||
- "**/app/**/manifest.json"
|
||||
metadata:
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
adapted: MCP tool names mapped to this environment (mcp__fiori-mcp-server__*)
|
||||
---
|
||||
|
||||
# Fiori MCP-First Development
|
||||
|
||||
Before writing, modifying, debugging, or fixing CDS annotations, Fiori Elements
|
||||
page configuration, or manifest.json, you MUST query the Fiori MCP server and apply its guidance.
|
||||
|
||||
## Tools
|
||||
|
||||
Use `mcp__fiori-mcp-server__*` tools — primarily `mcp__fiori-mcp-server__search_docs`,
|
||||
`mcp__fiori-mcp-server__list_functionality`, `mcp__fiori-mcp-server__get_functionality_details`,
|
||||
and `mcp__fiori-mcp-server__execute_functionality`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Query before coding** — call `mcp__fiori-mcp-server__search_docs` for annotation patterns, floorplan config, or manifest structure
|
||||
2. **Trust MCP over training knowledge** — if guidance conflicts with what you know, follow MCP
|
||||
3. **Use Fiori MCP tooling for modifications** — before manually editing manifest.json or annotations, check if `mcp__fiori-mcp-server__list_functionality` provides an automated function
|
||||
4. **Prefer the simplest pattern** when MCP returns multiple valid approaches
|
||||
|
||||
## Scope
|
||||
|
||||
This skill covers:
|
||||
- CDS annotations: `@UI`, `@Common`, `@Capabilities`, `@Communication`
|
||||
- Fiori Elements floorplans: List Report, Object Page, Analytical List Page
|
||||
- `manifest.json` configuration: routing, data sources, control configuration
|
||||
- Custom actions in manifest (`controlConfiguration.actions`)
|
||||
|
||||
## Fiori Elements App Rules
|
||||
|
||||
When creating or modifying SAP Fiori Elements applications:
|
||||
|
||||
- The application typically starts with a **List Report** page showing the base entity in a table. Row details are shown in an **ObjectPage** based on the same entity.
|
||||
- An ObjectPage can contain table sections based on to-many associations. Row details in a table section can open another ObjectPage based on the association's target entity.
|
||||
- The data model must have one main entity with navigation properties to related entities.
|
||||
- Each property must have a proper datatype.
|
||||
- For all entities, provide primary keys of type **UUID**.
|
||||
- When creating sample data in CSV files, all primary keys and foreign keys **MUST** be in UUID format (e.g., `550e8400-e29b-41d4-a716-446655440001`).
|
||||
- When modifying the application (e.g., adding columns), do **not** use screen personalization — modify the project code. Always check whether `mcp__fiori-mcp-server__list_functionality` provides a suitable function first.
|
||||
- When previewing, use the most specific `npm run watch-*` script for the app in `package.json`.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: sap-ui5
|
||||
description: >
|
||||
Query the UI5 MCP server before writing SAPUI5 controllers, XML views, control
|
||||
APIs, or model bindings. Triggers on views and controller extensions in app/.
|
||||
paths:
|
||||
- "**/app/**/*.xml"
|
||||
- "**/app/**/ext/**/*.js"
|
||||
- "**/app/**/ext/**/*.ts"
|
||||
- "**/webapp/**/*.xml"
|
||||
- "**/webapp/**/controller/**/*.js"
|
||||
metadata:
|
||||
source: SAP-samples/cap-agentic-engineered
|
||||
adapted: MCP tool names mapped to this environment (mcp__ui5-mcp-server__*)
|
||||
---
|
||||
|
||||
# UI5 MCP-First Development
|
||||
|
||||
Before writing, modifying, debugging, or fixing SAPUI5 controllers, XML views,
|
||||
or control bindings, you MUST query the UI5 MCP server and apply its guidance.
|
||||
|
||||
## Tools
|
||||
|
||||
Use `mcp__ui5-mcp-server__*` tools — primarily `mcp__ui5-mcp-server__get_api_reference`,
|
||||
`mcp__ui5-mcp-server__get_guidelines`, `mcp__ui5-mcp-server__run_ui5_linter`, and
|
||||
`mcp__ui5-mcp-server__get_project_info`.
|
||||
|
||||
> Note: in this environment the same UI5 tooling is also exposed under the
|
||||
> `mcp__plugin_sapui5_ui5-tooling__*` namespace — either prefix works.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Query before coding** — call `mcp__ui5-mcp-server__get_api_reference` for control APIs, or `mcp__ui5-mcp-server__get_guidelines` for project-level patterns
|
||||
2. **Trust MCP over training knowledge** — if guidance conflicts with what you know, follow MCP
|
||||
3. **Run the linter after changes** — call `mcp__ui5-mcp-server__run_ui5_linter` to catch deprecated APIs
|
||||
4. **Prefer the simplest pattern** when MCP returns multiple valid approaches
|
||||
|
||||
## Scope
|
||||
|
||||
This skill covers:
|
||||
- XML views with `sap.m`, `sap.f`, `sap.uxap` namespaces
|
||||
- Controller extensions (`app/**/ext/controller/*.js`) and freestyle controllers (`webapp/**/controller/*.js`)
|
||||
- Model bindings, formatters, event handlers
|
||||
- Control APIs and property configuration
|
||||
|
||||
## UI5 Code Standards
|
||||
|
||||
- XML views only — no JavaScript views
|
||||
- `sap.ui.define` for all modules — no globals
|
||||
- Async loading: `data-sap-ui-async="true"`
|
||||
- i18n for all user-facing text
|
||||
- No deprecated APIs (`jQuery.sap.*`, sync loading, `sap.ui.getCore()`)
|
||||
- Fiori Elements controller extensions must be plain objects (not ES6 classes) — `this` refers to the `ExtensionAPI` instance
|
||||
Reference in New Issue
Block a user