@cspeach/cli 1.1.31 → 1.1.32

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 (63) hide show
  1. package/dist/agent/loop.js +16 -6
  2. package/dist/cli.js +13 -4
  3. package/dist/commands/help.js +16 -1
  4. package/dist/commands/plan-chain.js +3 -0
  5. package/dist/commands/plan-gate.js +10 -1
  6. package/dist/commands/plan-resume.js +4 -0
  7. package/dist/commands/team-rules.js +187 -0
  8. package/dist/commands/team.js +632 -90
  9. package/dist/journey/chain.js +265 -15
  10. package/dist/journey/clean-core.js +25 -3
  11. package/dist/journey/flow.js +51 -1
  12. package/dist/journey/rebuild.js +464 -0
  13. package/dist/journey/run.js +213 -20
  14. package/dist/journey/session-deps.js +5 -1
  15. package/dist/lane/app-chain.js +12 -2
  16. package/dist/one-shot.js +24 -17
  17. package/dist/projects/clean-core-confirm.js +38 -5
  18. package/dist/projects/save-command.js +20 -3
  19. package/dist/register/baseline.js +39 -2
  20. package/dist/register/decision-rules.js +35 -0
  21. package/dist/register/discover.js +49 -19
  22. package/dist/register/export-files.js +58 -0
  23. package/dist/register/measure.js +191 -9
  24. package/dist/register/plan-import.js +6 -3
  25. package/dist/register/records.js +15 -3
  26. package/dist/register/rule-proposal.js +4 -0
  27. package/dist/register/rules-edit.js +257 -0
  28. package/dist/register/status-file/register-status-file.js +106 -108
  29. package/dist/register/status-file/report-model.js +569 -0
  30. package/dist/register/status-file/team-css.js +1 -1
  31. package/dist/register/status-file-sync.js +1 -0
  32. package/dist/register/status-html.js +25 -6
  33. package/dist/register/store.js +4 -3
  34. package/dist/register/variant.js +65 -0
  35. package/dist/register/workbooks.js +22 -56
  36. package/dist/register/write-whole.js +30 -2
  37. package/dist/renderer/tool-labels.js +2 -1
  38. package/dist/repl/builtin-commands.js +4 -0
  39. package/dist/repl/journey-entry.js +4 -2
  40. package/dist/repl/plan-hop.js +2 -9
  41. package/dist/repl/rules-menu.js +177 -0
  42. package/dist/repl/slash-completer.js +1 -0
  43. package/dist/repl/slash-picker.js +5 -4
  44. package/dist/repl.js +145 -86
  45. package/dist/router/classifier.js +8 -4
  46. package/dist/router/router-core.js +43 -7
  47. package/dist/router/routing-decision.js +1 -1
  48. package/dist/runs/bundled-runs.js +100 -0
  49. package/dist/runs/front-door.js +19 -0
  50. package/dist/runs/manifest-runs.js +50 -0
  51. package/dist/runs/run-command.js +24 -0
  52. package/dist/runs/runs.js +27 -0
  53. package/dist/runs/shadow.js +100 -0
  54. package/dist/runs/types.js +1 -0
  55. package/dist/session/queued-step.js +45 -0
  56. package/dist/skills/bundled-skills.js +53 -53
  57. package/dist/tools/dispatch-skill.js +8 -4
  58. package/dist/tools/lane/app-lane-resolve.js +1 -1
  59. package/dist/tools/project/rules_propose.js +59 -0
  60. package/dist/tools/project/team_findings.js +13 -2
  61. package/dist/upgrade-check.js +49 -4
  62. package/package.json +120 -120
  63. package/.cspeach/project.json +0 -4
@@ -15,8 +15,9 @@
15
15
  * user's keypress to make.
16
16
  * - The command argument MUST start with a known slash route. Free
17
17
  * text without a leading `/` is rejected.
18
- * - Set the slot via ctx.pendingDispatch.set(command). repl.tsx reads
19
- * it after runTurn and routes as the next submission.
18
+ * - Set the slot via ctx.pendingDispatch.set(command, { by: 'switch', … }).
19
+ * repl.tsx reads it after runTurn and routes it as the next submission,
20
+ * shown as "Auto-routed: <command>" — never as the person's own line.
20
21
  * - Skill ends the turn naturally after this tool call. Do not also
21
22
  * emit a "Type X at the next prompt" hint — the dispatch supersedes it.
22
23
  *
@@ -32,7 +33,7 @@
32
33
  * for every other dispatch (e.g. /abap-generate → /abap-rap switch).
33
34
  */
34
35
  import { registerTool } from './index.js';
35
- import { isPlanResumeCommand } from '../commands/plan-resume.js';
36
+ import { autoRoutedNotice, isPlanResumeCommand } from '../commands/plan-resume.js';
36
37
  export const PLAN_RESUME_DISPATCH_REASON = 'Do not dispatch /abap-plan --resume. When your plan manifest is saved, CSPeach runs the next step itself with the exact saved file. Simply end the turn now.';
37
38
  registerTool({
38
39
  name: 'dispatch_skill',
@@ -91,7 +92,10 @@ registerTool({
91
92
  }),
92
93
  };
93
94
  }
94
- ctx.pendingDispatch.set(command);
95
+ // Must-fix follow-up (b): the person agreed to the switch, but the model
96
+ // wrote the command, so it is never shown as theirs ("You › …"). Tagged
97
+ // `switch`, both REPLs show its one "Auto-routed:" line (queuedStepView).
98
+ ctx.pendingDispatch.set(command, { by: 'switch', line: autoRoutedNotice(command) ?? `Auto-routed: ${command}` });
95
99
  return {
96
100
  content: JSON.stringify({
97
101
  ok: true,
@@ -284,7 +284,7 @@ export async function appLaneResolveHandler(args, ctx, deps = {}) {
284
284
  ctx.chunkEmitter?.emit('info', `Lane chosen by the rules (no confirm card in this terminal): ${shown}.`);
285
285
  }
286
286
  if (flow?.specGapPath && planCmd) {
287
- ctx.pendingDispatch?.set(planCmd);
287
+ ctx.pendingDispatch?.set(planCmd, { by: 'app' }); // audit M-2: CSPeach's step, never shown as typed
288
288
  lines.push('Next: the plan is being created.');
289
289
  }
290
290
  else if (flow?.specGapPath && headless) {
@@ -0,0 +1,59 @@
1
+ /**
2
+ * rules_propose — read-only (step 2, Task 10, optional step; spec 2026-10-08 §7 D7).
3
+ *
4
+ * "Rule from words": the person says a rule in words, the model turns it into
5
+ * the decision-rule shape and calls this. Code checks it with register-core's
6
+ * strict reader against the team project's rules and says it back in plain
7
+ * words. Nothing is written: the proposal is kept in this process, and the
8
+ * person saves it from `/rules` (Add the proposed rule → card → Save), lead
9
+ * only. The model never writes the rules.
10
+ */
11
+ import { readDecisionRules } from '@cspeach/register-core';
12
+ import { registerTool } from '../index.js';
13
+ import { activeProject, readProjectFile, RegisterError } from '../../register/store.js';
14
+ import { addProposed, ruleWords } from '../../register/rules-edit.js';
15
+ import { setRuleProposal } from '../../register/rule-proposal.js';
16
+ export async function rulesProposeHandler(args, _ctx) {
17
+ const joined = activeProject();
18
+ if (!joined)
19
+ return { content: 'This laptop has not joined a team project. The developer runs: cspeach team join <folder>', is_error: true };
20
+ try {
21
+ const project = readProjectFile(joined.projectDir);
22
+ const current = readDecisionRules(project);
23
+ if (current.warning)
24
+ return { content: `${current.warning} Nothing can be added until the file is fixed or the rules are restored (/rules → Restore defaults).`, is_error: true };
25
+ const edit = addProposed(current.rules, args.rule, { by: 'proposed', at: new Date(0).toISOString() });
26
+ const rule = edit.after;
27
+ const { id: _id, on: _on, changedBy: _by, changedAt: _at, ...proposal } = rule;
28
+ setRuleProposal({ projectId: joined.projectId, proposal, words: ruleWords(rule) });
29
+ return {
30
+ content: JSON.stringify({
31
+ ok: true, id: rule.id, words: ruleWords(rule), note: rule.note, saved: false,
32
+ next: 'Nothing is saved. Tell the person: open /rules and pick "Add the proposed rule"; it is saved only when they press Save (the lead only). New rules go last: the first rule that is on and holds decides.',
33
+ }),
34
+ };
35
+ }
36
+ catch (e) {
37
+ if (e instanceof RegisterError)
38
+ return { content: e.message, is_error: true };
39
+ throw e;
40
+ }
41
+ }
42
+ registerTool({
43
+ name: 'rules_propose',
44
+ description: 'Read-only. Check a decision rule the person described in words, for the team project this laptop joined. '
45
+ + 'Give it as { when: { types?, levelNow?, targetAtMost?, minFindings?, maxFindings?, findingClasses?, onlyClasses?, findingRules? (rule-id prefixes, e.g. SEARCH_PROBLEMATIC_STATEMENTS_FOR_ALV_CALLS), callers?, noOpenForTarget? }, '
46
+ + 'then: { disposition: keep|fix|rebuild|retire-candidate, target? }, note }. Returns the rule in plain words or why it cannot be read. '
47
+ + 'Never saves: the person saves it from /rules. No SAP call.',
48
+ isMutating: false,
49
+ category: 'project',
50
+ requiresSap: false,
51
+ input_schema: {
52
+ type: 'object',
53
+ properties: {
54
+ rule: { type: 'object', description: 'The proposed rule: { when, then, note }. Any id, on or changedBy given is ignored.' },
55
+ },
56
+ required: ['rule'],
57
+ },
58
+ handler: (args, ctx) => rulesProposeHandler(args, ctx),
59
+ });
@@ -12,11 +12,17 @@
12
12
  * While a journey triage is queued in the session, only the page the journey
13
13
  * handed out (journey/flow.ts, at most 40 keys) is shown: the skill needs no
14
14
  * change to work in pages.
15
+ *
16
+ * Decision rules (step 2, D6): every row carries `ruleProposal` — the first
17
+ * rule of the team project (or the defaults) that decides it, or null when no
18
+ * rule does and the row is CSPeach's judgement. Rules the file names but this
19
+ * build cannot read give `rulesWarning` (one line) and the defaults.
15
20
  */
16
- import { dispositionOf, DISPOSITION_WORD, inLane } from '@cspeach/register-core';
21
+ import { dispositionOf, DISPOSITION_WORD, inLane, readDecisionRules, } from '@cspeach/register-core';
17
22
  import { registerTool } from '../index.js';
18
23
  import { findingsByObject, shownUpTo } from '../../register/findings.js';
19
24
  import { registerFor } from '../../register/picture.js';
25
+ import { ruleProposalOf } from '../../register/decision-rules.js';
20
26
  import { activeProject, readProjectFile } from '../../register/store.js';
21
27
  import { shown } from '../../register/atc-file.js';
22
28
  import { journeyPage } from '../../journey/flow.js';
@@ -93,6 +99,7 @@ export async function teamFindingsHandler(args, ctx) {
93
99
  const slice = objects.slice(start, start + limit);
94
100
  const more = objects.length - start - slice.length;
95
101
  const findings = findingsByObject(joined.projectDir, joined.outboxDir);
102
+ const rules = readDecisionRules(project);
96
103
  const list = slice.map((o) => {
97
104
  const f = findings.get(o.key);
98
105
  const d = dispositionOf(o.decision);
@@ -108,12 +115,15 @@ export async function teamFindingsHandler(args, ctx) {
108
115
  lastChanged: o.evidence.changed?.at ? shownUpTo(o.evidence.changed.at, 30) : null,
109
116
  retireCandidate: o.retireCandidate,
110
117
  priority: { band: o.priority.effective.band, source: o.priority.effective.source },
118
+ // The rule that decides this row, from the latest counted run's findings; null = no rule decides.
119
+ ruleProposal: ruleProposalOf(o, f, rules.rules),
111
120
  };
112
121
  });
113
122
  return {
114
123
  content: JSON.stringify({
115
124
  teamProject: project.name, scope, objects: list,
116
125
  ...(page ? { journey: JOURNEY_NOTE } : {}),
126
+ ...(rules.warning ? { rulesWarning: rules.warning } : {}),
117
127
  ...(more > 0 ? { more } : {}),
118
128
  ...(paged && more > 0 && slice.length ? { next: slice[slice.length - 1].key } : {}),
119
129
  }),
@@ -127,7 +137,8 @@ registerTool({
127
137
  name: 'team_findings',
128
138
  description: 'Read-only. The objects of a lane (or package patterns) in the team project this laptop joined, with the findings '
129
139
  + 'measurement zero recorded for each, any decision already recorded, and per object: usage (state and source), callers from the '
130
- + 'index (or why they were not read), last change (a change, not use), whether it is a retire candidate, and its priority. '
140
+ + 'index (or why they were not read), last change (a change, not use), whether it is a retire candidate, its priority, and '
141
+ + 'ruleProposal: the decision rule that decides the row (null = no rule decides; your judgement). '
131
142
  + 'Use it for a /abap-clean-core triage. No SAP call.',
132
143
  isMutating: false,
133
144
  category: 'project',
@@ -3,7 +3,9 @@ import { loadConfig } from './config/loader.js';
3
3
  import { CLI_VERSION } from './lib/version.js';
4
4
  import { getStore } from './auth/api-key.js';
5
5
  import { brandSettled } from './renderer/brand-settled.js';
6
- function compareVersions(a, b) {
6
+ import { ManifestClient, DEFAULT_MANIFEST_URL } from './skills/manifest-client.js';
7
+ import { asRunEntries, runsFloor } from './runs/manifest-runs.js';
8
+ export function compareVersions(a, b) {
7
9
  const pa = a.split('.').map(Number);
8
10
  const pb = b.split('.').map(Number);
9
11
  for (let i = 0; i < 3; i++) {
@@ -24,19 +26,20 @@ function compareVersions(a, b) {
24
26
  * GET with the bearer and read the body field (the header is still honoured
25
27
  * should the proxy add it); not signed in → skip (a 401 tells us nothing).
26
28
  * The warning goes to stderr so one-shot stdout stays the answer only.
29
+ * Returns true when it printed.
27
30
  */
28
- export async function checkForUpgrade() {
31
+ async function checkSkillFloor() {
29
32
  try {
30
33
  const cfg = await loadConfig();
31
34
  const key = await (await getStore()).get();
32
35
  if (!key)
33
- return;
36
+ return false;
34
37
  const r = await fetch(`${cfg.proxy_url}/v1/skills/abap-radar`, {
35
38
  headers: { Authorization: `Bearer ${key}` },
36
39
  signal: AbortSignal.timeout(3000),
37
40
  });
38
41
  if (!r.ok)
39
- return;
42
+ return false;
40
43
  let minCli = r.headers.get('x-skill-min-cli-version');
41
44
  if (!minCli) {
42
45
  const body = (await r.json().catch(() => null));
@@ -52,7 +55,49 @@ export async function checkForUpgrade() {
52
55
  text: `This CSPeach (${current}) is older than the minimum (${minCli})`,
53
56
  next: 'cspeach --upgrade',
54
57
  }).trimEnd());
58
+ return true;
55
59
  }
60
+ return false;
61
+ }
62
+ catch {
63
+ // Offline, keychain unavailable or proxy down — never block startup.
64
+ return false;
65
+ }
66
+ }
67
+ /**
68
+ * Guided runs (2026-10-09, spec §3.2): a run's min_cli_version comes from the
69
+ * signed manifest's runs[]; an older CLI gets the same "!" line a skill gives.
70
+ * Returns true when it printed. Any failure (offline, unsigned, malformed) is silent.
71
+ */
72
+ async function checkRunsFloor(deps) {
73
+ try {
74
+ const read = deps.fetchManifest ?? (() => new ManifestClient(DEFAULT_MANIFEST_URL).fetchAndVerifyManifest());
75
+ // The race caps the WAIT at 3 s; the underlying fetch keeps its own 10 s
76
+ // timeout (manifest-client.ts:32) and may outlive this call — harmless:
77
+ // nothing awaits it, and the process exit path does not wait for it (M-9).
78
+ const m = await Promise.race([read(), new Promise((_, rej) => { const t = setTimeout(() => rej(new Error('manifest timeout')), 3000); t.unref?.(); })]);
79
+ const floor = runsFloor(asRunEntries(m.runs), CLI_VERSION);
80
+ if (!floor)
81
+ return false;
82
+ await brandSettled();
83
+ console.error('\n' + formatAttention({
84
+ text: `This CSPeach (${CLI_VERSION}) is older than the guided run '${floor.name.replace(/^run-/, '')}' needs (${floor.min})`,
85
+ next: 'cspeach --upgrade',
86
+ }).trimEnd());
87
+ return true;
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ export async function checkForUpgrade(deps = {}) {
94
+ try {
95
+ const key = await (await getStore()).get();
96
+ if (!key)
97
+ return;
98
+ const printed = await checkSkillFloor();
99
+ if (!printed)
100
+ await checkRunsFloor(deps);
56
101
  }
57
102
  catch {
58
103
  // Offline, keychain unavailable or proxy down — never block startup.
package/package.json CHANGED
@@ -1,120 +1,120 @@
1
- {
2
- "name": "@cspeach/cli",
3
- "version": "1.1.31",
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
- "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
- "@cspeach/register-core": "0.3.0",
56
- "@cspeach/sap-client": "0.1.17",
57
- "@iarna/toml": "^2.2.5",
58
- "@inkjs/ui": "^2.0.0",
59
- "@inquirer/prompts": "^7.0.0",
60
- "@napi-rs/keyring": "2.1.0",
61
- "@noble/ed25519": "2.1.0",
62
- "@noble/hashes": "1.4.0",
63
- "@sap-ux/annotation-converter": "0.10.21",
64
- "@sap-ux/edmx-parser": "0.10.0",
65
- "@sap-ux/fe-fpm-writer": "0.43.7",
66
- "@sap-ux/fiori-elements-writer": "^3.0.39",
67
- "@sap-ux/fiori-freestyle-writer": "3.0.12",
68
- "@sap-ux/vocabularies-types": "0.15.0",
69
- "adm-zip": "0.5.17",
70
- "chalk": "^5.3.0",
71
- "cli-highlight": "^2.1.11",
72
- "cli-table3": "^0.6.5",
73
- "clipboardy": "^5.3.1",
74
- "cross-spawn": "^7.0.6",
75
- "diff": "^9.0.0",
76
- "ejs": "3.1.10",
77
- "fuse.js": "^7.0.0",
78
- "ink": "^5.0.0",
79
- "ink-text-input": "^6.0.0",
80
- "jose": "^5.9.0",
81
- "mammoth": "^1.8.0",
82
- "marked": "^11.2.0",
83
- "marked-terminal": "^6.2.0",
84
- "mem-fs": "2.1.0",
85
- "mem-fs-editor": "9.4.0",
86
- "node-machine-id": "^1.1.12",
87
- "openai": "^6.49.0",
88
- "puppeteer-core": "24.10.2",
89
- "react": "^18.3.0",
90
- "string-width": "^7.2.0",
91
- "turndown": "^7.2.0",
92
- "unpdf": "1.7.0",
93
- "zod": "^3.23.0"
94
- },
95
- "devDependencies": {
96
- "@sap-ux/ui5-middleware-fe-mockserver": "2.4.17",
97
- "@types/adm-zip": "0.5.8",
98
- "@types/cross-spawn": "^6.0.6",
99
- "@types/diff": "^7.0.2",
100
- "@types/ejs": "^3.1.5",
101
- "@types/node": "^20.17.0",
102
- "@types/react": "^18.3.0",
103
- "@types/turndown": "^5.0.5",
104
- "@ui5/builder": "4.3.2",
105
- "@ui5/fs": "4.0.6",
106
- "docx": "^9.0.2",
107
- "husky": "^9.1.7",
108
- "ink-testing-library": "^4.0.0",
109
- "pdf-lib": "^1.17.1",
110
- "strip-ansi": "6.0.1",
111
- "tsx": "^4.19.0",
112
- "typescript": "^5.7.0",
113
- "vitest": "^2.1.0"
114
- },
115
- "pnpm": {
116
- "onlyBuiltDependencies": [
117
- "esbuild"
118
- ]
119
- }
120
- }
1
+ {
2
+ "name": "@cspeach/cli",
3
+ "version": "1.1.32",
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
+ "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
+ "@cspeach/register-core": "0.4.0",
56
+ "@cspeach/sap-client": "0.1.18",
57
+ "@iarna/toml": "^2.2.5",
58
+ "@inkjs/ui": "^2.0.0",
59
+ "@inquirer/prompts": "^7.0.0",
60
+ "@napi-rs/keyring": "2.1.0",
61
+ "@noble/ed25519": "2.1.0",
62
+ "@noble/hashes": "1.4.0",
63
+ "@sap-ux/annotation-converter": "0.10.21",
64
+ "@sap-ux/edmx-parser": "0.10.0",
65
+ "@sap-ux/fe-fpm-writer": "0.43.7",
66
+ "@sap-ux/fiori-elements-writer": "^3.0.39",
67
+ "@sap-ux/fiori-freestyle-writer": "3.0.12",
68
+ "@sap-ux/vocabularies-types": "0.15.0",
69
+ "adm-zip": "0.5.17",
70
+ "chalk": "^5.3.0",
71
+ "cli-highlight": "^2.1.11",
72
+ "cli-table3": "^0.6.5",
73
+ "clipboardy": "^5.3.1",
74
+ "cross-spawn": "^7.0.6",
75
+ "diff": "^9.0.0",
76
+ "ejs": "3.1.10",
77
+ "fuse.js": "^7.0.0",
78
+ "ink": "^5.0.0",
79
+ "ink-text-input": "^6.0.0",
80
+ "jose": "^5.9.0",
81
+ "mammoth": "^1.8.0",
82
+ "marked": "^11.2.0",
83
+ "marked-terminal": "^6.2.0",
84
+ "mem-fs": "2.1.0",
85
+ "mem-fs-editor": "9.4.0",
86
+ "node-machine-id": "^1.1.12",
87
+ "openai": "^6.49.0",
88
+ "puppeteer-core": "24.10.2",
89
+ "react": "^18.3.0",
90
+ "string-width": "^7.2.0",
91
+ "turndown": "^7.2.0",
92
+ "unpdf": "1.7.0",
93
+ "zod": "^3.23.0"
94
+ },
95
+ "devDependencies": {
96
+ "@sap-ux/ui5-middleware-fe-mockserver": "2.4.17",
97
+ "@types/adm-zip": "0.5.8",
98
+ "@types/cross-spawn": "^6.0.6",
99
+ "@types/diff": "^7.0.2",
100
+ "@types/ejs": "^3.1.5",
101
+ "@types/node": "^20.17.0",
102
+ "@types/react": "^18.3.0",
103
+ "@types/turndown": "^5.0.5",
104
+ "@ui5/builder": "4.3.2",
105
+ "@ui5/fs": "4.0.6",
106
+ "docx": "^9.0.2",
107
+ "husky": "^9.1.7",
108
+ "ink-testing-library": "^4.0.0",
109
+ "pdf-lib": "^1.17.1",
110
+ "strip-ansi": "6.0.1",
111
+ "tsx": "^4.19.0",
112
+ "typescript": "^5.7.0",
113
+ "vitest": "^2.1.0"
114
+ },
115
+ "pnpm": {
116
+ "onlyBuiltDependencies": [
117
+ "esbuild"
118
+ ]
119
+ }
120
+ }
@@ -1,4 +0,0 @@
1
- {
2
- "id": "032010b3",
3
- "createdAt": "2026-10-08T22:43:38.438Z"
4
- }