@rubytech/create-maxy-code 0.1.548 → 0.1.549

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.548",
3
+ "version": "0.1.549",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -13,7 +13,7 @@
13
13
  "build": "tsc",
14
14
  "bundle": "node scripts/bundle.js",
15
15
  "test": "npm run build && node --test 'dist/__tests__/*.test.js'",
16
- "prepublishOnly": "bash ../../platform/scripts/verify-skill-tool-surface.sh && node ../../platform/scripts/check-plugin-tools-mcp-consistency.mjs && node ../../platform/scripts/check-risk-class.mjs && node ../../platform/scripts/check-specialist-tool-surface.mjs && node ../../platform/scripts/check-agent-contract.mjs && node ../../platform/scripts/check-routing-prose-bash.mjs && node ../../platform/scripts/check-no-task-id-leaks.mjs && node ../../platform/scripts/check-no-esm-require.mjs && node ../../platform/scripts/check-no-raw-mcp-registrations.mjs && node ../../platform/scripts/check-skill-load-coverage.mjs && node ../../platform/scripts/check-architecture-skill-no-drift.mjs && node ../../platform/ui/scripts/check-brand-fonts.mjs && node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && node --test 'dist/__tests__/*.test.js' && chmod +x dist/index.js && npm run bundle && bash ../../platform/scripts/smoke-boot-services.sh && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
16
+ "prepublishOnly": "bash ../../platform/scripts/verify-skill-tool-surface.sh && node ../../platform/scripts/check-plugin-tools-mcp-consistency.mjs && node ../../platform/scripts/check-risk-class.mjs && node ../../platform/scripts/check-specialist-tool-surface.mjs && node ../../platform/scripts/check-agent-contract.mjs && node ../../platform/scripts/check-routing-prose-bash.mjs && node ../../platform/scripts/check-no-task-id-leaks.mjs && node ../../platform/scripts/check-no-esm-require.mjs && node ../../platform/scripts/check-no-raw-mcp-registrations.mjs && node ../../platform/scripts/check-skill-load-coverage.mjs && node ../../platform/scripts/check-routine-roster-doc.mjs && node ../../platform/scripts/check-architecture-skill-no-drift.mjs && node ../../platform/ui/scripts/check-brand-fonts.mjs && node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && node --test 'dist/__tests__/*.test.js' && chmod +x dist/index.js && npm run bundle && bash ../../platform/scripts/smoke-boot-services.sh && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
17
17
  },
18
18
  "files": [
19
19
  "dist",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:db7c14326a115e7d05b687b72bef0ba3892f1614ee46484e0e90414bf2c16f27
4
+ content-hash: sha256:67fd64d3983bd1c788d6945201c05ee4afeb63fd02fb033123b63e364e915430
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -3664,13 +3664,15 @@ one, over the same automation set the routes list. The roster seed's own
3664
3664
  `descNull` counter is its narrow twin, scoped to seeded rows and so blind to
3665
3665
  exactly the operator-created ones most likely to carry no caption.
3666
3666
 
3667
- **The shipped roster.** Every account starts with eleven routines —
3667
+ **The shipped roster.** Every account starts with thirteen routines —
3668
3668
  `start-of-day`, `inbound-check`, `end-of-day`, `appointment-reminder`,
3669
3669
  `commitment-chase`, `calendar-reconcile`, `contact-reconcile`, `crm-reconcile`,
3670
- `finance-reconcile`, `channel-link-check`, `weekly-digest` — seeded
3671
- `eventStatus:'suspended'` with `nextRun` NULL and no
3672
- channel or destination, so `/routines` is a catalogue to enable rather than a
3673
- blank page. The list query applies no status filter and `AUTOMATION_WHERE`
3670
+ `finance-reconcile`, `weekly-digest`, `channel-link-check`, `graph-health`,
3671
+ `filesystem-health`, in `/routines` display order. `ROUTINE_ROSTER_KEYS.length`
3672
+ is authoritative for the count and this list follows it. They are seeded
3673
+ `eventStatus:'suspended'` with `nextRun` NULL and no channel or destination, so
3674
+ `/routines` is a catalogue to enable rather than a blank page. The list query
3675
+ applies no status filter and `AUTOMATION_WHERE`
3674
3676
  admits them on `recurrence IS NOT NULL`, so they render with no change here.
3675
3677
  They cannot fire: every dispatch query in `check-due-events.ts` selects
3676
3678
  `eventStatus = 'scheduled'`, and the recurring one additionally requires
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-02 (0.1.549)
13
+
14
+ - Telegram now answers on the address it tells you to use, fixing messages that never arrived after yesterday's per-account change.
15
+ - The list of built-in routines in the docs is now checked against the routines that actually ship, so it cannot quietly fall out of date.
16
+
12
17
  ## 2026-08-02 (0.1.548)
13
18
 
14
19
  - Telegram now works per account: each bot has its own address and secret, a scheduled message goes to the right account, and a check reports any bot that is set up but never delivers.
@@ -279,13 +279,15 @@ one, over the same automation set the routes list. The roster seed's own
279
279
  `descNull` counter is its narrow twin, scoped to seeded rows and so blind to
280
280
  exactly the operator-created ones most likely to carry no caption.
281
281
 
282
- **The shipped roster.** Every account starts with eleven routines —
282
+ **The shipped roster.** Every account starts with thirteen routines —
283
283
  `start-of-day`, `inbound-check`, `end-of-day`, `appointment-reminder`,
284
284
  `commitment-chase`, `calendar-reconcile`, `contact-reconcile`, `crm-reconcile`,
285
- `finance-reconcile`, `channel-link-check`, `weekly-digest` — seeded
286
- `eventStatus:'suspended'` with `nextRun` NULL and no
287
- channel or destination, so `/routines` is a catalogue to enable rather than a
288
- blank page. The list query applies no status filter and `AUTOMATION_WHERE`
285
+ `finance-reconcile`, `weekly-digest`, `channel-link-check`, `graph-health`,
286
+ `filesystem-health`, in `/routines` display order. `ROUTINE_ROSTER_KEYS.length`
287
+ is authoritative for the count and this list follows it. They are seeded
288
+ `eventStatus:'suspended'` with `nextRun` NULL and no channel or destination, so
289
+ `/routines` is a catalogue to enable rather than a blank page. The list query
290
+ applies no status filter and `AUTOMATION_WHERE`
289
291
  admits them on `recurrence IS NOT NULL`, so they render with no change here.
290
292
  They cannot fire: every dispatch query in `check-due-events.ts` selects
291
293
  `eventStatus = 'scheduled'`, and the recurring one additionally requires
@@ -210,6 +210,12 @@ Use standard 5-field cron syntax: `minute hour day-of-month month day-of-week`.
210
210
 
211
211
  When `agentPrompt` is not among the fields, the line ends `(the instruction sent to the agent was not changed)`. That clause is the answer to "did my prompt rewrite land" — a confirmation without it means the dispatch prompt was written. Editing a meeting opens `Meeting updated:` and names the caller's own field names, not the stored `title`/`startsAt`/`endsAt`.
212
212
 
213
+ ## What the search vector holds
214
+
215
+ A routine is findable by graph search through a vector built from its name and its description. `schedule-update` rebuilds that vector whenever a call changes either field, and it builds the text from the ROW, not from the call: an update that supplies only a description takes the name from the stored row, and one that supplies only a name takes the description. Building it from the call alone re-embedded a caption edit as `Event: — <description>`, leaving the routine findable by its caption but not its title. The admin Routines page rebuilds the same text the same way.
216
+
217
+ One line reports every rebuild, including a healthy one, so its absence cannot be read as a code path that stopped running: `[schedule-update] op=embed-recomputed eventId=<id> titled=<yes|no> nameSource=<param|stored|absent> chars=<n> wrote=<yes|no>`. `titled` is the assertion, since the vector itself cannot be read back to tell whether the title went into it. `nameSource` makes a `titled=no` actionable by separating a routine that genuinely has no name (`absent`) from a fallback that failed. `chars` is the length of the embedded text, the one figure that shows the input shrinking across successive edits. `wrote` is what keeps `titled` honest: the embedder returns nothing on any failure and the vector clause is then skipped, so `titled=yes wrote=no` means the text was correct but the row kept the vector it already had. The line is emitted after the write lands, so an id that turns out to be a meeting never produces one.
218
+
213
219
  ## Skip next
214
220
 
215
221
  For recurring events, `schedule-update` with `skipNext: true` advances `nextRun` by one cycle without triggering. Use when the user says "skip tomorrow's briefing" or similar.
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ // check-routine-roster-doc.mjs — pre-commit gate (Task 2367).
3
+ //
4
+ // `platform/plugins/docs/references/admin-ui.md` carries one paragraph naming
5
+ // the routines every account is seeded with. That paragraph is mirrored into
6
+ // the generated platform-architecture SKILL.md and ships to every account, so
7
+ // a routine added to ROUTINE_ROSTER and not to the paragraph is not an internal
8
+ // docs nit: an agent asked what routines ship answers the stale number and
9
+ // cannot name the ones it omits. That is exactly what happened between Task
10
+ // 2214 (which took the roster from eleven to thirteen) and Task 2367.
11
+ //
12
+ // Three assertions, all against the same paragraph:
13
+ //
14
+ // 1. Every roster key appears in it as a backticked token. Checked by exact
15
+ // substring per key, so it holds whatever a future key looks like. This is
16
+ // the drift that actually happened.
17
+ // 2. Every backticked key-SHAPED token in it is a roster key. A routine
18
+ // deleted from roster.ts leaves a key in the prose that names nothing, and
19
+ // assertion 1 cannot see that. Key-shaped means lowercase words joined by
20
+ // at least one hyphen, which is what every roster key has looked like
21
+ // since Task 2130. The cost of this rule is that an unrelated hyphenated
22
+ // term added to this paragraph fails the gate; the failure names the rule,
23
+ // so the fix is to move the term or correct the key. A hyphenless key
24
+ // would fall outside this scan but is still fully covered by assertion 1.
25
+ // 3. The number-word for the roster length appears immediately before
26
+ // "routines". The count and the list state one fact twice, and gating
27
+ // only the list leaves half the defect free to recur. This is not an
28
+ // English parser: it maps the length through a fixed table and asserts a
29
+ // literal. A length with no word in the table is a hard failure naming
30
+ // the missing word, so the gate is fail-closed on its own blind spot.
31
+ //
32
+ // The roster is read as SOURCE TEXT, so this runs in a fresh worktree with no
33
+ // install and no build. Precedent: readRiskClasses() in
34
+ // platform/scripts/lib/canonical-tool-names.mjs parses tool-surface.ts the
35
+ // same way.
36
+ //
37
+ // Ordering note: this gate runs BEFORE check-architecture-skill-no-drift.mjs in
38
+ // .husky/pre-commit. A wrong edit to the paragraph fails both, and the drift
39
+ // gate's message ("SKILL.md was out of sync") names the symptom rather than the
40
+ // cause. Under `set -e` the first failure is the one the author reads.
41
+
42
+ import { readFileSync, existsSync } from 'node:fs';
43
+ import { resolve, dirname, relative } from 'node:path';
44
+ import { fileURLToPath } from 'node:url';
45
+
46
+ const SELF = fileURLToPath(import.meta.url);
47
+ const MAXY_CODE_ROOT = resolve(dirname(SELF), '..', '..');
48
+
49
+ const ROSTER_TS = resolve(MAXY_CODE_ROOT, 'platform/lib/routine-templates/src/roster.ts');
50
+ const DOC = resolve(MAXY_CODE_ROOT, 'platform/plugins/docs/references/admin-ui.md');
51
+
52
+ const PARAGRAPH_MARKER = '**The shipped roster.**';
53
+
54
+ // Indexed by count. Extend when the roster outgrows it; a length past the end
55
+ // is a hard failure below rather than a silent pass.
56
+ const NUMBER_WORDS = [
57
+ 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
58
+ 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
59
+ 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty',
60
+ ];
61
+
62
+ // Assertion 2's scan only. Anchored inside backticks and requiring a hyphen, so
63
+ // `nextRun` (uppercase), `/routines` (slash) and `check-due-events.ts` (dot)
64
+ // are all excluded.
65
+ const KEY_SHAPE = /`([a-z0-9]+(?:-[a-z0-9]+)+)`/g;
66
+
67
+ const rel = (p) => relative(MAXY_CODE_ROOT, p);
68
+
69
+ for (const p of [ROSTER_TS, DOC]) {
70
+ if (!existsSync(p)) {
71
+ console.error(`[roster-doc] FAIL missing file: ${rel(p)}`);
72
+ process.exit(1);
73
+ }
74
+ }
75
+
76
+ // The roster's key list, in roster order. The interface declaration
77
+ // (`readonly key: string;`) carries no quotes and does not match.
78
+ const rosterKeys = [...readFileSync(ROSTER_TS, 'utf8').matchAll(/^\s*key:\s*"([^"]+)",/gm)]
79
+ .map((m) => m[1]);
80
+
81
+ if (rosterKeys.length === 0) {
82
+ console.error(`[roster-doc] FAIL parsed zero keys from ${rel(ROSTER_TS)} — the entry shape changed`);
83
+ process.exit(1);
84
+ }
85
+
86
+ const doc = readFileSync(DOC, 'utf8');
87
+ const start = doc.indexOf(PARAGRAPH_MARKER);
88
+ if (start === -1) {
89
+ console.error(`[roster-doc] FAIL ${rel(DOC)} has no paragraph starting "${PARAGRAPH_MARKER}"`);
90
+ process.exit(1);
91
+ }
92
+ const end = doc.indexOf('\n\n', start);
93
+ const paragraph = end === -1 ? doc.slice(start) : doc.slice(start, end);
94
+
95
+ // 1 — every roster key present, by exact backticked substring.
96
+ const present = rosterKeys.filter((k) => paragraph.includes(`\`${k}\``));
97
+ const missing = rosterKeys.filter((k) => !paragraph.includes(`\`${k}\``));
98
+
99
+ // 2 — every key-shaped token present is a roster key.
100
+ const rosterKeySet = new Set(rosterKeys);
101
+ const shaped = new Set([...paragraph.matchAll(KEY_SHAPE)].map((m) => m[1]));
102
+ const stale = [...shaped].filter((k) => !rosterKeySet.has(k));
103
+
104
+ const problems = [];
105
+ if (missing.length) problems.push(` MISSING from the paragraph: ${missing.join(', ')}`);
106
+ if (stale.length) problems.push(` STALE in the paragraph (no such roster key): ${stale.join(', ')}`);
107
+
108
+ // 3 — the count word.
109
+ const expectedWord = NUMBER_WORDS[rosterKeys.length];
110
+ if (expectedWord === undefined) {
111
+ problems.push(
112
+ ` no number word for ${rosterKeys.length} — add it to NUMBER_WORDS in ${rel(SELF)}`,
113
+ );
114
+ } else if (!paragraph.includes(`${expectedWord} routines`)) {
115
+ const found = NUMBER_WORDS.find((w) => paragraph.includes(`${w} routines`));
116
+ problems.push(
117
+ ` count word: expected "${expectedWord} routines", found ${found ? `"${found} routines"` : 'no number word'}`,
118
+ );
119
+ }
120
+
121
+ if (problems.length) {
122
+ console.error(`[roster-doc] FAIL prose=${present.length} roster=${rosterKeys.length}`);
123
+ for (const l of problems) console.error(l);
124
+ process.exit(1);
125
+ }
126
+
127
+ console.log(`[roster-doc] ok prose=${present.length} roster=${rosterKeys.length}`);
@@ -13057,6 +13057,12 @@ app10.post("/", async (c) => {
13057
13057
  });
13058
13058
  var telegram_default = app10;
13059
13059
 
13060
+ // server/telegram-mount.ts
13061
+ function mountTelegramRoutes(app77, telegramRoutes) {
13062
+ app77.route("/api/telegram", telegramRoutes);
13063
+ app77.route("/api/telegram/", telegramRoutes);
13064
+ }
13065
+
13060
13066
  // server/routes/quickbooks.ts
13061
13067
  import { join as join30 } from "path";
13062
13068
  import { existsSync as existsSync22, readFileSync as readFileSync24, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync as rmSync3, renameSync as renameSync5 } from "fs";
@@ -32861,7 +32867,7 @@ app76.route("/api/webchat", createWebchatRoutes({
32861
32867
  resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
32862
32868
  }));
32863
32869
  app76.route("/api/webchat/greeting", webchat_greeting_default);
32864
- app76.route("/api/telegram", telegram_default);
32870
+ mountTelegramRoutes(app76, telegram_default);
32865
32871
  app76.route("/api/quickbooks", quickbooks_default);
32866
32872
  app76.route("/api/onboarding", onboarding_default);
32867
32873
  app76.route("/api/admin", admin_default);