@hasna/knowledge 0.2.2 → 0.2.4
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 +86 -4
- package/bin/open-knowledge-mcp.js +14452 -0
- package/bin/open-knowledge.js +227 -4
- package/docs/architecture/ai-native-knowledge-base.md +191 -0
- package/docs/architecture/hybrid-semantic-search.md +135 -0
- package/package.json +20 -4
- package/src/artifact-store.ts +184 -0
- package/src/cli.ts +181 -25
- package/src/knowledge-db.ts +231 -0
- package/src/mcp.js +374 -415
- package/src/source-ref.ts +92 -0
- package/src/store.ts +16 -6
- package/src/wiki-layout.ts +104 -0
- package/src/workspace.ts +123 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -59
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -34
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -21
- package/.github/workflows/ci.yml +0 -49
- package/.takumi/settings.local.json +0 -7
- package/CODE_OF_CONDUCT.md +0 -31
- package/CONTRIBUTING.md +0 -83
- package/FUNDING.yml +0 -1
- package/SECURITY.md +0 -39
- package/npmignore +0 -9
- package/tests/cli.test.ts +0 -91
- package/tests/mcp-http.test.ts +0 -97
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export type SourceRefKind = 'open-files' | 's3' | 'file' | 'web';
|
|
2
|
+
|
|
3
|
+
export interface BaseSourceRef {
|
|
4
|
+
kind: SourceRefKind;
|
|
5
|
+
uri: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface OpenFilesSourceRef extends BaseSourceRef {
|
|
9
|
+
kind: 'open-files';
|
|
10
|
+
entity: 'file' | 'source';
|
|
11
|
+
id: string;
|
|
12
|
+
revision_id?: string;
|
|
13
|
+
path?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface S3SourceRef extends BaseSourceRef {
|
|
17
|
+
kind: 's3';
|
|
18
|
+
bucket: string;
|
|
19
|
+
key: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FileSourceRef extends BaseSourceRef {
|
|
23
|
+
kind: 'file';
|
|
24
|
+
path: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WebSourceRef extends BaseSourceRef {
|
|
28
|
+
kind: 'web';
|
|
29
|
+
url: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type SourceRef = OpenFilesSourceRef | S3SourceRef | FileSourceRef | WebSourceRef;
|
|
33
|
+
|
|
34
|
+
function assertNonEmpty(value: string | undefined, message: string): string {
|
|
35
|
+
if (!value) throw new Error(message);
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseOpenFilesRef(uri: string): OpenFilesSourceRef {
|
|
40
|
+
const withoutScheme = uri.slice('open-files://'.length);
|
|
41
|
+
const parts = withoutScheme.split('/').filter(Boolean);
|
|
42
|
+
const entity = parts[0];
|
|
43
|
+
if (entity !== 'file' && entity !== 'source') {
|
|
44
|
+
throw new Error("Invalid open-files ref. Expected open-files://file/<id>, open-files://file/<id>/revision/<revision_id>, or open-files://source/<id>/path/<path>.");
|
|
45
|
+
}
|
|
46
|
+
const id = assertNonEmpty(parts[1], 'Invalid open-files ref. Missing id.');
|
|
47
|
+
if (entity === 'file') {
|
|
48
|
+
if (parts.length === 2) return { kind: 'open-files', uri, entity, id };
|
|
49
|
+
if (parts[2] === 'revision' && parts[3] && parts.length === 4) {
|
|
50
|
+
return { kind: 'open-files', uri, entity, id, revision_id: decodeURIComponent(parts[3]) };
|
|
51
|
+
}
|
|
52
|
+
throw new Error('Invalid open-files file ref. Expected open-files://file/<id>/revision/<revision_id>.');
|
|
53
|
+
}
|
|
54
|
+
const pathIndex = parts.indexOf('path');
|
|
55
|
+
const path = pathIndex >= 0 ? decodeURIComponent(parts.slice(pathIndex + 1).join('/')) : undefined;
|
|
56
|
+
return { kind: 'open-files', uri, entity, id, path };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseS3Ref(uri: string): S3SourceRef {
|
|
60
|
+
const parsed = new URL(uri);
|
|
61
|
+
const bucket = assertNonEmpty(parsed.hostname, 'Invalid s3 ref. Missing bucket.');
|
|
62
|
+
const key = decodeURIComponent(parsed.pathname.replace(/^\/+/, ''));
|
|
63
|
+
if (!key) throw new Error('Invalid s3 ref. Missing object key.');
|
|
64
|
+
return { kind: 's3', uri, bucket, key };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseFileRef(uri: string): FileSourceRef {
|
|
68
|
+
const parsed = new URL(uri);
|
|
69
|
+
return { kind: 'file', uri, path: decodeURIComponent(parsed.pathname) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseWebRef(uri: string): WebSourceRef {
|
|
73
|
+
const parsed = new URL(uri);
|
|
74
|
+
return { kind: 'web', uri, url: parsed.toString() };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function parseSourceRef(uri: string): SourceRef {
|
|
78
|
+
if (uri.startsWith('open-files://')) return parseOpenFilesRef(uri);
|
|
79
|
+
if (uri.startsWith('s3://')) return parseS3Ref(uri);
|
|
80
|
+
if (uri.startsWith('file://')) return parseFileRef(uri);
|
|
81
|
+
if (uri.startsWith('https://') || uri.startsWith('http://')) return parseWebRef(uri);
|
|
82
|
+
throw new Error(`Unsupported source ref scheme: ${uri}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function isSupportedSourceRef(uri: string): boolean {
|
|
86
|
+
try {
|
|
87
|
+
parseSourceRef(uri);
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/store.ts
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
* Copyright 2026 Hasna Inc.
|
|
4
4
|
* Licensed under the Apache License, Version 2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { dirname } from 'node:path';
|
|
8
|
-
import { homedir } from 'node:os';
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync, renameSync, unlinkSync } from 'node:fs';
|
|
9
7
|
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { ensureParentDir, globalKnowledgeHome, legacyGlobalStorePath, workspaceForHome } from './workspace';
|
|
10
9
|
|
|
11
10
|
export interface KnowledgeItem {
|
|
12
11
|
id: string;
|
|
12
|
+
short_id?: string | null;
|
|
13
13
|
title: string;
|
|
14
14
|
content: string;
|
|
15
15
|
url: string | null;
|
|
16
16
|
tags: string[];
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
archived?: boolean;
|
|
17
19
|
created_at: string;
|
|
18
20
|
updated_at: string;
|
|
19
21
|
}
|
|
@@ -23,13 +25,17 @@ export interface Store {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export function defaultStorePath(): string {
|
|
26
|
-
return
|
|
28
|
+
return workspaceForHome(globalKnowledgeHome()).jsonStorePath;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export function ensureStore(path: string): void {
|
|
30
32
|
if (!existsSync(path)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
ensureParentDir(path);
|
|
34
|
+
if (path === defaultStorePath() && existsSync(legacyGlobalStorePath())) {
|
|
35
|
+
writeFileSync(path, readFileSync(legacyGlobalStorePath(), 'utf8'));
|
|
36
|
+
} else {
|
|
37
|
+
writeFileSync(path, JSON.stringify({ items: [] }, null, 2));
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -101,3 +107,7 @@ export function withLock<T>(path: string, fn: () => T): T {
|
|
|
101
107
|
export function makeId(): string {
|
|
102
108
|
return `k_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
103
109
|
}
|
|
110
|
+
|
|
111
|
+
export function makeShortId(id: string): string {
|
|
112
|
+
return id.replace(/^k_/, '').slice(0, 12);
|
|
113
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ArtifactStore } from './artifact-store';
|
|
2
|
+
|
|
3
|
+
export interface WikiLayoutInitResult {
|
|
4
|
+
schema_key: string;
|
|
5
|
+
root_index_key: string;
|
|
6
|
+
wiki_readme_key: string;
|
|
7
|
+
log_key: string;
|
|
8
|
+
written: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function todayParts(now: Date): { year: string; month: string; day: string } {
|
|
12
|
+
const year = String(now.getUTCFullYear());
|
|
13
|
+
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
14
|
+
const day = String(now.getUTCDate()).padStart(2, '0');
|
|
15
|
+
return { year, month, day };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function agentSchemaTemplate(): string {
|
|
19
|
+
return `# Knowledge Agent Schema v1
|
|
20
|
+
|
|
21
|
+
## Source Rules
|
|
22
|
+
|
|
23
|
+
- Treat open-files source references as the preferred source of truth.
|
|
24
|
+
- Do not copy raw source files into open-knowledge.
|
|
25
|
+
- Cite every durable fact with a source URI, revision/hash when available, and optional span.
|
|
26
|
+
- Mark uncertainty explicitly when sources disagree or are incomplete.
|
|
27
|
+
|
|
28
|
+
## Wiki Rules
|
|
29
|
+
|
|
30
|
+
- Write generated knowledge as Markdown pages under wiki/.
|
|
31
|
+
- Keep root indexes small; use topic, team, project, and machine-readable shards for scale.
|
|
32
|
+
- Preserve backlinks between related pages and decisions.
|
|
33
|
+
- Prefer updating existing pages over creating near-duplicates.
|
|
34
|
+
|
|
35
|
+
## Query Rules
|
|
36
|
+
|
|
37
|
+
- Search wiki pages first, then source chunks, then deeper read-only source refs.
|
|
38
|
+
- Use web search only when requested or when current external context is required.
|
|
39
|
+
- File useful answers back into the wiki only after approval or approved auto-write mode.
|
|
40
|
+
|
|
41
|
+
## Lint Rules
|
|
42
|
+
|
|
43
|
+
- Flag stale pages, missing citations, contradictions, orphan pages, duplicate pages, and unresolved source refs.
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function rootIndexTemplate(): string {
|
|
48
|
+
return `# Knowledge Index
|
|
49
|
+
|
|
50
|
+
This is a compact orientation index for agents. It is not the full search index.
|
|
51
|
+
|
|
52
|
+
## Shards
|
|
53
|
+
|
|
54
|
+
- wiki/
|
|
55
|
+
- indexes/
|
|
56
|
+
- schemas/
|
|
57
|
+
- logs/
|
|
58
|
+
|
|
59
|
+
## Source Ownership
|
|
60
|
+
|
|
61
|
+
Raw source files are resolved through open-files. This app stores source refs,
|
|
62
|
+
citations, chunks, generated wiki artifacts, indexes, and run records.
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function wikiReadmeTemplate(): string {
|
|
67
|
+
return `# Wiki
|
|
68
|
+
|
|
69
|
+
Generated durable knowledge pages live here.
|
|
70
|
+
|
|
71
|
+
Pages should be concise, cited, and organized for both humans and agents.
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function initializeWikiLayout(store: ArtifactStore, now = new Date()): Promise<WikiLayoutInitResult> {
|
|
76
|
+
const { year, month, day } = todayParts(now);
|
|
77
|
+
const schemaKey = 'schemas/v1.md';
|
|
78
|
+
const rootIndexKey = 'indexes/root.md';
|
|
79
|
+
const wikiReadmeKey = 'wiki/README.md';
|
|
80
|
+
const logKey = `logs/${year}/${month}/${day}.jsonl`;
|
|
81
|
+
const event = {
|
|
82
|
+
ts: now.toISOString(),
|
|
83
|
+
event: 'wiki_layout_initialized',
|
|
84
|
+
schema_key: schemaKey,
|
|
85
|
+
root_index_key: rootIndexKey,
|
|
86
|
+
wiki_readme_key: wikiReadmeKey,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const writes = [
|
|
90
|
+
store.put({ key: schemaKey, body: agentSchemaTemplate(), content_type: 'text/markdown' }),
|
|
91
|
+
store.put({ key: rootIndexKey, body: rootIndexTemplate(), content_type: 'text/markdown' }),
|
|
92
|
+
store.put({ key: wikiReadmeKey, body: wikiReadmeTemplate(), content_type: 'text/markdown' }),
|
|
93
|
+
store.put({ key: logKey, body: `${JSON.stringify(event)}\n`, content_type: 'application/x-ndjson' }),
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
await Promise.all(writes);
|
|
97
|
+
return {
|
|
98
|
+
schema_key: schemaKey,
|
|
99
|
+
root_index_key: rootIndexKey,
|
|
100
|
+
wiki_readme_key: wikiReadmeKey,
|
|
101
|
+
log_key: logKey,
|
|
102
|
+
written: [schemaKey, rootIndexKey, wikiReadmeKey, logKey],
|
|
103
|
+
};
|
|
104
|
+
}
|
package/src/workspace.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export const HASNA_KNOWLEDGE_APP_PATH = join('.hasna', 'apps', 'knowledge');
|
|
6
|
+
|
|
7
|
+
export interface KnowledgeWorkspace {
|
|
8
|
+
home: string;
|
|
9
|
+
configPath: string;
|
|
10
|
+
jsonStorePath: string;
|
|
11
|
+
knowledgeDbPath: string;
|
|
12
|
+
artifactsDir: string;
|
|
13
|
+
cacheDir: string;
|
|
14
|
+
exportsDir: string;
|
|
15
|
+
indexesDir: string;
|
|
16
|
+
logsDir: string;
|
|
17
|
+
runsDir: string;
|
|
18
|
+
schemasDir: string;
|
|
19
|
+
wikiDir: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface KnowledgeConfig {
|
|
23
|
+
version: 1;
|
|
24
|
+
mode: 'local' | 'hosted';
|
|
25
|
+
storage: {
|
|
26
|
+
type: 'local' | 's3';
|
|
27
|
+
artifacts_root: string;
|
|
28
|
+
s3?: {
|
|
29
|
+
bucket: string;
|
|
30
|
+
prefix?: string;
|
|
31
|
+
region?: string;
|
|
32
|
+
profile?: string;
|
|
33
|
+
max_attempts?: number;
|
|
34
|
+
server_side_encryption?: 'AES256' | 'aws:kms';
|
|
35
|
+
kms_key_id?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
sources: {
|
|
39
|
+
preferred_ref: 'open-files';
|
|
40
|
+
allowed_schemes: string[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function legacyGlobalStorePath(): string {
|
|
45
|
+
return join(homedir(), '.open-knowledge', 'db.json');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function globalKnowledgeHome(): string {
|
|
49
|
+
return join(homedir(), '.hasna', 'apps', 'knowledge');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function projectKnowledgeHome(cwd = process.cwd()): string {
|
|
53
|
+
return resolve(cwd, HASNA_KNOWLEDGE_APP_PATH);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function workspaceForHome(home: string): KnowledgeWorkspace {
|
|
57
|
+
return {
|
|
58
|
+
home,
|
|
59
|
+
configPath: join(home, 'config.json'),
|
|
60
|
+
jsonStorePath: join(home, 'db.json'),
|
|
61
|
+
knowledgeDbPath: join(home, 'knowledge.db'),
|
|
62
|
+
artifactsDir: join(home, 'artifacts'),
|
|
63
|
+
cacheDir: join(home, 'cache'),
|
|
64
|
+
exportsDir: join(home, 'exports'),
|
|
65
|
+
indexesDir: join(home, 'indexes'),
|
|
66
|
+
logsDir: join(home, 'logs'),
|
|
67
|
+
runsDir: join(home, 'runs'),
|
|
68
|
+
schemasDir: join(home, 'schemas'),
|
|
69
|
+
wikiDir: join(home, 'wiki'),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function defaultKnowledgeConfig(): KnowledgeConfig {
|
|
74
|
+
return {
|
|
75
|
+
version: 1,
|
|
76
|
+
mode: 'local',
|
|
77
|
+
storage: {
|
|
78
|
+
type: 'local',
|
|
79
|
+
artifacts_root: 'artifacts',
|
|
80
|
+
},
|
|
81
|
+
sources: {
|
|
82
|
+
preferred_ref: 'open-files',
|
|
83
|
+
allowed_schemes: ['open-files', 's3', 'file', 'https', 'http'],
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function ensureKnowledgeWorkspace(home: string): KnowledgeWorkspace {
|
|
89
|
+
const workspace = workspaceForHome(home);
|
|
90
|
+
mkdirSync(workspace.home, { recursive: true });
|
|
91
|
+
for (const dir of [
|
|
92
|
+
workspace.artifactsDir,
|
|
93
|
+
workspace.cacheDir,
|
|
94
|
+
workspace.exportsDir,
|
|
95
|
+
workspace.indexesDir,
|
|
96
|
+
workspace.logsDir,
|
|
97
|
+
workspace.runsDir,
|
|
98
|
+
workspace.schemasDir,
|
|
99
|
+
workspace.wikiDir,
|
|
100
|
+
]) {
|
|
101
|
+
mkdirSync(dir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
if (!existsSync(workspace.configPath)) {
|
|
104
|
+
writeFileSync(workspace.configPath, `${JSON.stringify(defaultKnowledgeConfig(), null, 2)}\n`);
|
|
105
|
+
}
|
|
106
|
+
return workspace;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resolveScopedWorkspace(scope: string | undefined, cwd = process.cwd()): KnowledgeWorkspace {
|
|
110
|
+
if (scope === 'project' || scope === 'local') {
|
|
111
|
+
return workspaceForHome(projectKnowledgeHome(cwd));
|
|
112
|
+
}
|
|
113
|
+
return workspaceForHome(globalKnowledgeHome());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function ensureParentDir(path: string): void {
|
|
117
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function readKnowledgeConfig(path: string): KnowledgeConfig {
|
|
121
|
+
const raw = readFileSync(path, 'utf8');
|
|
122
|
+
return JSON.parse(raw) as KnowledgeConfig;
|
|
123
|
+
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
name: Bug Report
|
|
2
|
-
description: Report a bug in open-knowledge
|
|
3
|
-
labels: [bug]
|
|
4
|
-
body:
|
|
5
|
-
- type: markdown
|
|
6
|
-
attributes:
|
|
7
|
-
value: |
|
|
8
|
-
Thanks for reporting a bug!
|
|
9
|
-
- type: textarea
|
|
10
|
-
id: description
|
|
11
|
-
attributes:
|
|
12
|
-
label: Bug Description
|
|
13
|
-
description: A clear description of the bug
|
|
14
|
-
validations:
|
|
15
|
-
required: true
|
|
16
|
-
- type: textarea
|
|
17
|
-
id: steps
|
|
18
|
-
attributes:
|
|
19
|
-
label: Steps to Reproduce
|
|
20
|
-
description: |
|
|
21
|
-
1.
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
validations:
|
|
25
|
-
required: true
|
|
26
|
-
- type: textarea
|
|
27
|
-
id: expected
|
|
28
|
-
attributes:
|
|
29
|
-
label: Expected Behavior
|
|
30
|
-
validations:
|
|
31
|
-
required: true
|
|
32
|
-
- type: textarea
|
|
33
|
-
id: actual
|
|
34
|
-
attributes:
|
|
35
|
-
label: Actual Behavior
|
|
36
|
-
validations:
|
|
37
|
-
required: true
|
|
38
|
-
- type: input
|
|
39
|
-
id: version
|
|
40
|
-
attributes:
|
|
41
|
-
label: Version
|
|
42
|
-
description: Output of `open-knowledge --version`
|
|
43
|
-
- type: dropdown
|
|
44
|
-
id: os
|
|
45
|
-
attributes:
|
|
46
|
-
label: Operating System
|
|
47
|
-
options:
|
|
48
|
-
- macOS
|
|
49
|
-
- Linux
|
|
50
|
-
- Windows
|
|
51
|
-
- Other
|
|
52
|
-
- type: dropdown
|
|
53
|
-
id: runtime
|
|
54
|
-
attributes:
|
|
55
|
-
label: Runtime
|
|
56
|
-
options:
|
|
57
|
-
- Bun
|
|
58
|
-
- Node.js
|
|
59
|
-
- Other
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Feature Request
|
|
2
|
-
description: Suggest a new feature or improvement
|
|
3
|
-
labels: [enhancement]
|
|
4
|
-
body:
|
|
5
|
-
- type: markdown
|
|
6
|
-
attributes:
|
|
7
|
-
value: |
|
|
8
|
-
Ideas are welcome! The best features solve real problems for AI agents and CLI users.
|
|
9
|
-
- type: textarea
|
|
10
|
-
id: problem
|
|
11
|
-
attributes:
|
|
12
|
-
label: Problem or Motivation
|
|
13
|
-
description: What problem does this solve?
|
|
14
|
-
validations:
|
|
15
|
-
required: true
|
|
16
|
-
- type: textarea
|
|
17
|
-
id: solution
|
|
18
|
-
attributes:
|
|
19
|
-
label: Proposed Solution
|
|
20
|
-
description: How would you like it to work?
|
|
21
|
-
validations:
|
|
22
|
-
required: true
|
|
23
|
-
- type: textarea
|
|
24
|
-
id: alternatives
|
|
25
|
-
attributes:
|
|
26
|
-
label: Alternatives Considered
|
|
27
|
-
description: Any other approaches you considered?
|
|
28
|
-
- type: checkboxes
|
|
29
|
-
id: willingness
|
|
30
|
-
attributes:
|
|
31
|
-
label: Willingness to Implement
|
|
32
|
-
options:
|
|
33
|
-
- label: I am willing to implement this feature
|
|
34
|
-
- label: I can help test a PR for this feature
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
## Summary
|
|
2
|
-
|
|
3
|
-
<!-- 1-3 sentence description of the change -->
|
|
4
|
-
|
|
5
|
-
## Motivation
|
|
6
|
-
|
|
7
|
-
<!-- Why is this change needed? What problem does it solve? -->
|
|
8
|
-
|
|
9
|
-
## Changes
|
|
10
|
-
|
|
11
|
-
<!-- Bulleted list of what was changed -->
|
|
12
|
-
|
|
13
|
-
## Testing
|
|
14
|
-
|
|
15
|
-
<!-- How was this tested? -->
|
|
16
|
-
|
|
17
|
-
## Checklist
|
|
18
|
-
|
|
19
|
-
- [ ] Tests added / updated
|
|
20
|
-
- [ ] Documentation updated (if needed)
|
|
21
|
-
- [ ] `bun test` passes
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
strategy:
|
|
12
|
-
matrix:
|
|
13
|
-
os: [ubuntu-latest, macos-latest]
|
|
14
|
-
runtime: [bun, node]
|
|
15
|
-
runs-on: ${{ matrix.os }}
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v4
|
|
18
|
-
|
|
19
|
-
- name: Setup Bun
|
|
20
|
-
if: matrix.runtime == 'bun'
|
|
21
|
-
uses: oven-sh/setup-bun@v2
|
|
22
|
-
with:
|
|
23
|
-
bun-version: latest
|
|
24
|
-
|
|
25
|
-
- name: Setup Node
|
|
26
|
-
if: matrix.runtime == 'node'
|
|
27
|
-
uses: actions/setup-node@v4
|
|
28
|
-
with:
|
|
29
|
-
node-version: latest
|
|
30
|
-
|
|
31
|
-
- name: Install dependencies
|
|
32
|
-
run: bun install
|
|
33
|
-
|
|
34
|
-
- name: Run tests
|
|
35
|
-
run: bun test
|
|
36
|
-
|
|
37
|
-
test-matrix:
|
|
38
|
-
strategy:
|
|
39
|
-
matrix:
|
|
40
|
-
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
41
|
-
runtime: [bun]
|
|
42
|
-
runs-on: ${{ matrix.os }}
|
|
43
|
-
steps:
|
|
44
|
-
- uses: actions/checkout@v4
|
|
45
|
-
- uses: oven-sh/setup-bun@v2
|
|
46
|
-
with:
|
|
47
|
-
bun-version: latest
|
|
48
|
-
- run: bun install
|
|
49
|
-
- run: bun test
|
package/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
6
|
-
|
|
7
|
-
## Our Standards
|
|
8
|
-
|
|
9
|
-
Examples of behavior that contributes to a positive environment:
|
|
10
|
-
|
|
11
|
-
* Using welcoming and inclusive language
|
|
12
|
-
* Being respectful of differing viewpoints and experiences
|
|
13
|
-
* Gracefully accepting constructive criticism
|
|
14
|
-
* Focusing on what is best for the community
|
|
15
|
-
* Showing empathy towards other community members
|
|
16
|
-
|
|
17
|
-
Examples of unacceptable behavior:
|
|
18
|
-
|
|
19
|
-
* The use of sexualized language or imagery and unwelcome sexual attention
|
|
20
|
-
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
21
|
-
* Public or private harassment
|
|
22
|
-
* Publishing others' private information without explicit permission
|
|
23
|
-
* Other conduct which could reasonably be considered inappropriate
|
|
24
|
-
|
|
25
|
-
## Enforcement
|
|
26
|
-
|
|
27
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate.
|
|
28
|
-
|
|
29
|
-
## Attribution
|
|
30
|
-
|
|
31
|
-
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Contributing to open-knowledge
|
|
2
|
-
|
|
3
|
-
Thank you for your interest in contributing!
|
|
4
|
-
|
|
5
|
-
## Development Setup
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Clone the repo
|
|
9
|
-
git clone https://github.com/hasna/knowledge.git
|
|
10
|
-
cd knowledge
|
|
11
|
-
|
|
12
|
-
# Install dependencies (Bun)
|
|
13
|
-
bun install
|
|
14
|
-
|
|
15
|
-
# Run tests
|
|
16
|
-
bun test
|
|
17
|
-
|
|
18
|
-
# Run a specific test file
|
|
19
|
-
bun test tests/cli.test.ts
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Project Structure
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
knowledge/
|
|
26
|
-
├── src/
|
|
27
|
-
│ ├── cli.js # CLI entry point, argument parsing, commands
|
|
28
|
-
│ └── store.js # Persistent store, file locking, ID generation
|
|
29
|
-
├── tests/
|
|
30
|
-
│ └── cli.test.ts # Integration tests using Bun.test
|
|
31
|
-
├── package.json
|
|
32
|
-
└── LICENSE
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Design Principles
|
|
36
|
-
|
|
37
|
-
**Agent-friendly first**: every output should be parseable by an LLM. Prefer `--json` for structured data. Keep error messages actionable.
|
|
38
|
-
|
|
39
|
-
**Minimal dependencies**: keep the dependency footprint small. The store is a plain JSON file.
|
|
40
|
-
|
|
41
|
-
**Safe by default**: destructive operations require explicit confirmation flags (`--yes`).
|
|
42
|
-
|
|
43
|
-
**Concurrent-safe**: all store mutations go through `withLock()`. Do not bypass it.
|
|
44
|
-
|
|
45
|
-
## Commit Conventions
|
|
46
|
-
|
|
47
|
-
Use [Conventional Commits](https://www.conventionalcommits.org/):
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
feat(cli): add --tag filter on list command
|
|
51
|
-
fix(store): handle empty store file gracefully
|
|
52
|
-
docs(readme): add installation instructions
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Types: `feat`, `fix`, `docs`, `chore`, `refactor`, `test`
|
|
56
|
-
|
|
57
|
-
## Pull Request Process
|
|
58
|
-
|
|
59
|
-
1. Fork the repo and create a branch from `main`.
|
|
60
|
-
2. Add tests for new functionality.
|
|
61
|
-
3. Ensure all tests pass: `bun test`.
|
|
62
|
-
4. Keep commits atomic and well-described.
|
|
63
|
-
5. Open a PR with a clear description of the change and motivation.
|
|
64
|
-
|
|
65
|
-
## Code Style
|
|
66
|
-
|
|
67
|
-
- 2-space indentation
|
|
68
|
-
- `for` loops over array methods where performance matters
|
|
69
|
-
- Descriptive variable names
|
|
70
|
-
- No unnecessary dependencies
|
|
71
|
-
|
|
72
|
-
## Reporting Issues
|
|
73
|
-
|
|
74
|
-
- Use the [bug report template](.github/ISSUE_TEMPLATE/bug_report.yml)
|
|
75
|
-
- Search existing issues first
|
|
76
|
-
- Include: Node/Bun version, OS, steps to reproduce, expected vs actual
|
|
77
|
-
|
|
78
|
-
## Suggesting Features
|
|
79
|
-
|
|
80
|
-
Open a [feature request issue](.github/ISSUE_TEMPLATE/feature_request.yml) describing:
|
|
81
|
-
- The problem you're solving
|
|
82
|
-
- How you envision the solution
|
|
83
|
-
- Whether you're willing to implement it
|
package/FUNDING.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
github: hasna
|