driggsby 0.9.0 → 0.9.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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// answer its rare question, and undo it; and driggsby accounts list. Built for agents with
|
|
4
4
|
// no Driggsby context: the file (or the ID Driggsby gives it) is the only
|
|
5
5
|
// handle, repeating any command is safe, `--wait` never outlasts a coding
|
|
6
|
-
// agent's command limit, and every output ends with the exact next
|
|
6
|
+
// agent's command limit, and every output ends with the exact next step
|
|
7
7
|
// (import-render.ts holds that contract).
|
|
8
8
|
import { apiBaseUrl } from "../api/base-url.js";
|
|
9
9
|
import { CliError } from "../cli-error.js";
|
|
@@ -117,7 +117,8 @@ async function runStart(session, options, io) {
|
|
|
117
117
|
}
|
|
118
118
|
return renderStatus(view, {
|
|
119
119
|
compact: options.wait && startedIn === "categorizing",
|
|
120
|
-
|
|
120
|
+
// Found already this far: a wait that watched it land shows it in full.
|
|
121
|
+
repeated: target.kind === "file" && !created && given === null && view.state === startedIn,
|
|
121
122
|
unchanged: outcome.note !== null,
|
|
122
123
|
rerun: { file: typedFile, account: options.account, create: options.create },
|
|
123
124
|
}, context(io));
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// What the import command prints. Every output follows one contract, so an
|
|
2
2
|
// agent with no Driggsby context never has to guess: the first line says
|
|
3
3
|
// where things stand ("Started:", "Waiting for an answer:", "Done:", ...)
|
|
4
|
-
// and is never wrapped, and the last lines give the exact
|
|
5
|
-
//
|
|
4
|
+
// and is never wrapped, and the last lines give the exact next step: a
|
|
5
|
+
// complete command wherever one exists, one per answer when the person must
|
|
6
|
+
// choose, and otherwise what to do (email support at the limit).
|
|
6
7
|
// Names Driggsby sends (a file, an account) print quoted and capped, so they
|
|
7
8
|
// read as names and never as the CLI's own sentence.
|
|
8
9
|
import { formatBytes } from "@driggsby/deploy";
|
|
@@ -60,7 +61,9 @@ function statusBody(view, options, context) {
|
|
|
60
61
|
case "waiting":
|
|
61
62
|
return ok(renderQuestion(view));
|
|
62
63
|
case "categorizing":
|
|
63
|
-
|
|
64
|
+
if (options.compact)
|
|
65
|
+
return ok(categorizingLine(view));
|
|
66
|
+
return ok(options.repeated ? alreadyImported(view) : landed(view, "Imported"));
|
|
64
67
|
case "done":
|
|
65
68
|
return ok(done(view, options, context));
|
|
66
69
|
case "stopped":
|
|
@@ -78,11 +81,15 @@ function undoingLine(view) {
|
|
|
78
81
|
: `Undoing: removing the transactions ${name(view.file)} added.`;
|
|
79
82
|
}
|
|
80
83
|
// Importing the file again, aimed where the first try was; a stop about the
|
|
81
|
-
// account starts from the account list instead
|
|
84
|
+
// account starts from the account list instead, and one at the limit from
|
|
85
|
+
// support, since running anything again would stop the same way.
|
|
82
86
|
function rerun(view, options) {
|
|
83
87
|
if (ACCOUNT_STOPS.has(view.stopped?.code ?? "")) {
|
|
84
88
|
return `Next, to see your accounts and their IDs:\n ${CLI} accounts list\n`;
|
|
85
89
|
}
|
|
90
|
+
if (view.stopped?.code === "limit_reached") {
|
|
91
|
+
return next("Email support@driggsby.com to keep importing.");
|
|
92
|
+
}
|
|
86
93
|
const flags = accountFlags(options.rerun.account, options.rerun.create);
|
|
87
94
|
if (options.rerun.file !== null && flags !== "") {
|
|
88
95
|
return next(`${CLI} import ${options.rerun.file}${flags}`);
|
|
@@ -136,10 +143,15 @@ export function renderQuestion(view) {
|
|
|
136
143
|
export function choiceLines(choices, command) {
|
|
137
144
|
return `${choices.map((choice) => ` ${name(choice.label)}\n ${command(choice)}`).join("\n")}\n`;
|
|
138
145
|
}
|
|
146
|
+
// One transaction "is", any other count "are".
|
|
147
|
+
function be(count) {
|
|
148
|
+
return count === 1 ? "is" : "are";
|
|
149
|
+
}
|
|
139
150
|
// The transactions are in: what landed, then what's left to do.
|
|
140
151
|
function landed(view, headline) {
|
|
141
|
-
const
|
|
142
|
-
const
|
|
152
|
+
const count = view.counts?.new ?? 0;
|
|
153
|
+
const where = view.account === null ? " landed" : ` ${be(count)} in ${name(view.account)}`;
|
|
154
|
+
const top = `${headline}: ${plural(count, "transaction")} from ${name(view.file)}${where}.`;
|
|
143
155
|
const undoCommand = ` ${CLI} import undo ${view.ref}\n`;
|
|
144
156
|
// A new account is the person's to name by its ID from here on.
|
|
145
157
|
const again = view.newAccount && view.accountRef !== null ? `Next time, for this account:\n ${CLI} import <FILE> --account ${view.accountRef}\n\n` : "";
|
|
@@ -182,6 +194,14 @@ function progressText(view) {
|
|
|
182
194
|
const left = progress.secondsLeft === null ? "" : `, ${timeLeft(progress.secondsLeft)}`;
|
|
183
195
|
return `${progress.categorized.toLocaleString("en-US")} of ${progress.total.toLocaleString("en-US")} so far${left}`;
|
|
184
196
|
}
|
|
197
|
+
// The same file sent again while categories fill in: it's already in, and
|
|
198
|
+
// this run imported nothing, so it never reads as a second import.
|
|
199
|
+
function alreadyImported(view) {
|
|
200
|
+
const count = view.counts?.new ?? 0;
|
|
201
|
+
const into = view.account === null ? " landed" : ` ${be(count)} in ${name(view.account)}`;
|
|
202
|
+
return (`Already imported: this file's ${plural(count, "transaction")}${into} (${view.ref}). Categories: ${progressText(view)}.\n\n` +
|
|
203
|
+
`${wrapProse("Nothing else to do unless you need the categories. To wait for them:")}\n ${waitCommand(view.ref)}\n`);
|
|
204
|
+
}
|
|
185
205
|
function categorizingLine(view) {
|
|
186
206
|
const progress = view.progress;
|
|
187
207
|
const where = progress === null || progress.categorized === 0
|
|
@@ -194,14 +214,15 @@ function done(view, options, context) {
|
|
|
194
214
|
const why = NOTHING_NEW[view.nothingNew] ?? "none of its transactions were new";
|
|
195
215
|
return `Done: nothing new in ${name(view.file)}: ${why}.\n\nNothing else to do.\n`;
|
|
196
216
|
}
|
|
217
|
+
const count = view.counts?.new ?? 0;
|
|
197
218
|
// A wait that followed categorizing ends on how it went, even for a file
|
|
198
219
|
// sent again.
|
|
199
220
|
if (options.compact && view.categorized === false) {
|
|
200
|
-
return (`Done: ${plural(
|
|
221
|
+
return (`Done: ${plural(count, "transaction")} from ${name(view.file)} ${be(count)} in, but some couldn't be categorized.\n\n` +
|
|
201
222
|
`${wrapProse("Those show as uncategorized. Nothing else to do: the import is finished.")}\n`);
|
|
202
223
|
}
|
|
203
224
|
if (options.compact) {
|
|
204
|
-
return `Done: all ${plural(
|
|
225
|
+
return `Done: ${count === 1 ? "the" : "all"} ${plural(count, "transaction")} from ${name(view.file)} ${be(count)} categorized.\n\nNothing else to do: the import is finished.\n`;
|
|
205
226
|
}
|
|
206
227
|
if (options.repeated) {
|
|
207
228
|
const on = view.finishedAt === null ? "" : ` on ${shortDate(view.finishedAt, context)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driggsby",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Set up and build on Driggsby, the MCP server for personal finance.",
|
|
5
5
|
"license": "Apache-2.0 AND OFL-1.1",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"node": ">=18"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@driggsby/deploy": "0.9.
|
|
34
|
-
"@driggsby/sdk": "0.9.
|
|
33
|
+
"@driggsby/deploy": "0.9.2",
|
|
34
|
+
"@driggsby/sdk": "0.9.2"
|
|
35
35
|
}
|
|
36
36
|
}
|