From taskmanager
Manages project memories—constraints, decisions, conventions—in SQLite DB with conflict detection, resolution, full-text search, and usage tracking for task relevance.
npx claudepluginhub mwguerra/claude-code-plugins --plugin taskmanagerThis skill is limited to using the following tools:
You manage the **project-wide memory** for this repository using SQLite.
Manages persistent semantic memory across sessions: store/retrieve knowledge/TODOs/issues, hybrid semantic search, hierarchy/tags organization, and maintenance tools.
Loads and applies project memories from prior sessions for consistent decisions, conventions, and preferences. Stores new entries automatically or via /remember.
PROACTIVELY query Forgetful MCP (mcp__forgetful__* tools) when starting work on any project, when user references past decisions or patterns, when implementing features that may have been solved before, or when needing context about preferences. Save important decisions, patterns, and architectural insights to memory.
Share bugs, ideas, or general feedback.
You manage the project-wide memory for this repository using SQLite.
Your goal is to:
memories table in .taskmanager/taskmanager.db valid and consistent.Always work relative to the project root.
.taskmanager/taskmanager.dbmemoriesmemories_fts (FTS5 virtual table)state.task_memory (JSON column in state table)Use sqlite3 via the Bash tool for all database operations.
The memories table has these columns:
| Column | Type | Description |
|---|---|---|
id | TEXT PRIMARY KEY | Stable ID, e.g. "M-0001" |
title | TEXT NOT NULL | Short summary (<= 140 chars) |
kind | TEXT NOT NULL | One of: constraint, decision, bugfix, workaround, convention, architecture, process, integration, anti-pattern, other |
why_important | TEXT NOT NULL | Concise explanation of why this memory matters |
body | TEXT NOT NULL | Detailed description / rationale / examples |
source_type | TEXT NOT NULL | One of: user, agent, command, hook, other |
source_name | TEXT | Human/agent/command identifier |
source_via | TEXT | Free-text, e.g. "cli", "tests/run-test-suite" |
auto_updatable | INTEGER | 0 for user-created (never auto-update), 1 for system-created |
importance | INTEGER | 1-5 (how critical), default 3 |
confidence | REAL | 0-1 (how sure we are), default 0.8 |
status | TEXT | One of: active, deprecated, superseded, draft |
superseded_by | TEXT | ID of newer memory (if superseded) |
scope | TEXT (JSON) | Object with: project, files, tasks, commands, agents, domains. The tasks field links memories to specific task IDs for auto-loading during execution. |
tags | TEXT (JSON) | Array of free-form tags, e.g. ["testing", "laravel"] |
links | TEXT (JSON) | Array of links to docs/PRs/etc |
use_count | INTEGER | Usage counter, default 0 |
last_used_at | TEXT | ISO timestamp of last use |
last_conflict_at | TEXT | ISO timestamp of last detected conflict |
conflict_resolutions | TEXT (JSON) | Array of conflict resolution history entries |
created_at | TEXT | ISO timestamp |
updated_at | TEXT | ISO timestamp |
Deferrals (tracked in the deferrals table) are separate from memories. Deferrals track work deferred from one task to another with source-target linkage and lifecycle management. They are managed by the run and update commands, not by this memory skill. Do not create memories to track deferred work; use the deferrals system instead.
When you start working:
.taskmanager/taskmanager.db exists.memories table exists:
SELECT name FROM sqlite_master WHERE type='table' AND name='memories';
Given a natural-language description of the current work (files, task IDs, domains):
Parse the description into:
domains (e.g. testing, performance, security, architecture).files / directories.Use SQL to find matching memories:
Full-text search (for keyword matching):
SELECT m.id, m.title, m.kind, m.why_important, m.importance, m.use_count
FROM memories m
JOIN memories_fts fts ON m.rowid = fts.rowid
WHERE m.status = 'active'
AND memories_fts MATCH '<search_terms>'
ORDER BY rank, m.importance DESC, m.use_count DESC
LIMIT 10;
Scope-based search (for file matching):
SELECT id, title, kind, why_important, importance, use_count
FROM memories
WHERE status = 'active'
AND (
scope = '{}'
OR json_extract(scope, '$.files') IS NULL
OR EXISTS (
SELECT 1 FROM json_each(json_extract(scope, '$.files')) f
WHERE '<current_file>' LIKE f.value
)
)
ORDER BY importance DESC, use_count DESC
LIMIT 10;
Domain-based search:
SELECT id, title, kind, why_important, importance, use_count
FROM memories
WHERE status = 'active'
AND EXISTS (
SELECT 1 FROM json_each(json_extract(scope, '$.domains')) d
WHERE d.value IN ('<domain1>', '<domain2>')
)
ORDER BY importance DESC, use_count DESC;
Task-based search:
SELECT id, title, kind, why_important, importance, use_count
FROM memories
WHERE status = 'active'
AND EXISTS (
SELECT 1 FROM json_each(json_extract(scope, '$.tasks')) t
WHERE t.value = '<task_id>'
)
ORDER BY importance DESC;
Prefer:
importance.use_count.last_used_at.Return a compact summary (bullet list) with:
id, title, kind, why_important.You should never dump all memories into context unless explicitly asked; always select the smallest relevant subset.
When a user or another skill makes a decision that should persist for future work:
Check whether a similar memory already exists:
SELECT id, title, kind FROM memories
WHERE status = 'active'
AND kind = '<kind>'
AND (
title LIKE '%<keyword>%'
OR EXISTS (
SELECT 1 FROM json_each(tags) t WHERE t.value = '<tag>'
)
);
If it is truly new, generate the next ID:
SELECT 'M-' || printf('%04d', COALESCE(MAX(CAST(SUBSTR(id, 3) AS INTEGER)), 0) + 1)
FROM memories;
Insert the new memory:
INSERT INTO memories (
id, title, kind, why_important, body,
source_type, source_name, source_via, auto_updatable,
importance, confidence, status,
scope, tags, links,
use_count, created_at, updated_at
) VALUES (
'<id>', '<title>', '<kind>', '<why_important>', '<body>',
'<source_type>', '<source_name>', '<source_via>', <0_or_1>,
<importance>, <confidence>, 'active',
'<scope_json>', '<tags_json>', '<links_json>',
0, datetime('now'), datetime('now')
);
When in doubt whether something deserves a memory, ask: "Will this decision/convention matter for future tasks?" If yes, create a memory.
When the plan command's macro architectural questions (Phase 3) capture a user decision:
INSERT INTO memories (
id, title, kind, why_important, body,
source_type, source_name, source_via, auto_updatable,
importance, confidence, status,
scope, tags
) VALUES (
'M-0012',
'Use Redis for queue driver',
'architecture',
'Affects all background job processing',
'User chose Redis as the queue driver during macro analysis. Rationale: existing Redis infrastructure, supports priorities and delayed jobs.',
'user', 'developer', 'taskmanager:plan:macro-questions', 0,
4, 1.0, 'active',
'{"domains": ["infrastructure", "queues"], "tasks": ["1.3", "2.1"]}',
'["redis", "queue", "architecture"]'
);
Note the scope.tasks field linking this memory to relevant task IDs. During task execution, the run command auto-loads memories where the current task ID appears in scope.tasks.
When an existing memory is refined or corrected:
If it's a small correction:
UPDATE memories SET
body = '<new_body>',
tags = '<new_tags_json>',
scope = '<new_scope_json>',
updated_at = datetime('now')
WHERE id = '<memory_id>';
If it's a substantial change or reversal:
UPDATE memories SET
status = 'superseded',
superseded_by = '<new_id>',
updated_at = datetime('now')
WHERE id = '<old_id>';
Never silently rewrite history in a way that hides past decisions.
Whenever a memory directly influences planning or execution:
UPDATE memories SET
use_count = use_count + 1,
last_used_at = datetime('now'),
updated_at = datetime('now')
WHERE id = '<memory_id>';
This allows future tools to treat highly-used, high-importance memories as more trustworthy.
When planning or executing non-trivial work (new features, refactors, risky changes):
This way, the memories table becomes the single, durable "project brain" that all agents/commands/skills can rely on.
Task-scoped memories are temporary memories that live only for the duration of a single task. They are stored in the state table under the task_memory JSON column.
When a user provides --task-memory "description" or -tm "description" to a command:
UPDATE state SET
task_memory = json_insert(
task_memory,
'$[#]',
json_object(
'content', '<the description>',
'addedAt', datetime('now'),
'taskId', '<current task ID>',
'source', 'user'
)
)
WHERE id = 1;
System-generated task memories use 'source', 'system'.
Before executing a task:
SELECT value FROM state, json_each(state.task_memory)
WHERE state.id = 1
AND (
json_extract(value, '$.taskId') = '<current_task_id>'
OR json_extract(value, '$.taskId') = '*'
);
Include these memories alongside global memories when applying constraints.
At task completion (before marking "done"):
Check if any task memories exist for this task.
If task memories exist, use AskUserQuestion to ask:
"The following task memories were used during this task. Should any be promoted to global (persistent) memory?"
Options for each memory:
For promoted memories:
memories table.source_type = 'user' if originally from user, or 'agent' if from system.Clear the task memories for this task:
UPDATE state SET
task_memory = (
SELECT json_group_array(value)
FROM json_each(task_memory)
WHERE json_extract(value, '$.taskId') != '<task_id>'
)
WHERE id = 1;
Conflict detection is available via taskmanager:memory conflicts. It is NOT run automatically during task execution.
When invoked, it checks active memories for:
scope.files that no longer existuse_count = 0 and created_at older than 30 daysFor each conflict found, offer resolution options via AskUserQuestion:
Record all resolutions in the conflict_resolutions JSON column:
UPDATE memories SET
conflict_resolutions = json_insert(
conflict_resolutions,
'$[#]',
json_object(
'timestamp', datetime('now'),
'resolution', '<kept|modified|deprecated>',
'reason', '<brief explanation>',
'taskId', '<task ID>'
)
),
last_conflict_at = datetime('now'),
updated_at = datetime('now')
WHERE id = '<memory_id>';
When resolving conflicts (whether via opt-in detection or during manual review):
source_type = 'user')NEVER auto-update. ALWAYS ask the user via AskUserQuestion.
source_type != 'user')auto_updatableWhen creating a memory, set auto_updatable based on source_type:
auto_updatable = (source_type != 'user') ? 1 : 0
source_type = 'user' -> auto_updatable = 0source_type = 'agent' | 'command' | 'hook' | 'other' -> auto_updatable = 1| Source Type | Small Update | Substantial Change |
|---|---|---|
user | Ask user | Ask user |
agent | Auto-update | Ask user |
command | Auto-update | Ask user |
hook | Auto-update | Ask user |
other | Auto-update | Ask user |
Memories are never deleted. They are either:
activedeprecated (no longer relevant)superseded with a pointer to the new memoryThis preserves decision history and audit trail.
All logging goes to a single file: .taskmanager/logs/activity.log.
<timestamp> [<level>] [memory] <message>
Levels: ERROR, DECISION. Logs are append-only.
Log these events:
Examples:
2025-12-11T10:00:00Z [DECISION] [memory] Created memory M-0005: "Always validate API inputs"
2025-12-11T10:00:01Z [DECISION] [memory] Applied memories to task 1.2: M-0001, M-0003, M-0005
2025-12-11T10:00:02Z [ERROR] [memory] Conflict: M-0001 references deleted file app/OldAuth.php
2025-12-11T10:05:00Z [DECISION] [memory] Deprecated M-0002: "No longer using old auth pattern"
Get all active memories:
SELECT * FROM memories WHERE status = 'active' ORDER BY importance DESC, use_count DESC;
Full-text search:
SELECT m.* FROM memories m
JOIN memories_fts fts ON m.rowid = fts.rowid
WHERE memories_fts MATCH '<search_term>'
AND m.status = 'active'
ORDER BY rank, m.importance DESC;
Get memories for a specific file:
SELECT * FROM memories
WHERE status = 'active'
AND (
scope = '{}'
OR json_extract(scope, '$.files') IS NULL
OR EXISTS (
SELECT 1 FROM json_each(json_extract(scope, '$.files')) f
WHERE '<current_file>' LIKE f.value
)
)
ORDER BY importance DESC;
Record memory usage:
UPDATE memories
SET use_count = use_count + 1,
last_used_at = datetime('now'),
updated_at = datetime('now')
WHERE id = '<memory_id>';
Record conflict resolution:
UPDATE memories SET
conflict_resolutions = json_insert(
conflict_resolutions,
'$[#]',
json_object(
'timestamp', datetime('now'),
'resolution', '<resolution>',
'reason', '<reason>',
'taskId', '<taskId>'
)
),
last_conflict_at = datetime('now'),
updated_at = datetime('now')
WHERE id = '<memory_id>';
Get next memory ID:
SELECT 'M-' || printf('%04d', COALESCE(MAX(CAST(SUBSTR(id, 3) AS INTEGER)), 0) + 1)
FROM memories;
Deprecate a memory:
UPDATE memories SET
status = 'deprecated',
updated_at = datetime('now')
WHERE id = '<memory_id>';
Supersede a memory:
UPDATE memories SET
status = 'superseded',
superseded_by = '<new_memory_id>',
updated_at = datetime('now')
WHERE id = '<old_memory_id>';