mdkg 0.0.3 → 0.0.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 +171 -169
- package/dist/cli.js +980 -624
- package/dist/commands/checkpoint.js +17 -6
- package/dist/commands/event.js +46 -0
- package/dist/commands/event_support.js +146 -0
- package/dist/commands/format.js +6 -7
- package/dist/commands/index.js +10 -4
- package/dist/commands/init.js +198 -16
- package/dist/commands/list.js +18 -1
- package/dist/commands/new.js +30 -5
- package/dist/commands/pack.js +194 -2
- package/dist/commands/query_output.js +84 -0
- package/dist/commands/search.js +22 -5
- package/dist/commands/show.js +26 -11
- package/dist/commands/skill.js +359 -0
- package/dist/commands/skill_support.js +121 -0
- package/dist/commands/task.js +270 -0
- package/dist/commands/validate.js +104 -7
- package/dist/graph/edges.js +2 -2
- package/dist/graph/frontmatter.js +1 -0
- package/dist/graph/indexer.js +21 -0
- package/dist/graph/node.js +20 -4
- package/dist/graph/skills_index_cache.js +94 -0
- package/dist/graph/skills_indexer.js +160 -0
- package/dist/init/core/rule-1-mdkg-conventions.md +9 -2
- package/dist/init/core/rule-3-cli-contract.md +73 -14
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +9 -3
- package/dist/init/core/rule-6-templates-and-schemas.md +6 -2
- package/dist/init/skills/SKILL.md.example +41 -0
- package/dist/init/templates/default/bug.md +1 -0
- package/dist/init/templates/default/chk.md +1 -0
- package/dist/init/templates/default/epic.md +1 -0
- package/dist/init/templates/default/feat.md +1 -0
- package/dist/init/templates/default/task.md +1 -0
- package/dist/init/templates/default/test.md +1 -0
- package/dist/pack/export_md.js +6 -0
- package/dist/pack/export_xml.js +6 -0
- package/dist/pack/pack.js +35 -0
- package/dist/util/argparse.js +25 -0
- package/dist/util/filter.js +18 -0
- package/dist/util/id.js +23 -0
- package/package.json +5 -2
|
@@ -11,8 +11,8 @@ const index_cache_1 = require("../graph/index_cache");
|
|
|
11
11
|
const loader_1 = require("../templates/loader");
|
|
12
12
|
const date_1 = require("../util/date");
|
|
13
13
|
const errors_1 = require("../util/errors");
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const id_1 = require("../util/id");
|
|
15
|
+
const event_support_1 = require("./event_support");
|
|
16
16
|
function parseCsvList(raw) {
|
|
17
17
|
if (!raw) {
|
|
18
18
|
return [];
|
|
@@ -24,14 +24,14 @@ function parseCsvList(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
function normalizeId(value, key) {
|
|
26
26
|
const normalized = value.toLowerCase();
|
|
27
|
-
if (!
|
|
28
|
-
throw new errors_1.UsageError(`${key} entries must match <prefix>-<number
|
|
27
|
+
if (!(0, id_1.isCanonicalId)(normalized)) {
|
|
28
|
+
throw new errors_1.UsageError(`${key} entries must match <prefix>-<number> or reserved id: ${value}`);
|
|
29
29
|
}
|
|
30
30
|
return normalized;
|
|
31
31
|
}
|
|
32
32
|
function normalizeIdRef(value, key) {
|
|
33
33
|
const normalized = value.toLowerCase();
|
|
34
|
-
if (!
|
|
34
|
+
if (!(0, id_1.isCanonicalIdRef)(normalized)) {
|
|
35
35
|
throw new errors_1.UsageError(`${key} entries must match <id> or <ws>:<id>: ${value}`);
|
|
36
36
|
}
|
|
37
37
|
return normalized;
|
|
@@ -115,7 +115,8 @@ function runCheckpointNewCommand(options) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
const scope = parseCsvList(options.scope).map((value) => normalizeId(value, "--scope"));
|
|
118
|
-
const
|
|
118
|
+
const now = options.now ?? new Date();
|
|
119
|
+
const today = (0, date_1.formatDate)(now);
|
|
119
120
|
const template = (0, loader_1.loadTemplate)(options.root, config, "checkpoint", options.template);
|
|
120
121
|
const content = (0, loader_1.renderTemplate)(template, {
|
|
121
122
|
id,
|
|
@@ -129,5 +130,15 @@ function runCheckpointNewCommand(options) {
|
|
|
129
130
|
});
|
|
130
131
|
fs_1.default.mkdirSync(workDir, { recursive: true });
|
|
131
132
|
fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
133
|
+
(0, event_support_1.appendAutomaticEvent)({
|
|
134
|
+
root: options.root,
|
|
135
|
+
ws,
|
|
136
|
+
kind: "CHECKPOINT_CREATED",
|
|
137
|
+
status: "ok",
|
|
138
|
+
refs: [id],
|
|
139
|
+
notes: options.note ?? `checkpoint created via mdkg checkpoint new`,
|
|
140
|
+
runId: options.runId,
|
|
141
|
+
now,
|
|
142
|
+
});
|
|
132
143
|
console.log(`checkpoint created: ${ws}:${id} (${path_1.default.relative(options.root, filePath)})`);
|
|
133
144
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runEventEnableCommand = runEventEnableCommand;
|
|
4
|
+
exports.runEventAppendCommand = runEventAppendCommand;
|
|
5
|
+
const event_support_1 = require("./event_support");
|
|
6
|
+
const errors_1 = require("../util/errors");
|
|
7
|
+
function runEventEnableCommand(options) {
|
|
8
|
+
const result = (0, event_support_1.ensureEventsEnabled)(options);
|
|
9
|
+
const createdLabel = result.created ? "created" : "already present";
|
|
10
|
+
const ignoreLabel = result.gitignoreUpdated ? "updated .gitignore" : "left .gitignore unchanged";
|
|
11
|
+
console.log(`event logging enabled: ${result.ws} (${createdLabel}; ${ignoreLabel})`);
|
|
12
|
+
}
|
|
13
|
+
function normalizeEventStatus(value) {
|
|
14
|
+
const normalized = value.trim().toLowerCase();
|
|
15
|
+
if (normalized === "ok" ||
|
|
16
|
+
normalized === "error" ||
|
|
17
|
+
normalized === "retry" ||
|
|
18
|
+
normalized === "skipped") {
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
throw new errors_1.UsageError("--status must be one of ok, error, retry, skipped");
|
|
22
|
+
}
|
|
23
|
+
function runEventAppendCommand(options) {
|
|
24
|
+
const kind = options.kind.trim();
|
|
25
|
+
if (!kind) {
|
|
26
|
+
throw new errors_1.UsageError("--kind is required");
|
|
27
|
+
}
|
|
28
|
+
const refs = (0, event_support_1.normalizeEventRefList)(options.refs);
|
|
29
|
+
if (refs.length === 0) {
|
|
30
|
+
throw new errors_1.UsageError("--refs requires at least one id or qid");
|
|
31
|
+
}
|
|
32
|
+
const record = (0, event_support_1.appendEvent)({
|
|
33
|
+
root: options.root,
|
|
34
|
+
ws: options.ws,
|
|
35
|
+
kind,
|
|
36
|
+
status: normalizeEventStatus(options.status),
|
|
37
|
+
refs,
|
|
38
|
+
artifacts: (0, event_support_1.normalizeEventStringList)(options.artifacts),
|
|
39
|
+
notes: options.notes,
|
|
40
|
+
runId: options.runId,
|
|
41
|
+
agent: options.agent,
|
|
42
|
+
skill: options.skill,
|
|
43
|
+
tool: options.tool,
|
|
44
|
+
});
|
|
45
|
+
console.log(`event appended: ${record.workspace}:${record.kind} (${record.run_id})`);
|
|
46
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
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.normalizeWorkspaceForEvents = normalizeWorkspaceForEvents;
|
|
7
|
+
exports.resolveEventsPath = resolveEventsPath;
|
|
8
|
+
exports.createLocalRunId = createLocalRunId;
|
|
9
|
+
exports.isEventLoggingEnabled = isEventLoggingEnabled;
|
|
10
|
+
exports.ensureEventsEnabled = ensureEventsEnabled;
|
|
11
|
+
exports.normalizeEventRefList = normalizeEventRefList;
|
|
12
|
+
exports.normalizeEventStringList = normalizeEventStringList;
|
|
13
|
+
exports.appendEvent = appendEvent;
|
|
14
|
+
exports.appendAutomaticEvent = appendAutomaticEvent;
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const config_1 = require("../core/config");
|
|
18
|
+
const errors_1 = require("../util/errors");
|
|
19
|
+
function appendIgnoreEntries(filePath, entries) {
|
|
20
|
+
const normalizedEntries = entries.map((entry) => entry.trim()).filter(Boolean);
|
|
21
|
+
if (normalizedEntries.length === 0) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
let existing = "";
|
|
25
|
+
if (fs_1.default.existsSync(filePath)) {
|
|
26
|
+
existing = fs_1.default.readFileSync(filePath, "utf8");
|
|
27
|
+
}
|
|
28
|
+
const existingLines = new Set(existing
|
|
29
|
+
.split(/\r?\n/)
|
|
30
|
+
.map((line) => line.trim())
|
|
31
|
+
.filter(Boolean));
|
|
32
|
+
const additions = normalizedEntries.filter((entry) => !existingLines.has(entry));
|
|
33
|
+
if (additions.length === 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const next = existing.length === 0 ? `${additions.join("\n")}\n` : `${existing.replace(/\s*$/, "\n")}${additions.join("\n")}\n`;
|
|
37
|
+
fs_1.default.writeFileSync(filePath, next, "utf8");
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function normalizeWorkspaceForEvents(config, raw) {
|
|
41
|
+
const normalized = (raw ?? "root").toLowerCase();
|
|
42
|
+
if (normalized === "all") {
|
|
43
|
+
throw new errors_1.UsageError("--ws all is not valid here");
|
|
44
|
+
}
|
|
45
|
+
if (!config.workspaces[normalized]) {
|
|
46
|
+
throw new errors_1.NotFoundError(`workspace not found: ${normalized}`);
|
|
47
|
+
}
|
|
48
|
+
return normalized;
|
|
49
|
+
}
|
|
50
|
+
function resolveEventsPath(root, config, ws = "root") {
|
|
51
|
+
const entry = config.workspaces[ws];
|
|
52
|
+
if (!entry) {
|
|
53
|
+
throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
|
|
54
|
+
}
|
|
55
|
+
return path_1.default.resolve(root, entry.path, entry.mdkg_dir, "work", "events", "events.jsonl");
|
|
56
|
+
}
|
|
57
|
+
function formatRunIdTimestamp(now) {
|
|
58
|
+
const iso = now.toISOString().replace(/[-:]/g, "").replace(/\./g, "").replace("Z", "Z");
|
|
59
|
+
return iso;
|
|
60
|
+
}
|
|
61
|
+
function createLocalRunId(kind, now = new Date()) {
|
|
62
|
+
const normalizedKind = kind.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
63
|
+
return `run_mdkg_cli_${normalizedKind}_${formatRunIdTimestamp(now)}`;
|
|
64
|
+
}
|
|
65
|
+
function isEventLoggingEnabled(root, config, ws) {
|
|
66
|
+
const normalizedWs = normalizeWorkspaceForEvents(config, ws);
|
|
67
|
+
return fs_1.default.existsSync(resolveEventsPath(root, config, normalizedWs));
|
|
68
|
+
}
|
|
69
|
+
function ensureEventsEnabled(options) {
|
|
70
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
71
|
+
const ws = normalizeWorkspaceForEvents(config, options.ws);
|
|
72
|
+
const eventsPath = resolveEventsPath(options.root, config, ws);
|
|
73
|
+
const updateGitignore = options.updateGitignore !== false;
|
|
74
|
+
let created = false;
|
|
75
|
+
if (!fs_1.default.existsSync(eventsPath)) {
|
|
76
|
+
fs_1.default.mkdirSync(path_1.default.dirname(eventsPath), { recursive: true });
|
|
77
|
+
fs_1.default.writeFileSync(eventsPath, "", "utf8");
|
|
78
|
+
created = true;
|
|
79
|
+
}
|
|
80
|
+
let gitignoreUpdated = false;
|
|
81
|
+
if (updateGitignore) {
|
|
82
|
+
gitignoreUpdated = appendIgnoreEntries(path_1.default.join(options.root, ".gitignore"), [
|
|
83
|
+
".mdkg/work/events/*.jsonl",
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
return { ws, eventsPath, created, gitignoreUpdated };
|
|
87
|
+
}
|
|
88
|
+
function normalizeIdOrIdRef(value) {
|
|
89
|
+
return value.trim().toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
function normalizeEventRefList(raw) {
|
|
92
|
+
if (!raw) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return raw
|
|
96
|
+
.split(",")
|
|
97
|
+
.map((value) => normalizeIdOrIdRef(value))
|
|
98
|
+
.filter(Boolean);
|
|
99
|
+
}
|
|
100
|
+
function normalizeEventStringList(raw) {
|
|
101
|
+
if (!raw) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
return raw
|
|
105
|
+
.split(",")
|
|
106
|
+
.map((value) => value.trim())
|
|
107
|
+
.filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
function buildEventRecord(config, options) {
|
|
110
|
+
const now = options.now ?? new Date();
|
|
111
|
+
const ws = normalizeWorkspaceForEvents(config, options.ws);
|
|
112
|
+
return {
|
|
113
|
+
ts: now.toISOString(),
|
|
114
|
+
run_id: options.runId?.trim() || createLocalRunId(options.kind, now),
|
|
115
|
+
workspace: ws,
|
|
116
|
+
agent: options.agent?.trim() || "mdkg-cli",
|
|
117
|
+
kind: options.kind,
|
|
118
|
+
status: options.status,
|
|
119
|
+
refs: options.refs.map(normalizeIdOrIdRef).filter(Boolean),
|
|
120
|
+
artifacts: (options.artifacts ?? []).map((value) => value.trim()).filter(Boolean),
|
|
121
|
+
notes: options.notes?.trim() ?? "",
|
|
122
|
+
...(options.skill ? { skill: options.skill.trim().toLowerCase() } : {}),
|
|
123
|
+
...(options.tool ? { tool: options.tool.trim() } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function appendEvent(options) {
|
|
127
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
128
|
+
const record = buildEventRecord(config, options);
|
|
129
|
+
if (record.refs.length === 0) {
|
|
130
|
+
throw new errors_1.UsageError("--refs requires at least one id or qid");
|
|
131
|
+
}
|
|
132
|
+
const eventsPath = resolveEventsPath(options.root, config, record.workspace);
|
|
133
|
+
if (!fs_1.default.existsSync(eventsPath)) {
|
|
134
|
+
throw new errors_1.NotFoundError(`event logging is not enabled for workspace ${record.workspace}; run \`mdkg event enable --ws ${record.workspace}\``);
|
|
135
|
+
}
|
|
136
|
+
fs_1.default.appendFileSync(eventsPath, `${JSON.stringify(record)}\n`, "utf8");
|
|
137
|
+
return record;
|
|
138
|
+
}
|
|
139
|
+
function appendAutomaticEvent(options) {
|
|
140
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
141
|
+
const ws = normalizeWorkspaceForEvents(config, options.ws);
|
|
142
|
+
if (!isEventLoggingEnabled(options.root, config, ws)) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
return appendEvent({ ...options, ws });
|
|
146
|
+
}
|
package/dist/commands/format.js
CHANGED
|
@@ -13,8 +13,7 @@ const node_1 = require("../graph/node");
|
|
|
13
13
|
const workspace_files_1 = require("../graph/workspace_files");
|
|
14
14
|
const errors_1 = require("../util/errors");
|
|
15
15
|
const date_1 = require("../util/date");
|
|
16
|
-
const
|
|
17
|
-
const ID_REF_RE = /^([a-z][a-z0-9_]*:)?[a-z]+-[0-9]+$/;
|
|
16
|
+
const id_1 = require("../util/id");
|
|
18
17
|
const DEC_ID_RE = /^dec-[0-9]+$/;
|
|
19
18
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
20
19
|
const ID_LIST_KEYS = new Set(["refs", "scope"]);
|
|
@@ -22,7 +21,7 @@ const ID_REF_LIST_KEYS = new Set(["relates", "blocked_by", "blocks"]);
|
|
|
22
21
|
const ID_REF_SCALAR_KEYS = new Set(["epic", "parent", "prev", "next"]);
|
|
23
22
|
const PRESERVE_CASE_LIST_KEYS = new Set(["links", "artifacts"]);
|
|
24
23
|
function isValidId(value) {
|
|
25
|
-
return
|
|
24
|
+
return (0, id_1.isCanonicalId)(value);
|
|
26
25
|
}
|
|
27
26
|
function isCoreListFile(filePath) {
|
|
28
27
|
return path_1.default.basename(filePath) === "core.md" && path_1.default.basename(path_1.default.dirname(filePath)) === "core";
|
|
@@ -59,9 +58,9 @@ function normalizeList(values, key, errors, filePath) {
|
|
|
59
58
|
continue;
|
|
60
59
|
}
|
|
61
60
|
if (ID_LIST_KEYS.has(key) && !isValidId(entry)) {
|
|
62
|
-
errors.push(`${filePath}: ${key} entries must match <prefix>-<number
|
|
61
|
+
errors.push(`${filePath}: ${key} entries must match <prefix>-<number> or reserved id`);
|
|
63
62
|
}
|
|
64
|
-
if (ID_REF_LIST_KEYS.has(key) && !
|
|
63
|
+
if (ID_REF_LIST_KEYS.has(key) && !(0, id_1.isCanonicalIdRef)(entry)) {
|
|
65
64
|
errors.push(`${filePath}: ${key} entries must be valid id references`);
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -69,7 +68,7 @@ function normalizeList(values, key, errors, filePath) {
|
|
|
69
68
|
}
|
|
70
69
|
function normalizeIdRef(value, key, errors, filePath) {
|
|
71
70
|
const normalized = normalizeScalar(value).toLowerCase();
|
|
72
|
-
if (!
|
|
71
|
+
if (!(0, id_1.isCanonicalIdRef)(normalized)) {
|
|
73
72
|
errors.push(`${filePath}: ${key} must be a valid id reference`);
|
|
74
73
|
}
|
|
75
74
|
return normalized;
|
|
@@ -124,7 +123,7 @@ function normalizeFrontmatter(frontmatter, schema, type, workStatusEnum, priorit
|
|
|
124
123
|
else {
|
|
125
124
|
const normalizedId = idValue.toLowerCase();
|
|
126
125
|
if (!isValidId(normalizedId)) {
|
|
127
|
-
errors.push(`${filePath}: id must match <prefix>-<number
|
|
126
|
+
errors.push(`${filePath}: id must match <prefix>-<number> or reserved id`);
|
|
128
127
|
}
|
|
129
128
|
normalized.id = normalizedId;
|
|
130
129
|
}
|
package/dist/commands/index.js
CHANGED
|
@@ -8,10 +8,16 @@ const path_1 = __importDefault(require("path"));
|
|
|
8
8
|
const config_1 = require("../core/config");
|
|
9
9
|
const indexer_1 = require("../graph/indexer");
|
|
10
10
|
const index_cache_1 = require("../graph/index_cache");
|
|
11
|
+
const skills_index_cache_1 = require("../graph/skills_index_cache");
|
|
12
|
+
const skills_indexer_1 = require("../graph/skills_indexer");
|
|
11
13
|
function runIndexCommand(options) {
|
|
12
14
|
const config = (0, config_1.loadConfig)(options.root);
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const nodeIndex = (0, indexer_1.buildIndex)(options.root, config, { tolerant: options.tolerant });
|
|
16
|
+
const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(options.root, config);
|
|
17
|
+
const nodesOutputPath = path_1.default.resolve(options.root, config.index.global_index_path);
|
|
18
|
+
const skillsOutputPath = (0, skills_indexer_1.resolveSkillsIndexPath)(options.root);
|
|
19
|
+
(0, index_cache_1.writeIndex)(nodesOutputPath, nodeIndex);
|
|
20
|
+
(0, skills_index_cache_1.writeSkillsIndex)(skillsOutputPath, skillsIndex);
|
|
21
|
+
console.log(`index written: ${path_1.default.relative(options.root, nodesOutputPath)}`);
|
|
22
|
+
console.log(`skills index written: ${path_1.default.relative(options.root, skillsOutputPath)}`);
|
|
17
23
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -7,7 +7,17 @@ exports.runInitCommand = runInitCommand;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const errors_1 = require("../util/errors");
|
|
10
|
+
const date_1 = require("../util/date");
|
|
11
|
+
const skill_support_1 = require("./skill_support");
|
|
10
12
|
const DEFAULT_SEED_SUBDIR = path_1.default.resolve(__dirname, "..", "init");
|
|
13
|
+
const SOUL_PIN_ID = "rule-soul";
|
|
14
|
+
const HUMAN_PIN_ID = "rule-human";
|
|
15
|
+
const DEFAULT_CORE_LIST_HEADER = [
|
|
16
|
+
"# mdkg verbose core list",
|
|
17
|
+
"",
|
|
18
|
+
"# One node ID per line. Lines starting with # are comments.",
|
|
19
|
+
"# This list is included by `mdkg pack --verbose`.",
|
|
20
|
+
];
|
|
11
21
|
function listFiles(dir) {
|
|
12
22
|
if (!fs_1.default.existsSync(dir)) {
|
|
13
23
|
return [];
|
|
@@ -59,11 +69,160 @@ function appendIgnoreEntries(filePath, entries) {
|
|
|
59
69
|
fs_1.default.writeFileSync(filePath, updated, "utf8");
|
|
60
70
|
return true;
|
|
61
71
|
}
|
|
72
|
+
function writeFileIfMissing(filePath, content, force, stats) {
|
|
73
|
+
if (fs_1.default.existsSync(filePath) && !force) {
|
|
74
|
+
stats.skipped += 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
78
|
+
fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
79
|
+
stats.created += 1;
|
|
80
|
+
}
|
|
81
|
+
function soulTemplate(created) {
|
|
82
|
+
return [
|
|
83
|
+
"---",
|
|
84
|
+
`id: ${SOUL_PIN_ID}`,
|
|
85
|
+
"type: rule",
|
|
86
|
+
"title: agent soul and execution contract",
|
|
87
|
+
"tags: [omni, agent, constraints]",
|
|
88
|
+
"owners: []",
|
|
89
|
+
"links: []",
|
|
90
|
+
"artifacts: []",
|
|
91
|
+
"relates: []",
|
|
92
|
+
"refs: []",
|
|
93
|
+
"aliases: [soul]",
|
|
94
|
+
`created: ${created}`,
|
|
95
|
+
`updated: ${created}`,
|
|
96
|
+
"---",
|
|
97
|
+
"",
|
|
98
|
+
"# Purpose",
|
|
99
|
+
"",
|
|
100
|
+
"Define the canonical agent execution boundaries and memory contract for this repository.",
|
|
101
|
+
"",
|
|
102
|
+
"# Scope",
|
|
103
|
+
"",
|
|
104
|
+
"Applies to all orchestrators and coding-agent executions using mdkg in this repo.",
|
|
105
|
+
"",
|
|
106
|
+
"# Requirements",
|
|
107
|
+
"",
|
|
108
|
+
"- Ask for approval before destructive operations or policy-sensitive actions.",
|
|
109
|
+
"- Prefer deterministic mdkg packs over ad-hoc context assembly.",
|
|
110
|
+
"- Follow single-writer commit discipline and event-driven batching.",
|
|
111
|
+
"- Never place secrets in mdkg docs or generated packs.",
|
|
112
|
+
"",
|
|
113
|
+
"# Notes",
|
|
114
|
+
"",
|
|
115
|
+
"Customize this file to encode repo-specific constraints, approval boundaries, and memory cadence.",
|
|
116
|
+
"",
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
function humanTemplate(created) {
|
|
120
|
+
return [
|
|
121
|
+
"---",
|
|
122
|
+
`id: ${HUMAN_PIN_ID}`,
|
|
123
|
+
"type: rule",
|
|
124
|
+
"title: human working profile and collaboration preferences",
|
|
125
|
+
"tags: [human, collaboration, preferences]",
|
|
126
|
+
"owners: []",
|
|
127
|
+
"links: []",
|
|
128
|
+
"artifacts: []",
|
|
129
|
+
"relates: []",
|
|
130
|
+
"refs: []",
|
|
131
|
+
"aliases: [human]",
|
|
132
|
+
`created: ${created}`,
|
|
133
|
+
`updated: ${created}`,
|
|
134
|
+
"---",
|
|
135
|
+
"",
|
|
136
|
+
"# Purpose",
|
|
137
|
+
"",
|
|
138
|
+
"Capture stable collaboration preferences and boundaries so agents can work with less ambiguity.",
|
|
139
|
+
"",
|
|
140
|
+
"# Scope",
|
|
141
|
+
"",
|
|
142
|
+
"Applies to planning, implementation, and review interactions in this repository.",
|
|
143
|
+
"",
|
|
144
|
+
"# Requirements",
|
|
145
|
+
"",
|
|
146
|
+
"- Keep top goals, boundaries, and style preferences current.",
|
|
147
|
+
"- Include ask-before-doing constraints for risky or high-impact actions.",
|
|
148
|
+
"- Record preferred environment assumptions and validation commands.",
|
|
149
|
+
"",
|
|
150
|
+
"# Notes",
|
|
151
|
+
"",
|
|
152
|
+
"Suggested prompts:",
|
|
153
|
+
"- What are your top 3 goals in this repo right now?",
|
|
154
|
+
"- What should never happen without confirmation?",
|
|
155
|
+
"- What coding/review style should the agent prefer?",
|
|
156
|
+
"- What OS/runtime/test commands should be assumed?",
|
|
157
|
+
"",
|
|
158
|
+
].join("\n");
|
|
159
|
+
}
|
|
160
|
+
function seededInitEvent(nowIso) {
|
|
161
|
+
const event = {
|
|
162
|
+
ts: nowIso,
|
|
163
|
+
run_id: `init-${nowIso.replace(/[^0-9]/g, "").slice(0, 14)}`,
|
|
164
|
+
workspace: "root",
|
|
165
|
+
agent: "mdkg",
|
|
166
|
+
kind: "RUN_STARTED",
|
|
167
|
+
status: "ok",
|
|
168
|
+
refs: ["edd-4"],
|
|
169
|
+
artifacts: [],
|
|
170
|
+
notes: "init omni scaffold target initialized",
|
|
171
|
+
redacted: true,
|
|
172
|
+
};
|
|
173
|
+
return `${JSON.stringify(event)}\n`;
|
|
174
|
+
}
|
|
175
|
+
function parseCoreList(raw) {
|
|
176
|
+
const lines = raw.split(/\r?\n/);
|
|
177
|
+
const header = [];
|
|
178
|
+
const ids = [];
|
|
179
|
+
let seenFirstId = false;
|
|
180
|
+
for (const line of lines) {
|
|
181
|
+
const trimmed = line.trim();
|
|
182
|
+
const isComment = trimmed.startsWith("#");
|
|
183
|
+
if (!seenFirstId && (trimmed.length === 0 || isComment)) {
|
|
184
|
+
header.push(line);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (trimmed.length === 0 || isComment) {
|
|
188
|
+
seenFirstId = true;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
seenFirstId = true;
|
|
192
|
+
ids.push(trimmed.toLowerCase());
|
|
193
|
+
}
|
|
194
|
+
return { header, ids };
|
|
195
|
+
}
|
|
196
|
+
function ensureCorePins(coreListPath, requiredPins) {
|
|
197
|
+
const raw = fs_1.default.existsSync(coreListPath) ? fs_1.default.readFileSync(coreListPath, "utf8") : "";
|
|
198
|
+
const { header, ids } = parseCoreList(raw);
|
|
199
|
+
const seen = new Set();
|
|
200
|
+
const dedupedExisting = [];
|
|
201
|
+
for (const id of ids) {
|
|
202
|
+
if (seen.has(id)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
seen.add(id);
|
|
206
|
+
dedupedExisting.push(id);
|
|
207
|
+
}
|
|
208
|
+
const required = requiredPins.map((value) => value.toLowerCase());
|
|
209
|
+
const filteredExisting = dedupedExisting.filter((value) => !required.includes(value));
|
|
210
|
+
const finalIds = [...required, ...filteredExisting];
|
|
211
|
+
const headerLines = header.length > 0 ? header : DEFAULT_CORE_LIST_HEADER;
|
|
212
|
+
const normalizedHeader = headerLines.slice();
|
|
213
|
+
while (normalizedHeader.length > 0 && normalizedHeader[normalizedHeader.length - 1].trim() === "") {
|
|
214
|
+
normalizedHeader.pop();
|
|
215
|
+
}
|
|
216
|
+
const output = [...normalizedHeader, "", ...finalIds, ""].join("\n");
|
|
217
|
+
fs_1.default.mkdirSync(path_1.default.dirname(coreListPath), { recursive: true });
|
|
218
|
+
fs_1.default.writeFileSync(coreListPath, output, "utf8");
|
|
219
|
+
}
|
|
62
220
|
function runInitCommand(options) {
|
|
63
221
|
const root = path_1.default.resolve(options.root);
|
|
64
222
|
const seedRoot = options.seedRoot ? path_1.default.resolve(options.seedRoot) : DEFAULT_SEED_SUBDIR;
|
|
65
223
|
const createAgents = Boolean(options.createAgents || options.createLlm);
|
|
66
224
|
const createClaude = Boolean(options.createClaude || options.createLlm);
|
|
225
|
+
const force = Boolean(options.force);
|
|
67
226
|
const seedConfig = path_1.default.join(seedRoot, "config.json");
|
|
68
227
|
const seedCore = path_1.default.join(seedRoot, "core");
|
|
69
228
|
const seedTemplates = path_1.default.join(seedRoot, "templates");
|
|
@@ -87,34 +246,57 @@ function runInitCommand(options) {
|
|
|
87
246
|
fs_1.default.mkdirSync(path_1.default.join(mdkgDir, "work"), { recursive: true });
|
|
88
247
|
fs_1.default.mkdirSync(path_1.default.join(mdkgDir, "design"), { recursive: true });
|
|
89
248
|
const stats = { created: 0, skipped: 0 };
|
|
90
|
-
copySeedFile(seedConfig, path_1.default.join(mdkgDir, "config.json"),
|
|
91
|
-
copySeedFile(seedReadme, path_1.default.join(mdkgDir, "README.md"),
|
|
92
|
-
copySeedDir(seedCore, path_1.default.join(mdkgDir, "core"),
|
|
93
|
-
copySeedDir(seedTemplates, path_1.default.join(mdkgDir, "templates"),
|
|
249
|
+
copySeedFile(seedConfig, path_1.default.join(mdkgDir, "config.json"), force, stats);
|
|
250
|
+
copySeedFile(seedReadme, path_1.default.join(mdkgDir, "README.md"), force, stats);
|
|
251
|
+
copySeedDir(seedCore, path_1.default.join(mdkgDir, "core"), force, stats);
|
|
252
|
+
copySeedDir(seedTemplates, path_1.default.join(mdkgDir, "templates"), force, stats);
|
|
94
253
|
if (createAgents) {
|
|
95
|
-
copySeedFile(seedAgents, path_1.default.join(root, "AGENTS.md"),
|
|
254
|
+
copySeedFile(seedAgents, path_1.default.join(root, "AGENTS.md"), force, stats);
|
|
96
255
|
}
|
|
97
256
|
if (createClaude) {
|
|
98
|
-
copySeedFile(seedClaude, path_1.default.join(root, "CLAUDE.md"),
|
|
257
|
+
copySeedFile(seedClaude, path_1.default.join(root, "CLAUDE.md"), force, stats);
|
|
99
258
|
}
|
|
100
|
-
if (options.
|
|
101
|
-
|
|
259
|
+
if (options.omni) {
|
|
260
|
+
const today = (0, date_1.formatDate)(new Date());
|
|
261
|
+
const soulPath = path_1.default.join(mdkgDir, "core", "SOUL.md");
|
|
262
|
+
const humanPath = path_1.default.join(mdkgDir, "core", "HUMAN.md");
|
|
263
|
+
const skillsDir = path_1.default.join(mdkgDir, "skills");
|
|
264
|
+
const registryPath = path_1.default.join(skillsDir, "registry.md");
|
|
265
|
+
const eventsDir = path_1.default.join(mdkgDir, "work", "events");
|
|
266
|
+
const eventsPath = path_1.default.join(eventsDir, "events.jsonl");
|
|
267
|
+
fs_1.default.mkdirSync(skillsDir, { recursive: true });
|
|
268
|
+
fs_1.default.mkdirSync(eventsDir, { recursive: true });
|
|
269
|
+
writeFileIfMissing(soulPath, soulTemplate(today), force, stats);
|
|
270
|
+
writeFileIfMissing(humanPath, humanTemplate(today), force, stats);
|
|
271
|
+
writeFileIfMissing(registryPath, (0, skill_support_1.registryTemplate)(), force, stats);
|
|
272
|
+
if (!fs_1.default.existsSync(eventsPath) || force) {
|
|
273
|
+
writeFileIfMissing(eventsPath, seededInitEvent(new Date().toISOString()), force, stats);
|
|
274
|
+
}
|
|
275
|
+
const coreListPath = path_1.default.join(mdkgDir, "core", "core.md");
|
|
276
|
+
ensureCorePins(coreListPath, [SOUL_PIN_ID, HUMAN_PIN_ID]);
|
|
102
277
|
}
|
|
103
|
-
|
|
278
|
+
const noUpdateIgnores = Boolean(options.noUpdateIgnores);
|
|
279
|
+
const shouldUpdateGitignore = Boolean(options.updateGitignore || !noUpdateIgnores);
|
|
280
|
+
const shouldUpdateNpmignore = Boolean(options.updateNpmignore || !noUpdateIgnores);
|
|
281
|
+
if (shouldUpdateGitignore) {
|
|
282
|
+
appendIgnoreEntries(path_1.default.join(root, ".gitignore"), [
|
|
283
|
+
".mdkg/index/",
|
|
284
|
+
".mdkg/pack/",
|
|
285
|
+
".mdkg/work/events/*.jsonl",
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
if (shouldUpdateNpmignore) {
|
|
104
289
|
appendIgnoreEntries(path_1.default.join(root, ".npmignore"), [".mdkg/", ".mdkg/index/", ".mdkg/pack/"]);
|
|
105
290
|
}
|
|
106
291
|
if (options.updateDockerignore) {
|
|
107
292
|
appendIgnoreEntries(path_1.default.join(root, ".dockerignore"), [".mdkg/"]);
|
|
108
293
|
}
|
|
109
|
-
if (!options.updateGitignore && !options.updateNpmignore && !options.updateDockerignore) {
|
|
110
|
-
console.error("warning: ignore files were not updated");
|
|
111
|
-
console.error("hint: rerun `mdkg init --update-gitignore --update-npmignore` to avoid committing cache/pack artifacts");
|
|
112
|
-
}
|
|
113
294
|
console.log(`mdkg init complete: ${stats.created} file(s) created, ${stats.skipped} skipped`);
|
|
114
295
|
console.log("next:");
|
|
115
|
-
console.log(" mdkg index");
|
|
116
296
|
console.log(' mdkg new task "..." --status todo --priority 1');
|
|
117
|
-
console.log(
|
|
118
|
-
console.log(" mdkg
|
|
297
|
+
console.log(' mdkg search "..."');
|
|
298
|
+
console.log(" mdkg show <id>");
|
|
299
|
+
console.log(" mdkg next");
|
|
300
|
+
console.log(" mdkg pack <id>");
|
|
119
301
|
console.log(" mdkg validate");
|
|
120
302
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -8,6 +8,7 @@ const errors_1 = require("../util/errors");
|
|
|
8
8
|
const qid_1 = require("../util/qid");
|
|
9
9
|
const sort_1 = require("../util/sort");
|
|
10
10
|
const node_card_1 = require("./node_card");
|
|
11
|
+
const query_output_1 = require("./query_output");
|
|
11
12
|
function normalizeWorkspace(value) {
|
|
12
13
|
if (!value || value === "all") {
|
|
13
14
|
return undefined;
|
|
@@ -20,6 +21,10 @@ function runListCommand(options) {
|
|
|
20
21
|
if (ws && !config.workspaces[ws]) {
|
|
21
22
|
throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
|
|
22
23
|
}
|
|
24
|
+
const normalizedType = options.type?.toLowerCase();
|
|
25
|
+
if (normalizedType === "skill") {
|
|
26
|
+
throw new errors_1.UsageError("--type skill is no longer supported here; use `mdkg skill list`");
|
|
27
|
+
}
|
|
23
28
|
const { index, rebuilt, stale } = (0, index_cache_1.loadIndex)({
|
|
24
29
|
root: options.root,
|
|
25
30
|
config,
|
|
@@ -39,13 +44,25 @@ function runListCommand(options) {
|
|
|
39
44
|
}
|
|
40
45
|
const filtered = (0, filter_1.filterNodes)(Object.values(index.nodes), {
|
|
41
46
|
ws,
|
|
42
|
-
type:
|
|
47
|
+
type: normalizedType,
|
|
43
48
|
status: options.status,
|
|
44
49
|
epic: epicQid,
|
|
45
50
|
priority: options.priority,
|
|
46
51
|
blocked: options.blocked,
|
|
52
|
+
tags: options.tags,
|
|
53
|
+
tagsMode: options.tagsMode,
|
|
47
54
|
});
|
|
48
55
|
const sorted = (0, sort_1.sortNodesByQid)(filtered);
|
|
56
|
+
if (options.json) {
|
|
57
|
+
(0, query_output_1.writeJson)({
|
|
58
|
+
command: "list",
|
|
59
|
+
kind: "node",
|
|
60
|
+
count: sorted.length,
|
|
61
|
+
items: sorted.map(query_output_1.toNodeSummaryJson),
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
(0, query_output_1.writeCount)(sorted.length, sorted.length === 0 ? "no nodes matched current filters" : undefined);
|
|
49
66
|
for (const node of sorted) {
|
|
50
67
|
console.log((0, node_card_1.formatNodeCard)(node));
|
|
51
68
|
}
|