@manyoars/oars 0.5.1 → 0.5.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.
@@ -46,7 +46,7 @@ export const normalizeRemote = (url) => {
46
46
  return slash < 0 ? bare.toLowerCase() : `${bare.slice(0, slash).toLowerCase()}${bare.slice(slash)}`;
47
47
  };
48
48
  /** The `url` of `[remote "origin"]` in a git `config`, or null. */
49
- const originUrl = (config) => {
49
+ export const originUrl = (config) => {
50
50
  let inOrigin = false;
51
51
  for (const line of config.split(/\r?\n/)) {
52
52
  const section = /^\s*\[(.*)\]\s*$/.exec(line);
@@ -71,6 +71,21 @@ const readOrNull = (path) => {
71
71
  return null;
72
72
  }
73
73
  };
74
+ /**
75
+ * The git `config` text of a repository root, or null: `<root>/.git/config`, or for a linked worktree or submodule (`.git` is a file)
76
+ * the common directory's, reached through the file's `gitdir:` line and that directory's `commondir` file.
77
+ */
78
+ export const configOf = (root, read = readOrNull) => {
79
+ const direct = read(`${root}/.git/config`);
80
+ if (direct !== null)
81
+ return direct;
82
+ const pointer = /^gitdir:\s*(.+?)\s*$/m.exec(read(`${root}/.git`) ?? '')?.[1];
83
+ if (pointer === undefined)
84
+ return null;
85
+ const gitdir = resolveFrom(root, pointer);
86
+ const common = read(`${gitdir}/commondir`)?.trim();
87
+ return read(`${common === undefined || common === '' ? gitdir : resolveFrom(gitdir, common)}/config`);
88
+ };
74
89
  /**
75
90
  * A resolver from a repository root to its identity string: `origin:<normalized remote>` when the repository's `config` names an
76
91
  * `origin`, else `path:<root>`. The `config` is `<root>/.git/config`, or for a linked worktree (`.git` is a file) the common
@@ -78,22 +93,11 @@ const readOrNull = (path) => {
78
93
  */
79
94
  export const makeRepoIdentity = (read = readOrNull) => {
80
95
  const cache = new Map();
81
- const configOf = (root) => {
82
- const direct = read(`${root}/.git/config`);
83
- if (direct !== null)
84
- return direct;
85
- const pointer = /^gitdir:\s*(.+?)\s*$/m.exec(read(`${root}/.git`) ?? '')?.[1];
86
- if (pointer === undefined)
87
- return null;
88
- const gitdir = resolveFrom(root, pointer);
89
- const common = read(`${gitdir}/commondir`)?.trim();
90
- return read(`${common === undefined || common === '' ? gitdir : resolveFrom(gitdir, common)}/config`);
91
- };
92
96
  return (root) => {
93
97
  const known = cache.get(root);
94
98
  if (known !== undefined)
95
99
  return known;
96
- const url = originUrl(configOf(root) ?? '');
100
+ const url = originUrl(configOf(root, read) ?? '');
97
101
  const identity = url === null ? `path:${root.toLowerCase()}` : `origin:${normalizeRemote(url)}`;
98
102
  cache.set(root, identity);
99
103
  return identity;
@@ -17,10 +17,13 @@ export interface Vocabulary {
17
17
  models: ReadonlySet<string>;
18
18
  harnessVersions: ReadonlyMap<string, ReadonlySet<string>>;
19
19
  }
20
- /** packages/cli/vocab/, from src/sessions/ in a test and from dist/sessions/ once built: the same depth. */
21
- export declare const VOCAB_DIR: import("url").URL;
20
+ /**
21
+ * packages/cli/vocab/, from src/sessions/ in a test and from dist/sessions/ once built: the same depth. A function, not a constant: a bundle imported from a
22
+ * `data:` URL (the pool's sample-pack command) has no file location, and building this URL at load time would throw before that caller could pass its own folder.
23
+ */
24
+ export declare const vocabDir: () => URL;
22
25
  export declare const PACKAGE_FILES: readonly ["npm.json", "pypi.json", "crates.json", "rubygems.json", "homebrew.json", "go.json", "maven.json"];
23
26
  /** Reads and checks every snapshot under `dir` (default: the shipped folder). */
24
27
  export declare const readVocabulary: (dir?: URL) => Vocabulary;
25
- /** The shipped vocabulary, read once on first use: reading 6 MB of JSON is not free, and most commands never project. */
26
- export declare const vocabulary: () => Vocabulary;
28
+ /** The vocabulary of a folder (the shipped one by default), read once per folder on first use: reading 6 MB of JSON is not free, and most commands never project. `dir` is for a caller that cannot locate the folder itself. */
29
+ export declare const vocabulary: (dir?: URL) => Vocabulary;
@@ -3,8 +3,11 @@
3
3
  // A snapshot that does not parse is an error and never an empty list: an empty list would quietly project every name as `private`.
4
4
  import { readFileSync } from 'node:fs';
5
5
  import { VOCAB_SNAPSHOT_DATE, VOCAB_VERSION } from './public-vocabulary.js';
6
- /** packages/cli/vocab/, from src/sessions/ in a test and from dist/sessions/ once built: the same depth. */
7
- export const VOCAB_DIR = new URL('../../vocab/', import.meta.url);
6
+ /**
7
+ * packages/cli/vocab/, from src/sessions/ in a test and from dist/sessions/ once built: the same depth. A function, not a constant: a bundle imported from a
8
+ * `data:` URL (the pool's sample-pack command) has no file location, and building this URL at load time would throw before that caller could pass its own folder.
9
+ */
10
+ export const vocabDir = () => new URL('../../vocab/', import.meta.url);
8
11
  const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
9
12
  const recordAt = (value, where) => {
10
13
  if (!isRecord(value))
@@ -35,7 +38,7 @@ const packageSnapshot = (dir, file) => {
35
38
  };
36
39
  export const PACKAGE_FILES = ['npm.json', 'pypi.json', 'crates.json', 'rubygems.json', 'homebrew.json', 'go.json', 'maven.json'];
37
40
  /** Reads and checks every snapshot under `dir` (default: the shipped folder). */
38
- export const readVocabulary = (dir = VOCAB_DIR) => {
41
+ export const readVocabulary = (dir = vocabDir()) => {
39
42
  const manifest = readJson(dir, 'manifest.json');
40
43
  const snapshotDate = manifest['snapshotDate'];
41
44
  if (snapshotDate !== VOCAB_SNAPSHOT_DATE) {
@@ -56,6 +59,13 @@ export const readVocabulary = (dir = VOCAB_DIR) => {
56
59
  harnessVersions: new Map(Object.entries(harnesses).map(([harness, versions]) => [harness, new Set(stringsAt(versions, `harness.${harness}`))])),
57
60
  };
58
61
  };
59
- let loaded;
60
- /** The shipped vocabulary, read once on first use: reading 6 MB of JSON is not free, and most commands never project. */
61
- export const vocabulary = () => (loaded ??= readVocabulary());
62
+ const loaded = new Map();
63
+ /** The vocabulary of a folder (the shipped one by default), read once per folder on first use: reading 6 MB of JSON is not free, and most commands never project. `dir` is for a caller that cannot locate the folder itself. */
64
+ export const vocabulary = (dir = vocabDir()) => {
65
+ const held = loaded.get(dir.href);
66
+ if (held !== undefined)
67
+ return held;
68
+ const read = readVocabulary(dir);
69
+ loaded.set(dir.href, read);
70
+ return read;
71
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manyoars/oars",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "mcpName": "io.github.manyoars/oars",
5
5
  "description": "A local, searchable record of your coding agent sessions. Reads Claude Code, Cursor, Codex, Copilot CLI, Gemini CLI, and more, and keeps the record on your machine.",
6
6
  "homepage": "https://manyoars.com/",
@@ -49,7 +49,7 @@
49
49
  "test:live": "vitest run --config vitest.live.config.ts"
50
50
  },
51
51
  "dependencies": {
52
- "@manyoars/oars-client": "^0.1.1",
52
+ "@manyoars/oars-client": "^0.1.3",
53
53
  "commander": "^15.0.0",
54
54
  "openid-client": "^6.8.8"
55
55
  },