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.
- package/config/skills/_common/lib.sh +47 -0
- package/config/skills/agent/core/accept-task/execute.sh +6 -6
- package/config/skills/agent/core/get-my-context/execute.sh +5 -5
- package/config/skills/agent/core/get-my-tasks/execute.sh +4 -4
- package/config/skills/agent/core/recall/execute.sh +5 -5
- package/config/skills/agent/core/record-learning/execute.sh +7 -7
- package/config/skills/agent/core/register-self/execute.sh +6 -6
- package/config/skills/agent/core/remember/execute.sh +9 -9
- package/config/skills/agent/core/report-status/execute.sh +12 -12
- package/config/skills/agent/core/send-message/execute.sh +6 -5
- package/config/skills/orchestrator/delegate-task/execute.sh +19 -19
- package/config/skills/orchestrator/send-message/execute.sh +7 -6
- package/config/skills/team-leader/delegate-task/execute.sh +13 -13
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +29 -6
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/system/scheduler.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/system/scheduler.controller.js +10 -2
- package/dist/backend/backend/src/controllers/system/scheduler.controller.js.map +1 -1
- package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/session-command-helper.js +11 -5
- package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts +8 -2
- package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/scheduler.service.js +30 -6
- package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
- package/frontend/dist/assets/index-27a2c65e.js +5215 -0
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- 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
|
|
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=$(
|
|
10
|
+
SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
|
|
11
11
|
require_param "sessionName" "$SESSION_NAME"
|
|
12
12
|
|
|
13
|
-
TEAM_MEMBER_ID=$(
|
|
14
|
-
PROJECT_PATH=$(
|
|
15
|
-
TASK_GROUP=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"
|
|
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=$(
|
|
11
|
-
AGENT_ROLE=$(
|
|
12
|
-
PROJECT_PATH=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"
|
|
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=$(
|
|
11
|
-
PROJECT_PATH=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"context\":\"
|
|
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=$(
|
|
11
|
-
CONTEXT=$(
|
|
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=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"agentRole\":\"developer\",\"projectPath\":\"
|
|
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=$(
|
|
11
|
-
AGENT_ROLE=$(
|
|
12
|
-
PROJECT_PATH=$(
|
|
13
|
-
LEARNING=$(
|
|
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=$(
|
|
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
|
|
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=$(
|
|
12
|
-
SESSION_NAME=$(
|
|
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=$(
|
|
18
|
-
TEAM_MEMBER_ID=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"agentId\":\"dev-1\",\"content\":\"
|
|
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=$(
|
|
11
|
-
CONTENT=$(
|
|
12
|
-
CATEGORY=$(
|
|
13
|
-
SCOPE=$(
|
|
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=$(
|
|
20
|
+
PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
|
|
21
21
|
if [ -z "$PROJECT_PATH" ] && [ -n "${CREWLY_PROJECT_PATH:-}" ]; then
|
|
22
|
-
INPUT=$(
|
|
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=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sessionName\":\"dev-1\",\"status\":\"done\",\"summary\":\"
|
|
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=$(
|
|
11
|
-
STATUS=$(
|
|
12
|
-
SUMMARY=$(
|
|
13
|
-
TASK_PATH=$(
|
|
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=$(
|
|
20
|
-
PROGRESS=$(
|
|
21
|
-
ARTIFACTS=$(
|
|
22
|
-
BLOCKERS=$(
|
|
23
|
-
USE_STRUCTURED=$(
|
|
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=$(
|
|
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
|
|
8
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"message\":\"
|
|
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=$(
|
|
12
|
-
MESSAGE=$(
|
|
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
|
|
10
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"agent-session\",\"task\":\"
|
|
11
|
-
|
|
12
|
-
TO=$(
|
|
13
|
-
TASK=$(
|
|
14
|
-
PRIORITY=$(
|
|
15
|
-
CONTEXT=$(
|
|
16
|
-
PROJECT_PATH=$(
|
|
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=$(
|
|
18
|
+
TASK_TYPE=$(printf '%s' "$INPUT" | jq -r '.taskType // "general"')
|
|
19
19
|
# #180: Cross-team validation
|
|
20
|
-
TEAM_ID=$(
|
|
21
|
-
FORCE_CROSS_TEAM=$(
|
|
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=$(
|
|
51
|
-
PARENT_TASK_ID=$(
|
|
52
|
-
EXPECTED_ARTIFACTS=$(
|
|
53
|
-
CONTEXT_FILES=$(
|
|
54
|
-
DEADLINE_HINT=$(
|
|
55
|
-
USE_STRUCTURED=$(
|
|
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=$(
|
|
61
|
-
MONITOR_FALLBACK_MINUTES=$(
|
|
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
|
|
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=$(
|
|
11
|
-
MESSAGE=$(
|
|
12
|
-
FORCE=$(
|
|
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
|
|
10
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"to\":\"worker-session\",\"task\":\"
|
|
11
|
-
|
|
12
|
-
TO=$(
|
|
13
|
-
TASK=$(
|
|
14
|
-
PRIORITY=$(
|
|
15
|
-
CONTEXT=$(
|
|
16
|
-
PROJECT_PATH=$(
|
|
17
|
-
TEAM_ID=$(
|
|
18
|
-
TL_MEMBER_ID=$(
|
|
19
|
-
FROM_SESSION=$(
|
|
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=$(
|
|
97
|
-
MONITOR_FALLBACK_MINUTES=$(
|
|
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;
|
|
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
|
-
|
|
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.
|
|
770
|
-
//
|
|
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');
|