diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..b71440d --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,217 @@ +name: Create Release + +on: + push: + branches: + - development + - production + +permissions: + contents: write + +jobs: + create-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Determine release metadata + id: release + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + branch="${GITHUB_REF_NAME}" + before="${{ github.event.before }}" + current_version="$(node -p "require('./package.json').version")" + + previous_version="" + if [[ "$before" != "0000000000000000000000000000000000000000" ]]; then + if git cat-file -e "${before}:package.json" 2>/dev/null; then + previous_version="$( + git show "${before}:package.json" \ + | node -p "JSON.parse(require('node:fs').readFileSync(0, 'utf8')).version" + )" + fi + fi + + version_changed=false + if [[ "$current_version" != "$previous_version" ]]; then + version_changed=true + fi + + if [[ "$branch" == "development" ]]; then + tag="v${current_version}-dev" + previous_tag="$( + git tag --list 'v*-dev' --sort=-creatordate \ + | grep -Fxv "$tag" \ + | head -n 1 || true + )" + elif [[ "$branch" == "production" ]]; then + tag="v${current_version}" + previous_tag="$( + git tag --list 'v*' --sort=-creatordate \ + | grep -Ev -- '-dev$' \ + | grep -Fxv "$tag" \ + | head -n 1 || true + )" + else + echo "release_action=skip" >> "$GITHUB_OUTPUT" + echo "Branch ${branch} is not configured for releases." + exit 0 + fi + + if gh release view "$tag" >/dev/null 2>&1; then + release_action="update" + else + release_action="create" + fi + + if [[ "$release_action" == "create" && "$version_changed" == "true" ]]; then + echo "Package version changed; creating ${tag}." + elif [[ "$release_action" == "create" ]]; then + echo "No release exists for ${tag}; creating it." + else + echo "Release ${tag} exists; updating its notes." + fi + + echo "release_action=${release_action}" >> "$GITHUB_OUTPUT" + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + echo "previous_tag=${previous_tag}" >> "$GITHUB_OUTPUT" + echo "version=${current_version}" >> "$GITHUB_OUTPUT" + echo "version_changed=${version_changed}" >> "$GITHUB_OUTPUT" + + - name: Generate release notes + if: steps.release.outputs.release_action != 'skip' + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + previous_tag="${{ steps.release.outputs.previous_tag }}" + + { + echo "## Changes" + echo + + if [[ -n "$previous_tag" ]]; then + echo "Commits since ${previous_tag}:" + echo + git log "${previous_tag}..${GITHUB_SHA}" --pretty=format:'- %s (%h)' + else + echo "Commits included in this release:" + echo + git log "${GITHUB_SHA}" --pretty=format:'- %s (%h)' + fi + } > release-notes.md + + if [[ ! -s release-notes.md ]]; then + printf '## Changes\n\n- No commits found.\n' > release-notes.md + fi + + echo "Prepared release notes for ${tag}:" + cat release-notes.md + + - name: Install dependencies + if: steps.release.outputs.release_action != 'skip' + run: npm ci + + - name: Build release assets + if: steps.release.outputs.release_action != 'skip' + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + asset_base="buddy-${tag}" + + rm -rf release-assets + mkdir -p release-assets + + pack_json="$(npm pack --json --pack-destination release-assets)" + pack_file="$( + node -e 'const data = JSON.parse(process.argv[1]); process.stdout.write(data[0].filename);' \ + "$pack_json" + )" + + mv "release-assets/${pack_file}" "release-assets/${asset_base}.tgz" + + temp_dir="$(mktemp -d)" + trap 'rm -rf "$temp_dir"' EXIT + + tar -xzf "release-assets/${asset_base}.tgz" -C "$temp_dir" + ( + cd "$temp_dir/package" + zip -rq "$GITHUB_WORKSPACE/release-assets/${asset_base}.zip" . + ) + + ls -lh release-assets + + - name: Update release tag + if: steps.release.outputs.release_action != 'skip' + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git tag -f "$tag" "${GITHUB_SHA}" + git push --force origin "refs/tags/${tag}" + + - name: Create GitHub release + if: steps.release.outputs.release_action == 'create' + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + gh release create "$tag" \ + --title "$tag" \ + --verify-tag \ + --notes-file release-notes.md + + - name: Update GitHub release notes + if: steps.release.outputs.release_action == 'update' + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + gh release edit "$tag" \ + --title "$tag" \ + --notes-file release-notes.md + + - name: Upload release assets + if: steps.release.outputs.release_action != 'skip' + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + tag="${{ steps.release.outputs.tag }}" + asset_base="buddy-${tag}" + + gh release upload "$tag" \ + "release-assets/${asset_base}.tgz" \ + "release-assets/${asset_base}.zip" \ + --clobber diff --git a/package.json b/package.json index e5e822c..1340f53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@teichai/buddy", - "version": "0.0.2", + "version": "0.0.3", "description": "Terminal-first AI assistant with onboarding, chat UI, and local or remote server support.", "license": "MIT", "type": "module", diff --git a/src/llm/system-prompt.ts b/src/llm/system-prompt.ts index d8b78dc..e378f0e 100644 --- a/src/llm/system-prompt.ts +++ b/src/llm/system-prompt.ts @@ -23,18 +23,18 @@ export function buildSystemPrompt(config: BuddyConfig, channel: PromptChannel = } return [ - `You are ${botName}, a local terminal-first AI assistant that helps the user inside a CLI application called buddy.`, + `You are ${botName}, a AI assistant that helps the user.`, userName ? `The user's name is ${userName}.` : "The user's name has not been configured.", "", "Your role:", - "- Help the user think, plan, write, edit, and operate files from a local assistant interface.", + "- Help the user think, plan, write, edit, and operate the users computer in some ways.", "- Be practical, accurate, and clear.", "- Prefer useful action and direct answers over vague commentary.", "", "Your environment:", - "- You are operating in a local assistant environment, not a web chat.", + "- You are operating in a assistant environment, not a web chat.", "- The user may expect help with files, code, configuration, and terminal-oriented tasks.", - "- You should act like a capable local assistant that can inspect and modify files through tools when those tools are available.", + "- You should act like a capable assistant that can inspect and modify files through tools when those tools are available.", `- Your default workspace is ${workspacePath}. Unless the user asks otherwise, you should treat that as the main place to read, create, edit, and organize files.`, "- When a file path is relative, treat it as relative to the workspace by default.", "- If the user says 'desktop', assume they mean this assistant's own desktop/local environment by default, not the OS Desktop folder.", @@ -51,11 +51,11 @@ export function buildSystemPrompt(config: BuddyConfig, channel: PromptChannel = : undefined, "- Do not claim you changed a file unless you actually used a file-writing tool successfully.", "- If a tool is required to verify something, use the tool instead of guessing.", - "- Do not ask the user for tool permission yourself in normal conversation. Attempt the tool call directly when it is appropriate.", + "- Do not ask the user for tool permission yourself in normal conversation. Attempt the tool call directly. If it requires permission, the user will be prompted automatically.", "- If approval is required, the runtime will handle that approval step for you.", - "- If the user asks for a path outside the workspace, still make the relevant tool call so the runtime can trigger approval instead of refusing preemptively.", + "- If the users task path is outside the workspace, still make the relevant tool call so the runtime can trigger approval instead of refusing preemptively.", "- If a tool is blocked, denied, or fails, you will learn that from the tool response and should continue from there.", - "- If you cannot complete an action, explain exactly what is blocked.", + "- If you cannot complete an action, explain exactly what is blocked. Make sure to try everything before giving up.", "", "Guardrails and restrictions:", `- Access level is currently set to ${config.restrictions.accessLevel}.`, @@ -69,9 +69,6 @@ export function buildSystemPrompt(config: BuddyConfig, channel: PromptChannel = "How to respond:", "- Be concise by default, but include enough detail to be useful.", "- Use clear, direct language and avoid filler.", - "- If the user asks for an explanation, give a structured explanation.", - "- If the user asks for help writing or editing something, provide concrete output rather than abstract advice.", - "- If there is uncertainty, say what is certain and what is uncertain.", channel === "discord" ? "- You are replying inside Discord, so avoid tables and other non-Discord markdown." : undefined, channel === "discord" ? "- Prefer short paragraphs, simple bullets, inline code, and fenced code blocks. Do not use markdown tables, footnotes, HTML, or other formatting that may render poorly in Discord."