@cspeach/cli 1.1.27 → 1.1.29
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 +33 -4
- package/dist/agent/provider-shape.js +9 -2
- package/dist/agent/providers/ai-hub-provider.js +2 -2
- package/dist/agent/providers/byok-provider.js +2 -2
- package/dist/agent/providers/local-provider.js +4 -4
- package/dist/agent/system-prompt.js +34 -7
- package/dist/agent/tool-dispatch.js +20 -1
- package/dist/agent/turn-error-ux.js +40 -0
- package/dist/approvals/grant-claims.js +70 -0
- package/dist/approvals/jwt.js +21 -1
- package/dist/approvals/plan-grant.js +297 -0
- package/dist/commands/config-set.js +12 -0
- package/dist/commands/deploy-card.js +10 -5
- package/dist/commands/export-audit.js +1 -1
- package/dist/commands/plan-audit-evidence.js +47 -1
- package/dist/commands/plan-audit.js +23 -8
- package/dist/commands/plan-batch-audit.js +349 -0
- package/dist/commands/plan-boundary.js +88 -0
- package/dist/commands/plan-chain.js +409 -566
- package/dist/commands/plan-continue.js +21 -7
- package/dist/commands/plan-gate.js +28 -49
- package/dist/commands/plan-reground.js +174 -0
- package/dist/commands/plan-resume.js +227 -103
- package/dist/commands/plan-sap-pause.js +105 -0
- package/dist/commands/plan-startup.js +218 -0
- package/dist/config/loader.js +9 -0
- package/dist/doctor/checks/forge-rules.js +67 -85
- package/dist/doctor/run.js +4 -4
- package/dist/lane/app-chain.js +88 -9
- package/dist/one-shot.js +1 -0
- package/dist/projects/extract-plan.js +34 -1
- package/dist/projects/handover-md.js +1 -1
- package/dist/projects/plan-objects.js +75 -0
- package/dist/projects/plan-run.js +74 -18
- package/dist/projects/plan-schema.js +6 -0
- package/dist/projects/plan-sources.js +199 -0
- package/dist/projects/promote-command.js +1 -1
- package/dist/projects/save-command.js +11 -13
- package/dist/projects/saved-lines.js +36 -0
- package/dist/renderer/checklist-format.js +5 -5
- package/dist/renderer/machine-blocks.js +141 -0
- package/dist/renderer/tool-labels.js +3 -0
- package/dist/repl/plan-hop.js +22 -0
- package/dist/repl/rule8-detector.js +16 -1
- package/dist/repl.js +320 -55
- package/dist/router/router-core.js +2 -2
- package/dist/sap/reachability-state.js +25 -0
- package/dist/session/audit-export.js +10 -16
- package/dist/session/recap.js +16 -26
- package/dist/skills/bundled-skills.js +81 -81
- package/dist/tools/_filesystem-shared.js +37 -0
- package/dist/tools/approval.js +48 -2
- package/dist/tools/dispatch-skill.js +3 -2
- package/dist/tools/filesystem/file-read.js +5 -3
- package/dist/tools/filesystem/glob.js +27 -4
- package/dist/tools/fiori/tools.js +6 -0
- package/dist/tools/lane/app-lane-resolve.js +1 -1
- package/dist/tools/sap-read.js +37 -3
- package/dist/tools/sap-write.js +65 -0
- package/dist/tools/text-elements/pool.js +250 -0
- package/dist/tools/text-elements/tools.js +433 -0
- package/dist/tools/tool-loading.js +3 -0
- package/dist/tools/transport.js +26 -5
- package/dist/tools/write-mode.js +4 -3
- package/dist/ui/app.js +4 -0
- package/dist/ui/widgets/hold-raw-mode.js +26 -0
- package/package.json +120 -121
- package/dist/projects/email-template.js +0 -56
|
@@ -36,7 +36,7 @@ export const ROUTER_SCHEMA_VERSION = 1;
|
|
|
36
36
|
* its own bundled copy — so a stale published manifest never rolls a newer
|
|
37
37
|
* CLI back to older routing. router-revision-pin.test.ts fails until bumped.
|
|
38
38
|
*/
|
|
39
|
-
export const ROUTER_REVISION =
|
|
39
|
+
export const ROUTER_REVISION = 2;
|
|
40
40
|
// 2026-06-12: keep in lockstep with .claude/skills (35 abap skills).
|
|
41
41
|
// abap-dump replaced abap-dump-analyze + abap-dump-triage (portfolio merge);
|
|
42
42
|
// added previously-missing abap-plan, abap-fiori-build, abap-cca-merge, abap-upgrade-merge.
|
|
@@ -74,7 +74,7 @@ export const SKILL_DESCRIPTIONS = {
|
|
|
74
74
|
'abap-incident': 'End-to-end incident pipeline — from short dump to fix deployed and documented.',
|
|
75
75
|
'abap-migrate': 'ECC→S/4HANA migration assessment for custom objects — API analysis, successor lookup, per-object effort.',
|
|
76
76
|
'abap-modernize': 'Transform a CLASSIC program into a modern architecture (e.g. report → Fiori Elements / RAP) for the release level.',
|
|
77
|
-
'abap-plan': 'Plan and run a multi-
|
|
77
|
+
'abap-plan': 'Plan and run a multi-step SAP project — one approval of the plan and its object list, then the steps run back to back; the plan file carries state across sessions.',
|
|
78
78
|
'abap-preflight': 'Pre-release checklist for a change set — dependencies, tests, rollback, readiness.',
|
|
79
79
|
'abap-rap': 'Scaffold a complete RAP stack from scratch — table, CDS interface+projection, BDEF, behavior class, service definition+binding.',
|
|
80
80
|
'abap-refactor': 'Modify / improve / add a feature to EXISTING custom ABAP code in place — restructure, clean up, or change a Z/Y program/class (incl. small feature changes like adding output coloring).',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// cspeach-cli/src/sap/reachability-state.ts
|
|
2
|
+
//
|
|
3
|
+
// 2026-10-04 (continuous run, spec §3.5) — set when a SAP tool fails because
|
|
4
|
+
// the SAP host is not reachable from this machine; read (and cleared) by the
|
|
5
|
+
// REPL after a plan turn so the build pauses instead of failing.
|
|
6
|
+
let unreachable = null;
|
|
7
|
+
const CODE_RE = /\b(ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|ENETUNREACH|ECONNRESET|EAI_AGAIN)\b/;
|
|
8
|
+
export const SAP_UNREACHABLE_DIRECTIVE = 'SAP is not reachable from this machine right now. Stop here: do not retry SAP tools and do not write the plan manifest. '
|
|
9
|
+
+ 'End the turn with one short sentence. CSPeach pauses the build and runs this step again when the network is back.';
|
|
10
|
+
export function noteSapUnreachable(code) {
|
|
11
|
+
unreachable = code;
|
|
12
|
+
}
|
|
13
|
+
/** True while a SAP-unreachable failure is noted for this turn (does not clear it). */
|
|
14
|
+
export function isSapUnreachableNoted() {
|
|
15
|
+
return unreachable !== null;
|
|
16
|
+
}
|
|
17
|
+
export function getAndResetSapUnreachable() {
|
|
18
|
+
const c = unreachable;
|
|
19
|
+
unreachable = null;
|
|
20
|
+
return c;
|
|
21
|
+
}
|
|
22
|
+
export function unreachableCodeInText(text) {
|
|
23
|
+
const m = CODE_RE.exec(String(text ?? ''));
|
|
24
|
+
return m ? m[1] : null;
|
|
25
|
+
}
|
|
@@ -45,6 +45,7 @@ const WRITE_TOOLS = new Set([
|
|
|
45
45
|
'sap_delete_object',
|
|
46
46
|
'sap_create_object',
|
|
47
47
|
'sap_set_class_include',
|
|
48
|
+
'sap_set_text_elements',
|
|
48
49
|
'sap_create_domain',
|
|
49
50
|
'sap_create_data_element',
|
|
50
51
|
'sap_activate',
|
|
@@ -277,18 +278,11 @@ function outcomeCell(c) {
|
|
|
277
278
|
}
|
|
278
279
|
// ── Operator identity ─────────────────────────────────────────────────────
|
|
279
280
|
//
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// are never conflated. (Inlined rather than importing agent/loop.js to keep
|
|
286
|
-
// this a dependency-light pure builder.)
|
|
287
|
-
function resolveOperator() {
|
|
288
|
-
return (process.env.CSPEACH_AUTHOR_NAME ||
|
|
289
|
-
process.env.USER ||
|
|
290
|
-
process.env.USERNAME ||
|
|
291
|
-
'unknown');
|
|
281
|
+
// 2026-10-04 — the person who RAN CSPeach, as CSPeach knows them: the signed-in
|
|
282
|
+
// account (from /v1/me), else an explicit CSPEACH_AUTHOR_NAME, else "not
|
|
283
|
+
// signed in". NEVER the OS account name (USER/USERNAME printed "Admin").
|
|
284
|
+
function resolveOperator(operator) {
|
|
285
|
+
return operator?.trim() || process.env.CSPEACH_AUTHOR_NAME?.trim() || 'not signed in (local run)';
|
|
292
286
|
}
|
|
293
287
|
// ── Transports seen ───────────────────────────────────────────────────────
|
|
294
288
|
function transportsSeen(toolCalls, sessionTransport) {
|
|
@@ -320,7 +314,7 @@ function transportsSeen(toolCalls, sessionTransport) {
|
|
|
320
314
|
}
|
|
321
315
|
// ── Main builder ──────────────────────────────────────────────────────────
|
|
322
316
|
export function buildAuditReport(input) {
|
|
323
|
-
const { session, costEntries, transport, credits } = input;
|
|
317
|
+
const { session, costEntries, transport, credits, operator } = input;
|
|
324
318
|
const calls = session.toolCalls;
|
|
325
319
|
const completed = calls.filter((c) => c.completed === true);
|
|
326
320
|
const approvals = parseApprovals(calls);
|
|
@@ -344,14 +338,14 @@ export function buildAuditReport(input) {
|
|
|
344
338
|
L();
|
|
345
339
|
L(`- **Session:** \`${session.id}\``);
|
|
346
340
|
L(`- **SAP system:** ${session.sap_system ?? '—'}`);
|
|
347
|
-
L(`- **Operator (
|
|
341
|
+
L(`- **Operator (CSPeach sign-in):** ${resolveOperator(operator)}`);
|
|
348
342
|
L(`- **Period:** ${dateFrom} → ${dateTo}`);
|
|
349
343
|
const trs = transportsSeen(calls, transport);
|
|
350
344
|
L(`- **Transport(s):** ${trs.length ? trs.map((t) => `\`${t}\``).join(', ') : '—'}`);
|
|
351
345
|
L();
|
|
352
346
|
L('> Scope: this record covers SAP-system mutations only — local file and');
|
|
353
|
-
L('> shell operations are excluded by design. Operator is the
|
|
354
|
-
L('>
|
|
347
|
+
L('> shell operations are excluded by design. Operator is the CSPeach sign-in');
|
|
348
|
+
L('> of the person who ran the session; the SAP logon user is not recorded in');
|
|
355
349
|
L('> session state. Plan audit states, if any, live in the plan handover,');
|
|
356
350
|
L('> not this report.');
|
|
357
351
|
L();
|
package/dist/session/recap.js
CHANGED
|
@@ -90,25 +90,18 @@ export function buildRecap(session, cwd, opts) {
|
|
|
90
90
|
return lines;
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* "Newest family": envelope files group into families by filename base
|
|
99
|
-
* (`<base>-v<N>[-<sub>].cspeach.json` — same convention as
|
|
100
|
-
* pickNewestPlanVersion / findNewestVersion in commands/plan-resume.ts);
|
|
101
|
-
* within a family the highest version wins; families rank by their newest
|
|
102
|
-
* file's mtime. Non-plan envelopes (design, spec-gap, …) are skipped so the
|
|
103
|
-
* newest PLAN family is found even when a newer non-plan artefact exists.
|
|
93
|
+
* Newest file per envelope family, families newest first (by that file's
|
|
94
|
+
* mtime). [] when the folder is missing. Within a family the highest version
|
|
95
|
+
* wins, then the collision sub, then mtime. A version-less file is its own
|
|
96
|
+
* single-file family. Shared by the recap and the startup Continue offer.
|
|
104
97
|
*/
|
|
105
|
-
function
|
|
98
|
+
export function newestPlanFamilyFiles(workspaceDir) {
|
|
106
99
|
let names;
|
|
107
100
|
try {
|
|
108
101
|
names = readdirSync(workspaceDir);
|
|
109
102
|
}
|
|
110
103
|
catch {
|
|
111
|
-
return
|
|
104
|
+
return []; // workspace missing
|
|
112
105
|
}
|
|
113
106
|
const re = /^(.+)-v(\d+)(?:-(\d+))?\.cspeach\.json$/;
|
|
114
107
|
const cands = [];
|
|
@@ -124,17 +117,10 @@ function buildPlanPart(workspaceDir) {
|
|
|
124
117
|
continue;
|
|
125
118
|
}
|
|
126
119
|
const m = re.exec(name);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
else {
|
|
131
|
-
// Hand-renamed / version-less envelope: its own single-file family.
|
|
132
|
-
cands.push({ path, family: name.slice(0, -'.cspeach.json'.length), version: 0, sub: 0, mtimeMs });
|
|
133
|
-
}
|
|
120
|
+
cands.push(m
|
|
121
|
+
? { path, family: m[1], version: Number(m[2]), sub: m[3] ? Number(m[3]) : 0, mtimeMs }
|
|
122
|
+
: { path, family: name.slice(0, -'.cspeach.json'.length), version: 0, sub: 0, mtimeMs });
|
|
134
123
|
}
|
|
135
|
-
if (cands.length === 0)
|
|
136
|
-
return null;
|
|
137
|
-
// Newest version per family (version, then collision sub, then mtime).
|
|
138
124
|
const byFamily = new Map();
|
|
139
125
|
for (const c of cands) {
|
|
140
126
|
const cur = byFamily.get(c.family);
|
|
@@ -144,9 +130,13 @@ function buildPlanPart(workspaceDir) {
|
|
|
144
130
|
byFamily.set(c.family, c);
|
|
145
131
|
}
|
|
146
132
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
return [...byFamily.values()].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
134
|
+
}
|
|
135
|
+
function buildPlanPart(workspaceDir) {
|
|
136
|
+
// The first family whose envelope IS a plan is "the newest plan family".
|
|
137
|
+
const familiesNewestFirst = newestPlanFamilyFiles(workspaceDir);
|
|
138
|
+
if (familiesNewestFirst.length === 0)
|
|
139
|
+
return null;
|
|
150
140
|
for (const cand of familiesNewestFirst) {
|
|
151
141
|
let env;
|
|
152
142
|
try {
|