generated from coulomb/repo-seed
Refactor issue-facade to conform to the new ReusableCapabilitiesArchitecture specification, improving discoverability and establishing consistent patterns for capability integration. Architecture Changes: - Rename .feedback/ → feedback/ (visible user interface) - Rename CAPABILITY.yaml → CAPABILITY-issue-tracking.yaml (explicit family) - Keep .capability/ hidden (evolving implementation infrastructure) File Updates: - Updated all documentation references (.feedback → feedback) - Updated .capability/feedback script paths - Updated Makefile, README.md, CLAUDE.md, examples - Fixed CAPABILITY.yaml → CAPABILITY-issue-tracking.yaml references New Tools: - Created .capability/detach script for clean capability removal - Supports git submodule and directory-based integrations - Generates detachment manifest for re-integration guidance Rationale: - feedback/ is visible: encourages user participation, shows capability identity - .capability/ is hidden: implementation details that will evolve - CAPABILITY-<family>.yaml: explicit family declaration, supports multiple capabilities per repo - Underscore prefix pattern: flatter hierarchy, clear signal of integration This aligns with the principle that capabilities are conceptual units designed for natural language integration by devhumans and devagents, not just technical libraries. See ReusableCapabilitiesArchitecture.md for complete specification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
392 lines
10 KiB
Bash
Executable File
392 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# feedback - Universal feedback submission tool for capabilities
|
|
#
|
|
# Usage:
|
|
# feedback submit "Your feedback text"
|
|
# feedback submit path/to/feedback.md
|
|
# feedback submit "Text" --category=bug --contact=me@email.com
|
|
# feedback list [--reviewed] [--archived]
|
|
# feedback show <filename>
|
|
#
|
|
# This tool can be copied to any capability that wants to use the feedback pattern.
|
|
|
|
set -e
|
|
|
|
FEEDBACK_DIR="feedback"
|
|
INBOUND_DIR="${FEEDBACK_DIR}/inbound"
|
|
REVIEWED_DIR="${FEEDBACK_DIR}/reviewed"
|
|
ARCHIVED_DIR="${FEEDBACK_DIR}/archived"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Helper functions
|
|
error() {
|
|
echo -e "${RED}Error: $1${NC}" >&2
|
|
exit 1
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}$1${NC}"
|
|
}
|
|
|
|
info() {
|
|
echo -e "${BLUE}$1${NC}"
|
|
}
|
|
|
|
warn() {
|
|
echo -e "${YELLOW}$1${NC}"
|
|
}
|
|
|
|
# Check if we're in a capability directory
|
|
check_capability_dir() {
|
|
if [ ! -d "$FEEDBACK_DIR" ]; then
|
|
error "Not in a capability directory with feedback support.\n Looking for: $FEEDBACK_DIR/\n Run from capability root or initialize with: mkdir -p $INBOUND_DIR"
|
|
fi
|
|
}
|
|
|
|
# Initialize feedback directories
|
|
init_dirs() {
|
|
mkdir -p "$INBOUND_DIR" "$REVIEWED_DIR" "$ARCHIVED_DIR"
|
|
}
|
|
|
|
# Generate metadata
|
|
generate_metadata() {
|
|
local category="${1:-}"
|
|
local contact="${2:-}"
|
|
|
|
local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
local git_repo=$(git rev-parse --show-toplevel 2>/dev/null || echo "unknown")
|
|
local git_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
local python_version=$(python3 --version 2>/dev/null | cut -d' ' -f2 || echo "unknown")
|
|
|
|
# Try to find capability version
|
|
local cap_version="unknown"
|
|
if [ -f "CAPABILITY.yaml" ]; then
|
|
cap_version=$(grep "^version:" CAPABILITY.yaml | awk '{print $2}' | tr -d '"' || echo "unknown")
|
|
fi
|
|
|
|
cat <<EOF
|
|
---
|
|
timestamp: $timestamp
|
|
source: cli
|
|
EOF
|
|
|
|
[ -n "$category" ] && echo "category: $category"
|
|
[ -n "$contact" ] && echo "contact: $contact"
|
|
|
|
cat <<EOF
|
|
context:
|
|
git_repo: $git_repo
|
|
git_branch: $git_branch
|
|
capability_version: $cap_version
|
|
python_version: $python_version
|
|
---
|
|
|
|
EOF
|
|
}
|
|
|
|
# Submit feedback
|
|
submit_feedback() {
|
|
local content="$1"
|
|
local category="${2:-}"
|
|
local contact="${3:-}"
|
|
|
|
init_dirs
|
|
|
|
local timestamp=$(date +%Y%m%d-%H%M%S)
|
|
local hash=$(echo "$content" | md5sum 2>/dev/null | cut -c1-8 || echo "$(date +%N)")
|
|
local filename="${INBOUND_DIR}/${timestamp}-${hash}.md"
|
|
|
|
# Check if content is a file
|
|
if [ -f "$content" ]; then
|
|
info "Submitting feedback from file: $content"
|
|
{
|
|
generate_metadata "$category" "$contact"
|
|
cat "$content"
|
|
} > "$filename"
|
|
else
|
|
info "Submitting text feedback"
|
|
{
|
|
generate_metadata "$category" "$contact"
|
|
echo "$content"
|
|
} > "$filename"
|
|
fi
|
|
|
|
success "✓ Feedback submitted: $filename"
|
|
echo ""
|
|
info "Your feedback has been recorded and will be reviewed by the capability maintainers."
|
|
echo ""
|
|
echo "To view: feedback show $(basename "$filename")"
|
|
}
|
|
|
|
# List feedback
|
|
list_feedback() {
|
|
local dir="$INBOUND_DIR"
|
|
local label="Inbound"
|
|
|
|
case "${1:-}" in
|
|
--reviewed)
|
|
dir="$REVIEWED_DIR"
|
|
label="Reviewed"
|
|
;;
|
|
--archived)
|
|
dir="$ARCHIVED_DIR"
|
|
label="Archived"
|
|
;;
|
|
esac
|
|
|
|
check_capability_dir
|
|
|
|
if [ ! -d "$dir" ]; then
|
|
warn "No feedback directory: $dir"
|
|
return
|
|
fi
|
|
|
|
local count=$(ls -1 "$dir" 2>/dev/null | wc -l)
|
|
|
|
echo -e "${BLUE}=== $label Feedback ($count) ===${NC}"
|
|
echo ""
|
|
|
|
if [ "$count" -eq 0 ]; then
|
|
echo " (none)"
|
|
return
|
|
fi
|
|
|
|
ls -lt "$dir" | tail -n +2 | while read -r line; do
|
|
local file=$(echo "$line" | awk '{print $NF}')
|
|
local date=$(echo "$line" | awk '{print $6, $7, $8}')
|
|
|
|
# Try to extract category from metadata
|
|
local category=""
|
|
if [ -f "$dir/$file" ]; then
|
|
category=$(grep "^category:" "$dir/$file" | awk '{print $2}' || echo "")
|
|
fi
|
|
|
|
# Colorize based on category
|
|
local color=$NC
|
|
case "$category" in
|
|
bug) color=$RED ;;
|
|
feature) color=$GREEN ;;
|
|
improvement) color=$YELLOW ;;
|
|
esac
|
|
|
|
echo -e " ${color}${file}${NC}"
|
|
[ -n "$category" ] && echo " Category: $category"
|
|
echo " Date: $date"
|
|
echo ""
|
|
done
|
|
}
|
|
|
|
# Show specific feedback
|
|
show_feedback() {
|
|
local filename="$1"
|
|
|
|
check_capability_dir
|
|
|
|
# Search in all directories
|
|
local filepath=""
|
|
for dir in "$INBOUND_DIR" "$REVIEWED_DIR" "$ARCHIVED_DIR"; do
|
|
if [ -f "$dir/$filename" ]; then
|
|
filepath="$dir/$filename"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$filepath" ]; then
|
|
error "Feedback not found: $filename"
|
|
fi
|
|
|
|
echo -e "${BLUE}=== Feedback: $filename ===${NC}"
|
|
echo ""
|
|
cat "$filepath"
|
|
}
|
|
|
|
# Review feedback (move to reviewed)
|
|
review_feedback() {
|
|
local filename="$1"
|
|
local create_issue="${2:-}"
|
|
|
|
check_capability_dir
|
|
init_dirs
|
|
|
|
if [ ! -f "$INBOUND_DIR/$filename" ]; then
|
|
error "Feedback not found in inbound: $filename"
|
|
fi
|
|
|
|
if [ "$create_issue" = "--create-issue" ]; then
|
|
info "Creating issue from feedback..."
|
|
|
|
# Extract title and body
|
|
local title=$(head -20 "$INBOUND_DIR/$filename" | grep -v "^---" | grep -v "^$" | head -1 | sed 's/^# *//')
|
|
local body=$(cat "$INBOUND_DIR/$filename")
|
|
|
|
if command -v issue &> /dev/null; then
|
|
issue create "$title" --description "$body" --label=feedback
|
|
success "✓ Issue created"
|
|
else
|
|
warn "Issue command not found. Please create issue manually."
|
|
fi
|
|
fi
|
|
|
|
mv "$INBOUND_DIR/$filename" "$REVIEWED_DIR/$filename"
|
|
success "✓ Feedback moved to reviewed: $filename"
|
|
}
|
|
|
|
# Archive feedback
|
|
archive_feedback() {
|
|
local filename="$1"
|
|
|
|
check_capability_dir
|
|
init_dirs
|
|
|
|
# Try both inbound and reviewed
|
|
if [ -f "$INBOUND_DIR/$filename" ]; then
|
|
mv "$INBOUND_DIR/$filename" "$ARCHIVED_DIR/$filename"
|
|
elif [ -f "$REVIEWED_DIR/$filename" ]; then
|
|
mv "$REVIEWED_DIR/$filename" "$ARCHIVED_DIR/$filename"
|
|
else
|
|
error "Feedback not found: $filename"
|
|
fi
|
|
|
|
success "✓ Feedback archived: $filename"
|
|
}
|
|
|
|
# Show usage
|
|
usage() {
|
|
cat <<EOF
|
|
feedback - Universal feedback submission tool
|
|
|
|
Usage:
|
|
feedback submit <content> [options] Submit feedback
|
|
feedback list [--reviewed|--archived] List feedback
|
|
feedback show <filename> Show specific feedback
|
|
feedback review <filename> [options] Mark feedback as reviewed (maintainers)
|
|
feedback archive <filename> Archive feedback (maintainers)
|
|
feedback stats Show feedback statistics
|
|
feedback help Show this help
|
|
|
|
Submit Options:
|
|
--category=<type> Category: bug, feature, improvement, question, other
|
|
--contact=<email> Optional contact for follow-up
|
|
|
|
Review Options:
|
|
--create-issue Create an issue from the feedback
|
|
|
|
Examples:
|
|
# Quick text feedback
|
|
feedback submit "The sync command is slow with 1000+ issues"
|
|
|
|
# Feedback from file
|
|
feedback submit my-feedback.md
|
|
|
|
# With metadata
|
|
feedback submit "Bug: crashes on startup" --category=bug --contact=me@email.com
|
|
|
|
# List feedback
|
|
feedback list
|
|
feedback list --reviewed
|
|
|
|
# Show specific feedback
|
|
feedback show 20251217-103045-abc12345.md
|
|
|
|
# Review and create issue (maintainers)
|
|
feedback review 20251217-103045-abc12345.md --create-issue
|
|
|
|
# Archive (maintainers)
|
|
feedback archive 20251217-103045-abc12345.md
|
|
|
|
For more information, see feedback/README.md
|
|
EOF
|
|
}
|
|
|
|
# Show statistics
|
|
show_stats() {
|
|
check_capability_dir
|
|
|
|
local inbound=$(ls -1 "$INBOUND_DIR" 2>/dev/null | wc -l)
|
|
local reviewed=$(ls -1 "$REVIEWED_DIR" 2>/dev/null | wc -l)
|
|
local archived=$(ls -1 "$ARCHIVED_DIR" 2>/dev/null | wc -l)
|
|
local total=$((inbound + reviewed + archived))
|
|
|
|
echo -e "${BLUE}=== Feedback Statistics ===${NC}"
|
|
echo ""
|
|
echo " Pending: $inbound"
|
|
echo " Reviewed: $reviewed"
|
|
echo " Archived: $archived"
|
|
echo " ─────────────"
|
|
echo " Total: $total"
|
|
echo ""
|
|
|
|
if [ "$inbound" -gt 0 ]; then
|
|
warn "⚠ $inbound feedback items awaiting review"
|
|
else
|
|
success "✓ No pending feedback"
|
|
fi
|
|
}
|
|
|
|
# Main command dispatcher
|
|
main() {
|
|
local command="${1:-help}"
|
|
shift || true
|
|
|
|
case "$command" in
|
|
submit)
|
|
local content="${1:-}"
|
|
[ -z "$content" ] && error "Usage: feedback submit <content|file> [--category=TYPE] [--contact=EMAIL]"
|
|
|
|
local category=""
|
|
local contact=""
|
|
|
|
# Parse options
|
|
shift || true
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--category=*)
|
|
category="${1#--category=}"
|
|
;;
|
|
--contact=*)
|
|
contact="${1#--contact=}"
|
|
;;
|
|
*)
|
|
warn "Unknown option: $1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
submit_feedback "$content" "$category" "$contact"
|
|
;;
|
|
list)
|
|
list_feedback "$@"
|
|
;;
|
|
show)
|
|
[ -z "${1:-}" ] && error "Usage: feedback show <filename>"
|
|
show_feedback "$1"
|
|
;;
|
|
review)
|
|
[ -z "${1:-}" ] && error "Usage: feedback review <filename> [--create-issue]"
|
|
review_feedback "$1" "${2:-}"
|
|
;;
|
|
archive)
|
|
[ -z "${1:-}" ] && error "Usage: feedback archive <filename>"
|
|
archive_feedback "$1"
|
|
;;
|
|
stats)
|
|
show_stats
|
|
;;
|
|
help|--help|-h)
|
|
usage
|
|
;;
|
|
*)
|
|
error "Unknown command: $command\n\n$(usage)"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|