From d7fd1a24da71246e0ae6301ec76087fd3c43db5a Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Thu, 18 Dec 2025 13:41:57 +0100 Subject: [PATCH] Big upgrade to windows and linux compability, also introduced a auto upgrading functionality to work cross platform with also the posiblity for Git init on app startup and checking when adding new projects. --- auto-claude-ui/electron.vite.config.ts | 10 +- auto-claude-ui/package-lock.json | 78 +++---- auto-claude-ui/package.json | 17 +- auto-claude-ui/resources/icon.ico | Bin 0 -> 75423 bytes .../src/main/agent/agent-process.ts | 11 +- auto-claude-ui/src/main/agent/agent-queue.ts | 24 +- .../src/main/changelog/generator.ts | 4 +- .../src/main/changelog/version-suggester.ts | 4 +- auto-claude-ui/src/main/index.ts | 2 +- auto-claude-ui/src/main/insights/config.ts | 3 +- .../src/main/ipc-handlers/project-handlers.ts | 45 +++- .../ipc-handlers/task/execution-handlers.ts | 54 +++++ .../ipc-handlers/task/worktree-handlers.ts | 6 +- .../src/main/project-initializer.ts | 146 +++++++++++++ .../src/main/terminal-name-generator.ts | 3 +- auto-claude-ui/src/main/title-generator.ts | 3 +- auto-claude-ui/src/preload/api/project-api.ts | 13 +- .../src/renderer/components/GitSetupModal.tsx | 206 ++++++++++++++++++ .../src/renderer/components/Sidebar.tsx | 51 ++++- .../src/renderer/lib/mocks/project-mock.ts | 14 ++ auto-claude-ui/src/shared/constants/ipc.ts | 2 + auto-claude-ui/src/shared/types/index.ts | 2 - auto-claude-ui/src/shared/types/ipc.ts | 5 +- auto-claude-ui/src/shared/types/project.ts | 7 + auto-claude/core/worktree.py | 18 +- auto-claude/implementation_plan/plan.py | 6 +- auto-claude/run.py | 32 +++ auto-claude/runners/spec_runner.py | 34 ++- auto-claude/spec/pipeline/agent_runner.py | 5 + auto-claude/spec/pipeline/orchestrator.py | 6 +- auto-claude/spec/validate_pkg/models.py | 6 +- auto-claude/task_logger/storage.py | 8 +- auto-claude/ui/__init__.py | 2 + auto-claude/ui/capabilities.py | 46 ++++ 34 files changed, 784 insertions(+), 89 deletions(-) create mode 100644 auto-claude-ui/resources/icon.ico create mode 100644 auto-claude-ui/src/renderer/components/GitSetupModal.tsx diff --git a/auto-claude-ui/electron.vite.config.ts b/auto-claude-ui/electron.vite.config.ts index 6fc4aa22..7d3b23aa 100644 --- a/auto-claude-ui/electron.vite.config.ts +++ b/auto-claude-ui/electron.vite.config.ts @@ -5,13 +5,21 @@ import { resolve } from 'path'; export default defineConfig({ main: { plugins: [externalizeDepsPlugin({ - exclude: [] + // Bundle these packages into the main process (they won't be in node_modules in packaged app) + exclude: [ + 'uuid', + 'chokidar', + 'ioredis', + 'electron-updater', + '@electron-toolkit/utils' + ] })], build: { rollupOptions: { input: { index: resolve(__dirname, 'src/main/index.ts') }, + // Only node-pty needs to be external (native module rebuilt by electron-builder) external: ['node-pty'] } } diff --git a/auto-claude-ui/package-lock.json b/auto-claude-ui/package-lock.json index 92920912..32602cef 100644 --- a/auto-claude-ui/package-lock.json +++ b/auto-claude-ui/package-lock.json @@ -42,7 +42,7 @@ "ioredis": "^5.8.2", "lucide-react": "^0.560.0", "motion": "^12.23.26", - "node-pty": "^1.0.0", + "node-pty": "^1.1.0-beta42", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", @@ -150,6 +150,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -535,6 +536,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -558,6 +560,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -597,6 +600,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -991,7 +995,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1013,7 +1016,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3928,8 +3930,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4126,6 +4127,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4136,6 +4138,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4227,6 +4230,7 @@ "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", @@ -4626,7 +4630,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/7zip-bin": { "version": "5.2.0", @@ -4648,6 +4653,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4708,6 +4714,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4880,7 +4887,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -5265,6 +5271,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5944,8 +5951,7 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -6289,6 +6295,7 @@ "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.0.12", "builder-util": "26.0.11", @@ -6346,8 +6353,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dotenv": { "version": "16.6.1", @@ -6423,6 +6429,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^22.7.7", @@ -6551,7 +6558,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -6572,7 +6578,6 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -6588,7 +6593,6 @@ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", - "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -6599,7 +6603,6 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 4.0.0" } @@ -6969,6 +6972,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8988,6 +8992,7 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -9931,7 +9936,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -11198,12 +11202,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/nan": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "license": "MIT" - }, "node_modules/nano-spawn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", @@ -11285,15 +11283,21 @@ } }, "node_modules/node-pty": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", - "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", + "version": "1.1.0-beta42", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta42.tgz", + "integrity": "sha512-59KoV6xxhJciRVpo4lQ9wnP38SPaBlXgwszYS8nlHAHrt02d14peg+kHtJ4AOtyLWiCf8WPCeJNbxBkiA7Oy7Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "nan": "^2.17.0" + "node-addon-api": "^7.1.0" } }, + "node_modules/node-pty/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -11771,6 +11775,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11868,6 +11873,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11904,7 +11910,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -11922,7 +11927,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -11943,7 +11947,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -11959,7 +11962,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -11972,8 +11974,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/proc-log": { "version": "2.0.1", @@ -12077,6 +12078,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12086,6 +12088,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -13402,7 +13405,8 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -13459,7 +13463,6 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -13486,7 +13489,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -13508,7 +13510,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13522,7 +13523,6 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -13537,7 +13537,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -13854,6 +13853,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14194,6 +14194,7 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -15227,6 +15228,7 @@ "integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/auto-claude-ui/package.json b/auto-claude-ui/package.json index 9da2ff1c..3d5ebb96 100644 --- a/auto-claude-ui/package.json +++ b/auto-claude-ui/package.json @@ -59,7 +59,7 @@ "ioredis": "^5.8.2", "lucide-react": "^0.560.0", "motion": "^12.23.26", - "node-pty": "^1.0.0", + "node-pty": "^1.1.0-beta42", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", @@ -127,7 +127,18 @@ "buildResources": "resources" }, "files": [ - "out/**/*" + "out/**/*", + "package.json" + ], + "extraResources": [ + { + "from": "node_modules/node-pty", + "to": "node_modules/node-pty" + }, + { + "from": "resources/icon.ico", + "to": "icon.ico" + } ], "mac": { "category": "public.app-category.developer-tools", @@ -138,7 +149,7 @@ ] }, "win": { - "icon": "resources/icon-256.png", + "icon": "resources/icon.ico", "target": [ "nsis", "zip" diff --git a/auto-claude-ui/resources/icon.ico b/auto-claude-ui/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9d5a5ff3789e2ef9812679dcf0526e9054a7ee04 GIT binary patch literal 75423 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�Z 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 Bc3J=c literal 0 HcmV?d00001 diff --git a/auto-claude-ui/src/main/agent/agent-process.ts b/auto-claude-ui/src/main/agent/agent-process.ts index d0afe73e..d55b57e8 100644 --- a/auto-claude-ui/src/main/agent/agent-process.ts +++ b/auto-claude-ui/src/main/agent/agent-process.ts @@ -176,7 +176,8 @@ export class AgentProcessManager { ...extraEnv, ...profileEnv, // Include active Claude profile config PYTHONUNBUFFERED: '1', // Ensure real-time output - PYTHONIOENCODING: 'utf-8' // Ensure UTF-8 encoding on Windows + PYTHONIOENCODING: 'utf-8', // Ensure UTF-8 encoding on Windows + PYTHONUTF8: '1' // Force Python UTF-8 mode on Windows (Python 3.7+) } }); @@ -241,17 +242,17 @@ export class AgentProcessManager { } }; - // Handle stdout + // Handle stdout - explicitly decode as UTF-8 for cross-platform Unicode support childProcess.stdout?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); console.log('[spawnProcess] stdout:', log.substring(0, 200)); this.emitter.emit('log', taskId, log); processLog(log); }); - // Handle stderr + // Handle stderr - explicitly decode as UTF-8 for cross-platform Unicode support childProcess.stderr?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); console.log('[spawnProcess] stderr:', log.substring(0, 200)); // Some Python output goes to stderr (like progress bars) // so we treat it as log, not error diff --git a/auto-claude-ui/src/main/agent/agent-queue.ts b/auto-claude-ui/src/main/agent/agent-queue.ts index 167151a5..7dd6cdd4 100644 --- a/auto-claude-ui/src/main/agent/agent-queue.ts +++ b/auto-claude-ui/src/main/agent/agent-queue.ts @@ -156,7 +156,9 @@ export class AgentQueueManager { ...process.env, ...combinedEnv, ...profileEnv, - PYTHONUNBUFFERED: '1' + PYTHONUNBUFFERED: '1', + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' } }); @@ -193,9 +195,9 @@ export class AgentQueueManager { const completedTypes = new Set(); const totalTypes = 7; // Default all types - // Handle stdout + // Handle stdout - explicitly decode as UTF-8 for cross-platform Unicode support childProcess.stdout?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); // Collect output for rate limit detection (keep last 10KB) allOutput = (allOutput + log).slice(-10000); @@ -242,9 +244,9 @@ export class AgentQueueManager { }); }); - // Handle stderr - also emit as logs + // Handle stderr - also emit as logs, explicitly decode as UTF-8 childProcess.stderr?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); // Collect stderr for rate limit detection too allOutput = (allOutput + log).slice(-10000); console.error('[Ideation STDERR]', log); @@ -354,7 +356,9 @@ export class AgentQueueManager { ...process.env, ...combinedEnv, ...profileEnv, - PYTHONUNBUFFERED: '1' + PYTHONUNBUFFERED: '1', + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' } }); @@ -384,9 +388,9 @@ export class AgentQueueManager { } }; - // Handle stdout + // Handle stdout - explicitly decode as UTF-8 for cross-platform Unicode support childProcess.stdout?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); // Collect output for rate limit detection (keep last 10KB) allRoadmapOutput = (allRoadmapOutput + log).slice(-10000); @@ -406,9 +410,9 @@ export class AgentQueueManager { }); }); - // Handle stderr + // Handle stderr - explicitly decode as UTF-8 childProcess.stderr?.on('data', (data: Buffer) => { - const log = data.toString(); + const log = data.toString('utf8'); // Collect stderr for rate limit detection too allRoadmapOutput = (allRoadmapOutput + log).slice(-10000); console.error('[Roadmap STDERR]', log); diff --git a/auto-claude-ui/src/main/changelog/generator.ts b/auto-claude-ui/src/main/changelog/generator.ts index 72c88e5e..3e4769a9 100644 --- a/auto-claude-ui/src/main/changelog/generator.ts +++ b/auto-claude-ui/src/main/changelog/generator.ts @@ -277,7 +277,9 @@ export class ChangelogGenerator extends EventEmitter { USER: process.env.USER || process.env.USERNAME || 'user', // Add common binary locations to PATH for claude CLI PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter), - PYTHONUNBUFFERED: '1' + PYTHONUNBUFFERED: '1', + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' }; this.debug('Spawn environment', { diff --git a/auto-claude-ui/src/main/changelog/version-suggester.ts b/auto-claude-ui/src/main/changelog/version-suggester.ts index c2ac06c8..ebb945f6 100644 --- a/auto-claude-ui/src/main/changelog/version-suggester.ts +++ b/auto-claude-ui/src/main/changelog/version-suggester.ts @@ -237,7 +237,9 @@ except Exception as e: ...(isWindows ? { USERPROFILE: homeDir } : { HOME: homeDir }), USER: process.env.USER || process.env.USERNAME || 'user', PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter), - PYTHONUNBUFFERED: '1' + PYTHONUNBUFFERED: '1', + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' }; return spawnEnv; diff --git a/auto-claude-ui/src/main/index.ts b/auto-claude-ui/src/main/index.ts index dcf8530b..a6d973e7 100644 --- a/auto-claude-ui/src/main/index.ts +++ b/auto-claude-ui/src/main/index.ts @@ -22,7 +22,7 @@ function getIconPath(): string { // Use PNG in dev mode (works better), ICNS in production iconName = is.dev ? 'icon-256.png' : 'icon.icns'; } else if (process.platform === 'win32') { - iconName = 'icon-256.png'; + iconName = 'icon.ico'; } else { iconName = 'icon.png'; } diff --git a/auto-claude-ui/src/main/insights/config.ts b/auto-claude-ui/src/main/insights/config.ts index 11884379..848449f1 100644 --- a/auto-claude-ui/src/main/insights/config.ts +++ b/auto-claude-ui/src/main/insights/config.ts @@ -105,7 +105,8 @@ export class InsightsConfig { ...autoBuildEnv, ...profileEnv, PYTHONUNBUFFERED: '1', - PYTHONIOENCODING: 'utf-8' + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' }; } } diff --git a/auto-claude-ui/src/main/ipc-handlers/project-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/project-handlers.ts index 868a5b25..230fcadd 100644 --- a/auto-claude-ui/src/main/ipc-handlers/project-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/project-handlers.ts @@ -9,13 +9,16 @@ import type { ProjectSettings, IPCResult, InitializationResult, - AutoBuildVersionInfo + AutoBuildVersionInfo, + GitStatus } from '../../shared/types'; import { projectStore } from '../project-store'; import { initializeProject, isInitialized, - hasLocalSource + hasLocalSource, + checkGitStatus, + initializeGit } from '../project-initializer'; import { PythonEnvManager, type PythonEnvStatus } from '../python-env-manager'; import { AgentManager } from '../agent'; @@ -505,4 +508,42 @@ export function registerProjectHandlers( } } ); + + // Check git status for a project (is it a repo? has commits?) + ipcMain.handle( + IPC_CHANNELS.GIT_CHECK_STATUS, + async (_, projectPath: string): Promise> => { + try { + if (!existsSync(projectPath)) { + return { success: false, error: 'Directory does not exist' }; + } + const gitStatus = checkGitStatus(projectPath); + return { success: true, data: gitStatus }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } + } + ); + + // Initialize git in a project (run git init and create initial commit) + ipcMain.handle( + IPC_CHANNELS.GIT_INITIALIZE, + async (_, projectPath: string): Promise> => { + try { + if (!existsSync(projectPath)) { + return { success: false, error: 'Directory does not exist' }; + } + const result = initializeGit(projectPath); + return { success: result.success, data: result, error: result.error }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } + } + ); } diff --git a/auto-claude-ui/src/main/ipc-handlers/task/execution-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/task/execution-handlers.ts index cb98c302..aaade6a0 100644 --- a/auto-claude-ui/src/main/ipc-handlers/task/execution-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/task/execution-handlers.ts @@ -6,6 +6,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { AgentManager } from '../../agent'; import { fileWatcher } from '../../file-watcher'; import { findTaskAndProject } from './shared'; +import { checkGitStatus } from '../../project-initializer'; /** * Register task execution handlers (start, stop, review, status management, recovery) @@ -40,6 +41,27 @@ export function registerTaskExecutionHandlers( return; } + // Check git status - Auto Claude requires git for worktree-based builds + const gitStatus = checkGitStatus(project.path); + if (!gitStatus.isGitRepo) { + console.log('[TASK_START] Project is not a git repository:', project.path); + mainWindow.webContents.send( + IPC_CHANNELS.TASK_ERROR, + taskId, + 'Git repository required. Please run "git init" in your project directory. Auto Claude uses git worktrees for isolated builds.' + ); + return; + } + if (!gitStatus.hasCommits) { + console.log('[TASK_START] Git repository has no commits:', project.path); + mainWindow.webContents.send( + IPC_CHANNELS.TASK_ERROR, + taskId, + 'Git repository has no commits. Please make an initial commit first (git add . && git commit -m "Initial commit").' + ); + return; + } + console.log('[TASK_START] Found task:', task.specId, 'status:', task.status, 'subtasks:', task.subtasks.length); // Start file watcher for this task @@ -287,6 +309,21 @@ export function registerTaskExecutionHandlers( // Auto-start task when status changes to 'in_progress' and no process is running if (status === 'in_progress' && !agentManager.isRunning(taskId)) { const mainWindow = getMainWindow(); + + // Check git status before auto-starting + const gitStatusCheck = checkGitStatus(project.path); + if (!gitStatusCheck.isGitRepo || !gitStatusCheck.hasCommits) { + console.log('[TASK_UPDATE_STATUS] Git check failed, cannot auto-start task'); + if (mainWindow) { + mainWindow.webContents.send( + IPC_CHANNELS.TASK_ERROR, + taskId, + gitStatusCheck.error || 'Git repository with commits required to run tasks.' + ); + } + return { success: false, error: gitStatusCheck.error || 'Git repository required' }; + } + console.log('[TASK_UPDATE_STATUS] Auto-starting task:', taskId); // Start file watcher for this task @@ -498,6 +535,23 @@ export function registerTaskExecutionHandlers( // Auto-restart the task if requested let autoRestarted = false; if (autoRestart && project) { + // Check git status before auto-restarting + const gitStatusForRestart = checkGitStatus(project.path); + if (!gitStatusForRestart.isGitRepo || !gitStatusForRestart.hasCommits) { + console.log('[Recovery] Git check failed, cannot auto-restart task'); + // Recovery succeeded but we can't restart without git + return { + success: true, + data: { + taskId, + recovered: true, + newStatus, + message: `Task recovered but cannot restart: ${gitStatusForRestart.error || 'Git repository with commits required.'}`, + autoRestarted: false + } + }; + } + try { // Set status to in_progress for the restart newStatus = 'in_progress'; diff --git a/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts index 92371e5d..bc424886 100644 --- a/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts @@ -315,7 +315,9 @@ export function registerWorktreeHandlers( env: { ...process.env, ...profileEnv, // Include active Claude profile OAuth token - PYTHONUNBUFFERED: '1' + PYTHONUNBUFFERED: '1', + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' }, stdio: ['ignore', 'pipe', 'pipe'] // Don't connect stdin to avoid blocking }); @@ -568,7 +570,7 @@ export function registerWorktreeHandlers( return new Promise((resolve) => { const previewProcess = spawn(pythonPath, args, { cwd: sourcePath, - env: { ...process.env, ...previewProfileEnv, PYTHONUNBUFFERED: '1', DEBUG: 'true' } + env: { ...process.env, ...previewProfileEnv, PYTHONUNBUFFERED: '1', PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1', DEBUG: 'true' } }); let stdout = ''; diff --git a/auto-claude-ui/src/main/project-initializer.ts b/auto-claude-ui/src/main/project-initializer.ts index 9bc687b2..2ba339ac 100644 --- a/auto-claude-ui/src/main/project-initializer.ts +++ b/auto-claude-ui/src/main/project-initializer.ts @@ -1,5 +1,6 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs'; import path from 'path'; +import { execSync } from 'child_process'; /** * Debug logging - only logs when AUTO_CLAUDE_DEBUG env var is set @@ -16,6 +17,137 @@ function debug(message: string, data?: Record): void { } } +/** + * Git status information for a project + */ +export interface GitStatus { + isGitRepo: boolean; + hasCommits: boolean; + currentBranch: string | null; + error?: string; +} + +/** + * Check if a directory is a git repository and has at least one commit + */ +export function checkGitStatus(projectPath: string): GitStatus { + try { + // Check if it's a git repository + execSync('git rev-parse --git-dir', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }); + } catch { + return { + isGitRepo: false, + hasCommits: false, + currentBranch: null, + error: 'Not a git repository. Please run "git init" to initialize git.' + }; + } + + // Check if there are any commits + let hasCommits = false; + try { + execSync('git rev-parse HEAD', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }); + hasCommits = true; + } catch { + // No commits yet + hasCommits = false; + } + + // Get current branch + let currentBranch: string | null = null; + try { + currentBranch = execSync('git rev-parse --abbrev-ref HEAD', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }).trim(); + } catch { + // Branch detection failed + } + + if (!hasCommits) { + return { + isGitRepo: true, + hasCommits: false, + currentBranch, + error: 'Git repository has no commits. Please make an initial commit first.' + }; + } + + return { + isGitRepo: true, + hasCommits: true, + currentBranch + }; +} + +/** + * Initialize git in a project directory and create an initial commit. + * This is a user-friendly way to set up git for non-technical users. + */ +export function initializeGit(projectPath: string): InitializationResult { + debug('initializeGit called', { projectPath }); + + // Check current git status + const status = checkGitStatus(projectPath); + + try { + // Step 1: Initialize git if needed + if (!status.isGitRepo) { + debug('Initializing git repository'); + execSync('git init', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }); + } + + // Step 2: Check if there are files to commit + const statusOutput = execSync('git status --porcelain', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }).trim(); + + // Step 3: If there are untracked/modified files, add and commit them + if (statusOutput || !status.hasCommits) { + debug('Adding files and creating initial commit'); + + // Add all files + execSync('git add -A', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }); + + // Create initial commit + execSync('git commit -m "Initial commit" --allow-empty', { + cwd: projectPath, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'] + }); + } + + debug('Git initialization complete'); + return { success: true }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error during git initialization'; + debug('Git initialization failed', { error: errorMessage }); + return { + success: false, + error: errorMessage + }; + } +} + /** * Entries to add to .gitignore when initializing a project */ @@ -130,6 +262,10 @@ export function isInitialized(projectPath: string): boolean { * * Creates .auto-claude/ with data directories (specs, ideation, insights, roadmap). * The framework code runs from the source repo - only data is stored here. + * + * Requires: + * - Project directory must exist + * - Project must be a git repository with at least one commit */ export function initializeProject(projectPath: string): InitializationResult { debug('initializeProject called', { projectPath }); @@ -143,6 +279,16 @@ export function initializeProject(projectPath: string): InitializationResult { }; } + // Check git status - Auto Claude requires git for worktree-based builds + const gitStatus = checkGitStatus(projectPath); + if (!gitStatus.isGitRepo || !gitStatus.hasCommits) { + debug('Git check failed', { gitStatus }); + return { + success: false, + error: gitStatus.error || 'Git repository required. Auto Claude uses git worktrees for isolated builds.' + }; + } + // Check if already initialized const dotAutoBuildPath = path.join(projectPath, '.auto-claude'); diff --git a/auto-claude-ui/src/main/terminal-name-generator.ts b/auto-claude-ui/src/main/terminal-name-generator.ts index d5d8010e..d7f9153b 100644 --- a/auto-claude-ui/src/main/terminal-name-generator.ts +++ b/auto-claude-ui/src/main/terminal-name-generator.ts @@ -137,7 +137,8 @@ export class TerminalNameGenerator extends EventEmitter { ...autoBuildEnv, ...profileEnv, // Include active Claude profile config PYTHONUNBUFFERED: '1', - PYTHONIOENCODING: 'utf-8' + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' } }); diff --git a/auto-claude-ui/src/main/title-generator.ts b/auto-claude-ui/src/main/title-generator.ts index e53fb6ee..9085604a 100644 --- a/auto-claude-ui/src/main/title-generator.ts +++ b/auto-claude-ui/src/main/title-generator.ts @@ -136,7 +136,8 @@ export class TitleGenerator extends EventEmitter { ...autoBuildEnv, ...profileEnv, // Include active Claude profile config PYTHONUNBUFFERED: '1', - PYTHONIOENCODING: 'utf-8' + PYTHONIOENCODING: 'utf-8', + PYTHONUTF8: '1' } }); diff --git a/auto-claude-ui/src/preload/api/project-api.ts b/auto-claude-ui/src/preload/api/project-api.ts index 6b55c47c..e8a8955d 100644 --- a/auto-claude-ui/src/preload/api/project-api.ts +++ b/auto-claude-ui/src/preload/api/project-api.ts @@ -15,7 +15,8 @@ import type { ClaudeAuthResult, InfrastructureStatus, GraphitiValidationResult, - GraphitiConnectionTestResult + GraphitiConnectionTestResult, + GitStatus } from '../../shared/types'; export interface ProjectAPI { @@ -72,6 +73,8 @@ export interface ProjectAPI { getGitBranches: (projectPath: string) => Promise>; getCurrentGitBranch: (projectPath: string) => Promise>; detectMainBranch: (projectPath: string) => Promise>; + checkGitStatus: (projectPath: string) => Promise>; + initializeGit: (projectPath: string) => Promise>; } export const createProjectAPI = (): ProjectAPI => ({ @@ -180,5 +183,11 @@ export const createProjectAPI = (): ProjectAPI => ({ ipcRenderer.invoke(IPC_CHANNELS.GIT_GET_CURRENT_BRANCH, projectPath), detectMainBranch: (projectPath: string): Promise> => - ipcRenderer.invoke(IPC_CHANNELS.GIT_DETECT_MAIN_BRANCH, projectPath) + ipcRenderer.invoke(IPC_CHANNELS.GIT_DETECT_MAIN_BRANCH, projectPath), + + checkGitStatus: (projectPath: string): Promise> => + ipcRenderer.invoke(IPC_CHANNELS.GIT_CHECK_STATUS, projectPath), + + initializeGit: (projectPath: string): Promise> => + ipcRenderer.invoke(IPC_CHANNELS.GIT_INITIALIZE, projectPath) }); diff --git a/auto-claude-ui/src/renderer/components/GitSetupModal.tsx b/auto-claude-ui/src/renderer/components/GitSetupModal.tsx new file mode 100644 index 00000000..16ac15a6 --- /dev/null +++ b/auto-claude-ui/src/renderer/components/GitSetupModal.tsx @@ -0,0 +1,206 @@ +import { useState } from 'react'; +import { GitBranch, Terminal, CheckCircle2, AlertCircle, Loader2, FolderGit2 } from 'lucide-react'; +import { Button } from './ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from './ui/dialog'; +import type { Project, GitStatus } from '../../shared/types'; + +interface GitSetupModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + project: Project | null; + gitStatus: GitStatus | null; + onGitInitialized: () => void; + onSkip?: () => void; +} + +export function GitSetupModal({ + open, + onOpenChange, + project, + gitStatus, + onGitInitialized, + onSkip +}: GitSetupModalProps) { + const [isInitializing, setIsInitializing] = useState(false); + const [error, setError] = useState(null); + const [step, setStep] = useState<'info' | 'initializing' | 'success'>('info'); + + const needsGitInit = gitStatus && !gitStatus.isGitRepo; + const needsCommit = gitStatus && gitStatus.isGitRepo && !gitStatus.hasCommits; + + const handleInitializeGit = async () => { + if (!project) return; + + setIsInitializing(true); + setError(null); + setStep('initializing'); + + try { + // Call the backend to initialize git + const result = await window.electronAPI.initializeGit(project.path); + + if (result.success) { + setStep('success'); + // Wait a moment to show success, then trigger callback + setTimeout(() => { + onGitInitialized(); + onOpenChange(false); + setStep('info'); + }, 1500); + } else { + setError(result.error || 'Failed to initialize git'); + setStep('info'); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to initialize git'); + setStep('info'); + } finally { + setIsInitializing(false); + } + }; + + const handleSkip = () => { + onSkip?.(); + onOpenChange(false); + }; + + const renderInfoStep = () => ( + <> + + + + Git Repository Required + + + Auto Claude uses git to safely build features in isolated workspaces + + + +
+ {/* Status indicator */} +
+
+ +
+

+ {needsGitInit + ? 'This folder is not a git repository' + : 'Git repository has no commits'} +

+

+ {needsGitInit + ? 'Git needs to be initialized before Auto Claude can manage your code.' + : 'At least one commit is required for Auto Claude to create worktrees.'} +

+
+
+
+ + {/* What will happen */} +
+

We'll set up git for you:

+
    + {needsGitInit && ( +
  • + + Initialize a new git repository +
  • + )} +
  • + + Create an initial commit with your current files +
  • +
+
+ + {/* Manual instructions for advanced users */} +
+ + Prefer to do it manually? + +
+

Open a terminal in your project folder and run:

+ {needsGitInit &&

git init

} +

git add .

+

git commit -m "Initial commit"

+
+
+ + {error && ( +
+ {error} +
+ )} +
+ + + + + + + ); + + const renderInitializingStep = () => ( + <> + + + + Setting up Git + + + +
+
+ +

+ Initializing git repository and creating initial commit... +

+
+
+ + ); + + const renderSuccessStep = () => ( + <> + + + + Git Initialized + + + +
+
+
+ +
+

+ Your project is now ready to use with Auto Claude! +

+
+
+ + ); + + return ( + + + {step === 'info' && renderInfoStep()} + {step === 'initializing' && renderInitializingStep()} + {step === 'success' && renderSuccessStep()} + + + ); +} diff --git a/auto-claude-ui/src/renderer/components/Sidebar.tsx b/auto-claude-ui/src/renderer/components/Sidebar.tsx index bbbd008b..f0eb41e8 100644 --- a/auto-claude-ui/src/renderer/components/Sidebar.tsx +++ b/auto-claude-ui/src/renderer/components/Sidebar.tsx @@ -55,8 +55,9 @@ import { } from '../stores/project-store'; import { useSettingsStore, saveSettings } from '../stores/settings-store'; import { AddProjectModal } from './AddProjectModal'; +import { GitSetupModal } from './GitSetupModal'; import { RateLimitIndicator } from './RateLimitIndicator'; -import type { Project, AutoBuildVersionInfo } from '../../shared/types'; +import type { Project, AutoBuildVersionInfo, GitStatus } from '../../shared/types'; export type SidebarView = 'kanban' | 'terminals' | 'roadmap' | 'context' | 'ideation' | 'github-issues' | 'changelog' | 'insights' | 'worktrees' | 'agent-tools' | 'agent-profiles'; @@ -104,6 +105,8 @@ export function Sidebar({ const [showAddProjectModal, setShowAddProjectModal] = useState(false); const [showInitDialog, setShowInitDialog] = useState(false); const [showUpdateDialog, setShowUpdateDialog] = useState(false); + const [showGitSetupModal, setShowGitSetupModal] = useState(false); + const [gitStatus, setGitStatus] = useState(null); const [pendingProject, setPendingProject] = useState(null); const [versionInfo, setVersionInfo] = useState(null); const [isInitializing, setIsInitializing] = useState(false); @@ -159,6 +162,29 @@ export function Sidebar({ checkUpdates(); }, [selectedProjectId, settings.autoUpdateAutoBuild]); + // Check git status when project changes + useEffect(() => { + const checkGit = async () => { + if (selectedProject) { + try { + const result = await window.electronAPI.checkGitStatus(selectedProject.path); + if (result.success && result.data) { + setGitStatus(result.data); + // Show git setup modal if project is not a git repo or has no commits + if (!result.data.isGitRepo || !result.data.hasCommits) { + setShowGitSetupModal(true); + } + } + } catch (error) { + console.error('Failed to check git status:', error); + } + } else { + setGitStatus(null); + } + }; + checkGit(); + }, [selectedProject]); + const handleAddProject = () => { setShowAddProjectModal(true); }; @@ -210,6 +236,20 @@ export function Sidebar({ setVersionInfo(null); }; + const handleGitInitialized = async () => { + // Refresh git status after initialization + if (selectedProject) { + try { + const result = await window.electronAPI.checkGitStatus(selectedProject.path); + if (result.success && result.data) { + setGitStatus(result.data); + } + } catch (error) { + console.error('Failed to refresh git status:', error); + } + } + }; + const handleRemoveProject = async (projectId: string, e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); @@ -518,6 +558,15 @@ export function Sidebar({ onOpenChange={setShowAddProjectModal} onProjectAdded={handleProjectAdded} /> + + {/* Git Setup Modal */} + ); } diff --git a/auto-claude-ui/src/renderer/lib/mocks/project-mock.ts b/auto-claude-ui/src/renderer/lib/mocks/project-mock.ts index 2c8ba526..13926a44 100644 --- a/auto-claude-ui/src/renderer/lib/mocks/project-mock.ts +++ b/auto-claude-ui/src/renderer/lib/mocks/project-mock.ts @@ -84,5 +84,19 @@ export const projectMock = { detectMainBranch: async () => ({ success: true, data: 'main' + }), + + checkGitStatus: async () => ({ + success: true, + data: { + isGitRepo: true, + hasCommits: true, + currentBranch: 'main' + } + }), + + initializeGit: async () => ({ + success: true, + data: { success: true } }) }; diff --git a/auto-claude-ui/src/shared/constants/ipc.ts b/auto-claude-ui/src/shared/constants/ipc.ts index 3999b4d4..e3f2563a 100644 --- a/auto-claude-ui/src/shared/constants/ipc.ts +++ b/auto-claude-ui/src/shared/constants/ipc.ts @@ -258,6 +258,8 @@ export const IPC_CHANNELS = { GIT_GET_BRANCHES: 'git:getBranches', GIT_GET_CURRENT_BRANCH: 'git:getCurrentBranch', GIT_DETECT_MAIN_BRANCH: 'git:detectMainBranch', + GIT_CHECK_STATUS: 'git:checkStatus', + GIT_INITIALIZE: 'git:initialize', // App auto-update operations APP_UPDATE_CHECK: 'app-update:check', diff --git a/auto-claude-ui/src/shared/types/index.ts b/auto-claude-ui/src/shared/types/index.ts index d779164b..5da875a6 100644 --- a/auto-claude-ui/src/shared/types/index.ts +++ b/auto-claude-ui/src/shared/types/index.ts @@ -15,8 +15,6 @@ export * from './changelog'; export * from './insights'; export * from './roadmap'; export * from './integrations'; -export * from './glossary'; -export * from './status'; export * from './app-update'; // IPC types (must be last to use types from other modules) diff --git a/auto-claude-ui/src/shared/types/ipc.ts b/auto-claude-ui/src/shared/types/ipc.ts index 2c82e633..44b147bc 100644 --- a/auto-claude-ui/src/shared/types/ipc.ts +++ b/auto-claude-ui/src/shared/types/ipc.ts @@ -18,7 +18,8 @@ import type { ProjectEnvConfig, InfrastructureStatus, GraphitiValidationResult, - GraphitiConnectionTestResult + GraphitiConnectionTestResult, + GitStatus } from './project'; import type { Task, @@ -500,6 +501,8 @@ export interface ElectronAPI { getGitBranches: (projectPath: string) => Promise>; getCurrentGitBranch: (projectPath: string) => Promise>; detectMainBranch: (projectPath: string) => Promise>; + checkGitStatus: (projectPath: string) => Promise>; + initializeGit: (projectPath: string) => Promise>; } declare global { diff --git a/auto-claude-ui/src/shared/types/project.ts b/auto-claude-ui/src/shared/types/project.ts index 840cbcbf..c4675fca 100644 --- a/auto-claude-ui/src/shared/types/project.ts +++ b/auto-claude-ui/src/shared/types/project.ts @@ -305,6 +305,13 @@ export interface InitializationResult { error?: string; } +export interface GitStatus { + isGitRepo: boolean; + hasCommits: boolean; + currentBranch: string | null; + error?: string; +} + export interface CreateProjectFolderResult { path: string; name: string; diff --git a/auto-claude/core/worktree.py b/auto-claude/core/worktree.py index ef7e0f80..8e3dece8 100644 --- a/auto-claude/core/worktree.py +++ b/auto-claude/core/worktree.py @@ -79,11 +79,15 @@ class WorktreeManager: cwd=self.project_dir, capture_output=True, text=True, + encoding="utf-8", + errors="replace", ) if result.returncode == 0: return env_branch else: - print(f"Warning: DEFAULT_BRANCH '{env_branch}' not found, auto-detecting...") + print( + f"Warning: DEFAULT_BRANCH '{env_branch}' not found, auto-detecting..." + ) # 2. Auto-detect main/master for branch in ["main", "master"]: @@ -92,15 +96,17 @@ class WorktreeManager: cwd=self.project_dir, capture_output=True, text=True, + encoding="utf-8", + errors="replace", ) if result.returncode == 0: return branch # 3. Fall back to current branch with warning current = self._get_current_branch() - print(f"Warning: Could not find 'main' or 'master' branch.") + print("Warning: Could not find 'main' or 'master' branch.") print(f"Warning: Using current branch '{current}' as base for worktree.") - print(f"Tip: Set DEFAULT_BRANCH=your-branch in .env to avoid this.") + print("Tip: Set DEFAULT_BRANCH=your-branch in .env to avoid this.") return current def _get_current_branch(self) -> str: @@ -110,6 +116,8 @@ class WorktreeManager: cwd=self.project_dir, capture_output=True, text=True, + encoding="utf-8", + errors="replace", ) if result.returncode != 0: raise WorktreeError(f"Failed to get current branch: {result.stderr}") @@ -124,6 +132,8 @@ class WorktreeManager: cwd=cwd or self.project_dir, capture_output=True, text=True, + encoding="utf-8", + errors="replace", ) def _unstage_gitignored_files(self) -> None: @@ -153,6 +163,8 @@ class WorktreeManager: input="\n".join(staged_files), capture_output=True, text=True, + encoding="utf-8", + errors="replace", ) if result.stdout.strip(): diff --git a/auto-claude/implementation_plan/plan.py b/auto-claude/implementation_plan/plan.py index 277cb103..46ef62eb 100644 --- a/auto-claude/implementation_plan/plan.py +++ b/auto-claude/implementation_plan/plan.py @@ -105,8 +105,8 @@ class ImplementationPlan: self.update_status_from_subtasks() path.parent.mkdir(parents=True, exist_ok=True) - with open(path, "w") as f: - json.dump(self.to_dict(), f, indent=2) + with open(path, "w", encoding="utf-8") as f: + json.dump(self.to_dict(), f, indent=2, ensure_ascii=False) def update_status_from_subtasks(self): """Update overall status and planStatus based on subtask completion state. @@ -169,7 +169,7 @@ class ImplementationPlan: @classmethod def load(cls, path: Path) -> "ImplementationPlan": """Load plan from JSON file.""" - with open(path) as f: + with open(path, encoding="utf-8") as f: return cls.from_dict(json.load(f)) def get_available_phases(self) -> list[Phase]: diff --git a/auto-claude/run.py b/auto-claude/run.py index 3def46ca..f8936d74 100644 --- a/auto-claude/run.py +++ b/auto-claude/run.py @@ -28,6 +28,38 @@ Prerequisites: - Claude Code CLI installed """ +import io +import sys + +# Configure safe encoding on Windows BEFORE any imports that might print +# This handles both TTY and piped output (e.g., from Electron) +if sys.platform == "win32": + for _stream_name in ("stdout", "stderr"): + _stream = getattr(sys, _stream_name) + # Method 1: Try reconfigure (works for TTY) + if hasattr(_stream, "reconfigure"): + try: + _stream.reconfigure(encoding="utf-8", errors="replace") + continue + except (AttributeError, io.UnsupportedOperation, OSError): + pass + # Method 2: Wrap with TextIOWrapper for piped output + try: + if hasattr(_stream, "buffer"): + _new_stream = io.TextIOWrapper( + _stream.buffer, + encoding="utf-8", + errors="replace", + line_buffering=True, + ) + setattr(sys, _stream_name, _new_stream) + except (AttributeError, io.UnsupportedOperation, OSError): + pass + # Clean up temporary variables + del _stream_name, _stream + if "_new_stream" in dir(): + del _new_stream + from cli import main if __name__ == "__main__": diff --git a/auto-claude/runners/spec_runner.py b/auto-claude/runners/spec_runner.py index 928b7354..c4426465 100644 --- a/auto-claude/runners/spec_runner.py +++ b/auto-claude/runners/spec_runner.py @@ -34,10 +34,40 @@ Usage: """ import asyncio +import io import os import sys from pathlib import Path +# Configure safe encoding on Windows BEFORE any imports that might print +# This handles both TTY and piped output (e.g., from Electron) +if sys.platform == "win32": + for _stream_name in ("stdout", "stderr"): + _stream = getattr(sys, _stream_name) + # Method 1: Try reconfigure (works for TTY) + if hasattr(_stream, "reconfigure"): + try: + _stream.reconfigure(encoding="utf-8", errors="replace") + continue + except (AttributeError, io.UnsupportedOperation, OSError): + pass + # Method 2: Wrap with TextIOWrapper for piped output + try: + if hasattr(_stream, "buffer"): + _new_stream = io.TextIOWrapper( + _stream.buffer, + encoding="utf-8", + errors="replace", + line_buffering=True, + ) + setattr(sys, _stream_name, _new_stream) + except (AttributeError, io.UnsupportedOperation, OSError): + pass + # Clean up temporary variables + del _stream_name, _stream + if "_new_stream" in dir(): + del _new_stream + # Add auto-claude to path (parent of runners/) sys.path.insert(0, str(Path(__file__).parent.parent)) @@ -230,7 +260,9 @@ Examples: print( f" {muted('Or re-run spec_runner with --auto-approve to skip review:')}" ) - example_cmd = 'python auto-claude/spec_runner.py --task "..." --auto-approve' + example_cmd = ( + 'python auto-claude/spec_runner.py --task "..." --auto-approve' + ) print(f" {highlight(example_cmd)}") sys.exit(1) diff --git a/auto-claude/spec/pipeline/agent_runner.py b/auto-claude/spec/pipeline/agent_runner.py index 49edcc65..e06c2285 100644 --- a/auto-claude/spec/pipeline/agent_runner.py +++ b/auto-claude/spec/pipeline/agent_runner.py @@ -7,6 +7,11 @@ Handles the execution of AI agents for the spec creation pipeline. from pathlib import Path +# Configure safe encoding before any output (fixes Windows encoding errors) +from ui.capabilities import configure_safe_encoding + +configure_safe_encoding() + from client import create_client from task_logger import ( LogEntryType, diff --git a/auto-claude/spec/pipeline/orchestrator.py b/auto-claude/spec/pipeline/orchestrator.py index b488268b..4db90d9a 100644 --- a/auto-claude/spec/pipeline/orchestrator.py +++ b/auto-claude/spec/pipeline/orchestrator.py @@ -457,9 +457,11 @@ class SpecOrchestrator: print_key_value("Reasoning", assessment.reasoning) if assessment.needs_research: - print(f" {muted('→ Research phase enabled')}") + print(f" {muted(icon(Icons.ARROW_RIGHT) + ' Research phase enabled')}") if assessment.needs_self_critique: - print(f" {muted('→ Self-critique phase enabled')}") + print( + f" {muted(icon(Icons.ARROW_RIGHT) + ' Self-critique phase enabled')}" + ) def _print_phases_to_run(self) -> None: """Print the list of phases that will be executed.""" diff --git a/auto-claude/spec/validate_pkg/models.py b/auto-claude/spec/validate_pkg/models.py index 6ece9156..984f4c07 100644 --- a/auto-claude/spec/validate_pkg/models.py +++ b/auto-claude/spec/validate_pkg/models.py @@ -30,16 +30,16 @@ class ValidationResult: if self.errors: lines.append("\nErrors:") for err in self.errors: - lines.append(f" ✗ {err}") + lines.append(f" [X] {err}") if self.warnings: lines.append("\nWarnings:") for warn in self.warnings: - lines.append(f" ⚠ {warn}") + lines.append(f" [!] {warn}") if self.fixes and not self.valid: lines.append("\nSuggested Fixes:") for fix in self.fixes: - lines.append(f" → {fix}") + lines.append(f" -> {fix}") return "\n".join(lines) diff --git a/auto-claude/task_logger/storage.py b/auto-claude/task_logger/storage.py index 572c14de..ba2605e9 100644 --- a/auto-claude/task_logger/storage.py +++ b/auto-claude/task_logger/storage.py @@ -30,7 +30,7 @@ class LogStorage: """Load existing logs or create new structure.""" if self.log_file.exists(): try: - with open(self.log_file) as f: + with open(self.log_file, encoding="utf-8") as f: return json.load(f) except (OSError, json.JSONDecodeError): pass @@ -69,8 +69,8 @@ class LogStorage: self._data["updated_at"] = self._timestamp() try: self.spec_dir.mkdir(parents=True, exist_ok=True) - with open(self.log_file, "w") as f: - json.dump(self._data, f, indent=2) + with open(self.log_file, "w", encoding="utf-8") as f: + json.dump(self._data, f, indent=2, ensure_ascii=False) except OSError as e: print(f"Warning: Failed to save task logs: {e}", file=sys.stderr) @@ -159,7 +159,7 @@ def load_task_logs(spec_dir: Path) -> dict | None: return None try: - with open(log_file) as f: + with open(log_file, encoding="utf-8") as f: return json.load(f) except (OSError, json.JSONDecodeError): return None diff --git a/auto-claude/ui/__init__.py b/auto-claude/ui/__init__.py index 13b57982..959db946 100644 --- a/auto-claude/ui/__init__.py +++ b/auto-claude/ui/__init__.py @@ -21,6 +21,7 @@ from .capabilities import ( FANCY_UI, INTERACTIVE, UNICODE, + configure_safe_encoding, supports_color, supports_interactive, supports_unicode, @@ -57,6 +58,7 @@ _INTERACTIVE = INTERACTIVE __all__ = [ # Capabilities + "configure_safe_encoding", "supports_unicode", "supports_color", "supports_interactive", diff --git a/auto-claude/ui/capabilities.py b/auto-claude/ui/capabilities.py index ed4c7c34..ac8de510 100644 --- a/auto-claude/ui/capabilities.py +++ b/auto-claude/ui/capabilities.py @@ -8,10 +8,56 @@ Detects terminal capabilities for: - Interactive input support """ +import io import os import sys +def configure_safe_encoding() -> None: + """ + Configure stdout/stderr to handle Unicode safely on Windows. + + On Windows, the default console encoding (cp1252) can't display many + Unicode characters. This function forces UTF-8 encoding with 'replace' + error handling, so unrenderable characters are replaced with '?' instead + of raising exceptions. + + This handles both: + 1. Regular console output (reconfigure method) + 2. Piped output from subprocess (TextIOWrapper replacement) + """ + if sys.platform != "win32": + return + + # Method 1: Try reconfigure (works for TTY) + for stream_name in ("stdout", "stderr"): + stream = getattr(sys, stream_name) + if hasattr(stream, "reconfigure"): + try: + stream.reconfigure(encoding="utf-8", errors="replace") + continue + except (AttributeError, io.UnsupportedOperation, OSError): + pass + + # Method 2: Wrap with TextIOWrapper for piped output + # This is needed when stdout/stderr are pipes (e.g., from Electron) + try: + if hasattr(stream, "buffer"): + new_stream = io.TextIOWrapper( + stream.buffer, + encoding="utf-8", + errors="replace", + line_buffering=True, + ) + setattr(sys, stream_name, new_stream) + except (AttributeError, io.UnsupportedOperation, OSError): + pass + + +# Configure safe encoding on module import +configure_safe_encoding() + + def _is_fancy_ui_enabled() -> bool: """Check if fancy UI is enabled via environment variable.""" value = os.environ.get("ENABLE_FANCY_UI", "true").lower()