Skip to content

Commit 80e1444

Browse files
Fixes
1 parent ad78503 commit 80e1444

8 files changed

Lines changed: 610 additions & 268 deletions

File tree

docs/database.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Database Schema
2+
3+
**SPEC-DB-001**
4+
5+
Three tables store AI summaries, tag definitions, and tag assignments.
6+
7+
```sql
8+
CREATE TABLE IF NOT EXISTS commands (
9+
command_id TEXT PRIMARY KEY,
10+
content_hash TEXT NOT NULL,
11+
summary TEXT NOT NULL,
12+
security_warning TEXT,
13+
last_updated TEXT NOT NULL
14+
);
15+
16+
CREATE TABLE IF NOT EXISTS tags (
17+
tag_id TEXT PRIMARY KEY,
18+
tag_name TEXT NOT NULL UNIQUE,
19+
description TEXT
20+
);
21+
22+
CREATE TABLE IF NOT EXISTS command_tags (
23+
command_id TEXT NOT NULL,
24+
tag_id TEXT NOT NULL,
25+
display_order INTEGER NOT NULL DEFAULT 0,
26+
PRIMARY KEY (command_id, tag_id),
27+
FOREIGN KEY (command_id) REFERENCES commands(command_id) ON DELETE CASCADE,
28+
FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE
29+
);
30+
```
31+
32+
CRITICAL: No backwards compatibility. If the database structure is wrong, the extension blows it away and recreates it from scratch.
33+
34+
## Implementation
35+
36+
**SPEC-DB-010**
37+
38+
- **Engine**: SQLite via `node-sqlite3-wasm`
39+
- **Location**: `{workspaceFolder}/.commandtree/commandtree.sqlite3`
40+
- **Runtime**: Pure WASM, no native compilation (~1.3 MB)
41+
- **CRITICAL**: `PRAGMA foreign_keys = ON;` MUST be executed on EVERY database connection
42+
- **Orphan Prevention**: `ensureCommandExists()` inserts placeholder command rows before adding tags
43+
- **API**: Synchronous, no async overhead for reads
44+
45+
### Test Coverage
46+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "initSchema is idempotent — calling twice succeeds"
47+
48+
## Commands Table
49+
50+
**SPEC-DB-020**
51+
52+
- **`command_id`**: `{type}:{filePath}:{name}` (PRIMARY KEY)
53+
- **`content_hash`**: SHA-256 hash for change detection (NOT NULL)
54+
- **`summary`**: AI-generated description, 1-3 sentences (NOT NULL)
55+
- **`security_warning`**: AI-detected security risk (nullable)
56+
- **`last_updated`**: ISO 8601 timestamp (NOT NULL)
57+
58+
### Test Coverage
59+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "inserts new command", "upsert updates content hash on conflict", "returns undefined for non-existent command"
60+
61+
## Tags Table
62+
63+
**SPEC-DB-030**
64+
65+
- **`tag_id`**: UUID primary key
66+
- **`tag_name`**: Tag identifier, UNIQUE (NOT NULL)
67+
- **`description`**: Optional description (nullable)
68+
69+
### Test Coverage
70+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "addTagToCommand creates tag and junction record", "getAllTagNames returns all distinct tags"
71+
72+
## Command Tags Junction Table
73+
74+
**SPEC-DB-040**
75+
76+
- **`command_id`**: FK to `commands.command_id` with CASCADE DELETE
77+
- **`tag_id`**: FK to `tags.tag_id` with CASCADE DELETE
78+
- **`display_order`**: Integer for ordering (default 0)
79+
- **Primary Key**: `(command_id, tag_id)`
80+
81+
### Test Coverage
82+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "addTagToCommand creates tag and junction record", "addTagToCommand is idempotent", "removeTagFromCommand removes junction record", "removeTagFromCommand succeeds for non-existent tag"
83+
84+
## Content Hashing
85+
86+
**SPEC-DB-050**
87+
88+
Content hashing is used for change detection to avoid re-processing unchanged commands.
89+
90+
### Test Coverage
91+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "returns consistent hash for same input", "returns different hash for different input", "returns 16-char hex string"

docs/parameters.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Parameterized Commands
2+
3+
**SPEC-PARAM-001**
4+
5+
Commands can accept user input at runtime through a flexible parameter system that adapts to different tool requirements.
6+
7+
## Parameter Definition
8+
9+
**SPEC-PARAM-010**
10+
11+
Parameters are defined during discovery with metadata describing how they should be collected and formatted:
12+
13+
```typescript
14+
{
15+
name: 'filter',
16+
description: 'Test filter expression',
17+
default: '',
18+
options: ['option1', 'option2'],
19+
format: 'flag',
20+
flag: '--filter'
21+
}
22+
```
23+
24+
### Test Coverage
25+
- [execution.e2e.test.ts](../src/test/e2e/execution.e2e.test.ts): "task with params has param definitions", "param with options creates quick pick choices", "param with default value provides placeholder"
26+
27+
## Parameter Formats
28+
29+
**SPEC-PARAM-020**
30+
31+
The `format` field controls how parameter values are inserted into commands:
32+
33+
| Format | Example Input | Example Output | Use Case |
34+
|--------|--------------|----------------|----------|
35+
| `positional` (default) | `value` | `command "value"` | Shell scripts, Python positional args |
36+
| `flag` | `value` | `command --flag "value"` | Named options (npm, dotnet test) |
37+
| `flag-equals` | `value` | `command --flag=value` | Equals-style flags (some CLIs) |
38+
| `dashdash-args` | `arg1 arg2` | `command -- arg1 arg2` | Runtime args (dotnet run, npm run) |
39+
40+
**Empty value behavior**: All formats skip adding anything to the command if the user provides an empty value, making all parameters effectively optional.
41+
42+
### Test Coverage
43+
- [taskRunner.unit.test.ts](../src/test/unit/taskRunner.unit.test.ts): "positional format wraps value in quotes", "positional is default when format is omitted", "flag format uses --name by default", "flag format uses custom flag when provided", "flag-equals format uses --name=value", "flag-equals format uses custom flag", "dashdash-args format prepends --", "empty value is skipped in buildCommand", "buildCommand with no params returns base command", "buildCommand with multiple params joins them", "buildCommand skips all empty values"
44+
45+
## Language-Specific Examples
46+
47+
**SPEC-PARAM-030**
48+
49+
### .NET Projects
50+
```typescript
51+
// dotnet run with runtime arguments
52+
{ name: 'args', format: 'dashdash-args', description: 'Runtime arguments' }
53+
// Result: dotnet run -- arg1 arg2
54+
55+
// dotnet test with filter
56+
{ name: 'filter', format: 'flag', flag: '--filter', description: 'Test filter' }
57+
// Result: dotnet test --filter "FullyQualifiedName~MyTest"
58+
```
59+
60+
### Shell Scripts
61+
```bash
62+
#!/bin/bash
63+
# @param environment Target environment (staging, production)
64+
# @param verbose Enable verbose output (default: false)
65+
```
66+
67+
### Python Scripts
68+
```python
69+
# @param config Config file path
70+
# @param debug Enable debug mode (default: False)
71+
```
72+
73+
### NPM Scripts
74+
For runtime args, use `dashdash-args` format:
75+
```typescript
76+
{ name: 'args', format: 'dashdash-args' }
77+
// Result: npm run start -- --port=3000
78+
```
79+
80+
## VS Code Tasks
81+
82+
**SPEC-PARAM-040**
83+
84+
VS Code tasks using `${input:*}` variables prompt automatically via the built-in input UI. These are handled natively by VS Code's task system.
85+
86+
### Test Coverage
87+
- [execution.e2e.test.ts](../src/test/e2e/execution.e2e.test.ts): "vscode task with inputs has parameter definitions"

docs/quick-launch.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Quick Launch
2+
3+
**SPEC-QL-001**
4+
5+
Users can star commands to pin them in a "Quick Launch" panel at the top of the tree view. Starred command identifiers are persisted as `quick` tags in the database.
6+
7+
## Adding to Quick Launch
8+
9+
**SPEC-QL-010**
10+
11+
Right-click a command and select "Add to Quick Launch" or use the `commandtree.addToQuick` command.
12+
13+
### Test Coverage
14+
- [quicktasks.e2e.test.ts](../src/test/e2e/quicktasks.e2e.test.ts): "addToQuick command is registered", "E2E: Add quick command → stored in junction table"
15+
16+
## Removing from Quick Launch
17+
18+
**SPEC-QL-020**
19+
20+
Right-click a quick command and select "Remove from Quick Launch" or use the `commandtree.removeFromQuick` command.
21+
22+
### Test Coverage
23+
- [quicktasks.e2e.test.ts](../src/test/e2e/quicktasks.e2e.test.ts): "removeFromQuick command is registered", "E2E: Remove quick command → junction record deleted"
24+
25+
## Display Order
26+
27+
**SPEC-QL-030**
28+
29+
Quick launch items maintain insertion order via `display_order` column in the `command_tags` junction table. Items can be reordered via drag-and-drop.
30+
31+
### Test Coverage
32+
- [quicktasks.e2e.test.ts](../src/test/e2e/quicktasks.e2e.test.ts): "E2E: Quick commands ordered by display_order", "display_order column maintains insertion order"
33+
34+
## Duplicate Prevention
35+
36+
**SPEC-QL-040**
37+
38+
The same command cannot be added to quick launch twice. The UNIQUE constraint on `(command_id, tag_id)` prevents duplicates.
39+
40+
### Test Coverage
41+
- [quicktasks.e2e.test.ts](../src/test/e2e/quicktasks.e2e.test.ts): "E2E: Cannot add same command to quick twice"
42+
43+
## Empty State
44+
45+
**SPEC-QL-050**
46+
47+
When no commands are starred, the Quick Launch panel shows a placeholder message.
48+
49+
### Test Coverage
50+
- [quicktasks.e2e.test.ts](../src/test/e2e/quicktasks.e2e.test.ts): "Quick tasks view shows placeholder when empty"

docs/settings.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Settings
2+
3+
**SPEC-SET-001**
4+
5+
All settings are configured via VS Code settings (`Cmd+,` / `Ctrl+,`).
6+
7+
## Exclude Patterns
8+
9+
**SPEC-SET-010**
10+
11+
`commandtree.excludePatterns` - Glob patterns to exclude from command discovery. Default includes `**/node_modules/**`, `**/.vscode-test/**`, and others.
12+
13+
### Test Coverage
14+
- [configuration.e2e.test.ts](../src/test/e2e/configuration.e2e.test.ts): "excludePatterns setting exists", "excludePatterns has sensible defaults", "exclude patterns use glob syntax", "exclude patterns support common directories"
15+
16+
## Sort Order
17+
18+
**SPEC-SET-020**
19+
20+
`commandtree.sortOrder` - How commands are sorted within categories:
21+
22+
| Value | Description |
23+
|-------|-------------|
24+
| `folder` | Sort by folder path, then alphabetically (default) |
25+
| `name` | Sort alphabetically by command name |
26+
| `type` | Sort by command type, then alphabetically |
27+
28+
### Test Coverage
29+
- [configuration.e2e.test.ts](../src/test/e2e/configuration.e2e.test.ts): "sortOrder setting exists", "sortOrder has valid enum values", "sortOrder defaults to folder", "sortOrder has descriptive enum descriptions", "sortOrder config has valid value"
30+
31+
## Configuration Reading
32+
33+
**SPEC-SET-030**
34+
35+
Settings are read from the VS Code workspace configuration. The configuration section title is "CommandTree".
36+
37+
### Test Coverage
38+
- [configuration.e2e.test.ts](../src/test/e2e/configuration.e2e.test.ts): "workspace settings are read correctly", "configuration has correct section title"

docs/tagging.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Tagging
2+
3+
**SPEC-TAG-001**
4+
5+
Tags are simple one-word identifiers (e.g., "build", "test", "deploy") that link to commands via a many-to-many relationship in the database.
6+
7+
## Command ID Format
8+
9+
**SPEC-TAG-010**
10+
11+
Every command has a unique ID generated as: `{type}:{filePath}:{name}`
12+
13+
Examples:
14+
- `npm:/Users/you/project/package.json:build`
15+
- `shell:/Users/you/project/scripts/deploy.sh:deploy.sh`
16+
- `make:/Users/you/project/Makefile:test`
17+
- `launch:/Users/you/project/.vscode/launch.json:Launch Chrome`
18+
19+
## How Tagging Works
20+
21+
**SPEC-TAG-020**
22+
23+
1. User right-clicks a command and selects "Add Tag"
24+
2. Tag is created in `tags` table if it doesn't exist: `(tag_id UUID, tag_name, description)`
25+
3. Junction record is created in `command_tags` table: `(command_id, tag_id, display_order)`
26+
4. The `command_id` is the exact ID string from above
27+
5. To filter by tag: `SELECT c.* FROM commands c JOIN command_tags ct ON c.command_id = ct.command_id JOIN tags t ON ct.tag_id = t.tag_id WHERE t.tag_name = 'build'`
28+
6. Display the matching commands in the tree view
29+
30+
**No pattern matching, no wildcards** - just exact `command_id` matching via straightforward database JOINs.
31+
32+
### Test Coverage
33+
- [tagging.e2e.test.ts](../src/test/e2e/tagging.e2e.test.ts): "E2E: Add tag via UI → exact ID stored in junction table", "E2E: Remove tag via UI → junction record deleted", "E2E: Cannot add same tag twice (UNIQUE constraint)", "E2E: Filter by tag → only exact ID matches shown"
34+
- [tagconfig.e2e.test.ts](../src/test/e2e/tagconfig.e2e.test.ts): "E2E: Add tag via UI → exact ID stored in junction table", "E2E: Remove tag via UI → junction record deleted", "E2E: Cannot add same tag twice (UNIQUE constraint)", "E2E: Filter by tag → only exact ID matches shown"
35+
36+
## Database Operations
37+
38+
**SPEC-TAG-030**
39+
40+
Implemented in `src/semantic/db.ts`:
41+
42+
- `addTagToCommand(params)` - Creates tag in `tags` table if needed, then adds junction record
43+
- `removeTagFromCommand(params)` - Removes junction record from `command_tags`
44+
- `getCommandIdsByTag(params)` - Returns all command IDs for a tag (ordered by `display_order`)
45+
- `getTagsForCommand(params)` - Returns all tags assigned to a command
46+
- `getAllTagNames(handle)` - Returns all distinct tag names from `tags` table
47+
- `updateTagDisplayOrder(params)` - Updates display order in `command_tags` for drag-and-drop
48+
49+
### Test Coverage
50+
- [db.e2e.test.ts](../src/test/e2e/db.e2e.test.ts): "addTagToCommand creates tag and junction record", "addTagToCommand is idempotent", "removeTagFromCommand removes junction record", "removeTagFromCommand succeeds for non-existent tag", "getAllTagNames returns all distinct tags"
51+
52+
## Managing Tags
53+
54+
**SPEC-TAG-040**
55+
56+
- **Add tag to command**: Right-click a command > "Add Tag" > select existing or create new
57+
- **Remove tag from command**: Right-click a command > "Remove Tag"
58+
59+
### Test Coverage
60+
- [tagging.e2e.test.ts](../src/test/e2e/tagging.e2e.test.ts): "addTag command is registered", "removeTag command is registered", "addTag and removeTag are in view item context menu", "tag commands are in 3_tagging group"
61+
62+
## Tag Filter
63+
64+
**SPEC-TAG-050**
65+
66+
Pick a tag from the toolbar picker (`commandtree.filterByTag`) to show only commands that have that tag assigned in the database.
67+
68+
### Test Coverage
69+
- [filtering.e2e.test.ts](../src/test/e2e/filtering.e2e.test.ts): "filterByTag command is registered"
70+
71+
## Clear Filter
72+
73+
**SPEC-TAG-060**
74+
75+
Remove all active filters via toolbar button or `commandtree.clearFilter` command.
76+
77+
### Test Coverage
78+
- [filtering.e2e.test.ts](../src/test/e2e/filtering.e2e.test.ts): "clearFilter command is registered"
79+
80+
## Tag Config Sync
81+
82+
**SPEC-TAG-070**
83+
84+
Tags from `commandtree.json` are synced to the database at activation.
85+
86+
### Test Coverage
87+
- [tagging.e2e.test.ts](../src/test/e2e/tagging.e2e.test.ts): "E2E: Tags from commandtree.json are synced at activation"
88+
- [tagconfig.e2e.test.ts](../src/test/e2e/tagconfig.e2e.test.ts): "E2E: Tags from commandtree.json are synced at activation"

0 commit comments

Comments
 (0)