@cspeach/cli 0.9.0 → 1.1.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/README.md +1 -1
- package/dist/agent/intent-system-prompt.js +1 -1
- package/dist/agent/loop.js +228 -26
- package/dist/agent/providers/license-gate.js +44 -0
- package/dist/agent/skill-checkpoint.js +1 -1
- package/dist/agent/tool-dispatch.js +15 -0
- package/dist/approvals/canonical.js +91 -0
- package/dist/approvals/jwt.js +39 -2
- package/dist/approvals/op-labels.js +124 -0
- package/dist/approvals/render.js +42 -36
- package/dist/auth/org-anthropic-key.js +25 -0
- package/dist/classifier/client.js +18 -3
- package/dist/cli.js +15 -0
- package/dist/commands/compact.js +28 -2
- package/dist/commands/config-set.js +284 -0
- package/dist/commands/config-show.js +20 -0
- package/dist/commands/export-audit.js +43 -0
- package/dist/commands/help.js +5 -0
- package/dist/commands/login.js +31 -14
- package/dist/commands/plan-audit-evidence.js +266 -0
- package/dist/commands/plan-audit.js +692 -0
- package/dist/commands/plan-chain.js +671 -0
- package/dist/commands/plan-continue.js +179 -0
- package/dist/commands/plan-gate.js +154 -0
- package/dist/commands/plan-model-tier.js +83 -0
- package/dist/commands/plan-resume.js +728 -46
- package/dist/config/loader.js +223 -5
- package/dist/config/model-defaults.js +14 -0
- package/dist/cost/pricing.js +27 -1
- package/dist/doctor/checks/_http-probe.js +1 -0
- package/dist/doctor/checks/cert.js +14 -3
- package/dist/doctor/checks/sap.js +30 -8
- package/dist/doctor/checks/system-roles.js +41 -0
- package/dist/doctor/checks/zcspeach.js +19 -4
- package/dist/doctor/run.js +2 -0
- package/dist/models/resolve.js +61 -0
- package/dist/models/server-config.js +155 -0
- package/dist/one-shot.js +76 -6
- package/dist/projects/answer-blockers.js +137 -0
- package/dist/projects/extract-cca.js +111 -17
- package/dist/projects/extract-modernize.js +4 -2
- package/dist/projects/extract-plan.js +184 -37
- package/dist/projects/extract-spec-gap.js +34 -7
- package/dist/projects/extract-test-coverage.js +4 -2
- package/dist/projects/extract-upgrade.js +116 -23
- package/dist/projects/handover-md.js +195 -0
- package/dist/projects/index.js +5 -2
- package/dist/projects/merge-cca.js +292 -0
- package/dist/projects/merge-upgrade.js +173 -0
- package/dist/projects/migration.js +103 -1
- package/dist/projects/output-paths.js +27 -0
- package/dist/projects/plan-run.js +285 -27
- package/dist/projects/plan-schema.js +136 -3
- package/dist/projects/promote-command.js +25 -2
- package/dist/projects/promote.js +128 -0
- package/dist/projects/run-lease.js +157 -0
- package/dist/projects/save-command.js +259 -21
- package/dist/projects/status.js +3 -1
- package/dist/projects/validate.js +1 -1
- package/dist/projects/workspace.js +164 -20
- package/dist/renderer/notices.js +64 -0
- package/dist/renderer/progress-chatter.js +8 -0
- package/dist/renderer/status-footer.js +22 -12
- package/dist/renderer/thinking-heartbeat.js +64 -8
- package/dist/renderer/todo-block.js +51 -0
- package/dist/renderer/tool-widget.js +55 -4
- package/dist/renderer/tty.js +43 -4
- package/dist/renderer/verify-chain.js +77 -0
- package/dist/repl/at-picker.js +60 -7
- package/dist/repl/bracketed-paste.js +28 -19
- package/dist/repl/builtin-commands.js +42 -0
- package/dist/repl/current-transport.js +10 -0
- package/dist/repl/early-line-buffer.js +68 -0
- package/dist/repl/history.js +86 -0
- package/dist/repl/ink-stdin-guard.js +64 -0
- package/dist/repl/inquirer-guard.js +70 -5
- package/dist/repl/mode-ceiling.js +16 -0
- package/dist/repl/mode-cycle.js +104 -0
- package/dist/repl/numbered-menu.js +131 -0
- package/dist/repl/post-turn-status.js +26 -6
- package/dist/repl/rule8-detector.js +17 -2
- package/dist/repl/safety-confirm.js +111 -2
- package/dist/repl/safety-mode-state.js +19 -3
- package/dist/repl/slash-completer.js +5 -0
- package/dist/repl/slash-picker.js +10 -15
- package/dist/repl.js +1232 -95
- package/dist/rewind/candidates.js +194 -0
- package/dist/rewind/cli.js +137 -0
- package/dist/rewind/format.js +27 -0
- package/dist/rewind/restore.js +245 -0
- package/dist/router/classifier.js +150 -6
- package/dist/sap/capability-matrix.js +20 -0
- package/dist/sap/capability-matrix.json +11236 -0
- package/dist/sap/capability.js +146 -0
- package/dist/sap/connection-manager.js +19 -1
- package/dist/sap/onboarding.js +42 -4
- package/dist/session/audit-export.js +459 -0
- package/dist/session/context-report.js +163 -0
- package/dist/session/pending.js +27 -0
- package/dist/session/recap.js +160 -0
- package/dist/skill-catalog.js +51 -40
- package/dist/skills/bundled-skills.js +272 -1
- package/dist/skills/promotion-dispatch.js +23 -0
- package/dist/tools/_command-shared.js +36 -12
- package/dist/tools/_filesystem-shared.js +139 -4
- package/dist/tools/_flag.js +25 -0
- package/dist/tools/approval.js +177 -26
- package/dist/tools/ask-question.js +400 -7
- package/dist/tools/capability/tool.js +74 -0
- package/dist/tools/dispatch-skill.js +22 -1
- package/dist/tools/extend-model/anchored-insert.js +1414 -0
- package/dist/tools/extend-model/tool.js +340 -0
- package/dist/tools/filesystem/extract-document.js +57 -0
- package/dist/tools/filesystem/file-edit.js +12 -2
- package/dist/tools/filesystem/file-read.js +2 -2
- package/dist/tools/filesystem/file-write.js +11 -2
- package/dist/tools/filesystem/glob.js +11 -0
- package/dist/tools/filesystem/grep.js +10 -0
- package/dist/tools/filesystem/read-document.js +107 -0
- package/dist/tools/fiori/apply.js +50 -0
- package/dist/tools/fiori/bin.js +3 -0
- package/dist/tools/fiori/catalog/index.js +27 -0
- package/dist/tools/fiori/catalog/value-help.js +230 -0
- package/dist/tools/fiori/catalog/viz-chart.js +177 -0
- package/dist/tools/fiori/cli.js +71 -0
- package/dist/tools/fiori/deploy-config.js +73 -0
- package/dist/tools/fiori/fe-extend.js +76 -0
- package/dist/tools/fiori/fe-scaffold.js +71 -0
- package/dist/tools/fiori/floorplan-map.js +19 -0
- package/dist/tools/fiori/i18n.js +39 -0
- package/dist/tools/fiori/manifest.js +70 -0
- package/dist/tools/fiori/render.js +77 -0
- package/dist/tools/fiori/samples/data/index.json +13602 -0
- package/dist/tools/fiori/samples/data/sources.generated.js +808 -0
- package/dist/tools/fiori/samples/loader.js +248 -0
- package/dist/tools/fiori/samples/search.js +63 -0
- package/dist/tools/fiori/samples/types.js +2 -0
- package/dist/tools/fiori/scaffold.js +39 -0
- package/dist/tools/fiori/smoke/assertions.js +74 -0
- package/dist/tools/fiori/smoke/browser.js +52 -0
- package/dist/tools/fiori/smoke/driver.js +89 -0
- package/dist/tools/fiori/smoke/freestyle-spec.js +317 -0
- package/dist/tools/fiori/smoke/run-smoke.js +149 -0
- package/dist/tools/fiori/tools.js +681 -0
- package/dist/tools/fiori/types.js +1 -0
- package/dist/tools/local-build.js +86 -0
- package/dist/tools/local-files.js +31 -0
- package/dist/tools/project/_merge-shared.js +68 -0
- package/dist/tools/project/cca_merge.js +164 -0
- package/dist/tools/project/playbook_get.js +1 -1
- package/dist/tools/project/upgrade_merge_progress.js +206 -0
- package/dist/tools/sap-read.js +132 -20
- package/dist/tools/sap-write.js +550 -21
- package/dist/tools/shell/shell_exec.js +41 -6
- package/dist/tools/snapshot.js +63 -14
- package/dist/tools/subagent/agent_run.js +27 -3
- package/dist/tools/subagent/background_run.js +17 -1
- package/dist/tools/todo.js +144 -0
- package/dist/tools/transport-resolution.js +86 -0
- package/dist/tools/transport.js +224 -5
- package/dist/tools/write-mode.js +4 -0
- package/dist/ui/app.js +378 -21
- package/dist/ui/approval-modal.js +49 -16
- package/dist/ui/ask-question-emitter.js +14 -0
- package/dist/ui/body.js +13 -0
- package/dist/ui/context-grid.js +108 -0
- package/dist/ui/footer.js +120 -27
- package/dist/ui/header.js +7 -0
- package/dist/ui/line-resolution.js +35 -8
- package/dist/ui/rewind-emitter.js +10 -0
- package/dist/ui/rewind-panel.js +81 -0
- package/dist/ui/sap-state-store.js +1 -0
- package/dist/ui/session-timeline.js +1 -0
- package/dist/ui/status-line.js +43 -0
- package/dist/ui/text-input.js +214 -0
- package/dist/ui/todo-emitter.js +25 -0
- package/dist/ui/todo-panel.js +64 -0
- package/dist/ui/turn-status-emitter.js +50 -4
- package/dist/ui/turn-status.js +18 -3
- package/dist/ui/widgets/ask-form.js +242 -0
- package/dist/ui/widgets/ask-question-modal.js +21 -8
- package/package.json +22 -3
- package/bench/README.md +0 -78
- package/bench/prompts/abap-document-cds.md +0 -44
- package/bench/prompts/abap-explain-bdef-handler.md +0 -57
- package/bench/prompts/abap-test-method.md +0 -42
- package/bench/results/abap-document-cds/claude-haiku-4-5.md +0 -189
- package/bench/results/abap-document-cds/claude-opus-4-7.md +0 -120
- package/bench/results/abap-document-cds/claude-sonnet-4-6.md +0 -151
- package/bench/results/abap-explain-bdef-handler/claude-haiku-4-5.md +0 -112
- package/bench/results/abap-explain-bdef-handler/claude-opus-4-7.md +0 -101
- package/bench/results/abap-explain-bdef-handler/claude-sonnet-4-6.md +0 -101
- package/bench/results/abap-test-method/claude-haiku-4-5.md +0 -186
- package/bench/results/abap-test-method/claude-opus-4-7.md +0 -193
- package/bench/results/abap-test-method/claude-sonnet-4-6.md +0 -234
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// cspeach-cli/src/session/recap.ts
|
|
2
|
+
//
|
|
3
|
+
// UX Wave 2 / Task 6 — session recap: three lines that re-orient a developer
|
|
4
|
+
// who just resumed (or typed /recap) without scrolling back through the
|
|
5
|
+
// transcript.
|
|
6
|
+
//
|
|
7
|
+
// ↩ last session: /abap-generate · 4 turns · 3h ago · "add a validation…"
|
|
8
|
+
// ▶ tasks: 2 done / 3 open — next: write the BDEF validation
|
|
9
|
+
// ▷ plan Maintenance Requests: phase 3/7 validated — next: /abap-plan --resume @…
|
|
10
|
+
//
|
|
11
|
+
// Line 1 always prints (unless the session has zero user turns → recap is
|
|
12
|
+
// empty and callers print nothing). Line 2 only when the session's todo list
|
|
13
|
+
// has non-completed entries. Line 3 only when the NEWEST plan family in the
|
|
14
|
+
// workspace still has unsatisfied phases (audit-aware, same predicates the
|
|
15
|
+
// plan DAG uses).
|
|
16
|
+
//
|
|
17
|
+
// PURE except the plan-envelope reads: no writes, no prompts, never throws —
|
|
18
|
+
// a bad/missing workspace or a corrupt envelope silently drops line 3. The
|
|
19
|
+
// workspace dir is injectable (opts.workspaceDir) so call sites can pass the
|
|
20
|
+
// config-resolved path (resolveWorkspacePath) and tests can point at a temp
|
|
21
|
+
// dir; the fallback is the project-rooted default for `cwd`.
|
|
22
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
23
|
+
import { basename, join } from 'node:path';
|
|
24
|
+
import { formatTimeAgo } from './time-ago.js';
|
|
25
|
+
import { readProjectFile } from '../projects/status.js';
|
|
26
|
+
import { parsePlanContent } from '../projects/plan-schema.js';
|
|
27
|
+
import { statusesFromItems, isPhaseSatisfied } from '../projects/plan-run.js';
|
|
28
|
+
import { defaultWorkspaceFor } from '../projects/workspace.js';
|
|
29
|
+
/** Wave-1 truncation rule (see /reroute): ≤60 chars total, '...' suffix. */
|
|
30
|
+
function truncatePrompt(s) {
|
|
31
|
+
return s.length > 60 ? s.slice(0, 57) + '...' : s;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the recap lines for a session. Returns [] for an empty session
|
|
35
|
+
* (zero user turns) — callers print nothing in that case.
|
|
36
|
+
*/
|
|
37
|
+
export function buildRecap(session, cwd, opts) {
|
|
38
|
+
const lines = [];
|
|
39
|
+
// ── line 1: session orientation ─────────────────────────────────────────
|
|
40
|
+
// "User turns" = plain-string user messages (tool_result rounds are arrays)
|
|
41
|
+
// — same counting rule as the resume picker (session/store.ts).
|
|
42
|
+
const userMessages = session.messages.filter((m) => m.role === 'user' && typeof m.content === 'string');
|
|
43
|
+
const turns = userMessages.length;
|
|
44
|
+
if (turns === 0)
|
|
45
|
+
return [];
|
|
46
|
+
const skillLabel = session.skill ? `/${session.skill}` : 'no skill';
|
|
47
|
+
// Last user prompt: prefer the persisted lastUserPrompt (user-typed prose;
|
|
48
|
+
// null after slash-only commands), fall back to the last string user
|
|
49
|
+
// message with the first-turn <session_context> wrapper stripped (same
|
|
50
|
+
// rule as the picker's promptSnippet).
|
|
51
|
+
let prompt = session.lastUserPrompt;
|
|
52
|
+
if (!prompt) {
|
|
53
|
+
const last = userMessages[userMessages.length - 1].content;
|
|
54
|
+
prompt = last.replace(/<session_context>[\s\S]*?<\/session_context>\s*/g, '').trim() || null;
|
|
55
|
+
}
|
|
56
|
+
const quoted = prompt ? `"${truncatePrompt(prompt)}"` : '(no prompt)';
|
|
57
|
+
const ago = formatTimeAgo(session.last_turn_at, opts?.now);
|
|
58
|
+
lines.push(`↩ last session: ${skillLabel} · ${turns} turn${turns === 1 ? '' : 's'} · ${ago} · ${quoted}`);
|
|
59
|
+
// ── line 2: todo list (Task 4's todo_set) — only when open work exists ──
|
|
60
|
+
const todos = session.todos ?? [];
|
|
61
|
+
const open = todos.filter((t) => t.status !== 'completed');
|
|
62
|
+
if (open.length > 0) {
|
|
63
|
+
const done = todos.length - open.length;
|
|
64
|
+
const next = todos.find((t) => t.status === 'in_progress') ?? todos.find((t) => t.status === 'pending');
|
|
65
|
+
lines.push(`▶ tasks: ${done} done / ${open.length} open — next: ${next.text}`);
|
|
66
|
+
}
|
|
67
|
+
// ── line 3: newest plan family with unsatisfied phases ──────────────────
|
|
68
|
+
// Never throws: any fs/parse problem silently drops the line.
|
|
69
|
+
try {
|
|
70
|
+
const planLine = buildPlanLine(opts?.workspaceDir ?? defaultWorkspaceFor(cwd));
|
|
71
|
+
if (planLine)
|
|
72
|
+
lines.push(planLine);
|
|
73
|
+
}
|
|
74
|
+
catch { /* recap must never throw */ }
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Scan `workspaceDir` for the newest plan family and, when it still has
|
|
79
|
+
* unsatisfied phases, produce the plan recap line. Returns null when there
|
|
80
|
+
* is no plan, the newest plan family is fully satisfied, or its envelope is
|
|
81
|
+
* invalid (silent skip by design).
|
|
82
|
+
*
|
|
83
|
+
* "Newest family": envelope files group into families by filename base
|
|
84
|
+
* (`<base>-v<N>[-<sub>].cspeach.json` — same convention as
|
|
85
|
+
* pickNewestPlanVersion / findNewestVersion in commands/plan-resume.ts);
|
|
86
|
+
* within a family the highest version wins; families rank by their newest
|
|
87
|
+
* file's mtime. Non-plan envelopes (design, spec-gap, …) are skipped so the
|
|
88
|
+
* newest PLAN family is found even when a newer non-plan artefact exists.
|
|
89
|
+
*/
|
|
90
|
+
function buildPlanLine(workspaceDir) {
|
|
91
|
+
let names;
|
|
92
|
+
try {
|
|
93
|
+
names = readdirSync(workspaceDir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null; // workspace missing — nothing to recap
|
|
97
|
+
}
|
|
98
|
+
const re = /^(.+)-v(\d+)(?:-(\d+))?\.cspeach\.json$/;
|
|
99
|
+
const cands = [];
|
|
100
|
+
for (const name of names) {
|
|
101
|
+
if (!name.endsWith('.cspeach.json'))
|
|
102
|
+
continue;
|
|
103
|
+
const path = join(workspaceDir, name);
|
|
104
|
+
let mtimeMs;
|
|
105
|
+
try {
|
|
106
|
+
mtimeMs = statSync(path).mtimeMs;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const m = re.exec(name);
|
|
112
|
+
if (m) {
|
|
113
|
+
cands.push({ path, family: m[1], version: Number(m[2]), sub: m[3] ? Number(m[3]) : 0, mtimeMs });
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Hand-renamed / version-less envelope: its own single-file family.
|
|
117
|
+
cands.push({ path, family: name.slice(0, -'.cspeach.json'.length), version: 0, sub: 0, mtimeMs });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (cands.length === 0)
|
|
121
|
+
return null;
|
|
122
|
+
// Newest version per family (version, then collision sub, then mtime).
|
|
123
|
+
const byFamily = new Map();
|
|
124
|
+
for (const c of cands) {
|
|
125
|
+
const cur = byFamily.get(c.family);
|
|
126
|
+
if (!cur
|
|
127
|
+
|| c.version > cur.version
|
|
128
|
+
|| (c.version === cur.version && (c.sub > cur.sub || (c.sub === cur.sub && c.mtimeMs > cur.mtimeMs)))) {
|
|
129
|
+
byFamily.set(c.family, c);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Families newest-first by their representative file's mtime; the first
|
|
133
|
+
// family whose envelope IS a plan is "the newest plan family".
|
|
134
|
+
const familiesNewestFirst = [...byFamily.values()].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
135
|
+
for (const cand of familiesNewestFirst) {
|
|
136
|
+
let env;
|
|
137
|
+
try {
|
|
138
|
+
env = readProjectFile(cand.path);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
continue; // unreadable/invalid file — can't tell it's a plan; skip family
|
|
142
|
+
}
|
|
143
|
+
if (env.artefactType !== 'plan')
|
|
144
|
+
continue;
|
|
145
|
+
// This is the newest plan family. Invalid content → silent skip (no
|
|
146
|
+
// falling through to an older plan family — stale info is worse than none).
|
|
147
|
+
const pc = parsePlanContent(env.content);
|
|
148
|
+
if (!pc.ok)
|
|
149
|
+
return null;
|
|
150
|
+
const statuses = statusesFromItems(env.interaction.items, pc.content.phases);
|
|
151
|
+
const total = pc.content.phases.length;
|
|
152
|
+
// Audit-aware satisfaction — same predicate as the plan DAG (a
|
|
153
|
+
// validated-but-audit-failed phase is NOT done).
|
|
154
|
+
const validated = pc.content.phases.filter((p) => isPhaseSatisfied(statuses[p.id], p.audit)).length;
|
|
155
|
+
if (total === 0 || validated >= total)
|
|
156
|
+
return null; // fully satisfied — no line
|
|
157
|
+
return `▷ plan ${env.title}: phase ${validated}/${total} validated — next: /abap-plan --resume @${basename(cand.path)}`;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
package/dist/skill-catalog.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static skill catalog for CSPeach CLI v0.2.
|
|
3
3
|
*
|
|
4
|
-
* STALENESS RISK (spec §3.3): This constant is hardcoded against the
|
|
5
|
-
* in .claude/skills/ as of 2026-
|
|
4
|
+
* STALENESS RISK (spec §3.3): This constant is hardcoded against the 33 skills
|
|
5
|
+
* in .claude/skills/ as of 2026-07-03 (abap-dump-analyze + abap-dump-triage
|
|
6
|
+
* merged into abap-dump 2026-06-12; abap-extend-model added 2026-06-25;
|
|
7
|
+
* abap-radar/abap-document/abap-performance/abap-cloud-check merged into
|
|
8
|
+
* abap-explain/abap-handover/abap-review 2026-06-28 per
|
|
9
|
+
* plan 2026-06-28-skill-portfolio-merge; abap-clean-core added 2026-06-29). It will drift if skills are added or
|
|
6
10
|
* removed. At Plan 4b kickoff decision the build-time generation option
|
|
7
11
|
* (scripts/generate-skill-catalog.ts reads SKILL.md frontmatter) or runtime
|
|
8
12
|
* fetch (/v1/skills endpoint) will be chosen to replace this file. Until then,
|
|
@@ -10,65 +14,41 @@
|
|
|
10
14
|
*/
|
|
11
15
|
export const SKILL_CATALOG = [
|
|
12
16
|
// ── Read / Analyze ───────────────────────────────────────────────────────
|
|
13
|
-
{
|
|
14
|
-
name: 'abap-radar',
|
|
15
|
-
category: 'Read / Analyze',
|
|
16
|
-
description: 'Understand an ABAP object quickly — dependencies, tables, risk areas, modernisation summary',
|
|
17
|
-
whenToUse: 'pick this for a 3-min cold read of an object (not /abap-review — no quality grade)',
|
|
18
|
-
},
|
|
19
17
|
{
|
|
20
18
|
name: 'abap-impact',
|
|
21
19
|
category: 'Read / Analyze',
|
|
22
20
|
description: 'Impact analysis before changes — traces usage, callers, transports',
|
|
23
|
-
whenToUse: 'pick this for blast radius before a change (not /abap-
|
|
21
|
+
whenToUse: 'pick this for blast radius before a change (not /abap-explain — explain = teach the code)',
|
|
24
22
|
},
|
|
25
23
|
{
|
|
26
24
|
name: 'abap-explain',
|
|
27
25
|
category: 'Read / Analyze',
|
|
28
|
-
description: '
|
|
29
|
-
whenToUse: `
|
|
26
|
+
description: 'Understand an object — plain-language (default) or `technical` orientation (deps, tables, risks)',
|
|
27
|
+
whenToUse: `plain teaching, or technical=fast orientation (not /abap-impact = blast radius)`,
|
|
30
28
|
},
|
|
31
29
|
{
|
|
32
30
|
name: 'abap-review',
|
|
33
31
|
category: 'Read / Analyze',
|
|
34
|
-
description: '
|
|
35
|
-
whenToUse: `
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: 'abap-performance',
|
|
39
|
-
category: 'Read / Analyze',
|
|
40
|
-
description: 'Identify N+1 queries, missing indexes, SELECT *, FOR ALL ENTRIES pitfalls',
|
|
41
|
-
whenToUse: 'pick this for deep SQL/loop bottleneck hunt (not /abap-review — review only skims perf)',
|
|
32
|
+
description: 'Read-only review across lenses: quality (default), performance, cloud-readiness',
|
|
33
|
+
whenToUse: `grade source: quality (default) + performance/cloud lenses`,
|
|
42
34
|
},
|
|
43
35
|
{
|
|
44
|
-
name: 'abap-
|
|
36
|
+
name: 'abap-clean-core',
|
|
45
37
|
category: 'Read / Analyze',
|
|
46
|
-
description: '
|
|
47
|
-
whenToUse: `pick this for
|
|
38
|
+
description: 'Clean-core decision & defense — recommend the clean-core-correct approach, classify the level (A/B/C/D), produce citable evidence; verifies live when connected. Read-only.',
|
|
39
|
+
whenToUse: `pick this for "is this clean-core-OK?" decisions + defendable evidence (not /abap-review cloud lens — that grades code)`,
|
|
48
40
|
},
|
|
49
41
|
{
|
|
50
42
|
name: 'abap-handover',
|
|
51
43
|
category: 'Read / Analyze',
|
|
52
|
-
description: '
|
|
53
|
-
whenToUse: `
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'abap-document',
|
|
57
|
-
category: 'Read / Analyze',
|
|
58
|
-
description: 'Reference doc for ONE ABAP object — purpose, interface, logic flow, dependencies, where-used',
|
|
59
|
-
whenToUse: `pick this for a reference doc on ONE object (not /abap-handover — that's whole-package)`,
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'abap-dump-analyze',
|
|
63
|
-
category: 'Read / Analyze',
|
|
64
|
-
description: 'Analyze ABAP short dumps (ST22) — root cause, call stack, fix suggestions',
|
|
65
|
-
whenToUse: `pick this to diagnose an ST22 dump AND apply the fix (not /abap-dump-triage — read-only)`,
|
|
44
|
+
description: 'Documentation for one object (`object` scope) or a whole package (`package` scope)',
|
|
45
|
+
whenToUse: `object doc OR full package handover dossier`,
|
|
66
46
|
},
|
|
67
47
|
{
|
|
68
|
-
name: 'abap-dump
|
|
48
|
+
name: 'abap-dump',
|
|
69
49
|
category: 'Read / Analyze',
|
|
70
|
-
description: '
|
|
71
|
-
whenToUse: `pick this for
|
|
50
|
+
description: 'Diagnose ABAP short dumps (ST22) and fix them — root cause, call stack, fix with approval; ask for "triage" for ranked read-only hypotheses',
|
|
51
|
+
whenToUse: `pick this for any ST22 dump — diagnose + fix by default, say "triage" for read-only ranking (not /abap-incident — that's the full dump-to-transport pipeline)`,
|
|
72
52
|
},
|
|
73
53
|
// ── Modify / Fix ─────────────────────────────────────────────────────────
|
|
74
54
|
{
|
|
@@ -89,11 +69,17 @@ export const SKILL_CATALOG = [
|
|
|
89
69
|
description: 'Extend SAP standard via BAdI implementations, enhancement spots, user exits',
|
|
90
70
|
whenToUse: `pick this to hook into SAP-STANDARD code via BAdI/exit (not /abap-refactor — that's for YOUR Z code)`,
|
|
91
71
|
},
|
|
72
|
+
{
|
|
73
|
+
name: 'abap-extend-model',
|
|
74
|
+
category: 'Modify / Fix',
|
|
75
|
+
description: 'Add a field or @UI column to an EXISTING custom CDS view / DDLX metadata extension in place — anchored insertion, snapshot, syntax-check, activate, republish-if-needed. The Fiori Elements column-add.',
|
|
76
|
+
whenToUse: `pick this to add a field/column to an EXISTING custom CDS/DDLX in place (not /abap-rap — that's a new stack; not /abap-refactor — that's ABAP code, not CDS)`,
|
|
77
|
+
},
|
|
92
78
|
{
|
|
93
79
|
name: 'abap-incident',
|
|
94
80
|
category: 'Modify / Fix',
|
|
95
81
|
description: 'End-to-end incident pipeline — dump to fix deployed to transport in one skill',
|
|
96
|
-
whenToUse: `pick this for full ST22-to-transport pipeline with formal report (not /abap-dump
|
|
82
|
+
whenToUse: `pick this for full ST22-to-transport pipeline with formal report (not /abap-dump — single fix only)`,
|
|
97
83
|
},
|
|
98
84
|
{
|
|
99
85
|
name: 'abap-test',
|
|
@@ -126,6 +112,12 @@ export const SKILL_CATALOG = [
|
|
|
126
112
|
description: 'Scaffold a complete RAP stack on S/4HANA 1909+ — table, CDS interface, projection, BDEF, BP class, SRVD, SRVB, DDLX, DCL. ECC/older S/4 → use /abap-segw.',
|
|
127
113
|
whenToUse: `pick this for a full RAP stack on S/4 1909+ (10 objects, table → SRVB; not /abap-segw — that's ECC/older S/4 OData; not /abap-generate — that's one object)`,
|
|
128
114
|
},
|
|
115
|
+
{
|
|
116
|
+
name: 'abap-fiori-build',
|
|
117
|
+
category: 'Create / Generate',
|
|
118
|
+
description: 'Build a working freestyle SAPUI5 app on the laptop from a published OData service — scaffold, pages, dialogs, chart, value help, local preview. Freestyle only, not Fiori Elements.',
|
|
119
|
+
whenToUse: `pick this for a freestyle UI5 frontend on a published OData service (not /abap-rap — that's the backend; produces local files, not ADT objects)`,
|
|
120
|
+
},
|
|
129
121
|
{
|
|
130
122
|
name: 'abap-data-model',
|
|
131
123
|
category: 'Create / Generate',
|
|
@@ -226,3 +218,22 @@ export const SKILL_CATALOG = [
|
|
|
226
218
|
whenToUse: `pick this when the work spans multiple sessions or components (not /abap-design — that's one design in one session; plan calls design per phase)`,
|
|
227
219
|
},
|
|
228
220
|
];
|
|
221
|
+
/**
|
|
222
|
+
* Portfolio-merge redirects (2026-06-12). Old skill names that were merged
|
|
223
|
+
* into a surviving skill keep routing for ONE release: `/abap-dump-analyze`
|
|
224
|
+
* resolves to `abap-dump` with a dim "renamed" notice instead of the
|
|
225
|
+
* unknown-skill error. Resolution happens in parseShortcut (slash input)
|
|
226
|
+
* and classifyPrompt (stale proxy-classifier outputs). Remove the entries
|
|
227
|
+
* — and the proxy-side alias seeds — one release after the rename ships.
|
|
228
|
+
*/
|
|
229
|
+
export const SKILL_ALIASES = {
|
|
230
|
+
'abap-dump-analyze': 'abap-dump',
|
|
231
|
+
'abap-dump-triage': 'abap-dump',
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Resolve a (possibly retired) skill name to its current catalog name.
|
|
235
|
+
* Returns the input unchanged when it is not an alias.
|
|
236
|
+
*/
|
|
237
|
+
export function resolveSkillAlias(skill) {
|
|
238
|
+
return SKILL_ALIASES[skill] ?? skill;
|
|
239
|
+
}
|