From aeef799afda1e10d27bd152344e49365e3923f8d Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 10:08:22 -0700 Subject: [PATCH 01/21] Move docs generation files from fork to branch --- docs-generation/create-docs-pr.sh | 30 ++++++++++++++++++++ docs-generation/read-pr.sh | 24 ++++++++++++++++ docs-generation/update-docs.sh | 14 ++++++++++ scripts/docs-generator.yaml | 46 +++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100755 docs-generation/create-docs-pr.sh create mode 100755 docs-generation/read-pr.sh create mode 100755 docs-generation/update-docs.sh create mode 100644 scripts/docs-generator.yaml diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh new file mode 100755 index 0000000000..2a0480ccd9 --- /dev/null +++ b/docs-generation/create-docs-pr.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +PR_NUMBER=$1 +BRANCH_NAME="docs-update-pr-$PR_NUMBER" + +# Ensure we have changes to merge +if [ -z "$(git status --porcelain)" ]; then + echo "No changes to commit" + exit 0 +fi + +git config user.name "docs-generator[bot]" +git config user.email "docs-generator[bot]@amazon.com" + +# Create branch and push +git checkout -b "$BRANCH_NAME" +git add . +git commit -m "Update docs based on PR #$PR_NUMBER + +Auto-generated by Q" + +git push origin "$BRANCH_NAME" + +# Create PR +gh pr create \ + --title "Update docs based on PR #$PR_NUMBER" \ + --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ + --base main \ + --head "$BRANCH_NAME" diff --git a/docs-generation/read-pr.sh b/docs-generation/read-pr.sh new file mode 100755 index 0000000000..766d32f804 --- /dev/null +++ b/docs-generation/read-pr.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +PR_NUMBER=$1 + +# Add PR information +echo "====== PR Information ======\n" > $PR_FILE +gh pr view $PR_NUMBER --json title,body --jq '"Title: " + .title + "\nDescription: " + .body' >> $PR_FILE + +# Include updated files +echo -e "\n====== Updated files ======\n" >> $PR_FILE +gh pr view $PR_NUMBER --json files --jq ".files[].path" | while read file; do + if [ -f "$file" ]; then + echo "---- $file ----" >> $PR_FILE + cat "$file" >> $PR_FILE + echo -e "\n" >> $PR_FILE + fi +done + + + + + + \ No newline at end of file diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh new file mode 100755 index 0000000000..5a468d828a --- /dev/null +++ b/docs-generation/update-docs.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +if [ ! -f "$PR_FILE" ]; then + echo "PR file not found, aborting" + exit 1 +fi + +PROMPT="Before making any changes, read the 'docs' directory for the project's current +documentation. Then read 'pr-contents.txt' to see the contents of the current PR.\n\n +After reading both the directory and the PR file, update the files in the 'docs' directory +with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." + +echo -e $PROMPT | cargo run --bin chat_cli -- chat --non-interactive diff --git a/scripts/docs-generator.yaml b/scripts/docs-generator.yaml new file mode 100644 index 0000000000..cda5852f64 --- /dev/null +++ b/scripts/docs-generator.yaml @@ -0,0 +1,46 @@ +name: docs-generator + +on: + workflow_dispatch: + inputs: + pr_number: + description: 'Number of PR to document' + required: true + type: string + +jobs: + generate_docs: + runs-on: ubuntu-latest + env: + PR_FILE: "pr-contents.txt" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source ~/.cargo/env + + - name: Make scripts executable + run: | + chmod +x docs-generation/create-docs-pr.sh + chmod +x docs-generation/read-pr.sh + chmod +x docs-generation/update-docs.sh + + - name: Generate PR contents file + run: bash docs-generation/read-pr.sh ${{ inputs.pr_number }} + + # For testing purposes, we're building from source + - name: Update docs + run: bash docs-generation/update-docs.sh + + - name: Create PR + if: success() + run: bash docs-generation/create-docs-pr.sh ${{ inputs.pr_number }} + + + + + + \ No newline at end of file From 80dbe74c5780b3c2b7fccdfe5628355c6b7cc043 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 10:53:13 -0700 Subject: [PATCH 02/21] Add credentials to docs generator --- .../workflows}/docs-generator.yaml | 30 ++++++++++++++----- docs-generation/update-docs.sh | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) rename {scripts => .github/workflows}/docs-generator.yaml (55%) diff --git a/scripts/docs-generator.yaml b/.github/workflows/docs-generator.yaml similarity index 55% rename from scripts/docs-generator.yaml rename to .github/workflows/docs-generator.yaml index cda5852f64..1c1d33ad09 100644 --- a/scripts/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -12,26 +12,42 @@ jobs: generate_docs: runs-on: ubuntu-latest env: + AMAZON_Q_SIGV4: 1 + CHAT_DOWNLOAD_ROLE_ARN: ${{ secrets.CHAT_DOWNLOAD_ROLE_ARN }} + CHAT_BUILD_BUCKET_NAME: ${{ secrets.CHAT_BUILD_BUCKET_NAME }} PR_FILE: "pr-contents.txt" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + GIT_HASH: "latest" + permissions: + id-token: write + contents: write + pull-requests: write + steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - source ~/.cargo/env + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_TB_ROLE }} + aws-region: us-east-1 + - name: Run setup_amazon_q.sh from terminal-bench + run: | + cd terminal-bench-testing + chmod +x setup_amazon_q.sh + bash setup_amazon_q.sh + - name: Make scripts executable run: | chmod +x docs-generation/create-docs-pr.sh chmod +x docs-generation/read-pr.sh chmod +x docs-generation/update-docs.sh + - name: Checkout repository + uses: actions/checkout@v4 + - name: Generate PR contents file run: bash docs-generation/read-pr.sh ${{ inputs.pr_number }} - # For testing purposes, we're building from source - name: Update docs run: bash docs-generation/update-docs.sh diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 5a468d828a..e8ac9a28de 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,4 +11,4 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." -echo -e $PROMPT | cargo run --bin chat_cli -- chat --non-interactive +echo -e $PROMPT | qchat chat --non-interactive From eb9734c50c162e25cdabddf7aa2971a1863375b4 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 11:15:42 -0700 Subject: [PATCH 03/21] Reorder checkout step --- .github/workflows/docs-generator.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 1c1d33ad09..0ad61033e8 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -24,6 +24,9 @@ jobs: pull-requests: write steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -42,9 +45,6 @@ jobs: chmod +x docs-generation/read-pr.sh chmod +x docs-generation/update-docs.sh - - name: Checkout repository - uses: actions/checkout@v4 - - name: Generate PR contents file run: bash docs-generation/read-pr.sh ${{ inputs.pr_number }} From c8be55f8aa3b2e788846f39f51fd86da3a67fe6f Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 11:39:06 -0700 Subject: [PATCH 04/21] Adding push trigger to run once --- .github/workflows/docs-generator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 0ad61033e8..b83832509c 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -7,6 +7,7 @@ on: description: 'Number of PR to document' required: true type: string + push: jobs: generate_docs: From aa61cfcb2e1ac84fc134936de70101a1f2b061b2 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 11:40:27 -0700 Subject: [PATCH 05/21] Remove push trigger from workflow The push trigger was added so that the workflow would show up on the Actions tab on GitHub. --- .github/workflows/docs-generator.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index b83832509c..0ad61033e8 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -7,7 +7,6 @@ on: description: 'Number of PR to document' required: true type: string - push: jobs: generate_docs: From 2f7d5dfb5a8d1c721ab001d3c6886d869c3c007b Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 11:46:50 -0700 Subject: [PATCH 06/21] Re-add temporary push trigger --- .github/workflows/docs-generator.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 0ad61033e8..ec43d43a93 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -7,6 +7,7 @@ on: description: 'Number of PR to document' required: true type: string + push: jobs: generate_docs: @@ -35,7 +36,7 @@ jobs: - name: Run setup_amazon_q.sh from terminal-bench run: | - cd terminal-bench-testing + cd terminal-bench-test chmod +x setup_amazon_q.sh bash setup_amazon_q.sh From b842e74e36b850f080d3ff55b791a77624ebc712 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 11:58:45 -0700 Subject: [PATCH 07/21] Run setup with sudo for testing --- .github/workflows/docs-generator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index ec43d43a93..6b621efb63 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -38,7 +38,7 @@ jobs: run: | cd terminal-bench-test chmod +x setup_amazon_q.sh - bash setup_amazon_q.sh + sudo bash setup_amazon_q.sh - name: Make scripts executable run: | From 6838c041b84cd36256f5497d7a370a93817e71f7 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:19:48 -0700 Subject: [PATCH 08/21] Add docs-specific setup script --- .github/workflows/docs-generator.yaml | 10 ++--- docs-generation/setup_amazon_q.sh | 54 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 docs-generation/setup_amazon_q.sh diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 6b621efb63..656ce18bf7 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -33,19 +33,17 @@ jobs: with: role-to-assume: ${{ secrets.AWS_TB_ROLE }} aws-region: us-east-1 - - - name: Run setup_amazon_q.sh from terminal-bench - run: | - cd terminal-bench-test - chmod +x setup_amazon_q.sh - sudo bash setup_amazon_q.sh - name: Make scripts executable run: | + chmod +x dogs-generation/setup_amazon_q.sh chmod +x docs-generation/create-docs-pr.sh chmod +x docs-generation/read-pr.sh chmod +x docs-generation/update-docs.sh + - name: Run setup script + run: bash docs-generation/setup_amazon_q.sh + - name: Generate PR contents file run: bash docs-generation/read-pr.sh ${{ inputs.pr_number }} diff --git a/docs-generation/setup_amazon_q.sh b/docs-generation/setup_amazon_q.sh new file mode 100644 index 0000000000..e5c2c923ee --- /dev/null +++ b/docs-generation/setup_amazon_q.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e +# if git hash empty then set to latest auto +sudo apt-get update +sudo apt-get install -y curl wget unzip jq + +# Create AWS credentials from environment variables +mkdir -p ~/.aws +cat > ~/.aws/credentials << EOF +[default] +aws_access_key_id = ${AWS_ACCESS_KEY_ID} +aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY} +aws_session_token = ${AWS_SESSION_TOKEN} +EOF +chmod 600 ~/.aws/credentials + +cat > ~/.aws/config << EOF +[default] +region = us-east-1 +EOF +chmod 600 ~/.aws/config + +# Assume role and capture temporary credentials --> needed for s3 bucket access for build +echo "Assuming AWS s3 role" +TEMP_CREDENTIALS=$(aws sts assume-role --role-arn ${CHAT_DOWNLOAD_ROLE_ARN} --role-session-name S3AccessSession 2>/dev/null || echo '{}') +QCHAT_ACCESSKEY=$(echo $TEMP_CREDENTIALS | jq -r '.Credentials.AccessKeyId') +Q_SECRET_ACCESS_KEY=$(echo $TEMP_CREDENTIALS | jq -r '.Credentials.SecretAccessKey') +Q_SESSION_TOKEN=$(echo $TEMP_CREDENTIALS | jq -r '.Credentials.SessionToken') + +# Download specific build from S3 based on commit hash +echo "Downloading Amazon Q CLI build from S3..." +S3_PREFIX="main/${GIT_HASH}/x86_64-unknown-linux-musl" +echo "Downloading qchat.zip from s3://.../${S3_PREFIX}/qchat.zip" + +# Try download, if hash is invalid we fail. +AWS_ACCESS_KEY_ID="$QCHAT_ACCESSKEY" AWS_SECRET_ACCESS_KEY="$Q_SECRET_ACCESS_KEY" AWS_SESSION_TOKEN="$Q_SESSION_TOKEN" \ + aws s3 cp s3://${CHAT_BUILD_BUCKET_NAME}/${S3_PREFIX}/qchat.zip ./qchat.zip --region us-east-1 + +# Handle the zip file, copy the qchat executable to /usr/local/bin + symlink from old code +echo "Extracting qchat.zip..." +unzip -q qchat.zip + +# move it to /usr/local/bin/qchat for path as qchat may not work otherwise +if cp qchat /usr/local/bin/ && chmod +x /usr/local/bin/qchat; then + ln -sf /usr/local/bin/qchat /usr/local/bin/q + echo "qchat installed successfully" +else + echo "ERROR: Failed to install qchat" + exit 1 +fi + +echo "Cleaning q zip" +rm -f qchat.zip +rm -rf qchat From 9ab3587e43516588fdc277928c34548a5d4ba866 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:20:59 -0700 Subject: [PATCH 09/21] Fix typo from dogs to docs --- .github/workflows/docs-generator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 656ce18bf7..c14967d6ae 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -36,7 +36,7 @@ jobs: - name: Make scripts executable run: | - chmod +x dogs-generation/setup_amazon_q.sh + chmod +x docs-generation/setup_amazon_q.sh chmod +x docs-generation/create-docs-pr.sh chmod +x docs-generation/read-pr.sh chmod +x docs-generation/update-docs.sh From fc42b528dd0a964404d7cb996fea598716f5ab2d Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:27:28 -0700 Subject: [PATCH 10/21] Add debug statements, fixed test PR number --- .github/workflows/docs-generator.yaml | 17 ++++++++------- docs-generation/create-docs-pr.sh | 30 ++++++++++++++------------- docs-generation/update-docs.sh | 2 ++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index c14967d6ae..6d10c74b32 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -1,12 +1,12 @@ name: docs-generator on: - workflow_dispatch: - inputs: - pr_number: - description: 'Number of PR to document' - required: true - type: string + # workflow_dispatch: + # inputs: + # pr_number: + # description: 'Number of PR to document' + # required: true + # type: string push: jobs: @@ -19,6 +19,7 @@ jobs: PR_FILE: "pr-contents.txt" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GIT_HASH: "latest" + TEST_PR_NUMBER: 2533 permissions: id-token: write contents: write @@ -45,14 +46,14 @@ jobs: run: bash docs-generation/setup_amazon_q.sh - name: Generate PR contents file - run: bash docs-generation/read-pr.sh ${{ inputs.pr_number }} + run: bash docs-generation/read-pr.sh ${{ env.TEST_PR_NUMBER }} - name: Update docs run: bash docs-generation/update-docs.sh - name: Create PR if: success() - run: bash docs-generation/create-docs-pr.sh ${{ inputs.pr_number }} + run: bash docs-generation/create-docs-pr.sh ${{ env.TEST_PR_NUMBER }} diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh index 2a0480ccd9..1601cc135c 100755 --- a/docs-generation/create-docs-pr.sh +++ b/docs-generation/create-docs-pr.sh @@ -10,21 +10,23 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 fi -git config user.name "docs-generator[bot]" -git config user.email "docs-generator[bot]@amazon.com" +echo "Would make PR now" -# Create branch and push -git checkout -b "$BRANCH_NAME" -git add . -git commit -m "Update docs based on PR #$PR_NUMBER +# git config user.name "docs-generator[bot]" +# git config user.email "docs-generator[bot]@amazon.com" -Auto-generated by Q" +# # Create branch and push +# git checkout -b "$BRANCH_NAME" +# git add . +# git commit -m "Update docs based on PR #$PR_NUMBER -git push origin "$BRANCH_NAME" +# Auto-generated by Q" -# Create PR -gh pr create \ - --title "Update docs based on PR #$PR_NUMBER" \ - --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ - --base main \ - --head "$BRANCH_NAME" +# git push origin "$BRANCH_NAME" + +# # Create PR +# gh pr create \ +# --title "Update docs based on PR #$PR_NUMBER" \ +# --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ +# --base main \ +# --head "$BRANCH_NAME" diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index e8ac9a28de..84e8c036fb 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,4 +11,6 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." +cat pr-contents.txt + echo -e $PROMPT | qchat chat --non-interactive From faef1fe9ba4198f308a14b542e6dd9a8e66c456a Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:31:42 -0700 Subject: [PATCH 11/21] Add trust all tools, exit with q chat exit code --- docs-generation/update-docs.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 84e8c036fb..db6f144b11 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -13,4 +13,5 @@ with new documentation reflecting the proposed changes in the PR. Make new files cat pr-contents.txt -echo -e $PROMPT | qchat chat --non-interactive +echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +exit $? \ No newline at end of file From 5034f511eb52177943a3bd42cabb346243d941a0 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:37:43 -0700 Subject: [PATCH 12/21] Add timeout for q chat --- docs-generation/update-docs.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index db6f144b11..a75eba7b4f 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,7 +11,5 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." -cat pr-contents.txt - -echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +timeout 5m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From 6d7cfd4f044e06276b3d126eaaa057c551920fc8 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:45:04 -0700 Subject: [PATCH 13/21] Uncomment PR generation, increase q chat timeout to 10m --- docs-generation/create-docs-pr.sh | 30 ++++++++++++++---------------- docs-generation/update-docs.sh | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh index 1601cc135c..2a0480ccd9 100755 --- a/docs-generation/create-docs-pr.sh +++ b/docs-generation/create-docs-pr.sh @@ -10,23 +10,21 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 fi -echo "Would make PR now" +git config user.name "docs-generator[bot]" +git config user.email "docs-generator[bot]@amazon.com" -# git config user.name "docs-generator[bot]" -# git config user.email "docs-generator[bot]@amazon.com" +# Create branch and push +git checkout -b "$BRANCH_NAME" +git add . +git commit -m "Update docs based on PR #$PR_NUMBER -# # Create branch and push -# git checkout -b "$BRANCH_NAME" -# git add . -# git commit -m "Update docs based on PR #$PR_NUMBER +Auto-generated by Q" -# Auto-generated by Q" +git push origin "$BRANCH_NAME" -# git push origin "$BRANCH_NAME" - -# # Create PR -# gh pr create \ -# --title "Update docs based on PR #$PR_NUMBER" \ -# --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ -# --base main \ -# --head "$BRANCH_NAME" +# Create PR +gh pr create \ + --title "Update docs based on PR #$PR_NUMBER" \ + --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ + --base main \ + --head "$BRANCH_NAME" diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index a75eba7b4f..8f1c29d591 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,5 +11,5 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." -timeout 5m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From 9141f7837e982598c1a57b2aac5d21da3a6c2bff Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:53:07 -0700 Subject: [PATCH 14/21] Ignore several patterns while reading PR --- docs-generation/create-docs-pr.sh | 2 +- docs-generation/read-pr.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh index 2a0480ccd9..fc9b01a44c 100755 --- a/docs-generation/create-docs-pr.sh +++ b/docs-generation/create-docs-pr.sh @@ -2,7 +2,7 @@ set -e PR_NUMBER=$1 -BRANCH_NAME="docs-update-pr-$PR_NUMBER" +BRANCH_NAME="docs-update-for-pr-$PR_NUMBER" # Ensure we have changes to merge if [ -z "$(git status --porcelain)" ]; then diff --git a/docs-generation/read-pr.sh b/docs-generation/read-pr.sh index 766d32f804..ab084275b9 100755 --- a/docs-generation/read-pr.sh +++ b/docs-generation/read-pr.sh @@ -10,6 +10,11 @@ gh pr view $PR_NUMBER --json title,body --jq '"Title: " + .title + "\nDescriptio # Include updated files echo -e "\n====== Updated files ======\n" >> $PR_FILE gh pr view $PR_NUMBER --json files --jq ".files[].path" | while read file; do + case "$file" in + *.lock|*-lock.*|*.min.*|dist/*|build/*|target/*) + continue + ;; + esac if [ -f "$file" ]; then echo "---- $file ----" >> $PR_FILE cat "$file" >> $PR_FILE From 298ed66765446628af8232d57935470e07eb0991 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 12:57:18 -0700 Subject: [PATCH 15/21] Add print statements for debug, disable q chat run temporarily --- docs-generation/update-docs.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 8f1c29d591..3ffe551574 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,5 +11,7 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." -timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +cat pr-contents.txt +echo "Would prompt q chat here" +# timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From 07db3d0848300db44744498674b943d31308fd36 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 13:07:08 -0700 Subject: [PATCH 16/21] Display only file diffs to q chat rather than entire file contents, comment out PR creation for debug --- docs-generation/create-docs-pr.sh | 30 ++++++++++++++++-------------- docs-generation/read-pr.sh | 2 +- docs-generation/update-docs.sh | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh index fc9b01a44c..3c9c087705 100755 --- a/docs-generation/create-docs-pr.sh +++ b/docs-generation/create-docs-pr.sh @@ -10,21 +10,23 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 fi -git config user.name "docs-generator[bot]" -git config user.email "docs-generator[bot]@amazon.com" +echo "Would create PR now" -# Create branch and push -git checkout -b "$BRANCH_NAME" -git add . -git commit -m "Update docs based on PR #$PR_NUMBER +# git config user.name "docs-generator[bot]" +# git config user.email "docs-generator[bot]@amazon.com" -Auto-generated by Q" +# # Create branch and push +# git checkout -b "$BRANCH_NAME" +# git add . +# git commit -m "Update docs based on PR #$PR_NUMBER -git push origin "$BRANCH_NAME" +# Auto-generated by Q" -# Create PR -gh pr create \ - --title "Update docs based on PR #$PR_NUMBER" \ - --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ - --base main \ - --head "$BRANCH_NAME" +# git push origin "$BRANCH_NAME" + +# # Create PR +# gh pr create \ +# --title "Update docs based on PR #$PR_NUMBER" \ +# --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ +# --base main \ +# --head "$BRANCH_NAME" diff --git a/docs-generation/read-pr.sh b/docs-generation/read-pr.sh index ab084275b9..e661d021c7 100755 --- a/docs-generation/read-pr.sh +++ b/docs-generation/read-pr.sh @@ -17,7 +17,7 @@ gh pr view $PR_NUMBER --json files --jq ".files[].path" | while read file; do esac if [ -f "$file" ]; then echo "---- $file ----" >> $PR_FILE - cat "$file" >> $PR_FILE + git diff main -- "$file" >> $PR_FILE echo -e "\n" >> $PR_FILE fi done diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 3ffe551574..1f872a43e5 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -13,5 +13,5 @@ with new documentation reflecting the proposed changes in the PR. Make new files cat pr-contents.txt echo "Would prompt q chat here" -# timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From 8760adc8fd601af475646b754b5ff93e3f39cec0 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 13:09:57 -0700 Subject: [PATCH 17/21] Fetch main for file diffs --- .github/workflows/docs-generator.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs-generator.yaml b/.github/workflows/docs-generator.yaml index 6d10c74b32..f8a50902de 100644 --- a/.github/workflows/docs-generator.yaml +++ b/.github/workflows/docs-generator.yaml @@ -29,6 +29,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Fetch main branch + run: git fetch origin main:main + - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: From 8a83eeadfb9077c78c6083dfaa516a6c8a89e3cd Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 13:13:43 -0700 Subject: [PATCH 18/21] Try gh pr diff instead of manually getting diff --- docs-generation/read-pr.sh | 17 +++-------------- docs-generation/update-docs.sh | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/docs-generation/read-pr.sh b/docs-generation/read-pr.sh index e661d021c7..217c7aa9e0 100755 --- a/docs-generation/read-pr.sh +++ b/docs-generation/read-pr.sh @@ -7,20 +7,9 @@ PR_NUMBER=$1 echo "====== PR Information ======\n" > $PR_FILE gh pr view $PR_NUMBER --json title,body --jq '"Title: " + .title + "\nDescription: " + .body' >> $PR_FILE -# Include updated files -echo -e "\n====== Updated files ======\n" >> $PR_FILE -gh pr view $PR_NUMBER --json files --jq ".files[].path" | while read file; do - case "$file" in - *.lock|*-lock.*|*.min.*|dist/*|build/*|target/*) - continue - ;; - esac - if [ -f "$file" ]; then - echo "---- $file ----" >> $PR_FILE - git diff main -- "$file" >> $PR_FILE - echo -e "\n" >> $PR_FILE - fi -done +# Include PR diffs +echo -e "\n====== PR Diffs ======\n" >> $PR_FILE +gh pr diff $PR_NUMBER >> $PR_FILE diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 1f872a43e5..3ffe551574 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -13,5 +13,5 @@ with new documentation reflecting the proposed changes in the PR. Make new files cat pr-contents.txt echo "Would prompt q chat here" -timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +# timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From f9c76578922154891d858e51f0c1b2ddacabb684 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 13:20:03 -0700 Subject: [PATCH 19/21] Remove debug statements, test full workflow --- docs-generation/create-docs-pr.sh | 30 ++++++++++++++---------------- docs-generation/update-docs.sh | 4 +--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/docs-generation/create-docs-pr.sh b/docs-generation/create-docs-pr.sh index 3c9c087705..fc9b01a44c 100755 --- a/docs-generation/create-docs-pr.sh +++ b/docs-generation/create-docs-pr.sh @@ -10,23 +10,21 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 fi -echo "Would create PR now" +git config user.name "docs-generator[bot]" +git config user.email "docs-generator[bot]@amazon.com" -# git config user.name "docs-generator[bot]" -# git config user.email "docs-generator[bot]@amazon.com" +# Create branch and push +git checkout -b "$BRANCH_NAME" +git add . +git commit -m "Update docs based on PR #$PR_NUMBER -# # Create branch and push -# git checkout -b "$BRANCH_NAME" -# git add . -# git commit -m "Update docs based on PR #$PR_NUMBER +Auto-generated by Q" -# Auto-generated by Q" +git push origin "$BRANCH_NAME" -# git push origin "$BRANCH_NAME" - -# # Create PR -# gh pr create \ -# --title "Update docs based on PR #$PR_NUMBER" \ -# --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ -# --base main \ -# --head "$BRANCH_NAME" +# Create PR +gh pr create \ + --title "Update docs based on PR #$PR_NUMBER" \ + --body "Auto-generated documentation updates based on changes in PR #$PR_NUMBER" \ + --base main \ + --head "$BRANCH_NAME" diff --git a/docs-generation/update-docs.sh b/docs-generation/update-docs.sh index 3ffe551574..8f1c29d591 100755 --- a/docs-generation/update-docs.sh +++ b/docs-generation/update-docs.sh @@ -11,7 +11,5 @@ documentation. Then read 'pr-contents.txt' to see the contents of the current PR After reading both the directory and the PR file, update the files in the 'docs' directory with new documentation reflecting the proposed changes in the PR. Make new files as appropriate." -cat pr-contents.txt -echo "Would prompt q chat here" -# timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools +timeout 10m echo -e $PROMPT | qchat chat --non-interactive --trust-all-tools exit $? \ No newline at end of file From 85d78fb07214295cd9385fc51e1fe5838d19ad88 Mon Sep 17 00:00:00 2001 From: kiran-garre Date: Mon, 25 Aug 2025 14:25:00 -0700 Subject: [PATCH 20/21] Trigger docs-generationn workflow 1 From 81954862415c2bb86b0ee1024afbe6325f25e7e5 Mon Sep 17 00:00:00 2001 From: "docs-generator[bot]" Date: Mon, 25 Aug 2025 21:29:28 +0000 Subject: [PATCH 21/21] Update docs based on PR #2533 Auto-generated by Q --- docs-generation/setup_amazon_q.sh | 0 docs/SUMMARY.md | 2 + docs/agent-file-locations.md | 20 + docs/built-in-tools.md | 66 ++ docs/default-agent-behavior.md | 3 +- docs/slash-commands.md | 234 ++++++ docs/todo-lists.md | 181 ++++ pr-contents.txt | 1294 +++++++++++++++++++++++++++++ 8 files changed, 1799 insertions(+), 1 deletion(-) mode change 100644 => 100755 docs-generation/setup_amazon_q.sh create mode 100644 docs/slash-commands.md create mode 100644 docs/todo-lists.md create mode 100644 pr-contents.txt diff --git a/docs-generation/setup_amazon_q.sh b/docs-generation/setup_amazon_q.sh old mode 100644 new mode 100755 diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 0fc45bba3d..7c827ab8ec 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -4,5 +4,7 @@ - [The Agent Format](./agent-format.md) - [Built-in Tools](./built-in-tools.md) +- [Slash Commands Reference](./slash-commands.md) +- [To-Do List Management](./todo-lists.md) - [Knowledge Management](./knowledge-management.md) - [Profile to Agent Migration](./legacy-profile-to-agent-migration.md) diff --git a/docs/agent-file-locations.md b/docs/agent-file-locations.md index c09ed9311b..1da57c5e85 100644 --- a/docs/agent-file-locations.md +++ b/docs/agent-file-locations.md @@ -108,3 +108,23 @@ EOF ## Directory Creation Q CLI will automatically create the global agents directory (`~/.aws/amazonq/cli-agents/`) if it doesn't exist. However, you need to manually create the local agents directory (`.amazonq/cli-agents/`) in your workspace if you want to use local agents. + +## Related Local Storage + +In addition to agent configurations, Q CLI stores other workspace-specific data in the `.amazonq/` directory: + +### To-Do Lists +To-do lists created by the `todo_list` tool are stored in: +``` +.amazonq/cli-todo-lists/ +``` + +These files persist across chat sessions and allow you to resume work on incomplete tasks using the `/todos` slash commands. + +### Legacy Configuration +Legacy MCP server configurations may be stored in: +``` +.amazonq/mcp.json +``` + +This file is used when agents have `useLegacyMcpJson` set to `true`. diff --git a/docs/built-in-tools.md b/docs/built-in-tools.md index 49aaf5d715..84dcc5ddf0 100644 --- a/docs/built-in-tools.md +++ b/docs/built-in-tools.md @@ -8,6 +8,7 @@ Amazon Q CLI includes several built-in tools that agents can use. This document - [`report_issue`](#report_issue-tool) — Open a GitHub issue template. - [`knowledge`](#knowledge-tool) — Store and retrieve information in a knowledge base. - [`thinking`](#thinking-tool) — Internal reasoning mechanism. +- [`todo_list`](#todo_list-tool) — Create and manage to-do lists for multi-step tasks. - [`use_aws`](#use_aws-tool) — Make AWS CLI API calls. ## Execute_bash Tool @@ -102,6 +103,71 @@ An internal reasoning mechanism that improves the quality of complex tasks by br This tool has no configuration options. +## Todo_list Tool + +Create and manage to-do lists for multi-step tasks. This tool helps track progress on complex tasks by breaking them down into manageable steps and marking completion as work progresses. + +The tool automatically creates to-do lists when you give Amazon Q multi-step tasks and tracks completion status. To-do lists are stored locally in the `.amazonq/cli-todo-lists/` directory and persist across chat sessions. + +### Commands + +#### `create` +Creates a new to-do list with specified tasks and description. + +**Required parameters:** +- `tasks`: Array of distinct task descriptions +- `todo_list_description`: Brief summary of the to-do list + +#### `complete` +Marks specified tasks as completed and updates context. + +**Required parameters:** +- `completed_indices`: Array of 0-indexed task numbers to mark complete +- `context_update`: Important information about completed tasks +- `current_id`: ID of the currently loaded to-do list + +**Optional parameters:** +- `modified_files`: Array of file paths that were modified during the task + +#### `load` +Loads an existing to-do list by ID. + +**Required parameters:** +- `load_id`: ID of the to-do list to load + +#### `add` +Adds new tasks to the current to-do list. + +**Required parameters:** +- `new_tasks`: Array of new task descriptions +- `insert_indices`: Array of 0-indexed positions where tasks should be inserted +- `current_id`: ID of the currently loaded to-do list + +**Optional parameters:** +- `new_description`: Updated description if tasks significantly change the goal + +#### `remove` +Removes tasks from the current to-do list. + +**Required parameters:** +- `remove_indices`: Array of 0-indexed positions of tasks to remove +- `current_id`: ID of the currently loaded to-do list + +**Optional parameters:** +- `new_description`: Updated description if removal significantly changes the goal + +### Configuration + +This tool has no configuration options and is trusted by default. + +### Usage Notes + +- To-do lists are automatically created when you give Amazon Q complex, multi-step tasks +- Tasks should be marked as completed immediately after finishing each step +- The tool tracks file modifications and important context for each completed task +- To-do lists persist across chat sessions and can be resumed later +- Use the `/todos` slash command to view, manage, and resume existing to-do lists + ## Use_aws Tool Make AWS CLI API calls with the specified service, operation, and parameters. diff --git a/docs/default-agent-behavior.md b/docs/default-agent-behavior.md index 0510906a60..9a08a5b17c 100644 --- a/docs/default-agent-behavior.md +++ b/docs/default-agent-behavior.md @@ -49,10 +49,11 @@ If no agent is specified or found, Q CLI uses a built-in default agent with the The built-in default agent provides: ### Available Tools -- **All tools**: Uses `"*"` wildcard to include all built-in tools and MCP server tools +- **All tools**: Uses `"*"` wildcard to include all built-in tools (including `todo_list`) and MCP server tools ### Trusted Tools - **fs_read only**: Only the `fs_read` tool is pre-approved and won't prompt for permission +- **todo_list**: The `todo_list` tool is also trusted by default for task management - All other tools will require user confirmation before execution ### Default Resources diff --git a/docs/slash-commands.md b/docs/slash-commands.md new file mode 100644 index 0000000000..ea9d4d183b --- /dev/null +++ b/docs/slash-commands.md @@ -0,0 +1,234 @@ +# Slash Commands Reference + +Amazon Q CLI provides several slash commands that allow you to perform specific actions and manage various features directly within your chat session. Slash commands start with `/` and provide quick access to functionality without needing to exit the chat interface. + +## Available Commands + +### General Commands + +#### `/help` +Display help information about available commands and features. + +#### `/quit` or `/exit` +Exit the current chat session and return to the command line. + +#### `/clear` +Clear the current conversation history while maintaining the same agent and configuration. + +### Agent Management + +#### `/agent list` +List all available agents in your current workspace and global directories. + +#### `/agent create` +Create a new agent configuration. Opens an interactive wizard to set up agent properties. + +#### `/agent switch ` +Switch to a different agent for the current session. + +### Model Management + +#### `/model list` +List all available language models that can be used with Amazon Q. + +#### `/model switch ` +Switch to a different language model for the current session. + +### Conversation Management + +#### `/save ` +Save the current conversation with a given name for later retrieval. + +#### `/load ` +Load a previously saved conversation by name. + +### Subscription Management + +#### `/subscribe` +Display your current Amazon Q subscription status and usage information. + +### To-Do List Management + +The `/todos` command provides comprehensive to-do list management functionality: + +#### `/todos view` +View an existing to-do list. Opens an interactive selection menu to choose from available lists. + +**Usage:** +```bash +/todos view +``` + +**Features:** +- Lists all available to-do lists with completion status +- Shows progress indicators (e.g., "3/5 tasks completed") +- Displays completed lists with ✓ and in-progress lists with ✗ +- Interactive fuzzy search for easy selection + +#### `/todos resume` +Resume working on a selected to-do list. Amazon Q will load the list and continue from where it left off. + +**Usage:** +```bash +/todos resume +``` + +**Features:** +- Automatically loads the selected to-do list state +- Restores previous context and file modifications +- Continues execution from the last completed task +- Provides seamless continuation of interrupted work + +#### `/todos delete` +Delete a specific to-do list or all lists. + +**Usage:** +```bash +/todos delete # Delete a selected list (interactive) +/todos delete --all # Delete all lists (requires confirmation) +``` + +**Options:** +- `--all`: Delete all to-do lists without individual selection + +#### `/todos clear-finished` +Remove all completed to-do lists to clean up your workspace. + +**Usage:** +```bash +/todos clear-finished +``` + +**Features:** +- Only removes lists where all tasks are marked complete +- Preserves in-progress lists +- Provides confirmation of cleanup actions + +### Knowledge Management + +The `/knowledge` command provides persistent knowledge base functionality: + +#### `/knowledge show` +Display all entries in your knowledge base with detailed information. + +#### `/knowledge add [options]` +Add files or directories to your knowledge base. + +**Usage:** +```bash +/knowledge add "project-docs" /path/to/documentation +/knowledge add "rust-code" /path/to/project --include "*.rs" --exclude "target/**" +``` + +**Options:** +- `--include `: Include files matching the pattern +- `--exclude `: Exclude files matching the pattern +- `--index-type `: Choose indexing approach + +#### `/knowledge remove ` +Remove entries from your knowledge base by name, path, or ID. + +#### `/knowledge update ` +Update an existing knowledge base entry with new content. + +#### `/knowledge clear` +Remove all entries from your knowledge base (requires confirmation). + +#### `/knowledge status` +View the status of background indexing operations. + +#### `/knowledge cancel [operation_id]` +Cancel background operations by ID, or all operations if no ID provided. + +## Command Categories + +### Interactive Commands +Commands that open selection menus or wizards: +- `/todos view` +- `/todos resume` +- `/todos delete` (without --all) +- `/agent list` +- `/model list` + +### Immediate Action Commands +Commands that perform actions directly: +- `/todos clear-finished` +- `/todos delete --all` +- `/knowledge show` +- `/clear` +- `/quit` + +### Commands with Arguments +Commands that require additional parameters: +- `/knowledge add ` +- `/save ` +- `/load ` +- `/agent switch ` + +## Tips and Best Practices + +### General Usage +- Use tab completion to discover available commands +- Most commands provide help when used incorrectly +- Commands are case-insensitive +- Use quotes around names or paths with spaces + +### To-Do List Management +- Let Amazon Q create to-do lists automatically for complex tasks +- Use `/todos resume` to continue interrupted work sessions +- Regularly use `/todos clear-finished` to maintain a clean workspace +- View lists with `/todos view` to check progress without resuming + +### Knowledge Management +- Use descriptive names when adding knowledge bases +- Leverage include/exclude patterns to focus on relevant files +- Monitor indexing progress with `/knowledge status` +- Use `/knowledge clear` sparingly as it removes all data + +### Workflow Integration +- Save important conversations before switching agents or models +- Use `/subscribe` to monitor your usage and subscription status +- Combine slash commands with natural language requests for efficient workflows + +## Error Handling + +### Common Error Messages + +**"No to-do lists found"** +- No to-do lists exist in the current directory +- Create complex tasks to generate new lists automatically + +**"Agent not found"** +- The specified agent doesn't exist in current workspace or global directories +- Use `/agent list` to see available agents + +**"Knowledge base operation failed"** +- Check file permissions and disk space +- Verify paths exist and are accessible +- Use `/knowledge status` to check for ongoing operations + +### Recovery Actions +- Most commands can be safely retried after fixing underlying issues +- Use `/clear` to reset conversation state if commands behave unexpectedly +- Check the Amazon Q CLI logs for detailed error information + +## Advanced Usage + +### Combining Commands +You can use multiple slash commands in sequence: +```bash +/knowledge add "current-project" . +/todos resume +/save "project-work-session" +``` + +### Automation Integration +Slash commands work well with: +- Shell scripts that launch Q CLI with specific agents +- Workflow automation that saves/loads conversations +- CI/CD pipelines that use knowledge bases for context + +### Customization +- Configure default knowledge base patterns with `q settings` +- Set default agents to avoid repeated `/agent switch` commands +- Use agent configurations to pre-configure tool permissions for smoother workflows diff --git a/docs/todo-lists.md b/docs/todo-lists.md new file mode 100644 index 0000000000..96d40ffa48 --- /dev/null +++ b/docs/todo-lists.md @@ -0,0 +1,181 @@ +# To-Do List Management + +Amazon Q CLI includes comprehensive to-do list functionality that helps you track progress on complex, multi-step tasks. The system automatically creates to-do lists when you give Amazon Q tasks that require multiple steps, and provides tools to manage and resume these lists across chat sessions. + +## Overview + +The to-do list system consists of two main components: + +1. **`todo_list` tool**: Used by Amazon Q to automatically create and manage to-do lists during task execution +2. **`/todos` slash command**: Allows you to manually view, manage, and resume existing to-do lists + +## How It Works + +### Automatic To-Do List Creation + +When you give Amazon Q a complex task that requires multiple steps, it will automatically: + +1. Create a to-do list with all necessary steps +2. Display the list to show what work will be done +3. Mark off tasks as they are completed +4. Track important context and file modifications +5. Save progress that persists across chat sessions + +### Example Workflow + +``` +User: "Help me set up a new React project with TypeScript, ESLint, and deploy it to AWS" + +Q creates a to-do list: +TODO: + ☐ Initialize new React project with TypeScript + ☐ Configure ESLint with TypeScript rules + ☐ Set up build configuration + ☐ Create AWS deployment configuration + ☐ Deploy to AWS and verify + +Q then works through each task, marking them complete: + ■ Initialize new React project with TypeScript + ☐ Configure ESLint with TypeScript rules + ... +``` + +## Managing To-Do Lists + +### The `/todos` Slash Command + +Use `/todos` followed by a subcommand to manage your to-do lists: + +#### `/todos view` +View an existing to-do list. Opens a selection menu to choose from available lists. + +```bash +/todos view +``` + +#### `/todos resume` +Resume working on a selected to-do list. Amazon Q will load the list and continue from where it left off. + +```bash +/todos resume +``` + +#### `/todos delete` +Delete a specific to-do list or all lists. + +```bash +/todos delete # Delete a selected list +/todos delete --all # Delete all lists +``` + +#### `/todos clear-finished` +Remove all completed to-do lists to clean up your workspace. + +```bash +/todos clear-finished +``` + +## To-Do List Storage + +### Local Storage +To-do lists are stored locally in your current working directory under: +``` +.amazonq/cli-todo-lists/ +``` + +Each to-do list is saved as a JSON file with a unique timestamp-based ID. + +### Persistence +- To-do lists persist across chat sessions +- You can resume work on any incomplete list +- Lists are automatically saved when tasks are completed or modified +- Context and file modifications are tracked for each completed task + +## To-Do List States + +### Task Status +- **☐** Incomplete task (displayed in normal text) +- **■** Completed task (displayed in green italic text) + +### List Status +- **In Progress**: Has incomplete tasks (displayed with ✗ and progress count) +- **Completed**: All tasks finished (displayed with ✓) + +### Display Format +``` +✗ Set up React project (2/5) # In progress +✓ Deploy website # Completed +``` + +## Best Practices + +### For Users +1. **Let Amazon Q create lists**: Don't manually create to-do lists - let Amazon Q generate them based on your requests +2. **Use descriptive requests**: Provide clear, detailed descriptions of what you want to accomplish +3. **Resume incomplete work**: Use `/todos resume` to continue work on unfinished tasks +4. **Clean up regularly**: Use `/todos clear-finished` to remove completed lists + +### For Complex Tasks +1. **Break down large requests**: If you have a very complex project, consider breaking it into smaller, focused requests +2. **Provide context**: Give Amazon Q relevant information about your project, preferences, and constraints +3. **Review progress**: Use `/todos view` to check the status of ongoing work + +## Integration with Chat Sessions + +### Conversation Summaries +When you save a conversation that includes to-do list work, the summary will include: +- The ID of any currently loaded to-do list +- Progress made on tasks +- Important context and insights + +### Resuming Work +When you resume a conversation or load a to-do list: +- Amazon Q automatically loads the list state +- Previous context and file modifications are available +- Work continues from the last completed task + +## Troubleshooting + +### Common Issues + +**To-do lists not appearing** +- Ensure you're in the same directory where the lists were created +- Check that `.amazonq/cli-todo-lists/` exists in your current directory + +**Cannot resume a list** +- Verify the list still exists with `/todos view` +- Check that the list file hasn't been corrupted or manually modified + +**Lists not saving progress** +- Ensure you have write permissions in the current directory +- Check that there's sufficient disk space + +### Error Messages + +**"No to-do lists to [action]"** +- No lists exist in the current directory +- Create a new task that requires multiple steps to generate a list + +**"Could not load to-do list"** +- The list file may be corrupted or manually modified +- Try deleting the problematic list and creating a new one + +## Technical Details + +### File Format +To-do lists are stored as JSON files containing: +- Task descriptions and completion status +- List description and metadata +- Context updates from completed tasks +- Modified file paths +- Unique identifier + +### Security +- Lists are stored locally and never transmitted +- No sensitive information is automatically captured +- File paths and context are only what you explicitly work with + +### Performance +- Lists are loaded on-demand +- Minimal impact on chat session performance +- Automatic cleanup of temporary files diff --git a/pr-contents.txt b/pr-contents.txt new file mode 100644 index 0000000000..624c817f16 --- /dev/null +++ b/pr-contents.txt @@ -0,0 +1,1294 @@ +====== PR Information ======\n +Title: Add to-do list functionality to QCLI +Description: Adds a to-do list tool (called todo_list) with several commands that allow Q to create a to-do list and update it as it completes tasks, along with a slash command (/todos) that allows users to view and manage their in-progress to-do lists. + + +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. + + +====== PR Diffs ====== + +diff --git a/Cargo.lock b/Cargo.lock +index 5637b427f8..7ec0858992 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -231,9 +231,9 @@ dependencies = [ + + [[package]] + name = "anyhow" +-version = "1.0.98" ++version = "1.0.99" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" ++checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + + [[package]] + name = "arbitrary" +@@ -670,7 +670,7 @@ dependencies = [ + "regex-lite", + "roxmltree", + "serde_json", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + ] + + [[package]] +@@ -1052,7 +1052,7 @@ dependencies = [ + "cached_proc_macro_types", + "hashbrown 0.15.5", + "once_cell", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "web-time", + ] + +@@ -1305,7 +1305,7 @@ dependencies = [ + "syntect", + "sysinfo", + "tempfile", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "time", + "tokio", + "tokio-tungstenite", +@@ -1392,9 +1392,9 @@ dependencies = [ + + [[package]] + name = "clap" +-version = "4.5.43" ++version = "4.5.44" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" ++checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8" + dependencies = [ + "clap_builder", + "clap_derive", +@@ -1402,9 +1402,9 @@ dependencies = [ + + [[package]] + name = "clap_builder" +-version = "4.5.43" ++version = "4.5.44" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" ++checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" + dependencies = [ + "anstream", + "anstyle", +@@ -1417,9 +1417,9 @@ dependencies = [ + + [[package]] + name = "clap_complete" +-version = "4.5.56" ++version = "4.5.57" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "67e4efcbb5da11a92e8a609233aa1e8a7d91e38de0be865f016d14700d45a7fd" ++checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" + dependencies = [ + "clap", + ] +@@ -2762,9 +2762,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + + [[package]] + name = "glob" +-version = "0.3.2" ++version = "0.3.3" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" ++checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + + [[package]] + name = "globset" +@@ -3475,9 +3475,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + + [[package]] + name = "libc" +-version = "0.2.174" ++version = "0.2.175" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" ++checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + + [[package]] + name = "libloading" +@@ -4016,7 +4016,7 @@ dependencies = [ + "serde_json", + "strum 0.26.3", + "strum_macros 0.26.4", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "typetag", + "web-time", + "windows-sys 0.48.0", +@@ -4692,9 +4692,9 @@ dependencies = [ + + [[package]] + name = "proc-macro2" +-version = "1.0.96" ++version = "1.0.97" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" ++checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" + dependencies = [ + "unicode-ident", + ] +@@ -4825,7 +4825,7 @@ dependencies = [ + "rustc-hash 2.1.1", + "rustls 0.23.31", + "socket2 0.5.10", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "tokio", + "tracing", + "web-time", +@@ -4846,7 +4846,7 @@ dependencies = [ + "rustls 0.23.31", + "rustls-pki-types", + "slab", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "tinyvec", + "tracing", + "web-time", +@@ -5065,7 +5065,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" + dependencies = [ + "getrandom 0.2.16", + "libredox", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + ] + + [[package]] +@@ -5583,7 +5583,7 @@ dependencies = [ + "serde_json", + "sha2", + "tempfile", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "tokenizers", + "tokio", + "tokio-stream", +@@ -6164,12 +6164,12 @@ dependencies = [ + + [[package]] + name = "terminal_size" +-version = "0.4.2" ++version = "0.4.3" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" ++checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" + dependencies = [ + "rustix 1.0.8", +- "windows-sys 0.59.0", ++ "windows-sys 0.60.2", + ] + + [[package]] +@@ -6199,11 +6199,11 @@ dependencies = [ + + [[package]] + name = "thiserror" +-version = "2.0.12" ++version = "2.0.14" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" ++checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" + dependencies = [ +- "thiserror-impl 2.0.12", ++ "thiserror-impl 2.0.14", + ] + + [[package]] +@@ -6219,9 +6219,9 @@ dependencies = [ + + [[package]] + name = "thiserror-impl" +-version = "2.0.12" ++version = "2.0.14" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" ++checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" + dependencies = [ + "proc-macro2", + "quote", +@@ -6342,7 +6342,7 @@ dependencies = [ + "serde", + "serde_json", + "spm_precompiled", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "unicode-normalization-alignments", + "unicode-segmentation", + "unicode_categories", +@@ -6687,7 +6687,7 @@ dependencies = [ + "log", + "rand 0.9.2", + "sha1", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "utf-8", + ] + +@@ -6848,9 +6848,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + + [[package]] + name = "uuid" +-version = "1.17.0" ++version = "1.18.0" + source = "registry+https://github.com/rust-lang/crates.io-index" +-checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" ++checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" + dependencies = [ + "getrandom 0.3.3", + "js-sys", +@@ -7725,7 +7725,7 @@ dependencies = [ + "os_pipe", + "rustix 0.38.44", + "tempfile", +- "thiserror 2.0.12", ++ "thiserror 2.0.14", + "tree_magic_mini", + "wayland-backend", + "wayland-client", +diff --git a/build-config/buildspec-linux.yml b/build-config/buildspec-linux.yml +index fb50d66f5b..8c20235acf 100644 +--- a/build-config/buildspec-linux.yml ++++ b/build-config/buildspec-linux.yml +@@ -44,4 +44,3 @@ artifacts: + # Signatures + - ./*.asc + - ./*.sig +- +diff --git a/build-config/buildspec-macos.yml b/build-config/buildspec-macos.yml +index 0fbe335ae2..bb9bb58d75 100644 +--- a/build-config/buildspec-macos.yml ++++ b/build-config/buildspec-macos.yml +@@ -38,4 +38,3 @@ artifacts: + - ./*.zip + # Hashes + - ./*.sha256 +- +diff --git a/crates/chat-cli/src/cli/agent/mod.rs b/crates/chat-cli/src/cli/agent/mod.rs +index a11c0cb7e2..f2ff3954aa 100644 +--- a/crates/chat-cli/src/cli/agent/mod.rs ++++ b/crates/chat-cli/src/cli/agent/mod.rs +@@ -728,6 +728,7 @@ impl Agents { + "use_aws" => "trust read-only commands".dark_grey(), + "report_issue" => "trusted".dark_green().bold(), + "thinking" => "trusted (prerelease)".dark_green().bold(), ++ "todo_list" => "trusted".dark_green().bold(), + _ if self.trust_all_tools => "trusted".dark_grey().bold(), + _ => "not trusted".dark_grey(), + }; +diff --git a/crates/chat-cli/src/cli/chat/cli/mod.rs b/crates/chat-cli/src/cli/chat/cli/mod.rs +index 4805426d06..12bd0aa51b 100644 +--- a/crates/chat-cli/src/cli/chat/cli/mod.rs ++++ b/crates/chat-cli/src/cli/chat/cli/mod.rs +@@ -10,6 +10,7 @@ pub mod persist; + pub mod profile; + pub mod prompts; + pub mod subscribe; ++pub mod todos; + pub mod tools; + pub mod usage; + +@@ -25,6 +26,7 @@ use model::ModelArgs; + use persist::PersistSubcommand; + use profile::AgentSubcommand; + use prompts::PromptsArgs; ++use todos::TodoSubcommand; + use tools::ToolsArgs; + + use crate::cli::chat::cli::subscribe::SubscribeArgs; +@@ -85,6 +87,9 @@ pub enum SlashCommand { + Persist(PersistSubcommand), + // #[command(flatten)] + // Root(RootSubcommand), ++ /// View, manage, and resume to-do lists ++ #[command(subcommand)] ++ Todos(TodoSubcommand), + } + + impl SlashCommand { +@@ -146,6 +151,7 @@ impl SlashCommand { + // skip_printing_tools: true, + // }) + // }, ++ Self::Todos(subcommand) => subcommand.execute(os, session).await, + } + } + +@@ -171,6 +177,7 @@ impl SlashCommand { + PersistSubcommand::Save { .. } => "save", + PersistSubcommand::Load { .. } => "load", + }, ++ Self::Todos(_) => "todos", + } + } + +diff --git a/crates/chat-cli/src/cli/chat/cli/todos.rs b/crates/chat-cli/src/cli/chat/cli/todos.rs +new file mode 100644 +index 0000000000..eb107e8110 +--- /dev/null ++++ b/crates/chat-cli/src/cli/chat/cli/todos.rs +@@ -0,0 +1,202 @@ ++use clap::Subcommand; ++use crossterm::execute; ++use crossterm::style::{ ++ self, ++ Stylize, ++}; ++use dialoguer::FuzzySelect; ++use eyre::Result; ++ ++use crate::cli::chat::tools::todo::{ ++ TodoListState, ++ delete_todo, ++ get_all_todos, ++}; ++use crate::cli::chat::{ ++ ChatError, ++ ChatSession, ++ ChatState, ++}; ++use crate::os::Os; ++ ++/// Defines subcommands that allow users to view and manage todo lists ++#[derive(Debug, PartialEq, Subcommand)] ++pub enum TodoSubcommand { ++ /// Delete all completed to-do lists ++ ClearFinished, ++ ++ /// Resume a selected to-do list ++ Resume, ++ ++ /// View a to-do list ++ View, ++ ++ /// Delete a to-do list ++ Delete { ++ #[arg(long, short)] ++ all: bool, ++ }, ++} ++ ++/// Used for displaying completed and in-progress todo lists ++pub struct TodoDisplayEntry { ++ pub num_completed: usize, ++ pub num_tasks: usize, ++ pub description: String, ++ pub id: String, ++} ++ ++impl std::fmt::Display for TodoDisplayEntry { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ if self.num_completed == self.num_tasks { ++ write!(f, "{} {}", "✓".green().bold(), self.description.clone(),) ++ } else { ++ write!( ++ f, ++ "{} {} ({}/{})", ++ "✗".red().bold(), ++ self.description.clone(), ++ self.num_completed, ++ self.num_tasks ++ ) ++ } ++ } ++} ++ ++impl TodoSubcommand { ++ pub async fn execute(self, os: &mut Os, session: &mut ChatSession) -> Result { ++ match self { ++ Self::ClearFinished => { ++ let (todos, errors) = match get_all_todos(os).await { ++ Ok(res) => res, ++ Err(e) => return Err(ChatError::Custom(format!("Could not get to-do lists: {e}").into())), ++ }; ++ let mut cleared_one = false; ++ ++ for todo_status in todos.iter() { ++ if todo_status.completed.iter().all(|b| *b) { ++ match delete_todo(os, &todo_status.id).await { ++ Ok(_) => cleared_one = true, ++ Err(e) => { ++ return Err(ChatError::Custom(format!("Could not delete to-do list: {e}").into())); ++ }, ++ }; ++ } ++ } ++ if cleared_one { ++ execute!( ++ session.stderr, ++ style::Print("✔ Cleared finished to-do lists!\n".green()) ++ )?; ++ } else { ++ execute!(session.stderr, style::Print("No finished to-do lists to clear!\n"))?; ++ } ++ if !errors.is_empty() { ++ execute!( ++ session.stderr, ++ style::Print(format!("* Failed to get {} todo list(s)\n", errors.len()).dark_grey()) ++ )?; ++ } ++ }, ++ Self::Resume => match Self::get_descriptions_and_statuses(os).await { ++ Ok(entries) => { ++ if entries.is_empty() { ++ execute!(session.stderr, style::Print("No to-do lists to resume!\n"),)?; ++ } else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to resume:") { ++ if index < entries.len() { ++ execute!( ++ session.stderr, ++ style::Print(format!( ++ "{} {}", ++ "⟳ Resuming:".magenta(), ++ entries[index].description.clone() ++ )) ++ )?; ++ return session.resume_todo_request(os, &entries[index].id).await; ++ } ++ } ++ }, ++ Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())), ++ }, ++ Self::View => match Self::get_descriptions_and_statuses(os).await { ++ Ok(entries) => { ++ if entries.is_empty() { ++ execute!(session.stderr, style::Print("No to-do lists to view!\n"))?; ++ } else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to view:") { ++ if index < entries.len() { ++ let list = TodoListState::load(os, &entries[index].id).await.map_err(|e| { ++ ChatError::Custom(format!("Could not load current to-do list: {e}").into()) ++ })?; ++ execute!( ++ session.stderr, ++ style::Print(format!( ++ "{} {}\n\n", ++ "Viewing:".magenta(), ++ entries[index].description.clone() ++ )) ++ )?; ++ if list.display_list(&mut session.stderr).is_err() { ++ return Err(ChatError::Custom("Could not display the selected to-do list".into())); ++ } ++ execute!(session.stderr, style::Print("\n"),)?; ++ } ++ } ++ }, ++ Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())), ++ }, ++ Self::Delete { all } => match Self::get_descriptions_and_statuses(os).await { ++ Ok(entries) => { ++ if entries.is_empty() { ++ execute!(session.stderr, style::Print("No to-do lists to delete!\n"))?; ++ } else if all { ++ for entry in entries { ++ delete_todo(os, &entry.id) ++ .await ++ .map_err(|_e| ChatError::Custom("Could not delete all to-do lists".into()))?; ++ } ++ execute!(session.stderr, style::Print("✔ Deleted all to-do lists!\n".green()),)?; ++ } else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to delete:") { ++ if index < entries.len() { ++ delete_todo(os, &entries[index].id).await.map_err(|e| { ++ ChatError::Custom(format!("Could not delete the selected to-do list: {e}").into()) ++ })?; ++ execute!( ++ session.stderr, ++ style::Print("✔ Deleted to-do list: ".green()), ++ style::Print(format!("{}\n", entries[index].description.clone().dark_grey())) ++ )?; ++ } ++ } ++ }, ++ Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())), ++ }, ++ } ++ Ok(ChatState::PromptUser { ++ skip_printing_tools: true, ++ }) ++ } ++ ++ /// Convert all to-do list state entries to displayable entries ++ async fn get_descriptions_and_statuses(os: &Os) -> Result> { ++ let mut out = Vec::new(); ++ let (todos, _) = get_all_todos(os).await?; ++ for todo in todos.iter() { ++ out.push(TodoDisplayEntry { ++ num_completed: todo.completed.iter().filter(|b| **b).count(), ++ num_tasks: todo.completed.len(), ++ description: todo.description.clone(), ++ id: todo.id.clone(), ++ }); ++ } ++ Ok(out) ++ } ++} ++ ++fn fuzzy_select_todos(entries: &[TodoDisplayEntry], prompt_str: &str) -> Option { ++ FuzzySelect::new() ++ .with_prompt(prompt_str) ++ .items(entries) ++ .report(false) ++ .interact_opt() ++ .unwrap_or(None) ++} +diff --git a/crates/chat-cli/src/cli/chat/conversation.rs b/crates/chat-cli/src/cli/chat/conversation.rs +index ca7b87d2c4..8718696529 100644 +--- a/crates/chat-cli/src/cli/chat/conversation.rs ++++ b/crates/chat-cli/src/cli/chat/conversation.rs +@@ -12,6 +12,7 @@ use crossterm::{ + execute, + style, + }; ++use eyre::Result; + use serde::{ + Deserialize, + Serialize, +@@ -489,12 +490,15 @@ impl ConversationState { + 2) Bullet points for all significant tools executed and their results\n\ + 3) Bullet points for any code or technical information shared\n\ + 4) A section of key insights gained\n\n\ ++ 5) REQUIRED: the ID of the currently loaded todo list, if any\n\n\ + FORMAT THE SUMMARY IN THIRD PERSON, NOT AS A DIRECT RESPONSE. Example format:\n\n\ + ## CONVERSATION SUMMARY\n\ + * Topic 1: Key information\n\ + * Topic 2: Key information\n\n\ + ## TOOLS EXECUTED\n\ + * Tool X: Result Y\n\n\ ++ ## TODO ID\n\ ++ * \n\n\ + Remember this is a DOCUMENT not a chat response. The custom instruction above modifies what to prioritize.\n\ + FILTER OUT CHAT CONVENTIONS (greetings, offers to help, etc).", + custom_prompt.as_ref() +@@ -509,12 +513,15 @@ impl ConversationState { + 2) Bullet points for all significant tools executed and their results\n\ + 3) Bullet points for any code or technical information shared\n\ + 4) A section of key insights gained\n\n\ ++ 5) REQUIRED: the ID of the currently loaded todo list, if any\n\n\ + FORMAT THE SUMMARY IN THIRD PERSON, NOT AS A DIRECT RESPONSE. Example format:\n\n\ + ## CONVERSATION SUMMARY\n\ + * Topic 1: Key information\n\ + * Topic 2: Key information\n\n\ + ## TOOLS EXECUTED\n\ + * Tool X: Result Y\n\n\ ++ ## TODO ID\n\ ++ * \n\n\ + Remember this is a DOCUMENT not a chat response.\n\ + FILTER OUT CHAT CONVENTIONS (greetings, offers to help, etc).".to_string() + }, +diff --git a/crates/chat-cli/src/cli/chat/mod.rs b/crates/chat-cli/src/cli/chat/mod.rs +index 0d3b7b1d8c..e159d66641 100644 +--- a/crates/chat-cli/src/cli/chat/mod.rs ++++ b/crates/chat-cli/src/cli/chat/mod.rs +@@ -131,6 +131,7 @@ use crate::api_client::{ + }; + use crate::auth::AuthError; + use crate::auth::builder_id::is_idc_user; ++use crate::cli::TodoListState; + use crate::cli::agent::Agents; + use crate::cli::chat::cli::SlashCommand; + use crate::cli::chat::cli::model::find_model; +@@ -138,6 +139,8 @@ use crate::cli::chat::cli::prompts::{ + GetPromptError, + PromptsSubcommand, + }; ++use crate::cli::chat::message::UserMessage; ++use crate::cli::chat::tools::ToolOrigin; + use crate::cli::chat::util::sanitize_unicode_tags; + use crate::database::settings::Setting; + use crate::mcp_client::Prompt; +@@ -639,6 +642,11 @@ impl ChatSession { + } + }); + ++ // Create for cleaner error handling for todo lists ++ // This is more of a convenience thing but is not required, so the Result ++ // is ignored ++ let _ = TodoListState::init_dir(os).await; ++ + Ok(Self { + stdout, + stderr, +@@ -2778,6 +2786,53 @@ impl ChatSession { + tracing::warn!("Failed to send slash command telemetry: {}", e); + } + } ++ ++ /// Prompts Q to resume a to-do list with the given id by calling the load ++ /// command of the todo_list tool ++ pub async fn resume_todo_request(&mut self, os: &mut Os, id: &str) -> Result { ++ // Have to unpack each value separately since Reports can't be converted to ++ // ChatError ++ let todo_list = match TodoListState::load(os, id).await { ++ Ok(todo) => todo, ++ Err(e) => { ++ return Err(ChatError::Custom(format!("Error getting todo list: {e}").into())); ++ }, ++ }; ++ let contents = match serde_json::to_string(&todo_list) { ++ Ok(s) => s, ++ Err(e) => return Err(ChatError::Custom(format!("Error deserializing todo list: {e}").into())), ++ }; ++ let summary_content = format!( ++ "[SYSTEM NOTE: This is an automated request, not from the user]\n ++ Read the TODO list contents below and understand the task description, completed tasks, and provided context.\n ++ Call the `load` command of the todo_list tool with the given ID as an argument to display the TODO list to the user and officially resume execution of the TODO list tasks.\n ++ You do not need to display the tasks to the user yourself. You can begin completing the tasks after calling the `load` command.\n ++ TODO LIST CONTENTS: {}\n ++ ID: {}\n", ++ contents, ++ id ++ ); ++ ++ let summary_message = UserMessage::new_prompt(summary_content.clone(), None); ++ ++ // Only send the todo_list tool ++ let mut tools = self.conversation.tools.clone(); ++ tools.retain(|k, v| match k { ++ ToolOrigin::Native => { ++ v.retain(|tool| match tool { ++ api_client::model::Tool::ToolSpecification(tool_spec) => tool_spec.name == "todo_list", ++ }); ++ true ++ }, ++ ToolOrigin::McpServer(_) => false, ++ }); ++ ++ Ok(ChatState::HandleInput { ++ input: summary_message ++ .into_user_input_message(self.conversation.model.clone(), &tools) ++ .content, ++ }) ++ } + } + + /// Replaces amzn_codewhisperer_client::types::SubscriptionStatus with a more descriptive type. +@@ -2838,7 +2893,7 @@ async fn get_subscription_status_with_spinner( + .await; + } + +-async fn with_spinner(output: &mut impl std::io::Write, spinner_text: &str, f: F) -> Result ++pub async fn with_spinner(output: &mut impl std::io::Write, spinner_text: &str, f: F) -> Result + where + F: FnOnce() -> Fut, + Fut: std::future::Future>, +diff --git a/crates/chat-cli/src/cli/chat/prompt.rs b/crates/chat-cli/src/cli/chat/prompt.rs +index 291fe35ba3..fc0aef58da 100644 +--- a/crates/chat-cli/src/cli/chat/prompt.rs ++++ b/crates/chat-cli/src/cli/chat/prompt.rs +@@ -83,6 +83,11 @@ pub const COMMANDS: &[&str] = &[ + "/save", + "/load", + "/subscribe", ++ "/todos", ++ "/todos resume", ++ "/todos clear-finished", ++ "/todos view", ++ "/todos delete", + ]; + + /// Complete commands that start with a slash +diff --git a/crates/chat-cli/src/cli/chat/tool_manager.rs b/crates/chat-cli/src/cli/chat/tool_manager.rs +index 95739a1068..f23aa6910d 100644 +--- a/crates/chat-cli/src/cli/chat/tool_manager.rs ++++ b/crates/chat-cli/src/cli/chat/tool_manager.rs +@@ -79,6 +79,7 @@ use crate::cli::chat::tools::fs_write::FsWrite; + use crate::cli::chat::tools::gh_issue::GhIssue; + use crate::cli::chat::tools::knowledge::Knowledge; + use crate::cli::chat::tools::thinking::Thinking; ++use crate::cli::chat::tools::todo::TodoList; + use crate::cli::chat::tools::use_aws::UseAws; + use crate::cli::chat::tools::{ + Tool, +@@ -1066,6 +1067,7 @@ impl ToolManager { + "report_issue" => Tool::GhIssue(serde_json::from_value::(value.args).map_err(map_err)?), + "thinking" => Tool::Thinking(serde_json::from_value::(value.args).map_err(map_err)?), + "knowledge" => Tool::Knowledge(serde_json::from_value::(value.args).map_err(map_err)?), ++ "todo_list" => Tool::Todo(serde_json::from_value::(value.args).map_err(map_err)?), + // Note that this name is namespaced with server_name{DELIMITER}tool_name + name => { + // Note: tn_map also has tools that underwent no transformation. In otherwords, if +diff --git a/crates/chat-cli/src/cli/chat/tools/mod.rs b/crates/chat-cli/src/cli/chat/tools/mod.rs +index ea2aef2529..fef93b62a6 100644 +--- a/crates/chat-cli/src/cli/chat/tools/mod.rs ++++ b/crates/chat-cli/src/cli/chat/tools/mod.rs +@@ -5,6 +5,7 @@ pub mod fs_write; + pub mod gh_issue; + pub mod knowledge; + pub mod thinking; ++pub mod todo; + pub mod use_aws; + + use std::borrow::{ +@@ -35,6 +36,7 @@ use serde::{ + Serialize, + }; + use thinking::Thinking; ++use todo::TodoList; + use tracing::error; + use use_aws::UseAws; + +@@ -79,6 +81,7 @@ pub enum Tool { + GhIssue(GhIssue), + Knowledge(Knowledge), + Thinking(Thinking), ++ Todo(TodoList), + } + + impl Tool { +@@ -96,6 +99,7 @@ impl Tool { + Tool::GhIssue(_) => "gh_issue", + Tool::Knowledge(_) => "knowledge", + Tool::Thinking(_) => "thinking (prerelease)", ++ Tool::Todo(_) => "todo_list", + } + .to_owned() + } +@@ -111,6 +115,7 @@ impl Tool { + Tool::GhIssue(_) => PermissionEvalResult::Allow, + Tool::Thinking(_) => PermissionEvalResult::Allow, + Tool::Knowledge(knowledge) => knowledge.eval_perm(agent), ++ Tool::Todo(_) => PermissionEvalResult::Allow, + } + } + +@@ -130,6 +135,7 @@ impl Tool { + Tool::GhIssue(gh_issue) => gh_issue.invoke(os, stdout).await, + Tool::Knowledge(knowledge) => knowledge.invoke(os, stdout).await, + Tool::Thinking(think) => think.invoke(stdout).await, ++ Tool::Todo(todo) => todo.invoke(os, stdout).await, + } + } + +@@ -144,6 +150,7 @@ impl Tool { + Tool::GhIssue(gh_issue) => gh_issue.queue_description(output), + Tool::Knowledge(knowledge) => knowledge.queue_description(os, output).await, + Tool::Thinking(thinking) => thinking.queue_description(output), ++ Tool::Todo(_) => Ok(()), + } + } + +@@ -158,6 +165,7 @@ impl Tool { + Tool::GhIssue(gh_issue) => gh_issue.validate(os).await, + Tool::Knowledge(knowledge) => knowledge.validate(os).await, + Tool::Thinking(think) => think.validate(os).await, ++ Tool::Todo(todo) => todo.validate(os).await, + } + } + +diff --git a/crates/chat-cli/src/cli/chat/tools/todo.rs b/crates/chat-cli/src/cli/chat/tools/todo.rs +new file mode 100644 +index 0000000000..f874e962d0 +--- /dev/null ++++ b/crates/chat-cli/src/cli/chat/tools/todo.rs +@@ -0,0 +1,402 @@ ++use std::collections::HashSet; ++use std::io::Write; ++use std::path::PathBuf; ++use std::time::{ ++ SystemTime, ++ UNIX_EPOCH, ++}; ++ ++use crossterm::style::Stylize; ++use crossterm::{ ++ queue, ++ style, ++}; ++use eyre::{ ++ OptionExt, ++ Report, ++ Result, ++ bail, ++ eyre, ++}; ++use serde::{ ++ Deserialize, ++ Serialize, ++}; ++ ++use super::InvokeOutput; ++use crate::os::Os; ++ ++// Local directory to store todo lists ++const TODO_LIST_DIR: &str = ".amazonq/cli-todo-lists/"; ++ ++/// Contains all state to be serialized and deserialized into a todo list ++#[derive(Debug, Default, Serialize, Deserialize, Clone)] ++pub struct TodoListState { ++ pub tasks: Vec, ++ pub completed: Vec, ++ pub description: String, ++ pub context: Vec, ++ pub modified_files: Vec, ++ pub id: String, ++} ++ ++impl TodoListState { ++ /// Creates a local directory to store todo lists ++ pub async fn init_dir(os: &Os) -> Result<()> { ++ os.fs.create_dir_all(os.env.current_dir()?.join(TODO_LIST_DIR)).await?; ++ Ok(()) ++ } ++ ++ /// Loads a TodoListState with the given id ++ pub async fn load(os: &Os, id: &str) -> Result { ++ let state_str = os ++ .fs ++ .read_to_string(id_to_path(os, id)?) ++ .await ++ .map_err(|e| eyre!("Could not load todo list: {e}"))?; ++ serde_json::from_str::(&state_str).map_err(|e| eyre!("Could not deserialize todo list: {e}")) ++ } ++ ++ /// Saves this TodoListState with the given id ++ pub async fn save(&self, os: &Os, id: &str) -> Result<()> { ++ let path = id_to_path(os, id)?; ++ Self::init_dir(os).await?; ++ if !os.fs.exists(&path) { ++ os.fs.create_new(&path).await?; ++ } ++ os.fs.write(path, serde_json::to_string(self)?).await?; ++ Ok(()) ++ } ++ ++ /// Displays the TodoListState as a to-do list ++ pub fn display_list(&self, output: &mut impl Write) -> Result<()> { ++ queue!(output, style::Print("TODO:\n".yellow()))?; ++ for (index, (task, completed)) in self.tasks.iter().zip(self.completed.iter()).enumerate() { ++ queue_next_without_newline(output, task.clone(), *completed)?; ++ if index < self.tasks.len() - 1 { ++ queue!(output, style::Print("\n"))?; ++ } ++ } ++ Ok(()) ++ } ++} ++ ++/// Displays a single empty or marked off to-do list task depending on ++/// the completion status ++fn queue_next_without_newline(output: &mut impl Write, task: String, completed: bool) -> Result<()> { ++ if completed { ++ queue!( ++ output, ++ style::SetAttribute(style::Attribute::Italic), ++ style::SetForegroundColor(style::Color::Green), ++ style::Print(" ■ "), ++ style::SetForegroundColor(style::Color::DarkGrey), ++ style::Print(task), ++ style::SetAttribute(style::Attribute::NoItalic), ++ )?; ++ } else { ++ queue!( ++ output, ++ style::SetForegroundColor(style::Color::Reset), ++ style::Print(format!(" ☐ {task}")), ++ )?; ++ } ++ Ok(()) ++} ++ ++/// Generates a new unique id be used for new to-do lists ++pub fn generate_new_todo_id() -> String { ++ let timestamp = SystemTime::now() ++ .duration_since(UNIX_EPOCH) ++ .expect("Time went backwards") ++ .as_millis(); ++ ++ format!("{timestamp}") ++} ++ ++/// Converts a todo list id to an absolute path in the cwd ++pub fn id_to_path(os: &Os, id: &str) -> Result { ++ Ok(os.env.current_dir()?.join(TODO_LIST_DIR).join(format!("{id}.json"))) ++} ++ ++/// Gets all todo lists from the local directory ++pub async fn get_all_todos(os: &Os) -> Result<(Vec, Vec)> { ++ let todo_list_dir = os.env.current_dir()?.join(TODO_LIST_DIR); ++ let mut read_dir_output = os.fs.read_dir(todo_list_dir).await?; ++ ++ let mut todos = Vec::new(); ++ let mut errors = Vec::new(); ++ ++ while let Some(entry) = read_dir_output.next_entry().await? { ++ match TodoListState::load( ++ os, ++ &entry ++ .path() ++ .with_extension("") ++ .file_name() ++ .ok_or_eyre("Path is not a file")? ++ .to_string_lossy(), ++ ) ++ .await ++ { ++ Ok(todo) => todos.push(todo), ++ Err(e) => errors.push(e), ++ }; ++ } ++ ++ Ok((todos, errors)) ++} ++ ++/// Deletes a todo list ++pub async fn delete_todo(os: &Os, id: &str) -> Result<()> { ++ os.fs.remove_file(id_to_path(os, id)?).await?; ++ Ok(()) ++} ++ ++/// Contains the command definitions that allow the model to create, ++/// modify, and mark todo list tasks as complete ++#[derive(Debug, Clone, Deserialize)] ++#[serde(tag = "command", rename_all = "camelCase")] ++pub enum TodoList { ++ // Creates a todo list ++ Create { ++ tasks: Vec, ++ todo_list_description: String, ++ }, ++ ++ // Completes tasks corresponding to the provided indices ++ // on the currently loaded todo list ++ Complete { ++ completed_indices: Vec, ++ context_update: String, ++ modified_files: Option>, ++ current_id: String, ++ }, ++ ++ // Loads a todo list with the given id ++ Load { ++ load_id: String, ++ }, ++ ++ // Inserts new tasks into the current todo list ++ Add { ++ new_tasks: Vec, ++ insert_indices: Vec, ++ new_description: Option, ++ current_id: String, ++ }, ++ ++ // Removes tasks from the current todo list ++ Remove { ++ remove_indices: Vec, ++ new_description: Option, ++ current_id: String, ++ }, ++} ++ ++impl TodoList { ++ pub async fn invoke(&self, os: &Os, output: &mut impl Write) -> Result { ++ let (state, id) = match self { ++ TodoList::Create { ++ tasks, ++ todo_list_description: task_description, ++ } => { ++ let new_id = generate_new_todo_id(); ++ ++ // Create a new todo list with the given tasks and save state ++ let state = TodoListState { ++ tasks: tasks.clone(), ++ completed: vec![false; tasks.len()], ++ description: task_description.clone(), ++ context: Vec::new(), ++ modified_files: Vec::new(), ++ id: new_id.clone(), ++ }; ++ state.save(os, &new_id).await?; ++ state.display_list(output)?; ++ (state, new_id) ++ }, ++ TodoList::Complete { ++ completed_indices, ++ context_update, ++ modified_files, ++ current_id: id, ++ } => { ++ let mut state = TodoListState::load(os, id).await?; ++ ++ for i in completed_indices.iter() { ++ state.completed[*i] = true; ++ } ++ ++ state.context.push(context_update.clone()); ++ ++ if let Some(files) = modified_files { ++ state.modified_files.extend_from_slice(files); ++ } ++ state.save(os, id).await?; ++ ++ // As tasks are being completed, display only the newly completed tasks ++ // and the next. Only display the whole list when all tasks are completed ++ let last_completed = completed_indices.iter().max().unwrap(); ++ if *last_completed == state.tasks.len() - 1 || state.completed.iter().all(|c| *c) { ++ state.display_list(output)?; ++ } else { ++ let mut display_list = TodoListState { ++ tasks: completed_indices.iter().map(|i| state.tasks[*i].clone()).collect(), ++ ..Default::default() ++ }; ++ for _ in 0..completed_indices.len() { ++ display_list.completed.push(true); ++ } ++ ++ // For next state, mark it true/false depending on actual completion state ++ // This only matters when the model skips around tasks ++ display_list.tasks.push(state.tasks[*last_completed + 1].clone()); ++ display_list.completed.push(state.completed[*last_completed + 1]); ++ ++ display_list.display_list(output)?; ++ } ++ (state, id.clone()) ++ }, ++ TodoList::Load { load_id: id } => { ++ let state = TodoListState::load(os, id).await?; ++ state.display_list(output)?; ++ (state, id.clone()) ++ }, ++ TodoList::Add { ++ new_tasks, ++ insert_indices, ++ new_description, ++ current_id: id, ++ } => { ++ let mut state = TodoListState::load(os, id).await?; ++ for (i, task) in insert_indices.iter().zip(new_tasks.iter()) { ++ state.tasks.insert(*i, task.clone()); ++ state.completed.insert(*i, false); ++ } ++ if let Some(description) = new_description { ++ state.description = description.clone(); ++ } ++ state.save(os, id).await?; ++ state.display_list(output)?; ++ (state, id.clone()) ++ }, ++ TodoList::Remove { ++ remove_indices, ++ new_description, ++ current_id: id, ++ } => { ++ let mut state = TodoListState::load(os, id).await?; ++ ++ // Remove entries in reverse order so indices aren't mismatched ++ let mut remove_indices = remove_indices.clone(); ++ remove_indices.sort(); ++ for i in remove_indices.iter().rev() { ++ state.tasks.remove(*i); ++ state.completed.remove(*i); ++ } ++ if let Some(description) = new_description { ++ state.description = description.clone(); ++ } ++ state.save(os, id).await?; ++ state.display_list(output)?; ++ (state, id.clone()) ++ }, ++ }; ++ ++ let invoke_output = format!("TODO LIST STATE: {}\n\n ID: {id}", serde_json::to_string(&state)?); ++ Ok(InvokeOutput { ++ output: super::OutputKind::Text(invoke_output), ++ }) ++ } ++ ++ pub async fn validate(&mut self, os: &Os) -> Result<()> { ++ match self { ++ TodoList::Create { ++ tasks, ++ todo_list_description: task_description, ++ } => { ++ if tasks.is_empty() { ++ bail!("No tasks were provided"); ++ } else if tasks.iter().any(|task| task.trim().is_empty()) { ++ bail!("Tasks cannot be empty"); ++ } else if task_description.is_empty() { ++ bail!("No task description was provided"); ++ } ++ }, ++ TodoList::Complete { ++ completed_indices, ++ context_update, ++ current_id, ++ .. ++ } => { ++ let state = TodoListState::load(os, current_id).await?; ++ if completed_indices.is_empty() { ++ bail!("At least one completed index must be provided"); ++ } else if context_update.is_empty() { ++ bail!("No context update was provided"); ++ } ++ for i in completed_indices.iter() { ++ if *i >= state.tasks.len() { ++ bail!("Index {i} is out of bounds for length {}, ", state.tasks.len()); ++ } ++ } ++ }, ++ TodoList::Load { load_id: id } => { ++ let state = TodoListState::load(os, id).await?; ++ if state.tasks.is_empty() { ++ bail!("Loaded todo list is empty"); ++ } ++ }, ++ TodoList::Add { ++ new_tasks, ++ insert_indices, ++ new_description, ++ current_id: id, ++ } => { ++ let state = TodoListState::load(os, id).await?; ++ if new_tasks.iter().any(|task| task.trim().is_empty()) { ++ bail!("New tasks cannot be empty"); ++ } else if has_duplicates(insert_indices) { ++ bail!("Insertion indices must be unique") ++ } else if new_tasks.len() != insert_indices.len() { ++ bail!("Must provide an index for every new task"); ++ } else if new_description.is_some() && new_description.as_ref().unwrap().trim().is_empty() { ++ bail!("New description cannot be empty"); ++ } ++ for i in insert_indices.iter() { ++ if *i > state.tasks.len() { ++ bail!("Index {i} is out of bounds for length {}, ", state.tasks.len()); ++ } ++ } ++ }, ++ TodoList::Remove { ++ remove_indices, ++ new_description, ++ current_id: id, ++ } => { ++ let state = TodoListState::load(os, id).await?; ++ if has_duplicates(remove_indices) { ++ bail!("Removal indices must be unique") ++ } else if new_description.is_some() && new_description.as_ref().unwrap().trim().is_empty() { ++ bail!("New description cannot be empty"); ++ } ++ for i in remove_indices.iter() { ++ if *i >= state.tasks.len() { ++ bail!("Index {i} is out of bounds for length {}, ", state.tasks.len()); ++ } ++ } ++ }, ++ } ++ Ok(()) ++ } ++} ++ ++/// Generated by Q ++fn has_duplicates(vec: &[T]) -> bool ++where ++ T: std::hash::Hash + Eq, ++{ ++ let mut seen = HashSet::with_capacity(vec.len()); ++ vec.iter().any(|item| !seen.insert(item)) ++} +diff --git a/crates/chat-cli/src/cli/chat/tools/tool_index.json b/crates/chat-cli/src/cli/chat/tools/tool_index.json +index 067e5df1ec..44def78fca 100644 +--- a/crates/chat-cli/src/cli/chat/tools/tool_index.json ++++ b/crates/chat-cli/src/cli/chat/tools/tool_index.json +@@ -281,5 +281,88 @@ + "command" + ] + } ++ }, ++ "todo_list": { ++ "name": "todo_list", ++ "description": "A tool for creating a TODO list and keeping track of tasks. This tool should be requested EVERY time the user gives you a task that will take multiple steps. A TODO list should be made BEFORE executing any steps. Steps should be marked off AS YOU COMPLETE THEM. DO NOT display your own tasks or todo list AT ANY POINT; this is done for you. Complete the tasks in the same order that you provide them. If the user tells you to skip a step, DO NOT mark it as completed.", ++ "input_schema": { ++ "type": "object", ++ "properties": { ++ "command": { ++ "type": "string", ++ "enum": [ ++ "create", ++ "complete", ++ "load", ++ "add", ++ "remove" ++ ], ++ "description": "The command to run. Allowed options are `create`, `complete`, `load`, `add`, and `remove`." ++ }, ++ "tasks": { ++ "description": "Required parameter of `create` command containing the list of DISTINCT tasks to be added to the TODO list.", ++ "type": "array", ++ "items": { ++ "type": "string" ++ } ++ }, ++ "todo_list_description": { ++ "description": "Required parameter of `create` command containing a BRIEF summary of the todo list being created. The summary should be detailed enough to refer to without knowing the problem context beforehand.", ++ "type": "string" ++ }, ++ "completed_indices": { ++ "description": "Required parameter of `complete` command containing the 0-INDEXED numbers of EVERY completed task. Each task should be marked as completed IMMEDIATELY after it is finished.", ++ "type": "array", ++ "items": { ++ "type": "integer" ++ } ++ }, ++ "context_update": { ++ "description": "Required parameter of `complete` command containing important task context. Use this command to track important information about the task AND information about files you have read.", ++ "type": "string" ++ }, ++ "modified_files": { ++ "description": "Optional parameter of `complete` command containing a list of paths of files that were modified during the task. This is useful for tracking file changes that are important to the task.", ++ "type": "array", ++ "items": { ++ "type": "string" ++ } ++ }, ++ "load_id": { ++ "description": "Required parameter of `load` command containing ID of todo list to load", ++ "type": "string" ++ }, ++ "current_id": { ++ "description": "Required parameter of `complete`, `add`, and `remove` commands containing the ID of the currently loaded todo list. The ID will ALWAYS be provided after every `todo_list` call after the serialized todo list state.", ++ "type": "string" ++ }, ++ "new_tasks": { ++ "description": "Required parameter of `add` command containing a list of new tasks to be added to the to-do list.", ++ "type": "array", ++ "items": { ++ "type": "string" ++ } ++ }, ++ "insert_indices": { ++ "description": "Required parameter of `add` command containing a list of 0-INDEXED positions to insert the new tasks. There MUST be an index for every new task being added.", ++ "type": "array", ++ "items": { ++ "type": "integer" ++ } ++ }, ++ "new_description": { ++ "description": "Optional parameter of `add` and `remove` containing a new todo list description. Use this when the updated set of tasks significantly change the goal or overall procedure of the todo list.", ++ "type": "string" ++ }, ++ "remove_indices": { ++ "description": "Required parameter of `remove` command containing a list of 0-INDEXED positions of tasks to remove.", ++ "type": "array", ++ "items": { ++ "type": "integer" ++ } ++ } ++ }, ++ "required": ["command"] ++ } + } + } +\ No newline at end of file +diff --git a/crates/chat-cli/src/cli/mod.rs b/crates/chat-cli/src/cli/mod.rs +index 33238b9da3..803c35d5c5 100644 +--- a/crates/chat-cli/src/cli/mod.rs ++++ b/crates/chat-cli/src/cli/mod.rs +@@ -18,6 +18,7 @@ use std::process::ExitCode; + use agent::AgentArgs; + use anstream::println; + pub use chat::ConversationState; ++pub use chat::tools::todo::TodoListState; + use clap::{ + ArgAction, + CommandFactory,