knowns 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -321
- package/CLAUDE.md +22 -8
- package/README.md +8 -1
- package/dist/index.js +1204 -213
- package/dist/mcp/server.js +47 -13
- package/dist/ui/assets/{index-BqJqFGQL.js → index-BdxLDbKo.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3313,10 +3313,10 @@ var require_stringify = __commonJS({
|
|
|
3313
3313
|
data = Object.assign({}, file3.data, data);
|
|
3314
3314
|
const open = opts.delimiters[0];
|
|
3315
3315
|
const close = opts.delimiters[1];
|
|
3316
|
-
const
|
|
3316
|
+
const matter10 = engine.stringify(data, options2).trim();
|
|
3317
3317
|
let buf = "";
|
|
3318
|
-
if (
|
|
3319
|
-
buf = newline(open) + newline(
|
|
3318
|
+
if (matter10 !== "{}") {
|
|
3319
|
+
buf = newline(open) + newline(matter10) + newline(close);
|
|
3320
3320
|
}
|
|
3321
3321
|
if (typeof file3.excerpt === "string" && file3.excerpt !== "") {
|
|
3322
3322
|
if (str2.indexOf(file3.excerpt.trim()) === -1) {
|
|
@@ -3422,19 +3422,19 @@ var require_gray_matter = __commonJS({
|
|
|
3422
3422
|
var toFile = require_to_file();
|
|
3423
3423
|
var parse4 = require_parse();
|
|
3424
3424
|
var utils = require_utils();
|
|
3425
|
-
function
|
|
3425
|
+
function matter10(input, options2) {
|
|
3426
3426
|
if (input === "") {
|
|
3427
3427
|
return { data: {}, content: input, excerpt: "", orig: input };
|
|
3428
3428
|
}
|
|
3429
3429
|
let file3 = toFile(input);
|
|
3430
|
-
const cached2 =
|
|
3430
|
+
const cached2 = matter10.cache[file3.content];
|
|
3431
3431
|
if (!options2) {
|
|
3432
3432
|
if (cached2) {
|
|
3433
3433
|
file3 = Object.assign({}, cached2);
|
|
3434
3434
|
file3.orig = cached2.orig;
|
|
3435
3435
|
return file3;
|
|
3436
3436
|
}
|
|
3437
|
-
|
|
3437
|
+
matter10.cache[file3.content] = file3;
|
|
3438
3438
|
}
|
|
3439
3439
|
return parseMatter(file3, options2);
|
|
3440
3440
|
}
|
|
@@ -3456,7 +3456,7 @@ var require_gray_matter = __commonJS({
|
|
|
3456
3456
|
}
|
|
3457
3457
|
str2 = str2.slice(openLen);
|
|
3458
3458
|
const len = str2.length;
|
|
3459
|
-
const language =
|
|
3459
|
+
const language = matter10.language(str2, opts);
|
|
3460
3460
|
if (language.name) {
|
|
3461
3461
|
file3.language = language.name;
|
|
3462
3462
|
str2 = str2.slice(language.raw.length);
|
|
@@ -3491,24 +3491,24 @@ var require_gray_matter = __commonJS({
|
|
|
3491
3491
|
}
|
|
3492
3492
|
return file3;
|
|
3493
3493
|
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
if (typeof file3 === "string") file3 =
|
|
3494
|
+
matter10.engines = engines2;
|
|
3495
|
+
matter10.stringify = function(file3, data, options2) {
|
|
3496
|
+
if (typeof file3 === "string") file3 = matter10(file3, options2);
|
|
3497
3497
|
return stringify(file3, data, options2);
|
|
3498
3498
|
};
|
|
3499
|
-
|
|
3499
|
+
matter10.read = function(filepath, options2) {
|
|
3500
3500
|
const str2 = fs.readFileSync(filepath, "utf8");
|
|
3501
|
-
const file3 =
|
|
3501
|
+
const file3 = matter10(str2, options2);
|
|
3502
3502
|
file3.path = filepath;
|
|
3503
3503
|
return file3;
|
|
3504
3504
|
};
|
|
3505
|
-
|
|
3505
|
+
matter10.test = function(str2, options2) {
|
|
3506
3506
|
return utils.startsWith(str2, defaults(options2).delimiters[0]);
|
|
3507
3507
|
};
|
|
3508
|
-
|
|
3508
|
+
matter10.language = function(str2, options2) {
|
|
3509
3509
|
const opts = defaults(options2);
|
|
3510
3510
|
const open = opts.delimiters[0];
|
|
3511
|
-
if (
|
|
3511
|
+
if (matter10.test(str2)) {
|
|
3512
3512
|
str2 = str2.slice(open.length);
|
|
3513
3513
|
}
|
|
3514
3514
|
const language = str2.slice(0, str2.search(/\r?\n/));
|
|
@@ -3517,11 +3517,11 @@ var require_gray_matter = __commonJS({
|
|
|
3517
3517
|
name: language ? language.trim() : ""
|
|
3518
3518
|
};
|
|
3519
3519
|
};
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3520
|
+
matter10.cache = {};
|
|
3521
|
+
matter10.clearCache = function() {
|
|
3522
|
+
matter10.cache = {};
|
|
3523
3523
|
};
|
|
3524
|
-
module2.exports =
|
|
3524
|
+
module2.exports = matter10;
|
|
3525
3525
|
}
|
|
3526
3526
|
});
|
|
3527
3527
|
|
|
@@ -46167,6 +46167,22 @@ var FileStore = class {
|
|
|
46167
46167
|
return null;
|
|
46168
46168
|
}
|
|
46169
46169
|
}
|
|
46170
|
+
/**
|
|
46171
|
+
* Skipped files due to parse errors (for graceful degradation)
|
|
46172
|
+
*/
|
|
46173
|
+
skippedFiles = [];
|
|
46174
|
+
/**
|
|
46175
|
+
* Get list of skipped files from last load
|
|
46176
|
+
*/
|
|
46177
|
+
getSkippedFiles() {
|
|
46178
|
+
return this.skippedFiles;
|
|
46179
|
+
}
|
|
46180
|
+
/**
|
|
46181
|
+
* Clear skipped files list
|
|
46182
|
+
*/
|
|
46183
|
+
clearSkippedFiles() {
|
|
46184
|
+
this.skippedFiles = [];
|
|
46185
|
+
}
|
|
46170
46186
|
/**
|
|
46171
46187
|
* Get all tasks
|
|
46172
46188
|
*/
|
|
@@ -46175,24 +46191,42 @@ var FileStore = class {
|
|
|
46175
46191
|
const files = await readdir(this.tasksPath);
|
|
46176
46192
|
const taskFiles = files.filter((f) => f.startsWith("task-") && f.endsWith(".md"));
|
|
46177
46193
|
const allTimeEntries = await this.loadTimeEntries();
|
|
46194
|
+
this.skippedFiles = [];
|
|
46178
46195
|
const tasksMap = /* @__PURE__ */ new Map();
|
|
46179
46196
|
for (const taskFile of taskFiles) {
|
|
46180
|
-
|
|
46181
|
-
|
|
46182
|
-
|
|
46183
|
-
|
|
46184
|
-
|
|
46185
|
-
|
|
46186
|
-
|
|
46187
|
-
|
|
46188
|
-
|
|
46189
|
-
|
|
46190
|
-
|
|
46191
|
-
|
|
46192
|
-
|
|
46197
|
+
try {
|
|
46198
|
+
const filePath = join2(this.tasksPath, taskFile);
|
|
46199
|
+
const content = await file(filePath).text();
|
|
46200
|
+
const taskData = parseTaskMarkdown(content);
|
|
46201
|
+
if (taskData.id) {
|
|
46202
|
+
const timeEntries = (allTimeEntries[taskData.id] || []).map((e) => ({
|
|
46203
|
+
...e,
|
|
46204
|
+
startedAt: new Date(e.startedAt),
|
|
46205
|
+
endedAt: e.endedAt ? new Date(e.endedAt) : void 0
|
|
46206
|
+
}));
|
|
46207
|
+
tasksMap.set(taskData.id, {
|
|
46208
|
+
...taskData,
|
|
46209
|
+
subtasks: [],
|
|
46210
|
+
timeEntries
|
|
46211
|
+
});
|
|
46212
|
+
} else {
|
|
46213
|
+
this.skippedFiles.push({
|
|
46214
|
+
file: taskFile,
|
|
46215
|
+
error: "Missing task ID in frontmatter"
|
|
46216
|
+
});
|
|
46217
|
+
}
|
|
46218
|
+
} catch (parseError) {
|
|
46219
|
+
this.skippedFiles.push({
|
|
46220
|
+
file: taskFile,
|
|
46221
|
+
error: parseError instanceof Error ? parseError.message : String(parseError)
|
|
46193
46222
|
});
|
|
46194
46223
|
}
|
|
46195
46224
|
}
|
|
46225
|
+
if (this.skippedFiles.length > 0) {
|
|
46226
|
+
console.warn(
|
|
46227
|
+
`\u26A0 Skipped ${this.skippedFiles.length} corrupted task file(s). Run "knowns task validate <id>" or "knowns task repair <id>" to fix.`
|
|
46228
|
+
);
|
|
46229
|
+
}
|
|
46196
46230
|
for (const task of tasksMap.values()) {
|
|
46197
46231
|
const subtasks = [];
|
|
46198
46232
|
for (const otherTask of tasksMap.values()) {
|
|
@@ -46953,17 +46987,25 @@ var {
|
|
|
46953
46987
|
// src/commands/init.ts
|
|
46954
46988
|
var import_prompts2 = __toESM(require_prompts3(), 1);
|
|
46955
46989
|
|
|
46956
|
-
// src/templates/knowns-guidelines-cli.md
|
|
46957
|
-
var knowns_guidelines_cli_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI Guidelines\n\n## Core Principles\n\n### 1. CLI-Only Operations\n**NEVER edit .md files directly. ALL operations MUST use CLI commands.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```bash\n# 1. List all available documentation\nknowns doc list --plain\n\n# 2. Read essential project docs (prioritize these)\nknowns doc "README" --plain # Project overview\nknowns doc "ARCHITECTURE" --plain # System design\nknowns doc "CONVENTIONS" --plain # Coding standards\nknowns doc "API" --plain # API specifications\n\n# 3. Review current task backlog\nknowns task list --plain\nknowns task list --status in-progress --plain\n```\n\n### Before Taking Any Task\n\n```bash\n# 1. View the task details\nknowns task <id> --plain\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 knowns task 44 --plain\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# 3. Search for additional related documentation\nknowns search "<keywords from task>" --type doc --plain\n\n# 4. Read ALL related docs before planning\nknowns doc "<related-doc>" --plain\n\n# 5. Check for similar completed tasks (learn from history)\nknowns search "<keywords>" --type task --status done --plain\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | CLI Command |\n|------|---------------------|----------------------|-------------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `knowns task <id> --plain` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `knowns doc <path> --plain` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output from `--plain`: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format (`@task-<id>`, `@doc/<path>`)\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```bash\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nknowns task 44 --plain\n\n# Follow doc ref (extract path, remove .md)\nknowns doc "patterns/module" --plain\n```\n\n### Parsing Rules\n\n1. **Task refs**: `@.knowns/tasks/task-<id> - ...` \u2192 extract `<id>` \u2192 `knowns task <id> --plain`\n2. **Doc refs**: `@.knowns/docs/<path>.md` \u2192 extract `<path>` \u2192 `knowns doc "<path>" --plain`\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n### When to Follow Refs\n\n| Situation | Action |\n|-----------|--------|\n| Refs in Description | ALWAYS follow - critical context |\n| Refs in Implementation Plan | Follow if implementing related work |\n| Refs in Notes | Optional - for historical context |\n| Dependency mentions | Follow if marked as blocker |\n\n### Example: Complete Ref Resolution\n\n```bash\n# 1. Read the task\n$ knowns task 42 --plain\n\n# Output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/README.md\n\n# 2. Follow task ref\n$ knowns task 44 --plain\n\n# 3. Follow doc ref\n$ knowns doc "README" --plain\n\n# 4. If README contains more refs, follow them too\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# Now you have complete context\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```bash\n# Initialize project\nknowns init [name]\n\n# Create task with acceptance criteria\nknowns task create "Title" -d "Description" --ac "Criterion 1" --ac "Criterion 2"\n\n# View task (ALWAYS use --plain for AI)\nknowns task <id> --plain # Shorthand\nknowns task view <id> --plain # Full command\n\n# View doc (ALWAYS use --plain for AI)\nknowns doc <path> --plain # Shorthand\nknowns doc view "<path>" --plain # Full command\n\n# List tasks\nknowns task list --plain\n\n# Search (tasks + docs)\nknowns search "query" --plain\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```bash\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\n$ knowns doc list --plain\n\n# Output:\n# DOC: README.md\n# DOC: ARCHITECTURE.md\n# DOC: CONVENTIONS.md\n# DOC: security-patterns.md\n# DOC: api-guidelines.md\n# DOC: email-templates.md\n\n# 0b. Read essential project docs\n$ knowns doc "README" --plain\n$ knowns doc "ARCHITECTURE" --plain\n$ knowns doc "CONVENTIONS" --plain\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\n$ knowns task create "Add password reset flow" \\\n -d "Users need ability to reset forgotten passwords via email" \\\n --ac "User can request password reset via email" \\\n --ac "Reset link expires after 1 hour" \\\n --ac "User can set new password via reset link" \\\n --ac "Unit tests cover all scenarios" \\\n --priority high \\\n -l "auth,feature"\n\n# Output: Created task AUTH-042\n\n# 2. Take the task and start timer (uses defaultAssignee or @me fallback)\n$ knowns task edit AUTH-042 -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n$ knowns time start AUTH-042\n\n# Output: Timer started for AUTH-042\n\n# 3. Search for related documentation\n$ knowns search "password security" --type doc --plain\n\n# Output:\n# DOC: security-patterns.md (score: 0.92)\n# DOC: email-templates.md (score: 0.78)\n\n# 4. Read the documentation\n$ knowns doc "security-patterns" --plain\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\n$ knowns task edit AUTH-042 --plan $\'1. Review security patterns (see @doc/security-patterns)\n2. Design token generation with 1-hour expiry\n3. Create email template (see @doc/email-templates)\n4. Implement /forgot-password endpoint\n5. Implement /reset-password endpoint\n6. Add unit tests\n7. Update API documentation\'\n\n# 6. After approval, implement and check criteria as you go\n$ knowns task edit AUTH-042 --check-ac 1\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /forgot-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 2\n$ knowns task edit AUTH-042 --append-notes "\u2713 Token expiry set to 1 hour"\n\n$ knowns task edit AUTH-042 --check-ac 3\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /reset-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 4\n$ knowns task edit AUTH-042 --append-notes "\u2713 Added 12 unit tests, 100% coverage"\n\n# 7. Add final implementation notes\n$ knowns task edit AUTH-042 --notes $\'## Summary\nImplemented complete password reset flow with secure token generation.\n\n## Changes\n- Added POST /forgot-password endpoint\n- Added POST /reset-password endpoint\n- Created password_reset_tokens table\n- Added email template for reset link\n\n## Security\n- Tokens use crypto.randomBytes(32)\n- 1-hour expiry enforced at DB level\n- Rate limiting: 3 requests per hour per email\n\n## Tests\n- 12 unit tests added\n- Coverage: 100% for new code\n\n## Documentation\n- Updated API.md with new endpoints\'\n\n# 8. Stop timer and complete\n$ knowns time stop\n$ knowns task edit AUTH-042 -s done\n\n# Output: Task AUTH-042 marked as done\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```bash\n# Assign using defaultAssignee from config (falls back to @me if not set)\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n```\n\n> **Note**: The `defaultAssignee` is configured in `.knowns/config.json` during `knowns init`. If not set, `@me` is used as fallback. To update: `knowns config set defaultAssignee "@username"`\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```bash\nknowns time start <id>\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```bash\n# Search for related docs\nknowns search "authentication" --type doc --plain\n\n# View relevant documents\nknowns doc "API Guidelines" --plain\nknowns doc "Security Patterns" --plain\n\n# Also check for similar completed tasks\nknowns search "auth" --type task --status done --plain\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning! Understanding existing patterns and conventions prevents rework and ensures consistency.\n\n### Step 4: Create Implementation Plan\n\n```bash\nknowns task edit <id> --plan $\'1. Research patterns (see @doc/security-patterns)\n2. Design middleware\n3. Implement\n4. Add tests\n5. Update docs\'\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```bash\n# Work through implementation plan step by step\n# IMPORTANT: Only check AC AFTER completing the work, not before\n\n# After completing work for AC #1:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n\n# After completing work for AC #2:\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n```\n\n> **CRITICAL**: Never check an AC before the work is actually done. ACs represent completed outcomes, not intentions.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```bash\n# Add new acceptance criteria\nknowns task edit <id> --ac "New requirement from user"\n\n# Update implementation plan to include new steps\nknowns task edit <id> --plan $\'1. Original step 1\n2. Original step 2\n3. NEW: Handle user request for X\n4. Continue with remaining work\'\n\n# Append note about scope change\nknowns task edit <id> --append-notes "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```bash\n# Add comprehensive notes (suitable for PR description)\nknowns task edit <id> --notes $\'## Summary\n\nImplemented JWT auth.\n\n## Changes\n- Added middleware\n- Added tests\'\n\n# OR append progressively (recommended)\nknowns task edit <id> --append-notes "\u2713 Implemented middleware"\nknowns task edit <id> --append-notes "\u2713 Added tests"\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```bash\nknowns time stop\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry: `knowns time add <id> <duration> -n "Forgot to stop timer"`\n\n### Step 9: Complete Task\n\n```bash\nknowns task edit <id> -s done\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```bash\n# 1. Reopen task - set back to in-progress\nknowns task edit <id> -s in-progress\n\n# 2. Restart time tracking (REQUIRED)\nknowns time start <id>\n\n# 3. Add new AC for the changes requested\nknowns task edit <id> --ac "Post-completion fix: <description>"\n\n# 4. Document the reopen reason\nknowns task edit <id> --append-notes "\u{1F504} Reopened: User requested changes - <reason>"\n\n# 5. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Add AC for traceability\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```bash\n# Search if similar pattern already documented\nknowns search "<pattern/concept>" --type doc --plain\n\n# If new knowledge, create a doc for future reference\nknowns doc create "Pattern: <Name>" \\\n -d "Reusable pattern discovered during task implementation" \\\n -t "pattern,<domain>" \\\n -f "patterns"\n\n# Or append to existing doc\nknowns doc edit "<existing-doc>" -a "## New Section\\n\\nLearned from task <id>: ..."\n\n# Reference the source task\nknowns doc edit "<doc-name>" -a "\\n\\n> Source: @task-<id>"\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential Commands\n\n### Task Management\n\n```bash\n# Create task\nknowns task create "Title" -d "Description" --ac "Criterion" -l "labels" --priority high\n\n# Edit task\nknowns task edit <id> -t "New title"\nknowns task edit <id> -d "New description"\nknowns task edit <id> -s in-progress\nknowns task edit <id> --priority high\nknowns task edit <id> -a <assignee> # $(knowns config get defaultAssignee --plain || echo "@me")\n\n# Acceptance Criteria\nknowns task edit <id> --ac "New criterion" # Add\nknowns task edit <id> --check-ac 1 --check-ac 2 # Check (1-indexed)\nknowns task edit <id> --uncheck-ac 2 # Uncheck\nknowns task edit <id> --remove-ac 3 # Remove\n\n# Implementation Plan & Notes\nknowns task edit <id> --plan $\'1. Step\\n2. Step\'\nknowns task edit <id> --notes "Implementation summary"\nknowns task edit <id> --append-notes "Progress update"\n\n# View & List\nknowns task <id> --plain # Shorthand (ALWAYS use --plain)\nknowns task view <id> --plain # Full command\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task list --assignee <assignee> --plain # $(knowns config get defaultAssignee --plain || echo "@me")\nknowns task list --tree --plain # Tree hierarchy\n```\n\n### Time Tracking\n\n```bash\n# Timer\nknowns time start <id>\nknowns time stop\nknowns time pause\nknowns time resume\nknowns time status\n\n# Manual entry\nknowns time add <id> 2h -n "Note" -d "2025-12-25"\n\n# Reports\nknowns time report --from "2025-12-01" --to "2025-12-31"\nknowns time report --by-label --csv > report.csv\n```\n\n### Documentation\n\n```bash\n# List & View\nknowns doc list --plain\nknowns doc list --tag architecture --plain\nknowns doc <path> --plain # Shorthand (ALWAYS use --plain)\nknowns doc view "<path>" --plain # Full command\n\n# Create (with optional folder)\nknowns doc create "Title" -d "Description" -t "tags"\nknowns doc create "Title" -d "Description" -t "tags" -f "folder/path"\n\n# Edit metadata\nknowns doc edit "Doc Name" -t "New Title" --tags "new,tags"\n\n# Edit content\nknowns doc edit "Doc Name" -c "New content" # Replace content\nknowns doc edit "Doc Name" -a "Appended content" # Append to content\n```\n\n### Search\n\n```bash\n# Search everything\nknowns search "query" --plain\n\n# Search specific type\nknowns search "auth" --type task --plain\nknowns search "patterns" --type doc --plain\n\n# Filter\nknowns search "bug" --status in-progress --priority high --plain\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```markdown\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n### Implementation Plan\n\nHOW to solve. Added AFTER taking task, BEFORE coding.\n\n```markdown\n1. Research JWT libraries (see @doc/security-patterns)\n2. Design token structure (access + refresh tokens)\n3. Implement auth middleware\n4. Add unit tests (aim for 90%+ coverage)\n5. Update API.md with new endpoints\n```\n\n### Implementation Notes\n\nSummary for PR description. Added AFTER completion.\n\n```markdown\n## Summary\nImplemented JWT auth using jsonwebtoken library.\n\n## Changes\n- Added auth middleware in src/middleware/auth.ts\n- Added /login and /refresh endpoints\n- Created JWT utility functions\n\n## Tests\n- Added 15 unit tests\n- Coverage: 94%\n\n## Documentation\n- Updated API.md with authentication section\n```\n\n---\n\n## Error Handling\n\n### Common Errors and Solutions\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Error: Task not found` | Invalid task ID | Run `knowns task list --plain` to find correct ID |\n| `Error: No active timer` | Calling `time stop` without active timer | Start timer first: `knowns time start <id>` |\n| `Error: Timer already running` | Starting timer when one is active | Stop current: `knowns time stop` |\n| `Error: Invalid status` | Wrong status format | Use lowercase with hyphens: `in-progress`, not `In Progress` |\n| `Error: AC index out of range` | Checking non-existent criterion | View task first: `knowns task <id> --plain` |\n| `Error: Document not found` | Wrong document name | Run `knowns doc list --plain` to find correct name |\n| `Error: Not initialized` | Running commands without init | Run `knowns init` first |\n\n### Debugging Commands\n\n```bash\n# Check CLI version\nknowns --version\n\n# Verify project is initialized\nknowns status\n\n# View raw task data (for debugging)\nknowns task <id> --json\n\n# Check timer status\nknowns time status\n```\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via CLI (Required)\n\n- [ ] All acceptance criteria checked: `--check-ac <index>` (only after work is actually done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] \u23F1\uFE0F Timer stopped: `knowns time stop` (MANDATORY - do not skip!)\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\nUse **lowercase with hyphens**:\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use `knowns task edit` |\n| Change `- [ ]` to `- [x]` in file | Use `--check-ac <index>` |\n| Check AC before completing work | Only check AC AFTER work is actually done |\n| Skip time tracking | ALWAYS use `time start` and `time stop` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Skip `knowns doc list` on new project | Always list docs when starting |\n| Assume you know the conventions | Read CONVENTIONS/ARCHITECTURE docs |\n| Plan without checking docs | Read docs before planning |\n| Ignore similar completed tasks | Search done tasks for patterns |\n| Missing doc links in description/plan | Link docs using `@doc/<path>` |\n| Write refs as `@.knowns/docs/...` or `@.knowns/tasks/...` | Use input format: `@doc/<path>`, `@task-<id>` |\n| Forget `--plain` flag | Always use `--plain` for AI |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Check ALL criteria first |\n| Write implementation steps in AC | Write outcome-oriented criteria |\n| Use `"In Progress"` or `"Done"` | Use `in-progress`, `done` |\n| Use `@yourself` or unknown assignee | Use `$(knowns config get defaultAssignee --plain \\|\\| echo "@me")` |\n| Ignore refs in task description | Follow ALL refs (`@.knowns/...`) before planning |\n| See `@.knowns/docs/...` but don\'t read | Use `knowns doc "<path>" --plain` |\n| See `@.knowns/tasks/task-X` but don\'t check | Use `knowns task X --plain` for context |\n| Follow only first-level refs | Recursively follow nested refs until complete |\n\n---\n\n## Platform-Specific Notes\n\n### Multi-line Input\n\nDifferent shells handle multi-line strings differently:\n\n**Bash / Zsh (Recommended)**\n```bash\nknowns task edit <id> --plan $\'1. First step\\n2. Second step\\n3. Third step\'\n```\n\n**PowerShell**\n```powershell\nknowns task edit <id> --plan "1. First step`n2. Second step`n3. Third step"\n```\n\n**Cross-platform (Using printf)**\n```bash\nknowns task edit <id> --plan "$(printf \'1. First step\\n2. Second step\\n3. Third step\')"\n```\n\n**Using heredoc (for long content)**\n```bash\nknowns task edit <id> --plan "$(cat <<EOF\n1. First step\n2. Second step\n3. Third step\nEOF\n)"\n```\n\n### Path Separators\n\n- **Unix/macOS**: Use forward slashes: `./docs/api.md`\n- **Windows**: Both work, but prefer forward slashes for consistency\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `knowns doc list --plain`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task has clear acceptance criteria\n- [ ] ALL refs in task followed (`@.knowns/...`)\n- [ ] Nested refs recursively followed until complete context gathered\n- [ ] Related docs searched: `knowns search "keyword" --type doc --plain`\n- [ ] ALL relevant docs read: `knowns doc "Doc Name" --plain`\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self: `-a $(knowns config get defaultAssignee --plain || echo "@me")`\n- [ ] Status set to in-progress: `-s in-progress`\n- [ ] Timer started: `knowns time start <id>`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Criteria checked as completed: `--check-ac <index>`\n- [ ] Progress notes appended: `--append-notes "\u2713 ..."`\n\n### After Work\n\n- [ ] All acceptance criteria checked (only after work is done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] Timer stopped: `knowns time stop`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable): patterns, solutions, conventions\n\n---\n\n## Quick Reference Card\n\n```bash\n# === AGENT INITIALIZATION (Once per session) ===\nknowns doc list --plain\nknowns doc "README" --plain\nknowns doc "ARCHITECTURE" --plain\nknowns doc "CONVENTIONS" --plain\n\n# === FULL WORKFLOW ===\nknowns task create "Title" -d "Description" --ac "Criterion"\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\nknowns time start <id> # \u23F1\uFE0F REQUIRED: Start timer\nknowns search "keyword" --type doc --plain\nknowns doc "Doc Name" --plain\nknowns search "keyword" --type task --status done --plain # Learn from history\nknowns task edit <id> --plan $\'1. Step (see @doc/file)\\n2. Step\'\n# ... wait for approval, then implement ...\n# Only check AC AFTER completing the work:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: feature X"\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: feature Y"\nknowns time stop # \u23F1\uFE0F REQUIRED: Stop timer\nknowns task edit <id> -s done\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nknowns task <id> --plain # Shorthand for view\nknowns task list --plain\nknowns task list --status in-progress --assignee $(knowns config get defaultAssignee --plain || echo "@me") --plain\nknowns search "query" --plain\nknowns search "bug" --type task --status in-progress --plain\n\n# === TIME TRACKING ===\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time report --from "2025-12-01" --to "2025-12-31"\n\n# === DOCUMENTATION ===\nknowns doc list --plain\nknowns doc "path/doc-name" --plain # Shorthand for view\nknowns doc create "Title" -d "Description" -t "tags" -f "folder"\nknowns doc edit "doc-name" -c "New content"\nknowns doc edit "doc-name" -a "Appended content"\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
46958
|
-
|
|
46959
|
-
// src/templates/knowns-guidelines-mcp.md
|
|
46960
|
-
var knowns_guidelines_mcp_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP Guidelines\n\n## Core Principles\n\n### 1. MCP Tool Operations\n**Use MCP tools for ALL Knowns operations. NEVER edit .md files directly.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```\n# 1. List all available documentation\nmcp__knowns__list_docs({})\n\n# 2. Read essential project docs (prioritize these)\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\nmcp__knowns__get_doc({ path: "API" })\n\n# 3. Review current task backlog\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\n```\n\n### Before Taking Any Task\n\n```\n# 1. View the task details\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 mcp__knowns__get_task({ taskId: "44" })\n# @.knowns/docs/patterns/module.md \u2192 mcp__knowns__get_doc({ path: "patterns/module" })\n\n# 3. Search for additional related documentation\nmcp__knowns__search_docs({ query: "<keywords from task>" })\n\n# 4. Read ALL related docs before planning\nmcp__knowns__get_doc({ path: "<related-doc>" })\n\n# 5. Check for similar completed tasks (learn from history)\nmcp__knowns__search_tasks({ query: "<keywords>", status: "done" })\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | MCP Tool |\n|------|---------------------|----------------------|----------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `mcp__knowns__get_task({ taskId: "<id>" })` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `mcp__knowns__get_doc({ path: "<path>" })` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nmcp__knowns__get_task({ taskId: "44" })\n\n# Follow doc ref (extract path, remove .md)\nmcp__knowns__get_doc({ path: "patterns/module" })\n```\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```\n# Initialize project (use CLI for this)\nknowns init [name]\n\n# Create task with acceptance criteria\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high",\n labels: ["label1", "label2"]\n})\n\n# View task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# List tasks\nmcp__knowns__list_tasks({})\n\n# Search (tasks + docs)\nmcp__knowns__search_tasks({ query: "query" })\nmcp__knowns__search_docs({ query: "query" })\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\nmcp__knowns__list_docs({})\n\n# 0b. Read essential project docs\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\nmcp__knowns__create_task({\n title: "Add password reset flow",\n description: "Users need ability to reset forgotten passwords via email",\n priority: "high",\n labels: ["auth", "feature"]\n})\n\n# Output: Created task 42\n\n# 2. Take the task and start timer\nmcp__knowns__update_task({\n taskId: "42",\n status: "in-progress",\n assignee: "@howznguyen"\n})\nmcp__knowns__start_time({ taskId: "42" })\n\n# 3. Search for related documentation\nmcp__knowns__search_docs({ query: "password security" })\n\n# 4. Read the documentation\nmcp__knowns__get_doc({ path: "security-patterns" })\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\nmcp__knowns__update_task({\n taskId: "42",\n plan: "1. Review security patterns (see @doc/security-patterns)\\n2. Design token generation with 1-hour expiry\\n3. Implement /forgot-password endpoint\\n4. Add unit tests"\n})\n\n# 6. After approval, implement and update task progressively\nmcp__knowns__update_task({\n taskId: "42",\n appendNotes: "\u2713 Implemented /forgot-password endpoint"\n})\n\n# 7. Add final implementation notes\nmcp__knowns__update_task({\n taskId: "42",\n notes: "## Summary\\nImplemented complete password reset flow.\\n\\n## Changes\\n- Added POST /forgot-password endpoint\\n- Added POST /reset-password endpoint"\n})\n\n# 8. Stop timer and complete\nmcp__knowns__stop_time({ taskId: "42" })\nmcp__knowns__update_task({\n taskId: "42",\n status: "done"\n})\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```\n# Update task status and assign to self\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress",\n assignee: "@<your-username>"\n})\n```\n\n> **Note**: Use your username as configured in the project. Check project config for `defaultAssignee`.\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```\n# Search for related docs\nmcp__knowns__search_docs({ query: "authentication" })\n\n# View relevant documents\nmcp__knowns__get_doc({ path: "API Guidelines" })\nmcp__knowns__get_doc({ path: "Security Patterns" })\n\n# Also check for similar completed tasks\nmcp__knowns__search_tasks({ query: "auth", status: "done" })\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning!\n\n### Step 4: Create Implementation Plan\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n plan: "1. Research patterns (see @doc/security-patterns)\\n2. Design middleware\\n3. Implement\\n4. Add tests\\n5. Update docs"\n})\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```\n# Work through implementation plan step by step\n# IMPORTANT: Only update task AFTER completing the work, not before\n\n# After completing work, append notes:\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Completed: <brief description>"\n})\n```\n\n> **CRITICAL**: Never claim work is done before it\'s actually completed.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```\n# Append note about scope change\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n})\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```\n# Add comprehensive notes (suitable for PR description)\nmcp__knowns__update_task({\n taskId: "<id>",\n notes: "## Summary\\n\\nImplemented JWT auth.\\n\\n## Changes\\n- Added middleware\\n- Added tests"\n})\n\n# OR append progressively (recommended)\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Implemented middleware"\n})\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry:\n> `mcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Forgot to stop timer" })`\n\n### Step 9: Complete Task\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "done"\n})\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```\n# 1. Reopen task - set back to in-progress\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress"\n})\n\n# 2. Restart time tracking (REQUIRED)\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# 3. Document the reopen reason\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u{1F504} Reopened: User requested changes - <reason>"\n})\n\n# 4. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```\n# Search if similar pattern already documented\nmcp__knowns__search_docs({ query: "<pattern/concept>" })\n\n# If new knowledge, create a doc for future reference\nmcp__knowns__create_doc({\n title: "Pattern: <Name>",\n description: "Reusable pattern discovered during task implementation",\n tags: ["pattern", "<domain>"],\n folder: "patterns"\n})\n\n# Or append to existing doc\nmcp__knowns__update_doc({\n path: "<existing-doc>",\n appendContent: "## New Section\\n\\nLearned from task <id>: ..."\n})\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential MCP Tools\n\n### Task Management\n\n```\n# Create task\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high", # low, medium, high\n labels: ["label1"],\n status: "todo", # todo, in-progress, in-review, done, blocked\n assignee: "@username"\n})\n\n# Get task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# Update task\nmcp__knowns__update_task({\n taskId: "<id>",\n title: "New title",\n description: "New description",\n status: "in-progress",\n priority: "high",\n assignee: "@username",\n labels: ["new", "labels"],\n plan: "Implementation plan...",\n notes: "Implementation notes...",\n appendNotes: "Append to existing notes..."\n})\n\n# List tasks\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__list_tasks({ assignee: "@username" })\nmcp__knowns__list_tasks({ priority: "high" })\nmcp__knowns__list_tasks({ label: "feature" })\n\n# Search tasks\nmcp__knowns__search_tasks({ query: "auth" })\n```\n\n### Time Tracking\n\n```\n# Start timer\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# Stop timer\nmcp__knowns__stop_time({ taskId: "<id>" })\n\n# Manual entry\nmcp__knowns__add_time({\n taskId: "<id>",\n duration: "2h", # e.g., "2h", "30m", "1h30m"\n note: "Optional note",\n date: "2025-12-25" # Optional, defaults to now\n})\n\n# Reports\nmcp__knowns__get_time_report({})\nmcp__knowns__get_time_report({\n from: "2025-12-01",\n to: "2025-12-31",\n groupBy: "task" # task, label, status\n})\n```\n\n### Documentation\n\n```\n# List docs\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "architecture" })\n\n# Get doc\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "patterns/module" })\n\n# Create doc\nmcp__knowns__create_doc({\n title: "Title",\n description: "Description",\n tags: ["tag1", "tag2"],\n folder: "folder/path", # Optional\n content: "Initial content..."\n})\n\n# Update doc\nmcp__knowns__update_doc({\n path: "doc-name",\n title: "New Title",\n description: "New description",\n tags: ["new", "tags"],\n content: "Replace content...",\n appendContent: "Append to content..."\n})\n\n# Search docs\nmcp__knowns__search_docs({ query: "patterns" })\nmcp__knowns__search_docs({ query: "auth", tag: "security" })\n```\n\n### Board\n\n```\n# Get kanban board view\nmcp__knowns__get_board({})\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via MCP Tools (Required)\n\n- [ ] All work completed and verified\n- [ ] Implementation notes added via `update_task`\n- [ ] \u23F1\uFE0F Timer stopped: `mcp__knowns__stop_time` (MANDATORY - do not skip!)\n- [ ] Status set to done via `update_task`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use MCP tools |\n| Skip time tracking | ALWAYS use `start_time` and `stop_time` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Plan without checking docs | Read docs before planning |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Verify ALL criteria first |\n| Ignore refs in task description | Follow ALL refs before planning |\n| Follow only first-level refs | Recursively follow nested refs |\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `mcp__knowns__list_docs({})`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task details retrieved: `mcp__knowns__get_task`\n- [ ] ALL refs in task followed\n- [ ] Nested refs recursively followed\n- [ ] Related docs searched and read\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self via `update_task`\n- [ ] Status set to in-progress\n- [ ] Timer started: `mcp__knowns__start_time`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Progress notes appended: `appendNotes`\n\n### After Work\n\n- [ ] All work verified complete\n- [ ] Implementation notes added\n- [ ] Timer stopped: `mcp__knowns__stop_time`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done\n- [ ] Knowledge extracted to docs (if applicable)\n\n---\n\n## Quick Reference\n\n```\n# === AGENT INITIALIZATION (Once per session) ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# === FULL WORKFLOW ===\nmcp__knowns__create_task({ title: "Title", description: "Description" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__search_docs({ query: "keyword" })\nmcp__knowns__get_doc({ path: "Doc Name" })\nmcp__knowns__search_tasks({ query: "keyword", status: "done" }) # Learn from history\nmcp__knowns__update_task({ taskId: "<id>", plan: "1. Step\\n2. Step" })\n# ... wait for approval, then implement ...\nmcp__knowns__update_task({ taskId: "<id>", appendNotes: "\u2713 Completed: feature X" })\nmcp__knowns__stop_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "bug" })\nmcp__knowns__search_docs({ query: "pattern" })\n\n# === TIME TRACKING ===\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h" })\nmcp__knowns__get_time_report({})\n\n# === DOCUMENTATION ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "path/doc-name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag"] })\nmcp__knowns__update_doc({ path: "doc-name", appendContent: "New content" })\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
46961
|
-
|
|
46962
46990
|
// src/commands/agents.ts
|
|
46963
46991
|
import { existsSync } from "node:fs";
|
|
46964
46992
|
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
46965
46993
|
import { dirname, join as join3 } from "node:path";
|
|
46966
46994
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
46995
|
+
|
|
46996
|
+
// src/templates/cli/gemini.md
|
|
46997
|
+
var gemini_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use CLI only\n- ALWAYS use `--plain` flag\n- Read docs BEFORE coding\n- Start timer when taking task, stop when done\n\n## SESSION START\n```bash\nknowns doc list --plain\nknowns doc "README" --plain\nknowns task list --plain\n```\n\n## TASK WORKFLOW\n\n### 1. Take Task\n```bash\nknowns task <id> --plain # View task\nknowns task edit <id> -s in-progress -a @me\nknowns time start <id>\n```\n\n### 2. Read Context\n```bash\nknowns doc list "guides/" --plain # List by folder\nknowns doc "path/name" --plain # View doc\nknowns search "keyword" --type doc --plain\n```\n\n### 3. Plan & Implement\n```bash\nknowns task edit <id> --plan $\'1. Step one\\n2. Step two\'\n# Wait for approval, then code\nknowns task edit <id> --check-ac 1 # Check criteria after done\nknowns task edit <id> --append-notes "Done: feature X"\n```\n\n### 4. Complete\n```bash\nknowns time stop\nknowns task edit <id> -s done\n```\n\n## COMMANDS CHEATSHEET\n\n### Task\n```bash\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task <id> --plain\nknowns task create "Title" -d "Desc" --ac "Criterion" --priority high\nknowns task edit <id> -s <status> -a @me\nknowns task edit <id> --check-ac 1 --check-ac 2\nknowns task edit <id> --plan "..."\nknowns task edit <id> --notes "..."\nknowns task edit <id> --append-notes "..."\n```\n\n### Doc\n```bash\nknowns doc list --plain\nknowns doc list "folder/" --plain\nknowns doc "name" --plain\nknowns doc create "Title" -d "Desc" -t "tags" -f "folder"\nknowns doc edit "name" -c "content"\nknowns doc edit "name" -a "append"\nknowns doc edit "name" --content-file ./file.md\nknowns doc edit "name" --append-file ./file.md\nknowns doc search-in "name" "query" --plain\nknowns doc replace "name" "old" "new"\n```\n\n### Time\n```bash\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time add <id> 2h -n "Note"\n```\n\n### Search\n```bash\nknowns search "query" --plain\nknowns search "query" --type doc --plain\nknowns search "query" --type task --plain\n```\n\n## REFS\n\n### Reading (output format)\n- `@.knowns/tasks/task-X - Title.md` \u2192 `knowns task X --plain`\n- `@.knowns/docs/path/name.md` \u2192 `knowns doc "path/name" --plain`\n\n### Writing (input format)\n- Task: `@task-X`\n- Doc: `@doc/path/name`\n\n## STATUS & PRIORITY\n\n**Status:** `todo`, `in-progress`, `in-review`, `blocked`, `done`\n**Priority:** `low`, `medium`, `high`\n\n## LONG CONTENT\n\nWindows has ~8191 char limit. Use:\n\n```bash\n# Append in chunks\nknowns doc edit "name" -a "Section 1..."\nknowns doc edit "name" -a "Section 2..."\n\n# Or file-based\nknowns doc edit "name" --content-file ./content.md\nknowns doc edit "name" --append-file ./more.md\n```\n\n## VALIDATE & REPAIR\n\n```bash\nknowns doc validate "name" --plain\nknowns doc repair "name" --plain\nknowns task validate <id> --plain\nknowns task repair <id> --plain\n```\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
46998
|
+
|
|
46999
|
+
// src/templates/cli/general.md
|
|
47000
|
+
var general_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI Guidelines\n\n## Core Principles\n\n### 1. CLI-Only Operations\n**NEVER edit .md files directly. ALL operations MUST use CLI commands.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```bash\n# 1. List all available documentation\nknowns doc list --plain\n\n# 2. Read essential project docs (prioritize these)\nknowns doc "README" --plain # Project overview\nknowns doc "ARCHITECTURE" --plain # System design\nknowns doc "CONVENTIONS" --plain # Coding standards\nknowns doc "API" --plain # API specifications\n\n# 3. Review current task backlog\nknowns task list --plain\nknowns task list --status in-progress --plain\n```\n\n### Before Taking Any Task\n\n```bash\n# 1. View the task details\nknowns task <id> --plain\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 knowns task 44 --plain\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# 3. Search for additional related documentation\nknowns search "<keywords from task>" --type doc --plain\n\n# 4. Read ALL related docs before planning\nknowns doc "<related-doc>" --plain\n\n# 5. Check for similar completed tasks (learn from history)\nknowns search "<keywords>" --type task --status done --plain\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | CLI Command |\n|------|---------------------|----------------------|-------------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `knowns task <id> --plain` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `knowns doc <path> --plain` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output from `--plain`: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format (`@task-<id>`, `@doc/<path>`)\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```bash\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nknowns task 44 --plain\n\n# Follow doc ref (extract path, remove .md)\nknowns doc "patterns/module" --plain\n```\n\n### Parsing Rules\n\n1. **Task refs**: `@.knowns/tasks/task-<id> - ...` \u2192 extract `<id>` \u2192 `knowns task <id> --plain`\n2. **Doc refs**: `@.knowns/docs/<path>.md` \u2192 extract `<path>` \u2192 `knowns doc "<path>" --plain`\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n### When to Follow Refs\n\n| Situation | Action |\n|-----------|--------|\n| Refs in Description | ALWAYS follow - critical context |\n| Refs in Implementation Plan | Follow if implementing related work |\n| Refs in Notes | Optional - for historical context |\n| Dependency mentions | Follow if marked as blocker |\n\n### Example: Complete Ref Resolution\n\n```bash\n# 1. Read the task\n$ knowns task 42 --plain\n\n# Output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/README.md\n\n# 2. Follow task ref\n$ knowns task 44 --plain\n\n# 3. Follow doc ref\n$ knowns doc "README" --plain\n\n# 4. If README contains more refs, follow them too\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# Now you have complete context\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```bash\n# Initialize project\nknowns init [name]\n\n# Create task with acceptance criteria\nknowns task create "Title" -d "Description" --ac "Criterion 1" --ac "Criterion 2"\n\n# View task (ALWAYS use --plain for AI)\nknowns task <id> --plain # Shorthand\nknowns task view <id> --plain # Full command\n\n# View doc (ALWAYS use --plain for AI)\nknowns doc <path> --plain # Shorthand\nknowns doc view "<path>" --plain # Full command\n\n# List tasks\nknowns task list --plain\n\n# Search (tasks + docs)\nknowns search "query" --plain\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```bash\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\n$ knowns doc list --plain\n\n# Output:\n# DOC: README.md\n# DOC: ARCHITECTURE.md\n# DOC: CONVENTIONS.md\n# DOC: security-patterns.md\n# DOC: api-guidelines.md\n# DOC: email-templates.md\n\n# 0b. Read essential project docs\n$ knowns doc "README" --plain\n$ knowns doc "ARCHITECTURE" --plain\n$ knowns doc "CONVENTIONS" --plain\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\n$ knowns task create "Add password reset flow" \\\n -d "Users need ability to reset forgotten passwords via email" \\\n --ac "User can request password reset via email" \\\n --ac "Reset link expires after 1 hour" \\\n --ac "User can set new password via reset link" \\\n --ac "Unit tests cover all scenarios" \\\n --priority high \\\n -l "auth,feature"\n\n# Output: Created task AUTH-042\n\n# 2. Take the task and start timer (uses defaultAssignee or @me fallback)\n$ knowns task edit AUTH-042 -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n$ knowns time start AUTH-042\n\n# Output: Timer started for AUTH-042\n\n# 3. Search for related documentation\n$ knowns search "password security" --type doc --plain\n\n# Output:\n# DOC: security-patterns.md (score: 0.92)\n# DOC: email-templates.md (score: 0.78)\n\n# 4. Read the documentation\n$ knowns doc "security-patterns" --plain\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\n$ knowns task edit AUTH-042 --plan $\'1. Review security patterns (see @doc/security-patterns)\n2. Design token generation with 1-hour expiry\n3. Create email template (see @doc/email-templates)\n4. Implement /forgot-password endpoint\n5. Implement /reset-password endpoint\n6. Add unit tests\n7. Update API documentation\'\n\n# 6. After approval, implement and check criteria as you go\n$ knowns task edit AUTH-042 --check-ac 1\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /forgot-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 2\n$ knowns task edit AUTH-042 --append-notes "\u2713 Token expiry set to 1 hour"\n\n$ knowns task edit AUTH-042 --check-ac 3\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /reset-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 4\n$ knowns task edit AUTH-042 --append-notes "\u2713 Added 12 unit tests, 100% coverage"\n\n# 7. Add final implementation notes\n$ knowns task edit AUTH-042 --notes $\'## Summary\nImplemented complete password reset flow with secure token generation.\n\n## Changes\n- Added POST /forgot-password endpoint\n- Added POST /reset-password endpoint\n- Created password_reset_tokens table\n- Added email template for reset link\n\n## Security\n- Tokens use crypto.randomBytes(32)\n- 1-hour expiry enforced at DB level\n- Rate limiting: 3 requests per hour per email\n\n## Tests\n- 12 unit tests added\n- Coverage: 100% for new code\n\n## Documentation\n- Updated API.md with new endpoints\'\n\n# 8. Stop timer and complete\n$ knowns time stop\n$ knowns task edit AUTH-042 -s done\n\n# Output: Task AUTH-042 marked as done\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```bash\n# Assign using defaultAssignee from config (falls back to @me if not set)\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n```\n\n> **Note**: The `defaultAssignee` is configured in `.knowns/config.json` during `knowns init`. If not set, `@me` is used as fallback. To update: `knowns config set defaultAssignee "@username"`\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```bash\nknowns time start <id>\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```bash\n# Search for related docs\nknowns search "authentication" --type doc --plain\n\n# View relevant documents\nknowns doc "API Guidelines" --plain\nknowns doc "Security Patterns" --plain\n\n# Also check for similar completed tasks\nknowns search "auth" --type task --status done --plain\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning! Understanding existing patterns and conventions prevents rework and ensures consistency.\n\n### Step 4: Create Implementation Plan\n\n```bash\nknowns task edit <id> --plan $\'1. Research patterns (see @doc/security-patterns)\n2. Design middleware\n3. Implement\n4. Add tests\n5. Update docs\'\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```bash\n# Work through implementation plan step by step\n# IMPORTANT: Only check AC AFTER completing the work, not before\n\n# After completing work for AC #1:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n\n# After completing work for AC #2:\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n```\n\n> **CRITICAL**: Never check an AC before the work is actually done. ACs represent completed outcomes, not intentions.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```bash\n# Add new acceptance criteria\nknowns task edit <id> --ac "New requirement from user"\n\n# Update implementation plan to include new steps\nknowns task edit <id> --plan $\'1. Original step 1\n2. Original step 2\n3. NEW: Handle user request for X\n4. Continue with remaining work\'\n\n# Append note about scope change\nknowns task edit <id> --append-notes "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```bash\n# Add comprehensive notes (suitable for PR description)\nknowns task edit <id> --notes $\'## Summary\n\nImplemented JWT auth.\n\n## Changes\n- Added middleware\n- Added tests\'\n\n# OR append progressively (recommended)\nknowns task edit <id> --append-notes "\u2713 Implemented middleware"\nknowns task edit <id> --append-notes "\u2713 Added tests"\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```bash\nknowns time stop\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry: `knowns time add <id> <duration> -n "Forgot to stop timer"`\n\n### Step 9: Complete Task\n\n```bash\nknowns task edit <id> -s done\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```bash\n# 1. Reopen task - set back to in-progress\nknowns task edit <id> -s in-progress\n\n# 2. Restart time tracking (REQUIRED)\nknowns time start <id>\n\n# 3. Add new AC for the changes requested\nknowns task edit <id> --ac "Post-completion fix: <description>"\n\n# 4. Document the reopen reason\nknowns task edit <id> --append-notes "\u{1F504} Reopened: User requested changes - <reason>"\n\n# 5. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Add AC for traceability\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```bash\n# Search if similar pattern already documented\nknowns search "<pattern/concept>" --type doc --plain\n\n# If new knowledge, create a doc for future reference\nknowns doc create "Pattern: <Name>" \\\n -d "Reusable pattern discovered during task implementation" \\\n -t "pattern,<domain>" \\\n -f "patterns"\n\n# Or append to existing doc\nknowns doc edit "<existing-doc>" -a "## New Section\\n\\nLearned from task <id>: ..."\n\n# Reference the source task\nknowns doc edit "<doc-name>" -a "\\n\\n> Source: @task-<id>"\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential Commands\n\n### Task Management\n\n```bash\n# Create task\nknowns task create "Title" -d "Description" --ac "Criterion" -l "labels" --priority high\n\n# Edit task\nknowns task edit <id> -t "New title"\nknowns task edit <id> -d "New description"\nknowns task edit <id> -s in-progress\nknowns task edit <id> --priority high\nknowns task edit <id> -a <assignee> # $(knowns config get defaultAssignee --plain || echo "@me")\n\n# Acceptance Criteria\nknowns task edit <id> --ac "New criterion" # Add\nknowns task edit <id> --check-ac 1 --check-ac 2 # Check (1-indexed)\nknowns task edit <id> --uncheck-ac 2 # Uncheck\nknowns task edit <id> --remove-ac 3 # Remove\n\n# Implementation Plan & Notes\nknowns task edit <id> --plan $\'1. Step\\n2. Step\'\nknowns task edit <id> --notes "Implementation summary"\nknowns task edit <id> --append-notes "Progress update"\n\n# View & List\nknowns task <id> --plain # Shorthand (ALWAYS use --plain)\nknowns task view <id> --plain # Full command\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task list --assignee <assignee> --plain # $(knowns config get defaultAssignee --plain || echo "@me")\nknowns task list --tree --plain # Tree hierarchy\n```\n\n### Time Tracking\n\n```bash\n# Timer\nknowns time start <id>\nknowns time stop\nknowns time pause\nknowns time resume\nknowns time status\n\n# Manual entry\nknowns time add <id> 2h -n "Note" -d "2025-12-25"\n\n# Reports\nknowns time report --from "2025-12-01" --to "2025-12-31"\nknowns time report --by-label --csv > report.csv\n```\n\n### Documentation\n\n```bash\n# List & View\nknowns doc list --plain\nknowns doc list --tag architecture --plain\nknowns doc <path> --plain # Shorthand (ALWAYS use --plain)\nknowns doc view "<path>" --plain # Full command\n\n# Create (with optional folder)\nknowns doc create "Title" -d "Description" -t "tags"\nknowns doc create "Title" -d "Description" -t "tags" -f "folder/path"\n\n# Edit metadata\nknowns doc edit "Doc Name" -t "New Title" --tags "new,tags"\n\n# Edit content\nknowns doc edit "Doc Name" -c "New content" # Replace content\nknowns doc edit "Doc Name" -a "Appended content" # Append to content\n```\n\n### Search\n\n```bash\n# Search everything\nknowns search "query" --plain\n\n# Search specific type\nknowns search "auth" --type task --plain\nknowns search "patterns" --type doc --plain\n\n# Filter\nknowns search "bug" --status in-progress --priority high --plain\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```markdown\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n### Implementation Plan\n\nHOW to solve. Added AFTER taking task, BEFORE coding.\n\n```markdown\n1. Research JWT libraries (see @doc/security-patterns)\n2. Design token structure (access + refresh tokens)\n3. Implement auth middleware\n4. Add unit tests (aim for 90%+ coverage)\n5. Update API.md with new endpoints\n```\n\n### Implementation Notes\n\nSummary for PR description. Added AFTER completion.\n\n```markdown\n## Summary\nImplemented JWT auth using jsonwebtoken library.\n\n## Changes\n- Added auth middleware in src/middleware/auth.ts\n- Added /login and /refresh endpoints\n- Created JWT utility functions\n\n## Tests\n- Added 15 unit tests\n- Coverage: 94%\n\n## Documentation\n- Updated API.md with authentication section\n```\n\n---\n\n## Error Handling\n\n### Common Errors and Solutions\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Error: Task not found` | Invalid task ID | Run `knowns task list --plain` to find correct ID |\n| `Error: No active timer` | Calling `time stop` without active timer | Start timer first: `knowns time start <id>` |\n| `Error: Timer already running` | Starting timer when one is active | Stop current: `knowns time stop` |\n| `Error: Invalid status` | Wrong status format | Use lowercase with hyphens: `in-progress`, not `In Progress` |\n| `Error: AC index out of range` | Checking non-existent criterion | View task first: `knowns task <id> --plain` |\n| `Error: Document not found` | Wrong document name | Run `knowns doc list --plain` to find correct name |\n| `Error: Not initialized` | Running commands without init | Run `knowns init` first |\n\n### Debugging Commands\n\n```bash\n# Check CLI version\nknowns --version\n\n# Verify project is initialized\nknowns status\n\n# View raw task data (for debugging)\nknowns task <id> --json\n\n# Check timer status\nknowns time status\n```\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via CLI (Required)\n\n- [ ] All acceptance criteria checked: `--check-ac <index>` (only after work is actually done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] \u23F1\uFE0F Timer stopped: `knowns time stop` (MANDATORY - do not skip!)\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\nUse **lowercase with hyphens**:\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use `knowns task edit` |\n| Change `- [ ]` to `- [x]` in file | Use `--check-ac <index>` |\n| Check AC before completing work | Only check AC AFTER work is actually done |\n| Skip time tracking | ALWAYS use `time start` and `time stop` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Skip `knowns doc list` on new project | Always list docs when starting |\n| Assume you know the conventions | Read CONVENTIONS/ARCHITECTURE docs |\n| Plan without checking docs | Read docs before planning |\n| Ignore similar completed tasks | Search done tasks for patterns |\n| Missing doc links in description/plan | Link docs using `@doc/<path>` |\n| Write refs as `@.knowns/docs/...` or `@.knowns/tasks/...` | Use input format: `@doc/<path>`, `@task-<id>` |\n| Forget `--plain` flag | Always use `--plain` for AI |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Check ALL criteria first |\n| Write implementation steps in AC | Write outcome-oriented criteria |\n| Use `"In Progress"` or `"Done"` | Use `in-progress`, `done` |\n| Use `@yourself` or unknown assignee | Use `$(knowns config get defaultAssignee --plain \\|\\| echo "@me")` |\n| Ignore refs in task description | Follow ALL refs (`@.knowns/...`) before planning |\n| See `@.knowns/docs/...` but don\'t read | Use `knowns doc "<path>" --plain` |\n| See `@.knowns/tasks/task-X` but don\'t check | Use `knowns task X --plain` for context |\n| Follow only first-level refs | Recursively follow nested refs until complete |\n\n---\n\n## Platform-Specific Notes\n\n### Multi-line Input\n\nDifferent shells handle multi-line strings differently:\n\n**Bash / Zsh (Recommended)**\n```bash\nknowns task edit <id> --plan $\'1. First step\\n2. Second step\\n3. Third step\'\n```\n\n**PowerShell**\n```powershell\nknowns task edit <id> --plan "1. First step`n2. Second step`n3. Third step"\n```\n\n**Cross-platform (Using printf)**\n```bash\nknowns task edit <id> --plan "$(printf \'1. First step\\n2. Second step\\n3. Third step\')"\n```\n\n**Using heredoc (for long content)**\n```bash\nknowns task edit <id> --plan "$(cat <<EOF\n1. First step\n2. Second step\n3. Third step\nEOF\n)"\n```\n\n### Path Separators\n\n- **Unix/macOS**: Use forward slashes: `./docs/api.md`\n- **Windows**: Both work, but prefer forward slashes for consistency\n\n### Windows Command Line Limit\n\nWindows has ~8191 character limit. For long content, append in chunks:\n\n```bash\n# 1. Create or reset with short content\nknowns doc edit "doc-name" -c "## Overview\\n\\nShort intro."\n\n# 2. Append each section separately\nknowns doc edit "doc-name" -a "## Section 1\\n\\nContent..."\nknowns doc edit "doc-name" -a "## Section 2\\n\\nMore content..."\n```\n\nOr use file-based options:\n\n```bash\nknowns doc edit "doc-name" --content-file ./content.md\nknowns doc edit "doc-name" --append-file ./more.md\n```\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `knowns doc list --plain`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task has clear acceptance criteria\n- [ ] ALL refs in task followed (`@.knowns/...`)\n- [ ] Nested refs recursively followed until complete context gathered\n- [ ] Related docs searched: `knowns search "keyword" --type doc --plain`\n- [ ] ALL relevant docs read: `knowns doc "Doc Name" --plain`\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self: `-a $(knowns config get defaultAssignee --plain || echo "@me")`\n- [ ] Status set to in-progress: `-s in-progress`\n- [ ] Timer started: `knowns time start <id>`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Criteria checked as completed: `--check-ac <index>`\n- [ ] Progress notes appended: `--append-notes "\u2713 ..."`\n\n### After Work\n\n- [ ] All acceptance criteria checked (only after work is done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] Timer stopped: `knowns time stop`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable): patterns, solutions, conventions\n\n---\n\n## Quick Reference Card\n\n```bash\n# === AGENT INITIALIZATION (Once per session) ===\nknowns doc list --plain\nknowns doc "README" --plain\nknowns doc "ARCHITECTURE" --plain\nknowns doc "CONVENTIONS" --plain\n\n# === FULL WORKFLOW ===\nknowns task create "Title" -d "Description" --ac "Criterion"\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\nknowns time start <id> # \u23F1\uFE0F REQUIRED: Start timer\nknowns search "keyword" --type doc --plain\nknowns doc "Doc Name" --plain\nknowns search "keyword" --type task --status done --plain # Learn from history\nknowns task edit <id> --plan $\'1. Step (see @doc/file)\\n2. Step\'\n# ... wait for approval, then implement ...\n# Only check AC AFTER completing the work:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: feature X"\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: feature Y"\nknowns time stop # \u23F1\uFE0F REQUIRED: Stop timer\nknowns task edit <id> -s done\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nknowns task <id> --plain # Shorthand for view\nknowns task list --plain\nknowns task list --status in-progress --assignee $(knowns config get defaultAssignee --plain || echo "@me") --plain\nknowns search "query" --plain\nknowns search "bug" --type task --status in-progress --plain\n\n# === TIME TRACKING ===\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time report --from "2025-12-01" --to "2025-12-31"\n\n# === DOCUMENTATION ===\nknowns doc list --plain\nknowns doc "path/doc-name" --plain # Shorthand for view\nknowns doc create "Title" -d "Description" -t "tags" -f "folder"\nknowns doc edit "doc-name" -c "New content"\nknowns doc edit "doc-name" -a "Appended content"\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47001
|
+
|
|
47002
|
+
// src/templates/mcp/gemini.md
|
|
47003
|
+
var gemini_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use MCP tools only\n- Read docs BEFORE coding\n- Start timer when taking task, stop when done\n\n## SESSION START\n```\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__list_tasks({})\n```\n\n## TASK WORKFLOW\n\n### 1. Take Task\n```\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n### 2. Read Context\n```\nmcp__knowns__list_docs({ tag: "guides" })\nmcp__knowns__get_doc({ path: "path/name" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n### 3. Plan & Implement\n```\nmcp__knowns__update_task({ taskId: "<id>", description: "Updated with plan..." })\n// Wait for approval, then code\nmcp__knowns__update_task({ taskId: "<id>", labels: ["done-ac-1"] })\n```\n\n### 4. Complete\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n```\n\n## MCP TOOLS CHEATSHEET\n\n### Task\n```\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__create_task({ title: "Title", description: "Desc", priority: "high" })\nmcp__knowns__update_task({ taskId: "<id>", status: "<status>", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "keyword" })\n```\n\n### Doc\n```\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "patterns" })\nmcp__knowns__get_doc({ path: "name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag1"] })\nmcp__knowns__update_doc({ path: "name", content: "new content" })\nmcp__knowns__update_doc({ path: "name", appendContent: "more content" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n### Time\n```\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Note" })\nmcp__knowns__get_time_report({})\n```\n\n### Board\n```\nmcp__knowns__get_board({})\n```\n\n## REFS\n\n### Reading (output format)\n- `@.knowns/tasks/task-X - Title.md` \u2192 `mcp__knowns__get_task({ taskId: "X" })`\n- `@.knowns/docs/path/name.md` \u2192 `mcp__knowns__get_doc({ path: "path/name" })`\n\n### Writing (input format)\n- Task: `@task-X`\n- Doc: `@doc/path/name`\n\n## STATUS & PRIORITY\n\n**Status:** `todo`, `in-progress`, `in-review`, `blocked`, `done`\n**Priority:** `low`, `medium`, `high`\n\n## LONG CONTENT\n\nFor large docs, append in chunks:\n```\n# Create with initial content\nmcp__knowns__create_doc({ title: "Title", content: "## Overview\\n\\nIntro." })\n\n# Append sections\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 1\\n\\n..." })\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 2\\n\\n..." })\n```\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47004
|
+
|
|
47005
|
+
// src/templates/mcp/general.md
|
|
47006
|
+
var general_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP Guidelines\n\n## Core Principles\n\n### 1. MCP Tool Operations\n**Use MCP tools for ALL Knowns operations. NEVER edit .md files directly.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```\n# 1. List all available documentation\nmcp__knowns__list_docs({})\n\n# 2. Read essential project docs (prioritize these)\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\nmcp__knowns__get_doc({ path: "API" })\n\n# 3. Review current task backlog\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\n```\n\n### Before Taking Any Task\n\n```\n# 1. View the task details\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 mcp__knowns__get_task({ taskId: "44" })\n# @.knowns/docs/patterns/module.md \u2192 mcp__knowns__get_doc({ path: "patterns/module" })\n\n# 3. Search for additional related documentation\nmcp__knowns__search_docs({ query: "<keywords from task>" })\n\n# 4. Read ALL related docs before planning\nmcp__knowns__get_doc({ path: "<related-doc>" })\n\n# 5. Check for similar completed tasks (learn from history)\nmcp__knowns__search_tasks({ query: "<keywords>", status: "done" })\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | MCP Tool |\n|------|---------------------|----------------------|----------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `mcp__knowns__get_task({ taskId: "<id>" })` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `mcp__knowns__get_doc({ path: "<path>" })` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nmcp__knowns__get_task({ taskId: "44" })\n\n# Follow doc ref (extract path, remove .md)\nmcp__knowns__get_doc({ path: "patterns/module" })\n```\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```\n# Initialize project (use CLI for this)\nknowns init [name]\n\n# Create task with acceptance criteria\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high",\n labels: ["label1", "label2"]\n})\n\n# View task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# List tasks\nmcp__knowns__list_tasks({})\n\n# Search (tasks + docs)\nmcp__knowns__search_tasks({ query: "query" })\nmcp__knowns__search_docs({ query: "query" })\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\nmcp__knowns__list_docs({})\n\n# 0b. Read essential project docs\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\nmcp__knowns__create_task({\n title: "Add password reset flow",\n description: "Users need ability to reset forgotten passwords via email",\n priority: "high",\n labels: ["auth", "feature"]\n})\n\n# Output: Created task 42\n\n# 2. Take the task and start timer\nmcp__knowns__update_task({\n taskId: "42",\n status: "in-progress",\n assignee: "@howznguyen"\n})\nmcp__knowns__start_time({ taskId: "42" })\n\n# 3. Search for related documentation\nmcp__knowns__search_docs({ query: "password security" })\n\n# 4. Read the documentation\nmcp__knowns__get_doc({ path: "security-patterns" })\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\nmcp__knowns__update_task({\n taskId: "42",\n plan: "1. Review security patterns (see @doc/security-patterns)\\n2. Design token generation with 1-hour expiry\\n3. Implement /forgot-password endpoint\\n4. Add unit tests"\n})\n\n# 6. After approval, implement and update task progressively\nmcp__knowns__update_task({\n taskId: "42",\n appendNotes: "\u2713 Implemented /forgot-password endpoint"\n})\n\n# 7. Add final implementation notes\nmcp__knowns__update_task({\n taskId: "42",\n notes: "## Summary\\nImplemented complete password reset flow.\\n\\n## Changes\\n- Added POST /forgot-password endpoint\\n- Added POST /reset-password endpoint"\n})\n\n# 8. Stop timer and complete\nmcp__knowns__stop_time({ taskId: "42" })\nmcp__knowns__update_task({\n taskId: "42",\n status: "done"\n})\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```\n# Update task status and assign to self\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress",\n assignee: "@<your-username>"\n})\n```\n\n> **Note**: Use your username as configured in the project. Check project config for `defaultAssignee`.\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```\n# Search for related docs\nmcp__knowns__search_docs({ query: "authentication" })\n\n# View relevant documents\nmcp__knowns__get_doc({ path: "API Guidelines" })\nmcp__knowns__get_doc({ path: "Security Patterns" })\n\n# Also check for similar completed tasks\nmcp__knowns__search_tasks({ query: "auth", status: "done" })\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning!\n\n### Step 4: Create Implementation Plan\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n plan: "1. Research patterns (see @doc/security-patterns)\\n2. Design middleware\\n3. Implement\\n4. Add tests\\n5. Update docs"\n})\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```\n# Work through implementation plan step by step\n# IMPORTANT: Only update task AFTER completing the work, not before\n\n# After completing work, append notes:\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Completed: <brief description>"\n})\n```\n\n> **CRITICAL**: Never claim work is done before it\'s actually completed.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```\n# Append note about scope change\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n})\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```\n# Add comprehensive notes (suitable for PR description)\nmcp__knowns__update_task({\n taskId: "<id>",\n notes: "## Summary\\n\\nImplemented JWT auth.\\n\\n## Changes\\n- Added middleware\\n- Added tests"\n})\n\n# OR append progressively (recommended)\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Implemented middleware"\n})\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry:\n> `mcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Forgot to stop timer" })`\n\n### Step 9: Complete Task\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "done"\n})\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```\n# 1. Reopen task - set back to in-progress\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress"\n})\n\n# 2. Restart time tracking (REQUIRED)\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# 3. Document the reopen reason\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u{1F504} Reopened: User requested changes - <reason>"\n})\n\n# 4. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```\n# Search if similar pattern already documented\nmcp__knowns__search_docs({ query: "<pattern/concept>" })\n\n# If new knowledge, create a doc for future reference\nmcp__knowns__create_doc({\n title: "Pattern: <Name>",\n description: "Reusable pattern discovered during task implementation",\n tags: ["pattern", "<domain>"],\n folder: "patterns"\n})\n\n# Or append to existing doc\nmcp__knowns__update_doc({\n path: "<existing-doc>",\n appendContent: "## New Section\\n\\nLearned from task <id>: ..."\n})\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential MCP Tools\n\n### Task Management\n\n```\n# Create task\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high", # low, medium, high\n labels: ["label1"],\n status: "todo", # todo, in-progress, in-review, done, blocked\n assignee: "@username"\n})\n\n# Get task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# Update task\nmcp__knowns__update_task({\n taskId: "<id>",\n title: "New title",\n description: "New description",\n status: "in-progress",\n priority: "high",\n assignee: "@username",\n labels: ["new", "labels"],\n plan: "Implementation plan...",\n notes: "Implementation notes...",\n appendNotes: "Append to existing notes..."\n})\n\n# List tasks\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__list_tasks({ assignee: "@username" })\nmcp__knowns__list_tasks({ priority: "high" })\nmcp__knowns__list_tasks({ label: "feature" })\n\n# Search tasks\nmcp__knowns__search_tasks({ query: "auth" })\n```\n\n### Time Tracking\n\n```\n# Start timer\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# Stop timer\nmcp__knowns__stop_time({ taskId: "<id>" })\n\n# Manual entry\nmcp__knowns__add_time({\n taskId: "<id>",\n duration: "2h", # e.g., "2h", "30m", "1h30m"\n note: "Optional note",\n date: "2025-12-25" # Optional, defaults to now\n})\n\n# Reports\nmcp__knowns__get_time_report({})\nmcp__knowns__get_time_report({\n from: "2025-12-01",\n to: "2025-12-31",\n groupBy: "task" # task, label, status\n})\n```\n\n### Documentation\n\n```\n# List docs\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "architecture" })\n\n# Get doc\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "patterns/module" })\n\n# Create doc\nmcp__knowns__create_doc({\n title: "Title",\n description: "Description",\n tags: ["tag1", "tag2"],\n folder: "folder/path", # Optional\n content: "Initial content..."\n})\n\n# Update doc\nmcp__knowns__update_doc({\n path: "doc-name",\n title: "New Title",\n description: "New description",\n tags: ["new", "tags"],\n content: "Replace content...",\n appendContent: "Append to content..."\n})\n\n# Search docs\nmcp__knowns__search_docs({ query: "patterns" })\nmcp__knowns__search_docs({ query: "auth", tag: "security" })\n```\n\n### Board\n\n```\n# Get kanban board view\nmcp__knowns__get_board({})\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via MCP Tools (Required)\n\n- [ ] All work completed and verified\n- [ ] Implementation notes added via `update_task`\n- [ ] \u23F1\uFE0F Timer stopped: `mcp__knowns__stop_time` (MANDATORY - do not skip!)\n- [ ] Status set to done via `update_task`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use MCP tools |\n| Skip time tracking | ALWAYS use `start_time` and `stop_time` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Plan without checking docs | Read docs before planning |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Verify ALL criteria first |\n| Ignore refs in task description | Follow ALL refs before planning |\n| Follow only first-level refs | Recursively follow nested refs |\n\n---\n\n## Long Content Handling\n\nFor long documentation content, append in chunks:\n\n```\n# 1. Create doc with initial content\nmcp__knowns__create_doc({\n title: "Doc Title",\n content: "## Overview\\n\\nShort intro."\n})\n\n# 2. Append each section separately\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 1\\n\\nContent for section 1..."\n})\n\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 2\\n\\nContent for section 2..."\n})\n```\n\n> **Tip**: Each `appendContent` adds content after existing content. Use this for large docs to avoid context limits.\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `mcp__knowns__list_docs({})`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task details retrieved: `mcp__knowns__get_task`\n- [ ] ALL refs in task followed\n- [ ] Nested refs recursively followed\n- [ ] Related docs searched and read\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self via `update_task`\n- [ ] Status set to in-progress\n- [ ] Timer started: `mcp__knowns__start_time`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Progress notes appended: `appendNotes`\n\n### After Work\n\n- [ ] All work verified complete\n- [ ] Implementation notes added\n- [ ] Timer stopped: `mcp__knowns__stop_time`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done\n- [ ] Knowledge extracted to docs (if applicable)\n\n---\n\n## Quick Reference\n\n```\n# === AGENT INITIALIZATION (Once per session) ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# === FULL WORKFLOW ===\nmcp__knowns__create_task({ title: "Title", description: "Description" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__search_docs({ query: "keyword" })\nmcp__knowns__get_doc({ path: "Doc Name" })\nmcp__knowns__search_tasks({ query: "keyword", status: "done" }) # Learn from history\nmcp__knowns__update_task({ taskId: "<id>", plan: "1. Step\\n2. Step" })\n# ... wait for approval, then implement ...\nmcp__knowns__update_task({ taskId: "<id>", appendNotes: "\u2713 Completed: feature X" })\nmcp__knowns__stop_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "bug" })\nmcp__knowns__search_docs({ query: "pattern" })\n\n# === TIME TRACKING ===\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h" })\nmcp__knowns__get_time_report({})\n\n# === DOCUMENTATION ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "path/doc-name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag"] })\nmcp__knowns__update_doc({ path: "doc-name", appendContent: "New content" })\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47007
|
+
|
|
47008
|
+
// src/commands/agents.ts
|
|
46967
47009
|
var PROJECT_ROOT = process.cwd();
|
|
46968
47010
|
var INSTRUCTION_FILES = [
|
|
46969
47011
|
{ path: "CLAUDE.md", name: "Claude Code", selected: true },
|
|
@@ -46971,8 +47013,11 @@ var INSTRUCTION_FILES = [
|
|
|
46971
47013
|
{ path: "GEMINI.md", name: "Gemini", selected: false },
|
|
46972
47014
|
{ path: ".github/copilot-instructions.md", name: "GitHub Copilot", selected: false }
|
|
46973
47015
|
];
|
|
46974
|
-
function getGuidelines(
|
|
46975
|
-
|
|
47016
|
+
function getGuidelines(type, variant = "general") {
|
|
47017
|
+
if (type === "mcp") {
|
|
47018
|
+
return variant === "gemini" ? gemini_default2 : general_default2;
|
|
47019
|
+
}
|
|
47020
|
+
return variant === "gemini" ? gemini_default : general_default;
|
|
46976
47021
|
}
|
|
46977
47022
|
async function updateInstructionFile(filePath, guidelines) {
|
|
46978
47023
|
const fullPath = join3(PROJECT_ROOT, filePath);
|
|
@@ -47003,11 +47048,12 @@ ${guidelines}
|
|
|
47003
47048
|
await writeFile2(fullPath, newContent, "utf-8");
|
|
47004
47049
|
return { success: true, action: "updated" };
|
|
47005
47050
|
}
|
|
47006
|
-
var agentsCommand = new Command("agents").description("Manage agent instruction files (CLAUDE.md, GEMINI.md, etc.)").option("--update-instructions", "Update agent instruction files (non-interactive)").option("--type <type>", "Guidelines type: cli or mcp", "cli").option("--files <files>", "Comma-separated list of files to update").action(async (options2) => {
|
|
47051
|
+
var agentsCommand = new Command("agents").description("Manage agent instruction files (CLAUDE.md, GEMINI.md, etc.)").enablePositionalOptions().passThroughOptions().option("--update-instructions", "Update agent instruction files (non-interactive)").option("--type <type>", "Guidelines type: cli or mcp", "cli").option("--gemini", "Use compact Gemini variant (smaller context)").option("--files <files>", "Comma-separated list of files to update").action(async (options2) => {
|
|
47007
47052
|
try {
|
|
47008
47053
|
if (options2.updateInstructions) {
|
|
47009
|
-
const
|
|
47010
|
-
const
|
|
47054
|
+
const type = options2.type === "mcp" ? "mcp" : "cli";
|
|
47055
|
+
const variant = options2.gemini ? "gemini" : "general";
|
|
47056
|
+
const guidelines2 = getGuidelines(type, variant);
|
|
47011
47057
|
let filesToUpdate = INSTRUCTION_FILES;
|
|
47012
47058
|
if (options2.files) {
|
|
47013
47059
|
const requestedFiles = options2.files.split(",").map((f) => f.trim());
|
|
@@ -47015,32 +47061,55 @@ var agentsCommand = new Command("agents").description("Manage agent instruction
|
|
|
47015
47061
|
(f) => requestedFiles.includes(f.path) || requestedFiles.includes(f.name)
|
|
47016
47062
|
);
|
|
47017
47063
|
}
|
|
47064
|
+
const label2 = `${type.toUpperCase()}${variant === "gemini" ? " (Gemini)" : ""}`;
|
|
47018
47065
|
console.log(source_default.bold(`
|
|
47019
|
-
Updating agent instruction files (${
|
|
47066
|
+
Updating agent instruction files (${label2})...
|
|
47020
47067
|
`));
|
|
47021
47068
|
await updateFiles(filesToUpdate, guidelines2);
|
|
47022
47069
|
return;
|
|
47023
47070
|
}
|
|
47024
47071
|
console.log(source_default.bold("\n\u{1F916} Agent Instructions Manager\n"));
|
|
47025
|
-
const
|
|
47072
|
+
const typeResponse = await (0, import_prompts.default)({
|
|
47026
47073
|
type: "select",
|
|
47027
|
-
name: "
|
|
47028
|
-
message: "Select guidelines
|
|
47074
|
+
name: "type",
|
|
47075
|
+
message: "Select guidelines type:",
|
|
47029
47076
|
choices: [
|
|
47030
47077
|
{
|
|
47031
47078
|
title: "CLI",
|
|
47032
47079
|
value: "cli",
|
|
47033
|
-
description: "
|
|
47080
|
+
description: "CLI commands (knowns task edit, knowns doc view, etc.)"
|
|
47034
47081
|
},
|
|
47035
47082
|
{
|
|
47036
47083
|
title: "MCP",
|
|
47037
47084
|
value: "mcp",
|
|
47038
|
-
description: "
|
|
47085
|
+
description: "MCP tools (mcp__knowns__update_task, etc.)"
|
|
47086
|
+
}
|
|
47087
|
+
],
|
|
47088
|
+
initial: 0
|
|
47089
|
+
});
|
|
47090
|
+
if (!typeResponse.type) {
|
|
47091
|
+
console.log(source_default.yellow("\n\u26A0\uFE0F Cancelled"));
|
|
47092
|
+
return;
|
|
47093
|
+
}
|
|
47094
|
+
const variantResponse = await (0, import_prompts.default)({
|
|
47095
|
+
type: "select",
|
|
47096
|
+
name: "variant",
|
|
47097
|
+
message: "Select variant:",
|
|
47098
|
+
choices: [
|
|
47099
|
+
{
|
|
47100
|
+
title: "General (Full)",
|
|
47101
|
+
value: "general",
|
|
47102
|
+
description: "Complete guidelines for Claude, GPT, etc."
|
|
47103
|
+
},
|
|
47104
|
+
{
|
|
47105
|
+
title: "Gemini (Compact)",
|
|
47106
|
+
value: "gemini",
|
|
47107
|
+
description: "Condensed version for Gemini 2.5 Flash (smaller context)"
|
|
47039
47108
|
}
|
|
47040
47109
|
],
|
|
47041
47110
|
initial: 0
|
|
47042
47111
|
});
|
|
47043
|
-
if (!
|
|
47112
|
+
if (!variantResponse.variant) {
|
|
47044
47113
|
console.log(source_default.yellow("\n\u26A0\uFE0F Cancelled"));
|
|
47045
47114
|
return;
|
|
47046
47115
|
}
|
|
@@ -47059,19 +47128,23 @@ Updating agent instruction files (${version2.toUpperCase()} version)...
|
|
|
47059
47128
|
console.log(source_default.yellow("\n\u26A0\uFE0F No files selected"));
|
|
47060
47129
|
return;
|
|
47061
47130
|
}
|
|
47131
|
+
const label = `${typeResponse.type.toUpperCase()}${variantResponse.variant === "gemini" ? " (Gemini)" : ""}`;
|
|
47062
47132
|
const confirmResponse = await (0, import_prompts.default)({
|
|
47063
47133
|
type: "confirm",
|
|
47064
47134
|
name: "confirm",
|
|
47065
|
-
message: `Update ${filesResponse.files.length} file(s) with ${
|
|
47135
|
+
message: `Update ${filesResponse.files.length} file(s) with ${label} guidelines?`,
|
|
47066
47136
|
initial: true
|
|
47067
47137
|
});
|
|
47068
47138
|
if (!confirmResponse.confirm) {
|
|
47069
47139
|
console.log(source_default.yellow("\n\u26A0\uFE0F Cancelled"));
|
|
47070
47140
|
return;
|
|
47071
47141
|
}
|
|
47072
|
-
const guidelines = getGuidelines(
|
|
47142
|
+
const guidelines = getGuidelines(
|
|
47143
|
+
typeResponse.type,
|
|
47144
|
+
variantResponse.variant
|
|
47145
|
+
);
|
|
47073
47146
|
console.log(source_default.bold(`
|
|
47074
|
-
Updating files with ${
|
|
47147
|
+
Updating files with ${label} guidelines...
|
|
47075
47148
|
`));
|
|
47076
47149
|
await updateFiles(filesResponse.files, guidelines);
|
|
47077
47150
|
} catch (error46) {
|
|
@@ -47122,6 +47195,23 @@ async function updateFiles(files, guidelines) {
|
|
|
47122
47195
|
}
|
|
47123
47196
|
console.log();
|
|
47124
47197
|
}
|
|
47198
|
+
var syncCommand = new Command("sync").description("Sync/update all agent instruction files with latest guidelines").option("--type <type>", "Guidelines type: cli or mcp", "cli").option("--gemini", "Use compact Gemini variant (smaller context)").option("--all", "Update all instruction files (including Gemini, Copilot)").action(async (options2) => {
|
|
47199
|
+
try {
|
|
47200
|
+
const type = options2.type === "mcp" ? "mcp" : "cli";
|
|
47201
|
+
const variant = options2.gemini ? "gemini" : "general";
|
|
47202
|
+
const guidelines = getGuidelines(type, variant);
|
|
47203
|
+
const filesToUpdate = options2.all ? INSTRUCTION_FILES : INSTRUCTION_FILES.filter((f) => f.selected);
|
|
47204
|
+
const label = `${type.toUpperCase()}${variant === "gemini" ? " (Gemini)" : ""}`;
|
|
47205
|
+
console.log(source_default.bold(`
|
|
47206
|
+
Syncing agent instructions (${label})...
|
|
47207
|
+
`));
|
|
47208
|
+
await updateFiles(filesToUpdate, guidelines);
|
|
47209
|
+
} catch (error46) {
|
|
47210
|
+
console.error(source_default.red("Error:"), error46 instanceof Error ? error46.message : String(error46));
|
|
47211
|
+
process.exit(1);
|
|
47212
|
+
}
|
|
47213
|
+
});
|
|
47214
|
+
agentsCommand.addCommand(syncCommand);
|
|
47125
47215
|
|
|
47126
47216
|
// src/commands/init.ts
|
|
47127
47217
|
async function checkAndInitGit(projectRoot) {
|
|
@@ -47251,8 +47341,8 @@ async function runWizard() {
|
|
|
47251
47341
|
},
|
|
47252
47342
|
{
|
|
47253
47343
|
type: "select",
|
|
47254
|
-
name: "
|
|
47255
|
-
message: "AI Guidelines
|
|
47344
|
+
name: "guidelinesType",
|
|
47345
|
+
message: "AI Guidelines type",
|
|
47256
47346
|
choices: [
|
|
47257
47347
|
{ title: "CLI", value: "cli", description: "Use CLI commands (knowns task edit, knowns doc view)" },
|
|
47258
47348
|
{ title: "MCP", value: "mcp", description: "Use MCP tools (mcp__knowns__update_task, etc.)" }
|
|
@@ -47289,7 +47379,7 @@ async function runWizard() {
|
|
|
47289
47379
|
defaultPriority: response.defaultPriority,
|
|
47290
47380
|
defaultLabels: response.defaultLabels?.filter((l) => l.trim()) || [],
|
|
47291
47381
|
timeFormat: response.timeFormat,
|
|
47292
|
-
|
|
47382
|
+
guidelinesType: response.guidelinesType || "cli",
|
|
47293
47383
|
agentFiles: response.agentFiles || []
|
|
47294
47384
|
};
|
|
47295
47385
|
}
|
|
@@ -47322,7 +47412,7 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
|
|
|
47322
47412
|
defaultPriority: "medium",
|
|
47323
47413
|
defaultLabels: [],
|
|
47324
47414
|
timeFormat: "24h",
|
|
47325
|
-
|
|
47415
|
+
guidelinesType: "cli",
|
|
47326
47416
|
agentFiles: INSTRUCTION_FILES.filter((f) => f.selected)
|
|
47327
47417
|
};
|
|
47328
47418
|
}
|
|
@@ -47347,8 +47437,8 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
|
|
|
47347
47437
|
console.log(source_default.gray(` Time Format: ${config2.timeFormat}`));
|
|
47348
47438
|
console.log();
|
|
47349
47439
|
if (config2.agentFiles.length > 0) {
|
|
47350
|
-
const guidelines = config2.
|
|
47351
|
-
console.log(source_default.bold(`Updating AI instruction files (${config2.
|
|
47440
|
+
const guidelines = getGuidelines(config2.guidelinesType);
|
|
47441
|
+
console.log(source_default.bold(`Updating AI instruction files (${config2.guidelinesType.toUpperCase()} version)...`));
|
|
47352
47442
|
console.log();
|
|
47353
47443
|
let syncedCount = 0;
|
|
47354
47444
|
for (const file3 of config2.agentFiles) {
|
|
@@ -47383,7 +47473,7 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
|
|
|
47383
47473
|
});
|
|
47384
47474
|
|
|
47385
47475
|
// src/commands/task.ts
|
|
47386
|
-
import { mkdir as mkdir4, readdir as readdir2, unlink as unlink3 } from "node:fs/promises";
|
|
47476
|
+
import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, unlink as unlink3 } from "node:fs/promises";
|
|
47387
47477
|
import { join as join8 } from "node:path";
|
|
47388
47478
|
|
|
47389
47479
|
// src/utils/doc-links.ts
|
|
@@ -47582,6 +47672,402 @@ async function notifyTimeUpdate(active) {
|
|
|
47582
47672
|
}
|
|
47583
47673
|
}
|
|
47584
47674
|
|
|
47675
|
+
// src/utils/validate.ts
|
|
47676
|
+
var import_gray_matter2 = __toESM(require_gray_matter(), 1);
|
|
47677
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
47678
|
+
import { copyFile, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
47679
|
+
var DOC_REQUIRED_FIELDS = ["title", "createdAt", "updatedAt"];
|
|
47680
|
+
function validateDoc(content) {
|
|
47681
|
+
const errors = [];
|
|
47682
|
+
const warnings = [];
|
|
47683
|
+
let data;
|
|
47684
|
+
let bodyContent;
|
|
47685
|
+
try {
|
|
47686
|
+
const parsed = (0, import_gray_matter2.default)(content);
|
|
47687
|
+
data = parsed.data;
|
|
47688
|
+
bodyContent = parsed.content;
|
|
47689
|
+
} catch (error46) {
|
|
47690
|
+
errors.push({
|
|
47691
|
+
field: "frontmatter",
|
|
47692
|
+
message: `Invalid YAML frontmatter: ${error46 instanceof Error ? error46.message : String(error46)}`,
|
|
47693
|
+
fixable: false
|
|
47694
|
+
});
|
|
47695
|
+
return { valid: false, errors, warnings };
|
|
47696
|
+
}
|
|
47697
|
+
for (const field of DOC_REQUIRED_FIELDS) {
|
|
47698
|
+
if (!data[field]) {
|
|
47699
|
+
errors.push({
|
|
47700
|
+
field,
|
|
47701
|
+
message: `Missing required field: ${field}`,
|
|
47702
|
+
fixable: true
|
|
47703
|
+
});
|
|
47704
|
+
}
|
|
47705
|
+
}
|
|
47706
|
+
if (data.title && typeof data.title !== "string") {
|
|
47707
|
+
errors.push({
|
|
47708
|
+
field: "title",
|
|
47709
|
+
message: "Title must be a string",
|
|
47710
|
+
fixable: true
|
|
47711
|
+
});
|
|
47712
|
+
}
|
|
47713
|
+
if (data.createdAt && !isValidISODate(data.createdAt)) {
|
|
47714
|
+
errors.push({
|
|
47715
|
+
field: "createdAt",
|
|
47716
|
+
message: "createdAt must be a valid ISO date",
|
|
47717
|
+
fixable: true
|
|
47718
|
+
});
|
|
47719
|
+
}
|
|
47720
|
+
if (data.updatedAt && !isValidISODate(data.updatedAt)) {
|
|
47721
|
+
errors.push({
|
|
47722
|
+
field: "updatedAt",
|
|
47723
|
+
message: "updatedAt must be a valid ISO date",
|
|
47724
|
+
fixable: true
|
|
47725
|
+
});
|
|
47726
|
+
}
|
|
47727
|
+
if (data.tags !== void 0) {
|
|
47728
|
+
if (typeof data.tags === "string") {
|
|
47729
|
+
warnings.push({
|
|
47730
|
+
field: "tags",
|
|
47731
|
+
message: "Tags should be an array, not a string"
|
|
47732
|
+
});
|
|
47733
|
+
} else if (!Array.isArray(data.tags)) {
|
|
47734
|
+
errors.push({
|
|
47735
|
+
field: "tags",
|
|
47736
|
+
message: "Tags must be an array of strings",
|
|
47737
|
+
fixable: true
|
|
47738
|
+
});
|
|
47739
|
+
}
|
|
47740
|
+
}
|
|
47741
|
+
if (!bodyContent.trim()) {
|
|
47742
|
+
warnings.push({
|
|
47743
|
+
field: "content",
|
|
47744
|
+
message: "Document has no content"
|
|
47745
|
+
});
|
|
47746
|
+
}
|
|
47747
|
+
return {
|
|
47748
|
+
valid: errors.length === 0,
|
|
47749
|
+
errors,
|
|
47750
|
+
warnings
|
|
47751
|
+
};
|
|
47752
|
+
}
|
|
47753
|
+
async function repairDoc(filePath) {
|
|
47754
|
+
const fixes = [];
|
|
47755
|
+
const repairErrors = [];
|
|
47756
|
+
if (!existsSync6(filePath)) {
|
|
47757
|
+
return {
|
|
47758
|
+
success: false,
|
|
47759
|
+
errors: ["File not found"],
|
|
47760
|
+
fixes: []
|
|
47761
|
+
};
|
|
47762
|
+
}
|
|
47763
|
+
const backupPath = `${filePath}.bak`;
|
|
47764
|
+
try {
|
|
47765
|
+
await copyFile(filePath, backupPath);
|
|
47766
|
+
} catch (error46) {
|
|
47767
|
+
return {
|
|
47768
|
+
success: false,
|
|
47769
|
+
errors: [`Failed to create backup: ${error46 instanceof Error ? error46.message : String(error46)}`],
|
|
47770
|
+
fixes: []
|
|
47771
|
+
};
|
|
47772
|
+
}
|
|
47773
|
+
let content;
|
|
47774
|
+
try {
|
|
47775
|
+
content = await readFile3(filePath, "utf-8");
|
|
47776
|
+
} catch (error46) {
|
|
47777
|
+
return {
|
|
47778
|
+
success: false,
|
|
47779
|
+
backupPath,
|
|
47780
|
+
errors: [`Failed to read file: ${error46 instanceof Error ? error46.message : String(error46)}`],
|
|
47781
|
+
fixes: []
|
|
47782
|
+
};
|
|
47783
|
+
}
|
|
47784
|
+
let data;
|
|
47785
|
+
let bodyContent;
|
|
47786
|
+
try {
|
|
47787
|
+
const parsed = (0, import_gray_matter2.default)(content);
|
|
47788
|
+
data = parsed.data;
|
|
47789
|
+
bodyContent = parsed.content;
|
|
47790
|
+
} catch {
|
|
47791
|
+
const lines = content.split("\n");
|
|
47792
|
+
const titleMatch = lines.find((l) => l.startsWith("# "));
|
|
47793
|
+
const title = titleMatch ? titleMatch.replace("# ", "").trim() : "Untitled";
|
|
47794
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
47795
|
+
data = {
|
|
47796
|
+
title,
|
|
47797
|
+
createdAt: now2,
|
|
47798
|
+
updatedAt: now2
|
|
47799
|
+
};
|
|
47800
|
+
const frontmatterEnd = content.indexOf("---", 3);
|
|
47801
|
+
bodyContent = frontmatterEnd > 0 ? content.substring(frontmatterEnd + 3).trim() : content.replace(/^---[\s\S]*?---/, "").trim();
|
|
47802
|
+
fixes.push("Rebuilt corrupted frontmatter");
|
|
47803
|
+
}
|
|
47804
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
47805
|
+
if (!data.title) {
|
|
47806
|
+
const titleMatch = bodyContent.match(/^#\s+(.+)$/m);
|
|
47807
|
+
data.title = titleMatch ? titleMatch[1] : "Untitled Document";
|
|
47808
|
+
fixes.push(`Added missing title: "${data.title}"`);
|
|
47809
|
+
}
|
|
47810
|
+
if (!data.createdAt || !isValidISODate(data.createdAt)) {
|
|
47811
|
+
data.createdAt = now;
|
|
47812
|
+
fixes.push("Fixed createdAt date");
|
|
47813
|
+
}
|
|
47814
|
+
if (!data.updatedAt || !isValidISODate(data.updatedAt)) {
|
|
47815
|
+
data.updatedAt = now;
|
|
47816
|
+
fixes.push("Fixed updatedAt date");
|
|
47817
|
+
}
|
|
47818
|
+
if (typeof data.tags === "string") {
|
|
47819
|
+
data.tags = data.tags.split(",").map((t) => t.trim());
|
|
47820
|
+
fixes.push("Converted tags from string to array");
|
|
47821
|
+
}
|
|
47822
|
+
if (data.tags && !Array.isArray(data.tags)) {
|
|
47823
|
+
data.tags = [];
|
|
47824
|
+
fixes.push("Reset invalid tags to empty array");
|
|
47825
|
+
}
|
|
47826
|
+
try {
|
|
47827
|
+
const repairedContent = import_gray_matter2.default.stringify(bodyContent, data);
|
|
47828
|
+
await writeFile3(filePath, repairedContent, "utf-8");
|
|
47829
|
+
} catch (error46) {
|
|
47830
|
+
repairErrors.push(`Failed to write repaired file: ${error46 instanceof Error ? error46.message : String(error46)}`);
|
|
47831
|
+
return {
|
|
47832
|
+
success: false,
|
|
47833
|
+
backupPath,
|
|
47834
|
+
errors: repairErrors,
|
|
47835
|
+
fixes
|
|
47836
|
+
};
|
|
47837
|
+
}
|
|
47838
|
+
return {
|
|
47839
|
+
success: true,
|
|
47840
|
+
backupPath,
|
|
47841
|
+
fixes,
|
|
47842
|
+
errors: repairErrors
|
|
47843
|
+
};
|
|
47844
|
+
}
|
|
47845
|
+
var TASK_REQUIRED_FIELDS = ["id", "title", "status", "priority", "createdAt", "updatedAt"];
|
|
47846
|
+
var VALID_STATUSES = ["todo", "in-progress", "in-review", "done", "blocked", "on-hold"];
|
|
47847
|
+
var VALID_PRIORITIES = ["low", "medium", "high"];
|
|
47848
|
+
function validateTask(content) {
|
|
47849
|
+
const errors = [];
|
|
47850
|
+
const warnings = [];
|
|
47851
|
+
let data;
|
|
47852
|
+
let bodyContent;
|
|
47853
|
+
try {
|
|
47854
|
+
const parsed = (0, import_gray_matter2.default)(content);
|
|
47855
|
+
data = parsed.data;
|
|
47856
|
+
bodyContent = parsed.content;
|
|
47857
|
+
} catch (error46) {
|
|
47858
|
+
errors.push({
|
|
47859
|
+
field: "frontmatter",
|
|
47860
|
+
message: `Invalid YAML frontmatter: ${error46 instanceof Error ? error46.message : String(error46)}`,
|
|
47861
|
+
fixable: false
|
|
47862
|
+
});
|
|
47863
|
+
return { valid: false, errors, warnings };
|
|
47864
|
+
}
|
|
47865
|
+
for (const field of TASK_REQUIRED_FIELDS) {
|
|
47866
|
+
if (data[field] === void 0) {
|
|
47867
|
+
errors.push({
|
|
47868
|
+
field,
|
|
47869
|
+
message: `Missing required field: ${field}`,
|
|
47870
|
+
fixable: field !== "id"
|
|
47871
|
+
// ID can't be auto-fixed
|
|
47872
|
+
});
|
|
47873
|
+
}
|
|
47874
|
+
}
|
|
47875
|
+
if (data.id && !/^\d+(\.\d+)*$/.test(data.id)) {
|
|
47876
|
+
errors.push({
|
|
47877
|
+
field: "id",
|
|
47878
|
+
message: "Invalid ID format (expected: number or hierarchical like 7.1.2)",
|
|
47879
|
+
fixable: false
|
|
47880
|
+
});
|
|
47881
|
+
}
|
|
47882
|
+
if (data.status && !VALID_STATUSES.includes(data.status)) {
|
|
47883
|
+
errors.push({
|
|
47884
|
+
field: "status",
|
|
47885
|
+
message: `Invalid status: ${data.status}. Expected one of: ${VALID_STATUSES.join(", ")}`,
|
|
47886
|
+
fixable: true
|
|
47887
|
+
});
|
|
47888
|
+
}
|
|
47889
|
+
if (data.priority && !VALID_PRIORITIES.includes(data.priority)) {
|
|
47890
|
+
errors.push({
|
|
47891
|
+
field: "priority",
|
|
47892
|
+
message: `Invalid priority: ${data.priority}. Expected one of: ${VALID_PRIORITIES.join(", ")}`,
|
|
47893
|
+
fixable: true
|
|
47894
|
+
});
|
|
47895
|
+
}
|
|
47896
|
+
if (data.createdAt && !isValidISODate(data.createdAt)) {
|
|
47897
|
+
errors.push({
|
|
47898
|
+
field: "createdAt",
|
|
47899
|
+
message: "createdAt must be a valid ISO date",
|
|
47900
|
+
fixable: true
|
|
47901
|
+
});
|
|
47902
|
+
}
|
|
47903
|
+
if (data.updatedAt && !isValidISODate(data.updatedAt)) {
|
|
47904
|
+
errors.push({
|
|
47905
|
+
field: "updatedAt",
|
|
47906
|
+
message: "updatedAt must be a valid ISO date",
|
|
47907
|
+
fixable: true
|
|
47908
|
+
});
|
|
47909
|
+
}
|
|
47910
|
+
if (data.labels !== void 0) {
|
|
47911
|
+
if (typeof data.labels === "string") {
|
|
47912
|
+
warnings.push({
|
|
47913
|
+
field: "labels",
|
|
47914
|
+
message: "Labels should be an array, not a string"
|
|
47915
|
+
});
|
|
47916
|
+
} else if (!Array.isArray(data.labels)) {
|
|
47917
|
+
errors.push({
|
|
47918
|
+
field: "labels",
|
|
47919
|
+
message: "Labels must be an array of strings",
|
|
47920
|
+
fixable: true
|
|
47921
|
+
});
|
|
47922
|
+
}
|
|
47923
|
+
}
|
|
47924
|
+
if (data.timeSpent !== void 0 && (typeof data.timeSpent !== "number" || data.timeSpent < 0)) {
|
|
47925
|
+
errors.push({
|
|
47926
|
+
field: "timeSpent",
|
|
47927
|
+
message: "timeSpent must be a non-negative number",
|
|
47928
|
+
fixable: true
|
|
47929
|
+
});
|
|
47930
|
+
}
|
|
47931
|
+
if (!bodyContent.includes("# ")) {
|
|
47932
|
+
warnings.push({
|
|
47933
|
+
field: "content",
|
|
47934
|
+
message: "Missing title heading in body"
|
|
47935
|
+
});
|
|
47936
|
+
}
|
|
47937
|
+
return {
|
|
47938
|
+
valid: errors.length === 0,
|
|
47939
|
+
errors,
|
|
47940
|
+
warnings
|
|
47941
|
+
};
|
|
47942
|
+
}
|
|
47943
|
+
async function repairTask(filePath, extractedId) {
|
|
47944
|
+
const fixes = [];
|
|
47945
|
+
const repairErrors = [];
|
|
47946
|
+
if (!existsSync6(filePath)) {
|
|
47947
|
+
return {
|
|
47948
|
+
success: false,
|
|
47949
|
+
errors: ["File not found"],
|
|
47950
|
+
fixes: []
|
|
47951
|
+
};
|
|
47952
|
+
}
|
|
47953
|
+
const backupPath = `${filePath}.bak`;
|
|
47954
|
+
try {
|
|
47955
|
+
await copyFile(filePath, backupPath);
|
|
47956
|
+
} catch (error46) {
|
|
47957
|
+
return {
|
|
47958
|
+
success: false,
|
|
47959
|
+
errors: [`Failed to create backup: ${error46 instanceof Error ? error46.message : String(error46)}`],
|
|
47960
|
+
fixes: []
|
|
47961
|
+
};
|
|
47962
|
+
}
|
|
47963
|
+
let content;
|
|
47964
|
+
try {
|
|
47965
|
+
content = await readFile3(filePath, "utf-8");
|
|
47966
|
+
} catch (error46) {
|
|
47967
|
+
return {
|
|
47968
|
+
success: false,
|
|
47969
|
+
backupPath,
|
|
47970
|
+
errors: [`Failed to read file: ${error46 instanceof Error ? error46.message : String(error46)}`],
|
|
47971
|
+
fixes: []
|
|
47972
|
+
};
|
|
47973
|
+
}
|
|
47974
|
+
let data;
|
|
47975
|
+
let bodyContent;
|
|
47976
|
+
try {
|
|
47977
|
+
const parsed = (0, import_gray_matter2.default)(content);
|
|
47978
|
+
data = parsed.data;
|
|
47979
|
+
bodyContent = parsed.content;
|
|
47980
|
+
} catch {
|
|
47981
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
47982
|
+
const title = titleMatch ? titleMatch[1] : "Untitled Task";
|
|
47983
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
47984
|
+
data = {
|
|
47985
|
+
id: extractedId,
|
|
47986
|
+
title,
|
|
47987
|
+
status: "todo",
|
|
47988
|
+
priority: "medium",
|
|
47989
|
+
labels: [],
|
|
47990
|
+
createdAt: now2,
|
|
47991
|
+
updatedAt: now2,
|
|
47992
|
+
timeSpent: 0
|
|
47993
|
+
};
|
|
47994
|
+
const frontmatterEnd = content.indexOf("---", 3);
|
|
47995
|
+
bodyContent = frontmatterEnd > 0 ? content.substring(frontmatterEnd + 3).trim() : content.replace(/^---[\s\S]*?---/, "").trim();
|
|
47996
|
+
fixes.push("Rebuilt corrupted frontmatter");
|
|
47997
|
+
}
|
|
47998
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
47999
|
+
if (!data.id && extractedId) {
|
|
48000
|
+
data.id = extractedId;
|
|
48001
|
+
fixes.push(`Set ID from filename: ${extractedId}`);
|
|
48002
|
+
}
|
|
48003
|
+
if (!data.title) {
|
|
48004
|
+
const titleMatch = bodyContent.match(/^#\s+(.+)$/m);
|
|
48005
|
+
data.title = titleMatch ? titleMatch[1] : "Untitled Task";
|
|
48006
|
+
fixes.push(`Added missing title: "${data.title}"`);
|
|
48007
|
+
}
|
|
48008
|
+
if (!data.status || !VALID_STATUSES.includes(data.status)) {
|
|
48009
|
+
data.status = "todo";
|
|
48010
|
+
fixes.push("Set status to 'todo'");
|
|
48011
|
+
}
|
|
48012
|
+
if (!data.priority || !VALID_PRIORITIES.includes(data.priority)) {
|
|
48013
|
+
data.priority = "medium";
|
|
48014
|
+
fixes.push("Set priority to 'medium'");
|
|
48015
|
+
}
|
|
48016
|
+
if (!data.createdAt || !isValidISODate(data.createdAt)) {
|
|
48017
|
+
data.createdAt = now;
|
|
48018
|
+
fixes.push("Fixed createdAt date");
|
|
48019
|
+
}
|
|
48020
|
+
if (!data.updatedAt || !isValidISODate(data.updatedAt)) {
|
|
48021
|
+
data.updatedAt = now;
|
|
48022
|
+
fixes.push("Fixed updatedAt date");
|
|
48023
|
+
}
|
|
48024
|
+
if (typeof data.labels === "string") {
|
|
48025
|
+
data.labels = data.labels.split(",").map((l) => l.trim());
|
|
48026
|
+
fixes.push("Converted labels from string to array");
|
|
48027
|
+
}
|
|
48028
|
+
if (!Array.isArray(data.labels)) {
|
|
48029
|
+
data.labels = [];
|
|
48030
|
+
fixes.push("Reset invalid labels to empty array");
|
|
48031
|
+
}
|
|
48032
|
+
if (typeof data.timeSpent !== "number" || data.timeSpent < 0) {
|
|
48033
|
+
data.timeSpent = 0;
|
|
48034
|
+
fixes.push("Reset timeSpent to 0");
|
|
48035
|
+
}
|
|
48036
|
+
if (!bodyContent.includes("# ") && data.title) {
|
|
48037
|
+
bodyContent = `# ${data.title}
|
|
48038
|
+
|
|
48039
|
+
${bodyContent}`;
|
|
48040
|
+
fixes.push("Added title heading to body");
|
|
48041
|
+
}
|
|
48042
|
+
try {
|
|
48043
|
+
const repairedContent = import_gray_matter2.default.stringify(bodyContent, data);
|
|
48044
|
+
await writeFile3(filePath, repairedContent, "utf-8");
|
|
48045
|
+
} catch (error46) {
|
|
48046
|
+
repairErrors.push(`Failed to write repaired file: ${error46 instanceof Error ? error46.message : String(error46)}`);
|
|
48047
|
+
return {
|
|
48048
|
+
success: false,
|
|
48049
|
+
backupPath,
|
|
48050
|
+
errors: repairErrors,
|
|
48051
|
+
fixes
|
|
48052
|
+
};
|
|
48053
|
+
}
|
|
48054
|
+
return {
|
|
48055
|
+
success: true,
|
|
48056
|
+
backupPath,
|
|
48057
|
+
fixes,
|
|
48058
|
+
errors: repairErrors
|
|
48059
|
+
};
|
|
48060
|
+
}
|
|
48061
|
+
function isValidISODate(dateString) {
|
|
48062
|
+
if (typeof dateString !== "string") return false;
|
|
48063
|
+
const date5 = new Date(dateString);
|
|
48064
|
+
return !Number.isNaN(date5.getTime());
|
|
48065
|
+
}
|
|
48066
|
+
function extractTaskIdFromFilename(filename) {
|
|
48067
|
+
const match = filename.match(/^task-(\d+(?:\.\d+)*)\s*-/);
|
|
48068
|
+
return match ? match[1] : void 0;
|
|
48069
|
+
}
|
|
48070
|
+
|
|
47585
48071
|
// src/commands/task.ts
|
|
47586
48072
|
function getFileStore() {
|
|
47587
48073
|
const projectRoot = findProjectRoot();
|
|
@@ -48649,7 +49135,151 @@ var restoreCommand = new Command("restore").description("Restore task to a previ
|
|
|
48649
49135
|
process.exit(1);
|
|
48650
49136
|
}
|
|
48651
49137
|
});
|
|
48652
|
-
var
|
|
49138
|
+
var validateCommand = new Command("validate").description("Validate a task file format").argument("<id>", "Task ID to validate").option("--plain", "Plain text output for AI").action(async (id, options2) => {
|
|
49139
|
+
try {
|
|
49140
|
+
const projectRoot = findProjectRoot();
|
|
49141
|
+
if (!projectRoot) {
|
|
49142
|
+
console.error(source_default.red("\u2717 Not a knowns project"));
|
|
49143
|
+
process.exit(1);
|
|
49144
|
+
}
|
|
49145
|
+
const tasksPath = join8(projectRoot, ".knowns", "tasks");
|
|
49146
|
+
const files = await readdir2(tasksPath);
|
|
49147
|
+
const taskFile = files.find((f) => f.startsWith(`task-${id} -`));
|
|
49148
|
+
if (!taskFile) {
|
|
49149
|
+
console.error(source_default.red(`\u2717 Task ${id} not found`));
|
|
49150
|
+
process.exit(1);
|
|
49151
|
+
}
|
|
49152
|
+
const filePath = join8(tasksPath, taskFile);
|
|
49153
|
+
const content = await readFile4(filePath, "utf-8");
|
|
49154
|
+
const result = validateTask(content);
|
|
49155
|
+
if (options2.plain) {
|
|
49156
|
+
console.log(`Validating: task-${id}`);
|
|
49157
|
+
console.log(`Valid: ${result.valid}`);
|
|
49158
|
+
if (result.errors.length > 0) {
|
|
49159
|
+
console.log("\nErrors:");
|
|
49160
|
+
for (const error46 of result.errors) {
|
|
49161
|
+
console.log(` \u2717 ${error46.field}: ${error46.message}${error46.fixable ? " (fixable)" : ""}`);
|
|
49162
|
+
}
|
|
49163
|
+
}
|
|
49164
|
+
if (result.warnings.length > 0) {
|
|
49165
|
+
console.log("\nWarnings:");
|
|
49166
|
+
for (const warning of result.warnings) {
|
|
49167
|
+
console.log(` \u26A0 ${warning.field}: ${warning.message}`);
|
|
49168
|
+
}
|
|
49169
|
+
}
|
|
49170
|
+
if (result.valid && result.warnings.length === 0) {
|
|
49171
|
+
console.log("No issues found.");
|
|
49172
|
+
}
|
|
49173
|
+
} else {
|
|
49174
|
+
console.log(source_default.bold(`
|
|
49175
|
+
\u{1F4CB} Validation: Task ${id}
|
|
49176
|
+
`));
|
|
49177
|
+
if (result.valid) {
|
|
49178
|
+
console.log(source_default.green("\u2713 Task is valid"));
|
|
49179
|
+
} else {
|
|
49180
|
+
console.log(source_default.red("\u2717 Task has errors"));
|
|
49181
|
+
}
|
|
49182
|
+
if (result.errors.length > 0) {
|
|
49183
|
+
console.log(source_default.red("\nErrors:"));
|
|
49184
|
+
for (const error46 of result.errors) {
|
|
49185
|
+
const fixable = error46.fixable ? source_default.gray(" (fixable)") : "";
|
|
49186
|
+
console.log(source_default.red(` \u2717 ${error46.field}: ${error46.message}${fixable}`));
|
|
49187
|
+
}
|
|
49188
|
+
}
|
|
49189
|
+
if (result.warnings.length > 0) {
|
|
49190
|
+
console.log(source_default.yellow("\nWarnings:"));
|
|
49191
|
+
for (const warning of result.warnings) {
|
|
49192
|
+
console.log(source_default.yellow(` \u26A0 ${warning.field}: ${warning.message}`));
|
|
49193
|
+
}
|
|
49194
|
+
}
|
|
49195
|
+
if (result.valid && result.warnings.length === 0) {
|
|
49196
|
+
console.log(source_default.gray("\nNo issues found."));
|
|
49197
|
+
}
|
|
49198
|
+
console.log();
|
|
49199
|
+
}
|
|
49200
|
+
process.exit(result.valid ? 0 : 1);
|
|
49201
|
+
} catch (error46) {
|
|
49202
|
+
console.error(source_default.red("\u2717 Failed to validate task"));
|
|
49203
|
+
if (error46 instanceof Error) {
|
|
49204
|
+
console.error(source_default.red(` ${error46.message}`));
|
|
49205
|
+
}
|
|
49206
|
+
process.exit(1);
|
|
49207
|
+
}
|
|
49208
|
+
});
|
|
49209
|
+
var repairCommand = new Command("repair").description("Repair a corrupted task file").argument("<id>", "Task ID to repair").option("--plain", "Plain text output for AI").action(async (id, options2) => {
|
|
49210
|
+
try {
|
|
49211
|
+
const projectRoot = findProjectRoot();
|
|
49212
|
+
if (!projectRoot) {
|
|
49213
|
+
console.error(source_default.red("\u2717 Not a knowns project"));
|
|
49214
|
+
process.exit(1);
|
|
49215
|
+
}
|
|
49216
|
+
const tasksPath = join8(projectRoot, ".knowns", "tasks");
|
|
49217
|
+
const files = await readdir2(tasksPath);
|
|
49218
|
+
const taskFile = files.find((f) => f.startsWith(`task-${id} -`));
|
|
49219
|
+
if (!taskFile) {
|
|
49220
|
+
console.error(source_default.red(`\u2717 Task ${id} not found`));
|
|
49221
|
+
process.exit(1);
|
|
49222
|
+
}
|
|
49223
|
+
const filePath = join8(tasksPath, taskFile);
|
|
49224
|
+
const extractedId = extractTaskIdFromFilename(taskFile);
|
|
49225
|
+
const result = await repairTask(filePath, extractedId);
|
|
49226
|
+
if (options2.plain) {
|
|
49227
|
+
console.log(`Repairing: task-${id}`);
|
|
49228
|
+
console.log(`Success: ${result.success}`);
|
|
49229
|
+
if (result.backupPath) {
|
|
49230
|
+
console.log(`Backup: ${result.backupPath}`);
|
|
49231
|
+
}
|
|
49232
|
+
if (result.fixes.length > 0) {
|
|
49233
|
+
console.log("\nFixes applied:");
|
|
49234
|
+
for (const fix of result.fixes) {
|
|
49235
|
+
console.log(` \u2713 ${fix}`);
|
|
49236
|
+
}
|
|
49237
|
+
}
|
|
49238
|
+
if (result.errors.length > 0) {
|
|
49239
|
+
console.log("\nErrors:");
|
|
49240
|
+
for (const err of result.errors) {
|
|
49241
|
+
console.log(` \u2717 ${err}`);
|
|
49242
|
+
}
|
|
49243
|
+
}
|
|
49244
|
+
} else {
|
|
49245
|
+
console.log(source_default.bold(`
|
|
49246
|
+
\u{1F527} Repair: Task ${id}
|
|
49247
|
+
`));
|
|
49248
|
+
if (result.success) {
|
|
49249
|
+
console.log(source_default.green("\u2713 Task repaired successfully"));
|
|
49250
|
+
} else {
|
|
49251
|
+
console.log(source_default.red("\u2717 Repair failed"));
|
|
49252
|
+
}
|
|
49253
|
+
if (result.backupPath) {
|
|
49254
|
+
console.log(source_default.gray(` Backup created: ${result.backupPath}`));
|
|
49255
|
+
}
|
|
49256
|
+
if (result.fixes.length > 0) {
|
|
49257
|
+
console.log(source_default.green("\nFixes applied:"));
|
|
49258
|
+
for (const fix of result.fixes) {
|
|
49259
|
+
console.log(source_default.green(` \u2713 ${fix}`));
|
|
49260
|
+
}
|
|
49261
|
+
}
|
|
49262
|
+
if (result.errors.length > 0) {
|
|
49263
|
+
console.log(source_default.red("\nErrors:"));
|
|
49264
|
+
for (const err of result.errors) {
|
|
49265
|
+
console.log(source_default.red(` \u2717 ${err}`));
|
|
49266
|
+
}
|
|
49267
|
+
}
|
|
49268
|
+
console.log();
|
|
49269
|
+
}
|
|
49270
|
+
if (result.success) {
|
|
49271
|
+
await notifyTaskUpdate(id);
|
|
49272
|
+
}
|
|
49273
|
+
process.exit(result.success ? 0 : 1);
|
|
49274
|
+
} catch (error46) {
|
|
49275
|
+
console.error(source_default.red("\u2717 Failed to repair task"));
|
|
49276
|
+
if (error46 instanceof Error) {
|
|
49277
|
+
console.error(source_default.red(` ${error46.message}`));
|
|
49278
|
+
}
|
|
49279
|
+
process.exit(1);
|
|
49280
|
+
}
|
|
49281
|
+
});
|
|
49282
|
+
var taskCommand = new Command("task").description("Manage tasks").argument("[id]", "Task ID (shorthand for 'task view <id>')").option("--plain", "Plain text output for AI").enablePositionalOptions().passThroughOptions().action(async (id, options2) => {
|
|
48653
49283
|
if (!id) {
|
|
48654
49284
|
taskCommand.help();
|
|
48655
49285
|
return;
|
|
@@ -48679,6 +49309,8 @@ taskCommand.addCommand(unarchiveCommand);
|
|
|
48679
49309
|
taskCommand.addCommand(historyCommand);
|
|
48680
49310
|
taskCommand.addCommand(diffCommand);
|
|
48681
49311
|
taskCommand.addCommand(restoreCommand);
|
|
49312
|
+
taskCommand.addCommand(validateCommand);
|
|
49313
|
+
taskCommand.addCommand(repairCommand);
|
|
48682
49314
|
|
|
48683
49315
|
// src/commands/board.ts
|
|
48684
49316
|
function getFileStore2() {
|
|
@@ -48840,10 +49472,10 @@ var boardCommand = new Command("board").description("Display Kanban board").opti
|
|
|
48840
49472
|
);
|
|
48841
49473
|
|
|
48842
49474
|
// src/commands/search.ts
|
|
48843
|
-
import { existsSync as
|
|
48844
|
-
import { readFile as
|
|
49475
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
49476
|
+
import { readFile as readFile5, readdir as readdir3 } from "node:fs/promises";
|
|
48845
49477
|
import { join as join9 } from "node:path";
|
|
48846
|
-
var
|
|
49478
|
+
var import_gray_matter3 = __toESM(require_gray_matter(), 1);
|
|
48847
49479
|
function getFileStore3() {
|
|
48848
49480
|
const projectRoot = findProjectRoot();
|
|
48849
49481
|
if (!projectRoot) {
|
|
@@ -48895,7 +49527,7 @@ function calculateDocScore(doc, query) {
|
|
|
48895
49527
|
}
|
|
48896
49528
|
async function searchDocs(query, projectRoot) {
|
|
48897
49529
|
const docsDir = join9(projectRoot, ".knowns", "docs");
|
|
48898
|
-
if (!
|
|
49530
|
+
if (!existsSync7(docsDir)) {
|
|
48899
49531
|
return [];
|
|
48900
49532
|
}
|
|
48901
49533
|
try {
|
|
@@ -48903,8 +49535,8 @@ async function searchDocs(query, projectRoot) {
|
|
|
48903
49535
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
48904
49536
|
const results = [];
|
|
48905
49537
|
for (const file3 of mdFiles) {
|
|
48906
|
-
const content = await
|
|
48907
|
-
const { data, content: docContent } = (0,
|
|
49538
|
+
const content = await readFile5(join9(docsDir, file3), "utf-8");
|
|
49539
|
+
const { data, content: docContent } = (0, import_gray_matter3.default)(content);
|
|
48908
49540
|
const metadata = data;
|
|
48909
49541
|
const doc = {
|
|
48910
49542
|
filename: file3,
|
|
@@ -49507,13 +50139,13 @@ timeCommand.addCommand(addCommand);
|
|
|
49507
50139
|
timeCommand.addCommand(reportCommand);
|
|
49508
50140
|
|
|
49509
50141
|
// src/commands/browser.ts
|
|
49510
|
-
import { existsSync as
|
|
49511
|
-
import { mkdir as mkdir6, readFile as
|
|
50142
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
50143
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile7 } from "node:fs/promises";
|
|
49512
50144
|
import { join as join16 } from "node:path";
|
|
49513
50145
|
|
|
49514
50146
|
// src/server/index.ts
|
|
49515
50147
|
import { spawn } from "node:child_process";
|
|
49516
|
-
import { existsSync as
|
|
50148
|
+
import { existsSync as existsSync12, realpathSync } from "node:fs";
|
|
49517
50149
|
import { createServer } from "node:http";
|
|
49518
50150
|
import { basename as basename2, dirname as dirname3, join as join15 } from "node:path";
|
|
49519
50151
|
import { fileURLToPath } from "node:url";
|
|
@@ -49577,8 +50209,8 @@ function createActivityRoutes(ctx) {
|
|
|
49577
50209
|
|
|
49578
50210
|
// src/server/routes/config.ts
|
|
49579
50211
|
var import_express2 = __toESM(require_express2(), 1);
|
|
49580
|
-
import { existsSync as
|
|
49581
|
-
import { readFile as
|
|
50212
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
50213
|
+
import { readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
49582
50214
|
import { join as join10 } from "node:path";
|
|
49583
50215
|
function createConfigRoutes(ctx) {
|
|
49584
50216
|
const router = (0, import_express2.Router)();
|
|
@@ -49586,7 +50218,7 @@ function createConfigRoutes(ctx) {
|
|
|
49586
50218
|
const configPath = join10(store.projectRoot, ".knowns", "config.json");
|
|
49587
50219
|
router.get("/", async (_req, res) => {
|
|
49588
50220
|
try {
|
|
49589
|
-
if (!
|
|
50221
|
+
if (!existsSync8(configPath)) {
|
|
49590
50222
|
res.json({
|
|
49591
50223
|
config: {
|
|
49592
50224
|
name: "Knowns",
|
|
@@ -49598,7 +50230,7 @@ function createConfigRoutes(ctx) {
|
|
|
49598
50230
|
});
|
|
49599
50231
|
return;
|
|
49600
50232
|
}
|
|
49601
|
-
const content = await
|
|
50233
|
+
const content = await readFile6(configPath, "utf-8");
|
|
49602
50234
|
const data = JSON.parse(content);
|
|
49603
50235
|
const settings = data.settings || {};
|
|
49604
50236
|
const config2 = {
|
|
@@ -49620,8 +50252,8 @@ function createConfigRoutes(ctx) {
|
|
|
49620
50252
|
try {
|
|
49621
50253
|
const config2 = req.body;
|
|
49622
50254
|
let existingData = {};
|
|
49623
|
-
if (
|
|
49624
|
-
const content = await
|
|
50255
|
+
if (existsSync8(configPath)) {
|
|
50256
|
+
const content = await readFile6(configPath, "utf-8");
|
|
49625
50257
|
existingData = JSON.parse(content);
|
|
49626
50258
|
}
|
|
49627
50259
|
const { name, ...settings } = config2;
|
|
@@ -49630,7 +50262,7 @@ function createConfigRoutes(ctx) {
|
|
|
49630
50262
|
name: name || existingData.name,
|
|
49631
50263
|
settings
|
|
49632
50264
|
};
|
|
49633
|
-
await
|
|
50265
|
+
await writeFile4(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
49634
50266
|
res.json({ success: true });
|
|
49635
50267
|
} catch (error46) {
|
|
49636
50268
|
console.error("Error saving config:", error46);
|
|
@@ -49642,9 +50274,9 @@ function createConfigRoutes(ctx) {
|
|
|
49642
50274
|
|
|
49643
50275
|
// src/server/routes/docs.ts
|
|
49644
50276
|
var import_express3 = __toESM(require_express2(), 1);
|
|
49645
|
-
var
|
|
49646
|
-
import { existsSync as
|
|
49647
|
-
import { mkdir as mkdir5, readFile as
|
|
50277
|
+
var import_gray_matter4 = __toESM(require_gray_matter(), 1);
|
|
50278
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
50279
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
|
|
49648
50280
|
import { join as join12 } from "node:path";
|
|
49649
50281
|
|
|
49650
50282
|
// src/server/utils/markdown.ts
|
|
@@ -49673,7 +50305,7 @@ function createDocRoutes(ctx) {
|
|
|
49673
50305
|
const docsDir = join12(store.projectRoot, ".knowns", "docs");
|
|
49674
50306
|
router.get("/", async (_req, res) => {
|
|
49675
50307
|
try {
|
|
49676
|
-
if (!
|
|
50308
|
+
if (!existsSync9(docsDir)) {
|
|
49677
50309
|
res.json({ docs: [] });
|
|
49678
50310
|
return;
|
|
49679
50311
|
}
|
|
@@ -49681,8 +50313,8 @@ function createDocRoutes(ctx) {
|
|
|
49681
50313
|
const docs = await Promise.all(
|
|
49682
50314
|
mdFiles.map(async (relativePath) => {
|
|
49683
50315
|
const fullPath = join12(docsDir, relativePath);
|
|
49684
|
-
const content = await
|
|
49685
|
-
const { data, content: docContent } = (0,
|
|
50316
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
50317
|
+
const { data, content: docContent } = (0, import_gray_matter4.default)(content);
|
|
49686
50318
|
const pathParts = relativePath.split("/");
|
|
49687
50319
|
const filename = pathParts[pathParts.length - 1];
|
|
49688
50320
|
const folder = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
|
|
@@ -49709,12 +50341,12 @@ function createDocRoutes(ctx) {
|
|
|
49709
50341
|
res.status(400).json({ error: "Invalid path" });
|
|
49710
50342
|
return;
|
|
49711
50343
|
}
|
|
49712
|
-
if (!
|
|
50344
|
+
if (!existsSync9(fullPath)) {
|
|
49713
50345
|
res.status(404).json({ error: "Document not found" });
|
|
49714
50346
|
return;
|
|
49715
50347
|
}
|
|
49716
|
-
const content = await
|
|
49717
|
-
const { data, content: docContent } = (0,
|
|
50348
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
50349
|
+
const { data, content: docContent } = (0, import_gray_matter4.default)(content);
|
|
49718
50350
|
const pathParts = docPath.split("/");
|
|
49719
50351
|
const filename = pathParts[pathParts.length - 1];
|
|
49720
50352
|
const folder = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
|
|
@@ -49736,7 +50368,7 @@ function createDocRoutes(ctx) {
|
|
|
49736
50368
|
});
|
|
49737
50369
|
router.post("/", async (req, res) => {
|
|
49738
50370
|
try {
|
|
49739
|
-
if (!
|
|
50371
|
+
if (!existsSync9(docsDir)) {
|
|
49740
50372
|
await mkdir5(docsDir, { recursive: true });
|
|
49741
50373
|
}
|
|
49742
50374
|
const data = req.body;
|
|
@@ -49756,10 +50388,10 @@ function createDocRoutes(ctx) {
|
|
|
49756
50388
|
targetDir = docsDir;
|
|
49757
50389
|
filepath = join12(docsDir, filename);
|
|
49758
50390
|
}
|
|
49759
|
-
if (!
|
|
50391
|
+
if (!existsSync9(targetDir)) {
|
|
49760
50392
|
await mkdir5(targetDir, { recursive: true });
|
|
49761
50393
|
}
|
|
49762
|
-
if (
|
|
50394
|
+
if (existsSync9(filepath)) {
|
|
49763
50395
|
res.status(409).json({ error: "Document with this title already exists in this folder" });
|
|
49764
50396
|
return;
|
|
49765
50397
|
}
|
|
@@ -49771,8 +50403,8 @@ function createDocRoutes(ctx) {
|
|
|
49771
50403
|
updatedAt: now,
|
|
49772
50404
|
tags: tags || []
|
|
49773
50405
|
};
|
|
49774
|
-
const markdown =
|
|
49775
|
-
await
|
|
50406
|
+
const markdown = import_gray_matter4.default.stringify(content || "", frontmatter);
|
|
50407
|
+
await writeFile5(filepath, markdown, "utf-8");
|
|
49776
50408
|
res.status(201).json({
|
|
49777
50409
|
success: true,
|
|
49778
50410
|
filename,
|
|
@@ -49793,14 +50425,14 @@ function createDocRoutes(ctx) {
|
|
|
49793
50425
|
res.status(400).json({ error: "Invalid path" });
|
|
49794
50426
|
return;
|
|
49795
50427
|
}
|
|
49796
|
-
if (!
|
|
50428
|
+
if (!existsSync9(fullPath)) {
|
|
49797
50429
|
res.status(404).json({ error: "Document not found" });
|
|
49798
50430
|
return;
|
|
49799
50431
|
}
|
|
49800
50432
|
const data = req.body;
|
|
49801
50433
|
const { content, title, description, tags } = data;
|
|
49802
|
-
const existingContent = await
|
|
49803
|
-
const { data: existingData } = (0,
|
|
50434
|
+
const existingContent = await readFile7(fullPath, "utf-8");
|
|
50435
|
+
const { data: existingData } = (0, import_gray_matter4.default)(existingContent);
|
|
49804
50436
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
49805
50437
|
const updatedFrontmatter = {
|
|
49806
50438
|
...existingData,
|
|
@@ -49809,8 +50441,8 @@ function createDocRoutes(ctx) {
|
|
|
49809
50441
|
tags: tags ?? existingData.tags,
|
|
49810
50442
|
updatedAt: now
|
|
49811
50443
|
};
|
|
49812
|
-
const newFileContent =
|
|
49813
|
-
await
|
|
50444
|
+
const newFileContent = import_gray_matter4.default.stringify(content ?? "", updatedFrontmatter);
|
|
50445
|
+
await writeFile5(fullPath, newFileContent, "utf-8");
|
|
49814
50446
|
const pathParts = docPath.split("/");
|
|
49815
50447
|
const filename = pathParts[pathParts.length - 1];
|
|
49816
50448
|
const folder = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
|
|
@@ -49878,9 +50510,9 @@ function createNotifyRoutes(ctx) {
|
|
|
49878
50510
|
|
|
49879
50511
|
// src/server/routes/search.ts
|
|
49880
50512
|
var import_express5 = __toESM(require_express2(), 1);
|
|
49881
|
-
var
|
|
49882
|
-
import { existsSync as
|
|
49883
|
-
import { readFile as
|
|
50513
|
+
var import_gray_matter5 = __toESM(require_gray_matter(), 1);
|
|
50514
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
50515
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
49884
50516
|
import { join as join13 } from "node:path";
|
|
49885
50517
|
function createSearchRoutes(ctx) {
|
|
49886
50518
|
const router = (0, import_express5.Router)();
|
|
@@ -49908,12 +50540,12 @@ function createSearchRoutes(ctx) {
|
|
|
49908
50540
|
});
|
|
49909
50541
|
const docsDir = join13(store.projectRoot, ".knowns", "docs");
|
|
49910
50542
|
const docResults = [];
|
|
49911
|
-
if (
|
|
50543
|
+
if (existsSync10(docsDir)) {
|
|
49912
50544
|
const mdFiles = await findMarkdownFiles(docsDir, docsDir);
|
|
49913
50545
|
for (const relativePath of mdFiles) {
|
|
49914
50546
|
const fullPath = join13(docsDir, relativePath);
|
|
49915
|
-
const content = await
|
|
49916
|
-
const { data, content: docContent } = (0,
|
|
50547
|
+
const content = await readFile8(fullPath, "utf-8");
|
|
50548
|
+
const { data, content: docContent } = (0, import_gray_matter5.default)(content);
|
|
49917
50549
|
const searchText = `${data.title || ""} ${data.description || ""} ${data.tags?.join(" ") || ""} ${docContent}`.toLowerCase();
|
|
49918
50550
|
if (searchText.includes(q)) {
|
|
49919
50551
|
const pathParts = relativePath.split("/");
|
|
@@ -50037,22 +50669,22 @@ function createTaskRoutes(ctx) {
|
|
|
50037
50669
|
|
|
50038
50670
|
// src/server/routes/time.ts
|
|
50039
50671
|
var import_express7 = __toESM(require_express2(), 1);
|
|
50040
|
-
import { existsSync as
|
|
50041
|
-
import { readFile as
|
|
50672
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
50673
|
+
import { readFile as readFile9, writeFile as writeFile6 } from "node:fs/promises";
|
|
50042
50674
|
import { join as join14 } from "node:path";
|
|
50043
50675
|
function createTimeRoutes(ctx) {
|
|
50044
50676
|
const router = (0, import_express7.Router)();
|
|
50045
50677
|
const { store, broadcast } = ctx;
|
|
50046
50678
|
const timePath = join14(store.projectRoot, ".knowns", "time.json");
|
|
50047
50679
|
async function loadTimeData2() {
|
|
50048
|
-
if (!
|
|
50680
|
+
if (!existsSync11(timePath)) {
|
|
50049
50681
|
return { active: null };
|
|
50050
50682
|
}
|
|
50051
|
-
const content = await
|
|
50683
|
+
const content = await readFile9(timePath, "utf-8");
|
|
50052
50684
|
return JSON.parse(content);
|
|
50053
50685
|
}
|
|
50054
50686
|
async function saveTimeData2(data) {
|
|
50055
|
-
await
|
|
50687
|
+
await writeFile6(timePath, JSON.stringify(data, null, 2), "utf-8");
|
|
50056
50688
|
}
|
|
50057
50689
|
router.get("/status", async (_req, res) => {
|
|
50058
50690
|
try {
|
|
@@ -50241,7 +50873,7 @@ async function startServer(options2) {
|
|
|
50241
50873
|
packageRoot = join15(currentDir, "..", "..");
|
|
50242
50874
|
}
|
|
50243
50875
|
const uiDistPath = join15(packageRoot, "dist", "ui");
|
|
50244
|
-
if (!
|
|
50876
|
+
if (!existsSync12(join15(uiDistPath, "index.html"))) {
|
|
50245
50877
|
throw new Error(`UI build not found at ${uiDistPath}. Run 'bun run build:ui' first.`);
|
|
50246
50878
|
}
|
|
50247
50879
|
const app = (0, import_express9.default)();
|
|
@@ -50272,7 +50904,7 @@ async function startServer(options2) {
|
|
|
50272
50904
|
app.use(errorHandler);
|
|
50273
50905
|
app.get("/{*path}", (_req, res) => {
|
|
50274
50906
|
const indexPath = join15(uiDistPath, "index.html");
|
|
50275
|
-
if (
|
|
50907
|
+
if (existsSync12(indexPath)) {
|
|
50276
50908
|
res.sendFile("index.html", { root: uiDistPath });
|
|
50277
50909
|
} else {
|
|
50278
50910
|
res.status(404).send("Not Found");
|
|
@@ -50323,19 +50955,19 @@ var CONFIG_FILE = ".knowns/config.json";
|
|
|
50323
50955
|
async function saveServerPort(projectRoot, port) {
|
|
50324
50956
|
const configPath = join16(projectRoot, CONFIG_FILE);
|
|
50325
50957
|
const knownsDir = join16(projectRoot, ".knowns");
|
|
50326
|
-
if (!
|
|
50958
|
+
if (!existsSync13(knownsDir)) {
|
|
50327
50959
|
await mkdir6(knownsDir, { recursive: true });
|
|
50328
50960
|
}
|
|
50329
50961
|
try {
|
|
50330
50962
|
let config2 = {};
|
|
50331
|
-
if (
|
|
50332
|
-
const content = await
|
|
50963
|
+
if (existsSync13(configPath)) {
|
|
50964
|
+
const content = await readFile10(configPath, "utf-8");
|
|
50333
50965
|
config2 = JSON.parse(content);
|
|
50334
50966
|
}
|
|
50335
50967
|
const settings = config2.settings || {};
|
|
50336
50968
|
settings.serverPort = port;
|
|
50337
50969
|
config2.settings = settings;
|
|
50338
|
-
await
|
|
50970
|
+
await writeFile7(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
50339
50971
|
} catch {
|
|
50340
50972
|
}
|
|
50341
50973
|
}
|
|
@@ -50365,10 +50997,10 @@ var browserCommand = new Command("browser").description("Open web UI for task ma
|
|
|
50365
50997
|
});
|
|
50366
50998
|
|
|
50367
50999
|
// src/commands/doc.ts
|
|
50368
|
-
import { existsSync as
|
|
50369
|
-
import { mkdir as mkdir7, readFile as
|
|
51000
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
51001
|
+
import { mkdir as mkdir7, readFile as readFile11, readdir as readdir5, writeFile as writeFile8 } from "node:fs/promises";
|
|
50370
51002
|
import { join as join17 } from "node:path";
|
|
50371
|
-
var
|
|
51003
|
+
var import_gray_matter6 = __toESM(require_gray_matter(), 1);
|
|
50372
51004
|
var DOCS_DIR = join17(process.cwd(), ".knowns", "docs");
|
|
50373
51005
|
async function getAllMdFiles(dir, basePath = "") {
|
|
50374
51006
|
const files = [];
|
|
@@ -50386,7 +51018,7 @@ async function getAllMdFiles(dir, basePath = "") {
|
|
|
50386
51018
|
return files;
|
|
50387
51019
|
}
|
|
50388
51020
|
async function ensureDocsDir() {
|
|
50389
|
-
if (!
|
|
51021
|
+
if (!existsSync14(DOCS_DIR)) {
|
|
50390
51022
|
await mkdir7(DOCS_DIR, { recursive: true });
|
|
50391
51023
|
}
|
|
50392
51024
|
}
|
|
@@ -50397,11 +51029,11 @@ async function resolveDocPath(name) {
|
|
|
50397
51029
|
await ensureDocsDir();
|
|
50398
51030
|
let filename = name.endsWith(".md") ? name : `${name}.md`;
|
|
50399
51031
|
let filepath = join17(DOCS_DIR, filename);
|
|
50400
|
-
if (!
|
|
51032
|
+
if (!existsSync14(filepath)) {
|
|
50401
51033
|
filename = `${titleToFilename(name)}.md`;
|
|
50402
51034
|
filepath = join17(DOCS_DIR, filename);
|
|
50403
51035
|
}
|
|
50404
|
-
if (!
|
|
51036
|
+
if (!existsSync14(filepath)) {
|
|
50405
51037
|
const allFiles = await getAllMdFiles(DOCS_DIR);
|
|
50406
51038
|
const searchName = name.toLowerCase().replace(/\.md$/, "");
|
|
50407
51039
|
const matchingFile = allFiles.find((file3) => {
|
|
@@ -50414,7 +51046,7 @@ async function resolveDocPath(name) {
|
|
|
50414
51046
|
filepath = join17(DOCS_DIR, matchingFile);
|
|
50415
51047
|
}
|
|
50416
51048
|
}
|
|
50417
|
-
if (!
|
|
51049
|
+
if (!existsSync14(filepath)) {
|
|
50418
51050
|
return null;
|
|
50419
51051
|
}
|
|
50420
51052
|
return { filepath, filename };
|
|
@@ -50429,12 +51061,12 @@ var createCommand3 = new Command("create").description("Create a new documentati
|
|
|
50429
51061
|
const folderPath = options2.folder.replace(/^\/|\/$/g, "");
|
|
50430
51062
|
targetDir = join17(DOCS_DIR, folderPath);
|
|
50431
51063
|
relativePath = join17(folderPath, filename);
|
|
50432
|
-
if (!
|
|
51064
|
+
if (!existsSync14(targetDir)) {
|
|
50433
51065
|
await mkdir7(targetDir, { recursive: true });
|
|
50434
51066
|
}
|
|
50435
51067
|
}
|
|
50436
51068
|
const filepath = join17(targetDir, filename);
|
|
50437
|
-
if (
|
|
51069
|
+
if (existsSync14(filepath)) {
|
|
50438
51070
|
console.error(source_default.red(`\u2717 Document already exists: ${relativePath}`));
|
|
50439
51071
|
process.exit(1);
|
|
50440
51072
|
}
|
|
@@ -50450,8 +51082,8 @@ var createCommand3 = new Command("create").description("Create a new documentati
|
|
|
50450
51082
|
if (options2.tags) {
|
|
50451
51083
|
metadata.tags = options2.tags.split(",").map((t) => t.trim());
|
|
50452
51084
|
}
|
|
50453
|
-
const content =
|
|
50454
|
-
await
|
|
51085
|
+
const content = import_gray_matter6.default.stringify("# Content\n\nWrite your documentation here.\n", metadata);
|
|
51086
|
+
await writeFile8(filepath, content, "utf-8");
|
|
50455
51087
|
await notifyDocUpdate(relativePath);
|
|
50456
51088
|
if (options2.plain) {
|
|
50457
51089
|
console.log(`Created: ${relativePath}`);
|
|
@@ -50464,7 +51096,7 @@ var createCommand3 = new Command("create").description("Create a new documentati
|
|
|
50464
51096
|
process.exit(1);
|
|
50465
51097
|
}
|
|
50466
51098
|
});
|
|
50467
|
-
var listCommand2 = new Command("list").description("List all documentation files").option("--plain", "Plain text output for AI").option("-t, --tag <tag>", "Filter by tag").action(async (options2) => {
|
|
51099
|
+
var listCommand2 = new Command("list").description("List all documentation files").argument("[path]", "Filter by path (e.g., 'guides/' or 'patterns/')").option("--plain", "Plain text output for AI").option("-t, --tag <tag>", "Filter by tag").action(async (path, options2) => {
|
|
50468
51100
|
try {
|
|
50469
51101
|
await ensureDocsDir();
|
|
50470
51102
|
const mdFiles = await getAllMdFiles(DOCS_DIR);
|
|
@@ -50479,55 +51111,107 @@ var listCommand2 = new Command("list").description("List all documentation files
|
|
|
50479
51111
|
}
|
|
50480
51112
|
const docs = [];
|
|
50481
51113
|
for (const file3 of mdFiles) {
|
|
50482
|
-
const content = await
|
|
50483
|
-
const { data } = (0,
|
|
51114
|
+
const content = await readFile11(join17(DOCS_DIR, file3), "utf-8");
|
|
51115
|
+
const { data } = (0, import_gray_matter6.default)(content);
|
|
50484
51116
|
docs.push({
|
|
50485
51117
|
filename: file3,
|
|
50486
51118
|
metadata: data
|
|
50487
51119
|
});
|
|
50488
51120
|
}
|
|
50489
51121
|
let filteredDocs = docs;
|
|
51122
|
+
if (path) {
|
|
51123
|
+
const normalizedPath = path.endsWith("/") ? path : `${path}/`;
|
|
51124
|
+
filteredDocs = docs.filter((doc) => doc.filename.startsWith(normalizedPath));
|
|
51125
|
+
}
|
|
50490
51126
|
if (options2.tag) {
|
|
50491
|
-
filteredDocs =
|
|
51127
|
+
filteredDocs = filteredDocs.filter((doc) => doc.metadata.tags?.includes(options2.tag));
|
|
50492
51128
|
}
|
|
50493
51129
|
if (filteredDocs.length === 0) {
|
|
51130
|
+
const filterMsg = path ? `path: ${path}` : options2.tag ? `tag: ${options2.tag}` : "";
|
|
50494
51131
|
console.log(
|
|
50495
|
-
options2.plain ? "No documentation found" : source_default.yellow(`No documentation found with
|
|
51132
|
+
options2.plain ? "No documentation found" : source_default.yellow(`No documentation found${filterMsg ? ` with ${filterMsg}` : ""}`)
|
|
50496
51133
|
);
|
|
50497
51134
|
return;
|
|
50498
51135
|
}
|
|
50499
51136
|
if (options2.plain) {
|
|
50500
|
-
const
|
|
50501
|
-
|
|
50502
|
-
console.log(border);
|
|
50503
|
-
console.log();
|
|
51137
|
+
const folders = /* @__PURE__ */ new Map();
|
|
51138
|
+
const rootDocs = [];
|
|
50504
51139
|
for (const doc of filteredDocs) {
|
|
50505
|
-
|
|
50506
|
-
if (
|
|
50507
|
-
|
|
51140
|
+
const parts = doc.filename.split("/");
|
|
51141
|
+
if (parts.length === 1) {
|
|
51142
|
+
rootDocs.push({
|
|
51143
|
+
name: parts[0].replace(/\.md$/, ""),
|
|
51144
|
+
title: doc.metadata.title
|
|
51145
|
+
});
|
|
51146
|
+
} else {
|
|
51147
|
+
const folder = parts.slice(0, -1).join("/");
|
|
51148
|
+
const name = parts[parts.length - 1].replace(/\.md$/, "");
|
|
51149
|
+
if (!folders.has(folder)) {
|
|
51150
|
+
folders.set(folder, []);
|
|
51151
|
+
}
|
|
51152
|
+
folders.get(folder)?.push({ name, title: doc.metadata.title });
|
|
50508
51153
|
}
|
|
50509
|
-
|
|
50510
|
-
|
|
50511
|
-
|
|
51154
|
+
}
|
|
51155
|
+
const sortedFolders = Array.from(folders.keys()).sort();
|
|
51156
|
+
for (const folder of sortedFolders) {
|
|
51157
|
+
console.log(`${folder}/`);
|
|
51158
|
+
const docs2 = folders.get(folder)?.sort((a, b) => a.name.localeCompare(b.name));
|
|
51159
|
+
for (const doc of docs2) {
|
|
51160
|
+
console.log(` ${doc.name} - ${doc.title}`);
|
|
51161
|
+
}
|
|
51162
|
+
}
|
|
51163
|
+
if (rootDocs.length > 0) {
|
|
51164
|
+
rootDocs.sort((a, b) => a.name.localeCompare(b.name));
|
|
51165
|
+
for (const doc of rootDocs) {
|
|
51166
|
+
console.log(`${doc.name} - ${doc.title}`);
|
|
50512
51167
|
}
|
|
50513
|
-
console.log();
|
|
50514
51168
|
}
|
|
50515
51169
|
} else {
|
|
50516
|
-
|
|
51170
|
+
const folders = /* @__PURE__ */ new Map();
|
|
51171
|
+
const rootDocs = [];
|
|
50517
51172
|
for (const doc of filteredDocs) {
|
|
50518
|
-
|
|
50519
|
-
if (
|
|
50520
|
-
|
|
51173
|
+
const parts = doc.filename.split("/");
|
|
51174
|
+
if (parts.length === 1) {
|
|
51175
|
+
rootDocs.push(doc);
|
|
51176
|
+
} else {
|
|
51177
|
+
const folder = parts.slice(0, -1).join("/");
|
|
51178
|
+
if (!folders.has(folder)) {
|
|
51179
|
+
folders.set(folder, []);
|
|
51180
|
+
}
|
|
51181
|
+
folders.get(folder)?.push(doc);
|
|
50521
51182
|
}
|
|
50522
|
-
|
|
50523
|
-
|
|
50524
|
-
|
|
51183
|
+
}
|
|
51184
|
+
console.log(source_default.bold(`
|
|
51185
|
+
Documentation (${filteredDocs.length})
|
|
51186
|
+
`));
|
|
51187
|
+
const sortedFolders = Array.from(folders.keys()).sort();
|
|
51188
|
+
for (const folder of sortedFolders) {
|
|
51189
|
+
console.log(source_default.cyan.bold(`${folder}/`));
|
|
51190
|
+
const docs2 = folders.get(folder)?.sort((a, b) => a.metadata.title.localeCompare(b.metadata.title));
|
|
51191
|
+
for (const doc of docs2) {
|
|
51192
|
+
console.log(source_default.white(` ${doc.metadata.title}`));
|
|
51193
|
+
if (doc.metadata.description) {
|
|
51194
|
+
console.log(source_default.gray(` ${doc.metadata.description}`));
|
|
51195
|
+
}
|
|
51196
|
+
if (doc.metadata.tags && doc.metadata.tags.length > 0) {
|
|
51197
|
+
console.log(source_default.gray(` Tags: ${doc.metadata.tags.join(", ")}`));
|
|
51198
|
+
}
|
|
50525
51199
|
}
|
|
50526
|
-
console.log(source_default.gray(` Updated: ${new Date(doc.metadata.updatedAt).toLocaleString()}`));
|
|
50527
51200
|
console.log();
|
|
50528
51201
|
}
|
|
50529
|
-
|
|
50530
|
-
|
|
51202
|
+
if (rootDocs.length > 0) {
|
|
51203
|
+
rootDocs.sort((a, b) => a.metadata.title.localeCompare(b.metadata.title));
|
|
51204
|
+
for (const doc of rootDocs) {
|
|
51205
|
+
console.log(source_default.white(`${doc.metadata.title}`));
|
|
51206
|
+
if (doc.metadata.description) {
|
|
51207
|
+
console.log(source_default.gray(` ${doc.metadata.description}`));
|
|
51208
|
+
}
|
|
51209
|
+
if (doc.metadata.tags && doc.metadata.tags.length > 0) {
|
|
51210
|
+
console.log(source_default.gray(` Tags: ${doc.metadata.tags.join(", ")}`));
|
|
51211
|
+
}
|
|
51212
|
+
console.log();
|
|
51213
|
+
}
|
|
51214
|
+
}
|
|
50531
51215
|
}
|
|
50532
51216
|
} catch (error46) {
|
|
50533
51217
|
console.error(source_default.red("Error listing documentation:"), error46 instanceof Error ? error46.message : String(error46));
|
|
@@ -50539,11 +51223,11 @@ var viewCommand2 = new Command("view").description("View a documentation file").
|
|
|
50539
51223
|
await ensureDocsDir();
|
|
50540
51224
|
let filename = name.endsWith(".md") ? name : `${name}.md`;
|
|
50541
51225
|
let filepath = join17(DOCS_DIR, filename);
|
|
50542
|
-
if (!
|
|
51226
|
+
if (!existsSync14(filepath)) {
|
|
50543
51227
|
filename = `${titleToFilename(name)}.md`;
|
|
50544
51228
|
filepath = join17(DOCS_DIR, filename);
|
|
50545
51229
|
}
|
|
50546
|
-
if (!
|
|
51230
|
+
if (!existsSync14(filepath)) {
|
|
50547
51231
|
const allFiles = await getAllMdFiles(DOCS_DIR);
|
|
50548
51232
|
const searchName = name.toLowerCase().replace(/\.md$/, "");
|
|
50549
51233
|
const matchingFile = allFiles.find((file3) => {
|
|
@@ -50556,12 +51240,12 @@ var viewCommand2 = new Command("view").description("View a documentation file").
|
|
|
50556
51240
|
filepath = join17(DOCS_DIR, matchingFile);
|
|
50557
51241
|
}
|
|
50558
51242
|
}
|
|
50559
|
-
if (!
|
|
51243
|
+
if (!existsSync14(filepath)) {
|
|
50560
51244
|
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
50561
51245
|
process.exit(1);
|
|
50562
51246
|
}
|
|
50563
|
-
const fileContent = await
|
|
50564
|
-
const { data, content } = (0,
|
|
51247
|
+
const fileContent = await readFile11(filepath, "utf-8");
|
|
51248
|
+
const { data, content } = (0, import_gray_matter6.default)(fileContent);
|
|
50565
51249
|
const metadata = data;
|
|
50566
51250
|
if (options2.plain) {
|
|
50567
51251
|
const border = "-".repeat(50);
|
|
@@ -50642,18 +51326,18 @@ var viewCommand2 = new Command("view").description("View a documentation file").
|
|
|
50642
51326
|
process.exit(1);
|
|
50643
51327
|
}
|
|
50644
51328
|
});
|
|
50645
|
-
var editCommand2 = new Command("edit").description("Edit a documentation file (metadata and content)").argument("<name>", "Document name or path (e.g., guides/my-doc)").option("-t, --title <text>", "New title").option("-d, --description <text>", "New description").option("--tags <tags>", "Comma-separated tags").option("-c, --content <text>", "Replace content").option("-a, --append <text>", "Append to content").option("--plain", "Plain text output for AI").action(
|
|
51329
|
+
var editCommand2 = new Command("edit").description("Edit a documentation file (metadata and content)").argument("<name>", "Document name or path (e.g., guides/my-doc)").option("-t, --title <text>", "New title").option("-d, --description <text>", "New description").option("--tags <tags>", "Comma-separated tags").option("-c, --content <text>", "Replace content").option("-a, --append <text>", "Append to content").option("--content-file <path>", "Replace content with file contents").option("--append-file <path>", "Append file contents to document").option("--plain", "Plain text output for AI").action(
|
|
50646
51330
|
async (name, options2) => {
|
|
50647
51331
|
try {
|
|
50648
51332
|
await ensureDocsDir();
|
|
50649
51333
|
let filename = name.endsWith(".md") ? name : `${name}.md`;
|
|
50650
51334
|
let filepath = join17(DOCS_DIR, filename);
|
|
50651
|
-
if (!
|
|
51335
|
+
if (!existsSync14(filepath)) {
|
|
50652
51336
|
const baseName = name.includes("/") ? name : titleToFilename(name);
|
|
50653
51337
|
filename = baseName.endsWith(".md") ? baseName : `${baseName}.md`;
|
|
50654
51338
|
filepath = join17(DOCS_DIR, filename);
|
|
50655
51339
|
}
|
|
50656
|
-
if (!
|
|
51340
|
+
if (!existsSync14(filepath)) {
|
|
50657
51341
|
const allFiles = await getAllMdFiles(DOCS_DIR);
|
|
50658
51342
|
const searchName = name.toLowerCase().replace(/\.md$/, "");
|
|
50659
51343
|
const matchingFile = allFiles.find((file3) => {
|
|
@@ -50666,33 +51350,57 @@ var editCommand2 = new Command("edit").description("Edit a documentation file (m
|
|
|
50666
51350
|
filepath = join17(DOCS_DIR, matchingFile);
|
|
50667
51351
|
}
|
|
50668
51352
|
}
|
|
50669
|
-
if (!
|
|
51353
|
+
if (!existsSync14(filepath)) {
|
|
50670
51354
|
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
50671
51355
|
process.exit(1);
|
|
50672
51356
|
}
|
|
50673
|
-
const fileContent = await
|
|
50674
|
-
const { data, content } = (0,
|
|
51357
|
+
const fileContent = await readFile11(filepath, "utf-8");
|
|
51358
|
+
const { data, content } = (0, import_gray_matter6.default)(fileContent);
|
|
50675
51359
|
const metadata = data;
|
|
50676
51360
|
if (options2.title) metadata.title = options2.title;
|
|
50677
51361
|
if (options2.description) metadata.description = normalizeRefs(options2.description);
|
|
50678
51362
|
if (options2.tags) metadata.tags = options2.tags.split(",").map((t) => t.trim());
|
|
50679
51363
|
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
50680
51364
|
let updatedContent = content;
|
|
50681
|
-
|
|
51365
|
+
let sourceFile;
|
|
51366
|
+
if (options2.contentFile) {
|
|
51367
|
+
if (!existsSync14(options2.contentFile)) {
|
|
51368
|
+
console.error(source_default.red(`\u2717 File not found: ${options2.contentFile}`));
|
|
51369
|
+
process.exit(1);
|
|
51370
|
+
}
|
|
51371
|
+
const fileData = await readFile11(options2.contentFile, "utf-8");
|
|
51372
|
+
updatedContent = normalizeRefs(fileData);
|
|
51373
|
+
sourceFile = options2.contentFile;
|
|
51374
|
+
} else if (options2.appendFile) {
|
|
51375
|
+
if (!existsSync14(options2.appendFile)) {
|
|
51376
|
+
console.error(source_default.red(`\u2717 File not found: ${options2.appendFile}`));
|
|
51377
|
+
process.exit(1);
|
|
51378
|
+
}
|
|
51379
|
+
const fileData = await readFile11(options2.appendFile, "utf-8");
|
|
51380
|
+
updatedContent = `${content.trimEnd()}
|
|
51381
|
+
|
|
51382
|
+
${normalizeRefs(fileData)}`;
|
|
51383
|
+
sourceFile = options2.appendFile;
|
|
51384
|
+
} else if (options2.content) {
|
|
50682
51385
|
updatedContent = normalizeRefs(options2.content);
|
|
50683
|
-
}
|
|
50684
|
-
if (options2.append) {
|
|
51386
|
+
} else if (options2.append) {
|
|
50685
51387
|
updatedContent = `${content.trimEnd()}
|
|
50686
51388
|
|
|
50687
51389
|
${normalizeRefs(options2.append)}`;
|
|
50688
51390
|
}
|
|
50689
|
-
const newFileContent =
|
|
50690
|
-
await
|
|
51391
|
+
const newFileContent = import_gray_matter6.default.stringify(updatedContent, metadata);
|
|
51392
|
+
await writeFile8(filepath, newFileContent, "utf-8");
|
|
50691
51393
|
await notifyDocUpdate(filename);
|
|
50692
51394
|
if (options2.plain) {
|
|
50693
51395
|
console.log(`Updated: ${filename}`);
|
|
51396
|
+
if (sourceFile) {
|
|
51397
|
+
console.log(`Content from: ${sourceFile}`);
|
|
51398
|
+
}
|
|
50694
51399
|
} else {
|
|
50695
51400
|
console.log(source_default.green(`\u2713 Updated documentation: ${source_default.bold(filename)}`));
|
|
51401
|
+
if (sourceFile) {
|
|
51402
|
+
console.log(source_default.gray(` Content from: ${sourceFile}`));
|
|
51403
|
+
}
|
|
50696
51404
|
}
|
|
50697
51405
|
} catch (error46) {
|
|
50698
51406
|
console.error(
|
|
@@ -50703,7 +51411,285 @@ ${normalizeRefs(options2.append)}`;
|
|
|
50703
51411
|
}
|
|
50704
51412
|
}
|
|
50705
51413
|
);
|
|
50706
|
-
var
|
|
51414
|
+
var validateCommand2 = new Command("validate").description("Validate a documentation file format").argument("<name>", "Document name or path").option("--plain", "Plain text output for AI").action(async (name, options2) => {
|
|
51415
|
+
try {
|
|
51416
|
+
const resolved = await resolveDocPath(name);
|
|
51417
|
+
if (!resolved) {
|
|
51418
|
+
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
51419
|
+
process.exit(1);
|
|
51420
|
+
}
|
|
51421
|
+
const content = await readFile11(resolved.filepath, "utf-8");
|
|
51422
|
+
const result = validateDoc(content);
|
|
51423
|
+
if (options2.plain) {
|
|
51424
|
+
console.log(`Validating: ${resolved.filename}`);
|
|
51425
|
+
console.log(`Valid: ${result.valid}`);
|
|
51426
|
+
if (result.errors.length > 0) {
|
|
51427
|
+
console.log("\nErrors:");
|
|
51428
|
+
for (const error46 of result.errors) {
|
|
51429
|
+
console.log(` \u2717 ${error46.field}: ${error46.message}${error46.fixable ? " (fixable)" : ""}`);
|
|
51430
|
+
}
|
|
51431
|
+
}
|
|
51432
|
+
if (result.warnings.length > 0) {
|
|
51433
|
+
console.log("\nWarnings:");
|
|
51434
|
+
for (const warning of result.warnings) {
|
|
51435
|
+
console.log(` \u26A0 ${warning.field}: ${warning.message}`);
|
|
51436
|
+
}
|
|
51437
|
+
}
|
|
51438
|
+
if (result.valid && result.warnings.length === 0) {
|
|
51439
|
+
console.log("No issues found.");
|
|
51440
|
+
}
|
|
51441
|
+
} else {
|
|
51442
|
+
console.log(source_default.bold(`
|
|
51443
|
+
\u{1F4CB} Validation: ${resolved.filename}
|
|
51444
|
+
`));
|
|
51445
|
+
if (result.valid) {
|
|
51446
|
+
console.log(source_default.green("\u2713 Document is valid"));
|
|
51447
|
+
} else {
|
|
51448
|
+
console.log(source_default.red("\u2717 Document has errors"));
|
|
51449
|
+
}
|
|
51450
|
+
if (result.errors.length > 0) {
|
|
51451
|
+
console.log(source_default.red("\nErrors:"));
|
|
51452
|
+
for (const error46 of result.errors) {
|
|
51453
|
+
const fixable = error46.fixable ? source_default.gray(" (fixable)") : "";
|
|
51454
|
+
console.log(source_default.red(` \u2717 ${error46.field}: ${error46.message}${fixable}`));
|
|
51455
|
+
}
|
|
51456
|
+
}
|
|
51457
|
+
if (result.warnings.length > 0) {
|
|
51458
|
+
console.log(source_default.yellow("\nWarnings:"));
|
|
51459
|
+
for (const warning of result.warnings) {
|
|
51460
|
+
console.log(source_default.yellow(` \u26A0 ${warning.field}: ${warning.message}`));
|
|
51461
|
+
}
|
|
51462
|
+
}
|
|
51463
|
+
if (result.valid && result.warnings.length === 0) {
|
|
51464
|
+
console.log(source_default.gray("\nNo issues found."));
|
|
51465
|
+
}
|
|
51466
|
+
console.log();
|
|
51467
|
+
}
|
|
51468
|
+
process.exit(result.valid ? 0 : 1);
|
|
51469
|
+
} catch (error46) {
|
|
51470
|
+
console.error(
|
|
51471
|
+
source_default.red("Error validating documentation:"),
|
|
51472
|
+
error46 instanceof Error ? error46.message : String(error46)
|
|
51473
|
+
);
|
|
51474
|
+
process.exit(1);
|
|
51475
|
+
}
|
|
51476
|
+
});
|
|
51477
|
+
var repairCommand2 = new Command("repair").description("Repair a corrupted documentation file").argument("<name>", "Document name or path").option("--plain", "Plain text output for AI").action(async (name, options2) => {
|
|
51478
|
+
try {
|
|
51479
|
+
const resolved = await resolveDocPath(name);
|
|
51480
|
+
if (!resolved) {
|
|
51481
|
+
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
51482
|
+
process.exit(1);
|
|
51483
|
+
}
|
|
51484
|
+
const result = await repairDoc(resolved.filepath);
|
|
51485
|
+
if (options2.plain) {
|
|
51486
|
+
console.log(`Repairing: ${resolved.filename}`);
|
|
51487
|
+
console.log(`Success: ${result.success}`);
|
|
51488
|
+
if (result.backupPath) {
|
|
51489
|
+
console.log(`Backup: ${result.backupPath}`);
|
|
51490
|
+
}
|
|
51491
|
+
if (result.fixes.length > 0) {
|
|
51492
|
+
console.log("\nFixes applied:");
|
|
51493
|
+
for (const fix of result.fixes) {
|
|
51494
|
+
console.log(` \u2713 ${fix}`);
|
|
51495
|
+
}
|
|
51496
|
+
}
|
|
51497
|
+
if (result.errors.length > 0) {
|
|
51498
|
+
console.log("\nErrors:");
|
|
51499
|
+
for (const error46 of result.errors) {
|
|
51500
|
+
console.log(` \u2717 ${error46}`);
|
|
51501
|
+
}
|
|
51502
|
+
}
|
|
51503
|
+
} else {
|
|
51504
|
+
console.log(source_default.bold(`
|
|
51505
|
+
\u{1F527} Repair: ${resolved.filename}
|
|
51506
|
+
`));
|
|
51507
|
+
if (result.success) {
|
|
51508
|
+
console.log(source_default.green("\u2713 Document repaired successfully"));
|
|
51509
|
+
} else {
|
|
51510
|
+
console.log(source_default.red("\u2717 Repair failed"));
|
|
51511
|
+
}
|
|
51512
|
+
if (result.backupPath) {
|
|
51513
|
+
console.log(source_default.gray(` Backup created: ${result.backupPath}`));
|
|
51514
|
+
}
|
|
51515
|
+
if (result.fixes.length > 0) {
|
|
51516
|
+
console.log(source_default.green("\nFixes applied:"));
|
|
51517
|
+
for (const fix of result.fixes) {
|
|
51518
|
+
console.log(source_default.green(` \u2713 ${fix}`));
|
|
51519
|
+
}
|
|
51520
|
+
}
|
|
51521
|
+
if (result.errors.length > 0) {
|
|
51522
|
+
console.log(source_default.red("\nErrors:"));
|
|
51523
|
+
for (const error46 of result.errors) {
|
|
51524
|
+
console.log(source_default.red(` \u2717 ${error46}`));
|
|
51525
|
+
}
|
|
51526
|
+
}
|
|
51527
|
+
console.log();
|
|
51528
|
+
}
|
|
51529
|
+
if (result.success) {
|
|
51530
|
+
await notifyDocUpdate(resolved.filename);
|
|
51531
|
+
}
|
|
51532
|
+
process.exit(result.success ? 0 : 1);
|
|
51533
|
+
} catch (error46) {
|
|
51534
|
+
console.error(
|
|
51535
|
+
source_default.red("Error repairing documentation:"),
|
|
51536
|
+
error46 instanceof Error ? error46.message : String(error46)
|
|
51537
|
+
);
|
|
51538
|
+
process.exit(1);
|
|
51539
|
+
}
|
|
51540
|
+
});
|
|
51541
|
+
var searchInCommand = new Command("search-in").description("Search text within a specific document").argument("<name>", "Document name or path").argument("<query>", "Text to search for").option("--plain", "Plain text output for AI").option("-i, --ignore-case", "Case insensitive search").action(async (name, query, options2) => {
|
|
51542
|
+
try {
|
|
51543
|
+
const resolved = await resolveDocPath(name);
|
|
51544
|
+
if (!resolved) {
|
|
51545
|
+
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
51546
|
+
process.exit(1);
|
|
51547
|
+
}
|
|
51548
|
+
const fileContent = await readFile11(resolved.filepath, "utf-8");
|
|
51549
|
+
const { content } = (0, import_gray_matter6.default)(fileContent);
|
|
51550
|
+
const lines = content.split("\n");
|
|
51551
|
+
const matches = [];
|
|
51552
|
+
const searchQuery = options2.ignoreCase ? query.toLowerCase() : query;
|
|
51553
|
+
for (let i = 0; i < lines.length; i++) {
|
|
51554
|
+
const line = lines[i];
|
|
51555
|
+
const searchLine = options2.ignoreCase ? line.toLowerCase() : line;
|
|
51556
|
+
if (searchLine.includes(searchQuery)) {
|
|
51557
|
+
const contextStart = Math.max(0, i - 1);
|
|
51558
|
+
const contextEnd = Math.min(lines.length - 1, i + 1);
|
|
51559
|
+
const context = lines.slice(contextStart, contextEnd + 1).join("\n");
|
|
51560
|
+
matches.push({
|
|
51561
|
+
lineNum: i + 1,
|
|
51562
|
+
// 1-indexed
|
|
51563
|
+
line,
|
|
51564
|
+
context
|
|
51565
|
+
});
|
|
51566
|
+
}
|
|
51567
|
+
}
|
|
51568
|
+
if (options2.plain) {
|
|
51569
|
+
console.log(`Searching in: ${resolved.filename}`);
|
|
51570
|
+
console.log(`Query: "${query}"`);
|
|
51571
|
+
console.log(`Found: ${matches.length} match(es)`);
|
|
51572
|
+
console.log();
|
|
51573
|
+
for (const match of matches) {
|
|
51574
|
+
console.log(`Line ${match.lineNum}: ${match.line.trim()}`);
|
|
51575
|
+
}
|
|
51576
|
+
} else {
|
|
51577
|
+
console.log(source_default.bold(`
|
|
51578
|
+
\u{1F50D} Search in: ${resolved.filename}
|
|
51579
|
+
`));
|
|
51580
|
+
console.log(source_default.gray(`Query: "${query}"`));
|
|
51581
|
+
console.log(source_default.gray(`Found: ${matches.length} match(es)
|
|
51582
|
+
`));
|
|
51583
|
+
for (const match of matches) {
|
|
51584
|
+
console.log(source_default.cyan(`Line ${match.lineNum}:`));
|
|
51585
|
+
console.log(source_default.white(` ${match.line.trim()}`));
|
|
51586
|
+
console.log();
|
|
51587
|
+
}
|
|
51588
|
+
}
|
|
51589
|
+
} catch (error46) {
|
|
51590
|
+
console.error(source_default.red("Error searching document:"), error46 instanceof Error ? error46.message : String(error46));
|
|
51591
|
+
process.exit(1);
|
|
51592
|
+
}
|
|
51593
|
+
});
|
|
51594
|
+
var replaceCommand = new Command("replace").description("Replace text in a document").argument("<name>", "Document name or path").argument("<old-text>", "Text to find").argument("<new-text>", "Text to replace with").option("--plain", "Plain text output for AI").option("-a, --all", "Replace all occurrences (default: first only)").action(async (name, oldText, newText, options2) => {
|
|
51595
|
+
try {
|
|
51596
|
+
const resolved = await resolveDocPath(name);
|
|
51597
|
+
if (!resolved) {
|
|
51598
|
+
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
51599
|
+
process.exit(1);
|
|
51600
|
+
}
|
|
51601
|
+
const fileContent = await readFile11(resolved.filepath, "utf-8");
|
|
51602
|
+
const { data, content } = (0, import_gray_matter6.default)(fileContent);
|
|
51603
|
+
if (!content.includes(oldText)) {
|
|
51604
|
+
if (options2.plain) {
|
|
51605
|
+
console.log(`Text not found: "${oldText}"`);
|
|
51606
|
+
} else {
|
|
51607
|
+
console.log(source_default.yellow(`\u26A0 Text not found: "${oldText}"`));
|
|
51608
|
+
}
|
|
51609
|
+
process.exit(1);
|
|
51610
|
+
}
|
|
51611
|
+
let newContent;
|
|
51612
|
+
let count = 0;
|
|
51613
|
+
if (options2.all) {
|
|
51614
|
+
const parts = content.split(oldText);
|
|
51615
|
+
count = parts.length - 1;
|
|
51616
|
+
newContent = parts.join(newText);
|
|
51617
|
+
} else {
|
|
51618
|
+
newContent = content.replace(oldText, newText);
|
|
51619
|
+
count = 1;
|
|
51620
|
+
}
|
|
51621
|
+
const metadata = data;
|
|
51622
|
+
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
51623
|
+
const updatedFileContent = import_gray_matter6.default.stringify(newContent, data);
|
|
51624
|
+
await writeFile8(resolved.filepath, updatedFileContent, "utf-8");
|
|
51625
|
+
await notifyDocUpdate(resolved.filename);
|
|
51626
|
+
if (options2.plain) {
|
|
51627
|
+
console.log(`Updated: ${resolved.filename}`);
|
|
51628
|
+
console.log(`Replaced: ${count} occurrence(s)`);
|
|
51629
|
+
} else {
|
|
51630
|
+
console.log(source_default.green(`\u2713 Updated: ${resolved.filename}`));
|
|
51631
|
+
console.log(source_default.gray(` Replaced ${count} occurrence(s)`));
|
|
51632
|
+
}
|
|
51633
|
+
} catch (error46) {
|
|
51634
|
+
console.error(source_default.red("Error replacing text:"), error46 instanceof Error ? error46.message : String(error46));
|
|
51635
|
+
process.exit(1);
|
|
51636
|
+
}
|
|
51637
|
+
});
|
|
51638
|
+
var replaceSectionCommand = new Command("replace-section").description("Replace an entire section by its header").argument("<name>", "Document name or path").argument("<header>", "Section header (e.g., '## Section Name')").argument("<content>", "New section content (without header)").option("--plain", "Plain text output for AI").action(async (name, header, newSectionContent, options2) => {
|
|
51639
|
+
try {
|
|
51640
|
+
const resolved = await resolveDocPath(name);
|
|
51641
|
+
if (!resolved) {
|
|
51642
|
+
console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
|
|
51643
|
+
process.exit(1);
|
|
51644
|
+
}
|
|
51645
|
+
const fileContent = await readFile11(resolved.filepath, "utf-8");
|
|
51646
|
+
const { data, content } = (0, import_gray_matter6.default)(fileContent);
|
|
51647
|
+
const headerLevel = (header.match(/^#+/) || ["##"])[0];
|
|
51648
|
+
const headerPattern = new RegExp(`^${header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
51649
|
+
const headerMatch = content.match(headerPattern);
|
|
51650
|
+
if (!headerMatch) {
|
|
51651
|
+
if (options2.plain) {
|
|
51652
|
+
console.log(`Section not found: "${header}"`);
|
|
51653
|
+
} else {
|
|
51654
|
+
console.log(source_default.yellow(`\u26A0 Section not found: "${header}"`));
|
|
51655
|
+
}
|
|
51656
|
+
process.exit(1);
|
|
51657
|
+
}
|
|
51658
|
+
const headerIndex = content.indexOf(headerMatch[0]);
|
|
51659
|
+
const afterHeader = content.substring(headerIndex + headerMatch[0].length);
|
|
51660
|
+
const nextHeaderPattern = new RegExp(`^#{1,${headerLevel.length}}\\s+`, "m");
|
|
51661
|
+
const nextHeaderMatch = afterHeader.match(nextHeaderPattern);
|
|
51662
|
+
let sectionEnd;
|
|
51663
|
+
if (nextHeaderMatch) {
|
|
51664
|
+
sectionEnd = headerIndex + headerMatch[0].length + afterHeader.indexOf(nextHeaderMatch[0]);
|
|
51665
|
+
} else {
|
|
51666
|
+
sectionEnd = content.length;
|
|
51667
|
+
}
|
|
51668
|
+
const beforeSection = content.substring(0, headerIndex);
|
|
51669
|
+
const afterSection = content.substring(sectionEnd);
|
|
51670
|
+
const newContent = `${beforeSection}${header}
|
|
51671
|
+
|
|
51672
|
+
${newSectionContent}
|
|
51673
|
+
|
|
51674
|
+
${afterSection.trimStart()}`;
|
|
51675
|
+
const metadata = data;
|
|
51676
|
+
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
51677
|
+
const updatedFileContent = import_gray_matter6.default.stringify(newContent.trim(), data);
|
|
51678
|
+
await writeFile8(resolved.filepath, updatedFileContent, "utf-8");
|
|
51679
|
+
await notifyDocUpdate(resolved.filename);
|
|
51680
|
+
if (options2.plain) {
|
|
51681
|
+
console.log(`Updated: ${resolved.filename}`);
|
|
51682
|
+
console.log(`Replaced section: "${header}"`);
|
|
51683
|
+
} else {
|
|
51684
|
+
console.log(source_default.green(`\u2713 Updated: ${resolved.filename}`));
|
|
51685
|
+
console.log(source_default.gray(` Replaced section: "${header}"`));
|
|
51686
|
+
}
|
|
51687
|
+
} catch (error46) {
|
|
51688
|
+
console.error(source_default.red("Error replacing section:"), error46 instanceof Error ? error46.message : String(error46));
|
|
51689
|
+
process.exit(1);
|
|
51690
|
+
}
|
|
51691
|
+
});
|
|
51692
|
+
var docCommand = new Command("doc").description("Manage documentation").argument("[name]", "Document name (shorthand for 'doc view <name>')").option("--plain", "Plain text output for AI").enablePositionalOptions().passThroughOptions().action(async (name, options2) => {
|
|
50707
51693
|
if (!name) {
|
|
50708
51694
|
docCommand.help();
|
|
50709
51695
|
return;
|
|
@@ -50715,8 +51701,8 @@ var docCommand = new Command("doc").description("Manage documentation").argument
|
|
|
50715
51701
|
process.exit(1);
|
|
50716
51702
|
}
|
|
50717
51703
|
const { filepath, filename } = resolved;
|
|
50718
|
-
const fileContent = await
|
|
50719
|
-
const { data, content } = (0,
|
|
51704
|
+
const fileContent = await readFile11(filepath, "utf-8");
|
|
51705
|
+
const { data, content } = (0, import_gray_matter6.default)(fileContent);
|
|
50720
51706
|
const metadata = data;
|
|
50721
51707
|
if (options2.plain) {
|
|
50722
51708
|
const border = "-".repeat(50);
|
|
@@ -50801,10 +51787,15 @@ docCommand.addCommand(createCommand3);
|
|
|
50801
51787
|
docCommand.addCommand(listCommand2);
|
|
50802
51788
|
docCommand.addCommand(viewCommand2);
|
|
50803
51789
|
docCommand.addCommand(editCommand2);
|
|
51790
|
+
docCommand.addCommand(validateCommand2);
|
|
51791
|
+
docCommand.addCommand(repairCommand2);
|
|
51792
|
+
docCommand.addCommand(searchInCommand);
|
|
51793
|
+
docCommand.addCommand(replaceCommand);
|
|
51794
|
+
docCommand.addCommand(replaceSectionCommand);
|
|
50804
51795
|
|
|
50805
51796
|
// src/commands/config.ts
|
|
50806
|
-
import { existsSync as
|
|
50807
|
-
import { mkdir as mkdir8, readFile as
|
|
51797
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
51798
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile9 } from "node:fs/promises";
|
|
50808
51799
|
import { join as join18 } from "node:path";
|
|
50809
51800
|
|
|
50810
51801
|
// node_modules/zod/v4/classic/external.js
|
|
@@ -64363,11 +65354,11 @@ function getProjectRoot2() {
|
|
|
64363
65354
|
}
|
|
64364
65355
|
async function loadConfig(projectRoot) {
|
|
64365
65356
|
const configPath = join18(projectRoot, CONFIG_FILE2);
|
|
64366
|
-
if (!
|
|
65357
|
+
if (!existsSync15(configPath)) {
|
|
64367
65358
|
return { ...DEFAULT_CONFIG };
|
|
64368
65359
|
}
|
|
64369
65360
|
try {
|
|
64370
|
-
const content = await
|
|
65361
|
+
const content = await readFile12(configPath, "utf-8");
|
|
64371
65362
|
const data = JSON.parse(content);
|
|
64372
65363
|
const settings = data.settings || {};
|
|
64373
65364
|
const validated = ConfigSchema.parse(settings);
|
|
@@ -64383,20 +65374,20 @@ async function loadConfig(projectRoot) {
|
|
|
64383
65374
|
async function saveConfig(projectRoot, config2) {
|
|
64384
65375
|
const configPath = join18(projectRoot, CONFIG_FILE2);
|
|
64385
65376
|
const knownsDir = join18(projectRoot, ".knowns");
|
|
64386
|
-
if (!
|
|
65377
|
+
if (!existsSync15(knownsDir)) {
|
|
64387
65378
|
await mkdir8(knownsDir, { recursive: true });
|
|
64388
65379
|
}
|
|
64389
65380
|
try {
|
|
64390
65381
|
let existingData = {};
|
|
64391
|
-
if (
|
|
64392
|
-
const content = await
|
|
65382
|
+
if (existsSync15(configPath)) {
|
|
65383
|
+
const content = await readFile12(configPath, "utf-8");
|
|
64393
65384
|
existingData = JSON.parse(content);
|
|
64394
65385
|
}
|
|
64395
65386
|
const merged = {
|
|
64396
65387
|
...existingData,
|
|
64397
65388
|
settings: config2
|
|
64398
65389
|
};
|
|
64399
|
-
await
|
|
65390
|
+
await writeFile9(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
64400
65391
|
} catch (error46) {
|
|
64401
65392
|
console.error(source_default.red("\u2717 Failed to save config"));
|
|
64402
65393
|
if (error46 instanceof Error) {
|
|
@@ -64538,8 +65529,8 @@ var resetCommand = new Command("reset").description("Reset configuration to defa
|
|
|
64538
65529
|
var configCommand = new Command("config").description("Manage configuration settings").addCommand(listCommand3).addCommand(getCommand).addCommand(setCommand).addCommand(resetCommand);
|
|
64539
65530
|
|
|
64540
65531
|
// src/mcp/server.ts
|
|
64541
|
-
import { existsSync as
|
|
64542
|
-
import { readFile as
|
|
65532
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
65533
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
64543
65534
|
import { join as join21 } from "node:path";
|
|
64544
65535
|
|
|
64545
65536
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -71600,12 +72591,12 @@ var StdioServerTransport = class {
|
|
|
71600
72591
|
};
|
|
71601
72592
|
|
|
71602
72593
|
// src/mcp/server.ts
|
|
71603
|
-
var
|
|
72594
|
+
var import_gray_matter9 = __toESM(require_gray_matter(), 1);
|
|
71604
72595
|
|
|
71605
72596
|
// src/mcp/utils.ts
|
|
71606
|
-
import { readFile as
|
|
72597
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
71607
72598
|
import { join as join19 } from "node:path";
|
|
71608
|
-
var
|
|
72599
|
+
var import_gray_matter7 = __toESM(require_gray_matter(), 1);
|
|
71609
72600
|
function parseDuration2(durationStr) {
|
|
71610
72601
|
let totalSeconds = 0;
|
|
71611
72602
|
const hoursMatch = durationStr.match(/(\d+)h/);
|
|
@@ -71646,8 +72637,8 @@ async function fetchLinkedDocs(task) {
|
|
|
71646
72637
|
try {
|
|
71647
72638
|
const filename = ref.resolvedPath.replace("@.knowns/docs/", "");
|
|
71648
72639
|
const filepath = join19(docsDir, filename);
|
|
71649
|
-
const fileContent = await
|
|
71650
|
-
const { data, content } = (0,
|
|
72640
|
+
const fileContent = await readFile13(filepath, "utf-8");
|
|
72641
|
+
const { data, content } = (0, import_gray_matter7.default)(fileContent);
|
|
71651
72642
|
linkedDocs.push({
|
|
71652
72643
|
path: ref.resolvedPath,
|
|
71653
72644
|
title: data.title || ref.text,
|
|
@@ -72208,10 +73199,10 @@ async function handleGetBoard(fileStore2) {
|
|
|
72208
73199
|
}
|
|
72209
73200
|
|
|
72210
73201
|
// src/mcp/handlers/doc.ts
|
|
72211
|
-
import { existsSync as
|
|
72212
|
-
import { mkdir as mkdir9, readFile as
|
|
73202
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
73203
|
+
import { mkdir as mkdir9, readFile as readFile14, readdir as readdir6, writeFile as writeFile10 } from "node:fs/promises";
|
|
72213
73204
|
import { join as join20 } from "node:path";
|
|
72214
|
-
var
|
|
73205
|
+
var import_gray_matter8 = __toESM(require_gray_matter(), 1);
|
|
72215
73206
|
var DOCS_DIR2 = join20(process.cwd(), ".knowns", "docs");
|
|
72216
73207
|
var listDocsSchema = external_exports.object({
|
|
72217
73208
|
tag: external_exports.string().optional()
|
|
@@ -72329,7 +73320,7 @@ var docTools = [
|
|
|
72329
73320
|
}
|
|
72330
73321
|
];
|
|
72331
73322
|
async function ensureDocsDir2() {
|
|
72332
|
-
if (!
|
|
73323
|
+
if (!existsSync16(DOCS_DIR2)) {
|
|
72333
73324
|
await mkdir9(DOCS_DIR2, { recursive: true });
|
|
72334
73325
|
}
|
|
72335
73326
|
}
|
|
@@ -72338,7 +73329,7 @@ function titleToFilename2(title) {
|
|
|
72338
73329
|
}
|
|
72339
73330
|
async function getAllMdFiles2(dir, basePath = "") {
|
|
72340
73331
|
const files = [];
|
|
72341
|
-
if (!
|
|
73332
|
+
if (!existsSync16(dir)) {
|
|
72342
73333
|
return files;
|
|
72343
73334
|
}
|
|
72344
73335
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
@@ -72358,12 +73349,12 @@ async function resolveDocPath2(name) {
|
|
|
72358
73349
|
await ensureDocsDir2();
|
|
72359
73350
|
let filename = name.endsWith(".md") ? name : `${name}.md`;
|
|
72360
73351
|
let filepath = join20(DOCS_DIR2, filename);
|
|
72361
|
-
if (
|
|
73352
|
+
if (existsSync16(filepath)) {
|
|
72362
73353
|
return { filepath, filename };
|
|
72363
73354
|
}
|
|
72364
73355
|
filename = `${titleToFilename2(name)}.md`;
|
|
72365
73356
|
filepath = join20(DOCS_DIR2, filename);
|
|
72366
|
-
if (
|
|
73357
|
+
if (existsSync16(filepath)) {
|
|
72367
73358
|
return { filepath, filename };
|
|
72368
73359
|
}
|
|
72369
73360
|
const allFiles = await getAllMdFiles2(DOCS_DIR2);
|
|
@@ -72394,8 +73385,8 @@ async function handleListDocs(args) {
|
|
|
72394
73385
|
}
|
|
72395
73386
|
const docs = [];
|
|
72396
73387
|
for (const file3 of mdFiles) {
|
|
72397
|
-
const content = await
|
|
72398
|
-
const { data } = (0,
|
|
73388
|
+
const content = await readFile14(join20(DOCS_DIR2, file3), "utf-8");
|
|
73389
|
+
const { data } = (0, import_gray_matter8.default)(content);
|
|
72399
73390
|
const metadata = data;
|
|
72400
73391
|
if (input.tag && !metadata.tags?.includes(input.tag)) {
|
|
72401
73392
|
continue;
|
|
@@ -72419,8 +73410,8 @@ async function handleGetDoc(args) {
|
|
|
72419
73410
|
if (!resolved) {
|
|
72420
73411
|
return errorResponse(`Documentation not found: ${input.path}`);
|
|
72421
73412
|
}
|
|
72422
|
-
const fileContent = await
|
|
72423
|
-
const { data, content } = (0,
|
|
73413
|
+
const fileContent = await readFile14(resolved.filepath, "utf-8");
|
|
73414
|
+
const { data, content } = (0, import_gray_matter8.default)(fileContent);
|
|
72424
73415
|
const metadata = data;
|
|
72425
73416
|
return successResponse({
|
|
72426
73417
|
doc: {
|
|
@@ -72444,12 +73435,12 @@ async function handleCreateDoc(args) {
|
|
|
72444
73435
|
const folderPath = input.folder.replace(/^\/|\/$/g, "");
|
|
72445
73436
|
targetDir = join20(DOCS_DIR2, folderPath);
|
|
72446
73437
|
relativePath = join20(folderPath, filename);
|
|
72447
|
-
if (!
|
|
73438
|
+
if (!existsSync16(targetDir)) {
|
|
72448
73439
|
await mkdir9(targetDir, { recursive: true });
|
|
72449
73440
|
}
|
|
72450
73441
|
}
|
|
72451
73442
|
const filepath = join20(targetDir, filename);
|
|
72452
|
-
if (
|
|
73443
|
+
if (existsSync16(filepath)) {
|
|
72453
73444
|
return errorResponse(`Document already exists: ${relativePath}`);
|
|
72454
73445
|
}
|
|
72455
73446
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -72465,8 +73456,8 @@ async function handleCreateDoc(args) {
|
|
|
72465
73456
|
metadata.tags = input.tags;
|
|
72466
73457
|
}
|
|
72467
73458
|
const initialContent = input.content || "# Content\n\nWrite your documentation here.";
|
|
72468
|
-
const fileContent =
|
|
72469
|
-
await
|
|
73459
|
+
const fileContent = import_gray_matter8.default.stringify(initialContent, metadata);
|
|
73460
|
+
await writeFile10(filepath, fileContent, "utf-8");
|
|
72470
73461
|
await notifyDocUpdate(relativePath);
|
|
72471
73462
|
return successResponse({
|
|
72472
73463
|
message: `Created documentation: ${relativePath}`,
|
|
@@ -72484,8 +73475,8 @@ async function handleUpdateDoc(args) {
|
|
|
72484
73475
|
if (!resolved) {
|
|
72485
73476
|
return errorResponse(`Documentation not found: ${input.path}`);
|
|
72486
73477
|
}
|
|
72487
|
-
const fileContent = await
|
|
72488
|
-
const { data, content } = (0,
|
|
73478
|
+
const fileContent = await readFile14(resolved.filepath, "utf-8");
|
|
73479
|
+
const { data, content } = (0, import_gray_matter8.default)(fileContent);
|
|
72489
73480
|
const metadata = data;
|
|
72490
73481
|
if (input.title) metadata.title = input.title;
|
|
72491
73482
|
if (input.description) metadata.description = input.description;
|
|
@@ -72500,8 +73491,8 @@ async function handleUpdateDoc(args) {
|
|
|
72500
73491
|
|
|
72501
73492
|
${input.appendContent}`;
|
|
72502
73493
|
}
|
|
72503
|
-
const newFileContent =
|
|
72504
|
-
await
|
|
73494
|
+
const newFileContent = import_gray_matter8.default.stringify(updatedContent, metadata);
|
|
73495
|
+
await writeFile10(resolved.filepath, newFileContent, "utf-8");
|
|
72505
73496
|
await notifyDocUpdate(resolved.filename);
|
|
72506
73497
|
return successResponse({
|
|
72507
73498
|
message: `Updated documentation: ${resolved.filename}`,
|
|
@@ -72521,8 +73512,8 @@ async function handleSearchDocs(args) {
|
|
|
72521
73512
|
const query = input.query.toLowerCase();
|
|
72522
73513
|
const results = [];
|
|
72523
73514
|
for (const file3 of mdFiles) {
|
|
72524
|
-
const fileContent = await
|
|
72525
|
-
const { data, content } = (0,
|
|
73515
|
+
const fileContent = await readFile14(join20(DOCS_DIR2, file3), "utf-8");
|
|
73516
|
+
const { data, content } = (0, import_gray_matter8.default)(fileContent);
|
|
72526
73517
|
const metadata = data;
|
|
72527
73518
|
if (input.tag && !metadata.tags?.includes(input.tag)) {
|
|
72528
73519
|
continue;
|
|
@@ -72630,7 +73621,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
72630
73621
|
description: `Task #${task.id}: ${task.title}`
|
|
72631
73622
|
}));
|
|
72632
73623
|
const docResources = [];
|
|
72633
|
-
if (
|
|
73624
|
+
if (existsSync17(docsDir)) {
|
|
72634
73625
|
const { readdir: readdir7 } = await import("node:fs/promises");
|
|
72635
73626
|
async function getAllMdFiles3(dir, basePath = "") {
|
|
72636
73627
|
const files = [];
|
|
@@ -72650,8 +73641,8 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
72650
73641
|
const mdFiles = await getAllMdFiles3(docsDir);
|
|
72651
73642
|
for (const file3 of mdFiles) {
|
|
72652
73643
|
const filepath = join21(docsDir, file3);
|
|
72653
|
-
const content = await
|
|
72654
|
-
const { data } = (0,
|
|
73644
|
+
const content = await readFile15(filepath, "utf-8");
|
|
73645
|
+
const { data } = (0, import_gray_matter9.default)(content);
|
|
72655
73646
|
docResources.push({
|
|
72656
73647
|
uri: `knowns://doc/${file3.replace(/\.md$/, "")}`,
|
|
72657
73648
|
name: data.title || file3.replace(/\.md$/, ""),
|
|
@@ -72688,11 +73679,11 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
72688
73679
|
const docPath = docMatch[1];
|
|
72689
73680
|
const docsDir = join21(process.cwd(), ".knowns", "docs");
|
|
72690
73681
|
const filepath = join21(docsDir, `${docPath}.md`);
|
|
72691
|
-
if (!
|
|
73682
|
+
if (!existsSync17(filepath)) {
|
|
72692
73683
|
throw new Error(`Documentation ${docPath} not found`);
|
|
72693
73684
|
}
|
|
72694
|
-
const content = await
|
|
72695
|
-
const { data, content: docContent } = (0,
|
|
73685
|
+
const content = await readFile15(filepath, "utf-8");
|
|
73686
|
+
const { data, content: docContent } = (0, import_gray_matter9.default)(content);
|
|
72696
73687
|
return {
|
|
72697
73688
|
contents: [
|
|
72698
73689
|
{
|
|
@@ -72795,7 +73786,7 @@ function showConfigInfo() {
|
|
|
72795
73786
|
// package.json
|
|
72796
73787
|
var package_default = {
|
|
72797
73788
|
name: "knowns",
|
|
72798
|
-
version: "0.
|
|
73789
|
+
version: "0.4.0",
|
|
72799
73790
|
description: "CLI tool for dev teams to manage tasks and documentation",
|
|
72800
73791
|
module: "index.ts",
|
|
72801
73792
|
type: "module",
|
|
@@ -72928,7 +73919,7 @@ function showBanner() {
|
|
|
72928
73919
|
console.log();
|
|
72929
73920
|
}
|
|
72930
73921
|
var program2 = new Command();
|
|
72931
|
-
program2.name("knowns").description("CLI tool for dev teams to manage tasks, track time, and sync").version(package_default.version);
|
|
73922
|
+
program2.name("knowns").description("CLI tool for dev teams to manage tasks, track time, and sync").version(package_default.version).enablePositionalOptions();
|
|
72932
73923
|
program2.addCommand(initCommand);
|
|
72933
73924
|
program2.addCommand(taskCommand);
|
|
72934
73925
|
program2.addCommand(boardCommand);
|