create-lore 0.9.0 → 0.10.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/.github/CODEOWNERS +1 -0
- package/.github/workflows/release.yml +8 -7
- package/.github/workflows/test.yml +9 -6
- package/.prettierignore +1 -0
- package/.prettierrc +7 -0
- package/README.md +6 -5
- package/SECURITY.md +2 -2
- package/bin/create-lore.js +50 -11
- package/eslint.config.js +31 -0
- package/package.json +13 -2
- package/test/create-lore.test.js +51 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @drewswiredin
|
|
@@ -6,6 +6,7 @@ on:
|
|
|
6
6
|
|
|
7
7
|
permissions:
|
|
8
8
|
contents: write
|
|
9
|
+
id-token: write
|
|
9
10
|
|
|
10
11
|
jobs:
|
|
11
12
|
test:
|
|
@@ -16,13 +17,13 @@ jobs:
|
|
|
16
17
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
17
18
|
node-version: [18, 20]
|
|
18
19
|
steps:
|
|
19
|
-
- uses: actions/checkout@v4 # checkout create-lore (this repo)
|
|
20
|
-
- uses: actions/checkout@v4 # checkout lore template at matching tag
|
|
20
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 # checkout create-lore (this repo)
|
|
21
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 # checkout lore template at matching tag
|
|
21
22
|
with:
|
|
22
23
|
repository: lorehq/lore
|
|
23
24
|
ref: ${{ github.ref_name }}
|
|
24
25
|
path: lore
|
|
25
|
-
- uses: actions/setup-node@v4
|
|
26
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
26
27
|
with:
|
|
27
28
|
node-version: ${{ matrix.node-version }}
|
|
28
29
|
- run: npm test
|
|
@@ -32,7 +33,7 @@ jobs:
|
|
|
32
33
|
verify-tag:
|
|
33
34
|
runs-on: ubuntu-latest
|
|
34
35
|
steps:
|
|
35
|
-
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
36
37
|
- name: Verify tag matches package.json version
|
|
37
38
|
run: |
|
|
38
39
|
tag="${GITHUB_REF#refs/tags/v}"
|
|
@@ -55,13 +56,13 @@ jobs:
|
|
|
55
56
|
needs: [test, verify-tag]
|
|
56
57
|
runs-on: ubuntu-latest
|
|
57
58
|
steps:
|
|
58
|
-
- uses: actions/checkout@v4
|
|
59
|
-
- uses: actions/setup-node@v4
|
|
59
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
60
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
60
61
|
with:
|
|
61
62
|
node-version: 20
|
|
62
63
|
registry-url: https://registry.npmjs.org
|
|
63
64
|
- name: Publish to npm
|
|
64
|
-
run: npm publish
|
|
65
|
+
run: npm publish --provenance
|
|
65
66
|
env:
|
|
66
67
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
67
68
|
- name: Create GitHub Release
|
|
@@ -15,14 +15,17 @@ jobs:
|
|
|
15
15
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
16
|
node-version: [18, 20]
|
|
17
17
|
steps:
|
|
18
|
-
- uses: actions/checkout@v4 # checkout create-lore (this repo)
|
|
19
|
-
- uses: actions/checkout@v4 # checkout lore template (used as LORE_TEMPLATE)
|
|
18
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 # checkout create-lore (this repo)
|
|
19
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 # checkout lore template (used as LORE_TEMPLATE)
|
|
20
20
|
with:
|
|
21
21
|
repository: lorehq/lore
|
|
22
22
|
path: lore
|
|
23
|
-
- uses: actions/setup-node@v4
|
|
23
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
24
24
|
with:
|
|
25
25
|
node-version: ${{ matrix.node-version }}
|
|
26
|
+
- run: npm ci
|
|
27
|
+
- name: Lint & format
|
|
28
|
+
run: npx eslint . && npx prettier --check .
|
|
26
29
|
- run: npm test
|
|
27
30
|
env:
|
|
28
31
|
LORE_TEMPLATE: ${{ github.workspace }}/lore
|
|
@@ -34,12 +37,12 @@ jobs:
|
|
|
34
37
|
matrix:
|
|
35
38
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
36
39
|
steps:
|
|
37
|
-
- uses: actions/checkout@v4
|
|
38
|
-
- uses: actions/checkout@v4
|
|
40
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
41
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
39
42
|
with:
|
|
40
43
|
repository: lorehq/lore
|
|
41
44
|
path: lore
|
|
42
|
-
- uses: actions/setup-node@v4
|
|
45
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
43
46
|
with:
|
|
44
47
|
node-version: 20
|
|
45
48
|
- name: Scaffold a project and validate it
|
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lore/
|
package/.prettierrc
ADDED
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Lore wraps your coding agent in a git-versioned knowledge base. Hooks fire autom
|
|
|
14
14
|
- **Knowledge docs** — Environment details, runbooks, architecture decisions. Accumulated across sessions.
|
|
15
15
|
- **Work tracking** — Roadmaps, plans, and brainstorms that persist and appear in every session banner.
|
|
16
16
|
- **Hooks** — Session init, capture reminders, memory protection. All automatic.
|
|
17
|
+
- **Docs UI & Semantic Search** — Run `/lore-docker` to start a local Docker sidecar. Gives agents semantic search over the full knowledge base and opens a live MkDocs site for browsing it visually. Falls back to Grep/Glob without Docker.
|
|
17
18
|
|
|
18
19
|
## Quick Start
|
|
19
20
|
|
|
@@ -27,11 +28,11 @@ Then open the project in your agent. Hooks fire automatically.
|
|
|
27
28
|
|
|
28
29
|
## Supported Platforms
|
|
29
30
|
|
|
30
|
-
| Platform
|
|
31
|
-
|
|
32
|
-
| Claude Code |
|
|
33
|
-
| Cursor
|
|
34
|
-
| OpenCode
|
|
31
|
+
| Platform | Integration |
|
|
32
|
+
| ----------- | --------------------------------------------------------------------- |
|
|
33
|
+
| Claude Code | `.lore/hooks/` + `CLAUDE.md` |
|
|
34
|
+
| Cursor | `.lore/hooks/` + `.cursor/hooks/` + `.cursor/mcp/` + `.cursor/rules/` |
|
|
35
|
+
| OpenCode | `.opencode/plugins/` + `opencode.json` |
|
|
35
36
|
|
|
36
37
|
All platforms share the same knowledge base. No configuration needed.
|
|
37
38
|
|
package/SECURITY.md
CHANGED
package/bin/create-lore.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// How it works:
|
|
8
8
|
// 1. Clones the Lore template from GitHub (or copies from LORE_TEMPLATE env var)
|
|
9
9
|
// 2. Strips the template's .git history
|
|
10
|
-
// 3. Writes
|
|
10
|
+
// 3. Writes .lore/config.json with the project name and creation date
|
|
11
11
|
// 4. Runs git init for a clean start
|
|
12
12
|
//
|
|
13
13
|
// The LORE_TEMPLATE env var is used by tests to point at a local template
|
|
@@ -99,27 +99,66 @@ try {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
fs.rmSync(path.join(tmpDir, '.git'), { recursive: true, force: true });
|
|
102
|
+
|
|
103
|
+
// Only keep files needed in instances — remove everything else
|
|
104
|
+
const keep = new Set([
|
|
105
|
+
'.lore',
|
|
106
|
+
'.claude',
|
|
107
|
+
'.cursor',
|
|
108
|
+
'.opencode',
|
|
109
|
+
'docs',
|
|
110
|
+
'CLAUDE.md',
|
|
111
|
+
'opencode.json',
|
|
112
|
+
'.gitattributes',
|
|
113
|
+
'.gitignore',
|
|
114
|
+
]);
|
|
115
|
+
for (const entry of fs.readdirSync(tmpDir)) {
|
|
116
|
+
if (!keep.has(entry)) {
|
|
117
|
+
fs.rmSync(path.join(tmpDir, entry), { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
fs.cpSync(tmpDir, targetDir, { recursive: true });
|
|
103
122
|
} finally {
|
|
104
123
|
// Always clean up the temp dir
|
|
105
124
|
if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true });
|
|
106
125
|
}
|
|
107
126
|
|
|
108
|
-
// -- Write .lore
|
|
127
|
+
// -- Write .lore/config.json --
|
|
109
128
|
const projectName = isPath ? path.basename(targetDir) : name;
|
|
110
|
-
// Read version from the
|
|
129
|
+
// Read version from the copied config.json, then rewrite from template
|
|
111
130
|
let templateConfig;
|
|
112
131
|
try {
|
|
113
|
-
templateConfig = JSON.parse(fs.readFileSync(path.join(targetDir, '.lore
|
|
114
|
-
} catch
|
|
132
|
+
templateConfig = JSON.parse(fs.readFileSync(path.join(targetDir, '.lore', 'config.json'), 'utf8'));
|
|
133
|
+
} catch {
|
|
115
134
|
templateConfig = {};
|
|
116
135
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
136
|
+
const templateVersion = templateConfig.version || '0.0.0';
|
|
137
|
+
const createdDate = new Date().toISOString().split('T')[0];
|
|
138
|
+
const configTemplate = fs.readFileSync(path.join(targetDir, '.lore', 'templates', 'config.json'), 'utf8');
|
|
139
|
+
const configContent = configTemplate
|
|
140
|
+
.replace('{{name}}', projectName)
|
|
141
|
+
.replace('{{version}}', templateVersion)
|
|
142
|
+
.replace('{{created}}', createdDate);
|
|
143
|
+
fs.writeFileSync(path.join(targetDir, '.lore', 'config.json'), configContent);
|
|
144
|
+
|
|
145
|
+
// -- Create gitignored files from templates --
|
|
146
|
+
// Templates are already in targetDir (copied from the template clone via .lore/).
|
|
147
|
+
// These are normally recreated by ensureStickyFiles() each session, but the
|
|
148
|
+
// installer should produce a complete instance from the start.
|
|
149
|
+
const tplDir = path.join(targetDir, '.lore', 'templates');
|
|
150
|
+
const localDir = path.join(targetDir, 'docs', 'knowledge', 'local');
|
|
151
|
+
fs.mkdirSync(localDir, { recursive: true });
|
|
152
|
+
|
|
153
|
+
fs.writeFileSync(path.join(localDir, 'index.md'), fs.readFileSync(path.join(tplDir, 'local-index.md'), 'utf8'));
|
|
154
|
+
fs.writeFileSync(
|
|
155
|
+
path.join(localDir, 'operator-profile.md'),
|
|
156
|
+
fs.readFileSync(path.join(tplDir, 'operator-profile.md'), 'utf8'),
|
|
157
|
+
);
|
|
158
|
+
fs.writeFileSync(
|
|
159
|
+
path.join(targetDir, '.lore', 'memory.local.md'),
|
|
160
|
+
fs.readFileSync(path.join(tplDir, 'memory-local.md'), 'utf8'),
|
|
161
|
+
);
|
|
123
162
|
|
|
124
163
|
// -- Initialize git --
|
|
125
164
|
execSync('git init -b main', { cwd: targetDir, stdio: 'pipe' });
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const prettier = require('eslint-config-prettier');
|
|
2
|
+
|
|
3
|
+
module.exports = [
|
|
4
|
+
{ ignores: ['lore/'] },
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.js'],
|
|
7
|
+
languageOptions: {
|
|
8
|
+
ecmaVersion: 2022,
|
|
9
|
+
sourceType: 'commonjs',
|
|
10
|
+
globals: {
|
|
11
|
+
console: 'readonly',
|
|
12
|
+
process: 'readonly',
|
|
13
|
+
require: 'readonly',
|
|
14
|
+
module: 'readonly',
|
|
15
|
+
__dirname: 'readonly',
|
|
16
|
+
__filename: 'readonly',
|
|
17
|
+
exports: 'readonly',
|
|
18
|
+
Buffer: 'readonly',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
rules: {
|
|
22
|
+
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
23
|
+
'no-undef': 'error',
|
|
24
|
+
'no-constant-condition': 'warn',
|
|
25
|
+
eqeqeq: ['error', 'always'],
|
|
26
|
+
'no-var': 'error',
|
|
27
|
+
'prefer-const': 'warn',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
prettier,
|
|
31
|
+
];
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Create a new Lore knowledge-persistent agent repo",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-lore": "bin/create-lore.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "node --test test/create-lore.test.js"
|
|
9
|
+
"test": "node --test test/create-lore.test.js",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"format:check": "prettier --check ."
|
|
10
12
|
},
|
|
11
13
|
"keywords": [
|
|
12
14
|
"lore",
|
|
@@ -15,7 +17,16 @@
|
|
|
15
17
|
"knowledge"
|
|
16
18
|
],
|
|
17
19
|
"license": "Apache-2.0",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/lorehq/create-lore"
|
|
23
|
+
},
|
|
18
24
|
"engines": {
|
|
19
25
|
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"eslint": "^9.0.0",
|
|
29
|
+
"eslint-config-prettier": "^10.1.8",
|
|
30
|
+
"prettier": "^3.8.1"
|
|
20
31
|
}
|
|
21
32
|
}
|
package/test/create-lore.test.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { describe, it, before, after } = require('node:test');
|
|
6
6
|
const assert = require('node:assert/strict');
|
|
7
|
-
const { execSync } = require('child_process');
|
|
7
|
+
const { execSync, execFileSync } = require('child_process');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ const OUTPUT = path.resolve(__dirname, '../test-output');
|
|
|
14
14
|
|
|
15
15
|
// Run the installer with LORE_TEMPLATE pointing at the local template
|
|
16
16
|
function run(args = '') {
|
|
17
|
-
return
|
|
17
|
+
return execFileSync('node', [BIN, ...args.split(' ').filter(Boolean)], {
|
|
18
18
|
env: { ...process.env, LORE_TEMPLATE: TEMPLATE },
|
|
19
19
|
stdio: 'pipe',
|
|
20
20
|
encoding: 'utf8',
|
|
@@ -23,7 +23,7 @@ function run(args = '') {
|
|
|
23
23
|
|
|
24
24
|
// Run with exact argv (no shell interpretation) — for testing shell metacharacters
|
|
25
25
|
function runExact(name) {
|
|
26
|
-
return
|
|
26
|
+
return execFileSync('node', [BIN, name], {
|
|
27
27
|
env: { ...process.env, LORE_TEMPLATE: TEMPLATE },
|
|
28
28
|
stdio: 'pipe',
|
|
29
29
|
encoding: 'utf8',
|
|
@@ -59,8 +59,14 @@ describe('create-lore', () => {
|
|
|
59
59
|
|
|
60
60
|
assert.ok(fs.existsSync(OUTPUT), 'output directory exists');
|
|
61
61
|
|
|
62
|
-
// .lore
|
|
63
|
-
const
|
|
62
|
+
// .lore/config.json has required fields (JSONC — strip comments before parsing)
|
|
63
|
+
const raw = fs.readFileSync(path.join(OUTPUT, '.lore', 'config.json'), 'utf8');
|
|
64
|
+
const config = JSON.parse(
|
|
65
|
+
raw
|
|
66
|
+
.replace(/^\s*\/\/.*$/gm, '')
|
|
67
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
68
|
+
.replace(/,(\s*[}\]])/g, '$1'),
|
|
69
|
+
);
|
|
64
70
|
assert.ok(config.name, 'name present');
|
|
65
71
|
assert.ok(config.created, 'created date present');
|
|
66
72
|
|
|
@@ -70,6 +76,34 @@ describe('create-lore', () => {
|
|
|
70
76
|
assert.ok(entries.includes('HEAD'), '.git looks like a fresh init');
|
|
71
77
|
});
|
|
72
78
|
|
|
79
|
+
it('strips dev-only files from scaffolded instance', () => {
|
|
80
|
+
cleanup();
|
|
81
|
+
run(OUTPUT);
|
|
82
|
+
|
|
83
|
+
const devOnly = [
|
|
84
|
+
'test',
|
|
85
|
+
'.github',
|
|
86
|
+
'node_modules',
|
|
87
|
+
'site',
|
|
88
|
+
'docs/assets',
|
|
89
|
+
'docs/javascripts',
|
|
90
|
+
'docs/stylesheets',
|
|
91
|
+
'CODE_OF_CONDUCT.md',
|
|
92
|
+
'CONTRIBUTING.md',
|
|
93
|
+
'SECURITY.md',
|
|
94
|
+
'LICENSE',
|
|
95
|
+
'README.md',
|
|
96
|
+
'.prettierrc',
|
|
97
|
+
'.prettierignore',
|
|
98
|
+
'eslint.config.js',
|
|
99
|
+
'package-lock.json',
|
|
100
|
+
];
|
|
101
|
+
for (const name of devOnly) {
|
|
102
|
+
assert.ok(!fs.existsSync(path.join(OUTPUT, name)), `${name} should be stripped`);
|
|
103
|
+
}
|
|
104
|
+
cleanup();
|
|
105
|
+
});
|
|
106
|
+
|
|
73
107
|
it('fails if target directory already exists', () => {
|
|
74
108
|
fs.mkdirSync(OUTPUT, { recursive: true });
|
|
75
109
|
assert.throws(() => run(OUTPUT), /already exists/);
|
|
@@ -113,14 +147,16 @@ describe('create-lore', () => {
|
|
|
113
147
|
if (!fs.existsSync(path.join(TEMPLATE, '.opencode/plugins'))) return;
|
|
114
148
|
|
|
115
149
|
run(OUTPUT);
|
|
116
|
-
assert.ok(fs.existsSync(path.join(OUTPUT, '.opencode', 'plugins', 'session-init.js')),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
'knowledge-tracker.js copied'
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
'.
|
|
150
|
+
assert.ok(fs.existsSync(path.join(OUTPUT, '.opencode', 'plugins', 'session-init.js')), 'session-init.js copied');
|
|
151
|
+
assert.ok(
|
|
152
|
+
fs.existsSync(path.join(OUTPUT, '.opencode', 'plugins', 'knowledge-tracker.js')),
|
|
153
|
+
'knowledge-tracker.js copied',
|
|
154
|
+
);
|
|
155
|
+
assert.ok(
|
|
156
|
+
fs.existsSync(path.join(OUTPUT, '.opencode', 'plugins', 'protect-memory.js')),
|
|
157
|
+
'protect-memory.js copied',
|
|
158
|
+
);
|
|
159
|
+
assert.ok(fs.existsSync(path.join(OUTPUT, '.opencode', 'package.json')), '.opencode/package.json copied');
|
|
124
160
|
cleanup();
|
|
125
161
|
});
|
|
126
162
|
|
|
@@ -135,10 +171,10 @@ describe('create-lore', () => {
|
|
|
135
171
|
});
|
|
136
172
|
|
|
137
173
|
it('passes validate-consistency.sh when template provides it', () => {
|
|
138
|
-
if (!fs.existsSync(path.join(TEMPLATE, 'scripts/validate-consistency.sh'))) return;
|
|
174
|
+
if (!fs.existsSync(path.join(TEMPLATE, '.lore/scripts/validate-consistency.sh'))) return;
|
|
139
175
|
|
|
140
176
|
run(OUTPUT);
|
|
141
|
-
const result = execSync('bash scripts/validate-consistency.sh', {
|
|
177
|
+
const result = execSync('bash .lore/scripts/validate-consistency.sh', {
|
|
142
178
|
cwd: OUTPUT,
|
|
143
179
|
encoding: 'utf8',
|
|
144
180
|
stdio: 'pipe',
|