同步最新

This commit is contained in:
Zane
2026-05-07 11:14:29 +08:00
parent ff654b91f2
commit 8f3aa6fd3a
12 changed files with 319 additions and 381 deletions

View File

@@ -0,0 +1,11 @@
{
"tasks": [
{
"id": "d572d1d5",
"cron": "30 10 * * *",
"prompt": "Run the daily git sync script at D:/fork_project/obsidian-notes/git_sync_daily.ps1. Execute: pwsh -ExecutionPolicy Bypass -File \"D:/fork_project/obsidian-notes/git_sync_daily.ps1\"",
"createdAt": 1777459843227,
"recurring": true
}
]
}

View File

@@ -0,0 +1 @@
{"sessionId":"1edcf1ba-95d0-4aa3-b979-c6be03a8dec5","pid":37752,"acquiredAt":1777459843947}

View File

@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(python -c \"import graphify; print\\('installed'\\)\")",
"Bash(python -c ':*)",
"Bash(python)"
]
}
}

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@ $RECYCLE.BIN/
# Windows shortcuts # Windows shortcuts
*.lnk *.lnk
.obsidian/workspace.json

View File

@@ -1,264 +0,0 @@
{
"main": {
"id": "c01acc1e0c3642a1",
"type": "split",
"children": [
{
"id": "4463cbe9dee439a2",
"type": "tabs",
"children": [
{
"id": "bf4a47cd3f5340ef",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "1Project/大富翁Demo/关于AI/离全自动发起PR有多远.md",
"mode": "source",
"source": false,
"backlinks": true,
"backlinkOpts": {
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
}
},
"icon": "lucide-file",
"title": "离全自动发起PR有多远"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "b3f2fe956c600b2b",
"type": "split",
"children": [
{
"id": "b892947aee3a10a9",
"type": "tabs",
"children": [
{
"id": "f2ac97a7addb8678",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "文件列表"
}
},
{
"id": "bd33cd6e5600118d",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "搜索"
}
},
{
"id": "8c8dfe109a7f116f",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "书签"
}
},
{
"id": "4c191f6555b427bd",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "文件列表"
}
}
]
}
],
"direction": "horizontal",
"width": 401.5
},
"right": {
"id": "d809b577c235ae94",
"type": "split",
"children": [
{
"id": "c6a04719e9395783",
"type": "tabs",
"children": [
{
"id": "a0097fbc2c860370",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "4Archives/归档-目录.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "归档-目录 的反向链接列表"
}
},
{
"id": "d6423d1f79769c25",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "4Archives/归档-目录.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "归档-目录 的出链列表"
}
},
{
"id": "009d8bcaab15bbb9",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "标签"
}
},
{
"id": "11a36b370174df61",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "4Archives/归档-目录.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "归档-目录 的大纲"
}
},
{
"id": "4a0e6c346bdcec3c",
"type": "leaf",
"state": {
"type": "calendar",
"state": {},
"icon": "calendar-with-checkmark",
"title": "Calendar"
}
}
]
}
],
"direction": "horizontal",
"width": 408.5,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"bases:新建数据库": false,
"switcher:打开快速切换": false,
"graph:查看关系图谱": false,
"canvas:新建白板": false,
"templates:插入模板": false,
"command-palette:打开命令面板": false,
"copilot:Open Copilot Chat": false
}
},
"active": "bf4a47cd3f5340ef",
"lastOpenFiles": [
"1Project/大富翁Demo/关于AI/护栏与AI.md",
"1Project/大富翁Demo/关于AI/离全自动发起PR有多远.md",
"笔记数据库.base",
"1Project/大富翁Demo/关于AI",
"1Project/大富翁Demo",
"未命名 1.base",
"未命名.base",
"卡片数据库.base",
"卡片盒skill.md",
"查询数据skill.md",
"00-Home.md",
"copilot-custom-prompts/Remove URLs.md",
"copilot-custom-prompts/Make shorter.md",
"copilot-custom-prompts/Explain like I am 5.md",
"copilot-custom-prompts/Emojify.md",
"1Project/GPU地形/地形优化.md",
"1Project/理财.md",
"1Project/GPU地形/Houdini PCG.md",
"1Project/GPU地形/GPU地形-目录.md",
"1Project/GPU地形/GPU Driven Terrain 跟学与实现.md",
"1Project/GPU地形/ComputeShader生成无限世界地形.md",
"1Project/海量渲染战斗/批量渲染gpu动画.md",
"1Project/外包/Project_城堡/_城堡.md",
"1Project/海量渲染战斗/基于GPU蒙皮动画的Spine实现.md",
"1Project/海量渲染战斗/基于GPU动画和ComputeShader的大批量动画渲染Demo之GPU顶点动画.md",
"1Project/海量渲染战斗/海量渲染战斗-目录.md",
"1Project/海量渲染战斗/GPU动画工程/TODO转为Job组织数据.md",
"1Project/海量渲染战斗/GPU动画工程/TODO将Time存进buffer中做成不同的实例可以使用不同的动画时间.md",
"1Project/海量渲染战斗/GPU动画工程/TODO不同动作存进Buffer中.md",
"1Project/海量渲染战斗/GPU动画工程/TODO 不同动作间的混合.md",
"1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md",
"1Project/海量渲染战斗/GPU动画工程/GPU顶点动画.md",
"skills/obsidian-markdown",
"skills/obsidian-bases",
"skills/json-canvas",
"skills",
"渲染/软渲染/图片/9702b7cbafa93d6e3d0cd59e7cf37e31.png",
"渲染/软渲染/图片/86aa46af36c4c3b1f1efef2a1ccc6310.png",
"渲染/软渲染/图片/62ad22d3e981954fc083018fb850413a.png",
"渲染/软渲染/图片/23d61a2b7376ea3149d74d6bd4ea9430.png",
"wallhaven-rq75r7.jpg",
"1Project/构建新框架/管道模式/未命名.canvas",
"2Areas/如何记录笔记/P.A.R.A/Pasted image 20250614192838.png",
"1Project/战斗编辑器/随想/动作时间轴 随想.canvas",
"2Areas/开发感悟/管道模式/未命名.canvas",
"1Project/宠物宇宙/地图编辑器/地图工作流.canvas",
"InBox/编辑器/时间轴编辑器/Timeline TODO.canvas",
"InBox/编辑器/图片/Pasted image 20221025121100.png",
"渲染/软渲染/图片/img_v3_02n6_ff40a1a4-d813-45f7-b132-6f14a74cfd7g.jpg",
"InBox/TODO.canvas",
"渲染/软渲染/图片/Pasted image 20250609171235.png",
"InBox/渲染/GPU/Pasted image 20230413163234.png",
"未命名 1.canvas",
"并行计算/GPU.canvas",
"并行计算/CPU.canvas",
"未命名 2.canvas"
]
}

View File

@@ -1,14 +1,13 @@
--- ---
原文发布链接: https://zhuanlan.zhihu.com/p/2016839356833341880 原文发布链接: https://zhuanlan.zhihu.com/p/2016839356833341880
tags: tags:
- AutoHarness
- AI - AI
- 论文解读 - 论文解读
- AutoHerness
- LLM - LLM
- Agent - Agent
- ProgramSynthesis - ProgramSynthesis
- GRPO强化学习 - GRPO强化学习
- harness
--- ---
## 一句话总结 ## 一句话总结

View File

@@ -1,3 +1,9 @@
---
tags:
- harness
---
# Harness Engineering 最佳实践Agent Harness 底层原理、核心组件与实战应用 # Harness Engineering 最佳实践Agent Harness 底层原理、核心组件与实战应用
## 目录 ## 目录

View File

@@ -1,114 +0,0 @@
# Karpathy用「harness」彻底终结了RAG
> 来源: https://mp.weixin.qq.com/s/ABzwrSGeFs1Wv2RUBjLtwg
> 抓取时间: 2025-04-10
---
假期的时候Karpathy 大神发了一个llm.wiki的想法。 这条推文火爆了。
在LLM Agent时代分享具体代码或应用的意义正在变弱现在只需要分享想法然后把它交给 Claude、Grok 等 Agent它就可以根据你的需求自动搭建一个属于你自己的个人知识库。
还有最近特别火的各种 personal skill ,同事.skill、前任.skill、自己的skill甚至卡兹克把自己的创作skills都开源了。。。。
整体看下来我觉得它们都在做一件事情:把现实世界里原本只能"看"的东西,编译成 AI 可以持续操作的东西。
llm.wiki 在编译知识。创作 skill 编译方法论。persona skill 编译人格。
全网的博主都在分享理论。今天我们分享一下如何能跑通这种知识的编译。
llm.wiki这套理论虽然看起来就是又一次渐进式披露的实践。
但是很多人觉着,这不只是一个 AI 工具而更像是一种元框架meta-framework。它并不依赖某个具体模型或技术栈而是在尝试定义一种人类与 AI 协作管理知识的方式。随着模型不断迭代、框架持续演进,让 LLM 帮助编译并维护一个持续生长的 Wiki 这一模式,反而具备更长期的稳定性和适用性。
## 所以llm.wiki在干什么
过去大模型使用文档都是用RAG。问一个要综合五份文档的问题模型每次都要重新找、重新拼。没有积累。NotebookLM、ChatGPT 的文件上传,其实都是这个模式。
但是llm.wiki的模式是 你丢一份新材料进去,模型不是索引它等着以后检索,而是立刻读它、提炼它、把关键信息编入已有的 wiki。更新实体页、修订概念页、标注新旧数据的矛盾。一份材料可能触及十几个 wiki 页面。
而且知识编译一次,然后持续维护。不是每次都从头来。
这个模式其实非常有意思不止可以用在建个人wiki场景还可以往很多场景拓展比如记忆。
karpathy大佬举了一些场景case如下图也很实用。尤其是今天的ai发展的这么快。前脚龙虾后脚就hermes。上个月的harness可能这个月就要拆掉一些了。 这种知识的管理。不论是对个人还是对自己的agent系统都非常重要。
正常情况下如果想打通这种自动wiki工作流很容易遇到各种奇形怪状的数据。但是llm.wiki 这些都假设数据是干净的markdown这还还挺不符合实际场景的。
所以我找了一批更符合真实场景的数据但是也没有精挑细选。主要是Anthropic、OpenAI关于harness的博客。还有新模型mythos的博客、智谱glm5.1的博客、以及智谱的招股书本来准备下载财报的好像下错了不过这个500多页pdf也有很多复杂的图表
然后就可以开始按照llm wiki的要求3层架构构建了。
把llm.wiki丢给agent会自动构建好目录结构。 我用的 cursor + opus 4.6。raw 放原始材料wiki 放 AI 维护的知识中间层AGENTS.md 告诉 AI 这个 wiki 怎么组织。
整体的一个壳子大概长下面这个样子。
然后第一道坎就来了。
如果你的实际数据不是规整的txt或者markdownai用pdfplumber转成的markdown就会变成这个样子。文字换行、缩进、表格、图片这些都没法保留甚至可能混乱。
不管是简单的博客,还是复杂的文档,解析成这个样子其实对模型都特别不友好。
还好我用的opus 4.6,对这些东西会鲁棒一些,如果用国产平替估计影响就比较大了。
但是。正好我们最近有一些业务涉及到复杂的word格式文档处理订阅了合合信息 TextIn 的 API所以我顺手做了个对比。
比如这是TextIn转写的博客结果图片和格式排版这些都有保留。
TextIn对表格的表示用的是用的html形式的所以它可以表示更复杂的无线表、合并单元格这些。
可以看下图。在招股书的解析里边,图表的结果也非常的不错。
最后还附上TextIn api的耗时参考。目前这套解析在我们现在内部的一个业务上跑的还不错。可以在这里测试TextIn的解析https://cc.co/16YSdj
搞定预编译之后,开始走 ingest 流程。这里有一个很蠢的坑模型喜欢偷懒一次性看一点文档然后ingest很多的文档。
结果每份材料都是浅读,生成的 wiki 页面跟目录没什么区别。信息密度极低。
所以这里我优化了一下默认的AGENTS.md让它一份份处理超过的还要分段来处理。
这个小优化会带来比较明显的数据处理质量的提升。
10 份材料全部 ingest 完之后,结构是这样的:
大概的一个流程是,解析->然后模型会按照AGENTS.md 梳理每份内容的要点,文档要点示例如下:
然后会整理出实体、概念。 以下是二者的示例。都会有明确的跟其他文件的link关系。
concept之间还会自动构建起对比comparisons示例
最后从结果来看因为我已经用了最顶级的Opus4.6模型了,所以不论是不是最好的解析方式。
带来的wiki结构密度其实差异不大。但是信息密度差异比较大。用TextIn API解析的数据可以保留更多的原始信息让整个库的信息密度更高。
所有有考虑搭建这种自动更新wiki的同学可以考虑尽量用最好的解析策略。
接下来就可以看出来这种关联wiki的魅力了。
比如Harness我放了4篇博客包含Ralph Wiggum的反对多Agent的博客以及OpenAI、Anthropic的相关博客。
这样在Agent Harness概念页就出现了同时容纳了正反两方的观点还用表格对比了两种流派的差异。
这种跨文档的交叉引用和矛盾标注RAG 是做不到的。RAG 能从单份文档里检索片段但它不会主动发现两个人其实在用不同方式解决同一个问题。但是通过这种模式构建的wiki 它就全都懂了。
或者需要结合多个跨文档综合的问题。比如「智谱跟 Anthropic 的商业化策略有什么不同」。
Agent就可以依据信息的链接跳转自动的去探索需要的信息找到最终的答案了。
而传统的利用单文档的longcontext chatbot 或者 RAG其实很难做这些事情。但是wiki已经把他们编译好了。
## 写在最后
坦率的讲,跑完整个流程之后我有一个很强的感受。
llm.wiki 这个模式从理论上肯定是跑得通的。你在里面能看到 GraphRAG 的影子,能看到 Skills 的影子,能看到 Context Engineering 的影子。这些东西换了不同的名字,但做的事情有很大的重叠。
而在 Harness Engineering 爆火的今天llm.wiki 其实又是在强调,过去那些手动维护知识库的包袱,可以扔了。一整套编译工作,交给模型就行。
但一个东西没变。
garbage in, garbage out。今天依然成立。
解析,仍然是很多项目真正的卡点。如果你现在还在被这个问题困扰,可以试试 TextIn地址在这里https://cc.co/16YSdj
实验的完整目录结构、AGENTS.md、解析脚本均已开源。https://github.com/Nipi64310/llmwiki-test

252
git_sync_daily.ps1 Normal file
View File

@@ -0,0 +1,252 @@
#!/usr/bin/env pwsh
# Git Sync Script with Gantt Integration
# Runs daily at 10:30 AM
param(
[string]$RepoPath = "D:/fork_project/obsidian-notes"
)
$InBoxPath = Join-Path $RepoPath "InBox"
$GanttTag = "obsidian"
$GitArgs = @("-c", "safe.directory=$RepoPath")
function Resolve-GanttApp {
$candidates = @(
"C:/Users/Administrator/Desktop/我的/app_gant2/src-tauri/target/release/gantt-app.exe",
"C:/Users/Administrator/Desktop/我的/app_gant2/src-tauri/target/debug/gantt-app.exe"
)
foreach ($candidate in $candidates) {
if (Test-Path $candidate) {
return $candidate
}
}
$searchRoot = "C:/Users/Administrator/Desktop"
if (Test-Path $searchRoot) {
$found = Get-ChildItem -Path $searchRoot -Filter "gantt-app.exe" -Recurse -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending |
Select-Object -First 1 -ExpandProperty FullName
if ($found) {
return $found
}
}
return $null
}
$GanttApp = Resolve-GanttApp
# Change to repo directory
Set-Location $RepoPath
function Invoke-GitCommand {
param(
[Parameter(Mandatory)]
[string[]]$Arguments
)
$output = & git @GitArgs @Arguments 2>&1
return [PSCustomObject]@{
Output = @($output)
ExitCode = $LASTEXITCODE
}
}
function Get-InBoxFileNames {
if (Test-Path $InBoxPath) {
return Get-ChildItem -Path $InBoxPath -File | Select-Object -ExpandProperty Name
}
return @()
}
# Function to get all Gantt tasks
function Get-GanttTasks {
if ($GanttApp -and (Test-Path $GanttApp)) {
try {
$result = & $GanttApp --cli list-tasks 2>$null | ConvertFrom-Json
if ($result.ok -and $result.data.tasks) {
return $result.data.tasks
}
} catch {
Write-Host "Failed to get Gantt tasks: $_"
}
}
return @()
}
# Function to show Windows notification
function Show-Notification {
param([string]$Title, [string]$Message)
Add-Type -AssemblyName System.Windows.Forms
$global:balloon = New-Object System.Windows.Forms.NotifyIcon
$path = (Get-Process -Id $pid).Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Warning
$balloon.BalloonTipText = $Message
$balloon.BalloonTipTitle = $Title
$balloon.Visible = $true
$balloon.ShowBalloonTip(5000)
}
function Fail-AndExit {
param(
[string]$Title,
[string]$Message
)
Write-Host $Message
Show-Notification -Title $Title -Message $Message
exit 1
}
function Test-RepoReady {
if (-not (Test-Path $RepoPath)) {
Fail-AndExit -Title "Git Sync Error" -Message "Repository path not found: $RepoPath"
}
$statusResult = Invoke-GitCommand -Arguments @("status", "--short", "--branch")
if ($statusResult.ExitCode -ne 0) {
$message = ($statusResult.Output -join [Environment]::NewLine).Trim()
if ([string]::IsNullOrWhiteSpace($message)) {
$message = "Failed to read git status."
}
Fail-AndExit -Title "Git Sync Error" -Message $message
}
$dirtyEntries = $statusResult.Output | Where-Object {
$_ -and $_ -notmatch "^## "
}
if ($dirtyEntries.Count -gt 0) {
$preview = ($dirtyEntries | Select-Object -First 5) -join "; "
$message = "Working tree is not clean. Resolve or commit local changes before auto sync. $preview"
Fail-AndExit -Title "Git Sync Blocked" -Message $message
}
}
function Add-GanttTask {
param([string]$TaskName)
$today = Get-Date -Format "yyyy-MM-dd"
$taskId = [Guid]::NewGuid().ToString()
if ($GanttApp -and (Test-Path $GanttApp)) {
try {
& $GanttApp --cli add-task --id $taskId --name $TaskName --start $today --description "Auto-created from InBox sync" --tag $GanttTag 2>$null
Write-Host "Created Gantt task: $TaskName"
} catch {
Write-Host "Failed to create Gantt task: $_"
}
} else {
Write-Host "Gantt app not found"
}
}
function Update-GanttTaskTag {
param(
[string]$TaskId,
[string]$TaskName
)
if ($GanttApp -and (Test-Path $GanttApp)) {
try {
& $GanttApp --cli update-task --id $TaskId --tag $GanttTag 2>$null
Write-Host "Updated Gantt task tag: $TaskName"
} catch {
Write-Host "Failed to update Gantt task tag: $_"
}
}
}
function Remove-GanttTask {
param([string]$TaskId)
if ($GanttApp -and (Test-Path $GanttApp)) {
try {
& $GanttApp --cli delete-task --id $TaskId 2>$null
Write-Host "Deleted Gantt task: $TaskId"
} catch {
Write-Host "Failed to delete Gantt task: $_"
}
}
}
function Sync-InBoxTasks {
if (-not ($GanttApp -and (Test-Path $GanttApp))) {
Write-Host "Skipping Gantt sync because gantt-app.exe was not found"
return
}
$inboxFiles = Get-InBoxFileNames
$ganttTasks = Get-GanttTasks
$existingInboxTasks = $ganttTasks | Where-Object { $_.name -match "^Process InBox: " }
foreach ($task in $existingInboxTasks) {
$taskFileName = $task.name -replace "^Process InBox: ", ""
if ($taskFileName -notin $inboxFiles) {
Write-Host "File no longer exists, deleting task: $($task.name)"
Remove-GanttTask -TaskId $task.id
}
}
foreach ($file in $inboxFiles) {
$taskName = "Process InBox: $file"
$matchingTasks = @($existingInboxTasks | Where-Object { $_.name -eq $taskName })
if ($matchingTasks.Count -eq 0) {
Add-GanttTask -TaskName $taskName
continue
}
foreach ($task in $matchingTasks) {
if ($task.tag -ne $GanttTag) {
Update-GanttTaskTag -TaskId $task.id -TaskName $task.name
} else {
Write-Host "Task already exists for: $file"
}
}
}
}
# Perform git pull
Test-RepoReady
Write-Host "Starting git pull at $(Get-Date)..."
$pullResult = Invoke-GitCommand -Arguments @("pull")
$pullOutput = $pullResult.Output
$pullExitCode = $pullResult.ExitCode
if ($pullExitCode -ne 0) {
$message = "Git pull failed: " + (($pullOutput -join [Environment]::NewLine).Trim())
Fail-AndExit -Title "Git Sync Error" -Message $message
}
Write-Host "Git pull completed: $($pullOutput -join [Environment]::NewLine)"
# Check for merge conflicts
if ($pullOutput -match "CONFLICT|conflict|Automatic merge failed") {
Show-Notification -Title "Git Sync Conflict" -Message "Merge conflicts detected during git pull. Please resolve manually."
exit 1
}
# Sync Gantt tasks against the full current InBox state
Start-Sleep -Seconds 2
Sync-InBoxTasks
# Perform git push
Write-Host "Starting git push..."
$pushResult = Invoke-GitCommand -Arguments @("push")
$pushOutput = $pushResult.Output
$pushExitCode = $pushResult.ExitCode
if ($pushExitCode -ne 0) {
$pushText = ($pushOutput -join [Environment]::NewLine).Trim()
if ($pushText -match "rejected|failed|conflict|non-fast-forward") {
Show-Notification -Title "Git Push Conflict" -Message "Push rejected. Please pull and resolve conflicts manually."
} else {
Show-Notification -Title "Git Push Error" -Message "Git push failed: $pushText"
}
exit 1
}
Write-Host "Git push completed successfully at $(Get-Date)"

37
setup-task.ps1 Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env pwsh
# Register the daily 10:30 AM git sync task for this repository.
param(
[string]$TaskName = "Obsidian-Git-Sync-Daily",
[string]$RepoPath = "D:\fork_project\obsidian-notes"
)
$scriptPath = Join-Path $RepoPath "git_sync_daily.ps1"
if (-not (Test-Path $scriptPath)) {
throw "Sync script not found: $scriptPath"
}
$existing = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($existing) {
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-Host "Removed existing task: $TaskName"
}
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$scriptPath`" -RepoPath `"$RepoPath`""
$trigger = New-ScheduledTaskTrigger -Daily -At 10:30AM
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable
Register-ScheduledTask -TaskName $TaskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Description "Daily 10:30 AM git sync for obsidian-notes and InBox/Gantt sync"
Write-Host "Task '$TaskName' registered."
Write-Host "Schedule: daily at 10:30 AM"
Write-Host "Script: $scriptPath"