start-vibing 4.4.3 → 4.4.5
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/dist/cli.js +230 -56
- package/package.json +42 -42
- package/template/.claude/agents/research-query.md +128 -0
- package/template/.claude/agents/research-scout.md +124 -0
- package/template/.claude/agents/research-synthesize.md +139 -0
- package/template/.claude/agents/research-verify.md +84 -0
- package/template/.claude/commands/research.md +18 -0
- package/template/.claude/hooks/research-session-start.sh +4 -0
- package/template/.claude/settings.json +4 -0
- package/template/.claude/skills/research/SKILL.md +285 -0
- package/template/.claude/skills/research/references/domain-playbooks.md +604 -0
- package/template/.claude/skills/research/references/ontology-patterns.md +376 -0
- package/template/.claude/skills/research/references/research-methodology.md +794 -0
- package/template/.claude/skills/research/references/source-directory.md +280 -0
- package/template/.claude/skills/research/scripts/__pycache__/extract-claims.cpython-313.pyc +0 -0
- package/template/.claude/skills/research/scripts/check-cache.sh +129 -0
- package/template/.claude/skills/research/scripts/dedup-research.sh +80 -0
- package/template/.claude/skills/research/scripts/extract-claims.py +83 -0
- package/template/.claude/skills/research/scripts/update-index.sh +106 -0
- package/template/.claude/skills/research/scripts/verify-citations.sh +107 -0
- package/template/.claude/skills/research/templates/adr.md.tpl +66 -0
- package/template/.claude/skills/research/templates/index.md.tpl +25 -0
- package/template/.claude/skills/research/templates/moc.md.tpl +39 -0
- package/template/.claude/skills/research/templates/research-state.schema.json +64 -0
- package/template/.claude/skills/research/templates/research.md.tpl +117 -0
- package/template/.claude/agents/research-web.md +0 -164
package/dist/cli.js
CHANGED
|
@@ -121,15 +121,133 @@ async function copyClaudeSetup(targetDir, options = {}) {
|
|
|
121
121
|
return result;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// src/cleanup.ts
|
|
125
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2, unlinkSync } from "fs";
|
|
126
|
+
import { createHash } from "crypto";
|
|
127
|
+
import { homedir } from "os";
|
|
128
|
+
import { join as join2, resolve, sep } from "path";
|
|
129
|
+
var STALE_FILES = [
|
|
130
|
+
{
|
|
131
|
+
category: "agents",
|
|
132
|
+
relPath: "research-web.md",
|
|
133
|
+
replacedBy: "research skill (scout \u2192 query \u2192 synthesize \u2192 verify)",
|
|
134
|
+
staleChecksums: [
|
|
135
|
+
"49d0cb862d199d1dcb59ed932ed04d8abd0064de3b65ef4f346268528dc21772",
|
|
136
|
+
"72ba14b6cad76a2d8718234775883242e94e82497e4edeec7fb35faf24327d6a",
|
|
137
|
+
"37c6ace245e2d8441e4f472e0123b5e7693e12bb869ced31f304fa17b7c75398",
|
|
138
|
+
"4f50d0080c54a972b6ec7053a6b6e3d36e6b27d14207357db26150707426bb2c"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
category: "agents",
|
|
143
|
+
relPath: "domain-updater.md",
|
|
144
|
+
replacedBy: "documenter agent (removed in start-vibing v4)",
|
|
145
|
+
staleChecksums: ["7499e0dd753805e6e459e687ce905ac3e949f28231e25bb2ab71036043b00f93"]
|
|
146
|
+
}
|
|
147
|
+
];
|
|
148
|
+
var VALID_CATEGORIES = new Set(["agents", "skills", "hooks", "commands"]);
|
|
149
|
+
var MAX_FILE_BYTES = 1e6;
|
|
150
|
+
function sha256OfFile(path) {
|
|
151
|
+
const buf = readFileSync2(path);
|
|
152
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
153
|
+
}
|
|
154
|
+
function isInside(parent, child) {
|
|
155
|
+
const p = resolve(parent) + sep;
|
|
156
|
+
const c = resolve(child);
|
|
157
|
+
return c === resolve(parent) || c.startsWith(p);
|
|
158
|
+
}
|
|
159
|
+
function safeRelPath(rel) {
|
|
160
|
+
if (typeof rel !== "string" || rel.length === 0)
|
|
161
|
+
return null;
|
|
162
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
163
|
+
if (normalized.startsWith("/"))
|
|
164
|
+
return null;
|
|
165
|
+
if (normalized.split("/").some((seg) => seg === ".." || seg === "" || seg === ".")) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return normalized;
|
|
169
|
+
}
|
|
170
|
+
function tryDelete(claudeDir, locationLabel, stale, result) {
|
|
171
|
+
if (!VALID_CATEGORIES.has(stale.category))
|
|
172
|
+
return;
|
|
173
|
+
const safeRel = safeRelPath(stale.relPath);
|
|
174
|
+
if (safeRel === null)
|
|
175
|
+
return;
|
|
176
|
+
const categoryDir = join2(claudeDir, stale.category);
|
|
177
|
+
if (!existsSync2(categoryDir))
|
|
178
|
+
return;
|
|
179
|
+
const fullPath = join2(categoryDir, ...safeRel.split("/"));
|
|
180
|
+
if (!isInside(categoryDir, fullPath))
|
|
181
|
+
return;
|
|
182
|
+
if (!existsSync2(fullPath))
|
|
183
|
+
return;
|
|
184
|
+
let stat;
|
|
185
|
+
try {
|
|
186
|
+
stat = statSync2(fullPath);
|
|
187
|
+
} catch {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!stat.isFile())
|
|
191
|
+
return;
|
|
192
|
+
if (stat.size > MAX_FILE_BYTES)
|
|
193
|
+
return;
|
|
194
|
+
let actual;
|
|
195
|
+
try {
|
|
196
|
+
actual = sha256OfFile(fullPath);
|
|
197
|
+
} catch {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (!stale.staleChecksums.includes(actual)) {
|
|
201
|
+
result.keptCustomized.push({
|
|
202
|
+
file: `${stale.category}/${safeRel}`,
|
|
203
|
+
location: locationLabel
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
let preDeleteCheck;
|
|
208
|
+
try {
|
|
209
|
+
preDeleteCheck = sha256OfFile(fullPath);
|
|
210
|
+
} catch {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (preDeleteCheck !== actual)
|
|
214
|
+
return;
|
|
215
|
+
try {
|
|
216
|
+
unlinkSync(fullPath);
|
|
217
|
+
result.deleted.push({
|
|
218
|
+
file: `${stale.category}/${safeRel}`,
|
|
219
|
+
location: locationLabel,
|
|
220
|
+
replacedBy: stale.replacedBy
|
|
221
|
+
});
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function cleanupStaleAgents(targetDir) {
|
|
226
|
+
const result = { deleted: [], keptCustomized: [] };
|
|
227
|
+
const globalClaude = join2(homedir(), ".claude");
|
|
228
|
+
if (existsSync2(globalClaude)) {
|
|
229
|
+
for (const stale of STALE_FILES) {
|
|
230
|
+
tryDelete(globalClaude, "~/.claude", stale, result);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const projectClaude = join2(targetDir, ".claude");
|
|
234
|
+
if (existsSync2(projectClaude)) {
|
|
235
|
+
for (const stale of STALE_FILES) {
|
|
236
|
+
tryDelete(projectClaude, ".claude", stale, result);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
124
242
|
// src/cli.ts
|
|
125
|
-
import { existsSync as
|
|
126
|
-
import { join as
|
|
243
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, appendFileSync, writeFileSync as writeFileSync5 } from "fs";
|
|
244
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
127
245
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
128
246
|
import { execSync as execSync3 } from "child_process";
|
|
129
247
|
|
|
130
248
|
// src/update.ts
|
|
131
|
-
import { existsSync as
|
|
132
|
-
import { join as
|
|
249
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
250
|
+
import { join as join3 } from "path";
|
|
133
251
|
|
|
134
252
|
// src/platform.ts
|
|
135
253
|
import { platform } from "os";
|
|
@@ -200,8 +318,8 @@ function getHomeDir() {
|
|
|
200
318
|
|
|
201
319
|
// src/update.ts
|
|
202
320
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/start-vibing/latest";
|
|
203
|
-
var CACHE_DIR =
|
|
204
|
-
var CACHE_FILE =
|
|
321
|
+
var CACHE_DIR = join3(getHomeDir(), ".start-vibing-cache");
|
|
322
|
+
var CACHE_FILE = join3(CACHE_DIR, "version-check.json");
|
|
205
323
|
var CACHE_TTL_MS = 60 * 60 * 1000;
|
|
206
324
|
function compareVersions(a, b) {
|
|
207
325
|
const partsA = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -218,10 +336,10 @@ function compareVersions(a, b) {
|
|
|
218
336
|
}
|
|
219
337
|
function readCache() {
|
|
220
338
|
try {
|
|
221
|
-
if (!
|
|
339
|
+
if (!existsSync3(CACHE_FILE)) {
|
|
222
340
|
return null;
|
|
223
341
|
}
|
|
224
|
-
const content =
|
|
342
|
+
const content = readFileSync3(CACHE_FILE, "utf-8");
|
|
225
343
|
const entry = JSON.parse(content);
|
|
226
344
|
if (typeof entry.timestamp !== "number" || typeof entry.latestVersion !== "string") {
|
|
227
345
|
return null;
|
|
@@ -233,7 +351,7 @@ function readCache() {
|
|
|
233
351
|
}
|
|
234
352
|
function writeCache(latestVersion) {
|
|
235
353
|
try {
|
|
236
|
-
if (!
|
|
354
|
+
if (!existsSync3(CACHE_DIR)) {
|
|
237
355
|
mkdirSync2(CACHE_DIR, { recursive: true });
|
|
238
356
|
}
|
|
239
357
|
const entry = {
|
|
@@ -311,9 +429,9 @@ function getUpdateCommand() {
|
|
|
311
429
|
|
|
312
430
|
// src/claude.ts
|
|
313
431
|
import { spawn, execSync as execSync2, spawnSync } from "child_process";
|
|
314
|
-
import { existsSync as
|
|
315
|
-
import { join as
|
|
316
|
-
import { homedir } from "os";
|
|
432
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
433
|
+
import { join as join4 } from "path";
|
|
434
|
+
import { homedir as homedir2 } from "os";
|
|
317
435
|
function isClaudeInstalled() {
|
|
318
436
|
return commandExists("claude");
|
|
319
437
|
}
|
|
@@ -423,11 +541,11 @@ function encodeProjectPath(absolutePath) {
|
|
|
423
541
|
}
|
|
424
542
|
function hasExistingSession(projectDir) {
|
|
425
543
|
const encodedPath = encodeProjectPath(projectDir);
|
|
426
|
-
const sessionDir =
|
|
427
|
-
const indexFile =
|
|
428
|
-
if (
|
|
544
|
+
const sessionDir = join4(homedir2(), ".claude", "projects", encodedPath);
|
|
545
|
+
const indexFile = join4(sessionDir, "sessions-index.json");
|
|
546
|
+
if (existsSync4(indexFile)) {
|
|
429
547
|
try {
|
|
430
|
-
const raw =
|
|
548
|
+
const raw = readFileSync4(indexFile, "utf-8");
|
|
431
549
|
const index = JSON.parse(raw);
|
|
432
550
|
if (Array.isArray(index.entries)) {
|
|
433
551
|
const mainSessions = index.entries.filter((e) => !e.isSidechain);
|
|
@@ -437,7 +555,7 @@ function hasExistingSession(projectDir) {
|
|
|
437
555
|
} catch {
|
|
438
556
|
}
|
|
439
557
|
}
|
|
440
|
-
if (
|
|
558
|
+
if (existsSync4(sessionDir)) {
|
|
441
559
|
try {
|
|
442
560
|
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/;
|
|
443
561
|
return readdirSync2(sessionDir).some((f) => uuidPattern.test(f));
|
|
@@ -479,12 +597,12 @@ function launchClaude(cwd, options = {}) {
|
|
|
479
597
|
}
|
|
480
598
|
function ensureHooksEnabled() {
|
|
481
599
|
try {
|
|
482
|
-
const home =
|
|
483
|
-
const globalSettingsPath =
|
|
484
|
-
if (!
|
|
600
|
+
const home = homedir2();
|
|
601
|
+
const globalSettingsPath = join4(home, ".claude", "settings.json");
|
|
602
|
+
if (!existsSync4(globalSettingsPath)) {
|
|
485
603
|
return { modified: false };
|
|
486
604
|
}
|
|
487
|
-
const content =
|
|
605
|
+
const content = readFileSync4(globalSettingsPath, "utf-8");
|
|
488
606
|
let settings;
|
|
489
607
|
try {
|
|
490
608
|
settings = JSON.parse(content);
|
|
@@ -577,7 +695,7 @@ async function installMcp(server) {
|
|
|
577
695
|
if (isMcpInstalled(server.name)) {
|
|
578
696
|
return { server: server.name, success: true, message: "Already installed", skipped: true };
|
|
579
697
|
}
|
|
580
|
-
return new Promise((
|
|
698
|
+
return new Promise((resolve2) => {
|
|
581
699
|
const args = ["mcp", "add", "-s", "user", server.name, "--", server.command, ...server.args];
|
|
582
700
|
const proc = spawn2("claude", args, {
|
|
583
701
|
shell: true,
|
|
@@ -592,14 +710,14 @@ async function installMcp(server) {
|
|
|
592
710
|
stderr += data.toString();
|
|
593
711
|
});
|
|
594
712
|
proc.on("close", (code) => {
|
|
595
|
-
|
|
713
|
+
resolve2(code === 0 ? { server: server.name, success: true, message: "Installed" } : { server: server.name, success: false, message: stderr || stdout || `Exit code: ${code}` });
|
|
596
714
|
});
|
|
597
715
|
proc.on("error", (err) => {
|
|
598
|
-
|
|
716
|
+
resolve2({ server: server.name, success: false, message: err.message });
|
|
599
717
|
});
|
|
600
718
|
setTimeout(() => {
|
|
601
719
|
proc.kill();
|
|
602
|
-
|
|
720
|
+
resolve2({ server: server.name, success: false, message: "Installation timed out" });
|
|
603
721
|
}, 60000);
|
|
604
722
|
});
|
|
605
723
|
}
|
|
@@ -674,7 +792,7 @@ async function installPlugin(plugin) {
|
|
|
674
792
|
if (isPluginInstalled(plugin.name, plugin.marketplace)) {
|
|
675
793
|
return { plugin: pluginId, success: true, message: "Already installed", skipped: true };
|
|
676
794
|
}
|
|
677
|
-
return new Promise((
|
|
795
|
+
return new Promise((resolve2) => {
|
|
678
796
|
const args = ["plugin", "install", pluginId, "--scope", "user"];
|
|
679
797
|
const proc = spawn3("claude", args, {
|
|
680
798
|
shell: true,
|
|
@@ -695,14 +813,14 @@ async function installPlugin(plugin) {
|
|
|
695
813
|
stderr += data.toString();
|
|
696
814
|
});
|
|
697
815
|
proc.on("close", (code) => {
|
|
698
|
-
|
|
816
|
+
resolve2(code === 0 ? { plugin: pluginId, success: true, message: "Installed" } : { plugin: pluginId, success: false, message: stderr || stdout || `Exit code: ${code}` });
|
|
699
817
|
});
|
|
700
818
|
proc.on("error", (err) => {
|
|
701
|
-
|
|
819
|
+
resolve2({ plugin: pluginId, success: false, message: err.message });
|
|
702
820
|
});
|
|
703
821
|
setTimeout(() => {
|
|
704
822
|
proc.kill();
|
|
705
|
-
|
|
823
|
+
resolve2({ plugin: pluginId, success: false, message: "Installation timed out" });
|
|
706
824
|
}, 30000);
|
|
707
825
|
});
|
|
708
826
|
}
|
|
@@ -729,8 +847,8 @@ function getRecommendedPlugins() {
|
|
|
729
847
|
}
|
|
730
848
|
|
|
731
849
|
// src/skills.ts
|
|
732
|
-
import { existsSync as
|
|
733
|
-
import { join as
|
|
850
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
851
|
+
import { join as join5 } from "path";
|
|
734
852
|
import https from "https";
|
|
735
853
|
var GITHUB_RAW = "https://raw.githubusercontent.com";
|
|
736
854
|
var RECOMMENDED_SKILLS = [
|
|
@@ -753,12 +871,12 @@ var RECOMMENDED_SKILLS = [
|
|
|
753
871
|
{ name: "typeui-paper", url: `${GITHUB_RAW}/bergside/awesome-design-skills/main/skills/paper/SKILL.md`, description: "paper design system" }
|
|
754
872
|
];
|
|
755
873
|
function fetchUrl(url) {
|
|
756
|
-
return new Promise((
|
|
874
|
+
return new Promise((resolve2, reject) => {
|
|
757
875
|
const request = https.get(url, { headers: { "User-Agent": '"start-vibing"' } }, (res) => {
|
|
758
876
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
759
877
|
const location = res.headers["location"];
|
|
760
878
|
if (location) {
|
|
761
|
-
fetchUrl(location).then(
|
|
879
|
+
fetchUrl(location).then(resolve2, reject);
|
|
762
880
|
return;
|
|
763
881
|
}
|
|
764
882
|
}
|
|
@@ -770,7 +888,7 @@ function fetchUrl(url) {
|
|
|
770
888
|
res.on("data", (chunk) => {
|
|
771
889
|
data += chunk;
|
|
772
890
|
});
|
|
773
|
-
res.on("end", () =>
|
|
891
|
+
res.on("end", () => resolve2(data));
|
|
774
892
|
res.on("error", reject);
|
|
775
893
|
});
|
|
776
894
|
request.on("error", reject);
|
|
@@ -782,9 +900,9 @@ function fetchUrl(url) {
|
|
|
782
900
|
}
|
|
783
901
|
async function installSkill(skill) {
|
|
784
902
|
try {
|
|
785
|
-
const skillDir =
|
|
786
|
-
const skillFile =
|
|
787
|
-
if (
|
|
903
|
+
const skillDir = join5(process.cwd(), ".claude", "skills", skill.name);
|
|
904
|
+
const skillFile = join5(skillDir, "SKILL.md");
|
|
905
|
+
if (existsSync5(skillFile)) {
|
|
788
906
|
return { skill: skill.name, success: true, message: "Already installed", skipped: true };
|
|
789
907
|
}
|
|
790
908
|
const content = await fetchUrl(skill.url);
|
|
@@ -912,12 +1030,12 @@ var TOTAL_PHASES = 6;
|
|
|
912
1030
|
function getVersion() {
|
|
913
1031
|
try {
|
|
914
1032
|
const paths = [
|
|
915
|
-
|
|
916
|
-
|
|
1033
|
+
join6(__dirname3, "..", "package.json"),
|
|
1034
|
+
join6(__dirname3, "..", "..", "package.json")
|
|
917
1035
|
];
|
|
918
1036
|
for (const pkgPath of paths) {
|
|
919
|
-
if (
|
|
920
|
-
const pkg = JSON.parse(
|
|
1037
|
+
if (existsSync6(pkgPath)) {
|
|
1038
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
921
1039
|
return pkg.version || "4.0.0";
|
|
922
1040
|
}
|
|
923
1041
|
}
|
|
@@ -927,28 +1045,67 @@ function getVersion() {
|
|
|
927
1045
|
}
|
|
928
1046
|
}
|
|
929
1047
|
var VERSION = getVersion();
|
|
930
|
-
|
|
1048
|
+
var MANAGED_PATHS = [
|
|
1049
|
+
".claude/agents/",
|
|
1050
|
+
".claude/skills/",
|
|
1051
|
+
".claude/commands/",
|
|
1052
|
+
".claude/hooks/",
|
|
1053
|
+
".claude/scripts/",
|
|
1054
|
+
".claude/config/",
|
|
1055
|
+
".claude/settings.json",
|
|
1056
|
+
".claude/settings.local.json",
|
|
1057
|
+
".claude/CLAUDE.md",
|
|
1058
|
+
"CLAUDE.md"
|
|
1059
|
+
];
|
|
1060
|
+
function isPathTracked(targetDir, path) {
|
|
931
1061
|
try {
|
|
932
|
-
|
|
933
|
-
execSync3("git rev-parse --git-dir", { cwd: targetDir, stdio: "pipe" });
|
|
934
|
-
} catch {
|
|
935
|
-
return { success: false, message: "Not a git repository" };
|
|
936
|
-
}
|
|
937
|
-
const status = execSync3("git status --porcelain .claude CLAUDE.md .claude/", {
|
|
1062
|
+
const out = execSync3(`git ls-files -- "${path}"`, {
|
|
938
1063
|
cwd: targetDir,
|
|
939
1064
|
encoding: "utf-8",
|
|
940
1065
|
stdio: ["pipe", "pipe", "pipe"]
|
|
941
1066
|
}).trim();
|
|
942
|
-
|
|
943
|
-
return { success: true, message: "No changes to commit" };
|
|
944
|
-
}
|
|
945
|
-
execSync3("git add .claude/ CLAUDE.md", { cwd: targetDir, stdio: "pipe" });
|
|
946
|
-
execSync3(`git commit --no-verify -m "chore: update Claude Code setup (start-vibing v${VERSION})"`, { cwd: targetDir, stdio: "pipe" });
|
|
947
|
-
return { success: true, message: "Changes committed" };
|
|
1067
|
+
return out.length > 0;
|
|
948
1068
|
} catch {
|
|
949
|
-
return
|
|
1069
|
+
return false;
|
|
950
1070
|
}
|
|
951
1071
|
}
|
|
1072
|
+
function syncGitignore(targetDir) {
|
|
1073
|
+
try {
|
|
1074
|
+
execSync3("git rev-parse --git-dir", { cwd: targetDir, stdio: "pipe" });
|
|
1075
|
+
} catch {
|
|
1076
|
+
return { success: false, added: [], tracked: [], message: "Not a git repository" };
|
|
1077
|
+
}
|
|
1078
|
+
const toIgnore = [];
|
|
1079
|
+
const tracked = [];
|
|
1080
|
+
for (const p of MANAGED_PATHS) {
|
|
1081
|
+
if (isPathTracked(targetDir, p))
|
|
1082
|
+
tracked.push(p);
|
|
1083
|
+
else
|
|
1084
|
+
toIgnore.push(p);
|
|
1085
|
+
}
|
|
1086
|
+
const gitignorePath = join6(targetDir, ".gitignore");
|
|
1087
|
+
const header = "# start-vibing (local Claude setup - not tracked in this repo)";
|
|
1088
|
+
let current = "";
|
|
1089
|
+
if (existsSync6(gitignorePath))
|
|
1090
|
+
current = readFileSync5(gitignorePath, "utf-8");
|
|
1091
|
+
const existing = new Set(current.split(/\r?\n/).map((l) => l.trim()).filter(Boolean));
|
|
1092
|
+
const missing = toIgnore.filter((e) => !existing.has(e));
|
|
1093
|
+
if (missing.length === 0) {
|
|
1094
|
+
return { success: true, added: [], tracked, message: "Already in .gitignore" };
|
|
1095
|
+
}
|
|
1096
|
+
if (current.length === 0) {
|
|
1097
|
+
writeFileSync5(gitignorePath, `${header}\n${missing.join("\n")}\n`, "utf-8");
|
|
1098
|
+
} else {
|
|
1099
|
+
const prefix = current.endsWith("\n") ? "" : "\n";
|
|
1100
|
+
appendFileSync(gitignorePath, `${prefix}\n${header}\n${missing.join("\n")}\n`, "utf-8");
|
|
1101
|
+
}
|
|
1102
|
+
return {
|
|
1103
|
+
success: true,
|
|
1104
|
+
added: missing,
|
|
1105
|
+
tracked,
|
|
1106
|
+
message: `Added ${missing.length} entr${missing.length === 1 ? "y" : "ies"} to .gitignore`
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
952
1109
|
var HELP = `${createBanner(VERSION)}
|
|
953
1110
|
Setup Claude Code agents, skills, plugins, and MCP servers.
|
|
954
1111
|
|
|
@@ -1022,13 +1179,30 @@ async function main() {
|
|
|
1022
1179
|
const spinner1 = createSpinner(phaseHeader(1, TOTAL_PHASES, "Copying template files..."));
|
|
1023
1180
|
try {
|
|
1024
1181
|
const result = await copyClaudeSetup(targetDir, { force });
|
|
1025
|
-
|
|
1182
|
+
const cleanup = cleanupStaleAgents(targetDir);
|
|
1183
|
+
const ignoreResult = syncGitignore(targetDir);
|
|
1026
1184
|
ensureHooksEnabled();
|
|
1027
1185
|
const counts = `${result.agents} agents, ${result.skills} skills`;
|
|
1028
1186
|
spinner1.succeed(phaseHeader(1, TOTAL_PHASES, `Template files ${c.dim}${"\xB7".repeat(14)}${c.reset} ${counts} ${c.dim}${formatElapsed(phase1Start)}${c.reset}`));
|
|
1187
|
+
if (ignoreResult.success && ignoreResult.added.length > 0) {
|
|
1188
|
+
console.log(` ${c.dim}gitignored ${ignoreResult.added.length} path(s) not tracked upstream (remove from .gitignore to commit)${c.reset}`);
|
|
1189
|
+
}
|
|
1190
|
+
if (ignoreResult.success && ignoreResult.tracked.length > 0) {
|
|
1191
|
+
console.log(` ${c.dim}left ${ignoreResult.tracked.length} tracked path(s) alone (e.g. shared settings)${c.reset}`);
|
|
1192
|
+
}
|
|
1029
1193
|
if (result.preserved > 0) {
|
|
1030
1194
|
console.log(` ${c.dim}(preserved ${result.preserved} custom file(s))${c.reset}`);
|
|
1031
1195
|
}
|
|
1196
|
+
if (cleanup.deleted.length > 0) {
|
|
1197
|
+
for (const d of cleanup.deleted) {
|
|
1198
|
+
console.log(` ${c.yellow}removed${c.reset} ${d.location}/${d.file} ${c.dim}-> ${d.replacedBy}${c.reset}`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (cleanup.keptCustomized.length > 0) {
|
|
1202
|
+
for (const k of cleanup.keptCustomized) {
|
|
1203
|
+
console.log(` ${c.dim}kept customized ${k.location}/${k.file} (checksum diverges from any known stale version)${c.reset}`);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1032
1206
|
} catch (error) {
|
|
1033
1207
|
spinner1.fail(phaseHeader(1, TOTAL_PHASES, `Template files failed`));
|
|
1034
1208
|
console.error(` ${c.red}${error}${c.reset}`);
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "start-vibing",
|
|
3
|
-
"version": "4.4.
|
|
4
|
-
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. e2e-audit 0.2.0 refactor (skill-only, no agents): SessionStart hook + slash command make the skill keyword-invokable (\"e2e audit\", \"roda o e2e\", \"integration test\", \"test coverage gaps\"). Source-first discovery via detect-stack, discover-routes (Next app/pages/Remix/SvelteKit/Nuxt/Astro), discover-api-surface (HTTP handlers, tRPC procedures, GraphQL, server actions, middleware auth), inventory-existing-tests (preserve prior corpus + sha256 drift hash), and detect-uncovered (branch-diff vs origin/main finds changes not covered by existing specs). Report-then-ask between mapping and Playwright run; post-run-feedback report before writing findings. SHOT+TRACE+ASSERT+SOURCE evidence quad per non-meta finding; meta rules (coverage-gap-*, uncovered-*, test-drift, stack-detect, post-run-feedback) exempt. verify-audit.sh enforces schema + quad. Generic (no project leakage). super-design 0.7.0 carries over.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"start-vibing": "./dist/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"template"
|
|
12
|
-
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "bun build ./src/cli.ts --outdir ./dist --target node",
|
|
15
|
-
"dev": "bun run ./src/cli.ts",
|
|
16
|
-
"prepublishOnly": "bun run build"
|
|
17
|
-
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"claude",
|
|
20
|
-
"claude-code",
|
|
21
|
-
"ai",
|
|
22
|
-
"agents",
|
|
23
|
-
"skills",
|
|
24
|
-
"hooks",
|
|
25
|
-
"automation",
|
|
26
|
-
"workflow",
|
|
27
|
-
"cli"
|
|
28
|
-
],
|
|
29
|
-
"author": "joaov",
|
|
30
|
-
"license": "MIT",
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "https://github.com/LimaTechnologies/ai-development"
|
|
34
|
-
},
|
|
35
|
-
"engines": {
|
|
36
|
-
"node": ">=18.0.0"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"@types/node": "^20.0.0",
|
|
40
|
-
"typescript": "^5.0.0"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "start-vibing",
|
|
3
|
+
"version": "4.4.5",
|
|
4
|
+
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. e2e-audit 0.2.0 refactor (skill-only, no agents): SessionStart hook + slash command make the skill keyword-invokable (\"e2e audit\", \"roda o e2e\", \"integration test\", \"test coverage gaps\"). Source-first discovery via detect-stack, discover-routes (Next app/pages/Remix/SvelteKit/Nuxt/Astro), discover-api-surface (HTTP handlers, tRPC procedures, GraphQL, server actions, middleware auth), inventory-existing-tests (preserve prior corpus + sha256 drift hash), and detect-uncovered (branch-diff vs origin/main finds changes not covered by existing specs). Report-then-ask between mapping and Playwright run; post-run-feedback report before writing findings. SHOT+TRACE+ASSERT+SOURCE evidence quad per non-meta finding; meta rules (coverage-gap-*, uncovered-*, test-drift, stack-detect, post-run-feedback) exempt. verify-audit.sh enforces schema + quad. Generic (no project leakage). super-design 0.7.0 carries over.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"start-vibing": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "bun build ./src/cli.ts --outdir ./dist --target node",
|
|
15
|
+
"dev": "bun run ./src/cli.ts",
|
|
16
|
+
"prepublishOnly": "bun run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"claude",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"ai",
|
|
22
|
+
"agents",
|
|
23
|
+
"skills",
|
|
24
|
+
"hooks",
|
|
25
|
+
"automation",
|
|
26
|
+
"workflow",
|
|
27
|
+
"cli"
|
|
28
|
+
],
|
|
29
|
+
"author": "joaov",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/LimaTechnologies/ai-development"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: research-query
|
|
3
|
+
description: Executes the research plan from scout-plan.json. Runs parallel WebSearch + WebFetch + context7 lookups, extracts atomic claims with URL+QUOTE+ACCESSED-AT evidence, and writes claims.jsonl + sources.jsonl to the session directory. Honors per-domain authority hierarchies from references/source-directory.md and per-bucket freshness windows from research-methodology.md §7.
|
|
4
|
+
tools: Read, Write, Glob, Grep, Bash, WebSearch, WebFetch
|
|
5
|
+
model: sonnet
|
|
6
|
+
color: blue
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Role
|
|
10
|
+
|
|
11
|
+
You are the query executor. You take a scout-plan and turn it into raw
|
|
12
|
+
evidence: a stream of atomic claims with verifiable citations. You do
|
|
13
|
+
**not** triangulate or synthesize — that is the next agent's job. You
|
|
14
|
+
optimize for evidence density and citation integrity.
|
|
15
|
+
|
|
16
|
+
# When invoked
|
|
17
|
+
|
|
18
|
+
You receive: `$SESSION_DIR/scout-plan.json` + the path to
|
|
19
|
+
`/docs/research/.cache/sessions/<id>/`.
|
|
20
|
+
|
|
21
|
+
# Steps
|
|
22
|
+
|
|
23
|
+
## 1. Load context
|
|
24
|
+
|
|
25
|
+
Read:
|
|
26
|
+
|
|
27
|
+
- `$SESSION_DIR/scout-plan.json`
|
|
28
|
+
- `.claude/skills/research/references/source-directory.md` (the domain table for `scout.domain`)
|
|
29
|
+
- `.claude/skills/research/references/research-methodology.md` §5 (query engineering) and §7 (freshness)
|
|
30
|
+
- The relevant playbook from `.claude/skills/research/references/domain-playbooks.md`
|
|
31
|
+
|
|
32
|
+
## 2. Build the query plan
|
|
33
|
+
|
|
34
|
+
For each sub-question in `scout.decomposition`, generate 2–4 search
|
|
35
|
+
queries using the templates in §5 of research-methodology.md:
|
|
36
|
+
|
|
37
|
+
- Boolean: `("RSC" OR "React Server Components") AND "data fetching"`
|
|
38
|
+
- Time-boxed: `after:2025-01-01`
|
|
39
|
+
- Site-narrowed: `site:nextjs.org` for canonical, `site:react.dev`, `site:github.com`
|
|
40
|
+
- Negative-space: `"X disadvantages"`, `"X alternatives"`
|
|
41
|
+
- Authority-first: query official docs and IETF/W3C/ECMA before blog aggregators
|
|
42
|
+
|
|
43
|
+
Cap total queries at `scout.estimated_queries × 1.25`. Stop early if
|
|
44
|
+
diminishing returns (3 consecutive queries return only republications).
|
|
45
|
+
|
|
46
|
+
## 3. Execute searches in PARALLEL
|
|
47
|
+
|
|
48
|
+
Use multiple `WebSearch` calls in a single message when sub-questions
|
|
49
|
+
are independent. Collect all result URLs into a candidate pool.
|
|
50
|
+
|
|
51
|
+
## 4. Filter by authority
|
|
52
|
+
|
|
53
|
+
Per `source-directory.md`, rank candidates 1–5 by authority. Drop level-1
|
|
54
|
+
SEO-farm domains unless they are the only source for a niche claim
|
|
55
|
+
(then add `quality_warning: "low-authority-only-source"` in the claim).
|
|
56
|
+
|
|
57
|
+
## 5. Fetch + snapshot
|
|
58
|
+
|
|
59
|
+
For each high-authority candidate, run `WebFetch` with a focused prompt
|
|
60
|
+
("extract the section that addresses <sub-question>, return verbatim
|
|
61
|
+
quotes with their headings"). Save the raw markdown response to
|
|
62
|
+
`$SESSION_DIR/snapshots/<n>.md` (used later by verify-citations.sh for
|
|
63
|
+
quote-grep verification).
|
|
64
|
+
|
|
65
|
+
For library/framework docs, prefer `mcp__context7__query-docs` over
|
|
66
|
+
WebFetch — it's already structured.
|
|
67
|
+
|
|
68
|
+
## 6. Extract atomic claims
|
|
69
|
+
|
|
70
|
+
For each fetched source, extract 1–8 atomic claims. Each claim:
|
|
71
|
+
|
|
72
|
+
```jsonc
|
|
73
|
+
{
|
|
74
|
+
"id": "C-0042",
|
|
75
|
+
"sub_question": "How does Promise.all interact with cache()?",
|
|
76
|
+
"assertion": "Calling fetch inside cache() within Promise.all parallelizes requests but cache key is the deduped URL.",
|
|
77
|
+
"source_id": "S-0007",
|
|
78
|
+
"quote": "verbatim string from the source — must be greppable in snapshots/<n>.md",
|
|
79
|
+
"quote_location": "section heading or paragraph anchor",
|
|
80
|
+
"verify_method": "web-fetch",
|
|
81
|
+
"confidence_signal": "official-docs",
|
|
82
|
+
"tags": ["nextjs", "rsc", "data-fetching"],
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Append one JSON per line to `$SESSION_DIR/claims.jsonl`.
|
|
87
|
+
|
|
88
|
+
## 7. Record sources
|
|
89
|
+
|
|
90
|
+
For each unique source, write to `$SESSION_DIR/sources.jsonl`:
|
|
91
|
+
|
|
92
|
+
```jsonc
|
|
93
|
+
{
|
|
94
|
+
"id": "S-0007",
|
|
95
|
+
"url": "https://nextjs.org/docs/app/building-your-application/caching",
|
|
96
|
+
"title": "Caching | Next.js",
|
|
97
|
+
"publisher": "Vercel",
|
|
98
|
+
"publisher_independence": "vendor", // wire | primary-journalism | vendor | academic | community | seo-content
|
|
99
|
+
"author": null,
|
|
100
|
+
"doi": null,
|
|
101
|
+
"published_at": "2024-11-03",
|
|
102
|
+
"accessed_at": "2026-04-25T13:45:11Z",
|
|
103
|
+
"authority_level": 5,
|
|
104
|
+
"snapshot_path": ".cache/sessions/<id>/snapshots/7.md",
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 8. Independence check
|
|
109
|
+
|
|
110
|
+
Before exiting, group sources by `publisher` and ownership tree (per
|
|
111
|
+
source-directory.md "AI content red flags" section). If a claim's
|
|
112
|
+
sources all belong to the same ownership/wire chain, mark the claim
|
|
113
|
+
`triangulation_warning: "single-ownership-cluster"`.
|
|
114
|
+
|
|
115
|
+
## 9. Return summary
|
|
116
|
+
|
|
117
|
+
≤5 lines: claim count, source count, distinct ownership clusters,
|
|
118
|
+
warnings. Hand off to research-synthesize.
|
|
119
|
+
|
|
120
|
+
# Hard rules
|
|
121
|
+
|
|
122
|
+
1. **Every claim has a verbatim QUOTE that is greppable in its snapshot.** No paraphrase-only claims.
|
|
123
|
+
2. **Every URL must HTTP 200 at fetch time.** If WebFetch fails, drop the claim and log to `$SESSION_DIR/fetch-errors.log`.
|
|
124
|
+
3. **Never invent sources.** If you cannot fetch, you cannot cite.
|
|
125
|
+
4. **Honor freshness window** from `scout.freshness_window_days`. Sources older than the window get `freshness_warning: true` and require explicit reasoning to keep.
|
|
126
|
+
5. **Parallelize** — independent WebSearch calls go in one message.
|
|
127
|
+
6. **Stop at claims.jsonl.** Do not write to `/docs/research/<slug>.md`.
|
|
128
|
+
7. **Snapshots are mandatory** — they are the evidence the verify agent will grep.
|