crewly 1.5.8 → 1.5.9

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 (30) hide show
  1. package/config/skills/_common/lib.sh +47 -0
  2. package/config/skills/agent/core/accept-task/execute.sh +6 -6
  3. package/config/skills/agent/core/get-my-context/execute.sh +5 -5
  4. package/config/skills/agent/core/get-my-tasks/execute.sh +4 -4
  5. package/config/skills/agent/core/recall/execute.sh +5 -5
  6. package/config/skills/agent/core/record-learning/execute.sh +7 -7
  7. package/config/skills/agent/core/register-self/execute.sh +6 -6
  8. package/config/skills/agent/core/remember/execute.sh +9 -9
  9. package/config/skills/agent/core/report-status/execute.sh +12 -12
  10. package/config/skills/agent/core/send-message/execute.sh +6 -5
  11. package/config/skills/orchestrator/delegate-task/execute.sh +19 -19
  12. package/config/skills/orchestrator/send-message/execute.sh +7 -6
  13. package/config/skills/team-leader/delegate-task/execute.sh +13 -13
  14. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +29 -6
  16. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/system/scheduler.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/system/scheduler.controller.js +10 -2
  19. package/dist/backend/backend/src/controllers/system/scheduler.controller.js.map +1 -1
  20. package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/session/session-command-helper.js +11 -5
  22. package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
  23. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts +8 -2
  24. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/workflow/scheduler.service.js +30 -6
  26. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  27. package/frontend/dist/assets/index-27a2c65e.js +5215 -0
  28. package/frontend/dist/index.html +1 -1
  29. package/package.json +1 -1
  30. package/frontend/dist/assets/index-512efc8e.js +0 -4921
@@ -7,6 +7,53 @@
7
7
  # Base URL for the Crewly backend API
8
8
  CREWLY_API_URL="${CREWLY_API_URL:-http://localhost:8787}"
9
9
 
10
+ # -----------------------------------------------------------------------------
11
+ # read_json_input [arg]
12
+ #
13
+ # Reads JSON input from either:
14
+ # 1. The provided command-line argument ($1)
15
+ # 2. A file path prefixed with @ (e.g. @/tmp/input.json)
16
+ # 3. Standard input (stdin) — for piped or heredoc usage
17
+ #
18
+ # This solves Gemini CLI's run_shell_command escaping issues (#292, #293):
19
+ # special characters in JSON (parentheses, quotes, newlines) are mangled
20
+ # when passed as shell arguments. Piping via stdin bypasses shell parsing.
21
+ #
22
+ # Usage in skill scripts:
23
+ # INPUT=$(read_json_input "${1:-}")
24
+ # -----------------------------------------------------------------------------
25
+ read_json_input() {
26
+ local arg="${1:-}"
27
+
28
+ # Option 1: Direct argument provided (non-empty)
29
+ if [ -n "$arg" ]; then
30
+ # Support @filepath syntax: read JSON from file
31
+ if [[ "$arg" == @* ]]; then
32
+ local filepath="${arg:1}"
33
+ if [ -f "$filepath" ]; then
34
+ cat "$filepath"
35
+ else
36
+ echo '{}' >&2
37
+ echo "File not found: $filepath" >&2
38
+ return 1
39
+ fi
40
+ else
41
+ printf '%s' "$arg"
42
+ fi
43
+ return 0
44
+ fi
45
+
46
+ # Option 2: Read from stdin (pipe or heredoc)
47
+ if [ ! -t 0 ]; then
48
+ cat
49
+ return 0
50
+ fi
51
+
52
+ # Option 3: No input available
53
+ echo ''
54
+ return 0
55
+ }
56
+
10
57
  # -----------------------------------------------------------------------------
11
58
  # api_call METHOD endpoint [json_body]
12
59
  #
@@ -4,15 +4,15 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"dev-1\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"dev-1\"}' or echo '{...}' | execute.sh"
9
9
 
10
- SESSION_NAME=$(echo "$INPUT" | jq -r '.sessionName // empty')
10
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
11
11
  require_param "sessionName" "$SESSION_NAME"
12
12
 
13
- TEAM_MEMBER_ID=$(echo "$INPUT" | jq -r '.teamMemberId // empty')
14
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
15
- TASK_GROUP=$(echo "$INPUT" | jq -r '.taskGroup // empty')
13
+ TEAM_MEMBER_ID=$(printf '%s' "$INPUT" | jq -r '.teamMemberId // empty')
14
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
15
+ TASK_GROUP=$(printf '%s' "$INPUT" | jq -r '.taskGroup // empty')
16
16
 
17
17
  BODY=$(jq -n \
18
18
  --arg sessionName "$SESSION_NAME" \
@@ -4,12 +4,12 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"/projects/app\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- AGENT_ID=$(echo "$INPUT" | jq -r '.agentId // empty')
11
- AGENT_ROLE=$(echo "$INPUT" | jq -r '.agentRole // empty')
12
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
10
+ AGENT_ID=$(printf '%s' "$INPUT" | jq -r '.agentId // empty')
11
+ AGENT_ROLE=$(printf '%s' "$INPUT" | jq -r '.agentRole // empty')
12
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
13
13
  require_param "agentId" "$AGENT_ID"
14
14
  require_param "agentRole" "$AGENT_ROLE"
15
15
  require_param "projectPath" "$PROJECT_PATH"
@@ -4,11 +4,11 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"crewly-product-sam-xxxx\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"...\",\"projectPath\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- SESSION_NAME=$(echo "$INPUT" | jq -r '.sessionName // empty')
11
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
10
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
11
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
12
12
  require_param "sessionName" "$SESSION_NAME"
13
13
  require_param "projectPath" "$PROJECT_PATH"
14
14
 
@@ -4,16 +4,16 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"context\":\"authentication patterns\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"context\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- AGENT_ID=$(echo "$INPUT" | jq -r '.agentId // empty')
11
- CONTEXT=$(echo "$INPUT" | jq -r '.context // empty')
10
+ AGENT_ID=$(printf '%s' "$INPUT" | jq -r '.agentId // empty')
11
+ CONTEXT=$(printf '%s' "$INPUT" | jq -r '.context // empty')
12
12
  require_param "agentId" "$AGENT_ID"
13
13
  require_param "context" "$CONTEXT"
14
14
 
15
15
  # Build body with required and optional fields
16
- BODY=$(echo "$INPUT" | jq '{
16
+ BODY=$(printf '%s' "$INPUT" | jq '{
17
17
  agentId: .agentId,
18
18
  context: .context
19
19
  } +
@@ -4,20 +4,20 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"/projects/app\",\"learning\":\"Jest mock resets are needed between tests\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"...\",\"learning\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- AGENT_ID=$(echo "$INPUT" | jq -r '.agentId // empty')
11
- AGENT_ROLE=$(echo "$INPUT" | jq -r '.agentRole // empty')
12
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
13
- LEARNING=$(echo "$INPUT" | jq -r '.learning // empty')
10
+ AGENT_ID=$(printf '%s' "$INPUT" | jq -r '.agentId // empty')
11
+ AGENT_ROLE=$(printf '%s' "$INPUT" | jq -r '.agentRole // empty')
12
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
13
+ LEARNING=$(printf '%s' "$INPUT" | jq -r '.learning // empty')
14
14
  require_param "agentId" "$AGENT_ID"
15
15
  require_param "agentRole" "$AGENT_ROLE"
16
16
  require_param "projectPath" "$PROJECT_PATH"
17
17
  require_param "learning" "$LEARNING"
18
18
 
19
19
  # Build body with required and optional fields
20
- BODY=$(echo "$INPUT" | jq '{
20
+ BODY=$(printf '%s' "$INPUT" | jq '{
21
21
  agentId: .agentId,
22
22
  agentRole: .agentRole,
23
23
  projectPath: .projectPath,
@@ -4,18 +4,18 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"role\":\"developer\",\"sessionName\":\"dev-1\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"role\":\"developer\",\"sessionName\":\"dev-1\"}' or echo '{...}' | execute.sh"
9
9
 
10
10
  # Accept both canonical (role/sessionName) and legacy (agentRole/agentId) parameter names
11
- ROLE=$(echo "$INPUT" | jq -r '.role // .agentRole // empty')
12
- SESSION_NAME=$(echo "$INPUT" | jq -r '.sessionName // .agentId // empty')
11
+ ROLE=$(printf '%s' "$INPUT" | jq -r '.role // .agentRole // empty')
12
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // .agentId // empty')
13
13
  require_param "role" "$ROLE"
14
14
  require_param "sessionName" "$SESSION_NAME"
15
15
 
16
16
  # Optional: claudeSessionId for resume support
17
- CLAUDE_SESSION_ID=$(echo "$INPUT" | jq -r '.claudeSessionId // empty')
18
- TEAM_MEMBER_ID=$(echo "$INPUT" | jq -r '.teamMemberId // empty')
17
+ CLAUDE_SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.claudeSessionId // empty')
18
+ TEAM_MEMBER_ID=$(printf '%s' "$INPUT" | jq -r '.teamMemberId // empty')
19
19
 
20
20
  BODY=$(jq -n \
21
21
  --arg role "$ROLE" \
@@ -4,26 +4,26 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"content\":\"The auth module uses JWT\",\"category\":\"architecture\",\"scope\":\"project\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"content\":\"...\",\"category\":\"...\",\"scope\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- AGENT_ID=$(echo "$INPUT" | jq -r '.agentId // empty')
11
- CONTENT=$(echo "$INPUT" | jq -r '.content // empty')
12
- CATEGORY=$(echo "$INPUT" | jq -r '.category // empty')
13
- SCOPE=$(echo "$INPUT" | jq -r '.scope // empty')
10
+ AGENT_ID=$(printf '%s' "$INPUT" | jq -r '.agentId // empty')
11
+ CONTENT=$(printf '%s' "$INPUT" | jq -r '.content // empty')
12
+ CATEGORY=$(printf '%s' "$INPUT" | jq -r '.category // empty')
13
+ SCOPE=$(printf '%s' "$INPUT" | jq -r '.scope // empty')
14
14
  require_param "agentId" "$AGENT_ID"
15
15
  require_param "content" "$CONTENT"
16
16
  require_param "category" "$CATEGORY"
17
17
  require_param "scope" "$SCOPE"
18
18
 
19
19
  # #187: Auto-inject projectPath from CREWLY_PROJECT_PATH env var when not provided
20
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
20
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
21
21
  if [ -z "$PROJECT_PATH" ] && [ -n "${CREWLY_PROJECT_PATH:-}" ]; then
22
- INPUT=$(echo "$INPUT" | jq --arg pp "$CREWLY_PROJECT_PATH" '. + {projectPath: $pp}')
22
+ INPUT=$(printf '%s' "$INPUT" | jq --arg pp "$CREWLY_PROJECT_PATH" '. + {projectPath: $pp}')
23
23
  fi
24
24
 
25
25
  # Build body with required and optional fields
26
- BODY=$(echo "$INPUT" | jq '{
26
+ BODY=$(printf '%s' "$INPUT" | jq '{
27
27
  agentId: .agentId,
28
28
  content: .content,
29
29
  category: .category,
@@ -4,23 +4,23 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
6
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"dev-1\",\"status\":\"done\",\"summary\":\"Finished implementing auth module\",\"taskPath\":\"/path/to/task.md\"}'"
7
+ INPUT=$(read_json_input "${1:-}")
8
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"dev-1\",\"status\":\"done\",\"summary\":\"...\"}' or echo '{...}' | execute.sh"
9
9
 
10
- SESSION_NAME=$(echo "$INPUT" | jq -r '.sessionName // empty')
11
- STATUS=$(echo "$INPUT" | jq -r '.status // empty')
12
- SUMMARY=$(echo "$INPUT" | jq -r '.summary // empty')
13
- TASK_PATH=$(echo "$INPUT" | jq -r '.taskPath // empty')
10
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
11
+ STATUS=$(printf '%s' "$INPUT" | jq -r '.status // empty')
12
+ SUMMARY=$(printf '%s' "$INPUT" | jq -r '.summary // empty')
13
+ TASK_PATH=$(printf '%s' "$INPUT" | jq -r '.taskPath // empty')
14
14
  require_param "sessionName" "$SESSION_NAME"
15
15
  require_param "status" "$STATUS"
16
16
  require_param "summary" "$SUMMARY"
17
17
 
18
18
  # Optional structured StatusReport fields (for hierarchical workflows)
19
- TASK_ID=$(echo "$INPUT" | jq -r '.taskId // empty')
20
- PROGRESS=$(echo "$INPUT" | jq -r '.progress // empty')
21
- ARTIFACTS=$(echo "$INPUT" | jq -c '.artifacts // empty')
22
- BLOCKERS=$(echo "$INPUT" | jq -c '.blockers // empty')
23
- USE_STRUCTURED=$(echo "$INPUT" | jq -r '.structured // "false"')
19
+ TASK_ID=$(printf '%s' "$INPUT" | jq -r '.taskId // empty')
20
+ PROGRESS=$(printf '%s' "$INPUT" | jq -r '.progress // empty')
21
+ ARTIFACTS=$(printf '%s' "$INPUT" | jq -c '.artifacts // empty')
22
+ BLOCKERS=$(printf '%s' "$INPUT" | jq -c '.blockers // empty')
23
+ USE_STRUCTURED=$(printf '%s' "$INPUT" | jq -r '.structured // "false"')
24
24
 
25
25
  # Map simple status strings to InProgressTaskStatus values
26
26
  map_status_to_state() {
@@ -93,6 +93,6 @@ fi
93
93
  # Use [COMPLETED] prefix so recall can distinguish completed tasks from other patterns.
94
94
  # This prevents PM from re-delegating tasks that were already done.
95
95
  if [ "$STATUS" = "done" ] && [ -n "$SUMMARY" ]; then
96
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
96
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
97
97
  auto_remember "$SESSION_NAME" "[COMPLETED] Task completed by ${SESSION_NAME}: ${SUMMARY}" "decision" "project" "$PROJECT_PATH"
98
98
  fi
@@ -1,15 +1,16 @@
1
1
  #!/bin/bash
2
- # Send a direct message to another agent's terminal session
2
+ # Send a direct message to another agent's terminal session.
3
+ # Supports: argument, stdin pipe, or @filepath for JSON input (#292, #293).
3
4
  set -euo pipefail
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
6
  source "${SCRIPT_DIR}/../../_common/lib.sh"
6
7
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"message\":\"Can you review my PR?\"}'"
8
+ INPUT=$(read_json_input "${1:-}")
9
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"message\":\"...\"}' or echo '{...}' | execute.sh"
9
10
 
10
11
  # Accept both "to" and "sessionName" for compatibility (#231)
11
- TO=$(echo "$INPUT" | jq -r '.to // .sessionName // empty')
12
- MESSAGE=$(echo "$INPUT" | jq -r '.message // empty')
12
+ TO=$(printf '%s' "$INPUT" | jq -r '.to // .sessionName // empty')
13
+ MESSAGE=$(printf '%s' "$INPUT" | jq -r '.message // empty')
13
14
  require_param "to (or sessionName)" "$TO"
14
15
  require_param "message" "$MESSAGE"
15
16
 
@@ -6,19 +6,19 @@ set -euo pipefail
6
6
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
7
  source "${SCRIPT_DIR}/../_common/lib.sh"
8
8
 
9
- INPUT="${1:-}"
10
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"task\":\"implement feature X\",\"priority\":\"high\",\"projectPath\":\"/path/to/project\",\"monitor\":{\"idleEvent\":true,\"fallbackCheckMinutes\":5}}'"
11
-
12
- TO=$(echo "$INPUT" | jq -r '.to // empty')
13
- TASK=$(echo "$INPUT" | jq -r '.task // empty')
14
- PRIORITY=$(echo "$INPUT" | jq -r '.priority // "normal"')
15
- CONTEXT=$(echo "$INPUT" | jq -r '.context // empty')
16
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
9
+ INPUT=$(read_json_input "${1:-}")
10
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"task\":\"...\"}' or echo '{...}' | execute.sh"
11
+
12
+ TO=$(printf '%s' "$INPUT" | jq -r '.to // empty')
13
+ TASK=$(printf '%s' "$INPUT" | jq -r '.task // empty')
14
+ PRIORITY=$(printf '%s' "$INPUT" | jq -r '.priority // "normal"')
15
+ CONTEXT=$(printf '%s' "$INPUT" | jq -r '.context // empty')
16
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
17
17
  # #150: Task type classification — 'technical' tasks can bypass PM routing
18
- TASK_TYPE=$(echo "$INPUT" | jq -r '.taskType // "general"')
18
+ TASK_TYPE=$(printf '%s' "$INPUT" | jq -r '.taskType // "general"')
19
19
  # #180: Cross-team validation
20
- TEAM_ID=$(echo "$INPUT" | jq -r '.teamId // empty')
21
- FORCE_CROSS_TEAM=$(echo "$INPUT" | jq -r '.forceCrossTeam // "false"')
20
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
21
+ FORCE_CROSS_TEAM=$(printf '%s' "$INPUT" | jq -r '.forceCrossTeam // "false"')
22
22
  require_param "to" "$TO"
23
23
  require_param "task" "$TASK"
24
24
 
@@ -47,18 +47,18 @@ if [ "$TASK_TYPE" = "technical" ] && [ -n "$TEAM_ID" ]; then
47
47
  fi
48
48
 
49
49
  # Structured message parameters (for hierarchical teams)
50
- TITLE=$(echo "$INPUT" | jq -r '.title // empty')
51
- PARENT_TASK_ID=$(echo "$INPUT" | jq -r '.parentTaskId // empty')
52
- EXPECTED_ARTIFACTS=$(echo "$INPUT" | jq -c '.expectedArtifacts // empty')
53
- CONTEXT_FILES=$(echo "$INPUT" | jq -c '.contextFiles // empty')
54
- DEADLINE_HINT=$(echo "$INPUT" | jq -r '.deadlineHint // empty')
55
- USE_STRUCTURED=$(echo "$INPUT" | jq -r '.structured // "false"')
50
+ TITLE=$(printf '%s' "$INPUT" | jq -r '.title // empty')
51
+ PARENT_TASK_ID=$(printf '%s' "$INPUT" | jq -r '.parentTaskId // empty')
52
+ EXPECTED_ARTIFACTS=$(printf '%s' "$INPUT" | jq -c '.expectedArtifacts // empty')
53
+ CONTEXT_FILES=$(printf '%s' "$INPUT" | jq -c '.contextFiles // empty')
54
+ DEADLINE_HINT=$(printf '%s' "$INPUT" | jq -r '.deadlineHint // empty')
55
+ USE_STRUCTURED=$(printf '%s' "$INPUT" | jq -r '.structured // "false"')
56
56
 
57
57
  # Monitor parameters — enabled by default to ensure proactive progress notifications.
58
58
  # Use explicit null-check so that `false` / `0` are respected as opt-out values,
59
59
  # while omitted fields default to enabled (idleEvent=true, fallbackCheckMinutes=5).
60
- MONITOR_IDLE=$(echo "$INPUT" | jq -r 'if .monitor.idleEvent == null then true else .monitor.idleEvent end')
61
- MONITOR_FALLBACK_MINUTES=$(echo "$INPUT" | jq -r 'if .monitor.fallbackCheckMinutes == null then 5 else .monitor.fallbackCheckMinutes end')
60
+ MONITOR_IDLE=$(printf '%s' "$INPUT" | jq -r 'if .monitor.idleEvent == null then true else .monitor.idleEvent end')
61
+ MONITOR_FALLBACK_MINUTES=$(printf '%s' "$INPUT" | jq -r 'if .monitor.fallbackCheckMinutes == null then 5 else .monitor.fallbackCheckMinutes end')
62
62
 
63
63
  # Resolve Crewly root from this script path:
64
64
  # config/skills/orchestrator/delegate-task/execute.sh -> project root
@@ -1,15 +1,16 @@
1
1
  #!/bin/bash
2
- # Send a message to an agent's terminal session
2
+ # Send a message to an agent's terminal session.
3
+ # Supports: argument, stdin pipe, or @filepath for JSON input (#292, #293).
3
4
  set -euo pipefail
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
6
  source "${SCRIPT_DIR}/../_common/lib.sh"
6
7
 
7
- INPUT="${1:-}"
8
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"agent-session\",\"message\":\"hello\"}'"
8
+ INPUT=$(read_json_input "${1:-}")
9
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"agent-session\",\"message\":\"hello\"}' or echo '{...}' | execute.sh"
9
10
 
10
- SESSION_NAME=$(echo "$INPUT" | jq -r '.sessionName // empty')
11
- MESSAGE=$(echo "$INPUT" | jq -r '.message // empty')
12
- FORCE=$(echo "$INPUT" | jq -r '.force // empty')
11
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
12
+ MESSAGE=$(printf '%s' "$INPUT" | jq -r '.message // empty')
13
+ FORCE=$(printf '%s' "$INPUT" | jq -r '.force // empty')
13
14
  require_param "sessionName" "$SESSION_NAME"
14
15
  require_param "message" "$MESSAGE"
15
16
 
@@ -6,17 +6,17 @@ set -euo pipefail
6
6
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
7
  source "${SCRIPT_DIR}/../_common/lib.sh"
8
8
 
9
- INPUT="${1:-}"
10
- [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"worker-session\",\"task\":\"implement feature X\",\"priority\":\"high\",\"teamId\":\"team-123\",\"tlMemberId\":\"tl-member-id\",\"projectPath\":\"/path/to/project\"}'"
11
-
12
- TO=$(echo "$INPUT" | jq -r '.to // empty')
13
- TASK=$(echo "$INPUT" | jq -r '.task // empty')
14
- PRIORITY=$(echo "$INPUT" | jq -r '.priority // "normal"')
15
- CONTEXT=$(echo "$INPUT" | jq -r '.context // empty')
16
- PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // empty')
17
- TEAM_ID=$(echo "$INPUT" | jq -r '.teamId // empty')
18
- TL_MEMBER_ID=$(echo "$INPUT" | jq -r '.tlMemberId // empty')
19
- FROM_SESSION=$(echo "$INPUT" | jq -r '.fromSession // empty')
9
+ INPUT=$(read_json_input "${1:-}")
10
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"worker-session\",\"task\":\"...\"}' or echo '{...}' | execute.sh"
11
+
12
+ TO=$(printf '%s' "$INPUT" | jq -r '.to // empty')
13
+ TASK=$(printf '%s' "$INPUT" | jq -r '.task // empty')
14
+ PRIORITY=$(printf '%s' "$INPUT" | jq -r '.priority // "normal"')
15
+ CONTEXT=$(printf '%s' "$INPUT" | jq -r '.context // empty')
16
+ PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
17
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
18
+ TL_MEMBER_ID=$(printf '%s' "$INPUT" | jq -r '.tlMemberId // empty')
19
+ FROM_SESSION=$(printf '%s' "$INPUT" | jq -r '.fromSession // empty')
20
20
  require_param "to" "$TO"
21
21
  require_param "task" "$TASK"
22
22
 
@@ -93,8 +93,8 @@ if [ -n "$PROJECT_PATH" ]; then
93
93
  fi
94
94
 
95
95
  # Set up idle event subscription for TL monitoring
96
- MONITOR_IDLE=$(echo "$INPUT" | jq -r 'if .monitor.idleEvent == null then true else .monitor.idleEvent end')
97
- MONITOR_FALLBACK_MINUTES=$(echo "$INPUT" | jq -r 'if .monitor.fallbackCheckMinutes == null then 5 else .monitor.fallbackCheckMinutes end')
96
+ MONITOR_IDLE=$(printf '%s' "$INPUT" | jq -r 'if .monitor.idleEvent == null then true else .monitor.idleEvent end')
97
+ MONITOR_FALLBACK_MINUTES=$(printf '%s' "$INPUT" | jq -r 'if .monitor.fallbackCheckMinutes == null then 5 else .monitor.fallbackCheckMinutes end')
98
98
 
99
99
  COLLECTED_SCHEDULE_IDS="[]"
100
100
  COLLECTED_SUBSCRIPTION_IDS="[]"
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/monitoring/terminal.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAW9C;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BrF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA6C9E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA+EhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA0J/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFlF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFhF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD5E;AAiCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAwNjG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFjG"}
1
+ {"version":3,"file":"terminal.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/monitoring/terminal.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiC9C;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BrF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA6C9E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA+EhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4J/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFlF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFhF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD5E;AAiCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAyNjG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFjG"}
@@ -18,6 +18,26 @@ import { readFile } from 'fs/promises';
18
18
  import { PtySessionBackend } from '../../services/session/pty/pty-session-backend.js';
19
19
  import { InProcessLogBuffer } from '../../services/agent/crewly-agent/in-process-log-buffer.js';
20
20
  import { ADAPTIVE_HEARTBEAT_DEFAULTS } from '../../services/agent/adaptive-heartbeat.service.js';
21
+ /**
22
+ * Bracketed paste mode markers.
23
+ * Wrapping terminal input in these markers tells TUI applications (Gemini CLI,
24
+ * Codex CLI, etc.) to treat the content as pasted text rather than typed input.
25
+ * This prevents special shell characters like (), $, `, etc. from triggering
26
+ * shell mode or being interpreted as key sequences (#293).
27
+ */
28
+ const BRACKETED_PASTE_START = '\x1b[200~';
29
+ const BRACKETED_PASTE_END = '\x1b[201~';
30
+ /**
31
+ * Wrap a message in bracketed paste mode markers for safe delivery to TUI terminals.
32
+ * This ensures parentheses, quotes, dollar signs, and other shell metacharacters
33
+ * are treated as literal text by the receiving terminal application (#292, #293).
34
+ *
35
+ * @param text - The raw message text
36
+ * @returns The text wrapped in bracketed paste markers
37
+ */
38
+ function wrapInBracketedPaste(text) {
39
+ return `${BRACKETED_PASTE_START}${text}${BRACKETED_PASTE_END}`;
40
+ }
21
41
  /** Logger instance for terminal controller */
22
42
  const logger = LoggerService.getInstance().createComponentLogger('TerminalController');
23
43
  /**
@@ -316,8 +336,10 @@ export async function writeToSession(req, res) {
316
336
  });
317
337
  }
318
338
  }
319
- // Two-step write: text first, then Enter separately
320
- session.write(dataStr);
339
+ // Two-step write: text first (wrapped in bracketed paste), then Enter separately.
340
+ // Bracketed paste markers prevent TUI runtimes from interpreting special
341
+ // characters like (), $, ` as shell commands (#293).
342
+ session.write(wrapInBracketedPaste(dataStr));
321
343
  // Scale delay based on message length for TUI paste processing
322
344
  const enterDelay = Math.min(1000 + Math.ceil(dataStr.length / 10), 5000);
323
345
  await new Promise(resolve => setTimeout(resolve, enterDelay));
@@ -766,12 +788,13 @@ export async function deliverMessage(req, res) {
766
788
  return;
767
789
  }
768
790
  // For TUI runtimes (Gemini CLI, Codex CLI), use two-step write:
769
- // text first, then Enter separately. Bundling message+\r in a single
770
- // write causes the \r to be consumed by bracketed paste mode (#130).
791
+ // text first (in bracketed paste mode), then Enter separately.
792
+ // Bracketed paste markers prevent TUI runtimes from interpreting
793
+ // special characters like (), $, ` as shell commands (#130, #293).
771
794
  const isTuiRuntime = resolvedRuntimeType &&
772
795
  resolvedRuntimeType !== RUNTIME_TYPES.CLAUDE_CODE;
773
796
  if (isTuiRuntime) {
774
- session.write(message);
797
+ session.write(wrapInBracketedPaste(message));
775
798
  const enterDelay = Math.min(1000 + Math.ceil(message.length / 10), 5000);
776
799
  await new Promise(resolve => setTimeout(resolve, enterDelay));
777
800
  session.write('\r');
@@ -781,7 +804,7 @@ export async function deliverMessage(req, res) {
781
804
  else {
782
805
  // Claude Code also benefits from two-step write: bundling message+\r
783
806
  // in a single write can be swallowed by bracketed paste mode.
784
- session.write(message);
807
+ session.write(wrapInBracketedPaste(message));
785
808
  const ccEnterDelay = Math.min(1000 + Math.ceil(message.length / 10), 5000);
786
809
  await new Promise(resolve => setTimeout(resolve, ccEnterDelay));
787
810
  session.write('\r');