knowns 0.10.1 → 0.10.3
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/dist/index.js +427 -204
- package/dist/mcp/server.js +298 -152
- package/dist/ui/assets/{index-CavOnkoC.js → index-Djj_i5GU.js} +62 -62
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -58829,7 +58829,7 @@ function evaluateCondition(condition, context) {
|
|
|
58829
58829
|
var commands_reference_default = '{{#if mcp}}\n# MCP Tools Reference\n\n## Task Tools\n\n### mcp__knowns__create_task\n\n```json\n{\n "title": "Task title",\n "description": "Task description",\n "status": "todo",\n "priority": "medium",\n "labels": ["label1"],\n "assignee": "@me",\n "parent": "parent-id"\n}\n```\n\n### mcp__knowns__update_task\n\n```json\n{\n "taskId": "<id>",\n "status": "in-progress",\n "assignee": "@me",\n "addAc": ["Criterion 1", "Criterion 2"],\n "checkAc": [1, 2],\n "uncheckAc": [3],\n "removeAc": [4],\n "plan": "1. Step one\\n2. Step two",\n "notes": "Implementation notes",\n "appendNotes": "Additional notes"\n}\n```\n\n| Field | Purpose |\n|-------|---------|\n| `addAc` | Add new acceptance criteria |\n| `checkAc` | Mark AC done (1-based index) |\n| `uncheckAc` | Unmark AC (1-based index) |\n| `removeAc` | Remove AC (1-based index) |\n| `plan` | Set implementation plan |\n| `notes` | Replace implementation notes |\n| `appendNotes` | Append to notes |\n\n### mcp__knowns__get_task\n\n```json\n{ "taskId": "<id>" }\n```\n\n### mcp__knowns__list_tasks\n\n```json\n{ "status": "in-progress", "assignee": "@me" }\n```\n\n### mcp__knowns__search_tasks\n\n```json\n{ "query": "keyword" }\n```\n\n---\n\n## Doc Tools\n\n### mcp__knowns__get_doc\n\n**ALWAYS use `smart: true`** - auto-handles small/large docs:\n\n```json\n{ "path": "readme", "smart": true }\n```\n\nIf large, returns TOC. Then read section:\n```json\n{ "path": "readme", "section": "3" }\n```\n\n### mcp__knowns__list_docs\n\n```json\n{ "tag": "api" }\n```\n\n### mcp__knowns__create_doc\n\n```json\n{\n "title": "Doc Title",\n "description": "Description",\n "tags": ["tag1"],\n "folder": "guides",\n "content": "Initial content"\n}\n```\n\n### mcp__knowns__update_doc\n\n```json\n{\n "path": "readme",\n "content": "Replace content",\n "section": "2"\n}\n```\n\n### mcp__knowns__search_docs\n\n```json\n{ "query": "keyword", "tag": "api" }\n```\n\n### mcp__knowns__search (Unified)\n\n```json\n{\n "query": "keyword",\n "type": "all",\n "status": "in-progress",\n "priority": "high",\n "assignee": "@me",\n "label": "feature",\n "tag": "api",\n "limit": 20\n}\n```\n\n| Field | Purpose |\n|-------|---------|\n| `type` | "all", "task", or "doc" |\n| `status/priority/assignee/label` | Task filters |\n| `tag` | Doc filter |\n| `limit` | Max results (default: 20) |\n\n---\n\n## Time Tools\n\n### mcp__knowns__start_time\n\n```json\n{ "taskId": "<id>" }\n```\n\n### mcp__knowns__stop_time\n\n```json\n{ "taskId": "<id>" }\n```\n\n### mcp__knowns__add_time\n\n```json\n{\n "taskId": "<id>",\n "duration": "2h30m",\n "note": "Note",\n "date": "2025-01-15"\n}\n```\n\n### mcp__knowns__get_time_report\n\n```json\n{ "from": "2025-01-01", "to": "2025-01-31", "groupBy": "task" }\n```\n\n---\n\n## Template Tools\n\n### mcp__knowns__list_templates\n\n```json\n{}\n```\n\n### mcp__knowns__get_template\n\n```json\n{ "name": "template-name" }\n```\n\n### mcp__knowns__run_template\n\n```json\n{\n "name": "template-name",\n "variables": { "name": "MyComponent" },\n "dryRun": true\n}\n```\n\n### mcp__knowns__create_template\n\n```json\n{\n "name": "my-template",\n "description": "Description",\n "doc": "patterns/my-pattern"\n}\n```\n\n---\n\n## Other\n\n### mcp__knowns__get_board\n\n```json\n{}\n```\n\n{{else}}\n# CLI Commands Reference\n\n## task create\n\n```bash\nknowns task create <title> [options]\n```\n\n| Flag | Short | Purpose |\n|------|-------|---------|\n| `--description` | `-d` | Task description |\n| `--ac` | | Acceptance criterion (repeatable) |\n| `--labels` | `-l` | Comma-separated labels |\n| `--assignee` | `-a` | Assign to user |\n| `--priority` | | low/medium/high |\n| `--parent` | | Parent task ID (raw ID only!) |\n\n**`-a` = assignee, NOT acceptance criteria! Use `--ac` for AC.**\n\n---\n\n## task edit\n\n```bash\nknowns task edit <id> [options]\n```\n\n| Flag | Short | Purpose |\n|------|-------|---------|\n| `--status` | `-s` | Change status |\n| `--assignee` | `-a` | Assign user |\n| `--ac` | | Add acceptance criterion |\n| `--check-ac` | | Mark AC done (1-indexed) |\n| `--uncheck-ac` | | Unmark AC |\n| `--plan` | | Set implementation plan |\n| `--notes` | | Replace notes |\n| `--append-notes` | | Add to notes |\n\n---\n\n## task view/list\n\n```bash\nknowns task <id> --plain\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task list --tree --plain\n```\n\n---\n\n## doc create\n\n```bash\nknowns doc create <title> [options]\n```\n\n| Flag | Short | Purpose |\n|------|-------|---------|\n| `--description` | `-d` | Description |\n| `--tags` | `-t` | Comma-separated tags |\n| `--folder` | `-f` | Folder path |\n\n---\n\n## doc edit\n\n```bash\nknowns doc edit <name> [options]\n```\n\n| Flag | Short | Purpose |\n|------|-------|---------|\n| `--content` | `-c` | Replace content |\n| `--append` | `-a` | Append content |\n| `--section` | | Target section (use with -c) |\n\n**In doc edit, `-a` = append content, NOT assignee!**\n\n---\n\n## doc view/list\n\n**ALWAYS use `--smart`** - auto-handles small/large docs:\n\n```bash\nknowns doc <path> --plain --smart\n```\n\nIf large, returns TOC. Then read section:\n```bash\nknowns doc <path> --plain --section 3\n```\n\n```bash\nknowns doc list --plain\nknowns doc list --tag api --plain\n```\n\n---\n\n## time\n\n```bash\nknowns time start <id> # REQUIRED when taking task\nknowns time stop # REQUIRED when completing\nknowns time status\nknowns time add <id> <duration> -n "Note"\n```\n\n---\n\n## search\n\n```bash\nknowns search "query" --plain\nknowns search "auth" --type task --plain\nknowns search "api" --type doc --plain\n```\n\n---\n\n## template\n\n```bash\nknowns template list\nknowns template info <name>\nknowns template run <name> --name "X" --dry-run\nknowns template create <name>\n```\n\n---\n\n## Multi-line Input\n\n```bash\nknowns task edit <id> --plan $\'1. Step\\n2. Step\\n3. Step\'\n```\n{{/if}}\n';
|
|
58830
58830
|
|
|
58831
58831
|
// src/instructions/guidelines/unified/common-mistakes.md
|
|
58832
|
-
var common_mistakes_default = '# Common Mistakes\n\n{{#unless mcp}}\n## CRITICAL: The -a Flag\n\n| Command | `-a` Means | NOT This! |\n|---------|------------|-----------|\n| `task create/edit` | `--assignee` | ~~acceptance criteria~~ |\n| `doc edit` | `--append` | ~~assignee~~ |\n\n```bash\n# WRONG (sets assignee to garbage!)\nknowns task edit 35 -a "Criterion text"\n\n# CORRECT (use --ac)\nknowns task edit 35 --ac "Criterion text"\n```\n\n---\n{{/unless}}\n\n## Quick Reference\n\n| DON\'T | DO |\n|-------|-----|\n{{#if mcp}}\n| Edit .md files directly | Use MCP tools |\n{{else}}\n| Edit .md files directly | Use CLI commands |\n| `-a "criterion"` | `--ac "criterion"` |\n| `--parent task-48` | `--parent 48` (raw ID) |\n| `--plain` with create/edit | `--plain` only for view/list |\n{{/if}}\n| Check AC before work done | Check AC AFTER work done |\n| Code before plan approval | Wait for user approval |\n| Code before reading docs | Read docs FIRST |\n| Skip time tracking | Always start/stop timer |\n| Ignore refs | Follow ALL `@task-xxx`, `@doc/xxx`, `@template/xxx` refs |\n\n{{#if mcp}}\n---\n\n## MCP Task Operations\n\nAll task operations are available via MCP:\n\n| Operation | MCP Field |\n|-----------|-----------|\n| Add acceptance criteria | `addAc: ["criterion"]` |\n| Check AC | `checkAc: [1, 2]` (1-based) |\n| Uncheck AC | `uncheckAc: [1]` (1-based) |\n| Remove AC | `removeAc: [1]` (1-based) |\n| Set plan | `plan: "..."` |\n| Set notes | `notes: "..."` |\n| Append notes | `appendNotes: "..."` |\n| Change status | `status: "in-progress"` |\n| Assign | `assignee: "@me"` |\n{{/if}}\n\n---\n\n## Error Recovery\n\n| Problem | Solution |\n|---------|----------|\n{{#if mcp}}\n| Forgot to stop timer | `mcp__knowns__add_time` with duration |\n| Wrong status | `mcp__knowns__update_task` to fix |\n| Task not found | `mcp__knowns__list_tasks` to find ID |\n| Need to uncheck AC | `mcp__knowns__update_task` with `uncheckAc: [N]` |\n| Checked AC too early | `mcp__knowns__update_task` with `uncheckAc: [N]` |\n{{else}}\n| Set assignee to AC text | `knowns task edit <id> -a @me` |\n| Forgot to stop timer | `knowns time add <id> <duration>` |\n| Checked AC too early | `knowns task edit <id> --uncheck-ac N` |\n| Task not found | `knowns task list --plain` |\n{{/if}}\n';
|
|
58832
|
+
var common_mistakes_default = '# Common Mistakes\n\n{{#unless mcp}}\n## CRITICAL: The -a Flag\n\n| Command | `-a` Means | NOT This! |\n|---------|------------|-----------|\n| `task create/edit` | `--assignee` | ~~acceptance criteria~~ |\n| `doc edit` | `--append` | ~~assignee~~ |\n\n```bash\n# WRONG (sets assignee to garbage!)\nknowns task edit 35 -a "Criterion text"\n\n# CORRECT (use --ac)\nknowns task edit 35 --ac "Criterion text"\n```\n\n---\n{{/unless}}\n\n## Quick Reference\n\n| DON\'T | DO |\n|-------|-----|\n{{#if mcp}}\n| Edit .md files directly | Use MCP tools |\n{{else}}\n| Edit .md files directly | Use CLI commands |\n| `-a "criterion"` | `--ac "criterion"` |\n| `--parent task-48` | `--parent 48` (raw ID) |\n| `--plain` with create/edit | `--plain` only for view/list |\n{{/if}}\n| Check AC before work done | Check AC AFTER work done |\n| Code before plan approval | Wait for user approval |\n| Code before reading docs | Read docs FIRST |\n| Skip time tracking | Always start/stop timer |\n| Ignore refs | Follow ALL `@task-xxx`, `@doc/xxx`, `@template/xxx` refs |\n\n{{#if mcp}}\n---\n\n## MCP Task Operations\n\nAll task operations are available via MCP:\n\n| Operation | MCP Field |\n|-----------|-----------|\n| Add acceptance criteria | `addAc: ["criterion"]` |\n| Check AC | `checkAc: [1, 2]` (1-based) |\n| Uncheck AC | `uncheckAc: [1]` (1-based) |\n| Remove AC | `removeAc: [1]` (1-based) |\n| Set plan | `plan: "..."` |\n| Set notes | `notes: "..."` |\n| Append notes | `appendNotes: "..."` |\n| Change status | `status: "in-progress"` |\n| Assign | `assignee: "@me"` |\n{{/if}}\n\n---\n\n## Template Syntax Pitfalls\n\nWhen writing `.hbs` templates, **NEVER** create `$` followed by triple-brace - Handlebars interprets triple-brace as unescaped output:\n\n```\n// \u274C WRONG - Parse error!\nthis.logger.log(`Created: $` + `{` + `{` + `{camelCase entity}.id}`);\n\n// \u2705 CORRECT - Add space between ${ and double-brace, use ~ to trim whitespace\nthis.logger.log(`Created: ${ \\{{~camelCase entity~}}.id}`);\n```\n\n| DON\'T | DO |\n|-------|-----|\n| `$` + triple-brace | `${ \\{{~helper~}}}` (space + escaped) |\n\n**Rules:**\n- Add space between `${` and double-brace\n- Use `~` (tilde) to trim whitespace in output\n- Escape literal braces with backslash\n\n---\n\n## Error Recovery\n\n| Problem | Solution |\n|---------|----------|\n{{#if mcp}}\n| Forgot to stop timer | `mcp__knowns__add_time` with duration |\n| Wrong status | `mcp__knowns__update_task` to fix |\n| Task not found | `mcp__knowns__list_tasks` to find ID |\n| Need to uncheck AC | `mcp__knowns__update_task` with `uncheckAc: [N]` |\n| Checked AC too early | `mcp__knowns__update_task` with `uncheckAc: [N]` |\n{{else}}\n| Set assignee to AC text | `knowns task edit <id> -a @me` |\n| Forgot to stop timer | `knowns time add <id> <duration>` |\n| Checked AC too early | `knowns task edit <id> --uncheck-ac N` |\n| Task not found | `knowns task list --plain` |\n{{/if}}\n';
|
|
58833
58833
|
|
|
58834
58834
|
// src/instructions/guidelines/unified/context-optimization.md
|
|
58835
58835
|
var context_optimization_default = '# Context Optimization\n\nOptimize your context usage to work more efficiently within token limits.\n\n---\n\n{{#unless mcp}}\n## Output Format\n\n```bash\n# Verbose output\nknowns task 42 --json\n\n# Compact output (always use --plain)\nknowns task 42 --plain\n```\n\n---\n{{/unless}}\n\n## Search Before Read\n\n{{#if mcp}}\n```json\n// DON\'T: Read all docs hoping to find info\nmcp__knowns__get_doc({ "path": "doc1" })\nmcp__knowns__get_doc({ "path": "doc2" })\n\n// DO: Search first, then read only relevant docs\nmcp__knowns__search_docs({ "query": "authentication" })\nmcp__knowns__get_doc({ "path": "security-patterns" })\n```\n{{else}}\n```bash\n# DON\'T: Read all docs hoping to find info\nknowns doc "doc1" --plain\nknowns doc "doc2" --plain\n\n# DO: Search first, then read only relevant docs\nknowns search "authentication" --type doc --plain\nknowns doc "security-patterns" --plain\n```\n{{/if}}\n\n---\n\n{{#if mcp}}\n## Use Filters\n\n```json\n// DON\'T: List all then filter manually\nmcp__knowns__list_tasks({})\n\n// DO: Use filters in the query\nmcp__knowns__list_tasks({\n "status": "in-progress",\n "assignee": "@me"\n})\n```\n\n---\n{{/if}}\n\n## Reading Documents\n\n{{#if mcp}}\n**ALWAYS use `smart: true`** - auto-handles both small and large docs:\n\n```json\n// DON\'T: Read without smart\nmcp__knowns__get_doc({ "path": "readme" })\n\n// DO: Always use smart\nmcp__knowns__get_doc({ "path": "readme", "smart": true })\n// Small doc \u2192 full content\n// Large doc \u2192 stats + TOC\n\n// If large, read specific section:\nmcp__knowns__get_doc({ "path": "readme", "section": "3" })\n```\n{{else}}\n**ALWAYS use `--smart`** - auto-handles both small and large docs:\n\n```bash\n# DON\'T: Read without --smart\nknowns doc readme --plain\n\n# DO: Always use --smart\nknowns doc readme --plain --smart\n# Small doc \u2192 full content\n# Large doc \u2192 stats + TOC\n\n# If large, read specific section:\nknowns doc readme --plain --section 3\n```\n{{/if}}\n\n**Behavior:**\n- **\u22642000 tokens**: Returns full content automatically\n- **>2000 tokens**: Returns stats + TOC, then use section parameter\n\n---\n\n## Compact Notes\n\n```bash\n# DON\'T: Verbose notes\nknowns task edit 42 --append-notes "I have successfully completed the implementation..."\n\n# DO: Compact notes\nknowns task edit 42 --append-notes "Done: Auth middleware + JWT validation"\n```\n\n---\n\n## Avoid Redundant Operations\n\n| Don\'t | Do Instead |\n|-------|------------|\n| Re-read files already in context | Reference from memory |\n| List tasks/docs multiple times | List once, remember results |\n| Quote entire file contents | Summarize key points |\n\n---\n\n## Efficient Workflow\n\n| Phase | Context-Efficient Approach |\n|-------|---------------------------|\n| **Research** | Search \u2192 Read only matches |\n| **Planning** | Brief plan, not detailed prose |\n| **Coding** | Read only files being modified |\n| **Notes** | Bullet points, not paragraphs |\n| **Completion** | Summary, not full log |\n\n---\n\n## Quick Rules\n\n{{#if mcp}}\n1. **Always `smart: true`** - Auto-handles doc size\n{{else}}\n1. **Always `--plain`** - Never use `--json` unless needed\n2. **Always `--smart`** - Auto-handles doc size\n{{/if}}\n3. **Search first** - Don\'t read all docs hoping to find info\n4. **Read selectively** - Only fetch what you need\n5. **Write concise** - Compact notes, not essays\n6. **Don\'t repeat** - Reference context already loaded\n';
|
|
@@ -59011,7 +59011,7 @@ var SKILL_default9 = '---\nname: knowns.task.reopen\ndescription: Use when reope
|
|
|
59011
59011
|
var SKILL_default10 = '---\nname: knowns.task\ndescription: Use when working on a Knowns task - view task details and decide next action\n---\n\n# Working on a Task\n\nView task details and determine the appropriate next action.\n\n**Announce at start:** "I\'m using the knowns.task skill to view task [ID]."\n\n**Core principle:** VIEW AND ROUTE - analyze state, suggest next skill.\n\n## The Process\n\n### Step 1: View Task\n\n{{#if mcp}}\n```json\nmcp__knowns__get_task({ "taskId": "$ARGUMENTS" })\n```\n{{else}}\n```bash\nknowns task $ARGUMENTS --plain\n```\n{{/if}}\n\n### Step 2: Analyze State\n\nCheck:\n- **Status**: todo, in-progress, done?\n- **Assignee**: Assigned to someone?\n- **AC**: Any checked? All checked?\n- **Plan**: Has implementation plan?\n- **Refs**: Any `@doc/` or `@task-` references?\n\n### Step 3: Suggest Next Action\n\nBased on task state, recommend the appropriate skill:\n\n| State | Next Skill |\n|-------|------------|\n| `todo`, not started | `knowns.task.plan` |\n| `in-progress`, no plan | `knowns.task.plan` |\n| `in-progress`, has plan | `knowns.task.implement` |\n| `done`, needs changes | `knowns.task.reopen` |\n| Requirements unclear | `knowns.task.brainstorm` |\n\n### Step 4: Follow Refs (if needed)\n\nIf task has references, follow them for context:\n\n{{#if mcp}}\n```json\n// Doc ref: @doc/path \u2192\nmcp__knowns__get_doc({ "path": "<path>", "smart": true })\n\n// Task ref: @task-<id> \u2192\nmcp__knowns__get_task({ "taskId": "<id>" })\n```\n{{else}}\n```bash\n# Doc ref: @doc/path \u2192\nknowns doc "<path>" --plain\n\n# Task ref: @task-<id> \u2192\nknowns task <id> --plain\n```\n{{/if}}\n\n## Quick Actions\n\n**Start planning (includes taking ownership):**\n```\n/knowns.task.plan $ARGUMENTS\n```\n\n**Continue implementing:**\n```\n/knowns.task.implement $ARGUMENTS\n```\n\n**Requirements unclear:**\n```\n/knowns.task.brainstorm $ARGUMENTS\n```\n\n**Reopen completed task:**\n```\n/knowns.task.reopen $ARGUMENTS\n```\n\n## Remember\n\n- This skill is for viewing and routing\n- Use `plan` to start a new task (takes ownership, starts timer)\n- Use `implement` to continue/complete in-progress tasks\n- Always follow refs for full context\n';
|
|
59012
59012
|
|
|
59013
59013
|
// src/instructions/skills/knowns.template/SKILL.md
|
|
59014
|
-
var SKILL_default11 = '---\nname: knowns.template\ndescription: Use when generating code from templates - list, run, or create templates\n---\n\n# Working with Templates\n\nGenerate code from predefined templates stored in `.knowns/templates/`.\n\n**Announce at start:** "I\'m using the knowns.template skill to work with templates."\n\n**Core principle:** USE TEMPLATES FOR CONSISTENT CODE GENERATION.\n\n## The Process\n\n### Step 1: List Available Templates\n\n{{#if mcp}}\n```json\nmcp__knowns__list_templates({})\n```\n{{else}}\n```bash\nknowns template list\n```\n{{/if}}\n\n### Step 2: Get Template Details\n\n{{#if mcp}}\n```json\nmcp__knowns__get_template({ "name": "<template-name>" })\n```\n{{else}}\n```bash\nknowns template info <template-name>\n```\n{{/if}}\n\nCheck:\n- Required variables (prompts)\n- Linked documentation (`doc:`)\n- Files that will be generated\n\n### Step 3: Read Linked Documentation\n\nIf template has a `doc:` field, read it first:\n\n{{#if mcp}}\n```json\nmcp__knowns__get_doc({ "path": "<doc-path>", "smart": true })\n```\n{{else}}\n```bash\nknowns doc "<doc-path>" --plain\n```\n{{/if}}\n\n### Step 4: Run Template\n\n{{#if mcp}}\n```json\n// Dry run first (preview)\nmcp__knowns__run_template({\n "name": "<template-name>",\n "variables": { "name": "MyComponent", "type": "page" },\n "dryRun": true\n})\n\n// Then run for real\nmcp__knowns__run_template({\n "name": "<template-name>",\n "variables": { "name": "MyComponent", "type": "page" },\n "dryRun": false\n})\n```\n{{else}}\n```bash\n# Dry run (preview)\nknowns template run <template-name> --name "MyComponent" --dry-run\n\n# Run for real\nknowns template run <template-name> --name "MyComponent"\n```\n{{/if}}\n\n### Step 5: Create New Template\n\n{{#if mcp}}\n```json\nmcp__knowns__create_template({\n "name": "<template-name>",\n "description": "Template description",\n "doc": "patterns/<related-doc>" // Optional: link to documentation\n})\n```\n{{else}}\n```bash\nknowns template create <template-name>\n```\n{{/if}}\n\nThis creates:\n```\n.knowns/templates/<template-name>/\n \u251C\u2500\u2500 _template.yaml # Config\n \u2514\u2500\u2500 example.ts.hbs # Example file\n```\n\n## Template Config (`_template.yaml`)\n\n```yaml\nname: react-component\ndescription: Create a React component with tests\ndoc: patterns/react-component # Link to documentation\n\nprompts:\n - name: name\n message: Component name?\n validate: required\n\n - name: type\n message: Component type?\n type: select\n choices:\n - page\n - component\n - layout\n\nfiles:\n - template: "{{name}}.tsx.hbs"\n destination: "src/components/{{pascalCase name}}/{{pascalCase name}}.tsx"\n\n - template: "{{name}}.test.tsx.hbs"\n destination: "src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx"\n condition: "{{includeTests}}"\n```\n\n## Template-Doc Linking\n\nTemplates can reference docs and vice versa:\n\n**In `_template.yaml`:**\n```yaml\ndoc: patterns/react-component\n```\n\n**In doc (markdown):**\n```markdown\nUse @template/react-component to generate.\n```\n\n**AI workflow:**\n1. Get template config\n2. Follow `doc:` link to understand patterns\n3. Run template with appropriate variables\n\n## Handlebars Helpers\n\nTemplates use Handlebars with built-in helpers:\n\n| Helper | Example | Output |\n|--------|---------|--------|\n| `camelCase` | `{{camelCase "my name"}}` | `myName` |\n| `pascalCase` | `{{pascalCase "my name"}}` | `MyName` |\n| `kebabCase` | `{{kebabCase "MyName"}}` | `my-name` |\n| `snakeCase` | `{{snakeCase "MyName"}}` | `my_name` |\n| `upperCase` | `{{upperCase "name"}}` | `NAME` |\n| `lowerCase` | `{{lowerCase "NAME"}}` | `name` |\n\n## When to Use Templates\n\n| Scenario | Action |\n|----------|--------|\n| Creating new component | Run `react-component` template |\n| Adding API endpoint | Run `api-endpoint` template |\n| Setting up new feature | Run `feature-module` template |\n| Consistent file structure | Use template instead of copy-paste |\n\n## Integrated Workflows\n\n### During Implementation (Use Template)\n\n```\nTask \u2192 Read Context \u2192 Find Template \u2192 Generate Code \u2192 Customize\n```\n\n1. Read task and understand requirements\n2. List templates to find applicable one\n3. Get template details and read linked doc\n4. Run template (dry run first, then real)\n5. Customize generated code as needed\n6. Continue with remaining implementation\n\n**Benefits:**\n- Reduces context (no need to generate boilerplate)\n- Ensures consistency with project patterns\n- Faster implementation\n\n### During Extract (Create Template)\n\n```\nContext \u2192 Identify Pattern \u2192 Create Doc \u2192 Create Template \u2192 Link Both\n```\n\n1. Identify repeatable code pattern\n2. Create doc with `/knowns.extract`\n3. Create template with `knowns template create <name>`\n4. Link template to doc: `doc: patterns/<name>`\n5. Link doc to template: `@template/<name>`\n\n**When to create template:**\n- Pattern will be used multiple times\n- Has consistent file structure\n- Can be parameterized\n\n## Checklist\n\n- [ ] Listed available templates\n- [ ] Got template details (prompts, files)\n- [ ] Read linked documentation (if any)\n- [ ] Understood required variables\n- [ ] Ran dry run first\n- [ ] Ran template with correct inputs\n- [ ] Verified generated files\n\n## Remember\n\n- Always dry run first before writing files\n- Check `doc:` link in template for context\n- Templates ensure consistent code structure\n- Create new templates for repeated patterns\n';
|
|
59014
|
+
var SKILL_default11 = '---\nname: knowns.template\ndescription: Use when generating code from templates - list, run, or create templates\n---\n\n# Working with Templates\n\nGenerate code from predefined templates stored in `.knowns/templates/`.\n\n**Announce at start:** "I\'m using the knowns.template skill to work with templates."\n\n**Core principle:** USE TEMPLATES FOR CONSISTENT CODE GENERATION.\n\n## The Process\n\n### Step 1: List Available Templates\n\n{{#if mcp}}\n```json\nmcp__knowns__list_templates({})\n```\n{{else}}\n```bash\nknowns template list\n```\n{{/if}}\n\n### Step 2: Get Template Details\n\n{{#if mcp}}\n```json\nmcp__knowns__get_template({ "name": "<template-name>" })\n```\n{{else}}\n```bash\nknowns template info <template-name>\n```\n{{/if}}\n\nCheck:\n- Required variables (prompts)\n- Linked documentation (`doc:`)\n- Files that will be generated\n\n### Step 3: Read Linked Documentation\n\nIf template has a `doc:` field, read it first:\n\n{{#if mcp}}\n```json\nmcp__knowns__get_doc({ "path": "<doc-path>", "smart": true })\n```\n{{else}}\n```bash\nknowns doc "<doc-path>" --plain\n```\n{{/if}}\n\n### Step 4: Run Template\n\n{{#if mcp}}\n```json\n// Dry run first (preview)\nmcp__knowns__run_template({\n "name": "<template-name>",\n "variables": { "name": "MyComponent", "type": "page" },\n "dryRun": true\n})\n\n// Then run for real\nmcp__knowns__run_template({\n "name": "<template-name>",\n "variables": { "name": "MyComponent", "type": "page" },\n "dryRun": false\n})\n```\n{{else}}\n```bash\n# Dry run (preview)\nknowns template run <template-name> --name "MyComponent" --dry-run\n\n# Run for real\nknowns template run <template-name> --name "MyComponent"\n```\n{{/if}}\n\n### Step 5: Create New Template\n\n{{#if mcp}}\n```json\nmcp__knowns__create_template({\n "name": "<template-name>",\n "description": "Template description",\n "doc": "patterns/<related-doc>" // Optional: link to documentation\n})\n```\n{{else}}\n```bash\nknowns template create <template-name>\n```\n{{/if}}\n\nThis creates:\n```\n.knowns/templates/<template-name>/\n \u251C\u2500\u2500 _template.yaml # Config\n \u2514\u2500\u2500 example.ts.hbs # Example file\n```\n\n## Template Config (`_template.yaml`)\n\n```yaml\nname: react-component\ndescription: Create a React component with tests\ndoc: patterns/react-component # Link to documentation\n\nprompts:\n - name: name\n message: Component name?\n validate: required\n\n - name: type\n message: Component type?\n type: select\n choices:\n - page\n - component\n - layout\n\nfiles:\n - template: "{{name}}.tsx.hbs"\n destination: "src/components/{{pascalCase name}}/{{pascalCase name}}.tsx"\n\n - template: "{{name}}.test.tsx.hbs"\n destination: "src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx"\n condition: "{{includeTests}}"\n```\n\n## Template-Doc Linking\n\nTemplates can reference docs and vice versa:\n\n**In `_template.yaml`:**\n```yaml\ndoc: patterns/react-component\n```\n\n**In doc (markdown):**\n```markdown\nUse @template/react-component to generate.\n```\n\n**AI workflow:**\n1. Get template config\n2. Follow `doc:` link to understand patterns\n3. Run template with appropriate variables\n\n## Handlebars Helpers\n\nTemplates use Handlebars with built-in helpers:\n\n| Helper | Example | Output |\n|--------|---------|--------|\n| `camelCase` | `{{camelCase "my name"}}` | `myName` |\n| `pascalCase` | `{{pascalCase "my name"}}` | `MyName` |\n| `kebabCase` | `{{kebabCase "MyName"}}` | `my-name` |\n| `snakeCase` | `{{snakeCase "MyName"}}` | `my_name` |\n| `upperCase` | `{{upperCase "name"}}` | `NAME` |\n| `lowerCase` | `{{lowerCase "NAME"}}` | `name` |\n\n## CRITICAL: Template Syntax Pitfalls\n\n### JavaScript Template Literals + Handlebars\n\n**NEVER write `$` followed by triple-brace** - Handlebars interprets triple-brace as unescaped output:\n\n```\n// \u274C WRONG - Parse error!\nthis.logger.log(`Created: $` + `\\{{\\{camelCase entity}.id}`);\n\n// \u2705 CORRECT - Add space, use ~ to trim whitespace\nthis.logger.log(`Created: ${ \\{{~camelCase entity~}}.id}`);\n// Output: this.logger.log(`Created: ${product.id}`);\n```\n\n**Rules when writing .hbs templates:**\n1. Never `$` + triple-brace - always add space: `${ \\{{`\n2. Use `~` (tilde) to trim whitespace: `\\{{~helper~}}`\n3. For literal braces, escape with backslash\n\n## When to Use Templates\n\n| Scenario | Action |\n|----------|--------|\n| Creating new component | Run `react-component` template |\n| Adding API endpoint | Run `api-endpoint` template |\n| Setting up new feature | Run `feature-module` template |\n| Consistent file structure | Use template instead of copy-paste |\n\n## Integrated Workflows\n\n### During Implementation (Use Template)\n\n```\nTask \u2192 Read Context \u2192 Find Template \u2192 Generate Code \u2192 Customize\n```\n\n1. Read task and understand requirements\n2. List templates to find applicable one\n3. Get template details and read linked doc\n4. Run template (dry run first, then real)\n5. Customize generated code as needed\n6. Continue with remaining implementation\n\n**Benefits:**\n- Reduces context (no need to generate boilerplate)\n- Ensures consistency with project patterns\n- Faster implementation\n\n### During Extract (Create Template)\n\n```\nContext \u2192 Identify Pattern \u2192 Create Doc \u2192 Create Template \u2192 Link Both\n```\n\n1. Identify repeatable code pattern\n2. Create doc with `/knowns.extract`\n3. Create template with `knowns template create <name>`\n4. Link template to doc: `doc: patterns/<name>`\n5. Link doc to template: `@template/<name>`\n\n**When to create template:**\n- Pattern will be used multiple times\n- Has consistent file structure\n- Can be parameterized\n\n## Checklist\n\n- [ ] Listed available templates\n- [ ] Got template details (prompts, files)\n- [ ] Read linked documentation (if any)\n- [ ] Understood required variables\n- [ ] Ran dry run first\n- [ ] Ran template with correct inputs\n- [ ] Verified generated files\n\n## Remember\n\n- Always dry run first before writing files\n- Check `doc:` link in template for context\n- Templates ensure consistent code structure\n- Create new templates for repeated patterns\n- **NEVER write `$` + triple-brace** - use `${ \\{{~helper~}}` instead (add space, use tilde)\n';
|
|
59015
59015
|
|
|
59016
59016
|
// src/instructions/skills/index.ts
|
|
59017
59017
|
function parseSkillFrontmatter(content) {
|
|
@@ -61849,22 +61849,25 @@ var searchCommand = new Command("search").description("Search tasks and document
|
|
|
61849
61849
|
const pathGroups = {};
|
|
61850
61850
|
for (const doc of docResults) {
|
|
61851
61851
|
const parts = doc.filename.split("/");
|
|
61852
|
-
const folder = parts.length > 1 ? `${parts.slice(0, -1).join("/")}/` : "
|
|
61852
|
+
const folder = parts.length > 1 ? `${parts.slice(0, -1).join("/")}/` : "";
|
|
61853
61853
|
if (!pathGroups[folder]) {
|
|
61854
61854
|
pathGroups[folder] = [];
|
|
61855
61855
|
}
|
|
61856
61856
|
pathGroups[folder].push(doc);
|
|
61857
61857
|
}
|
|
61858
61858
|
const sortedPaths = Object.keys(pathGroups).sort((a, b) => {
|
|
61859
|
-
if (a === "
|
|
61860
|
-
if (b === "
|
|
61859
|
+
if (a === "") return -1;
|
|
61860
|
+
if (b === "") return 1;
|
|
61861
61861
|
return a.localeCompare(b);
|
|
61862
61862
|
});
|
|
61863
61863
|
for (const path3 of sortedPaths) {
|
|
61864
|
-
|
|
61864
|
+
if (path3) {
|
|
61865
|
+
console.log(` ${path3}:`);
|
|
61866
|
+
}
|
|
61867
|
+
const indent = path3 ? " " : " ";
|
|
61865
61868
|
for (const doc of pathGroups[path3]) {
|
|
61866
61869
|
const filename = doc.filename.split("/").pop() || doc.filename;
|
|
61867
|
-
console.log(
|
|
61870
|
+
console.log(`${indent}${filename} - ${doc.metadata.title}`);
|
|
61868
61871
|
}
|
|
61869
61872
|
}
|
|
61870
61873
|
}
|
|
@@ -62482,6 +62485,7 @@ function createConfigRoutes(ctx) {
|
|
|
62482
62485
|
name: data.name,
|
|
62483
62486
|
id: data.id,
|
|
62484
62487
|
createdAt: data.createdAt,
|
|
62488
|
+
imports: data.imports,
|
|
62485
62489
|
...settings
|
|
62486
62490
|
};
|
|
62487
62491
|
if (!config2.visibleColumns) {
|
|
@@ -62501,10 +62505,13 @@ function createConfigRoutes(ctx) {
|
|
|
62501
62505
|
const content = await readFile7(configPath, "utf-8");
|
|
62502
62506
|
existingData = JSON.parse(content);
|
|
62503
62507
|
}
|
|
62504
|
-
const { name, ...settings } = config2;
|
|
62508
|
+
const { name, imports, id, createdAt, ...settings } = config2;
|
|
62505
62509
|
const merged = {
|
|
62506
62510
|
...existingData,
|
|
62507
62511
|
name: name || existingData.name,
|
|
62512
|
+
id: id || existingData.id,
|
|
62513
|
+
createdAt: createdAt || existingData.createdAt,
|
|
62514
|
+
imports: imports !== void 0 ? imports : existingData.imports,
|
|
62508
62515
|
settings
|
|
62509
62516
|
};
|
|
62510
62517
|
await writeFile4(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
@@ -63966,7 +63973,7 @@ async function importSource(projectRoot, source, options2 = {}) {
|
|
|
63966
63973
|
if (!nameValidation.valid) {
|
|
63967
63974
|
throw new ImportError(nameValidation.error || "Invalid name", "NAME_CONFLICT" /* NAME_CONFLICT */);
|
|
63968
63975
|
}
|
|
63969
|
-
if (!options2.force && await importExists(projectRoot, name)) {
|
|
63976
|
+
if (!options2.force && !options2.isSync && await importExists(projectRoot, name)) {
|
|
63970
63977
|
throw new ImportError(
|
|
63971
63978
|
`Import "${name}" already exists`,
|
|
63972
63979
|
"NAME_CONFLICT" /* NAME_CONFLICT */,
|
|
@@ -64144,8 +64151,10 @@ async function syncImport(projectRoot, name, options2 = {}) {
|
|
|
64144
64151
|
exclude: config2.exclude,
|
|
64145
64152
|
force: options2.force,
|
|
64146
64153
|
dryRun: options2.dryRun,
|
|
64147
|
-
noSave: true
|
|
64154
|
+
noSave: true,
|
|
64148
64155
|
// Don't update config on sync
|
|
64156
|
+
isSync: true
|
|
64157
|
+
// Bypass "already exists" check
|
|
64149
64158
|
});
|
|
64150
64159
|
}
|
|
64151
64160
|
async function syncAllImports(projectRoot, options2 = {}) {
|
|
@@ -64153,7 +64162,7 @@ async function syncAllImports(projectRoot, options2 = {}) {
|
|
|
64153
64162
|
const configs = await getImportConfigs2(projectRoot);
|
|
64154
64163
|
const results = [];
|
|
64155
64164
|
for (const config2 of configs) {
|
|
64156
|
-
if (config2.autoSync === false
|
|
64165
|
+
if (options2.autoOnly && config2.autoSync === false) {
|
|
64157
64166
|
continue;
|
|
64158
64167
|
}
|
|
64159
64168
|
try {
|
|
@@ -64197,7 +64206,38 @@ import { existsSync as existsSync14 } from "node:fs";
|
|
|
64197
64206
|
import { readdir as readdir7 } from "node:fs/promises";
|
|
64198
64207
|
import { join as join17 } from "node:path";
|
|
64199
64208
|
var KNOWNS_DIR5 = ".knowns";
|
|
64209
|
+
var TEMPLATES_DIR2 = "templates";
|
|
64200
64210
|
var DOCS_DIR2 = "docs";
|
|
64211
|
+
async function getTemplateDirectories(projectRoot) {
|
|
64212
|
+
const results = [];
|
|
64213
|
+
const localTemplates = join17(projectRoot, KNOWNS_DIR5, TEMPLATES_DIR2);
|
|
64214
|
+
if (existsSync14(localTemplates)) {
|
|
64215
|
+
results.push({
|
|
64216
|
+
path: localTemplates,
|
|
64217
|
+
source: "local",
|
|
64218
|
+
isImported: false
|
|
64219
|
+
});
|
|
64220
|
+
}
|
|
64221
|
+
const importsDir = getImportsDir(projectRoot);
|
|
64222
|
+
if (existsSync14(importsDir)) {
|
|
64223
|
+
try {
|
|
64224
|
+
const entries = await readdir7(importsDir, { withFileTypes: true });
|
|
64225
|
+
for (const entry of entries) {
|
|
64226
|
+
if (!entry.isDirectory()) continue;
|
|
64227
|
+
const importedTemplates = join17(importsDir, entry.name, TEMPLATES_DIR2);
|
|
64228
|
+
if (existsSync14(importedTemplates)) {
|
|
64229
|
+
results.push({
|
|
64230
|
+
path: importedTemplates,
|
|
64231
|
+
source: entry.name,
|
|
64232
|
+
isImported: true
|
|
64233
|
+
});
|
|
64234
|
+
}
|
|
64235
|
+
}
|
|
64236
|
+
} catch {
|
|
64237
|
+
}
|
|
64238
|
+
}
|
|
64239
|
+
return results;
|
|
64240
|
+
}
|
|
64201
64241
|
async function getDocDirectories(projectRoot) {
|
|
64202
64242
|
const results = [];
|
|
64203
64243
|
const localDocs = join17(projectRoot, KNOWNS_DIR5, DOCS_DIR2);
|
|
@@ -64300,8 +64340,36 @@ async function resolveDocWithContext(projectRoot, docPath, context) {
|
|
|
64300
64340
|
}
|
|
64301
64341
|
return null;
|
|
64302
64342
|
}
|
|
64343
|
+
async function listAllTemplates(projectRoot) {
|
|
64344
|
+
const directories = await getTemplateDirectories(projectRoot);
|
|
64345
|
+
const importConfigs = await getImportConfigs(projectRoot);
|
|
64346
|
+
const sourceUrlMap = new Map(importConfigs.map((c) => [c.name, c.source]));
|
|
64347
|
+
const results = [];
|
|
64348
|
+
for (const dir of directories) {
|
|
64349
|
+
try {
|
|
64350
|
+
const entries = await readdir7(dir.path, { withFileTypes: true });
|
|
64351
|
+
for (const entry of entries) {
|
|
64352
|
+
if (!entry.isDirectory()) continue;
|
|
64353
|
+
if (entry.name.startsWith(".")) continue;
|
|
64354
|
+
const ref = dir.isImported ? `${dir.source}/${entry.name}` : entry.name;
|
|
64355
|
+
results.push({
|
|
64356
|
+
name: entry.name,
|
|
64357
|
+
ref,
|
|
64358
|
+
source: dir.source,
|
|
64359
|
+
sourceUrl: dir.isImported ? sourceUrlMap.get(dir.source) : void 0,
|
|
64360
|
+
path: join17(dir.path, entry.name),
|
|
64361
|
+
isImported: dir.isImported
|
|
64362
|
+
});
|
|
64363
|
+
}
|
|
64364
|
+
} catch {
|
|
64365
|
+
}
|
|
64366
|
+
}
|
|
64367
|
+
return results.sort((a, b) => a.ref.localeCompare(b.ref));
|
|
64368
|
+
}
|
|
64303
64369
|
async function listAllDocs(projectRoot) {
|
|
64304
64370
|
const directories = await getDocDirectories(projectRoot);
|
|
64371
|
+
const importConfigs = await getImportConfigs(projectRoot);
|
|
64372
|
+
const sourceUrlMap = new Map(importConfigs.map((c) => [c.name, c.source]));
|
|
64305
64373
|
const results = [];
|
|
64306
64374
|
async function scanDir(baseDir, relativePath, source, isImported) {
|
|
64307
64375
|
const currentDir = join17(baseDir, relativePath);
|
|
@@ -64319,6 +64387,7 @@ async function listAllDocs(projectRoot) {
|
|
|
64319
64387
|
name: docPath,
|
|
64320
64388
|
ref,
|
|
64321
64389
|
source,
|
|
64390
|
+
sourceUrl: isImported ? sourceUrlMap.get(source) : void 0,
|
|
64322
64391
|
fullPath: join17(currentDir, entry.name),
|
|
64323
64392
|
isImported
|
|
64324
64393
|
});
|
|
@@ -87092,131 +87161,190 @@ var createCommand3 = new Command("create").description("Create a new documentati
|
|
|
87092
87161
|
}
|
|
87093
87162
|
}
|
|
87094
87163
|
);
|
|
87095
|
-
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").
|
|
87096
|
-
|
|
87097
|
-
|
|
87098
|
-
|
|
87099
|
-
|
|
87100
|
-
|
|
87101
|
-
|
|
87102
|
-
|
|
87103
|
-
|
|
87104
|
-
console.log(source_default.gray(`Create one with: knowns doc create "Title"`));
|
|
87105
|
-
}
|
|
87106
|
-
return;
|
|
87107
|
-
}
|
|
87108
|
-
const docs = [];
|
|
87109
|
-
for (const file3 of mdFiles) {
|
|
87110
|
-
const fileContent = await readFile18(join29(DOCS_DIR3, file3), "utf-8");
|
|
87111
|
-
const { data, content } = (0, import_gray_matter7.default)(fileContent);
|
|
87112
|
-
const stats = calculateDocStats(content);
|
|
87113
|
-
docs.push({
|
|
87114
|
-
filename: file3,
|
|
87115
|
-
metadata: data,
|
|
87116
|
-
tokens: stats.estimatedTokens
|
|
87117
|
-
});
|
|
87118
|
-
}
|
|
87119
|
-
let filteredDocs = docs;
|
|
87120
|
-
if (path3) {
|
|
87121
|
-
const normalizedPath = path3.endsWith("/") ? path3 : `${path3}/`;
|
|
87122
|
-
filteredDocs = docs.filter((doc) => doc.filename.startsWith(normalizedPath));
|
|
87123
|
-
}
|
|
87124
|
-
if (options2.tag) {
|
|
87125
|
-
filteredDocs = filteredDocs.filter((doc) => doc.metadata.tags?.includes(options2.tag));
|
|
87126
|
-
}
|
|
87127
|
-
if (filteredDocs.length === 0) {
|
|
87128
|
-
const filterMsg = path3 ? `path: ${path3}` : options2.tag ? `tag: ${options2.tag}` : "";
|
|
87129
|
-
console.log(
|
|
87130
|
-
options2.plain ? "No documentation found" : source_default.yellow(`No documentation found${filterMsg ? ` with ${filterMsg}` : ""}`)
|
|
87131
|
-
);
|
|
87132
|
-
return;
|
|
87133
|
-
}
|
|
87134
|
-
if (options2.plain) {
|
|
87135
|
-
const folders = /* @__PURE__ */ new Map();
|
|
87136
|
-
const rootDocs = [];
|
|
87137
|
-
for (const doc of filteredDocs) {
|
|
87138
|
-
const parts = doc.filename.split("/");
|
|
87139
|
-
if (parts.length === 1) {
|
|
87140
|
-
rootDocs.push({
|
|
87141
|
-
name: parts[0].replace(/\.md$/, ""),
|
|
87142
|
-
title: doc.metadata.title,
|
|
87143
|
-
tokens: doc.tokens
|
|
87144
|
-
});
|
|
87164
|
+
var listCommand2 = new Command("list").description("List all documentation files (local + imported)").argument("[path]", "Filter by path (e.g., 'guides/' or 'patterns/')").option("--plain", "Plain text output for AI").option("-t, --tag <tag>", "Filter by tag").option("--local", "Show only local docs").option("--imported", "Show only imported docs").action(
|
|
87165
|
+
async (path3, options2) => {
|
|
87166
|
+
try {
|
|
87167
|
+
await ensureDocsDir();
|
|
87168
|
+
const projectRoot = await findProjectRoot() || process.cwd();
|
|
87169
|
+
const allDocs = await listAllDocs(projectRoot);
|
|
87170
|
+
if (allDocs.length === 0) {
|
|
87171
|
+
if (options2.plain) {
|
|
87172
|
+
console.log("No documentation found");
|
|
87145
87173
|
} else {
|
|
87146
|
-
|
|
87147
|
-
|
|
87148
|
-
if (!folders.has(folder)) {
|
|
87149
|
-
folders.set(folder, []);
|
|
87150
|
-
}
|
|
87151
|
-
folders.get(folder)?.push({ name, title: doc.metadata.title, tokens: doc.tokens });
|
|
87174
|
+
console.log(source_default.yellow("No documentation files found."));
|
|
87175
|
+
console.log(source_default.gray(`Create one with: knowns doc create "Title"`));
|
|
87152
87176
|
}
|
|
87177
|
+
return;
|
|
87153
87178
|
}
|
|
87154
|
-
const
|
|
87155
|
-
for (const
|
|
87156
|
-
|
|
87157
|
-
|
|
87158
|
-
|
|
87159
|
-
|
|
87179
|
+
const docs = [];
|
|
87180
|
+
for (const doc of allDocs) {
|
|
87181
|
+
try {
|
|
87182
|
+
const fileContent = await readFile18(doc.fullPath, "utf-8");
|
|
87183
|
+
const { data, content } = (0, import_gray_matter7.default)(fileContent);
|
|
87184
|
+
const stats = calculateDocStats(content);
|
|
87185
|
+
docs.push({
|
|
87186
|
+
ref: doc.ref,
|
|
87187
|
+
name: doc.name,
|
|
87188
|
+
source: doc.source,
|
|
87189
|
+
sourceUrl: doc.sourceUrl,
|
|
87190
|
+
isImported: doc.isImported,
|
|
87191
|
+
fullPath: doc.fullPath,
|
|
87192
|
+
metadata: data,
|
|
87193
|
+
tokens: stats.estimatedTokens
|
|
87194
|
+
});
|
|
87195
|
+
} catch {
|
|
87160
87196
|
}
|
|
87161
87197
|
}
|
|
87162
|
-
|
|
87163
|
-
|
|
87164
|
-
|
|
87165
|
-
|
|
87166
|
-
|
|
87198
|
+
let filteredDocs = docs;
|
|
87199
|
+
if (options2.local) {
|
|
87200
|
+
filteredDocs = docs.filter((doc) => !doc.isImported);
|
|
87201
|
+
} else if (options2.imported) {
|
|
87202
|
+
filteredDocs = docs.filter((doc) => doc.isImported);
|
|
87167
87203
|
}
|
|
87168
|
-
|
|
87169
|
-
|
|
87170
|
-
|
|
87171
|
-
|
|
87172
|
-
|
|
87173
|
-
|
|
87174
|
-
|
|
87175
|
-
|
|
87176
|
-
|
|
87177
|
-
|
|
87178
|
-
|
|
87204
|
+
if (path3) {
|
|
87205
|
+
const normalizedPath = path3.endsWith("/") ? path3 : `${path3}/`;
|
|
87206
|
+
filteredDocs = filteredDocs.filter(
|
|
87207
|
+
(doc) => doc.ref.startsWith(normalizedPath) || doc.name.startsWith(normalizedPath)
|
|
87208
|
+
);
|
|
87209
|
+
}
|
|
87210
|
+
if (options2.tag) {
|
|
87211
|
+
filteredDocs = filteredDocs.filter((doc) => doc.metadata.tags?.includes(options2.tag));
|
|
87212
|
+
}
|
|
87213
|
+
if (filteredDocs.length === 0) {
|
|
87214
|
+
const filterMsg = path3 ? `path: ${path3}` : options2.tag ? `tag: ${options2.tag}` : "";
|
|
87215
|
+
console.log(
|
|
87216
|
+
options2.plain ? "No documentation found" : source_default.yellow(`No documentation found${filterMsg ? ` with ${filterMsg}` : ""}`)
|
|
87217
|
+
);
|
|
87218
|
+
return;
|
|
87219
|
+
}
|
|
87220
|
+
if (options2.plain) {
|
|
87221
|
+
const sources = /* @__PURE__ */ new Map();
|
|
87222
|
+
for (const doc of filteredDocs) {
|
|
87223
|
+
const sourceKey = doc.isImported ? `[${doc.source}]` : "[local]";
|
|
87224
|
+
if (!sources.has(sourceKey)) {
|
|
87225
|
+
sources.set(sourceKey, []);
|
|
87226
|
+
}
|
|
87227
|
+
sources.get(sourceKey)?.push(doc);
|
|
87228
|
+
}
|
|
87229
|
+
const sortedSources = Array.from(sources.keys()).sort((a, b) => {
|
|
87230
|
+
if (a === "[local]") return -1;
|
|
87231
|
+
if (b === "[local]") return 1;
|
|
87232
|
+
return a.localeCompare(b);
|
|
87233
|
+
});
|
|
87234
|
+
for (const sourceKey of sortedSources) {
|
|
87235
|
+
const sourceDocs = sources.get(sourceKey) || [];
|
|
87236
|
+
const sourceUrl = sourceDocs[0]?.sourceUrl;
|
|
87237
|
+
if (sourceUrl) {
|
|
87238
|
+
console.log(`${sourceKey} (${sourceUrl})`);
|
|
87239
|
+
} else {
|
|
87240
|
+
console.log(`${sourceKey}`);
|
|
87241
|
+
}
|
|
87242
|
+
const folders = /* @__PURE__ */ new Map();
|
|
87243
|
+
const rootDocs = [];
|
|
87244
|
+
for (const doc of sourceDocs) {
|
|
87245
|
+
const parts = doc.name.split("/");
|
|
87246
|
+
if (parts.length === 1) {
|
|
87247
|
+
rootDocs.push({
|
|
87248
|
+
name: parts[0],
|
|
87249
|
+
title: doc.metadata.title || parts[0],
|
|
87250
|
+
tokens: doc.tokens
|
|
87251
|
+
});
|
|
87252
|
+
} else {
|
|
87253
|
+
const folder = parts.slice(0, -1).join("/");
|
|
87254
|
+
const name = parts[parts.length - 1];
|
|
87255
|
+
if (!folders.has(folder)) {
|
|
87256
|
+
folders.set(folder, []);
|
|
87257
|
+
}
|
|
87258
|
+
folders.get(folder)?.push({ name, title: doc.metadata.title || name, tokens: doc.tokens });
|
|
87259
|
+
}
|
|
87260
|
+
}
|
|
87261
|
+
const sortedFolders = Array.from(folders.keys()).sort();
|
|
87262
|
+
for (const folder of sortedFolders) {
|
|
87263
|
+
console.log(` ${folder}/`);
|
|
87264
|
+
const folderDocs = folders.get(folder)?.sort((a, b) => a.name.localeCompare(b.name));
|
|
87265
|
+
for (const doc of folderDocs || []) {
|
|
87266
|
+
console.log(` ${doc.name} - ${doc.title} (~${doc.tokens} tokens)`);
|
|
87267
|
+
}
|
|
87268
|
+
}
|
|
87269
|
+
if (rootDocs.length > 0) {
|
|
87270
|
+
rootDocs.sort((a, b) => a.name.localeCompare(b.name));
|
|
87271
|
+
for (const doc of rootDocs) {
|
|
87272
|
+
console.log(` ${doc.name} - ${doc.title} (~${doc.tokens} tokens)`);
|
|
87273
|
+
}
|
|
87179
87274
|
}
|
|
87180
|
-
folders.get(folder)?.push(doc);
|
|
87181
87275
|
}
|
|
87182
|
-
}
|
|
87183
|
-
|
|
87276
|
+
} else {
|
|
87277
|
+
const sources = /* @__PURE__ */ new Map();
|
|
87278
|
+
for (const doc of filteredDocs) {
|
|
87279
|
+
const sourceKey = doc.isImported ? doc.source : "local";
|
|
87280
|
+
if (!sources.has(sourceKey)) {
|
|
87281
|
+
sources.set(sourceKey, []);
|
|
87282
|
+
}
|
|
87283
|
+
sources.get(sourceKey)?.push(doc);
|
|
87284
|
+
}
|
|
87285
|
+
const sortedSources = Array.from(sources.keys()).sort((a, b) => {
|
|
87286
|
+
if (a === "local") return -1;
|
|
87287
|
+
if (b === "local") return 1;
|
|
87288
|
+
return a.localeCompare(b);
|
|
87289
|
+
});
|
|
87290
|
+
console.log(source_default.bold(`
|
|
87184
87291
|
Documentation (${filteredDocs.length})
|
|
87185
87292
|
`));
|
|
87186
|
-
|
|
87187
|
-
|
|
87188
|
-
|
|
87189
|
-
|
|
87190
|
-
|
|
87191
|
-
|
|
87192
|
-
|
|
87193
|
-
console.log(source_default.
|
|
87194
|
-
}
|
|
87195
|
-
|
|
87196
|
-
|
|
87293
|
+
for (const sourceKey of sortedSources) {
|
|
87294
|
+
const sourceDocs = sources.get(sourceKey) || [];
|
|
87295
|
+
const sourceUrl = sourceDocs[0]?.sourceUrl;
|
|
87296
|
+
const sourceLabel = sourceKey === "local" ? "Local" : `Import: ${sourceKey}`;
|
|
87297
|
+
if (sourceUrl) {
|
|
87298
|
+
console.log(source_default.magenta.bold(`[${sourceLabel}]`) + source_default.gray(` ${sourceUrl}`));
|
|
87299
|
+
} else {
|
|
87300
|
+
console.log(source_default.magenta.bold(`[${sourceLabel}]`));
|
|
87301
|
+
}
|
|
87302
|
+
const folders = /* @__PURE__ */ new Map();
|
|
87303
|
+
const rootDocs = [];
|
|
87304
|
+
for (const doc of sourceDocs) {
|
|
87305
|
+
const parts = doc.name.split("/");
|
|
87306
|
+
if (parts.length === 1) {
|
|
87307
|
+
rootDocs.push(doc);
|
|
87308
|
+
} else {
|
|
87309
|
+
const folder = parts.slice(0, -1).join("/");
|
|
87310
|
+
if (!folders.has(folder)) {
|
|
87311
|
+
folders.set(folder, []);
|
|
87312
|
+
}
|
|
87313
|
+
folders.get(folder)?.push(doc);
|
|
87314
|
+
}
|
|
87197
87315
|
}
|
|
87198
|
-
|
|
87199
|
-
|
|
87200
|
-
|
|
87201
|
-
|
|
87202
|
-
|
|
87203
|
-
|
|
87204
|
-
|
|
87205
|
-
|
|
87206
|
-
|
|
87316
|
+
const sortedFolders = Array.from(folders.keys()).sort();
|
|
87317
|
+
for (const folder of sortedFolders) {
|
|
87318
|
+
console.log(source_default.cyan.bold(` ${folder}/`));
|
|
87319
|
+
const folderDocs = folders.get(folder)?.sort((a, b) => (a.metadata.title || "").localeCompare(b.metadata.title || ""));
|
|
87320
|
+
for (const doc of folderDocs || []) {
|
|
87321
|
+
console.log(source_default.white(` ${doc.metadata.title || doc.name}`));
|
|
87322
|
+
if (doc.metadata.description) {
|
|
87323
|
+
console.log(source_default.gray(` ${doc.metadata.description}`));
|
|
87324
|
+
}
|
|
87325
|
+
}
|
|
87207
87326
|
}
|
|
87208
|
-
if (
|
|
87209
|
-
|
|
87327
|
+
if (rootDocs.length > 0) {
|
|
87328
|
+
rootDocs.sort((a, b) => (a.metadata.title || "").localeCompare(b.metadata.title || ""));
|
|
87329
|
+
for (const doc of rootDocs) {
|
|
87330
|
+
console.log(source_default.white(` ${doc.metadata.title || doc.name}`));
|
|
87331
|
+
if (doc.metadata.description) {
|
|
87332
|
+
console.log(source_default.gray(` ${doc.metadata.description}`));
|
|
87333
|
+
}
|
|
87334
|
+
}
|
|
87210
87335
|
}
|
|
87211
87336
|
console.log();
|
|
87212
87337
|
}
|
|
87213
87338
|
}
|
|
87339
|
+
} catch (error48) {
|
|
87340
|
+
console.error(
|
|
87341
|
+
source_default.red("Error listing documentation:"),
|
|
87342
|
+
error48 instanceof Error ? error48.message : String(error48)
|
|
87343
|
+
);
|
|
87344
|
+
process.exit(1);
|
|
87214
87345
|
}
|
|
87215
|
-
} catch (error48) {
|
|
87216
|
-
console.error(source_default.red("Error listing documentation:"), error48 instanceof Error ? error48.message : String(error48));
|
|
87217
|
-
process.exit(1);
|
|
87218
87346
|
}
|
|
87219
|
-
|
|
87347
|
+
);
|
|
87220
87348
|
var viewCommand2 = new Command("view").description("View a documentation file").argument("<name>", "Document name (filename or title or path)").option("--plain", "Plain text output for AI").option("--info", "Show document stats (size, tokens, headings) without content").option("--toc", "Show table of contents only").option("--section <title>", "Show specific section by heading title or number (e.g., '2. Overview' or '2')").option("--smart", "Smart mode: auto-return full content if small, or stats+TOC if large (>2000 tokens)").action(
|
|
87221
87349
|
async (name, options2) => {
|
|
87222
87350
|
try {
|
|
@@ -96471,8 +96599,9 @@ async function resolveDocPath2(name) {
|
|
|
96471
96599
|
async function handleListDocs(args2) {
|
|
96472
96600
|
const input = listDocsSchema.parse(args2);
|
|
96473
96601
|
await ensureDocsDir2();
|
|
96474
|
-
const
|
|
96475
|
-
|
|
96602
|
+
const projectRoot = process.cwd();
|
|
96603
|
+
const allDocs = await listAllDocs(projectRoot);
|
|
96604
|
+
if (allDocs.length === 0) {
|
|
96476
96605
|
return successResponse({
|
|
96477
96606
|
count: 0,
|
|
96478
96607
|
docs: [],
|
|
@@ -96480,22 +96609,29 @@ async function handleListDocs(args2) {
|
|
|
96480
96609
|
});
|
|
96481
96610
|
}
|
|
96482
96611
|
const docs = [];
|
|
96483
|
-
for (const
|
|
96484
|
-
|
|
96485
|
-
|
|
96486
|
-
|
|
96487
|
-
|
|
96488
|
-
|
|
96489
|
-
|
|
96612
|
+
for (const doc of allDocs) {
|
|
96613
|
+
try {
|
|
96614
|
+
const fileContent = await readFile21(doc.fullPath, "utf-8");
|
|
96615
|
+
const { data, content } = (0, import_gray_matter9.default)(fileContent);
|
|
96616
|
+
const metadata = data;
|
|
96617
|
+
const stats = calculateDocStats(content);
|
|
96618
|
+
if (input.tag && !metadata.tags?.includes(input.tag)) {
|
|
96619
|
+
continue;
|
|
96620
|
+
}
|
|
96621
|
+
docs.push({
|
|
96622
|
+
path: doc.ref,
|
|
96623
|
+
// Use full ref path (includes import prefix if imported)
|
|
96624
|
+
title: metadata.title || doc.name,
|
|
96625
|
+
description: metadata.description,
|
|
96626
|
+
tags: metadata.tags,
|
|
96627
|
+
tokens: stats.estimatedTokens,
|
|
96628
|
+
updatedAt: metadata.updatedAt,
|
|
96629
|
+
source: doc.source,
|
|
96630
|
+
sourceUrl: doc.sourceUrl,
|
|
96631
|
+
isImported: doc.isImported
|
|
96632
|
+
});
|
|
96633
|
+
} catch {
|
|
96490
96634
|
}
|
|
96491
|
-
docs.push({
|
|
96492
|
-
path: file3.replace(/\.md$/, ""),
|
|
96493
|
-
title: metadata.title || file3.replace(/\.md$/, ""),
|
|
96494
|
-
description: metadata.description,
|
|
96495
|
-
tags: metadata.tags,
|
|
96496
|
-
tokens: stats.estimatedTokens,
|
|
96497
|
-
updatedAt: metadata.updatedAt
|
|
96498
|
-
});
|
|
96499
96635
|
}
|
|
96500
96636
|
return successResponse({
|
|
96501
96637
|
count: docs.length,
|
|
@@ -96803,7 +96939,7 @@ async function handleGetGuideline(args2) {
|
|
|
96803
96939
|
import { existsSync as existsSync29 } from "node:fs";
|
|
96804
96940
|
import { mkdir as mkdir18, writeFile as writeFile16 } from "node:fs/promises";
|
|
96805
96941
|
import { join as join34 } from "node:path";
|
|
96806
|
-
var
|
|
96942
|
+
var TEMPLATES_DIR3 = join34(process.cwd(), ".knowns", "templates");
|
|
96807
96943
|
var listTemplatesSchema = external_exports.object({});
|
|
96808
96944
|
var getTemplateSchema = external_exports.object({
|
|
96809
96945
|
name: external_exports.string()
|
|
@@ -96896,30 +97032,45 @@ var templateTools = [
|
|
|
96896
97032
|
];
|
|
96897
97033
|
async function handleListTemplates(_args) {
|
|
96898
97034
|
listTemplatesSchema.parse(_args);
|
|
96899
|
-
|
|
96900
|
-
return successResponse({
|
|
96901
|
-
count: 0,
|
|
96902
|
-
templates: [],
|
|
96903
|
-
message: "No templates directory found. Create templates in .knowns/templates/"
|
|
96904
|
-
});
|
|
96905
|
-
}
|
|
97035
|
+
const projectRoot = process.cwd();
|
|
96906
97036
|
try {
|
|
96907
|
-
const
|
|
96908
|
-
if (
|
|
97037
|
+
const allTemplates = await listAllTemplates(projectRoot);
|
|
97038
|
+
if (allTemplates.length === 0) {
|
|
96909
97039
|
return successResponse({
|
|
96910
97040
|
count: 0,
|
|
96911
97041
|
templates: [],
|
|
96912
|
-
message: "No templates found in .knowns/templates/"
|
|
97042
|
+
message: "No templates found. Create templates in .knowns/templates/"
|
|
96913
97043
|
});
|
|
96914
97044
|
}
|
|
96915
|
-
const templateList =
|
|
96916
|
-
|
|
96917
|
-
|
|
96918
|
-
|
|
96919
|
-
|
|
96920
|
-
|
|
96921
|
-
|
|
96922
|
-
|
|
97045
|
+
const templateList = [];
|
|
97046
|
+
for (const t of allTemplates) {
|
|
97047
|
+
try {
|
|
97048
|
+
const loaded = await listTemplates(join34(t.path, ".."));
|
|
97049
|
+
const match2 = loaded.find((l) => l.name === t.name);
|
|
97050
|
+
templateList.push({
|
|
97051
|
+
name: t.name,
|
|
97052
|
+
ref: t.ref,
|
|
97053
|
+
description: match2?.description || "No description",
|
|
97054
|
+
doc: match2?.doc,
|
|
97055
|
+
promptCount: match2?.prompts?.length || 0,
|
|
97056
|
+
fileCount: match2?.files?.length || 0,
|
|
97057
|
+
source: t.source,
|
|
97058
|
+
sourceUrl: t.sourceUrl,
|
|
97059
|
+
isImported: t.isImported
|
|
97060
|
+
});
|
|
97061
|
+
} catch {
|
|
97062
|
+
templateList.push({
|
|
97063
|
+
name: t.name,
|
|
97064
|
+
ref: t.ref,
|
|
97065
|
+
description: "No description",
|
|
97066
|
+
promptCount: 0,
|
|
97067
|
+
fileCount: 0,
|
|
97068
|
+
source: t.source,
|
|
97069
|
+
sourceUrl: t.sourceUrl,
|
|
97070
|
+
isImported: t.isImported
|
|
97071
|
+
});
|
|
97072
|
+
}
|
|
97073
|
+
}
|
|
96923
97074
|
return successResponse({
|
|
96924
97075
|
count: templateList.length,
|
|
96925
97076
|
templates: templateList
|
|
@@ -96930,27 +97081,37 @@ async function handleListTemplates(_args) {
|
|
|
96930
97081
|
}
|
|
96931
97082
|
async function handleGetTemplate(args2) {
|
|
96932
97083
|
const input = getTemplateSchema.parse(args2);
|
|
96933
|
-
if (!existsSync29(
|
|
97084
|
+
if (!existsSync29(TEMPLATES_DIR3)) {
|
|
96934
97085
|
return errorResponse("No templates directory found");
|
|
96935
97086
|
}
|
|
96936
97087
|
try {
|
|
96937
|
-
const template = await loadTemplateByName(input.name
|
|
97088
|
+
const template = await loadTemplateByName(TEMPLATES_DIR3, input.name);
|
|
96938
97089
|
if (!template) {
|
|
96939
97090
|
return errorResponse(`Template not found: ${input.name}`);
|
|
96940
97091
|
}
|
|
96941
97092
|
const prompts4 = template.config.prompts?.map((p) => ({
|
|
96942
97093
|
name: p.name,
|
|
96943
97094
|
message: p.message,
|
|
96944
|
-
type: p.type || "
|
|
97095
|
+
type: p.type || "text",
|
|
96945
97096
|
required: p.validate === "required",
|
|
96946
|
-
default: p.
|
|
97097
|
+
default: p.initial,
|
|
96947
97098
|
choices: p.choices
|
|
96948
97099
|
}));
|
|
96949
|
-
const
|
|
96950
|
-
|
|
96951
|
-
|
|
96952
|
-
|
|
96953
|
-
|
|
97100
|
+
const actions = template.config.actions?.map((a) => {
|
|
97101
|
+
if (a.type === "add") {
|
|
97102
|
+
return { type: a.type, template: a.template, path: a.path, skipIfExists: a.skipIfExists };
|
|
97103
|
+
}
|
|
97104
|
+
if (a.type === "addMany") {
|
|
97105
|
+
return { type: a.type, source: a.source, destination: a.destination, globPattern: a.globPattern };
|
|
97106
|
+
}
|
|
97107
|
+
if (a.type === "modify") {
|
|
97108
|
+
return { type: a.type, path: a.path, pattern: a.pattern };
|
|
97109
|
+
}
|
|
97110
|
+
if (a.type === "append") {
|
|
97111
|
+
return { type: a.type, path: a.path, unique: a.unique };
|
|
97112
|
+
}
|
|
97113
|
+
return a;
|
|
97114
|
+
});
|
|
96954
97115
|
return successResponse({
|
|
96955
97116
|
template: {
|
|
96956
97117
|
name: template.config.name,
|
|
@@ -96958,7 +97119,7 @@ async function handleGetTemplate(args2) {
|
|
|
96958
97119
|
doc: template.config.doc,
|
|
96959
97120
|
// Linked documentation - AI should read this
|
|
96960
97121
|
prompts: prompts4 || [],
|
|
96961
|
-
|
|
97122
|
+
actions: actions || [],
|
|
96962
97123
|
messages: template.config.messages
|
|
96963
97124
|
},
|
|
96964
97125
|
hint: template.config.doc ? `This template links to @doc/${template.config.doc}. Read the doc for context before running.` : void 0
|
|
@@ -96970,11 +97131,11 @@ async function handleGetTemplate(args2) {
|
|
|
96970
97131
|
async function handleRunTemplate(args2) {
|
|
96971
97132
|
const input = runTemplateSchema.parse(args2);
|
|
96972
97133
|
const dryRun = input.dryRun !== false;
|
|
96973
|
-
if (!existsSync29(
|
|
97134
|
+
if (!existsSync29(TEMPLATES_DIR3)) {
|
|
96974
97135
|
return errorResponse("No templates directory found");
|
|
96975
97136
|
}
|
|
96976
97137
|
try {
|
|
96977
|
-
const template = await loadTemplateByName(input.name
|
|
97138
|
+
const template = await loadTemplateByName(TEMPLATES_DIR3, input.name);
|
|
96978
97139
|
if (!template) {
|
|
96979
97140
|
return errorResponse(`Template not found: ${input.name}`);
|
|
96980
97141
|
}
|
|
@@ -96989,8 +97150,8 @@ async function handleRunTemplate(args2) {
|
|
|
96989
97150
|
for (const prompt of template.config.prompts || []) {
|
|
96990
97151
|
if (input.variables?.[prompt.name]) {
|
|
96991
97152
|
values[prompt.name] = input.variables[prompt.name];
|
|
96992
|
-
} else if (prompt.
|
|
96993
|
-
values[prompt.name] = String(prompt.
|
|
97153
|
+
} else if (prompt.initial !== void 0) {
|
|
97154
|
+
values[prompt.name] = String(prompt.initial);
|
|
96994
97155
|
}
|
|
96995
97156
|
}
|
|
96996
97157
|
const result = await runTemplate(template, {
|
|
@@ -97001,19 +97162,18 @@ async function handleRunTemplate(args2) {
|
|
|
97001
97162
|
if (!result.success) {
|
|
97002
97163
|
return errorResponse(`Template failed: ${result.error}`);
|
|
97003
97164
|
}
|
|
97004
|
-
const
|
|
97005
|
-
|
|
97006
|
-
|
|
97007
|
-
skipped: f.skipped,
|
|
97008
|
-
skipReason: f.skipReason
|
|
97009
|
-
}));
|
|
97165
|
+
const totalCreated = result.created?.length || 0;
|
|
97166
|
+
const totalModified = result.modified?.length || 0;
|
|
97167
|
+
const totalSkipped = result.skipped?.length || 0;
|
|
97010
97168
|
return successResponse({
|
|
97011
97169
|
success: true,
|
|
97012
97170
|
dryRun,
|
|
97013
97171
|
template: input.name,
|
|
97014
97172
|
variables: values,
|
|
97015
|
-
|
|
97016
|
-
|
|
97173
|
+
created: result.created || [],
|
|
97174
|
+
modified: result.modified || [],
|
|
97175
|
+
skipped: result.skipped || [],
|
|
97176
|
+
message: dryRun ? `Dry run complete. ${totalCreated} files would be created, ${totalModified} modified. Set dryRun: false to write files.` : `Template executed. ${totalCreated} files created, ${totalModified} modified, ${totalSkipped} skipped.`
|
|
97017
97177
|
});
|
|
97018
97178
|
} catch (error48) {
|
|
97019
97179
|
return errorResponse(`Failed to run template: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
@@ -97022,10 +97182,10 @@ async function handleRunTemplate(args2) {
|
|
|
97022
97182
|
async function handleCreateTemplate(args2) {
|
|
97023
97183
|
const input = createTemplateSchema.parse(args2);
|
|
97024
97184
|
try {
|
|
97025
|
-
if (!existsSync29(
|
|
97026
|
-
await mkdir18(
|
|
97185
|
+
if (!existsSync29(TEMPLATES_DIR3)) {
|
|
97186
|
+
await mkdir18(TEMPLATES_DIR3, { recursive: true });
|
|
97027
97187
|
}
|
|
97028
|
-
const templateDir = join34(
|
|
97188
|
+
const templateDir = join34(TEMPLATES_DIR3, input.name);
|
|
97029
97189
|
if (existsSync29(templateDir)) {
|
|
97030
97190
|
return errorResponse(`Template "${input.name}" already exists`);
|
|
97031
97191
|
}
|
|
@@ -97645,7 +97805,7 @@ function showConfigInfo() {
|
|
|
97645
97805
|
import { existsSync as existsSync33 } from "node:fs";
|
|
97646
97806
|
import { mkdir as mkdir19, writeFile as writeFile17 } from "node:fs/promises";
|
|
97647
97807
|
import { join as join38 } from "node:path";
|
|
97648
|
-
var
|
|
97808
|
+
var TEMPLATES_DIR4 = ".knowns/templates";
|
|
97649
97809
|
function getProjectRoot3() {
|
|
97650
97810
|
const projectRoot = findProjectRoot();
|
|
97651
97811
|
if (!projectRoot) {
|
|
@@ -97656,7 +97816,7 @@ function getProjectRoot3() {
|
|
|
97656
97816
|
return projectRoot;
|
|
97657
97817
|
}
|
|
97658
97818
|
function getTemplatesDir(projectRoot) {
|
|
97659
|
-
return join38(projectRoot,
|
|
97819
|
+
return join38(projectRoot, TEMPLATES_DIR4);
|
|
97660
97820
|
}
|
|
97661
97821
|
async function ensureTemplatesDir(projectRoot) {
|
|
97662
97822
|
const templatesDir = getTemplatesDir(projectRoot);
|
|
@@ -97666,20 +97826,16 @@ async function ensureTemplatesDir(projectRoot) {
|
|
|
97666
97826
|
return templatesDir;
|
|
97667
97827
|
}
|
|
97668
97828
|
var templateCommand = new Command("template").description("Manage code generation templates").enablePositionalOptions();
|
|
97669
|
-
var listCommand4 = new Command("list").description("List available templates").option("--plain", "Plain text output for AI").action(async (options2) => {
|
|
97829
|
+
var listCommand4 = new Command("list").description("List available templates (local + imported)").option("--plain", "Plain text output for AI").option("--local", "Show only local templates").option("--imported", "Show only imported templates").action(async (options2) => {
|
|
97670
97830
|
try {
|
|
97671
97831
|
const projectRoot = getProjectRoot3();
|
|
97672
|
-
const
|
|
97673
|
-
|
|
97674
|
-
|
|
97675
|
-
|
|
97676
|
-
|
|
97677
|
-
|
|
97678
|
-
console.log(source_default.gray(" Create one with: knowns template create <name>"));
|
|
97679
|
-
}
|
|
97680
|
-
return;
|
|
97832
|
+
const allTemplates = await listAllTemplates(projectRoot);
|
|
97833
|
+
let templates = allTemplates;
|
|
97834
|
+
if (options2.local) {
|
|
97835
|
+
templates = allTemplates.filter((t) => !t.isImported);
|
|
97836
|
+
} else if (options2.imported) {
|
|
97837
|
+
templates = allTemplates.filter((t) => t.isImported);
|
|
97681
97838
|
}
|
|
97682
|
-
const templates = await listTemplates(templatesDir);
|
|
97683
97839
|
if (templates.length === 0) {
|
|
97684
97840
|
if (options2.plain) {
|
|
97685
97841
|
console.log("No templates found");
|
|
@@ -97689,21 +97845,88 @@ var listCommand4 = new Command("list").description("List available templates").o
|
|
|
97689
97845
|
}
|
|
97690
97846
|
return;
|
|
97691
97847
|
}
|
|
97848
|
+
const templatesWithDesc = [];
|
|
97849
|
+
for (const t of templates) {
|
|
97850
|
+
try {
|
|
97851
|
+
const loaded = await listTemplates(join38(t.path, ".."));
|
|
97852
|
+
const match2 = loaded.find((l) => l.name === t.name);
|
|
97853
|
+
templatesWithDesc.push({
|
|
97854
|
+
ref: t.ref,
|
|
97855
|
+
name: t.name,
|
|
97856
|
+
source: t.source,
|
|
97857
|
+
sourceUrl: t.sourceUrl,
|
|
97858
|
+
isImported: t.isImported,
|
|
97859
|
+
description: match2?.description || "No description"
|
|
97860
|
+
});
|
|
97861
|
+
} catch {
|
|
97862
|
+
templatesWithDesc.push({
|
|
97863
|
+
ref: t.ref,
|
|
97864
|
+
name: t.name,
|
|
97865
|
+
source: t.source,
|
|
97866
|
+
sourceUrl: t.sourceUrl,
|
|
97867
|
+
isImported: t.isImported,
|
|
97868
|
+
description: "No description"
|
|
97869
|
+
});
|
|
97870
|
+
}
|
|
97871
|
+
}
|
|
97692
97872
|
if (options2.plain) {
|
|
97693
|
-
|
|
97694
|
-
|
|
97873
|
+
const sources = /* @__PURE__ */ new Map();
|
|
97874
|
+
for (const t of templatesWithDesc) {
|
|
97875
|
+
const sourceKey = t.isImported ? `[${t.source}]` : "[local]";
|
|
97876
|
+
if (!sources.has(sourceKey)) {
|
|
97877
|
+
sources.set(sourceKey, []);
|
|
97878
|
+
}
|
|
97879
|
+
sources.get(sourceKey)?.push(t);
|
|
97880
|
+
}
|
|
97881
|
+
const sortedSources = Array.from(sources.keys()).sort((a, b) => {
|
|
97882
|
+
if (a === "[local]") return -1;
|
|
97883
|
+
if (b === "[local]") return 1;
|
|
97884
|
+
return a.localeCompare(b);
|
|
97885
|
+
});
|
|
97886
|
+
for (const sourceKey of sortedSources) {
|
|
97887
|
+
const sourceTemplates = sources.get(sourceKey) || [];
|
|
97888
|
+
const sourceUrl = sourceTemplates[0]?.sourceUrl;
|
|
97889
|
+
if (sourceUrl) {
|
|
97890
|
+
console.log(`${sourceKey} (${sourceUrl})`);
|
|
97891
|
+
} else {
|
|
97892
|
+
console.log(sourceKey);
|
|
97893
|
+
}
|
|
97894
|
+
for (const t of sourceTemplates) {
|
|
97895
|
+
console.log(` ${t.name} - ${t.description}`);
|
|
97896
|
+
}
|
|
97695
97897
|
}
|
|
97696
97898
|
} else {
|
|
97697
97899
|
console.log(source_default.bold("\nTemplates:\n"));
|
|
97698
|
-
const
|
|
97699
|
-
|
|
97700
|
-
|
|
97701
|
-
|
|
97702
|
-
|
|
97703
|
-
|
|
97704
|
-
|
|
97900
|
+
const sources = /* @__PURE__ */ new Map();
|
|
97901
|
+
for (const t of templatesWithDesc) {
|
|
97902
|
+
const sourceKey = t.isImported ? t.source : "local";
|
|
97903
|
+
if (!sources.has(sourceKey)) {
|
|
97904
|
+
sources.set(sourceKey, []);
|
|
97905
|
+
}
|
|
97906
|
+
sources.get(sourceKey)?.push(t);
|
|
97907
|
+
}
|
|
97908
|
+
const sortedSources = Array.from(sources.keys()).sort((a, b) => {
|
|
97909
|
+
if (a === "local") return -1;
|
|
97910
|
+
if (b === "local") return 1;
|
|
97911
|
+
return a.localeCompare(b);
|
|
97912
|
+
});
|
|
97913
|
+
for (const sourceKey of sortedSources) {
|
|
97914
|
+
const sourceTemplates = sources.get(sourceKey) || [];
|
|
97915
|
+
const sourceUrl = sourceTemplates[0]?.sourceUrl;
|
|
97916
|
+
const sourceLabel = sourceKey === "local" ? "Local" : `Import: ${sourceKey}`;
|
|
97917
|
+
if (sourceUrl) {
|
|
97918
|
+
console.log(source_default.magenta.bold(`[${sourceLabel}]`) + source_default.gray(` ${sourceUrl}`));
|
|
97919
|
+
} else {
|
|
97920
|
+
console.log(source_default.magenta.bold(`[${sourceLabel}]`));
|
|
97921
|
+
}
|
|
97922
|
+
const maxNameLen = Math.max(...sourceTemplates.map((t) => t.name.length), 4);
|
|
97923
|
+
for (const t of sourceTemplates) {
|
|
97924
|
+
const name = source_default.cyan(t.name.padEnd(maxNameLen + 2));
|
|
97925
|
+
const desc = t.description || source_default.gray("No description");
|
|
97926
|
+
console.log(` ${name}${desc}`);
|
|
97927
|
+
}
|
|
97928
|
+
console.log();
|
|
97705
97929
|
}
|
|
97706
|
-
console.log();
|
|
97707
97930
|
}
|
|
97708
97931
|
} catch (error48) {
|
|
97709
97932
|
console.error(source_default.red("Error:"), error48 instanceof Error ? error48.message : String(error48));
|
|
@@ -97831,7 +98054,7 @@ export function {{camelCase name}}() {
|
|
|
97831
98054
|
await writeFile17(join38(templateDir, "example.ts.hbs"), exampleTemplate, "utf-8");
|
|
97832
98055
|
console.log();
|
|
97833
98056
|
console.log(source_default.green(`\u2713 Created template: ${name}`));
|
|
97834
|
-
console.log(source_default.gray(` Location: ${
|
|
98057
|
+
console.log(source_default.gray(` Location: ${TEMPLATES_DIR4}/${name}/`));
|
|
97835
98058
|
console.log();
|
|
97836
98059
|
console.log(source_default.cyan("Files created:"));
|
|
97837
98060
|
console.log(source_default.gray(" - _template.yaml (config)"));
|
|
@@ -98636,7 +98859,7 @@ async function notifyCliUpdate(options2) {
|
|
|
98636
98859
|
// package.json
|
|
98637
98860
|
var package_default = {
|
|
98638
98861
|
name: "knowns",
|
|
98639
|
-
version: "0.10.
|
|
98862
|
+
version: "0.10.3",
|
|
98640
98863
|
description: "AI-native task and documentation management for dev teams",
|
|
98641
98864
|
module: "index.ts",
|
|
98642
98865
|
type: "module",
|