@cspeach/cli 1.1.49 → 1.1.50
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/cold-prune.js +1 -1
- package/dist/agent/conversation-prompt.js +52 -0
- package/dist/agent/loop.js +83 -2
- package/dist/agent/oversized-results.js +204 -0
- package/dist/agent/turn-error-ux.js +14 -0
- package/dist/commands/plan-continue-gate.js +11 -0
- package/dist/cost/cost-log.js +4 -0
- package/dist/cost/system-block.js +25 -0
- package/dist/models/server-config.js +13 -0
- package/dist/projects/workspace.js +6 -3
- package/dist/repl.js +16 -6
- package/dist/session/fresh-start.js +15 -0
- package/dist/tools/_filesystem-shared.js +7 -0
- package/dist/tools/cut-markers.js +47 -0
- package/dist/tools/filesystem/extract-document.js +50 -2
- package/dist/tools/filesystem/file-read.js +2 -2
- package/dist/tools/filesystem/read-document.js +20 -4
- package/dist/tools/tool-output-read.js +5 -1
- package/dist/tools/write-pipeline.js +5 -0
- package/package.json +121 -121
- package/dist/commands/project.js +0 -301
- package/dist/doctor/checks/zcspeach.js +0 -96
- package/dist/projects/email-template.js +0 -56
- package/dist/projects/merge-cca.js +0 -292
- package/dist/tools/local-files.js +0 -31
- package/dist/tools/project/cca_merge.js +0 -164
- package/dist/tools/subagent/schedule_create.js +0 -131
- package/dist/ui/error-emitter.js +0 -21
- package/dist/ui/header.js +0 -41
- package/dist/ui/ink-classifier-route.js +0 -19
- package/dist/ui/rich-error-box.js +0 -9
- package/dist/ui/sidebar.js +0 -12
- package/dist/ui/status-emitter.js +0 -19
- package/dist/ui/status-line.js +0 -62
- package/dist/ui/status-row.js +0 -38
- package/dist/ui/todo-panel.js +0 -85
- package/dist/ui/widgets/ask-form.js +0 -250
- package/dist/ui/widgets/ask-question-modal.js +0 -178
- package/fiori/zlas-inspection-lr/package.json +0 -26
- package/fiori/zlas-inspection-lr/ui5-local.yaml +0 -45
- package/fiori/zlas-inspection-lr/ui5.yaml +0 -41
- package/fiori/zlas-inspection-lr/webapp/Component.js +0 -12
- package/fiori/zlas-inspection-lr/webapp/i18n/i18n.properties +0 -9
- package/fiori/zlas-inspection-lr/webapp/index.html +0 -35
- package/fiori/zlas-inspection-lr/webapp/manifest.json +0 -145
- package/live-check-2026-10-17-1343.txt +0 -41
|
@@ -45,6 +45,13 @@ export const BLOCKED_BASENAME_RE = /^\.env(\.|$)/i;
|
|
|
45
45
|
* (skill detail files) — only the rest of `.cspeach/` is blocked.
|
|
46
46
|
*/
|
|
47
47
|
export const DENYLIST_DESCRIPTION = [...BLOCKED_PREFIXES, '.env*'].join(', ');
|
|
48
|
+
/**
|
|
49
|
+
* The paging trailer of file_read and read_document (also refused in SAP
|
|
50
|
+
* writes: tools/cut-markers.ts).
|
|
51
|
+
*/
|
|
52
|
+
export function rereadTrailer(more, nextOffset) {
|
|
53
|
+
return `... (truncated, ${more} more lines — re-read with offset=${nextOffset})`;
|
|
54
|
+
}
|
|
48
55
|
/**
|
|
49
56
|
* Executable / script file extensions the filesystem write tools refuse to
|
|
50
57
|
* create. Defense-in-depth: shell_exec / background_run only run a safelisted
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cut-note guard for SAP writes (2026-10-22, owner go).
|
|
3
|
+
*
|
|
4
|
+
* Large tool results reach the model shortened, each with a note of ours:
|
|
5
|
+
* - agent/oversized-results.ts — "[result cut: N more chars …]" and
|
|
6
|
+
* "[removed: this result was too large to send …]";
|
|
7
|
+
* - agent/cold-prune.ts — "[result elided at cold start — N chars]";
|
|
8
|
+
* - tools/result-spill.ts — the spill preview: the pointer line ("… full
|
|
9
|
+
* output saved to …; read slices with tool_output_read(…)"), the omitted
|
|
10
|
+
* line ("[… lines A–B omitted …]" / "[… N lines omitted — tool_output_read
|
|
11
|
+
* offset=K …]") and a clipped line ("… [+N chars]");
|
|
12
|
+
* - the paging trailers (tool_output_read, file_read, read_document) and the
|
|
13
|
+
* @file attachment note "... [truncated, file exceeds 64 KB]".
|
|
14
|
+
* A model that writes back what it saw would write those notes into SAP in
|
|
15
|
+
* place of the real code. The write pipeline refuses any write whose
|
|
16
|
+
* arguments carry one of them, before the approval is spent or SAP is
|
|
17
|
+
* called. Only our own notes: "[image N]" and other bracket text can be
|
|
18
|
+
* legitimate ABAP and are never refused. A write without them is untouched.
|
|
19
|
+
*/
|
|
20
|
+
export const CUT_NOTE_REFUSAL = 'This source still contains a note that part of an earlier read was cut or moved to a file. Read the full source first (in parts if needed), then write the complete text.';
|
|
21
|
+
const CUT_NOTE_RES = [
|
|
22
|
+
/\[result cut: \d+ more chars\./,
|
|
23
|
+
/\[removed: this result was too large to send — \d+ chars\./,
|
|
24
|
+
/\[result elided at cold start — \d+ chars\]/,
|
|
25
|
+
/full output saved to .*; read slices with tool_output_read\(path, offset, limit\)\]/,
|
|
26
|
+
/\[… lines \d+–\d+ omitted …\]/,
|
|
27
|
+
/\[… \d+ lines omitted — tool_output_read offset=\d+ …\]/,
|
|
28
|
+
/… \[\+\d+ chars\]/,
|
|
29
|
+
// tool-output-read.ts sliceTrailer; _filesystem-shared.ts rereadTrailer
|
|
30
|
+
// (file_read, read_document); workspace.ts ATTACHMENT_TRUNCATED_NOTE (@file).
|
|
31
|
+
/\.\.\. \(\d+ more lines — next offset=\d+\)/,
|
|
32
|
+
/\.\.\. \(truncated, \d+ more lines — re-read with offset=\d+\)/,
|
|
33
|
+
/\.\.\. \[truncated, file exceeds 64 KB\]/,
|
|
34
|
+
];
|
|
35
|
+
/** True when `text` carries one of our cut / removed / spill notes. */
|
|
36
|
+
export function hasCutNote(text) {
|
|
37
|
+
return CUT_NOTE_RES.some((re) => re.test(text));
|
|
38
|
+
}
|
|
39
|
+
/** True when any string anywhere in `value` (objects, arrays) carries a cut note. */
|
|
40
|
+
export function argsHaveCutNote(value, depth = 0) {
|
|
41
|
+
if (typeof value === 'string')
|
|
42
|
+
return hasCutNote(value);
|
|
43
|
+
if (depth > 8 || value === null || typeof value !== 'object')
|
|
44
|
+
return false;
|
|
45
|
+
const items = Array.isArray(value) ? value : Object.values(value);
|
|
46
|
+
return items.some((v) => argsHaveCutNote(v, depth + 1));
|
|
47
|
+
}
|
|
@@ -50,11 +50,59 @@ async function extractPdf(buffer, maxPages) {
|
|
|
50
50
|
}).join('\n\n');
|
|
51
51
|
return { text: body, pages: total, warnings: hasText ? [] : [NO_TEXT_WARNING] };
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Longest line a document read hands to the model. A longer one is split
|
|
55
|
+
* into consecutive lines, so paging by line reaches every character; a real
|
|
56
|
+
* line (a dense PDF page is a few thousand chars) fits well inside it.
|
|
57
|
+
*/
|
|
58
|
+
export const DOC_MAX_LINE_CHARS = 10_000;
|
|
59
|
+
/** Longest picture description kept in an `[image N: …]` marker. */
|
|
60
|
+
const IMAGE_ALT_MAX_CHARS = 120;
|
|
61
|
+
// 2026-10-22 customer bug — mammoth's default inlines every picture as a
|
|
62
|
+
// base64 data: URI (a 1.5 MB picture ≈ 670k tokens on ONE line), and the
|
|
63
|
+
// prompt was then too long for every later turn. Pictures never become text:
|
|
64
|
+
// mammoth gets an img converter that never reads the bytes, and turndown
|
|
65
|
+
// writes each <img> as a short numbered marker.
|
|
66
|
+
const NO_IMAGE_DATA = mammoth.images.imgElement(async () => ({ src: '' }));
|
|
67
|
+
/** Remove any data: URI left in the text (a linked or pasted picture). */
|
|
68
|
+
export function stripDataUris(text) {
|
|
69
|
+
return text.replace(/data:[a-z0-9.+\/-]*(?:;[a-z0-9=._-]+)*;base64,[a-z0-9+\/=]*/gi, '');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Split every line longer than `maxLineChars` into consecutive lines of at
|
|
73
|
+
* most `maxLineChars`, so nothing is lost. Text whose lines all fit is
|
|
74
|
+
* returned unchanged (the same string).
|
|
75
|
+
*/
|
|
76
|
+
export function splitLongLines(text, maxLineChars) {
|
|
77
|
+
const lines = text.split('\n');
|
|
78
|
+
if (!lines.some((l) => l.length > maxLineChars))
|
|
79
|
+
return text;
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const l of lines) {
|
|
82
|
+
if (l.length <= maxLineChars) {
|
|
83
|
+
out.push(l);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
for (let i = 0; i < l.length; i += maxLineChars)
|
|
87
|
+
out.push(l.slice(i, i + maxLineChars));
|
|
88
|
+
}
|
|
89
|
+
return out.join('\n');
|
|
90
|
+
}
|
|
53
91
|
async function extractDocx(buffer) {
|
|
54
92
|
// Fix 2: esModuleInterop:true makes the default import correct — no interop guard needed.
|
|
55
|
-
const { value: html } = await mammoth.convertToHtml({ buffer });
|
|
93
|
+
const { value: html } = await mammoth.convertToHtml({ buffer }, { convertImage: NO_IMAGE_DATA });
|
|
56
94
|
const td = new TurndownService({ headingStyle: 'atx', bulletListMarker: '-' });
|
|
57
|
-
|
|
95
|
+
let images = 0;
|
|
96
|
+
td.addRule('pictureMarker', {
|
|
97
|
+
filter: 'img',
|
|
98
|
+
replacement: (_content, node) => {
|
|
99
|
+
images++;
|
|
100
|
+
const alt = (node.getAttribute('alt') ?? '')
|
|
101
|
+
.replace(/\s+/g, ' ').trim().slice(0, IMAGE_ALT_MAX_CHARS).trim();
|
|
102
|
+
return alt ? `[image ${images}: ${alt}]` : `[image ${images}]`;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const raw = stripDataUris(td.turndown(html)).trim();
|
|
58
106
|
// Turndown emits "- text" (3 spaces) for bullet items; collapse to "- text".
|
|
59
107
|
// The regex anchors on a line-leading "-" so mid-line double spaces (e.g. "Col A Col B")
|
|
60
108
|
// and indented nested bullets (leading whitespace is captured in $1) are both preserved.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { promises as fs } from 'node:fs';
|
|
12
12
|
import { registerTool } from '../index.js';
|
|
13
|
-
import { resolveSafePath, assertRealPathContained, PathOutsideRootError, DENYLIST_DESCRIPTION, isDenylistedPath, isReadOnlyProjectFile } from '../_filesystem-shared.js';
|
|
13
|
+
import { resolveSafePath, assertRealPathContained, PathOutsideRootError, DENYLIST_DESCRIPTION, isDenylistedPath, isReadOnlyProjectFile, rereadTrailer } from '../_filesystem-shared.js';
|
|
14
14
|
import { teamReadTarget } from '../_team-work-path.js';
|
|
15
15
|
import { spillToolResult, isUnderSpillRoot } from '../result-spill.js';
|
|
16
16
|
import { basename } from 'node:path';
|
|
@@ -74,7 +74,7 @@ export async function fileReadHandler(args, ctx) {
|
|
|
74
74
|
const numbered = window.map((line, i) => `${offset + i + 1}\t${line}`).join('\n');
|
|
75
75
|
const truncated = allLines.length > offset + window.length;
|
|
76
76
|
const trailer = truncated
|
|
77
|
-
? `\n
|
|
77
|
+
? `\n${rereadTrailer(allLines.length - (offset + window.length), offset + window.length)}`
|
|
78
78
|
: '';
|
|
79
79
|
// Task 17 (spec D12) — a window above 8 000 chars is saved to the session
|
|
80
80
|
// spill file and previewed; today's trailer stays last, byte-identical. A
|
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
import { promises as fs } from 'node:fs';
|
|
8
8
|
import * as path from 'node:path';
|
|
9
9
|
import { registerTool } from '../index.js';
|
|
10
|
-
import { resolveSafePath, assertRealPathContained, PathOutsideRootError, DENYLIST_DESCRIPTION, isDenylistedPath, } from '../_filesystem-shared.js';
|
|
10
|
+
import { resolveSafePath, assertRealPathContained, PathOutsideRootError, DENYLIST_DESCRIPTION, isDenylistedPath, rereadTrailer, } from '../_filesystem-shared.js';
|
|
11
11
|
import { teamWorkTarget } from '../_team-work-path.js';
|
|
12
|
-
import { extractDocument, DocumentTooLargeError } from './extract-document.js';
|
|
12
|
+
import { extractDocument, DocumentTooLargeError, splitLongLines, DOC_MAX_LINE_CHARS, } from './extract-document.js';
|
|
13
13
|
const MAX_LINES = 2000;
|
|
14
|
+
// 2026-10-22 customer bug — one read returned ~2.3M tokens and every later
|
|
15
|
+
// turn of the session failed. A read now has a hard size: long lines are split
|
|
16
|
+
// (DOC_MAX_LINE_CHARS) and one call returns at most DOC_MAX_CHARS_PER_READ
|
|
17
|
+
// chars (about 60k tokens); the offset trailer says where to read on.
|
|
18
|
+
export const DOC_MAX_CHARS_PER_READ = 200_000;
|
|
14
19
|
const MAX_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
15
20
|
const MAX_PAGES = 150;
|
|
16
21
|
const SUPPORTED = new Set(['.pdf', '.docx', '.xlsx']);
|
|
@@ -89,14 +94,25 @@ export async function readDocumentHandler(args, ctx) {
|
|
|
89
94
|
: 'document';
|
|
90
95
|
const header = `${args.path} — ${scale}`;
|
|
91
96
|
const warnLines = result.warnings.map((w) => `[warning] ${w}`);
|
|
92
|
-
const allLines = result.text.replace(/\n$/, '').split('\n');
|
|
97
|
+
const allLines = splitLongLines(result.text.replace(/\n$/, ''), DOC_MAX_LINE_CHARS).split('\n');
|
|
93
98
|
const offset = Math.max(0, args.offset ?? 0);
|
|
94
99
|
const limit = Math.min(args.limit ?? MAX_LINES, MAX_LINES);
|
|
95
100
|
const window = allLines.slice(offset, offset + limit);
|
|
101
|
+
// Total cap: stop before the line that would pass it (always keep one line).
|
|
102
|
+
let used = 0;
|
|
103
|
+
let fit = 0;
|
|
104
|
+
for (const line of window) {
|
|
105
|
+
const cost = `${offset + fit + 1}\t${line}`.length + 1;
|
|
106
|
+
if (fit > 0 && used + cost > DOC_MAX_CHARS_PER_READ)
|
|
107
|
+
break;
|
|
108
|
+
used += cost;
|
|
109
|
+
fit++;
|
|
110
|
+
}
|
|
111
|
+
window.length = fit;
|
|
96
112
|
const numbered = window.map((line, i) => `${offset + i + 1}\t${line}`).join('\n');
|
|
97
113
|
const truncated = allLines.length > offset + window.length;
|
|
98
114
|
const trailer = truncated
|
|
99
|
-
? `\n
|
|
115
|
+
? `\n${rereadTrailer(allLines.length - (offset + window.length), offset + window.length)}`
|
|
100
116
|
: '';
|
|
101
117
|
return { content: [header, ...warnLines, numbered].join('\n') + trailer };
|
|
102
118
|
}
|
|
@@ -95,7 +95,11 @@ export async function toolOutputReadHandler(args, ctx) {
|
|
|
95
95
|
next = i + 1;
|
|
96
96
|
}
|
|
97
97
|
const rest = all.length - next;
|
|
98
|
-
return { content: out.join('\n') + (rest > 0 ? `\n
|
|
98
|
+
return { content: out.join('\n') + (rest > 0 ? `\n${sliceTrailer(rest, next)}` : '') };
|
|
99
|
+
}
|
|
100
|
+
/** The slice trailer (also refused in SAP writes: tools/cut-markers.ts). */
|
|
101
|
+
export function sliceTrailer(rest, next) {
|
|
102
|
+
return `... (${rest} more lines — next offset=${next})`;
|
|
99
103
|
}
|
|
100
104
|
registerTool({
|
|
101
105
|
name: 'tool_output_read',
|
|
@@ -44,6 +44,7 @@ import { resolveWriteTransport } from './transport-resolution.js';
|
|
|
44
44
|
import { noteWriteTransport } from '../repl/current-transport.js';
|
|
45
45
|
import { presentSafetyConfirmation } from '../repl/safety-confirm.js';
|
|
46
46
|
import { isSafetyConfirmEnabled } from '../repl/safety-mode-state.js';
|
|
47
|
+
import { argsHaveCutNote, CUT_NOTE_REFUSAL } from './cut-markers.js';
|
|
47
48
|
// ── Transcript visibility helpers (D16-display + tour slide-7, 2026-06-12) ──
|
|
48
49
|
//
|
|
49
50
|
// Rule 7 auto-snapshots always fired but the transcript never said so
|
|
@@ -334,6 +335,10 @@ function auditOutcome(syntax, stillExists, postCheckError) {
|
|
|
334
335
|
*/
|
|
335
336
|
export async function runWritePipeline(args, ctx, spec) {
|
|
336
337
|
const raw = args;
|
|
338
|
+
// ── 2026-10-22 — never write our own cut / spill notes into SAP ────────────
|
|
339
|
+
// First, before the approval is spent or SAP is called (cut-markers.ts).
|
|
340
|
+
if (argsHaveCutNote(args))
|
|
341
|
+
return refuse({ error: 'source_has_cut_note', detail: CUT_NOTE_REFUSAL });
|
|
337
342
|
// ── Before the approval is spent (input / capability checks) ──────────────
|
|
338
343
|
if (spec.beforeApproval) {
|
|
339
344
|
const refusal = spec.beforeApproval(args, ctx);
|
package/package.json
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@cspeach/cli",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "CSPeach — AI-assisted ABAP development CLI. Direct SAP access, signed 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
|
-
"test:smoke-e2e": "vitest run --config vitest.smoke-e2e.config.ts",
|
|
41
|
-
"adt:smoke": "tsx scripts/adt-smoke.ts",
|
|
42
|
-
"smoke:all": "pnpm adt:smoke",
|
|
43
|
-
"bench:models": "tsx scripts/bench-models.ts",
|
|
44
|
-
"bench:audit": "tsx scripts/bench-audit-task.ts",
|
|
45
|
-
"bench:replay": "tsx bench/replay/run.ts",
|
|
46
|
-
"fiori": "tsx src/tools/fiori/bin.ts",
|
|
47
|
-
"prepare": "husky",
|
|
48
|
-
"prepublishOnly": "node -e \"const deps=require('./package.json').dependencies||{}; for (const n of ['@cspeach/sap-client','@cspeach/register-core']) { const d=deps[n]||''; if(d.startsWith('file:')||d.startsWith('link:')){console.error('publish blocked: '+n+' is a local link ('+d+') — point it at the published version before npm publish'); process.exit(1)} }\""
|
|
49
|
-
},
|
|
50
|
-
"engines": {
|
|
51
|
-
"node": ">=20.0.0"
|
|
52
|
-
},
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"@anthropic-ai/sdk": "^0.90.0",
|
|
55
|
-
"@azure/msal-node": "6.0.1",
|
|
56
|
-
"@cspeach/register-core": "0.4.2",
|
|
57
|
-
"@cspeach/sap-client": "0.1.
|
|
58
|
-
"@iarna/toml": "^2.2.5",
|
|
59
|
-
"@inkjs/ui": "^2.0.0",
|
|
60
|
-
"@inquirer/prompts": "^7.0.0",
|
|
61
|
-
"@napi-rs/keyring": "2.1.0",
|
|
62
|
-
"@noble/ed25519": "2.1.0",
|
|
63
|
-
"@noble/hashes": "1.4.0",
|
|
64
|
-
"@sap-ux/annotation-converter": "0.10.21",
|
|
65
|
-
"@sap-ux/edmx-parser": "0.10.0",
|
|
66
|
-
"@sap-ux/fe-fpm-writer": "0.43.7",
|
|
67
|
-
"@sap-ux/fiori-elements-writer": "^3.0.39",
|
|
68
|
-
"@sap-ux/fiori-freestyle-writer": "3.0.12",
|
|
69
|
-
"@sap-ux/vocabularies-types": "0.15.0",
|
|
70
|
-
"adm-zip": "0.5.17",
|
|
71
|
-
"chalk": "^5.3.0",
|
|
72
|
-
"cli-highlight": "^2.1.11",
|
|
73
|
-
"cli-table3": "^0.6.5",
|
|
74
|
-
"clipboardy": "^5.3.1",
|
|
75
|
-
"cross-spawn": "^7.0.6",
|
|
76
|
-
"diff": "^9.0.0",
|
|
77
|
-
"docx": "^9.0.2",
|
|
78
|
-
"ejs": "3.1.10",
|
|
79
|
-
"fuse.js": "^7.0.0",
|
|
80
|
-
"ink": "^5.0.0",
|
|
81
|
-
"ink-text-input": "^6.0.0",
|
|
82
|
-
"jose": "^5.9.0",
|
|
83
|
-
"mammoth": "^1.8.0",
|
|
84
|
-
"marked": "^11.2.0",
|
|
85
|
-
"marked-terminal": "^6.2.0",
|
|
86
|
-
"mem-fs": "2.1.0",
|
|
87
|
-
"mem-fs-editor": "9.4.0",
|
|
88
|
-
"node-machine-id": "^1.1.12",
|
|
89
|
-
"openai": "^6.49.0",
|
|
90
|
-
"puppeteer-core": "24.10.2",
|
|
91
|
-
"react": "^18.3.0",
|
|
92
|
-
"string-width": "^7.2.0",
|
|
93
|
-
"turndown": "^7.2.0",
|
|
94
|
-
"unpdf": "1.7.0",
|
|
95
|
-
"zod": "^3.23.0"
|
|
96
|
-
},
|
|
97
|
-
"devDependencies": {
|
|
98
|
-
"@sap-ux/ui5-middleware-fe-mockserver": "2.4.17",
|
|
99
|
-
"@types/adm-zip": "0.5.8",
|
|
100
|
-
"@types/cross-spawn": "^6.0.6",
|
|
101
|
-
"@types/diff": "^7.0.2",
|
|
102
|
-
"@types/ejs": "^3.1.5",
|
|
103
|
-
"@types/node": "^20.17.0",
|
|
104
|
-
"@types/react": "^18.3.0",
|
|
105
|
-
"@types/turndown": "^5.0.5",
|
|
106
|
-
"@ui5/builder": "4.3.2",
|
|
107
|
-
"@ui5/fs": "4.0.6",
|
|
108
|
-
"husky": "^9.1.7",
|
|
109
|
-
"ink-testing-library": "^4.0.0",
|
|
110
|
-
"pdf-lib": "^1.17.1",
|
|
111
|
-
"strip-ansi": "6.0.1",
|
|
112
|
-
"tsx": "^4.19.0",
|
|
113
|
-
"typescript": "^5.7.0",
|
|
114
|
-
"vitest": "^2.1.0"
|
|
115
|
-
},
|
|
116
|
-
"pnpm": {
|
|
117
|
-
"onlyBuiltDependencies": [
|
|
118
|
-
"esbuild"
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@cspeach/cli",
|
|
3
|
+
"version": "1.1.50",
|
|
4
|
+
"description": "CSPeach — AI-assisted ABAP development CLI. Direct SAP access, signed 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
|
+
"test:smoke-e2e": "vitest run --config vitest.smoke-e2e.config.ts",
|
|
41
|
+
"adt:smoke": "tsx scripts/adt-smoke.ts",
|
|
42
|
+
"smoke:all": "pnpm adt:smoke",
|
|
43
|
+
"bench:models": "tsx scripts/bench-models.ts",
|
|
44
|
+
"bench:audit": "tsx scripts/bench-audit-task.ts",
|
|
45
|
+
"bench:replay": "tsx bench/replay/run.ts",
|
|
46
|
+
"fiori": "tsx src/tools/fiori/bin.ts",
|
|
47
|
+
"prepare": "husky",
|
|
48
|
+
"prepublishOnly": "node -e \"const deps=require('./package.json').dependencies||{}; for (const n of ['@cspeach/sap-client','@cspeach/register-core']) { const d=deps[n]||''; if(d.startsWith('file:')||d.startsWith('link:')){console.error('publish blocked: '+n+' is a local link ('+d+') — point it at the published version before npm publish'); process.exit(1)} }\""
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@anthropic-ai/sdk": "^0.90.0",
|
|
55
|
+
"@azure/msal-node": "6.0.1",
|
|
56
|
+
"@cspeach/register-core": "0.4.2",
|
|
57
|
+
"@cspeach/sap-client": "0.1.30",
|
|
58
|
+
"@iarna/toml": "^2.2.5",
|
|
59
|
+
"@inkjs/ui": "^2.0.0",
|
|
60
|
+
"@inquirer/prompts": "^7.0.0",
|
|
61
|
+
"@napi-rs/keyring": "2.1.0",
|
|
62
|
+
"@noble/ed25519": "2.1.0",
|
|
63
|
+
"@noble/hashes": "1.4.0",
|
|
64
|
+
"@sap-ux/annotation-converter": "0.10.21",
|
|
65
|
+
"@sap-ux/edmx-parser": "0.10.0",
|
|
66
|
+
"@sap-ux/fe-fpm-writer": "0.43.7",
|
|
67
|
+
"@sap-ux/fiori-elements-writer": "^3.0.39",
|
|
68
|
+
"@sap-ux/fiori-freestyle-writer": "3.0.12",
|
|
69
|
+
"@sap-ux/vocabularies-types": "0.15.0",
|
|
70
|
+
"adm-zip": "0.5.17",
|
|
71
|
+
"chalk": "^5.3.0",
|
|
72
|
+
"cli-highlight": "^2.1.11",
|
|
73
|
+
"cli-table3": "^0.6.5",
|
|
74
|
+
"clipboardy": "^5.3.1",
|
|
75
|
+
"cross-spawn": "^7.0.6",
|
|
76
|
+
"diff": "^9.0.0",
|
|
77
|
+
"docx": "^9.0.2",
|
|
78
|
+
"ejs": "3.1.10",
|
|
79
|
+
"fuse.js": "^7.0.0",
|
|
80
|
+
"ink": "^5.0.0",
|
|
81
|
+
"ink-text-input": "^6.0.0",
|
|
82
|
+
"jose": "^5.9.0",
|
|
83
|
+
"mammoth": "^1.8.0",
|
|
84
|
+
"marked": "^11.2.0",
|
|
85
|
+
"marked-terminal": "^6.2.0",
|
|
86
|
+
"mem-fs": "2.1.0",
|
|
87
|
+
"mem-fs-editor": "9.4.0",
|
|
88
|
+
"node-machine-id": "^1.1.12",
|
|
89
|
+
"openai": "^6.49.0",
|
|
90
|
+
"puppeteer-core": "24.10.2",
|
|
91
|
+
"react": "^18.3.0",
|
|
92
|
+
"string-width": "^7.2.0",
|
|
93
|
+
"turndown": "^7.2.0",
|
|
94
|
+
"unpdf": "1.7.0",
|
|
95
|
+
"zod": "^3.23.0"
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"@sap-ux/ui5-middleware-fe-mockserver": "2.4.17",
|
|
99
|
+
"@types/adm-zip": "0.5.8",
|
|
100
|
+
"@types/cross-spawn": "^6.0.6",
|
|
101
|
+
"@types/diff": "^7.0.2",
|
|
102
|
+
"@types/ejs": "^3.1.5",
|
|
103
|
+
"@types/node": "^20.17.0",
|
|
104
|
+
"@types/react": "^18.3.0",
|
|
105
|
+
"@types/turndown": "^5.0.5",
|
|
106
|
+
"@ui5/builder": "4.3.2",
|
|
107
|
+
"@ui5/fs": "4.0.6",
|
|
108
|
+
"husky": "^9.1.7",
|
|
109
|
+
"ink-testing-library": "^4.0.0",
|
|
110
|
+
"pdf-lib": "^1.17.1",
|
|
111
|
+
"strip-ansi": "6.0.1",
|
|
112
|
+
"tsx": "^4.19.0",
|
|
113
|
+
"typescript": "^5.7.0",
|
|
114
|
+
"vitest": "^2.1.0"
|
|
115
|
+
},
|
|
116
|
+
"pnpm": {
|
|
117
|
+
"onlyBuiltDependencies": [
|
|
118
|
+
"esbuild"
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
}
|