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.
This commit is contained in:
@@ -5,13 +5,21 @@ import { resolve } from 'path';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
main: {
|
main: {
|
||||||
plugins: [externalizeDepsPlugin({
|
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: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
index: resolve(__dirname, 'src/main/index.ts')
|
index: resolve(__dirname, 'src/main/index.ts')
|
||||||
},
|
},
|
||||||
|
// Only node-pty needs to be external (native module rebuilt by electron-builder)
|
||||||
external: ['node-pty']
|
external: ['node-pty']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+40
-38
@@ -42,7 +42,7 @@
|
|||||||
"ioredis": "^5.8.2",
|
"ioredis": "^5.8.2",
|
||||||
"lucide-react": "^0.560.0",
|
"lucide-react": "^0.560.0",
|
||||||
"motion": "^12.23.26",
|
"motion": "^12.23.26",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.1.0-beta42",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -535,6 +536,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
@@ -558,6 +560,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@@ -597,6 +600,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/accessibility": "^3.1.1",
|
"@dnd-kit/accessibility": "^3.1.1",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -991,7 +995,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -1013,7 +1016,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -3928,8 +3930,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
@@ -4126,6 +4127,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -4136,6 +4138,7 @@
|
|||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -4227,6 +4230,7 @@
|
|||||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.49.0",
|
"@typescript-eslint/scope-manager": "8.49.0",
|
||||||
"@typescript-eslint/types": "8.49.0",
|
"@typescript-eslint/types": "8.49.0",
|
||||||
@@ -4626,7 +4630,8 @@
|
|||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/7zip-bin": {
|
"node_modules/7zip-bin": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
@@ -4648,6 +4653,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -4708,6 +4714,7 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -4880,7 +4887,6 @@
|
|||||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dequal": "^2.0.3"
|
"dequal": "^2.0.3"
|
||||||
}
|
}
|
||||||
@@ -5265,6 +5271,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -5944,8 +5951,7 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
@@ -6289,6 +6295,7 @@
|
|||||||
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.0.12",
|
"app-builder-lib": "26.0.12",
|
||||||
"builder-util": "26.0.11",
|
"builder-util": "26.0.11",
|
||||||
@@ -6346,8 +6353,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.6.1",
|
"version": "16.6.1",
|
||||||
@@ -6423,6 +6429,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^22.7.7",
|
"@types/node": "^22.7.7",
|
||||||
@@ -6551,7 +6558,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -6572,7 +6578,6 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -6588,7 +6593,6 @@
|
|||||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
@@ -6599,7 +6603,6 @@
|
|||||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
@@ -6969,6 +6972,7 @@
|
|||||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -8988,6 +8992,7 @@
|
|||||||
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
|
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssstyle": "^4.2.1",
|
"cssstyle": "^4.2.1",
|
||||||
"data-urls": "^5.0.0",
|
"data-urls": "^5.0.0",
|
||||||
@@ -9931,7 +9936,6 @@
|
|||||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"lz-string": "bin/bin.js"
|
"lz-string": "bin/bin.js"
|
||||||
}
|
}
|
||||||
@@ -11198,12 +11202,6 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/nano-spawn": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz",
|
||||||
@@ -11285,15 +11283,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-pty": {
|
"node_modules/node-pty": {
|
||||||
"version": "1.0.0",
|
"version": "1.1.0-beta42",
|
||||||
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta42.tgz",
|
||||||
"integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==",
|
"integrity": "sha512-59KoV6xxhJciRVpo4lQ9wnP38SPaBlXgwszYS8nlHAHrt02d14peg+kHtJ4AOtyLWiCf8WPCeJNbxBkiA7Oy7Q==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"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": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.27",
|
"version": "2.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||||
@@ -11771,6 +11775,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -11868,6 +11873,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -11904,7 +11910,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -11922,7 +11927,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -11943,7 +11947,6 @@
|
|||||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1",
|
"ansi-regex": "^5.0.1",
|
||||||
"ansi-styles": "^5.0.0",
|
"ansi-styles": "^5.0.0",
|
||||||
@@ -11959,7 +11962,6 @@
|
|||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -11972,8 +11974,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/proc-log": {
|
"node_modules/proc-log": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -12077,6 +12078,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -12086,6 +12088,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -13402,7 +13405,8 @@
|
|||||||
"version": "4.1.18",
|
"version": "4.1.18",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
|
||||||
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
|
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@@ -13459,7 +13463,6 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -13486,7 +13489,6 @@
|
|||||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -13508,7 +13510,6 @@
|
|||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -13522,7 +13523,6 @@
|
|||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -13537,7 +13537,6 @@
|
|||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
@@ -13854,6 +13853,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -14194,6 +14194,7 @@
|
|||||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -15227,6 +15228,7 @@
|
|||||||
"integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==",
|
"integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"ioredis": "^5.8.2",
|
"ioredis": "^5.8.2",
|
||||||
"lucide-react": "^0.560.0",
|
"lucide-react": "^0.560.0",
|
||||||
"motion": "^12.23.26",
|
"motion": "^12.23.26",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.1.0-beta42",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
@@ -127,7 +127,18 @@
|
|||||||
"buildResources": "resources"
|
"buildResources": "resources"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"out/**/*"
|
"out/**/*",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "node_modules/node-pty",
|
||||||
|
"to": "node_modules/node-pty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "resources/icon.ico",
|
||||||
|
"to": "icon.ico"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.developer-tools",
|
"category": "public.app-category.developer-tools",
|
||||||
@@ -138,7 +149,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "resources/icon-256.png",
|
"icon": "resources/icon.ico",
|
||||||
"target": [
|
"target": [
|
||||||
"nsis",
|
"nsis",
|
||||||
"zip"
|
"zip"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
@@ -176,7 +176,8 @@ export class AgentProcessManager {
|
|||||||
...extraEnv,
|
...extraEnv,
|
||||||
...profileEnv, // Include active Claude profile config
|
...profileEnv, // Include active Claude profile config
|
||||||
PYTHONUNBUFFERED: '1', // Ensure real-time output
|
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) => {
|
childProcess.stdout?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
console.log('[spawnProcess] stdout:', log.substring(0, 200));
|
console.log('[spawnProcess] stdout:', log.substring(0, 200));
|
||||||
this.emitter.emit('log', taskId, log);
|
this.emitter.emit('log', taskId, log);
|
||||||
processLog(log);
|
processLog(log);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle stderr
|
// Handle stderr - explicitly decode as UTF-8 for cross-platform Unicode support
|
||||||
childProcess.stderr?.on('data', (data: Buffer) => {
|
childProcess.stderr?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
console.log('[spawnProcess] stderr:', log.substring(0, 200));
|
console.log('[spawnProcess] stderr:', log.substring(0, 200));
|
||||||
// Some Python output goes to stderr (like progress bars)
|
// Some Python output goes to stderr (like progress bars)
|
||||||
// so we treat it as log, not error
|
// so we treat it as log, not error
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ export class AgentQueueManager {
|
|||||||
...process.env,
|
...process.env,
|
||||||
...combinedEnv,
|
...combinedEnv,
|
||||||
...profileEnv,
|
...profileEnv,
|
||||||
PYTHONUNBUFFERED: '1'
|
PYTHONUNBUFFERED: '1',
|
||||||
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -193,9 +195,9 @@ export class AgentQueueManager {
|
|||||||
const completedTypes = new Set<string>();
|
const completedTypes = new Set<string>();
|
||||||
const totalTypes = 7; // Default all types
|
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) => {
|
childProcess.stdout?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
// Collect output for rate limit detection (keep last 10KB)
|
// Collect output for rate limit detection (keep last 10KB)
|
||||||
allOutput = (allOutput + log).slice(-10000);
|
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) => {
|
childProcess.stderr?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
// Collect stderr for rate limit detection too
|
// Collect stderr for rate limit detection too
|
||||||
allOutput = (allOutput + log).slice(-10000);
|
allOutput = (allOutput + log).slice(-10000);
|
||||||
console.error('[Ideation STDERR]', log);
|
console.error('[Ideation STDERR]', log);
|
||||||
@@ -354,7 +356,9 @@ export class AgentQueueManager {
|
|||||||
...process.env,
|
...process.env,
|
||||||
...combinedEnv,
|
...combinedEnv,
|
||||||
...profileEnv,
|
...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) => {
|
childProcess.stdout?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
// Collect output for rate limit detection (keep last 10KB)
|
// Collect output for rate limit detection (keep last 10KB)
|
||||||
allRoadmapOutput = (allRoadmapOutput + log).slice(-10000);
|
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) => {
|
childProcess.stderr?.on('data', (data: Buffer) => {
|
||||||
const log = data.toString();
|
const log = data.toString('utf8');
|
||||||
// Collect stderr for rate limit detection too
|
// Collect stderr for rate limit detection too
|
||||||
allRoadmapOutput = (allRoadmapOutput + log).slice(-10000);
|
allRoadmapOutput = (allRoadmapOutput + log).slice(-10000);
|
||||||
console.error('[Roadmap STDERR]', log);
|
console.error('[Roadmap STDERR]', log);
|
||||||
|
|||||||
@@ -277,7 +277,9 @@ export class ChangelogGenerator extends EventEmitter {
|
|||||||
USER: process.env.USER || process.env.USERNAME || 'user',
|
USER: process.env.USER || process.env.USERNAME || 'user',
|
||||||
// Add common binary locations to PATH for claude CLI
|
// Add common binary locations to PATH for claude CLI
|
||||||
PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter),
|
PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter),
|
||||||
PYTHONUNBUFFERED: '1'
|
PYTHONUNBUFFERED: '1',
|
||||||
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
};
|
};
|
||||||
|
|
||||||
this.debug('Spawn environment', {
|
this.debug('Spawn environment', {
|
||||||
|
|||||||
@@ -237,7 +237,9 @@ except Exception as e:
|
|||||||
...(isWindows ? { USERPROFILE: homeDir } : { HOME: homeDir }),
|
...(isWindows ? { USERPROFILE: homeDir } : { HOME: homeDir }),
|
||||||
USER: process.env.USER || process.env.USERNAME || 'user',
|
USER: process.env.USER || process.env.USERNAME || 'user',
|
||||||
PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter),
|
PATH: [process.env.PATH || '', ...pathAdditions].filter(Boolean).join(path.delimiter),
|
||||||
PYTHONUNBUFFERED: '1'
|
PYTHONUNBUFFERED: '1',
|
||||||
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
};
|
};
|
||||||
|
|
||||||
return spawnEnv;
|
return spawnEnv;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function getIconPath(): string {
|
|||||||
// Use PNG in dev mode (works better), ICNS in production
|
// Use PNG in dev mode (works better), ICNS in production
|
||||||
iconName = is.dev ? 'icon-256.png' : 'icon.icns';
|
iconName = is.dev ? 'icon-256.png' : 'icon.icns';
|
||||||
} else if (process.platform === 'win32') {
|
} else if (process.platform === 'win32') {
|
||||||
iconName = 'icon-256.png';
|
iconName = 'icon.ico';
|
||||||
} else {
|
} else {
|
||||||
iconName = 'icon.png';
|
iconName = 'icon.png';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ export class InsightsConfig {
|
|||||||
...autoBuildEnv,
|
...autoBuildEnv,
|
||||||
...profileEnv,
|
...profileEnv,
|
||||||
PYTHONUNBUFFERED: '1',
|
PYTHONUNBUFFERED: '1',
|
||||||
PYTHONIOENCODING: 'utf-8'
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,16 @@ import type {
|
|||||||
ProjectSettings,
|
ProjectSettings,
|
||||||
IPCResult,
|
IPCResult,
|
||||||
InitializationResult,
|
InitializationResult,
|
||||||
AutoBuildVersionInfo
|
AutoBuildVersionInfo,
|
||||||
|
GitStatus
|
||||||
} from '../../shared/types';
|
} from '../../shared/types';
|
||||||
import { projectStore } from '../project-store';
|
import { projectStore } from '../project-store';
|
||||||
import {
|
import {
|
||||||
initializeProject,
|
initializeProject,
|
||||||
isInitialized,
|
isInitialized,
|
||||||
hasLocalSource
|
hasLocalSource,
|
||||||
|
checkGitStatus,
|
||||||
|
initializeGit
|
||||||
} from '../project-initializer';
|
} from '../project-initializer';
|
||||||
import { PythonEnvManager, type PythonEnvStatus } from '../python-env-manager';
|
import { PythonEnvManager, type PythonEnvStatus } from '../python-env-manager';
|
||||||
import { AgentManager } from '../agent';
|
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<IPCResult<GitStatus>> => {
|
||||||
|
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<IPCResult<InitializationResult>> => {
|
||||||
|
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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|||||||
import { AgentManager } from '../../agent';
|
import { AgentManager } from '../../agent';
|
||||||
import { fileWatcher } from '../../file-watcher';
|
import { fileWatcher } from '../../file-watcher';
|
||||||
import { findTaskAndProject } from './shared';
|
import { findTaskAndProject } from './shared';
|
||||||
|
import { checkGitStatus } from '../../project-initializer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register task execution handlers (start, stop, review, status management, recovery)
|
* Register task execution handlers (start, stop, review, status management, recovery)
|
||||||
@@ -40,6 +41,27 @@ export function registerTaskExecutionHandlers(
|
|||||||
return;
|
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);
|
console.log('[TASK_START] Found task:', task.specId, 'status:', task.status, 'subtasks:', task.subtasks.length);
|
||||||
|
|
||||||
// Start file watcher for this task
|
// 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
|
// Auto-start task when status changes to 'in_progress' and no process is running
|
||||||
if (status === 'in_progress' && !agentManager.isRunning(taskId)) {
|
if (status === 'in_progress' && !agentManager.isRunning(taskId)) {
|
||||||
const mainWindow = getMainWindow();
|
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);
|
console.log('[TASK_UPDATE_STATUS] Auto-starting task:', taskId);
|
||||||
|
|
||||||
// Start file watcher for this task
|
// Start file watcher for this task
|
||||||
@@ -498,6 +535,23 @@ export function registerTaskExecutionHandlers(
|
|||||||
// Auto-restart the task if requested
|
// Auto-restart the task if requested
|
||||||
let autoRestarted = false;
|
let autoRestarted = false;
|
||||||
if (autoRestart && project) {
|
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 {
|
try {
|
||||||
// Set status to in_progress for the restart
|
// Set status to in_progress for the restart
|
||||||
newStatus = 'in_progress';
|
newStatus = 'in_progress';
|
||||||
|
|||||||
@@ -315,7 +315,9 @@ export function registerWorktreeHandlers(
|
|||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
...profileEnv, // Include active Claude profile OAuth token
|
...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
|
stdio: ['ignore', 'pipe', 'pipe'] // Don't connect stdin to avoid blocking
|
||||||
});
|
});
|
||||||
@@ -568,7 +570,7 @@ export function registerWorktreeHandlers(
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const previewProcess = spawn(pythonPath, args, {
|
const previewProcess = spawn(pythonPath, args, {
|
||||||
cwd: sourcePath,
|
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 = '';
|
let stdout = '';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs';
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug logging - only logs when AUTO_CLAUDE_DEBUG env var is set
|
* Debug logging - only logs when AUTO_CLAUDE_DEBUG env var is set
|
||||||
@@ -16,6 +17,137 @@ function debug(message: string, data?: Record<string, unknown>): 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
|
* 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).
|
* Creates .auto-claude/ with data directories (specs, ideation, insights, roadmap).
|
||||||
* The framework code runs from the source repo - only data is stored here.
|
* 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 {
|
export function initializeProject(projectPath: string): InitializationResult {
|
||||||
debug('initializeProject called', { projectPath });
|
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
|
// Check if already initialized
|
||||||
const dotAutoBuildPath = path.join(projectPath, '.auto-claude');
|
const dotAutoBuildPath = path.join(projectPath, '.auto-claude');
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ export class TerminalNameGenerator extends EventEmitter {
|
|||||||
...autoBuildEnv,
|
...autoBuildEnv,
|
||||||
...profileEnv, // Include active Claude profile config
|
...profileEnv, // Include active Claude profile config
|
||||||
PYTHONUNBUFFERED: '1',
|
PYTHONUNBUFFERED: '1',
|
||||||
PYTHONIOENCODING: 'utf-8'
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ export class TitleGenerator extends EventEmitter {
|
|||||||
...autoBuildEnv,
|
...autoBuildEnv,
|
||||||
...profileEnv, // Include active Claude profile config
|
...profileEnv, // Include active Claude profile config
|
||||||
PYTHONUNBUFFERED: '1',
|
PYTHONUNBUFFERED: '1',
|
||||||
PYTHONIOENCODING: 'utf-8'
|
PYTHONIOENCODING: 'utf-8',
|
||||||
|
PYTHONUTF8: '1'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import type {
|
|||||||
ClaudeAuthResult,
|
ClaudeAuthResult,
|
||||||
InfrastructureStatus,
|
InfrastructureStatus,
|
||||||
GraphitiValidationResult,
|
GraphitiValidationResult,
|
||||||
GraphitiConnectionTestResult
|
GraphitiConnectionTestResult,
|
||||||
|
GitStatus
|
||||||
} from '../../shared/types';
|
} from '../../shared/types';
|
||||||
|
|
||||||
export interface ProjectAPI {
|
export interface ProjectAPI {
|
||||||
@@ -72,6 +73,8 @@ export interface ProjectAPI {
|
|||||||
getGitBranches: (projectPath: string) => Promise<IPCResult<string[]>>;
|
getGitBranches: (projectPath: string) => Promise<IPCResult<string[]>>;
|
||||||
getCurrentGitBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
getCurrentGitBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
||||||
detectMainBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
detectMainBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
||||||
|
checkGitStatus: (projectPath: string) => Promise<IPCResult<GitStatus>>;
|
||||||
|
initializeGit: (projectPath: string) => Promise<IPCResult<InitializationResult>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createProjectAPI = (): ProjectAPI => ({
|
export const createProjectAPI = (): ProjectAPI => ({
|
||||||
@@ -180,5 +183,11 @@ export const createProjectAPI = (): ProjectAPI => ({
|
|||||||
ipcRenderer.invoke(IPC_CHANNELS.GIT_GET_CURRENT_BRANCH, projectPath),
|
ipcRenderer.invoke(IPC_CHANNELS.GIT_GET_CURRENT_BRANCH, projectPath),
|
||||||
|
|
||||||
detectMainBranch: (projectPath: string): Promise<IPCResult<string | null>> =>
|
detectMainBranch: (projectPath: string): Promise<IPCResult<string | null>> =>
|
||||||
ipcRenderer.invoke(IPC_CHANNELS.GIT_DETECT_MAIN_BRANCH, projectPath)
|
ipcRenderer.invoke(IPC_CHANNELS.GIT_DETECT_MAIN_BRANCH, projectPath),
|
||||||
|
|
||||||
|
checkGitStatus: (projectPath: string): Promise<IPCResult<GitStatus>> =>
|
||||||
|
ipcRenderer.invoke(IPC_CHANNELS.GIT_CHECK_STATUS, projectPath),
|
||||||
|
|
||||||
|
initializeGit: (projectPath: string): Promise<IPCResult<InitializationResult>> =>
|
||||||
|
ipcRenderer.invoke(IPC_CHANNELS.GIT_INITIALIZE, projectPath)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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<string | null>(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 = () => (
|
||||||
|
<>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
<FolderGit2 className="h-5 w-5 text-primary" />
|
||||||
|
Git Repository Required
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Auto Claude uses git to safely build features in isolated workspaces
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="py-4 space-y-4">
|
||||||
|
{/* Status indicator */}
|
||||||
|
<div className="rounded-lg bg-muted p-4">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<AlertCircle className="h-5 w-5 text-warning mt-0.5 shrink-0" />
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="font-medium text-sm">
|
||||||
|
{needsGitInit
|
||||||
|
? 'This folder is not a git repository'
|
||||||
|
: 'Git repository has no commits'}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{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.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* What will happen */}
|
||||||
|
<div className="rounded-lg border border-border p-4">
|
||||||
|
<p className="font-medium text-sm mb-3">We'll set up git for you:</p>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{needsGitInit && (
|
||||||
|
<li className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<GitBranch className="h-4 w-4 text-primary" />
|
||||||
|
Initialize a new git repository
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<CheckCircle2 className="h-4 w-4 text-primary" />
|
||||||
|
Create an initial commit with your current files
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Manual instructions for advanced users */}
|
||||||
|
<details className="text-sm">
|
||||||
|
<summary className="cursor-pointer text-muted-foreground hover:text-foreground">
|
||||||
|
Prefer to do it manually?
|
||||||
|
</summary>
|
||||||
|
<div className="mt-3 rounded-lg bg-muted/50 p-3 font-mono text-xs space-y-1">
|
||||||
|
<p className="text-muted-foreground">Open a terminal in your project folder and run:</p>
|
||||||
|
{needsGitInit && <p>git init</p>}
|
||||||
|
<p>git add .</p>
|
||||||
|
<p>git commit -m "Initial commit"</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-lg bg-destructive/10 border border-destructive/20 p-3 text-sm text-destructive">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={handleSkip}>
|
||||||
|
Skip for now
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleInitializeGit} disabled={isInitializing}>
|
||||||
|
<GitBranch className="mr-2 h-4 w-4" />
|
||||||
|
Initialize Git
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderInitializingStep = () => (
|
||||||
|
<>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
<Loader2 className="h-5 w-5 animate-spin text-primary" />
|
||||||
|
Setting up Git
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="py-8 flex flex-col items-center justify-center">
|
||||||
|
<div className="space-y-3 text-center">
|
||||||
|
<Terminal className="h-12 w-12 text-muted-foreground mx-auto" />
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Initializing git repository and creating initial commit...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderSuccessStep = () => (
|
||||||
|
<>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
<CheckCircle2 className="h-5 w-5 text-success" />
|
||||||
|
Git Initialized
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="py-8 flex flex-col items-center justify-center">
|
||||||
|
<div className="space-y-3 text-center">
|
||||||
|
<div className="h-16 w-16 rounded-full bg-success/10 flex items-center justify-center mx-auto">
|
||||||
|
<CheckCircle2 className="h-8 w-8 text-success" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Your project is now ready to use with Auto Claude!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
{step === 'info' && renderInfoStep()}
|
||||||
|
{step === 'initializing' && renderInitializingStep()}
|
||||||
|
{step === 'success' && renderSuccessStep()}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -55,8 +55,9 @@ import {
|
|||||||
} from '../stores/project-store';
|
} from '../stores/project-store';
|
||||||
import { useSettingsStore, saveSettings } from '../stores/settings-store';
|
import { useSettingsStore, saveSettings } from '../stores/settings-store';
|
||||||
import { AddProjectModal } from './AddProjectModal';
|
import { AddProjectModal } from './AddProjectModal';
|
||||||
|
import { GitSetupModal } from './GitSetupModal';
|
||||||
import { RateLimitIndicator } from './RateLimitIndicator';
|
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';
|
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 [showAddProjectModal, setShowAddProjectModal] = useState(false);
|
||||||
const [showInitDialog, setShowInitDialog] = useState(false);
|
const [showInitDialog, setShowInitDialog] = useState(false);
|
||||||
const [showUpdateDialog, setShowUpdateDialog] = useState(false);
|
const [showUpdateDialog, setShowUpdateDialog] = useState(false);
|
||||||
|
const [showGitSetupModal, setShowGitSetupModal] = useState(false);
|
||||||
|
const [gitStatus, setGitStatus] = useState<GitStatus | null>(null);
|
||||||
const [pendingProject, setPendingProject] = useState<Project | null>(null);
|
const [pendingProject, setPendingProject] = useState<Project | null>(null);
|
||||||
const [versionInfo, setVersionInfo] = useState<AutoBuildVersionInfo | null>(null);
|
const [versionInfo, setVersionInfo] = useState<AutoBuildVersionInfo | null>(null);
|
||||||
const [isInitializing, setIsInitializing] = useState(false);
|
const [isInitializing, setIsInitializing] = useState(false);
|
||||||
@@ -159,6 +162,29 @@ export function Sidebar({
|
|||||||
checkUpdates();
|
checkUpdates();
|
||||||
}, [selectedProjectId, settings.autoUpdateAutoBuild]);
|
}, [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 = () => {
|
const handleAddProject = () => {
|
||||||
setShowAddProjectModal(true);
|
setShowAddProjectModal(true);
|
||||||
};
|
};
|
||||||
@@ -210,6 +236,20 @@ export function Sidebar({
|
|||||||
setVersionInfo(null);
|
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) => {
|
const handleRemoveProject = async (projectId: string, e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -518,6 +558,15 @@ export function Sidebar({
|
|||||||
onOpenChange={setShowAddProjectModal}
|
onOpenChange={setShowAddProjectModal}
|
||||||
onProjectAdded={handleProjectAdded}
|
onProjectAdded={handleProjectAdded}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Git Setup Modal */}
|
||||||
|
<GitSetupModal
|
||||||
|
open={showGitSetupModal}
|
||||||
|
onOpenChange={setShowGitSetupModal}
|
||||||
|
project={selectedProject || null}
|
||||||
|
gitStatus={gitStatus}
|
||||||
|
onGitInitialized={handleGitInitialized}
|
||||||
|
/>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,5 +84,19 @@ export const projectMock = {
|
|||||||
detectMainBranch: async () => ({
|
detectMainBranch: async () => ({
|
||||||
success: true,
|
success: true,
|
||||||
data: 'main'
|
data: 'main'
|
||||||
|
}),
|
||||||
|
|
||||||
|
checkGitStatus: async () => ({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
isGitRepo: true,
|
||||||
|
hasCommits: true,
|
||||||
|
currentBranch: 'main'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
initializeGit: async () => ({
|
||||||
|
success: true,
|
||||||
|
data: { success: true }
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -258,6 +258,8 @@ export const IPC_CHANNELS = {
|
|||||||
GIT_GET_BRANCHES: 'git:getBranches',
|
GIT_GET_BRANCHES: 'git:getBranches',
|
||||||
GIT_GET_CURRENT_BRANCH: 'git:getCurrentBranch',
|
GIT_GET_CURRENT_BRANCH: 'git:getCurrentBranch',
|
||||||
GIT_DETECT_MAIN_BRANCH: 'git:detectMainBranch',
|
GIT_DETECT_MAIN_BRANCH: 'git:detectMainBranch',
|
||||||
|
GIT_CHECK_STATUS: 'git:checkStatus',
|
||||||
|
GIT_INITIALIZE: 'git:initialize',
|
||||||
|
|
||||||
// App auto-update operations
|
// App auto-update operations
|
||||||
APP_UPDATE_CHECK: 'app-update:check',
|
APP_UPDATE_CHECK: 'app-update:check',
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ export * from './changelog';
|
|||||||
export * from './insights';
|
export * from './insights';
|
||||||
export * from './roadmap';
|
export * from './roadmap';
|
||||||
export * from './integrations';
|
export * from './integrations';
|
||||||
export * from './glossary';
|
|
||||||
export * from './status';
|
|
||||||
export * from './app-update';
|
export * from './app-update';
|
||||||
|
|
||||||
// IPC types (must be last to use types from other modules)
|
// IPC types (must be last to use types from other modules)
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import type {
|
|||||||
ProjectEnvConfig,
|
ProjectEnvConfig,
|
||||||
InfrastructureStatus,
|
InfrastructureStatus,
|
||||||
GraphitiValidationResult,
|
GraphitiValidationResult,
|
||||||
GraphitiConnectionTestResult
|
GraphitiConnectionTestResult,
|
||||||
|
GitStatus
|
||||||
} from './project';
|
} from './project';
|
||||||
import type {
|
import type {
|
||||||
Task,
|
Task,
|
||||||
@@ -500,6 +501,8 @@ export interface ElectronAPI {
|
|||||||
getGitBranches: (projectPath: string) => Promise<IPCResult<string[]>>;
|
getGitBranches: (projectPath: string) => Promise<IPCResult<string[]>>;
|
||||||
getCurrentGitBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
getCurrentGitBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
||||||
detectMainBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
detectMainBranch: (projectPath: string) => Promise<IPCResult<string | null>>;
|
||||||
|
checkGitStatus: (projectPath: string) => Promise<IPCResult<GitStatus>>;
|
||||||
|
initializeGit: (projectPath: string) => Promise<IPCResult<InitializationResult>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -305,6 +305,13 @@ export interface InitializationResult {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GitStatus {
|
||||||
|
isGitRepo: boolean;
|
||||||
|
hasCommits: boolean;
|
||||||
|
currentBranch: string | null;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateProjectFolderResult {
|
export interface CreateProjectFolderResult {
|
||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -79,11 +79,15 @@ class WorktreeManager:
|
|||||||
cwd=self.project_dir,
|
cwd=self.project_dir,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return env_branch
|
return env_branch
|
||||||
else:
|
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
|
# 2. Auto-detect main/master
|
||||||
for branch in ["main", "master"]:
|
for branch in ["main", "master"]:
|
||||||
@@ -92,15 +96,17 @@ class WorktreeManager:
|
|||||||
cwd=self.project_dir,
|
cwd=self.project_dir,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return branch
|
return branch
|
||||||
|
|
||||||
# 3. Fall back to current branch with warning
|
# 3. Fall back to current branch with warning
|
||||||
current = self._get_current_branch()
|
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"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
|
return current
|
||||||
|
|
||||||
def _get_current_branch(self) -> str:
|
def _get_current_branch(self) -> str:
|
||||||
@@ -110,6 +116,8 @@ class WorktreeManager:
|
|||||||
cwd=self.project_dir,
|
cwd=self.project_dir,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
raise WorktreeError(f"Failed to get current branch: {result.stderr}")
|
raise WorktreeError(f"Failed to get current branch: {result.stderr}")
|
||||||
@@ -124,6 +132,8 @@ class WorktreeManager:
|
|||||||
cwd=cwd or self.project_dir,
|
cwd=cwd or self.project_dir,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _unstage_gitignored_files(self) -> None:
|
def _unstage_gitignored_files(self) -> None:
|
||||||
@@ -153,6 +163,8 @@ class WorktreeManager:
|
|||||||
input="\n".join(staged_files),
|
input="\n".join(staged_files),
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.stdout.strip():
|
if result.stdout.strip():
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ class ImplementationPlan:
|
|||||||
self.update_status_from_subtasks()
|
self.update_status_from_subtasks()
|
||||||
|
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(path, "w") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
json.dump(self.to_dict(), f, indent=2)
|
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
def update_status_from_subtasks(self):
|
def update_status_from_subtasks(self):
|
||||||
"""Update overall status and planStatus based on subtask completion state.
|
"""Update overall status and planStatus based on subtask completion state.
|
||||||
@@ -169,7 +169,7 @@ class ImplementationPlan:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, path: Path) -> "ImplementationPlan":
|
def load(cls, path: Path) -> "ImplementationPlan":
|
||||||
"""Load plan from JSON file."""
|
"""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))
|
return cls.from_dict(json.load(f))
|
||||||
|
|
||||||
def get_available_phases(self) -> list[Phase]:
|
def get_available_phases(self) -> list[Phase]:
|
||||||
|
|||||||
@@ -28,6 +28,38 @@ Prerequisites:
|
|||||||
- Claude Code CLI installed
|
- 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
|
from cli import main
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -34,10 +34,40 @@ Usage:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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/)
|
# Add auto-claude to path (parent of runners/)
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
@@ -230,7 +260,9 @@ Examples:
|
|||||||
print(
|
print(
|
||||||
f" {muted('Or re-run spec_runner with --auto-approve to skip review:')}"
|
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)}")
|
print(f" {highlight(example_cmd)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ Handles the execution of AI agents for the spec creation pipeline.
|
|||||||
|
|
||||||
from pathlib import Path
|
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 client import create_client
|
||||||
from task_logger import (
|
from task_logger import (
|
||||||
LogEntryType,
|
LogEntryType,
|
||||||
|
|||||||
@@ -457,9 +457,11 @@ class SpecOrchestrator:
|
|||||||
print_key_value("Reasoning", assessment.reasoning)
|
print_key_value("Reasoning", assessment.reasoning)
|
||||||
|
|
||||||
if assessment.needs_research:
|
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:
|
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:
|
def _print_phases_to_run(self) -> None:
|
||||||
"""Print the list of phases that will be executed."""
|
"""Print the list of phases that will be executed."""
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ class ValidationResult:
|
|||||||
if self.errors:
|
if self.errors:
|
||||||
lines.append("\nErrors:")
|
lines.append("\nErrors:")
|
||||||
for err in self.errors:
|
for err in self.errors:
|
||||||
lines.append(f" ✗ {err}")
|
lines.append(f" [X] {err}")
|
||||||
|
|
||||||
if self.warnings:
|
if self.warnings:
|
||||||
lines.append("\nWarnings:")
|
lines.append("\nWarnings:")
|
||||||
for warn in self.warnings:
|
for warn in self.warnings:
|
||||||
lines.append(f" ⚠ {warn}")
|
lines.append(f" [!] {warn}")
|
||||||
|
|
||||||
if self.fixes and not self.valid:
|
if self.fixes and not self.valid:
|
||||||
lines.append("\nSuggested Fixes:")
|
lines.append("\nSuggested Fixes:")
|
||||||
for fix in self.fixes:
|
for fix in self.fixes:
|
||||||
lines.append(f" → {fix}")
|
lines.append(f" -> {fix}")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class LogStorage:
|
|||||||
"""Load existing logs or create new structure."""
|
"""Load existing logs or create new structure."""
|
||||||
if self.log_file.exists():
|
if self.log_file.exists():
|
||||||
try:
|
try:
|
||||||
with open(self.log_file) as f:
|
with open(self.log_file, encoding="utf-8") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except (OSError, json.JSONDecodeError):
|
except (OSError, json.JSONDecodeError):
|
||||||
pass
|
pass
|
||||||
@@ -69,8 +69,8 @@ class LogStorage:
|
|||||||
self._data["updated_at"] = self._timestamp()
|
self._data["updated_at"] = self._timestamp()
|
||||||
try:
|
try:
|
||||||
self.spec_dir.mkdir(parents=True, exist_ok=True)
|
self.spec_dir.mkdir(parents=True, exist_ok=True)
|
||||||
with open(self.log_file, "w") as f:
|
with open(self.log_file, "w", encoding="utf-8") as f:
|
||||||
json.dump(self._data, f, indent=2)
|
json.dump(self._data, f, indent=2, ensure_ascii=False)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Warning: Failed to save task logs: {e}", file=sys.stderr)
|
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
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(log_file) as f:
|
with open(log_file, encoding="utf-8") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except (OSError, json.JSONDecodeError):
|
except (OSError, json.JSONDecodeError):
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from .capabilities import (
|
|||||||
FANCY_UI,
|
FANCY_UI,
|
||||||
INTERACTIVE,
|
INTERACTIVE,
|
||||||
UNICODE,
|
UNICODE,
|
||||||
|
configure_safe_encoding,
|
||||||
supports_color,
|
supports_color,
|
||||||
supports_interactive,
|
supports_interactive,
|
||||||
supports_unicode,
|
supports_unicode,
|
||||||
@@ -57,6 +58,7 @@ _INTERACTIVE = INTERACTIVE
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Capabilities
|
# Capabilities
|
||||||
|
"configure_safe_encoding",
|
||||||
"supports_unicode",
|
"supports_unicode",
|
||||||
"supports_color",
|
"supports_color",
|
||||||
"supports_interactive",
|
"supports_interactive",
|
||||||
|
|||||||
@@ -8,10 +8,56 @@ Detects terminal capabilities for:
|
|||||||
- Interactive input support
|
- Interactive input support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
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:
|
def _is_fancy_ui_enabled() -> bool:
|
||||||
"""Check if fancy UI is enabled via environment variable."""
|
"""Check if fancy UI is enabled via environment variable."""
|
||||||
value = os.environ.get("ENABLE_FANCY_UI", "true").lower()
|
value = os.environ.get("ENABLE_FANCY_UI", "true").lower()
|
||||||
|
|||||||
Reference in New Issue
Block a user