agentikit 0.0.3

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.
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "agentikit",
3
+ "description": "Search, open, and run extension assets from an Agentikit stash directory. Provides tools, skills, commands, and agents management for AI coding assistants.",
4
+ "version": "0.0.1",
5
+ "author": {
6
+ "name": "itlackey",
7
+ "url": "https://github.com/itlackey"
8
+ },
9
+ "homepage": "https://github.com/itlackey/agentikit#readme",
10
+ "repository": "https://github.com/itlackey/agentikit",
11
+ "license": "CC-BY-4.0",
12
+ "keywords": [
13
+ "agentikit",
14
+ "stash",
15
+ "tools",
16
+ "skills",
17
+ "commands",
18
+ "agents",
19
+ "plugin"
20
+ ]
21
+ }
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # agentikit
2
+
3
+ Agentikit is a stash toolkit for AI coding assistants. It exposes three tools so agents can **search**, **open**, and **run** extension assets directly from a stash directory. Works as both an **OpenCode plugin** and a **Claude Code plugin**.
4
+
5
+ ## Installation
6
+
7
+ ### npm / bun
8
+
9
+ ```sh
10
+ npm install @itlackey/agentikit
11
+ # or
12
+ bun add @itlackey/agentikit
13
+ ```
14
+
15
+ ### Standalone binary
16
+
17
+ Use the install scripts for a copy/paste install:
18
+
19
+ ```sh
20
+ # macOS / Linux (recommended: pin a release tag)
21
+ curl -fsSL https://raw.githubusercontent.com/itlackey/agentikit/main/install.sh | bash -s -- v1.2.3
22
+ ```
23
+
24
+ ```sh
25
+ # PowerShell (Windows)
26
+ irm https://raw.githubusercontent.com/itlackey/agentikit/main/install.ps1 -OutFile install.ps1; ./install.ps1 v1.2.3
27
+ ```
28
+
29
+ The shell installer verifies the downloaded binary against release `checksums.txt` before installing it.
30
+
31
+ ### OpenCode plugin
32
+
33
+ Add agentikit to the `plugin` array in your OpenCode config (`opencode.json`):
34
+
35
+ ```json
36
+ {
37
+ "plugin": ["@itlackey/agentikit"]
38
+ }
39
+ ```
40
+
41
+ ### Claude Code plugin
42
+
43
+ Install agentikit as a Claude Code plugin by pointing to the repo directory:
44
+
45
+ ```sh
46
+ claude --plugin-dir /path/to/agentikit
47
+ ```
48
+
49
+ Or add it to a plugin marketplace for team distribution. See the [Claude Code plugins documentation](https://code.claude.com/docs/en/plugins) for details.
50
+
51
+ Once installed, the plugin provides:
52
+
53
+ - **Skill** (`agentikit:stash`) — Claude automatically uses this when you ask about stash assets
54
+ - **Commands** — `/agentikit:search`, `/agentikit:open`, `/agentikit:run` slash commands
55
+
56
+ ## Stash model
57
+
58
+ Set a stash path via `AGENTIKIT_STASH_DIR`.
59
+
60
+ ```sh
61
+ export AGENTIKIT_STASH_DIR=/abs/path/to/your-stash
62
+ ```
63
+
64
+ Expected stash layout:
65
+
66
+ ```
67
+ $AGENTIKIT_STASH_DIR/
68
+ ├── tools/ # recursive files (.sh, .ts, .js, .ps1, .cmd, .bat)
69
+ ├── skills/ # skill directories containing SKILL.md
70
+ ├── commands/ # markdown files
71
+ └── agents/ # markdown files
72
+ ```
73
+
74
+ ## Tools
75
+
76
+ When loaded as a plugin (OpenCode or Claude Code), Agentikit provides three tools:
77
+
78
+ - `agentikit_search({ query, type?, limit? })`
79
+ - `agentikit_open({ ref })`
80
+ - `agentikit_run({ ref })`
81
+
82
+ ### `agentikit_search`
83
+
84
+ Search the stash for extension assets.
85
+
86
+ - `query`: case-insensitive substring over stable names (relative paths)
87
+ - `type`: `tool | skill | command | agent | any` (default: `any`)
88
+ - `limit`: defaults to `20`
89
+
90
+ Returns typed hits with `openRef` and, for tools, execution-ready `runCmd`.
91
+
92
+ Tool command generation:
93
+
94
+ - `.sh` → `bash "<absolute-file>"`
95
+ - `.ps1` → `powershell -ExecutionPolicy Bypass -File "<absolute-file>"`
96
+ - `.cmd`/`.bat` → `cmd /c "<absolute-file>"`
97
+ - `.ts`/`.js`:
98
+ - find nearest `package.json` from script dir upward to stash `tools/` root
99
+ - if found: `cd "<pkgDir>" && bun "<absolute-file>"`
100
+ - else: `bun "<absolute-file>"`
101
+ - optional: set `AGENTIKIT_BUN_INSTALL=true` to include `bun install` before running
102
+
103
+ ### `agentikit_open`
104
+
105
+ Open a hit using `openRef` from search results.
106
+
107
+ Returns full payload by type:
108
+
109
+ - `skill` → full `SKILL.md` content
110
+ - `command` → full markdown body as `template` (+ best-effort `description`)
111
+ - `agent` → full markdown body as `prompt` (+ best-effort `description`, `toolPolicy`, `modelHint`)
112
+ - `tool` → `runCmd`/`kind`
113
+
114
+ ### `agentikit_run`
115
+
116
+ Execute a tool from the stash by its `openRef`. Only `tool:` refs are supported.
117
+
118
+ - `ref`: open reference of a tool returned by `agentikit_search`
119
+
120
+ Returns `{ type, name, path, output, exitCode }`.
121
+
122
+ ## Usage example
123
+
124
+ 1. `agentikit_search({ query: "deploy", type: "tool" })`
125
+ 2. `agentikit_run({ ref: "<openRef from search>" })`
126
+
127
+ Or:
128
+
129
+ 1. `agentikit_search({ query: "release", type: "command" })`
130
+ 2. `agentikit_open({ ref: "<openRef from search>" })`
131
+ 3. Apply returned template in-session
132
+
133
+ ## Package exports
134
+
135
+ - `plugin` — OpenCode plugin exposing `agentikit_search`, `agentikit_open`, and `agentikit_run`
136
+ - `agentikitSearch` / `agentikitOpen` / `agentikitRun` — direct library APIs
137
+
138
+ ## Notes
139
+
140
+ - Agentikit does not write to `.opencode/` or `.claude/`.
141
+ - Agentikit does not install or copy kit files.
142
+ - Missing or unreadable stash paths return friendly errors.
143
+
144
+ ## Docs
145
+
146
+ - **OpenCode**: [Plugins](https://opencode.ai/docs/plugins/) · [Commands](https://opencode.ai/docs/commands/) · [Agents](https://opencode.ai/docs/agents/) · [Agent Skills](https://opencode.ai/docs/skills/) · [Custom tools](https://opencode.ai/docs/custom-tools/) · [Config](https://opencode.ai/docs/config/)
147
+ - **Claude Code**: [Plugins](https://code.claude.com/docs/en/plugins) · [Skills](https://code.claude.com/docs/en/skills) · [Plugins reference](https://code.claude.com/docs/en/plugins-reference)
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Open an Agentikit stash asset by its openRef
3
+ ---
4
+
5
+ Open an Agentikit stash asset using the CLI. Run:
6
+
7
+ ```bash
8
+ agentikit open $ARGUMENTS
9
+ ```
10
+
11
+ Parse the JSON output and present the asset content to the user based on its type (skill content, command template, agent prompt, or tool info).
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Run an Agentikit stash tool by its openRef
3
+ ---
4
+
5
+ Run an Agentikit stash tool using the CLI. Run:
6
+
7
+ ```bash
8
+ agentikit run $ARGUMENTS
9
+ ```
10
+
11
+ Parse the JSON output and present the tool's output and exit code to the user.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Search the Agentikit stash for tools, skills, commands, and agents using semantic search
3
+ ---
4
+
5
+ Search the Agentikit stash using the CLI. If a search index exists, results are ranked by semantic relevance. Run `agentikit index` first to enable semantic search.
6
+
7
+ ```bash
8
+ agentikit search $ARGUMENTS
9
+ ```
10
+
11
+ Parse the JSON output and present the results to the user in a readable format. Include the `openRef` for each hit so the user can open or run assets. Results may include `description`, `tags`, and `score` fields when semantic search is active.
@@ -0,0 +1,6 @@
1
+ export { plugin } from "./src/plugin";
2
+ export { agentikitSearch, agentikitOpen, agentikitRun, agentikitInit } from "./src/stash";
3
+ export type { AgentikitAssetType, AgentikitSearchType, SearchHit, SearchResponse, OpenResponse, RunResponse, InitResponse, } from "./src/stash";
4
+ export { agentikitIndex } from "./src/indexer";
5
+ export type { IndexResponse } from "./src/indexer";
6
+ export type { StashEntry, StashFile, StashIntent } from "./src/metadata";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { plugin } from "./src/plugin";
2
+ export { agentikitSearch, agentikitOpen, agentikitRun, agentikitInit } from "./src/stash";
3
+ export { agentikitIndex } from "./src/indexer";
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import { agentikitSearch, agentikitOpen, agentikitRun, agentikitInit } from "./stash";
3
+ import { agentikitIndex } from "./indexer";
4
+ const args = process.argv.slice(2);
5
+ const command = args[0];
6
+ function flag(name) {
7
+ const idx = args.indexOf(name);
8
+ return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : undefined;
9
+ }
10
+ function usage() {
11
+ console.error("Usage: agentikit <init|search|open|run> [options]");
12
+ console.error("");
13
+ console.error("Commands:");
14
+ console.error(" init Initialize agentikit stash directory and set AGENTIKIT_STASH_DIR");
15
+ console.error(" index Build search index with metadata generation");
16
+ console.error(" search [query] Search the stash (--type tool|skill|command|agent|any) (--limit N)");
17
+ console.error(" open <type:name> Open a stash asset by ref");
18
+ console.error(" run <type:name> Run a tool by ref");
19
+ process.exit(1);
20
+ }
21
+ switch (command) {
22
+ case "init": {
23
+ const result = agentikitInit();
24
+ console.log(JSON.stringify(result, null, 2));
25
+ break;
26
+ }
27
+ case "index": {
28
+ const result = agentikitIndex();
29
+ console.log(JSON.stringify(result, null, 2));
30
+ break;
31
+ }
32
+ case "search": {
33
+ const query = args.find((a, i) => i > 0 && !a.startsWith("--") && args[i - 1] !== "--type" && args[i - 1] !== "--limit") ?? "";
34
+ const type = flag("--type");
35
+ const limitStr = flag("--limit");
36
+ const limit = limitStr ? parseInt(limitStr, 10) : undefined;
37
+ console.log(JSON.stringify(agentikitSearch({ query, type, limit }), null, 2));
38
+ break;
39
+ }
40
+ case "open": {
41
+ const ref = args[1];
42
+ if (!ref) {
43
+ console.error("Error: missing ref argument\n");
44
+ usage();
45
+ }
46
+ console.log(JSON.stringify(agentikitOpen({ ref }), null, 2));
47
+ break;
48
+ }
49
+ case "run": {
50
+ const ref = args[1];
51
+ if (!ref) {
52
+ console.error("Error: missing ref argument\n");
53
+ usage();
54
+ }
55
+ const result = agentikitRun({ ref });
56
+ console.log(JSON.stringify(result, null, 2));
57
+ process.exit(result.exitCode);
58
+ break;
59
+ }
60
+ default:
61
+ usage();
62
+ }
@@ -0,0 +1,26 @@
1
+ import { type StashEntry } from "./metadata";
2
+ export interface IndexedEntry {
3
+ entry: StashEntry;
4
+ path: string;
5
+ dirPath: string;
6
+ }
7
+ export interface SearchIndex {
8
+ version: number;
9
+ builtAt: string;
10
+ stashDir: string;
11
+ entries: IndexedEntry[];
12
+ /** Serialized TF-IDF state (term frequencies, idf values) */
13
+ tfidf?: unknown;
14
+ }
15
+ export interface IndexResponse {
16
+ stashDir: string;
17
+ totalEntries: number;
18
+ generatedMetadata: number;
19
+ indexPath: string;
20
+ }
21
+ export declare function getIndexPath(): string;
22
+ export declare function loadSearchIndex(): SearchIndex | null;
23
+ export declare function agentikitIndex(options?: {
24
+ stashDir?: string;
25
+ }): IndexResponse;
26
+ export declare function buildSearchText(entry: StashEntry): string;
@@ -0,0 +1,167 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { loadStashFile, writeStashFile, generateMetadata, } from "./metadata";
4
+ import { TfIdfAdapter } from "./similarity";
5
+ // ── Constants ───────────────────────────────────────────────────────────────
6
+ const INDEX_VERSION = 1;
7
+ const SCRIPT_EXTENSIONS = new Set([".sh", ".ts", ".js", ".ps1", ".cmd", ".bat"]);
8
+ const TYPE_DIRS = {
9
+ tool: "tools",
10
+ skill: "skills",
11
+ command: "commands",
12
+ agent: "agents",
13
+ };
14
+ // ── Index Path ──────────────────────────────────────────────────────────────
15
+ export function getIndexPath() {
16
+ const cacheDir = process.env.XDG_CACHE_HOME
17
+ || path.join(process.env.HOME || process.env.USERPROFILE || "", ".cache");
18
+ return path.join(cacheDir, "agentikit", "index.json");
19
+ }
20
+ export function loadSearchIndex() {
21
+ const indexPath = getIndexPath();
22
+ if (!fs.existsSync(indexPath))
23
+ return null;
24
+ try {
25
+ const raw = JSON.parse(fs.readFileSync(indexPath, "utf8"));
26
+ if (raw?.version !== INDEX_VERSION)
27
+ return null;
28
+ return raw;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ // ── Indexer ──────────────────────────────────────────────────────────────────
35
+ export function agentikitIndex(options) {
36
+ const stashDir = options?.stashDir || resolveStashDirForIndex();
37
+ const allEntries = [];
38
+ let generatedCount = 0;
39
+ for (const assetType of Object.keys(TYPE_DIRS)) {
40
+ const typeRoot = path.join(stashDir, TYPE_DIRS[assetType]);
41
+ if (!fs.existsSync(typeRoot) || !fs.statSync(typeRoot).isDirectory())
42
+ continue;
43
+ // Group files by their immediate parent directory
44
+ const dirGroups = collectDirectoryGroups(typeRoot, assetType);
45
+ for (const [dirPath, files] of dirGroups) {
46
+ // Try loading existing .stash.json
47
+ let stash = loadStashFile(dirPath);
48
+ if (!stash) {
49
+ // Generate metadata
50
+ stash = generateMetadata(dirPath, assetType, files);
51
+ if (stash.entries.length > 0) {
52
+ writeStashFile(dirPath, stash);
53
+ generatedCount += stash.entries.length;
54
+ }
55
+ }
56
+ if (stash) {
57
+ for (const entry of stash.entries) {
58
+ const entryPath = entry.entry
59
+ ? path.join(dirPath, entry.entry)
60
+ : files[0] || dirPath;
61
+ allEntries.push({ entry, path: entryPath, dirPath });
62
+ }
63
+ }
64
+ }
65
+ }
66
+ // Build TF-IDF index
67
+ const adapter = new TfIdfAdapter();
68
+ const scoredEntries = allEntries.map((ie) => ({
69
+ id: `${ie.entry.type}:${ie.entry.name}`,
70
+ text: buildSearchText(ie.entry),
71
+ entry: ie.entry,
72
+ path: ie.path,
73
+ }));
74
+ adapter.buildIndex(scoredEntries);
75
+ // Persist index
76
+ const indexPath = getIndexPath();
77
+ const indexDir = path.dirname(indexPath);
78
+ if (!fs.existsSync(indexDir)) {
79
+ fs.mkdirSync(indexDir, { recursive: true });
80
+ }
81
+ const index = {
82
+ version: INDEX_VERSION,
83
+ builtAt: new Date().toISOString(),
84
+ stashDir,
85
+ entries: allEntries,
86
+ tfidf: adapter.serialize(),
87
+ };
88
+ fs.writeFileSync(indexPath, JSON.stringify(index) + "\n", "utf8");
89
+ return {
90
+ stashDir,
91
+ totalEntries: allEntries.length,
92
+ generatedMetadata: generatedCount,
93
+ indexPath,
94
+ };
95
+ }
96
+ // ── Helpers ─────────────────────────────────────────────────────────────────
97
+ function collectDirectoryGroups(typeRoot, assetType) {
98
+ const groups = new Map();
99
+ const walk = (dir) => {
100
+ if (!fs.existsSync(dir))
101
+ return;
102
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
103
+ for (const entry of entries) {
104
+ if (entry.name === ".stash.json")
105
+ continue;
106
+ const fullPath = path.join(dir, entry.name);
107
+ if (entry.isDirectory()) {
108
+ walk(fullPath);
109
+ }
110
+ else if (entry.isFile() && isRelevantFile(entry.name, assetType)) {
111
+ const parentDir = path.dirname(fullPath);
112
+ const existing = groups.get(parentDir);
113
+ if (existing) {
114
+ existing.push(fullPath);
115
+ }
116
+ else {
117
+ groups.set(parentDir, [fullPath]);
118
+ }
119
+ }
120
+ }
121
+ };
122
+ walk(typeRoot);
123
+ return groups;
124
+ }
125
+ function isRelevantFile(fileName, assetType) {
126
+ const ext = path.extname(fileName).toLowerCase();
127
+ switch (assetType) {
128
+ case "tool":
129
+ return SCRIPT_EXTENSIONS.has(ext);
130
+ case "skill":
131
+ return fileName === "SKILL.md";
132
+ case "command":
133
+ case "agent":
134
+ return ext === ".md";
135
+ default:
136
+ return false;
137
+ }
138
+ }
139
+ export function buildSearchText(entry) {
140
+ const parts = [entry.name.replace(/[-_]/g, " ")];
141
+ if (entry.description)
142
+ parts.push(entry.description);
143
+ if (entry.tags)
144
+ parts.push(entry.tags.join(" "));
145
+ if (entry.examples)
146
+ parts.push(entry.examples.join(" "));
147
+ if (entry.intent) {
148
+ if (entry.intent.when)
149
+ parts.push(entry.intent.when);
150
+ if (entry.intent.input)
151
+ parts.push(entry.intent.input);
152
+ if (entry.intent.output)
153
+ parts.push(entry.intent.output);
154
+ }
155
+ return parts.join(" ").toLowerCase();
156
+ }
157
+ function resolveStashDirForIndex() {
158
+ const raw = process.env.AGENTIKIT_STASH_DIR?.trim();
159
+ if (!raw) {
160
+ throw new Error("AGENTIKIT_STASH_DIR is not set. Run 'agentikit init' first.");
161
+ }
162
+ const stashDir = path.resolve(raw);
163
+ if (!fs.existsSync(stashDir) || !fs.statSync(stashDir).isDirectory()) {
164
+ throw new Error(`AGENTIKIT_STASH_DIR does not exist or is not a directory: "${stashDir}"`);
165
+ }
166
+ return stashDir;
167
+ }
@@ -0,0 +1,33 @@
1
+ import type { AgentikitAssetType } from "./stash";
2
+ export interface StashIntent {
3
+ when?: string;
4
+ input?: string;
5
+ output?: string;
6
+ }
7
+ export interface StashEntry {
8
+ name: string;
9
+ type: AgentikitAssetType;
10
+ description?: string;
11
+ tags?: string[];
12
+ examples?: string[];
13
+ intent?: StashIntent;
14
+ entry?: string;
15
+ generated?: boolean;
16
+ }
17
+ export interface StashFile {
18
+ entries: StashEntry[];
19
+ }
20
+ export declare function stashFilePath(dirPath: string): string;
21
+ export declare function loadStashFile(dirPath: string): StashFile | null;
22
+ export declare function writeStashFile(dirPath: string, stash: StashFile): void;
23
+ export declare function validateStashEntry(entry: unknown): StashEntry | null;
24
+ export declare function generateMetadata(dirPath: string, assetType: AgentikitAssetType, files: string[]): StashFile;
25
+ export declare function extractDescriptionFromComments(filePath: string): string | null;
26
+ export declare function extractFrontmatterDescription(filePath: string): string | null;
27
+ export declare function extractPackageMetadata(dirPath: string): {
28
+ name?: string;
29
+ description?: string;
30
+ keywords?: string[];
31
+ } | null;
32
+ export declare function fileNameToDescription(fileName: string): string;
33
+ export declare function extractTagsFromPath(filePath: string, rootDir: string): string[];