Files
claude-code/src/main/java/com/claudecode/command/impl/FilesCommand.java
T
abel533andCopilot 9f4023bdf0 refactor: apply BaseSlashCommand to all 54 command implementations
- implements SlashCommand -> extends BaseSlashCommand (all 54 files)
- Replace CommandUtils.parseArgs/args null checks -> args(args)
- Replace agentLoop null checks -> requireAgentLoop(context)/noSession()
- Replace context.agentLoop().getToolContext() -> toolCtx(context)
- Replace CommandUtils.success/error/header/subtitle/warning/info -> instance methods
- Remove unused CommandUtils/AnsiStyle imports where applicable
- 87 tests passing, zero compilation errors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-05 13:16:55 +08:00

137 lines
5.1 KiB
Java

package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* /files 命令 —— 列出当前工作目录的文件结构。
* <p>
* 对应 claude-code/src/commands/files.ts。
* 显示项目目录树(默认2层深度)。
*/
public class FilesCommand extends BaseSlashCommand {
@Override
public String name() {
return "files";
}
@Override
public String description() {
return "List project files. Use /files [depth] to control depth (default: 2)";
}
@Override
public String execute(String args, CommandContext context) {
Path projectDir = Path.of(System.getProperty("user.dir"));
int maxDepth = 2;
if (args != null && !args.isBlank()) {
try {
maxDepth = Integer.parseInt(args.strip());
maxDepth = Math.max(1, Math.min(maxDepth, 5));
} catch (NumberFormatException ignored) {
// Treat as path filter
return listFiltered(projectDir, args.strip());
}
}
StringBuilder sb = new StringBuilder();
sb.append("\n ").append(AnsiStyle.bold("📁 " + projectDir.getFileName())).append("\n");
sb.append(" ").append("─".repeat(50)).append("\n");
try {
int[] counts = {0, 0}; // [files, dirs]
listDirectory(sb, projectDir, "", maxDepth, 0, counts);
sb.append(" ").append("─".repeat(50)).append("\n");
sb.append(" ").append(AnsiStyle.dim(counts[1] + " directories, " + counts[0] + " files")).append("\n");
} catch (IOException e) {
sb.append(" ").append(AnsiStyle.red("Error: " + e.getMessage())).append("\n");
}
return sb.toString();
}
private void listDirectory(StringBuilder sb, Path dir, String indent, int maxDepth, int depth, int[] counts) throws IOException {
if (depth >= maxDepth) return;
try (Stream<Path> stream = Files.list(dir).sorted()) {
var entries = stream
.filter(p -> !isHidden(p))
.toList();
for (int i = 0; i < entries.size(); i++) {
Path entry = entries.get(i);
boolean isLast = (i == entries.size() - 1);
String connector = isLast ? "└── " : "├── ";
String childIndent = indent + (isLast ? " " : "│ ");
String name = entry.getFileName().toString();
if (Files.isDirectory(entry)) {
counts[1]++;
sb.append(" ").append(indent).append(connector)
.append(AnsiStyle.CYAN).append(name).append("/").append(AnsiStyle.RESET).append("\n");
listDirectory(sb, entry, childIndent, maxDepth, depth + 1, counts);
} else {
counts[0]++;
String sizeStr = formatSize(Files.size(entry));
sb.append(" ").append(indent).append(connector).append(name)
.append(AnsiStyle.dim(" (" + sizeStr + ")")).append("\n");
}
}
}
}
private String listFiltered(Path dir, String pattern) {
StringBuilder sb = new StringBuilder();
sb.append("\n ").append(AnsiStyle.bold("Files matching \"" + pattern + "\":")).append("\n\n");
try (Stream<Path> walk = Files.walk(dir, 5)) {
var matches = walk
.filter(Files::isRegularFile)
.filter(p -> !isHiddenPath(p, dir))
.filter(p -> p.getFileName().toString().contains(pattern))
.toList();
if (matches.isEmpty()) {
sb.append(" ").append(AnsiStyle.dim("No files found matching \"" + pattern + "\"")).append("\n");
} else {
for (Path match : matches) {
sb.append(" ").append(dir.relativize(match)).append("\n");
}
sb.append("\n ").append(AnsiStyle.dim(matches.size() + " file(s) found")).append("\n");
}
} catch (IOException e) {
sb.append(" ").append(AnsiStyle.red("Error: " + e.getMessage())).append("\n");
}
return sb.toString();
}
private boolean isHidden(Path p) {
String name = p.getFileName().toString();
return name.startsWith(".") || name.equals("node_modules") || name.equals("target")
|| name.equals("build") || name.equals("__pycache__") || name.equals(".git");
}
private boolean isHiddenPath(Path p, Path root) {
Path rel = root.relativize(p);
for (Path part : rel) {
if (isHidden(part)) return true;
}
return false;
}
private String formatSize(long bytes) {
if (bytes < 1024) return bytes + "B";
if (bytes < 1024 * 1024) return String.format("%.1fK", bytes / 1024.0);
return String.format("%.1fM", bytes / (1024.0 * 1024));
}
}