Last active
July 27, 2025 17:23
-
-
Save dfinke/af1063ab9cea740b6eada9c1bdd26261 to your computer and use it in GitHub Desktop.
AI-Powered To-Do List in PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# using psaisuite | |
# Ensure PSAISuite is available | |
#Requires -Module PSAISuite | |
<# | |
.SYNOPSIS | |
Outputs text using the 'glow' markdown renderer if available, otherwise falls back to Write-Host. | |
.DESCRIPTION | |
This function checks if the 'glow' command is available. If so, it pipes the input to 'glow' for rich markdown rendering. Otherwise, it outputs the text using Write-Host. | |
.PARAMETER InputObject | |
The string to output. | |
#> | |
function Write-Glow { | |
param( | |
[Parameter(ValueFromPipeline = $true)] | |
[string]$InputObject | |
) | |
if (Get-Command glow -ErrorAction SilentlyContinue) { | |
$InputObject | glow | |
} | |
else { | |
Write-Host $InputObject | |
} | |
} | |
# Path to the JSON file for storing tasks | |
$TaskFile = "$PSScriptRoot\tasks.json" | |
# Path to config file | |
$ConfigFile = "$PSScriptRoot\config.json" | |
# Load model from config file if present, else use external or default | |
if (Test-Path $ConfigFile) { | |
$config = Get-Content $ConfigFile | ConvertFrom-Json | |
if ($config.Model) { $Model = $config.Model } | |
} | |
if (-not $Model) { $Model = 'github:openai/gpt-4.1' } | |
<# | |
.SYNOPSIS | |
Loads the current to-do list from disk. | |
.DESCRIPTION | |
Reads the tasks from the JSON file if it exists, otherwise returns an empty array. | |
#> | |
function Get-TaskList { | |
if (Test-Path $TaskFile) { | |
$tasks = Get-Content $TaskFile | ConvertFrom-Json | |
return $tasks | |
} | |
else { | |
@() | |
} | |
} | |
<# | |
.SYNOPSIS | |
Saves the current to-do list to disk. | |
.DESCRIPTION | |
Writes the provided tasks array to the JSON file. | |
.PARAMETER tasks | |
The array of task objects to save. | |
#> | |
function Save-TaskList { | |
param($tasks) | |
$tasks | ConvertTo-Json -Depth 5 | Set-Content $TaskFile | |
# No technical output for users | |
} | |
<# | |
.SYNOPSIS | |
Adds a new task to the to-do list. | |
.DESCRIPTION | |
Uses the AI model to extract task details from input text, adds the task, saves the list, and displays the updated list as a markdown table. | |
.PARAMETER InputText | |
The user's input describing the task to add. | |
#> | |
function Add-Task { | |
param( | |
[string]$InputText | |
) | |
# Use psaisuite to parse the input and extract details | |
$prompt = @" | |
Extract the following from this task description: | |
- Task title | |
- Due date (if any) | |
- Priority (high/medium/low, if mentioned) | |
Text: $InputText | |
Return as JSON. | |
"@ | |
$result = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
$task = $result | ConvertFrom-Json | |
$tasks = Get-TaskList | |
$newTask = [PSCustomObject]@{ | |
Id = ($tasks | Measure-Object).Count + 1 | |
Title = $task.'Task title' | |
DueDate = $task.'Due date' | |
Priority = $task.Priority | |
Status = 'Pending' | |
Notes = '' | |
} | |
$tasks += $newTask | |
Save-TaskList $tasks | |
Write-Host "Task added: $($newTask.Title)" -ForegroundColor Green | |
# Show updated list in markdown table | |
$prompt = @" | |
Convert the following to-do list JSON to a markdown table. Include all fields and format the table nicely. | |
$($tasks | ConvertTo-Json -Depth 5) | |
"@ | |
$result = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
Write-Glow $result | |
} | |
<# | |
.SYNOPSIS | |
Displays all tasks in the to-do list in plain text. | |
.DESCRIPTION | |
Prints each task with its details to the console. | |
#> | |
function Get-Task { | |
$tasks = Get-TaskList | |
if ($tasks.Count -eq 0) { | |
Write-Host "You have no tasks yet. Add a new one!" -ForegroundColor Yellow | |
return | |
} | |
$tasks | ForEach-Object { | |
Write-Host ("{0}. {1} (Priority: {2}, Due: {3}, Status: {4})" -f $_.Id, $_.Title, $_.Priority, $_.DueDate, $_.Status) | |
} | |
} | |
<# | |
.SYNOPSIS | |
Marks a task as complete by its ID. | |
.DESCRIPTION | |
Finds the task by ID, marks it as complete, and saves the updated list. | |
.PARAMETER Id | |
The ID of the task to mark as complete. | |
#> | |
function Complete-Task { | |
param( | |
[int]$Id | |
) | |
$tasks = Get-TaskList | |
$task = $tasks | Where-Object { $_.Id -eq $Id } | |
if ($null -eq $task) { | |
Write-Host "Sorry, I couldn't find that task." -ForegroundColor Yellow | |
return | |
} | |
$task.Status = 'Complete' | |
Save-TaskList $tasks | |
Write-Host "Great job! Task $Id marked as complete." -ForegroundColor Green | |
} | |
# On startup, get suggestions from AI | |
# Show a friendly welcome message | |
Write-Host "Welcome to your To-Do List!" -ForegroundColor Cyan | |
$tasks = Get-TaskList | |
$today = Get-Date | |
$todayFmt = $today.ToString('dddd MMMM dd, yyyy hh:mm tt') | |
$prompt = @" | |
Today is $todayFmt. | |
Here is my current to-do list: | |
$($tasks | ConvertTo-Json -Depth 5) | |
What should I focus on today? Are any tasks urgent or overdue? | |
"@ | |
$result = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
Write-Glow $result | |
# Pure NLP-driven main loop | |
while ($true) { | |
$userInput = Read-Host "What would you like to do? (e.g., 'call bank next week', 'what's on my list?')" | |
if ($userInput -match '^(exit|quit)$') { break } | |
$tasks = Get-TaskList | |
$today = Get-Date | |
$todayFmt = $today.ToString('dddd MMMM dd, yyyy hh:mm tt') | |
# Check if user wants to see the list | |
if ($userInput -match "(show|list|what's on my list | display).*") { | |
Write-Host "[AI] Calling AI model for: markdown table conversion" -ForegroundColor Magenta | |
$prompt = @" | |
Convert the following to-do list JSON to a markdown table. Include all fields and format the table nicely. | |
$($tasks | ConvertTo-Json -Depth 5) | |
"@ | |
$result = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
Write-Host "`nYour To-Do List in Markdown:`n" -ForegroundColor Yellow | |
Write-Glow $result | |
continue | |
} | |
$prompt = @" | |
Today is $todayFmt. | |
You are a helpful to-do list assistant. Here is the current list: | |
$($tasks | ConvertTo-Json -Depth 5) | |
User input: $userInput | |
Interpret the user's intent (add, list, complete, query, etc.), extract all relevant details, update the list if needed, and return a response for the user. If the list should be updated, return the new list as JSON under the key 'updatedList'. | |
When saving a due date, always use the format: 'Tuesday July 29, 2025 12:00 pm'. | |
"@ | |
$result = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
# Only process and display user-friendly output, suppressing raw AI/JSON output | |
if ($result -match '(?:"updatedList"|updatedList)\s*:\s*(\[[\s\S]*?\])') { | |
try { | |
$jsonMatch = $matches[1] | |
$updatedList = $jsonMatch | ConvertFrom-Json | |
Save-TaskList $updatedList | |
# Show updated list in markdown table | |
$prompt = @" | |
Convert the following to-do list JSON to a markdown table. Include all fields and format the table nicely. | |
$($updatedList | ConvertTo-Json -Depth 5) | |
"@ | |
$mdResult = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
Write-Glow $mdResult | |
# Show the rest of the AI response (excluding the updatedList JSON block), but only if it contains user-friendly text | |
$responseWithoutList = $result -replace '(?:"updatedList"|updatedList)\s*:\s*\[[\s\S]*?\][\s\r\n,]*', '' | |
if ($responseWithoutList.Trim()) { | |
Write-Glow $responseWithoutList | |
} | |
} | |
catch { | |
# If something goes wrong, show a friendly error | |
Write-Host "Sorry, something went wrong updating your list." -ForegroundColor Red | |
} | |
} | |
else { | |
# Check if the AI response contains task information without updatedList wrapper | |
if ($result -match '\{.*"task".*"due".*\}') { | |
try { | |
# Try to extract the task object and wrap it in an array | |
$taskMatch = [regex]::Match($result, '\{[^}]*"task"[^}]*\}') | |
if ($taskMatch.Success) { | |
$taskJson = "[$($taskMatch.Value)]" | |
$updatedList = $taskJson | ConvertFrom-Json | |
Save-TaskList $updatedList | |
# Show updated list in markdown table | |
$prompt = @" | |
Convert the following to-do list JSON to a markdown table. Include all fields and format the table nicely. | |
$($updatedList | ConvertTo-Json -Depth 5) | |
"@ | |
$mdResult = Invoke-ChatCompletion -Model $Model -Prompt $prompt | |
Write-Glow $mdResult | |
} | |
} | |
catch { | |
Write-Host "Sorry, something went wrong updating your list." -ForegroundColor Red | |
} | |
} | |
else { | |
# If no updatedList or task JSON, just show the AI's user-friendly response | |
Write-Glow $result | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment