bitacora-cli 1.0.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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/src/cli.js +175 -0
- package/dist/src/commands/init.js +94 -0
- package/dist/src/commands/log.js +46 -0
- package/dist/src/commands/new-track.js +62 -0
- package/dist/src/commands/rebuild-state.js +15 -0
- package/dist/src/commands/validate.js +21 -0
- package/dist/src/core/boot.js +40 -0
- package/dist/src/core/parser.js +109 -0
- package/dist/src/core/state-builder.js +51 -0
- package/dist/src/core/templates.js +333 -0
- package/dist/src/core/validator.js +128 -0
- package/dist/src/index.js +12 -0
- package/dist/src/types/index.js +5 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SK3L3TØR
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Bitacora is a deterministic project memory CLI for agent-driven workflows. It stores project context in files, validates structure, and keeps state rebuildable.
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g bitacora-cli
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
Show help:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bitacora --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Typical flow:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# init bitacora files structure and add SKILL
|
|
21
|
+
bitacora init
|
|
22
|
+
|
|
23
|
+
# ask your agent to complete bitacora
|
|
24
|
+
# whith your project info. You can
|
|
25
|
+
# complete bitacora by yourself if you wish.
|
|
26
|
+
codex "$bitacora read and fill bitacora template with this project info"
|
|
27
|
+
|
|
28
|
+
# plan some tasks
|
|
29
|
+
codex (planner )"$bitacora plan the unit test for this repo and add separated tasks to bitacora"
|
|
30
|
+
|
|
31
|
+
# use another agent
|
|
32
|
+
codex "$bitacora implement the 003 track"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `--root <path>` on any command to target a different project directory.
|
|
36
|
+
|
|
37
|
+
## What Each Bitacora File Stores
|
|
38
|
+
|
|
39
|
+
- `bitacora/index.md`: memory map and session read order.
|
|
40
|
+
- `bitacora/product.md`: project goals, scope, constraints, and non-goals.
|
|
41
|
+
- `bitacora/tech-stack.md`: runtime/tooling/dependencies and technical rules.
|
|
42
|
+
- `bitacora/workflow.md`: development method, quality gates, and handoff checklist.
|
|
43
|
+
- `bitacora/ux-style-guide.md`: visual tokens and UX interaction rules.
|
|
44
|
+
- `bitacora/tracks/tracks.md`: canonical track registry, current snapshot, and handoff summary.
|
|
45
|
+
- `bitacora/tracks/TRACK-*/track.md`: per-track plan, tasks, decisions, and timestamped log.
|
|
46
|
+
- `.agents/skills/bitacora/SKILL.md`: instructions agents follow to keep the memory system updated.
|
|
47
|
+
|
|
48
|
+
## How Agents Read and Update Bitacora
|
|
49
|
+
|
|
50
|
+
Recommended memory read order for agents:
|
|
51
|
+
|
|
52
|
+
1. `bitacora/index.md`
|
|
53
|
+
2. `bitacora/product.md`
|
|
54
|
+
3. `bitacora/tech-stack.md`
|
|
55
|
+
4. `bitacora/workflow.md`
|
|
56
|
+
5. `bitacora/ux-style-guide.md`
|
|
57
|
+
6. `bitacora/tracks/tracks.md`
|
|
58
|
+
7. Active files in `bitacora/tracks/TRACK-*/track.md`
|
|
59
|
+
|
|
60
|
+
Typical agent write operations:
|
|
61
|
+
|
|
62
|
+
- `bitacora new-track`: create a new work unit.
|
|
63
|
+
- `bitacora log --track-id ... --message ...`: append progress updates.
|
|
64
|
+
- direct updates to `bitacora/tracks/TRACK-*/track.md`: tasks, decisions, execution details.
|
|
65
|
+
- direct updates to `bitacora/tracks/tracks.md`: canonical project status and next action.
|
|
66
|
+
- `bitacora validate` and `bitacora rebuild-state`: ensure memory remains valid and deterministic.
|
|
67
|
+
|
|
68
|
+
## Agent Skill Integration
|
|
69
|
+
|
|
70
|
+
When you run `bitacora init`, Bitacora also creates an agent skill at:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
.agents/skills/bitacora/SKILL.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This skill guides agents to keep memory updated during implementation:
|
|
77
|
+
|
|
78
|
+
- Read project memory before coding.
|
|
79
|
+
- Create and update tracks as work progresses.
|
|
80
|
+
- Log decisions and progress.
|
|
81
|
+
- Keep a consistent handoff summary in `bitacora/tracks/tracks.md`.
|
|
82
|
+
|
|
83
|
+
## Commands
|
|
84
|
+
|
|
85
|
+
### `bitacora init [--force] [--root <path>]`
|
|
86
|
+
|
|
87
|
+
Creates the `bitacora/` memory structure in the project root.
|
|
88
|
+
|
|
89
|
+
- `--force`: recreates memory files if they already exist.
|
|
90
|
+
- `--root <path>`: sets the project root (default: current directory).
|
|
91
|
+
|
|
92
|
+
### `bitacora new-track [trackId] [--status <status>] [--priority <priority>] [--root <path>]`
|
|
93
|
+
|
|
94
|
+
Creates a new track file. If `trackId` is omitted, Bitacora picks the next sequential ID.
|
|
95
|
+
|
|
96
|
+
- `trackId`: optional explicit track ID (example: `TRACK-010`).
|
|
97
|
+
- `--status <status>`: sets the initial track status.
|
|
98
|
+
- `--priority <priority>`: sets the initial track priority.
|
|
99
|
+
- `--root <path>`: sets the project root.
|
|
100
|
+
|
|
101
|
+
### `bitacora log --track-id <trackId> --message <text> [--root <path>]`
|
|
102
|
+
|
|
103
|
+
Appends a timestamped log entry to an existing track.
|
|
104
|
+
|
|
105
|
+
- `--track-id <trackId>`: required track identifier.
|
|
106
|
+
- `--message <text>`: required log message.
|
|
107
|
+
- `--root <path>`: sets the project root.
|
|
108
|
+
|
|
109
|
+
### `bitacora validate [--json] [--root <path>]`
|
|
110
|
+
|
|
111
|
+
Validates the Bitacora file/folder structure and reports errors.
|
|
112
|
+
|
|
113
|
+
- `--json`: prints validation output as JSON.
|
|
114
|
+
- `--root <path>`: sets the project root.
|
|
115
|
+
|
|
116
|
+
### `bitacora rebuild-state [--root <path>]`
|
|
117
|
+
|
|
118
|
+
Revalidates memory and confirms deterministic state can be rebuilt from tracks.
|
|
119
|
+
|
|
120
|
+
- `--root <path>`: sets the project root.
|
|
121
|
+
|
|
122
|
+
## Exit Codes
|
|
123
|
+
|
|
124
|
+
- `0`: success
|
|
125
|
+
- `1`: validation or input error
|
|
126
|
+
- `2`: unexpected runtime error
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
Internal development documentation is in `README-DEV.md`.
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.createCliProgram = createCliProgram;
|
|
5
|
+
exports.runCli = runCli;
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const init_1 = require("./commands/init");
|
|
8
|
+
const log_1 = require("./commands/log");
|
|
9
|
+
const new_track_1 = require("./commands/new-track");
|
|
10
|
+
const rebuild_state_1 = require("./commands/rebuild-state");
|
|
11
|
+
const validate_1 = require("./commands/validate");
|
|
12
|
+
const COMMAND_HELP_OVERVIEW = `
|
|
13
|
+
Command details:
|
|
14
|
+
init
|
|
15
|
+
Creates the bitacora memory structure in the selected project root.
|
|
16
|
+
Options:
|
|
17
|
+
--force Recreate bitacora memory if it already exists.
|
|
18
|
+
--root <path> Project root path (default: current directory).
|
|
19
|
+
|
|
20
|
+
new-track [trackId]
|
|
21
|
+
Creates a new track file, automatically picking the next track id when omitted.
|
|
22
|
+
Options:
|
|
23
|
+
--status <status> Initial status for the track.
|
|
24
|
+
--priority <priority> Initial priority for the track.
|
|
25
|
+
--root <path> Project root path (default: current directory).
|
|
26
|
+
|
|
27
|
+
validate
|
|
28
|
+
Validates bitacora files and folders against the expected deterministic format.
|
|
29
|
+
Options:
|
|
30
|
+
--json Prints validation output as JSON.
|
|
31
|
+
--root <path> Project root path (default: current directory).
|
|
32
|
+
|
|
33
|
+
rebuild-state
|
|
34
|
+
Revalidates track memory and confirms deterministic state can be rebuilt.
|
|
35
|
+
Options:
|
|
36
|
+
--root <path> Project root path (default: current directory).
|
|
37
|
+
|
|
38
|
+
log
|
|
39
|
+
Appends a timestamped log entry to an existing track.
|
|
40
|
+
Options:
|
|
41
|
+
--track-id <trackId> Track identifier.
|
|
42
|
+
--message <text> Log entry message.
|
|
43
|
+
--root <path> Project root path (default: current directory).
|
|
44
|
+
`;
|
|
45
|
+
function writeLine(writer, message) {
|
|
46
|
+
writer(message.endsWith("\n") ? message : `${message}\n`);
|
|
47
|
+
}
|
|
48
|
+
function createCliProgram(runtime = {}, onCommandExitCode) {
|
|
49
|
+
const now = runtime.now ?? (() => new Date().toISOString());
|
|
50
|
+
const cwd = runtime.cwd ?? (() => process.cwd());
|
|
51
|
+
const stdout = runtime.stdout ?? ((message) => process.stdout.write(message));
|
|
52
|
+
const stderr = runtime.stderr ?? ((message) => process.stderr.write(message));
|
|
53
|
+
const program = new commander_1.Command();
|
|
54
|
+
program
|
|
55
|
+
.name("bitacora")
|
|
56
|
+
.description("Deterministic agent-oriented project memory system")
|
|
57
|
+
.configureOutput({
|
|
58
|
+
writeOut: (message) => writeLine(stdout, message),
|
|
59
|
+
writeErr: (message) => writeLine(stderr, message)
|
|
60
|
+
});
|
|
61
|
+
program
|
|
62
|
+
.command("init")
|
|
63
|
+
.description("Create bitacora memory files and folders.")
|
|
64
|
+
.option("--force", "delete and recreate bitacora memory")
|
|
65
|
+
.option("--root <path>", "project root path")
|
|
66
|
+
.action((options) => {
|
|
67
|
+
const exitCode = (0, init_1.runInitCommand)({
|
|
68
|
+
rootDir: options.root ?? cwd(),
|
|
69
|
+
force: Boolean(options.force)
|
|
70
|
+
}, {
|
|
71
|
+
now,
|
|
72
|
+
onError: (message) => writeLine(stderr, message)
|
|
73
|
+
});
|
|
74
|
+
onCommandExitCode?.(exitCode);
|
|
75
|
+
});
|
|
76
|
+
program
|
|
77
|
+
.command("new-track [trackId]")
|
|
78
|
+
.description("Create a new track with optional id, status, and priority.")
|
|
79
|
+
.option("--status <status>", "track status")
|
|
80
|
+
.option("--priority <priority>", "track priority")
|
|
81
|
+
.option("--root <path>", "project root path")
|
|
82
|
+
.action((trackId, options) => {
|
|
83
|
+
const newTrackOptions = {
|
|
84
|
+
rootDir: options.root ?? cwd()
|
|
85
|
+
};
|
|
86
|
+
if (trackId !== undefined) {
|
|
87
|
+
newTrackOptions.trackId = trackId;
|
|
88
|
+
}
|
|
89
|
+
if (options.status !== undefined) {
|
|
90
|
+
newTrackOptions.status = options.status;
|
|
91
|
+
}
|
|
92
|
+
if (options.priority !== undefined) {
|
|
93
|
+
newTrackOptions.priority = options.priority;
|
|
94
|
+
}
|
|
95
|
+
const exitCode = (0, new_track_1.runNewTrackCommand)(newTrackOptions, {
|
|
96
|
+
now,
|
|
97
|
+
onError: (message) => writeLine(stderr, message)
|
|
98
|
+
});
|
|
99
|
+
onCommandExitCode?.(exitCode);
|
|
100
|
+
});
|
|
101
|
+
program
|
|
102
|
+
.command("validate")
|
|
103
|
+
.description("Validate bitacora structure and report errors.")
|
|
104
|
+
.option("--json", "output JSON validation result")
|
|
105
|
+
.option("--root <path>", "project root path")
|
|
106
|
+
.action((options) => {
|
|
107
|
+
const exitCode = (0, validate_1.runValidateCommand)({
|
|
108
|
+
rootDir: options.root ?? cwd(),
|
|
109
|
+
json: Boolean(options.json)
|
|
110
|
+
}, {
|
|
111
|
+
onOutput: (message) => writeLine(stdout, message)
|
|
112
|
+
});
|
|
113
|
+
onCommandExitCode?.(exitCode);
|
|
114
|
+
});
|
|
115
|
+
program
|
|
116
|
+
.command("rebuild-state")
|
|
117
|
+
.description("Revalidate memory and rebuild in-memory state from tracks.")
|
|
118
|
+
.option("--root <path>", "project root path")
|
|
119
|
+
.action((options) => {
|
|
120
|
+
const exitCode = (0, rebuild_state_1.runRebuildStateCommand)({
|
|
121
|
+
rootDir: options.root ?? cwd()
|
|
122
|
+
}, {
|
|
123
|
+
onOutput: (message) => writeLine(stdout, message),
|
|
124
|
+
onError: (message) => writeLine(stderr, message)
|
|
125
|
+
});
|
|
126
|
+
onCommandExitCode?.(exitCode);
|
|
127
|
+
});
|
|
128
|
+
program
|
|
129
|
+
.command("log")
|
|
130
|
+
.description("Append a log entry to a track.")
|
|
131
|
+
.requiredOption("--track-id <trackId>", "track identifier")
|
|
132
|
+
.requiredOption("--message <text>", "log message")
|
|
133
|
+
.option("--root <path>", "project root path")
|
|
134
|
+
.action((options) => {
|
|
135
|
+
const exitCode = (0, log_1.runLogCommand)({
|
|
136
|
+
rootDir: options.root ?? cwd(),
|
|
137
|
+
trackId: options.trackId,
|
|
138
|
+
message: options.message
|
|
139
|
+
}, {
|
|
140
|
+
now,
|
|
141
|
+
onError: (message) => writeLine(stderr, message)
|
|
142
|
+
});
|
|
143
|
+
onCommandExitCode?.(exitCode);
|
|
144
|
+
});
|
|
145
|
+
program.addHelpText("after", COMMAND_HELP_OVERVIEW);
|
|
146
|
+
return program;
|
|
147
|
+
}
|
|
148
|
+
async function runCli(argv, runtime = {}) {
|
|
149
|
+
let commandExitCode = 0;
|
|
150
|
+
const program = createCliProgram(runtime, (exitCode) => {
|
|
151
|
+
commandExitCode = exitCode;
|
|
152
|
+
});
|
|
153
|
+
program.exitOverride();
|
|
154
|
+
try {
|
|
155
|
+
await program.parseAsync(argv, { from: "node" });
|
|
156
|
+
return commandExitCode;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (error instanceof commander_1.CommanderError) {
|
|
160
|
+
return commandExitCode !== 0 ? commandExitCode : 1;
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (require.main === module) {
|
|
166
|
+
runCli(process.argv)
|
|
167
|
+
.then((exitCode) => {
|
|
168
|
+
process.exitCode = exitCode;
|
|
169
|
+
})
|
|
170
|
+
.catch((error) => {
|
|
171
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
172
|
+
process.stderr.write(`${message}\n`);
|
|
173
|
+
process.exitCode = 2;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runInitCommand = runInitCommand;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
|
+
const templates_1 = require("../core/templates");
|
|
11
|
+
function computeHash(content) {
|
|
12
|
+
return (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
function readSkillsLockFile(filePath) {
|
|
15
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
16
|
+
return {
|
|
17
|
+
version: 1,
|
|
18
|
+
skills: {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
|
|
23
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
24
|
+
return { version: 1, skills: {} };
|
|
25
|
+
}
|
|
26
|
+
const versionRaw = parsed.version;
|
|
27
|
+
const skillsRaw = parsed.skills;
|
|
28
|
+
const normalized = {};
|
|
29
|
+
if (typeof skillsRaw === "object" && skillsRaw !== null) {
|
|
30
|
+
for (const [name, value] of Object.entries(skillsRaw)) {
|
|
31
|
+
if (typeof value !== "object" || value === null) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const source = value.source;
|
|
35
|
+
const sourceType = value.sourceType;
|
|
36
|
+
const computedHash = value.computedHash;
|
|
37
|
+
if (typeof source === "string" &&
|
|
38
|
+
typeof sourceType === "string" &&
|
|
39
|
+
typeof computedHash === "string") {
|
|
40
|
+
normalized[name] = { source, sourceType, computedHash };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
version: typeof versionRaw === "number" ? versionRaw : 1,
|
|
46
|
+
skills: normalized
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {
|
|
51
|
+
version: 1,
|
|
52
|
+
skills: {}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function writeSkillsLockFile(rootDir, skillContent) {
|
|
57
|
+
const lockPath = node_path_1.default.join(rootDir, "skills-lock.json");
|
|
58
|
+
const lock = readSkillsLockFile(lockPath);
|
|
59
|
+
lock.skills.bitacora = {
|
|
60
|
+
source: ".agents/skills/bitacora/SKILL.md",
|
|
61
|
+
sourceType: "local",
|
|
62
|
+
computedHash: computeHash(skillContent)
|
|
63
|
+
};
|
|
64
|
+
node_fs_1.default.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
|
|
65
|
+
}
|
|
66
|
+
function runInitCommand(options, dependencies = {}) {
|
|
67
|
+
const now = dependencies.now ?? (() => new Date().toISOString());
|
|
68
|
+
const onError = dependencies.onError ?? (() => { });
|
|
69
|
+
const memoryRoot = node_path_1.default.join(options.rootDir, "bitacora");
|
|
70
|
+
if (node_fs_1.default.existsSync(memoryRoot)) {
|
|
71
|
+
if (!options.force) {
|
|
72
|
+
onError("bitacora already exists");
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
node_fs_1.default.rmSync(memoryRoot, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
const createdAt = now();
|
|
78
|
+
const createdOnDate = createdAt.slice(0, 10);
|
|
79
|
+
const trackId = "TRACK-001";
|
|
80
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(memoryRoot, "tracks", trackId), { recursive: true });
|
|
81
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(options.rootDir, ".agents", "skills", "bitacora"), { recursive: true });
|
|
82
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "product.md"), (0, templates_1.createProductTemplate)(), "utf8");
|
|
83
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "tech-stack.md"), (0, templates_1.createTechStackTemplate)(), "utf8");
|
|
84
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "workflow.md"), (0, templates_1.createWorkflowTemplate)(), "utf8");
|
|
85
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "ux-style-guide.md"), (0, templates_1.createUxStyleGuideTemplate)(), "utf8");
|
|
86
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "index.md"), (0, templates_1.createIndexTemplate)(), "utf8");
|
|
87
|
+
const skillContent = (0, templates_1.createAgentSkillTemplate)();
|
|
88
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(options.rootDir, ".agents", "skills", "bitacora", "SKILL.md"), skillContent, "utf8");
|
|
89
|
+
writeSkillsLockFile(options.rootDir, skillContent);
|
|
90
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "tracks", "tracks.md"), (0, templates_1.createTracksRegistryTemplate)(createdOnDate), "utf8");
|
|
91
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "tracks", "tracks-template.md"), (0, templates_1.createTracksTemplate)(), "utf8");
|
|
92
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(memoryRoot, "tracks", trackId, "track.md"), (0, templates_1.createTrackTemplate)(trackId, "active", "medium", createdAt), "utf8");
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runLogCommand = runLogCommand;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const parser_1 = require("../core/parser");
|
|
10
|
+
function getTrackDirectories(rootDir) {
|
|
11
|
+
const tracksRoot = node_path_1.default.join(rootDir, "bitacora", "tracks");
|
|
12
|
+
if (!node_fs_1.default.existsSync(tracksRoot)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return node_fs_1.default
|
|
16
|
+
.readdirSync(tracksRoot, { withFileTypes: true })
|
|
17
|
+
.filter((entry) => entry.isDirectory())
|
|
18
|
+
.map((entry) => entry.name)
|
|
19
|
+
.sort((left, right) => left.localeCompare(right));
|
|
20
|
+
}
|
|
21
|
+
function runLogCommand(options, dependencies = {}) {
|
|
22
|
+
const now = dependencies.now ?? (() => new Date().toISOString());
|
|
23
|
+
const onError = dependencies.onError ?? (() => { });
|
|
24
|
+
const trackPath = node_path_1.default.join(options.rootDir, "bitacora", "tracks", options.trackId, "track.md");
|
|
25
|
+
if (!node_fs_1.default.existsSync(trackPath)) {
|
|
26
|
+
onError(`Track not found: ${options.trackId}`);
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
const timestamp = now();
|
|
30
|
+
const normalized = node_fs_1.default.readFileSync(trackPath, "utf8").replace(/\r\n/g, "\n");
|
|
31
|
+
try {
|
|
32
|
+
(0, parser_1.parseTrackMarkdown)(normalized);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
+
onError(message);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
const updatedHeader = normalized.replace(/^updated_at:\s*.+$/m, `updated_at: ${timestamp}`);
|
|
40
|
+
const line = `- ${timestamp} | ${options.message}`;
|
|
41
|
+
const finalContent = updatedHeader.endsWith("\n")
|
|
42
|
+
? `${updatedHeader}${line}\n`
|
|
43
|
+
: `${updatedHeader}\n${line}\n`;
|
|
44
|
+
node_fs_1.default.writeFileSync(trackPath, finalContent, "utf8");
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runNewTrackCommand = runNewTrackCommand;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const templates_1 = require("../core/templates");
|
|
10
|
+
const validator_1 = require("../core/validator");
|
|
11
|
+
const TRACK_ID_REGEX = /^TRACK-\d{3}$/;
|
|
12
|
+
function getTrackDirectories(rootDir) {
|
|
13
|
+
const tracksRoot = node_path_1.default.join(rootDir, "bitacora", "tracks");
|
|
14
|
+
if (!node_fs_1.default.existsSync(tracksRoot)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return node_fs_1.default
|
|
18
|
+
.readdirSync(tracksRoot, { withFileTypes: true })
|
|
19
|
+
.filter((entry) => entry.isDirectory())
|
|
20
|
+
.map((entry) => entry.name)
|
|
21
|
+
.sort((left, right) => left.localeCompare(right));
|
|
22
|
+
}
|
|
23
|
+
function computeNextTrackId(trackDirectories) {
|
|
24
|
+
const lastTrackId = trackDirectories.at(-1);
|
|
25
|
+
if (!lastTrackId) {
|
|
26
|
+
return "TRACK-001";
|
|
27
|
+
}
|
|
28
|
+
const trackNumber = Number.parseInt(lastTrackId.slice("TRACK-".length), 10);
|
|
29
|
+
const nextTrackNumber = Number.isFinite(trackNumber) ? trackNumber + 1 : 1;
|
|
30
|
+
return `TRACK-${String(nextTrackNumber).padStart(3, "0")}`;
|
|
31
|
+
}
|
|
32
|
+
function runNewTrackCommand(options, dependencies = {}) {
|
|
33
|
+
const now = dependencies.now ?? (() => new Date().toISOString());
|
|
34
|
+
const onError = dependencies.onError ?? (() => { });
|
|
35
|
+
const validation = (0, validator_1.validateMemory)(options.rootDir);
|
|
36
|
+
if (!validation.ok) {
|
|
37
|
+
onError(`Memory structure is invalid: ${validation.errors.join("; ")}`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
const trackDirectories = getTrackDirectories(options.rootDir);
|
|
41
|
+
const nextTrackId = computeNextTrackId(trackDirectories);
|
|
42
|
+
const desiredTrackId = options.trackId ?? nextTrackId;
|
|
43
|
+
if (!TRACK_ID_REGEX.test(desiredTrackId)) {
|
|
44
|
+
onError("Invalid track ID format. Expected TRACK-###");
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
if (options.trackId && options.trackId !== nextTrackId) {
|
|
48
|
+
onError(`Explicit track ID must match next sequential ID: ${nextTrackId}`);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
const trackDir = node_path_1.default.join(options.rootDir, "bitacora", "tracks", desiredTrackId);
|
|
52
|
+
if (node_fs_1.default.existsSync(trackDir)) {
|
|
53
|
+
onError(`Track already exists: ${desiredTrackId}`);
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
const status = options.status ?? "active";
|
|
57
|
+
const priority = options.priority ?? "medium";
|
|
58
|
+
const timestamp = now();
|
|
59
|
+
node_fs_1.default.mkdirSync(trackDir, { recursive: true });
|
|
60
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(trackDir, "track.md"), (0, templates_1.createTrackTemplate)(desiredTrackId, status, priority, timestamp), "utf8");
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runRebuildStateCommand = runRebuildStateCommand;
|
|
4
|
+
const validator_1 = require("../core/validator");
|
|
5
|
+
function runRebuildStateCommand(options, dependencies = {}) {
|
|
6
|
+
const onOutput = dependencies.onOutput ?? (() => { });
|
|
7
|
+
const onError = dependencies.onError ?? (() => { });
|
|
8
|
+
const validation = (0, validator_1.validateMemory)(options.rootDir);
|
|
9
|
+
if (!validation.ok) {
|
|
10
|
+
onError(`Memory structure is invalid: ${validation.errors.join("; ")}`);
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
onOutput("State rebuilt");
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runValidateCommand = runValidateCommand;
|
|
4
|
+
const validator_1 = require("../core/validator");
|
|
5
|
+
function runValidateCommand(options, dependencies = {}) {
|
|
6
|
+
const onOutput = dependencies.onOutput ?? (() => { });
|
|
7
|
+
const result = (0, validator_1.validateMemory)(options.rootDir);
|
|
8
|
+
if (options.json) {
|
|
9
|
+
onOutput(`${JSON.stringify(result, null, 2)}\n`);
|
|
10
|
+
return result.ok ? 0 : 1;
|
|
11
|
+
}
|
|
12
|
+
if (result.ok) {
|
|
13
|
+
onOutput("Validation passed");
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
onOutput("Validation failed");
|
|
17
|
+
for (const error of result.errors) {
|
|
18
|
+
onOutput(error);
|
|
19
|
+
}
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.boot = boot;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const state_builder_1 = require("./state-builder");
|
|
10
|
+
const validator_1 = require("./validator");
|
|
11
|
+
function readRequiredFile(rootDir, relativePath) {
|
|
12
|
+
const targetPath = node_path_1.default.join(rootDir, relativePath);
|
|
13
|
+
if (!node_fs_1.default.existsSync(targetPath)) {
|
|
14
|
+
throw new Error("Memory structure is invalid");
|
|
15
|
+
}
|
|
16
|
+
return node_fs_1.default.readFileSync(targetPath, "utf8");
|
|
17
|
+
}
|
|
18
|
+
function boot(rootDir, loaders = {}) {
|
|
19
|
+
const index = readRequiredFile(rootDir, node_path_1.default.join("bitacora", "index.md"));
|
|
20
|
+
const product = readRequiredFile(rootDir, node_path_1.default.join("bitacora", "product.md"));
|
|
21
|
+
const techStack = readRequiredFile(rootDir, node_path_1.default.join("bitacora", "tech-stack.md"));
|
|
22
|
+
const workflow = readRequiredFile(rootDir, node_path_1.default.join("bitacora", "workflow.md"));
|
|
23
|
+
const uxStyleGuide = readRequiredFile(rootDir, node_path_1.default.join("bitacora", "ux-style-guide.md"));
|
|
24
|
+
loaders.loadContext?.({ index, product, techStack, workflow, uxStyleGuide });
|
|
25
|
+
const tracksRoot = node_path_1.default.join(rootDir, "bitacora", "tracks");
|
|
26
|
+
if (!node_fs_1.default.existsSync(tracksRoot)) {
|
|
27
|
+
throw new Error("Memory structure is invalid");
|
|
28
|
+
}
|
|
29
|
+
const trackFiles = node_fs_1.default
|
|
30
|
+
.readdirSync(tracksRoot, { withFileTypes: true })
|
|
31
|
+
.filter((entry) => entry.isDirectory())
|
|
32
|
+
.map((entry) => node_path_1.default.join(tracksRoot, entry.name, "track.md"))
|
|
33
|
+
.sort((left, right) => left.localeCompare(right));
|
|
34
|
+
loaders.loadTracks?.(trackFiles);
|
|
35
|
+
const validation = (0, validator_1.validateMemory)(rootDir);
|
|
36
|
+
if (!validation.ok) {
|
|
37
|
+
throw new Error(`Memory structure is invalid: ${validation.errors.join("; ")}`);
|
|
38
|
+
}
|
|
39
|
+
return (0, state_builder_1.buildStateFromTracks)(validation.tracks);
|
|
40
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTrackMarkdown = parseTrackMarkdown;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
function parseFrontmatterBlock(markdown) {
|
|
6
|
+
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
7
|
+
const parts = normalized.split("\n");
|
|
8
|
+
if (parts[0] !== "---") {
|
|
9
|
+
throw new Error("Missing frontmatter");
|
|
10
|
+
}
|
|
11
|
+
const endIndex = parts.indexOf("---", 1);
|
|
12
|
+
if (endIndex === -1) {
|
|
13
|
+
throw new Error("Missing frontmatter");
|
|
14
|
+
}
|
|
15
|
+
const raw = {};
|
|
16
|
+
for (const line of parts.slice(1, endIndex)) {
|
|
17
|
+
if (line.trim() === "") {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const separatorIndex = line.indexOf(":");
|
|
21
|
+
if (separatorIndex === -1) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
25
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
26
|
+
raw[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return raw;
|
|
29
|
+
}
|
|
30
|
+
function parseSections(markdown) {
|
|
31
|
+
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
32
|
+
const parts = normalized.split("\n---\n");
|
|
33
|
+
const body = parts.length > 1 ? parts.slice(1).join("\n---\n") : "";
|
|
34
|
+
const sectionMap = {};
|
|
35
|
+
const headings = ["Overview", "Tasks", "Decisions", "Log"];
|
|
36
|
+
for (let index = 0; index < headings.length; index += 1) {
|
|
37
|
+
const heading = headings[index];
|
|
38
|
+
if (heading === undefined) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const startToken = `# ${heading}\n`;
|
|
42
|
+
const startIndex = body.indexOf(startToken);
|
|
43
|
+
if (startIndex === -1) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const contentStart = startIndex + startToken.length;
|
|
47
|
+
const nextHeading = headings[index + 1];
|
|
48
|
+
const endIndex = nextHeading === undefined
|
|
49
|
+
? body.length
|
|
50
|
+
: body.indexOf(`# ${nextHeading}\n`, contentStart);
|
|
51
|
+
const content = body.slice(contentStart, endIndex === -1 ? body.length : endIndex).trim();
|
|
52
|
+
sectionMap[heading.toLowerCase()] = content;
|
|
53
|
+
}
|
|
54
|
+
return sectionMap;
|
|
55
|
+
}
|
|
56
|
+
function parseTrackMarkdown(markdown) {
|
|
57
|
+
const frontmatter = parseFrontmatterBlock(markdown);
|
|
58
|
+
const sections = parseSections(markdown);
|
|
59
|
+
const getRequiredField = (field) => {
|
|
60
|
+
const value = frontmatter[field];
|
|
61
|
+
if (!value) {
|
|
62
|
+
throw new Error(`Missing frontmatter field: ${field}`);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
};
|
|
66
|
+
const requiredFrontmatterFields = [
|
|
67
|
+
"track_id",
|
|
68
|
+
"status",
|
|
69
|
+
"priority",
|
|
70
|
+
"created_at",
|
|
71
|
+
"updated_at"
|
|
72
|
+
];
|
|
73
|
+
for (const field of requiredFrontmatterFields) {
|
|
74
|
+
getRequiredField(field);
|
|
75
|
+
}
|
|
76
|
+
const status = getRequiredField("status");
|
|
77
|
+
const priority = getRequiredField("priority");
|
|
78
|
+
const trackId = getRequiredField("track_id");
|
|
79
|
+
const createdAt = getRequiredField("created_at");
|
|
80
|
+
const updatedAt = getRequiredField("updated_at");
|
|
81
|
+
if (!types_1.TRACK_STATUSES.includes(status)) {
|
|
82
|
+
throw new Error("Invalid status");
|
|
83
|
+
}
|
|
84
|
+
if (!types_1.TRACK_PRIORITIES.includes(priority)) {
|
|
85
|
+
throw new Error("Invalid priority");
|
|
86
|
+
}
|
|
87
|
+
const requiredSections = ["overview", "tasks", "decisions", "log"];
|
|
88
|
+
for (const requiredSection of requiredSections) {
|
|
89
|
+
if (!(requiredSection in sections)) {
|
|
90
|
+
const heading = requiredSection.charAt(0).toUpperCase() + requiredSection.slice(1);
|
|
91
|
+
throw new Error(`Missing section: ${heading}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
frontmatter: {
|
|
96
|
+
track_id: trackId,
|
|
97
|
+
status: status,
|
|
98
|
+
priority: priority,
|
|
99
|
+
created_at: createdAt,
|
|
100
|
+
updated_at: updatedAt
|
|
101
|
+
},
|
|
102
|
+
sections: {
|
|
103
|
+
overview: sections.overview ?? "",
|
|
104
|
+
tasks: sections.tasks ?? "",
|
|
105
|
+
decisions: sections.decisions ?? "",
|
|
106
|
+
log: sections.log ?? ""
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildIndexFromTracks = buildIndexFromTracks;
|
|
7
|
+
exports.buildStateFromTracks = buildStateFromTracks;
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
function getTrackNumber(trackId) {
|
|
10
|
+
const match = /^TRACK-(\d{3})$/.exec(trackId);
|
|
11
|
+
if (!match) {
|
|
12
|
+
return Number.MAX_SAFE_INTEGER;
|
|
13
|
+
}
|
|
14
|
+
const numericPart = match[1];
|
|
15
|
+
if (!numericPart) {
|
|
16
|
+
return Number.MAX_SAFE_INTEGER;
|
|
17
|
+
}
|
|
18
|
+
return Number.parseInt(numericPart, 10);
|
|
19
|
+
}
|
|
20
|
+
function sortTrackEntries(entries) {
|
|
21
|
+
return [...entries].sort((left, right) => getTrackNumber(left.track_id) - getTrackNumber(right.track_id));
|
|
22
|
+
}
|
|
23
|
+
function buildIndexFromTracks(tracks) {
|
|
24
|
+
const entries = tracks.map((track) => ({
|
|
25
|
+
track_id: track.frontmatter.track_id,
|
|
26
|
+
status: track.frontmatter.status,
|
|
27
|
+
priority: track.frontmatter.priority,
|
|
28
|
+
created_at: track.frontmatter.created_at,
|
|
29
|
+
updated_at: track.frontmatter.updated_at,
|
|
30
|
+
path: node_path_1.default.join("bitacora", "tracks", track.directoryName, "track.md").replace(/\\/g, "/")
|
|
31
|
+
}));
|
|
32
|
+
return sortTrackEntries(entries);
|
|
33
|
+
}
|
|
34
|
+
function buildStateFromTracks(tracks) {
|
|
35
|
+
const indexEntries = buildIndexFromTracks(tracks);
|
|
36
|
+
return {
|
|
37
|
+
project: {
|
|
38
|
+
memory_root: "bitacora",
|
|
39
|
+
context_paths: [
|
|
40
|
+
"bitacora/product.md",
|
|
41
|
+
"bitacora/tech-stack.md",
|
|
42
|
+
"bitacora/workflow.md",
|
|
43
|
+
"bitacora/ux-style-guide.md"
|
|
44
|
+
],
|
|
45
|
+
track_count: tracks.length
|
|
46
|
+
},
|
|
47
|
+
active_tracks: sortTrackEntries(indexEntries.filter((entry) => entry.status === "active")),
|
|
48
|
+
blocked_tracks: sortTrackEntries(indexEntries.filter((entry) => entry.status === "blocked")),
|
|
49
|
+
completed_tracks: sortTrackEntries(indexEntries.filter((entry) => entry.status === "completed"))
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createIndexTemplate = createIndexTemplate;
|
|
4
|
+
exports.createProductTemplate = createProductTemplate;
|
|
5
|
+
exports.createTechStackTemplate = createTechStackTemplate;
|
|
6
|
+
exports.createWorkflowTemplate = createWorkflowTemplate;
|
|
7
|
+
exports.createUxStyleGuideTemplate = createUxStyleGuideTemplate;
|
|
8
|
+
exports.createAgentSkillTemplate = createAgentSkillTemplate;
|
|
9
|
+
exports.createTracksRegistryTemplate = createTracksRegistryTemplate;
|
|
10
|
+
exports.createTracksTemplate = createTracksTemplate;
|
|
11
|
+
exports.createTrackTemplate = createTrackTemplate;
|
|
12
|
+
function createIndexTemplate() {
|
|
13
|
+
return `# Bitacora Index
|
|
14
|
+
|
|
15
|
+
Quick map of where to find each kind of project memory.
|
|
16
|
+
|
|
17
|
+
## Read Order (Session Start)
|
|
18
|
+
|
|
19
|
+
1. \`product.md\` (product goals, constraints, and scope)
|
|
20
|
+
2. \`tech-stack.md\` (runtime, dependencies, and technical rules)
|
|
21
|
+
3. \`workflow.md\` (execution process and mandatory handoff rules)
|
|
22
|
+
4. \`ux-style-guide.md\` (visual style tokens and UX constraints)
|
|
23
|
+
5. \`tracks/tracks.md\` (canonical project status and next actions)
|
|
24
|
+
6. \`tracks/TRACK-*/track.md\` (details for active or relevant tracks)
|
|
25
|
+
|
|
26
|
+
## File Index
|
|
27
|
+
|
|
28
|
+
### Root Docs
|
|
29
|
+
- \`product.md\`: what the project is, why it exists, and what is in scope.
|
|
30
|
+
- \`tech-stack.md\`: runtime, dependencies, architecture constraints, and technical rules.
|
|
31
|
+
- \`workflow.md\`: TDD process, quality gates, and mandatory handoff rules.
|
|
32
|
+
- \`ux-style-guide.md\`: colors, typography, spacing, and interaction style rules.
|
|
33
|
+
|
|
34
|
+
### \`tracks/\`
|
|
35
|
+
- \`tracks/tracks.md\`: canonical project status, registry, and handoff summary.
|
|
36
|
+
- \`tracks/tracks-template.md\`: template used when creating new tracks.
|
|
37
|
+
- \`tracks/TRACK-001/track.md\`: first active track created by \`bitacora init\`.
|
|
38
|
+
- \`tracks/TRACK-*/track.md\`: per-track execution details (overview, tasks, decisions, log).
|
|
39
|
+
|
|
40
|
+
Mandatory behavior:
|
|
41
|
+
|
|
42
|
+
- Always read this index at session start.
|
|
43
|
+
- Always update memory before session end.
|
|
44
|
+
- Always keep \`tracks/tracks.md\` aligned with track-level changes.
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
function createProductTemplate() {
|
|
48
|
+
return `# Product
|
|
49
|
+
|
|
50
|
+
## Name
|
|
51
|
+
<project name>
|
|
52
|
+
|
|
53
|
+
## One-liner
|
|
54
|
+
<single sentence describing the product and core value>
|
|
55
|
+
|
|
56
|
+
## Problem
|
|
57
|
+
<what pain/problem this project solves>
|
|
58
|
+
|
|
59
|
+
## Goals
|
|
60
|
+
- <goal 1>
|
|
61
|
+
- <goal 2>
|
|
62
|
+
|
|
63
|
+
## Non-goals
|
|
64
|
+
- <out of scope 1>
|
|
65
|
+
- <out of scope 2>
|
|
66
|
+
|
|
67
|
+
## Success Criteria
|
|
68
|
+
- <measurable outcome 1>
|
|
69
|
+
- <measurable outcome 2>
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
function createTechStackTemplate() {
|
|
73
|
+
return `# Tech Stack
|
|
74
|
+
|
|
75
|
+
## Runtime
|
|
76
|
+
- Node.js <version>
|
|
77
|
+
- TypeScript
|
|
78
|
+
|
|
79
|
+
## Tooling
|
|
80
|
+
- <test runner>
|
|
81
|
+
- <build tool>
|
|
82
|
+
- <dev runtime>
|
|
83
|
+
|
|
84
|
+
## Runtime Dependencies
|
|
85
|
+
- <dependency>: <why it exists>
|
|
86
|
+
|
|
87
|
+
## Core Technical Rules
|
|
88
|
+
- Keep core/domain modules isolated from infra adapters.
|
|
89
|
+
- Keep persistence and external integrations behind explicit contracts.
|
|
90
|
+
- Keep logs and outputs deterministic when possible.
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
function createWorkflowTemplate() {
|
|
94
|
+
return `# Workflow
|
|
95
|
+
|
|
96
|
+
## Development Method
|
|
97
|
+
- Mandatory TDD: RED -> GREEN -> REFACTOR.
|
|
98
|
+
- Prefer small vertical slices with deterministic verification.
|
|
99
|
+
|
|
100
|
+
## Implementation Rules
|
|
101
|
+
- Start from domain/core behavior before adapters when feasible.
|
|
102
|
+
- Keep side effects isolated behind explicit interfaces.
|
|
103
|
+
- Keep tests focused, deterministic, and close to behavior.
|
|
104
|
+
|
|
105
|
+
## Non-negotiable Session Rules
|
|
106
|
+
- Always read \`bitacora/index.md\` at the beginning of every session.
|
|
107
|
+
- Always update \`bitacora/tracks/tracks.md\` after meaningful implementation changes.
|
|
108
|
+
- Always write handoff updates before ending a session.
|
|
109
|
+
|
|
110
|
+
## Handoff Checklist
|
|
111
|
+
- What changed
|
|
112
|
+
- Tests run (exact commands + result)
|
|
113
|
+
- Current TDD phase
|
|
114
|
+
- Blockers or assumptions
|
|
115
|
+
- Single best next action
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
function createUxStyleGuideTemplate() {
|
|
119
|
+
return `# UX Style Guide
|
|
120
|
+
|
|
121
|
+
## Visual Tokens
|
|
122
|
+
- Background: <hex>
|
|
123
|
+
- Surface: <hex>
|
|
124
|
+
- Surface alt: <hex>
|
|
125
|
+
- Text: <hex>
|
|
126
|
+
- Muted text: <hex>
|
|
127
|
+
- Primary accent: <hex>
|
|
128
|
+
- Success: <hex>
|
|
129
|
+
- Warning: <hex>
|
|
130
|
+
- Error: <hex>
|
|
131
|
+
- Border: <hex>
|
|
132
|
+
|
|
133
|
+
## Layout / Spacing
|
|
134
|
+
- Base spacing unit: 4px grid.
|
|
135
|
+
- Panel gap: <value>.
|
|
136
|
+
- Panel padding: <value>.
|
|
137
|
+
- Header padding: <value>.
|
|
138
|
+
|
|
139
|
+
## Borders / Depth
|
|
140
|
+
- Border style: <width and color>.
|
|
141
|
+
- Shadow style: <value>.
|
|
142
|
+
- Effects policy: avoid visual noise; prefer crisp, readable UI.
|
|
143
|
+
|
|
144
|
+
## Typography
|
|
145
|
+
- Font family: <font stack>.
|
|
146
|
+
- Heading style: <size/weight>.
|
|
147
|
+
- Body style: <size/weight>.
|
|
148
|
+
- Label style: <size/weight/casing>.
|
|
149
|
+
|
|
150
|
+
## Components / Interaction Rules
|
|
151
|
+
- Buttons: <primary and secondary behavior>.
|
|
152
|
+
- Inputs: <states and readability constraints>.
|
|
153
|
+
- Data views: <tables/charts/cards style constraints>.
|
|
154
|
+
- Feedback states: define clear success/warning/error treatment.
|
|
155
|
+
|
|
156
|
+
## Motion / Performance
|
|
157
|
+
- Keep animations minimal and purposeful.
|
|
158
|
+
- Prefer readability and responsiveness over decoration.
|
|
159
|
+
- Avoid expensive full-screen effects in frequent update paths.
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
function createAgentSkillTemplate() {
|
|
163
|
+
return `---
|
|
164
|
+
name: bitacora
|
|
165
|
+
description: Keep deterministic project memory in bitacora/ and update it continuously during implementation sessions.
|
|
166
|
+
version: 1.0.0
|
|
167
|
+
type: local
|
|
168
|
+
source: .agents/skills/bitacora/SKILL.md
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# Bitacora Skill
|
|
172
|
+
|
|
173
|
+
Use this skill to keep deterministic project memory in \`bitacora/\` and to update it continuously while implementing changes.
|
|
174
|
+
|
|
175
|
+
Canonical skill file path: \`.agents/skills/bitacora/SKILL.md\`.
|
|
176
|
+
|
|
177
|
+
## Mandatory Session Protocol
|
|
178
|
+
- Always read \`bitacora/index.md\` at session start.
|
|
179
|
+
- Always read \`bitacora/product.md\`, \`bitacora/tech-stack.md\`, \`bitacora/workflow.md\`, and \`bitacora/ux-style-guide.md\` before making code changes.
|
|
180
|
+
- Always read \`bitacora/tracks/tracks.md\` and the active \`bitacora/tracks/TRACK-*/track.md\` files before implementation.
|
|
181
|
+
- Always write memory updates before ending the session.
|
|
182
|
+
|
|
183
|
+
## What To Update During Work
|
|
184
|
+
- Update \`bitacora/tracks/TRACK-*/track.md\` while work progresses (tasks, decisions, logs).
|
|
185
|
+
- Update \`bitacora/tracks/tracks.md\` after meaningful changes, including current status and next action.
|
|
186
|
+
- Update root docs when product/tech/workflow/ux assumptions change.
|
|
187
|
+
|
|
188
|
+
## File Map (Where To Look)
|
|
189
|
+
- \`bitacora/product.md\`: scope, goals, constraints, non-goals.
|
|
190
|
+
- \`bitacora/tech-stack.md\`: runtime, dependencies, architecture rules.
|
|
191
|
+
- \`bitacora/workflow.md\`: TDD rules, handoff checklist, quality gates.
|
|
192
|
+
- \`bitacora/ux-style-guide.md\`: visual tokens, layout, typography, interaction style.
|
|
193
|
+
- \`bitacora/tracks/tracks.md\`: canonical status registry and handoff summary.
|
|
194
|
+
- \`bitacora/tracks/tracks-template.md\`: template for new tracks.
|
|
195
|
+
- \`bitacora/tracks/TRACK-*/track.md\`: per-track execution details.
|
|
196
|
+
|
|
197
|
+
## Manual Bootstrap (No CLI Required)
|
|
198
|
+
If the CLI is unavailable, create this exact structure manually:
|
|
199
|
+
|
|
200
|
+
\`\`\`text
|
|
201
|
+
bitacora/
|
|
202
|
+
index.md
|
|
203
|
+
product.md
|
|
204
|
+
tech-stack.md
|
|
205
|
+
workflow.md
|
|
206
|
+
ux-style-guide.md
|
|
207
|
+
tracks/
|
|
208
|
+
tracks.md
|
|
209
|
+
tracks-template.md
|
|
210
|
+
TRACK-001/
|
|
211
|
+
track.md
|
|
212
|
+
\`\`\`
|
|
213
|
+
|
|
214
|
+
Required initial metadata for \`TRACK-001\` in \`track.md\` frontmatter:
|
|
215
|
+
- \`track_id: TRACK-001\`
|
|
216
|
+
- \`status: active\`
|
|
217
|
+
- \`priority: medium\`
|
|
218
|
+
- \`created_at\` and \`updated_at\` as ISO timestamps
|
|
219
|
+
|
|
220
|
+
Required track sections in \`track.md\`:
|
|
221
|
+
- \`# Overview\`
|
|
222
|
+
- \`# Tasks\`
|
|
223
|
+
- \`# Decisions\`
|
|
224
|
+
- \`# Log\`
|
|
225
|
+
|
|
226
|
+
## End Of Session Checklist
|
|
227
|
+
- Confirm \`bitacora/tracks/tracks.md\` is updated.
|
|
228
|
+
- Confirm active \`TRACK-*/track.md\` files include latest decisions and logs.
|
|
229
|
+
`;
|
|
230
|
+
}
|
|
231
|
+
function createTracksRegistryTemplate(date) {
|
|
232
|
+
return `# Tracks
|
|
233
|
+
|
|
234
|
+
> Canonical project status and handoff registry.
|
|
235
|
+
>
|
|
236
|
+
> Last updated: ${date}
|
|
237
|
+
>
|
|
238
|
+
> Rule: update this file after meaningful implementation changes.
|
|
239
|
+
|
|
240
|
+
## Snapshot
|
|
241
|
+
|
|
242
|
+
- Project: <project-name>
|
|
243
|
+
- Current status: TRACK-001 initialized
|
|
244
|
+
- Active tracks: TRACK-001
|
|
245
|
+
|
|
246
|
+
## Track Registry
|
|
247
|
+
|
|
248
|
+
| ID | Title | Status | Phase | Last Update | Notes |
|
|
249
|
+
| --- | --- | --- | --- | --- | --- |
|
|
250
|
+
| TRACK-001 | Bootstrap memory | active | red | ${date} | Initial track created by \`bitacora init\` |
|
|
251
|
+
|
|
252
|
+
## Session Handoff (Required)
|
|
253
|
+
|
|
254
|
+
After each substantial change, update:
|
|
255
|
+
|
|
256
|
+
1. This file (\`tracks/tracks.md\`).
|
|
257
|
+
2. Affected \`tracks/TRACK-*/track.md\` files.
|
|
258
|
+
|
|
259
|
+
Minimum handoff payload:
|
|
260
|
+
- Track(s) touched
|
|
261
|
+
- Tests run (exact command + result)
|
|
262
|
+
- Current TDD phase
|
|
263
|
+
- Blockers/assumptions
|
|
264
|
+
- Next recommended action
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
function createTracksTemplate() {
|
|
268
|
+
return `# Tracks Template
|
|
269
|
+
|
|
270
|
+
Use this template when creating a new track.
|
|
271
|
+
|
|
272
|
+
## Header
|
|
273
|
+
|
|
274
|
+
- Track ID: \`TRACK-XYZ\`
|
|
275
|
+
- Title: <short title>
|
|
276
|
+
- Status: \`active | blocked | completed | archived\`
|
|
277
|
+
- Priority: \`low | medium | high\`
|
|
278
|
+
- Updated at: \`YYYY-MM-DDTHH:mm:ss.sssZ\`
|
|
279
|
+
|
|
280
|
+
## Overview
|
|
281
|
+
|
|
282
|
+
- Goal: <one sentence>
|
|
283
|
+
- Scope:
|
|
284
|
+
- <item>
|
|
285
|
+
- <item>
|
|
286
|
+
- Out of scope:
|
|
287
|
+
- <item>
|
|
288
|
+
|
|
289
|
+
## Tasks
|
|
290
|
+
|
|
291
|
+
- [ ] RED: <failing test>
|
|
292
|
+
- [ ] GREEN: <minimal implementation>
|
|
293
|
+
- [ ] REFACTOR: <safe cleanup>
|
|
294
|
+
|
|
295
|
+
## Decisions
|
|
296
|
+
|
|
297
|
+
- <YYYY-MM-DD> | <decision and rationale>
|
|
298
|
+
|
|
299
|
+
## Log
|
|
300
|
+
|
|
301
|
+
- <YYYY-MM-DDTHH:mm:ss.sssZ> | <progress update>
|
|
302
|
+
`;
|
|
303
|
+
}
|
|
304
|
+
function createTrackTemplate(trackId, status, priority, isoTimestamp) {
|
|
305
|
+
return `---
|
|
306
|
+
track_id: ${trackId}
|
|
307
|
+
status: ${status}
|
|
308
|
+
priority: ${priority}
|
|
309
|
+
created_at: ${isoTimestamp}
|
|
310
|
+
updated_at: ${isoTimestamp}
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
# Overview
|
|
314
|
+
|
|
315
|
+
- Goal: <one sentence>
|
|
316
|
+
- Scope:
|
|
317
|
+
- <item>
|
|
318
|
+
- <item>
|
|
319
|
+
|
|
320
|
+
# Tasks
|
|
321
|
+
|
|
322
|
+
- [ ] RED: add failing test(s)
|
|
323
|
+
- [ ] GREEN: implement minimal passing behavior
|
|
324
|
+
- [ ] REFACTOR: improve without changing behavior
|
|
325
|
+
|
|
326
|
+
# Decisions
|
|
327
|
+
|
|
328
|
+
- ${isoTimestamp} | track created
|
|
329
|
+
|
|
330
|
+
# Log
|
|
331
|
+
- ${isoTimestamp} | track created
|
|
332
|
+
`;
|
|
333
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateMemory = validateMemory;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const parser_1 = require("./parser");
|
|
10
|
+
const TRACK_DIRECTORY_REGEX = /^TRACK-(\d{3})$/;
|
|
11
|
+
function getTrackDirectories(rootDir) {
|
|
12
|
+
const tracksRoot = node_path_1.default.join(rootDir, "bitacora", "tracks");
|
|
13
|
+
if (!node_fs_1.default.existsSync(tracksRoot)) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return node_fs_1.default
|
|
17
|
+
.readdirSync(tracksRoot, { withFileTypes: true })
|
|
18
|
+
.filter((entry) => entry.isDirectory())
|
|
19
|
+
.map((entry) => entry.name)
|
|
20
|
+
.sort();
|
|
21
|
+
}
|
|
22
|
+
function loadTracks(rootDir, errors) {
|
|
23
|
+
const tracksRoot = node_path_1.default.join(rootDir, "bitacora", "tracks");
|
|
24
|
+
const trackDirs = getTrackDirectories(rootDir);
|
|
25
|
+
const tracks = [];
|
|
26
|
+
for (const directoryName of trackDirs) {
|
|
27
|
+
const filePath = node_path_1.default.join(tracksRoot, directoryName, "track.md");
|
|
28
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
29
|
+
errors.push(`Missing track.md: ${directoryName}`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const markdown = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
33
|
+
try {
|
|
34
|
+
const parsed = (0, parser_1.parseTrackMarkdown)(markdown);
|
|
35
|
+
tracks.push({
|
|
36
|
+
...parsed,
|
|
37
|
+
filePath,
|
|
38
|
+
directoryName
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43
|
+
errors.push(`Invalid track file ${directoryName}: ${message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return tracks;
|
|
47
|
+
}
|
|
48
|
+
function validateTrackNumbering(trackDirectories, errors) {
|
|
49
|
+
const numericTrackIds = trackDirectories
|
|
50
|
+
.map((directoryName) => {
|
|
51
|
+
const match = TRACK_DIRECTORY_REGEX.exec(directoryName);
|
|
52
|
+
if (!match) {
|
|
53
|
+
errors.push(`Invalid track directory name: ${directoryName}`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const numericPart = match[1];
|
|
57
|
+
if (!numericPart) {
|
|
58
|
+
errors.push(`Invalid track directory name: ${directoryName}`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return Number.parseInt(numericPart, 10);
|
|
62
|
+
})
|
|
63
|
+
.filter((trackNumber) => trackNumber !== null)
|
|
64
|
+
.sort((left, right) => left - right);
|
|
65
|
+
for (let index = 0; index < numericTrackIds.length; index += 1) {
|
|
66
|
+
const expected = index + 1;
|
|
67
|
+
const actual = numericTrackIds[index];
|
|
68
|
+
if (actual !== expected) {
|
|
69
|
+
const expectedId = `TRACK-${String(expected).padStart(3, "0")}`;
|
|
70
|
+
const actualId = `TRACK-${String(actual).padStart(3, "0")}`;
|
|
71
|
+
errors.push(`Track numbering gap: expected ${expectedId} but found ${actualId}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function validateDuplicateTrackIds(tracks, errors) {
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
for (const track of tracks) {
|
|
79
|
+
const trackId = track.frontmatter.track_id;
|
|
80
|
+
if (seen.has(trackId)) {
|
|
81
|
+
errors.push(`Duplicate track_id: ${trackId}`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
seen.add(trackId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function parseLogTimestamp(line) {
|
|
88
|
+
const match = /^-\s+([^|]+)\s+\|\s+.+$/.exec(line.trim());
|
|
89
|
+
if (!match) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const timestampRaw = match[1];
|
|
93
|
+
if (!timestampRaw) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const timestamp = Date.parse(timestampRaw.trim());
|
|
97
|
+
return Number.isNaN(timestamp) ? null : timestamp;
|
|
98
|
+
}
|
|
99
|
+
function validateUpdatedAtAgainstLogEntries(tracks, errors) {
|
|
100
|
+
for (const track of tracks) {
|
|
101
|
+
const updatedAt = Date.parse(track.frontmatter.updated_at);
|
|
102
|
+
if (Number.isNaN(updatedAt)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const latestLogTimestamp = track.sections.log
|
|
106
|
+
.split("\n")
|
|
107
|
+
.map((line) => parseLogTimestamp(line))
|
|
108
|
+
.filter((timestamp) => timestamp !== null)
|
|
109
|
+
.reduce((latest, current) => (latest === null || current > latest ? current : latest), null);
|
|
110
|
+
if (latestLogTimestamp !== null && updatedAt < latestLogTimestamp) {
|
|
111
|
+
errors.push(`updated_at is older than latest log entry for ${track.frontmatter.track_id}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function validateMemory(rootDir) {
|
|
116
|
+
const errors = [];
|
|
117
|
+
const trackDirectories = getTrackDirectories(rootDir);
|
|
118
|
+
validateTrackNumbering(trackDirectories, errors);
|
|
119
|
+
const tracks = loadTracks(rootDir, errors);
|
|
120
|
+
validateDuplicateTrackIds(tracks, errors);
|
|
121
|
+
validateUpdatedAtAgainstLogEntries(tracks, errors);
|
|
122
|
+
errors.sort((left, right) => left.localeCompare(right));
|
|
123
|
+
return {
|
|
124
|
+
ok: errors.length === 0,
|
|
125
|
+
errors,
|
|
126
|
+
tracks
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateMemory = exports.buildStateFromTracks = exports.buildIndexFromTracks = exports.parseTrackMarkdown = exports.boot = void 0;
|
|
4
|
+
var boot_1 = require("./core/boot");
|
|
5
|
+
Object.defineProperty(exports, "boot", { enumerable: true, get: function () { return boot_1.boot; } });
|
|
6
|
+
var parser_1 = require("./core/parser");
|
|
7
|
+
Object.defineProperty(exports, "parseTrackMarkdown", { enumerable: true, get: function () { return parser_1.parseTrackMarkdown; } });
|
|
8
|
+
var state_builder_1 = require("./core/state-builder");
|
|
9
|
+
Object.defineProperty(exports, "buildIndexFromTracks", { enumerable: true, get: function () { return state_builder_1.buildIndexFromTracks; } });
|
|
10
|
+
Object.defineProperty(exports, "buildStateFromTracks", { enumerable: true, get: function () { return state_builder_1.buildStateFromTracks; } });
|
|
11
|
+
var validator_1 = require("./core/validator");
|
|
12
|
+
Object.defineProperty(exports, "validateMemory", { enumerable: true, get: function () { return validator_1.validateMemory; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bitacora-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Deterministic agent-oriented project memory system. Provides structured Markdown-based long-term memory, strict validation, state reconstruction, and an enforcement CLI with agent skill integration to guide and limit agent behavior.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/N0M4D-D3V/bitacora.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/N0M4D-D3V/bitacora#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/N0M4D-D3V/bitacora/issues"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"bitacora",
|
|
15
|
+
"deterministic",
|
|
16
|
+
"memory",
|
|
17
|
+
"cli",
|
|
18
|
+
"agent"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "SK3L3TØR",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist/src/**/*",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"type": "commonjs",
|
|
28
|
+
"main": "dist/src/index.js",
|
|
29
|
+
"bin": {
|
|
30
|
+
"bitacora": "dist/src/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json",
|
|
40
|
+
"prepack": "npm run build",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:unit": "vitest run",
|
|
45
|
+
"start": "tsx src/cli.ts"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"commander": "^12.1.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.13.10",
|
|
52
|
+
"tsx": "^4.19.3",
|
|
53
|
+
"typescript": "^5.8.2",
|
|
54
|
+
"vitest": "^3.0.9"
|
|
55
|
+
}
|
|
56
|
+
}
|