opencode-tsift 0.1.63

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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # opencode-tsift
2
+
3
+ OpenCode command shortcuts for `tsift`.
4
+
5
+ Install the plugin from npm:
6
+
7
+ ```sh
8
+ opencode plugin opencode-tsift
9
+ ```
10
+
11
+ OpenCode installs npm plugins with Bun at startup and loads plugins listed in
12
+ the `plugin` config array. This package writes marker-owned project commands
13
+ into `.opencode/commands/` when the plugin loads, so the following commands are
14
+ available in a project without cloning the `tsift` repository:
15
+
16
+ - `/tsift-status`
17
+ - `/tsift-session-review`
18
+ - `/tsift-context-pack`
19
+ - `/tsift-memory-status`
20
+ - `/tsift-memory-search`
21
+ - `/tsift-memory-guard`
22
+ - `/tsift-diff-digest`
23
+ - `/tsift-test-digest`
24
+ - `/tsift-log-digest`
25
+ - `/tsift-rewrite-run`
26
+
27
+ The commands shell out to the `tsift` binary. Install `tsift` first:
28
+
29
+ ```sh
30
+ curl -fsSL https://raw.githubusercontent.com/btakita/tsift/main/scripts/install.sh | sh
31
+ ```
32
+
33
+ You can also install or refresh the command files directly:
34
+
35
+ ```sh
36
+ npx opencode-tsift .
37
+ ```
38
+
39
+ Existing command files with the same names are only replaced when they already
40
+ contain the `tsift:opencode-command` ownership marker.
41
+
42
+ ## Permissions
43
+
44
+ All commands shell out to the `tsift` binary via Bash. Without
45
+ `--dangerously-skip-permissions`, OpenCode will prompt for approval on each
46
+ shell invocation. The required permissions break down by command:
47
+
48
+ | Command | Bash execution | File read | File write |
49
+ |---|---|---|---|
50
+ | `/tsift-status` | `tsift status --fix` | `.tsift/`, source tree | `.tsift/`, AGENTS.md, CLAUDE.md |
51
+ | `/tsift-session-review` | `tsift --envelope session-review` | `.tsift/`, agent-doc logs | — |
52
+ | `/tsift-context-pack` | `tsift --envelope context-pack` | `.tsift/`, source files | — |
53
+ | `/tsift-memory-status` | `tsift memory status` | `.tsift/`, `.claude-mem/` fallback import source | — |
54
+ | `/tsift-memory-search` | `tsift graph-db --path . --json related` | `.tsift/graph.db`, `.tsift/memory.db` projected rows | — |
55
+ | `/tsift-memory-guard` | `tsift memory budget-guard` | target payload | — |
56
+ | `/tsift-diff-digest` | `tsift diff-digest` | `.tsift/`, git working tree | — |
57
+ | `/tsift-test-digest` | `tsift --envelope digest-runner --kind test` | `.tsift/`, test output | — |
58
+ | `/tsift-log-digest` | `tsift --envelope digest-runner --kind log` | `.tsift/`, build output | — |
59
+ | `/tsift-rewrite-run` | `tsift rewrite --run` | `.tsift/`, command-dependent input | command-dependent |
60
+
61
+ `/tsift-status` is the only command that writes files (index, instructions).
62
+ `/tsift-rewrite-run` has the same read/write profile as the rewritten command it
63
+ executes. The other commands are read-only.
64
+
65
+ ## Troubleshooting
66
+
67
+ **`tsift: command not found`** — Install the tsift binary first:
68
+
69
+ ```sh
70
+ curl -fsSL https://raw.githubusercontent.com/btakita/tsift/main/scripts/install.sh | sh
71
+ ```
72
+
73
+ **`tsift status` reports stale index** — Run `/tsift-status` again; it passes
74
+ `--fix` which reindexes automatically.
75
+
76
+ **Command files conflict with existing files** — If `.opencode/commands/tsift-*.md`
77
+ exists without the `tsift:opencode-command` marker, the installer refuses to
78
+ overwrite it. Move or rename the conflicting file, then reinstall.
79
+
80
+ **Plugin does not install commands at startup** — Verify the plugin is listed in
81
+ your OpenCode config:
82
+
83
+ ```json
84
+ {
85
+ "plugin": ["opencode-tsift"]
86
+ }
87
+ ```
88
+
89
+ OpenCode installs and loads plugins with Bun on startup. Check that Bun can
90
+ resolve the package by running `opencode plugin opencode-tsift` again.
91
+
92
+ **`npx opencode-tsift .` does nothing** — If the command files are already
93
+ current, the tool reports `already present` for each file. This is the expected
94
+ idempotent behavior.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { installFromCli } from "../src/install.js";
3
+
4
+ installFromCli().catch((error) => {
5
+ console.error(error instanceof Error ? error.message : String(error));
6
+ process.exitCode = 1;
7
+ });
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-context-pack -->
2
+ ---
3
+ description: Build a bounded tsift context pack
4
+ ---
5
+
6
+ Run `tsift --envelope context-pack <target> --budget normal`, where `<target>` is `$ARGUMENTS` or `.` when no argument is provided. Use source handles and expansion commands from the packet before reading whole files.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-diff-digest -->
2
+ ---
3
+ description: Digest current or requested git diff
4
+ ---
5
+
6
+ Run `tsift diff-digest <target>`, where `<target>` is `$ARGUMENTS` or `.` when no argument is provided. Summarize changed paths, high-signal hunks, and any follow-up expansion commands instead of pasting the raw diff.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-explain -->
2
+ ---
3
+ description: Explain a symbol via callers, callees, and community preview
4
+ ---
5
+
6
+ Explain the symbol named by `$ARGUMENTS` using `tsift --envelope explain '<symbol>' --budget normal`. Prefer this when you need callers, callees, or community context for a function, struct, or type. The envelope returns ranked caller/callee lists with file locations, community membership, and expansion commands for graph traversal. When the report includes a `scale_guard`, run one of its `narrow_commands` before dispatching parallel agents. Use `tsift graph '<symbol>' --callers` or `--callees` for full call-graph navigation. Fall back to `tsift --envelope search '<symbol>' --budget normal` when the symbol is not found in the index.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-graph -->
2
+ ---
3
+ description: Call graph navigation via tsift graph
4
+ ---
5
+
6
+ Navigate the call graph for the symbol named by `$ARGUMENTS` using `tsift graph '<symbol>' --callers` or `tsift graph '<symbol>' --callees`. Use `--callers` to find who calls the symbol, `--callees` to find what the symbol calls. The output lists edges with file locations, edge kinds, and navigation hints. Adjust `--limit` (default 20) to cap edges per direction. For a broader overview including community membership, prefer `tsift --envelope explain '<symbol>' --budget normal`. Fall back to `tsift --envelope search '<symbol>' --budget normal` when the symbol is not found in the index.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-log-digest -->
2
+ ---
3
+ description: Run a verbose command through the bounded log digest
4
+ ---
5
+
6
+ Run a bounded log digest. If `$ARGUMENTS` names a build, install, or verification command, run `tsift --envelope digest-runner --kind log --path . --shell-command '<command>'`; otherwise ask for the command before running. Summarize compact output, failures, and artifact handles.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-memory-guard -->
2
+ ---
3
+ description: Guard a memory or tool payload before model handoff
4
+ ---
5
+
6
+ Run `tsift memory budget-guard --file <target> --json` when `$ARGUMENTS` names a file, or `tsift memory budget-guard --text '<payload>' --json` for inline payload text. Summarize whether the payload is allowed, the estimated token count, replacement digest/context commands, and retryable chunk commands; file retry commands may include `--byte-start` / `--byte-end`. Do not send the raw payload to a model when the guard returns `blocked_split_required`.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-memory-search -->
2
+ ---
3
+ description: Search first-party tsift memory graph
4
+ ---
5
+
6
+ Run `tsift graph-db --path . --json related '<query>'`, where `<query>` is `$ARGUMENTS`; ask for a query if `$ARGUMENTS` is empty. Summarize semantic readiness, useful memory/source hits, and any refresh/import fallback commands. Prefer tsift-memory/graph-db retrieval and do not call direct claude-mem or `/mem-search`; claude-mem remains only a fallback import source through `tsift memory import-claude-mem` when graph memory is missing.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-memory-status -->
2
+ ---
3
+ description: Inspect first-party tsift memory readiness
4
+ ---
5
+
6
+ Run `tsift memory status <target> --json`, where `<target>` is `$ARGUMENTS` or `.` when no argument is provided. Summarize schema initialization, agent-doc hook contract, graph-db retrieval readiness, the claude-mem retirement gate, and rollback commands. Do not import data unless the user explicitly asks for `--apply`.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-rewrite-run -->
2
+ ---
3
+ description: Run a shell command through tsift rewrite
4
+ ---
5
+
6
+ Run the shell command named by `$ARGUMENTS` through `tsift rewrite --run '<command>'`. Use this for broad `rg`/recursive `grep`, raw transcript/session/log reads, `git diff`/`git show`/single-patch `git log`, `cargo test`/`pytest`, and cargo build/check/clippy/install commands so Codex/OpenCode get the same bounded search, session-digest, diff-digest, and digest-runner path as the Claude hook. If tsift reports no rewrite, do not retry automatically; summarize the reason and run the original command only when the user still needs exact raw output.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-search -->
2
+ ---
3
+ description: AST-aware content search via tsift search
4
+ ---
5
+
6
+ Search code using `tsift --envelope search '<query>' --budget normal`, where `<query>` is `$ARGUMENTS`. Prefer this over grep/rg for content search in indexed projects. The envelope returns ranked search hits with symbol families, file previews, AST-aware scoring, and expansion commands. When the report includes a `scale_guard`, run one of its `narrow_commands` before dispatching parallel agents. Use `tsift workflow search` for the ordered exact/search/explain/summarize/digest recipe that preserves result handles across expansions. Fall back to grep/rg only when the project is not indexed or for non-code file patterns (e.g. glob-only searches).
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-session-review -->
2
+ ---
3
+ description: Summarize bounded agent session context
4
+ ---
5
+
6
+ Run `tsift --envelope session-review <target> --next-context --budget normal`, where `<target>` is `$ARGUMENTS` or `.` when no argument is provided. Summarize prompt targets, unresolved failures, touched files/symbols, and next digest commands. Do not replay raw transcripts.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-source-read -->
2
+ ---
3
+ description: AST-aware source code reading via tsift source-read
4
+ ---
5
+
6
+ Read source code using `tsift --envelope source-read <file> --start <n> --lines <n> --budget normal`, where `<file>` is `$ARGUMENTS` or the file the user wants to inspect. Prefer this over the raw Read tool for source code files (Rust, TypeScript, JavaScript, Python, Markdown, and other indexed languages). The envelope returns a bounded source window with AST symbol metadata, line previews, and expansion commands for before/after/full-file ranges. When `$ARGUMENTS` includes a line range, parse `start` and `lines` from it; otherwise default to `--start 1 --lines 80`. Use the returned `expand` commands to read adjacent ranges instead of re-reading the entire file. Fall back to the raw Read tool only for non-indexed files or binary assets.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-status -->
2
+ ---
3
+ description: Refresh and summarize tsift index status
4
+ ---
5
+
6
+ Run `tsift status --fix` from the project root, then summarize index freshness, instruction freshness, summary-cache state, and any recommended `use:` or `run:` commands. Stop and report the exact failure if the command fails.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-symbol-read -->
2
+ ---
3
+ description: Read symbol body with AST metadata via tsift symbol-read
4
+ ---
5
+
6
+ Read the symbol named by `$ARGUMENTS` using `tsift --envelope symbol-read '<symbol>' --budget normal`. Prefer this over reading entire source files when you need a specific function, struct, or type definition. The envelope returns the symbol body, AST span metadata, child references, and expansion commands for graph/source navigation. When `$ARGUMENTS` includes a file hint, pass it as `--file '<path>'` to disambiguate duplicate names. Use the returned `expand` commands to inspect callers, callees, or the full source file. Fall back to `tsift --envelope source-read '<file>' --start <n> --lines <n> --budget normal` when the symbol is not found or when you need raw source without AST context.
@@ -0,0 +1,6 @@
1
+ <!-- tsift:opencode-command v=0.1.63 name=tsift-test-digest -->
2
+ ---
3
+ description: Run tests through the bounded digest runner
4
+ ---
5
+
6
+ Run a bounded test digest. If `$ARGUMENTS` names a test command, run `tsift --envelope digest-runner --kind test --path . --shell-command '<command>'`; otherwise choose the project test command from the local instructions and wrap it the same way. Summarize failing tests, failure lines, and artifact handles.
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "opencode-tsift",
3
+ "version": "0.1.63",
4
+ "description": "OpenCode command shortcuts for tsift bounded code navigation and session evidence",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./server": "./src/index.js"
10
+ },
11
+ "bin": {
12
+ "opencode-tsift": "bin/opencode-tsift.js"
13
+ },
14
+ "files": [
15
+ "bin/",
16
+ "commands/",
17
+ "src/",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "test": "node --test",
22
+ "pack:check": "npm pack --dry-run",
23
+ "publish:check": "npm publish --access public --dry-run"
24
+ },
25
+ "keywords": [
26
+ "opencode",
27
+ "opencode-plugin",
28
+ "tsift",
29
+ "agent",
30
+ "commands"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/btakita/tsift.git",
35
+ "directory": "packages/opencode-tsift"
36
+ },
37
+ "license": "MIT",
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,92 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ import { installCommands } from "./install.js";
5
+
6
+ const execFileAsync = promisify(execFile);
7
+
8
+ async function resolveTsiftBin() {
9
+ const binName = process.platform === "win32" ? "tsift.exe" : "tsift";
10
+ try {
11
+ const { stdout } = await execFileAsync("which", [binName], { timeout: 5000 });
12
+ return stdout.trim();
13
+ } catch {
14
+ return binName;
15
+ }
16
+ }
17
+
18
+ export const TsiftOpenCodePlugin = async (ctx = {}) => {
19
+ const projectDir = ctx.directory || ctx.worktree || process.cwd();
20
+ const log = ctx.client?.app?.log?.bind(ctx.client.app) ?? (() => {});
21
+
22
+ async function ensureInstalled() {
23
+ const updates = await installCommands(projectDir);
24
+ const changed = updates.filter((update) => update.action !== "already present");
25
+ if (changed.length > 0) {
26
+ await log({
27
+ body: {
28
+ service: "opencode-tsift",
29
+ level: "info",
30
+ message: "tsift OpenCode commands installed",
31
+ extra: {
32
+ projectDir,
33
+ files: changed.map((update) => update.name),
34
+ },
35
+ },
36
+ });
37
+ }
38
+ }
39
+
40
+ async function ensureIndexFresh() {
41
+ let tsiftBin;
42
+ try {
43
+ tsiftBin = await resolveTsiftBin();
44
+ } catch {
45
+ return;
46
+ }
47
+
48
+ let report;
49
+ try {
50
+ const { stdout } = await execFileAsync(tsiftBin, ["status", "--json", projectDir], { timeout: 15000 });
51
+ report = JSON.parse(stdout);
52
+ } catch {
53
+ return;
54
+ }
55
+
56
+ const state = report?.index?.state;
57
+ if (state !== "stale" && state !== "missing") return;
58
+
59
+ try {
60
+ await execFileAsync(tsiftBin, ["status", "--fix", projectDir], { timeout: 120000 });
61
+ await log({
62
+ body: {
63
+ service: "opencode-tsift",
64
+ level: "info",
65
+ message: "tsift auto-reindex completed",
66
+ extra: { projectDir, previousState: state },
67
+ },
68
+ });
69
+ } catch (error) {
70
+ await log({
71
+ body: {
72
+ service: "opencode-tsift",
73
+ level: "warn",
74
+ message: `tsift auto-reindex failed: ${error.message}`,
75
+ extra: { projectDir },
76
+ },
77
+ });
78
+ }
79
+ }
80
+
81
+ await ensureInstalled();
82
+ await ensureIndexFresh();
83
+
84
+ return {
85
+ "installation.updated": async () => {
86
+ await ensureInstalled();
87
+ await ensureIndexFresh();
88
+ },
89
+ };
90
+ };
91
+
92
+ export default TsiftOpenCodePlugin;
package/src/install.js ADDED
@@ -0,0 +1,71 @@
1
+ import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join, relative, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const COMMAND_MARKER = "<!-- tsift:opencode-command";
6
+
7
+ function packageRoot() {
8
+ return dirname(dirname(fileURLToPath(import.meta.url)));
9
+ }
10
+
11
+ async function readIfPresent(path) {
12
+ try {
13
+ return await readFile(path, "utf8");
14
+ } catch (error) {
15
+ if (error?.code === "ENOENT") {
16
+ return undefined;
17
+ }
18
+ throw error;
19
+ }
20
+ }
21
+
22
+ export async function installCommands(projectDir = process.cwd()) {
23
+ const root = resolve(projectDir);
24
+ const sourceDir = join(packageRoot(), "commands");
25
+ const targetDir = join(root, ".opencode", "commands");
26
+ await mkdir(targetDir, { recursive: true });
27
+
28
+ const files = (await readdir(sourceDir))
29
+ .filter((file) => file.endsWith(".md"))
30
+ .sort();
31
+
32
+ const updates = [];
33
+ for (const file of files) {
34
+ const content = await readFile(join(sourceDir, file), "utf8");
35
+ const target = join(targetDir, file);
36
+ const existing = await readIfPresent(target);
37
+ let action = "created";
38
+
39
+ if (existing !== undefined) {
40
+ if (existing === content) {
41
+ action = "already present";
42
+ } else if (!existing.includes(COMMAND_MARKER)) {
43
+ throw new Error(
44
+ `${target} already exists and is not managed by tsift; move it or add the tsift marker before installing opencode-tsift`,
45
+ );
46
+ } else {
47
+ action = "updated";
48
+ }
49
+ }
50
+
51
+ if (action !== "already present") {
52
+ await writeFile(target, content);
53
+ }
54
+
55
+ updates.push({
56
+ action,
57
+ file: target,
58
+ name: file.replace(/\.md$/, ""),
59
+ });
60
+ }
61
+
62
+ return updates;
63
+ }
64
+
65
+ export async function installFromCli(args = process.argv.slice(2)) {
66
+ const projectDir = resolve(args[0] ?? process.cwd());
67
+ const updates = await installCommands(projectDir);
68
+ for (const update of updates) {
69
+ console.log(`${relative(projectDir, update.file)}: ${update.action} (OpenCode /${update.name} tsift shortcut)`);
70
+ }
71
+ }