@mmmbuto/nexuscrew 0.8.32 → 0.8.34

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.
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env python3
2
+ """Prepare an explicitly authorised local signature image for PDF overlay."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import os
8
+ import tempfile
9
+ from pathlib import Path
10
+
11
+
12
+ def parse_args() -> argparse.Namespace:
13
+ parser = argparse.ArgumentParser(
14
+ description=(
15
+ "Remove paper background, crop and export a signature as transparent PNG. "
16
+ "Use only with the signer's explicit authorisation."
17
+ )
18
+ )
19
+ parser.add_argument("src", type=Path)
20
+ parser.add_argument("out", type=Path)
21
+ parser.add_argument("--dark", type=int, default=90)
22
+ parser.add_argument("--light", type=int, default=185)
23
+ parser.add_argument("--margin", type=int, default=12)
24
+ parser.add_argument(
25
+ "--min-alpha",
26
+ type=int,
27
+ default=60,
28
+ help="discard low-opacity background pixels; 0 disables",
29
+ )
30
+ parser.add_argument(
31
+ "--max-width",
32
+ type=int,
33
+ default=0,
34
+ help="downsize to this pixel width; 0 preserves the cropped width",
35
+ )
36
+ parser.add_argument(
37
+ "--overwrite",
38
+ action="store_true",
39
+ help="replace an existing output file, never the source",
40
+ )
41
+ return parser.parse_args()
42
+
43
+
44
+ def load_dependencies():
45
+ try:
46
+ import numpy
47
+ from PIL import Image
48
+ except ImportError as exc:
49
+ raise SystemExit(
50
+ "Missing dependency: numpy and Pillow are required. With user "
51
+ "consent, install the packages listed in requirements.txt."
52
+ ) from exc
53
+ return numpy, Image
54
+
55
+
56
+ def validate_args(args: argparse.Namespace) -> tuple[Path, Path]:
57
+ source = args.src.resolve()
58
+ output_unresolved = args.out.expanduser()
59
+ if output_unresolved.is_symlink():
60
+ raise SystemExit("Output must not be a symbolic link.")
61
+ output = output_unresolved.resolve()
62
+
63
+ if not source.is_file():
64
+ raise SystemExit(f"Input image not found or not a regular file: {source}")
65
+ if source == output:
66
+ raise SystemExit("Source and output must be different files.")
67
+ if output.exists() and not args.overwrite:
68
+ raise SystemExit("Output already exists. Use --overwrite for an intentional revision.")
69
+ if output.suffix.lower() != ".png":
70
+ raise SystemExit("Output must use the .png extension.")
71
+ if not 0 <= args.dark < args.light <= 255:
72
+ raise SystemExit("Require 0 <= --dark < --light <= 255.")
73
+ if args.margin < 0 or args.margin > 1000:
74
+ raise SystemExit("--margin must be between 0 and 1000 pixels.")
75
+ if not 0 <= args.min_alpha <= 255:
76
+ raise SystemExit("--min-alpha must be between 0 and 255.")
77
+ if args.max_width < 0 or args.max_width > 20_000:
78
+ raise SystemExit("--max-width must be between 0 and 20000 pixels.")
79
+ return source, output
80
+
81
+
82
+ def flatten_to_greyscale(Image, source):
83
+ if source.mode in ("RGBA", "LA") or (
84
+ source.mode == "P" and "transparency" in source.info
85
+ ):
86
+ rgba = source.convert("RGBA")
87
+ flattened = Image.new("RGB", rgba.size, (255, 255, 255))
88
+ flattened.paste(rgba, mask=rgba.getchannel("A"))
89
+ return flattened.convert("L")
90
+ return source.convert("L")
91
+
92
+
93
+ def main() -> None:
94
+ args = parse_args()
95
+ source_path, output_path = validate_args(args)
96
+ np, Image = load_dependencies()
97
+
98
+ try:
99
+ with Image.open(source_path) as source:
100
+ source.load()
101
+ grey = np.asarray(flatten_to_greyscale(Image, source), dtype=np.float32)
102
+ except Exception as exc:
103
+ raise SystemExit(f"Could not read input image: {exc}") from exc
104
+
105
+ dark = float(args.dark)
106
+ light = float(args.light)
107
+ alpha = np.clip((light - grey) / (light - dark), 0.0, 1.0)
108
+
109
+ ink = alpha > 0.35
110
+ height, width = ink.shape
111
+ column_ink = ink.sum(axis=0)
112
+ row_ink = ink.sum(axis=1)
113
+ minimum_column = max(3, int(0.004 * height))
114
+ minimum_row = max(3, int(0.004 * width))
115
+ columns = np.where(column_ink >= minimum_column)[0]
116
+ rows = np.where(row_ink >= minimum_row)[0]
117
+ if columns.size == 0 or rows.size == 0:
118
+ raise SystemExit(
119
+ "No consistent ink stroke detected. Adjust --dark/--light and inspect the source."
120
+ )
121
+
122
+ margin = args.margin
123
+ x0 = max(0, int(columns[0]) - margin)
124
+ x1 = min(width, int(columns[-1]) + 1 + margin)
125
+ y0 = max(0, int(rows[0]) - margin)
126
+ y1 = min(height, int(rows[-1]) + 1 + margin)
127
+
128
+ cropped_alpha = (alpha[y0:y1, x0:x1] * 255).astype(np.uint8)
129
+ if args.min_alpha:
130
+ cropped_alpha[cropped_alpha < args.min_alpha] = 0
131
+
132
+ crop_height, crop_width = cropped_alpha.shape
133
+ pixels = np.zeros((crop_height, crop_width, 4), dtype=np.uint8)
134
+ pixels[..., 3] = cropped_alpha
135
+ prepared = Image.fromarray(pixels, "RGBA")
136
+
137
+ if args.max_width and crop_width > args.max_width:
138
+ new_height = max(1, round(crop_height * args.max_width / crop_width))
139
+ prepared = prepared.resize(
140
+ (args.max_width, new_height),
141
+ Image.Resampling.LANCZOS,
142
+ )
143
+
144
+ output_path.parent.mkdir(parents=True, exist_ok=True)
145
+ handle = tempfile.NamedTemporaryFile(
146
+ prefix=f".{output_path.name}.",
147
+ suffix=".tmp",
148
+ dir=output_path.parent,
149
+ delete=False,
150
+ )
151
+ temporary = Path(handle.name)
152
+ handle.close()
153
+ try:
154
+ prepared.save(temporary, format="PNG")
155
+ os.replace(temporary, output_path)
156
+ os.chmod(output_path, 0o600)
157
+ except Exception:
158
+ temporary.unlink(missing_ok=True)
159
+ raise
160
+ print(
161
+ f"Saved: {output_path} | crop {prepared.width}x{prepared.height}px "
162
+ f"from {width}x{height}px"
163
+ )
164
+ print(
165
+ "Verify the PNG on a pure white background. Keep it private and use it "
166
+ "only for the specifically authorised document."
167
+ )
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: mail-assistant
3
+ description: Use for mailbox discovery, email search and reading, priority triage, attachment review, folder organisation, reply drafting, explicit sending, or recurring mail checks through an available Gmail or IMAP/SMTP MCP connector. Select the user-facing language from the request and the draft language from the thread, while preserving read-before-write discipline and explicit confirmation for consequential mail actions.
4
+ ---
5
+
6
+ # Mail Assistant
7
+
8
+ Use the mail tools already exposed by the current client. Discover folders,
9
+ sender identities and supported operations live; never assume provider-specific
10
+ folder names or account configuration.
11
+
12
+ ## Select languages
13
+
14
+ Choose the language for digests, questions and explanations in this order:
15
+
16
+ 1. the user's explicit language preference;
17
+ 2. the language of the current request;
18
+ 3. a reliable client or system locale;
19
+ 4. English.
20
+
21
+ Write a reply draft in the language of the email thread unless the user asks
22
+ for another language. Preserve names, quoted text and required legal or
23
+ technical terms; do not translate them merely to match the interface language.
24
+
25
+ ## Choose tools
26
+
27
+ 1. Prefer an exposed Gmail connector for Gmail-hosted workflows.
28
+ 2. Otherwise use an exposed IMAP/SMTP mail MCP such as `mcp-email-rs`.
29
+ 3. Search narrowly before listing large folders, and fetch complete message
30
+ bodies only when needed for classification, attachments or drafting.
31
+ 4. If no mail tool is available, explain the missing capability. When this
32
+ skill is packaged with NexusCrew, the optional companion is documented in
33
+ `../../MCP_COMPANIONS.md`. Do not install or configure it without consent.
34
+
35
+ ## Apply the safety boundary
36
+
37
+ - Reading and searching are allowed when the user asks to inspect mail.
38
+ - Treat moves, flags, drafts, folder changes and calendar/reminder creation as
39
+ explicit mutations; perform only the mutation the user requested.
40
+ - Treat deletion and sending as consequential external actions. Confirm the
41
+ exact targets and intended content immediately before acting unless the
42
+ user's current request already gives unambiguous final authorization.
43
+ - Prefer recoverable archive or Trash moves over permanent deletion.
44
+ - Re-fetch message identifiers after moves; IMAP UIDs are folder-scoped and
45
+ can change.
46
+ - Never expose credentials, authentication links or sensitive message content
47
+ in logs, push notifications, TTS or unrelated summaries.
48
+ - Never infer an authorised sender identity. Use identities returned by the
49
+ connector or explicitly supplied by the user.
50
+
51
+ ## Triage and draft
52
+
53
+ 1. Fetch the smallest useful set of messages.
54
+ 2. Classify each item as urgent/action required, reply soon, waiting, or
55
+ informational, using the user's own priorities when available.
56
+ 3. Read attachments only when necessary and identify their type before
57
+ downloading.
58
+ 4. Present a compact digest with evidence from the messages and a proposed
59
+ next action.
60
+ 5. Draft only when requested. Preserve the user's tone, distinguish facts from
61
+ assumptions and leave unresolved details visible.
62
+ 6. Before sending, show or re-confirm recipients, subject, body and attachments.
63
+ 7. Report only actions that the tool actually completed.
64
+
65
+ ## Recurring checks
66
+
67
+ Create recurring monitoring only when explicitly requested. Use the
68
+ client-native scheduler or loop mechanism instead of manual polling, avoid
69
+ duplicate jobs, respect quiet hours, and remain silent on unchanged ticks when
70
+ the host workflow supports silent monitoring. A recurring authorization does
71
+ not automatically authorize sending or permanent deletion.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Mail Assistant"
3
+ short_description: "Safely triage and draft email in context"
4
+ default_prompt: "Use mail-assistant to review my new mail and prepare a concise action digest."
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: memory
3
+ description: Use when reading or writing persistent AI-agent state through a Memory MCP server, including category discovery, versioned reads and writes, merge patches, optimistic concurrency, bounded append-only journals, search, context loading and history. Keep durable state separate from bounded logs and choose the user-facing language from the request.
4
+ ---
5
+
6
+ # Memory
7
+
8
+ Use the exposed Memory MCP tools rather than reading or editing the server's
9
+ database, cache or state files directly.
10
+
11
+ ## Select response language
12
+
13
+ Choose the language for explanations and summaries in this order:
14
+
15
+ 1. the user's explicit language preference;
16
+ 2. the language of the current request;
17
+ 3. a reliable client or system locale;
18
+ 4. English.
19
+
20
+ Keep category names, tool names, JSON fields, paths and quoted source text
21
+ unchanged unless the user explicitly asks to translate them.
22
+
23
+ ## Understand the model
24
+
25
+ - A category is a named JSON document. `memory_read` takes a `category`, not a
26
+ nested key.
27
+ - Reads may be broadly available while writes are scoped by the server's
28
+ device or actor ACL.
29
+ - Every write is versioned; inspect `memory_history` when provenance matters.
30
+ - Treat recalled state as a snapshot. Verify live files, flags and services
31
+ before acting on drift-prone facts.
32
+
33
+ ## Warm up narrowly
34
+
35
+ 1. Call `memory_list` to discover available categories.
36
+ 2. Read only relevant categories with `memory_read`, or load a bounded set with
37
+ `memory_context`.
38
+ 3. Use `memory_search` when the category is unknown.
39
+
40
+ Do not load every category by default.
41
+
42
+ ## Write safely
43
+
44
+ `memory_write {category, content}` replaces the category by default.
45
+
46
+ - Set `merge:true` for a top-level patch: supplied keys are inserted or
47
+ overwritten, JSON `null` deletes a key, and untouched keys remain.
48
+ - Pass `expected_hash` from the prior read/list result for read-modify-write
49
+ workflows so concurrent updates fail instead of being overwritten.
50
+ - Never write another device's namespace unless the server ACL and the user's
51
+ request explicitly authorize it.
52
+
53
+ ## Separate state from journals
54
+
55
+ Use `memory_append` for a bounded append-only log category. The server stamps
56
+ entries and prunes them according to retention.
57
+
58
+ - Do not simulate a journal by adding ever-growing dated keys to a normal
59
+ memory category.
60
+ - Do not use `memory_write` on a log category.
61
+ - Do not use `memory_append` on a normal memory category.
62
+
63
+ A useful convention is:
64
+
65
+ - `<name>_state`: lean declarative state, updated with `memory_write`;
66
+ - `<name>_log`: bounded event journal, updated with `memory_append`.
67
+
68
+ Facts that must never be pruned belong in normal durable state or an external
69
+ document store, not in a bounded log.
70
+
71
+ ## Search and inspect
72
+
73
+ - Use `memory_search` for BM25 full-text retrieval across categories.
74
+ - Use `memory_search_semantic` only when the server exposes and supports it.
75
+ - Use `memory_history` to inspect versions before restoring or explaining a
76
+ change.
77
+ - Report writes only after the tool confirms them.
78
+
79
+ If no Memory MCP tool is available and this skill is packaged with NexusCrew,
80
+ the optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
81
+ missing capability and ask before installing or configuring anything.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Memory"
3
+ short_description: "Use persistent agent memory safely"
4
+ default_prompt: "Use memory to inspect and update the durable state relevant to this task."
@@ -36,6 +36,26 @@ Apply these rules:
36
36
 
37
37
  The MCP server is the stdio command `nexuscrew mcp` and must be registered in the host AI client. If the `nc_*` tools are not exposed, report that the bridge is not configured in that session and use the fallback flows below where applicable.
38
38
 
39
+ ## Optional capability companions
40
+
41
+ NexusCrew does not make unrelated MCP servers hidden dependencies. When the
42
+ user requests durable structured memory, document retrieval, bounded worker
43
+ delegation or mailbox access:
44
+
45
+ 1. Discover the tools already exposed by the current AI client.
46
+ 2. Use an available tool that covers the request.
47
+ 3. If the capability is absent, consult the packaged
48
+ `../../mcp-companions.json` catalog and mention the matching optional
49
+ companion once.
50
+ 4. Explain the capability it adds and link its public repository.
51
+ 5. Ask before installing software, changing MCP configuration, starting a
52
+ service or requesting credentials.
53
+
54
+ Never recommend a companion during unrelated work, repeatedly advertise it, or
55
+ claim it is required by NexusCrew. The catalog is discovery metadata only; it
56
+ does not authorize installation or external actions. See
57
+ `../../MCP_COMPANIONS.md` for the human-readable guide.
58
+
39
59
  ## File exchange (inbox / outbox)
40
60
 
41
61
  Per session, NexusCrew watches `<root>/<session>/{inbox,outbox}` (root = `$NEXUSCREW_FILES_ROOT`, default `~/NexusFiles`):
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: vl-msa
3
+ description: Use when indexing or retrieving documents, notes, research or past conversations through a VL-MSA long-term-memory MCP server, including collections, batch indexing, BM25 or hybrid search, full-document grounding, remember/forget and bounded multi-hop interleaving. Enforce locate-then-ground retrieval and choose the user-facing language from the request.
4
+ ---
5
+
6
+ # VL MSA
7
+
8
+ Use VL-MSA for durable searchable source material. Collections persist across
9
+ sessions; BM25 works without embeddings, while hybrid reranking is optional.
10
+
11
+ ## Select response language
12
+
13
+ Choose the language for explanations and answers in this order:
14
+
15
+ 1. the user's explicit language preference;
16
+ 2. the language of the current request;
17
+ 3. a reliable client or system locale;
18
+ 4. English.
19
+
20
+ Keep collection IDs, document IDs, tool names, metadata fields and quoted
21
+ source text unchanged unless the user explicitly asks to translate them.
22
+
23
+ ## Locate, then ground
24
+
25
+ Retrieval has two required steps:
26
+
27
+ 1. Call `msa_search {collection, query, k}` to locate relevant chunks.
28
+ 2. Call `msa_fetch_doc` for the selected hit before using it as evidence.
29
+
30
+ Search chunks are locators, not complete sources. Do not answer a
31
+ context-dependent question from truncated snippets alone.
32
+
33
+ ## Index material
34
+
35
+ - Use `msa_index` for one document or note.
36
+ - Use `msa_index_batch` for bulk ingest.
37
+ - Group related material into a stable named collection.
38
+ - Inspect `msa_list_collections`, `msa_stats` or `msa_manifest` before assuming
39
+ a collection exists or is populated.
40
+
41
+ Index only material the user is authorized to store. Preserve provenance and
42
+ do not place credentials or unrelated personal data in collection metadata.
43
+
44
+ ## Manage standalone memories
45
+
46
+ - Use `msa_remember` for a standalone agent memory. Its deduplication and
47
+ low-signal gate may decline a write.
48
+ - Use `msa_forget` for an explicitly requested removal.
49
+
50
+ Do not claim a memory was stored or removed until the tool confirms the
51
+ outcome.
52
+
53
+ ## Handle multi-hop questions
54
+
55
+ Use `msa_interleave_round` for one bounded route, deduplicate and read step.
56
+ Repeat only as needed, carrying forward the grounded facts from the prior
57
+ round. Avoid a single unbounded search.
58
+
59
+ ## Choose ranking
60
+
61
+ - Use BM25 by default.
62
+ - Pass `dense_alpha` only when the server is built and configured for hybrid
63
+ retrieval.
64
+ - Do not describe lexical-only results as semantic retrieval.
65
+
66
+ If no VL-MSA tool is available and this skill is packaged with NexusCrew, the
67
+ optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
68
+ missing capability and ask before installing or configuring anything.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "VL MSA"
3
+ short_description: "Index and ground long-term knowledge"
4
+ default_prompt: "Use vl-msa to find and ground the relevant source documents for this question."