tumwater 0.4.0 → 0.4.2
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 +4 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +5 -2
- package/dist/src/agent/backend-traits.js +21 -0
- package/dist/src/agent/claude/claude-backend.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +181 -182
- package/dist/src/agent/claude/{write-guard-hook.js → claude-write-guard-hook.js} +1 -1
- package/dist/src/agent/claude/claude-write-guard.js +5 -4
- package/dist/src/agent/codex/codex-backend.js +1 -1
- package/dist/src/agent/codex/codex-rollout.js +48 -45
- package/dist/src/agent/codex/codex-stream.js +269 -215
- package/dist/src/agent/codex/{write-guard-hook.js → codex-write-guard-hook.js} +2 -1
- package/dist/src/agent/codex/codex-write-guard.js +3 -3
- package/dist/src/agent/image-input.js +18 -87
- package/dist/src/agent/notes-shim.js +2 -2
- package/dist/src/agent/opencode/opencode-backend.js +2 -2
- package/dist/src/agent/opencode/opencode-catalog.js +2 -2
- package/dist/src/agent/opencode/opencode-config.js +2 -2
- package/dist/src/agent/opencode/opencode-stream.js +101 -92
- package/dist/src/agent/run-agent.js +76 -382
- package/dist/src/agent/run-settle.js +25 -15
- package/dist/src/agent/run-spawn.js +226 -0
- package/dist/src/agent/run-stream.js +185 -0
- package/dist/src/agent/session-ledger.js +16 -0
- package/dist/src/agent/write-guard.js +5 -3
- package/dist/src/backlog/backlog-archive.js +86 -170
- package/dist/src/backlog/backlog-cli-view.js +89 -70
- package/dist/src/backlog/backlog-eligibility.js +51 -59
- package/dist/src/backlog/backlog-file-bugs.js +133 -0
- package/dist/src/backlog/backlog-index-data.js +159 -0
- package/dist/src/backlog/backlog-index.js +7 -5
- package/dist/src/backlog/backlog-md.js +39 -28
- package/dist/src/backlog/backlog-release.js +21 -36
- package/dist/src/backlog/backlog-render.js +1 -1
- package/dist/src/backlog/backlog-structure.js +14 -145
- package/dist/src/backlog/backlog-write.js +31 -18
- package/dist/src/backlog/backlog.js +14 -4
- package/dist/src/backlog/question-answer.js +33 -15
- package/dist/src/baseline/main-baseline.js +71 -26
- package/dist/src/baseline/main-guardrails.js +76 -15
- package/dist/src/baseline/main-red.js +45 -94
- package/dist/src/baseline/main-watch.js +9 -3
- package/dist/src/brief/brief.js +40 -0
- package/dist/src/budget/budget-fallback-view.js +50 -8
- package/dist/src/budget/budget-wiring.js +15 -11
- package/dist/src/budget/budget.js +3 -2
- package/dist/src/build/build-check-events.js +4 -2
- package/dist/src/build/build-check-report.js +2 -1
- package/dist/src/build/build-check-scoped.js +70 -41
- package/dist/src/change/change-data.js +4 -2
- package/dist/src/cli/cli-args.js +9 -0
- package/dist/src/cli/cli-command-args.js +199 -98
- package/dist/src/cli/cli-flag-specs.js +6 -4
- package/dist/src/cli/cli-marker-commands.js +2 -2
- package/dist/src/cli/cli-query-commands.js +41 -6
- package/dist/src/cli/cli-review-commands.js +2 -1
- package/dist/src/cli/cli-role-command.js +46 -92
- package/dist/src/cli/cli-start.js +123 -47
- package/dist/src/cli/follow-view.js +3 -1
- package/dist/src/cli/help.js +19 -12
- package/dist/src/cli/log-commands.js +128 -107
- package/dist/src/cli/question-commands.js +82 -63
- package/dist/src/cli.js +396 -343
- package/dist/src/collections.js +46 -9
- package/dist/src/config/config-fallbacks.js +0 -4
- package/dist/src/config/config-live.js +179 -147
- package/dist/src/config/config-request.js +176 -51
- package/dist/src/config/config-validation-roles.js +271 -0
- package/dist/src/config/config-validation.js +10 -205
- package/dist/src/config/config-views.js +2 -7
- package/dist/src/config/config-write.js +205 -175
- package/dist/src/config/config.js +2 -1
- package/dist/src/doctor/doctor-backlog-bugs.js +24 -122
- package/dist/src/doctor/doctor-backlog-plans.js +24 -25
- package/dist/src/doctor/doctor-backlog.js +34 -42
- package/dist/src/doctor/doctor-check-names.js +4 -2
- package/dist/src/doctor/doctor-checks.js +16 -4
- package/dist/src/doctor/doctor-claude-checks.js +2 -2
- package/dist/src/doctor/doctor-codex-checks.js +9 -20
- package/dist/src/doctor/doctor-contention-checks.js +52 -19
- package/dist/src/doctor/doctor-holds.js +22 -20
- package/dist/src/doctor/doctor-model-checks.js +8 -184
- package/dist/src/doctor/doctor-notify.js +2 -5
- package/dist/src/doctor/doctor-opencode-checks.js +90 -41
- package/dist/src/doctor/doctor-principles.js +26 -0
- package/dist/src/doctor/doctor-project-checks.js +69 -19
- package/dist/src/doctor/doctor-role-notes.js +33 -0
- package/dist/src/doctor/doctor.js +13 -6
- package/dist/src/doctor/probe.js +1 -1
- package/dist/src/events/event-format.js +307 -162
- package/dist/src/events/event-labels.js +0 -1
- package/dist/src/events/event-legacy.js +1 -1
- package/dist/src/events/event-window.js +1 -1
- package/dist/src/events/events.js +15 -2
- package/dist/src/events/notify.js +1 -1
- package/dist/src/failure/failure-cluster.js +4 -3
- package/dist/src/failure/failure-data.js +126 -94
- package/dist/src/failure/failure-render.js +58 -17
- package/dist/src/failure/failure-state-change.js +308 -27
- package/dist/src/failure/time-spend.js +139 -106
- package/dist/src/files/files.js +7 -0
- package/dist/src/files/json-object.js +15 -2
- package/dist/src/gates/budget-gates.js +90 -68
- package/dist/src/gates/gate-polls.js +92 -61
- package/dist/src/gates/role-cap-gates.js +1 -1
- package/dist/src/gates/startup-gate.js +28 -28
- package/dist/src/gates/streak-gate.js +1 -1
- package/dist/src/git/git.js +1 -1
- package/dist/src/git/slot-salvage.js +9 -9
- package/dist/src/git/slots-state.js +12 -3
- package/dist/src/git/worktree-pool-startup.js +127 -0
- package/dist/src/git/worktree-pool.js +1 -120
- package/dist/src/gui/gui-admin-endpoints.js +3 -4
- package/dist/src/gui/gui-args.js +1 -1
- package/dist/src/gui/gui-backlog-commands.js +20 -11
- package/dist/src/gui/gui-backlog-endpoints.js +2 -8
- package/dist/src/gui/gui-config-commands.js +51 -76
- package/dist/src/gui/gui-endpoint-commands.js +4 -2
- package/dist/src/gui/gui-endpoint-common.js +4 -3
- package/dist/src/gui/gui-history-endpoints.js +36 -27
- package/dist/src/gui/gui-operator-commands.js +43 -17
- package/dist/src/gui/gui-prompt-commands.js +27 -21
- package/dist/src/gui/gui-quarantine-commands.js +30 -0
- package/dist/src/gui/gui-report-endpoints.js +13 -9
- package/dist/src/gui/gui-server.js +5 -3
- package/dist/src/gui/gui-team-commands.js +1 -1
- package/dist/src/history/history-data.js +7 -1
- package/dist/src/history/history-render.js +15 -10
- package/dist/src/history/history.js +103 -68
- package/dist/src/inbox/inbox-submit.js +1 -5
- package/dist/src/inbox/pending-prompt.js +3 -2
- package/dist/src/inbox/prompt-commands.js +194 -161
- package/dist/src/init/init.js +104 -25
- package/dist/src/merge/merge-core.js +1 -1
- package/dist/src/merge/merge-drain.js +1 -1
- package/dist/src/merge/merge-git.js +5 -5
- package/dist/src/merge/{merge-post-review.js → merge-post-review-drain.js} +1 -1
- package/dist/src/merge/merge-queue.js +2 -1
- package/dist/src/merge/merge-to-main.js +1 -1
- package/dist/src/merge/merge-verify.js +3 -2
- package/dist/src/operator/operator-commands.js +84 -52
- package/dist/src/operator/operator-intent.js +26 -3
- package/dist/src/operator/retire.js +2 -1
- package/dist/src/operator/revert.js +18 -12
- package/dist/src/orchestrator/orchestrator-poll.js +2 -2
- package/dist/src/orchestrator/orchestrator.js +1 -1
- package/dist/src/orchestrator/retention.js +6 -5
- package/dist/src/paths.js +10 -10
- package/dist/src/pi/pi-bin.js +2 -2
- package/dist/src/pi/pi-models.js +9 -0
- package/dist/src/pi/pi-stream.js +63 -41
- package/dist/src/pi/pi-watchdogs.js +84 -49
- package/dist/src/pi-extension/context-shake.js +55 -31
- package/dist/src/pi-extension/full-output.js +2 -3
- package/dist/src/process/process.js +24 -1
- package/dist/src/process/run-marker.js +2 -2
- package/dist/src/process/supervisor.js +24 -5
- package/dist/src/prompt/gate-notes.js +41 -16
- package/dist/src/prompt/notes-backlog.js +22 -29
- package/dist/src/prompt/principles.js +11 -0
- package/dist/src/prompt/prompt-followup.js +4 -12
- package/dist/src/prompt/prompt.js +15 -12
- package/dist/src/redeploy/redeploy-probes.js +11 -1
- package/dist/src/redeploy/redeployer.js +54 -7
- package/dist/src/redeploy/self-reload.js +7 -2
- package/dist/src/report/report-data.js +224 -320
- package/dist/src/report/report-fold.js +23 -231
- package/dist/src/report/report-main-red.js +70 -3
- package/dist/src/report/report-render.js +181 -48
- package/dist/src/report/report-since-data.js +218 -0
- package/dist/src/report/report-usage-fold.js +288 -0
- package/dist/src/report/report.js +2 -1
- package/dist/src/review/known-flakes.js +22 -3
- package/dist/src/review/post-merge-review-accept.js +3 -2
- package/dist/src/review/post-merge-review.js +16 -14
- package/dist/src/review/quarantine-render.js +29 -11
- package/dist/src/review/review-precheck.js +2 -1
- package/dist/src/roles/role-catalog.js +30 -29
- package/dist/src/roles/role-guidance.js +26 -43
- package/dist/src/roles/role-render.js +19 -0
- package/dist/src/roles/role-view.js +19 -5
- package/dist/src/roles/roles.js +2 -22
- package/dist/src/run/ci-parity.js +5 -6
- package/dist/src/run/feedback-filings.js +10 -1
- package/dist/src/run/qa-coverage.js +4 -4
- package/dist/src/run/run-apply.js +14 -2
- package/dist/src/run/run-detail-data.js +50 -10
- package/dist/src/run/run-detail-render.js +32 -1
- package/dist/src/run/run-detail.js +44 -22
- package/dist/src/run/run-dismiss.js +43 -0
- package/dist/src/run/run-finalize.js +75 -46
- package/dist/src/run/run-prompt.js +271 -187
- package/dist/src/run/run-resume.js +32 -31
- package/dist/src/run/run-stage.js +116 -64
- package/dist/src/run/run-timing.js +18 -4
- package/dist/src/run/run-verdict.js +119 -83
- package/dist/src/run/stage-backlog-checks.js +4 -31
- package/dist/src/run/stage-check.js +4 -12
- package/dist/src/run/stage-checks-shared.js +15 -0
- package/dist/src/run/stage-name-checks.js +3 -13
- package/dist/src/run/stage-path-checks.js +26 -29
- package/dist/src/runner/leftover.js +13 -4
- package/dist/src/runner/revision.js +2 -2
- package/dist/src/runner/runner-fallback.js +1 -1
- package/dist/src/runner/runner-outcome.js +0 -5
- package/dist/src/runner/runner-pi.js +72 -49
- package/dist/src/runner/runner-run.js +19 -102
- package/dist/src/runner/runner-state-load.js +0 -10
- package/dist/src/runner/runner.js +1 -32
- package/dist/src/scheduling/claims.js +4 -11
- package/dist/src/status/status-data.js +140 -91
- package/dist/src/status/status-polls.js +41 -5
- package/dist/src/status/status-role-notes.js +53 -0
- package/dist/src/status/status-roles.js +10 -37
- package/dist/src/text/datetime.js +8 -1
- package/dist/src/text/format.js +13 -1
- package/dist/src/text/phrases.js +16 -2
- package/dist/src/text/text.js +12 -0
- package/dist/src/ui/alert-feeds.js +115 -0
- package/dist/src/ui/badges.js +3 -4
- package/dist/src/ui/gui/gui-client-boot.js +78 -57
- package/dist/src/ui/gui/gui-client-drawer-config.js +8 -3
- package/dist/src/ui/gui/gui-client-drawer.js +18 -2
- package/dist/src/ui/gui/gui-client-feed.js +3 -8
- package/dist/src/ui/gui/gui-client-history.js +16 -16
- package/dist/src/ui/gui/gui-client-model.js +9 -9
- package/dist/src/ui/gui/gui-client-operator.js +6 -10
- package/dist/src/ui/gui/gui-client-pending.js +4 -4
- package/dist/src/ui/gui/gui-client-report-chart.js +111 -10
- package/dist/src/ui/gui/gui-client-report.js +70 -11
- package/dist/src/ui/gui/gui-client-roles.js +52 -12
- package/dist/src/ui/gui/gui-client-settings.js +31 -26
- package/dist/src/ui/gui/gui-client-team.js +28 -2
- package/dist/src/ui/gui/gui-client.js +33 -4
- package/dist/src/ui/gui/gui-icons.js +7 -1
- package/dist/src/ui/progress-data.js +2 -2
- package/dist/src/ui/run-progress-model.js +2 -2
- package/dist/src/ui/status-model.js +26 -16
- package/dist/src/ui/status-payload.js +149 -326
- package/dist/src/ui/status-render.js +131 -80
- package/dist/src/ui/team-alert-backlog.js +236 -0
- package/dist/src/ui/team-alert-shared.js +92 -0
- package/dist/src/ui/team-alert-status.js +326 -0
- package/dist/src/ui/team-alert-tail.js +238 -0
- package/dist/src/ui/team-alerts.js +16 -984
- package/dist/src/ui/transcript-tail.js +137 -94
- package/dist/src/ui/tui/tui-backlog.js +1 -1
- package/dist/src/ui/tui/tui-editor-answer.js +54 -44
- package/dist/src/ui/tui/tui-editor-attach.js +74 -63
- package/dist/src/ui/tui/tui-editor-backends.js +62 -56
- package/dist/src/ui/tui/tui-editor-brief.js +64 -54
- package/dist/src/ui/tui/tui-editor-budget.js +106 -89
- package/dist/src/ui/tui/tui-editor-config.js +2 -4
- package/dist/src/ui/tui/tui-editor-ctx.js +12 -1
- package/dist/src/ui/tui/tui-editor-entry.js +171 -153
- package/dist/src/ui/tui/tui-editor-instances.js +2 -1
- package/dist/src/ui/tui/tui-editor-instructions.js +50 -107
- package/dist/src/ui/tui/tui-editor-interval.js +39 -112
- package/dist/src/ui/tui/tui-editor-notes.js +28 -84
- package/dist/src/ui/tui/tui-editor-pause.js +210 -183
- package/dist/src/ui/tui/tui-editor-prompt.js +46 -35
- package/dist/src/ui/tui/tui-editor-queued.js +74 -64
- package/dist/src/ui/tui/tui-editor-quiet.js +122 -108
- package/dist/src/ui/tui/tui-editor-release.js +85 -0
- package/dist/src/ui/tui/tui-editor-retire.js +57 -46
- package/dist/src/ui/tui/tui-editor-role-enabled.js +90 -80
- package/dist/src/ui/tui/tui-editor-role-file.js +87 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +50 -42
- package/dist/src/ui/tui/tui-editor-search.js +70 -58
- package/dist/src/ui/tui/tui-editor-session.js +3 -0
- package/dist/src/ui/tui/tui-editors.js +4 -1
- package/dist/src/ui/tui/tui-frame.js +7 -3
- package/dist/src/ui/tui/tui-history.js +12 -10
- package/dist/src/ui/tui/tui-keys.js +6 -1
- package/dist/src/ui/tui/tui-pane-activity.js +62 -50
- package/dist/src/ui/tui/tui-pane-backlog.js +4 -4
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +45 -26
- package/dist/src/ui/tui/tui-pane-history.js +296 -264
- package/dist/src/ui/tui/tui-pane-report.js +74 -57
- package/dist/src/ui/tui/tui-pane-session.js +6 -2
- package/dist/src/ui/tui/tui-pane-transcript.js +158 -131
- package/dist/src/ui/tui/tui-pane.js +201 -174
- package/dist/src/ui/tui/tui-panes.js +211 -148
- package/dist/src/ui/tui/tui.js +4 -4
- package/dist/src/verdict/fix-claim.js +18 -6
- package/dist/src/verdict/refusal.js +32 -7
- package/package.json +1 -1
- package/dist/src/doctor/image-probe.js +0 -257
- /package/dist/src/gui/{http-body.js → gui-http-body.js} +0 -0
- /package/dist/src/merge/{backlog-conflicts.js → merge-backlog-conflicts.js} +0 -0
|
@@ -31,174 +31,192 @@ export function createEntryEditor(session) {
|
|
|
31
31
|
session.held.entry = null;
|
|
32
32
|
restoreDraft();
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
// On the Backlog view Ctrl+N files a new entry: it opens the title editor with a null
|
|
35
|
+
// position (new plan), and pressed again inside that editor flips plan <-> bug.
|
|
36
|
+
const flipNewEntryKind = () => {
|
|
37
|
+
const other = entryEditFor.kind === "plan" ? "bug" : "plan";
|
|
38
|
+
entryEditFor = { ...entryEditFor, kind: other };
|
|
39
|
+
session.held.entry = entryEditFor;
|
|
40
|
+
session.flashMessage(`new ${other} — Enter to file, Ctrl+N switches to ${other === "plan" ? "bug" : "plan"}, Esc cancels`);
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
// A Ctrl+N on any other state (not the Backlog view's new-entry slot, or an editor
|
|
44
|
+
// already holding the line) is the notes/change/trail cycle, a pane arm the outer
|
|
45
|
+
// dispatch handles — this factory returns false so it runs.
|
|
46
|
+
const openNewEntry = () => {
|
|
47
|
+
restoreDraft = session.takeDraft();
|
|
48
|
+
entryEditFor = { kind: "plan", position: null, title: "" };
|
|
49
|
+
session.held.entry = entryEditFor;
|
|
50
|
+
session.input = "";
|
|
51
|
+
session.cursor = 0;
|
|
52
|
+
session.flashMessage("new plan — Enter to file, Ctrl+N switches to bug, Esc cancels");
|
|
53
|
+
return true;
|
|
54
|
+
};
|
|
55
|
+
// A second Ctrl+E inside the entry editor toggles the same prompt line into the file
|
|
56
|
+
// field, whose path's contents replace the whole entry (splitEntryFile's rule) through
|
|
57
|
+
// tryEditEntry — the terminal twin of `tumwater plan|bug --edit <n> --file <path>`. A
|
|
58
|
+
// third Ctrl+E returns to the title draft, parked in the saved pair; Esc cancels all.
|
|
59
|
+
const toggleEntryFileField = () => {
|
|
60
|
+
entryBodyFile = !entryBodyFile;
|
|
61
|
+
if (entryBodyFile) {
|
|
62
|
+
entryBodySavedInput = session.input;
|
|
63
|
+
entryBodySavedCursor = session.cursor;
|
|
64
|
+
session.input = "";
|
|
65
|
+
session.cursor = 0;
|
|
66
|
+
session.flashMessage(`replace ${entryEditFor.kind} #${entryEditFor.position} from a file — Enter to save, Ctrl+E back to the title, Esc cancels`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
session.input = entryBodySavedInput;
|
|
70
|
+
session.cursor = entryBodySavedCursor;
|
|
71
|
+
session.flashMessage(`editing ${entryEditFor.kind} #${entryEditFor.position} title — Enter to save, Ctrl+E to replace from a file, Esc cancels`);
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
75
|
+
// Ctrl+E opens the title editor on the selected plan or bug (mnemonic: **E**dit); Enter
|
|
76
|
+
// saves through tryEditEntry, the non-exiting core the dashboard drawer shares, keeping
|
|
77
|
+
// the body and stamp. The prompt line is single-line, so only the title is editable here.
|
|
78
|
+
const openSelectedEntry = (ctx) => {
|
|
79
|
+
try {
|
|
80
|
+
const selected = selectedEditableEntry(session, ctx.selectedEntry, "edited");
|
|
81
|
+
if (selected !== null) {
|
|
82
|
+
const kind = selected.kind;
|
|
83
|
+
const position = selected.position;
|
|
50
84
|
restoreDraft = session.takeDraft();
|
|
51
|
-
entryEditFor = { kind
|
|
85
|
+
entryEditFor = { kind, position, title: selected.picked.title };
|
|
52
86
|
session.held.entry = entryEditFor;
|
|
53
|
-
session.input =
|
|
54
|
-
session.cursor =
|
|
55
|
-
session.flashMessage(
|
|
56
|
-
return true;
|
|
87
|
+
session.input = stripEntryStamp(selected.picked.title);
|
|
88
|
+
session.cursor = session.input.length;
|
|
89
|
+
session.flashMessage(`editing ${kind} #${position} — Enter to save, Esc to cancel`);
|
|
57
90
|
}
|
|
58
|
-
return false;
|
|
59
91
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
catch (err) {
|
|
93
|
+
session.flashError(err);
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
};
|
|
97
|
+
// Esc cancels the title editor, restoring the previous prompt text; nothing is written.
|
|
98
|
+
const cancelEntryEdit = () => {
|
|
99
|
+
const filing = entryEditFor.position === null;
|
|
100
|
+
exitEntryEdit();
|
|
101
|
+
session.flashMessage(filing ? "new entry cancelled" : "edit cancelled");
|
|
102
|
+
return true;
|
|
103
|
+
};
|
|
104
|
+
// Shared tail of the title and file edit arms: re-read the entry by flat index and
|
|
105
|
+
// title, guard against a concurrent move (so a mismatch is treated as moved, not
|
|
106
|
+
// written), then settle the editor and commit through tryEditEntry. The only thing
|
|
107
|
+
// the two arms pass differently is the (title, body) pair taking the entry's place;
|
|
108
|
+
// an omitted body preserves the matched entry's own body (the title edit arm).
|
|
109
|
+
const applyEntryEdit = (target, newTitle, newBody) => {
|
|
110
|
+
try {
|
|
111
|
+
// Both call sites (the file arm's and the title arm's Entry-save paths) run only
|
|
112
|
+
// when `target.position` is non-null, so the position math never sees a null.
|
|
113
|
+
const position = target.position;
|
|
114
|
+
const entries = session.flat();
|
|
115
|
+
const plans = entries.filter((e) => e.label === "plan").length;
|
|
116
|
+
const entry = entries[target.kind === "plan" ? position - 1 : plans + position - 1];
|
|
117
|
+
const outcome = entry?.label === target.kind && entry.title === target.title
|
|
118
|
+
? tryEditEntry(session.root, target.kind, position, target.title, newTitle, newBody ?? entry.body)
|
|
119
|
+
: { status: "moved", title: "" };
|
|
120
|
+
exitEntryEdit();
|
|
121
|
+
if (outcome.status === "edited") {
|
|
122
|
+
session.commitBacklogEdit(outcome.file, `Edit ${target.kind}: ${outcome.title}`);
|
|
123
|
+
const holds = backlogHolds(session.root);
|
|
124
|
+
const hold = (target.kind === "bug" ? holds.bugs : holds.plans)[position - 1] ?? null;
|
|
125
|
+
session.flashMessage(`edited ${target.kind} #${target.position}: ${outcome.title}${hold === null ? "" : ` — ${stillHeldNote(hold)}`}`);
|
|
126
|
+
}
|
|
127
|
+
else if (outcome.status === "unchanged") {
|
|
128
|
+
session.flashMessage(`unchanged ${target.kind} #${target.position}: ${outcome.title}`);
|
|
72
129
|
}
|
|
73
130
|
else {
|
|
74
|
-
session.
|
|
75
|
-
session.
|
|
76
|
-
session.flashMessage(`editing ${entryEditFor.kind} #${entryEditFor.position} title — Enter to save, Ctrl+E to replace from a file, Esc cancels`);
|
|
131
|
+
session.clearBacklogSelection();
|
|
132
|
+
session.flashMessage(`that ${target.kind} was already removed or moved — nothing edited`);
|
|
77
133
|
}
|
|
78
|
-
return true;
|
|
79
134
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
135
|
+
catch (err) {
|
|
136
|
+
session.flashError(err);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
// File mode: the line holds a path whose contents replace title and body through the
|
|
140
|
+
// same title-checked tryEditEntry core the title edit uses, keeping the entry's
|
|
141
|
+
// position and its (planned …)/(found by …) stamp. A missing file, an empty path, a
|
|
142
|
+
// file with no title line, and a structurally unsound body all flash and stay here.
|
|
143
|
+
const commitEntryFromFile = (target) => {
|
|
144
|
+
const text = readEditorPath(session);
|
|
145
|
+
if (text === null)
|
|
146
|
+
return;
|
|
147
|
+
const { title: rawTitle, body } = splitEntryFile(text);
|
|
148
|
+
const newTitle = stripEntryStamp(rawTitle);
|
|
149
|
+
if (!newTitle) {
|
|
150
|
+
session.flashMessage("the file needs a title line");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const problem = bodyStructureProblem(body);
|
|
154
|
+
if (problem !== null) {
|
|
155
|
+
session.flashMessage(problem);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
applyEntryEdit(target, newTitle, body);
|
|
159
|
+
};
|
|
160
|
+
// New entry (Ctrl+N): file by title with an empty body and wake its role, the same
|
|
161
|
+
// steps handleBacklogFile takes; a length problem or a throw stays in the editor.
|
|
162
|
+
const fileNewEntry = (target) => {
|
|
163
|
+
try {
|
|
164
|
+
const { role, wakeText } = OPERATOR_FILE_KINDS[target.kind];
|
|
165
|
+
const problem = promptLengthProblem(wakeText(collapseWhitespace(session.input)), role);
|
|
166
|
+
if (problem) {
|
|
167
|
+
session.flashMessage(problem);
|
|
168
|
+
return;
|
|
101
169
|
}
|
|
170
|
+
const filed = fileEntryWithBody(session.root, target.kind, session.input, "");
|
|
171
|
+
session.commitBacklogEdit(filed.file, `File ${target.kind}: ${filed.title}`);
|
|
172
|
+
submitRolePromptAndWake(session.root, role, wakeText(filed.title));
|
|
173
|
+
exitEntryEdit();
|
|
174
|
+
session.flashMessage(`filed ${target.kind}: ${filed.title} — woke ${role}`);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
session.flashError(err);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const commitEntryTitle = (target) => {
|
|
181
|
+
applyEntryEdit(target, session.input);
|
|
182
|
+
};
|
|
183
|
+
// Enter saves the new title through tryEditEntry with the body re-read now, so a
|
|
184
|
+
// concurrent body edit is not clobbered by a stale copy. The entry is matched by flat
|
|
185
|
+
// index and title; a mismatch is treated as moved without writing. A blank title and a
|
|
186
|
+
// throw flash and STAY in the editor so the text survives.
|
|
187
|
+
const commitEntryEdit = () => {
|
|
188
|
+
const target = entryEditFor;
|
|
189
|
+
if (entryBodyFile && target.position !== null) {
|
|
190
|
+
commitEntryFromFile(target);
|
|
102
191
|
return true;
|
|
103
192
|
}
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
const filing = entryEditFor.position === null;
|
|
107
|
-
exitEntryEdit();
|
|
108
|
-
session.flashMessage(filing ? "new entry cancelled" : "edit cancelled");
|
|
193
|
+
if (!session.input.trim()) {
|
|
194
|
+
session.flashMessage("an entry needs a title");
|
|
109
195
|
return true;
|
|
110
196
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
// concurrent body edit is not clobbered by a stale copy. The entry is matched by flat
|
|
114
|
-
// index and title; a mismatch is treated as moved without writing. A blank title and a
|
|
115
|
-
// throw flash and STAY in the editor so the text survives.
|
|
116
|
-
const target = entryEditFor;
|
|
117
|
-
// Shared tail of the title and file edit arms: re-read the entry by flat index and
|
|
118
|
-
// title, guard against a concurrent move (so a mismatch is treated as moved, not
|
|
119
|
-
// written), then settle the editor and commit through tryEditEntry. The only thing
|
|
120
|
-
// the two arms pass differently is the (title, body) pair taking the entry's place;
|
|
121
|
-
// an omitted body preserves the matched entry's own body (the title edit arm).
|
|
122
|
-
const applyEntryEdit = (newTitle, newBody) => {
|
|
123
|
-
try {
|
|
124
|
-
// Both call sites (the file arm's and the title arm's Entry-save paths) run only
|
|
125
|
-
// when `target.position` is non-null, so the position math never sees a null.
|
|
126
|
-
const position = target.position;
|
|
127
|
-
const entries = session.flat();
|
|
128
|
-
const plans = entries.filter((e) => e.label === "plan").length;
|
|
129
|
-
const entry = entries[target.kind === "plan" ? position - 1 : plans + position - 1];
|
|
130
|
-
const outcome = entry?.label === target.kind && entry.title === target.title
|
|
131
|
-
? tryEditEntry(session.root, target.kind, position, target.title, newTitle, newBody ?? entry.body)
|
|
132
|
-
: { status: "moved", title: "" };
|
|
133
|
-
exitEntryEdit();
|
|
134
|
-
if (outcome.status === "edited") {
|
|
135
|
-
session.commitBacklogEdit(outcome.file, `Edit ${target.kind}: ${outcome.title}`);
|
|
136
|
-
const holds = backlogHolds(session.root);
|
|
137
|
-
const hold = (target.kind === "bug" ? holds.bugs : holds.plans)[position - 1] ?? null;
|
|
138
|
-
session.flashMessage(`edited ${target.kind} #${target.position}: ${outcome.title}${hold === null ? "" : ` — ${stillHeldNote(hold)}`}`);
|
|
139
|
-
}
|
|
140
|
-
else if (outcome.status === "unchanged") {
|
|
141
|
-
session.flashMessage(`unchanged ${target.kind} #${target.position}: ${outcome.title}`);
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
session.clearBacklogSelection();
|
|
145
|
-
session.flashMessage(`that ${target.kind} was already removed or moved — nothing edited`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
session.flashError(err);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
// File mode: the line holds a path whose contents replace title and body through the
|
|
153
|
-
// same title-checked tryEditEntry core the title edit uses, keeping the entry's
|
|
154
|
-
// position and its (planned …)/(found by …) stamp. A missing file, an empty path, a
|
|
155
|
-
// file with no title line, and a structurally unsound body all flash and stay here.
|
|
156
|
-
if (entryBodyFile && target.position !== null) {
|
|
157
|
-
const text = readEditorPath(session);
|
|
158
|
-
if (text === null)
|
|
159
|
-
return true;
|
|
160
|
-
const { title: rawTitle, body } = splitEntryFile(text);
|
|
161
|
-
const newTitle = stripEntryStamp(rawTitle);
|
|
162
|
-
if (!newTitle) {
|
|
163
|
-
session.flashMessage("the file needs a title line");
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
const problem = bodyStructureProblem(body);
|
|
167
|
-
if (problem !== null) {
|
|
168
|
-
session.flashMessage(problem);
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
applyEntryEdit(newTitle, body);
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
if (!session.input.trim()) {
|
|
175
|
-
session.flashMessage("an entry needs a title");
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
if (target.position === null) {
|
|
179
|
-
// New entry (Ctrl+N): file by title with an empty body and wake its role, the same
|
|
180
|
-
// steps handleBacklogFile takes; a length problem or a throw stays in the editor.
|
|
181
|
-
try {
|
|
182
|
-
const { role, wakeText } = OPERATOR_FILE_KINDS[target.kind];
|
|
183
|
-
const problem = promptLengthProblem(wakeText(collapseWhitespace(session.input)), role);
|
|
184
|
-
if (problem) {
|
|
185
|
-
session.flashMessage(problem);
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
const filed = fileEntryWithBody(session.root, target.kind, session.input, "");
|
|
189
|
-
session.commitBacklogEdit(filed.file, `File ${target.kind}: ${filed.title}`);
|
|
190
|
-
submitRolePromptAndWake(session.root, role, wakeText(filed.title));
|
|
191
|
-
exitEntryEdit();
|
|
192
|
-
session.flashMessage(`filed ${target.kind}: ${filed.title} — woke ${role}`);
|
|
193
|
-
}
|
|
194
|
-
catch (err) {
|
|
195
|
-
session.flashError(err);
|
|
196
|
-
}
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
applyEntryEdit(session.input);
|
|
197
|
+
if (target.position === null) {
|
|
198
|
+
fileNewEntry(target);
|
|
200
199
|
return true;
|
|
201
200
|
}
|
|
201
|
+
commitEntryTitle(target);
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
204
|
+
const handleKey = (_str, key, ctx) => {
|
|
205
|
+
if (key.ctrl && key.name === "n") {
|
|
206
|
+
if (entryEditFor !== null && entryEditFor.position === null)
|
|
207
|
+
return flipNewEntryKind();
|
|
208
|
+
if (ctx.view === ctx.roleIds.length + 1 && !session.anyEditorHeld())
|
|
209
|
+
return openNewEntry();
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
if (key.ctrl && key.name === "e" && entryEditFor !== null && entryEditFor.position !== null)
|
|
213
|
+
return toggleEntryFileField();
|
|
214
|
+
if (key.ctrl && key.name === "e" && !session.anyEditorHeld() && ctx.view === ctx.roleIds.length + 1 && ctx.selectedEntry !== null)
|
|
215
|
+
return openSelectedEntry(ctx);
|
|
216
|
+
if (key.name === "escape" && entryEditFor)
|
|
217
|
+
return cancelEntryEdit();
|
|
218
|
+
if (key.name === "return" && entryEditFor)
|
|
219
|
+
return commitEntryEdit();
|
|
202
220
|
return false;
|
|
203
221
|
};
|
|
204
222
|
return { handleKey, exit: exitEntryEdit };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
2
2
|
import { rawRoleField } from "../../roles/role-view.js";
|
|
3
|
+
import { isExponentSpelling } from "../../text/text.js";
|
|
3
4
|
import { createRoleKeyEditor } from "./tui-editor-role-key.js";
|
|
4
5
|
/** The Ctrl+G parallel-runner-count editor on the shared prompt line, the terminal twin of
|
|
5
6
|
* `tumwater role <id> --instances` and the dashboard role drawer's instance-count editor. Enter
|
|
@@ -32,7 +33,7 @@ export function createInstancesEditor(session) {
|
|
|
32
33
|
// refuses (and the dashboard drawer's instance editor), so this refuses it too, naming
|
|
33
34
|
// the key and staying in edit mode. A non-exponent spelling falls through to
|
|
34
35
|
// validateConfig, whose message names the key.
|
|
35
|
-
if (
|
|
36
|
+
if (isExponentSpelling(text)) {
|
|
36
37
|
return { ok: false, error: `${cfgKey} must be a plain integer from 1 to 8 — no exponent notation` };
|
|
37
38
|
}
|
|
38
39
|
const r = setConfigKey(s.root, cfgKey, text);
|
|
@@ -1,117 +1,60 @@
|
|
|
1
1
|
import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
2
2
|
import { baseRoleOf } from "../../roles/runner-ids.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createRoleFileEditor } from "./tui-editor-role-file.js";
|
|
4
4
|
/** The Transcript view's Ctrl+L replace-standing-instructions editor on the shared prompt line,
|
|
5
5
|
* the terminal twin of `tumwater role <id> --instructions-file` and the dashboard role drawer's
|
|
6
|
-
* `Instructions override` row.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* notes view's cached Markdown recomputes the same role-inspection body the `Instructions
|
|
21
|
-
* override` row renders). Unlike quiet hours' director refusal there is none here: the
|
|
22
|
-
* dashboard editor and `--instructions-file` both accept the director, so it edits too. */
|
|
6
|
+
* `Instructions override` row. Ctrl+L opens it on a role transcript (the same in-range guard
|
|
7
|
+
* every transcript editor arm carries — the instructions row is reachable from the plain
|
|
8
|
+
* transcript, not only the notes mode). Enter trims the line as a file path, reads it verbatim
|
|
9
|
+
* with the same readTextIfExists the CLI's non-interactive replacement uses, and writes the
|
|
10
|
+
* BASE role's `roles.<base>.instructions` through the same setConfigKey/unsetConfigKey family
|
|
11
|
+
* the CLI and dashboard drawer use — so validateConfig (naming the dotted key and its
|
|
12
|
+
* ROLE_INSTRUCTIONS_MAX_CHARS cap) owns the value's truth. An empty or whitespace-only file
|
|
13
|
+
* clears the key (unsetConfigKey, the dashboard's empty-clears convention, giving the terminal
|
|
14
|
+
* a way to clear); an over-cap or unknown-role write flashes the returned error and STAYS open.
|
|
15
|
+
* On success the notes repaints through the session's refreshNotes seam (the notes view's
|
|
16
|
+
* cached Markdown recomputes the same role-inspection body the `Instructions override` row
|
|
17
|
+
* renders). Unlike quiet hours' director refusal there is none here: the dashboard editor and
|
|
18
|
+
* `--instructions-file` both accept the director, so it edits too. The open/exit/read framing
|
|
19
|
+
* lives in the shared createRoleFileEditor helper that also serves the notes editor. */
|
|
23
20
|
export function createInstructionsEditor(session) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
ctx.roleIds.length > 0 && ctx.view >= 1 && ctx.view <= ctx.roleIds.length) {
|
|
38
|
-
// Ctrl+L toggles the replace-instructions editor on a role's Transcript view (mnemonic:
|
|
39
|
-
// the instructions **l**ine, bound on no other TUI view except the Activity view's
|
|
40
|
-
// fallback editor and the Health view's retire-leftovers arm — the in-range role check
|
|
41
|
-
// keeps the arm from firing off a role transcript, and those other arms (which run before
|
|
42
|
-
// this one) keep Ctrl+L on Activity and Health).
|
|
43
|
-
// The line edits one file path that becomes the viewed role's whole
|
|
44
|
-
// `roles.<base>.instructions` override through the same setConfigKey write the CLI and
|
|
45
|
-
// the dashboard drawer use. Entering saves the prompt draft and blanks the line; leaving
|
|
46
|
-
// restores it.
|
|
47
|
-
if (session.editorEntryDenied("instructions"))
|
|
48
|
-
return true;
|
|
49
|
-
if (instructionsFor !== null) {
|
|
50
|
-
exitInstructionsMode();
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// Mid-recall, hand the arrows' saved draft back before the instructions editor takes
|
|
54
|
-
// the line — the same settle the role editors do — so leaving restores the draft, not
|
|
55
|
-
// a history entry, with no stale recall state riding along.
|
|
56
|
-
const role = ctx.roleIds[ctx.view - 1]; // defined: the open-arm range guard above pins the view into roleIds
|
|
57
|
-
restoreDraft = session.takeDraft();
|
|
58
|
-
instructionsFor = role;
|
|
59
|
-
session.held.instructions = role;
|
|
60
|
-
session.input = "";
|
|
61
|
-
session.cursor = 0;
|
|
62
|
-
session.flashMessage(`replace ${role}'s instructions from a file — Enter to save, Esc to cancel`);
|
|
63
|
-
}
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if (key.name === "escape" && instructionsFor !== null) {
|
|
67
|
-
// Esc cancels the instructions editor, restoring the previous prompt text and writing
|
|
68
|
-
// nothing.
|
|
69
|
-
exitInstructionsMode();
|
|
70
|
-
session.flashMessage("instructions edit cancelled");
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
if (key.name === "return" && instructionsFor !== null) {
|
|
74
|
-
// Enter reads the typed path whole and writes it through the shared
|
|
75
|
-
// setConfigKey/unsetConfigKey family the CLI's `--instructions-file`/`--clear-instructions`
|
|
76
|
-
// and the dashboard drawer use, so ROLE_INSTRUCTIONS_MAX_CHARS and the unknown-role check
|
|
77
|
-
// (enforced by validateConfig inside writeConfigMutation) and their wording cannot drift.
|
|
78
|
-
// An empty path, a missing file, an unreadable file, an over-cap text, and an unknown id
|
|
79
|
-
// all flash and STAY in the editor so the operator can fix the path; an empty or
|
|
80
|
-
// whitespace-only file clears the override (the dashboard's empty-clears convention). On
|
|
81
|
-
// success the notes repaints the updated `Instructions override` row and the editor
|
|
82
|
-
// exits with the shared success wording.
|
|
83
|
-
const role = instructionsFor;
|
|
84
|
-
const text = readEditorPath(session);
|
|
85
|
-
if (text === null)
|
|
86
|
-
return true;
|
|
21
|
+
return createRoleFileEditor(session, {
|
|
22
|
+
held: "instructions",
|
|
23
|
+
openCtrl: "l",
|
|
24
|
+
mayOpen: (ctx) =>
|
|
25
|
+
// The in-range role guard every transcript editor arm carries: Ctrl+L is bound on no
|
|
26
|
+
// other TUI view except the Activity view's fallback editor and the Health view's
|
|
27
|
+
// retire-leftovers arm, and those other arms (which run before this one) keep Ctrl+L on
|
|
28
|
+
// Activity and Health — so no extra view restriction is needed here beyond the roster
|
|
29
|
+
// guard that keeps the arm from firing off a role transcript.
|
|
30
|
+
ctx.roleIds.length > 0 && ctx.view >= 1 && ctx.view <= ctx.roleIds.length,
|
|
31
|
+
openFlash: (role) => `replace ${role}'s instructions from a file — Enter to save, Esc to cancel`,
|
|
32
|
+
cancelFlash: "instructions edit cancelled",
|
|
33
|
+
commit: (s, role, text) => {
|
|
87
34
|
const base = baseRoleOf(role);
|
|
88
35
|
const cfgKey = `roles.${base}.instructions`;
|
|
89
36
|
if (text.trim() === "") {
|
|
90
|
-
const result = unsetConfigKey(
|
|
91
|
-
if (!result.ok)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
: `${role}: instructions cleared`);
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
const result = setConfigKey(session.root, cfgKey, JSON.stringify(text));
|
|
103
|
-
if (!result.ok) {
|
|
104
|
-
session.flashMessage(result.error); // over the cap, unknown id, or write failure — stay in edit mode
|
|
105
|
-
return true;
|
|
37
|
+
const result = unsetConfigKey(s.root, cfgKey);
|
|
38
|
+
if (!result.ok)
|
|
39
|
+
return { ok: false, reason: result.error };
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
message: result.oldValue === undefined
|
|
43
|
+
? `${role}: instructions is not set in tumwater.json, so nothing was cleared`
|
|
44
|
+
: `${role}: instructions cleared`,
|
|
45
|
+
};
|
|
106
46
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
47
|
+
const result = setConfigKey(s.root, cfgKey, JSON.stringify(text));
|
|
48
|
+
if (!result.ok)
|
|
49
|
+
return { ok: false, reason: result.error };
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
message: result.unchanged
|
|
53
|
+
? `${role}: instructions unchanged, so tumwater.json was not written`
|
|
54
|
+
: `${role}: instructions saved (${text.length} chars)`,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
flashFailure: (s, reason) => s.flashMessage(reason),
|
|
58
|
+
refresh: (s, role) => s.refreshNotes(role),
|
|
59
|
+
});
|
|
117
60
|
}
|