sanjang 0.3.1 → 0.3.3
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/dashboard/app.js +304 -44
- package/dashboard/index.html +43 -6
- package/dashboard/style.css +306 -0
- package/dist/bin/sanjang.d.ts +1 -0
- package/dist/bin/sanjang.js +138 -0
- package/dist/lib/config.d.ts +19 -0
- package/dist/lib/config.js +318 -0
- package/dist/lib/engine/cache.d.ts +7 -0
- package/dist/lib/engine/cache.js +183 -0
- package/dist/lib/engine/config-hotfix.d.ts +7 -0
- package/dist/lib/engine/config-hotfix.js +129 -0
- package/dist/lib/engine/conflict.d.ts +12 -0
- package/dist/lib/engine/conflict.js +32 -0
- package/dist/lib/engine/diagnostics.d.ts +15 -0
- package/dist/lib/engine/diagnostics.js +58 -0
- package/dist/lib/engine/naming.d.ts +10 -0
- package/dist/lib/engine/naming.js +83 -0
- package/dist/lib/engine/ports.d.ts +9 -0
- package/dist/lib/engine/ports.js +55 -0
- package/dist/lib/engine/pr.d.ts +27 -0
- package/dist/lib/engine/pr.js +54 -0
- package/dist/lib/engine/process.d.ts +15 -0
- package/dist/lib/engine/process.js +250 -0
- package/dist/lib/engine/self-heal.d.ts +12 -0
- package/dist/lib/engine/self-heal.js +98 -0
- package/dist/lib/engine/smart-init.d.ts +7 -0
- package/dist/lib/engine/smart-init.js +138 -0
- package/dist/lib/engine/smart-pr.d.ts +19 -0
- package/dist/lib/engine/smart-pr.js +105 -0
- package/dist/lib/engine/snapshot.d.ts +10 -0
- package/dist/lib/engine/snapshot.js +35 -0
- package/dist/lib/engine/state.d.ts +7 -0
- package/dist/lib/engine/state.js +53 -0
- package/dist/lib/engine/suggest.d.ts +21 -0
- package/dist/lib/engine/suggest.js +121 -0
- package/dist/lib/engine/warp.d.ts +23 -0
- package/dist/lib/engine/warp.js +32 -0
- package/dist/lib/engine/watcher.d.ts +11 -0
- package/dist/lib/engine/watcher.js +43 -0
- package/dist/lib/engine/worktree.d.ts +13 -0
- package/dist/lib/engine/worktree.js +91 -0
- package/dist/lib/server.d.ts +20 -0
- package/dist/lib/server.js +1466 -0
- package/dist/lib/types.d.ts +109 -0
- package/dist/lib/types.js +2 -0
- package/package.json +5 -4
- package/bin/__tests__/sanjang.test.ts +0 -42
- package/bin/sanjang.js +0 -17
- package/bin/sanjang.ts +0 -144
- package/lib/config.ts +0 -337
- package/lib/engine/cache.ts +0 -218
- package/lib/engine/config-hotfix.ts +0 -161
- package/lib/engine/conflict.ts +0 -33
- package/lib/engine/diagnostics.ts +0 -81
- package/lib/engine/naming.ts +0 -93
- package/lib/engine/ports.ts +0 -61
- package/lib/engine/pr.ts +0 -71
- package/lib/engine/process.ts +0 -283
- package/lib/engine/self-heal.ts +0 -130
- package/lib/engine/smart-init.ts +0 -136
- package/lib/engine/smart-pr.ts +0 -130
- package/lib/engine/snapshot.ts +0 -45
- package/lib/engine/state.ts +0 -60
- package/lib/engine/suggest.ts +0 -169
- package/lib/engine/warp.ts +0 -47
- package/lib/engine/watcher.ts +0 -40
- package/lib/engine/worktree.ts +0 -100
- package/lib/server.ts +0 -1560
- package/lib/types.ts +0 -130
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class CampWatcher {
|
|
2
|
+
private readonly dir;
|
|
3
|
+
private readonly onChange;
|
|
4
|
+
private readonly debounceMs;
|
|
5
|
+
private watcher;
|
|
6
|
+
private timer;
|
|
7
|
+
private stopped;
|
|
8
|
+
constructor(dir: string, onChange: () => void, debounceMs?: number);
|
|
9
|
+
start(): void;
|
|
10
|
+
stop(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { watch } from "node:fs";
|
|
2
|
+
export class CampWatcher {
|
|
3
|
+
dir;
|
|
4
|
+
onChange;
|
|
5
|
+
debounceMs;
|
|
6
|
+
watcher = null;
|
|
7
|
+
timer = null;
|
|
8
|
+
stopped = false;
|
|
9
|
+
constructor(dir, onChange, debounceMs = 500) {
|
|
10
|
+
this.dir = dir;
|
|
11
|
+
this.onChange = onChange;
|
|
12
|
+
this.debounceMs = debounceMs;
|
|
13
|
+
}
|
|
14
|
+
start() {
|
|
15
|
+
this.stopped = false;
|
|
16
|
+
try {
|
|
17
|
+
this.watcher = watch(this.dir, { recursive: true }, () => {
|
|
18
|
+
if (this.stopped)
|
|
19
|
+
return;
|
|
20
|
+
if (this.timer)
|
|
21
|
+
clearTimeout(this.timer);
|
|
22
|
+
this.timer = setTimeout(() => {
|
|
23
|
+
if (!this.stopped)
|
|
24
|
+
this.onChange();
|
|
25
|
+
}, this.debounceMs);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// fs.watch can fail on some platforms/dirs — silently degrade
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
stop() {
|
|
33
|
+
this.stopped = true;
|
|
34
|
+
if (this.timer) {
|
|
35
|
+
clearTimeout(this.timer);
|
|
36
|
+
this.timer = null;
|
|
37
|
+
}
|
|
38
|
+
if (this.watcher) {
|
|
39
|
+
this.watcher.close();
|
|
40
|
+
this.watcher = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface BranchInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
remote: boolean;
|
|
4
|
+
local: boolean;
|
|
5
|
+
date: string;
|
|
6
|
+
category?: "default" | "feature" | "fix" | "other";
|
|
7
|
+
}
|
|
8
|
+
export declare function setProjectRoot(root: string): void;
|
|
9
|
+
export declare function getProjectRoot(): string;
|
|
10
|
+
export declare function campPath(name: string): string;
|
|
11
|
+
export declare function listBranches(): Promise<BranchInfo[]>;
|
|
12
|
+
export declare function addWorktree(name: string, branch: string): Promise<void>;
|
|
13
|
+
export declare function removeWorktree(name: string): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { simpleGit } from "simple-git";
|
|
3
|
+
import { getCampsDir } from "./state.js";
|
|
4
|
+
let projectRoot = null;
|
|
5
|
+
export function setProjectRoot(root) {
|
|
6
|
+
projectRoot = root;
|
|
7
|
+
}
|
|
8
|
+
export function getProjectRoot() {
|
|
9
|
+
if (!projectRoot)
|
|
10
|
+
throw new Error("projectRoot not initialized. Call setProjectRoot() first.");
|
|
11
|
+
return projectRoot;
|
|
12
|
+
}
|
|
13
|
+
export function campPath(name) {
|
|
14
|
+
return join(getCampsDir(), name);
|
|
15
|
+
}
|
|
16
|
+
function git() {
|
|
17
|
+
return simpleGit(getProjectRoot());
|
|
18
|
+
}
|
|
19
|
+
export async function listBranches() {
|
|
20
|
+
// Best-effort fetch — continue with local refs on network failure
|
|
21
|
+
try {
|
|
22
|
+
await git().fetch(["--prune"]);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* offline is OK */
|
|
26
|
+
}
|
|
27
|
+
const raw = await git().raw([
|
|
28
|
+
"for-each-ref",
|
|
29
|
+
"--sort=-committerdate",
|
|
30
|
+
"--format=%(refname:short)\t%(committerdate:relative)\t%(refname)",
|
|
31
|
+
"refs/heads/",
|
|
32
|
+
"refs/remotes/origin/",
|
|
33
|
+
]);
|
|
34
|
+
const map = new Map();
|
|
35
|
+
for (const line of raw.trim().split("\n")) {
|
|
36
|
+
if (!line)
|
|
37
|
+
continue;
|
|
38
|
+
const [shortName, date, fullRef] = line.split("\t");
|
|
39
|
+
if (!shortName || !fullRef)
|
|
40
|
+
continue;
|
|
41
|
+
if (shortName.includes("HEAD"))
|
|
42
|
+
continue;
|
|
43
|
+
const isRemote = fullRef.startsWith("refs/remotes/origin/");
|
|
44
|
+
const clean = shortName.replace(/^origin\//, "").trim();
|
|
45
|
+
if (!clean)
|
|
46
|
+
continue;
|
|
47
|
+
const entry = map.get(clean) || { name: clean, remote: false, local: false, date: date ?? "" };
|
|
48
|
+
if (isRemote)
|
|
49
|
+
entry.remote = true;
|
|
50
|
+
else
|
|
51
|
+
entry.local = true;
|
|
52
|
+
if (!entry.date)
|
|
53
|
+
entry.date = date ?? "";
|
|
54
|
+
map.set(clean, entry);
|
|
55
|
+
}
|
|
56
|
+
const branches = [...map.values()];
|
|
57
|
+
for (const b of branches) {
|
|
58
|
+
if (["dev", "main", "master"].includes(b.name)) {
|
|
59
|
+
b.category = "default";
|
|
60
|
+
}
|
|
61
|
+
else if (b.name.startsWith("feature/")) {
|
|
62
|
+
b.category = "feature";
|
|
63
|
+
}
|
|
64
|
+
else if (b.name.startsWith("fix/") || b.name.startsWith("hotfix/")) {
|
|
65
|
+
b.category = "fix";
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
b.category = "other";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return branches;
|
|
72
|
+
}
|
|
73
|
+
export async function addWorktree(name, branch) {
|
|
74
|
+
const path = campPath(name);
|
|
75
|
+
const refs = [`origin/${branch}`, branch];
|
|
76
|
+
let lastErr;
|
|
77
|
+
for (const ref of refs) {
|
|
78
|
+
try {
|
|
79
|
+
await git().raw(["worktree", "add", "--detach", path, ref]);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
lastErr = err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw lastErr;
|
|
87
|
+
}
|
|
88
|
+
export async function removeWorktree(name) {
|
|
89
|
+
const path = campPath(name);
|
|
90
|
+
await git().raw(["worktree", "remove", "--force", path]);
|
|
91
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { type Server } from "node:http";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import { CampWatcher } from "./engine/watcher.ts";
|
|
5
|
+
interface CreateAppOptions {
|
|
6
|
+
port?: number;
|
|
7
|
+
}
|
|
8
|
+
interface CreateAppResult {
|
|
9
|
+
app: express.Application;
|
|
10
|
+
server: Server;
|
|
11
|
+
port: number;
|
|
12
|
+
runningTasks: Map<string, ChildProcess>;
|
|
13
|
+
warpStatus: {
|
|
14
|
+
installed: boolean;
|
|
15
|
+
};
|
|
16
|
+
watchers: Map<string, CampWatcher>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createApp(projectRoot: string, options?: CreateAppOptions): Promise<CreateAppResult>;
|
|
19
|
+
export declare function startServer(projectRoot: string, options?: CreateAppOptions): Promise<Server>;
|
|
20
|
+
export {};
|