@cspeach/cli 1.1.2 → 1.1.4
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/dist/agent/loop.js +31 -4
- package/dist/agent/session-context.js +24 -0
- package/dist/agent/tool-dispatch.js +17 -1
- package/dist/cli-args.js +24 -0
- package/dist/cli.js +33 -4
- package/dist/commands/config-set.js +155 -4
- package/dist/commands/cost.js +56 -4
- package/dist/commands/export-audit.js +4 -1
- package/dist/commands/plan-chain.js +8 -1
- package/dist/config/loader.js +44 -0
- package/dist/cost/cost-log.js +6 -1
- package/dist/cost/credits-wire.js +101 -0
- package/dist/cost/credits.js +199 -0
- package/dist/cost/display-mode.js +29 -0
- package/dist/cost/pricing.js +4 -0
- package/dist/doctor/checks/_standalone-skip.js +37 -0
- package/dist/doctor/checks/cert.js +5 -0
- package/dist/doctor/checks/credits.js +48 -0
- package/dist/doctor/checks/forge-rules.js +6 -0
- package/dist/doctor/checks/sap.js +5 -0
- package/dist/doctor/checks/standards.js +27 -0
- package/dist/doctor/checks/system-roles.js +5 -0
- package/dist/doctor/checks/zcspeach.js +5 -0
- package/dist/doctor/run.js +4 -0
- package/dist/one-shot.js +43 -11
- package/dist/renderer/status-footer.js +13 -3
- package/dist/repl/post-turn-status.js +5 -0
- package/dist/repl/session-spend-line.js +20 -0
- package/dist/repl.js +48 -9
- package/dist/sap/offline-adt-client.js +58 -0
- package/dist/sap/standalone-onboarding.js +118 -0
- package/dist/sap/standalone-profile.js +237 -0
- package/dist/session/audit-export.js +23 -4
- package/dist/standards/standards-file.js +107 -0
- package/dist/standards/standards-init.js +194 -0
- package/dist/tools/capability/tool.js +2 -0
- package/dist/tools/index.js +25 -0
- package/dist/tools/sap-read.js +18 -0
- package/dist/tools/sap-write.js +14 -0
- package/dist/tools/snapshot.js +3 -0
- package/dist/tools/transport.js +4 -0
- package/dist/ui/header.js +3 -1
- package/dist/ui/sap-state-store.js +4 -0
- package/dist/ui/status-line.js +20 -1
- package/package.json +109 -109
package/dist/tools/sap-read.js
CHANGED
|
@@ -36,6 +36,7 @@ registerTool({
|
|
|
36
36
|
+ 'created object with NO active version yet, the inactive (working) '
|
|
37
37
|
+ 'source is returned with version="inactive" and an honest note.',
|
|
38
38
|
isMutating: false,
|
|
39
|
+
requiresSap: true,
|
|
39
40
|
input_schema: {
|
|
40
41
|
type: 'object',
|
|
41
42
|
properties: {
|
|
@@ -130,6 +131,7 @@ registerTool({
|
|
|
130
131
|
name: 'sap_object_structure',
|
|
131
132
|
description: 'Retrieve metadata and structural information for an ABAP repository object (package, description, lock state, etc.).',
|
|
132
133
|
isMutating: false,
|
|
134
|
+
requiresSap: true,
|
|
133
135
|
input_schema: {
|
|
134
136
|
type: 'object',
|
|
135
137
|
properties: {
|
|
@@ -165,6 +167,7 @@ registerTool({
|
|
|
165
167
|
name: 'sap_search_object',
|
|
166
168
|
description: 'Search the SAP repository by object name pattern. Supports wildcard * (e.g. ZCL_ORDER*).',
|
|
167
169
|
isMutating: false,
|
|
170
|
+
requiresSap: true,
|
|
168
171
|
input_schema: {
|
|
169
172
|
type: 'object',
|
|
170
173
|
properties: {
|
|
@@ -192,6 +195,7 @@ registerTool({
|
|
|
192
195
|
name: 'sap_change_log',
|
|
193
196
|
description: 'Retrieve the version history (change log) of an ABAP object — date, author, description per revision.',
|
|
194
197
|
isMutating: false,
|
|
198
|
+
requiresSap: true,
|
|
195
199
|
input_schema: {
|
|
196
200
|
type: 'object',
|
|
197
201
|
properties: {
|
|
@@ -214,6 +218,7 @@ registerTool({
|
|
|
214
218
|
+ 'Dialect limits (violations return an opaque "SAP HTTP 400: sqlQuery"): no IN-lists and at most one LIKE per WHERE clause '
|
|
215
219
|
+ '(multi-OR LIKE chains are rejected) — use a single LIKE or split into separate queries.',
|
|
216
220
|
isMutating: false,
|
|
221
|
+
requiresSap: true,
|
|
217
222
|
input_schema: {
|
|
218
223
|
type: 'object',
|
|
219
224
|
properties: {
|
|
@@ -237,6 +242,7 @@ registerTool({
|
|
|
237
242
|
+ 'result includes a `coverage` line stating exactly which window was fetched — trust it '
|
|
238
243
|
+ 'over assumptions. With dumpId returns the full diagnostic detail.',
|
|
239
244
|
isMutating: false,
|
|
245
|
+
requiresSap: true,
|
|
240
246
|
input_schema: {
|
|
241
247
|
type: 'object',
|
|
242
248
|
properties: {
|
|
@@ -293,6 +299,7 @@ registerTool({
|
|
|
293
299
|
name: 'sap_atc_run',
|
|
294
300
|
description: 'Run ABAP Test Cockpit (ATC) checks on a specific object and return all findings.',
|
|
295
301
|
isMutating: false,
|
|
302
|
+
requiresSap: true,
|
|
296
303
|
input_schema: {
|
|
297
304
|
type: 'object',
|
|
298
305
|
properties: {
|
|
@@ -318,6 +325,7 @@ registerTool({
|
|
|
318
325
|
name: 'sap_usage_references',
|
|
319
326
|
description: 'Find all places where a given ABAP object is referenced (where-used list).',
|
|
320
327
|
isMutating: false,
|
|
328
|
+
requiresSap: true,
|
|
321
329
|
input_schema: {
|
|
322
330
|
type: 'object',
|
|
323
331
|
properties: {
|
|
@@ -339,6 +347,7 @@ registerTool({
|
|
|
339
347
|
name: 'sap_api_state',
|
|
340
348
|
description: 'Check the ABAP Cloud / clean-core release state of SAP objects. Pass comma-separated TYPE:NAME pairs, e.g. "CLAS:CL_GUI_ALV_GRID,FUNC:BAPI_SALESORDER_GETLIST".',
|
|
341
349
|
isMutating: false,
|
|
350
|
+
requiresSap: true,
|
|
342
351
|
input_schema: {
|
|
343
352
|
type: 'object',
|
|
344
353
|
properties: {
|
|
@@ -362,6 +371,7 @@ registerTool({
|
|
|
362
371
|
name: 'sap_abap_docu',
|
|
363
372
|
description: 'Retrieve official ABAP language documentation from the SAP system for a keyword, class, or function module.',
|
|
364
373
|
isMutating: false,
|
|
374
|
+
requiresSap: true,
|
|
365
375
|
input_schema: {
|
|
366
376
|
type: 'object',
|
|
367
377
|
properties: {
|
|
@@ -384,6 +394,7 @@ registerTool({
|
|
|
384
394
|
name: 'sap_class_includes',
|
|
385
395
|
description: 'List the includes of an ABAP class, or read one specific include source (testclasses, locals_def, locals_imp, macros).',
|
|
386
396
|
isMutating: false,
|
|
397
|
+
requiresSap: true,
|
|
387
398
|
input_schema: {
|
|
388
399
|
type: 'object',
|
|
389
400
|
properties: {
|
|
@@ -415,6 +426,7 @@ registerTool({
|
|
|
415
426
|
name: 'sap_inactive_objects',
|
|
416
427
|
description: 'List all ABAP objects that are currently inactive (not yet activated) in the system.',
|
|
417
428
|
isMutating: false,
|
|
429
|
+
requiresSap: true,
|
|
418
430
|
input_schema: {
|
|
419
431
|
type: 'object',
|
|
420
432
|
properties: {},
|
|
@@ -432,6 +444,7 @@ registerTool({
|
|
|
432
444
|
name: 'sap_syntax_check',
|
|
433
445
|
description: 'Run a syntax check on an ABAP object and return any errors or warnings without activating it.',
|
|
434
446
|
isMutating: false,
|
|
447
|
+
requiresSap: true,
|
|
435
448
|
input_schema: {
|
|
436
449
|
type: 'object',
|
|
437
450
|
properties: {
|
|
@@ -453,6 +466,7 @@ registerTool({
|
|
|
453
466
|
name: 'sap_diff',
|
|
454
467
|
description: 'Compute a unified diff for an ABAP object between two optional source strings; if omitted, diffs the current version against the previous from the change log.',
|
|
455
468
|
isMutating: false,
|
|
469
|
+
requiresSap: true,
|
|
456
470
|
input_schema: {
|
|
457
471
|
type: 'object',
|
|
458
472
|
properties: {
|
|
@@ -482,6 +496,7 @@ registerTool({
|
|
|
482
496
|
name: 'sap_preview_change',
|
|
483
497
|
description: 'Preview a proposed source change for an ABAP object as a unified diff without writing anything to the system.',
|
|
484
498
|
isMutating: false,
|
|
499
|
+
requiresSap: true,
|
|
485
500
|
input_schema: {
|
|
486
501
|
type: 'object',
|
|
487
502
|
properties: {
|
|
@@ -507,6 +522,7 @@ registerTool({
|
|
|
507
522
|
name: 'sap_run_unit_test',
|
|
508
523
|
description: 'Execute ABAP Unit tests for an object and return a structured pass/fail report with method-level results.',
|
|
509
524
|
isMutating: false,
|
|
525
|
+
requiresSap: true,
|
|
510
526
|
input_schema: {
|
|
511
527
|
type: 'object',
|
|
512
528
|
properties: {
|
|
@@ -553,6 +569,7 @@ registerTool({
|
|
|
553
569
|
name: 'sap_odata_test',
|
|
554
570
|
description: 'Test an OData service endpoint (V2 or V4). Fetches $metadata or reads an entity set and returns the real HTTP status, the request URL/Accept actually used, and the response body. Works on draft-enabled OData V4 RAP services: V4 is auto-detected from /odata4/ paths, $metadata gets an XML Accept, and data reads use a JSON Accept without the V2-only $format option. System query options ($metadata, $top, $filter, $select, $expand, $count) are preserved literally.',
|
|
555
571
|
isMutating: false,
|
|
572
|
+
requiresSap: true,
|
|
556
573
|
input_schema: {
|
|
557
574
|
type: 'object',
|
|
558
575
|
properties: {
|
|
@@ -593,6 +610,7 @@ registerTool({
|
|
|
593
610
|
name: 'sap_badi_list',
|
|
594
611
|
description: 'Search for BAdI definitions and enhancement spots by name pattern (supports * wildcard), with optional enhancement-spot drill-down.',
|
|
595
612
|
isMutating: false,
|
|
613
|
+
requiresSap: true,
|
|
596
614
|
input_schema: {
|
|
597
615
|
type: 'object',
|
|
598
616
|
properties: {
|
package/dist/tools/sap-write.js
CHANGED
|
@@ -89,6 +89,7 @@ registerTool({
|
|
|
89
89
|
+ 'a single method body. For method-body-only edits use sap_update_method (Rule 7a). '
|
|
90
90
|
+ 'The transport auto-resolves to the owning request if the object is already locked.',
|
|
91
91
|
isMutating: true,
|
|
92
|
+
requiresSap: true,
|
|
92
93
|
input_schema: {
|
|
93
94
|
type: 'object',
|
|
94
95
|
properties: {
|
|
@@ -252,6 +253,7 @@ registerTool({
|
|
|
252
253
|
+ 'method_source must be the lines between METHOD x. and ENDMETHOD. (no wrappers). '
|
|
253
254
|
+ 'The transport auto-resolves to the owning request if the class is already locked.',
|
|
254
255
|
isMutating: true,
|
|
256
|
+
requiresSap: true,
|
|
255
257
|
input_schema: {
|
|
256
258
|
type: 'object',
|
|
257
259
|
properties: {
|
|
@@ -441,6 +443,7 @@ registerTool({
|
|
|
441
443
|
+ 'Post-check: confirms the object no longer exists. '
|
|
442
444
|
+ 'The transport auto-resolves to the owning request if the object is already locked.',
|
|
443
445
|
isMutating: true,
|
|
446
|
+
requiresSap: true,
|
|
444
447
|
input_schema: {
|
|
445
448
|
type: 'object',
|
|
446
449
|
properties: {
|
|
@@ -563,6 +566,7 @@ registerTool({
|
|
|
563
566
|
+ 'Requires approval_id. No snapshot needed (new object). '
|
|
564
567
|
+ 'Write source separately via sap_set_source, then call sap_activate.',
|
|
565
568
|
isMutating: true,
|
|
569
|
+
requiresSap: true,
|
|
566
570
|
input_schema: {
|
|
567
571
|
type: 'object',
|
|
568
572
|
properties: {
|
|
@@ -670,6 +674,7 @@ registerTool({
|
|
|
670
674
|
+ 'After writing, activate the class via sap_activate. '
|
|
671
675
|
+ 'The transport auto-resolves to the owning request if the class is already locked.',
|
|
672
676
|
isMutating: true,
|
|
677
|
+
requiresSap: true,
|
|
673
678
|
input_schema: {
|
|
674
679
|
type: 'object',
|
|
675
680
|
properties: {
|
|
@@ -792,6 +797,7 @@ registerTool({
|
|
|
792
797
|
+ 'Requires approval_id. No snapshot needed (new object). '
|
|
793
798
|
+ 'Use the domain in a data element via sap_create_data_element.',
|
|
794
799
|
isMutating: true,
|
|
800
|
+
requiresSap: true,
|
|
795
801
|
input_schema: {
|
|
796
802
|
type: 'object',
|
|
797
803
|
properties: {
|
|
@@ -926,6 +932,7 @@ registerTool({
|
|
|
926
932
|
+ 'activate and verify it. Requires approval_id. No snapshot needed (new object). '
|
|
927
933
|
+ 'Use the data element as a table/structure field type.',
|
|
928
934
|
isMutating: true,
|
|
935
|
+
requiresSap: true,
|
|
929
936
|
input_schema: {
|
|
930
937
|
type: 'object',
|
|
931
938
|
properties: {
|
|
@@ -1054,6 +1061,7 @@ registerTool({
|
|
|
1054
1061
|
+ 'sap_update_method / sap_create_object (Rule 10). '
|
|
1055
1062
|
+ 'Post-check: objects must no longer appear in the inactive-objects list.',
|
|
1056
1063
|
isMutating: true,
|
|
1064
|
+
requiresSap: true,
|
|
1057
1065
|
input_schema: {
|
|
1058
1066
|
type: 'object',
|
|
1059
1067
|
properties: {
|
|
@@ -1169,6 +1177,7 @@ registerTool({
|
|
|
1169
1177
|
name: 'sap_lock',
|
|
1170
1178
|
description: 'RARELY needed. Returns a lock handle but other write tools cannot consume it — sap_set_source / sap_update_method / sap_delete_object each acquire their OWN lock internally. Calling sap_lock as a recovery step after a write failure CREATES A STALE LOCK that blocks the next write attempt; do not do that. Only call this when you need to inspect lock state or hold a lock for a coordinated multi-step ABAP workbench operation outside of CSPeach.',
|
|
1171
1179
|
isMutating: true,
|
|
1180
|
+
requiresSap: true,
|
|
1172
1181
|
input_schema: {
|
|
1173
1182
|
type: 'object',
|
|
1174
1183
|
properties: {
|
|
@@ -1190,6 +1199,7 @@ registerTool({
|
|
|
1190
1199
|
name: 'sap_unlock',
|
|
1191
1200
|
description: 'Release an ADT edit lock on an ABAP object using the lockHandle from sap_lock.',
|
|
1192
1201
|
isMutating: true,
|
|
1202
|
+
requiresSap: true,
|
|
1193
1203
|
input_schema: {
|
|
1194
1204
|
type: 'object',
|
|
1195
1205
|
properties: {
|
|
@@ -1212,6 +1222,7 @@ registerTool({
|
|
|
1212
1222
|
name: 'sap_service_binding_publish',
|
|
1213
1223
|
description: 'Publish an ADT service binding so its OData endpoint becomes active and reachable.',
|
|
1214
1224
|
isMutating: true,
|
|
1225
|
+
requiresSap: true,
|
|
1215
1226
|
input_schema: {
|
|
1216
1227
|
type: 'object',
|
|
1217
1228
|
properties: {
|
|
@@ -1235,6 +1246,7 @@ registerTool({
|
|
|
1235
1246
|
description: 'Unpublish an ADT service binding so its OData endpoint is removed. '
|
|
1236
1247
|
+ 'Required before deleting a published binding (SAP returns HTTP 412 "is published" otherwise).',
|
|
1237
1248
|
isMutating: true,
|
|
1249
|
+
requiresSap: true,
|
|
1238
1250
|
input_schema: {
|
|
1239
1251
|
type: 'object',
|
|
1240
1252
|
properties: {
|
|
@@ -1257,6 +1269,7 @@ registerTool({
|
|
|
1257
1269
|
+ 'Maintaining MSAG message-class entries via ADT requires XML message-class-format '
|
|
1258
1270
|
+ 'parsing not yet implemented in @cspeach/sap-client.',
|
|
1259
1271
|
isMutating: true,
|
|
1272
|
+
requiresSap: true,
|
|
1260
1273
|
input_schema: {
|
|
1261
1274
|
type: 'object',
|
|
1262
1275
|
properties: {
|
|
@@ -1287,6 +1300,7 @@ registerTool({
|
|
|
1287
1300
|
+ 'Writing NROB number-range intervals via ADT requires NR-object XML format '
|
|
1288
1301
|
+ 'not yet implemented in @cspeach/sap-client.',
|
|
1289
1302
|
isMutating: true,
|
|
1303
|
+
requiresSap: true,
|
|
1290
1304
|
input_schema: {
|
|
1291
1305
|
type: 'object',
|
|
1292
1306
|
properties: {
|
package/dist/tools/snapshot.js
CHANGED
|
@@ -30,6 +30,9 @@ registerTool({
|
|
|
30
30
|
name: 'sap_snapshot_take',
|
|
31
31
|
description: "Take a snapshot of an object's current source before modification. Usually invoked automatically by the CLI before any write — rarely called directly by the model.",
|
|
32
32
|
isMutating: false,
|
|
33
|
+
// Reads the live source via ctx.adt.getSource — the only snapshot tool that
|
|
34
|
+
// needs SAP. list / read / cleanup work purely on the local snapshot store.
|
|
35
|
+
requiresSap: true,
|
|
33
36
|
input_schema: {
|
|
34
37
|
type: 'object',
|
|
35
38
|
properties: {
|
package/dist/tools/transport.js
CHANGED
|
@@ -63,6 +63,7 @@ registerTool({
|
|
|
63
63
|
+ 'approval mismatch failure mode observed live 2026-05-07). '
|
|
64
64
|
+ 'Requires approval_id. Returns the transport number to use in subsequent writes.',
|
|
65
65
|
isMutating: true,
|
|
66
|
+
requiresSap: true,
|
|
66
67
|
input_schema: {
|
|
67
68
|
type: 'object',
|
|
68
69
|
properties: {
|
|
@@ -260,6 +261,7 @@ registerTool({
|
|
|
260
261
|
+ 'locked → use the returned owning transport; not locked → a new or '
|
|
261
262
|
+ 'existing transport may be used.',
|
|
262
263
|
isMutating: false,
|
|
264
|
+
requiresSap: true,
|
|
263
265
|
input_schema: {
|
|
264
266
|
type: 'object',
|
|
265
267
|
properties: {
|
|
@@ -330,6 +332,7 @@ registerTool({
|
|
|
330
332
|
description: 'List SAP CTS transport requests. '
|
|
331
333
|
+ 'Optionally filter by owner (user) or status (modifiable | released).',
|
|
332
334
|
isMutating: false,
|
|
335
|
+
requiresSap: true,
|
|
333
336
|
input_schema: {
|
|
334
337
|
type: 'object',
|
|
335
338
|
properties: {
|
|
@@ -377,6 +380,7 @@ registerTool({
|
|
|
377
380
|
+ 'All objects must be syntax-clean and ATC-clean before releasing (Rule 9). '
|
|
378
381
|
+ 'Requires approval_id.',
|
|
379
382
|
isMutating: true,
|
|
383
|
+
requiresSap: true,
|
|
380
384
|
input_schema: {
|
|
381
385
|
type: 'object',
|
|
382
386
|
properties: {
|
package/dist/ui/header.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { useSapState } from './sap-state-store.js';
|
|
4
|
+
import { formatSpendSegment } from './status-line.js';
|
|
4
5
|
import { CLI_VERSION } from '../lib/version.js';
|
|
5
6
|
const PEACH = '#FFCBA4';
|
|
6
7
|
/**
|
|
@@ -34,6 +35,7 @@ export function Header({ writeMode } = {}) {
|
|
|
34
35
|
const tokDisplay = s.tokenUsageToday >= 1000
|
|
35
36
|
? `${(s.tokenUsageToday / 1000).toFixed(1)}k tok`
|
|
36
37
|
: `${s.tokenUsageToday} tok`;
|
|
37
|
-
|
|
38
|
+
// Shared with <StatusLine /> so the credits/dollar switch has one home.
|
|
39
|
+
const spendDisplay = formatSpendSegment(s);
|
|
38
40
|
return (_jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: [_jsxs(Text, { bold: true, color: PEACH, children: ["\uD83C\uDF51 CSPeach ", CLI_VERSION] }), _jsx(Text, { color: "gray", children: " \u2502 " }), _jsx(Text, { color: "cyan", children: s.system || 'no system' }), _jsx(Text, { color: "gray", children: " \u2502 " }), _jsx(Text, { color: "white", children: s.user || '—' }), _jsx(Text, { color: "gray", children: " \u2502 tr " }), _jsx(Text, { color: "yellow", children: s.transport ?? 'none' }), _jsx(Text, { color: "gray", children: " \u2502 " }), _jsx(Text, { color: "green", children: tokDisplay }), _jsx(Text, { color: "gray", children: " \u2502 " }), _jsx(Text, { color: "green", children: spendDisplay }), modeLabel && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: " \u2502 " }), _jsx(Text, { color: modeColor, bold: isAdvisory, children: modeLabel })] }))] }));
|
|
39
41
|
}
|
|
@@ -15,6 +15,10 @@ const DEFAULT_STATE = {
|
|
|
15
15
|
system: '', user: '', transport: null, locksHeld: [],
|
|
16
16
|
tokenUsageToday: 0, dollarSpendToday: 0, dollarCap: 100, recentTurns: [],
|
|
17
17
|
writeMode: null, lastWriteTransport: null,
|
|
18
|
+
// Credits default OFF: BYOK/local/ai-hub and any pre-resolution frame keep
|
|
19
|
+
// the dollar rendering. Only an explicit managed+metered resolution flips it.
|
|
20
|
+
creditsMode: false, creditsSessionTotal: 0, creditsBalance: null,
|
|
21
|
+
creditsUnitLabel: 'credits', creditsPending: false,
|
|
18
22
|
};
|
|
19
23
|
/**
|
|
20
24
|
* Rate limiter: calls `fn` at most once every `minIntervalMs`. Trailing-only:
|
package/dist/ui/status-line.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Text } from 'ink';
|
|
3
3
|
import { useSapState } from './sap-state-store.js';
|
|
4
|
+
import { formatCredits, CREDITS_PENDING } from '../cost/credits.js';
|
|
5
|
+
/**
|
|
6
|
+
* The spend segment shared by <StatusLine /> and <Header />.
|
|
7
|
+
*
|
|
8
|
+
* Single home for the credits/dollar switch so the two chrome widgets can
|
|
9
|
+
* never drift apart — a drift here is exactly how our COGS would leak onto a
|
|
10
|
+
* managed customer's screen from one of them.
|
|
11
|
+
*/
|
|
12
|
+
export function formatSpendSegment(s) {
|
|
13
|
+
if (!s.creditsMode)
|
|
14
|
+
return `$${s.dollarSpendToday.toFixed(2)}`;
|
|
15
|
+
if (s.creditsPending && s.creditsSessionTotal === 0)
|
|
16
|
+
return CREDITS_PENDING;
|
|
17
|
+
return formatCredits(s.creditsSessionTotal, s.creditsUnitLabel);
|
|
18
|
+
}
|
|
4
19
|
/**
|
|
5
20
|
* Persistent SAP statusline (UX Wave 1, Task 4) — Header reborn,
|
|
6
21
|
* bottom-anchored. Single borderless line rendered between <TurnStatus />
|
|
@@ -25,7 +40,11 @@ const MODE_COLORS = {
|
|
|
25
40
|
};
|
|
26
41
|
export function StatusLine() {
|
|
27
42
|
const s = useSapState();
|
|
28
|
-
|
|
43
|
+
// Credits mode (2026-09-16): a managed customer sees what THEY are charged.
|
|
44
|
+
// The segment keeps its meaning — "what this session has cost me" — so it
|
|
45
|
+
// carries the session credit total, not the balance (the balance has its
|
|
46
|
+
// own home in the post-turn receipt and `cspeach doctor`).
|
|
47
|
+
const spendDisplay = formatSpendSegment(s);
|
|
29
48
|
// Wave1 live-fix (Bug 1) — one TRUNCATING <Text> instead of a <Box> of
|
|
30
49
|
// sibling Texts. The statusline lives in the dynamic Ink frame, where a
|
|
31
50
|
// row that soft-wraps in the terminal breaks log-update's logical-line
|
package/package.json
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@cspeach/cli",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "CSPeach — AI-assisted ABAP development CLI. Direct SAP access, 34 skills, safety gates.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"abap",
|
|
7
|
-
"sap",
|
|
8
|
-
"ai",
|
|
9
|
-
"cli",
|
|
10
|
-
"ai-assisted-development",
|
|
11
|
-
"s4hana",
|
|
12
|
-
"abap-development",
|
|
13
|
-
"claude",
|
|
14
|
-
"anthropic"
|
|
15
|
-
],
|
|
16
|
-
"homepage": "https://cspeach.dev",
|
|
17
|
-
"bugs": {
|
|
18
|
-
"email": "laeeq.siddique@cremencing.com"
|
|
19
|
-
},
|
|
20
|
-
"author": "Laeeq Siddique (Cremencing Solutions)",
|
|
21
|
-
"license": "SEE LICENSE IN LICENSE",
|
|
22
|
-
"private": false,
|
|
23
|
-
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
25
|
-
},
|
|
26
|
-
"type": "module",
|
|
27
|
-
"bin": {
|
|
28
|
-
"cspeach": "dist/index.js"
|
|
29
|
-
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"dev": "tsx src/index.ts",
|
|
32
|
-
"prebuild": "tsx scripts/prebuild.ts",
|
|
33
|
-
"build": "tsc && node scripts/make-bin-executable.js",
|
|
34
|
-
"install:global": "tsx scripts/install-global.ts",
|
|
35
|
-
"pretest": "tsx scripts/sync-signing-key.ts || node -e \"console.warn('WARN: sync-signing-key skipped — using stub')\"",
|
|
36
|
-
"start": "node dist/index.js",
|
|
37
|
-
"typecheck": "tsc --noEmit",
|
|
38
|
-
"test": "vitest run",
|
|
39
|
-
"test:watch": "vitest",
|
|
40
|
-
"adt:smoke": "tsx scripts/adt-smoke.ts",
|
|
41
|
-
"smoke:all": "pnpm zcspeach:smoke && pnpm adt:smoke",
|
|
42
|
-
"zcspeach:smoke": "tsx scripts/zcspeach-smoke.ts",
|
|
43
|
-
"bench:models": "tsx scripts/bench-models.ts",
|
|
44
|
-
"bench:audit": "tsx scripts/bench-audit-task.ts",
|
|
45
|
-
"fiori": "tsx src/tools/fiori/bin.ts",
|
|
46
|
-
"prepare": "husky",
|
|
47
|
-
"prepublishOnly": "node -e \"const d=(require('./package.json').dependencies||{})['@cspeach/sap-client']||''; if(d.startsWith('file:')||d.startsWith('link:')){console.error('publish blocked: @cspeach/sap-client is a local link ('+d+') — point it at the published version before npm publish'); process.exit(1)}\""
|
|
48
|
-
},
|
|
49
|
-
"engines": {
|
|
50
|
-
"node": ">=20.0.0"
|
|
51
|
-
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@anthropic-ai/sdk": "^0.90.0",
|
|
54
|
-
"@cspeach/sap-client": "^0.1.11",
|
|
55
|
-
"@iarna/toml": "^2.2.5",
|
|
56
|
-
"@inkjs/ui": "^2.0.0",
|
|
57
|
-
"@inquirer/prompts": "^7.0.0",
|
|
58
|
-
"@noble/ed25519": "2.1.0",
|
|
59
|
-
"@noble/hashes": "1.4.0",
|
|
60
|
-
"@sap-ux/fe-fpm-writer": "0.43.7",
|
|
61
|
-
"@sap-ux/fiori-elements-writer": "^3.0.39",
|
|
62
|
-
"@sap-ux/fiori-freestyle-writer": "3.0.12",
|
|
63
|
-
"chalk": "^5.3.0",
|
|
64
|
-
"cli-highlight": "^2.1.11",
|
|
65
|
-
"cli-table3": "^0.6.5",
|
|
66
|
-
"clipboardy": "^5.3.1",
|
|
67
|
-
"cross-spawn": "^7.0.6",
|
|
68
|
-
"diff": "^9.0.0",
|
|
69
|
-
"ejs": "3.1.10",
|
|
70
|
-
"fuse.js": "^7.0.0",
|
|
71
|
-
"ink": "^5.0.0",
|
|
72
|
-
"ink-text-input": "^6.0.0",
|
|
73
|
-
"jose": "^5.9.0",
|
|
74
|
-
"keytar": "^7.9.0",
|
|
75
|
-
"mammoth": "^1.8.0",
|
|
76
|
-
"marked": "^11.2.0",
|
|
77
|
-
"marked-terminal": "^6.2.0",
|
|
78
|
-
"mem-fs": "2.1.0",
|
|
79
|
-
"mem-fs-editor": "9.4.0",
|
|
80
|
-
"node-machine-id": "^1.1.12",
|
|
81
|
-
"openai": "4.67.3",
|
|
82
|
-
"puppeteer-core": "24.10.2",
|
|
83
|
-
"react": "^18.3.0",
|
|
84
|
-
"turndown": "^7.2.0",
|
|
85
|
-
"unpdf": "^0.12.1",
|
|
86
|
-
"zod": "^3.23.0"
|
|
87
|
-
},
|
|
88
|
-
"devDependencies": {
|
|
89
|
-
"@types/cross-spawn": "^6.0.6",
|
|
90
|
-
"@types/diff": "^7.0.2",
|
|
91
|
-
"@types/ejs": "^3.1.5",
|
|
92
|
-
"@types/node": "^20.17.0",
|
|
93
|
-
"@types/react": "^18.3.0",
|
|
94
|
-
"@types/turndown": "^5.0.5",
|
|
95
|
-
"docx": "^9.0.2",
|
|
96
|
-
"husky": "^9.1.7",
|
|
97
|
-
"ink-testing-library": "^4.0.0",
|
|
98
|
-
"pdf-lib": "^1.17.1",
|
|
99
|
-
"tsx": "^4.19.0",
|
|
100
|
-
"typescript": "^5.7.0",
|
|
101
|
-
"vitest": "^2.1.0"
|
|
102
|
-
},
|
|
103
|
-
"pnpm": {
|
|
104
|
-
"onlyBuiltDependencies": [
|
|
105
|
-
"keytar",
|
|
106
|
-
"esbuild"
|
|
107
|
-
]
|
|
108
|
-
}
|
|
109
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@cspeach/cli",
|
|
3
|
+
"version": "1.1.4",
|
|
4
|
+
"description": "CSPeach — AI-assisted ABAP development CLI. Direct SAP access, 34 skills, safety gates.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"abap",
|
|
7
|
+
"sap",
|
|
8
|
+
"ai",
|
|
9
|
+
"cli",
|
|
10
|
+
"ai-assisted-development",
|
|
11
|
+
"s4hana",
|
|
12
|
+
"abap-development",
|
|
13
|
+
"claude",
|
|
14
|
+
"anthropic"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://cspeach.dev",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"email": "laeeq.siddique@cremencing.com"
|
|
19
|
+
},
|
|
20
|
+
"author": "Laeeq Siddique (Cremencing Solutions)",
|
|
21
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
22
|
+
"private": false,
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"cspeach": "dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "tsx src/index.ts",
|
|
32
|
+
"prebuild": "tsx scripts/prebuild.ts",
|
|
33
|
+
"build": "tsc && node scripts/make-bin-executable.js",
|
|
34
|
+
"install:global": "tsx scripts/install-global.ts",
|
|
35
|
+
"pretest": "tsx scripts/sync-signing-key.ts || node -e \"console.warn('WARN: sync-signing-key skipped — using stub')\"",
|
|
36
|
+
"start": "node dist/index.js",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"adt:smoke": "tsx scripts/adt-smoke.ts",
|
|
41
|
+
"smoke:all": "pnpm zcspeach:smoke && pnpm adt:smoke",
|
|
42
|
+
"zcspeach:smoke": "tsx scripts/zcspeach-smoke.ts",
|
|
43
|
+
"bench:models": "tsx scripts/bench-models.ts",
|
|
44
|
+
"bench:audit": "tsx scripts/bench-audit-task.ts",
|
|
45
|
+
"fiori": "tsx src/tools/fiori/bin.ts",
|
|
46
|
+
"prepare": "husky",
|
|
47
|
+
"prepublishOnly": "node -e \"const d=(require('./package.json').dependencies||{})['@cspeach/sap-client']||''; if(d.startsWith('file:')||d.startsWith('link:')){console.error('publish blocked: @cspeach/sap-client is a local link ('+d+') — point it at the published version before npm publish'); process.exit(1)}\""
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.0.0"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@anthropic-ai/sdk": "^0.90.0",
|
|
54
|
+
"@cspeach/sap-client": "^0.1.11",
|
|
55
|
+
"@iarna/toml": "^2.2.5",
|
|
56
|
+
"@inkjs/ui": "^2.0.0",
|
|
57
|
+
"@inquirer/prompts": "^7.0.0",
|
|
58
|
+
"@noble/ed25519": "2.1.0",
|
|
59
|
+
"@noble/hashes": "1.4.0",
|
|
60
|
+
"@sap-ux/fe-fpm-writer": "0.43.7",
|
|
61
|
+
"@sap-ux/fiori-elements-writer": "^3.0.39",
|
|
62
|
+
"@sap-ux/fiori-freestyle-writer": "3.0.12",
|
|
63
|
+
"chalk": "^5.3.0",
|
|
64
|
+
"cli-highlight": "^2.1.11",
|
|
65
|
+
"cli-table3": "^0.6.5",
|
|
66
|
+
"clipboardy": "^5.3.1",
|
|
67
|
+
"cross-spawn": "^7.0.6",
|
|
68
|
+
"diff": "^9.0.0",
|
|
69
|
+
"ejs": "3.1.10",
|
|
70
|
+
"fuse.js": "^7.0.0",
|
|
71
|
+
"ink": "^5.0.0",
|
|
72
|
+
"ink-text-input": "^6.0.0",
|
|
73
|
+
"jose": "^5.9.0",
|
|
74
|
+
"keytar": "^7.9.0",
|
|
75
|
+
"mammoth": "^1.8.0",
|
|
76
|
+
"marked": "^11.2.0",
|
|
77
|
+
"marked-terminal": "^6.2.0",
|
|
78
|
+
"mem-fs": "2.1.0",
|
|
79
|
+
"mem-fs-editor": "9.4.0",
|
|
80
|
+
"node-machine-id": "^1.1.12",
|
|
81
|
+
"openai": "4.67.3",
|
|
82
|
+
"puppeteer-core": "24.10.2",
|
|
83
|
+
"react": "^18.3.0",
|
|
84
|
+
"turndown": "^7.2.0",
|
|
85
|
+
"unpdf": "^0.12.1",
|
|
86
|
+
"zod": "^3.23.0"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@types/cross-spawn": "^6.0.6",
|
|
90
|
+
"@types/diff": "^7.0.2",
|
|
91
|
+
"@types/ejs": "^3.1.5",
|
|
92
|
+
"@types/node": "^20.17.0",
|
|
93
|
+
"@types/react": "^18.3.0",
|
|
94
|
+
"@types/turndown": "^5.0.5",
|
|
95
|
+
"docx": "^9.0.2",
|
|
96
|
+
"husky": "^9.1.7",
|
|
97
|
+
"ink-testing-library": "^4.0.0",
|
|
98
|
+
"pdf-lib": "^1.17.1",
|
|
99
|
+
"tsx": "^4.19.0",
|
|
100
|
+
"typescript": "^5.7.0",
|
|
101
|
+
"vitest": "^2.1.0"
|
|
102
|
+
},
|
|
103
|
+
"pnpm": {
|
|
104
|
+
"onlyBuiltDependencies": [
|
|
105
|
+
"keytar",
|
|
106
|
+
"esbuild"
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
}
|