@rubytech/create-maxy-code 0.1.329 → 0.1.331
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/dist/index.js +19 -2
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts +33 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/account-enumeration/dist/index.js +76 -0
- package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -1
- package/payload/platform/lib/account-enumeration/src/index.ts +78 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +13 -1
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js +1 -2
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts +1 -1
- package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js +18 -13
- package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +102 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js +57 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +34 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +78 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -0
- package/payload/platform/plugins/admin/skills/plainly/SKILL.md +7 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js +71 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +10 -3
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/package.json +4 -2
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +3 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js +13 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js.map +1 -1
- package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +1 -1
- package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +40 -0
- package/payload/platform/scripts/__tests__/sweep-durability.test.sh +103 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +319 -0
- package/payload/platform/scripts/lib/resolve-account-dir.sh +79 -132
- package/payload/platform/scripts/setup-account.sh +28 -361
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +18 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.js +63 -7
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +44 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +50 -2
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-child-log.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/rc-child-log.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-child-log.js +52 -0
- package/payload/platform/services/claude-session-manager/dist/rc-child-log.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts +70 -0
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.js +213 -0
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.js.map +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +25 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +17 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +14 -0
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +66 -2
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +28 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +24 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/account.json +2 -0
- package/payload/premium-plugins/management-consulting/skills/pitch-deck/SKILL.md +44 -37
- package/payload/server/public/assets/AdminLoginScreens-DexSvtjD.js +1 -0
- package/payload/server/public/assets/AdminShell-Bf9tndNg.js +1 -0
- package/payload/server/public/assets/{Checkbox-DWGpkJD5.js → Checkbox-D58GsKoQ.js} +1 -1
- package/payload/server/public/assets/{OperatorConversations-BWu6GMQM.css → OperatorConversations-CDdp2nVn.css} +1 -1
- package/payload/server/public/assets/OperatorConversations-RmqANYz8.js +9 -0
- package/payload/server/public/assets/{admin-CnIlm4Yi.js → admin-CH5Vkkt4.js} +1 -1
- package/payload/server/public/assets/{browser-ePr5LxPd.js → browser-BwDtzSlr.js} +1 -1
- package/payload/server/public/assets/chat-3GY0LmYH.js +1 -0
- package/payload/server/public/assets/data-CttrzhfL.js +1 -0
- package/payload/server/public/assets/{graph-B5uxt5YB.js → graph-CZSdF2jv.js} +3 -3
- package/payload/server/public/assets/{graph-labels-fx-bv3Y-.js → graph-labels-Bi0fu8Ns.js} +1 -1
- package/payload/server/public/assets/operator-BEivxmRy.js +1 -0
- package/payload/server/public/assets/page-DUvT07SB.js +30 -0
- package/payload/server/public/assets/{public-DXAdRKJe.js → public-B3eIlVbd.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +7 -7
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +314 -222
- package/payload/server/public/assets/AdminLoginScreens-DstNf4M-.js +0 -1
- package/payload/server/public/assets/AdminShell-B5EcXCYR.js +0 -1
- package/payload/server/public/assets/OperatorConversations-CSQnX7Dx.js +0 -9
- package/payload/server/public/assets/chat-XjXSornk.js +0 -1
- package/payload/server/public/assets/data-DuObS80U.js +0 -1
- package/payload/server/public/assets/operator-CIGXegWd.js +0 -1
- package/payload/server/public/assets/page-BVOAwj-e.js +0 -30
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for resolve-account-dir.sh under the multi-account managed-service model
|
|
3
|
+
# (Task 983). Registry-membership keep-all: any dir with a valid account.json is
|
|
4
|
+
# a registered account and is NEVER swept; the destructive mv is removed (sweep
|
|
5
|
+
# is log-only this sprint). Boot resolves ACCOUNT_DIR to the single role:"house".
|
|
6
|
+
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
RESOLVER="$SCRIPT_DIR/../lib/resolve-account-dir.sh"
|
|
11
|
+
PASS=0
|
|
12
|
+
FAIL=0
|
|
13
|
+
FAILED_TESTS=()
|
|
14
|
+
|
|
15
|
+
assert_eq() {
|
|
16
|
+
if [ "$1" = "$2" ]; then PASS=$((PASS+1));
|
|
17
|
+
else FAIL=$((FAIL+1)); FAILED_TESTS+=("$3: expected [$2] got [$1]"); fi
|
|
18
|
+
}
|
|
19
|
+
assert_dir_exists() {
|
|
20
|
+
if [ -d "$1" ]; then PASS=$((PASS+1));
|
|
21
|
+
else FAIL=$((FAIL+1)); FAILED_TESTS+=("$2: dir missing [$1]"); fi
|
|
22
|
+
}
|
|
23
|
+
assert_dir_absent() {
|
|
24
|
+
if [ ! -d "$1" ]; then PASS=$((PASS+1));
|
|
25
|
+
else FAIL=$((FAIL+1)); FAILED_TESTS+=("$2: dir unexpectedly present [$1]"); fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
HOUSE="11111111-1111-1111-1111-111111111111"
|
|
29
|
+
CLIENT_A="22222222-2222-2222-2222-222222222222"
|
|
30
|
+
CLIENT_B="33333333-3333-3333-3333-333333333333"
|
|
31
|
+
ORPHAN="44444444-4444-4444-4444-444444444444"
|
|
32
|
+
|
|
33
|
+
seed_account() {
|
|
34
|
+
local root="$1" id="$2" role="$3"
|
|
35
|
+
mkdir -p "$root/$id"
|
|
36
|
+
printf '{"accountId":"%s","role":"%s"}\n' "$id" "$role" > "$root/$id/account.json"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# --- Test 1: house + 2 clients + 1 orphan; all registered survive, house resolved, nothing trashed ---
|
|
40
|
+
run_durability() {
|
|
41
|
+
local tmp; tmp=$(mktemp -d)
|
|
42
|
+
local accounts="$tmp/data/accounts"
|
|
43
|
+
mkdir -p "$accounts"
|
|
44
|
+
seed_account "$accounts" "$CLIENT_A" "client"
|
|
45
|
+
seed_account "$accounts" "$CLIENT_B" "client"
|
|
46
|
+
seed_account "$accounts" "$HOUSE" "house"
|
|
47
|
+
mkdir -p "$accounts/$ORPHAN" # orphan: no account.json
|
|
48
|
+
|
|
49
|
+
local resolved; resolved=$(
|
|
50
|
+
. "$RESOLVER"
|
|
51
|
+
ACCOUNTS_DIR="$accounts" USERS_FILE="/nonexistent/users.json" resolve_and_sweep_account_dir >/dev/null 2>&1
|
|
52
|
+
echo "$ACCOUNT_ID"
|
|
53
|
+
)
|
|
54
|
+
assert_eq "$resolved" "$HOUSE" "house-resolved"
|
|
55
|
+
assert_dir_exists "$accounts/$HOUSE" "house-survives"
|
|
56
|
+
assert_dir_exists "$accounts/$CLIENT_A" "clientA-survives"
|
|
57
|
+
assert_dir_exists "$accounts/$CLIENT_B" "clientB-survives"
|
|
58
|
+
assert_dir_exists "$accounts/$ORPHAN" "orphan-not-trashed"
|
|
59
|
+
assert_dir_absent "$accounts/.trash" "no-trash-created"
|
|
60
|
+
rm -rf "$tmp"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# --- Test 2: sole unlabelled account (legacy) resolves as the account ---
|
|
64
|
+
run_sole_legacy() {
|
|
65
|
+
local tmp; tmp=$(mktemp -d)
|
|
66
|
+
local accounts="$tmp/data/accounts"
|
|
67
|
+
mkdir -p "$accounts/$HOUSE"
|
|
68
|
+
printf '{"accountId":"%s"}\n' "$HOUSE" > "$accounts/$HOUSE/account.json" # no role
|
|
69
|
+
local resolved rc
|
|
70
|
+
resolved=$(
|
|
71
|
+
. "$RESOLVER"
|
|
72
|
+
ACCOUNTS_DIR="$accounts" USERS_FILE="/nonexistent/users.json" resolve_and_sweep_account_dir >/dev/null 2>&1
|
|
73
|
+
echo "$ACCOUNT_ID"
|
|
74
|
+
)
|
|
75
|
+
assert_eq "$resolved" "$HOUSE" "sole-legacy-resolved"
|
|
76
|
+
rm -rf "$tmp"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# --- Test 3: zero-house with multiple candidates aborts (rc != 0) ---
|
|
80
|
+
run_drift_aborts() {
|
|
81
|
+
local tmp; tmp=$(mktemp -d)
|
|
82
|
+
local accounts="$tmp/data/accounts"
|
|
83
|
+
mkdir -p "$accounts"
|
|
84
|
+
seed_account "$accounts" "$CLIENT_A" "client"
|
|
85
|
+
seed_account "$accounts" "$CLIENT_B" "client"
|
|
86
|
+
local rc=0
|
|
87
|
+
(
|
|
88
|
+
. "$RESOLVER"
|
|
89
|
+
ACCOUNTS_DIR="$accounts" USERS_FILE="/nonexistent/users.json" resolve_and_sweep_account_dir >/dev/null 2>&1
|
|
90
|
+
) || rc=$?
|
|
91
|
+
if [ "$rc" -ne 0 ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED_TESTS+=("drift-aborts: expected non-zero exit"); fi
|
|
92
|
+
rm -rf "$tmp"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
run_durability
|
|
96
|
+
run_sole_legacy
|
|
97
|
+
run_drift_aborts
|
|
98
|
+
|
|
99
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
100
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
101
|
+
printf '%s\n' "${FAILED_TESTS[@]}"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# provision-account-dir.sh — per-account scaffolding, callable per account.
|
|
2
|
+
# Sourced by setup-account.sh (for the resolved house account) and by the
|
|
3
|
+
# admin-core `account_create` lifecycle tool (for a new client account). Defines
|
|
4
|
+
# `provision_account_dir <account_dir> <account_id> <role>`.
|
|
5
|
+
#
|
|
6
|
+
# The function is pure scaffolding: it creates the account dir layout, writes the
|
|
7
|
+
# Claude Code project settings (hooks only), installs agent identities and the
|
|
8
|
+
# specialist plugin, seeds account.json (stamping accountId + role), seeds
|
|
9
|
+
# enabledPlugins from brand defaults, and stamps the install owner into admins[].
|
|
10
|
+
# It does NO account resolution and NO Neo4j work.
|
|
11
|
+
#
|
|
12
|
+
# Required globals (set by the caller before invoking):
|
|
13
|
+
# TEMPLATES_DIR — <project>/templates
|
|
14
|
+
# PROJECT_DIR — the platform/ dir
|
|
15
|
+
# SCRIPT_DIR — the platform/scripts dir (for lib bootstraps)
|
|
16
|
+
# USERS_FILE — $HOME/<brand.configDir>/users.json (owner stamp; optional)
|
|
17
|
+
|
|
18
|
+
provision_account_dir() {
|
|
19
|
+
local ACCOUNT_DIR="$1"
|
|
20
|
+
local ACCOUNT_ID="$2"
|
|
21
|
+
local ROLE="$3"
|
|
22
|
+
|
|
23
|
+
mkdir -p "$ACCOUNT_DIR/agents/admin" "$ACCOUNT_DIR/.claude" "$ACCOUNT_DIR/specialists/.claude-plugin" "$ACCOUNT_DIR/specialists/agents"
|
|
24
|
+
|
|
25
|
+
# Claude Code discovers project-level .claude/ relative to the nearest .git
|
|
26
|
+
# root. Without a .git in the account dir it traverses up to ~/maxy/.git and
|
|
27
|
+
# misses accountDir/.claude/. Initialise a repo so the account dir IS the root.
|
|
28
|
+
[ -d "$ACCOUNT_DIR/.git" ] || git init -q "$ACCOUNT_DIR"
|
|
29
|
+
|
|
30
|
+
# --- project-level .claude/settings.json (hooks; no allowlist) -------------
|
|
31
|
+
local ACCOUNT_SETTINGS="$ACCOUNT_DIR/.claude/settings.json"
|
|
32
|
+
local HOOKS_PATH="\$PLATFORM_ROOT/plugins/admin/hooks"
|
|
33
|
+
cat > "$ACCOUNT_SETTINGS" << SETTINGS_EOF
|
|
34
|
+
{
|
|
35
|
+
"permissions": {
|
|
36
|
+
"defaultMode": "bypassPermissions",
|
|
37
|
+
"allow": [],
|
|
38
|
+
"deny": []
|
|
39
|
+
},
|
|
40
|
+
"hooks": {
|
|
41
|
+
"PreToolUse": [
|
|
42
|
+
{
|
|
43
|
+
"matcher": "Write",
|
|
44
|
+
"hooks": [
|
|
45
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"matcher": "Edit",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"matcher": "NotebookEdit",
|
|
56
|
+
"hooks": [
|
|
57
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"matcher": "Bash",
|
|
62
|
+
"hooks": [
|
|
63
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"hooks": [
|
|
68
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"matcher": "WebFetch",
|
|
73
|
+
"hooks": [
|
|
74
|
+
{ "type": "command", "command": "node $HOOKS_PATH/webfetch-preflight.mjs" }
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"matcher": "AskUserQuestion",
|
|
79
|
+
"hooks": [
|
|
80
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/askuserquestion-investigate-gate.sh" }
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"PostToolUse": [
|
|
85
|
+
{
|
|
86
|
+
"matcher": "Agent",
|
|
87
|
+
"hooks": [
|
|
88
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/post-tool-use-agent.sh" }
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"matcher": "mcp__.*__.*-(export|import)-parse$",
|
|
93
|
+
"hooks": [
|
|
94
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"matcher": "mcp__.*",
|
|
99
|
+
"hooks": [
|
|
100
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/mcp-tool-missing.sh" }
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"matcher": "Write",
|
|
105
|
+
"hooks": [
|
|
106
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/admin-authoring-observer.sh" }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"matcher": "Edit",
|
|
111
|
+
"hooks": [
|
|
112
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/admin-authoring-observer.sh" }
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"UserPromptSubmit": [
|
|
117
|
+
{
|
|
118
|
+
"hooks": [
|
|
119
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/archive-ingest-surface-gate.sh" },
|
|
120
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/prompt-optimiser-directive.sh" }
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"Stop": [
|
|
125
|
+
{
|
|
126
|
+
"hooks": [
|
|
127
|
+
{ "type": "command", "command": "bash $HOOKS_PATH/prompt-optimiser-compliance.sh" }
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
SETTINGS_EOF
|
|
134
|
+
|
|
135
|
+
# --- agent identities + specialist plugin templates ------------------------
|
|
136
|
+
cp "$TEMPLATES_DIR/agents/admin/IDENTITY.md" "$ACCOUNT_DIR/agents/admin/IDENTITY.md"
|
|
137
|
+
|
|
138
|
+
# On re-provision, re-sync the Rubytech-controlled IDENTITY.md for every
|
|
139
|
+
# already-existing non-admin agent dir (security-load-bearing). Never create
|
|
140
|
+
# dirs; never write SOUL/KNOWLEDGE/config.
|
|
141
|
+
local _AGENT_DIR _AGENT_NAME
|
|
142
|
+
for _AGENT_DIR in "$ACCOUNT_DIR/agents"/*/; do
|
|
143
|
+
[ -d "$_AGENT_DIR" ] || continue
|
|
144
|
+
_AGENT_NAME="$(basename "$_AGENT_DIR")"
|
|
145
|
+
[ "$_AGENT_NAME" = "admin" ] && continue
|
|
146
|
+
[ -f "$_AGENT_DIR/IDENTITY.md" ] || continue
|
|
147
|
+
cp "$TEMPLATES_DIR/agents/public/IDENTITY.md" "$_AGENT_DIR/IDENTITY.md"
|
|
148
|
+
echo " agents/$_AGENT_NAME/IDENTITY.md re-synced"
|
|
149
|
+
done
|
|
150
|
+
|
|
151
|
+
cp "$TEMPLATES_DIR/specialists/.claude-plugin/plugin.json" "$ACCOUNT_DIR/specialists/.claude-plugin/plugin.json"
|
|
152
|
+
|
|
153
|
+
# Replace core specialist files (no '--'); preserve premium plugin agents.
|
|
154
|
+
local _existing
|
|
155
|
+
for _existing in "$ACCOUNT_DIR/specialists/agents/"*.md; do
|
|
156
|
+
[ -f "$_existing" ] || continue
|
|
157
|
+
case "$(basename "$_existing")" in
|
|
158
|
+
*--*) continue ;;
|
|
159
|
+
esac
|
|
160
|
+
rm -f "$_existing"
|
|
161
|
+
done
|
|
162
|
+
|
|
163
|
+
local specialist
|
|
164
|
+
for specialist in "$TEMPLATES_DIR/specialists/agents/"*.md; do
|
|
165
|
+
[ -f "$specialist" ] && cp "$specialist" "$ACCOUNT_DIR/specialists/agents/$(basename "$specialist")"
|
|
166
|
+
done
|
|
167
|
+
|
|
168
|
+
rm -f "$ACCOUNT_DIR/.claude/agents/"*.md 2>/dev/null || true
|
|
169
|
+
|
|
170
|
+
# SOUL.md — user-controlled. Only create if missing. Never overwrite.
|
|
171
|
+
if [ -f "$ACCOUNT_DIR/agents/admin/SOUL.md" ]; then
|
|
172
|
+
echo " agents/admin/SOUL.md preserved ($(wc -c < "$ACCOUNT_DIR/agents/admin/SOUL.md" | tr -d ' ') bytes)"
|
|
173
|
+
else
|
|
174
|
+
cp "$TEMPLATES_DIR/agents/admin/SOUL.md" "$ACCOUNT_DIR/agents/admin/SOUL.md"
|
|
175
|
+
echo " agents/admin/SOUL.md created ($(wc -c < "$ACCOUNT_DIR/agents/admin/SOUL.md" | tr -d ' ') bytes)"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
bash "$SCRIPT_DIR/lib/agents-md-bootstrap.sh" "$ACCOUNT_DIR"
|
|
179
|
+
bash "$SCRIPT_DIR/lib/admin-skills-bootstrap.sh" "$ACCOUNT_DIR" "$PROJECT_DIR/plugins"
|
|
180
|
+
|
|
181
|
+
# --- account.json: create from template, stamp accountId + role ------------
|
|
182
|
+
local TEMPLATE="$TEMPLATES_DIR/account.json"
|
|
183
|
+
if [ ! -f "$ACCOUNT_DIR/account.json" ]; then
|
|
184
|
+
if [ -f "$TEMPLATE" ]; then
|
|
185
|
+
sed "s/\"accountId\": *\"[^\"]*\"/\"accountId\":\"$ACCOUNT_ID\"/" "$TEMPLATE" \
|
|
186
|
+
| sed "s/\"tier\": *\"[^\"]*\"/\"tier\":\"solo\"/" \
|
|
187
|
+
> "$ACCOUNT_DIR/account.json"
|
|
188
|
+
else
|
|
189
|
+
echo "ERROR: account.json template not found at $TEMPLATE" >&2
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Stamp role on the account.json (idempotent migration + designation). A
|
|
195
|
+
# pre-existing legacy account.json with no role gets ROLE; an account already
|
|
196
|
+
# carrying a role keeps it unless ROLE differs (operator-driven via the
|
|
197
|
+
# lifecycle tools, never here).
|
|
198
|
+
ACCOUNT_ID="$ACCOUNT_ID" ROLE="$ROLE" python3 - "$ACCOUNT_DIR/account.json" <<'PY'
|
|
199
|
+
import json, os, sys
|
|
200
|
+
path = sys.argv[1]
|
|
201
|
+
role = os.environ["ROLE"]
|
|
202
|
+
acct_id = os.environ["ACCOUNT_ID"]
|
|
203
|
+
with open(path) as f:
|
|
204
|
+
cfg = json.load(f)
|
|
205
|
+
changed = False
|
|
206
|
+
if not cfg.get("accountId"):
|
|
207
|
+
cfg["accountId"] = acct_id; changed = True
|
|
208
|
+
if role and cfg.get("role") != role:
|
|
209
|
+
cfg["role"] = role; changed = True
|
|
210
|
+
if "managedService" not in cfg:
|
|
211
|
+
cfg["managedService"] = False; changed = True
|
|
212
|
+
if changed:
|
|
213
|
+
with open(path, "w") as f:
|
|
214
|
+
json.dump(cfg, f, indent=2); f.write("\n")
|
|
215
|
+
print(f" account.json role={cfg.get('role')} stamped")
|
|
216
|
+
PY
|
|
217
|
+
|
|
218
|
+
# --- enabledPlugins from brand.json plugins.defaultEnabled -----------------
|
|
219
|
+
local BRAND_JSON="$PROJECT_DIR/config/brand.json"
|
|
220
|
+
if [ -f "$BRAND_JSON" ] && [ -f "$ACCOUNT_DIR/account.json" ]; then
|
|
221
|
+
PROJECT_DIR="$PROJECT_DIR" BRAND_JSON="$BRAND_JSON" ACCOUNT_JSON="$ACCOUNT_DIR/account.json" python3 - <<'PY'
|
|
222
|
+
import json, os, sys
|
|
223
|
+
|
|
224
|
+
brand_path = os.environ['BRAND_JSON']
|
|
225
|
+
account_path = os.environ['ACCOUNT_JSON']
|
|
226
|
+
plugins_dir = os.path.join(os.environ['PROJECT_DIR'], 'plugins')
|
|
227
|
+
|
|
228
|
+
with open(brand_path) as f:
|
|
229
|
+
brand = json.load(f)
|
|
230
|
+
|
|
231
|
+
plugins_cfg = brand.get('plugins', {})
|
|
232
|
+
default_enabled = plugins_cfg.get('defaultEnabled', [])
|
|
233
|
+
core = set(plugins_cfg.get('core', []))
|
|
234
|
+
|
|
235
|
+
if not default_enabled:
|
|
236
|
+
print(' [setup] no defaultEnabled plugins in brand.json — skipping')
|
|
237
|
+
sys.exit(0)
|
|
238
|
+
|
|
239
|
+
satisfied = core | set(default_enabled)
|
|
240
|
+
validated = []
|
|
241
|
+
for plugin in default_enabled:
|
|
242
|
+
plugin_md = os.path.join(plugins_dir, plugin, 'PLUGIN.md')
|
|
243
|
+
requires = []
|
|
244
|
+
if os.path.isfile(plugin_md):
|
|
245
|
+
in_frontmatter = False
|
|
246
|
+
in_requires = False
|
|
247
|
+
with open(plugin_md) as f:
|
|
248
|
+
for line in f:
|
|
249
|
+
stripped = line.rstrip()
|
|
250
|
+
if stripped == '---':
|
|
251
|
+
if in_frontmatter:
|
|
252
|
+
break
|
|
253
|
+
in_frontmatter = True
|
|
254
|
+
continue
|
|
255
|
+
if not in_frontmatter:
|
|
256
|
+
continue
|
|
257
|
+
if stripped.startswith('requires:'):
|
|
258
|
+
inline = stripped.split(':', 1)[1].strip()
|
|
259
|
+
if inline.startswith('['):
|
|
260
|
+
requires = [x.strip().strip('"').strip("'") for x in inline.strip('[]').split(',') if x.strip()]
|
|
261
|
+
in_requires = False
|
|
262
|
+
else:
|
|
263
|
+
in_requires = True
|
|
264
|
+
continue
|
|
265
|
+
if in_requires:
|
|
266
|
+
if stripped.startswith(' - '):
|
|
267
|
+
requires.append(stripped.lstrip(' -').strip())
|
|
268
|
+
else:
|
|
269
|
+
in_requires = False
|
|
270
|
+
unmet = [r for r in requires if r not in satisfied]
|
|
271
|
+
if unmet:
|
|
272
|
+
print(f' [setup] WARNING: skipping defaultEnabled plugin "{plugin}" — unmet requires: {unmet}')
|
|
273
|
+
else:
|
|
274
|
+
validated.append(plugin)
|
|
275
|
+
|
|
276
|
+
with open(account_path) as f:
|
|
277
|
+
account = json.load(f)
|
|
278
|
+
|
|
279
|
+
existing = account.get('enabledPlugins', [])
|
|
280
|
+
existing_set = set(existing)
|
|
281
|
+
added = [p for p in validated if p not in existing_set]
|
|
282
|
+
account['enabledPlugins'] = existing + added
|
|
283
|
+
|
|
284
|
+
with open(account_path, 'w') as f:
|
|
285
|
+
json.dump(account, f, indent=2)
|
|
286
|
+
f.write('\n')
|
|
287
|
+
|
|
288
|
+
if not existing:
|
|
289
|
+
print(f' [setup] seeded enabledPlugins: {validated}')
|
|
290
|
+
elif added:
|
|
291
|
+
print(f' [setup] merged into enabledPlugins: +{added} (existing: {existing})')
|
|
292
|
+
else:
|
|
293
|
+
print(f' [setup] enabledPlugins already contains all defaultEnabled entries')
|
|
294
|
+
PY
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# --- stamp install owner into admins[] (idempotent) ------------------------
|
|
298
|
+
if [ -n "${USERS_FILE:-}" ] && [ -f "$USERS_FILE" ]; then
|
|
299
|
+
local USER_ID
|
|
300
|
+
USER_ID=$(python3 -c "import json; print(json.load(open('$USERS_FILE'))[0]['userId'])" 2>/dev/null || true)
|
|
301
|
+
if [ -n "$USER_ID" ] && [ -f "$ACCOUNT_DIR/account.json" ]; then
|
|
302
|
+
USER_ID="$USER_ID" python3 - "$ACCOUNT_DIR/account.json" <<'PY'
|
|
303
|
+
import json, os, sys
|
|
304
|
+
path = sys.argv[1]
|
|
305
|
+
uid = os.environ["USER_ID"]
|
|
306
|
+
with open(path) as f:
|
|
307
|
+
cfg = json.load(f)
|
|
308
|
+
cfg.setdefault("admins", [])
|
|
309
|
+
if not any(a.get("userId") == uid for a in cfg["admins"]):
|
|
310
|
+
cfg["admins"].append({"userId": uid, "role": "owner"})
|
|
311
|
+
with open(path, "w") as f:
|
|
312
|
+
json.dump(cfg, f, indent=2); f.write("\n")
|
|
313
|
+
print(f" Stamped userId={uid} as owner in account.json admins")
|
|
314
|
+
PY
|
|
315
|
+
fi
|
|
316
|
+
fi
|
|
317
|
+
|
|
318
|
+
echo " Account $ACCOUNT_ID at $ACCOUNT_DIR (role=$ROLE)"
|
|
319
|
+
}
|