Files
stack/packages/skills/gantt/gantt-api.sh
Jason Woltje 632b8fb2d2 fix: address code review feedback
- Fix incorrect API endpoint paths (removed /api prefix)
- Improve TypeScript strict typing with explicit metadata interfaces
- Update SKILL.md with clear trigger phrases and examples
- Fix README installation path reference
- Add clarification about API URL format (no /api suffix needed)
- Export new metadata type interfaces
2026-01-29 21:23:36 -06:00

206 lines
5.3 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# gantt-api.sh - Helper script for Mosaic Stack Gantt/Project API queries
#
set -euo pipefail
# Configuration from environment
API_URL="${MOSAIC_API_URL:-http://localhost:3000}"
WORKSPACE_ID="${MOSAIC_WORKSPACE_ID:-}"
API_TOKEN="${MOSAIC_API_TOKEN:-}"
# Check required environment variables
if [[ -z "$WORKSPACE_ID" ]]; then
echo "Error: MOSAIC_WORKSPACE_ID environment variable not set" >&2
exit 1
fi
if [[ -z "$API_TOKEN" ]]; then
echo "Error: MOSAIC_API_TOKEN environment variable not set" >&2
exit 1
fi
# Helper function to make API requests
api_request() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="${API_URL}${endpoint}"
local curl_args=(
-X "$method"
-H "Content-Type: application/json"
-H "X-Workspace-Id: $WORKSPACE_ID"
-H "Authorization: Bearer $API_TOKEN"
-s
)
if [[ -n "$data" ]]; then
curl_args+=(-d "$data")
fi
curl "${curl_args[@]}" "$url"
}
# List all projects
list_projects() {
local page="${1:-1}"
local limit="${2:-50}"
api_request GET "/projects?page=$page&limit=$limit" | jq .
}
# Get a single project with tasks
get_project() {
local project_id="$1"
api_request GET "/projects/$project_id" | jq .
}
# Get tasks with optional filters
get_tasks() {
local project_id="${1:-}"
local filters=""
if [[ -n "$project_id" ]]; then
filters="projectId=$project_id"
fi
api_request GET "/tasks?$filters" | jq .
}
# Get a single task
get_task() {
local task_id="$1"
api_request GET "/tasks/$task_id" | jq .
}
# Get dependency chain for a task
get_dependencies() {
local task_id="$1"
# Get the task
local task_json
task_json=$(get_task "$task_id")
# Extract dependency IDs from metadata
local dep_ids
dep_ids=$(echo "$task_json" | jq -r '.metadata.dependencies // [] | .[]')
if [[ -z "$dep_ids" ]]; then
echo "Task has no dependencies"
return
fi
echo "Dependencies for task: $(echo "$task_json" | jq -r '.title')"
echo ""
# Fetch each dependency
while IFS= read -r dep_id; do
if [[ -n "$dep_id" ]]; then
local dep_task
dep_task=$(get_task "$dep_id")
echo "- $(echo "$dep_task" | jq -r '.title') [$(echo "$dep_task" | jq -r '.status')]"
echo " Due: $(echo "$dep_task" | jq -r '.dueDate // "No due date"')"
fi
done <<< "$dep_ids"
}
# Calculate critical path for a project
critical_path() {
local project_id="$1"
# Get all tasks for the project
local tasks_json
tasks_json=$(get_tasks "$project_id")
# Use jq to build dependency graph and find longest path
echo "$tasks_json" | jq -r '
.data as $tasks |
# Build adjacency list
($tasks | map({
id: .id,
title: .title,
status: .status,
dueDate: .dueDate,
dependencies: (.metadata.dependencies // [])
})) as $nodes |
# Find tasks with no dependencies (starting points)
($nodes | map(select(.dependencies | length == 0))) as $starts |
# Display structure
"Critical Path Analysis\n" +
"======================\n\n" +
"Starting tasks (no dependencies):\n" +
($starts | map("- \(.title) [\(.status)]") | join("\n")) +
"\n\nAll tasks with dependencies:\n" +
($nodes | map(
select(.dependencies | length > 0) |
"- \(.title) [\(.status)]\n Depends on: \(.dependencies | join(", "))"
) | join("\n"))
'
}
# Main command dispatcher
main() {
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <command> [args...]" >&2
echo "" >&2
echo "Commands:" >&2
echo " projects - List all projects" >&2
echo " project <id> - Get project details" >&2
echo " tasks [project-id] - Get tasks (optionally filtered by project)" >&2
echo " task <id> - Get task details" >&2
echo " dependencies <id> - Get dependency chain for task" >&2
echo " critical-path <id> - Calculate critical path for project" >&2
exit 1
fi
local command="$1"
shift
case "$command" in
projects)
list_projects "$@"
;;
project)
if [[ $# -lt 1 ]]; then
echo "Error: project command requires project ID" >&2
exit 1
fi
get_project "$1"
;;
tasks)
get_tasks "$@"
;;
task)
if [[ $# -lt 1 ]]; then
echo "Error: task command requires task ID" >&2
exit 1
fi
get_task "$1"
;;
dependencies)
if [[ $# -lt 1 ]]; then
echo "Error: dependencies command requires task ID" >&2
exit 1
fi
get_dependencies "$1"
;;
critical-path)
if [[ $# -lt 1 ]]; then
echo "Error: critical-path command requires project ID" >&2
exit 1
fi
critical_path "$1"
;;
*)
echo "Error: unknown command '$command'" >&2
exit 1
;;
esac
}
main "$@"