knowns 0.1.5 → 0.1.6

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 CHANGED
@@ -10489,10 +10489,10 @@ function serializeTaskMarkdown(task) {
10489
10489
  title: task.title,
10490
10490
  status: task.status,
10491
10491
  priority: task.priority,
10492
- labels: task.labels,
10492
+ labels: task.labels || [],
10493
10493
  createdAt: task.createdAt.toISOString(),
10494
10494
  updatedAt: task.updatedAt.toISOString(),
10495
- timeSpent: task.timeSpent
10495
+ timeSpent: task.timeSpent ?? 0
10496
10496
  };
10497
10497
  if (task.assignee)
10498
10498
  frontmatter.assignee = task.assignee;
@@ -10743,6 +10743,11 @@ class FileStore {
10743
10743
  const task = {
10744
10744
  ...taskData,
10745
10745
  id,
10746
+ labels: taskData.labels || [],
10747
+ subtasks: taskData.subtasks || [],
10748
+ acceptanceCriteria: taskData.acceptanceCriteria || [],
10749
+ timeSpent: taskData.timeSpent ?? 0,
10750
+ timeEntries: taskData.timeEntries || [],
10746
10751
  createdAt: now,
10747
10752
  updatedAt: now
10748
10753
  };
@@ -11447,12 +11452,165 @@ var import_prompts = __toESM(require_prompts3(), 1);
11447
11452
  var KNOWNS_GUIDELINES = `<!-- KNOWNS GUIDELINES START -->
11448
11453
  # Knowns CLI Guidelines
11449
11454
 
11450
- ## Core Principle
11455
+ ## Core Principles
11451
11456
 
11457
+ ### 1. CLI-Only Operations
11452
11458
  **NEVER edit .md files directly. ALL operations MUST use CLI commands.**
11453
11459
 
11454
11460
  This ensures data integrity, maintains proper change history, and prevents file corruption.
11455
11461
 
11462
+ ### 2. Documentation-First (For AI Agents)
11463
+ **ALWAYS read project documentation BEFORE planning or coding.**
11464
+
11465
+ AI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.
11466
+
11467
+ ---
11468
+
11469
+ ## AI Agent Guidelines
11470
+
11471
+ > **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.
11472
+
11473
+ ### First-Time Initialization
11474
+
11475
+ When starting a new session or working on an unfamiliar project:
11476
+
11477
+ \`\`\`bash
11478
+ # 1. List all available documentation
11479
+ knowns doc list --plain
11480
+
11481
+ # 2. Read essential project docs (prioritize these)
11482
+ knowns doc view "README" --plain # Project overview
11483
+ knowns doc view "ARCHITECTURE" --plain # System design
11484
+ knowns doc view "CONVENTIONS" --plain # Coding standards
11485
+ knowns doc view "API" --plain # API specifications
11486
+
11487
+ # 3. Review current task backlog
11488
+ knowns task list --plain
11489
+ knowns task list --status in-progress --plain
11490
+ \`\`\`
11491
+
11492
+ ### Before Taking Any Task
11493
+
11494
+ \`\`\`bash
11495
+ # 1. View the task details
11496
+ knowns task view <id> --plain
11497
+
11498
+ # 2. Follow ALL refs in the task (see Reference System section)
11499
+ # @.knowns/tasks/task-44 - ... \u2192 knowns task view 44 --plain
11500
+ # @.knowns/docs/patterns/module.md \u2192 knowns doc view "patterns/module" --plain
11501
+
11502
+ # 3. Search for additional related documentation
11503
+ knowns search "<keywords from task>" --type doc --plain
11504
+
11505
+ # 4. Read ALL related docs before planning
11506
+ knowns doc view "<related-doc>" --plain
11507
+
11508
+ # 5. Check for similar completed tasks (learn from history)
11509
+ knowns search "<keywords>" --type task --status done --plain
11510
+ \`\`\`
11511
+
11512
+ ### Why Documentation First?
11513
+
11514
+ | Without Reading Docs | With Reading Docs |
11515
+ |---------------------|-------------------|
11516
+ | Reinvent existing patterns | Reuse established patterns |
11517
+ | Break conventions | Follow project standards |
11518
+ | Duplicate code | Use existing utilities |
11519
+ | Wrong architecture decisions | Align with system design |
11520
+ | Inconsistent naming | Match naming conventions |
11521
+
11522
+ ### Context Checklist for Agents
11523
+
11524
+ Before writing ANY code, ensure you can answer:
11525
+
11526
+ - [ ] Have I followed ALL refs (\`@.knowns/...\`) in the task?
11527
+ - [ ] Have I followed nested refs recursively?
11528
+ - [ ] What is the project's overall architecture?
11529
+ - [ ] What coding conventions does this project follow?
11530
+ - [ ] Are there existing patterns/utilities I should reuse?
11531
+ - [ ] What are the testing requirements?
11532
+ - [ ] How should I structure my implementation?
11533
+
11534
+ > **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.
11535
+
11536
+ ---
11537
+
11538
+ ## Reference System (Refs)
11539
+
11540
+ Tasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.
11541
+
11542
+ ### Reference Formats
11543
+
11544
+ | Type | User Input | System Output | CLI Command |
11545
+ |------|------------|---------------|-------------|
11546
+ | **Task ref** | \`@task-<id>\` | \`@.knowns/tasks/task-<id> - <title>.md\` | \`knowns task view <id> --plain\` |
11547
+ | **Doc ref** | \`@doc/<path>\` | \`@.knowns/docs/<path>.md\` | \`knowns doc view "<path>" --plain\` |
11548
+
11549
+ ### How to Follow Refs
11550
+
11551
+ When you read a task and see refs in system output format, follow them:
11552
+
11553
+ \`\`\`bash
11554
+ # Example: Task 42 output contains:
11555
+ # @.knowns/tasks/task-44 - CLI Task Create Command.md
11556
+ # @.knowns/docs/patterns/module.md
11557
+
11558
+ # Follow task ref (extract ID from task-<id>)
11559
+ knowns task view 44 --plain
11560
+
11561
+ # Follow doc ref (extract path, remove .md)
11562
+ knowns doc view "patterns/module" --plain
11563
+ \`\`\`
11564
+
11565
+ ### Parsing Rules
11566
+
11567
+ 1. **Task refs**: \`@.knowns/tasks/task-<id> - ...\` \u2192 extract \`<id>\` \u2192 \`knowns task view <id> --plain\`
11568
+ 2. **Doc refs**: \`@.knowns/docs/<path>.md\` \u2192 extract \`<path>\` \u2192 \`knowns doc view "<path>" --plain\`
11569
+
11570
+ ### Recursive Following
11571
+
11572
+ Refs can be nested. Follow until complete context is gathered:
11573
+
11574
+ \`\`\`
11575
+ Task 42
11576
+ \u2192 @.knowns/docs/README.md
11577
+ \u2192 @.knowns/docs/patterns/module.md (found in README)
11578
+ \u2192 (read for full pattern details)
11579
+ \`\`\`
11580
+
11581
+ ### When to Follow Refs
11582
+
11583
+ | Situation | Action |
11584
+ |-----------|--------|
11585
+ | Refs in Description | ALWAYS follow - critical context |
11586
+ | Refs in Implementation Plan | Follow if implementing related work |
11587
+ | Refs in Notes | Optional - for historical context |
11588
+ | Dependency mentions | Follow if marked as blocker |
11589
+
11590
+ ### Example: Complete Ref Resolution
11591
+
11592
+ \`\`\`bash
11593
+ # 1. Read the task
11594
+ $ knowns task view 42 --plain
11595
+
11596
+ # Output contains:
11597
+ # @.knowns/tasks/task-44 - CLI Task Create Command.md
11598
+ # @.knowns/docs/README.md
11599
+
11600
+ # 2. Follow task ref
11601
+ $ knowns task view 44 --plain
11602
+
11603
+ # 3. Follow doc ref
11604
+ $ knowns doc view "README" --plain
11605
+
11606
+ # 4. If README contains more refs, follow them too
11607
+ # @.knowns/docs/patterns/module.md \u2192 knowns doc view "patterns/module" --plain
11608
+
11609
+ # Now you have complete context
11610
+ \`\`\`
11611
+
11612
+ > **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.
11613
+
11456
11614
  ---
11457
11615
 
11458
11616
  ## Quick Start
@@ -11476,63 +11634,195 @@ knowns search "query" --plain
11476
11634
 
11477
11635
  ---
11478
11636
 
11479
- ## Task Workflow (CRITICAL - Follow Every Step!)
11637
+ ## End-to-End Example
11638
+
11639
+ Here's a complete workflow for an AI agent implementing a feature:
11640
+
11641
+ \`\`\`bash
11642
+ # === AGENT SESSION START (Do this once per session) ===
11643
+
11644
+ # 0a. List all available documentation
11645
+ $ knowns doc list --plain
11646
+
11647
+ # Output:
11648
+ # DOC: README.md
11649
+ # DOC: ARCHITECTURE.md
11650
+ # DOC: CONVENTIONS.md
11651
+ # DOC: security-patterns.md
11652
+ # DOC: api-guidelines.md
11653
+ # DOC: email-templates.md
11654
+
11655
+ # 0b. Read essential project docs
11656
+ $ knowns doc view "README" --plain
11657
+ $ knowns doc view "ARCHITECTURE" --plain
11658
+ $ knowns doc view "CONVENTIONS" --plain
11659
+
11660
+ # Now the agent understands project context and conventions
11661
+
11662
+ # === TASK WORKFLOW ===
11663
+
11664
+ # 1. Create the task
11665
+ $ knowns task create "Add password reset flow" \\
11666
+ -d "Users need ability to reset forgotten passwords via email" \\
11667
+ --ac "User can request password reset via email" \\
11668
+ --ac "Reset link expires after 1 hour" \\
11669
+ --ac "User can set new password via reset link" \\
11670
+ --ac "Unit tests cover all scenarios" \\
11671
+ --priority high \\
11672
+ -l "auth,feature"
11673
+
11674
+ # Output: Created task AUTH-042
11675
+
11676
+ # 2. Take the task and start timer
11677
+ $ knowns task edit AUTH-042 -s in-progress -a @me
11678
+ $ knowns time start AUTH-042
11679
+
11680
+ # Output: Timer started for AUTH-042
11681
+
11682
+ # 3. Search for related documentation
11683
+ $ knowns search "password security" --type doc --plain
11684
+
11685
+ # Output:
11686
+ # DOC: security-patterns.md (score: 0.92)
11687
+ # DOC: email-templates.md (score: 0.78)
11688
+
11689
+ # 4. Read the documentation
11690
+ $ knowns doc view "security-patterns" --plain
11691
+
11692
+ # 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)
11693
+ $ knowns task edit AUTH-042 --plan $'1. Review security patterns (see [security-patterns.md](./docs/security-patterns.md))
11694
+ 2. Design token generation with 1-hour expiry
11695
+ 3. Create email template (see [email-templates.md](./docs/email-templates.md))
11696
+ 4. Implement /forgot-password endpoint
11697
+ 5. Implement /reset-password endpoint
11698
+ 6. Add unit tests
11699
+ 7. Update API documentation'
11700
+
11701
+ # 6. After approval, implement and check criteria as you go
11702
+ $ knowns task edit AUTH-042 --check-ac 1
11703
+ $ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /forgot-password endpoint"
11704
+
11705
+ $ knowns task edit AUTH-042 --check-ac 2
11706
+ $ knowns task edit AUTH-042 --append-notes "\u2713 Token expiry set to 1 hour"
11707
+
11708
+ $ knowns task edit AUTH-042 --check-ac 3
11709
+ $ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /reset-password endpoint"
11710
+
11711
+ $ knowns task edit AUTH-042 --check-ac 4
11712
+ $ knowns task edit AUTH-042 --append-notes "\u2713 Added 12 unit tests, 100% coverage"
11713
+
11714
+ # 7. Add final implementation notes
11715
+ $ knowns task edit AUTH-042 --notes $'## Summary
11716
+ Implemented complete password reset flow with secure token generation.
11717
+
11718
+ ## Changes
11719
+ - Added POST /forgot-password endpoint
11720
+ - Added POST /reset-password endpoint
11721
+ - Created password_reset_tokens table
11722
+ - Added email template for reset link
11723
+
11724
+ ## Security
11725
+ - Tokens use crypto.randomBytes(32)
11726
+ - 1-hour expiry enforced at DB level
11727
+ - Rate limiting: 3 requests per hour per email
11728
+
11729
+ ## Tests
11730
+ - 12 unit tests added
11731
+ - Coverage: 100% for new code
11732
+
11733
+ ## Documentation
11734
+ - Updated API.md with new endpoints'
11735
+
11736
+ # 8. Stop timer and complete
11737
+ $ knowns time stop
11738
+ $ knowns task edit AUTH-042 -s done
11739
+
11740
+ # Output: Task AUTH-042 marked as done
11741
+ \`\`\`
11742
+
11743
+ ---
11744
+
11745
+ ## Task Workflow
11480
11746
 
11481
11747
  ### Step 1: Take Task
11748
+
11482
11749
  \`\`\`bash
11483
- knowns task edit <id> -s in-progress -a @yourself
11750
+ knowns task edit <id> -s in-progress -a @me
11484
11751
  \`\`\`
11485
11752
 
11753
+ > **Note**: \`@me\` is a special keyword that assigns the task to yourself. You can also use specific usernames like \`@harry\` or \`@john\`.
11754
+
11486
11755
  ### Step 2: Start Time Tracking
11756
+
11487
11757
  \`\`\`bash
11488
11758
  knowns time start <id>
11489
11759
  \`\`\`
11490
11760
 
11491
11761
  ### Step 3: Read Related Documentation
11762
+
11763
+ > **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.
11764
+
11492
11765
  \`\`\`bash
11493
11766
  # Search for related docs
11494
11767
  knowns search "authentication" --type doc --plain
11495
11768
 
11496
11769
  # View relevant documents
11497
11770
  knowns doc view "API Guidelines" --plain
11771
+ knowns doc view "Security Patterns" --plain
11772
+
11773
+ # Also check for similar completed tasks
11774
+ knowns search "auth" --type task --status done --plain
11498
11775
  \`\`\`
11499
11776
 
11500
- **CRITICAL**: ALWAYS read related documentation BEFORE planning!
11777
+ > **CRITICAL**: ALWAYS read related documentation BEFORE planning! Understanding existing patterns and conventions prevents rework and ensures consistency.
11501
11778
 
11502
11779
  ### Step 4: Create Implementation Plan
11780
+
11503
11781
  \`\`\`bash
11504
- # Add plan with documentation references
11505
- knowns task edit <id> --plan $'1. Research patterns (see [security-patterns.md](./security-patterns.md))\\n2. Design middleware\\n3. Implement\\n4. Add tests\\n5. Update docs'
11782
+ knowns task edit <id> --plan $'1. Research patterns (see [security-patterns.md](./security-patterns.md))
11783
+ 2. Design middleware
11784
+ 3. Implement
11785
+ 4. Add tests
11786
+ 5. Update docs'
11506
11787
  \`\`\`
11507
11788
 
11508
- **CRITICAL**:
11509
- - Share plan with user and **WAIT for approval** before coding
11510
- - Include doc references using \`[file.md](./path/file.md)\` format
11789
+ > **CRITICAL**:
11790
+ > - Share plan with user and **WAIT for approval** before coding
11791
+ > - Include doc references using \`[file.md](./path/file.md)\` format
11511
11792
 
11512
11793
  ### Step 5: Implement
11794
+
11513
11795
  \`\`\`bash
11514
11796
  # Check acceptance criteria as you complete them
11515
11797
  knowns task edit <id> --check-ac 1 --check-ac 2 --check-ac 3
11516
11798
  \`\`\`
11517
11799
 
11518
11800
  ### Step 6: Add Implementation Notes
11801
+
11519
11802
  \`\`\`bash
11520
11803
  # Add comprehensive notes (suitable for PR description)
11521
- knowns task edit <id> --notes $'## Summary\\n\\nImplemented JWT auth.\\n\\n## Changes\\n- Added middleware\\n- Added tests'
11804
+ knowns task edit <id> --notes $'## Summary
11522
11805
 
11523
- # OR append progressively
11806
+ Implemented JWT auth.
11807
+
11808
+ ## Changes
11809
+ - Added middleware
11810
+ - Added tests'
11811
+
11812
+ # OR append progressively (recommended)
11524
11813
  knowns task edit <id> --append-notes "\u2713 Implemented middleware"
11525
11814
  knowns task edit <id> --append-notes "\u2713 Added tests"
11526
11815
  \`\`\`
11527
11816
 
11528
11817
  ### Step 7: Stop Time Tracking
11818
+
11529
11819
  \`\`\`bash
11530
11820
  knowns time stop
11531
11821
  \`\`\`
11532
11822
 
11533
11823
  ### Step 8: Complete Task
11824
+
11534
11825
  \`\`\`bash
11535
- # Mark as done (only after ALL criteria are met)
11536
11826
  knowns task edit <id> -s done
11537
11827
  \`\`\`
11538
11828
 
@@ -11551,11 +11841,11 @@ knowns task edit <id> -t "New title"
11551
11841
  knowns task edit <id> -d "New description"
11552
11842
  knowns task edit <id> -s in-progress
11553
11843
  knowns task edit <id> --priority high
11554
- knowns task edit <id> -a @yourself
11844
+ knowns task edit <id> -a @me
11555
11845
 
11556
11846
  # Acceptance Criteria
11557
11847
  knowns task edit <id> --ac "New criterion" # Add
11558
- knowns task edit <id> --check-ac 1 --check-ac 2 # Check
11848
+ knowns task edit <id> --check-ac 1 --check-ac 2 # Check (1-indexed)
11559
11849
  knowns task edit <id> --uncheck-ac 2 # Uncheck
11560
11850
  knowns task edit <id> --remove-ac 3 # Remove
11561
11851
 
@@ -11568,7 +11858,7 @@ knowns task edit <id> --append-notes "Progress update"
11568
11858
  knowns task view <id> --plain # ALWAYS use --plain
11569
11859
  knowns task list --plain
11570
11860
  knowns task list --status in-progress --plain
11571
- knowns task list --assignee @yourself --plain
11861
+ knowns task list --assignee @me --plain
11572
11862
  knowns task list --tree --plain # Tree hierarchy
11573
11863
  \`\`\`
11574
11864
 
@@ -11598,11 +11888,16 @@ knowns doc list --plain
11598
11888
  knowns doc list --tag architecture --plain
11599
11889
  knowns doc view "Doc Name" --plain
11600
11890
 
11601
- # Create
11891
+ # Create (with optional folder)
11602
11892
  knowns doc create "Title" -d "Description" -t "tags"
11893
+ knowns doc create "Title" -d "Description" -t "tags" -f "folder/path"
11603
11894
 
11604
11895
  # Edit metadata
11605
11896
  knowns doc edit "Doc Name" -t "New Title" --tags "new,tags"
11897
+
11898
+ # Edit content
11899
+ knowns doc edit "Doc Name" -c "New content" # Replace content
11900
+ knowns doc edit "Doc Name" -a "Appended content" # Append to content
11606
11901
  \`\`\`
11607
11902
 
11608
11903
  ### Search
@@ -11624,51 +11919,100 @@ knowns search "bug" --status in-progress --priority high --plain
11624
11919
  ## Task Structure
11625
11920
 
11626
11921
  ### Title
11922
+
11627
11923
  Clear summary (WHAT needs to be done).
11628
- - Good: "Add JWT authentication"
11629
- - Bad: "Do auth stuff"
11924
+
11925
+ | Bad | Good |
11926
+ |-----|------|
11927
+ | Do auth stuff | Add JWT authentication |
11928
+ | Fix bug | Fix login timeout on slow networks |
11929
+ | Update docs | Document rate limiting in API.md |
11630
11930
 
11631
11931
  ### Description
11932
+
11632
11933
  Explains WHY and WHAT (not HOW). **Link related docs using \`[file.md](./path/file.md)\`**
11633
11934
 
11634
- \`\`\`
11635
- We need JWT authentication because sessions don't scale.
11935
+ \`\`\`markdown
11936
+ We need JWT authentication because sessions don't scale for our microservices architecture.
11636
11937
 
11637
11938
  Related docs:
11638
- - [security-patterns.md](./security-patterns.md)
11639
- - [api-guidelines.md](./api-guidelines.md)
11939
+ - [security-patterns.md](./docs/security-patterns.md)
11940
+ - [api-guidelines.md](./docs/api-guidelines.md)
11640
11941
  \`\`\`
11641
11942
 
11642
11943
  ### Acceptance Criteria
11643
- **Outcome-oriented**, testable criteria. NOT implementation details.
11644
11944
 
11645
- - Good: "User can login and receive JWT token"
11646
- - Good: "Token expires after 15 minutes"
11647
- - Bad: "Add function handleLogin() in auth.ts"
11945
+ **Outcome-oriented**, testable criteria. NOT implementation steps.
11946
+
11947
+ | Bad (Implementation details) | Good (Outcomes) |
11948
+ |------------------------------|-----------------|
11949
+ | Add function handleLogin() in auth.ts | User can login and receive JWT token |
11950
+ | Use bcrypt for hashing | Passwords are securely hashed |
11951
+ | Add try-catch blocks | Errors return appropriate HTTP status codes |
11648
11952
 
11649
11953
  ### Implementation Plan
11954
+
11650
11955
  HOW to solve. Added AFTER taking task, BEFORE coding.
11651
11956
 
11652
- \`\`\`
11653
- 1. Research JWT libraries (see [security-patterns.md](./security-patterns.md))
11654
- 2. Design token structure
11655
- 3. Implement middleware
11656
- 4. Add tests
11957
+ \`\`\`markdown
11958
+ 1. Research JWT libraries (see [security-patterns.md](./docs/security-patterns.md))
11959
+ 2. Design token structure (access + refresh tokens)
11960
+ 3. Implement auth middleware
11961
+ 4. Add unit tests (aim for 90%+ coverage)
11962
+ 5. Update API.md with new endpoints
11657
11963
  \`\`\`
11658
11964
 
11659
11965
  ### Implementation Notes
11660
- Summary for PR. Added AFTER completion.
11661
11966
 
11662
- \`\`\`
11967
+ Summary for PR description. Added AFTER completion.
11968
+
11969
+ \`\`\`markdown
11663
11970
  ## Summary
11664
- Implemented JWT auth using jsonwebtoken.
11971
+ Implemented JWT auth using jsonwebtoken library.
11665
11972
 
11666
11973
  ## Changes
11667
- - Added middleware in src/auth.ts
11668
- - Added tests with 100% coverage
11974
+ - Added auth middleware in src/middleware/auth.ts
11975
+ - Added /login and /refresh endpoints
11976
+ - Created JWT utility functions
11977
+
11978
+ ## Tests
11979
+ - Added 15 unit tests
11980
+ - Coverage: 94%
11669
11981
 
11670
11982
  ## Documentation
11671
- - Updated API.md
11983
+ - Updated API.md with authentication section
11984
+ \`\`\`
11985
+
11986
+ ---
11987
+
11988
+ ## Error Handling
11989
+
11990
+ ### Common Errors and Solutions
11991
+
11992
+ | Error | Cause | Solution |
11993
+ |-------|-------|----------|
11994
+ | \`Error: Task not found\` | Invalid task ID | Run \`knowns task list --plain\` to find correct ID |
11995
+ | \`Error: No active timer\` | Calling \`time stop\` without active timer | Start timer first: \`knowns time start <id>\` |
11996
+ | \`Error: Timer already running\` | Starting timer when one is active | Stop current: \`knowns time stop\` |
11997
+ | \`Error: Invalid status\` | Wrong status format | Use lowercase with hyphens: \`in-progress\`, not \`In Progress\` |
11998
+ | \`Error: AC index out of range\` | Checking non-existent criterion | View task first: \`knowns task view <id> --plain\` |
11999
+ | \`Error: Document not found\` | Wrong document name | Run \`knowns doc list --plain\` to find correct name |
12000
+ | \`Error: Not initialized\` | Running commands without init | Run \`knowns init\` first |
12001
+
12002
+ ### Debugging Commands
12003
+
12004
+ \`\`\`bash
12005
+ # Check CLI version
12006
+ knowns --version
12007
+
12008
+ # Verify project is initialized
12009
+ knowns status
12010
+
12011
+ # View raw task data (for debugging)
12012
+ knowns task view <id> --json
12013
+
12014
+ # Check timer status
12015
+ knowns time status
11672
12016
  \`\`\`
11673
12017
 
11674
12018
  ---
@@ -11678,109 +12022,193 @@ Implemented JWT auth using jsonwebtoken.
11678
12022
  A task is **Done** ONLY when **ALL** criteria are met:
11679
12023
 
11680
12024
  ### Via CLI (Required)
11681
- 1. All acceptance criteria checked: \`--check-ac <index>\`
11682
- 2. Implementation notes added: \`--notes "..."\`
11683
- 3. Status set to done: \`-s done\`
12025
+
12026
+ - [ ] All acceptance criteria checked: \`--check-ac <index>\`
12027
+ - [ ] Implementation notes added: \`--notes "..."\`
12028
+ - [ ] Timer stopped: \`knowns time stop\`
12029
+ - [ ] Status set to done: \`-s done\`
11684
12030
 
11685
12031
  ### Via Code (Required)
11686
- 4. Tests pass
11687
- 5. Documentation updated
11688
- 6. Code reviewed (linting, formatting)
11689
- 7. No regressions
12032
+
12033
+ - [ ] All tests pass
12034
+ - [ ] Documentation updated
12035
+ - [ ] Code reviewed (linting, formatting)
12036
+ - [ ] No regressions introduced
12037
+
12038
+ ---
12039
+
12040
+ ## Status & Priority Reference
12041
+
12042
+ ### Status Values
12043
+
12044
+ Use **lowercase with hyphens**:
12045
+
12046
+ | Status | Description | When to Use |
12047
+ |--------|-------------|-------------|
12048
+ | \`todo\` | Not started | Default for new tasks |
12049
+ | \`in-progress\` | Currently working | After taking task |
12050
+ | \`in-review\` | In code review | PR submitted |
12051
+ | \`blocked\` | Waiting on dependency | External blocker |
12052
+ | \`done\` | Completed | All criteria met |
12053
+
12054
+ ### Priority Values
12055
+
12056
+ | Priority | Description |
12057
+ |----------|-------------|
12058
+ | \`low\` | Can wait, nice-to-have |
12059
+ | \`medium\` | Normal priority (default) |
12060
+ | \`high\` | Urgent, time-sensitive |
11690
12061
 
11691
12062
  ---
11692
12063
 
11693
12064
  ## Common Mistakes
11694
12065
 
11695
12066
  | Wrong | Right |
11696
- |---------|---------|
12067
+ |-------|-------|
11697
12068
  | Edit .md files directly | Use \`knowns task edit\` |
11698
12069
  | Change \`- [ ]\` to \`- [x]\` in file | Use \`--check-ac <index>\` |
11699
- | Start coding without reading docs | Read docs FIRST, then plan |
11700
- | Plan without checking docs | Search and read docs before planning |
11701
- | Missing doc links in description/plan | Link docs using \`[file.md](./path/file.md)\` |
11702
- | Forget to use \`--plain\` flag | Always use \`--plain\` for AI |
11703
- | Forget to share plan before coding | Share plan, WAIT for approval |
11704
- | Mark done without all criteria checked | Check ALL criteria first |
11705
- | Write implementation details in AC | Write outcome-oriented criteria |
12070
+ | Start coding without reading docs | Read ALL related docs FIRST |
12071
+ | Skip \`knowns doc list\` on new project | Always list docs when starting |
12072
+ | Assume you know the conventions | Read CONVENTIONS/ARCHITECTURE docs |
12073
+ | Plan without checking docs | Read docs before planning |
12074
+ | Ignore similar completed tasks | Search done tasks for patterns |
12075
+ | Missing doc links in description/plan | Link docs using \`[file.md](./path)\` |
12076
+ | Forget \`--plain\` flag | Always use \`--plain\` for AI |
12077
+ | Code before plan approval | Share plan, WAIT for approval |
12078
+ | Mark done without all criteria | Check ALL criteria first |
12079
+ | Write implementation steps in AC | Write outcome-oriented criteria |
11706
12080
  | Use \`"In Progress"\` or \`"Done"\` | Use \`in-progress\`, \`done\` |
12081
+ | Use \`@yourself\` | Use \`@me\` or specific username |
12082
+ | Ignore refs in task description | Follow ALL refs (\`@.knowns/...\`) before planning |
12083
+ | See \`@.knowns/docs/...\` but don't read | Use \`knowns doc view "<path>" --plain\` |
12084
+ | See \`@.knowns/tasks/task-X\` but don't check | Use \`knowns task view X --plain\` for context |
12085
+ | Follow only first-level refs | Recursively follow nested refs until complete |
11707
12086
 
11708
12087
  ---
11709
12088
 
11710
- ## Best Practices
11711
-
11712
- ### Critical Rules
11713
- - **ALWAYS use \`--plain\` flag** for AI-readable output
11714
- - **Read documentation BEFORE planning**: \`knowns search "keyword" --type doc --plain\`
11715
- - **Link docs in tasks**: Use \`[file.md](./path/file.md)\` or \`[mapper.md](./patterns/mapper.md)\`
11716
- - **Share plan before coding**: Get approval first
11717
- - **Start time tracking**: \`knowns time start <id>\` before work
11718
- - **Stop time tracking**: \`knowns time stop\` after work
11719
- - **Check criteria as you go**: Don't wait until end
11720
- - **Add notes progressively**: Use \`--append-notes\`
12089
+ ## Platform-Specific Notes
11721
12090
 
11722
12091
  ### Multi-line Input
11723
- - **Bash/Zsh**: \`$'Line1\\nLine2\\nLine3'\`
11724
- - **PowerShell**: \`"Line1\\\`nLine2\\\`nLine3"\`
11725
- - **Portable**: \`"$(printf 'Line1\\nLine2\\nLine3')"\`
11726
12092
 
11727
- ### Documentation Workflow
11728
- 1. Search: \`knowns search "keyword" --type doc --plain\`
11729
- 2. Read: \`knowns doc view "Doc Name" --plain\`
11730
- 3. Link in description: \`[file.md](./path/file.md)\`
11731
- 4. Link in plan: Reference specific sections
11732
- 5. Update after implementation
12093
+ Different shells handle multi-line strings differently:
12094
+
12095
+ **Bash / Zsh (Recommended)**
12096
+ \`\`\`bash
12097
+ knowns task edit <id> --plan $'1. First step\\n2. Second step\\n3. Third step'
12098
+ \`\`\`
12099
+
12100
+ **PowerShell**
12101
+ \`\`\`powershell
12102
+ knowns task edit <id> --plan "1. First step\`n2. Second step\`n3. Third step"
12103
+ \`\`\`
12104
+
12105
+ **Cross-platform (Using printf)**
12106
+ \`\`\`bash
12107
+ knowns task edit <id> --plan "$(printf '1. First step\\n2. Second step\\n3. Third step')"
12108
+ \`\`\`
12109
+
12110
+ **Using heredoc (for long content)**
12111
+ \`\`\`bash
12112
+ knowns task edit <id> --plan "$(cat <<EOF
12113
+ 1. First step
12114
+ 2. Second step
12115
+ 3. Third step
12116
+ EOF
12117
+ )"
12118
+ \`\`\`
12119
+
12120
+ ### Path Separators
12121
+
12122
+ - **Unix/macOS**: Use forward slashes: \`./docs/api.md\`
12123
+ - **Windows**: Both work, but prefer forward slashes for consistency
11733
12124
 
11734
12125
  ---
11735
12126
 
11736
- ## Status & Priority Values
12127
+ ## Best Practices Checklist
12128
+
12129
+ ### For AI Agents: Session Start
11737
12130
 
11738
- **Status** (lowercase with hyphens):
11739
- - \`todo\` - Not started
11740
- - \`in-progress\` - Currently working
11741
- - \`in-review\` - In code review
11742
- - \`done\` - Completed
11743
- - \`blocked\` - Waiting on dependency
12131
+ - [ ] List all docs: \`knowns doc list --plain\`
12132
+ - [ ] Read README/ARCHITECTURE docs
12133
+ - [ ] Understand coding conventions
12134
+ - [ ] Review current task backlog
11744
12135
 
11745
- **Priority**:
11746
- - \`low\` - Can wait
11747
- - \`medium\` - Normal (default)
11748
- - \`high\` - Urgent, important
12136
+ ### Before Starting Work
12137
+
12138
+ - [ ] Task has clear acceptance criteria
12139
+ - [ ] ALL refs in task followed (\`@.knowns/...\`)
12140
+ - [ ] Nested refs recursively followed until complete context gathered
12141
+ - [ ] Related docs searched: \`knowns search "keyword" --type doc --plain\`
12142
+ - [ ] ALL relevant docs read: \`knowns doc view "Doc Name" --plain\`
12143
+ - [ ] Similar done tasks reviewed for patterns
12144
+ - [ ] Task assigned to self: \`-a @me\`
12145
+ - [ ] Status set to in-progress: \`-s in-progress\`
12146
+ - [ ] Timer started: \`knowns time start <id>\`
12147
+
12148
+ ### During Work
12149
+
12150
+ - [ ] Implementation plan created and approved
12151
+ - [ ] Doc links included in plan: \`[file.md](./path/file.md)\`
12152
+ - [ ] Criteria checked as completed: \`--check-ac <index>\`
12153
+ - [ ] Progress notes appended: \`--append-notes "\u2713 ..."\`
12154
+
12155
+ ### After Work
12156
+
12157
+ - [ ] All acceptance criteria checked
12158
+ - [ ] Implementation notes added: \`--notes "..."\`
12159
+ - [ ] Timer stopped: \`knowns time stop\`
12160
+ - [ ] Tests passing
12161
+ - [ ] Documentation updated
12162
+ - [ ] Status set to done: \`-s done\`
11749
12163
 
11750
12164
  ---
11751
12165
 
11752
- ## Quick Reference
12166
+ ## Quick Reference Card
11753
12167
 
11754
12168
  \`\`\`bash
11755
- # Full workflow
12169
+ # === AGENT INITIALIZATION (Once per session) ===
12170
+ knowns doc list --plain
12171
+ knowns doc view "README" --plain
12172
+ knowns doc view "ARCHITECTURE" --plain
12173
+ knowns doc view "CONVENTIONS" --plain
12174
+
12175
+ # === FULL WORKFLOW ===
11756
12176
  knowns task create "Title" -d "Description" --ac "Criterion"
11757
- knowns task edit <id> -s in-progress -a @yourself
12177
+ knowns task edit <id> -s in-progress -a @me
11758
12178
  knowns time start <id>
11759
12179
  knowns search "keyword" --type doc --plain
11760
12180
  knowns doc view "Doc Name" --plain
12181
+ knowns search "keyword" --type task --status done --plain # Learn from history
11761
12182
  knowns task edit <id> --plan $'1. Step (see [file.md](./file.md))\\n2. Step'
11762
- # ... implement code ...
12183
+ # ... wait for approval, then implement ...
11763
12184
  knowns task edit <id> --check-ac 1 --check-ac 2
11764
- knowns task edit <id> --append-notes "\u2713 Completed"
12185
+ knowns task edit <id> --append-notes "\u2713 Completed feature"
11765
12186
  knowns time stop
11766
12187
  knowns task edit <id> -s done
11767
12188
 
11768
- # View task
12189
+ # === VIEW & SEARCH ===
11769
12190
  knowns task view <id> --plain
11770
-
11771
- # List tasks
11772
12191
  knowns task list --plain
11773
- knowns task list --status in-progress --assignee @yourself --plain
11774
-
11775
- # Search
12192
+ knowns task list --status in-progress --assignee @me --plain
11776
12193
  knowns search "query" --plain
11777
12194
  knowns search "bug" --type task --status in-progress --plain
12195
+
12196
+ # === TIME TRACKING ===
12197
+ knowns time start <id>
12198
+ knowns time stop
12199
+ knowns time status
12200
+ knowns time report --from "2025-12-01" --to "2025-12-31"
12201
+
12202
+ # === DOCUMENTATION ===
12203
+ knowns doc list --plain
12204
+ knowns doc view "path/doc-name" --plain
12205
+ knowns doc create "Title" -d "Description" -t "tags" -f "folder"
12206
+ knowns doc edit "doc-name" -c "New content"
12207
+ knowns doc edit "doc-name" -a "Appended content"
11778
12208
  \`\`\`
11779
12209
 
11780
12210
  ---
11781
12211
 
11782
- **Last Updated**: 2025-12-26
11783
- **Version**: 2.1.0
11784
12212
  **Maintained By**: Knowns CLI Team
11785
12213
 
11786
12214
  <!-- KNOWNS GUIDELINES END -->
@@ -12029,7 +12457,7 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
12029
12457
  });
12030
12458
  // src/commands/task.ts
12031
12459
  import { mkdir as mkdir4, readdir as readdir2, unlink } from "fs/promises";
12032
- import { join as join7 } from "path";
12460
+ import { join as join8 } from "path";
12033
12461
 
12034
12462
  // src/utils/doc-links.ts
12035
12463
  import { existsSync as existsSync3 } from "fs";
@@ -12114,6 +12542,86 @@ function findProjectRoot(startPath = process.cwd()) {
12114
12542
  return null;
12115
12543
  }
12116
12544
 
12545
+ // src/utils/mention-refs.ts
12546
+ var TASK_MENTION_REGEX = /@task-(\d+)/g;
12547
+ var DOC_MENTION_REGEX = /@docs?\/([^\s|)]+)/g;
12548
+ function sanitizeTitle(title) {
12549
+ return title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").trim().slice(0, 50);
12550
+ }
12551
+ function transformTaskMention(taskId, tasks) {
12552
+ const task = tasks.get(taskId);
12553
+ if (task) {
12554
+ const sanitizedTitle = sanitizeTitle(task.title);
12555
+ return `@.knowns/tasks/task-${taskId} - ${sanitizedTitle}.md`;
12556
+ }
12557
+ return `@.knowns/tasks/task-${taskId}.md`;
12558
+ }
12559
+ function transformDocMention(docPath) {
12560
+ const normalizedPath = docPath.endsWith(".md") ? docPath : `${docPath}.md`;
12561
+ return `@.knowns/docs/${normalizedPath}`;
12562
+ }
12563
+ function transformMentionsToRefs(text, tasks) {
12564
+ let result = text;
12565
+ result = result.replace(TASK_MENTION_REGEX, (match, taskId) => {
12566
+ return transformTaskMention(taskId, tasks);
12567
+ });
12568
+ result = result.replace(DOC_MENTION_REGEX, (match, docPath) => {
12569
+ return transformDocMention(docPath);
12570
+ });
12571
+ return result;
12572
+ }
12573
+ function buildTaskMap(tasks) {
12574
+ const map = new Map;
12575
+ for (const task of tasks) {
12576
+ map.set(task.id, task);
12577
+ }
12578
+ return map;
12579
+ }
12580
+
12581
+ // src/utils/notify-server.ts
12582
+ import { existsSync as existsSync5, readFileSync } from "fs";
12583
+ import { join as join7 } from "path";
12584
+ var DEFAULT_PORT = 6420;
12585
+ function getServerPort() {
12586
+ try {
12587
+ const projectRoot = findProjectRoot();
12588
+ if (!projectRoot)
12589
+ return DEFAULT_PORT;
12590
+ const configPath = join7(projectRoot, ".knowns/config.json");
12591
+ if (!existsSync5(configPath))
12592
+ return DEFAULT_PORT;
12593
+ const content = readFileSync(configPath, "utf-8");
12594
+ const config = JSON.parse(content);
12595
+ const port = config.settings?.serverPort;
12596
+ return typeof port === "number" ? port : DEFAULT_PORT;
12597
+ } catch {
12598
+ return DEFAULT_PORT;
12599
+ }
12600
+ }
12601
+ async function notifyTaskUpdate(taskId) {
12602
+ try {
12603
+ const port = getServerPort();
12604
+ const response = await fetch(`http://localhost:${port}/api/notify`, {
12605
+ method: "POST",
12606
+ headers: { "Content-Type": "application/json" },
12607
+ body: JSON.stringify({ taskId }),
12608
+ signal: AbortSignal.timeout(1000)
12609
+ });
12610
+ if (response.ok) {}
12611
+ } catch {}
12612
+ }
12613
+ async function notifyDocUpdate(docPath) {
12614
+ try {
12615
+ const port = getServerPort();
12616
+ await fetch(`http://localhost:${port}/api/notify`, {
12617
+ method: "POST",
12618
+ headers: { "Content-Type": "application/json" },
12619
+ body: JSON.stringify({ type: "docs:updated", docPath }),
12620
+ signal: AbortSignal.timeout(1000)
12621
+ });
12622
+ } catch {}
12623
+ }
12624
+
12117
12625
  // src/commands/task.ts
12118
12626
  function getFileStore() {
12119
12627
  const projectRoot = findProjectRoot();
@@ -12153,6 +12661,9 @@ async function formatTask(task, fileStore, plain = false) {
12153
12661
  const border = "-".repeat(50);
12154
12662
  const titleBorder = "=".repeat(50);
12155
12663
  const output2 = [];
12664
+ const allTasks = await fileStore.getAllTasks();
12665
+ const taskMap = buildTaskMap(allTasks);
12666
+ const transformText = (text) => transformMentionsToRefs(text, taskMap);
12156
12667
  const projectRoot2 = findProjectRoot();
12157
12668
  if (projectRoot2) {
12158
12669
  const filename = `task-${task.id} - ${task.title.replace(/[^a-z0-9]+/gi, "-")}`;
@@ -12204,7 +12715,7 @@ async function formatTask(task, fileStore, plain = false) {
12204
12715
  if (task.description) {
12205
12716
  output2.push("Description:");
12206
12717
  output2.push(border);
12207
- output2.push(task.description);
12718
+ output2.push(transformText(task.description));
12208
12719
  output2.push("");
12209
12720
  }
12210
12721
  if (task.acceptanceCriteria.length > 0) {
@@ -12212,20 +12723,20 @@ async function formatTask(task, fileStore, plain = false) {
12212
12723
  output2.push(border);
12213
12724
  for (const [i, ac] of task.acceptanceCriteria.entries()) {
12214
12725
  const checkbox = ac.completed ? "[x]" : "[ ]";
12215
- output2.push(`- ${checkbox} #${i + 1} ${ac.text}`);
12726
+ output2.push(`- ${checkbox} #${i + 1} ${transformText(ac.text)}`);
12216
12727
  }
12217
12728
  output2.push("");
12218
12729
  }
12219
12730
  if (task.implementationPlan) {
12220
12731
  output2.push("Implementation Plan:");
12221
12732
  output2.push(border);
12222
- output2.push(task.implementationPlan);
12733
+ output2.push(transformText(task.implementationPlan));
12223
12734
  output2.push("");
12224
12735
  }
12225
12736
  if (task.implementationNotes) {
12226
12737
  output2.push("Implementation Notes:");
12227
12738
  output2.push(border);
12228
- output2.push(task.implementationNotes);
12739
+ output2.push(transformText(task.implementationNotes));
12229
12740
  output2.push("");
12230
12741
  }
12231
12742
  if (projectRoot2) {
@@ -12504,6 +13015,7 @@ var createCommand2 = new Command("create").description("Create a new task").argu
12504
13015
  timeSpent: 0,
12505
13016
  timeEntries: []
12506
13017
  });
13018
+ await notifyTaskUpdate(task.id);
12507
13019
  console.log(source_default.green(`\u2713 Created task-${task.id}: ${task.title}`));
12508
13020
  if (options2.parent) {
12509
13021
  console.log(source_default.gray(` Subtask of: ${options2.parent}`));
@@ -12668,6 +13180,7 @@ var editCommand = new Command("edit").description("Edit task properties").argume
12668
13180
  updates.implementationNotes = existingNotes + separator + options2.appendNotes;
12669
13181
  }
12670
13182
  await fileStore.updateTask(id, updates);
13183
+ await notifyTaskUpdate(id);
12671
13184
  console.log(source_default.green(`\u2713 Updated task-${id}`));
12672
13185
  const changes = [];
12673
13186
  if (options2.title)
@@ -12724,17 +13237,17 @@ var archiveCommand = new Command("archive").description("Archive a task").argume
12724
13237
  console.error(source_default.red(`\u2717 Task ${id} not found`));
12725
13238
  process.exit(1);
12726
13239
  }
12727
- const archiveDir = join7(projectRoot, ".knowns", "archive");
13240
+ const archiveDir = join8(projectRoot, ".knowns", "archive");
12728
13241
  await mkdir4(archiveDir, { recursive: true });
12729
- const tasksPath = join7(projectRoot, ".knowns", "tasks");
13242
+ const tasksPath = join8(projectRoot, ".knowns", "tasks");
12730
13243
  const files = await readdir2(tasksPath);
12731
13244
  const taskFile = files.find((f) => f.startsWith(`task-${id} -`));
12732
13245
  if (!taskFile) {
12733
13246
  console.error(source_default.red(`\u2717 Task file for ${id} not found`));
12734
13247
  process.exit(1);
12735
13248
  }
12736
- const oldPath = join7(tasksPath, taskFile);
12737
- const newPath = join7(archiveDir, taskFile);
13249
+ const oldPath = join8(tasksPath, taskFile);
13250
+ const newPath = join8(archiveDir, taskFile);
12738
13251
  const content = await Bun.file(oldPath).text();
12739
13252
  await Bun.write(newPath, content);
12740
13253
  await unlink(oldPath);
@@ -12755,16 +13268,16 @@ var unarchiveCommand = new Command("unarchive").description("Restore archived ta
12755
13268
  console.error(source_default.gray(' Run "knowns init" to initialize'));
12756
13269
  process.exit(1);
12757
13270
  }
12758
- const archiveDir = join7(projectRoot, ".knowns", "archive");
12759
- const tasksPath = join7(projectRoot, ".knowns", "tasks");
13271
+ const archiveDir = join8(projectRoot, ".knowns", "archive");
13272
+ const tasksPath = join8(projectRoot, ".knowns", "tasks");
12760
13273
  const files = await readdir2(archiveDir);
12761
13274
  const taskFile = files.find((f) => f.startsWith(`task-${id} -`));
12762
13275
  if (!taskFile) {
12763
13276
  console.error(source_default.red(`\u2717 Archived task ${id} not found`));
12764
13277
  process.exit(1);
12765
13278
  }
12766
- const archivePath = join7(archiveDir, taskFile);
12767
- const tasksFilePath = join7(tasksPath, taskFile);
13279
+ const archivePath = join8(archiveDir, taskFile);
13280
+ const tasksFilePath = join8(tasksPath, taskFile);
12768
13281
  const content = await Bun.file(archivePath).text();
12769
13282
  await Bun.write(tasksFilePath, content);
12770
13283
  await unlink(archivePath);
@@ -13355,9 +13868,9 @@ var boardCommand = new Command("board").description("Display Kanban board").opti
13355
13868
  }
13356
13869
  });
13357
13870
  // src/commands/search.ts
13358
- import { existsSync as existsSync5 } from "fs";
13871
+ import { existsSync as existsSync6 } from "fs";
13359
13872
  import { readFile as readFile2, readdir as readdir3 } from "fs/promises";
13360
- import { join as join8 } from "path";
13873
+ import { join as join9 } from "path";
13361
13874
  var import_gray_matter2 = __toESM(require_gray_matter(), 1);
13362
13875
  function getFileStore3() {
13363
13876
  const projectRoot = findProjectRoot();
@@ -13409,8 +13922,8 @@ function calculateDocScore(doc, query) {
13409
13922
  return score;
13410
13923
  }
13411
13924
  async function searchDocs(query, projectRoot) {
13412
- const docsDir = join8(projectRoot, ".knowns", "docs");
13413
- if (!existsSync5(docsDir)) {
13925
+ const docsDir = join9(projectRoot, ".knowns", "docs");
13926
+ if (!existsSync6(docsDir)) {
13414
13927
  return [];
13415
13928
  }
13416
13929
  try {
@@ -13418,7 +13931,7 @@ async function searchDocs(query, projectRoot) {
13418
13931
  const mdFiles = files.filter((f) => f.endsWith(".md"));
13419
13932
  const results = [];
13420
13933
  for (const file of mdFiles) {
13421
- const content = await readFile2(join8(docsDir, file), "utf-8");
13934
+ const content = await readFile2(join9(docsDir, file), "utf-8");
13422
13935
  const { data, content: docContent } = import_gray_matter2.default(content);
13423
13936
  const metadata = data;
13424
13937
  const doc = {
@@ -13676,6 +14189,7 @@ var stopCommand = new Command("stop").description("Stop current timer and save t
13676
14189
  timeEntries: task.timeEntries,
13677
14190
  timeSpent: task.timeSpent
13678
14191
  });
14192
+ await notifyTaskUpdate(taskId);
13679
14193
  }
13680
14194
  data.active = null;
13681
14195
  await saveTimeData(projectRoot, data);
@@ -13847,6 +14361,7 @@ var addCommand = new Command("add").description("Manually add time entry to a ta
13847
14361
  timeEntries: task.timeEntries,
13848
14362
  timeSpent: task.timeSpent
13849
14363
  });
14364
+ await notifyTaskUpdate(taskId);
13850
14365
  console.log(source_default.green(`\u2713 Added ${formatDuration(seconds)} to #${taskId}: ${task.title}`));
13851
14366
  if (options2.note) {
13852
14367
  console.log(source_default.gray(` Note: ${options2.note}`));
@@ -13999,13 +14514,16 @@ timeCommand.addCommand(statusCommand);
13999
14514
  timeCommand.addCommand(logCommand);
14000
14515
  timeCommand.addCommand(addCommand);
14001
14516
  timeCommand.addCommand(reportCommand);
14517
+ // src/commands/browser.ts
14518
+ import { existsSync as existsSync8 } from "fs";
14519
+ import { mkdir as mkdir6, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
14520
+ import { join as join11 } from "path";
14521
+
14002
14522
  // src/server/index.ts
14003
- import { watch } from "fs";
14004
- import { existsSync as existsSync6 } from "fs";
14523
+ import { existsSync as existsSync7 } from "fs";
14005
14524
  import { mkdir as mkdir5, readFile as readFile3, readdir as readdir4, writeFile as writeFile2 } from "fs/promises";
14006
- import { join as join9, relative } from "path";
14525
+ import { join as join10, relative } from "path";
14007
14526
  var import_gray_matter3 = __toESM(require_gray_matter(), 1);
14008
- var buildVersion = Date.now();
14009
14527
  async function startServer(options2) {
14010
14528
  const { port, projectRoot, open } = options2;
14011
14529
  const store = new FileStore(projectRoot);
@@ -14019,89 +14537,13 @@ async function startServer(options2) {
14019
14537
  const currentDir = import.meta.dir;
14020
14538
  let packageRoot = currentDir;
14021
14539
  if (currentDir.endsWith("/dist")) {
14022
- packageRoot = join9(currentDir, "..");
14540
+ packageRoot = join10(currentDir, "..");
14023
14541
  } else if (currentDir.includes("/src/server")) {
14024
- packageRoot = join9(currentDir, "..", "..");
14025
- }
14026
- const uiSourcePath = join9(packageRoot, "src", "ui");
14027
- const uiDistPath = join9(packageRoot, "dist", "ui");
14028
- let shouldBuild = false;
14029
- if (existsSync6(join9(uiDistPath, "main.js"))) {
14030
- shouldBuild = false;
14031
- } else if (existsSync6(join9(uiSourcePath, "index.html"))) {
14032
- shouldBuild = true;
14033
- } else {
14034
- throw new Error(`UI files not found. Tried:
14035
- - ${uiDistPath} (${existsSync6(uiDistPath) ? "exists but no main.js" : "not found"})
14036
- - ${uiSourcePath} (${existsSync6(uiSourcePath) ? "exists but no index.html" : "not found"})
14037
- Package root: ${packageRoot}
14038
- Current dir: ${currentDir}`);
14039
- }
14040
- const buildUI = async () => {
14041
- if (!shouldBuild) {
14042
- return true;
14043
- }
14044
- console.log("Building UI...");
14045
- const startTime = Date.now();
14046
- if (!existsSync6(uiDistPath)) {
14047
- await mkdir5(uiDistPath, { recursive: true });
14048
- }
14049
- const buildResult = await Bun.build({
14050
- entrypoints: [join9(uiSourcePath, "main.tsx")],
14051
- outdir: uiDistPath,
14052
- target: "browser",
14053
- minify: false,
14054
- sourcemap: "inline",
14055
- define: {
14056
- "process.env.NODE_ENV": JSON.stringify("development")
14057
- }
14058
- });
14059
- if (!buildResult.success) {
14060
- console.error("Build errors:", buildResult.logs);
14061
- return false;
14062
- }
14063
- const cssResult = await Bun.build({
14064
- entrypoints: [join9(uiSourcePath, "index.css")],
14065
- outdir: uiDistPath,
14066
- target: "browser",
14067
- minify: false
14068
- });
14069
- if (!cssResult.success) {
14070
- console.error("CSS build errors:", cssResult.logs);
14071
- }
14072
- buildVersion = Date.now();
14073
- console.log(`UI built in ${Date.now() - startTime}ms`);
14074
- return true;
14075
- };
14076
- if (shouldBuild) {
14077
- if (!await buildUI()) {
14078
- throw new Error("Failed to build UI");
14079
- }
14542
+ packageRoot = join10(currentDir, "..", "..");
14080
14543
  }
14081
- let watcher = null;
14082
- if (shouldBuild) {
14083
- let rebuildTimeout = null;
14084
- watcher = watch(uiSourcePath, { recursive: true }, async (_event, filename) => {
14085
- if (!filename)
14086
- return;
14087
- if (!filename.endsWith(".tsx") && !filename.endsWith(".ts") && !filename.endsWith(".css")) {
14088
- return;
14089
- }
14090
- if (rebuildTimeout) {
14091
- clearTimeout(rebuildTimeout);
14092
- }
14093
- rebuildTimeout = setTimeout(async () => {
14094
- console.log(`File changed: ${filename}`);
14095
- const success = await buildUI();
14096
- if (success) {
14097
- broadcast({ type: "reload" });
14098
- }
14099
- }, 100);
14100
- });
14101
- process.on("SIGINT", () => {
14102
- watcher?.close();
14103
- process.exit(0);
14104
- });
14544
+ const uiDistPath = join10(packageRoot, "dist", "ui");
14545
+ if (!existsSync7(join10(uiDistPath, "index.html"))) {
14546
+ throw new Error(`UI build not found at ${uiDistPath}. Run 'bun run build:ui' first.`);
14105
14547
  }
14106
14548
  const server = Bun.serve({
14107
14549
  port,
@@ -14119,89 +14561,31 @@ Current dir: ${currentDir}`);
14119
14561
  if (url.pathname.startsWith("/api/")) {
14120
14562
  return handleAPI(req, url, store, broadcast);
14121
14563
  }
14122
- if (url.pathname === "/main.js" || url.pathname.startsWith("/main.js?")) {
14123
- const file = Bun.file(join9(uiDistPath, "main.js"));
14564
+ if (url.pathname.startsWith("/assets/")) {
14565
+ const filePath = join10(uiDistPath, url.pathname);
14566
+ const file = Bun.file(filePath);
14124
14567
  if (await file.exists()) {
14568
+ const ext = url.pathname.split(".").pop();
14569
+ const contentType = ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : ext === "svg" ? "image/svg+xml" : ext === "png" ? "image/png" : ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "woff2" ? "font/woff2" : ext === "woff" ? "font/woff" : "application/octet-stream";
14125
14570
  return new Response(file, {
14126
14571
  headers: {
14127
- "Content-Type": "application/javascript",
14128
- "Cache-Control": "no-cache, no-store, must-revalidate"
14572
+ "Content-Type": contentType,
14573
+ "Cache-Control": "public, max-age=31536000, immutable"
14129
14574
  }
14130
14575
  });
14131
14576
  }
14132
14577
  }
14133
- if (url.pathname === "/index.css" || url.pathname.startsWith("/index.css?")) {
14134
- const file = Bun.file(join9(uiDistPath, "index.css"));
14135
- if (await file.exists()) {
14136
- return new Response(file, {
14578
+ if (url.pathname === "/" || url.pathname === "/index.html" || !url.pathname.includes(".")) {
14579
+ const indexFile = Bun.file(join10(uiDistPath, "index.html"));
14580
+ if (await indexFile.exists()) {
14581
+ return new Response(indexFile, {
14137
14582
  headers: {
14138
- "Content-Type": "text/css",
14583
+ "Content-Type": "text/html",
14139
14584
  "Cache-Control": "no-cache, no-store, must-revalidate"
14140
14585
  }
14141
14586
  });
14142
14587
  }
14143
14588
  }
14144
- if (url.pathname === "/" || url.pathname === "/index.html") {
14145
- const html = `<!DOCTYPE html>
14146
- <html lang="en">
14147
- <head>
14148
- <meta charset="UTF-8" />
14149
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
14150
- <title>Knowns - Task Management</title>
14151
- <link rel="stylesheet" href="/index.css?v=${buildVersion}" />
14152
- </head>
14153
- <body>
14154
- <div id="root"></div>
14155
- <script type="module" src="/main.js?v=${buildVersion}"></script>
14156
- <script>
14157
- // Live reload via WebSocket
14158
- (function() {
14159
- let ws;
14160
- let reconnectInterval;
14161
-
14162
- function connect() {
14163
- ws = new WebSocket('ws://' + location.host + '/ws');
14164
-
14165
- ws.onopen = function() {
14166
- console.log('[LiveReload] Connected');
14167
- if (reconnectInterval) {
14168
- clearInterval(reconnectInterval);
14169
- reconnectInterval = null;
14170
- }
14171
- };
14172
-
14173
- ws.onmessage = function(event) {
14174
- const data = JSON.parse(event.data);
14175
- if (data.type === 'reload') {
14176
- console.log('[LiveReload] Reloading...');
14177
- location.reload();
14178
- }
14179
- };
14180
-
14181
- ws.onclose = function() {
14182
- console.log('[LiveReload] Disconnected, reconnecting...');
14183
- if (!reconnectInterval) {
14184
- reconnectInterval = setInterval(connect, 1000);
14185
- }
14186
- };
14187
-
14188
- ws.onerror = function() {
14189
- ws.close();
14190
- };
14191
- }
14192
-
14193
- connect();
14194
- })();
14195
- </script>
14196
- </body>
14197
- </html>`;
14198
- return new Response(html, {
14199
- headers: {
14200
- "Content-Type": "text/html",
14201
- "Cache-Control": "no-cache, no-store, must-revalidate"
14202
- }
14203
- });
14204
- }
14205
14589
  return new Response("Not Found", { status: 404 });
14206
14590
  },
14207
14591
  websocket: {
@@ -14214,13 +14598,7 @@ Current dir: ${currentDir}`);
14214
14598
  message(_ws, _message) {}
14215
14599
  }
14216
14600
  });
14217
- console.log(`Server running at http://localhost:${port}`);
14218
- console.log(`Open in browser: http://localhost:${port}`);
14219
- if (shouldBuild) {
14220
- console.log(`Live reload enabled - watching ${uiSourcePath}`);
14221
- } else {
14222
- console.log(`Serving pre-built UI from ${uiDistPath}`);
14223
- }
14601
+ console.log(`\u2713 Server running at http://localhost:${port}`);
14224
14602
  if (open) {
14225
14603
  const openCommand = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
14226
14604
  try {
@@ -14235,7 +14613,7 @@ async function findMarkdownFiles(dir, baseDir) {
14235
14613
  const files = [];
14236
14614
  const entries = await readdir4(dir, { withFileTypes: true });
14237
14615
  for (const entry of entries) {
14238
- const fullPath = join9(dir, entry.name);
14616
+ const fullPath = join10(dir, entry.name);
14239
14617
  if (entry.isDirectory()) {
14240
14618
  const subFiles = await findMarkdownFiles(fullPath, baseDir);
14241
14619
  files.push(...subFiles);
@@ -14261,7 +14639,13 @@ async function handleAPI(req, url, store, broadcast) {
14261
14639
  const tasks = await store.getAllTasks();
14262
14640
  return new Response(JSON.stringify(tasks), { headers });
14263
14641
  }
14264
- const taskMatch = url.pathname.match(/^\/api\/tasks\/(.+)$/);
14642
+ const historyMatch = url.pathname.match(/^\/api\/tasks\/(.+)\/history$/);
14643
+ if (historyMatch && req.method === "GET") {
14644
+ const taskId = historyMatch[1];
14645
+ const history = await store.getTaskVersionHistory(taskId);
14646
+ return new Response(JSON.stringify({ versions: history }), { headers });
14647
+ }
14648
+ const taskMatch = url.pathname.match(/^\/api\/tasks\/([^/]+)$/);
14265
14649
  if (taskMatch && req.method === "GET") {
14266
14650
  const task = await store.getTask(taskMatch[1]);
14267
14651
  if (!task) {
@@ -14273,10 +14657,18 @@ async function handleAPI(req, url, store, broadcast) {
14273
14657
  return new Response(JSON.stringify(task), { headers });
14274
14658
  }
14275
14659
  if (taskMatch && req.method === "PUT") {
14276
- const updates = await req.json();
14277
- const task = await store.updateTask(taskMatch[1], updates);
14278
- broadcast({ type: "tasks:updated", task });
14279
- return new Response(JSON.stringify(task), { headers });
14660
+ try {
14661
+ const updates = await req.json();
14662
+ const task = await store.updateTask(taskMatch[1], updates);
14663
+ broadcast({ type: "tasks:updated", task });
14664
+ return new Response(JSON.stringify(task), { headers });
14665
+ } catch (error) {
14666
+ console.error("Error updating task:", error);
14667
+ return new Response(JSON.stringify({ error: String(error) }), {
14668
+ status: 500,
14669
+ headers
14670
+ });
14671
+ }
14280
14672
  }
14281
14673
  if (url.pathname === "/api/tasks" && req.method === "POST") {
14282
14674
  const data = await req.json();
@@ -14287,14 +14679,45 @@ async function handleAPI(req, url, store, broadcast) {
14287
14679
  status: 201
14288
14680
  });
14289
14681
  }
14682
+ if (url.pathname === "/api/notify" && req.method === "POST") {
14683
+ try {
14684
+ const { taskId, type, docPath } = await req.json();
14685
+ if (taskId) {
14686
+ const task = await store.getTask(taskId);
14687
+ if (task) {
14688
+ broadcast({ type: "tasks:updated", task });
14689
+ return new Response(JSON.stringify({ success: true }), { headers });
14690
+ }
14691
+ } else if (type === "tasks:refresh") {
14692
+ broadcast({ type: "tasks:refresh" });
14693
+ return new Response(JSON.stringify({ success: true }), { headers });
14694
+ } else if (type === "docs:updated" && docPath) {
14695
+ broadcast({ type: "docs:updated", docPath });
14696
+ return new Response(JSON.stringify({ success: true }), { headers });
14697
+ } else if (type === "docs:refresh") {
14698
+ broadcast({ type: "docs:refresh" });
14699
+ return new Response(JSON.stringify({ success: true }), { headers });
14700
+ }
14701
+ return new Response(JSON.stringify({ success: false, error: "Invalid notify request" }), {
14702
+ status: 400,
14703
+ headers
14704
+ });
14705
+ } catch (error) {
14706
+ console.error("[Server] Notify error:", error);
14707
+ return new Response(JSON.stringify({ error: String(error) }), {
14708
+ status: 500,
14709
+ headers
14710
+ });
14711
+ }
14712
+ }
14290
14713
  if (url.pathname === "/api/docs" && req.method === "GET") {
14291
- const docsDir = join9(store.projectRoot, ".knowns", "docs");
14292
- if (!existsSync6(docsDir)) {
14714
+ const docsDir = join10(store.projectRoot, ".knowns", "docs");
14715
+ if (!existsSync7(docsDir)) {
14293
14716
  return new Response(JSON.stringify({ docs: [] }), { headers });
14294
14717
  }
14295
14718
  const mdFiles = await findMarkdownFiles(docsDir, docsDir);
14296
14719
  const docs = await Promise.all(mdFiles.map(async (relativePath) => {
14297
- const fullPath = join9(docsDir, relativePath);
14720
+ const fullPath = join10(docsDir, relativePath);
14298
14721
  const content = await readFile3(fullPath, "utf-8");
14299
14722
  const { data, content: docContent } = import_gray_matter3.default(content);
14300
14723
  const pathParts = relativePath.split("/");
@@ -14310,9 +14733,88 @@ async function handleAPI(req, url, store, broadcast) {
14310
14733
  }));
14311
14734
  return new Response(JSON.stringify({ docs }), { headers });
14312
14735
  }
14736
+ if (url.pathname.startsWith("/api/docs/") && req.method === "GET") {
14737
+ const docPath = decodeURIComponent(url.pathname.replace("/api/docs/", ""));
14738
+ const docsDir = join10(store.projectRoot, ".knowns", "docs");
14739
+ const fullPath = join10(docsDir, docPath);
14740
+ if (!fullPath.startsWith(docsDir)) {
14741
+ return new Response(JSON.stringify({ error: "Invalid path" }), {
14742
+ status: 400,
14743
+ headers
14744
+ });
14745
+ }
14746
+ if (!existsSync7(fullPath)) {
14747
+ return new Response(JSON.stringify({ error: "Document not found" }), {
14748
+ status: 404,
14749
+ headers
14750
+ });
14751
+ }
14752
+ const content = await readFile3(fullPath, "utf-8");
14753
+ const { data, content: docContent } = import_gray_matter3.default(content);
14754
+ const pathParts = docPath.split("/");
14755
+ const filename = pathParts[pathParts.length - 1];
14756
+ const folder = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
14757
+ const doc = {
14758
+ filename,
14759
+ path: docPath,
14760
+ folder,
14761
+ title: data.title || filename.replace(/\.md$/, ""),
14762
+ description: data.description || "",
14763
+ tags: data.tags || [],
14764
+ metadata: data,
14765
+ content: docContent
14766
+ };
14767
+ return new Response(JSON.stringify(doc), { headers });
14768
+ }
14769
+ if (url.pathname.startsWith("/api/docs/") && req.method === "PUT") {
14770
+ const docPath = decodeURIComponent(url.pathname.replace("/api/docs/", ""));
14771
+ const docsDir = join10(store.projectRoot, ".knowns", "docs");
14772
+ const fullPath = join10(docsDir, docPath);
14773
+ if (!fullPath.startsWith(docsDir)) {
14774
+ return new Response(JSON.stringify({ error: "Invalid path" }), {
14775
+ status: 400,
14776
+ headers
14777
+ });
14778
+ }
14779
+ if (!existsSync7(fullPath)) {
14780
+ return new Response(JSON.stringify({ error: "Document not found" }), {
14781
+ status: 404,
14782
+ headers
14783
+ });
14784
+ }
14785
+ const data = await req.json();
14786
+ const { content, title, description, tags } = data;
14787
+ const existingContent = await readFile3(fullPath, "utf-8");
14788
+ const { data: existingData } = import_gray_matter3.default(existingContent);
14789
+ const now = new Date().toISOString();
14790
+ const updatedFrontmatter = {
14791
+ ...existingData,
14792
+ title: title ?? existingData.title,
14793
+ description: description ?? existingData.description,
14794
+ tags: tags ?? existingData.tags,
14795
+ updatedAt: now
14796
+ };
14797
+ const newFileContent = import_gray_matter3.default.stringify(content ?? "", updatedFrontmatter);
14798
+ await writeFile2(fullPath, newFileContent, "utf-8");
14799
+ const pathParts = docPath.split("/");
14800
+ const filename = pathParts[pathParts.length - 1];
14801
+ const folder = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
14802
+ const updatedDoc = {
14803
+ filename,
14804
+ path: docPath,
14805
+ folder,
14806
+ title: updatedFrontmatter.title || filename.replace(/\.md$/, ""),
14807
+ description: updatedFrontmatter.description || "",
14808
+ tags: updatedFrontmatter.tags || [],
14809
+ metadata: updatedFrontmatter,
14810
+ content: content ?? ""
14811
+ };
14812
+ broadcast({ type: "docs:updated", doc: updatedDoc });
14813
+ return new Response(JSON.stringify(updatedDoc), { headers });
14814
+ }
14313
14815
  if (url.pathname === "/api/docs" && req.method === "POST") {
14314
- const docsDir = join9(store.projectRoot, ".knowns", "docs");
14315
- if (!existsSync6(docsDir)) {
14816
+ const docsDir = join10(store.projectRoot, ".knowns", "docs");
14817
+ if (!existsSync7(docsDir)) {
14316
14818
  await mkdir5(docsDir, { recursive: true });
14317
14819
  }
14318
14820
  const data = await req.json();
@@ -14328,16 +14830,16 @@ async function handleAPI(req, url, store, broadcast) {
14328
14830
  let targetDir;
14329
14831
  if (folder?.trim()) {
14330
14832
  const cleanFolder = folder.trim().replace(/^\/+|\/+$/g, "");
14331
- targetDir = join9(docsDir, cleanFolder);
14332
- filepath = join9(targetDir, filename);
14833
+ targetDir = join10(docsDir, cleanFolder);
14834
+ filepath = join10(targetDir, filename);
14333
14835
  } else {
14334
14836
  targetDir = docsDir;
14335
- filepath = join9(docsDir, filename);
14837
+ filepath = join10(docsDir, filename);
14336
14838
  }
14337
- if (!existsSync6(targetDir)) {
14839
+ if (!existsSync7(targetDir)) {
14338
14840
  await mkdir5(targetDir, { recursive: true });
14339
14841
  }
14340
- if (existsSync6(filepath)) {
14842
+ if (existsSync7(filepath)) {
14341
14843
  return new Response(JSON.stringify({ error: "Document with this title already exists in this folder" }), {
14342
14844
  status: 409,
14343
14845
  headers
@@ -14362,8 +14864,8 @@ async function handleAPI(req, url, store, broadcast) {
14362
14864
  }), { status: 201, headers });
14363
14865
  }
14364
14866
  if (url.pathname === "/api/config" && req.method === "GET") {
14365
- const configPath = join9(store.projectRoot, ".knowns", "config.json");
14366
- if (!existsSync6(configPath)) {
14867
+ const configPath = join10(store.projectRoot, ".knowns", "config.json");
14868
+ if (!existsSync7(configPath)) {
14367
14869
  return new Response(JSON.stringify({
14368
14870
  config: {
14369
14871
  name: "Knowns",
@@ -14390,9 +14892,9 @@ async function handleAPI(req, url, store, broadcast) {
14390
14892
  }
14391
14893
  if (url.pathname === "/api/config" && req.method === "POST") {
14392
14894
  const config = await req.json();
14393
- const configPath = join9(store.projectRoot, ".knowns", "config.json");
14895
+ const configPath = join10(store.projectRoot, ".knowns", "config.json");
14394
14896
  let existingData = {};
14395
- if (existsSync6(configPath)) {
14897
+ if (existsSync7(configPath)) {
14396
14898
  const content = await readFile3(configPath, "utf-8");
14397
14899
  existingData = JSON.parse(content);
14398
14900
  }
@@ -14405,6 +14907,33 @@ async function handleAPI(req, url, store, broadcast) {
14405
14907
  await writeFile2(configPath, JSON.stringify(merged, null, 2), "utf-8");
14406
14908
  return new Response(JSON.stringify({ success: true }), { headers });
14407
14909
  }
14910
+ if (url.pathname === "/api/activities" && req.method === "GET") {
14911
+ const limit = Number.parseInt(url.searchParams.get("limit") || "50", 10);
14912
+ const type = url.searchParams.get("type");
14913
+ const tasks = await store.getAllTasks();
14914
+ const allActivities = [];
14915
+ for (const task of tasks) {
14916
+ const versions = await store.getTaskVersionHistory(task.id);
14917
+ for (const version of versions) {
14918
+ if (type) {
14919
+ const hasMatchingChange = version.changes.some((c) => c.field === type);
14920
+ if (!hasMatchingChange)
14921
+ continue;
14922
+ }
14923
+ allActivities.push({
14924
+ taskId: task.id,
14925
+ taskTitle: task.title,
14926
+ version: version.version,
14927
+ timestamp: version.timestamp,
14928
+ author: version.author,
14929
+ changes: version.changes
14930
+ });
14931
+ }
14932
+ }
14933
+ allActivities.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
14934
+ const limited = allActivities.slice(0, limit);
14935
+ return new Response(JSON.stringify({ activities: limited }), { headers });
14936
+ }
14408
14937
  if (url.pathname === "/api/search" && req.method === "GET") {
14409
14938
  const query = url.searchParams.get("q");
14410
14939
  if (!query) {
@@ -14424,12 +14953,12 @@ async function handleAPI(req, url, store, broadcast) {
14424
14953
  ].join(" ").toLowerCase();
14425
14954
  return searchText.includes(q);
14426
14955
  });
14427
- const docsDir = join9(store.projectRoot, ".knowns", "docs");
14956
+ const docsDir = join10(store.projectRoot, ".knowns", "docs");
14428
14957
  const docResults = [];
14429
- if (existsSync6(docsDir)) {
14958
+ if (existsSync7(docsDir)) {
14430
14959
  const mdFiles = await findMarkdownFiles(docsDir, docsDir);
14431
14960
  for (const relativePath of mdFiles) {
14432
- const fullPath = join9(docsDir, relativePath);
14961
+ const fullPath = join10(docsDir, relativePath);
14433
14962
  const content = await readFile3(fullPath, "utf-8");
14434
14963
  const { data, content: docContent } = import_gray_matter3.default(content);
14435
14964
  const searchText = `${data.title || ""} ${data.description || ""} ${data.tags?.join(" ") || ""} ${docContent}`.toLowerCase();
@@ -14464,18 +14993,42 @@ async function handleAPI(req, url, store, broadcast) {
14464
14993
  }
14465
14994
 
14466
14995
  // src/commands/browser.ts
14467
- var browserCommand = new Command("browser").description("Open web UI for task management").option("-p, --port <port>", "Port number", "6420").option("--no-open", "Don't open browser automatically").action(async (options2) => {
14996
+ var DEFAULT_PORT2 = 6420;
14997
+ var CONFIG_FILE = ".knowns/config.json";
14998
+ async function saveServerPort(projectRoot, port) {
14999
+ const configPath = join11(projectRoot, CONFIG_FILE);
15000
+ const knownsDir = join11(projectRoot, ".knowns");
15001
+ if (!existsSync8(knownsDir)) {
15002
+ await mkdir6(knownsDir, { recursive: true });
15003
+ }
15004
+ try {
15005
+ let config = {};
15006
+ if (existsSync8(configPath)) {
15007
+ const content = await readFile4(configPath, "utf-8");
15008
+ config = JSON.parse(content);
15009
+ }
15010
+ const settings = config.settings || {};
15011
+ settings.serverPort = port;
15012
+ config.settings = settings;
15013
+ await writeFile3(configPath, JSON.stringify(config, null, 2), "utf-8");
15014
+ } catch {}
15015
+ }
15016
+ var browserCommand = new Command("browser").description("Open web UI for task management").option("-p, --port <port>", "Port number", String(DEFAULT_PORT2)).option("--no-open", "Don't open browser automatically").action(async (options2) => {
14468
15017
  const projectRoot = findProjectRoot();
14469
15018
  if (!projectRoot) {
14470
15019
  console.error(source_default.red("\u2717 Not in a Knowns project"));
14471
15020
  console.log(source_default.gray("Run 'knowns init' to initialize a project"));
14472
15021
  process.exit(1);
14473
15022
  }
15023
+ const port = Number.parseInt(options2.port);
15024
+ if (options2.port !== String(DEFAULT_PORT2)) {
15025
+ await saveServerPort(projectRoot, port);
15026
+ }
14474
15027
  console.log(source_default.cyan("\u25C6 Starting Knowns.dev Web UI..."));
14475
15028
  console.log("");
14476
15029
  try {
14477
15030
  await startServer({
14478
- port: Number.parseInt(options2.port),
15031
+ port,
14479
15032
  projectRoot,
14480
15033
  open: options2.open
14481
15034
  });
@@ -14487,17 +15040,17 @@ var browserCommand = new Command("browser").description("Open web UI for task ma
14487
15040
  }
14488
15041
  });
14489
15042
  // src/commands/doc.ts
14490
- import { existsSync as existsSync7 } from "fs";
14491
- import { mkdir as mkdir6, readFile as readFile4, readdir as readdir5, writeFile as writeFile3 } from "fs/promises";
14492
- import { join as join10 } from "path";
15043
+ import { existsSync as existsSync9 } from "fs";
15044
+ import { mkdir as mkdir7, readFile as readFile5, readdir as readdir5, writeFile as writeFile4 } from "fs/promises";
15045
+ import { join as join12 } from "path";
14493
15046
  var import_gray_matter4 = __toESM(require_gray_matter(), 1);
14494
- var DOCS_DIR = join10(process.cwd(), ".knowns", "docs");
15047
+ var DOCS_DIR = join12(process.cwd(), ".knowns", "docs");
14495
15048
  async function getAllMdFiles(dir, basePath = "") {
14496
15049
  const files = [];
14497
15050
  const entries = await readdir5(dir, { withFileTypes: true });
14498
15051
  for (const entry of entries) {
14499
- const fullPath = join10(dir, entry.name);
14500
- const relativePath = basePath ? join10(basePath, entry.name) : entry.name;
15052
+ const fullPath = join12(dir, entry.name);
15053
+ const relativePath = basePath ? join12(basePath, entry.name) : entry.name;
14501
15054
  if (entry.isDirectory()) {
14502
15055
  const subFiles = await getAllMdFiles(fullPath, relativePath);
14503
15056
  files.push(...subFiles);
@@ -14508,20 +15061,30 @@ async function getAllMdFiles(dir, basePath = "") {
14508
15061
  return files;
14509
15062
  }
14510
15063
  async function ensureDocsDir() {
14511
- if (!existsSync7(DOCS_DIR)) {
14512
- await mkdir6(DOCS_DIR, { recursive: true });
15064
+ if (!existsSync9(DOCS_DIR)) {
15065
+ await mkdir7(DOCS_DIR, { recursive: true });
14513
15066
  }
14514
15067
  }
14515
15068
  function titleToFilename(title) {
14516
15069
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
14517
15070
  }
14518
- var createCommand3 = new Command("create").description("Create a new documentation file").argument("<title>", "Document title").option("-d, --description <text>", "Document description").option("-t, --tags <tags>", "Comma-separated tags").option("--plain", "Plain text output for AI").action(async (title, options2) => {
15071
+ var createCommand3 = new Command("create").description("Create a new documentation file").argument("<title>", "Document title").option("-d, --description <text>", "Document description").option("-t, --tags <tags>", "Comma-separated tags").option("-f, --folder <path>", "Folder path (e.g., guides, patterns/auth)").option("--plain", "Plain text output for AI").action(async (title, options2) => {
14519
15072
  try {
14520
15073
  await ensureDocsDir();
14521
15074
  const filename = `${titleToFilename(title)}.md`;
14522
- const filepath = join10(DOCS_DIR, filename);
14523
- if (existsSync7(filepath)) {
14524
- console.error(source_default.red(`\u2717 Document already exists: ${filename}`));
15075
+ let targetDir = DOCS_DIR;
15076
+ let relativePath = filename;
15077
+ if (options2.folder) {
15078
+ const folderPath = options2.folder.replace(/^\/|\/$/g, "");
15079
+ targetDir = join12(DOCS_DIR, folderPath);
15080
+ relativePath = join12(folderPath, filename);
15081
+ if (!existsSync9(targetDir)) {
15082
+ await mkdir7(targetDir, { recursive: true });
15083
+ }
15084
+ }
15085
+ const filepath = join12(targetDir, filename);
15086
+ if (existsSync9(filepath)) {
15087
+ console.error(source_default.red(`\u2717 Document already exists: ${relativePath}`));
14525
15088
  process.exit(1);
14526
15089
  }
14527
15090
  const now = new Date().toISOString();
@@ -14540,11 +15103,12 @@ var createCommand3 = new Command("create").description("Create a new documentati
14540
15103
 
14541
15104
  Write your documentation here.
14542
15105
  `, metadata);
14543
- await writeFile3(filepath, content, "utf-8");
15106
+ await writeFile4(filepath, content, "utf-8");
15107
+ await notifyDocUpdate(relativePath);
14544
15108
  if (options2.plain) {
14545
- console.log(`Created: ${filename}`);
15109
+ console.log(`Created: ${relativePath}`);
14546
15110
  } else {
14547
- console.log(source_default.green(`\u2713 Created documentation: ${source_default.bold(filename)}`));
15111
+ console.log(source_default.green(`\u2713 Created documentation: ${source_default.bold(relativePath)}`));
14548
15112
  console.log(source_default.gray(` Location: ${filepath}`));
14549
15113
  }
14550
15114
  } catch (error) {
@@ -14567,7 +15131,7 @@ var listCommand2 = new Command("list").description("List all documentation files
14567
15131
  }
14568
15132
  const docs = [];
14569
15133
  for (const file of mdFiles) {
14570
- const content = await readFile4(join10(DOCS_DIR, file), "utf-8");
15134
+ const content = await readFile5(join12(DOCS_DIR, file), "utf-8");
14571
15135
  const { data } = import_gray_matter4.default(content);
14572
15136
  docs.push({
14573
15137
  filename: file,
@@ -14626,12 +15190,12 @@ var viewCommand2 = new Command("view").description("View a documentation file").
14626
15190
  try {
14627
15191
  await ensureDocsDir();
14628
15192
  let filename = name.endsWith(".md") ? name : `${name}.md`;
14629
- let filepath = join10(DOCS_DIR, filename);
14630
- if (!existsSync7(filepath)) {
15193
+ let filepath = join12(DOCS_DIR, filename);
15194
+ if (!existsSync9(filepath)) {
14631
15195
  filename = `${titleToFilename(name)}.md`;
14632
- filepath = join10(DOCS_DIR, filename);
15196
+ filepath = join12(DOCS_DIR, filename);
14633
15197
  }
14634
- if (!existsSync7(filepath)) {
15198
+ if (!existsSync9(filepath)) {
14635
15199
  const allFiles = await getAllMdFiles(DOCS_DIR);
14636
15200
  const searchName = name.toLowerCase().replace(/\.md$/, "");
14637
15201
  const matchingFile = allFiles.find((file) => {
@@ -14641,14 +15205,14 @@ var viewCommand2 = new Command("view").description("View a documentation file").
14641
15205
  });
14642
15206
  if (matchingFile) {
14643
15207
  filename = matchingFile;
14644
- filepath = join10(DOCS_DIR, matchingFile);
15208
+ filepath = join12(DOCS_DIR, matchingFile);
14645
15209
  }
14646
15210
  }
14647
- if (!existsSync7(filepath)) {
15211
+ if (!existsSync9(filepath)) {
14648
15212
  console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
14649
15213
  process.exit(1);
14650
15214
  }
14651
- const fileContent = await readFile4(filepath, "utf-8");
15215
+ const fileContent = await readFile5(filepath, "utf-8");
14652
15216
  const { data, content } = import_gray_matter4.default(fileContent);
14653
15217
  const metadata = data;
14654
15218
  if (options2.plain) {
@@ -14701,6 +15265,13 @@ var viewCommand2 = new Command("view").description("View a documentation file").
14701
15265
  for (const { original, resolved } of linksToAdd) {
14702
15266
  enhancedContent = enhancedContent.replace(original, resolved);
14703
15267
  }
15268
+ const knownProjectRoot = findProjectRoot();
15269
+ if (knownProjectRoot) {
15270
+ const fileStore = new FileStore(knownProjectRoot);
15271
+ const allTasks = await fileStore.getAllTasks();
15272
+ const taskMap = buildTaskMap(allTasks);
15273
+ enhancedContent = transformMentionsToRefs(enhancedContent, taskMap);
15274
+ }
14704
15275
  console.log(enhancedContent);
14705
15276
  } else {
14706
15277
  console.log(source_default.bold(`
@@ -14723,20 +15294,34 @@ var viewCommand2 = new Command("view").description("View a documentation file").
14723
15294
  process.exit(1);
14724
15295
  }
14725
15296
  });
14726
- var editCommand2 = new Command("edit").description("Edit a documentation file metadata").argument("<name>", "Document name").option("-t, --title <text>", "New title").option("-d, --description <text>", "New description").option("--tags <tags>", "Comma-separated tags").action(async (name, options2) => {
15297
+ 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(async (name, options2) => {
14727
15298
  try {
14728
15299
  await ensureDocsDir();
14729
15300
  let filename = name.endsWith(".md") ? name : `${name}.md`;
14730
- let filepath = join10(DOCS_DIR, filename);
14731
- if (!existsSync7(filepath)) {
14732
- filename = `${titleToFilename(name)}.md`;
14733
- filepath = join10(DOCS_DIR, filename);
15301
+ let filepath = join12(DOCS_DIR, filename);
15302
+ if (!existsSync9(filepath)) {
15303
+ const baseName = name.includes("/") ? name : titleToFilename(name);
15304
+ filename = baseName.endsWith(".md") ? baseName : `${baseName}.md`;
15305
+ filepath = join12(DOCS_DIR, filename);
14734
15306
  }
14735
- if (!existsSync7(filepath)) {
15307
+ if (!existsSync9(filepath)) {
15308
+ const allFiles = await getAllMdFiles(DOCS_DIR);
15309
+ const searchName = name.toLowerCase().replace(/\.md$/, "");
15310
+ const matchingFile = allFiles.find((file) => {
15311
+ const fileNameOnly = file.toLowerCase().replace(/\.md$/, "");
15312
+ const fileBaseName = file.split("/").pop()?.toLowerCase().replace(/\.md$/, "");
15313
+ return fileNameOnly === searchName || fileBaseName === searchName || file === name;
15314
+ });
15315
+ if (matchingFile) {
15316
+ filename = matchingFile;
15317
+ filepath = join12(DOCS_DIR, matchingFile);
15318
+ }
15319
+ }
15320
+ if (!existsSync9(filepath)) {
14736
15321
  console.error(source_default.red(`\u2717 Documentation not found: ${name}`));
14737
15322
  process.exit(1);
14738
15323
  }
14739
- const fileContent = await readFile4(filepath, "utf-8");
15324
+ const fileContent = await readFile5(filepath, "utf-8");
14740
15325
  const { data, content } = import_gray_matter4.default(fileContent);
14741
15326
  const metadata = data;
14742
15327
  if (options2.title)
@@ -14746,9 +15331,23 @@ var editCommand2 = new Command("edit").description("Edit a documentation file me
14746
15331
  if (options2.tags)
14747
15332
  metadata.tags = options2.tags.split(",").map((t) => t.trim());
14748
15333
  metadata.updatedAt = new Date().toISOString();
14749
- const newContent = import_gray_matter4.default.stringify(content, metadata);
14750
- await writeFile3(filepath, newContent, "utf-8");
14751
- console.log(source_default.green(`\u2713 Updated documentation: ${source_default.bold(filename)}`));
15334
+ let updatedContent = content;
15335
+ if (options2.content) {
15336
+ updatedContent = options2.content;
15337
+ }
15338
+ if (options2.append) {
15339
+ updatedContent = `${content.trimEnd()}
15340
+
15341
+ ${options2.append}`;
15342
+ }
15343
+ const newFileContent = import_gray_matter4.default.stringify(updatedContent, metadata);
15344
+ await writeFile4(filepath, newFileContent, "utf-8");
15345
+ await notifyDocUpdate(filename);
15346
+ if (options2.plain) {
15347
+ console.log(`Updated: ${filename}`);
15348
+ } else {
15349
+ console.log(source_default.green(`\u2713 Updated documentation: ${source_default.bold(filename)}`));
15350
+ }
14752
15351
  } catch (error) {
14753
15352
  console.error(source_default.red("Error editing documentation:"), error instanceof Error ? error.message : String(error));
14754
15353
  process.exit(1);
@@ -14756,9 +15355,9 @@ var editCommand2 = new Command("edit").description("Edit a documentation file me
14756
15355
  });
14757
15356
  var docCommand = new Command("doc").description("Manage documentation").addCommand(createCommand3).addCommand(listCommand2).addCommand(viewCommand2).addCommand(editCommand2);
14758
15357
  // src/commands/config.ts
14759
- import { existsSync as existsSync8 } from "fs";
14760
- import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
14761
- import { join as join11 } from "path";
15358
+ import { existsSync as existsSync10 } from "fs";
15359
+ import { mkdir as mkdir8, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
15360
+ import { join as join13 } from "path";
14762
15361
 
14763
15362
  // node_modules/zod/v4/classic/external.js
14764
15363
  var exports_external = {};
@@ -28173,20 +28772,22 @@ function date4(params) {
28173
28772
  // node_modules/zod/v4/classic/external.js
28174
28773
  config(en_default());
28175
28774
  // src/commands/config.ts
28176
- var CONFIG_FILE = ".knowns/config.json";
28775
+ var CONFIG_FILE2 = ".knowns/config.json";
28177
28776
  var ConfigSchema = exports_external.object({
28178
28777
  defaultAssignee: exports_external.string().optional(),
28179
28778
  defaultPriority: exports_external.enum(["low", "medium", "high"]).optional(),
28180
28779
  defaultLabels: exports_external.array(exports_external.string()).optional(),
28181
28780
  timeFormat: exports_external.enum(["12h", "24h"]).optional(),
28182
28781
  editor: exports_external.string().optional(),
28183
- visibleColumns: exports_external.array(exports_external.enum(["todo", "in-progress", "in-review", "done", "blocked"])).optional()
28782
+ visibleColumns: exports_external.array(exports_external.enum(["todo", "in-progress", "in-review", "done", "blocked"])).optional(),
28783
+ serverPort: exports_external.number().optional()
28184
28784
  });
28185
28785
  var DEFAULT_CONFIG = {
28186
28786
  defaultPriority: "medium",
28187
28787
  defaultLabels: [],
28188
28788
  timeFormat: "24h",
28189
- visibleColumns: ["todo", "in-progress", "done"]
28789
+ visibleColumns: ["todo", "in-progress", "done"],
28790
+ serverPort: 6420
28190
28791
  };
28191
28792
  function getProjectRoot2() {
28192
28793
  const projectRoot = findProjectRoot();
@@ -28198,12 +28799,12 @@ function getProjectRoot2() {
28198
28799
  return projectRoot;
28199
28800
  }
28200
28801
  async function loadConfig(projectRoot) {
28201
- const configPath = join11(projectRoot, CONFIG_FILE);
28202
- if (!existsSync8(configPath)) {
28802
+ const configPath = join13(projectRoot, CONFIG_FILE2);
28803
+ if (!existsSync10(configPath)) {
28203
28804
  return { ...DEFAULT_CONFIG };
28204
28805
  }
28205
28806
  try {
28206
- const content = await readFile5(configPath, "utf-8");
28807
+ const content = await readFile6(configPath, "utf-8");
28207
28808
  const data = JSON.parse(content);
28208
28809
  const settings = data.settings || {};
28209
28810
  const validated = ConfigSchema.parse(settings);
@@ -28217,22 +28818,22 @@ async function loadConfig(projectRoot) {
28217
28818
  }
28218
28819
  }
28219
28820
  async function saveConfig(projectRoot, config2) {
28220
- const configPath = join11(projectRoot, CONFIG_FILE);
28221
- const knownsDir = join11(projectRoot, ".knowns");
28222
- if (!existsSync8(knownsDir)) {
28223
- await mkdir7(knownsDir, { recursive: true });
28821
+ const configPath = join13(projectRoot, CONFIG_FILE2);
28822
+ const knownsDir = join13(projectRoot, ".knowns");
28823
+ if (!existsSync10(knownsDir)) {
28824
+ await mkdir8(knownsDir, { recursive: true });
28224
28825
  }
28225
28826
  try {
28226
28827
  let existingData = {};
28227
- if (existsSync8(configPath)) {
28228
- const content = await readFile5(configPath, "utf-8");
28828
+ if (existsSync10(configPath)) {
28829
+ const content = await readFile6(configPath, "utf-8");
28229
28830
  existingData = JSON.parse(content);
28230
28831
  }
28231
28832
  const merged = {
28232
28833
  ...existingData,
28233
28834
  settings: config2
28234
28835
  };
28235
- await writeFile4(configPath, JSON.stringify(merged, null, 2), "utf-8");
28836
+ await writeFile5(configPath, JSON.stringify(merged, null, 2), "utf-8");
28236
28837
  } catch (error46) {
28237
28838
  console.error(source_default.red("\u2717 Failed to save config"));
28238
28839
  if (error46 instanceof Error) {
@@ -28374,9 +28975,94 @@ var resetCommand = new Command("reset").description("Reset configuration to defa
28374
28975
  }
28375
28976
  });
28376
28977
  var configCommand = new Command("config").description("Manage configuration settings").addCommand(listCommand3).addCommand(getCommand).addCommand(setCommand).addCommand(resetCommand);
28978
+ // package.json
28979
+ var package_default = {
28980
+ name: "knowns",
28981
+ version: "0.1.6",
28982
+ description: "CLI tool for dev teams to manage tasks and documentation",
28983
+ module: "index.ts",
28984
+ type: "module",
28985
+ bin: {
28986
+ knowns: "./dist/index.js",
28987
+ kn: "./dist/index.js"
28988
+ },
28989
+ main: "./dist/index.js",
28990
+ files: ["dist", "CLAUDE.md", "README.md", "CHANGELOG.md", "LICENSE"],
28991
+ keywords: ["cli", "task-management", "documentation", "knowledge-base", "team-tools", "developer-tools"],
28992
+ author: "howznguyen <howznguyen@knowns.dev>",
28993
+ license: "MIT",
28994
+ repository: {
28995
+ type: "git",
28996
+ url: "https://github.com/knowns-dev/knowns.git"
28997
+ },
28998
+ homepage: "https://knowns.dev",
28999
+ bugs: {
29000
+ url: "https://github.com/knowns-dev/knowns/issues"
29001
+ },
29002
+ scripts: {
29003
+ dev: "bun run --watch src/index.ts browser --no-open",
29004
+ watch: "nodemon",
29005
+ build: "bun run build:cli && bun run build:ui",
29006
+ "build:cli": "bun build src/index.ts --outdir dist --target bun && bun build src/mcp/server.ts --outdir dist/mcp --target bun",
29007
+ "build:ui": "vite build",
29008
+ start: "bun run src/index.ts",
29009
+ mcp: "bun run src/mcp/server.ts",
29010
+ test: "bun test",
29011
+ lint: "biome check .",
29012
+ "lint:fix": "biome check --write .",
29013
+ format: "biome format --write .",
29014
+ prepare: "husky"
29015
+ },
29016
+ dependencies: {
29017
+ "@modelcontextprotocol/sdk": "^1.25.1",
29018
+ "@radix-ui/react-collapsible": "^1.1.12",
29019
+ "@radix-ui/react-dialog": "^1.1.15",
29020
+ "@radix-ui/react-navigation-menu": "^1.2.14",
29021
+ "@radix-ui/react-popover": "^1.1.15",
29022
+ "@radix-ui/react-scroll-area": "^1.2.10",
29023
+ "@radix-ui/react-separator": "^1.1.8",
29024
+ "@radix-ui/react-slot": "^1.2.4",
29025
+ "@radix-ui/react-tooltip": "^1.2.8",
29026
+ "@uiw/react-md-editor": "^4.0.11",
29027
+ chalk: "^5.3.0",
29028
+ "class-variance-authority": "^0.7.1",
29029
+ clsx: "^2.1.1",
29030
+ cmdk: "^1.1.1",
29031
+ commander: "^12.1.0",
29032
+ "gray-matter": "^4.0.3",
29033
+ "lucide-react": "^0.562.0",
29034
+ marked: "^17.0.1",
29035
+ prompts: "^2.4.2",
29036
+ react: "^19.2.3",
29037
+ "react-diff-viewer": "^3.1.1",
29038
+ "react-dom": "^19.2.3",
29039
+ "tailwind-merge": "^3.4.0",
29040
+ turndown: "^7.2.2",
29041
+ "unist-util-visit": "^5.0.0",
29042
+ zod: "^4.2.1"
29043
+ },
29044
+ devDependencies: {
29045
+ "@biomejs/biome": "^1.9.4",
29046
+ "@tailwindcss/vite": "^4.1.18",
29047
+ "@types/bun": "latest",
29048
+ "@types/prompts": "^2.4.9",
29049
+ "@types/react": "^19.2.7",
29050
+ "@types/react-dom": "^19.2.3",
29051
+ "@vitejs/plugin-react": "^5.1.2",
29052
+ autoprefixer: "^10.4.23",
29053
+ concurrently: "^9.2.1",
29054
+ husky: "^9.1.7",
29055
+ "lint-staged": "^15.2.10",
29056
+ nodemon: "^3.1.11",
29057
+ tailwindcss: "^4.1.18",
29058
+ typescript: "^5.7.2",
29059
+ vite: "^7.3.0"
29060
+ }
29061
+ };
29062
+
28377
29063
  // src/index.ts
28378
29064
  var program2 = new Command;
28379
- program2.name("knowns").description("CLI tool for dev teams to manage tasks, track time, and sync").version("0.1.2");
29065
+ program2.name("knowns").description("CLI tool for dev teams to manage tasks, track time, and sync").version(package_default.version);
28380
29066
  program2.addCommand(initCommand);
28381
29067
  program2.addCommand(taskCommand);
28382
29068
  program2.addCommand(boardCommand);