@aigne/afs-cli 1.11.0-beta.5 → 1.11.0-beta.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.
Files changed (62) hide show
  1. package/dist/cli.cjs +41 -18
  2. package/dist/cli.mjs +42 -19
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/commands/exec.cjs +132 -14
  5. package/dist/commands/exec.mjs +129 -14
  6. package/dist/commands/exec.mjs.map +1 -1
  7. package/dist/commands/explain.cjs +1 -1
  8. package/dist/commands/explain.mjs +1 -1
  9. package/dist/commands/explain.mjs.map +1 -1
  10. package/dist/commands/index.mjs +1 -1
  11. package/dist/commands/ls.cjs +129 -30
  12. package/dist/commands/ls.mjs +129 -30
  13. package/dist/commands/ls.mjs.map +1 -1
  14. package/dist/commands/read.cjs +213 -14
  15. package/dist/commands/read.mjs +213 -14
  16. package/dist/commands/read.mjs.map +1 -1
  17. package/dist/commands/stat.cjs +116 -34
  18. package/dist/commands/stat.mjs +117 -34
  19. package/dist/commands/stat.mjs.map +1 -1
  20. package/dist/commands/write.cjs +37 -4
  21. package/dist/commands/write.mjs +38 -4
  22. package/dist/commands/write.mjs.map +1 -1
  23. package/dist/config/loader.cjs +12 -1
  24. package/dist/config/loader.mjs +12 -1
  25. package/dist/config/loader.mjs.map +1 -1
  26. package/dist/config/provider-factory.cjs +310 -3
  27. package/dist/config/provider-factory.mjs +310 -3
  28. package/dist/config/provider-factory.mjs.map +1 -1
  29. package/dist/config/uri-parser.cjs +195 -2
  30. package/dist/config/uri-parser.mjs +195 -2
  31. package/dist/config/uri-parser.mjs.map +1 -1
  32. package/dist/explorer/actions.cjs +53 -23
  33. package/dist/explorer/actions.mjs +54 -23
  34. package/dist/explorer/actions.mjs.map +1 -1
  35. package/dist/explorer/components/dialog.cjs +163 -10
  36. package/dist/explorer/components/dialog.mjs +163 -10
  37. package/dist/explorer/components/dialog.mjs.map +1 -1
  38. package/dist/explorer/components/file-list.mjs.map +1 -1
  39. package/dist/explorer/components/metadata-panel.cjs +39 -25
  40. package/dist/explorer/components/metadata-panel.mjs +39 -25
  41. package/dist/explorer/components/metadata-panel.mjs.map +1 -1
  42. package/dist/explorer/screen.cjs +23 -8
  43. package/dist/explorer/screen.mjs +24 -9
  44. package/dist/explorer/screen.mjs.map +1 -1
  45. package/dist/explorer/theme.cjs +3 -1
  46. package/dist/explorer/theme.mjs +3 -1
  47. package/dist/explorer/theme.mjs.map +1 -1
  48. package/dist/path-utils.cjs +2 -1
  49. package/dist/path-utils.mjs +1 -1
  50. package/dist/runtime.cjs +24 -0
  51. package/dist/runtime.mjs +24 -0
  52. package/dist/runtime.mjs.map +1 -1
  53. package/dist/ui/header.cjs +0 -9
  54. package/dist/ui/header.mjs +1 -9
  55. package/dist/ui/header.mjs.map +1 -1
  56. package/dist/ui/index.cjs +0 -2
  57. package/dist/ui/index.mjs +2 -3
  58. package/dist/ui/index.mjs.map +1 -1
  59. package/dist/utils/meta.cjs +51 -0
  60. package/dist/utils/meta.mjs +49 -0
  61. package/dist/utils/meta.mjs.map +1 -0
  62. package/package.json +19 -9
@@ -1,16 +1,108 @@
1
+ import YAML from "yaml";
2
+
1
3
  //#region src/commands/exec.ts
2
4
  /**
5
+ * Reserved CLI option names that should not be passed as exec arguments
6
+ */
7
+ const RESERVED_OPTIONS = new Set([
8
+ "json",
9
+ "yaml",
10
+ "view",
11
+ "help",
12
+ "h",
13
+ "version",
14
+ "V",
15
+ "_",
16
+ "$0",
17
+ "args"
18
+ ]);
19
+ /**
20
+ * Read JSON from stdin (non-blocking if TTY)
21
+ */
22
+ async function readStdin() {
23
+ if (process.stdin.isTTY) return null;
24
+ const chunks = [];
25
+ for await (const chunk of process.stdin) chunks.push(chunk);
26
+ return Buffer.concat(chunks).toString("utf-8").trim() || null;
27
+ }
28
+ /**
29
+ * Parse exec arguments with stdin support (async version)
30
+ *
31
+ * Priority: CLI flags > stdin > --args
32
+ */
33
+ async function parseExecArgsWithStdin(options) {
34
+ let result = {};
35
+ if (options.args && typeof options.args === "string") try {
36
+ const parsed = JSON.parse(options.args);
37
+ if (typeof parsed === "object" && parsed !== null) result = { ...parsed };
38
+ } catch (e) {
39
+ throw new Error(`Invalid JSON in --args: ${e.message}`);
40
+ }
41
+ const stdinContent = await readStdin();
42
+ if (stdinContent) try {
43
+ const parsed = JSON.parse(stdinContent);
44
+ if (typeof parsed === "object" && parsed !== null) result = {
45
+ ...result,
46
+ ...parsed
47
+ };
48
+ } catch (e) {
49
+ throw new Error(`Invalid JSON in stdin: ${e.message}`);
50
+ }
51
+ for (const [key, value] of Object.entries(options)) {
52
+ if (RESERVED_OPTIONS.has(key) || value === void 0 || value === null) continue;
53
+ result[key] = value;
54
+ }
55
+ return result;
56
+ }
57
+ /**
58
+ * Check if an entry is executable based on its metadata
59
+ *
60
+ * An entry is executable if:
61
+ * 1. Its `kinds` array includes "afs:executable", OR
62
+ * 2. Its `kind` field equals "afs:executable" (legacy support)
63
+ */
64
+ function isExecutable(metadata) {
65
+ if (!metadata) return false;
66
+ if (Array.isArray(metadata.kinds)) return metadata.kinds.includes("afs:executable");
67
+ return metadata.kind === "afs:executable";
68
+ }
69
+ /**
3
70
  * Execute an action on an AFS path
4
71
  *
5
- * Note: exec is not widely implemented by providers yet.
6
- * This is a placeholder for future functionality.
72
+ * This function directly executes the path without pre-checking if it's executable.
73
+ * The provider will return an appropriate error if the path is not executable.
74
+ *
75
+ * @param runtime - AFS runtime
76
+ * @param path - Path to execute
77
+ * @param args - Arguments to pass to the executable
78
+ * @returns Exec result
7
79
  */
8
- async function execCommand(_runtime, path, _action, _params = {}) {
9
- return {
10
- path,
11
- success: false,
12
- message: "exec operation is not yet implemented"
13
- };
80
+ async function execCommand(runtime, path, args = {}) {
81
+ try {
82
+ const execResult = await runtime.exec(path, args);
83
+ return {
84
+ path,
85
+ success: execResult.success,
86
+ data: execResult.data,
87
+ message: execResult.error?.message,
88
+ error: execResult.error
89
+ };
90
+ } catch (error) {
91
+ const errorCode = extractErrorCode(error);
92
+ return {
93
+ path,
94
+ success: false,
95
+ message: error instanceof Error ? error.message : String(error),
96
+ error: errorCode ? { code: errorCode } : void 0
97
+ };
98
+ }
99
+ }
100
+ /**
101
+ * Extract error code from an error object
102
+ */
103
+ function extractErrorCode(error) {
104
+ if (!error || typeof error !== "object") return void 0;
105
+ if ("code" in error && typeof error.code === "string") return error.code;
14
106
  }
15
107
  /**
16
108
  * Format exec output for different views
@@ -18,28 +110,51 @@ async function execCommand(_runtime, path, _action, _params = {}) {
18
110
  function formatExecOutput(result, view) {
19
111
  switch (view) {
20
112
  case "json": return JSON.stringify(result, null, 2);
113
+ case "yaml": return YAML.stringify(result);
21
114
  case "llm": return formatLlm(result);
22
115
  case "human": return formatHuman(result);
23
116
  default: return formatDefault(result);
24
117
  }
25
118
  }
26
119
  function formatDefault(result) {
27
- if (result.success) return `OK ${result.path}`;
28
- return `ERROR ${result.path} ${result.message}`;
120
+ if (result.success) {
121
+ if (result.data) return JSON.stringify(result.data);
122
+ return `OK ${result.path}`;
123
+ }
124
+ return `${result.error?.code || "ERROR"} ${result.path} ${result.message || ""}`.trim();
29
125
  }
30
126
  function formatLlm(result) {
31
127
  const lines = [];
32
128
  lines.push(`EXEC ${result.path}`);
33
129
  lines.push(`STATUS ${result.success ? "SUCCESS" : "FAILED"}`);
34
130
  if (result.data) lines.push(`DATA ${JSON.stringify(result.data)}`);
35
- if (result.message) lines.push(`MESSAGE ${result.message}`);
131
+ if (!result.success && result.error) {
132
+ if (result.error.code) lines.push(`ERROR ${result.error.code}`);
133
+ if (result.message) lines.push(`MESSAGE ${result.message}`);
134
+ if (result.error.details) lines.push(`DETAILS ${JSON.stringify(result.error.details)}`);
135
+ } else if (result.message) lines.push(`MESSAGE ${result.message}`);
36
136
  return lines.join("\n");
37
137
  }
38
138
  function formatHuman(result) {
39
- if (result.success) return `Executed on ${result.path}\n${result.data ? JSON.stringify(result.data, null, 2) : ""}`;
40
- return `Failed to execute on ${result.path}: ${result.message}`;
139
+ if (result.success) {
140
+ const lines$1 = [`✓ Executed: ${result.path}`];
141
+ if (result.data) {
142
+ lines$1.push("");
143
+ lines$1.push(JSON.stringify(result.data, null, 2));
144
+ }
145
+ return lines$1.join("\n");
146
+ }
147
+ const lines = [`✗ Failed: ${result.path}`];
148
+ lines.push("");
149
+ if (result.error?.code) lines.push(`Error: ${result.error.code}`);
150
+ if (result.message) lines.push(`Message: ${result.message}`);
151
+ if (result.error?.details) {
152
+ lines.push("Details:");
153
+ for (const [key, value] of Object.entries(result.error.details)) lines.push(` ${key}: ${JSON.stringify(value)}`);
154
+ }
155
+ return lines.join("\n");
41
156
  }
42
157
 
43
158
  //#endregion
44
- export { execCommand, formatExecOutput };
159
+ export { execCommand, formatExecOutput, isExecutable, parseExecArgsWithStdin };
45
160
  //# sourceMappingURL=exec.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exec.mjs","names":[],"sources":["../../src/commands/exec.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface ExecResult {\n path: string;\n success: boolean;\n data?: Record<string, unknown>;\n message?: string;\n}\n\n/**\n * Execute an action on an AFS path\n *\n * Note: exec is not widely implemented by providers yet.\n * This is a placeholder for future functionality.\n */\nexport async function execCommand(\n _runtime: AFSRuntime,\n path: string,\n _action: string,\n _params: Record<string, unknown> = {},\n): Promise<ExecResult> {\n // exec is not yet implemented in the runtime\n // This is a placeholder that returns an error\n\n return {\n path,\n success: false,\n message: \"exec operation is not yet implemented\",\n };\n}\n\n/**\n * Format exec output for different views\n */\nexport function formatExecOutput(result: ExecResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExecResult): string {\n if (result.success) {\n return `OK ${result.path}`;\n }\n return `ERROR ${result.path} ${result.message}`;\n}\n\nfunction formatLlm(result: ExecResult): string {\n const lines: string[] = [];\n\n lines.push(`EXEC ${result.path}`);\n lines.push(`STATUS ${result.success ? \"SUCCESS\" : \"FAILED\"}`);\n\n if (result.data) {\n lines.push(`DATA ${JSON.stringify(result.data)}`);\n }\n\n if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatHuman(result: ExecResult): string {\n if (result.success) {\n return `Executed on ${result.path}\\n${result.data ? JSON.stringify(result.data, null, 2) : \"\"}`;\n }\n return `Failed to execute on ${result.path}: ${result.message}`;\n}\n"],"mappings":";;;;;;;AAgBA,eAAsB,YACpB,UACA,MACA,SACA,UAAmC,EAAE,EAChB;AAIrB,QAAO;EACL;EACA,SAAS;EACT,SAAS;EACV;;;;;AAMH,SAAgB,iBAAiB,QAAoB,MAAwB;AAC3E,SAAQ,MAAR;EACE,KAAK,OACH,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;EACxC,KAAK,MACH,QAAO,UAAU,OAAO;EAC1B,KAAK,QACH,QAAO,YAAY,OAAO;EAC5B,QACE,QAAO,cAAc,OAAO;;;AAIlC,SAAS,cAAc,QAA4B;AACjD,KAAI,OAAO,QACT,QAAO,MAAM,OAAO;AAEtB,QAAO,SAAS,OAAO,KAAK,GAAG,OAAO;;AAGxC,SAAS,UAAU,QAA4B;CAC7C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,QAAQ,OAAO,OAAO;AACjC,OAAM,KAAK,UAAU,OAAO,UAAU,YAAY,WAAW;AAE7D,KAAI,OAAO,KACT,OAAM,KAAK,QAAQ,KAAK,UAAU,OAAO,KAAK,GAAG;AAGnD,KAAI,OAAO,QACT,OAAM,KAAK,WAAW,OAAO,UAAU;AAGzC,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,YAAY,QAA4B;AAC/C,KAAI,OAAO,QACT,QAAO,eAAe,OAAO,KAAK,IAAI,OAAO,OAAO,KAAK,UAAU,OAAO,MAAM,MAAM,EAAE,GAAG;AAE7F,QAAO,wBAAwB,OAAO,KAAK,IAAI,OAAO"}
1
+ {"version":3,"file":"exec.mjs","names":["lines"],"sources":["../../src/commands/exec.ts"],"sourcesContent":["import type { AFSEntryMetadata } from \"@aigne/afs\";\nimport YAML from \"yaml\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\n/**\n * Extended view type that includes yaml\n */\nexport type ExecViewType = ViewType | \"yaml\";\n\nexport interface ExecResult {\n path: string;\n success: boolean;\n data?: Record<string, unknown>;\n message?: string;\n error?: {\n code?: string;\n message?: string;\n details?: Record<string, unknown>;\n };\n}\n\n/**\n * Reserved CLI option names that should not be passed as exec arguments\n */\nconst RESERVED_OPTIONS = new Set([\n \"json\",\n \"yaml\",\n \"view\",\n \"help\",\n \"h\",\n \"version\",\n \"V\",\n \"_\", // yargs positional args array\n \"$0\", // yargs script name\n \"args\", // our --args option (processed separately)\n]);\n\n/**\n * Read JSON from stdin (non-blocking if TTY)\n */\nasync function readStdin(): Promise<string | null> {\n // Only read from stdin if it's not a TTY (i.e., there's pipe input)\n if (process.stdin.isTTY) {\n return null;\n }\n\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n const content = Buffer.concat(chunks).toString(\"utf-8\").trim();\n return content || null;\n}\n\n/**\n * Parse exec arguments from CLI options with stdin support\n *\n * Priority: CLI flags > stdin > --args\n *\n * Supports three input sources:\n * 1. JSON via --args: `afs exec /path --args '{\"key\": \"value\"}'`\n * 2. JSON via stdin: `echo '{\"key\": \"value\"}' | afs exec /path`\n * 3. Named parameters: `afs exec /path --sql \"SELECT * FROM users\" --limit 10`\n */\nexport function parseExecArgs(options: Record<string, unknown>): Record<string, unknown> {\n let result: Record<string, unknown> = {};\n\n // Parse --args JSON if provided (lowest priority)\n if (options.args && typeof options.args === \"string\") {\n try {\n const parsed = JSON.parse(options.args);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in --args: ${(e as Error).message}`);\n }\n }\n\n // Add named parameters (overwrite JSON args if same key) - highest priority\n for (const [key, value] of Object.entries(options)) {\n // Skip reserved options and undefined/null values\n if (RESERVED_OPTIONS.has(key) || value === undefined || value === null) {\n continue;\n }\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Parse exec arguments with stdin support (async version)\n *\n * Priority: CLI flags > stdin > --args\n */\nexport async function parseExecArgsWithStdin(\n options: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n let result: Record<string, unknown> = {};\n\n // 1. Parse --args JSON if provided (lowest priority)\n if (options.args && typeof options.args === \"string\") {\n try {\n const parsed = JSON.parse(options.args);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in --args: ${(e as Error).message}`);\n }\n }\n\n // 2. Read from stdin if available (medium priority)\n const stdinContent = await readStdin();\n if (stdinContent) {\n try {\n const parsed = JSON.parse(stdinContent);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...result, ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in stdin: ${(e as Error).message}`);\n }\n }\n\n // 3. Add named CLI parameters (highest priority)\n for (const [key, value] of Object.entries(options)) {\n if (RESERVED_OPTIONS.has(key) || value === undefined || value === null) {\n continue;\n }\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Check if an entry is executable based on its metadata\n *\n * An entry is executable if:\n * 1. Its `kinds` array includes \"afs:executable\", OR\n * 2. Its `kind` field equals \"afs:executable\" (legacy support)\n */\nexport function isExecutable(metadata: AFSEntryMetadata | null | undefined): boolean {\n if (!metadata) return false;\n\n // Check kinds array first (new way)\n if (Array.isArray(metadata.kinds)) {\n return metadata.kinds.includes(\"afs:executable\");\n }\n\n // Fall back to kind field (legacy)\n return metadata.kind === \"afs:executable\";\n}\n\n/**\n * Execute an action on an AFS path\n *\n * This function directly executes the path without pre-checking if it's executable.\n * The provider will return an appropriate error if the path is not executable.\n *\n * @param runtime - AFS runtime\n * @param path - Path to execute\n * @param args - Arguments to pass to the executable\n * @returns Exec result\n */\nexport async function execCommand(\n runtime: AFSRuntime,\n path: string,\n args: Record<string, unknown> = {},\n): Promise<ExecResult> {\n try {\n // Execute directly - let provider handle validation\n const execResult = await runtime.exec(path, args);\n\n return {\n path,\n success: execResult.success,\n data: execResult.data,\n message: execResult.error?.message,\n error: execResult.error,\n };\n } catch (error) {\n // Extract error code if available\n const errorCode = extractErrorCode(error);\n\n return {\n path,\n success: false,\n message: error instanceof Error ? error.message : String(error),\n error: errorCode ? { code: errorCode } : undefined,\n };\n }\n}\n\n/**\n * Extract error code from an error object\n */\nfunction extractErrorCode(error: unknown): string | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n if (\"code\" in error && typeof (error as { code: unknown }).code === \"string\") {\n return (error as { code: string }).code;\n }\n return undefined;\n}\n\n/**\n * Format exec output for different views\n */\nexport function formatExecOutput(result: ExecResult, view: ExecViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"yaml\":\n return YAML.stringify(result);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExecResult): string {\n if (result.success) {\n if (result.data) {\n return JSON.stringify(result.data);\n }\n return `OK ${result.path}`;\n }\n const errorCode = result.error?.code || \"ERROR\";\n return `${errorCode} ${result.path} ${result.message || \"\"}`.trim();\n}\n\nfunction formatLlm(result: ExecResult): string {\n const lines: string[] = [];\n\n lines.push(`EXEC ${result.path}`);\n lines.push(`STATUS ${result.success ? \"SUCCESS\" : \"FAILED\"}`);\n\n if (result.data) {\n lines.push(`DATA ${JSON.stringify(result.data)}`);\n }\n\n if (!result.success && result.error) {\n if (result.error.code) {\n lines.push(`ERROR ${result.error.code}`);\n }\n if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n if (result.error.details) {\n lines.push(`DETAILS ${JSON.stringify(result.error.details)}`);\n }\n } else if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatHuman(result: ExecResult): string {\n if (result.success) {\n const lines = [`✓ Executed: ${result.path}`];\n if (result.data) {\n lines.push(\"\");\n lines.push(JSON.stringify(result.data, null, 2));\n }\n return lines.join(\"\\n\");\n }\n\n const lines = [`✗ Failed: ${result.path}`];\n lines.push(\"\");\n if (result.error?.code) {\n lines.push(`Error: ${result.error.code}`);\n }\n if (result.message) {\n lines.push(`Message: ${result.message}`);\n }\n if (result.error?.details) {\n lines.push(\"Details:\");\n for (const [key, value] of Object.entries(result.error.details)) {\n lines.push(` ${key}: ${JSON.stringify(value)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
@@ -24,7 +24,7 @@ async function explainPathCommand(runtime, path) {
24
24
  type: "unknown"
25
25
  };
26
26
  const metadata = entry.metadata || {};
27
- const entryType = metadata.type || "file";
27
+ const entryType = metadata.kind === "afs:executable" ? "exec" : typeof metadata.childrenCount === "number" ? "directory" : "file";
28
28
  const result = {
29
29
  path: entry.path,
30
30
  type: entryType,
@@ -24,7 +24,7 @@ async function explainPathCommand(runtime, path) {
24
24
  type: "unknown"
25
25
  };
26
26
  const metadata = entry.metadata || {};
27
- const entryType = metadata.type || "file";
27
+ const entryType = metadata.kind === "afs:executable" ? "exec" : typeof metadata.childrenCount === "number" ? "directory" : "file";
28
28
  const result = {
29
29
  path: entry.path,
30
30
  type: entryType,
@@ -1 +1 @@
1
- {"version":3,"file":"explain.mjs","names":[],"sources":["../../src/commands/explain.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\nimport { mountListCommand } from \"./mount.js\";\n\nexport interface ExplainResult {\n topic: string;\n explanation: string;\n examples?: string[];\n}\n\nexport interface PathExplainResult {\n path: string;\n type: string;\n description?: string;\n inputs?: string[];\n outputs?: string[];\n errors?: string[];\n sideEffects?: string[];\n metadata?: Record<string, string>;\n}\n\n/**\n * Explain AFS concepts or current configuration\n */\nexport async function explainCommand(cwd: string, topic?: string): Promise<ExplainResult> {\n switch (topic) {\n case \"mount\":\n case \"mounts\":\n return explainMounts(cwd);\n case \"path\":\n case \"paths\":\n return explainPaths();\n case \"uri\":\n return explainUri();\n default:\n return explainOverview();\n }\n}\n\n/**\n * Explain an AFS object at a given path\n */\nexport async function explainPathCommand(\n runtime: AFSRuntime,\n path: string,\n): Promise<PathExplainResult> {\n const readResult = await runtime.read(path);\n const entry = readResult.data;\n\n if (!entry) {\n return {\n path,\n type: \"unknown\",\n };\n }\n\n const metadata = entry.metadata || {};\n const entryType = (metadata.type as string) || \"file\";\n\n const result: PathExplainResult = {\n path: entry.path,\n type: entryType,\n description: metadata.description as string | undefined,\n metadata: {},\n };\n\n // Add metadata\n if (metadata.provider) {\n result.metadata!.provider = String(metadata.provider);\n }\n if (metadata.permissions) {\n const perms = metadata.permissions;\n result.metadata!.permissions = Array.isArray(perms) ? perms.join(\", \") : String(perms);\n }\n\n // For exec type, try to get schema info\n if (entryType === \"exec\") {\n if (metadata.inputs) {\n result.inputs = Array.isArray(metadata.inputs)\n ? (metadata.inputs as string[])\n : [String(metadata.inputs)];\n }\n if (metadata.outputs) {\n result.outputs = Array.isArray(metadata.outputs)\n ? (metadata.outputs as string[])\n : [String(metadata.outputs)];\n }\n if (metadata.errors) {\n result.errors = Array.isArray(metadata.errors)\n ? (metadata.errors as string[])\n : [String(metadata.errors)];\n }\n if (metadata.sideEffects) {\n result.sideEffects = Array.isArray(metadata.sideEffects)\n ? (metadata.sideEffects as string[])\n : [String(metadata.sideEffects)];\n }\n }\n\n // Clean up empty metadata\n if (Object.keys(result.metadata!).length === 0) {\n delete result.metadata;\n }\n\n return result;\n}\n\nfunction explainOverview(): ExplainResult {\n return {\n topic: \"AFS Overview\",\n explanation: `AFS (Abstract File System) is a virtual filesystem that unifies different data sources into a single namespace.\n\nCore Concepts:\n- mount: Mount a data source to a virtual path\n- path: Virtual path, e.g., /src, /data\n- uri: Data source address, e.g., fs://, git://, sqlite://\n\nData Flow:\n User Path -> AFS -> /modules/{name} -> Provider -> Actual Data`,\n examples: [\n \"afs mount add /src fs:///path/to/source\",\n \"afs ls /modules/src\",\n \"afs read /modules/src/file.txt\",\n ],\n };\n}\n\nasync function explainMounts(cwd: string): Promise<ExplainResult> {\n const result = await mountListCommand(cwd);\n\n if (result.mounts.length === 0) {\n return {\n topic: \"Mounts\",\n explanation: `No mounts configured.\n\nUse mount add to add a mount:\n afs mount add <path> <uri>\n\npath: Virtual path for accessing data in AFS\nuri: Data source URI specifying the data origin`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /data sqlite:///data.db\",\n ],\n };\n }\n\n const mountList = result.mounts\n .map((m) => ` ${m.path} → ${m.uri}${m.description ? ` (${m.description})` : \"\"}`)\n .join(\"\\n\");\n\n return {\n topic: \"Mounts\",\n explanation: `Current mount configuration:\n\n${mountList}\n\nAfter mounting, data is accessed via /modules/{name}:\n path=\"/src\" -> /modules/src`,\n examples: result.mounts.map((m) => {\n const name = m.path.slice(1).replace(/\\//g, \"-\") || \"root\";\n return `afs ls /modules/${name}`;\n }),\n };\n}\n\nfunction explainPaths(): ExplainResult {\n return {\n topic: \"Paths\",\n explanation: `AFS Path Structure:\n\n/ Root directory\n/modules All mounted modules\n/modules/{name} Specific module, name derived from mount path\n\nPath Mapping:\n config: path=\"/src\" -> access: /modules/src\n config: path=\"/data\" -> access: /modules/data\n\nNote: The path configured by user is transformed to /modules/{name}`,\n };\n}\n\nfunction explainUri(): ExplainResult {\n return {\n topic: \"URI\",\n explanation: `AFS URI Schemes and Objects:\n\nfs:// Local Filesystem Provider\n Format: fs:///absolute/path or fs://./relative/path\n Objects:\n - directory: folder entries, supports list\n - file: file entries, supports read/write\n Operations: list, read, write, delete\n\ngit:// Git Repository Provider\n Format: git:///local/repo or git://github.com/user/repo?branch=main\n Objects:\n - directory: tree entries\n - file: blob entries, read-only\n Operations: list, read\n\nsqlite:// SQLite Database Provider\n Format: sqlite:///path/to/db.sqlite\n Objects:\n - directory: database root, tables list\n - exec: table entries, supports query operations\n Operations: list, read, exec (SQL queries)\n\njson:// JSON Data Provider\n Format: json:///path/to/data.json\n Objects:\n - directory: object/array entries\n - file: primitive values\n Operations: list, read, write`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /repo git://github.com/user/repo\",\n \"afs mount add /db sqlite:///data.sqlite\",\n ],\n };\n}\n\n/**\n * Format explain output for different views\n */\nexport function formatExplainOutput(result: ExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExplainResult): string {\n let output = `${result.topic}\\n${\"=\".repeat(result.topic.length)}\\n\\n${result.explanation}`;\n\n if (result.examples && result.examples.length > 0) {\n output += `\\n\\nExamples:\\n${result.examples.map((e) => ` $ ${e}`).join(\"\\n\")}`;\n }\n\n return output;\n}\n\nfunction formatLlm(result: ExplainResult): string {\n const lines = [`TOPIC ${result.topic}`, \"\", result.explanation];\n\n if (result.examples && result.examples.length > 0) {\n lines.push(\"\", \"EXAMPLES\");\n for (const example of result.examples) {\n lines.push(`CMD ${example}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format object explain output for different views\n */\nexport function formatPathExplainOutput(result: PathExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatPathLlm(result);\n default:\n return formatPathDefault(result);\n }\n}\n\nfunction formatPathDefault(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(\"\");\n lines.push(\"TYPE\");\n lines.push(result.type);\n\n if (result.description) {\n lines.push(\"\");\n lines.push(\"DESCRIPTION\");\n lines.push(result.description);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(\"\");\n lines.push(\"INPUTS\");\n for (const input of result.inputs) {\n lines.push(`- ${input}`);\n }\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(\"\");\n lines.push(\"OUTPUTS\");\n for (const output of result.outputs) {\n lines.push(`- ${output}`);\n }\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(\"\");\n lines.push(\"ERRORS\");\n for (const error of result.errors) {\n lines.push(`- ${error}`);\n }\n }\n\n if (result.sideEffects && result.sideEffects.length > 0) {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n for (const effect of result.sideEffects) {\n lines.push(`- ${effect}`);\n }\n } else {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n lines.push(\"- none\");\n }\n\n if (result.metadata && Object.keys(result.metadata).length > 0) {\n lines.push(\"\");\n lines.push(\"METADATA\");\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`- ${key}: ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatPathLlm(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(`TYPE ${result.type.toUpperCase()}`);\n\n if (result.description) {\n lines.push(`DESC ${result.description}`);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(`INPUTS ${result.inputs.join(\", \")}`);\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(`OUTPUTS ${result.outputs.join(\", \")}`);\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(`ERRORS ${result.errors.join(\", \")}`);\n }\n\n lines.push(`SIDE_EFFECTS ${result.sideEffects?.join(\", \") || \"none\"}`);\n\n if (result.metadata) {\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`${key.toUpperCase()} ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"explain.mjs","names":[],"sources":["../../src/commands/explain.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\nimport { mountListCommand } from \"./mount.js\";\n\nexport interface ExplainResult {\n topic: string;\n explanation: string;\n examples?: string[];\n}\n\nexport interface PathExplainResult {\n path: string;\n type: string;\n description?: string;\n inputs?: string[];\n outputs?: string[];\n errors?: string[];\n sideEffects?: string[];\n metadata?: Record<string, string>;\n}\n\n/**\n * Explain AFS concepts or current configuration\n */\nexport async function explainCommand(cwd: string, topic?: string): Promise<ExplainResult> {\n switch (topic) {\n case \"mount\":\n case \"mounts\":\n return explainMounts(cwd);\n case \"path\":\n case \"paths\":\n return explainPaths();\n case \"uri\":\n return explainUri();\n default:\n return explainOverview();\n }\n}\n\n/**\n * Explain an AFS object at a given path\n */\nexport async function explainPathCommand(\n runtime: AFSRuntime,\n path: string,\n): Promise<PathExplainResult> {\n const readResult = await runtime.read(path);\n const entry = readResult.data;\n\n if (!entry) {\n return {\n path,\n type: \"unknown\",\n };\n }\n\n const metadata = entry.metadata || {};\n // Determine type from childrenCount: if defined, it has children (directory-like); otherwise file-like\n const entryType =\n metadata.kind === \"afs:executable\"\n ? \"exec\"\n : typeof metadata.childrenCount === \"number\"\n ? \"directory\"\n : \"file\";\n\n const result: PathExplainResult = {\n path: entry.path,\n type: entryType,\n description: metadata.description as string | undefined,\n metadata: {},\n };\n\n // Add metadata\n if (metadata.provider) {\n result.metadata!.provider = String(metadata.provider);\n }\n if (metadata.permissions) {\n const perms = metadata.permissions;\n result.metadata!.permissions = Array.isArray(perms) ? perms.join(\", \") : String(perms);\n }\n\n // For exec type, try to get schema info\n if (entryType === \"exec\") {\n if (metadata.inputs) {\n result.inputs = Array.isArray(metadata.inputs)\n ? (metadata.inputs as string[])\n : [String(metadata.inputs)];\n }\n if (metadata.outputs) {\n result.outputs = Array.isArray(metadata.outputs)\n ? (metadata.outputs as string[])\n : [String(metadata.outputs)];\n }\n if (metadata.errors) {\n result.errors = Array.isArray(metadata.errors)\n ? (metadata.errors as string[])\n : [String(metadata.errors)];\n }\n if (metadata.sideEffects) {\n result.sideEffects = Array.isArray(metadata.sideEffects)\n ? (metadata.sideEffects as string[])\n : [String(metadata.sideEffects)];\n }\n }\n\n // Clean up empty metadata\n if (Object.keys(result.metadata!).length === 0) {\n delete result.metadata;\n }\n\n return result;\n}\n\nfunction explainOverview(): ExplainResult {\n return {\n topic: \"AFS Overview\",\n explanation: `AFS (Abstract File System) is a virtual filesystem that unifies different data sources into a single namespace.\n\nCore Concepts:\n- mount: Mount a data source to a virtual path\n- path: Virtual path, e.g., /src, /data\n- uri: Data source address, e.g., fs://, git://, sqlite://\n\nData Flow:\n User Path -> AFS -> /modules/{name} -> Provider -> Actual Data`,\n examples: [\n \"afs mount add /src fs:///path/to/source\",\n \"afs ls /modules/src\",\n \"afs read /modules/src/file.txt\",\n ],\n };\n}\n\nasync function explainMounts(cwd: string): Promise<ExplainResult> {\n const result = await mountListCommand(cwd);\n\n if (result.mounts.length === 0) {\n return {\n topic: \"Mounts\",\n explanation: `No mounts configured.\n\nUse mount add to add a mount:\n afs mount add <path> <uri>\n\npath: Virtual path for accessing data in AFS\nuri: Data source URI specifying the data origin`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /data sqlite:///data.db\",\n ],\n };\n }\n\n const mountList = result.mounts\n .map((m) => ` ${m.path} → ${m.uri}${m.description ? ` (${m.description})` : \"\"}`)\n .join(\"\\n\");\n\n return {\n topic: \"Mounts\",\n explanation: `Current mount configuration:\n\n${mountList}\n\nAfter mounting, data is accessed via /modules/{name}:\n path=\"/src\" -> /modules/src`,\n examples: result.mounts.map((m) => {\n const name = m.path.slice(1).replace(/\\//g, \"-\") || \"root\";\n return `afs ls /modules/${name}`;\n }),\n };\n}\n\nfunction explainPaths(): ExplainResult {\n return {\n topic: \"Paths\",\n explanation: `AFS Path Structure:\n\n/ Root directory\n/modules All mounted modules\n/modules/{name} Specific module, name derived from mount path\n\nPath Mapping:\n config: path=\"/src\" -> access: /modules/src\n config: path=\"/data\" -> access: /modules/data\n\nNote: The path configured by user is transformed to /modules/{name}`,\n };\n}\n\nfunction explainUri(): ExplainResult {\n return {\n topic: \"URI\",\n explanation: `AFS URI Schemes and Objects:\n\nfs:// Local Filesystem Provider\n Format: fs:///absolute/path or fs://./relative/path\n Objects:\n - directory: folder entries, supports list\n - file: file entries, supports read/write\n Operations: list, read, write, delete\n\ngit:// Git Repository Provider\n Format: git:///local/repo or git://github.com/user/repo?branch=main\n Objects:\n - directory: tree entries\n - file: blob entries, read-only\n Operations: list, read\n\nsqlite:// SQLite Database Provider\n Format: sqlite:///path/to/db.sqlite\n Objects:\n - directory: database root, tables list\n - exec: table entries, supports query operations\n Operations: list, read, exec (SQL queries)\n\njson:// JSON Data Provider\n Format: json:///path/to/data.json\n Objects:\n - directory: object/array entries\n - file: primitive values\n Operations: list, read, write`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /repo git://github.com/user/repo\",\n \"afs mount add /db sqlite:///data.sqlite\",\n ],\n };\n}\n\n/**\n * Format explain output for different views\n */\nexport function formatExplainOutput(result: ExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExplainResult): string {\n let output = `${result.topic}\\n${\"=\".repeat(result.topic.length)}\\n\\n${result.explanation}`;\n\n if (result.examples && result.examples.length > 0) {\n output += `\\n\\nExamples:\\n${result.examples.map((e) => ` $ ${e}`).join(\"\\n\")}`;\n }\n\n return output;\n}\n\nfunction formatLlm(result: ExplainResult): string {\n const lines = [`TOPIC ${result.topic}`, \"\", result.explanation];\n\n if (result.examples && result.examples.length > 0) {\n lines.push(\"\", \"EXAMPLES\");\n for (const example of result.examples) {\n lines.push(`CMD ${example}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format object explain output for different views\n */\nexport function formatPathExplainOutput(result: PathExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatPathLlm(result);\n default:\n return formatPathDefault(result);\n }\n}\n\nfunction formatPathDefault(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(\"\");\n lines.push(\"TYPE\");\n lines.push(result.type);\n\n if (result.description) {\n lines.push(\"\");\n lines.push(\"DESCRIPTION\");\n lines.push(result.description);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(\"\");\n lines.push(\"INPUTS\");\n for (const input of result.inputs) {\n lines.push(`- ${input}`);\n }\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(\"\");\n lines.push(\"OUTPUTS\");\n for (const output of result.outputs) {\n lines.push(`- ${output}`);\n }\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(\"\");\n lines.push(\"ERRORS\");\n for (const error of result.errors) {\n lines.push(`- ${error}`);\n }\n }\n\n if (result.sideEffects && result.sideEffects.length > 0) {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n for (const effect of result.sideEffects) {\n lines.push(`- ${effect}`);\n }\n } else {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n lines.push(\"- none\");\n }\n\n if (result.metadata && Object.keys(result.metadata).length > 0) {\n lines.push(\"\");\n lines.push(\"METADATA\");\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`- ${key}: ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatPathLlm(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(`TYPE ${result.type.toUpperCase()}`);\n\n if (result.description) {\n lines.push(`DESC ${result.description}`);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(`INPUTS ${result.inputs.join(\", \")}`);\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(`OUTPUTS ${result.outputs.join(\", \")}`);\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(`ERRORS ${result.errors.join(\", \")}`);\n }\n\n lines.push(`SIDE_EFFECTS ${result.sideEffects?.join(\", \") || \"none\"}`);\n\n if (result.metadata) {\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`${key.toUpperCase()} ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { execCommand, formatExecOutput } from "./exec.mjs";
1
+ import { execCommand, formatExecOutput, isExecutable, parseExecArgsWithStdin } from "./exec.mjs";
2
2
  import { formatMountListOutput, mountAddCommand, mountListCommand, mountRemoveCommand, mountValidateCommand } from "./mount.mjs";
3
3
  import { explainCommand, explainPathCommand, formatExplainOutput, formatPathExplainOutput } from "./explain.mjs";
4
4
  import { formatLsOutput, lsCommand } from "./ls.mjs";
@@ -1,4 +1,5 @@
1
1
  const require_index = require('../ui/index.cjs');
2
+ const require_meta = require('../utils/meta.cjs');
2
3
 
3
4
  //#region src/commands/ls.ts
4
5
  /**
@@ -11,15 +12,18 @@ async function lsCommand(runtime, path, options = {}) {
11
12
  maxChildren: options.maxChildren,
12
13
  pattern: options.pattern
13
14
  });
14
- const entries = result.data.map((entry) => ({
15
- path: entry.path,
16
- type: mapEntryType(entry.metadata?.type),
17
- size: entry.metadata?.size,
18
- modified: formatDate(entry.updatedAt),
19
- childrenCount: entry.metadata?.childrenCount,
20
- childrenTruncated: entry.metadata?.childrenTruncated
21
- }));
22
- const truncated = entries.some((e) => e.childrenTruncated) || options.limit !== void 0 && entries.length >= options.limit || result.message?.toLowerCase().includes("truncat");
15
+ const entries = result.data.map((entry) => {
16
+ const lsEntry = {
17
+ path: entry.path,
18
+ size: entry.metadata?.size,
19
+ modified: formatDate(entry.updatedAt),
20
+ childrenCount: entry.metadata?.childrenCount
21
+ };
22
+ const meta = extractMeta(entry.metadata);
23
+ if (meta && Object.keys(meta).length > 0) lsEntry.meta = meta;
24
+ return lsEntry;
25
+ });
26
+ const truncated = options.limit !== void 0 && entries.length >= options.limit || result.message?.toLowerCase().includes("truncat");
23
27
  return {
24
28
  entries,
25
29
  total: entries.length,
@@ -27,9 +31,20 @@ async function lsCommand(runtime, path, options = {}) {
27
31
  message: result.message
28
32
  };
29
33
  }
30
- function mapEntryType(type) {
31
- if (type === "file") return "file";
32
- return "directory";
34
+ /**
35
+ * Extract meta fields from entry metadata
36
+ */
37
+ function extractMeta(metadata) {
38
+ if (!metadata) return void 0;
39
+ const builtInFields = new Set([
40
+ "type",
41
+ "size",
42
+ "mimeType",
43
+ "childrenCount"
44
+ ]);
45
+ const meta = {};
46
+ for (const [key, value] of Object.entries(metadata)) if (!builtInFields.has(key) && value !== void 0) meta[key] = value;
47
+ return Object.keys(meta).length > 0 ? meta : void 0;
33
48
  }
34
49
  function formatDate(date) {
35
50
  if (!date) return void 0;
@@ -39,18 +54,20 @@ function formatDate(date) {
39
54
  /**
40
55
  * Format ls output for different views
41
56
  */
42
- function formatLsOutput(result, view) {
57
+ function formatLsOutput(result, view, options) {
58
+ const isActions = options?.path && require_meta.isActionsPath(options.path);
43
59
  switch (view) {
44
- case "json": return formatJson(result);
45
- case "llm": return formatLlm(result);
46
- case "human": return formatHuman(result);
47
- default: return formatDefault(result);
60
+ case "json": return isActions ? formatActionsJson(result, options.path) : formatJson(result);
61
+ case "llm": return isActions ? formatActionsLlm(result, options.path) : formatLlm(result);
62
+ case "human": return isActions ? formatActionsHuman(result, options.path) : formatHuman(result);
63
+ default: return isActions ? formatActionsDefault(result) : formatDefault(result);
48
64
  }
49
65
  }
50
66
  /**
51
67
  * Default format: Machine truth, one path per line
52
68
  */
53
69
  function formatDefault(result) {
70
+ if (result.entries.length === 0 && result.message) return `# ${result.message}`;
54
71
  const lines = result.entries.map((entry) => entry.path);
55
72
  if (result.truncated) lines.push(`# Results truncated (${result.total} shown)`);
56
73
  return lines.join("\n");
@@ -67,16 +84,19 @@ function formatJson(result) {
67
84
  }, null, 2);
68
85
  }
69
86
  /**
70
- * LLM format: Token-efficient, semantic facts
87
+ * LLM format: Token-efficient, semantic facts (includes KIND if present)
71
88
  */
72
89
  function formatLlm(result) {
73
90
  const lines = [];
91
+ if (result.entries.length === 0 && result.message) {
92
+ lines.push(`ERROR ${result.message}`);
93
+ return lines.join("\n");
94
+ }
74
95
  for (const entry of result.entries) {
75
96
  const parts = [`ENTRY ${entry.path}`];
76
- parts.push(`TYPE=${entry.type}`);
97
+ if (entry.meta?.kind) parts.push(`KIND=${entry.meta.kind}`);
77
98
  if (entry.size !== void 0) parts.push(`SIZE=${entry.size}`);
78
99
  if (entry.childrenCount !== void 0) parts.push(`CHILDREN=${entry.childrenCount}`);
79
- if (entry.childrenTruncated) parts.push("TRUNCATED");
80
100
  lines.push(parts.join(" "));
81
101
  }
82
102
  lines.push(`TOTAL ${result.total}`);
@@ -87,6 +107,7 @@ function formatLlm(result) {
87
107
  * Human format: Tree structure
88
108
  */
89
109
  function formatHuman(result) {
110
+ if (result.entries.length === 0 && result.message) return require_index.colors.yellow(result.message);
90
111
  const root = {
91
112
  name: "",
92
113
  children: /* @__PURE__ */ new Map()
@@ -118,25 +139,103 @@ function renderTree(node, prefix, lines) {
118
139
  const child = children[i];
119
140
  const isLast = i === children.length - 1;
120
141
  const connector = require_index.colors.dim(isLast ? "└── " : "├── ");
121
- const icon = child.entry ? getTypeIcon(child.entry.type) : "📂";
122
- const name = child.entry?.type === "directory" ? require_index.colors.cyan(child.name) : child.name;
142
+ const icon = typeof child.entry?.childrenCount === "number" || child.children.size > 0 ? "📁" : "📄";
123
143
  const sizeStr = child.entry?.size !== void 0 ? require_index.colors.dim(` ${formatSize(child.entry.size)}`) : "";
124
- lines.push(`${require_index.colors.dim(prefix)}${connector}${icon} ${name}${sizeStr}`);
144
+ const kindStr = child.entry?.meta?.kind ? require_index.colors.dim(` (${child.entry.meta.kind})`) : "";
145
+ lines.push(`${require_index.colors.dim(prefix)}${connector}${icon} ${child.name}${kindStr}${sizeStr}`);
125
146
  renderTree(child, prefix + (isLast ? " " : "│ "), lines);
126
147
  }
127
148
  }
128
- function getTypeIcon(type) {
129
- switch (type) {
130
- case "directory": return "📂";
131
- case "file": return "📄";
132
- default: return "📄";
133
- }
134
- }
135
149
  function formatSize(bytes) {
136
150
  if (bytes < 1024) return `${bytes}B`;
137
151
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
138
152
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
139
153
  }
154
+ /**
155
+ * Extract action name from path (last segment after .actions/)
156
+ */
157
+ function getActionName(entry) {
158
+ const parts = entry.path.split("/");
159
+ return parts[parts.length - 1] || entry.path;
160
+ }
161
+ /**
162
+ * Get action description from entry metadata
163
+ */
164
+ function getActionDescription(entry) {
165
+ return entry.meta?.description;
166
+ }
167
+ /**
168
+ * Get the node path from an actions path (path before /.actions)
169
+ */
170
+ function getNodePathFromActions(actionsPath) {
171
+ const idx = actionsPath.lastIndexOf("/.actions");
172
+ return idx > 0 ? actionsPath.substring(0, idx) : "/";
173
+ }
174
+ /**
175
+ * Format actions list - Human view
176
+ * Shows: Available actions for /path:
177
+ * export Export table data to file
178
+ * truncate Delete all rows from table
179
+ */
180
+ function formatActionsHuman(result, path) {
181
+ const lines = [];
182
+ const nodePath = getNodePathFromActions(path);
183
+ if (result.entries.length === 0) {
184
+ lines.push(`No actions available for ${nodePath}`);
185
+ return lines.join("\n");
186
+ }
187
+ lines.push(`Available actions for ${nodePath}:`);
188
+ lines.push("");
189
+ const maxNameLen = Math.max(...result.entries.map((e) => getActionName(e).length));
190
+ for (const entry of result.entries) {
191
+ const name = getActionName(entry);
192
+ const desc = getActionDescription(entry) || "";
193
+ lines.push(` ${name.padEnd(maxNameLen + 2)}${desc}`);
194
+ }
195
+ return lines.join("\n");
196
+ }
197
+ /**
198
+ * Format actions list - LLM view
199
+ * Shows: ACTIONS /path/.actions
200
+ * ACTION export DESCRIPTION "Export table data to file"
201
+ */
202
+ function formatActionsLlm(result, path) {
203
+ const lines = [];
204
+ lines.push(`ACTIONS ${path}`);
205
+ if (result.entries.length === 0) {
206
+ lines.push("ACTIONS_COUNT 0");
207
+ return lines.join("\n");
208
+ }
209
+ for (const entry of result.entries) {
210
+ const name = getActionName(entry);
211
+ const desc = getActionDescription(entry);
212
+ const descPart = desc ? ` DESCRIPTION "${desc}"` : "";
213
+ lines.push(`ACTION ${name}${descPart}`);
214
+ }
215
+ return lines.join("\n");
216
+ }
217
+ /**
218
+ * Format actions list - JSON view
219
+ * Shows structured JSON with path and actions array
220
+ */
221
+ function formatActionsJson(result, path) {
222
+ const actions = result.entries.map((entry) => ({
223
+ name: getActionName(entry),
224
+ description: getActionDescription(entry)
225
+ }));
226
+ return JSON.stringify({
227
+ path,
228
+ data: actions
229
+ }, null, 2);
230
+ }
231
+ /**
232
+ * Format actions list - Default view
233
+ * Shows action names, one per line
234
+ */
235
+ function formatActionsDefault(result) {
236
+ if (result.entries.length === 0) return "";
237
+ return result.entries.map((entry) => getActionName(entry)).join("\n");
238
+ }
140
239
 
141
240
  //#endregion
142
241
  exports.formatLsOutput = formatLsOutput;