@cspeach/cli 1.1.30 → 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.
- package/README.md +11 -1
- package/dist/agent/loop.js +132 -9
- package/dist/cli.js +49 -18
- package/dist/commands/config-set.js +1 -1
- package/dist/commands/help.js +16 -1
- package/dist/commands/plan-chain.js +3 -0
- package/dist/commands/plan-continue.js +1 -1
- package/dist/commands/plan-gate.js +17 -1
- package/dist/commands/plan-resume.js +28 -18
- package/dist/commands/plan-startup.js +49 -6
- package/dist/commands/team-discover.js +195 -0
- package/dist/commands/team-plan.js +585 -0
- package/dist/commands/team-rules.js +187 -0
- package/dist/commands/team.js +971 -95
- package/dist/journey/chain.js +978 -0
- package/dist/journey/clean-core.js +235 -0
- package/dist/journey/flow.js +216 -0
- package/dist/journey/rebuild.js +464 -0
- package/dist/journey/run.js +628 -0
- package/dist/journey/session-deps.js +81 -0
- package/dist/journey/trigger.js +153 -0
- package/dist/lane/app-chain.js +21 -9
- package/dist/lane/chain-token.js +9 -5
- package/dist/lane/roots.js +97 -41
- package/dist/one-shot.js +28 -2
- package/dist/projects/clean-core-confirm.js +192 -3
- package/dist/projects/detail-path.js +9 -4
- package/dist/projects/extract-clean-core.js +3 -0
- package/dist/projects/migration.js +4 -105
- package/dist/projects/plan-lane.js +67 -17
- package/dist/projects/plan-sources.js +13 -7
- package/dist/projects/project-folder.js +131 -0
- package/dist/projects/promote-command.js +39 -0
- package/dist/projects/save-command.js +100 -12
- package/dist/projects/saved-lines.js +17 -3
- package/dist/projects/sort-command.js +499 -0
- package/dist/projects/sort-core.js +248 -0
- package/dist/projects/workspace-retire.js +48 -0
- package/dist/projects/workspace.js +182 -121
- package/dist/register/baseline.js +39 -2
- package/dist/register/decision-rules.js +35 -0
- package/dist/register/discover.js +931 -0
- package/dist/register/export-files.js +58 -0
- package/dist/register/findings.js +5 -2
- package/dist/register/measure.js +191 -9
- package/dist/register/offer.js +100 -8
- package/dist/register/picture.js +6 -2
- package/dist/register/plan-import.js +328 -0
- package/dist/register/records.js +40 -3
- package/dist/register/rule-proposal.js +4 -0
- package/dist/register/rules-edit.js +257 -0
- package/dist/register/status-file/estate-parts.js +168 -0
- package/dist/register/status-file/plan-parts.js +68 -0
- package/dist/register/status-file/progress-chart.js +110 -0
- package/dist/register/status-file/register-map.js +11 -3
- package/dist/register/status-file/register-model.js +40 -6
- package/dist/register/status-file/register-parts.js +57 -11
- package/dist/register/status-file/register-status-file.js +106 -90
- package/dist/register/status-file/report-model.js +569 -0
- package/dist/register/status-file/team-css.js +1 -1
- package/dist/register/status-file-sync.js +4 -0
- package/dist/register/status-html.js +27 -6
- package/dist/register/store.js +44 -5
- package/dist/register/variant.js +65 -0
- package/dist/register/workbooks.js +28 -42
- package/dist/register/write-whole.js +30 -2
- package/dist/register/xlsx.js +13 -3
- package/dist/register/xref.js +117 -0
- package/dist/renderer/startup-lines.js +11 -0
- package/dist/renderer/tool-labels.js +3 -1
- package/dist/renderer/tool-widget.js +15 -1
- package/dist/repl/at-completer.js +6 -2
- package/dist/repl/at-picker.js +1 -1
- package/dist/repl/builtin-commands.js +4 -0
- package/dist/repl/journey-entry.js +27 -0
- package/dist/repl/plan-hop.js +2 -9
- package/dist/repl/rules-menu.js +177 -0
- package/dist/repl/slash-completer.js +1 -0
- package/dist/repl/slash-picker.js +5 -4
- package/dist/repl.js +236 -59
- package/dist/router/classifier.js +8 -4
- package/dist/router/router-core.js +43 -7
- package/dist/router/routing-decision.js +1 -1
- package/dist/runs/bundled-runs.js +100 -0
- package/dist/runs/front-door.js +19 -0
- package/dist/runs/manifest-runs.js +50 -0
- package/dist/runs/run-command.js +24 -0
- package/dist/runs/runs.js +27 -0
- package/dist/runs/shadow.js +100 -0
- package/dist/runs/types.js +1 -0
- package/dist/session/backfill.js +342 -0
- package/dist/session/project-match.js +63 -0
- package/dist/session/queued-step.js +45 -0
- package/dist/session/resume.js +72 -24
- package/dist/session/schema.js +3 -1
- package/dist/session/store.js +96 -51
- package/dist/skill-catalog.js +2 -2
- package/dist/skills/bundled-skills.js +53 -53
- package/dist/tools/_filesystem-shared.js +174 -12
- package/dist/tools/_team-work-path.js +133 -0
- package/dist/tools/approval.js +7 -1
- package/dist/tools/dispatch-skill.js +8 -4
- package/dist/tools/filesystem/extract-xlsx.js +27 -3
- package/dist/tools/filesystem/file-edit.js +12 -4
- package/dist/tools/filesystem/file-read.js +8 -4
- package/dist/tools/filesystem/file-write.js +12 -4
- package/dist/tools/filesystem/glob.js +33 -8
- package/dist/tools/filesystem/grep.js +9 -4
- package/dist/tools/filesystem/read-document.js +6 -3
- package/dist/tools/lane/app-lane-resolve.js +1 -1
- package/dist/tools/project/cca_merge.js +8 -2
- package/dist/tools/project/rules_propose.js +59 -0
- package/dist/tools/project/team_estate.js +91 -0
- package/dist/tools/project/team_findings.js +89 -5
- package/dist/tools/project/upgrade_merge_progress.js +8 -2
- package/dist/tools/sap-read.js +21 -4
- package/dist/tools/sap-write.js +200 -10
- package/dist/tools/subagent/read_agent.js +3 -0
- package/dist/tools/tool-loading.js +1 -1
- package/dist/tools/transport-fit-check.js +117 -0
- package/dist/tools/transport-fit.js +15 -7
- package/dist/tools/transport.js +57 -2
- package/dist/ui/app.js +2 -2
- package/dist/ui/approval-modal.js +74 -6
- package/dist/ui/widgets/confirm-card.js +68 -5
- package/dist/upgrade-check.js +49 -4
- package/dist/util/open-in-browser.js +143 -0
- package/package.json +3 -3
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cspeach team rules …` — the decision rules, read and changed from the
|
|
3
|
+
* terminal (step 2, Task 10; spec 2026-10-08 §7 D7). `/rules` in a session
|
|
4
|
+
* (repl/rules-menu.ts) saves through the same `saveRuleEdit`.
|
|
5
|
+
*
|
|
6
|
+
* rules anyone: the numbered table
|
|
7
|
+
* rules on|off <id> lead
|
|
8
|
+
* rules set <id> --min-findings N | --max-findings N | --then <disposition> [--target L] | --target L|none
|
|
9
|
+
* rules add --template <name> lead
|
|
10
|
+
* rules remove <id> lead
|
|
11
|
+
* rules reset lead: the file names no rules again
|
|
12
|
+
*
|
|
13
|
+
* A save is the whole team project file, then renamed (every other key as it
|
|
14
|
+
* was), and one plan record in the lead's `plan/` so the change is a line of
|
|
15
|
+
* History and of the CSV events. Never reachable from a model tool.
|
|
16
|
+
*/
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { readDecisionRules } from '@cspeach/register-core';
|
|
19
|
+
import { RegisterError, readProjectFile, TEAM_FILE } from '../register/store.js';
|
|
20
|
+
import { writeLeadRecord } from '../register/records.js';
|
|
21
|
+
import { writeWhole } from '../register/write-whole.js';
|
|
22
|
+
import { addFromTemplate, removeRule, resetRules, rulesTableLines, setOutcome, setTarget, setThreshold, toggleRule, RULE_TEMPLATES, } from '../register/rules-edit.js';
|
|
23
|
+
import { flags, joinedOrThrow, requireLead, whoAmI } from './team.js';
|
|
24
|
+
import { reasonFrom } from './team-plan.js';
|
|
25
|
+
export const RULES_USAGE = 'Usage: cspeach team rules [on|off <id> | set <id> --min-findings N | --max-findings N | --then keep|fix|rebuild|retire-candidate [--target A-D] '
|
|
26
|
+
+ '| --target A-D|none | add --template <name> | remove <id> | reset] [--reason "<why>"]';
|
|
27
|
+
const SET_USAGE = 'Usage: cspeach team rules set <id> --min-findings N | --max-findings N | --then keep|fix|rebuild|retire-candidate [--target A-D] | --target A-D|none';
|
|
28
|
+
const SET_OPTIONS = ['min-findings', 'max-findings', 'then', 'target', 'reason'];
|
|
29
|
+
/** The one line someone who is not the lead gets for any change. */
|
|
30
|
+
export const rulesRefusal = (lead) => `Only the lead (${lead}) changes the rules. You can view them: cspeach team rules`;
|
|
31
|
+
/** The joined team project and its rules as the next read will see them. */
|
|
32
|
+
export function rulesNow(projectDir) {
|
|
33
|
+
const project = readProjectFile(projectDir);
|
|
34
|
+
return { project, ...readDecisionRules(project) };
|
|
35
|
+
}
|
|
36
|
+
/** Lead only, in the rules' own words; `requireLead` decides who the lead is. */
|
|
37
|
+
export function requireRulesLead(project, deps) {
|
|
38
|
+
try {
|
|
39
|
+
requireLead(project, deps);
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (e instanceof RegisterError)
|
|
43
|
+
throw new RegisterError(rulesRefusal(project.lead.name));
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** An edit over rules the file holds but this build cannot read would quietly throw them away; only reset may go ahead. */
|
|
48
|
+
export function refuseUnreadable(warning, edit) {
|
|
49
|
+
if (!warning || edit === 'reset')
|
|
50
|
+
return;
|
|
51
|
+
throw new RegisterError(`${warning.replace(/, so the default rules are used\.$/, '.')} Fix the file, or start again from the defaults: cspeach team rules reset`);
|
|
52
|
+
}
|
|
53
|
+
export const stampFor = (deps) => ({ by: whoAmI(deps), at: (deps.now?.() ?? new Date()).toISOString() });
|
|
54
|
+
/** The rules changed between the card and the save (a sync, a second terminal): nothing is written. */
|
|
55
|
+
export const RULES_CHANGED_LINE = 'The rules changed since you opened them. Open /rules again.';
|
|
56
|
+
/** Said before the first save of a project whose file names no rules yet (review round 1). */
|
|
57
|
+
export const DEFAULTS_PINNED_LINE = "Saving writes all rules into this project's file; later CSPeach default changes will not apply here until you Restore defaults.";
|
|
58
|
+
/**
|
|
59
|
+
* Save one edit: the team project file whole, then renamed — `decisionRules`
|
|
60
|
+
* replaced, or removed on reset, every other key as it was — then the plan
|
|
61
|
+
* record that puts it in History. Returns the lines to print.
|
|
62
|
+
*
|
|
63
|
+
* The rules in the file must still be the ones the edit was made from: if a
|
|
64
|
+
* sync or a second terminal changed them meanwhile, nothing is written. A
|
|
65
|
+
* History record that cannot be written after the file was saved is said, not
|
|
66
|
+
* hidden; the file keeps the change (it is what the person confirmed).
|
|
67
|
+
*/
|
|
68
|
+
export function saveRuleEdit(projectDir, edit, deps, reason = '') {
|
|
69
|
+
const joined = joinedOrThrow();
|
|
70
|
+
const project = readProjectFile(projectDir);
|
|
71
|
+
requireRulesLead(project, deps);
|
|
72
|
+
if (edit.basis && JSON.stringify(readDecisionRules(project).rules) !== JSON.stringify(edit.basis)) {
|
|
73
|
+
throw new RegisterError(RULES_CHANGED_LINE);
|
|
74
|
+
}
|
|
75
|
+
const next = { ...project };
|
|
76
|
+
if (edit.rules === null)
|
|
77
|
+
delete next.decisionRules;
|
|
78
|
+
else
|
|
79
|
+
next.decisionRules = edit.rules;
|
|
80
|
+
writeWhole(path.join(projectDir, TEAM_FILE), `${JSON.stringify(next, null, 2)}\n`);
|
|
81
|
+
const lines = [`✓ Saved. ${edit.summary}`];
|
|
82
|
+
try {
|
|
83
|
+
writeLeadRecord(path.join(projectDir, 'plan'), project.projectId, 'plan', {
|
|
84
|
+
action: 'rules',
|
|
85
|
+
rules: { change: edit.change, ...(edit.id ? { id: edit.id } : {}), summary: edit.summary, before: edit.before, after: edit.after },
|
|
86
|
+
...(reason ? { reason } : {}),
|
|
87
|
+
}, [], { name: whoAmI(deps), writer: joined.writerId }, {
|
|
88
|
+
...(deps.now ? { now: deps.now() } : {}), ...(deps.rand ? { rand: deps.rand } : {}),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
const why = (e instanceof Error ? e.message : String(e)).split('\n')[0].replace(/[\u0000-\u001f\u007f]/g, ' ').slice(0, 200);
|
|
93
|
+
lines.push(`! Saved, but no History line was written: ${why}`);
|
|
94
|
+
}
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
97
|
+
function onlyOptions(opts, known, usage) {
|
|
98
|
+
for (const name of Object.keys(opts))
|
|
99
|
+
if (!known.includes(name))
|
|
100
|
+
throw new RegisterError(`Unknown option --${name}.\n${usage}`);
|
|
101
|
+
}
|
|
102
|
+
/** A flag that needs a value got none: say which, never read "true" as the value. */
|
|
103
|
+
function valueOf(opts, name) {
|
|
104
|
+
const v = opts[name];
|
|
105
|
+
return v === 'true' ? '' : v;
|
|
106
|
+
}
|
|
107
|
+
export function rulesCommand(args, deps) {
|
|
108
|
+
const { positional, opts } = flags(args);
|
|
109
|
+
const [verb, id, ...extra] = positional;
|
|
110
|
+
const joined = joinedOrThrow();
|
|
111
|
+
const now = rulesNow(joined.projectDir);
|
|
112
|
+
if (!verb || verb === 'list' || verb === 'show') {
|
|
113
|
+
onlyOptions(opts, [], RULES_USAGE);
|
|
114
|
+
for (const line of rulesTableLines(now.rules, now.from, now.warning))
|
|
115
|
+
deps.print(line);
|
|
116
|
+
deps.print(`Change them in a session with /rules, or: cspeach team rules on|off|set|add|remove|reset (lead: ${now.project.lead.name})`);
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
if (!['on', 'off', 'set', 'add', 'remove', 'reset'].includes(verb))
|
|
120
|
+
throw new RegisterError(RULES_USAGE);
|
|
121
|
+
requireRulesLead(now.project, deps);
|
|
122
|
+
const reason = reasonFrom(opts, false);
|
|
123
|
+
const stamp = stampFor(deps);
|
|
124
|
+
let edit;
|
|
125
|
+
switch (verb) {
|
|
126
|
+
case 'on':
|
|
127
|
+
case 'off':
|
|
128
|
+
case 'remove': {
|
|
129
|
+
onlyOptions(opts, ['reason'], RULES_USAGE);
|
|
130
|
+
if (!id || extra.length)
|
|
131
|
+
throw new RegisterError(RULES_USAGE);
|
|
132
|
+
refuseUnreadable(now.warning, verb === 'remove' ? 'remove' : verb);
|
|
133
|
+
edit = verb === 'remove' ? removeRule(now.rules, id) : toggleRule(now.rules, id, verb === 'on', stamp);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'set': {
|
|
137
|
+
onlyOptions(opts, SET_OPTIONS, SET_USAGE);
|
|
138
|
+
if (!id || extra.length)
|
|
139
|
+
throw new RegisterError(SET_USAGE);
|
|
140
|
+
refuseUnreadable(now.warning, 'set');
|
|
141
|
+
const min = valueOf(opts, 'min-findings');
|
|
142
|
+
const max = valueOf(opts, 'max-findings');
|
|
143
|
+
const then = valueOf(opts, 'then');
|
|
144
|
+
const target = valueOf(opts, 'target');
|
|
145
|
+
const counts = min !== undefined || max !== undefined;
|
|
146
|
+
if (!counts && then === undefined && target === undefined)
|
|
147
|
+
throw new RegisterError(SET_USAGE);
|
|
148
|
+
if (counts && (then !== undefined || target !== undefined))
|
|
149
|
+
throw new RegisterError(`One change at a time: a threshold, or the outcome.\n${SET_USAGE}`);
|
|
150
|
+
if (counts) {
|
|
151
|
+
edit = setThreshold(now.rules, id, { ...(min !== undefined ? { minFindings: min } : {}), ...(max !== undefined ? { maxFindings: max } : {}) }, stamp);
|
|
152
|
+
}
|
|
153
|
+
else if (then !== undefined) {
|
|
154
|
+
const t = target === undefined ? undefined : target.toLowerCase() === 'none' ? null : target.toUpperCase();
|
|
155
|
+
edit = setOutcome(now.rules, id, then.toLowerCase(), stamp, t);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
edit = setTarget(now.rules, id, target.toLowerCase() === 'none' ? null : target.toUpperCase(), stamp);
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 'add': {
|
|
163
|
+
onlyOptions(opts, ['template', 'reason'], RULES_USAGE);
|
|
164
|
+
const template = valueOf(opts, 'template');
|
|
165
|
+
if (id || !template)
|
|
166
|
+
throw new RegisterError(`Usage: cspeach team rules add --template <name> Templates: ${Object.keys(RULE_TEMPLATES).join(', ')}`);
|
|
167
|
+
refuseUnreadable(now.warning, 'add');
|
|
168
|
+
edit = addFromTemplate(now.rules, template, stamp);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
default: {
|
|
172
|
+
onlyOptions(opts, ['reason'], RULES_USAGE);
|
|
173
|
+
if (id)
|
|
174
|
+
throw new RegisterError(RULES_USAGE);
|
|
175
|
+
if (now.from === 'default' && !now.warning) {
|
|
176
|
+
deps.print('The rules are the defaults already. Nothing was written.');
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
edit = resetRules(now.rules);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const line of saveRuleEdit(joined.projectDir, edit, deps, reason))
|
|
183
|
+
deps.print(line);
|
|
184
|
+
if (now.from === 'default' && edit.rules !== null)
|
|
185
|
+
deps.print(` ${DEFAULTS_PINNED_LINE}`);
|
|
186
|
+
return 0;
|
|
187
|
+
}
|