Files
issue-core/.capability/feedback
tegwick 1627fd9673 feat: implement reusable feedback capability for continuous improvement
Add comprehensive feedback system that enables lightweight, unstructured feedback
collection from users of the issue-facade capability, establishing a continuous
improvement loop grounded in real-world usage.

Core Components:
- .feedback/ directory structure (inbound, reviewed, archived)
- Standalone CLI tool (.capability/feedback) for submission and management
- Comprehensive documentation (.feedback/README.md)
- Integration examples and usage guides

Key Features:
- Multiple submission methods (CLI, Makefile, direct file drop)
- No structure imposement - accepts any text/markdown format
- Automatic metadata capture (timestamp, git context, version)
- Maintainer workflow (list, review, archive, create issues)
- Colored terminal output for better UX
- Future-ready for API endpoint evolution

Integration:
- Updated CAPABILITY.yaml with feedback section
- Enhanced CLAUDE.md with comprehensive integration guide
- Added Makefile commands (feedback, feedback-list, feedback-stats, etc.)
- Created detailed usage examples (examples/feedback-example.md)

Design Philosophy:
- Capability-agnostic pattern (reusable across all markitect capabilities)
- Decentralized (each capability owns its feedback)
- Flexible (no required formats or fields)
- Durable (plain markdown files, git-tracked)
- Actionable (feedback lives where maintainers work)
- Scalable (works for 1 user or 1000 users)

Feedback Submission Examples:
  ./.capability/feedback submit "Your feedback"
  make feedback MSG="Your feedback"
  echo "Feedback" > .feedback/inbound/$(date +%Y%m%d)-feedback.md

Maintainer Workflow:
  make feedback-list                    # List pending
  make feedback-stats                   # Show statistics
  make feedback-review-issue FILE=xxx   # Review and create issue

This establishes a robust continuous improvement loop:
User Experience → Feedback → Review → Action → Improved Capability

The pattern is designed to be copied to any capability in the markitect
project, providing consistent feedback collection across all capabilities.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 21:09:36 +01:00

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 "$@"