agentikit 0.0.13 → 0.0.15

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 (156) hide show
  1. package/LICENSE +385 -0
  2. package/README.md +187 -110
  3. package/dist/{src/asset-spec.js → asset-spec.js} +11 -2
  4. package/dist/{src/asset-type-handler.js → asset-type-handler.js} +4 -3
  5. package/dist/cli.js +709 -0
  6. package/dist/common.js +192 -0
  7. package/dist/{src/config-cli.js → config-cli.js} +36 -30
  8. package/dist/{src/config.js → config.js} +95 -25
  9. package/dist/{src/db.js → db.js} +123 -51
  10. package/dist/{src/embedder.js → embedder.js} +57 -2
  11. package/dist/errors.js +28 -0
  12. package/dist/file-context.js +188 -0
  13. package/dist/{src/frontmatter.js → frontmatter.js} +1 -1
  14. package/dist/{src/github.js → github.js} +1 -3
  15. package/dist/handlers/agent-handler.js +19 -0
  16. package/dist/handlers/command-handler.js +20 -0
  17. package/dist/handlers/handler-bridge.js +51 -0
  18. package/dist/handlers/index.js +19 -0
  19. package/dist/handlers/knowledge-handler.js +32 -0
  20. package/dist/handlers/script-handler.js +42 -0
  21. package/dist/{src/handlers → handlers}/skill-handler.js +5 -6
  22. package/dist/{src/handlers → handlers}/tool-handler.js +8 -24
  23. package/dist/{src/indexer.js → indexer.js} +50 -26
  24. package/dist/init.js +43 -0
  25. package/dist/{src/llm.js → llm.js} +6 -11
  26. package/dist/lockfile.js +60 -0
  27. package/dist/matchers.js +163 -0
  28. package/dist/{src/metadata.js → metadata.js} +36 -16
  29. package/dist/{src/origin-resolve.js → origin-resolve.js} +10 -9
  30. package/dist/paths.js +83 -0
  31. package/dist/{src/registry-install.js → registry-install.js} +151 -19
  32. package/dist/{src/registry-resolve.js → registry-resolve.js} +190 -26
  33. package/dist/{src/registry-search.js → registry-search.js} +13 -21
  34. package/dist/renderers.js +286 -0
  35. package/dist/{src/ripgrep-install.js → ripgrep-install.js} +8 -27
  36. package/dist/{src/ripgrep-resolve.js → ripgrep-resolve.js} +21 -11
  37. package/dist/ripgrep.js +2 -0
  38. package/dist/self-update.js +226 -0
  39. package/dist/{src/stash-add.js → stash-add.js} +14 -4
  40. package/dist/stash-clone.js +115 -0
  41. package/dist/{src/stash-ref.js → stash-ref.js} +10 -9
  42. package/dist/{src/stash-registry.js → stash-registry.js} +21 -46
  43. package/dist/{src/stash-resolve.js → stash-resolve.js} +10 -9
  44. package/dist/{src/stash-search.js → stash-search.js} +89 -74
  45. package/dist/stash-show.js +74 -0
  46. package/dist/stash-source.js +127 -0
  47. package/dist/submit.js +557 -0
  48. package/dist/{src/tool-runner.js → tool-runner.js} +1 -5
  49. package/dist/{src/walker.js → walker.js} +38 -0
  50. package/dist/warn.js +20 -0
  51. package/package.json +13 -18
  52. package/dist/index.d.ts +0 -28
  53. package/dist/index.js +0 -15
  54. package/dist/src/asset-spec.d.ts +0 -16
  55. package/dist/src/asset-type-handler.d.ts +0 -27
  56. package/dist/src/cli.d.ts +0 -2
  57. package/dist/src/cli.js +0 -399
  58. package/dist/src/common.d.ts +0 -13
  59. package/dist/src/common.js +0 -60
  60. package/dist/src/config-cli.d.ts +0 -9
  61. package/dist/src/config.d.ts +0 -50
  62. package/dist/src/db.d.ts +0 -46
  63. package/dist/src/embedder.d.ts +0 -10
  64. package/dist/src/frontmatter.d.ts +0 -30
  65. package/dist/src/github.d.ts +0 -4
  66. package/dist/src/handlers/agent-handler.d.ts +0 -2
  67. package/dist/src/handlers/agent-handler.js +0 -26
  68. package/dist/src/handlers/command-handler.d.ts +0 -2
  69. package/dist/src/handlers/command-handler.js +0 -23
  70. package/dist/src/handlers/index.d.ts +0 -6
  71. package/dist/src/handlers/index.js +0 -23
  72. package/dist/src/handlers/knowledge-handler.d.ts +0 -2
  73. package/dist/src/handlers/knowledge-handler.js +0 -56
  74. package/dist/src/handlers/markdown-helpers.d.ts +0 -7
  75. package/dist/src/handlers/script-handler.d.ts +0 -2
  76. package/dist/src/handlers/script-handler.js +0 -78
  77. package/dist/src/handlers/skill-handler.d.ts +0 -2
  78. package/dist/src/handlers/tool-handler.d.ts +0 -2
  79. package/dist/src/indexer.d.ts +0 -22
  80. package/dist/src/init.d.ts +0 -19
  81. package/dist/src/init.js +0 -99
  82. package/dist/src/llm.d.ts +0 -15
  83. package/dist/src/markdown.d.ts +0 -18
  84. package/dist/src/metadata.d.ts +0 -41
  85. package/dist/src/origin-resolve.d.ts +0 -19
  86. package/dist/src/registry-install.d.ts +0 -11
  87. package/dist/src/registry-resolve.d.ts +0 -3
  88. package/dist/src/registry-search.d.ts +0 -27
  89. package/dist/src/registry-types.d.ts +0 -62
  90. package/dist/src/ripgrep-install.d.ts +0 -12
  91. package/dist/src/ripgrep-resolve.d.ts +0 -13
  92. package/dist/src/ripgrep.d.ts +0 -3
  93. package/dist/src/ripgrep.js +0 -2
  94. package/dist/src/stash-add.d.ts +0 -4
  95. package/dist/src/stash-clone.d.ts +0 -22
  96. package/dist/src/stash-clone.js +0 -83
  97. package/dist/src/stash-ref.d.ts +0 -31
  98. package/dist/src/stash-registry.d.ts +0 -18
  99. package/dist/src/stash-resolve.d.ts +0 -2
  100. package/dist/src/stash-search.d.ts +0 -8
  101. package/dist/src/stash-show.d.ts +0 -5
  102. package/dist/src/stash-show.js +0 -46
  103. package/dist/src/stash-source.d.ts +0 -24
  104. package/dist/src/stash-source.js +0 -81
  105. package/dist/src/stash-types.d.ts +0 -227
  106. package/dist/src/stash.d.ts +0 -16
  107. package/dist/src/stash.js +0 -9
  108. package/dist/src/tool-runner.d.ts +0 -35
  109. package/dist/src/walker.d.ts +0 -19
  110. package/src/asset-spec.ts +0 -85
  111. package/src/asset-type-handler.ts +0 -77
  112. package/src/cli.ts +0 -427
  113. package/src/common.ts +0 -76
  114. package/src/config-cli.ts +0 -499
  115. package/src/config.ts +0 -305
  116. package/src/db.ts +0 -411
  117. package/src/embedder.ts +0 -128
  118. package/src/frontmatter.ts +0 -95
  119. package/src/github.ts +0 -21
  120. package/src/handlers/agent-handler.ts +0 -32
  121. package/src/handlers/command-handler.ts +0 -29
  122. package/src/handlers/index.ts +0 -25
  123. package/src/handlers/knowledge-handler.ts +0 -62
  124. package/src/handlers/markdown-helpers.ts +0 -19
  125. package/src/handlers/script-handler.ts +0 -92
  126. package/src/handlers/skill-handler.ts +0 -37
  127. package/src/handlers/tool-handler.ts +0 -71
  128. package/src/indexer.ts +0 -392
  129. package/src/init.ts +0 -114
  130. package/src/llm.ts +0 -125
  131. package/src/markdown.ts +0 -106
  132. package/src/metadata.ts +0 -333
  133. package/src/origin-resolve.ts +0 -67
  134. package/src/registry-install.ts +0 -361
  135. package/src/registry-resolve.ts +0 -341
  136. package/src/registry-search.ts +0 -335
  137. package/src/registry-types.ts +0 -72
  138. package/src/ripgrep-install.ts +0 -200
  139. package/src/ripgrep-resolve.ts +0 -72
  140. package/src/ripgrep.ts +0 -3
  141. package/src/stash-add.ts +0 -63
  142. package/src/stash-clone.ts +0 -127
  143. package/src/stash-ref.ts +0 -99
  144. package/src/stash-registry.ts +0 -259
  145. package/src/stash-resolve.ts +0 -50
  146. package/src/stash-search.ts +0 -613
  147. package/src/stash-show.ts +0 -55
  148. package/src/stash-source.ts +0 -103
  149. package/src/stash-types.ts +0 -231
  150. package/src/stash.ts +0 -39
  151. package/src/tool-runner.ts +0 -142
  152. package/src/walker.ts +0 -53
  153. /package/dist/{src/handlers → handlers}/markdown-helpers.js +0 -0
  154. /package/dist/{src/markdown.js → markdown.js} +0 -0
  155. /package/dist/{src/registry-types.js → registry-types.js} +0 -0
  156. /package/dist/{src/stash-types.js → stash-types.js} +0 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Built-in asset matchers for the agentikit file classification system.
3
+ *
4
+ * Four matchers are registered at module load time, each at a different
5
+ * specificity level. Extension and content determine type; directories are
6
+ * optional specificity boosts, not requirements.
7
+ *
8
+ * - `extensionMatcher` (3) -- classifies any file by extension alone.
9
+ * Ensures every known file type is discoverable regardless of directory.
10
+ * - `directoryMatcher` (10) -- boosts specificity when the first ancestor
11
+ * directory matches a known type name (e.g. `scripts/`, `agents/`).
12
+ * - `parentDirHintMatcher` (15) -- boosts specificity based on the
13
+ * immediate parent directory name.
14
+ * - `smartMdMatcher` (20 / 18 / 8 / 5) -- inspects markdown frontmatter
15
+ * and body content for agent/command signals; falls back to "knowledge"
16
+ * at specificity 5 when no signals are found. Command signals (`agent`
17
+ * frontmatter, `$ARGUMENTS`/`$1`-`$3` placeholders) return 18.
18
+ */
19
+ import { SCRIPT_EXTENSIONS_BROAD } from "./asset-spec";
20
+ import { registerMatcher } from "./file-context";
21
+ // ── extensionMatcher (specificity: 3) ────────────────────────────────────────
22
+ /**
23
+ * Base-level matcher that classifies files purely by extension.
24
+ *
25
+ * This is the foundation of the classification system: every file with a
26
+ * known extension gets a type, regardless of what directory it lives in.
27
+ * Higher-specificity matchers (directory, content) can override this.
28
+ *
29
+ * .md files are NOT handled here -- smartMdMatcher provides richer
30
+ * classification for markdown via frontmatter inspection.
31
+ */
32
+ export function extensionMatcher(ctx) {
33
+ // SKILL.md is a skill regardless of location
34
+ if (ctx.fileName === "SKILL.md") {
35
+ return { type: "skill", specificity: 3, renderer: "skill-md" };
36
+ }
37
+ // Known script extensions (excluding .md, handled by smartMdMatcher)
38
+ if (SCRIPT_EXTENSIONS_BROAD.has(ctx.ext)) {
39
+ return { type: "script", specificity: 3, renderer: "script-source" };
40
+ }
41
+ return null;
42
+ }
43
+ // ── directoryMatcher (specificity: 10) ──────────────────────────────────────
44
+ /**
45
+ * Directory-based matcher that boosts specificity when the first ancestor
46
+ * directory segment from the stash root matches a known type name.
47
+ *
48
+ * Accepts ALL known script extensions in both `tools/` and `scripts/`
49
+ * directories -- the distinction is purely organizational.
50
+ */
51
+ export function directoryMatcher(ctx) {
52
+ const topDir = ctx.ancestorDirs[0];
53
+ if (!topDir)
54
+ return null;
55
+ const ext = ctx.ext;
56
+ if ((topDir === "tools" || topDir === "scripts") && SCRIPT_EXTENSIONS_BROAD.has(ext)) {
57
+ return { type: "script", specificity: 10, renderer: "script-source" };
58
+ }
59
+ if (topDir === "skills" && ctx.fileName === "SKILL.md") {
60
+ return { type: "skill", specificity: 10, renderer: "skill-md" };
61
+ }
62
+ if (topDir === "commands" && ext === ".md") {
63
+ return { type: "command", specificity: 10, renderer: "command-md" };
64
+ }
65
+ if (topDir === "agents" && ext === ".md") {
66
+ return { type: "agent", specificity: 10, renderer: "agent-md" };
67
+ }
68
+ if (topDir === "knowledge" && ext === ".md") {
69
+ return { type: "knowledge", specificity: 10, renderer: "knowledge-md" };
70
+ }
71
+ return null;
72
+ }
73
+ // ── parentDirHintMatcher (specificity: 15) ──────────────────────────────────
74
+ /**
75
+ * Uses the immediate parent directory name as a hint. More specific than
76
+ * the ancestor-based directory matcher because the file might be nested
77
+ * several levels deep, yet its immediate parent can still carry strong
78
+ * naming conventions (e.g. `my-project/agents/planning.md`).
79
+ *
80
+ * Accepts ALL known script extensions in both `tools/` and `scripts/`.
81
+ */
82
+ export function parentDirHintMatcher(ctx) {
83
+ const { parentDir, ext, fileName } = ctx;
84
+ if ((parentDir === "tools" || parentDir === "scripts") && SCRIPT_EXTENSIONS_BROAD.has(ext)) {
85
+ return { type: "script", specificity: 15, renderer: "script-source" };
86
+ }
87
+ if (parentDir === "skills" && fileName === "SKILL.md") {
88
+ return { type: "skill", specificity: 15, renderer: "skill-md" };
89
+ }
90
+ if (parentDir === "agents" && ext === ".md") {
91
+ return { type: "agent", specificity: 15, renderer: "agent-md" };
92
+ }
93
+ if (parentDir === "commands" && ext === ".md") {
94
+ return { type: "command", specificity: 15, renderer: "command-md" };
95
+ }
96
+ if (parentDir === "knowledge" && ext === ".md") {
97
+ return { type: "knowledge", specificity: 15, renderer: "knowledge-md" };
98
+ }
99
+ return null;
100
+ }
101
+ // ── smartMdMatcher (specificity: 20 / 18 / 8 / 5) ──────────────────────────
102
+ /** Pattern that matches OpenCode command placeholders in markdown body. */
103
+ const COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
104
+ /**
105
+ * Content-based matcher for `.md` files. Inspects frontmatter keys and body
106
+ * content to classify markdown as agent, command, or knowledge.
107
+ *
108
+ * Specificity levels:
109
+ * 20 -- agent-exclusive signals (`tools`, `toolPolicy`)
110
+ * 18 -- command content signals (`agent` frontmatter, `$ARGUMENTS`/`$1`-`$3`)
111
+ * 8 -- weak agent signal (`model` alone)
112
+ * 5 -- knowledge fallback (any unclassified `.md`)
113
+ *
114
+ * Command signals at 18 override directory hints (10/15) because the content
115
+ * unambiguously identifies a command template. Agent-exclusive signals at 20
116
+ * still win over command signals when both are present.
117
+ */
118
+ export function smartMdMatcher(ctx) {
119
+ if (ctx.ext !== ".md")
120
+ return null;
121
+ const fm = ctx.frontmatter();
122
+ if (fm) {
123
+ // Agent-exclusive indicators: toolPolicy or tools
124
+ // These return high specificity (20) to override everything else.
125
+ if ("toolPolicy" in fm || "tools" in fm) {
126
+ return { type: "agent", specificity: 20, renderer: "agent-md" };
127
+ }
128
+ // Command signal: `agent` frontmatter key names a dispatch target.
129
+ // This is an OpenCode convention specific to commands.
130
+ if ("agent" in fm) {
131
+ return { type: "command", specificity: 18, renderer: "command-md" };
132
+ }
133
+ }
134
+ // Command signal: body contains $ARGUMENTS or $1/$2/$3 placeholders.
135
+ // These are definitively command template patterns (OpenCode convention).
136
+ const body = ctx.content();
137
+ if (COMMAND_PLACEHOLDER_RE.test(body)) {
138
+ return { type: "command", specificity: 18, renderer: "command-md" };
139
+ }
140
+ if (fm) {
141
+ // model alone is a weaker agent signal (specificity 8) -- it can appear
142
+ // on commands too (OpenCode convention). Directory hints (10/15) win
143
+ // when the file lives in commands/, but model still classifies an .md
144
+ // as agent when no directory hint is present.
145
+ if ("model" in fm) {
146
+ return { type: "agent", specificity: 8, renderer: "agent-md" };
147
+ }
148
+ }
149
+ // Weak fallback: any .md file is assumed to be knowledge
150
+ return { type: "knowledge", specificity: 5, renderer: "knowledge-md" };
151
+ }
152
+ // ── Registration ────────────────────────────────────────────────────────────
153
+ /** All built-in matchers in registration order (later wins ties). */
154
+ const builtinMatchers = [extensionMatcher, directoryMatcher, parentDirHintMatcher, smartMdMatcher];
155
+ /**
156
+ * Register all built-in matchers with the file-context registry.
157
+ * Called once from the CLI entry point (or ensureBuiltinsRegistered).
158
+ */
159
+ export function registerBuiltinMatchers() {
160
+ for (const matcher of builtinMatchers) {
161
+ registerMatcher(matcher);
162
+ }
163
+ }
@@ -1,9 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { deriveCanonicalAssetName, isRelevantAssetFile } from "./asset-spec";
4
+ import { tryGetHandler } from "./asset-type-handler";
3
5
  import { isAssetType } from "./common";
4
- import { isRelevantAssetFile, deriveCanonicalAssetName } from "./asset-spec";
5
6
  import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
6
- import { tryGetHandler } from "./asset-type-handler";
7
+ import { warn } from "./warn";
7
8
  // ── Load / Write ────────────────────────────────────────────────────────────
8
9
  const STASH_FILENAME = ".stash.json";
9
10
  export function stashFilePath(dirPath) {
@@ -20,8 +21,15 @@ export function loadStashFile(dirPath) {
20
21
  const entries = [];
21
22
  for (const e of raw.entries) {
22
23
  const validated = validateStashEntry(e);
23
- if (validated)
24
+ if (validated) {
24
25
  entries.push(validated);
26
+ }
27
+ else {
28
+ const name = typeof e === "object" && e !== null && typeof e.name === "string"
29
+ ? e.name
30
+ : "(unknown)";
31
+ warn(`Warning: Skipping invalid entry "${name}" in ${filePath}`);
32
+ }
25
33
  }
26
34
  return entries.length > 0 ? { entries } : null;
27
35
  }
@@ -31,7 +39,20 @@ export function loadStashFile(dirPath) {
31
39
  }
32
40
  export function writeStashFile(dirPath, stash) {
33
41
  const filePath = stashFilePath(dirPath);
34
- fs.writeFileSync(filePath, JSON.stringify(stash, null, 2) + "\n", "utf8");
42
+ const tmpPath = filePath + `.tmp.${process.pid}`;
43
+ try {
44
+ fs.writeFileSync(tmpPath, JSON.stringify(stash, null, 2) + "\n", "utf8");
45
+ fs.renameSync(tmpPath, filePath);
46
+ }
47
+ catch (err) {
48
+ try {
49
+ fs.unlinkSync(tmpPath);
50
+ }
51
+ catch {
52
+ /* ignore cleanup failure */
53
+ }
54
+ throw err;
55
+ }
35
56
  }
36
57
  export function validateStashEntry(entry) {
37
58
  if (typeof entry !== "object" || entry === null)
@@ -74,7 +95,8 @@ export function validateStashEntry(entry) {
74
95
  result.quality = e.quality;
75
96
  if (typeof e.confidence === "number" && Number.isFinite(e.confidence))
76
97
  result.confidence = Math.max(0, Math.min(1, e.confidence));
77
- if (typeof e.source === "string" && ["package", "frontmatter", "comments", "filename", "manual", "llm"].includes(e.source)) {
98
+ if (typeof e.source === "string" &&
99
+ ["package", "frontmatter", "comments", "filename", "manual", "llm"].includes(e.source)) {
78
100
  result.source = e.source;
79
101
  }
80
102
  if (Array.isArray(e.aliases)) {
@@ -87,9 +109,7 @@ export function validateStashEntry(entry) {
87
109
  if (typeof h !== "object" || h === null)
88
110
  return false;
89
111
  const rec = h;
90
- return typeof rec.level === "number"
91
- && typeof rec.text === "string"
92
- && typeof rec.line === "number";
112
+ return typeof rec.level === "number" && typeof rec.text === "string" && typeof rec.line === "number";
93
113
  });
94
114
  if (validated.length > 0)
95
115
  result.toc = validated;
@@ -123,9 +143,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
123
143
  // Skip non-relevant files
124
144
  if (!isRelevantAssetFile(assetType, fileName))
125
145
  continue;
126
- const canonicalName = assetType === "skill"
127
- ? deriveCanonicalAssetName(assetType, typeRoot, file) ?? baseName
128
- : baseName;
146
+ const canonicalName = assetType === "skill" ? (deriveCanonicalAssetName(assetType, typeRoot, file) ?? baseName) : baseName;
129
147
  const entry = {
130
148
  name: canonicalName,
131
149
  type: assetType,
@@ -134,7 +152,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
134
152
  confidence: 0.55,
135
153
  source: "filename",
136
154
  };
137
- // Priority 1: package.json metadata
155
+ // Priority 1: Package.json metadata
138
156
  if (pkgMeta) {
139
157
  if (pkgMeta.description && !entry.description) {
140
158
  entry.description = pkgMeta.description;
@@ -144,7 +162,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
144
162
  if (pkgMeta.keywords && pkgMeta.keywords.length > 0)
145
163
  entry.tags = normalizeTerms(pkgMeta.keywords);
146
164
  }
147
- // Priority 2: Frontmatter (for .md files overrides package.json description)
165
+ // Priority 2: Frontmatter (for .md files -- overrides package.json description)
148
166
  if (ext === ".md") {
149
167
  const fm = extractFrontmatterDescription(file);
150
168
  if (fm) {
@@ -153,7 +171,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
153
171
  entry.confidence = 0.9;
154
172
  }
155
173
  }
156
- // Type-specific metadata extraction (e.g. TOC for knowledge, comments for tools/scripts)
174
+ // Priority 3: Type-specific metadata extraction (e.g. TOC for knowledge, comments for tools/scripts)
157
175
  const handler = tryGetHandler(assetType);
158
176
  if (handler?.extractTypeMetadata) {
159
177
  handler.extractTypeMetadata(entry, file, ext);
@@ -215,7 +233,10 @@ export function extractDescriptionFromComments(filePath) {
215
233
  const line = lines[i];
216
234
  if (i > blockStart && /\*\//.test(line))
217
235
  break;
218
- const cleaned = line.replace(/^\s*\/?\*\*?\s?/, "").replace(/\*\/\s*$/, "").trim();
236
+ const cleaned = line
237
+ .replace(/^\s*\/?\*\*?\s?/, "")
238
+ .replace(/\*\/\s*$/, "")
239
+ .trim();
219
240
  if (cleaned)
220
241
  desc.push(cleaned);
221
242
  }
@@ -233,7 +254,6 @@ export function extractDescriptionFromComments(filePath) {
233
254
  hashLines.push(line.replace(/^#+\s*/, "").trim());
234
255
  }
235
256
  else if (line === "") {
236
- continue;
237
257
  }
238
258
  else {
239
259
  break;
@@ -5,21 +5,22 @@ import { parseRegistryRef } from "./registry-resolve";
5
5
  * sources, return the subset of sources to search.
6
6
  *
7
7
  * Resolution order:
8
- * 1. undefined → all sources (working → mounted → installed)
9
- * 2. "local" → working stash only
10
- * 3. exact match → installed source whose registryId matches verbatim
8
+ * 1. undefined → all sources
9
+ * 2. "local" → primary stash only (first entry)
10
+ * 3. exact match → source whose registryId matches verbatim
11
11
  * 4. parsed match → parse origin as a registry ref, match by parsed ID
12
- * 5. path match → mounted source whose path matches
12
+ * 5. path match → source whose resolved path matches the origin
13
13
  * 6. empty → indicates a remote/uninstalled origin (caller decides)
14
14
  */
15
15
  export function resolveSourcesForOrigin(origin, allSources) {
16
16
  if (!origin)
17
17
  return allSources;
18
+ // "local" means the primary stash (first entry)
18
19
  if (origin === "local") {
19
- return allSources.filter((s) => s.kind === "working");
20
+ return allSources.length > 0 ? [allSources[0]] : [];
20
21
  }
21
22
  // Exact registryId match (e.g. origin is "npm:@scope/pkg")
22
- const byExactId = allSources.filter((s) => s.kind === "installed" && s.registryId === origin);
23
+ const byExactId = allSources.filter((s) => s.registryId !== undefined && s.registryId === origin);
23
24
  if (byExactId.length > 0)
24
25
  return byExactId;
25
26
  // Parse origin as a registry ref and match by parsed ID.
@@ -27,16 +28,16 @@ export function resolveSourcesForOrigin(origin, allSources) {
27
28
  // "@scope/pkg" matches "npm:@scope/pkg".
28
29
  try {
29
30
  const parsed = parseRegistryRef(origin);
30
- const byParsedId = allSources.filter((s) => s.kind === "installed" && s.registryId === parsed.id);
31
+ const byParsedId = allSources.filter((s) => s.registryId !== undefined && s.registryId === parsed.id);
31
32
  if (byParsedId.length > 0)
32
33
  return byParsedId;
33
34
  }
34
35
  catch {
35
36
  // Not a valid registry ref — continue to path matching
36
37
  }
37
- // Mounted stash by resolved path
38
+ // Match by resolved path (any source, including installed)
38
39
  const resolvedOrigin = path.resolve(origin);
39
- const byPath = allSources.filter((s) => s.kind === "mounted" && path.resolve(s.path) === resolvedOrigin);
40
+ const byPath = allSources.filter((s) => path.resolve(s.path) === resolvedOrigin);
40
41
  if (byPath.length > 0)
41
42
  return byPath;
42
43
  // No match — origin may be remote/uninstalled
package/dist/paths.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Centralized path resolution for all agentikit directories.
3
+ *
4
+ * Provides platform-aware paths for config, cache, and stash directories,
5
+ * following XDG Base Directory conventions on Unix and standard locations
6
+ * on Windows.
7
+ */
8
+ import path from "node:path";
9
+ import { ConfigError } from "./errors";
10
+ const IS_WINDOWS = process.platform === "win32";
11
+ // ── Config directory ─────────────────────────────────────────────────────────
12
+ export function getConfigDir(env = process.env, platform = process.platform) {
13
+ if (platform === "win32") {
14
+ const appData = env.APPDATA?.trim();
15
+ if (appData)
16
+ return path.join(appData, "agentikit");
17
+ const userProfile = env.USERPROFILE?.trim();
18
+ if (!userProfile) {
19
+ throw new ConfigError("Unable to determine config directory. Set APPDATA or USERPROFILE.");
20
+ }
21
+ return path.join(userProfile, "AppData", "Roaming", "agentikit");
22
+ }
23
+ const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
24
+ if (xdgConfigHome)
25
+ return path.join(xdgConfigHome, "agentikit");
26
+ const home = env.HOME?.trim();
27
+ if (!home) {
28
+ throw new ConfigError("Unable to determine config directory. Set XDG_CONFIG_HOME or HOME.");
29
+ }
30
+ return path.join(home, ".config", "agentikit");
31
+ }
32
+ export function getConfigPath() {
33
+ return path.join(getConfigDir(), "config.json");
34
+ }
35
+ // ── Cache directory ──────────────────────────────────────────────────────────
36
+ export function getCacheDir() {
37
+ if (IS_WINDOWS) {
38
+ const localAppData = process.env.LOCALAPPDATA?.trim();
39
+ if (localAppData)
40
+ return path.join(localAppData, "agentikit");
41
+ const userProfile = process.env.USERPROFILE?.trim();
42
+ if (userProfile)
43
+ return path.join(userProfile, "AppData", "Local", "agentikit");
44
+ const appData = process.env.APPDATA?.trim();
45
+ if (!appData) {
46
+ throw new ConfigError("Unable to determine cache directory. Set LOCALAPPDATA, USERPROFILE, or APPDATA.");
47
+ }
48
+ return path.join(appData, "..", "Local", "agentikit");
49
+ }
50
+ const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
51
+ if (xdgCacheHome)
52
+ return path.join(xdgCacheHome, "agentikit");
53
+ const home = process.env.HOME?.trim();
54
+ if (!home)
55
+ return path.join("/tmp", "agentikit-cache");
56
+ return path.join(home, ".cache", "agentikit");
57
+ }
58
+ export function getDbPath() {
59
+ return path.join(getCacheDir(), "index.db");
60
+ }
61
+ export function getRegistryCacheDir() {
62
+ return path.join(getCacheDir(), "registry");
63
+ }
64
+ export function getRegistryIndexCacheDir() {
65
+ return path.join(getCacheDir(), "registry-index");
66
+ }
67
+ export function getBinDir() {
68
+ return path.join(getCacheDir(), "bin");
69
+ }
70
+ // ── Default stash directory ──────────────────────────────────────────────────
71
+ export function getDefaultStashDir() {
72
+ if (IS_WINDOWS) {
73
+ const userProfile = process.env.USERPROFILE?.trim();
74
+ if (userProfile)
75
+ return path.join(userProfile, "Documents", "agentikit");
76
+ return path.join("C:\\", "agentikit");
77
+ }
78
+ const home = process.env.HOME?.trim();
79
+ if (!home) {
80
+ throw new ConfigError("Unable to determine default stash directory. Set HOME.");
81
+ }
82
+ return path.join(home, "agentikit");
83
+ }