@cspeach/cli 1.1.23 → 1.1.25

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.
Files changed (51) hide show
  1. package/dist/agent/loop.js +124 -0
  2. package/dist/classifier/client.js +27 -2
  3. package/dist/commands/plan-continue.js +11 -1
  4. package/dist/commands/plan-resume.js +38 -1
  5. package/dist/lane/app-chain.js +297 -0
  6. package/dist/lane/app-flow.js +55 -0
  7. package/dist/lane/card.js +148 -0
  8. package/dist/lane/chain-token.js +59 -0
  9. package/dist/lane/inputs.js +459 -0
  10. package/dist/lane/paths.js +21 -0
  11. package/dist/lane/resolve.js +109 -0
  12. package/dist/lane/roots.js +113 -0
  13. package/dist/lane/signals.js +80 -0
  14. package/dist/lane/store.js +232 -0
  15. package/dist/lane/types.js +1 -0
  16. package/dist/one-shot.js +1 -0
  17. package/dist/projects/detail-path.js +117 -0
  18. package/dist/projects/extract-plan.js +46 -6
  19. package/dist/projects/merge-cca.js +19 -0
  20. package/dist/projects/merge-upgrade.js +19 -1
  21. package/dist/projects/plan-lane.js +214 -0
  22. package/dist/projects/plan-schema.js +10 -0
  23. package/dist/projects/promote-command.js +8 -2
  24. package/dist/projects/promote.js +7 -1
  25. package/dist/projects/save-command.js +71 -25
  26. package/dist/projects/workspace.js +10 -0
  27. package/dist/register/records.js +7 -2
  28. package/dist/renderer/tool-labels.js +2 -1
  29. package/dist/repl/resume-standalone.js +3 -0
  30. package/dist/repl.js +23 -2
  31. package/dist/skill-catalog.js +14 -6
  32. package/dist/skills/bundled-skills.js +61 -54
  33. package/dist/tools/_filesystem-shared.js +363 -33
  34. package/dist/tools/cap/metadata-import.js +10 -4
  35. package/dist/tools/filesystem/file-write.js +12 -39
  36. package/dist/tools/filesystem/glob.js +25 -2
  37. package/dist/tools/filesystem/grep.js +25 -2
  38. package/dist/tools/fiori/apply.js +30 -21
  39. package/dist/tools/fiori/commit-gate.js +58 -0
  40. package/dist/tools/fiori/fe-extend.js +6 -1
  41. package/dist/tools/fiori/fe-scaffold.js +6 -1
  42. package/dist/tools/fiori/path-params.js +78 -0
  43. package/dist/tools/fiori/scaffold.js +2 -1
  44. package/dist/tools/fiori/tools.js +53 -14
  45. package/dist/tools/lane/app-lane-resolve.js +329 -0
  46. package/dist/tools/local-build.js +4 -0
  47. package/dist/tools/project/_merge-shared.js +36 -1
  48. package/dist/tools/project/cca_merge.js +4 -0
  49. package/dist/tools/project/upgrade_merge_progress.js +21 -10
  50. package/dist/tools/tool-loading.js +2 -0
  51. package/package.json +1 -1
package/dist/repl.js CHANGED
@@ -20,6 +20,7 @@ import { CLI_VERSION } from './lib/version.js';
20
20
  import { createOfflineAdtClient } from './sap/offline-adt-client.js';
21
21
  import { STANDALONE_ALIAS } from './tools/index.js';
22
22
  import { resumeStandaloneDecision, applySystemSwitch } from './repl/resume-standalone.js';
23
+ import { clearAppFlow } from './lane/app-flow.js';
23
24
  import { applyResumedSkill, isShortFollowUp } from './router/resume-routing.js';
24
25
  import { readableInterruptReason, isUserStopReason } from './session/interrupt-reason.js';
25
26
  import { getStore } from './auth/api-key.js';
@@ -152,6 +153,7 @@ import './tools/cap/service-catalog.js';
152
153
  import './tools/cap/metadata-import.js';
153
154
  import './tools/cap/sap-link-tool.js';
154
155
  import './tools/cap/link-smoke.js';
156
+ import './tools/lane/app-lane-resolve.js';
155
157
  import './tools/extend-model/tool.js';
156
158
  import './tools/capability/tool.js';
157
159
  import './tools/web/web_fetch.js';
@@ -1187,6 +1189,7 @@ export async function runRepl(opts) {
1187
1189
  // list AND the live view, so Ctrl+T / --resume can't bring it back.
1188
1190
  session.todos = [];
1189
1191
  todoEmitter.emit('update', []);
1192
+ clearAppFlow(session); // Task 7 — /new ends an /abap-app flow
1190
1193
  chunkEmitter.emit('chunk', '\n' + formatInfo('Fresh start · the next prompt picks a skill again'));
1191
1194
  return;
1192
1195
  }
@@ -1581,11 +1584,19 @@ export async function runRepl(opts) {
1581
1584
  if (skill === 'abap-plan' && planResume === null && session.plan_in_flight) {
1582
1585
  const marker = session.plan_in_flight;
1583
1586
  const { preparePlanContinuation } = await import('./commands/plan-continue.js');
1584
- const r = preparePlanContinuation({
1587
+ const r = await preparePlanContinuation({
1585
1588
  marker,
1586
1589
  sessionId: session.id,
1590
+ cwd: process.cwd(),
1587
1591
  log: (...lines) => chunkEmitter.emit('chunk', lines.join('\n') + '\n'),
1588
1592
  });
1593
+ // App lane routing — the plan's lane file changed or its phases do not
1594
+ // fit the lane: the plan is paused, no turn runs, the marker stays.
1595
+ if ('paused' in r && r.paused) {
1596
+ session.last_skill = previousSkill ?? null;
1597
+ session.last_object = extractedObject ?? previousObject ?? null;
1598
+ return;
1599
+ }
1589
1600
  if (r.prepared) {
1590
1601
  planContinue = { prepared: r.prepared, marker };
1591
1602
  planPhaseStartedAt = marker.attemptStartedAt;
@@ -1675,6 +1686,7 @@ export async function runRepl(opts) {
1675
1686
  ctx: { adt, sapAlias: selectedAlias, session, cwd: process.cwd(), provider, skillSource: provider.skillSource, previewHook: inkPreview, chunkEmitter, todoEmitter, currentTransport: currentTransportAccessor, pendingDispatch: pendingDispatchAccessor },
1676
1687
  chunkEmitter,
1677
1688
  pushSpend: true, // F-L2-2 — top-level REPL turn: live statusline spend
1689
+ explicitSlash: parsed.skill !== undefined, // Task 7 R11 — typed or queued slash command
1678
1690
  signal: currentTurnAbort.signal,
1679
1691
  suppressSaveHook: planResume !== null || planContinue !== null,
1680
1692
  // Task 19 — auto-compact at turn end, except plan resume/continue
@@ -2839,6 +2851,7 @@ export async function runRepl(opts) {
2839
2851
  if (trimmed === '/new' || trimmed === '/reset') {
2840
2852
  session.awaitingSkillAnswer = false;
2841
2853
  session.todos = []; // P2 — /new clears the plan (spec §4)
2854
+ clearAppFlow(session); // Task 7 — /new ends an /abap-app flow
2842
2855
  console.log(formatInfo('Fresh start · the next prompt picks a skill again').trimEnd());
2843
2856
  continue;
2844
2857
  }
@@ -3295,11 +3308,18 @@ export async function runRepl(opts) {
3295
3308
  if (skill === 'abap-plan' && planResume === null && session.plan_in_flight) {
3296
3309
  const marker = session.plan_in_flight;
3297
3310
  const { preparePlanContinuation } = await import('./commands/plan-continue.js');
3298
- const r = preparePlanContinuation({
3311
+ const r = await preparePlanContinuation({
3299
3312
  marker,
3300
3313
  sessionId: session.id,
3314
+ cwd: process.cwd(),
3301
3315
  log: (...lines) => lines.forEach((l) => console.log(l)),
3302
3316
  });
3317
+ // App lane routing — paused plan: no turn, marker kept (Ink mirror).
3318
+ if ('paused' in r && r.paused) {
3319
+ session.last_skill = previousSkill ?? null;
3320
+ session.last_object = extractedObject ?? previousObject ?? null;
3321
+ continue;
3322
+ }
3303
3323
  if (r.prepared) {
3304
3324
  planContinue = { prepared: r.prepared, marker };
3305
3325
  planPhaseStartedAt = marker.attemptStartedAt;
@@ -3403,6 +3423,7 @@ export async function runRepl(opts) {
3403
3423
  skill: skill,
3404
3424
  ctx: { adt, sapAlias: selectedAlias, session, cwd: process.cwd(), provider, skillSource: provider.skillSource, previewHook: replPreviewHook, chunkEmitter: classicOutputEmitter, todoEmitter, currentTransport: currentTransportAccessor, pendingDispatch: pendingDispatchAccessor },
3405
3425
  pushSpend: true, // F-L2-2 — top-level REPL turn: live statusline spend
3426
+ explicitSlash: parsed.skill !== undefined, // Task 7 R11 — typed or queued slash command
3406
3427
  signal: turnAbort.signal,
3407
3428
  suppressSaveHook: planResume !== null || planContinue !== null,
3408
3429
  // Task 19 — auto-compact at turn end, except plan resume/continue
@@ -1,12 +1,13 @@
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 33 skills
4
+ * STALENESS RISK (spec §3.3): This constant is hardcoded against the 35 skills
5
5
  * in .claude/skills/ as of 2026-07-03 (abap-dump-analyze + abap-dump-triage
6
6
  * merged into abap-dump 2026-06-12; abap-extend-model added 2026-06-25;
7
7
  * abap-radar/abap-document/abap-performance/abap-cloud-check merged into
8
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
9
+ * plan 2026-06-28-skill-portfolio-merge; abap-clean-core added 2026-06-29; abap-cap added
10
+ * 2026-09-25; abap-app added 2026-10-01). It will drift if skills are added or
10
11
  * removed. At Plan 4b kickoff decision the build-time generation option
11
12
  * (scripts/generate-skill-catalog.ts reads SKILL.md frontmatter) or runtime
12
13
  * fetch (/v1/skills endpoint) will be chosen to replace this file. Until then,
@@ -109,11 +110,18 @@ export const SKILL_CATALOG = [
109
110
  {
110
111
  name: 'abap-segw',
111
112
  category: 'Modify / Fix',
112
- description: 'Implement SEGW OData DPC_EXT method bodies — reads generated MPC/DPC base classes. SEGW project itself created in transaction SEGW. For S/4 1909+ RAP services use /abap-rap.',
113
- whenToUse: `pick this for DPC_EXT method bodies on an EXISTING SEGW project (ECC / older S/4; not /abap-rap — that's S/4 1909+ RAP; SEGW project + MPC/DPC must already exist)`,
113
+ description: 'Implement SEGW OData DPC_EXT method bodies — reads generated MPC/DPC base classes. SEGW project itself created in transaction SEGW. For S/4 2020+ RAP services use /abap-rap.',
114
+ whenToUse: `pick this for DPC_EXT method bodies on an EXISTING SEGW project (ECC / older S/4; not /abap-rap — that's S/4 2020+ RAP; SEGW project + MPC/DPC must already exist)`,
114
115
  asks: 'which project and operation? e.g. "GET_ENTITYSET for ZORDER_SRV"',
115
116
  },
116
117
  // ── Create / Generate ────────────────────────────────────────────────────
118
+ {
119
+ name: 'abap-app',
120
+ category: 'Create / Generate',
121
+ description: 'One entry from a spec (Word, PDF, Excel or text) to a running app — spec gaps, then the app lane (backend + UI, picked by the CLI and confirmed on the lane card), then the plan and the build.',
122
+ whenToUse: `pick this to build an app FROM a spec document (not /abap-rap or /abap-cap — those build one stack you already chose)`,
123
+ asks: 'the spec file. e.g. "@specs/inspection-log.docx"',
124
+ },
117
125
  {
118
126
  name: 'abap-generate',
119
127
  category: 'Create / Generate',
@@ -124,8 +132,8 @@ export const SKILL_CATALOG = [
124
132
  {
125
133
  name: 'abap-rap',
126
134
  category: 'Create / Generate',
127
- 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.',
128
- 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)`,
135
+ description: 'Scaffold a complete RAP stack on S/4HANA 2020+ — table, CDS interface, projection, BDEF, BP class, SRVD, SRVB, DDLX, DCL. ECC/older S/4 → use /abap-segw.',
136
+ whenToUse: `pick this for a full RAP stack on S/4 2020+ (10 objects, table → SRVB; not /abap-segw — that's ECC/older S/4 OData; not /abap-generate — that's one object)`,
129
137
  asks: 'the business object. e.g. "maintenance requests with draft and an approve action"',
130
138
  },
131
139
  {