Przeglądaj źródła

Improve qmd ls to use ls -l style with colors

- Add file size and modification date columns
- Format dates like ls -l (recent: Month Day Time, old: Month Day Year)
- Add colors: cyan filenames, dimmed qmd:// prefix
- Join with content table to get file sizes
- Format sizes in human-readable units (B, KB, MB)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Tobi Lutke 5 miesięcy temu
rodzic
commit
63b2138140
2 zmienionych plików z 38 dodań i 7 usunięć
  1. 1 0
      .beads/issues.jsonl
  2. 37 7
      src/qmd.ts

+ 1 - 0
.beads/issues.jsonl

@@ -24,3 +24,4 @@
 {"id":"qmd-vro","title":"Update 'qmd get' to support virtual paths","description":"Allow qmd get to accept both virtual paths (qmd://journals/...) and filesystem paths, plus fuzzy matching by filename.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-12T15:29:53.963113-05:00","updated_at":"2025-12-12T15:47:29.178955-05:00","closed_at":"2025-12-12T15:47:29.178955-05:00","dependencies":[{"issue_id":"qmd-vro","depends_on_id":"qmd-ama","type":"discovered-from","created_at":"2025-12-12T15:29:53.963641-05:00","created_by":"daemon"}]}
 {"id":"qmd-x19","title":"Update 'qmd add-context' for collection-scoped contexts","description":"Update add-context to work with collections:\n- qmd add-context \u003ccollection\u003e/\u003cpath\u003e \"context description\"\n- Support both virtual and filesystem paths\n- Update to use new path_contexts schema","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-12T15:29:38.142575-05:00","updated_at":"2025-12-12T15:53:00.525001-05:00","closed_at":"2025-12-12T15:53:00.525001-05:00"}
 {"id":"qmd-x64","title":"for each collection, on update, check if there is a .git directory, if so write out the git status, add --pull as a qmd update --pull parameter which also executes git pull before reindexing\n","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-12T17:04:15.994054-05:00","updated_at":"2025-12-12T17:14:40.107181-05:00","closed_at":"2025-12-12T17:14:40.107181-05:00"}
+{"id":"qmd-zin","title":"Improve qmd ls command to be more like ls -l with colors","description":"Make qmd ls more Unix-like:\n1. Format like ls -l with columns (permissions, size, date, name)\n2. Add colors (directories, files, etc.)\n3. Dim the qmd:// prefix to show it's optional\n4. Show file sizes in human-readable format\n5. Show modification times\n6. Consider adding -l flag for long format","status":"in_progress","priority":1,"issue_type":"task","created_at":"2025-12-13T09:44:48.703843-05:00","updated_at":"2025-12-13T09:44:54.304289-05:00"}

+ 37 - 7
src/qmd.ts

@@ -1248,7 +1248,7 @@ function listFiles(pathArg?: string): void {
 
     console.log(`${c.bold}Collections:${c.reset}\n`);
     for (const coll of collections) {
-      console.log(`${c.cyan}qmd://${coll.name}/${c.reset} (${coll.file_count} files)`);
+      console.log(`  ${c.dim}qmd://${c.reset}${c.cyan}${coll.name}/${c.reset}  ${c.dim}(${coll.file_count} files)${c.reset}`);
     }
     closeDb();
     return;
@@ -1286,15 +1286,16 @@ function listFiles(pathArg?: string): void {
     process.exit(1);
   }
 
-  // List files in the collection (optionally filtered by path prefix)
+  // List files in the collection with size and modification time
   let query: string;
   let params: any[];
 
   if (pathPrefix) {
     // List files under a specific path
     query = `
-      SELECT d.path
+      SELECT d.path, d.title, d.modified_at, LENGTH(ct.doc) as size
       FROM documents d
+      JOIN content ct ON d.hash = ct.hash
       WHERE d.collection_id = ? AND d.path LIKE ? AND d.active = 1
       ORDER BY d.path
     `;
@@ -1302,15 +1303,16 @@ function listFiles(pathArg?: string): void {
   } else {
     // List all files in the collection
     query = `
-      SELECT d.path
+      SELECT d.path, d.title, d.modified_at, LENGTH(ct.doc) as size
       FROM documents d
+      JOIN content ct ON d.hash = ct.hash
       WHERE d.collection_id = ? AND d.active = 1
       ORDER BY d.path
     `;
     params = [coll.id];
   }
 
-  const files = db.prepare(query).all(...params) as { path: string }[];
+  const files = db.prepare(query).all(...params) as { path: string; title: string; modified_at: string; size: number }[];
 
   if (files.length === 0) {
     if (pathPrefix) {
@@ -1322,14 +1324,42 @@ function listFiles(pathArg?: string): void {
     return;
   }
 
-  // Output virtual paths
+  // Calculate max widths for alignment
+  const maxSize = Math.max(...files.map(f => formatBytes(f.size).length));
+
+  // Output in ls -l style
   for (const file of files) {
-    console.log(buildVirtualPath(collectionName, file.path));
+    const sizeStr = formatBytes(file.size).padStart(maxSize);
+    const date = new Date(file.modified_at);
+    const timeStr = formatLsTime(date);
+
+    // Dim the qmd:// prefix, highlight the filename
+    console.log(`${sizeStr}  ${timeStr}  ${c.dim}qmd://${collectionName}/${c.reset}${c.cyan}${file.path}${c.reset}`);
   }
 
   closeDb();
 }
 
+// Format date/time like ls -l
+function formatLsTime(date: Date): string {
+  const now = new Date();
+  const sixMonthsAgo = new Date(now.getTime() - 6 * 30 * 24 * 60 * 60 * 1000);
+
+  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+  const month = months[date.getMonth()];
+  const day = date.getDate().toString().padStart(2, ' ');
+
+  // If file is older than 6 months, show year instead of time
+  if (date < sixMonthsAgo) {
+    const year = date.getFullYear();
+    return `${month} ${day}  ${year}`;
+  } else {
+    const hours = date.getHours().toString().padStart(2, '0');
+    const minutes = date.getMinutes().toString().padStart(2, '0');
+    return `${month} ${day} ${hours}:${minutes}`;
+  }
+}
+
 // Collection management commands
 function collectionList(): void {
   const db = getDb();