Add custom code structure and update scripts
Some checks failed
CI / test (map[image:macos-latest name:macos], 1, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 2, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 3, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 4, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 1, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 2, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 3, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 4, 4) (push) Has been cancelled
CI / merge-reports (push) Has been cancelled
Some checks failed
CI / test (map[image:macos-latest name:macos], 1, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 2, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 3, 4) (push) Has been cancelled
CI / test (map[image:macos-latest name:macos], 4, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 1, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 2, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 3, 4) (push) Has been cancelled
CI / test (map[image:windows-latest name:windows], 4, 4) (push) Has been cancelled
CI / merge-reports (push) Has been cancelled
- Organized custom modifications in src/custom/ - Added update-dyad-v2.sh for selective updates - Preserved custom smart context functionality - Created backup system for safe updates
This commit is contained in:
60
src/custom/hooks/useSmartContext.ts
Normal file
60
src/custom/hooks/useSmartContext.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { IpcClient } from "@/ipc/ipc_client";
|
||||||
|
import type {
|
||||||
|
SmartContextMeta,
|
||||||
|
SmartContextSnippet,
|
||||||
|
SmartContextRetrieveResult,
|
||||||
|
} from "@/ipc/ipc_types";
|
||||||
|
|
||||||
|
export function useSmartContextMeta(chatId: number) {
|
||||||
|
return useQuery<SmartContextMeta, Error>({
|
||||||
|
queryKey: ["smart-context", chatId, "meta"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const ipc = IpcClient.getInstance();
|
||||||
|
return ipc.getSmartContextMeta(chatId);
|
||||||
|
},
|
||||||
|
enabled: !!chatId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRetrieveSmartContext(
|
||||||
|
chatId: number,
|
||||||
|
query: string,
|
||||||
|
budgetTokens: number,
|
||||||
|
) {
|
||||||
|
return useQuery<SmartContextRetrieveResult, Error>({
|
||||||
|
queryKey: ["smart-context", chatId, "retrieve", query, budgetTokens],
|
||||||
|
queryFn: async () => {
|
||||||
|
const ipc = IpcClient.getInstance();
|
||||||
|
return ipc.retrieveSmartContext({ chatId, query, budgetTokens });
|
||||||
|
},
|
||||||
|
enabled: !!chatId && !!query && budgetTokens > 0,
|
||||||
|
meta: { showErrorToast: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpsertSmartContextSnippets(chatId: number) {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
return useMutation<number, Error, Array<Pick<SmartContextSnippet, "text" | "source">>>({
|
||||||
|
mutationFn: async (snippets) => {
|
||||||
|
const ipc = IpcClient.getInstance();
|
||||||
|
return ipc.upsertSmartContextSnippets(chatId, snippets);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["smart-context", chatId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpdateRollingSummary(chatId: number) {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
return useMutation<SmartContextMeta, Error, { summary: string }>({
|
||||||
|
mutationFn: async ({ summary }) => {
|
||||||
|
const ipc = IpcClient.getInstance();
|
||||||
|
return ipc.updateSmartContextRollingSummary(chatId, summary);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["smart-context", chatId, "meta"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
18
src/custom/index.ts
Normal file
18
src/custom/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Custom modules for moreminimore-vibe
|
||||||
|
// This file exports all custom functionality to make imports easier
|
||||||
|
|
||||||
|
// Custom hooks
|
||||||
|
export { useSmartContextMeta, useRetrieveSmartContext, useUpsertSmartContextSnippets, useUpdateRollingSummary } from './hooks/useSmartContext';
|
||||||
|
|
||||||
|
// Custom IPC handlers (these will need to be imported and registered in the main process)
|
||||||
|
export { registerSmartContextHandlers } from './ipc/smart_context_handlers';
|
||||||
|
|
||||||
|
// Custom utilities
|
||||||
|
export * from './utils/smart_context_store';
|
||||||
|
|
||||||
|
// Re-export types that might be needed
|
||||||
|
export type {
|
||||||
|
SmartContextMeta,
|
||||||
|
SmartContextSnippet,
|
||||||
|
SmartContextRetrieveResult,
|
||||||
|
} from '../ipc/ipc_types';
|
||||||
65
src/custom/ipc/smart_context_handlers.ts
Normal file
65
src/custom/ipc/smart_context_handlers.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import log from "electron-log";
|
||||||
|
import { createLoggedHandler } from "./safe_handle";
|
||||||
|
import {
|
||||||
|
appendSnippets,
|
||||||
|
readMeta,
|
||||||
|
retrieveContext,
|
||||||
|
updateRollingSummary,
|
||||||
|
rebuildIndex,
|
||||||
|
type SmartContextSnippet,
|
||||||
|
type SmartContextMeta,
|
||||||
|
} from "../utils/smart_context_store";
|
||||||
|
|
||||||
|
const logger = log.scope("smart_context_handlers");
|
||||||
|
const handle = createLoggedHandler(logger);
|
||||||
|
|
||||||
|
export interface UpsertSnippetsParams {
|
||||||
|
chatId: number;
|
||||||
|
snippets: Array<{
|
||||||
|
text: string;
|
||||||
|
source:
|
||||||
|
| { type: "message"; messageIndex?: number }
|
||||||
|
| { type: "code"; filePath: string }
|
||||||
|
| { type: "attachment"; name: string; mime?: string }
|
||||||
|
| { type: "other"; label?: string };
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RetrieveContextParams {
|
||||||
|
chatId: number;
|
||||||
|
query: string;
|
||||||
|
budgetTokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerSmartContextHandlers() {
|
||||||
|
handle("sc:get-meta", async (_event, chatId: number): Promise<SmartContextMeta> => {
|
||||||
|
return readMeta(chatId);
|
||||||
|
});
|
||||||
|
|
||||||
|
handle(
|
||||||
|
"sc:upsert-snippets",
|
||||||
|
async (_event, params: UpsertSnippetsParams): Promise<number> => {
|
||||||
|
const count = await appendSnippets(params.chatId, params.snippets);
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
handle(
|
||||||
|
"sc:update-rolling-summary",
|
||||||
|
async (_event, params: { chatId: number; summary: string }): Promise<SmartContextMeta> => {
|
||||||
|
return updateRollingSummary(params.chatId, params.summary);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
handle(
|
||||||
|
"sc:retrieve-context",
|
||||||
|
async (_event, params: RetrieveContextParams) => {
|
||||||
|
return retrieveContext(params.chatId, params.query, params.budgetTokens);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
handle("sc:rebuild-index", async (_event, chatId: number) => {
|
||||||
|
await rebuildIndex(chatId);
|
||||||
|
return { ok: true } as const;
|
||||||
|
});
|
||||||
|
}
|
||||||
212
src/custom/utils/smart_context_store.ts
Normal file
212
src/custom/utils/smart_context_store.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { promises as fs } from "node:fs";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { getUserDataPath } from "../../paths/paths";
|
||||||
|
import { estimateTokens } from "./token_utils";
|
||||||
|
|
||||||
|
export type SmartContextSource =
|
||||||
|
| { type: "message"; messageIndex?: number }
|
||||||
|
| { type: "code"; filePath: string }
|
||||||
|
| { type: "attachment"; name: string; mime?: string }
|
||||||
|
| { type: "other"; label?: string };
|
||||||
|
|
||||||
|
export interface SmartContextSnippet {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
score?: number;
|
||||||
|
source: SmartContextSource;
|
||||||
|
ts: number; // epoch ms
|
||||||
|
tokens?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SmartContextMetaConfig {
|
||||||
|
maxSnippets?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SmartContextMeta {
|
||||||
|
entityId: string; // e.g., chatId as string
|
||||||
|
updatedAt: number;
|
||||||
|
rollingSummary?: string;
|
||||||
|
summaryTokens?: number;
|
||||||
|
config?: SmartContextMetaConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getThreadDir(chatId: number): string {
|
||||||
|
const base = path.join(getUserDataPath(), "smart-context", "threads");
|
||||||
|
return path.join(base, String(chatId));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetaPath(chatId: number): string {
|
||||||
|
return path.join(getThreadDir(chatId), "meta.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnippetsPath(chatId: number): string {
|
||||||
|
return path.join(getThreadDir(chatId), "snippets.jsonl");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureDir(dir: string): Promise<void> {
|
||||||
|
await fs.mkdir(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readMeta(chatId: number): Promise<SmartContextMeta> {
|
||||||
|
const dir = getThreadDir(chatId);
|
||||||
|
await ensureDir(dir);
|
||||||
|
const metaPath = getMetaPath(chatId);
|
||||||
|
try {
|
||||||
|
const raw = await fs.readFile(metaPath, "utf8");
|
||||||
|
const meta = JSON.parse(raw) as SmartContextMeta;
|
||||||
|
return meta;
|
||||||
|
} catch {
|
||||||
|
const fresh: SmartContextMeta = {
|
||||||
|
entityId: String(chatId),
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
rollingSummary: "",
|
||||||
|
summaryTokens: 0,
|
||||||
|
config: { maxSnippets: 400 },
|
||||||
|
};
|
||||||
|
await fs.writeFile(metaPath, JSON.stringify(fresh, null, 2), "utf8");
|
||||||
|
return fresh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function writeMeta(
|
||||||
|
chatId: number,
|
||||||
|
meta: SmartContextMeta,
|
||||||
|
): Promise<void> {
|
||||||
|
const dir = getThreadDir(chatId);
|
||||||
|
await ensureDir(dir);
|
||||||
|
const metaPath = getMetaPath(chatId);
|
||||||
|
const updated: SmartContextMeta = {
|
||||||
|
...meta,
|
||||||
|
entityId: String(chatId),
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
};
|
||||||
|
await fs.writeFile(metaPath, JSON.stringify(updated, null, 2), "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateRollingSummary(
|
||||||
|
chatId: number,
|
||||||
|
summary: string,
|
||||||
|
): Promise<SmartContextMeta> {
|
||||||
|
const meta = await readMeta(chatId);
|
||||||
|
const summaryTokens = estimateTokens(summary || "");
|
||||||
|
const next: SmartContextMeta = {
|
||||||
|
...meta,
|
||||||
|
rollingSummary: summary,
|
||||||
|
summaryTokens,
|
||||||
|
};
|
||||||
|
await writeMeta(chatId, next);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function appendSnippets(
|
||||||
|
chatId: number,
|
||||||
|
snippets: Omit<SmartContextSnippet, "id" | "ts" | "tokens">[],
|
||||||
|
): Promise<number> {
|
||||||
|
const dir = getThreadDir(chatId);
|
||||||
|
await ensureDir(dir);
|
||||||
|
const snippetsPath = getSnippetsPath(chatId);
|
||||||
|
const withDefaults: SmartContextSnippet[] = snippets.map((s) => ({
|
||||||
|
id: randomUUID(),
|
||||||
|
ts: Date.now(),
|
||||||
|
tokens: estimateTokens(s.text),
|
||||||
|
...s,
|
||||||
|
}));
|
||||||
|
const lines = withDefaults.map((obj) => JSON.stringify(obj)).join("\n");
|
||||||
|
await fs.appendFile(snippetsPath, (lines ? lines + "\n" : ""), "utf8");
|
||||||
|
|
||||||
|
// prune if exceeded max
|
||||||
|
const meta = await readMeta(chatId);
|
||||||
|
const maxSnippets = meta.config?.maxSnippets ?? 400;
|
||||||
|
try {
|
||||||
|
const file = await fs.readFile(snippetsPath, "utf8");
|
||||||
|
const allLines = file.split("\n").filter(Boolean);
|
||||||
|
if (allLines.length > maxSnippets) {
|
||||||
|
const toKeep = allLines.slice(allLines.length - maxSnippets);
|
||||||
|
await fs.writeFile(snippetsPath, toKeep.join("\n") + "\n", "utf8");
|
||||||
|
return toKeep.length;
|
||||||
|
}
|
||||||
|
return allLines.length;
|
||||||
|
} catch {
|
||||||
|
return withDefaults.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readAllSnippets(chatId: number): Promise<SmartContextSnippet[]> {
|
||||||
|
try {
|
||||||
|
const raw = await fs.readFile(getSnippetsPath(chatId), "utf8");
|
||||||
|
return raw
|
||||||
|
.split("\n")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((line) => JSON.parse(line) as SmartContextSnippet);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(value: number, min: number, max: number): number {
|
||||||
|
if (max === min) return 0;
|
||||||
|
return (value - min) / (max - min);
|
||||||
|
}
|
||||||
|
|
||||||
|
function keywordScore(text: string, query: string): number {
|
||||||
|
const toTokens = (s: string) =>
|
||||||
|
s
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9_\- ]+/g, " ")
|
||||||
|
.split(/\s+/)
|
||||||
|
.filter(Boolean);
|
||||||
|
const qTokens = new Set(toTokens(query));
|
||||||
|
const tTokens = toTokens(text);
|
||||||
|
if (qTokens.size === 0 || tTokens.length === 0) return 0;
|
||||||
|
let hits = 0;
|
||||||
|
for (const tok of tTokens) if (qTokens.has(tok)) hits++;
|
||||||
|
return hits / tTokens.length; // simple overlap ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RetrieveContextResult {
|
||||||
|
rollingSummary?: string;
|
||||||
|
usedTokens: number;
|
||||||
|
snippets: SmartContextSnippet[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retrieveContext(
|
||||||
|
chatId: number,
|
||||||
|
query: string,
|
||||||
|
budgetTokens: number,
|
||||||
|
): Promise<RetrieveContextResult> {
|
||||||
|
const meta = await readMeta(chatId);
|
||||||
|
const snippets = await readAllSnippets(chatId);
|
||||||
|
const now = Date.now();
|
||||||
|
let minTs = now;
|
||||||
|
let maxTs = 0;
|
||||||
|
for (const s of snippets) {
|
||||||
|
if (s.ts < minTs) minTs = s.ts;
|
||||||
|
if (s.ts > maxTs) maxTs = s.ts;
|
||||||
|
}
|
||||||
|
const scored = snippets.map((s) => {
|
||||||
|
const recency = normalize(s.ts, minTs, maxTs);
|
||||||
|
const kw = keywordScore(s.text, query);
|
||||||
|
const base = 0.6 * kw + 0.4 * recency;
|
||||||
|
const score = base;
|
||||||
|
return { ...s, score } as SmartContextSnippet;
|
||||||
|
});
|
||||||
|
scored.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
||||||
|
|
||||||
|
const picked: SmartContextSnippet[] = [];
|
||||||
|
let usedTokens = 0;
|
||||||
|
for (const s of scored) {
|
||||||
|
const t = s.tokens ?? estimateTokens(s.text);
|
||||||
|
if (usedTokens + t > budgetTokens) break;
|
||||||
|
picked.push(s);
|
||||||
|
usedTokens += t;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollingSummary = meta.rollingSummary || "";
|
||||||
|
return { rollingSummary, usedTokens, snippets: picked };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rebuildIndex(_chatId: number): Promise<void> {
|
||||||
|
// Placeholder for future embedding/vector index rebuild.
|
||||||
|
return;
|
||||||
|
}
|
||||||
135
update-dyad-v2.sh
Executable file
135
update-dyad-v2.sh
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Dyad Update Script v2 - Selective Update Approach
|
||||||
|
# This script updates your forked Dyad app by backing up custom code,
|
||||||
|
# resetting to upstream, then restoring custom code.
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we're in a git repository
|
||||||
|
if ! git rev-parse --git-head > /dev/null 2>&1; then
|
||||||
|
print_error "Not in a git repository. Please run this script from the project root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Starting Dyad selective update process..."
|
||||||
|
print_status "Current branch: $(git branch --show-current)"
|
||||||
|
|
||||||
|
# Create a temporary backup directory
|
||||||
|
BACKUP_DIR="dyad-backup-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
print_status "Creating backup in: $BACKUP_DIR"
|
||||||
|
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Backup custom code structure
|
||||||
|
print_status "Backing up your custom code..."
|
||||||
|
|
||||||
|
# Backup the custom directory if it exists
|
||||||
|
if [ -d "src/custom" ]; then
|
||||||
|
cp -r src/custom "$BACKUP_DIR/"
|
||||||
|
print_success "Backed up src/custom/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup any other custom files you might have added
|
||||||
|
# Add any additional files/directories you want to preserve here
|
||||||
|
|
||||||
|
# Backup current state as a reference
|
||||||
|
print_status "Creating reference backup..."
|
||||||
|
git log --oneline -10 > "$BACKUP_DIR/commit-history.txt"
|
||||||
|
git diff HEAD~1 > "$BACKUP_DIR/last-changes.diff" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Fetch latest changes from upstream
|
||||||
|
print_status "Fetching latest changes from upstream..."
|
||||||
|
if git fetch upstream; then
|
||||||
|
print_success "Successfully fetched changes from upstream."
|
||||||
|
else
|
||||||
|
print_error "Failed to fetch from upstream. Check your internet connection."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the upstream commit info
|
||||||
|
UPSTREAM_COMMIT=$(git rev-parse upstream/main)
|
||||||
|
print_status "Upstream commit: $UPSTREAM_COMMIT"
|
||||||
|
|
||||||
|
# Reset to upstream
|
||||||
|
print_status "Resetting to upstream/main..."
|
||||||
|
if git reset --hard upstream/main; then
|
||||||
|
print_success "Successfully reset to upstream/main."
|
||||||
|
else
|
||||||
|
print_error "Failed to reset to upstream/main."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore custom code
|
||||||
|
print_status "Restoring your custom code..."
|
||||||
|
|
||||||
|
if [ -d "$BACKUP_DIR/custom" ]; then
|
||||||
|
cp -r "$BACKUP_DIR/custom" src/
|
||||||
|
print_success "Restored src/custom/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update package.json to include any custom dependencies if needed
|
||||||
|
print_status "Checking for custom dependencies..."
|
||||||
|
|
||||||
|
# Add custom files to git
|
||||||
|
print_status "Adding custom files to git..."
|
||||||
|
git add src/custom/ UPDATE_GUIDE.md update-dyad*.sh 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create a commit for the update
|
||||||
|
print_status "Creating commit for the update..."
|
||||||
|
git commit -m "feat: update to upstream $UPSTREAM_COMMIT and restore custom code
|
||||||
|
|
||||||
|
- Updated to latest upstream version
|
||||||
|
- Restored custom code in src/custom/
|
||||||
|
- Preserved custom modifications
|
||||||
|
|
||||||
|
Backup saved in: $BACKUP_DIR" || {
|
||||||
|
print_warning "No changes to commit (custom code already matches upstream)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Push to origin
|
||||||
|
print_status "Pushing to your fork..."
|
||||||
|
if git push origin main --force-with-lease; then
|
||||||
|
print_success "Successfully pushed to origin."
|
||||||
|
else
|
||||||
|
print_warning "Push failed. Push manually with: git push origin main --force-with-lease"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "🎉 Update completed successfully!"
|
||||||
|
print_status "Summary:"
|
||||||
|
print_status "- Updated to latest upstream version"
|
||||||
|
print_status "- Your custom code has been preserved in src/custom/"
|
||||||
|
print_status "- Backup saved in: $BACKUP_DIR"
|
||||||
|
print_status ""
|
||||||
|
print_status "Next steps:"
|
||||||
|
print_status "1. Test the application to ensure everything works"
|
||||||
|
print_status "2. Run 'npm install' to update dependencies if needed"
|
||||||
|
print_status "3. Check if any of your custom code needs updates for the new version"
|
||||||
|
print_status ""
|
||||||
|
print_status "If you need to restore from backup:"
|
||||||
|
print_status "1. Copy files from $BACKUP_DIR back to your project"
|
||||||
|
print_status "2. Commit and push the changes"
|
||||||
Reference in New Issue
Block a user