@maka/maka-cli 5.147.0 → 5.149.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.147.0",
3
+ "version": "5.149.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",
@@ -6,7 +6,7 @@ import { fuzzyPickName } from '../utilities/fuzzy-match.js';
6
6
  import { describeFledTarget } from '../utilities/pursuit.js';
7
7
  import { shellsInRoom, resolveBodyAttack, crossPlaneRefusal } from '../utilities/planes.js';
8
8
  import { heldBy, holding } from '../utilities/grapple.js';
9
- import { spotOf, ensureAtSpot, ensureAtExit, canReach, spotDistanceMeters } from '../utilities/spots.js';
9
+ import { spotOf, ensureAtSpot, ensureAtExit, canReach, spotDistanceMeters, OPEN_FLOOR } from '../utilities/spots.js';
10
10
  import { resolveBarrierStrike } from '../utilities/barrier-combat.js';
11
11
  import { billAction, notYourPhase, phaseHint } from '../utilities/action-cost.js';
12
12
  import { spendMovementMeters } from '../utilities/movement-cost.js';
@@ -369,6 +369,18 @@ export class AttackCommand extends Command {
369
369
  // MELEE REACH: ground costs (p.161-162).
370
370
  if (isMelee) {
371
371
  const targetSpot = spotOf(target);
372
+ // An attacker with no spot of their own reads to canReach as
373
+ // "reaches everything" (the same compat rule that lets an unbound
374
+ // exit be worked from anywhere) -- fine for a location query, but
375
+ // for the ATTACKER in a reach check it skips ground-closing
376
+ // billing entirely: a hostile spawned mid-room, never having
377
+ // moved, could swing on a target the player had just paced away
378
+ // from in a fully gridded room (MotczCjebFNdHAWq7). Resolving
379
+ // an unset spot to the open floor first -- move.ts's own default
380
+ // -- gives canReach and spotDistanceMeters a real position to
381
+ // measure the reach/cost from instead of "unknown = anywhere".
382
+ if (spotOf(actor) === undefined)
383
+ actor.atSpot = OPEN_FLOOR;
372
384
  const mine = spotOf(actor);
373
385
  if (targetSpot !== undefined && !canReach(actor, targetSpot)) {
374
386
  const meters = mine !== undefined && room.ensureGrid() ? spotDistanceMeters(room, mine, targetSpot) : 0;
@@ -38,10 +38,11 @@ export class BacklogCommand extends Command {
38
38
  static verb = "backlog";
39
39
  // Writes to the player OWN board over their auth token -- never an NPC.
40
40
  static humanOnly = true;
41
- static description = 'File a play-tester item that somebody acts on -- it goes to maka-cli.com, keeps a link to this session, and comes back checked against the rulebooks. "backlog <what you noticed>" files one; "backlog list [status]" reads the board. For private scribbles, use "note".';
41
+ static description = 'File a play-tester item that somebody acts on -- it goes to maka-cli.com, keeps a link to this session, and comes back checked against the rulebooks. "backlog <what you noticed>" files one; "backlog list [status]" reads the board; "backlog web" opens the live board in your browser. For private scribbles, use "note".';
42
42
  static subcommands = [
43
43
  { usage: 'backlog <text>', short: 'File an item against this session.' },
44
44
  { usage: 'backlog list', short: 'The whole board, what needs you last.' },
45
+ { usage: 'backlog web', short: 'Open the live backlog board in your browser.' },
45
46
  { usage: 'backlog <n>', short: 'Read board item n in full.' },
46
47
  { usage: 'backlog repro <n>', short: 'Play the repro bench for board item n.' },
47
48
  { usage: 'backlog close <n> [why]', short: 'Close item n outright -- filed in error, duplicate, no longer real.' },
@@ -71,6 +72,12 @@ export class BacklogCommand extends Command {
71
72
  if (args.length === 0 || sub === 'list') {
72
73
  return this.list(args[1]);
73
74
  }
75
+ // Claimed here, same reason "accept"/"reject"/"repro"/"close" are:
76
+ // every unclaimed word past this point is report text, and "backlog
77
+ // web" must open the board, not file an item whose text is "web".
78
+ if (sub === 'web') {
79
+ return this.webBoard();
80
+ }
74
81
  // A BARE NUMBER READS THAT ROW OF THE BOARD, and it has to be caught
75
82
  // here for the same reason `list` and `review` are: everything that
76
83
  // reaches the bottom of this method FILES AN ITEM. Without this,
@@ -118,6 +125,23 @@ export class BacklogCommand extends Command {
118
125
  }
119
126
  return this.file(args.join(' '));
120
127
  }
128
+ /**
129
+ * "backlog web" (user request, 2026-09-10): the same /cli-login
130
+ * handoff as "sheet web" (sheet.ts), landing on the shared board --
131
+ * /backlog, already on the NEXT_ALLOWLIST. The token is guaranteed
132
+ * present here: execute() already refused above if authToken() was
133
+ * empty, before any subcommand (this one included) could be reached.
134
+ */
135
+ async webBoard() {
136
+ const { buildBacklogBoardUrl } = await import('../utilities/web-character-generator.js');
137
+ const url = buildBacklogBoardUrl(authToken());
138
+ const { openBrowser } = await import('../../../../tools/browser/open-browser.js');
139
+ openBrowser(url);
140
+ return [
141
+ `Your backlog board is opening in the browser -- the game keeps running right here.`,
142
+ ` ${url}`,
143
+ ].join('\n');
144
+ }
121
145
  async file(text) {
122
146
  const game = this.game;
123
147
  // LAZY, and not for speed: shared-run.ts pulls in tools/version/
@@ -57,7 +57,10 @@ export class DocwagonCommand extends Command {
57
57
  const surcharge = Math.ceil(def.price * check.surchargePct);
58
58
  const total = def.price + surcharge;
59
59
  if (this.actor.currency < total) {
60
- return `${reachable.name} taps the rate card: ${total} nuyen a year for ${tier}${surcharge > 0 ? ` (papers with a smell run extra)` : ''}. You're carrying ${this.actor.currency}.`;
60
+ return [
61
+ ...(check.lines[0] ? [check.lines[0]] : []),
62
+ `${reachable.name} taps the rate card: ${total} nuyen a year for ${tier}${surcharge > 0 ? ` (papers with a smell run extra)` : ''}. You're carrying ${this.actor.currency}.`,
63
+ ].join('\n');
61
64
  }
62
65
  const renewal = this.game.docwagon?.tier === tier;
63
66
  this.actor.adjustCurrency(-total);
@@ -161,7 +161,10 @@ export class InstallCommand extends Command {
161
161
  paymentLine = `You put the ${part.name} on the tray. The doc turns it over once, nods. "Part's good. Installation's on the house story-rate."`;
162
162
  }
163
163
  else if (actor.currency < aug.cost) {
164
- return `${aug.name} runs ${aug.cost} nuyen -- you're carrying ${actor.currency}. The doc doesn't do tabs on surgery.`;
164
+ return [
165
+ ...(identityLine ? [identityLine] : []),
166
+ `${aug.name} runs ${aug.cost} nuyen -- you're carrying ${actor.currency}. The doc doesn't do tabs on surgery.`,
167
+ ].join('\n');
165
168
  }
166
169
  else {
167
170
  actor.adjustCurrency(-aug.cost);
@@ -1840,8 +1840,9 @@ ${worldEventsSummary}
1840
1840
  ${wares.map(i => `- "${i.name}" (${priceOf(i)} nuyen)`).join('\n')}
1841
1841
  When a customer orders or agrees to buy, SERVE it in the same response: [COMMAND] give <item name> to <customer name>
1842
1842
  Serve EXACTLY the stock item whose name best matches what the customer asked for (they say "noodles", you have a "Noodle Bowl" -- that's the one). Substitute a different dish ONLY if the match is out of stock, and say so out loud when you do. The stock list above is the truth about what you have -- never claim a listed item is gone.
1843
- The till settles automatically on handoff (they're charged street price, or what they can cover). Quote the price when they order. Never serve someone who hasn't asked.`
1844
- : '';
1843
+ The till settles automatically on handoff (they're charged street price, or what they can cover). Quote the price when they order. Never serve someone who hasn't asked.
1844
+ NEVER claim a sale is complete from dialogue alone -- no "here you go," no till chirping, no describing them pocketing anything, unless this SAME response also issues the [COMMAND] give line above. A sale without that command simply never happened, and the customer is left owning nothing while believing they bought something.`
1845
+ : `YOUR SHOP STOCK: you have NOTHING owner-tagged to you on the shelf/counter right now -- the list above is empty. If a customer asks to buy something, you do not have it to sell: say so honestly (out of stock, don't carry it, check back later), or point them at where they might actually find it. NEVER narrate handing over an item, taking payment, or a till settling for anything that isn't real stock -- there is nothing here to [COMMAND] give, so nothing changes hands no matter how the conversation reads.`;
1845
1846
  // SWAG OVER THE LINE (source.ts; the shop-stock lesson's remote
1846
1847
  // twin): a contact with a TRADE can order anything in their domain
1847
1848
  // through the network -- but only the order system closes a deal.
@@ -82,6 +82,18 @@ export function buildCharacterSheetUrl(resumeToken, slug) {
82
82
  const next = `${CHARACTERS_PAGE}/${encodeURIComponent(slug)}`;
83
83
  return `${cliLoginUrl()}?t=${Date.now()}#token=${encodeURIComponent(resumeToken)}&next=${next}`;
84
84
  }
85
+ const BACKLOG_PAGE = '/backlog';
86
+ /**
87
+ * "backlog web" (user request, 2026-09-10): the same /cli-login handoff
88
+ * as the live sheet, landing on the shared backlog board -- /backlog,
89
+ * already on cli-login-next.ts's NEXT_ALLOWLIST as a bare path. Unlike
90
+ * buildCharacterSheetUrl/buildDossierUrl there is no per-item deep link
91
+ * to validate here: the board itself is the destination, not a row on
92
+ * it.
93
+ */
94
+ export function buildBacklogBoardUrl(resumeToken) {
95
+ return `${cliLoginUrl()}?t=${Date.now()}#token=${encodeURIComponent(resumeToken)}&next=${BACKLOG_PAGE}`;
96
+ }
85
97
  /**
86
98
  * WHAT THE CLI SAYS ON ITS WAY OUT of the chargen handoff. Extracted
87
99
  * from the command handler so the two things that matter about it are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.147.0",
3
+ "version": "5.149.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",