simple-task-master
Version:
A simple command-line task management tool
901 lines (711 loc) • 22.9 kB
Markdown
# Integration Guide for External Tools
**Date**: 2025-01-19
**Version**: 1.0.0
**Feature**: Unknown Field Support for External Tool Integration
## Table of Contents
1. [Overview](#overview)
2. [Unknown Field Support](#unknown-field-support)
3. [Integration Patterns](#integration-patterns)
4. [Field Naming Conventions](#field-naming-conventions)
5. [Example Integrations](#example-integrations)
- [AutoAgent Integration](#autoagent-integration)
- [GitHub Actions Integration](#github-actions-integration)
- [JIRA Integration](#jira-integration)
- [Time Tracking Integration](#time-tracking-integration)
6. [Best Practices](#best-practices)
7. [API Reference](#api-reference)
8. [Troubleshooting](#troubleshooting)
9. [Performance Considerations](#performance-considerations)
10. [Security Considerations](#security-considerations)
## Overview
Simple Task Master (STM) allows external tools to seamlessly integrate by supporting arbitrary "unknown fields" in task frontmatter. This enables tools to add custom metadata without modifying STM's core functionality.
### Key Benefits
- **Zero Configuration**: No STM modifications needed to add custom fields
- **Type Preservation**: Complex data types (objects, arrays, numbers) are preserved
- **Full API Support**: Unknown fields work through CLI, library API, and direct file manipulation
- **Backward Compatible**: Existing integrations continue working without changes
## Unknown Field Support
### How It Works
STM uses YAML frontmatter in markdown files to store task metadata. While STM validates its core fields (id, title, status, etc.), it preserves any additional fields without validation:
```yaml
# STM Core Fields (validated)
id: 123
title: "Implement user authentication"
status: "in-progress"
schema: 1
created: "2025-01-19T10:00:00.000Z"
updated: "2025-01-19T15:30:00.000Z"
tags: ["feature", "security"]
dependencies: []
# External Tool Fields (preserved as-is)
github_issue: 456
assignee: "john.doe@company.com"
priority: "high"
automation_config:
auto_close: true
notify_on_complete: ["team-channel"]
```
### Supported Data Types
Unknown fields support all YAML data types:
- **Strings**: `field: "text value"`
- **Numbers**: `priority: 1`, `score: 95.5`
- **Booleans**: `reviewed: true`
- **Arrays**: `labels: ["bug", "urgent"]`
- **Objects**: `metadata: { version: "1.0", author: "bot" }`
- **Null**: `parent_task: null`
- **Dates**: `deadline: "2025-02-01T00:00:00.000Z"`
## Integration Patterns
### 1. Direct File Manipulation
Write directly to STM's task files:
```javascript
// Node.js example
import fs from 'fs/promises';
import yaml from 'js-yaml';
async function addCustomMetadata(taskPath, metadata) {
const content = await fs.readFile(taskPath, 'utf8');
const [, frontmatter, body] = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
const data = yaml.load(frontmatter);
// Add custom fields
data.tool_name = 'MyIntegration';
data.tool_version = '2.1.0';
data.custom_metadata = metadata;
const updatedContent = `---\n${yaml.dump(data)}---\n${body}`;
await fs.writeFile(taskPath, updatedContent);
}
```
### 2. CLI Integration
Use STM's CLI to update fields:
```bash
# Add single field
stm update 123 assignee=john.doe@company.com
# Add multiple fields
stm update 123 \
priority=high \
sprint=Sprint-23 \
team=backend \
estimated_hours=8
# Complex values (as JSON strings)
stm update 123 metadata='{"source": "api", "version": "2.0"}'
```
### 3. Library API Integration
```typescript
import { TaskManager } from 'simple-task-master';
const taskManager = await TaskManager.create();
// Create task with custom fields
const task = await taskManager.create({
title: 'API Integration Task',
// @ts-expect-error - Unknown fields require type assertion
integration_id: 'EXT-123',
external_status: 'synced',
metadata: {
source: 'external-api',
timestamp: new Date().toISOString()
}
});
// Update with custom fields
await taskManager.update(task.id, {
// @ts-expect-error - Unknown fields require type assertion
sync_status: 'completed',
last_sync: new Date().toISOString()
});
```
## Field Naming Conventions
### Recommended Patterns
1. **Namespace Your Fields**: Prevent conflicts with future STM fields
```yaml
# Good: Namespaced
autoagent_status: "active"
autoagent_config:
retry_count: 3
# Avoid: Generic names
status: "active" # Conflicts with STM's status field
config: {} # Too generic
```
2. **Use Descriptive Prefixes**: Make field purpose clear
```yaml
# Good: Clear purpose
github_issue_number: 123
jira_ticket_key: "PROJ-456"
ci_build_status: "passing"
# Avoid: Ambiguous
issue: 123
ticket: "PROJ-456"
build: "passing"
```
3. **Follow Consistent Casing**: Use snake_case for compatibility
```yaml
# Recommended: snake_case
external_task_id: "ABC-123"
last_sync_date: "2025-01-19"
# Also valid but less portable
externalTaskId: "ABC-123" # camelCase
external-task-id: "ABC-123" # kebab-case
```
### Reserved Patterns
Avoid these patterns to prevent future conflicts:
- `stm_*`: Reserved for future STM features
- Single-word generic terms: `data`, `metadata`, `info`, `config`
- STM core field names: `id`, `title`, `status`, `schema`, `created`, `updated`
### Field Name Validation Rules
Field names must:
- Not contain newlines (`\n`) or carriage returns (`\r`)
- Not have leading or trailing whitespace
- Not contain control characters (ASCII 0-31, except tab)
- Be non-empty after the `=` sign in CLI assignments
Valid special characters in field names:
- Dots: `field.with.dots`
- Dashes: `field-with-dashes`
- Underscores: `field_with_underscores`
- Most symbols: `@`, `#`, `$`, `%`, etc.
## Example Integrations
### AutoAgent Integration
AutoAgent can track task automation status and configuration:
```yaml
# STM Core Fields
id: 789
title: "Automated deployment pipeline"
status: "in-progress"
tags: ["automation", "devops"]
# AutoAgent Fields
autoagent_assigned: true
autoagent_version: "3.2.0"
autoagent_config:
automation_level: "full"
retry_on_failure: true
max_retries: 3
notification_channels:
- slack: "#deployments"
- email: "devops@company.com"
autoagent_status:
last_run: "2025-01-19T14:30:00.000Z"
runs_completed: 15
success_rate: 0.93
next_scheduled: "2025-01-19T16:00:00.000Z"
github_integration:
issue_number: 234
pr_number: 567
workflow_runs: [1234567, 1234568]
```
**CLI Usage**:
```bash
# Mark task as assigned to AutoAgent
stm update 789 autoagent_assigned=true
# Update automation configuration
stm update 789 \
autoagent_config='{"automation_level":"full","retry_on_failure":true}'
# Update status after run
stm update 789 \
autoagent_status='{"last_run":"2025-01-19T14:30:00.000Z","runs_completed":16}'
```
**API Usage**:
```typescript
// AutoAgent TypeScript Integration
interface AutoAgentMetadata {
autoagent_assigned: boolean;
autoagent_version: string;
autoagent_config: {
automation_level: 'full' | 'partial' | 'monitor';
retry_on_failure: boolean;
max_retries: number;
notification_channels: Array<Record<string, string>>;
};
autoagent_status: {
last_run: string;
runs_completed: number;
success_rate: number;
next_scheduled?: string;
};
}
async function assignToAutoAgent(taskId: number): Promise<void> {
const taskManager = await TaskManager.create();
const updates: Partial<AutoAgentMetadata> = {
autoagent_assigned: true,
autoagent_version: '3.2.0',
autoagent_config: {
automation_level: 'full',
retry_on_failure: true,
max_retries: 3,
notification_channels: [
{ slack: '#deployments' },
{ email: 'devops@company.com' }
]
}
};
// @ts-expect-error - Unknown fields require type assertion
await taskManager.update(taskId, updates);
}
```
### GitHub Actions Integration
Track GitHub Actions workflow status:
```yaml
# STM Core Fields
id: 456
title: "Fix security vulnerability"
status: "in-progress"
tags: ["security", "bug"]
# GitHub Actions Fields
github_workflow:
workflow_id: "security-scan.yml"
run_id: 987654321
run_number: 42
status: "in_progress"
conclusion: null
started_at: "2025-01-19T12:00:00Z"
html_url: "https://github.com/org/repo/actions/runs/987654321"
github_metadata:
repository: "org/repo"
branch: "fix/security-issue"
commit_sha: "abc123def456"
actor: "dependabot[bot]"
event: "pull_request"
github_outputs:
vulnerabilities_found: 2
vulnerabilities_fixed: 1
scan_duration_seconds: 240
```
**GitHub Actions Workflow Example**:
```yaml
# .github/workflows/update-stm-task.yml
name: Update STM Task
on:
workflow_run:
workflows: ["Security Scan"]
types: [completed]
jobs:
update-task:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install STM
run: npm install -g simple-task-master
- name: Update task with workflow status
run: |
stm update ${{ env.TASK_ID }} \
github_workflow.run_id=${{ github.run_id }} \
github_workflow.status=completed \
github_workflow.conclusion=${{ github.event.workflow_run.conclusion }} \
github_outputs.scan_duration_seconds=${{ env.SCAN_DURATION }}
```
### JIRA Integration
Sync with JIRA issue tracking:
```yaml
# STM Core Fields
id: 234
title: "Implement user preferences API"
status: "in-progress"
tags: ["feature", "api", "backend"]
# JIRA Integration Fields
jira_issue:
key: "PROJ-1234"
type: "Story"
priority: "High"
story_points: 8
sprint: "Sprint 23"
epic: "PROJ-1000"
labels: ["api", "user-preferences", "backend"]
jira_status:
workflow_status: "In Development"
assignee: "john.doe"
reporter: "jane.smith"
created: "2025-01-15T09:00:00.000Z"
updated: "2025-01-19T14:45:00.000Z"
due_date: "2025-01-25T17:00:00.000Z"
jira_links:
web_url: "https://company.atlassian.net/browse/PROJ-1234"
api_url: "https://company.atlassian.net/rest/api/2/issue/PROJ-1234"
related_issues: ["PROJ-1233", "PROJ-1235"]
blocks: ["PROJ-1236"]
```
**JIRA Webhook Integration**:
```javascript
// Express.js webhook handler
app.post('/webhooks/jira', async (req, res) => {
const { issue, webhookEvent } = req.body;
// Find STM task by JIRA key
const taskId = await findTaskByJiraKey(issue.key);
if (taskId) {
// Update STM task with JIRA data
await exec(`stm update ${taskId} \
jira_issue.priority="${issue.fields.priority.name}" \
jira_status.workflow_status="${issue.fields.status.name}" \
jira_status.assignee="${issue.fields.assignee?.name || 'unassigned'}" \
jira_status.updated="${issue.fields.updated}"`);
}
res.status(200).send('OK');
});
```
### Time Tracking Integration
Track time spent on tasks:
```yaml
---
# STM Core Fields
id: 567
title: "Code review for feature branch"
status: "done"
tags: ["review", "quality"]
# Time Tracking Fields
time_tracking:
estimated_hours: 4
actual_hours: 5.5
billable: true
rate_per_hour: 150
currency: "USD"
time_entries:
- date: "2025-01-18"
hours: 2.5
description: "Initial review and feedback"
user: "senior.dev@company.com"
- date: "2025-01-19"
hours: 3
description: "Follow-up review and approval"
user: "senior.dev@company.com"
time_summary:
total_hours: 5.5
total_cost: 825
efficiency_ratio: 0.73 # estimated/actual
completed_date: "2025-01-19T16:00:00.000Z"
```
**Time Tracking CLI Script**:
```bash
#!/bin/bash
# track-time.sh - Add time entry to STM task
TASK_ID=$1
HOURS=$2
DESCRIPTION=$3
USER=$(git config user.email)
DATE=$(date -u +"%Y-%m-%d")
# Get current time entries
CURRENT=$(stm show $TASK_ID --json | jq -r '.time_entries // []')
# Add new entry
NEW_ENTRY=$(jq -n \
--arg date "$DATE" \
--arg hours "$HOURS" \
--arg desc "$DESCRIPTION" \
--arg user "$USER" \
'{date: $date, hours: ($hours | tonumber), description: $desc, user: $user}')
# Update task
UPDATED_ENTRIES=$(echo "$CURRENT" | jq ". + [$NEW_ENTRY]")
stm update $TASK_ID time_entries="$UPDATED_ENTRIES"
# Update total hours
TOTAL_HOURS=$(echo "$UPDATED_ENTRIES" | jq '[.[].hours] | add')
stm update $TASK_ID time_summary.total_hours="$TOTAL_HOURS"
```
## Best Practices
### 1. Data Type Consistency
Maintain consistent data types for your fields:
```yaml
# Good: Consistent types
priority: 1 # Always number
estimated_hours: 8.5 # Always number
tags: ["bug", "urgent"] # Always array
# Bad: Inconsistent types
priority: "1" # Sometimes string
priority: 1 # Sometimes number
estimated_hours: "8h" # String format varies
```
### 2. Structured Namespacing
Use structured objects for related data:
```yaml
# Good: Structured
github:
issue: 123
pr: 456
labels: ["bug", "help-wanted"]
# Less organized
github_issue: 123
github_pr: 456
github_labels: ["bug", "help-wanted"]
```
### 3. Avoid Field Proliferation
Limit the number of top-level fields:
```yaml
# Good: Organized metadata
integration_metadata:
source: "api"
version: "2.0"
timestamp: "2025-01-19T10:00:00Z"
request_id: "abc-123"
# Avoid: Too many top-level fields
source: "api"
version: "2.0"
timestamp: "2025-01-19T10:00:00Z"
request_id: "abc-123"
```
### 4. Document Your Schema
Create documentation for your custom fields:
```yaml
# .stm/schemas/my-tool-fields.md
## My Tool Custom Fields
### `mytool_status` (string)
Current synchronization status with My Tool.
Values: "synced", "pending", "error", "disabled"
### `mytool_id` (string)
Unique identifier in My Tool's system.
Format: "MT-XXXXX" where X is a digit
### `mytool_metadata` (object)
Additional metadata from My Tool:
- `last_sync` (string): ISO 8601 timestamp
- `sync_errors` (array): List of error messages
- `version` (string): My Tool API version
```
### 5. Handle Missing Fields Gracefully
Always check for field existence:
```javascript
// Good: Defensive programming
const priority = task.custom_priority || 'medium';
const assignee = task.assignee || 'unassigned';
// Bad: Assumes fields exist
const priority = task.custom_priority; // Could be undefined
const teamSize = task.team.members.length; // Could throw
```
## API Reference
### CLI Commands
#### Update with Unknown Fields
```bash
stm update <task-id> <field>=<value> [<field>=<value> ...]
```
Examples:
```bash
# Single field
stm update 123 assignee=john.doe
# Multiple fields
stm update 123 priority=high team=backend sprint=23
# Complex values (JSON string)
stm update 123 config='{"auto_close": true, "notify": ["slack"]}'
# Empty value (allowed for unknown fields)
stm update 123 cleared_field=
```
#### Array Operations (Known Fields Only)
```bash
# These only work with known array fields (tags, dependencies)
stm update 123 tags+=urgent
stm update 123 tags-=low-priority
# Unknown fields don't support += or -=
stm update 123 custom_tags+=new # ERROR
```
### Library API
```typescript
// Type assertion for unknown fields
interface CustomFields {
[key: string]: unknown;
}
// Create with unknown fields
const task = await taskManager.create({
title: 'Task Title',
...customFields as any
});
// Update with unknown fields
await taskManager.update(taskId, {
...updates as any
});
// Access unknown fields
const customValue = (task as any).custom_field;
```
### Direct File API
```javascript
import { FrontmatterParser } from 'simple-task-master';
// Parse task file
const content = await fs.readFile(taskPath, 'utf8');
const { data, content: body } = FrontmatterParser.parse(content);
// Modify unknown fields
data.my_custom_field = 'new value';
data.my_metadata = { updated: Date.now() };
// Write back
const updated = FrontmatterParser.stringify(body, data);
await fs.writeFile(taskPath, updated);
```
## Troubleshooting
### Common Issues
#### 1. Field Not Appearing in Output
**Problem**: Updated field doesn't show in `stm show` or `stm list`
**Solution**: Check that you're using JSON output format:
```bash
stm show 123 --json # Shows all fields
stm list --json # Shows all fields
```
#### 2. Complex Values Treated as Strings
**Problem**: Objects/arrays saved as strings via CLI
**Solution**: CLI always saves as strings. Use the API or direct file manipulation for complex types:
```bash
# CLI saves as string
stm update 123 data='{"key": "value"}' # Saved as string
# Use API for proper types
await taskManager.update(123, {
data: { key: "value" } // Saved as object
});
```
#### 3. Field Name Validation Errors
**Problem**: "Field names cannot contain control characters"
**Solution**: Ensure field names are clean:
```javascript
// Clean field name
const fieldName = userInput
.trim()
.replace(/[\x00-\x08\x0B-\x1F]/g, ''); // Remove control chars
```
#### 4. Array Operations on Unknown Fields
**Problem**: `+=` or `-=` operations fail on custom fields
**Solution**: These operations only work on known array fields. For unknown fields, manage arrays manually:
```bash
# Get current value
CURRENT=$(stm show 123 --json | jq -r '.custom_array // []')
# Modify array
UPDATED=$(echo "$CURRENT" | jq '. + ["new-item"]')
# Save back
stm update 123 custom_array="$UPDATED"
```
### Debugging Tips
1. **Verify File Format**: Check the markdown file directly
```bash
cat tasks/123-*.md
```
2. **Validate YAML**: Ensure frontmatter is valid
```bash
# Extract frontmatter
sed -n '/^---$/,/^---$/p' tasks/123-*.md | sed '1d;$d' | yamllint -
```
3. **Check Task Loading**: Use the library API to debug
```javascript
const task = await taskManager.get(123);
console.log(JSON.stringify(task, null, 2));
```
4. **Monitor File Changes**: Watch for external modifications
```bash
fswatch tasks/ | while read path; do
echo "Changed: $path"
stm show $(basename $path | cut -d- -f1) --json
done
```
## Performance Considerations
### Field Count Limits
STM enforces performance limits to ensure optimal operation:
- **Maximum Fields**: 100 total fields per task (including core and unknown fields)
- **Maximum Task Size**: 1MB total content (including all fields and markdown content)
- **Performance Targets**: Task operations complete in <100ms with 50+ fields
### Performance Characteristics
Based on performance testing with unknown fields:
1. **Task Creation Performance**:
- 50 unknown fields: <100ms average operation time
- 100 fields (limit): ~150-200ms operation time
- Memory usage scales linearly with field count
2. **Update Performance**:
- Similar performance characteristics to creation
- Batch updates are more efficient than individual field updates
3. **Query Performance**:
- JSON output format has minimal overhead
- Large field counts may impact table formatting performance
### Best Practices for Performance
1. **Use Structured Data**: Group related fields in objects
```yaml
# Good: Structured data reduces top-level field count
automation_status:
last_run: "2025-01-19T10:00:00Z"
runs_completed: 15
success_rate: 0.93
# Less efficient: Many top-level fields
automation_last_run: "2025-01-19T10:00:00Z"
automation_runs_completed: 15
automation_success_rate: 0.93
```
2. **Batch Operations**: Update multiple fields in one operation
```bash
# Good: Single update operation
stm update 123 field1=value1 field2=value2 field3=value3
# Less efficient: Multiple operations
stm update 123 field1=value1
stm update 123 field2=value2
stm update 123 field3=value3
```
3. **Monitor Field Growth**: Regularly audit unknown field usage
```bash
# Check field count for a task
stm show 123 --json | jq 'keys | length'
# Find tasks with many fields
for task in tasks/*.md; do
count=$(stm show $(basename $task | cut -d- -f1) --json | jq 'keys | length')
if [ $count -gt 50 ]; then
echo "Task $(basename $task): $count fields"
fi
done
```
### Memory Usage Guidelines
- Base task overhead: ~1KB
- Per field overhead: ~100-500 bytes (depending on value size)
- 50 fields with small values: ~25KB total
- 100 fields with small values: ~50KB total
For large-scale integrations, consider:
- Storing large data externally and referencing it
- Using compression for large string values
- Implementing field cleanup for obsolete data
## Security Considerations
### Input Validation
STM performs minimal validation on unknown fields:
1. **Field Names**: Basic sanitization (no newlines, control chars)
2. **Field Values**: No validation - tools are responsible
3. **Size Limits**: Total task size limited to 1MB
### Best Practices for External Tools
1. **Sanitize Input**: Validate data before storing
```javascript
// Sanitize user input
function sanitizeFieldValue(value) {
if (typeof value === 'string') {
// Remove potential script injections
return value.replace(/<script[^>]*>.*?<\/script>/gi, '');
}
return value;
}
```
2. **Limit Field Count**: Prevent field proliferation
```javascript
const MAX_CUSTOM_FIELDS = 50;
if (Object.keys(customFields).length > MAX_CUSTOM_FIELDS) {
throw new Error('Too many custom fields');
}
```
3. **Validate Data Types**: Ensure type safety
```javascript
// Type validation
function validateCustomFields(fields) {
const schema = {
priority: 'number',
assignee: 'string',
tags: 'array'
};
for (const [key, expectedType] of Object.entries(schema)) {
if (key in fields) {
const actualType = Array.isArray(fields[key]) ? 'array' : typeof fields[key];
if (actualType !== expectedType) {
throw new Error(`Field ${key} must be ${expectedType}`);
}
}
}
}
```
4. **Namespace Sensitive Data**: Use clear prefixes
```yaml
# Clear indication of sensitive data
_private_api_key: "encrypted:AES256:..."
_internal_user_id: "user-12345"
```
### Data Protection
Remember that STM tasks are stored as plain text files:
- Don't store passwords or API keys
- Use references to secure storage instead
- Consider encryption for sensitive metadata
- Apply appropriate file system permissions
```bash
# Restrict task directory permissions
chmod 700 .stm/tasks/
```
## Conclusion
STM's unknown field support provides a flexible foundation for tool integration. By following the conventions and best practices in this guide, you can build robust integrations that enhance your task management workflow while maintaining compatibility with STM's core functionality.
For questions or issues with integration, consult the STM documentation or file an issue in the project repository.