@rubytech/create-maxy 1.0.794 → 1.0.795
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/hooks/__tests__/archive-ingest-gate.test.sh +166 -0
- package/payload/platform/plugins/admin/hooks/archive-ingest-gate.sh +147 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +2 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts +2 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/PLUGIN.md +4 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.d.ts +8 -2
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.js +66 -15
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export.test.ts +175 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/parse-export.ts +78 -17
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +2 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/export-parse.md +8 -6
- package/payload/platform/scripts/seed-neo4j.sh +43 -20
- package/payload/platform/templates/specialists/agents/database-operator.md +2 -0
- package/payload/server/public/assets/{Checkbox-DHsoNPeM.js → Checkbox-BruL6MSR.js} +1 -1
- package/payload/server/public/assets/{admin-DEhQ1wNO.js → admin-D8wbpnrW.js} +7 -7
- package/payload/server/public/assets/data-BhrQjgR5.js +1 -0
- package/payload/server/public/assets/graph-Jj7seS-w.js +1 -0
- package/payload/server/public/assets/{jsx-runtime-lOmSwjvd.css → jsx-runtime-foO6ZMix.css} +1 -1
- package/payload/server/public/assets/{page-DU8F3OGU.js → page-DIG7s5Jp.js} +1 -1
- package/payload/server/public/assets/{page-BuoQU1c6.js → page-sZb3wcOM.js} +1 -1
- package/payload/server/public/assets/{public-Bn-gEWOv.js → public-CfjzDdUe.js} +1 -1
- package/payload/server/public/assets/{share-2-0IDKUUq9.js → share-2-BndjMKeG.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-B1S_t3Hq.js → useVoiceRecorder-D_8P7xJU.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +77 -119
- package/payload/server/public/assets/data-bIkywng-.js +0 -1
- package/payload/server/public/assets/graph-DwzwJvlu.js +0 -1
- /package/payload/server/public/assets/{jsx-runtime-Br2bU3EJ.js → jsx-runtime-DJER3a7U.js} +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression test for archive-ingest-gate.sh (Task 846).
|
|
3
|
+
#
|
|
4
|
+
# Six cases cover the contract:
|
|
5
|
+
# 1. Edit on /platform/plugins/<x>/lib/* is BLOCKED (exit 2).
|
|
6
|
+
# 2. Edit on a benign path is ALLOWED (exit 0).
|
|
7
|
+
# 3. Bash with `npx vitest` is BLOCKED.
|
|
8
|
+
# 4. PostToolUse on whatsapp-export-parse with isError:true sets the flag.
|
|
9
|
+
# 5. Subsequent PreToolUse on ANY tool is BLOCKED (post-parse-error gate).
|
|
10
|
+
# 6. UserPromptSubmit clears the flag, restoring normal allow behavior.
|
|
11
|
+
#
|
|
12
|
+
# Tests use ARCHIVE_INGEST_GATE_STATE_DIR to point at a tmp dir so they run
|
|
13
|
+
# without a real account layout.
|
|
14
|
+
|
|
15
|
+
set -u
|
|
16
|
+
|
|
17
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/archive-ingest-gate.sh"
|
|
18
|
+
if [[ ! -x "$HOOK" ]]; then
|
|
19
|
+
echo "FAIL: $HOOK not executable" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Per-run isolated state dir
|
|
24
|
+
STATE_DIR=$(mktemp -d)
|
|
25
|
+
export ARCHIVE_INGEST_GATE_STATE_DIR="$STATE_DIR"
|
|
26
|
+
FLAG_FILE="$STATE_DIR/archive-ingest-parse-error.flag"
|
|
27
|
+
|
|
28
|
+
cleanup() { rm -rf "$STATE_DIR"; }
|
|
29
|
+
trap cleanup EXIT
|
|
30
|
+
|
|
31
|
+
PASS=0
|
|
32
|
+
FAIL=0
|
|
33
|
+
|
|
34
|
+
run_case() {
|
|
35
|
+
local name="$1" stdin="$2" expected_exit="$3"
|
|
36
|
+
local actual_exit
|
|
37
|
+
printf '%s' "$stdin" | bash "$HOOK" >/dev/null 2>/dev/null
|
|
38
|
+
actual_exit=$?
|
|
39
|
+
if [[ "$actual_exit" -eq "$expected_exit" ]]; then
|
|
40
|
+
echo "PASS: $name (exit=$actual_exit)"
|
|
41
|
+
PASS=$((PASS + 1))
|
|
42
|
+
else
|
|
43
|
+
echo "FAIL: $name (expected exit=$expected_exit, got=$actual_exit)" >&2
|
|
44
|
+
FAIL=$((FAIL + 1))
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Case 1 — Edit on plugin lib path: BLOCKED
|
|
49
|
+
run_case "Edit on platform/plugins/whatsapp-import/lib/src/parse-export.ts → BLOCKED" \
|
|
50
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/platform/plugins/whatsapp-import/lib/src/parse-export.ts","old_string":"a","new_string":"b"}}' \
|
|
51
|
+
2
|
|
52
|
+
|
|
53
|
+
# Case 2 — Edit on a benign path: ALLOWED
|
|
54
|
+
run_case "Edit on README.md → ALLOWED" \
|
|
55
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/README.md","old_string":"a","new_string":"b"}}' \
|
|
56
|
+
0
|
|
57
|
+
|
|
58
|
+
# Case 3 — Bash with `npx vitest`: BLOCKED
|
|
59
|
+
run_case "Bash 'npx vitest run parse-export.test.ts' → BLOCKED" \
|
|
60
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npx vitest run parse-export.test.ts"}}' \
|
|
61
|
+
2
|
|
62
|
+
|
|
63
|
+
# Case 3b — Bash with benign command: ALLOWED
|
|
64
|
+
run_case "Bash 'ls -la' → ALLOWED" \
|
|
65
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls -la"}}' \
|
|
66
|
+
0
|
|
67
|
+
|
|
68
|
+
# Case 3c — Bash with `bun test`: BLOCKED
|
|
69
|
+
run_case "Bash 'bun test' → BLOCKED" \
|
|
70
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"bun test"}}' \
|
|
71
|
+
2
|
|
72
|
+
|
|
73
|
+
# Case 3d — Bash with `npm test`: BLOCKED
|
|
74
|
+
run_case "Bash 'npm test' → BLOCKED" \
|
|
75
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npm test"}}' \
|
|
76
|
+
2
|
|
77
|
+
|
|
78
|
+
# Make sure flag is absent before parse-error simulation
|
|
79
|
+
rm -f "$FLAG_FILE"
|
|
80
|
+
|
|
81
|
+
# Case 4 — PostToolUse on whatsapp-export-parse with isError:true sets flag
|
|
82
|
+
run_case "PostToolUse parse-error sets flag (exit 0, flag side-effect)" \
|
|
83
|
+
'{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__whatsapp-export-parse","tool_input":{"filePath":"_chat.txt"},"tool_response":{"isError":true,"content":[{"type":"text","text":"parse-error file=_chat.txt line=1 reason=not-a-_chat.txt"}]}}' \
|
|
84
|
+
0
|
|
85
|
+
|
|
86
|
+
if [[ -f "$FLAG_FILE" ]]; then
|
|
87
|
+
echo "PASS: parse-error flag created at $FLAG_FILE"
|
|
88
|
+
PASS=$((PASS + 1))
|
|
89
|
+
else
|
|
90
|
+
echo "FAIL: parse-error flag NOT created at $FLAG_FILE" >&2
|
|
91
|
+
FAIL=$((FAIL + 1))
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Case 5 — Subsequent PreToolUse on ANY tool BLOCKED while flag is fresh
|
|
95
|
+
run_case "PreToolUse Read after parse-error → BLOCKED" \
|
|
96
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/foo"}}' \
|
|
97
|
+
2
|
|
98
|
+
|
|
99
|
+
run_case "PreToolUse Bash after parse-error → BLOCKED" \
|
|
100
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"echo hi"}}' \
|
|
101
|
+
2
|
|
102
|
+
|
|
103
|
+
# Case 6 — UserPromptSubmit clears flag
|
|
104
|
+
run_case "UserPromptSubmit clears flag (exit 0)" \
|
|
105
|
+
'{"hook_event_name":"UserPromptSubmit","prompt":"retry"}' \
|
|
106
|
+
0
|
|
107
|
+
|
|
108
|
+
if [[ ! -f "$FLAG_FILE" ]]; then
|
|
109
|
+
echo "PASS: UserPromptSubmit cleared flag"
|
|
110
|
+
PASS=$((PASS + 1))
|
|
111
|
+
else
|
|
112
|
+
echo "FAIL: UserPromptSubmit did NOT clear flag" >&2
|
|
113
|
+
FAIL=$((FAIL + 1))
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Case 7 — After clearance, normal allow resumes
|
|
117
|
+
run_case "PreToolUse Read after clearance → ALLOWED" \
|
|
118
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/foo"}}' \
|
|
119
|
+
0
|
|
120
|
+
|
|
121
|
+
# Case 8 — PostToolUse with isError:false does NOT set flag
|
|
122
|
+
rm -f "$FLAG_FILE"
|
|
123
|
+
run_case "PostToolUse parse-success (isError:false) does NOT set flag" \
|
|
124
|
+
'{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__whatsapp-export-parse","tool_input":{"filePath":"_chat.txt"},"tool_response":{"isError":false,"content":[{"type":"text","text":"{\"parsedLines\":[]}"}]}}' \
|
|
125
|
+
0
|
|
126
|
+
|
|
127
|
+
if [[ ! -f "$FLAG_FILE" ]]; then
|
|
128
|
+
echo "PASS: parse-success leaves flag absent"
|
|
129
|
+
PASS=$((PASS + 1))
|
|
130
|
+
else
|
|
131
|
+
echo "FAIL: parse-success incorrectly created flag" >&2
|
|
132
|
+
FAIL=$((FAIL + 1))
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Case 9 — Stale flag (>600s) auto-clears + allows
|
|
136
|
+
PAST=$(( $(date -u +%s) - 700 ))
|
|
137
|
+
echo "$PAST" > "$FLAG_FILE"
|
|
138
|
+
run_case "Stale flag auto-clears, PreToolUse Read → ALLOWED" \
|
|
139
|
+
'{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/foo"}}' \
|
|
140
|
+
0
|
|
141
|
+
|
|
142
|
+
# Case 10 — No stdin (terminal) fails closed
|
|
143
|
+
echo "Probing fail-closed behaviour (no stdin)..."
|
|
144
|
+
bash "$HOOK" </dev/null >/dev/null 2>/dev/null
|
|
145
|
+
ACTUAL=$?
|
|
146
|
+
# /dev/null IS a stdin — the `[ -t 0 ]` check tests for terminal, not file.
|
|
147
|
+
# A file/pipe stdin reads as empty, which produces empty hook_event_name and
|
|
148
|
+
# falls through to default `exit 0` (allow). The terminal-only fail-closed
|
|
149
|
+
# branch can't be tested non-interactively; verify the script reads `[ -t 0 ]`.
|
|
150
|
+
if grep -q '\[ -t 0 \]' "$HOOK"; then
|
|
151
|
+
echo "PASS: fail-closed terminal check is present"
|
|
152
|
+
PASS=$((PASS + 1))
|
|
153
|
+
else
|
|
154
|
+
echo "FAIL: fail-closed terminal check missing" >&2
|
|
155
|
+
FAIL=$((FAIL + 1))
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo
|
|
159
|
+
echo "──────── archive-ingest-gate test summary ────────"
|
|
160
|
+
echo "PASS: $PASS"
|
|
161
|
+
echo "FAIL: $FAIL"
|
|
162
|
+
|
|
163
|
+
if [[ "$FAIL" -gt 0 ]]; then
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
exit 0
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Archive-ingest gate (Task 846).
|
|
3
|
+
#
|
|
4
|
+
# Three enforcements, one script — phase decided by `hook_event_name` on stdin:
|
|
5
|
+
#
|
|
6
|
+
# 1. PreToolUse Edit/Write/NotebookEdit: deny writes under
|
|
7
|
+
# `*platform/plugins/*/lib/*` (parser/CSV-shape source for any *-import or
|
|
8
|
+
# *-export plugin). The database-operator subagent has Read+Bash but not
|
|
9
|
+
# Edit/Write per its `tools:` frontmatter — yet it can still mutate files
|
|
10
|
+
# via Bash heredoc. The path block applies to every tool that takes a
|
|
11
|
+
# `file_path` and has been observed as the improvisation surface.
|
|
12
|
+
#
|
|
13
|
+
# 2. PreToolUse Bash: deny commands invoking JavaScript test runners
|
|
14
|
+
# (vitest|bun test|npm test|npx jest|node .*vitest). The reproducer
|
|
15
|
+
# incident (conv 47c6a590) saw the operator run all four variants in
|
|
16
|
+
# sequence after parse-export returned isError.
|
|
17
|
+
#
|
|
18
|
+
# 3. Parse-error gate: PostToolUse on any `mcp__*__*-export-parse` /
|
|
19
|
+
# `mcp__*__*-import-parse` tool whose `tool_response.isError == true`
|
|
20
|
+
# writes a flag file. Subsequent PreToolUse on ANY tool blocks until
|
|
21
|
+
# UserPromptSubmit clears the flag (semantics: "subagent's next action
|
|
22
|
+
# must be a user-facing message; further tool calls blocked"). A 600s
|
|
23
|
+
# TTL is the cross-session safety net.
|
|
24
|
+
#
|
|
25
|
+
# Exit codes follow Claude Code hook protocol: 0 = allow, 2 = block (stderr
|
|
26
|
+
# message shown to the agent). Fail-closed on stdin read failure to match
|
|
27
|
+
# pre-tool-use.sh.
|
|
28
|
+
|
|
29
|
+
set -uo pipefail
|
|
30
|
+
|
|
31
|
+
# Read stdin — fail closed if unavailable
|
|
32
|
+
if [ -t 0 ]; then
|
|
33
|
+
echo "Blocked: archive-ingest-gate received no stdin (cannot inspect tool call). Failing closed." >&2
|
|
34
|
+
exit 2
|
|
35
|
+
fi
|
|
36
|
+
INPUT=$(cat)
|
|
37
|
+
|
|
38
|
+
# ----- Resolve account dir for state file ----------------------------------
|
|
39
|
+
# Mirrors pre-tool-use.sh lines 100-107: walk from this hook's location to
|
|
40
|
+
# platform/, then `../data/accounts/<single-account>/`. Phase 0 has exactly
|
|
41
|
+
# one account directory.
|
|
42
|
+
HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
|
|
43
|
+
PLATFORM_ROOT_RESOLVED="${HOOK_DIR}/../../.."
|
|
44
|
+
ACCOUNTS_DIR="${PLATFORM_ROOT_RESOLVED}/../data/accounts"
|
|
45
|
+
ACCOUNT_DIR=""
|
|
46
|
+
if [ -d "$ACCOUNTS_DIR" ]; then
|
|
47
|
+
ACCOUNT_DIR=$(find "$ACCOUNTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | head -1)
|
|
48
|
+
fi
|
|
49
|
+
# Test harness override — tests pass `ARCHIVE_INGEST_GATE_STATE_DIR` to point
|
|
50
|
+
# at a tmp dir without needing a real account layout.
|
|
51
|
+
STATE_DIR="${ARCHIVE_INGEST_GATE_STATE_DIR:-${ACCOUNT_DIR}/state}"
|
|
52
|
+
FLAG_FILE="${STATE_DIR}/archive-ingest-parse-error.flag"
|
|
53
|
+
TTL_SECONDS=600
|
|
54
|
+
|
|
55
|
+
# ----- Parse fields from stdin ---------------------------------------------
|
|
56
|
+
# Hook protocol: every event includes `hook_event_name`. PreToolUse +
|
|
57
|
+
# PostToolUse include `tool_name`. PostToolUse adds `tool_response`.
|
|
58
|
+
# UserPromptSubmit has neither. Use grep/sed against the JSON envelope —
|
|
59
|
+
# no jq dependency to match the rest of the hook fleet.
|
|
60
|
+
HOOK_EVENT=$(printf '%s' "$INPUT" | grep -o '"hook_event_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
61
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
62
|
+
|
|
63
|
+
# ============================================================================
|
|
64
|
+
# UserPromptSubmit — clear the parse-error flag.
|
|
65
|
+
# ============================================================================
|
|
66
|
+
if [ "$HOOK_EVENT" = "UserPromptSubmit" ]; then
|
|
67
|
+
if [ -f "$FLAG_FILE" ]; then
|
|
68
|
+
rm -f "$FLAG_FILE" 2>/dev/null
|
|
69
|
+
echo "[archive-ingest-gate] cleared reason=user-prompt-submit" >&2
|
|
70
|
+
fi
|
|
71
|
+
exit 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# ============================================================================
|
|
75
|
+
# PostToolUse — record parse-error from any *-export-parse / *-import-parse.
|
|
76
|
+
# ============================================================================
|
|
77
|
+
if [ "$HOOK_EVENT" = "PostToolUse" ]; then
|
|
78
|
+
case "$TOOL_NAME" in
|
|
79
|
+
mcp__*__*-export-parse|mcp__*__*-import-parse)
|
|
80
|
+
# Inspect tool_response for isError true. Body is a JSON object; match
|
|
81
|
+
# `"isError":true` with optional whitespace.
|
|
82
|
+
if printf '%s' "$INPUT" | grep -Eq '"isError"[[:space:]]*:[[:space:]]*true'; then
|
|
83
|
+
mkdir -p "$STATE_DIR" 2>/dev/null
|
|
84
|
+
date -u +%s > "$FLAG_FILE" 2>/dev/null
|
|
85
|
+
echo "[archive-ingest-gate] surfaced reason=parse-error tool=${TOOL_NAME}" >&2
|
|
86
|
+
fi
|
|
87
|
+
;;
|
|
88
|
+
esac
|
|
89
|
+
# PostToolUse must always allow the tool result through.
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# ============================================================================
|
|
94
|
+
# PreToolUse — three independent blocks.
|
|
95
|
+
# ============================================================================
|
|
96
|
+
if [ "$HOOK_EVENT" != "PreToolUse" ]; then
|
|
97
|
+
# Unknown event, or hook fired in a context without an event name. Allow.
|
|
98
|
+
exit 0
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# --- Block 1: post-parse-error gate (applies to ALL tools) -----------------
|
|
102
|
+
if [ -f "$FLAG_FILE" ]; then
|
|
103
|
+
FLAG_TS=$(cat "$FLAG_FILE" 2>/dev/null | head -1)
|
|
104
|
+
NOW=$(date -u +%s)
|
|
105
|
+
if [ -n "$FLAG_TS" ] && [ "$FLAG_TS" -gt 0 ] 2>/dev/null; then
|
|
106
|
+
AGE=$(( NOW - FLAG_TS ))
|
|
107
|
+
if [ "$AGE" -lt "$TTL_SECONDS" ]; then
|
|
108
|
+
echo "[archive-ingest-gate] block tool=${TOOL_NAME} reason=post-parse-error age_s=${AGE}" >&2
|
|
109
|
+
echo "Blocked: an archive-parser MCP tool returned isError=true earlier in this turn. The subagent's next action must be a user-facing message naming the parse-error and yielding back to the operator. Further tool calls are blocked until the operator submits a new prompt." >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
# Stale flag — clean up so we don't keep blocking on a corpse.
|
|
113
|
+
rm -f "$FLAG_FILE" 2>/dev/null
|
|
114
|
+
else
|
|
115
|
+
# Unparseable flag — remove rather than block on garbage.
|
|
116
|
+
rm -f "$FLAG_FILE" 2>/dev/null
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# --- Block 2: plugin-source path block (Edit/Write/NotebookEdit) -----------
|
|
121
|
+
case "$TOOL_NAME" in
|
|
122
|
+
Edit|Write|NotebookEdit)
|
|
123
|
+
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
124
|
+
case "$FILE_PATH" in
|
|
125
|
+
*/platform/plugins/*/lib/*|platform/plugins/*/lib/*)
|
|
126
|
+
echo "[archive-ingest-gate] block tool=${TOOL_NAME} reason=plugin-source-edit path=${FILE_PATH}" >&2
|
|
127
|
+
echo "Blocked: ${TOOL_NAME} on ${FILE_PATH} is a platform plugin lib/ path. The database-operator subagent does not own plugin source; if a parser is broken, surface the parse-error to the operator and let them dispatch a code-edit task instead." >&2
|
|
128
|
+
exit 2
|
|
129
|
+
;;
|
|
130
|
+
esac
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
|
|
134
|
+
# --- Block 3: shell test-runner block (Bash) -------------------------------
|
|
135
|
+
if [ "$TOOL_NAME" = "Bash" ]; then
|
|
136
|
+
COMMAND=$(printf '%s' "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
137
|
+
# Match any of: `vitest`, `bun test`, `npm test`, `npx jest`, `node ... vitest`.
|
|
138
|
+
# Word-boundary checks via grep -E with explicit token boundaries — POSIX
|
|
139
|
+
# `[[:space:]]` covers leading-token edge cases, end-of-string covers tail.
|
|
140
|
+
if printf '%s' "$COMMAND" | grep -Eq '(^|[[:space:]/])vitest($|[[:space:]])|(^|[[:space:]])bun[[:space:]]+test($|[[:space:]])|(^|[[:space:]])npm[[:space:]]+test($|[[:space:]])|(^|[[:space:]])npx[[:space:]]+jest($|[[:space:]])|node[[:space:]].*vitest'; then
|
|
141
|
+
echo "[archive-ingest-gate] block tool=Bash reason=test-runner command=${COMMAND}" >&2
|
|
142
|
+
echo "Blocked: Bash command invokes a JavaScript test runner (vitest/bun test/npm test/npx jest). The database-operator subagent does not run plugin tests; surface the parse-error to the operator." >&2
|
|
143
|
+
exit 2
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
exit 0
|
|
@@ -68,6 +68,8 @@ When the owner is an external Person (non-operator archive), the anchor is the c
|
|
|
68
68
|
|
|
69
69
|
**Doctrine:** raw Cypher and `cypher-shell` invocations are forbidden in this skill and its references. Writes route through `mcp__memory__memory-archive-write` (bulk archives) or `mcp__memory__memory-write` / `mcp__memory__memory-update` (single-node enrichments like `profile.md`). If a CSV needs a write shape no current MCP tool supports, file a task to extend `memory-archive-write` with a new `archiveType` handler — never improvise via Bash. See [database-operator's LOUD-FAIL prerogative](../../../../templates/specialists/agents/database-operator.md#prerogatives).
|
|
70
70
|
|
|
71
|
+
**LOUD-FAIL on parse errors (structurally enforced, Task 846).** When LinkedIn parser tools land that follow the `mcp__*__*-import-parse` naming convention, the harness-level `platform/plugins/admin/hooks/archive-ingest-gate.sh` will record an `isError: true` response and block every subsequent tool call this turn until the operator submits the next prompt. The hook also denies edits to `platform/plugins/*/lib/*` and JavaScript test runners (`vitest`, `bun test`, `npm test`, `npx jest`) unconditionally. The skill's "no Bash improvisation" doctrine above is the contract; the hook is the enforcement. See [.docs/hooks.md](../../../../../.docs/hooks.md) for the full gate surface.
|
|
72
|
+
|
|
71
73
|
## Selective-ingest threshold (bulk archives)
|
|
72
74
|
|
|
73
75
|
A LinkedIn export typically contains 3,000–10,000 connections. Writing all of them in one shot defeats compression-on-write — most rows will never be queried, and the noise compounds with every subsequent ingest. The skill compresses by interrogating the operator before bulk writes.
|
|
@@ -860,9 +860,9 @@ if (!readOnly) {
|
|
|
860
860
|
.min(1)
|
|
861
861
|
.describe("IANA timezone the operator confirmed (e.g. 'Europe/London', 'America/New_York', 'UTC'). Each parsed timestamp is emitted as ISO 8601 with the offset that this zone holds for the wall-clock instant — DST is handled correctly."),
|
|
862
862
|
dateFormat: z
|
|
863
|
-
.enum(["DD/MM/YY", "MM/DD/YY"])
|
|
863
|
+
.enum(["DD/MM/YY", "MM/DD/YY", "DD/MM/YYYY", "MM/DD/YYYY"])
|
|
864
864
|
.optional()
|
|
865
|
-
.describe("Date ordering
|
|
865
|
+
.describe("Date ordering and year shape. Omit for auto-detect (Task 845): the parser probes the first matched line as DD/MM and locks that ordering if range-valid; otherwise locks MM/DD. Year shape is independent — both 2-digit (legacy) and 4-digit (modern) years are accepted within the same file. Pass an explicit value only when the operator confirms a US-locale export or when auto-detect would mis-lock on a manually concatenated multi-locale archive."),
|
|
866
866
|
}, async ({ filePath, timezone, dateFormat }) => {
|
|
867
867
|
try {
|
|
868
868
|
const result = await whatsappExportParse({
|