shmakk 1.2.4 → 1.2.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/.env.example +11 -0
- package/README.md +75 -1
- package/docs/index.html +154 -16
- package/docs/mcp.md +78 -0
- package/docs/ssh.md +82 -0
- package/docs/vibedit-analysis.md +375 -0
- package/docs/vim.md +110 -0
- package/docs/voice.md +4 -0
- package/package.json +9 -5
- package/scripts/test-vibedit.js +45 -0
- package/scripts/vibedit-demo.sh +52 -0
- package/skills/shmakk-skill-creator.md +269 -0
- package/src/_check.js +7 -0
- package/src/_check_schema.js +5 -0
- package/src/_cleanup.js +18 -0
- package/src/_fix.js +9 -0
- package/src/_test_import.js +15 -0
- package/src/agent.js +11 -4
- package/src/browser-daemon.js +209 -0
- package/src/browser.js +10 -0
- package/src/cli/browserDaemon.js +60 -0
- package/src/cli/connectBrowser.js +137 -0
- package/src/cli.js +235 -8
- package/src/completions.js +8 -0
- package/src/control.js +273 -1
- package/src/core/browserConnector.js +523 -0
- package/src/electron.js +305 -0
- package/src/endpoints.js +74 -9
- package/src/index.js +24 -1
- package/src/llm.js +501 -61
- package/src/mobile.js +307 -0
- package/src/notify.js +51 -3
- package/src/orchestrator.js +35 -1
- package/src/pty.js +11 -6
- package/src/review.js +45 -11
- package/src/self-commands.js +153 -0
- package/src/session-convert.js +508 -0
- package/src/session-search.js +31 -0
- package/src/session.js +384 -46
- package/src/skills/browserActions.ts +984 -0
- package/src/skills.js +451 -24
- package/src/system-prompt.js +31 -25
- package/src/tools.js +81 -0
- package/src/vibedit/control.js +534 -0
- package/src/vibedit/electron.js +108 -0
- package/src/vibedit/files.js +171 -0
- package/src/vibedit/index.js +298 -0
- package/src/vibedit/overlay.js +1482 -0
- package/src/vibedit/prompts.js +245 -0
- package/src/vibedit/state.js +32 -0
- package/src/vim.js +410 -0
package/src/control.js
CHANGED
|
@@ -439,4 +439,276 @@ function mcpStatus() {
|
|
|
439
439
|
return 0;
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
-
|
|
442
|
+
function consolidateWorkspace() {
|
|
443
|
+
const fs = require('fs');
|
|
444
|
+
const path = require('path');
|
|
445
|
+
|
|
446
|
+
const cwd = process.cwd();
|
|
447
|
+
const rootShmakk = path.join(cwd, '.shmakk');
|
|
448
|
+
const rootSkills = path.join(rootShmakk, 'skills');
|
|
449
|
+
const rootState = path.join(rootShmakk, 'state');
|
|
450
|
+
|
|
451
|
+
// Ensure root .shmakk dirs exist
|
|
452
|
+
fs.mkdirSync(rootShmakk, { recursive: true });
|
|
453
|
+
fs.mkdirSync(rootSkills, { recursive: true });
|
|
454
|
+
fs.mkdirSync(rootState, { recursive: true });
|
|
455
|
+
|
|
456
|
+
// Find all nested .shmakk dirs (skip root itself, skip node_modules)
|
|
457
|
+
const nested = [];
|
|
458
|
+
function walk(dir, depth) {
|
|
459
|
+
if (depth > 50) return; // safety limit
|
|
460
|
+
let entries;
|
|
461
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
462
|
+
for (const e of entries) {
|
|
463
|
+
if (!e.isDirectory()) continue;
|
|
464
|
+
if (e.name === 'node_modules' || e.name === '.git') continue;
|
|
465
|
+
const full = path.join(dir, e.name);
|
|
466
|
+
if (e.name === '.shmakk' && depth > 0) {
|
|
467
|
+
nested.push(full);
|
|
468
|
+
} else if (!e.name.startsWith('.')) {
|
|
469
|
+
walk(full, depth + 1);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
walk(cwd, 0);
|
|
474
|
+
|
|
475
|
+
if (!nested.length) {
|
|
476
|
+
process.stdout.write('shmakk: no nested .shmakk directories found to consolidate\n');
|
|
477
|
+
return 1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
process.stdout.write(`shmakk: found ${nested.length} nested .shmakk director${nested.length > 1 ? 'ies' : 'y'}\n`);
|
|
481
|
+
|
|
482
|
+
let mergedSkills = 0;
|
|
483
|
+
let mergedMemory = 0;
|
|
484
|
+
let mergedRules = 0;
|
|
485
|
+
let mergedMcp = 0;
|
|
486
|
+
let mergedHosts = 0;
|
|
487
|
+
let mergedState = 0;
|
|
488
|
+
|
|
489
|
+
// Helper: read file if exists
|
|
490
|
+
const read = (p) => { try { return fs.readFileSync(p, 'utf8'); } catch { return null; } };
|
|
491
|
+
|
|
492
|
+
// Helper: read JSON if exists
|
|
493
|
+
const readJSON = (p) => { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; } };
|
|
494
|
+
|
|
495
|
+
// Helper: write JSON
|
|
496
|
+
const writeJSON = (p, obj) => fs.writeFileSync(p, JSON.stringify(obj, null, 2) + '\n');
|
|
497
|
+
|
|
498
|
+
// Helper: append line-deduplicated content (for memory.md, rules.md)
|
|
499
|
+
function mergeLines(rootFile, nestedFile) {
|
|
500
|
+
const rootContent = read(rootFile) || '';
|
|
501
|
+
const rootLines = new Set(rootContent.split('\n').map(l => l.trim()).filter(Boolean));
|
|
502
|
+
const nestedContent = read(nestedFile);
|
|
503
|
+
if (!nestedContent) return 0;
|
|
504
|
+
const newLines = nestedContent.split('\n')
|
|
505
|
+
.map(l => l.trim())
|
|
506
|
+
.filter(l => Boolean(l) && !rootLines.has(l));
|
|
507
|
+
if (!newLines.length) return 0;
|
|
508
|
+
const append = (rootContent.endsWith('\n') ? '' : '\n') + newLines.join('\n') + '\n';
|
|
509
|
+
fs.appendFileSync(rootFile, append);
|
|
510
|
+
return newLines.length;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Helper: merge JSON objects shallowly (root wins)
|
|
514
|
+
function mergeJSON(rootFile, nestedFile) {
|
|
515
|
+
const root = readJSON(rootFile) || {};
|
|
516
|
+
const nested = readJSON(nestedFile);
|
|
517
|
+
if (!nested) return 0;
|
|
518
|
+
let added = 0;
|
|
519
|
+
for (const [k, v] of Object.entries(nested)) {
|
|
520
|
+
if (!(k in root)) { root[k] = v; added++; }
|
|
521
|
+
}
|
|
522
|
+
if (added > 0) writeJSON(rootFile, root);
|
|
523
|
+
return added;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
for (const nsm of nested) {
|
|
527
|
+
const rel = path.relative(cwd, path.dirname(nsm)) || '(root)';
|
|
528
|
+
process.stdout.write(` merging ${rel}/.shmakk/\n`);
|
|
529
|
+
|
|
530
|
+
// memory.md
|
|
531
|
+
const memRoot = path.join(rootShmakk, 'memory.md');
|
|
532
|
+
const memNested = path.join(nsm, 'memory.md');
|
|
533
|
+
const memAdded = mergeLines(memRoot, memNested);
|
|
534
|
+
if (memAdded) process.stdout.write(` memory.md: +${memAdded} line(s)\n`);
|
|
535
|
+
mergedMemory += memAdded;
|
|
536
|
+
|
|
537
|
+
// rules.md
|
|
538
|
+
const rulesRoot = path.join(rootShmakk, 'rules.md');
|
|
539
|
+
const rulesNested = path.join(nsm, 'rules.md');
|
|
540
|
+
const rulesAdded = mergeLines(rulesRoot, rulesNested);
|
|
541
|
+
if (rulesAdded) process.stdout.write(` rules.md: +${rulesAdded} line(s)\n`);
|
|
542
|
+
mergedRules += rulesAdded;
|
|
543
|
+
|
|
544
|
+
// skills/*.md
|
|
545
|
+
const skillsNested = path.join(nsm, 'skills');
|
|
546
|
+
if (fs.existsSync(skillsNested)) {
|
|
547
|
+
let entries;
|
|
548
|
+
try { entries = fs.readdirSync(skillsNested, { withFileTypes: true }); } catch { entries = []; }
|
|
549
|
+
for (const e of entries) {
|
|
550
|
+
if (e.isFile() && e.name.endsWith('.md')) {
|
|
551
|
+
const dst = path.join(rootSkills, e.name);
|
|
552
|
+
if (!fs.existsSync(dst)) {
|
|
553
|
+
fs.copyFileSync(path.join(skillsNested, e.name), dst);
|
|
554
|
+
process.stdout.write(` skills/${e.name}: copied\n`);
|
|
555
|
+
mergedSkills++;
|
|
556
|
+
}
|
|
557
|
+
} else if (e.isDirectory()) {
|
|
558
|
+
const skillFile = path.join(skillsNested, e.name, 'SKILL.md');
|
|
559
|
+
const dstDir = path.join(rootSkills, e.name);
|
|
560
|
+
const dstFile = path.join(dstDir, 'SKILL.md');
|
|
561
|
+
if (fs.existsSync(skillFile) && !fs.existsSync(dstFile)) {
|
|
562
|
+
fs.mkdirSync(dstDir, { recursive: true });
|
|
563
|
+
fs.copyFileSync(skillFile, dstFile);
|
|
564
|
+
// Also copy sibling files if any
|
|
565
|
+
let subEntries;
|
|
566
|
+
try { subEntries = fs.readdirSync(path.join(skillsNested, e.name)); } catch { subEntries = []; }
|
|
567
|
+
for (const se of subEntries) {
|
|
568
|
+
const srcSub = path.join(skillsNested, e.name, se);
|
|
569
|
+
const dstSub = path.join(dstDir, se);
|
|
570
|
+
if (se !== 'SKILL.md' && !fs.existsSync(dstSub)) {
|
|
571
|
+
try { fs.copyFileSync(srcSub, dstSub); } catch {}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
process.stdout.write(` skills/${e.name}/: copied\n`);
|
|
575
|
+
mergedSkills++;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// mcp.json
|
|
582
|
+
const mcpRoot = path.join(rootShmakk, 'mcp.json');
|
|
583
|
+
const mcpNested = path.join(nsm, 'mcp.json');
|
|
584
|
+
const mcpRootObj = readJSON(mcpRoot) || {};
|
|
585
|
+
const mcpNestedObj = readJSON(mcpNested);
|
|
586
|
+
if (mcpNestedObj?.mcpServers) {
|
|
587
|
+
if (!mcpRootObj.mcpServers) mcpRootObj.mcpServers = {};
|
|
588
|
+
let added = 0;
|
|
589
|
+
for (const [k, v] of Object.entries(mcpNestedObj.mcpServers)) {
|
|
590
|
+
if (!(k in mcpRootObj.mcpServers)) {
|
|
591
|
+
mcpRootObj.mcpServers[k] = v;
|
|
592
|
+
added++;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (added) {
|
|
596
|
+
writeJSON(mcpRoot, mcpRootObj);
|
|
597
|
+
process.stdout.write(` mcp.json: +${added} server(s)\n`);
|
|
598
|
+
mergedMcp += added;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// hosts.json
|
|
603
|
+
const hostsRoot = path.join(rootShmakk, 'hosts.json');
|
|
604
|
+
const hostsNested = path.join(nsm, 'hosts.json');
|
|
605
|
+
const hAdded = mergeJSON(hostsRoot, hostsNested);
|
|
606
|
+
if (hAdded) {
|
|
607
|
+
process.stdout.write(` hosts.json: +${hAdded} host(s)\n`);
|
|
608
|
+
mergedHosts += hAdded;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// state/* — merge/copy all state files from nested into root
|
|
612
|
+
const stateNested = path.join(nsm, 'state');
|
|
613
|
+
if (fs.existsSync(stateNested)) {
|
|
614
|
+
let stateEntries;
|
|
615
|
+
try { stateEntries = fs.readdirSync(stateNested, { withFileTypes: true }); } catch { stateEntries = []; }
|
|
616
|
+
for (const se of stateEntries) {
|
|
617
|
+
if (!se.isFile()) continue;
|
|
618
|
+
const src = path.join(stateNested, se.name);
|
|
619
|
+
const dst = path.join(rootState, se.name);
|
|
620
|
+
|
|
621
|
+
// command-freq.json: merge maps, root wins
|
|
622
|
+
if (se.name === 'command-freq.json') {
|
|
623
|
+
const rootObj = readJSON(dst) || {};
|
|
624
|
+
const nestedObj = readJSON(src);
|
|
625
|
+
if (nestedObj) {
|
|
626
|
+
let added = 0;
|
|
627
|
+
for (const [k, v] of Object.entries(nestedObj)) {
|
|
628
|
+
if (!(k in rootObj)) { rootObj[k] = v; added++; }
|
|
629
|
+
}
|
|
630
|
+
if (added) {
|
|
631
|
+
writeJSON(dst, rootObj);
|
|
632
|
+
process.stdout.write(` state/${se.name}: +${added} command(s)\n`);
|
|
633
|
+
mergedState += added;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// task-journal.json: merge arrays deduped by id
|
|
640
|
+
if (se.name === 'task-journal.json') {
|
|
641
|
+
const rootArr = readJSON(dst) || [];
|
|
642
|
+
const nestedArr = readJSON(src);
|
|
643
|
+
if (Array.isArray(nestedArr) && nestedArr.length) {
|
|
644
|
+
const rootIds = new Set(rootArr.map(e => e.id || JSON.stringify(e)));
|
|
645
|
+
const newEntries = nestedArr.filter(e => !rootIds.has(e.id || JSON.stringify(e)));
|
|
646
|
+
if (newEntries.length) {
|
|
647
|
+
rootArr.push(...newEntries);
|
|
648
|
+
writeJSON(dst, rootArr);
|
|
649
|
+
process.stdout.write(` state/${se.name}: +${newEntries.length} task(s)\n`);
|
|
650
|
+
mergedState += newEntries.length;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Any other JSON file: shallow merge, root wins
|
|
657
|
+
if (se.name.endsWith('.json')) {
|
|
658
|
+
const rootObj = readJSON(dst) || {};
|
|
659
|
+
const nestedObj = readJSON(src);
|
|
660
|
+
if (nestedObj && typeof nestedObj === 'object') {
|
|
661
|
+
let added = 0;
|
|
662
|
+
if (Array.isArray(nestedObj)) {
|
|
663
|
+
const rootJson = JSON.stringify(rootObj);
|
|
664
|
+
for (const item of nestedObj) {
|
|
665
|
+
if (rootJson.indexOf(JSON.stringify(item)) === -1) {
|
|
666
|
+
rootObj.push(item);
|
|
667
|
+
added++;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
} else {
|
|
671
|
+
for (const [k, v] of Object.entries(nestedObj)) {
|
|
672
|
+
if (!(k in rootObj)) { rootObj[k] = v; added++; }
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (added) {
|
|
676
|
+
writeJSON(dst, rootObj);
|
|
677
|
+
process.stdout.write(` state/${se.name}: +${added} entry(ies)\n`);
|
|
678
|
+
mergedState += added;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Non-JSON files: copy if not already in root
|
|
685
|
+
if (!fs.existsSync(dst)) {
|
|
686
|
+
fs.copyFileSync(src, dst);
|
|
687
|
+
process.stdout.write(` state/${se.name}: copied\n`);
|
|
688
|
+
mergedState++;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Remove nested .shmakk dirs after successful merge
|
|
695
|
+
for (const nsm of nested) {
|
|
696
|
+
try { fs.rmSync(nsm, { recursive: true, force: true }); } catch {}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Summary
|
|
700
|
+
const total = mergedMemory + mergedRules + mergedSkills + mergedMcp + mergedHosts + mergedState;
|
|
701
|
+
process.stdout.write('\nshmakk: consolidation complete\n');
|
|
702
|
+
process.stdout.write(` memory: ${mergedMemory} line(s)\n`);
|
|
703
|
+
process.stdout.write(` rules: ${mergedRules} line(s)\n`);
|
|
704
|
+
process.stdout.write(` skills: ${mergedSkills} file(s)\n`);
|
|
705
|
+
process.stdout.write(` mcp.json: ${mergedMcp} server(s)\n`);
|
|
706
|
+
process.stdout.write(` hosts.json: ${mergedHosts} host(s)\n`);
|
|
707
|
+
process.stdout.write(` state: ${mergedState} entry(ies)\n`);
|
|
708
|
+
process.stdout.write(` total merged: ${total}\n`);
|
|
709
|
+
process.stdout.write(`\nshmakk: removed ${nested.length} nested .shmakk director${nested.length > 1 ? 'ies' : 'y'}\n`);
|
|
710
|
+
|
|
711
|
+
return 0;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
module.exports = { status, exitParent, restartParent, resetConversation, setProfileAndRestart, profileSignalPath, resumeStatus, compactContext, stats, loadSkill, listSkills, listSkillCategories, findSkills, skillStatus, unloadSkill, installSkill, showPlan, mcpStatus, consolidateWorkspace };
|