@baseline-studio/cli 2.2.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/bin/baseline +2 -0
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/context.js +212 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +343 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +27 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +113 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +22 -0
- package/dist/git.d.ts +5 -0
- package/dist/git.js +54 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +33 -0
- package/package.json +50 -0
package/bin/baseline
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function context(name?: string): Promise<void>;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.context = context;
|
|
4
|
+
const readline_1 = require("readline");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const js_yaml_1 = require("js-yaml");
|
|
8
|
+
const config_js_1 = require("../config.js");
|
|
9
|
+
let rlClosed = false;
|
|
10
|
+
function ask(rl, question) {
|
|
11
|
+
if (rlClosed)
|
|
12
|
+
return Promise.resolve("");
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
try {
|
|
15
|
+
rl.question(question, resolve);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
resolve("");
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function context(name) {
|
|
23
|
+
if (name) {
|
|
24
|
+
await contextAdd(name);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
await contextRefresh();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Re-run context prompts for existing files */
|
|
31
|
+
async function contextRefresh() {
|
|
32
|
+
const config = (0, config_js_1.readConfig)();
|
|
33
|
+
const cwd = process.cwd();
|
|
34
|
+
const contextDir = (0, path_1.join)(cwd, config.client.contextPath || "./context");
|
|
35
|
+
// Load prompts from local skills (they were pulled from core via update)
|
|
36
|
+
const skillsDir = (0, path_1.join)(cwd, "skills");
|
|
37
|
+
const prompts = loadPromptsFromLocal(cwd);
|
|
38
|
+
if (Object.keys(prompts).length === 0) {
|
|
39
|
+
console.error(" Error: No context prompts found. Run `baseline update` first.\n");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
43
|
+
rl.on("close", () => { rlClosed = true; });
|
|
44
|
+
console.log(`\n Baseline Context — Update Context Files`);
|
|
45
|
+
console.log(` ────────────────────────────────────────\n`);
|
|
46
|
+
console.log(` Existing answers shown in [brackets]. Press Enter to keep them.\n`);
|
|
47
|
+
let filesUpdated = 0;
|
|
48
|
+
for (const [ctxFile, prompt] of Object.entries(prompts)) {
|
|
49
|
+
const fullPath = (0, path_1.join)(contextDir, ctxFile);
|
|
50
|
+
const existingContent = (0, fs_1.existsSync)(fullPath) ? (0, fs_1.readFileSync)(fullPath, "utf-8") : "";
|
|
51
|
+
const existingAnswers = parseExistingAnswers(existingContent, prompt.questions);
|
|
52
|
+
console.log(` ── ${prompt.title} ──\n`);
|
|
53
|
+
const answers = [];
|
|
54
|
+
for (let i = 0; i < prompt.questions.length; i++) {
|
|
55
|
+
const q = prompt.questions[i];
|
|
56
|
+
const existing = existingAnswers[i] || "";
|
|
57
|
+
const hint = existing ? ` [${truncate(existing, 60)}]` : "";
|
|
58
|
+
const answer = await ask(rl, ` ${q}${hint}\n > `);
|
|
59
|
+
const final = answer.trim() || existing;
|
|
60
|
+
if (final) {
|
|
61
|
+
answers.push(`**${q}**\n${final}`);
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
}
|
|
65
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
66
|
+
if (answers.length > 0) {
|
|
67
|
+
(0, fs_1.writeFileSync)(fullPath, `# ${prompt.title}\n\n${answers.join("\n\n")}\n`);
|
|
68
|
+
filesUpdated++;
|
|
69
|
+
}
|
|
70
|
+
else if (!(0, fs_1.existsSync)(fullPath)) {
|
|
71
|
+
(0, fs_1.writeFileSync)(fullPath, `# ${prompt.title}\n\n<!-- Add your content here -->\n`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
rl.close();
|
|
75
|
+
console.log(` ────────────────────────────────────────`);
|
|
76
|
+
console.log(` Updated ${filesUpdated} context files.\n`);
|
|
77
|
+
}
|
|
78
|
+
/** Create a new context file and wire it into context.yaml */
|
|
79
|
+
async function contextAdd(name) {
|
|
80
|
+
const config = (0, config_js_1.readConfig)();
|
|
81
|
+
const cwd = process.cwd();
|
|
82
|
+
const contextDir = (0, path_1.join)(cwd, config.client.contextPath || "./context");
|
|
83
|
+
const skillsDir = (0, path_1.join)(cwd, "skills");
|
|
84
|
+
// Normalize name
|
|
85
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
86
|
+
const filePath = (0, path_1.join)(contextDir, "extended", fileName);
|
|
87
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
88
|
+
console.error(`\n Error: context/extended/${fileName} already exists.\n`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
// Get available skills
|
|
92
|
+
const skills = [];
|
|
93
|
+
if ((0, fs_1.existsSync)(skillsDir)) {
|
|
94
|
+
for (const entry of (0, fs_1.readdirSync)(skillsDir)) {
|
|
95
|
+
const manifestPath = (0, path_1.join)(skillsDir, entry, "manifest.yaml");
|
|
96
|
+
if ((0, fs_1.existsSync)(manifestPath)) {
|
|
97
|
+
skills.push(entry);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
skills.sort();
|
|
102
|
+
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
103
|
+
rl.on("close", () => { rlClosed = true; });
|
|
104
|
+
console.log(`\n Baseline Context — Add New File`);
|
|
105
|
+
console.log(` ───────────────────────────────\n`);
|
|
106
|
+
console.log(` Creating: context/extended/${fileName}\n`);
|
|
107
|
+
// Ask for a title
|
|
108
|
+
const title = await ask(rl, ` Title for this context file: `);
|
|
109
|
+
const fileTitle = title.trim() || name.replace(/\.md$/, "").replace(/-/g, " ");
|
|
110
|
+
console.log();
|
|
111
|
+
// Ask which skills should use this context
|
|
112
|
+
console.log(` Which skills should use this context file?`);
|
|
113
|
+
console.log(` Available skills:\n`);
|
|
114
|
+
for (let i = 0; i < skills.length; i++) {
|
|
115
|
+
console.log(` ${i + 1}. ${skills[i]}`);
|
|
116
|
+
}
|
|
117
|
+
console.log();
|
|
118
|
+
const selection = await ask(rl, ` Enter skill numbers (comma-separated) or "all":\n > `);
|
|
119
|
+
let selectedSkills;
|
|
120
|
+
if (selection.trim().toLowerCase() === "all") {
|
|
121
|
+
selectedSkills = [...skills];
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const indices = selection
|
|
125
|
+
.split(",")
|
|
126
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
127
|
+
.filter((i) => i >= 0 && i < skills.length);
|
|
128
|
+
selectedSkills = indices.map((i) => skills[i]);
|
|
129
|
+
}
|
|
130
|
+
// Create the file
|
|
131
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
|
|
132
|
+
(0, fs_1.writeFileSync)(filePath, `# ${fileTitle}\n\n<!-- Add your content here -->\n`);
|
|
133
|
+
// Update context.yaml
|
|
134
|
+
const contextYamlPath = (0, path_1.join)(contextDir, "context.yaml");
|
|
135
|
+
let contextYaml = { core: ["identity.md", "voice.md"], extended: {} };
|
|
136
|
+
if ((0, fs_1.existsSync)(contextYamlPath)) {
|
|
137
|
+
const parsed = (0, js_yaml_1.load)((0, fs_1.readFileSync)(contextYamlPath, "utf-8"));
|
|
138
|
+
if (parsed)
|
|
139
|
+
contextYaml = parsed;
|
|
140
|
+
if (!contextYaml.extended)
|
|
141
|
+
contextYaml.extended = {};
|
|
142
|
+
}
|
|
143
|
+
if (selectedSkills.length > 0) {
|
|
144
|
+
contextYaml.extended[fileName] = selectedSkills.sort();
|
|
145
|
+
}
|
|
146
|
+
// Write context.yaml back as formatted YAML
|
|
147
|
+
let yamlOut = "# Maps context files to skills. Merged with skill manifests during execution.\n";
|
|
148
|
+
yamlOut += "core:\n";
|
|
149
|
+
for (const c of contextYaml.core || ["identity.md", "voice.md"]) {
|
|
150
|
+
yamlOut += ` - ${c}\n`;
|
|
151
|
+
}
|
|
152
|
+
yamlOut += "extended:\n";
|
|
153
|
+
const entries = Object.entries(contextYaml.extended || {}).sort(([a], [b]) => a.localeCompare(b));
|
|
154
|
+
for (const [file, skillList] of entries) {
|
|
155
|
+
yamlOut += ` ${file}:\n`;
|
|
156
|
+
for (const s of skillList) {
|
|
157
|
+
yamlOut += ` - ${s}\n`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
(0, fs_1.writeFileSync)(contextYamlPath, yamlOut);
|
|
161
|
+
rl.close();
|
|
162
|
+
console.log(`\n ───────────────────────────────`);
|
|
163
|
+
console.log(` Created context/extended/${fileName}`);
|
|
164
|
+
if (selectedSkills.length > 0) {
|
|
165
|
+
console.log(` Wired to: ${selectedSkills.join(", ")}`);
|
|
166
|
+
}
|
|
167
|
+
console.log(` Updated context/context.yaml\n`);
|
|
168
|
+
}
|
|
169
|
+
/** Load context prompts from the local context-prompts.yaml or skill manifests */
|
|
170
|
+
function loadPromptsFromLocal(cwd) {
|
|
171
|
+
// First try context-prompts.yaml bundled with the skills
|
|
172
|
+
// Since we don't bundle context-prompts.yaml in client repos,
|
|
173
|
+
// we fetch it from the core repo via config
|
|
174
|
+
const config = (0, config_js_1.readConfig)();
|
|
175
|
+
const { cloneAtTag, getLatestTag } = require("../git.js");
|
|
176
|
+
const latest = getLatestTag(config.coreRepo);
|
|
177
|
+
if (!latest)
|
|
178
|
+
return {};
|
|
179
|
+
const tmpDir = cloneAtTag(config.coreRepo, latest);
|
|
180
|
+
const promptsPath = (0, path_1.join)(tmpDir, "context-prompts.yaml");
|
|
181
|
+
let prompts = {};
|
|
182
|
+
if ((0, fs_1.existsSync)(promptsPath)) {
|
|
183
|
+
prompts = (0, js_yaml_1.load)((0, fs_1.readFileSync)(promptsPath, "utf-8"));
|
|
184
|
+
}
|
|
185
|
+
const { rmSync } = require("fs");
|
|
186
|
+
rmSync(tmpDir, { recursive: true });
|
|
187
|
+
return prompts;
|
|
188
|
+
}
|
|
189
|
+
/** Parse existing answers from a context file */
|
|
190
|
+
function parseExistingAnswers(content, questions) {
|
|
191
|
+
const answers = [];
|
|
192
|
+
for (const q of questions) {
|
|
193
|
+
const marker = `**${q}**`;
|
|
194
|
+
const idx = content.indexOf(marker);
|
|
195
|
+
if (idx === -1) {
|
|
196
|
+
answers.push("");
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const afterMarker = content.slice(idx + marker.length).trimStart();
|
|
200
|
+
// Find the next question marker or end of file
|
|
201
|
+
const nextMarkerIdx = afterMarker.indexOf("**");
|
|
202
|
+
const answerText = nextMarkerIdx > 0
|
|
203
|
+
? afterMarker.slice(0, nextMarkerIdx).trim()
|
|
204
|
+
: afterMarker.trim();
|
|
205
|
+
answers.push(answerText);
|
|
206
|
+
}
|
|
207
|
+
return answers;
|
|
208
|
+
}
|
|
209
|
+
function truncate(str, max) {
|
|
210
|
+
const oneLine = str.replace(/\n/g, " ").trim();
|
|
211
|
+
return oneLine.length > max ? oneLine.slice(0, max - 3) + "..." : oneLine;
|
|
212
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function init(): Promise<void>;
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = init;
|
|
4
|
+
const readline_1 = require("readline");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const js_yaml_1 = require("js-yaml");
|
|
8
|
+
const git_js_1 = require("../git.js");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const SYNC_DIRS = ["skills", "frameworks", "scripts", "cli"];
|
|
11
|
+
let rlClosed = false;
|
|
12
|
+
function ask(rl, question) {
|
|
13
|
+
if (rlClosed)
|
|
14
|
+
return Promise.resolve("");
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
try {
|
|
17
|
+
rl.question(question, resolve);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
resolve("");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function init() {
|
|
25
|
+
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
26
|
+
rl.on("close", () => { rlClosed = true; });
|
|
27
|
+
console.log(`\n Baseline System — New Client Setup`);
|
|
28
|
+
console.log(` ───────────────────────────────────\n`);
|
|
29
|
+
// 1. Gather basic info
|
|
30
|
+
const clientName = await ask(rl, " Client name: ");
|
|
31
|
+
const folderName = await ask(rl, ` Folder name (${clientName.toLowerCase().replace(/\s+/g, "-")}-system): `);
|
|
32
|
+
const folder = folderName.trim() ||
|
|
33
|
+
`${clientName.toLowerCase().replace(/\s+/g, "-")}-system`;
|
|
34
|
+
const coreRepo = await ask(rl, " Core repo (TrentM6/baseline-core): ");
|
|
35
|
+
const repo = coreRepo.trim() || "TrentM6/baseline-core";
|
|
36
|
+
const destDir = (0, path_1.join)(process.cwd(), folder);
|
|
37
|
+
if ((0, fs_1.existsSync)(destDir)) {
|
|
38
|
+
console.error(`\n Error: ${folder} already exists.\n`);
|
|
39
|
+
rl.close();
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
// 2. Fetch latest from core
|
|
43
|
+
console.log(`\n Fetching latest from ${repo}...`);
|
|
44
|
+
const latest = (0, git_js_1.getLatestTag)(repo);
|
|
45
|
+
if (!latest) {
|
|
46
|
+
console.error(" Could not determine latest version.\n");
|
|
47
|
+
rl.close();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
console.log(` Using v${latest}\n`);
|
|
51
|
+
const tmpDir = (0, git_js_1.cloneAtTag)(repo, latest);
|
|
52
|
+
// 3. Create folder structure
|
|
53
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(destDir, "context", "core"), { recursive: true });
|
|
54
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(destDir, "context", "extended"), { recursive: true });
|
|
55
|
+
// 4. Copy skills, frameworks, scripts, cli
|
|
56
|
+
for (const dir of SYNC_DIRS) {
|
|
57
|
+
const srcDir = (0, path_1.join)(tmpDir, dir);
|
|
58
|
+
if ((0, fs_1.existsSync)(srcDir)) {
|
|
59
|
+
(0, fs_1.cpSync)(srcDir, (0, path_1.join)(destDir, dir), { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Remove CLI source files (clients only need bin/, dist/, package.json)
|
|
63
|
+
const cliCleanup = ["src", "tsconfig.json", "package-lock.json"];
|
|
64
|
+
for (const item of cliCleanup) {
|
|
65
|
+
const p = (0, path_1.join)(destDir, "cli", item);
|
|
66
|
+
if ((0, fs_1.existsSync)(p))
|
|
67
|
+
(0, fs_1.rmSync)(p, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
// 5. Collect unique context file paths from manifests
|
|
70
|
+
const contextFiles = collectContextPaths(tmpDir);
|
|
71
|
+
// 6. Load context-prompts.yaml from core
|
|
72
|
+
const promptsPath = (0, path_1.join)(tmpDir, "context-prompts.yaml");
|
|
73
|
+
let prompts = {};
|
|
74
|
+
if ((0, fs_1.existsSync)(promptsPath)) {
|
|
75
|
+
prompts = (0, js_yaml_1.load)((0, fs_1.readFileSync)(promptsPath, "utf-8"));
|
|
76
|
+
}
|
|
77
|
+
// 7. Ask questions and write context files
|
|
78
|
+
console.log(" Let's set up your context files.\n");
|
|
79
|
+
console.log(" (Press Enter to skip any question)\n");
|
|
80
|
+
for (const ctxFile of contextFiles) {
|
|
81
|
+
const prompt = prompts[ctxFile];
|
|
82
|
+
if (!prompt) {
|
|
83
|
+
// Create empty template for files without prompts
|
|
84
|
+
const fullPath = (0, path_1.join)(destDir, "context", ctxFile);
|
|
85
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
86
|
+
(0, fs_1.writeFileSync)(fullPath, `# ${ctxFile}\n\n<!-- Add your content here -->\n`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
console.log(` ── ${prompt.title} ──\n`);
|
|
90
|
+
const answers = [];
|
|
91
|
+
for (const q of prompt.questions) {
|
|
92
|
+
const answer = await ask(rl, ` ${q}\n > `);
|
|
93
|
+
if (answer.trim()) {
|
|
94
|
+
answers.push(`**${q}**\n${answer.trim()}`);
|
|
95
|
+
}
|
|
96
|
+
console.log();
|
|
97
|
+
}
|
|
98
|
+
const fullPath = (0, path_1.join)(destDir, "context", ctxFile);
|
|
99
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
100
|
+
if (answers.length > 0) {
|
|
101
|
+
(0, fs_1.writeFileSync)(fullPath, `# ${prompt.title}\n\n${answers.join("\n\n")}\n`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
(0, fs_1.writeFileSync)(fullPath, `# ${prompt.title}\n\n<!-- Add your content here -->\n`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// 8. Create context.yaml
|
|
108
|
+
const contextYaml = buildContextYaml(tmpDir, contextFiles);
|
|
109
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, "context", "context.yaml"), contextYaml);
|
|
110
|
+
// 9. Create baseline.config.json
|
|
111
|
+
const config = {
|
|
112
|
+
version: latest,
|
|
113
|
+
coreRepo: repo,
|
|
114
|
+
lastUpdated: new Date().toISOString(),
|
|
115
|
+
client: {
|
|
116
|
+
name: clientName,
|
|
117
|
+
contextPath: "./context",
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, "baseline.config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
121
|
+
// 10. Create CLAUDE.md
|
|
122
|
+
const templatePath = (0, path_1.join)(tmpDir, "claude-template.md");
|
|
123
|
+
if ((0, fs_1.existsSync)(templatePath)) {
|
|
124
|
+
let template = (0, fs_1.readFileSync)(templatePath, "utf-8");
|
|
125
|
+
template = template.replace(/\{client_name\}/g, clientName);
|
|
126
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, "CLAUDE.md"), template);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, "CLAUDE.md"), generateClaudeMd(clientName));
|
|
130
|
+
}
|
|
131
|
+
// 11. Create root package.json for local CLI access
|
|
132
|
+
const rootPkg = {
|
|
133
|
+
name: `${clientName.toLowerCase().replace(/\s+/g, "-")}-system`,
|
|
134
|
+
version: "1.0.0",
|
|
135
|
+
private: true,
|
|
136
|
+
description: `${clientName} Baseline System`,
|
|
137
|
+
dependencies: {
|
|
138
|
+
"baseline-cli": "file:cli",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, "package.json"), JSON.stringify(rootPkg, null, 2) + "\n");
|
|
142
|
+
// 12. Install CLI locally so npx baseline works
|
|
143
|
+
console.log(`\n Installing CLI...`);
|
|
144
|
+
(0, child_process_1.execSync)("npm install --silent", { cwd: destDir, stdio: "pipe" });
|
|
145
|
+
// 13. Create .gitignore
|
|
146
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(destDir, ".gitignore"), "node_modules/\n.DS_Store\n");
|
|
147
|
+
// 14. Initialize git repo
|
|
148
|
+
(0, child_process_1.execSync)("git init", { cwd: destDir, stdio: "pipe" });
|
|
149
|
+
(0, child_process_1.execSync)("git add -A", { cwd: destDir, stdio: "pipe" });
|
|
150
|
+
(0, child_process_1.execSync)(`git commit -m "Initialize ${clientName} Baseline System (v${latest})"`, { cwd: destDir, stdio: "pipe" });
|
|
151
|
+
// Clean up
|
|
152
|
+
(0, fs_1.rmSync)(tmpDir, { recursive: true });
|
|
153
|
+
rl.close();
|
|
154
|
+
console.log(` ───────────────────────────────────`);
|
|
155
|
+
console.log(` ${clientName} system created at ./${folder}`);
|
|
156
|
+
console.log(` Version: v${latest}`);
|
|
157
|
+
console.log(` Skills: ${SYNC_DIRS.filter((d) => d !== "cli").map((d) => (0, fs_1.existsSync)((0, path_1.join)(destDir, d)) ? (0, fs_1.readdirSync)((0, path_1.join)(destDir, d)).filter((f) => !f.startsWith(".") && !f.startsWith("_")).length : 0).join(" | ")}`);
|
|
158
|
+
console.log(`\n Next steps:`);
|
|
159
|
+
console.log(` cd ${folder}`);
|
|
160
|
+
console.log(` Edit context/ files to add more detail`);
|
|
161
|
+
console.log(` Run \`npx baseline status\` to check for updates\n`);
|
|
162
|
+
}
|
|
163
|
+
/** Scan all skill manifests and return unique context file paths (relative to context/) */
|
|
164
|
+
function collectContextPaths(coreDir) {
|
|
165
|
+
const skillsDir = (0, path_1.join)(coreDir, "skills");
|
|
166
|
+
if (!(0, fs_1.existsSync)(skillsDir))
|
|
167
|
+
return [];
|
|
168
|
+
const paths = new Set();
|
|
169
|
+
// Always include core files
|
|
170
|
+
paths.add("core/identity.md");
|
|
171
|
+
paths.add("core/voice.md");
|
|
172
|
+
for (const skill of (0, fs_1.readdirSync)(skillsDir)) {
|
|
173
|
+
const manifestPath = (0, path_1.join)(skillsDir, skill, "manifest.yaml");
|
|
174
|
+
if (!(0, fs_1.existsSync)(manifestPath))
|
|
175
|
+
continue;
|
|
176
|
+
try {
|
|
177
|
+
const manifest = (0, js_yaml_1.load)((0, fs_1.readFileSync)(manifestPath, "utf-8"));
|
|
178
|
+
if (!manifest?.context)
|
|
179
|
+
continue;
|
|
180
|
+
for (const section of ["core", "extended"]) {
|
|
181
|
+
const entries = manifest.context[section];
|
|
182
|
+
if (!entries)
|
|
183
|
+
continue;
|
|
184
|
+
for (const ctxPath of entries) {
|
|
185
|
+
const match = ctxPath.match(/context\/\{client\}\/(.+)/);
|
|
186
|
+
if (match)
|
|
187
|
+
paths.add(match[1]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Skip unparseable manifests
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Sort: core first, then extended
|
|
196
|
+
return [...paths].sort((a, b) => {
|
|
197
|
+
if (a.startsWith("core/") && !b.startsWith("core/"))
|
|
198
|
+
return -1;
|
|
199
|
+
if (!a.startsWith("core/") && b.startsWith("core/"))
|
|
200
|
+
return 1;
|
|
201
|
+
return a.localeCompare(b);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/** Build context.yaml mapping context files to skills */
|
|
205
|
+
function buildContextYaml(coreDir, contextFiles) {
|
|
206
|
+
const skillsDir = (0, path_1.join)(coreDir, "skills");
|
|
207
|
+
const extendedMap = new Map();
|
|
208
|
+
if ((0, fs_1.existsSync)(skillsDir)) {
|
|
209
|
+
for (const skill of (0, fs_1.readdirSync)(skillsDir)) {
|
|
210
|
+
const manifestPath = (0, path_1.join)(skillsDir, skill, "manifest.yaml");
|
|
211
|
+
if (!(0, fs_1.existsSync)(manifestPath))
|
|
212
|
+
continue;
|
|
213
|
+
try {
|
|
214
|
+
const manifest = (0, js_yaml_1.load)((0, fs_1.readFileSync)(manifestPath, "utf-8"));
|
|
215
|
+
if (!manifest?.context?.extended)
|
|
216
|
+
continue;
|
|
217
|
+
for (const ctxPath of manifest.context.extended) {
|
|
218
|
+
const match = ctxPath.match(/context\/\{client\}\/extended\/(.+)/);
|
|
219
|
+
if (!match)
|
|
220
|
+
continue;
|
|
221
|
+
const file = match[1];
|
|
222
|
+
if (!extendedMap.has(file))
|
|
223
|
+
extendedMap.set(file, []);
|
|
224
|
+
extendedMap.get(file).push(skill);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Skip
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
let yaml = "# Maps context files to skills. Merged with skill manifests during execution.\n";
|
|
233
|
+
yaml += "core:\n";
|
|
234
|
+
yaml += " - identity.md # loaded by all skills\n";
|
|
235
|
+
yaml += " - voice.md # loaded by all skills\n";
|
|
236
|
+
yaml += "extended:\n";
|
|
237
|
+
for (const [file, skills] of [...extendedMap.entries()].sort()) {
|
|
238
|
+
yaml += ` ${file}:\n`;
|
|
239
|
+
for (const s of skills.sort()) {
|
|
240
|
+
yaml += ` - ${s}\n`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return yaml;
|
|
244
|
+
}
|
|
245
|
+
/** Generate a CLAUDE.md for the client if no template exists in core */
|
|
246
|
+
function generateClaudeMd(clientName) {
|
|
247
|
+
return `# ${clientName} — Baseline System
|
|
248
|
+
|
|
249
|
+
> This file is automatically loaded at the start of every Claude Code session. It enforces consistent skill execution.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## What This Project Is
|
|
254
|
+
|
|
255
|
+
The Baseline System is a complete AI system for product work with four components:
|
|
256
|
+
|
|
257
|
+
1. **Skills** (\`skills/\`) — Domain expertise (12 universal skills)
|
|
258
|
+
2. **Context** (\`context/\`) — Business-specific knowledge
|
|
259
|
+
3. **Frameworks** (\`frameworks/\`) — Reusable methodologies
|
|
260
|
+
4. **Scripts** (\`scripts/\`) — Delivery to external tools
|
|
261
|
+
|
|
262
|
+
Skills provide methodology. Context personalizes output. Frameworks provide reusable patterns. Scripts deliver to tools.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Skill Execution Protocol
|
|
267
|
+
|
|
268
|
+
When a user invokes any skill — by name, by file path, or by describing a task that maps to a skill — you MUST follow this exact sequence. Do not skip steps.
|
|
269
|
+
|
|
270
|
+
### Step 1: Identify the Skill
|
|
271
|
+
|
|
272
|
+
Match the user's request to a skill using this table:
|
|
273
|
+
|
|
274
|
+
| Task | Skill |
|
|
275
|
+
|------|-------|
|
|
276
|
+
| Strategic decisions, roadmaps, prioritization | \`strategic-advisory\` |
|
|
277
|
+
| User research, interviews, synthesis | \`research-synthesis\` |
|
|
278
|
+
| PRDs, specs, briefs, stakeholder updates | \`product-communications\` |
|
|
279
|
+
| Interface design, wireframes, UI copy | \`ux-design\` |
|
|
280
|
+
| Metrics, dashboards, A/B tests | \`product-analytics\` |
|
|
281
|
+
| Coded prototypes, demos, POCs | \`prototyping\` |
|
|
282
|
+
| Planning, tracking, sprints | \`project-management\` |
|
|
283
|
+
| User guides, help center, release notes | \`technical-documentation\` |
|
|
284
|
+
| Presentations, graphics, diagrams | \`brand-design\` |
|
|
285
|
+
| LinkedIn content, website copy, campaigns | \`marketing\` |
|
|
286
|
+
| Outreach, proposals, discovery calls | \`sales\` |
|
|
287
|
+
| Creating new skills | \`skill-building\` |
|
|
288
|
+
|
|
289
|
+
### Step 2: Read the Manifest
|
|
290
|
+
|
|
291
|
+
Every skill has a \`manifest.yaml\` in its folder that lists every file the skill needs. Read it:
|
|
292
|
+
|
|
293
|
+
\`\`\`
|
|
294
|
+
skills/[skill-name]/manifest.yaml
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
This is the source of truth for what files to load. Do not guess or rely on file paths embedded in markdown prose.
|
|
298
|
+
|
|
299
|
+
### Step 3: Load All Files Listed in the Manifest
|
|
300
|
+
|
|
301
|
+
The manifest has three sections. Load them in this order:
|
|
302
|
+
|
|
303
|
+
1. **\`always_load\`** — Read every file in this list. These are the skill file and framework files. Always load all of them.
|
|
304
|
+
|
|
305
|
+
2. **\`context\`** — These paths use \`{client}\` as a placeholder. Replace \`{client}\` with the context folder path for this project. Read every file under \`core\` and \`extended\`. If a context file does not exist, skip it and continue — not all context files may be populated.
|
|
306
|
+
|
|
307
|
+
3. **\`references\`** — These are detailed reference materials. Load them when the task benefits from detailed guidance (e.g., document templates when writing a PRD, content playbooks when creating a campaign). For straightforward tasks, you may skip references.
|
|
308
|
+
|
|
309
|
+
**Do not skip files.** Do not summarize file names instead of reading them. Treat every path in the manifest as a load instruction.
|
|
310
|
+
|
|
311
|
+
### Step 4: Execute the Skill's Workflow
|
|
312
|
+
|
|
313
|
+
After loading all files, follow the workflow defined in the skill file and the workflow orchestration framework:
|
|
314
|
+
|
|
315
|
+
1. **Plan** — Present your plan to the user. Wait for approval before proceeding.
|
|
316
|
+
2. **Clarify** — Ask the skill's clarifying questions. Do not proceed with missing information.
|
|
317
|
+
3. **Execute** — Do the domain-specific work using the skill's methodology and loaded context.
|
|
318
|
+
4. **Validate** — Run the skill's quality checks. If any fail, apply error recovery and re-validate.
|
|
319
|
+
|
|
320
|
+
### Step 5: Deliver (If Requested)
|
|
321
|
+
|
|
322
|
+
If the user wants output delivered to an external tool, read the relevant script from \`scripts/\` and follow its instructions.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Session Management
|
|
327
|
+
|
|
328
|
+
- Scope each session to one major task. Multi-task sessions degrade output quality.
|
|
329
|
+
- After completing a major deliverable, recommend starting a fresh session.
|
|
330
|
+
- If the conversation has gone through 3+ revision cycles, proactively suggest a session break.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Anti-Patterns — Do Not
|
|
335
|
+
|
|
336
|
+
- **Skip reading the manifest** — Always read \`manifest.yaml\` first. It is the source of truth for what files a skill needs.
|
|
337
|
+
- **Acknowledge file paths without reading them** — If the manifest lists a file, read it.
|
|
338
|
+
- **Skip the Plan step** — Always present a plan and wait for approval.
|
|
339
|
+
- **Proceed without clarifying** — Ask the skill's questions before executing.
|
|
340
|
+
- **Skip quality checks** — Run every check defined by the skill.
|
|
341
|
+
- **Overload the session** — One major task per session. Recommend fresh sessions after milestones.
|
|
342
|
+
`;
|
|
343
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function status(): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.status = status;
|
|
4
|
+
const config_js_1 = require("../config.js");
|
|
5
|
+
const git_js_1 = require("../git.js");
|
|
6
|
+
function status() {
|
|
7
|
+
const config = (0, config_js_1.readConfig)();
|
|
8
|
+
console.log(`\n Baseline System`);
|
|
9
|
+
console.log(` ───────────────────────────`);
|
|
10
|
+
console.log(` Client: ${config.client.name}`);
|
|
11
|
+
console.log(` Version: v${config.version}`);
|
|
12
|
+
console.log(` Core repo: ${config.coreRepo}`);
|
|
13
|
+
console.log(` Last updated: ${config.lastUpdated}`);
|
|
14
|
+
console.log(`\n Checking for updates...`);
|
|
15
|
+
const latest = (0, git_js_1.getLatestTag)(config.coreRepo);
|
|
16
|
+
if (!latest) {
|
|
17
|
+
console.log(` Could not determine latest version.\n`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if ((0, git_js_1.isNewer)(latest, config.version)) {
|
|
21
|
+
console.log(` Update available: v${config.version} → v${latest}`);
|
|
22
|
+
console.log(` Run \`baseline update\` to pull the latest.\n`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.log(` Up to date (v${config.version}).\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function update(): void;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.update = update;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const config_js_1 = require("../config.js");
|
|
7
|
+
const git_js_1 = require("../git.js");
|
|
8
|
+
const js_yaml_1 = require("js-yaml");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const SYNC_DIRS = ["skills", "frameworks", "scripts", "cli"];
|
|
11
|
+
function update() {
|
|
12
|
+
const config = (0, config_js_1.readConfig)();
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
console.log(`\n Checking for updates...`);
|
|
15
|
+
const latest = (0, git_js_1.getLatestTag)(config.coreRepo);
|
|
16
|
+
if (!latest) {
|
|
17
|
+
console.log(` Could not determine latest version.\n`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!(0, git_js_1.isNewer)(latest, config.version)) {
|
|
21
|
+
console.log(` Already up to date (v${config.version}).\n`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
console.log(` Updating v${config.version} → v${latest}...`);
|
|
25
|
+
// Clone the latest tag to a temp directory
|
|
26
|
+
const tmpDir = (0, git_js_1.cloneAtTag)(config.coreRepo, latest);
|
|
27
|
+
// Sync each directory: full replace
|
|
28
|
+
const stats = { skills: 0, frameworks: 0, scripts: 0 };
|
|
29
|
+
for (const dir of SYNC_DIRS) {
|
|
30
|
+
const srcDir = (0, path_1.join)(tmpDir, dir);
|
|
31
|
+
const destDir = (0, path_1.join)(cwd, dir);
|
|
32
|
+
if (!(0, fs_1.existsSync)(srcDir))
|
|
33
|
+
continue;
|
|
34
|
+
// Count items for summary (skip cli in counts)
|
|
35
|
+
if (dir === "skills") {
|
|
36
|
+
stats.skills = (0, fs_1.readdirSync)(srcDir).filter((f) => (0, fs_1.statSync)((0, path_1.join)(srcDir, f)).isDirectory()).length;
|
|
37
|
+
}
|
|
38
|
+
else if (dir !== "cli") {
|
|
39
|
+
stats[dir] = (0, fs_1.readdirSync)(srcDir).filter((f) => f.endsWith(".md") || (0, fs_1.statSync)((0, path_1.join)(srcDir, f)).isDirectory()).length;
|
|
40
|
+
}
|
|
41
|
+
// Full replace
|
|
42
|
+
if ((0, fs_1.existsSync)(destDir)) {
|
|
43
|
+
(0, fs_1.rmSync)(destDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
(0, fs_1.cpSync)(srcDir, destDir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
// Remove CLI source files (clients only need bin/, dist/, package.json)
|
|
48
|
+
const cliCleanup = ["src", "tsconfig.json", "package-lock.json"];
|
|
49
|
+
for (const item of cliCleanup) {
|
|
50
|
+
const p = (0, path_1.join)(cwd, "cli", item);
|
|
51
|
+
if ((0, fs_1.existsSync)(p))
|
|
52
|
+
(0, fs_1.rmSync)(p, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
// Re-install CLI dependencies after update
|
|
55
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(cwd, "package.json"))) {
|
|
56
|
+
try {
|
|
57
|
+
(0, child_process_1.execSync)("npm install --silent", { cwd, stdio: "pipe" });
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Non-fatal — CLI still works from previous install
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check for missing context files
|
|
64
|
+
const contextPath = config.client.contextPath || "./context";
|
|
65
|
+
checkMissingContext(tmpDir, (0, path_1.join)(cwd, contextPath));
|
|
66
|
+
// Clean up temp dir
|
|
67
|
+
(0, fs_1.rmSync)(tmpDir, { recursive: true });
|
|
68
|
+
// Update config
|
|
69
|
+
config.version = latest;
|
|
70
|
+
config.lastUpdated = new Date().toISOString();
|
|
71
|
+
(0, config_js_1.writeConfig)(config);
|
|
72
|
+
console.log(`\n Updated v${config.version} successfully.`);
|
|
73
|
+
console.log(` Skills: ${stats.skills} | Frameworks: ${stats.frameworks} | Scripts: ${stats.scripts}`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
function checkMissingContext(coreDir, contextDir) {
|
|
77
|
+
const skillsDir = (0, path_1.join)(coreDir, "skills");
|
|
78
|
+
if (!(0, fs_1.existsSync)(skillsDir))
|
|
79
|
+
return;
|
|
80
|
+
const missingMap = new Map();
|
|
81
|
+
for (const skill of (0, fs_1.readdirSync)(skillsDir)) {
|
|
82
|
+
const manifestPath = (0, path_1.join)(skillsDir, skill, "manifest.yaml");
|
|
83
|
+
if (!(0, fs_1.existsSync)(manifestPath))
|
|
84
|
+
continue;
|
|
85
|
+
try {
|
|
86
|
+
const manifest = (0, js_yaml_1.load)((0, fs_1.readFileSync)(manifestPath, "utf-8"));
|
|
87
|
+
if (!manifest?.context?.extended)
|
|
88
|
+
continue;
|
|
89
|
+
for (const ctxPath of manifest.context.extended) {
|
|
90
|
+
const match = ctxPath.match(/context\/\{client\}\/(.+)/);
|
|
91
|
+
if (!match)
|
|
92
|
+
continue;
|
|
93
|
+
const relPath = match[1];
|
|
94
|
+
const fullPath = (0, path_1.join)(contextDir, relPath);
|
|
95
|
+
if (!(0, fs_1.existsSync)(fullPath)) {
|
|
96
|
+
if (!missingMap.has(relPath))
|
|
97
|
+
missingMap.set(relPath, []);
|
|
98
|
+
missingMap.get(relPath).push(skill);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Skip unparseable manifests
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (missingMap.size > 0) {
|
|
107
|
+
console.log(`\n Missing context files:`);
|
|
108
|
+
for (const [file, skills] of missingMap) {
|
|
109
|
+
console.log(` → ${file} (used by ${skills.join(", ")})`);
|
|
110
|
+
}
|
|
111
|
+
console.log(` Create these files to get the most out of these skills.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface BaselineConfig {
|
|
2
|
+
version: string;
|
|
3
|
+
coreRepo: string;
|
|
4
|
+
lastUpdated: string;
|
|
5
|
+
client: {
|
|
6
|
+
name: string;
|
|
7
|
+
contextPath: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function readConfig(): BaselineConfig;
|
|
11
|
+
export declare function writeConfig(config: BaselineConfig): void;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readConfig = readConfig;
|
|
4
|
+
exports.writeConfig = writeConfig;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const CONFIG_FILE = "baseline.config.json";
|
|
8
|
+
function readConfig() {
|
|
9
|
+
const configPath = (0, path_1.join)(process.cwd(), CONFIG_FILE);
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
console.error(`Error: ${CONFIG_FILE} not found in current directory.`);
|
|
15
|
+
console.error("Run this command from your baseline system root.");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function writeConfig(config) {
|
|
20
|
+
const configPath = (0, path_1.join)(process.cwd(), CONFIG_FILE);
|
|
21
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
22
|
+
}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function getLatestTag(coreRepo: string): string | null;
|
|
2
|
+
export declare function fetchAndExtract(coreRepo: string, tag: string, destDir: string): void;
|
|
3
|
+
/** Clone a specific tag to a temp dir and return the path */
|
|
4
|
+
export declare function cloneAtTag(coreRepo: string, tag: string): string;
|
|
5
|
+
export declare function isNewer(latest: string, current: string): boolean;
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLatestTag = getLatestTag;
|
|
4
|
+
exports.fetchAndExtract = fetchAndExtract;
|
|
5
|
+
exports.cloneAtTag = cloneAtTag;
|
|
6
|
+
exports.isNewer = isNewer;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const semver_1 = require("semver");
|
|
9
|
+
function getLatestTag(coreRepo) {
|
|
10
|
+
try {
|
|
11
|
+
const repoUrl = `https://github.com/${coreRepo}.git`;
|
|
12
|
+
const output = (0, child_process_1.execSync)(`git ls-remote --tags --sort=-v:refname ${repoUrl}`, {
|
|
13
|
+
encoding: "utf-8",
|
|
14
|
+
timeout: 15000,
|
|
15
|
+
});
|
|
16
|
+
for (const line of output.trim().split("\n")) {
|
|
17
|
+
if (!line)
|
|
18
|
+
continue;
|
|
19
|
+
const ref = line.split("\t")[1];
|
|
20
|
+
if (!ref || ref.endsWith("^{}"))
|
|
21
|
+
continue;
|
|
22
|
+
const tag = ref.replace("refs/tags/", "");
|
|
23
|
+
const version = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
24
|
+
if ((0, semver_1.valid)(version))
|
|
25
|
+
return version;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
console.error("Error: Could not reach baseline-core repo.");
|
|
31
|
+
console.error("Check your network connection and repo access.");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function fetchAndExtract(coreRepo, tag, destDir) {
|
|
36
|
+
const repoUrl = `https://github.com/${coreRepo}.git`;
|
|
37
|
+
const vTag = `v${tag}`;
|
|
38
|
+
(0, child_process_1.execSync)(`git archive --remote=${repoUrl} ${vTag} 2>/dev/null || git clone --depth 1 --branch ${vTag} ${repoUrl} /tmp/baseline-core-fetch`, { encoding: "utf-8", timeout: 60000, stdio: "pipe" });
|
|
39
|
+
}
|
|
40
|
+
/** Clone a specific tag to a temp dir and return the path */
|
|
41
|
+
function cloneAtTag(coreRepo, tag) {
|
|
42
|
+
const repoUrl = `https://github.com/${coreRepo}.git`;
|
|
43
|
+
const vTag = `v${tag}`;
|
|
44
|
+
const tmpDir = `/tmp/baseline-core-${Date.now()}`;
|
|
45
|
+
(0, child_process_1.execSync)(`git clone --depth 1 --branch ${vTag} ${repoUrl} ${tmpDir}`, {
|
|
46
|
+
encoding: "utf-8",
|
|
47
|
+
timeout: 60000,
|
|
48
|
+
stdio: "pipe",
|
|
49
|
+
});
|
|
50
|
+
return tmpDir;
|
|
51
|
+
}
|
|
52
|
+
function isNewer(latest, current) {
|
|
53
|
+
return (0, semver_1.compare)(latest, current) > 0;
|
|
54
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commander_1 = require("commander");
|
|
4
|
+
const status_js_1 = require("./commands/status.js");
|
|
5
|
+
const update_js_1 = require("./commands/update.js");
|
|
6
|
+
const init_js_1 = require("./commands/init.js");
|
|
7
|
+
const context_js_1 = require("./commands/context.js");
|
|
8
|
+
const program = new commander_1.Command();
|
|
9
|
+
program
|
|
10
|
+
.name("baseline")
|
|
11
|
+
.description("Distribute and update the Baseline System")
|
|
12
|
+
.version("2.2.0");
|
|
13
|
+
program
|
|
14
|
+
.command("status")
|
|
15
|
+
.description("Show current version and check for updates")
|
|
16
|
+
.action(status_js_1.status);
|
|
17
|
+
program
|
|
18
|
+
.command("update")
|
|
19
|
+
.description("Pull latest skills, frameworks, and scripts from baseline-core")
|
|
20
|
+
.action(update_js_1.update);
|
|
21
|
+
program
|
|
22
|
+
.command("init")
|
|
23
|
+
.description("Set up a new client system with guided onboarding")
|
|
24
|
+
.action(init_js_1.init);
|
|
25
|
+
const ctxCmd = program
|
|
26
|
+
.command("context")
|
|
27
|
+
.description("Manage context files")
|
|
28
|
+
.action(() => (0, context_js_1.context)());
|
|
29
|
+
ctxCmd
|
|
30
|
+
.command("add <name>")
|
|
31
|
+
.description("Create a new context file and wire it to skills")
|
|
32
|
+
.action((name) => (0, context_js_1.context)(name));
|
|
33
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@baseline-studio/cli",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "CLI for distributing and updating the Baseline System — an AI-powered workflow system for product teams",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"baseline",
|
|
7
|
+
"ai-system",
|
|
8
|
+
"cli",
|
|
9
|
+
"workflow",
|
|
10
|
+
"product-management",
|
|
11
|
+
"claude-code"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/TrentM6/baseline-core#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/TrentM6/baseline-core.git",
|
|
17
|
+
"directory": "cli"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/TrentM6/baseline-core/issues"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Baseline Studio <trent@baselinestudio.design>",
|
|
24
|
+
"bin": {
|
|
25
|
+
"baseline": "./bin/baseline"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"dist/"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc",
|
|
33
|
+
"dev": "tsc --watch",
|
|
34
|
+
"prepublishOnly": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^12.0.0",
|
|
41
|
+
"js-yaml": "^4.1.0",
|
|
42
|
+
"semver": "^7.6.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/js-yaml": "^4.0.0",
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"@types/semver": "^7.5.0",
|
|
48
|
+
"typescript": "^5.4.0"
|
|
49
|
+
}
|
|
50
|
+
}
|