@cspeach/cli 1.1.47 → 1.1.49
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/level.js +1 -1
- package/dist/agent/loop.js +7 -6
- package/dist/agent/sap-connection-adapter.js +2 -0
- package/dist/agent/system-prompt.js +2 -2
- package/dist/agent/tool-dispatch.js +13 -1
- package/dist/approvals/op-labels.js +49 -10
- package/dist/approvals/render.js +14 -23
- package/dist/approvals/risk-floor.js +196 -19
- package/dist/approvals/visible-changes.js +35 -0
- package/dist/commands/config-set.js +5 -5
- package/dist/commands/cost.js +2 -1
- package/dist/commands/help.js +1 -1
- package/dist/commands/project.js +301 -0
- package/dist/commands/team-plan.js +16 -6
- package/dist/commands/team.js +295 -68
- package/dist/config/loader.js +13 -0
- package/dist/cost/cost-log.js +7 -4
- package/dist/cost/credits-wire.js +11 -6
- package/dist/cost/credits.js +15 -0
- package/dist/cost/display-mode.js +16 -4
- package/dist/doctor/checks/zcspeach.js +96 -0
- package/dist/journey/chain.js +489 -61
- package/dist/journey/clean-core.js +61 -36
- package/dist/journey/flow.js +75 -2
- package/dist/journey/people.js +37 -0
- package/dist/journey/rebuild.js +7 -6
- package/dist/journey/run.js +129 -42
- package/dist/journey/trigger.js +16 -10
- package/dist/one-shot.js +1 -1
- package/dist/project-context/render.js +7 -0
- package/dist/projects/clean-core-confirm.js +166 -44
- package/dist/projects/email-template.js +56 -0
- package/dist/projects/extract-cca.js +8 -7
- package/dist/projects/index.js +0 -1
- package/dist/projects/merge-cca.js +0 -19
- package/dist/projects/merge-upgrade.js +2 -2
- package/dist/projects/project-folder.js +2 -1
- package/dist/projects/save-command.js +51 -14
- package/dist/register/decision-rules.js +2 -1
- package/dist/register/export-files.js +18 -0
- package/dist/register/picture.js +3 -3
- package/dist/register/project-transport.js +125 -0
- package/dist/register/records.js +25 -13
- package/dist/register/rules-edit.js +5 -0
- package/dist/register/status-file/progress-chart.js +17 -6
- package/dist/register/status-file/register-map.js +8 -5
- package/dist/register/status-file/register-model.js +55 -36
- package/dist/register/status-file/register-parts.js +97 -82
- package/dist/register/status-file/register-status-file.js +16 -7
- package/dist/register/status-file/report-model.js +293 -205
- package/dist/register/status-file/team-css.js +1 -1
- package/dist/register/status-html.js +11 -4
- package/dist/register/store.js +2 -2
- package/dist/register/workbooks.js +24 -13
- package/dist/renderer/footer-line.js +10 -2
- package/dist/renderer/status-footer.js +2 -1
- package/dist/renderer/tool-labels.js +2 -2
- package/dist/repl/builtin-commands.js +1 -1
- package/dist/repl/continue-skill.js +5 -0
- package/dist/repl/level-command.js +6 -15
- package/dist/repl.js +21 -8
- package/dist/router/router-core.js +5 -3
- package/dist/session/audit-export.js +4 -1
- package/dist/skill-catalog.js +4 -7
- package/dist/tools/_team-work-path.js +53 -0
- package/dist/tools/approval.js +97 -19
- package/dist/tools/filesystem/file-edit.js +8 -1
- package/dist/tools/filesystem/file-write.js +8 -1
- package/dist/tools/local-files.js +31 -0
- package/dist/tools/project/_merge-shared.js +2 -2
- package/dist/tools/project/_team-tool-shared.js +5 -0
- package/dist/tools/project/cca_merge.js +1 -11
- package/dist/tools/project/rules_propose.js +2 -1
- package/dist/tools/project/team_findings.js +81 -28
- package/dist/tools/project/team_lane_move.js +127 -0
- package/dist/tools/project/upgrade_merge_progress.js +8 -4
- package/dist/tools/subagent/schedule_create.js +131 -0
- package/dist/tools/text-elements/tools.js +7 -1
- package/dist/tools/tool-loading.js +1 -1
- package/dist/tools/update-method-intercept.js +18 -1
- package/dist/tools/write-pipeline.js +19 -5
- package/dist/ui/approval-modal.js +6 -3
- package/dist/ui/error-emitter.js +21 -0
- package/dist/ui/footer-line.js +19 -1
- package/dist/ui/header.js +41 -0
- package/dist/ui/ink-classifier-route.js +19 -0
- package/dist/ui/rich-error-box.js +9 -0
- package/dist/ui/sidebar.js +12 -0
- package/dist/ui/status-emitter.js +19 -0
- package/dist/ui/status-line.js +62 -0
- package/dist/ui/status-row.js +38 -0
- package/dist/ui/todo-panel.js +85 -0
- package/dist/ui/widgets/ask-form.js +250 -0
- package/dist/ui/widgets/ask-question-modal.js +178 -0
- package/fiori/zlas-inspection-lr/package.json +26 -0
- package/fiori/zlas-inspection-lr/ui5-local.yaml +45 -0
- package/fiori/zlas-inspection-lr/ui5.yaml +41 -0
- package/fiori/zlas-inspection-lr/webapp/Component.js +12 -0
- package/fiori/zlas-inspection-lr/webapp/i18n/i18n.properties +9 -0
- package/fiori/zlas-inspection-lr/webapp/index.html +35 -0
- package/fiori/zlas-inspection-lr/webapp/manifest.json +145 -0
- package/live-check-2026-10-17-1343.txt +41 -0
- package/package.json +121 -121
- package/test-harness/fe-draft/app.ts +0 -188
- package/test-harness/fe-draft/fe-draft.e2e.test.ts +0 -461
- package/test-harness/fe-draft/manifest-enhancer.unit.test.ts +0 -51
- package/test-harness/fe-draft/routes.ts +0 -224
- package/test-harness/fe-draft/routes.unit.test.ts +0 -45
- package/test-harness/fe-draft/server.ts +0 -225
- package/test-harness/fe-draft/variants.ts +0 -196
package/dist/journey/run.js
CHANGED
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
* inside a slash command the journey queues or names.
|
|
13
13
|
*/
|
|
14
14
|
import path from 'node:path';
|
|
15
|
-
import { estateSummaryOf, provenForTargetWords, usageLineWithWindow } from '@cspeach/register-core';
|
|
16
|
-
import { DEFAULT_ATC_VARIANT, FIRST_CHECK_PER_PACKAGE_LINE, FIRST_CHECK_WHOLE_MAX, ownerMatch, runTeamCommand, severalLine, whoAmI, writeExports, writeReports } from '../commands/team.js';
|
|
17
|
-
import { EXPORTS_DIR, exportsOn, setExportsAlways } from '../register/export-files.js';
|
|
15
|
+
import { estateSummaryOf, PART_ORDER, PART_WORDS, provenForTargetWords, usageLineWithWindow } from '@cspeach/register-core';
|
|
16
|
+
import { DEFAULT_ATC_VARIANT, FIRST_CHECK_PER_PACKAGE_LINE, FIRST_CHECK_WHOLE_MAX, NO_CHECK_ALL_LINE, ownerMatch, runTeamCommand, severalLine, whoAmI, writeExports, writeReports } from '../commands/team.js';
|
|
17
|
+
import { EXPORTS_DIR, exportsOff, exportsOn, exportsPresent, setExportsAlways } from '../register/export-files.js';
|
|
18
18
|
import { shown, shownUpTo } from '../register/atc-file.js';
|
|
19
|
+
import { withoutRuleId } from '../register/records.js';
|
|
19
20
|
import { activeProject, readEstate, readLanes, readProjectFile, VARIANT_NAME } from '../register/store.js';
|
|
20
21
|
import { registerFor } from '../register/picture.js';
|
|
21
|
-
import {
|
|
22
|
+
import { TRANSPORT_NONE_WORDS, TRANSPORT_REQUEST, transportWords } from '../register/project-transport.js';
|
|
23
|
+
import { claimedSinceCheck, fixFailedCheck, myObjects, newestRecordAt, provenForTarget, readJourneyFacts, stepOf, toDecide, toFix, toProve, unreadableReason, STEPS, } from './clean-core.js';
|
|
24
|
+
import { doneFingerprint, setDoneFingerprint } from './flow.js';
|
|
22
25
|
/* ── words ──────────────────────────────────────────────────────────────── */
|
|
23
26
|
export const UNREADABLE_LINES = {
|
|
24
27
|
'object-list': 'The project\'s object list in this folder can\'t be read right now — it may still be syncing. Try again in a minute.',
|
|
@@ -79,8 +82,14 @@ export function notRecordedLine(error) {
|
|
|
79
82
|
export const objectShown = (name) => (OBJECT_NAME.test(name.toUpperCase()) ? name.toUpperCase() : shown(name));
|
|
80
83
|
/** An object marked fix whose name CSPeach never puts in a command: one plain line, and it is passed over. */
|
|
81
84
|
export const passedOverLine = (name) => `${objectShown(name)}: the name is not an ABAP name, so CSPeach passed it over.`;
|
|
85
|
+
/** Fix round 1: an object marked fix that the project's own view does not hold right now: one plain line, and it is passed over. */
|
|
86
|
+
export const notInViewLine = (name) => `${objectShown(name)}: it is not in the project's list right now, so CSPeach passed it over.`;
|
|
82
87
|
/** F16: the check a project measures with, said as a line. */
|
|
83
88
|
export const checkedWithLine = (variant) => ` Checked with: ${variant}.`;
|
|
89
|
+
/** Task 8 (B11): a project that holds no transport request yet, said as a line (headless, or None yet on the card). The words live in project-transport.ts. */
|
|
90
|
+
export const TRANSPORT_NONE_LINE = ` ${TRANSPORT_NONE_WORDS}`;
|
|
91
|
+
/** Task 8 (B11): the request a project's fixes are recorded in, said as a line. */
|
|
92
|
+
export const transportLine = (request) => ` ${transportWords(request)}`;
|
|
84
93
|
/* ── commands the journey queues ────────────────────────────────────────── */
|
|
85
94
|
export const OBJECT_TYPE = /^[A-Z]{4}$/;
|
|
86
95
|
/** An ABAP object name: letters, digits, underscores, an optional /NAMESPACE/ in front. */
|
|
@@ -129,13 +138,17 @@ export function cleanReason(reason) {
|
|
|
129
138
|
export function triageCommand(laneId) {
|
|
130
139
|
return LANE_ID.test(laneId) ? `/abap-clean-core triage lane ${laneId}` : null;
|
|
131
140
|
}
|
|
132
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* `/abap-refactor <TYPE> <NAME> — <reason>`, names upper-cased; null when a
|
|
143
|
+
* name is not an ABAP name. A "Rule <id>:" an older record carries in its
|
|
144
|
+
* reason is left out (project view Task 4: rule ids never sit inside a reason).
|
|
145
|
+
*/
|
|
133
146
|
export function refactorCommand(o) {
|
|
134
147
|
const type = o.type.trim().toUpperCase();
|
|
135
148
|
const name = o.name.trim().toUpperCase();
|
|
136
149
|
if (!OBJECT_TYPE.test(type) || !OBJECT_NAME.test(name))
|
|
137
150
|
return null;
|
|
138
|
-
const reason = cleanReason(o.reason);
|
|
151
|
+
const reason = cleanReason(withoutRuleId(String(o.reason ?? '')));
|
|
139
152
|
return `/abap-refactor ${type} ${name}${reason ? ` — ${reason}` : ''}`;
|
|
140
153
|
}
|
|
141
154
|
/* ── the runner ─────────────────────────────────────────────────────────── */
|
|
@@ -196,6 +209,7 @@ function nextStepOnly(stage, ctx) {
|
|
|
196
209
|
return result(stage, [
|
|
197
210
|
`Next: ${step(stage, `start your project${named.length ? ` on ${named.join(', ')}` : ''} in this folder.`)}`,
|
|
198
211
|
checkedWithLine(DEFAULT_ATC_VARIANT),
|
|
212
|
+
...(ctx.transportAsked ? [TRANSPORT_NONE_LINE] : []),
|
|
199
213
|
// I15: above 500 objects the first check is per package, said as a line.
|
|
200
214
|
...(objects !== null && objects > FIRST_CHECK_WHOLE_MAX ? [FIRST_CHECK_PER_PACKAGE_LINE] : []),
|
|
201
215
|
], { kind: 'stop' });
|
|
@@ -252,28 +266,68 @@ async function start(ctx) {
|
|
|
252
266
|
const asWith = ctx.variant && VARIANT_NAME.test(ctx.variant) ? ['--atc-variant', ctx.variant] : [];
|
|
253
267
|
// I15: above 500 objects the start measures nothing; the chain offers the per-package first check.
|
|
254
268
|
const big = isBigStart(ctx.cwd, packages);
|
|
255
|
-
const
|
|
269
|
+
const developers = ctx.developers?.length ? ['--developers', ctx.developers.join(', ')] : [];
|
|
270
|
+
const transport = ctx.transport && TRANSPORT_REQUEST.test(ctx.transport) ? ['--transport', ctx.transport] : [];
|
|
271
|
+
const { code, said } = await quiet(ctx, ['start', ...packages, '--folder', ctx.cwd, ...asWith, ...(big ? ['--no-measure'] : []), ...developers, ...transport]);
|
|
256
272
|
if (code !== 0)
|
|
257
273
|
return stop(failure(said, 'The project could not be started.'));
|
|
258
274
|
let name = packages.join(', ');
|
|
259
275
|
let variant = asWith[1] ?? DEFAULT_ATC_VARIANT;
|
|
276
|
+
let stored;
|
|
260
277
|
try {
|
|
261
278
|
const project = readProjectFile(ctx.cwd);
|
|
262
279
|
name = shown(project.name);
|
|
263
280
|
if (project.atcVariant && VARIANT_NAME.test(project.atcVariant))
|
|
264
281
|
variant = project.atcVariant;
|
|
282
|
+
const r = project.transport?.request;
|
|
283
|
+
if (typeof r === 'string' && TRANSPORT_REQUEST.test(r))
|
|
284
|
+
stored = r;
|
|
265
285
|
}
|
|
266
286
|
catch { /* the name and the check it was started with */ }
|
|
267
|
-
|
|
268
|
-
const
|
|
287
|
+
// Project view Task 5: the counts are read from the folder's view, never from the lines the start printed.
|
|
288
|
+
const counts = startCounts(ctx);
|
|
289
|
+
const objects = counts?.objects;
|
|
290
|
+
const check = counts?.check;
|
|
269
291
|
const lines = [`✓ Your project on ${name} started here.`, checkedWithLine(variant)];
|
|
292
|
+
// Task 8 (B11): the transport, said only when the start asked for it.
|
|
293
|
+
if (stored)
|
|
294
|
+
lines.push(transportLine(stored));
|
|
295
|
+
else if (ctx.transportAsked)
|
|
296
|
+
lines.push(TRANSPORT_NONE_LINE);
|
|
270
297
|
if (objects)
|
|
271
|
-
lines.push(` ${objects
|
|
272
|
-
lines.push(check ? ` First check: ${check
|
|
298
|
+
lines.push(` ${plural(objects, 'object', 'objects')}, matched against the system's count.`);
|
|
299
|
+
lines.push(check ? ` First check: ${num(check.withFindings)} of ${plural(check.checked, 'object', 'objects')} with findings.` : ' No check yet.');
|
|
300
|
+
// B1: each person's share, as the start wrote it (never the lane-move or results lines).
|
|
301
|
+
if (developers.length)
|
|
302
|
+
lines.push(...said.map(shareLine).filter((l) => l !== null));
|
|
273
303
|
// Review T7 finding 2: init decides "big" from the live pull, which may differ from the inventory.
|
|
274
|
-
|
|
304
|
+
// B1: a start with people named measures nothing at all, so the first check runs per package too.
|
|
305
|
+
const perPackage = !check && (said.includes(FIRST_CHECK_PER_PACKAGE_LINE) || said.includes(NO_CHECK_ALL_LINE));
|
|
275
306
|
return { ...result('START', lines, { kind: 'continue' }), ...(perPackage ? { firstCheckPerPackage: true } : {}) };
|
|
276
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* What the start leaves in the folder, read from its project view: how many
|
|
310
|
+
* objects, and — when the start measured — how many of the objects checked
|
|
311
|
+
* have findings. Null when the folder cannot be read.
|
|
312
|
+
*/
|
|
313
|
+
function startCounts(ctx) {
|
|
314
|
+
const view = facts(ctx).view;
|
|
315
|
+
if (!view)
|
|
316
|
+
return null;
|
|
317
|
+
const { all } = view;
|
|
318
|
+
return { objects: all.total, check: all.coverage.checked ? { checked: all.coverage.checked, withFindings: all.coverage.withFindings } : null };
|
|
319
|
+
}
|
|
320
|
+
/** ` James Miller (james): ZSD_ORDERS, ZMM_STOCK · 10 objects` and ` James Miller: no package yet.` as team start prints them. */
|
|
321
|
+
const SHARE = /^ {2}(.+?) \([A-Za-z0-9_-]+\): (.+) · (\d[\d,]* objects?)$/;
|
|
322
|
+
const NO_SHARE = /^ {2}(.+): no package yet\.$/;
|
|
323
|
+
/** One person's share for the journey: ` James Miller: ZSD_ORDERS, ZMM_STOCK · 10 objects.`; null for any other line. */
|
|
324
|
+
function shareLine(l) {
|
|
325
|
+
const m = SHARE.exec(l);
|
|
326
|
+
if (m)
|
|
327
|
+
return ` ${shown(m[1])}: ${shownUpTo(m[2], 200)} · ${m[3]}.`;
|
|
328
|
+
const none = NO_SHARE.exec(l);
|
|
329
|
+
return none ? ` ${shown(none[1])}: no package yet.` : null;
|
|
330
|
+
}
|
|
277
331
|
/** Code objects the folder's inventory holds for these packages; null when it does not know them all. */
|
|
278
332
|
function inventoryObjects(cwd, packages) {
|
|
279
333
|
const found = readEstate(cwd);
|
|
@@ -406,7 +460,7 @@ function unowned(ctx) {
|
|
|
406
460
|
/* DECIDE — the triage of my objects, a model step. */
|
|
407
461
|
function decideStage(ctx) {
|
|
408
462
|
const f = facts(ctx);
|
|
409
|
-
const left = f.register ? toDecide(f.register, f.me, f.skip) : [];
|
|
463
|
+
const left = f.register ? toDecide(f.register, f.me, f.skip, f.view) : [];
|
|
410
464
|
const lane = left[0]?.lane;
|
|
411
465
|
const command = lane ? triageCommand(lane) : null;
|
|
412
466
|
if (!command)
|
|
@@ -416,22 +470,33 @@ function decideStage(ctx) {
|
|
|
416
470
|
/* FIX — the next object to fix, a model step. */
|
|
417
471
|
function fixStage(ctx) {
|
|
418
472
|
const f = facts(ctx);
|
|
419
|
-
|
|
420
|
-
|
|
473
|
+
return fixStep(f.register ? toFix(f.register, f.me, f.skip, f.view) : [], f.view);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* The fix step over the objects left to fix, in order: a name that is not an
|
|
477
|
+
* ABAP name, or an object the project view does not hold (fix round 2), is
|
|
478
|
+
* passed over with one line and put off for the session; the next one is queued.
|
|
479
|
+
*/
|
|
480
|
+
export function fixStep(left, view) {
|
|
421
481
|
const passed = [];
|
|
482
|
+
const skip = () => (passed.length ? { fix: new Set(passed.map((x) => x.key)) } : undefined);
|
|
422
483
|
for (const o of left) {
|
|
423
484
|
// The reason comes from the recorded decision only, never from the model's words.
|
|
424
485
|
const command = refactorCommand({ type: o.type, name: o.name, ...(o.decision?.reason ? { reason: o.decision.reason } : {}) });
|
|
425
486
|
if (!command) {
|
|
426
|
-
passed.push(o);
|
|
487
|
+
passed.push({ key: o.key, line: passedOverLine(o.name) });
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (view && !view.object(o.key)) {
|
|
491
|
+
passed.push({ key: o.key, line: notInViewLine(o.name) });
|
|
427
492
|
continue;
|
|
428
493
|
}
|
|
429
|
-
const lines = [...passed.map((x) =>
|
|
430
|
-
if (
|
|
494
|
+
const lines = [...passed.map((x) => x.line), `${step('FIX', `fix ${o.name.toUpperCase()}.`)}`];
|
|
495
|
+
if (view?.object(o.key)?.part === 'failed-check')
|
|
431
496
|
lines.push(' Its last fix failed the check.');
|
|
432
|
-
return result('FIX', lines, { kind: 'queue', command, key: o.key },
|
|
497
|
+
return result('FIX', lines, { kind: 'queue', command, key: o.key }, skip());
|
|
433
498
|
}
|
|
434
|
-
return result('FIX', passed.map((x) =>
|
|
499
|
+
return result('FIX', passed.map((x) => x.line), { kind: 'continue' }, skip());
|
|
435
500
|
}
|
|
436
501
|
/** What a check of my claimed fixes measures: my one area, or the packages of the claimed objects. */
|
|
437
502
|
function proveScope(f, waiting) {
|
|
@@ -439,7 +504,7 @@ function proveScope(f, waiting) {
|
|
|
439
504
|
const joined = activeProject();
|
|
440
505
|
const lanes = joined ? readLanes(joined.projectDir).filter((l) => l.owner.trim().toLowerCase() === f.me.trim().toLowerCase()) : [];
|
|
441
506
|
if (lanes.length === 1)
|
|
442
|
-
return { args: ['refresh'], objects: myObjects(r, f.me).length };
|
|
507
|
+
return { args: ['refresh'], objects: myObjects(r, f.me, f.view).length };
|
|
443
508
|
const packages = [...new Set(waiting.map((o) => o.package))];
|
|
444
509
|
return {
|
|
445
510
|
args: ['refresh', '--packages', packages.join(',')],
|
|
@@ -469,7 +534,7 @@ async function prove(ctx) {
|
|
|
469
534
|
const f = facts(ctx);
|
|
470
535
|
if (!f.register)
|
|
471
536
|
return result('PROVE', [UNREADABLE_LINES[unreadableReason(f) ?? 'other']], { kind: 'stop' });
|
|
472
|
-
const waiting = toProve(f.register, f.me);
|
|
537
|
+
const waiting = toProve(f.register, f.me, f.view);
|
|
473
538
|
if (!waiting.length)
|
|
474
539
|
return result('PROVE', [], { kind: 'continue' });
|
|
475
540
|
const scope = proveScope(f, waiting);
|
|
@@ -555,23 +620,29 @@ async function report(ctx) {
|
|
|
555
620
|
return result('REPORT', lines, { kind: 'continue' }, { report: true });
|
|
556
621
|
}
|
|
557
622
|
const CSV_SAVED = '✓ Saved the CSV files for your own tools in the exports folder.';
|
|
623
|
+
const CSV_UPDATED = '✓ Updated the CSV files in the exports folder.';
|
|
558
624
|
/**
|
|
559
625
|
* The CSV export at REPORT (spec §3.4, D2): written when the project file's
|
|
560
626
|
* switch is on; otherwise one card. "Every time" turns the switch on (visible
|
|
561
627
|
* in the project file), "Just this once" writes them, "No" or a card put away
|
|
562
|
-
* writes nothing.
|
|
563
|
-
*
|
|
628
|
+
* writes nothing. CSV files already in the exports folder (G5) are rewritten
|
|
629
|
+
* without a card, so they never go stale. A headless chain never reaches
|
|
630
|
+
* REPORT (it stops at the next-step line before it), so there is no card and
|
|
631
|
+
* no file there.
|
|
564
632
|
*/
|
|
565
633
|
async function csvExport(ctx, projectDir, project) {
|
|
566
|
-
const
|
|
567
|
-
|
|
634
|
+
const dir = path.join(projectDir, 'reports', EXPORTS_DIR);
|
|
635
|
+
const write = (savedLine = CSV_SAVED) => {
|
|
636
|
+
const { saved, held } = writeExports(projectDir, project, dir, ctx.team.deps);
|
|
568
637
|
return [
|
|
569
|
-
...(saved.length ? [
|
|
638
|
+
...(saved.length ? [savedLine] : []),
|
|
570
639
|
...held.map((name) => ` "${name}" is open in another program, so it was not updated. Close it and say carry on.`),
|
|
571
640
|
];
|
|
572
641
|
};
|
|
573
642
|
if (exportsOn(project))
|
|
574
643
|
return write();
|
|
644
|
+
if (!exportsOff(project) && exportsPresent(dir))
|
|
645
|
+
return write(CSV_UPDATED);
|
|
575
646
|
const res = await ctx.team.confirm({
|
|
576
647
|
id: 'journey-export',
|
|
577
648
|
tag: 'QUESTION',
|
|
@@ -587,11 +658,26 @@ async function csvExport(ctx, projectDir, project) {
|
|
|
587
658
|
setExportsAlways(projectDir);
|
|
588
659
|
return [...write(), ' They are written with every report from now on.'];
|
|
589
660
|
}
|
|
590
|
-
|
|
591
|
-
|
|
661
|
+
const num = (n) => n.toLocaleString('en-US');
|
|
662
|
+
/** The closing count of objects kept: "1 kept as is", "2 kept as they are" (the view's words). */
|
|
663
|
+
export const keptPart = (n) => PART_WORDS.kept(n);
|
|
664
|
+
/**
|
|
665
|
+
* G2, project view Task 5: the DONE count, ` 18 objects: 4 clean, …` — my
|
|
666
|
+
* objects (`view.me`, retired and out of scope included), each in exactly one
|
|
667
|
+
* of the view's parts, said in the view's order and words, the zero parts left
|
|
668
|
+
* out. Null when there are no objects.
|
|
669
|
+
*/
|
|
670
|
+
export function doneLine(mine) {
|
|
671
|
+
if (!mine.total)
|
|
672
|
+
return null;
|
|
673
|
+
const parts = PART_ORDER.filter((p) => mine.parts[p]).map((p) => PART_WORDS[p](mine.parts[p]));
|
|
674
|
+
return ` ${plural(mine.total, 'object', 'objects')}${parts.length ? `: ${parts.join(', ')}` : ''}.`;
|
|
675
|
+
}
|
|
592
676
|
/* DONE — a short summary. */
|
|
593
677
|
/** Said instead of "Done for now." when the project has no counted check at all. */
|
|
594
678
|
export const NOTHING_MEASURED_LINE = 'Nothing is measured yet.';
|
|
679
|
+
/** B8: said instead of "Done for now." when "carry on" finds nothing new since the last DONE. */
|
|
680
|
+
export const NOTHING_LEFT_LINE = 'Nothing left to carry on.';
|
|
595
681
|
/** The folder's project has no counted check run (I15, review T7 finding 1). */
|
|
596
682
|
export function nothingMeasured(f) {
|
|
597
683
|
return !!f.register && f.register.runs.length === 0;
|
|
@@ -607,19 +693,20 @@ function done(ctx) {
|
|
|
607
693
|
if (nothingMeasured(f))
|
|
608
694
|
return nothingMeasuredResult('DONE');
|
|
609
695
|
const lines = ['Done for now.'];
|
|
610
|
-
if (f.register) {
|
|
611
|
-
const mine =
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
696
|
+
if (f.register && f.view) {
|
|
697
|
+
const mine = f.view.me(f.me);
|
|
698
|
+
// B8: "carry on" after DONE with nothing new since says so, then the same counts.
|
|
699
|
+
if (ctx.session) {
|
|
700
|
+
const print = `${newestRecordAt(f.register) ?? ''}|${f.register.runs.length}|${mine.active}`;
|
|
701
|
+
if (doneFingerprint(ctx.session) === print)
|
|
702
|
+
lines[0] = NOTHING_LEFT_LINE;
|
|
703
|
+
setDoneFingerprint(ctx.session, print);
|
|
704
|
+
}
|
|
705
|
+
// The view's parts (built with this session's skipped rows): retired and out of scope are parts of their own, so it adds up.
|
|
706
|
+
const counts = doneLine(mine);
|
|
707
|
+
if (counts)
|
|
708
|
+
lines.push(counts);
|
|
709
|
+
const rebuild = mine.parts.rebuild;
|
|
623
710
|
if (rebuild)
|
|
624
711
|
lines.push(` ${plural(rebuild, 'object', 'objects')} planned for a rebuild — handled separately.`);
|
|
625
712
|
}
|
package/dist/journey/trigger.js
CHANGED
|
@@ -17,17 +17,18 @@
|
|
|
17
17
|
* A pattern (*, %, ?) in such a sentence is refused in one line; no journey.
|
|
18
18
|
*
|
|
19
19
|
* Resume, in a folder that holds a project with no plan step pending: any of
|
|
20
|
-
* "carry on" / "continue
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* "carry on" / "continue clean core" resumes when no turn was interrupted and
|
|
21
|
+
* no answer is pending (live re-run L2, 2026-10-21: after any turn, not only
|
|
22
|
+
* after DONE, a pause or as the first words). A bare "continue" often answers
|
|
23
|
+
* the turn before, so it resumes only after a pause or DONE, or as the first
|
|
24
|
+
* words. Right after a pause line any of the words resume. In a folder
|
|
25
|
+
* without a project, "continue" keeps its old meaning.
|
|
24
26
|
*/
|
|
25
27
|
import { folderKind } from './clean-core.js';
|
|
26
|
-
import { isJourneyPaused, isJourneyPausedJustNow, userTurnCount } from './flow.js';
|
|
28
|
+
import { isJourneyEndedJustNow, isJourneyPaused, isJourneyPausedJustNow, userTurnCount } from './flow.js';
|
|
27
29
|
/** The runner's own words for a pattern (run.ts START). */
|
|
28
30
|
export const PATTERN_LINE = 'Name each package in full, for example ZFI_AR — not a pattern.';
|
|
29
31
|
const CC = String.raw `clean[\s-]?core`;
|
|
30
|
-
const CLEAN_CORE = new RegExp(CC, 'i');
|
|
31
32
|
/** Words in front of the verb that do not change what is asked. */
|
|
32
33
|
const FILLER = new RegExp(String.raw `^(?:(?:let'?s|let us|please|now|ok(?:ay)?|so|then|and|right|go ahead and|time to|(?:i|we)(?:'d| would)? (?:like|want) to|(?:i|we) need to)[,]?\s+)+`, 'i');
|
|
33
34
|
const QUESTION = /^(?:what|why|how|is|are|was|were|can|could|would|should|does|did|will|explain|tell|describe|which|when|where|who|whether|show)\b/i;
|
|
@@ -129,9 +130,8 @@ export function isResumePhrase(input) {
|
|
|
129
130
|
* Resume needs a project in `cwd` and no plan step in flight. Then:
|
|
130
131
|
* - right after a pause line, with no turn of the person's since, any of the
|
|
131
132
|
* words resumes — even after an interrupted step (that is what paused it);
|
|
132
|
-
* - otherwise nothing interrupted
|
|
133
|
-
*
|
|
134
|
-
* first turn (text and image turns count; tool results do not).
|
|
133
|
+
* - otherwise nothing interrupted and no answer pending: the words resume,
|
|
134
|
+
* whatever turn came before (L2).
|
|
135
135
|
*/
|
|
136
136
|
export function detectJourney(input, ctx) {
|
|
137
137
|
const start = parseJourneyStart(input);
|
|
@@ -149,5 +149,11 @@ export function detectJourney(input, ctx) {
|
|
|
149
149
|
return { kind: 'resume' };
|
|
150
150
|
if (s.turnInterrupted || s.awaitingSkillAnswer)
|
|
151
151
|
return null;
|
|
152
|
-
|
|
152
|
+
// A bare "continue" often answers the turn before, so it keeps its old rule:
|
|
153
|
+
// after a pause or DONE, or as the session's first words.
|
|
154
|
+
if (/^continue[.!]*$/i.test(input.trim())) {
|
|
155
|
+
return isJourneyPaused(ctx.session) || isJourneyEndedJustNow(ctx.session) || userTurnCount(ctx.session) === 0 ? { kind: 'resume' } : null;
|
|
156
|
+
}
|
|
157
|
+
// L2: "carry on" and "continue clean core" always resume here (after DONE, a pause, or any other turn).
|
|
158
|
+
return { kind: 'resume' };
|
|
153
159
|
}
|
package/dist/one-shot.js
CHANGED
|
@@ -78,10 +78,10 @@ import './tools/web/web_search.js';
|
|
|
78
78
|
import './tools/project/project_context_get.js';
|
|
79
79
|
import './tools/project/playbook_get.js';
|
|
80
80
|
import './tools/project/convention_get.js';
|
|
81
|
-
import './tools/project/cca_merge.js';
|
|
82
81
|
import './tools/project/upgrade_merge_progress.js';
|
|
83
82
|
import './tools/project/team_findings.js';
|
|
84
83
|
import './tools/project/team_estate.js';
|
|
84
|
+
import './tools/project/team_lane_move.js';
|
|
85
85
|
import './tools/subagent/_background-shared.js';
|
|
86
86
|
import './tools/subagent/background_run.js';
|
|
87
87
|
import './tools/subagent/monitor_emit.js';
|
|
@@ -97,6 +97,13 @@ function renderSapConnection(s) {
|
|
|
97
97
|
if (s.currentTransport) {
|
|
98
98
|
lines.push(` <current_transport>${escapeXml(s.currentTransport)}</current_transport>`);
|
|
99
99
|
}
|
|
100
|
+
// Task 6 (project view): the request the project in this folder records its fixes in. Absent outside one.
|
|
101
|
+
if (s.projectTransport) {
|
|
102
|
+
lines.push(` <project_transport>${escapeXml(s.projectTransport)}</project_transport>`);
|
|
103
|
+
// Fix round 1: say plainly where a write with no request goes (the write tools' order).
|
|
104
|
+
lines.push(` <transport_rule>A write that names no transport goes to the project request ${escapeXml(s.projectTransport)}, `
|
|
105
|
+
+ 'not to the current transport (a request that already holds the object, or the request of an approved plan, still wins).</transport_rule>');
|
|
106
|
+
}
|
|
100
107
|
if (s.activeScope) {
|
|
101
108
|
if (s.activeScope.package) {
|
|
102
109
|
lines.push(` <active_package>${escapeXml(s.activeScope.package)}</active_package>`);
|