@rubytech/create-maxy-code 0.1.186 → 0.1.187
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 +1 -1
- package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +14 -14
- package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +9 -23
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/skill-builder/references/pdf-generation.md +4 -4
- package/payload/platform/plugins/browser/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/browser/PLUGIN.md +78 -8
- package/payload/platform/plugins/browser/mcp/dist/index.js +111 -7
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts +98 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js +455 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts +9 -31
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js +6 -165
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts +153 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js +401 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js.map +1 -0
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +6 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +38 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +28 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js +106 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +19 -15
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js +2 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -1
- package/payload/platform/scripts/check-cypher-int-params.mjs +277 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +2 -2
- package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -2
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +33 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +5 -2
- package/payload/server/{chunk-L2YK2VK3.js → chunk-EFJ2EXZK.js} +3 -1
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +1 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Build-time gate: rejects raw JS numbers bound into Cypher integer positions.
|
|
3
|
+
//
|
|
4
|
+
// The neo4j-driver serializes a plain JS `number` as a Cypher FLOAT (15 -> 15.0).
|
|
5
|
+
// Cypher `LIMIT`/`SKIP` reject non-integers: "Invalid input. '15.0' is not a
|
|
6
|
+
// valid value. Must be a non-negative integer." A `LIMIT $p` / `SKIP $p` bound a
|
|
7
|
+
// raw number therefore THROWS on every call — and the throw is usually swallowed
|
|
8
|
+
// by a tool's catch and returned as empty data, so it presents as "no results"
|
|
9
|
+
// rather than an error. The fix at every call site is `neo4j.int(p)` (or the
|
|
10
|
+
// package's `int(...)` alias). Live repro: writer-craft voice-retrieve returned an
|
|
11
|
+
// empty voice profile in production because `LIMIT $k` was bound k=15.
|
|
12
|
+
//
|
|
13
|
+
// Posture: TARGETED, FAIL-OPEN. The gate fails the build only on a DEFINITE
|
|
14
|
+
// unwrapped binding — a `LIMIT $p`/`SKIP $p` param bound as object shorthand
|
|
15
|
+
// (`{ p }`) or as `p: <expr>` where <expr> contains no `int(`. When the params
|
|
16
|
+
// object cannot be statically resolved (built across multiple statements, spread
|
|
17
|
+
// from `...rest`, or passed through a helper), the site is skipped rather than
|
|
18
|
+
// flagged — zero false positives is the priority for a gate that must stay
|
|
19
|
+
// trusted. A novel unparseable offender would still surface at runtime via the
|
|
20
|
+
// existing per-tool error logging.
|
|
21
|
+
//
|
|
22
|
+
// Detection model:
|
|
23
|
+
// 1. Tokenize once into two newline-preserving masked views: comments blanked
|
|
24
|
+
// in both; string/template CONTENTS kept in `keepStr` (so `LIMIT $p` inside a
|
|
25
|
+
// query template is visible) and blanked in `noStr` (so braces/parens/commas
|
|
26
|
+
// inside queries and string values never confuse structural scanning).
|
|
27
|
+
// 2. Collect integer-position param names from `keepStr`: `LIMIT $p` / `SKIP $p`.
|
|
28
|
+
// 3. For each `.run(` call, resolve its params argument from `noStr` — an inline
|
|
29
|
+
// `{...}` object, or a bare identifier traced back to its `const/let/var X = {`
|
|
30
|
+
// object literal in the same file.
|
|
31
|
+
// 4. Within that object, flag each integer-position param bound shorthand or
|
|
32
|
+
// without an `int(` wrap.
|
|
33
|
+
|
|
34
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
|
35
|
+
import { dirname, join, relative, basename } from 'node:path'
|
|
36
|
+
import { fileURLToPath } from 'node:url'
|
|
37
|
+
|
|
38
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
|
|
39
|
+
const REPO_ROOT = join(SCRIPT_DIR, '..', '..')
|
|
40
|
+
|
|
41
|
+
const SCAN_ROOTS = [
|
|
42
|
+
join(REPO_ROOT, 'premium-plugins'),
|
|
43
|
+
join(REPO_ROOT, 'platform', 'plugins'),
|
|
44
|
+
join(REPO_ROOT, 'platform', 'ui', 'server'),
|
|
45
|
+
join(REPO_ROOT, 'platform', 'ui', 'app'),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
const ALLOWED_EXTS = new Set(['.ts', '.tsx'])
|
|
49
|
+
const SKIP_DIR_NAMES = new Set([
|
|
50
|
+
'node_modules', 'dist', 'build', '.next', 'payload', '__tests__',
|
|
51
|
+
])
|
|
52
|
+
|
|
53
|
+
function shouldScanFile(filePath) {
|
|
54
|
+
const name = basename(filePath)
|
|
55
|
+
if (name.endsWith('.test.ts') || name.endsWith('.test.tsx')) return false
|
|
56
|
+
const ext = name.includes('.') ? '.' + name.split('.').pop() : ''
|
|
57
|
+
return ALLOWED_EXTS.has(ext)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function* walk(root) {
|
|
61
|
+
let entries
|
|
62
|
+
try { entries = readdirSync(root) } catch { return }
|
|
63
|
+
for (const name of entries) {
|
|
64
|
+
if (SKIP_DIR_NAMES.has(name)) continue
|
|
65
|
+
const full = join(root, name)
|
|
66
|
+
let st
|
|
67
|
+
try { st = statSync(full) } catch { continue }
|
|
68
|
+
if (st.isDirectory()) yield* walk(full)
|
|
69
|
+
else if (st.isFile()) yield full
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Single tokenizer pass. Returns two strings of identical length to `src`, with
|
|
74
|
+
// newlines preserved so character offsets map back to line numbers:
|
|
75
|
+
// keepStr — comment characters replaced by spaces; string/template contents kept.
|
|
76
|
+
// noStr — comment characters AND string/template contents replaced by spaces.
|
|
77
|
+
// Delimiters (quotes, backticks, comment markers) become spaces in both.
|
|
78
|
+
function mask(src) {
|
|
79
|
+
const keep = new Array(src.length)
|
|
80
|
+
const no = new Array(src.length)
|
|
81
|
+
// states: code, line (//), block (/* */), sq ('), dq ("), tpl (`)
|
|
82
|
+
let state = 'code'
|
|
83
|
+
for (let i = 0; i < src.length; i++) {
|
|
84
|
+
const c = src[i]
|
|
85
|
+
const n = src[i + 1]
|
|
86
|
+
const blank = c === '\n' ? '\n' : ' '
|
|
87
|
+
if (state === 'code') {
|
|
88
|
+
if (c === '/' && n === '/') { keep[i] = no[i] = blank; state = 'line'; continue }
|
|
89
|
+
if (c === '/' && n === '*') { keep[i] = no[i] = blank; state = 'block'; continue }
|
|
90
|
+
if (c === "'") { keep[i] = no[i] = blank; state = 'sq'; continue }
|
|
91
|
+
if (c === '"') { keep[i] = no[i] = blank; state = 'dq'; continue }
|
|
92
|
+
if (c === '`') { keep[i] = no[i] = blank; state = 'tpl'; continue }
|
|
93
|
+
keep[i] = no[i] = c
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
if (state === 'line') {
|
|
97
|
+
keep[i] = no[i] = blank
|
|
98
|
+
if (c === '\n') state = 'code'
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
if (state === 'block') {
|
|
102
|
+
keep[i] = no[i] = blank
|
|
103
|
+
if (c === '*' && n === '/') {
|
|
104
|
+
// blank the closing slash too on the next iteration
|
|
105
|
+
no[i + 1] = keep[i + 1] = ' '
|
|
106
|
+
i++
|
|
107
|
+
state = 'code'
|
|
108
|
+
}
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
// inside a string/template: keep content in `keep`, blank it in `no`.
|
|
112
|
+
if (state === 'sq' || state === 'dq' || state === 'tpl') {
|
|
113
|
+
const closer = state === 'sq' ? "'" : state === 'dq' ? '"' : '`'
|
|
114
|
+
if (c === '\\') {
|
|
115
|
+
// escape: consume this and the next char as string content
|
|
116
|
+
keep[i] = c === '\n' ? '\n' : (c)
|
|
117
|
+
no[i] = blank
|
|
118
|
+
if (i + 1 < src.length) {
|
|
119
|
+
keep[i + 1] = src[i + 1] === '\n' ? '\n' : src[i + 1]
|
|
120
|
+
no[i + 1] = src[i + 1] === '\n' ? '\n' : ' '
|
|
121
|
+
i++
|
|
122
|
+
}
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
if (c === closer) { keep[i] = no[i] = blank; state = 'code'; continue }
|
|
126
|
+
keep[i] = c
|
|
127
|
+
no[i] = blank
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { keep: keep.join(''), no: no.join('') }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function lineAt(text, index) {
|
|
135
|
+
let line = 1
|
|
136
|
+
for (let i = 0; i < index && i < text.length; i++) if (text[i] === '\n') line++
|
|
137
|
+
return line
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const IDENT_RE = /^[A-Za-z_$][\w$]*$/
|
|
141
|
+
|
|
142
|
+
// Capture a balanced {...} starting at `noStr[start]` (which must be '{').
|
|
143
|
+
// Returns [objTextInNoStr, endIndexExclusive] or null.
|
|
144
|
+
function captureBraces(noStr, start) {
|
|
145
|
+
if (noStr[start] !== '{') return null
|
|
146
|
+
let depth = 0
|
|
147
|
+
for (let i = start; i < noStr.length; i++) {
|
|
148
|
+
if (noStr[i] === '{') depth++
|
|
149
|
+
else if (noStr[i] === '}') { depth--; if (depth === 0) return [noStr.slice(start, i + 1), i + 1] }
|
|
150
|
+
}
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Capture a balanced (...) starting at `noStr[start]` (which must be '(').
|
|
155
|
+
// Returns [argsTextInNoStr, endIndexExclusive] or null.
|
|
156
|
+
function captureParens(noStr, start) {
|
|
157
|
+
if (noStr[start] !== '(') return null
|
|
158
|
+
let depth = 0
|
|
159
|
+
for (let i = start; i < noStr.length; i++) {
|
|
160
|
+
if (noStr[i] === '(') depth++
|
|
161
|
+
else if (noStr[i] === ')') { depth--; if (depth === 0) return [noStr.slice(start, i + 1), i + 1] }
|
|
162
|
+
}
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Split args text (without the outer parens) on TOP-LEVEL commas only.
|
|
167
|
+
function splitTopLevelArgs(argsInner) {
|
|
168
|
+
const parts = []
|
|
169
|
+
let depth = 0
|
|
170
|
+
let cur = ''
|
|
171
|
+
for (const ch of argsInner) {
|
|
172
|
+
if (ch === '(' || ch === '{' || ch === '[') depth++
|
|
173
|
+
else if (ch === ')' || ch === '}' || ch === ']') depth--
|
|
174
|
+
if (ch === ',' && depth === 0) { parts.push(cur); cur = ''; continue }
|
|
175
|
+
cur += ch
|
|
176
|
+
}
|
|
177
|
+
if (cur.trim() !== '') parts.push(cur)
|
|
178
|
+
return parts
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Given a params object's text (from noStr) and the set of integer-position
|
|
182
|
+
// param names, return the names that are bound shorthand or without an int() wrap.
|
|
183
|
+
function findUnwrapped(objText, intParams) {
|
|
184
|
+
const offenders = []
|
|
185
|
+
for (const name of intParams) {
|
|
186
|
+
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
187
|
+
// Explicit binding `name : value`
|
|
188
|
+
const explicit = new RegExp(`\\b${esc}\\s*:\\s*([^,}]+)`)
|
|
189
|
+
const m = objText.match(explicit)
|
|
190
|
+
if (m) {
|
|
191
|
+
if (!/\bint\s*\(/.test(m[1])) offenders.push(name)
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
// Shorthand `{ name }` / `, name ,` / `, name }` — bare identifier, never wrapped.
|
|
195
|
+
const shorthand = new RegExp(`[{,]\\s*${esc}\\s*[,}]`)
|
|
196
|
+
if (shorthand.test(objText)) offenders.push(name)
|
|
197
|
+
}
|
|
198
|
+
return offenders
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const violations = []
|
|
202
|
+
|
|
203
|
+
for (const root of SCAN_ROOTS) {
|
|
204
|
+
for (const file of walk(root)) {
|
|
205
|
+
if (!shouldScanFile(file)) continue
|
|
206
|
+
let src
|
|
207
|
+
try { src = readFileSync(file, 'utf-8') } catch { continue }
|
|
208
|
+
if (!/\.run\s*\(/.test(src)) continue
|
|
209
|
+
|
|
210
|
+
const { keep, no } = mask(src)
|
|
211
|
+
|
|
212
|
+
// 1. Integer-position param names from the query strings (kept in `keep`).
|
|
213
|
+
const intParams = new Set()
|
|
214
|
+
for (const mm of keep.matchAll(/\b(?:LIMIT|SKIP)\s+\$(\w+)/gi)) intParams.add(mm[1])
|
|
215
|
+
if (intParams.size === 0) continue
|
|
216
|
+
|
|
217
|
+
// 2. Walk each `.run(` call.
|
|
218
|
+
for (const rm of no.matchAll(/\.run\s*\(/g)) {
|
|
219
|
+
const parenStart = rm.index + rm[0].length - 1
|
|
220
|
+
const cap = captureParens(no, parenStart)
|
|
221
|
+
if (!cap) continue
|
|
222
|
+
const inner = cap[0].slice(1, -1)
|
|
223
|
+
const args = splitTopLevelArgs(inner)
|
|
224
|
+
if (args.length < 2) continue
|
|
225
|
+
const paramArg = args[1].trim()
|
|
226
|
+
|
|
227
|
+
let objText = null
|
|
228
|
+
if (paramArg.startsWith('{')) {
|
|
229
|
+
// Inline object literal: re-capture against `no` for balanced braces.
|
|
230
|
+
const objStart = no.indexOf('{', parenStart)
|
|
231
|
+
const objCap = objStart >= 0 ? captureBraces(no, objStart) : null
|
|
232
|
+
if (objCap) objText = objCap[0]
|
|
233
|
+
} else if (IDENT_RE.test(paramArg)) {
|
|
234
|
+
// Single-variable params object: trace back to its declaration.
|
|
235
|
+
const esc = paramArg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
236
|
+
const declRe = new RegExp(`(?:const|let|var)\\s+${esc}\\b[^=]*=\\s*\\{`)
|
|
237
|
+
const dm = no.match(declRe)
|
|
238
|
+
if (dm) {
|
|
239
|
+
const braceIdx = no.indexOf('{', dm.index)
|
|
240
|
+
const objCap = braceIdx >= 0 ? captureBraces(no, braceIdx) : null
|
|
241
|
+
if (objCap) objText = objCap[0]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Unresolved params shape -> fail open (skip).
|
|
245
|
+
if (!objText) continue
|
|
246
|
+
|
|
247
|
+
for (const name of findUnwrapped(objText, intParams)) {
|
|
248
|
+
violations.push({
|
|
249
|
+
file: relative(REPO_ROOT, file),
|
|
250
|
+
line: lineAt(no, parenStart),
|
|
251
|
+
name,
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (violations.length > 0) {
|
|
259
|
+
// De-duplicate identical (file, line, name) tuples.
|
|
260
|
+
const seen = new Set()
|
|
261
|
+
const unique = violations.filter((v) => {
|
|
262
|
+
const k = `${v.file}:${v.line}:${v.name}`
|
|
263
|
+
if (seen.has(k)) return false
|
|
264
|
+
seen.add(k)
|
|
265
|
+
return true
|
|
266
|
+
})
|
|
267
|
+
console.error(`check-cypher-int-params: ${unique.length} raw-number Cypher integer binding(s):`)
|
|
268
|
+
for (const v of unique) {
|
|
269
|
+
console.error(` ${v.file}:${v.line}: LIMIT/SKIP $${v.name} is bound a raw JS number — wrap with int(${v.name})`)
|
|
270
|
+
}
|
|
271
|
+
console.error('')
|
|
272
|
+
console.error('neo4j-driver serializes a JS number as a Cypher float (15 -> 15.0); LIMIT/SKIP')
|
|
273
|
+
console.error('reject non-integers. Bind neo4j.int(p) (or the package int() alias) instead.')
|
|
274
|
+
process.exit(1)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log('check-cypher-int-params: ok (0 raw-number integer-position bindings)')
|
|
@@ -3,7 +3,7 @@ name: content-producer
|
|
|
3
3
|
description: "Author professional business documents (proposal, report, brief, memo, plan, case study, brochure), render PDFs, generate images, and publish static websites. Delegate when the operator asks for a written document, a PDF render, a generated image, or to publish a prepared website."
|
|
4
4
|
summary: "Produces visual output from your graph: generates images, renders pages to PDF, and hosts static websites you upload as a zip."
|
|
5
5
|
model: claude-sonnet-4-6
|
|
6
|
-
tools: Bash, Skill, mcp__memory__memory-search, mcp__replicate__image-generate, mcp__browser__browser-render, mcp__admin__file-attach, mcp__admin__plugin-read, mcp__admin__public-hostname, mcp__admin__publish-site
|
|
6
|
+
tools: Bash, Skill, mcp__memory__memory-search, mcp__replicate__image-generate, mcp__browser__browser-render, mcp__browser__browser-navigate, mcp__browser__browser-evaluate, mcp__browser__browser-resize, mcp__browser__browser-screenshot, mcp__browser__browser-pdf-save, mcp__admin__file-attach, mcp__admin__plugin-read, mcp__admin__public-hostname, mcp__admin__publish-site
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Content Producer
|
|
@@ -34,7 +34,7 @@ Three models via `image-generate`. Pick by output need: `recraft-v4` for design-
|
|
|
34
34
|
|
|
35
35
|
Generate the HTML to the print-CSS constraints. For A4 print, **the first tool call of this workflow branch MUST be `Skill a4-print-documents`. Any rendering step before the skill content is loaded into context is a contract violation.** Load `skill-load skillName=a4-print-documents`: the skill carries the print-CSS constraints (page margins, glassmorphism fallback, page-break rules, dark-background colour-adjust). Do not paraphrase those rules; load and follow.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Render the PDF on device with `browser-pdf-save`: serve or open the HTML, `browser-navigate` to it, then `browser-pdf-save` to the output path — it uses Chromium's print pipeline (honours `@page`/print CSS, prints backgrounds). For glassmorphism cover/back pages that do not survive print, `a4-print-documents` directs you to capture print-fallback images with `browser-screenshot` (element clip) after `browser-resize`; use `browser-evaluate` to hide on-screen chrome before the capture. Do not write or run Playwright scripts — these on-device tools are the rendering path.
|
|
38
38
|
|
|
39
39
|
When the brief is to author a generic business document (proposal / report / brief / memo / white paper / plan / case study), **the first tool call MUST be `Skill professional-document`. Any browser tool call before the skill is loaded into context is a contract violation.** Load `skill-load skillName=professional-document`: it carries the document-type catalogue, the refusal taxonomy for briefs owned by other composers (fundraise → `venture-studio:investor-data-room`; property → `real-agent:property-brochure` or `:property-preval`; decks → `admin:deck-pages`), the brand-token resolution, and the outline-approve-draft-render-save-deliver workflow. The skill loads `a4-print-documents` itself at the render phase — do not load it earlier. Format-only briefs ("create a PDF of this HTML", "save this as a one-pager", "render this brochure") stay on the existing `a4-print-documents` route above.
|
|
40
40
|
|
|
@@ -3,7 +3,7 @@ name: personal-assistant
|
|
|
3
3
|
description: "Scheduling, platform settings, messaging channels (Telegram, WhatsApp, email, Outlook), tunnel and domain setup, and browser automation. Delegate when the work is on your calendar, configuring the platform, sending or reading messages, or driving a browser."
|
|
4
4
|
summary: "Handles the operational tasks you'd give a personal assistant: scheduling meetings, managing your platform settings, connecting messaging channels, and completing browser-based tasks on your behalf."
|
|
5
5
|
model: claude-sonnet-4-6
|
|
6
|
-
tools: Skill, mcp__admin__system-status, mcp__admin__brand-settings, mcp__admin__account-manage, mcp__admin__account-update, mcp__admin__logs-read, mcp__admin__plugin-read, mcp__admin__file-attach, mcp__admin__wifi, mcp__contacts__contact-create, mcp__contacts__contact-lookup, mcp__contacts__contact-update, mcp__contacts__contact-delete, mcp__contacts__contact-list, mcp__contacts__contact-export, mcp__contacts__contact-erase, mcp__contacts__group-create, mcp__contacts__group-manage, mcp__telegram__message, mcp__telegram__message-history, mcp__telegram__telegram-webhook-register, mcp__whatsapp__whatsapp-login-start, mcp__whatsapp__whatsapp-login-wait, mcp__whatsapp__whatsapp-status, mcp__whatsapp__whatsapp-disconnect, mcp__whatsapp__whatsapp-send, mcp__whatsapp__whatsapp-send-document, mcp__whatsapp__whatsapp-config, mcp__whatsapp__whatsapp-activity, mcp__whatsapp__whatsapp-conversations, mcp__whatsapp__whatsapp-messages, mcp__whatsapp__whatsapp-conversation-graph-state, mcp__whatsapp__whatsapp-group-info, mcp__email__email-setup, mcp__email__email-read, mcp__email__email-send, mcp__email__email-reply, mcp__email__email-search, mcp__email__email-graph-query, mcp__email__email-otp-extract, mcp__email__email-status, mcp__email__email-fetch, mcp__email__email-ingest, mcp__outlook__outlook-account-register, mcp__outlook__outlook-mail-list, mcp__outlook__outlook-mail-search, mcp__outlook__outlook-calendar-list, mcp__outlook__outlook-calendar-event, mcp__outlook__outlook-contacts-list, mcp__outlook__outlook-mailbox-info, mcp__scheduling__schedule-event, mcp__scheduling__schedule-list, mcp__scheduling__schedule-get, mcp__scheduling__schedule-update, mcp__scheduling__schedule-cancel, mcp__scheduling__schedule-export-ics, mcp__scheduling__schedule-import-ics, mcp__scheduling__time-resolve, mcp__memory__memory-search, mcp__memory__profile-update, mcp__url-get__url-get, mcp__browser__browser-render
|
|
6
|
+
tools: Skill, mcp__admin__system-status, mcp__admin__brand-settings, mcp__admin__account-manage, mcp__admin__account-update, mcp__admin__logs-read, mcp__admin__plugin-read, mcp__admin__file-attach, mcp__admin__wifi, mcp__contacts__contact-create, mcp__contacts__contact-lookup, mcp__contacts__contact-update, mcp__contacts__contact-delete, mcp__contacts__contact-list, mcp__contacts__contact-export, mcp__contacts__contact-erase, mcp__contacts__group-create, mcp__contacts__group-manage, mcp__telegram__message, mcp__telegram__message-history, mcp__telegram__telegram-webhook-register, mcp__whatsapp__whatsapp-login-start, mcp__whatsapp__whatsapp-login-wait, mcp__whatsapp__whatsapp-status, mcp__whatsapp__whatsapp-disconnect, mcp__whatsapp__whatsapp-send, mcp__whatsapp__whatsapp-send-document, mcp__whatsapp__whatsapp-config, mcp__whatsapp__whatsapp-activity, mcp__whatsapp__whatsapp-conversations, mcp__whatsapp__whatsapp-messages, mcp__whatsapp__whatsapp-conversation-graph-state, mcp__whatsapp__whatsapp-group-info, mcp__email__email-provider-info, mcp__email__email-setup, mcp__email__email-read, mcp__email__email-send, mcp__email__email-reply, mcp__email__email-search, mcp__email__email-graph-query, mcp__email__email-otp-extract, mcp__email__email-status, mcp__email__email-fetch, mcp__email__email-ingest, mcp__outlook__outlook-account-register, mcp__outlook__outlook-mail-list, mcp__outlook__outlook-mail-search, mcp__outlook__outlook-calendar-list, mcp__outlook__outlook-calendar-event, mcp__outlook__outlook-contacts-list, mcp__outlook__outlook-mailbox-info, mcp__scheduling__schedule-event, mcp__scheduling__schedule-list, mcp__scheduling__schedule-get, mcp__scheduling__schedule-update, mcp__scheduling__schedule-cancel, mcp__scheduling__schedule-export-ics, mcp__scheduling__schedule-import-ics, mcp__scheduling__time-resolve, mcp__memory__memory-search, mcp__memory__profile-update, mcp__url-get__url-get, mcp__browser__browser-render, mcp__browser__browser-navigate, mcp__browser__browser-snapshot, mcp__browser__browser-click, mcp__browser__browser-fill, mcp__browser__browser-fill-form, mcp__browser__browser-type, mcp__browser__browser-press-key, mcp__browser__browser-hover, mcp__browser__browser-select-option, mcp__browser__browser-wait-for, mcp__browser__browser-handle-dialog, mcp__browser__browser-evaluate, mcp__browser__browser-console-messages, mcp__browser__browser-tabs, mcp__browser__browser-pdf-save, mcp__browser__browser-screenshot, mcp__browser__browser-resize
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Personal Assistant
|
|
@@ -40,7 +40,11 @@ Each domain has a small set of tools and, where it exists, a skill that drives t
|
|
|
40
40
|
|
|
41
41
|
**Cloudflare lives in the operator's browser.** Cloudflare exposes zero MCP tools. The operator's logged-in dashboard is the single source of truth. Do not drive the dashboard with a browser tool. Do not synthesise `cloudflared` flag combinations. Do not call the Cloudflare API or SDK. Do not edit `cert.pem`, `tunnel.state`, `config.yml`, or `alias-domains.json` directly. When a sanctioned surface fails, report with evidence and cite `plugins/cloudflare/references/reset-guide.md`. User-facing language: "Cloudflare account", "domain", "address", "sign in", "browser". Never say "zone", "CNAME", "account ID", "API", "SDK", or any hexadecimal identifier. After a tunnel script finishes, verify with `curl -I https://<hostname>`; a non-530 response means the tunnel is live.
|
|
42
42
|
|
|
43
|
-
**Reading JavaScript-rendered pages.** When
|
|
43
|
+
**Reading JavaScript-rendered pages.** When you only need to *read* a JS page and `url-get` comes back empty or as a shell, use `browser-render` with the URL. It renders the page in the device's Chromium and returns the rendered HTML plus visible text in one stateless call — it does not keep the page open.
|
|
44
|
+
|
|
45
|
+
**Automating a page.** When a task needs to *act* on a page (log in, fill a form, click through a flow, drive a web app), use the persistent automation tools. Start with `browser-navigate` to open the page and keep it alive, then `browser-snapshot` to see what is on the page — it returns each interactive element as a role, a name, and a unique CSS selector. Pass those selectors verbatim to `browser-click`, `browser-fill`, `browser-fill-form`, `browser-hover`, and `browser-select-option`. Use `browser-type` / `browser-press-key` for real keyboard input (e.g. Enter to submit), `browser-wait-for` after anything that loads asynchronously, `browser-evaluate` to read page state, and `browser-console-messages` to debug. Arm `browser-handle-dialog` *before* the action that triggers an alert/confirm/prompt, because dialogs block the page. `browser-tabs` opens or switches to a fresh page so stale state from a prior task does not leak in. If an action returns `session-lost`, the page closed — call `browser-navigate` again. Element targeting is by CSS selector (always from a fresh `browser-snapshot`), not by Playwright-style refs. Report CAPTCHA or bot detection as a blocker; do not try to solve it.
|
|
46
|
+
|
|
47
|
+
**Saving PDFs and screenshots.** `browser-pdf-save` renders the current page to a PDF on device (Chromium's print pipeline — honours `@page`/print CSS). `browser-screenshot` saves a PNG of the page or one element (CSS selector clip); `browser-resize` sets the viewport first when you need exact dimensions. For A4 print documents, follow the `a4-print-documents` skill rather than driving these directly.
|
|
44
48
|
|
|
45
49
|
## Optional capabilities
|
|
46
50
|
|
package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-retrieve-conditioning.d.ts","sourceRoot":"","sources":["../../src/tools/voice-retrieve-conditioning.ts"],"names":[],"mappings":"AA+BA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAMhC,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,MAAM,EAAE,WAAW,CAAC;QACpB;;;;WAIG;QACH,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,+BAA+B,CAAC,
|
|
1
|
+
{"version":3,"file":"voice-retrieve-conditioning.d.ts","sourceRoot":"","sources":["../../src/tools/voice-retrieve-conditioning.ts"],"names":[],"mappings":"AA+BA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAMhC,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,MAAM,EAAE,WAAW,CAAC;QACpB;;;;WAIG;QACH,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,+BAA+B,CAAC,CA0I1C"}
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* voice-retrieve-conditioning — return the operator's style card plus K
|
|
3
|
+
* voice-matched exemplars for a drafting brief.
|
|
4
|
+
*
|
|
5
|
+
* Task 462 changes:
|
|
6
|
+
* - `brief.format` (K-selector: 'short'|'long') renamed to `brief.length`
|
|
7
|
+
* to free the name for the new `brief.format: VoiceFormat` axis.
|
|
8
|
+
* - `brief.format: VoiceFormat` (required) — corpus exemplars are filtered
|
|
9
|
+
* to this writing format; the `:VoiceProfile` is the per-format profile.
|
|
10
|
+
* - All callers updated in the same commit.
|
|
11
|
+
*
|
|
12
|
+
* Retrieval order (best to worst):
|
|
13
|
+
* 1. If brief.topic is provided AND keyword search on the per-format corpus
|
|
14
|
+
* returns ≥K matches → use those (ordered by recency).
|
|
15
|
+
* 2. Otherwise, return the K most-recent per-format corpus nodes.
|
|
16
|
+
*
|
|
17
|
+
* Embeddings are not used in v1: the corpus is small (single operator's
|
|
18
|
+
* personal writing — typically <500 docs), and recency-with-keyword-filter
|
|
19
|
+
* is both faster and more predictable than vector ANN at that scale.
|
|
20
|
+
* Vector-search is a Task 365-follow-up if the corpus grows beyond the
|
|
21
|
+
* 500-doc breakeven point.
|
|
22
|
+
*
|
|
23
|
+
* Graceful degradation, discriminated (Task 493): the function never throws.
|
|
24
|
+
* Missing profile / empty corpus returns status "no-data"; a failed lookup
|
|
25
|
+
* (blank identity, driver/connection fault, Cypher error) returns status
|
|
26
|
+
* "error" with the message. The calling drafting skill falls back to its
|
|
27
|
+
* default register on both, but only "no-data" means "you have no profile".
|
|
28
|
+
*/
|
|
29
|
+
import neo4j from "neo4j-driver";
|
|
1
30
|
import { getSession } from "../lib/neo4j.js";
|
|
2
31
|
import { notTrashed } from "../lib/voice-corpus.js";
|
|
3
32
|
import { voiceCorpusWhereWithFormat, } from "../lib/voice-corpus.js";
|
|
@@ -85,7 +114,10 @@ export async function voiceRetrieveConditioning(params) {
|
|
|
85
114
|
accountId,
|
|
86
115
|
format,
|
|
87
116
|
topic,
|
|
88
|
-
|
|
117
|
+
// neo4j-driver serializes a plain JS number as a Cypher float (15 → 15.0),
|
|
118
|
+
// which LIMIT rejects ("not a valid value. Must be a non-negative integer").
|
|
119
|
+
// Send it as a Neo4j integer.
|
|
120
|
+
k: neo4j.int(k),
|
|
89
121
|
});
|
|
90
122
|
let charsUsed = 0;
|
|
91
123
|
const exemplars = [];
|
package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-retrieve-conditioning.js","sourceRoot":"","sources":["../../src/tools/voice-retrieve-conditioning.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voice-retrieve-conditioning.js","sourceRoot":"","sources":["../../src/tools/voice-retrieve-conditioning.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* "error" with the message. The calling drafting skill falls back to its
|
|
27
27
|
* default register on both, but only "no-data" means "you have no profile".
|
|
28
28
|
*/
|
|
29
|
-
import
|
|
29
|
+
import neo4j, { type Session } from "neo4j-driver";
|
|
30
30
|
import { getSession } from "../lib/neo4j.js";
|
|
31
31
|
import { notTrashed } from "../lib/voice-corpus.js";
|
|
32
32
|
import {
|
|
@@ -183,7 +183,10 @@ export async function voiceRetrieveConditioning(
|
|
|
183
183
|
accountId,
|
|
184
184
|
format,
|
|
185
185
|
topic,
|
|
186
|
-
|
|
186
|
+
// neo4j-driver serializes a plain JS number as a Cypher float (15 → 15.0),
|
|
187
|
+
// which LIMIT rejects ("not a valid value. Must be a non-negative integer").
|
|
188
|
+
// Send it as a Neo4j integer.
|
|
189
|
+
k: neo4j.int(k),
|
|
187
190
|
});
|
|
188
191
|
|
|
189
192
|
let charsUsed = 0;
|
|
@@ -5042,7 +5042,9 @@ async function loadUserProfile(accountId, userId, sessionId) {
|
|
|
5042
5042
|
WITH up, p, collect(pref) AS allPrefs
|
|
5043
5043
|
WITH up, p, allPrefs[0..$limit] AS prefs
|
|
5044
5044
|
RETURN up, p AS person, prefs`,
|
|
5045
|
-
|
|
5045
|
+
// List-range slice bounds are an integer position; a raw JS number
|
|
5046
|
+
// serializes as a Cypher float. Bind a Neo4j integer.
|
|
5047
|
+
{ accountId, userId, threshold: INJECTION_THRESHOLD, limit: neo4j.int(MAX_SUMMARY_PREFERENCES) }
|
|
5046
5048
|
);
|
|
5047
5049
|
if (result.records.length === 0 || !result.records[0].get("up")) {
|
|
5048
5050
|
return null;
|