Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ generated/ # exclude generated dir
!*.cob # include COBOL files
```

Run `semble file-types` to see which extensions are indexed per content type. Extensions not listed (including data files such as `.json` and `.csv`) are not indexed by default and can be force-included with a `!` pattern as above.

Semble also always skips a set of well-known non-source directories regardless of ignore files (e.g. `node_modules/`, `.venv/`, `dist/`, `build/`, `__pycache__/`, and similar).

</details>
Expand Down
27 changes: 26 additions & 1 deletion src/semble/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from semble.cache import cache_key, resolve_cache_folder, save_index_to_cache
from semble.index import SembleIndex
from semble.index.files import get_extensions
from semble.index.types import PersistencePath
from semble.installer.agents import AGENTS, IntegrationType
from semble.stats import format_savings_report
Expand All @@ -26,7 +27,19 @@
from semble.version import __version__

_CLI_DISPATCH_ARGS = frozenset(
{"search", "find-related", "install", "uninstall", "savings", "-h", "--help", "clear", "--version", "-V"}
{
"search",
"find-related",
"file-types",
"install",
"uninstall",
"savings",
"-h",
"--help",
"clear",
"--version",
"-V",
}
)
_CLEAR_CHOICE = Literal["all", "index", "savings", "orphans"]

Expand Down Expand Up @@ -292,6 +305,16 @@ def _cli_main() -> None:
related_p.add_argument("line", type=int, help="Line number (1-indexed).")
_add_query_args(related_p)

file_types_p = sub.add_parser("file-types", help="List the file extensions indexed per content type.")
file_types_p.add_argument(
"--content",
nargs="+",
default=["all"],
choices=[ct.value for ct in ContentType] + ["all"],
metavar="TYPE",
help="Content types to list (space-separated). Choices: code, docs, config, all. Default: all.",
)

sub.add_parser("savings", help="Show token savings and usage stats.")

install_p = sub.add_parser("install", help="Configure semble across coding agents.")
Expand Down Expand Up @@ -332,6 +355,8 @@ def _cli_main() -> None:
run(args.command, agent_ids=args.agent, integration_ids=integration_ids, yes=args.yes)
elif args.command == "clear":
_run_clear(args.type)
elif args.command == "file-types":
print(json.dumps({ct.value: get_extensions([ct]) for ct in _resolve_content(args.content, False)}))
elif args.command == "search":
_run_search(
args.path,
Expand Down
2 changes: 1 addition & 1 deletion src/semble/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_triple__ = (0, 6, 2)
__version_triple__ = (0, 7, 0)
__version__ = ".".join(map(str, __version_triple__))
19 changes: 19 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,3 +478,22 @@ def test_cli_clear_command(
assert not (tmp_path / sha).exists()
if setup_savings:
assert not savings_file.exists()


@pytest.mark.parametrize(
("content", "expected"),
[
([], {"code": ".py", "docs": ".md", "config": ".toml"}),
(["--content", "docs"], {"docs": ".md"}),
],
)
def test_cli_file_types(
content: list[str], expected: dict[str, str], monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
"""`semble file-types` prints the indexed extensions per requested content type."""
monkeypatch.setattr(sys, "argv", ["semble", "file-types", *content])
main()
out = json.loads(capsys.readouterr().out)
assert list(out) == list(expected)
assert all(extension in out[content_type] for content_type, extension in expected.items())
assert all(".json" not in extensions for extensions in out.values())
Loading