@pixelguild/loom 0.1.0
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/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +45 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +9 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +129 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/output.d.ts +6 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +20 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/status.d.ts +13 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +69 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/config/config-loader.d.ts +9 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +64 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/types.d.ts +32 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/slugify.d.ts +2 -0
- package/dist/lib/slugify.d.ts.map +1 -0
- package/dist/lib/slugify.js +9 -0
- package/dist/lib/slugify.js.map +1 -0
- package/dist/lib/tokens.d.ts +2 -0
- package/dist/lib/tokens.d.ts.map +1 -0
- package/dist/lib/tokens.js +15 -0
- package/dist/lib/tokens.js.map +1 -0
- package/dist/providers/ollama-provider.d.ts +11 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -0
- package/dist/providers/ollama-provider.js +37 -0
- package/dist/providers/ollama-provider.js.map +1 -0
- package/dist/providers/openai-provider.d.ts +10 -0
- package/dist/providers/openai-provider.d.ts.map +1 -0
- package/dist/providers/openai-provider.js +25 -0
- package/dist/providers/openai-provider.js.map +1 -0
- package/dist/providers/provider-factory.d.ts +5 -0
- package/dist/providers/provider-factory.d.ts.map +1 -0
- package/dist/providers/provider-factory.js +19 -0
- package/dist/providers/provider-factory.js.map +1 -0
- package/dist/providers/types.d.ts +11 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/vertex-provider.d.ts +12 -0
- package/dist/providers/vertex-provider.d.ts.map +1 -0
- package/dist/providers/vertex-provider.js +32 -0
- package/dist/providers/vertex-provider.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +143 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/archive.d.ts +22 -0
- package/dist/storage/archive.d.ts.map +1 -0
- package/dist/storage/archive.js +114 -0
- package/dist/storage/archive.js.map +1 -0
- package/dist/storage/context-file.d.ts +16 -0
- package/dist/storage/context-file.d.ts.map +1 -0
- package/dist/storage/context-file.js +68 -0
- package/dist/storage/context-file.js.map +1 -0
- package/dist/storage/database.d.ts +28 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +195 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/intelligent-archive.d.ts +36 -0
- package/dist/storage/intelligent-archive.d.ts.map +1 -0
- package/dist/storage/intelligent-archive.js +183 -0
- package/dist/storage/intelligent-archive.js.map +1 -0
- package/dist/tools/archive-context.d.ts +7 -0
- package/dist/tools/archive-context.d.ts.map +1 -0
- package/dist/tools/archive-context.js +60 -0
- package/dist/tools/archive-context.js.map +1 -0
- package/dist/tools/consult-peer.d.ts +18 -0
- package/dist/tools/consult-peer.d.ts.map +1 -0
- package/dist/tools/consult-peer.js +48 -0
- package/dist/tools/consult-peer.js.map +1 -0
- package/dist/tools/create-manifest.d.ts +14 -0
- package/dist/tools/create-manifest.d.ts.map +1 -0
- package/dist/tools/create-manifest.js +23 -0
- package/dist/tools/create-manifest.js.map +1 -0
- package/dist/tools/find-pattern.d.ts +14 -0
- package/dist/tools/find-pattern.d.ts.map +1 -0
- package/dist/tools/find-pattern.js +13 -0
- package/dist/tools/find-pattern.js.map +1 -0
- package/dist/tools/get-context.d.ts +9 -0
- package/dist/tools/get-context.d.ts.map +1 -0
- package/dist/tools/get-context.js +11 -0
- package/dist/tools/get-context.js.map +1 -0
- package/dist/tools/get-manifest.d.ts +21 -0
- package/dist/tools/get-manifest.d.ts.map +1 -0
- package/dist/tools/get-manifest.js +53 -0
- package/dist/tools/get-manifest.js.map +1 -0
- package/dist/tools/get-session-status.d.ts +3 -0
- package/dist/tools/get-session-status.d.ts.map +1 -0
- package/dist/tools/get-session-status.js +20 -0
- package/dist/tools/get-session-status.js.map +1 -0
- package/dist/tools/log-context.d.ts +13 -0
- package/dist/tools/log-context.d.ts.map +1 -0
- package/dist/tools/log-context.js +29 -0
- package/dist/tools/log-context.js.map +1 -0
- package/dist/tools/save-pattern.d.ts +15 -0
- package/dist/tools/save-pattern.d.ts.map +1 -0
- package/dist/tools/save-pattern.js +19 -0
- package/dist/tools/save-pattern.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { runInit } from './init.js';
|
|
4
|
+
import { runStatus } from './status.js';
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const command = args[0];
|
|
7
|
+
function printUsage() {
|
|
8
|
+
console.log(`
|
|
9
|
+
Usage: loom <command>
|
|
10
|
+
|
|
11
|
+
Commands:
|
|
12
|
+
init Initialize Loom in the current project
|
|
13
|
+
status Show context health for the current project
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--help Show this help message
|
|
17
|
+
--version Show version
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
function printVersion() {
|
|
21
|
+
console.log('loom 0.1.0');
|
|
22
|
+
}
|
|
23
|
+
const projectRoot = path.resolve(process.cwd());
|
|
24
|
+
switch (command) {
|
|
25
|
+
case 'init':
|
|
26
|
+
runInit(projectRoot);
|
|
27
|
+
break;
|
|
28
|
+
case 'status':
|
|
29
|
+
runStatus(projectRoot);
|
|
30
|
+
break;
|
|
31
|
+
case '--help':
|
|
32
|
+
case '-h':
|
|
33
|
+
case undefined:
|
|
34
|
+
printUsage();
|
|
35
|
+
break;
|
|
36
|
+
case '--version':
|
|
37
|
+
case '-v':
|
|
38
|
+
printVersion();
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
console.error(`Unknown command: ${command}`);
|
|
42
|
+
printUsage();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;CAUb,CAAC,CAAC;AACH,CAAC;AAED,SAAS,YAAY;IACnB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAEhD,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,MAAM;QACT,OAAO,CAAC,WAAW,CAAC,CAAC;QACrB,MAAM;IACR,KAAK,QAAQ;QACX,SAAS,CAAC,WAAW,CAAC,CAAC;QACvB,MAAM;IACR,KAAK,QAAQ,CAAC;IACd,KAAK,IAAI,CAAC;IACV,KAAK,SAAS;QACZ,UAAU,EAAE,CAAC;QACb,MAAM;IACR,KAAK,WAAW,CAAC;IACjB,KAAK,IAAI;QACP,YAAY,EAAE,CAAC;QACf,MAAM;IACR;QACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QAC7C,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface InitResult {
|
|
2
|
+
dirsCreated: boolean;
|
|
3
|
+
claudeMdAction: 'created' | 'appended' | 'skipped';
|
|
4
|
+
mcpJsonAction: 'created' | 'merged' | 'skipped';
|
|
5
|
+
claudeOnPath: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function loomInit(projectRoot: string): InitResult;
|
|
8
|
+
export declare function runInit(projectRoot: string): void;
|
|
9
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IACnD,aAAa,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAChD,YAAY,EAAE,OAAO,CAAC;CACvB;AA8ED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAOxD;AAED,wBAAgB,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAwCjD"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { formatCreated, formatSkipped, formatWarning } from './output.js';
|
|
5
|
+
const LOOM_CLAUDE_BLOCK = `
|
|
6
|
+
## Loom
|
|
7
|
+
|
|
8
|
+
Loom is your persistent memory. The developer picks up where you left off using only what you log — if you don't log it, it's gone.
|
|
9
|
+
|
|
10
|
+
**Session start**: Call \`loom_get_context\` before any work. Read it fully.
|
|
11
|
+
|
|
12
|
+
**During work** — call \`loom_log_context\` after every:
|
|
13
|
+
- File created, modified, or deleted → type: \`action\`
|
|
14
|
+
- Decision made (approach, library, architecture) → type: \`decision\`
|
|
15
|
+
- Problem or error encountered → type: \`issue\`
|
|
16
|
+
- Dead end explored and abandoned → type: \`dead_end\`
|
|
17
|
+
- Open question to resolve later → type: \`question\`
|
|
18
|
+
|
|
19
|
+
**When stuck**: After two failed attempts, call \`loom_consult_peer\`. Describe the problem precisely. Use the response before trying again.
|
|
20
|
+
|
|
21
|
+
**Before implementing anything non-trivial**: Call \`loom_find_pattern\`. Check if this has been solved in another project first.
|
|
22
|
+
|
|
23
|
+
**Context health**: Call \`loom_get_session_status\` every 10 log entries. If above 60k tokens, call \`loom_archive_context\` before continuing.
|
|
24
|
+
|
|
25
|
+
**Session end**: When wrapping up, call \`loom_log_context\` with type \`session_end\` — what was completed, what remains, immediate next steps.
|
|
26
|
+
`;
|
|
27
|
+
function resolveServerPath() {
|
|
28
|
+
return path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'index.js');
|
|
29
|
+
}
|
|
30
|
+
function scaffoldDirs(projectRoot) {
|
|
31
|
+
const archivesDir = path.join(projectRoot, 'docs', 'loom', 'archives');
|
|
32
|
+
const manifestsDir = path.join(projectRoot, 'docs', 'loom', 'manifests');
|
|
33
|
+
if (fs.existsSync(archivesDir) && fs.existsSync(manifestsDir)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
fs.mkdirSync(archivesDir, { recursive: true });
|
|
37
|
+
fs.mkdirSync(manifestsDir, { recursive: true });
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function injectClaudeMd(projectRoot) {
|
|
41
|
+
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
42
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
43
|
+
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
44
|
+
if (content.includes('## Loom')) {
|
|
45
|
+
return 'skipped';
|
|
46
|
+
}
|
|
47
|
+
fs.appendFileSync(claudeMdPath, '\n' + LOOM_CLAUDE_BLOCK);
|
|
48
|
+
return 'appended';
|
|
49
|
+
}
|
|
50
|
+
fs.writeFileSync(claudeMdPath, LOOM_CLAUDE_BLOCK.trimStart());
|
|
51
|
+
return 'created';
|
|
52
|
+
}
|
|
53
|
+
function wireMcpJson(projectRoot) {
|
|
54
|
+
const mcpJsonPath = path.join(projectRoot, '.claude', 'mcp.json');
|
|
55
|
+
const serverPath = resolveServerPath();
|
|
56
|
+
const loomEntry = {
|
|
57
|
+
command: 'node',
|
|
58
|
+
args: [serverPath],
|
|
59
|
+
env: { LOOM_PROJECT_ROOT: projectRoot },
|
|
60
|
+
};
|
|
61
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
62
|
+
const raw = fs.readFileSync(mcpJsonPath, 'utf-8');
|
|
63
|
+
const config = JSON.parse(raw);
|
|
64
|
+
if (config.mcpServers?.loom) {
|
|
65
|
+
return 'skipped';
|
|
66
|
+
}
|
|
67
|
+
config.mcpServers = config.mcpServers ?? {};
|
|
68
|
+
config.mcpServers.loom = loomEntry;
|
|
69
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2) + '\n');
|
|
70
|
+
return 'merged';
|
|
71
|
+
}
|
|
72
|
+
fs.mkdirSync(path.join(projectRoot, '.claude'), { recursive: true });
|
|
73
|
+
const config = { mcpServers: { loom: loomEntry } };
|
|
74
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2) + '\n');
|
|
75
|
+
return 'created';
|
|
76
|
+
}
|
|
77
|
+
function checkClaudeOnPath() {
|
|
78
|
+
try {
|
|
79
|
+
execFileSync('which', ['claude'], { stdio: 'ignore' });
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function loomInit(projectRoot) {
|
|
87
|
+
const dirsCreated = scaffoldDirs(projectRoot);
|
|
88
|
+
const claudeMdAction = injectClaudeMd(projectRoot);
|
|
89
|
+
const mcpJsonAction = wireMcpJson(projectRoot);
|
|
90
|
+
const claudeOnPath = checkClaudeOnPath();
|
|
91
|
+
return { dirsCreated, claudeMdAction, mcpJsonAction, claudeOnPath };
|
|
92
|
+
}
|
|
93
|
+
export function runInit(projectRoot) {
|
|
94
|
+
console.log('\nInitializing Loom...\n');
|
|
95
|
+
const result = loomInit(projectRoot);
|
|
96
|
+
if (result.dirsCreated) {
|
|
97
|
+
console.log(formatCreated('docs/loom/ (archives/, manifests/)'));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log(formatSkipped('docs/loom/ already exists'));
|
|
101
|
+
}
|
|
102
|
+
switch (result.claudeMdAction) {
|
|
103
|
+
case 'created':
|
|
104
|
+
console.log(formatCreated('CLAUDE.md with Loom instructions'));
|
|
105
|
+
break;
|
|
106
|
+
case 'appended':
|
|
107
|
+
console.log(formatCreated('Loom block appended to CLAUDE.md'));
|
|
108
|
+
break;
|
|
109
|
+
case 'skipped':
|
|
110
|
+
console.log(formatSkipped('CLAUDE.md already has ## Loom section'));
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
switch (result.mcpJsonAction) {
|
|
114
|
+
case 'created':
|
|
115
|
+
console.log(formatCreated('.claude/mcp.json with Loom server'));
|
|
116
|
+
break;
|
|
117
|
+
case 'merged':
|
|
118
|
+
console.log(formatCreated('Loom server added to .claude/mcp.json'));
|
|
119
|
+
break;
|
|
120
|
+
case 'skipped':
|
|
121
|
+
console.log(formatSkipped('.claude/mcp.json already has loom entry'));
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
if (!result.claudeOnPath) {
|
|
125
|
+
console.log(formatWarning('claude CLI not found on PATH — install Claude Code to use Loom'));
|
|
126
|
+
}
|
|
127
|
+
console.log('\nDone! Restart Claude Code to activate Loom in this project.\n');
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function formatCreated(msg: string): string;
|
|
2
|
+
export declare function formatSkipped(msg: string): string;
|
|
3
|
+
export declare function formatWarning(msg: string): string;
|
|
4
|
+
export declare function formatError(msg: string): string;
|
|
5
|
+
export declare function stripAnsi(str: string): string;
|
|
6
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const green = '\x1b[32m';
|
|
2
|
+
const yellow = '\x1b[33m';
|
|
3
|
+
const red = '\x1b[31m';
|
|
4
|
+
const reset = '\x1b[0m';
|
|
5
|
+
export function formatCreated(msg) {
|
|
6
|
+
return ` ${green}+${reset} created ${msg}`;
|
|
7
|
+
}
|
|
8
|
+
export function formatSkipped(msg) {
|
|
9
|
+
return ` ${yellow}-${reset} skipped ${msg}`;
|
|
10
|
+
}
|
|
11
|
+
export function formatWarning(msg) {
|
|
12
|
+
return ` ${yellow}!${reset} warning ${msg}`;
|
|
13
|
+
}
|
|
14
|
+
export function formatError(msg) {
|
|
15
|
+
return ` ${red}x${reset} error ${msg}`;
|
|
16
|
+
}
|
|
17
|
+
export function stripAnsi(str) {
|
|
18
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,GAAG,GAAG,UAAU,CAAC;AACvB,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,KAAK,KAAK,IAAI,KAAK,YAAY,GAAG,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,KAAK,MAAM,IAAI,KAAK,YAAY,GAAG,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,KAAK,MAAM,IAAI,KAAK,YAAY,GAAG,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,KAAK,GAAG,IAAI,KAAK,UAAU,GAAG,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface StatusResult {
|
|
2
|
+
initialized: boolean;
|
|
3
|
+
tokenCount: number;
|
|
4
|
+
archiveCount: number;
|
|
5
|
+
lastEntryTimestamp: string | null;
|
|
6
|
+
warningThreshold: number;
|
|
7
|
+
archiveThreshold: number;
|
|
8
|
+
needsArchive: boolean;
|
|
9
|
+
registeredInDb: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function getStatus(projectRoot: string): StatusResult;
|
|
12
|
+
export declare function runStatus(projectRoot: string): void;
|
|
13
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAiD3D;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAgBnD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { ContextFile } from '../storage/context-file.js';
|
|
5
|
+
import { countTokens } from '../lib/tokens.js';
|
|
6
|
+
import { ConfigLoader } from '../config/config-loader.js';
|
|
7
|
+
import { LoomDatabase } from '../storage/database.js';
|
|
8
|
+
export function getStatus(projectRoot) {
|
|
9
|
+
const loomDir = path.join(projectRoot, 'docs', 'loom');
|
|
10
|
+
if (!fs.existsSync(loomDir) || !fs.statSync(loomDir).isDirectory()) {
|
|
11
|
+
return {
|
|
12
|
+
initialized: false,
|
|
13
|
+
tokenCount: 0,
|
|
14
|
+
archiveCount: 0,
|
|
15
|
+
lastEntryTimestamp: null,
|
|
16
|
+
warningThreshold: 0,
|
|
17
|
+
archiveThreshold: 0,
|
|
18
|
+
needsArchive: false,
|
|
19
|
+
registeredInDb: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const configLoader = new ConfigLoader(projectRoot);
|
|
23
|
+
const config = configLoader.load();
|
|
24
|
+
const { warning, archive } = config.archive_thresholds;
|
|
25
|
+
const contextFile = new ContextFile(projectRoot);
|
|
26
|
+
const content = contextFile.read();
|
|
27
|
+
const tokenCount = countTokens(content);
|
|
28
|
+
const archives = contextFile.listArchives();
|
|
29
|
+
const lastEntryTimestamp = contextFile.getLastEntryTimestamp();
|
|
30
|
+
let registeredInDb = false;
|
|
31
|
+
try {
|
|
32
|
+
const dbPath = path.join(os.homedir(), '.loom', 'loom.db');
|
|
33
|
+
if (fs.existsSync(dbPath)) {
|
|
34
|
+
const db = new LoomDatabase(dbPath);
|
|
35
|
+
const projects = db.listProjects();
|
|
36
|
+
registeredInDb = projects.some(p => p.path === projectRoot);
|
|
37
|
+
db.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// DB check is best-effort
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
initialized: true,
|
|
45
|
+
tokenCount,
|
|
46
|
+
archiveCount: archives.length,
|
|
47
|
+
lastEntryTimestamp,
|
|
48
|
+
warningThreshold: warning,
|
|
49
|
+
archiveThreshold: archive,
|
|
50
|
+
needsArchive: tokenCount > archive,
|
|
51
|
+
registeredInDb,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function runStatus(projectRoot) {
|
|
55
|
+
const result = getStatus(projectRoot);
|
|
56
|
+
if (!result.initialized) {
|
|
57
|
+
console.log('\nLoom is not initialized in this project.');
|
|
58
|
+
console.log('Run `loom init` to get started.\n');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log('\nLoom Status\n');
|
|
62
|
+
console.log(` Tokens: ${result.tokenCount.toLocaleString()} / ${result.archiveThreshold.toLocaleString()}`);
|
|
63
|
+
console.log(` Archives: ${result.archiveCount}`);
|
|
64
|
+
console.log(` Last entry: ${result.lastEntryTimestamp ?? 'none'}`);
|
|
65
|
+
console.log(` Archive: ${result.needsArchive ? '\x1b[33mneeded\x1b[0m' : '\x1b[32mok\x1b[0m'}`);
|
|
66
|
+
console.log(` Global DB: ${result.registeredInDb ? 'registered' : 'not registered'}`);
|
|
67
|
+
console.log('');
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LoomConfig } from './types.js';
|
|
2
|
+
export declare const DEFAULT_CONFIG: LoomConfig;
|
|
3
|
+
export declare class ConfigLoader {
|
|
4
|
+
private readonly globalPath;
|
|
5
|
+
private readonly projectPath;
|
|
6
|
+
constructor(projectRoot: string, homedir?: string);
|
|
7
|
+
load(): LoomConfig;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,cAAc,EAAE,UAW5B,CAAC;AAqCF,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAMjD,IAAI,IAAI,UAAU;CAenB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
export const DEFAULT_CONFIG = {
|
|
5
|
+
default_provider: 'openai',
|
|
6
|
+
providers: {},
|
|
7
|
+
archive_thresholds: {
|
|
8
|
+
warning: 60_000,
|
|
9
|
+
archive: 80_000,
|
|
10
|
+
},
|
|
11
|
+
peer_consultation: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
allowed_providers: ['openai', 'vertex', 'ollama'],
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
function readJsonFile(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
console.error(`Warning: failed to parse config at ${filePath}:`, err);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function mergeConfig(base, overlay) {
|
|
30
|
+
return {
|
|
31
|
+
default_provider: overlay.default_provider ?? base.default_provider,
|
|
32
|
+
providers: overlay.providers
|
|
33
|
+
? { ...base.providers, ...overlay.providers }
|
|
34
|
+
: base.providers,
|
|
35
|
+
archive_thresholds: overlay.archive_thresholds
|
|
36
|
+
? { ...base.archive_thresholds, ...overlay.archive_thresholds }
|
|
37
|
+
: base.archive_thresholds,
|
|
38
|
+
peer_consultation: overlay.peer_consultation
|
|
39
|
+
? { ...base.peer_consultation, ...overlay.peer_consultation }
|
|
40
|
+
: base.peer_consultation,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export class ConfigLoader {
|
|
44
|
+
globalPath;
|
|
45
|
+
projectPath;
|
|
46
|
+
constructor(projectRoot, homedir) {
|
|
47
|
+
const home = homedir ?? os.homedir();
|
|
48
|
+
this.globalPath = path.join(home, '.loom', 'config.json');
|
|
49
|
+
this.projectPath = path.join(projectRoot, 'docs', 'loom', 'loom.config.json');
|
|
50
|
+
}
|
|
51
|
+
load() {
|
|
52
|
+
let config = { ...DEFAULT_CONFIG };
|
|
53
|
+
const globalRaw = readJsonFile(this.globalPath);
|
|
54
|
+
if (globalRaw) {
|
|
55
|
+
config = mergeConfig(config, globalRaw);
|
|
56
|
+
}
|
|
57
|
+
const projectRaw = readJsonFile(this.projectPath);
|
|
58
|
+
if (projectRaw) {
|
|
59
|
+
config = mergeConfig(config, projectRaw);
|
|
60
|
+
}
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/config/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAQzB,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE,EAAE;IACb,kBAAkB,EAAE;QAClB,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,MAAM;KAChB;IACD,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;KAClD;CACF,CAAC;AASF,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9F,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,sCAAsC,QAAQ,GAAG,EAAE,GAAG,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,IAAgB,EAAE,OAAkB;IACvD,OAAO;QACL,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC,gBAAgB;QACnE,SAAS,EAAE,OAAO,CAAC,SAAS;YAC1B,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE;YAC7C,CAAC,CAAC,IAAI,CAAC,SAAS;QAClB,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC5C,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE;YAC/D,CAAC,CAAC,IAAI,CAAC,kBAAkB;QAC3B,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC1C,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE;YAC7D,CAAC,CAAC,IAAI,CAAC,iBAAiB;KAC3B,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,YAAY;IACN,UAAU,CAAS;IACnB,WAAW,CAAS;IAErC,YAAY,WAAmB,EAAE,OAAgB;QAC/C,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChF,CAAC;IAED,IAAI;QACF,IAAI,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QAEnC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC1C,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface OpenAIProviderConfig {
|
|
2
|
+
model: string;
|
|
3
|
+
}
|
|
4
|
+
export interface VertexProviderConfig {
|
|
5
|
+
model: string;
|
|
6
|
+
project: string;
|
|
7
|
+
location: string;
|
|
8
|
+
}
|
|
9
|
+
export interface OllamaProviderConfig {
|
|
10
|
+
model: string;
|
|
11
|
+
host: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ProvidersConfig {
|
|
14
|
+
openai?: OpenAIProviderConfig;
|
|
15
|
+
vertex?: VertexProviderConfig;
|
|
16
|
+
ollama?: OllamaProviderConfig;
|
|
17
|
+
}
|
|
18
|
+
export interface ArchiveThresholdsConfig {
|
|
19
|
+
warning: number;
|
|
20
|
+
archive: number;
|
|
21
|
+
}
|
|
22
|
+
export interface PeerConsultationConfig {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
allowed_providers: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface LoomConfig {
|
|
27
|
+
default_provider: string;
|
|
28
|
+
providers: ProvidersConfig;
|
|
29
|
+
archive_thresholds: ArchiveThresholdsConfig;
|
|
30
|
+
peer_consultation: PeerConsultationConfig;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,kBAAkB,EAAE,uBAAuB,CAAC;IAC5C,iBAAiB,EAAE,sBAAsB,CAAC;CAC3C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { createServer } from './server.js';
|
|
5
|
+
import { LoomDatabase } from './storage/database.js';
|
|
6
|
+
import { ConfigLoader } from './config/config-loader.js';
|
|
7
|
+
async function main() {
|
|
8
|
+
const projectRoot = process.env['LOOM_PROJECT_ROOT'] || process.cwd();
|
|
9
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
10
|
+
const dbPath = path.join(os.homedir(), '.loom', 'loom.db');
|
|
11
|
+
const database = new LoomDatabase(dbPath);
|
|
12
|
+
const configLoader = new ConfigLoader(resolvedRoot);
|
|
13
|
+
const config = configLoader.load();
|
|
14
|
+
const loomCtx = {
|
|
15
|
+
projectRoot: resolvedRoot,
|
|
16
|
+
loomDir: path.join(resolvedRoot, 'docs', 'loom'),
|
|
17
|
+
contextFilePath: path.join(resolvedRoot, 'docs', 'loom', 'context.md'),
|
|
18
|
+
archivesDir: path.join(resolvedRoot, 'docs', 'loom', 'archives'),
|
|
19
|
+
manifestsDir: path.join(resolvedRoot, 'docs', 'loom', 'manifests'),
|
|
20
|
+
database,
|
|
21
|
+
config,
|
|
22
|
+
};
|
|
23
|
+
const server = createServer(loomCtx);
|
|
24
|
+
const transport = new StdioServerTransport();
|
|
25
|
+
await server.connect(transport);
|
|
26
|
+
console.error('Loom MCP server running on stdio');
|
|
27
|
+
}
|
|
28
|
+
main().catch((error) => {
|
|
29
|
+
console.error('Fatal error:', error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,KAAK,UAAU,IAAI;IACjB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACtE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAE3D,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,OAAO,GAAgB;QAC3B,WAAW,EAAE,YAAY;QACzB,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC;QAChD,eAAe,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC;QACtE,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC;QAChE,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC;QAClE,QAAQ;QACR,MAAM;KACP,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACpD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../src/lib/slugify.ts"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugify.js","sourceRoot":"","sources":["../../src/lib/slugify.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/lib/tokens.ts"],"names":[],"mappings":"AAaA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { encoding_for_model } from 'tiktoken';
|
|
2
|
+
let encoder = null;
|
|
3
|
+
function getEncoder() {
|
|
4
|
+
if (!encoder) {
|
|
5
|
+
encoder = encoding_for_model('gpt-4o');
|
|
6
|
+
}
|
|
7
|
+
return encoder;
|
|
8
|
+
}
|
|
9
|
+
export function countTokens(text) {
|
|
10
|
+
if (text.length === 0) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
return getEncoder().encode(text).length;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/lib/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAI9C,IAAI,OAAO,GAA2B,IAAI,CAAC;AAE3C,SAAS,UAAU;IACjB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,UAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PeerProvider, PeerResponse } from './types.js';
|
|
2
|
+
import type { OllamaProviderConfig } from '../config/types.js';
|
|
3
|
+
export declare class OllamaProvider implements PeerProvider {
|
|
4
|
+
readonly name = "ollama";
|
|
5
|
+
private readonly model;
|
|
6
|
+
private readonly host;
|
|
7
|
+
constructor(config?: OllamaProviderConfig);
|
|
8
|
+
isAvailable(): boolean;
|
|
9
|
+
call(prompt: string): Promise<PeerResponse>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ollama-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama-provider.d.ts","sourceRoot":"","sources":["../../src/providers/ollama-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAS/D,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,MAAM,CAAC,EAAE,oBAAoB;IAKzC,WAAW,IAAI,OAAO;IAIhB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CA4BlD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class OllamaProvider {
|
|
2
|
+
name = 'ollama';
|
|
3
|
+
model;
|
|
4
|
+
host;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.model = config?.model ?? 'llama3.1';
|
|
7
|
+
this.host = config?.host ?? 'http://localhost:11434';
|
|
8
|
+
}
|
|
9
|
+
isAvailable() {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
async call(prompt) {
|
|
13
|
+
const url = `${this.host}/api/chat`;
|
|
14
|
+
const res = await fetch(url, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
model: this.model,
|
|
19
|
+
messages: [{ role: 'user', content: prompt }],
|
|
20
|
+
stream: false,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
const body = await res.text();
|
|
25
|
+
throw new Error(`Ollama returned ${res.status}: ${body}`);
|
|
26
|
+
}
|
|
27
|
+
const data = (await res.json());
|
|
28
|
+
return {
|
|
29
|
+
response: data.message.content,
|
|
30
|
+
model: data.model,
|
|
31
|
+
tokens_used: data.eval_count !== undefined && data.prompt_eval_count !== undefined
|
|
32
|
+
? data.eval_count + data.prompt_eval_count
|
|
33
|
+
: undefined,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=ollama-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama-provider.js","sourceRoot":"","sources":["../../src/providers/ollama-provider.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,QAAQ,CAAC;IACR,KAAK,CAAS;IACd,IAAI,CAAS;IAE9B,YAAY,MAA6B;QACvC,IAAI,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,UAAU,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,MAAM,EAAE,IAAI,IAAI,wBAAwB,CAAC;IACvD,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,WAAW,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;gBAC7C,MAAM,EAAE,KAAK;aACd,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QAEtD,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;YAC9B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EACT,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS;gBACnE,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB;gBAC1C,CAAC,CAAC,SAAS;SAChB,CAAC;IACJ,CAAC;CACF"}
|