@rubytech/create-maxy-code 0.1.219 → 0.1.222

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.
Files changed (124) hide show
  1. package/dist/index.js +46 -1
  2. package/package.json +1 -1
  3. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +60 -23
  4. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
  5. package/payload/platform/lib/graph-write/dist/index.d.ts +4 -1
  6. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  7. package/payload/platform/lib/graph-write/dist/index.js +17 -5
  8. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  9. package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +58 -31
  10. package/payload/platform/lib/graph-write/src/index.ts +16 -7
  11. package/payload/platform/plugins/admin/PLUGIN.md +2 -3
  12. package/payload/platform/plugins/admin/__tests__/admin-can-enumerate.test.sh +129 -0
  13. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +1 -2
  14. package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +3 -2
  15. package/payload/platform/plugins/admin/mcp/dist/index.js +41 -9
  16. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/admin/references/publish-site-routing.md +44 -0
  18. package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +1 -1
  19. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +11 -4
  20. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +2 -2
  21. package/payload/platform/plugins/contacts/PLUGIN.md +2 -2
  22. package/payload/platform/plugins/docs/references/internals.md +1 -1
  23. package/payload/platform/plugins/docs/references/platform.md +2 -2
  24. package/payload/platform/plugins/docs/references/troubleshooting.md +7 -0
  25. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  26. package/payload/platform/plugins/memory/references/graph-primitives.md +2 -2
  27. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts +2 -1
  28. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts.map +1 -1
  29. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js +27 -21
  30. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js.map +1 -1
  31. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  32. package/payload/platform/scripts/setup-account.sh +0 -15
  33. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts +4 -0
  34. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts.map +1 -0
  35. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js +50 -0
  36. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js.map +1 -0
  37. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/http-server.js +19 -2
  39. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  41. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +39 -19
  42. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  43. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +24 -0
  44. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  45. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +100 -11
  46. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  47. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +8 -0
  48. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
  49. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +7 -2
  50. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
  51. package/payload/server/public/assets/{AdminShell-BcxEZnpu.js → AdminShell-BiEwDSRL.js} +1 -1
  52. package/payload/server/public/assets/{Checkbox-DL-HdT29.js → Checkbox-COiFXiXL.js} +1 -1
  53. package/payload/server/public/assets/{admin-DMo5DWKH.js → admin-Ca9ckLZx.js} +1 -1
  54. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BoUw4oYz.js → architectureDiagram-Q4EWVU46-DI6JZI13.js} +1 -1
  55. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DOscnO3m.js → blockDiagram-DXYQGD6D-dgDvswLd.js} +1 -1
  56. package/payload/server/public/assets/{brand-SZW-27T7.css → brand-CAMMU5lz.css} +1 -1
  57. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-aZ1IuZ_F.js → c4Diagram-AHTNJAMY-odaGY4GZ.js} +1 -1
  58. package/payload/server/public/assets/channel-DfwLbKck.js +1 -0
  59. package/payload/server/public/assets/{chunk-336JU56O-DS3ksXBG.js → chunk-336JU56O-BpbY8F27.js} +2 -2
  60. package/payload/server/public/assets/{chunk-426QAEUC-BLRn2STO.js → chunk-426QAEUC-BWbVhEVC.js} +1 -1
  61. package/payload/server/public/assets/{chunk-4TB4RGXK-BrhOvJ01.js → chunk-4TB4RGXK-C3IfRdvA.js} +1 -1
  62. package/payload/server/public/assets/{chunk-5FUZZQ4R-BZZwZX5Q.js → chunk-5FUZZQ4R-CSRMBAGr.js} +1 -1
  63. package/payload/server/public/assets/{chunk-5PVQY5BW-YXZKnPCz.js → chunk-5PVQY5BW-CGAe6GJJ.js} +1 -1
  64. package/payload/server/public/assets/{chunk-EDXVE4YY-DAMCJHKQ.js → chunk-EDXVE4YY-BnXC8cYQ.js} +1 -1
  65. package/payload/server/public/assets/{chunk-ENJZ2VHE-DsD95Bqn.js → chunk-ENJZ2VHE-7qfDalAB.js} +1 -1
  66. package/payload/server/public/assets/{chunk-ICPOFSXX-DxtrRClx.js → chunk-ICPOFSXX-TRvVGIDw.js} +1 -1
  67. package/payload/server/public/assets/{chunk-OYMX7WX6-CN4kLK-H.js → chunk-OYMX7WX6-Cfv8vBvI.js} +1 -1
  68. package/payload/server/public/assets/{chunk-U2HBQHQK-BPTv3Oj1.js → chunk-U2HBQHQK-BfLvbEyu.js} +1 -1
  69. package/payload/server/public/assets/{chunk-X2U36JSP-DxBry33v.js → chunk-X2U36JSP-Cj3k-dYC.js} +1 -1
  70. package/payload/server/public/assets/{chunk-YZCP3GAM-BZ4N75oy.js → chunk-YZCP3GAM-CygW6I0h.js} +1 -1
  71. package/payload/server/public/assets/{chunk-ZZ45TVLE-Dqr161Q1.js → chunk-ZZ45TVLE-CBF3hFJH.js} +1 -1
  72. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CE0XZQre.js +1 -0
  73. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Bj41Y3XD.js +1 -0
  74. package/payload/server/public/assets/clone-DTd5nONj.js +1 -0
  75. package/payload/server/public/assets/{dagre-DtZ0wKkH.js → dagre-BTGABQJX.js} +1 -1
  76. package/payload/server/public/assets/{dagre-KV5264BT-BPKhT-ZX.js → dagre-KV5264BT-B2lbkuGd.js} +1 -1
  77. package/payload/server/public/assets/{data-D6IQdm0R.js → data-CHdaTjgn.js} +1 -1
  78. package/payload/server/public/assets/{diagram-5BDNPKRD-CATnafi7.js → diagram-5BDNPKRD-BUK4LqT8.js} +1 -1
  79. package/payload/server/public/assets/{diagram-G4DWMVQ6-ChZYQfji.js → diagram-G4DWMVQ6-e49HrvxL.js} +1 -1
  80. package/payload/server/public/assets/{diagram-MMDJMWI5-DHW6kYd3.js → diagram-MMDJMWI5-k4Kx_qWe.js} +1 -1
  81. package/payload/server/public/assets/{diagram-TYMM5635-CnS4Zci8.js → diagram-TYMM5635-DzavOUdK.js} +1 -1
  82. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DKYv2YWp.js → erDiagram-SMLLAGMA-CJKecRhi.js} +1 -1
  83. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BWvoZWlh.js → flowDiagram-DWJPFMVM-GM7H1TDt.js} +1 -1
  84. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CpCb-xKy.js → ganttDiagram-T4ZO3ILL-kDEA1A-3.js} +1 -1
  85. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CvHrDKQ-.js → gitGraphDiagram-UUTBAWPF-jVZZdeg2.js} +1 -1
  86. package/payload/server/public/assets/{graph-labels-CWJy0O4w.js → graph-labels-CavkO4DG.js} +1 -1
  87. package/payload/server/public/assets/{graph-PkZKVkWD.js → graph-tqCqSngu.js} +1 -1
  88. package/payload/server/public/assets/{graphlib-DjnyHqGy.js → graphlib-DFU2rJiY.js} +1 -1
  89. package/payload/server/public/assets/{infoDiagram-42DDH7IO-D_MywcVn.js → infoDiagram-42DDH7IO-CFxVoRBy.js} +1 -1
  90. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-N9m-f-Eb.js → ishikawaDiagram-UXIWVN3A-CDvZs3Ua.js} +1 -1
  91. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DE0q7uSM.js → journeyDiagram-VCZTEJTY-DUI1m9vW.js} +1 -1
  92. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Dj5hb9Bq.js → kanban-definition-6JOO6SKY-C_jSVK7W.js} +1 -1
  93. package/payload/server/public/assets/{line-C6ph0i4M.js → line-DfwJeo6J.js} +1 -1
  94. package/payload/server/public/assets/{mermaid-parser.core-BrfmEvCA.js → mermaid-parser.core-BN-2kBfU.js} +1 -1
  95. package/payload/server/public/assets/{mermaid.core-DYnHKfDv.js → mermaid.core-C6amhHAQ.js} +3 -3
  96. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DaSaZfhR.js → mindmap-definition-QFDTVHPH-eTKDlw3c.js} +1 -1
  97. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BAu9Ezbv.js → pieDiagram-DEJITSTG-DNX5VeMZ.js} +1 -1
  98. package/payload/server/public/assets/{public-UKgkJxKv.js → public-BADBLzOZ.js} +3 -3
  99. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DRsbL1Xd.js → quadrantDiagram-34T5L4WZ-BFnmYiHX.js} +1 -1
  100. package/payload/server/public/assets/{requirementDiagram-MS252O5E-B9QcsXV6.js → requirementDiagram-MS252O5E-DvHvTyzu.js} +1 -1
  101. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DsZbvIlD.js → sankeyDiagram-XADWPNL6-CMFnNT1G.js} +1 -1
  102. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BlT4O2N8.js → sequenceDiagram-FGHM5R23-BYon1ouU.js} +1 -1
  103. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-JFgk1K3Y.js → stateDiagram-FHFEXIEX-n6l3hcuJ.js} +1 -1
  104. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CS46-Mhi.js +1 -0
  105. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DOsHw1ip.js → timeline-definition-GMOUNBTQ-DFPFHrQF.js} +1 -1
  106. package/payload/server/public/assets/{useSelectionMode-JDGvwvIk.js → useSelectionMode-iqIyL24g.js} +1 -1
  107. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CQWIhqZY.js → vennDiagram-DHZGUBPP-DZLturYa.js} +1 -1
  108. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-D5Fh7YVL.js → wardleyDiagram-NUSXRM2D-BBt2pdjM.js} +1 -1
  109. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-C-tINQEs.js → xychartDiagram-5P7HB3ND-DOZxLvEi.js} +1 -1
  110. package/payload/server/public/data.html +5 -5
  111. package/payload/server/public/graph.html +6 -6
  112. package/payload/server/public/index.html +6 -6
  113. package/payload/server/public/public.html +5 -5
  114. package/payload/server/server.js +49 -6
  115. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +0 -109
  116. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +0 -204
  117. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-passthrough.test.sh +0 -118
  118. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +0 -337
  119. package/payload/server/public/assets/channel-CzcSNxDm.js +0 -1
  120. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DFOM3vCn.js +0 -1
  121. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-HplDfaL1.js +0 -1
  122. package/payload/server/public/assets/clone-G-2rqnSR.js +0 -1
  123. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-NXz27DaV.js +0 -1
  124. /package/payload/server/public/assets/{brand-SzZmKKjz.js → brand-DfykKss4.js} +0 -0
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env bash
2
+ # admin-can-enumerate.test.sh
3
+ #
4
+ # Acceptance gate for Task 579. Verifies that a fresh admin session
5
+ # locates a file under <accountDir>/sites/ on the FIRST enumeration
6
+ # attempt — no slug guessing, no `EISDIR`, no false-empty 200s, no
7
+ # "Log directory does not exist" against a path that resolves
8
+ # elsewhere. The gate runs from the installer post-install step against
9
+ # the freshly-deployed payload, before systemd services are restarted
10
+ # into it. A non-zero exit aborts the deploy and the old payload keeps
11
+ # running.
12
+ #
13
+ # Required environment:
14
+ # CLAUDE_CONFIG_DIR per-brand .claude directory (e.g. ~/.realagent-code/.claude)
15
+ # INSTALL_DIR the brand install root (e.g. ~/realagent-code)
16
+ #
17
+ # Exit codes:
18
+ # 0 pass — admin enumerated and emitted the seeded path within budget
19
+ # 1 fail — one or more assertions tripped (see stderr for the line)
20
+ # 2 setup error — could not seed test account or could not invoke claude
21
+
22
+ set -euo pipefail
23
+
24
+ CONFIG_DIR="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}"
25
+ INSTALL_DIR="${INSTALL_DIR:?INSTALL_DIR not set}"
26
+
27
+ if ! command -v claude >/dev/null 2>&1; then
28
+ echo "[admin-enumerate-gate] setup-error reason=claude-not-on-path" >&2
29
+ exit 2
30
+ fi
31
+ if ! command -v jq >/dev/null 2>&1; then
32
+ echo "[admin-enumerate-gate] setup-error reason=jq-not-on-path" >&2
33
+ exit 2
34
+ fi
35
+
36
+ ACCOUNTS_DIR="$INSTALL_DIR/data/accounts"
37
+ TEST_ACCT_ID="enumerate-gate-$$"
38
+ TEST_ACCT_DIR="$ACCOUNTS_DIR/$TEST_ACCT_ID"
39
+ JSONL_OUT=$(mktemp)
40
+ EXIT=0
41
+
42
+ cleanup() {
43
+ rm -rf "$TEST_ACCT_DIR"
44
+ if [ "$EXIT" -eq 0 ]; then
45
+ rm -f "$JSONL_OUT"
46
+ else
47
+ echo "[admin-enumerate-gate] evidence-jsonl=$JSONL_OUT" >&2
48
+ fi
49
+ }
50
+ trap cleanup EXIT
51
+
52
+ # Seed: one target site + 5 decoys. Target index.html is the file the
53
+ # agent must locate and emit the absolute path for.
54
+ mkdir -p "$TEST_ACCT_DIR/sites/exp-uk-got-talent"
55
+ echo "<html><body>eXp Has Got Talent</body></html>" > "$TEST_ACCT_DIR/sites/exp-uk-got-talent/index.html"
56
+ for decoy in alpha-corp-careers beta-properties gamma-events delta-news epsilon-portal; do
57
+ mkdir -p "$TEST_ACCT_DIR/sites/$decoy"
58
+ echo "<html>decoy</html>" > "$TEST_ACCT_DIR/sites/$decoy/index.html"
59
+ done
60
+
61
+ TARGET_PATH="$TEST_ACCT_DIR/sites/exp-uk-got-talent/index.html"
62
+
63
+ PROMPT="find the eXp Has Got Talent site in this account and print the absolute path to its index.html"
64
+
65
+ # Spawn claude headless against the test account. ACCOUNT_ID + the
66
+ # MAXY_SESSION_ROLE/PLATFORM_ROOT env are what the platform MCPs read
67
+ # to scope themselves; PLATFORM_ROOT resolves the on-disk payload that
68
+ # was just deployed. JSONL goes to a file we parse below.
69
+ ACCOUNT_ID="$TEST_ACCT_ID" \
70
+ MAXY_SESSION_ROLE="admin" \
71
+ PLATFORM_ROOT="$INSTALL_DIR/platform" \
72
+ CLAUDE_CONFIG_DIR="$CONFIG_DIR" \
73
+ claude -p "$PROMPT" --output-format=stream-json --verbose \
74
+ > "$JSONL_OUT" 2>&1 \
75
+ || { echo "[admin-enumerate-gate] setup-error reason=claude-spawn-failed exit=$?" >&2; EXIT=2; exit 2; }
76
+
77
+ fail() {
78
+ echo "[admin-enumerate-gate] FAIL $1" >&2
79
+ EXIT=1
80
+ }
81
+
82
+ # Assertion 1: the target path appears verbatim in some assistant turn
83
+ # or tool-use input. Either as the agent's final message or as an arg
84
+ # passed to a tool.
85
+ if ! grep -F -q "$TARGET_PATH" "$JSONL_OUT"; then
86
+ fail "correct-path-not-emitted target=$TARGET_PATH"
87
+ fi
88
+
89
+ # Assertion 2: zero forbidden error patterns from the surfaces this
90
+ # task fixed.
91
+ FORBIDDEN_HITS=$(grep -E -c "EISDIR|Log directory does not exist|Plugin file not found|page has no readable text content" "$JSONL_OUT" || true)
92
+ if [ "${FORBIDDEN_HITS:-0}" -gt 0 ]; then
93
+ fail "forbidden-error-patterns=$FORBIDDEN_HITS"
94
+ fi
95
+
96
+ # Assertion 3: total tool-use calls from the user prompt to the
97
+ # correct-path emission are at most 3. The earlier failing run issued
98
+ # ~15.
99
+ TOOL_USE_COUNT=$(jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' < "$JSONL_OUT" 2>/dev/null | wc -l | tr -d ' ' || echo 0)
100
+ if [ "${TOOL_USE_COUNT:-0}" -gt 3 ]; then
101
+ fail "tool-use-budget-exceeded count=$TOOL_USE_COUNT cap=3"
102
+ fi
103
+
104
+ # Assertion 4: the FIRST disk-listing tool call returns an enumeration
105
+ # of <accountDir>/sites/. "Disk-listing" means LS, Glob, or Bash with
106
+ # `ls`. We accept any of those as the first relevant call; what we
107
+ # reject is a Read against a directory path.
108
+ FIRST_DIR_TOOL=$(jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | {name, input} | @json' < "$JSONL_OUT" 2>/dev/null \
109
+ | jq -r 'select(.name=="LS" or .name=="Glob" or (.name=="Bash" and (.input.command // "" | test("\\bls\\b")))) | .name' \
110
+ | head -1)
111
+ if [ -z "$FIRST_DIR_TOOL" ]; then
112
+ fail "no-disk-listing-tool-call-emitted"
113
+ fi
114
+
115
+ # Assertion 5: no Read tool call against a path under <accountDir>/sites/
116
+ # that is a directory (would have produced EISDIR pre-fix; should not
117
+ # even be attempted now).
118
+ BAD_READ=$(jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Read") | .input.file_path // ""' < "$JSONL_OUT" 2>/dev/null \
119
+ | while read -r p; do
120
+ [ -n "$p" ] && [ -d "$p" ] && echo "$p"
121
+ done | head -1)
122
+ if [ -n "$BAD_READ" ]; then
123
+ fail "read-against-directory path=$BAD_READ"
124
+ fi
125
+
126
+ if [ "$EXIT" -eq 0 ]; then
127
+ echo "[admin-enumerate-gate] PASS target=$TARGET_PATH tool-use-count=$TOOL_USE_COUNT first-dir-tool=$FIRST_DIR_TOOL"
128
+ fi
129
+ exit "$EXIT"
@@ -52,8 +52,7 @@
52
52
  # server.log alongside the [whatsapp-ingest] script lines.
53
53
  #
54
54
  # Exit codes follow Claude Code hook protocol: 0 = allow, 2 = block (stderr
55
- # message shown to the agent). Fail-closed on terminal stdin to match
56
- # pre-tool-use.sh.
55
+ # message shown to the agent). Fail-closed on terminal stdin.
57
56
 
58
57
  set -uo pipefail
59
58
 
@@ -130,8 +130,9 @@ PY
130
130
 
131
131
  # hook_block_with_emit <agentId> <hookName> <toolName> <reason>
132
132
  # <userMessage> [durationMs]
133
- # Convenience wrapper for pre-tool-use.sh: emits the propagation record,
134
- # echoes the user-facing block message to stderr, and exits 2.
133
+ # Convenience wrapper for any PreToolUse hook that records a block decision:
134
+ # emits the propagation record, echoes the user-facing block message to stderr,
135
+ # and exits 2.
135
136
  hook_block_with_emit() {
136
137
  local agentId="$1" hookName="$2" toolName="$3" reason="$4"
137
138
  local userMessage="$5" durationMs="${6:-0}"
@@ -755,8 +755,8 @@ eagerTool(server, "account-update", "Update a user-configurable setting in accou
755
755
  }
756
756
  });
757
757
  // Plugin enable/disable: deterministic write to account.json.enabledPlugins.
758
- // The pre-tool-use hook denies the agent's direct Edit on account.json,
759
- // so the agent can no longer toggle enablement by hand-editing the file. This
758
+ // IDENTITY.md doctrine forbids the agent's direct Edit on account.json,
759
+ // so the agent must not toggle enablement by hand-editing the file. This
760
760
  // tool is the legitimate path: validates the plugin name, refuses core plugins,
761
761
  // confirms the plugin directory exists, and atomically updates the array.
762
762
  // Entitlement-bearing fields (tier, purchasedPlugins) are NOT writable here.
@@ -1397,6 +1397,15 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
1397
1397
  if (!ACCOUNT_ID)
1398
1398
  return refuseNoAccount("logs-read");
1399
1399
  try {
1400
+ // Account-scoped per-session log dir. `<installDir>/data/accounts/
1401
+ // <accountId>/logs/` mirrors the resolution in
1402
+ // `platform/scripts/logs-read.sh`. This dir may not exist on a Pi
1403
+ // where no claude session has yet tee'd stream output — that is NOT
1404
+ // a fatal condition for platform-scoped types like `server` / `vnc`,
1405
+ // which resolve against CONFIG_DIR below. Task 579 — the previous
1406
+ // early-existence check returned "Log directory does not exist" for
1407
+ // every type, including those whose actual target file lived
1408
+ // elsewhere. Each branch now checks its own resolved path.
1400
1409
  const LOG_DIR = resolve(getAccountDir(), "logs");
1401
1410
  // sessionKey is the single identifier on disk.
1402
1411
  // `sessionId` is retained as a legacy alias that maps to the
@@ -1404,7 +1413,12 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
1404
1413
  const idForResolve = sessionKey ?? sessionId;
1405
1414
  if (idForResolve) {
1406
1415
  if (!existsSync(LOG_DIR)) {
1407
- return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
1416
+ return {
1417
+ content: [{
1418
+ type: "text",
1419
+ text: `No per-session logs for this account yet (resolved dir does not exist on disk): ${LOG_DIR}. The directory is created on first claude session spawn.`,
1420
+ }],
1421
+ };
1408
1422
  }
1409
1423
  const prefixMap = {
1410
1424
  "agent-stream": "claude-agent-stream",
@@ -1443,9 +1457,10 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
1443
1457
  }],
1444
1458
  };
1445
1459
  }
1446
- if (!existsSync(LOG_DIR)) {
1447
- return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
1448
- }
1460
+ // Note: no early existence check on LOG_DIR here. Platform-scoped
1461
+ // types (server, vnc, heartbeat) resolve against CONFIG_DIR below
1462
+ // and must run even when the account log dir has not been created
1463
+ // yet. Account-scoped types check LOG_DIR before they reach for it.
1449
1464
  // --- Session-key filtered mode: grep across log files ---
1450
1465
  if (sessionKey) {
1451
1466
  const prefixes = {
@@ -1463,7 +1478,13 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
1463
1478
  const searchPrefixes = type && type !== "heartbeat" && type !== "server" && type !== "vnc" && prefixes[type]
1464
1479
  ? { [type]: prefixes[type] }
1465
1480
  : (type === "server" || type === "vnc") ? {} : prefixes;
1466
- const allFiles = readdirSync(LOG_DIR).filter(f => f.endsWith(".log"));
1481
+ // LOG_DIR may not exist on a Pi with no per-session logs yet. The
1482
+ // platform-scoped fallback dirs (CONFIG_DIR/logs/) are searched
1483
+ // below; this branch simply contributes nothing rather than
1484
+ // throwing on readdir.
1485
+ const allFiles = existsSync(LOG_DIR)
1486
+ ? readdirSync(LOG_DIR).filter(f => f.endsWith(".log"))
1487
+ : [];
1467
1488
  const sections = [];
1468
1489
  // Search account-scoped prefix-based log files
1469
1490
  for (const [logType, prefix] of Object.entries(searchPrefixes)) {
@@ -1571,13 +1592,24 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
1571
1592
  : resolvedType === "public" ? "public-agent-stream-"
1572
1593
  : resolvedType === "mcp" ? "mcp-"
1573
1594
  : "claude-agent-stream-";
1574
- // Find the most recently modified file with the matching prefix
1595
+ // Find the most recently modified file with the matching prefix.
1596
+ // LOG_DIR may not exist yet on a Pi with no per-session logs; return
1597
+ // the resolved path in the error envelope so the operator can
1598
+ // verify on disk in one step rather than re-tracing the resolver.
1599
+ if (!existsSync(LOG_DIR)) {
1600
+ return {
1601
+ content: [{
1602
+ type: "text",
1603
+ text: `No per-session logs for this account yet (resolved dir does not exist on disk): ${LOG_DIR}. The directory is created on first claude session spawn.`,
1604
+ }],
1605
+ };
1606
+ }
1575
1607
  const match = readdirSync(LOG_DIR)
1576
1608
  .filter(f => f.startsWith(prefix) && f.endsWith(".log"))
1577
1609
  .map(f => ({ file: f, mtime: statSync(resolve(LOG_DIR, f)).mtimeMs }))
1578
1610
  .sort((a, b) => b.mtime - a.mtime)[0];
1579
1611
  if (!match) {
1580
- return { content: [{ type: "text", text: `No log file found for type '${resolvedType}' in ${LOG_DIR}` }] };
1612
+ return { content: [{ type: "text", text: `No log file found for type '${resolvedType}' (resolved dir: ${LOG_DIR}, prefix: ${prefix})` }] };
1581
1613
  }
1582
1614
  const logFile = resolve(LOG_DIR, match.file);
1583
1615
  const result = execFileSync("tail", ["-n", String(lines), logFile], {