@time-machine-lab/tmlbrain 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/README.md +152 -0
- package/bin/tmlbrain.js +1653 -0
- package/docs/.gitkeep +0 -0
- package/docs/api/.gitkeep +0 -0
- package/docs/architecture.md +84 -0
- package/docs/backup.md +76 -0
- package/docs/decisions/.gitkeep +1 -0
- package/docs/decisions/0001-core-runtime.md +18 -0
- package/docs/design/.gitkeep +0 -0
- package/docs/fixtures/knowledge-sample/alpha-reference.md +14 -0
- package/docs/fixtures/knowledge-sample/project-alpha.md +18 -0
- package/docs/fixtures/patches/update-alpha-reference.patch +9 -0
- package/docs/indexing.md +32 -0
- package/docs/install.md +166 -0
- package/docs/preview/.gitkeep +0 -0
- package/docs/roadmap.md +27 -0
- package/docs/runtime.md +95 -0
- package/docs/server-api.md +120 -0
- package/docs/spec/.gitkeep +0 -0
- package/docs/spikes/cocoindex-local-indexing.md +31 -0
- package/docs/spikes/lightrag-retrieval.md +27 -0
- package/docs/sql/.gitkeep +0 -0
- package/docs/sync.md +110 -0
- package/knowledge/README.md +71 -0
- package/knowledge/_templates/area.md +18 -0
- package/knowledge/_templates/decision.md +18 -0
- package/knowledge/_templates/meeting.md +18 -0
- package/knowledge/_templates/project.md +18 -0
- package/knowledge/_templates/reference.md +18 -0
- package/knowledge/_templates/resource.md +18 -0
- package/package.json +41 -0
- package/scripts/README.md +12 -0
- package/scripts/backup/server-to-github.ps1 +20 -0
- package/scripts/backup/server-to-github.sh +13 -0
- package/scripts/docker/server-entrypoint.sh +84 -0
- package/scripts/install/install.ps1 +44 -0
- package/scripts/install/install.sh +41 -0
- package/scripts/release/parse-auto-release.js +166 -0
- package/scripts/sync/.gitkeep +1 -0
- package/scripts/sync/post-receive.sample +8 -0
- package/skills/tmlbrain/SKILL.md +192 -0
- package/skills/tmlbrain/agents/openai.yaml +7 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const args = {
|
|
7
|
+
messageFile: null,
|
|
8
|
+
githubOutput: null,
|
|
9
|
+
githubEnv: null,
|
|
10
|
+
json: false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
if (arg === "--message-file") {
|
|
16
|
+
args.messageFile = argv[++i];
|
|
17
|
+
} else if (arg === "--github-output") {
|
|
18
|
+
args.githubOutput = argv[++i];
|
|
19
|
+
} else if (arg === "--github-env") {
|
|
20
|
+
args.githubEnv = argv[++i];
|
|
21
|
+
} else if (arg === "--json") {
|
|
22
|
+
args.json = true;
|
|
23
|
+
} else {
|
|
24
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return args;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readStdin() {
|
|
32
|
+
return fs.readFileSync(0, "utf8");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findFlagValue(message, flag) {
|
|
36
|
+
const prefix = `-${flag}:`;
|
|
37
|
+
const start = message.indexOf(prefix);
|
|
38
|
+
if (start === -1) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let cursor = start + prefix.length;
|
|
43
|
+
while (message[cursor] === " " || message[cursor] === "\t") {
|
|
44
|
+
cursor += 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const first = message[cursor];
|
|
48
|
+
if (first === "<") {
|
|
49
|
+
const end = message.indexOf(">", cursor + 1);
|
|
50
|
+
if (end === -1) {
|
|
51
|
+
throw new Error(`Missing closing ">" for -${flag}: value`);
|
|
52
|
+
}
|
|
53
|
+
return message.slice(cursor + 1, end).trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (first === '"' || first === "'") {
|
|
57
|
+
const end = message.indexOf(first, cursor + 1);
|
|
58
|
+
if (end === -1) {
|
|
59
|
+
throw new Error(`Missing closing quote for -${flag}: value`);
|
|
60
|
+
}
|
|
61
|
+
return message.slice(cursor + 1, end).trim();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const rest = message.slice(cursor);
|
|
65
|
+
const match = rest.match(/^[^\s]+/);
|
|
66
|
+
return match ? match[0].trim() : "";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseReleaseMessage(message) {
|
|
70
|
+
const auto = message.includes("<Auto>");
|
|
71
|
+
const result = {
|
|
72
|
+
auto,
|
|
73
|
+
version: "",
|
|
74
|
+
runtimePort: "7389",
|
|
75
|
+
dockerExtraArgs: "",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (!auto) {
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const version = findFlagValue(message, "v");
|
|
83
|
+
if (!version) {
|
|
84
|
+
throw new Error('Auto release commit messages must include "-v:<version>"');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const semverPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
88
|
+
if (!semverPattern.test(version)) {
|
|
89
|
+
throw new Error(`Invalid -v version "${version}". Expected a Docker-compatible semver value such as 0.1.0`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const runtimePort = findFlagValue(message, "rp") || result.runtimePort;
|
|
93
|
+
const parsedPort = Number.parseInt(runtimePort, 10);
|
|
94
|
+
if (!Number.isInteger(parsedPort) || parsedPort < 1 || parsedPort > 65535 || String(parsedPort) !== runtimePort) {
|
|
95
|
+
throw new Error(`Invalid -rp port "${runtimePort}". Expected an integer from 1 to 65535`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const dockerExtraArgs = findFlagValue(message, "de") || "";
|
|
99
|
+
if (/[\r\n]/.test(dockerExtraArgs)) {
|
|
100
|
+
throw new Error("-de value must be a single-line docker runtime argument string");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
result.version = version;
|
|
104
|
+
result.runtimePort = runtimePort;
|
|
105
|
+
result.dockerExtraArgs = dockerExtraArgs;
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function appendOutput(file, pairs) {
|
|
110
|
+
if (!file) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const lines = [];
|
|
115
|
+
for (const [key, value] of Object.entries(pairs)) {
|
|
116
|
+
lines.push(`${key}=${value}`);
|
|
117
|
+
}
|
|
118
|
+
fs.appendFileSync(file, `${lines.join("\n")}\n`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function appendEnv(file, result) {
|
|
122
|
+
if (!file) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fs.appendFileSync(
|
|
127
|
+
file,
|
|
128
|
+
[
|
|
129
|
+
`TMLBRAIN_RELEASE_VERSION=${result.version}`,
|
|
130
|
+
`TMLBRAIN_RELEASE_RUNTIME_PORT=${result.runtimePort}`,
|
|
131
|
+
`TMLBRAIN_RELEASE_DOCKER_EXTRA_ARGS=${result.dockerExtraArgs}`,
|
|
132
|
+
"",
|
|
133
|
+
].join("\n"),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function main() {
|
|
138
|
+
const args = parseArgs(process.argv.slice(2));
|
|
139
|
+
const message = args.messageFile ? fs.readFileSync(args.messageFile, "utf8") : readStdin();
|
|
140
|
+
const result = parseReleaseMessage(message);
|
|
141
|
+
|
|
142
|
+
appendOutput(args.githubOutput, {
|
|
143
|
+
auto: result.auto ? "true" : "false",
|
|
144
|
+
version: result.version,
|
|
145
|
+
runtime_port: result.runtimePort,
|
|
146
|
+
docker_extra_args: result.dockerExtraArgs,
|
|
147
|
+
});
|
|
148
|
+
appendEnv(args.githubEnv, result);
|
|
149
|
+
|
|
150
|
+
if (args.json) {
|
|
151
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (require.main === module) {
|
|
156
|
+
try {
|
|
157
|
+
main();
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(error.message);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
parseReleaseMessage,
|
|
166
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tmlbrain
|
|
3
|
+
description: Use when an AI agent needs to search, read, update, classify, validate, index, or synchronize the TMLBrain team knowledge base; invoke for TMLBrain knowledge questions, local-first retrieval, server-side knowledge writes, Markdown knowledge edits, conflict-aware updates, and dedicated knowledge repository backup workflows where clients must not operate remote Git repositories directly.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TMLBrain Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when an AI agent needs to search, update, classify, or maintain
|
|
9
|
+
the TMLBrain knowledge base.
|
|
10
|
+
|
|
11
|
+
## Core Principles
|
|
12
|
+
|
|
13
|
+
- Treat Markdown files under `knowledge/` as the source of truth.
|
|
14
|
+
- Use TMLBrain commands and tools instead of asking normal users to run raw Git commands.
|
|
15
|
+
- Run `tmlbrain capabilities --json` first when the available command surface is unclear.
|
|
16
|
+
- Search local knowledge before answering knowledge-base questions.
|
|
17
|
+
- Prefer exact search first, deterministic metadata/graph indexes second, and LightRAG semantic retrieval when enabled.
|
|
18
|
+
- Clients use read-only local snapshots for search and indexing.
|
|
19
|
+
- Clients MUST NOT push to the server Git repository or dedicated knowledge repository.
|
|
20
|
+
- Add, update, and ingest requests MUST go through the TMLBrain server API with `tmlbrain remote ...` commands.
|
|
21
|
+
- The server is the only runtime that mutates Markdown files, validates them, commits repository changes, and pushes backup state.
|
|
22
|
+
- Ask the server to update the smallest useful Markdown region, usually a front matter field, heading section, block, or link context.
|
|
23
|
+
- Show or summarize server-returned diffs after write requests.
|
|
24
|
+
- Never silently overwrite remote knowledge.
|
|
25
|
+
- Avoid deletion unless the user explicitly asks for it; prefer marking content stale.
|
|
26
|
+
- Normal users do not need to know the internal archive folder/status.
|
|
27
|
+
|
|
28
|
+
## User-Facing Terms
|
|
29
|
+
|
|
30
|
+
- When a user says "archive to the knowledge base", "store this", "save this",
|
|
31
|
+
"record this", or similar, treat it as "save this knowledge into TMLBrain".
|
|
32
|
+
- Use `tmlbrain save` for that user-facing save path. Do not set
|
|
33
|
+
`status: archived` or choose `knowledge/90-archive/` unless the user
|
|
34
|
+
explicitly means inactive historical content.
|
|
35
|
+
- Explain available actions as save, search, update, and sync. Avoid exposing
|
|
36
|
+
internal names such as archive, ingest, Git, commit, or backup unless the user
|
|
37
|
+
asks about implementation details.
|
|
38
|
+
|
|
39
|
+
## Knowledge Structure
|
|
40
|
+
|
|
41
|
+
Use the approved folder taxonomy:
|
|
42
|
+
|
|
43
|
+
- `knowledge/00-inbox/`: temporary or unclassified notes.
|
|
44
|
+
- `knowledge/10-projects/`: project-specific documents, plans, decisions, and retrospectives.
|
|
45
|
+
- `knowledge/20-areas/`: long-lived responsibility areas.
|
|
46
|
+
- `knowledge/30-resources/`: reusable research, notes, methods, and learning material.
|
|
47
|
+
- `knowledge/40-references/`: stable reference material and checklists.
|
|
48
|
+
- `knowledge/90-archive/`: inactive historical content kept for future search.
|
|
49
|
+
|
|
50
|
+
Documents outside `00-inbox/` must include:
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
---
|
|
54
|
+
title: Example
|
|
55
|
+
type: project|area|resource|reference|meeting|decision
|
|
56
|
+
status: draft|active|stale|archived
|
|
57
|
+
owner: TML
|
|
58
|
+
tags: []
|
|
59
|
+
updated: YYYY-MM-DD
|
|
60
|
+
---
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Archive reason is optional. A document in `90-archive/` must use
|
|
64
|
+
`status: archived`. This is an internal lifecycle state, not the default place
|
|
65
|
+
for newly saved knowledge.
|
|
66
|
+
|
|
67
|
+
## Default Workflow
|
|
68
|
+
|
|
69
|
+
1. Run `tmlbrain capabilities --json` to confirm the installed command surface.
|
|
70
|
+
2. Run `tmlbrain sync --pull` to refresh the local read-only snapshot from the TMLBrain server.
|
|
71
|
+
3. Search with `tmlbrain find "<query>"` or `tmlbrain search "<query>"`.
|
|
72
|
+
4. If needed, use `tmlbrain index` and inspect `.tmlbrain/index/` outputs.
|
|
73
|
+
5. If graph retrieval is enabled, use CocoIndex-derived metadata/graph indexes and LightRAG retrieval after exact search.
|
|
74
|
+
6. Read relevant local source documents.
|
|
75
|
+
7. For new knowledge, use `tmlbrain save`.
|
|
76
|
+
8. For updates, use `tmlbrain remote update --file <path> --replace <old> --with <new>` or a content-file update when a full document replacement is explicitly intended.
|
|
77
|
+
9. Show or summarize the server-returned diff and result.
|
|
78
|
+
10. Run `tmlbrain sync --pull` again so the local snapshot and indexes reflect the accepted server commit.
|
|
79
|
+
|
|
80
|
+
## Command Recipes
|
|
81
|
+
|
|
82
|
+
Initialize or repair local runtime:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
npx @time-machine-lab/tmlbrain client install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run local CLI after global/package installation:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
tmlbrain client install
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Enable optional graph runtime:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
tmlbrain graph setup
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Validate metadata and links:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
tmlbrain validate
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Search exact local Markdown:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
tmlbrain find "keyword"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Rebuild deterministic local index:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
tmlbrain index
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Save new knowledge:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
tmlbrain save --title "Title" --content "Content"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Save an existing local file through the server:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
tmlbrain save ./note.md
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Update a precise server-side text region:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
tmlbrain remote update --file knowledge/00-inbox/note.md --replace "old text" --with "new text"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Refresh local snapshot from the server:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
tmlbrain sync --pull
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Show or change the configured server:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
tmlbrain config show
|
|
146
|
+
tmlbrain config set-server http://server-host:7389 --token <token>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Start the server API from the server worktree:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
tmlbrain serve --host 0.0.0.0 --port 7389
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Deploy the server with Docker:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
docker run -d --name tmlbrain-server -p 7389:7389 -e TMLBRAIN_SERVER_TOKEN=<token> -e TMLBRAIN_KNOWLEDGE_REPO=<git-url> -v tmlbrain-data:/data <DOCKER_REPO>:latest
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Back up server state:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
tmlbrain backup --dry-run --remote backup
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Conflict Policy
|
|
168
|
+
|
|
169
|
+
If TMLBrain reports a conflict package:
|
|
170
|
+
|
|
171
|
+
1. Read the package with `tmlbrain conflict show <id>`.
|
|
172
|
+
2. Summarize the base, local, and remote differences in user-facing language.
|
|
173
|
+
3. Propose a merged Markdown result.
|
|
174
|
+
4. Ask the user to confirm the merged result.
|
|
175
|
+
5. Apply the confirmed merge through `tmlbrain conflict resolve <id> --merged-file <path>`.
|
|
176
|
+
|
|
177
|
+
Do not ask the user to interpret raw Git conflict markers. Do not force-push or
|
|
178
|
+
silently overwrite remote content.
|
|
179
|
+
|
|
180
|
+
## Forbidden Client Actions
|
|
181
|
+
|
|
182
|
+
Do not run these as a normal client workflow:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
tmlbrain sync --push
|
|
186
|
+
git push
|
|
187
|
+
git pull from the server repository
|
|
188
|
+
git push to dedicated knowledge repository backup
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
If a workflow needs repository mutation, call the TMLBrain server through
|
|
192
|
+
`tmlbrain remote ...` instead.
|