@unerr-ai/unerr 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
package/dist/skills/resolver.js
DELETED
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 2-Tier Skill Resolution System
|
|
3
|
-
*
|
|
4
|
-
* Cascade order (additive, later tiers override same-named skills):
|
|
5
|
-
* Tier 1: Bundled skills (local-pack.ts — compiled into dist/)
|
|
6
|
-
* Tier 2: Local directory skills (.unerr/skills/*.md + ~/.unerr/skills/*.md)
|
|
7
|
-
*/
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
11
|
-
import { LOCAL_SKILLS } from "./local-pack.js";
|
|
12
|
-
// ── IDE Skill Directory ────────────────────────────────────────────
|
|
13
|
-
function getSkillDir(ide, cwd) {
|
|
14
|
-
switch (ide) {
|
|
15
|
-
case "claude-code":
|
|
16
|
-
// Claude Code uses directory-per-skill: .claude/skills/{name}/SKILL.md
|
|
17
|
-
return {
|
|
18
|
-
dir: join(cwd, ".claude", "skills"),
|
|
19
|
-
ext: ".md",
|
|
20
|
-
dirPerSkill: true,
|
|
21
|
-
};
|
|
22
|
-
case "cursor":
|
|
23
|
-
return {
|
|
24
|
-
dir: join(cwd, ".cursor", "rules"),
|
|
25
|
-
ext: ".mdc",
|
|
26
|
-
dirPerSkill: false,
|
|
27
|
-
};
|
|
28
|
-
case "vscode":
|
|
29
|
-
return {
|
|
30
|
-
dir: join(cwd, ".github", "copilot"),
|
|
31
|
-
ext: ".md",
|
|
32
|
-
dirPerSkill: false,
|
|
33
|
-
};
|
|
34
|
-
case "windsurf":
|
|
35
|
-
return {
|
|
36
|
-
dir: join(cwd, ".windsurf", "skills"),
|
|
37
|
-
ext: ".md",
|
|
38
|
-
dirPerSkill: true,
|
|
39
|
-
};
|
|
40
|
-
case "zed":
|
|
41
|
-
return {
|
|
42
|
-
dir: join(cwd, ".zed", "rules"),
|
|
43
|
-
ext: ".md",
|
|
44
|
-
dirPerSkill: false,
|
|
45
|
-
};
|
|
46
|
-
case "gemini-cli":
|
|
47
|
-
return {
|
|
48
|
-
dir: join(cwd, ".gemini", "skills"),
|
|
49
|
-
ext: ".md",
|
|
50
|
-
dirPerSkill: true,
|
|
51
|
-
};
|
|
52
|
-
case "github-copilot-cli":
|
|
53
|
-
return {
|
|
54
|
-
dir: join(cwd, ".github", "skills"),
|
|
55
|
-
ext: ".md",
|
|
56
|
-
dirPerSkill: true,
|
|
57
|
-
};
|
|
58
|
-
case "antigravity":
|
|
59
|
-
return {
|
|
60
|
-
dir: join(cwd, ".agents", "skills"),
|
|
61
|
-
ext: ".md",
|
|
62
|
-
dirPerSkill: true,
|
|
63
|
-
};
|
|
64
|
-
default:
|
|
65
|
-
return {
|
|
66
|
-
dir: join(cwd, ".unerr", "skills"),
|
|
67
|
-
ext: ".md",
|
|
68
|
-
dirPerSkill: false,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function writeSkillFile(skill, ide, skillDir, ext, dirPerSkill) {
|
|
73
|
-
mkdirSync(skillDir, { recursive: true });
|
|
74
|
-
// Strip existing "unerr-" prefix to avoid double-prefixing (e.g., from Tier 2 local skills)
|
|
75
|
-
const baseName = skill.name.replace(/^unerr-/, "");
|
|
76
|
-
const skillName = `unerr-${baseName}`;
|
|
77
|
-
let filePath;
|
|
78
|
-
let content;
|
|
79
|
-
if (dirPerSkill && ide === "windsurf") {
|
|
80
|
-
const skillSubDir = join(skillDir, skillName);
|
|
81
|
-
mkdirSync(skillSubDir, { recursive: true });
|
|
82
|
-
filePath = join(skillSubDir, "SKILL.md");
|
|
83
|
-
content = formatWindsurfSkillMd(skill);
|
|
84
|
-
}
|
|
85
|
-
else if (dirPerSkill && ide === "gemini-cli") {
|
|
86
|
-
const skillSubDir = join(skillDir, skillName);
|
|
87
|
-
mkdirSync(skillSubDir, { recursive: true });
|
|
88
|
-
filePath = join(skillSubDir, "SKILL.md");
|
|
89
|
-
content = formatGeminiSkill(skill);
|
|
90
|
-
}
|
|
91
|
-
else if (dirPerSkill && ide === "github-copilot-cli") {
|
|
92
|
-
const skillSubDir = join(skillDir, skillName);
|
|
93
|
-
mkdirSync(skillSubDir, { recursive: true });
|
|
94
|
-
filePath = join(skillSubDir, "SKILL.md");
|
|
95
|
-
content = formatCopilotSkill(skill);
|
|
96
|
-
}
|
|
97
|
-
else if (dirPerSkill && ide === "antigravity") {
|
|
98
|
-
const skillSubDir = join(skillDir, skillName);
|
|
99
|
-
mkdirSync(skillSubDir, { recursive: true });
|
|
100
|
-
filePath = join(skillSubDir, "SKILL.md");
|
|
101
|
-
content = formatAntigravitySkill(skill);
|
|
102
|
-
}
|
|
103
|
-
else if (dirPerSkill) {
|
|
104
|
-
// Claude Code: .claude/skills/{skill-name}/SKILL.md
|
|
105
|
-
const skillSubDir = join(skillDir, skillName);
|
|
106
|
-
mkdirSync(skillSubDir, { recursive: true });
|
|
107
|
-
filePath = join(skillSubDir, "SKILL.md");
|
|
108
|
-
content = formatClaudeCodeSkill(skill);
|
|
109
|
-
}
|
|
110
|
-
else if (ide === "cursor" && ext === ".mdc") {
|
|
111
|
-
filePath = join(skillDir, `${skillName}${ext}`);
|
|
112
|
-
content = formatCursorSkill(skill);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
filePath = join(skillDir, `${skillName}${ext}`);
|
|
116
|
-
content = formatMarkdownSkill(skill);
|
|
117
|
-
}
|
|
118
|
-
writeFileSync(filePath, content);
|
|
119
|
-
return filePath;
|
|
120
|
-
}
|
|
121
|
-
/** Claude Code SKILL.md format — directory-per-skill with proper frontmatter */
|
|
122
|
-
function formatClaudeCodeSkill(skill) {
|
|
123
|
-
const trigger = skill.trigger;
|
|
124
|
-
const triggerType = trigger?.type ?? "always";
|
|
125
|
-
// Map unerr trigger types to Claude Code frontmatter fields
|
|
126
|
-
let disableModelInvocation = false;
|
|
127
|
-
let userInvocable = true;
|
|
128
|
-
let paths;
|
|
129
|
-
switch (triggerType) {
|
|
130
|
-
case "always":
|
|
131
|
-
// Claude loads automatically, user can also invoke
|
|
132
|
-
break;
|
|
133
|
-
case "auto":
|
|
134
|
-
// Claude loads when working with matching files
|
|
135
|
-
paths = trigger?.globs;
|
|
136
|
-
break;
|
|
137
|
-
case "agent-requested":
|
|
138
|
-
// Only Claude invokes, not shown in / menu
|
|
139
|
-
userInvocable = false;
|
|
140
|
-
break;
|
|
141
|
-
case "manual":
|
|
142
|
-
// Only user can invoke via /skill-name
|
|
143
|
-
disableModelInvocation = true;
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
// Build frontmatter
|
|
147
|
-
let frontmatter = `description: "${skill.description}"`;
|
|
148
|
-
if (disableModelInvocation) {
|
|
149
|
-
frontmatter += "\ndisable-model-invocation: true";
|
|
150
|
-
}
|
|
151
|
-
if (!userInvocable) {
|
|
152
|
-
frontmatter += "\nuser-invocable: false";
|
|
153
|
-
}
|
|
154
|
-
if (paths && paths.length > 0) {
|
|
155
|
-
frontmatter += `\npaths: ${paths.join(", ")}`;
|
|
156
|
-
}
|
|
157
|
-
return `---
|
|
158
|
-
${frontmatter}
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
${skill.content}
|
|
162
|
-
`;
|
|
163
|
-
}
|
|
164
|
-
/** Cursor .mdc format with trigger-aware frontmatter */
|
|
165
|
-
function formatCursorSkill(skill) {
|
|
166
|
-
const trigger = skill.trigger;
|
|
167
|
-
const triggerType = trigger?.type ?? "always";
|
|
168
|
-
// Map unerr trigger types to Cursor .mdc fields
|
|
169
|
-
let alwaysApply;
|
|
170
|
-
let globs;
|
|
171
|
-
switch (triggerType) {
|
|
172
|
-
case "always":
|
|
173
|
-
alwaysApply = true;
|
|
174
|
-
globs = [];
|
|
175
|
-
break;
|
|
176
|
-
case "auto":
|
|
177
|
-
// Auto-triggered skills should always apply in Cursor — Cursor's glob matching
|
|
178
|
-
// is unreliable, and these skills contain critical behavioral instructions
|
|
179
|
-
alwaysApply = true;
|
|
180
|
-
globs = [];
|
|
181
|
-
break;
|
|
182
|
-
case "agent-requested":
|
|
183
|
-
// Cursor: not alwaysApply, no globs → description-based activation
|
|
184
|
-
alwaysApply = false;
|
|
185
|
-
globs = [];
|
|
186
|
-
break;
|
|
187
|
-
case "manual":
|
|
188
|
-
alwaysApply = false;
|
|
189
|
-
globs = [];
|
|
190
|
-
break;
|
|
191
|
-
default:
|
|
192
|
-
alwaysApply = true;
|
|
193
|
-
globs = [];
|
|
194
|
-
}
|
|
195
|
-
const globLine = globs.length > 0 ? `\nglobs: ${JSON.stringify(globs)}` : "";
|
|
196
|
-
return `---
|
|
197
|
-
unerr_skill_version: "${skill.version ?? "1.0.0"}"
|
|
198
|
-
description: "${skill.description}"${globLine}
|
|
199
|
-
alwaysApply: ${alwaysApply}
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
${skill.content}
|
|
203
|
-
`;
|
|
204
|
-
}
|
|
205
|
-
/** Standard markdown format for Claude Code, VS Code, Zed */
|
|
206
|
-
function formatMarkdownSkill(skill) {
|
|
207
|
-
const trigger = skill.trigger;
|
|
208
|
-
const triggerType = trigger?.type ?? "always";
|
|
209
|
-
const globLine = trigger?.globs && trigger.globs.length > 0
|
|
210
|
-
? `\nglobs: ${JSON.stringify(trigger.globs)}`
|
|
211
|
-
: "";
|
|
212
|
-
return `---
|
|
213
|
-
description: "${skill.description}"
|
|
214
|
-
trigger: ${triggerType}${globLine}
|
|
215
|
-
version: "${skill.version ?? "1.0.0"}"
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
# ${skill.name}
|
|
219
|
-
|
|
220
|
-
${skill.content}
|
|
221
|
-
`;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Format a skill for Windsurf.
|
|
225
|
-
* Windsurf skills use folder-per-skill: .windsurf/skills/{name}/SKILL.md
|
|
226
|
-
* with YAML frontmatter (name, description).
|
|
227
|
-
*/
|
|
228
|
-
function formatWindsurfSkillMd(skill) {
|
|
229
|
-
return `---
|
|
230
|
-
name: ${skill.name}
|
|
231
|
-
description: ${skill.description}
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
${skill.content}
|
|
235
|
-
`;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Format a skill for Gemini CLI.
|
|
239
|
-
* Gemini CLI skills use folder-per-skill: .gemini/skills/{name}/SKILL.md
|
|
240
|
-
* with YAML frontmatter (name, description).
|
|
241
|
-
*/
|
|
242
|
-
function formatGeminiSkill(skill) {
|
|
243
|
-
return `---
|
|
244
|
-
name: ${skill.name}
|
|
245
|
-
description: ${skill.description}
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
${skill.content}
|
|
249
|
-
`;
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Format a skill for GitHub Copilot CLI.
|
|
253
|
-
* Copilot skills use folder-per-skill: .github/skills/{name}/SKILL.md
|
|
254
|
-
* with YAML frontmatter (name, description, allowed-tools).
|
|
255
|
-
*/
|
|
256
|
-
function formatCopilotSkill(skill) {
|
|
257
|
-
return `---
|
|
258
|
-
name: ${skill.name}
|
|
259
|
-
description: ${skill.description}
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
${skill.content}
|
|
263
|
-
`;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Format a skill for Google Antigravity.
|
|
267
|
-
* Antigravity skills use folder-per-skill: .agents/skills/{name}/SKILL.md
|
|
268
|
-
* with YAML frontmatter (name, description).
|
|
269
|
-
*/
|
|
270
|
-
function formatAntigravitySkill(skill) {
|
|
271
|
-
const trigger = skill.trigger;
|
|
272
|
-
const triggerType = trigger?.type ?? "always";
|
|
273
|
-
let globs = [];
|
|
274
|
-
if (trigger?.globs) {
|
|
275
|
-
globs = trigger.globs;
|
|
276
|
-
}
|
|
277
|
-
let frontmatter = `---\nname: ${skill.name}\ndescription: ${skill.description}`;
|
|
278
|
-
if (globs.length > 0) {
|
|
279
|
-
frontmatter += `\nglobs:\n${globs.map((g) => ` - ${g}`).join("\n")}`;
|
|
280
|
-
}
|
|
281
|
-
if (triggerType === "manual") {
|
|
282
|
-
frontmatter += "\nuser_invocable: true";
|
|
283
|
-
}
|
|
284
|
-
frontmatter += "\n---";
|
|
285
|
-
return `${frontmatter}\n\n${skill.content}\n`;
|
|
286
|
-
}
|
|
287
|
-
// ── Tier 1: Bundled Skills ─────────────────────────────────────────
|
|
288
|
-
function loadBundledSkills() {
|
|
289
|
-
return LOCAL_SKILLS.map((s) => ({
|
|
290
|
-
name: s.id,
|
|
291
|
-
description: s.description,
|
|
292
|
-
content: s.instructions,
|
|
293
|
-
version: s.version,
|
|
294
|
-
source: "bundled",
|
|
295
|
-
trigger: s.trigger,
|
|
296
|
-
}));
|
|
297
|
-
}
|
|
298
|
-
// ── Tier 2: Local Directory Skills ─────────────────────────────────
|
|
299
|
-
/**
|
|
300
|
-
* Parse optional YAML-like frontmatter from a .md skill file.
|
|
301
|
-
* Supports: name, description, version in `---` delimited block.
|
|
302
|
-
*/
|
|
303
|
-
function parseFrontmatter(raw) {
|
|
304
|
-
const meta = {};
|
|
305
|
-
if (!raw.startsWith("---")) {
|
|
306
|
-
return { meta, content: raw };
|
|
307
|
-
}
|
|
308
|
-
const endIdx = raw.indexOf("---", 3);
|
|
309
|
-
if (endIdx === -1) {
|
|
310
|
-
return { meta, content: raw };
|
|
311
|
-
}
|
|
312
|
-
const frontBlock = raw.slice(3, endIdx).trim();
|
|
313
|
-
for (const line of frontBlock.split("\n")) {
|
|
314
|
-
const colonIdx = line.indexOf(":");
|
|
315
|
-
if (colonIdx === -1)
|
|
316
|
-
continue;
|
|
317
|
-
const key = line.slice(0, colonIdx).trim();
|
|
318
|
-
const value = line
|
|
319
|
-
.slice(colonIdx + 1)
|
|
320
|
-
.trim()
|
|
321
|
-
.replace(/^["']|["']$/g, "");
|
|
322
|
-
if (key && value)
|
|
323
|
-
meta[key] = value;
|
|
324
|
-
}
|
|
325
|
-
const content = raw.slice(endIdx + 3).trim();
|
|
326
|
-
return { meta, content };
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Scan a directory for .md skill files.
|
|
330
|
-
* Returns skills with optional YAML frontmatter parsed.
|
|
331
|
-
*/
|
|
332
|
-
export function scanSkillDirectory(dir) {
|
|
333
|
-
if (!existsSync(dir))
|
|
334
|
-
return [];
|
|
335
|
-
const skills = [];
|
|
336
|
-
try {
|
|
337
|
-
const files = readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
338
|
-
for (const file of files) {
|
|
339
|
-
try {
|
|
340
|
-
const raw = readFileSync(join(dir, file), "utf-8");
|
|
341
|
-
const { meta, content } = parseFrontmatter(raw);
|
|
342
|
-
const name = meta.name ?? file.replace(/\.md$/, "");
|
|
343
|
-
skills.push({
|
|
344
|
-
name,
|
|
345
|
-
description: meta.description ?? `Local skill: ${name}`,
|
|
346
|
-
content,
|
|
347
|
-
version: meta.version,
|
|
348
|
-
source: "local",
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
catch {
|
|
352
|
-
// Skip unreadable files
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
// Directory unreadable
|
|
358
|
-
}
|
|
359
|
-
return skills;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Load Tier 2 skills from project (.unerr/skills/) and user (~/.unerr/skills/).
|
|
363
|
-
* Project-level overrides user-level on same name.
|
|
364
|
-
*/
|
|
365
|
-
function loadLocalDirectorySkills(cwd) {
|
|
366
|
-
const userDir = join(homedir(), ".unerr", "skills");
|
|
367
|
-
const projectDir = join(cwd, ".unerr", "skills");
|
|
368
|
-
const userSkills = scanSkillDirectory(userDir);
|
|
369
|
-
const projectSkills = scanSkillDirectory(projectDir);
|
|
370
|
-
// Project overrides user on same name
|
|
371
|
-
const byName = new Map();
|
|
372
|
-
for (const s of userSkills)
|
|
373
|
-
byName.set(s.name, s);
|
|
374
|
-
for (const s of projectSkills)
|
|
375
|
-
byName.set(s.name, s);
|
|
376
|
-
return Array.from(byName.values());
|
|
377
|
-
}
|
|
378
|
-
// ── Cascade Resolution ─────────────────────────────────────────────
|
|
379
|
-
/**
|
|
380
|
-
* Resolve skills from Tier 1 (bundled) and Tier 2 (local directories),
|
|
381
|
-
* then install them into the IDE skill directory.
|
|
382
|
-
*
|
|
383
|
-
* Tiers are ADDITIVE: Tier 1 → Tier 2 supplements/overrides.
|
|
384
|
-
*/
|
|
385
|
-
export async function resolveAndInstallSkills(opts) {
|
|
386
|
-
const byName = new Map();
|
|
387
|
-
// Tier 1: Bundled
|
|
388
|
-
for (const s of loadBundledSkills())
|
|
389
|
-
byName.set(s.name, s);
|
|
390
|
-
// Tier 2: Local directories
|
|
391
|
-
for (const s of loadLocalDirectorySkills(opts.cwd))
|
|
392
|
-
byName.set(s.name, s);
|
|
393
|
-
// Install resolved skills into IDE directory
|
|
394
|
-
const skills = Array.from(byName.values());
|
|
395
|
-
const { dir, ext, dirPerSkill } = getSkillDir(opts.ide, opts.cwd);
|
|
396
|
-
const installed = [];
|
|
397
|
-
for (const skill of skills) {
|
|
398
|
-
try {
|
|
399
|
-
writeSkillFile(skill, opts.ide, dir, ext, dirPerSkill);
|
|
400
|
-
installed.push(skill.name);
|
|
401
|
-
}
|
|
402
|
-
catch {
|
|
403
|
-
// Non-blocking: skip failed installs
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
// Build source attribution string
|
|
407
|
-
const sources = new Set(skills.map((s) => s.source));
|
|
408
|
-
const source = Array.from(sources).join("+");
|
|
409
|
-
return { installed, source, skills };
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Check if skills are present in the IDE directory.
|
|
413
|
-
* If missing, run the full cascade silently.
|
|
414
|
-
*
|
|
415
|
-
* Called on every proxy boot for self-healing.
|
|
416
|
-
* Returns the number of skills installed (0 = already present).
|
|
417
|
-
*/
|
|
418
|
-
export async function ensureSkillsPresent(opts) {
|
|
419
|
-
const { dir, ext, dirPerSkill } = getSkillDir(opts.ide, opts.cwd);
|
|
420
|
-
// Check if any unerr skills exist
|
|
421
|
-
if (existsSync(dir)) {
|
|
422
|
-
try {
|
|
423
|
-
const entries = readdirSync(dir);
|
|
424
|
-
let hasSkills;
|
|
425
|
-
if (dirPerSkill) {
|
|
426
|
-
// Claude Code: check for unerr-*/SKILL.md directories
|
|
427
|
-
hasSkills = entries.some((f) => f.startsWith("unerr-") && existsSync(join(dir, f, "SKILL.md")));
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
hasSkills = entries.some((f) => f.startsWith("unerr-") && f.endsWith(ext));
|
|
431
|
-
}
|
|
432
|
-
if (hasSkills)
|
|
433
|
-
return 0; // Skills present, nothing to do
|
|
434
|
-
}
|
|
435
|
-
catch {
|
|
436
|
-
// Directory unreadable, reinstall
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
// Skills missing — run cascade
|
|
440
|
-
const result = await resolveAndInstallSkills(opts);
|
|
441
|
-
return result.installed.length;
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* List installed skills from the IDE directory with source detection.
|
|
445
|
-
*/
|
|
446
|
-
export function listInstalledSkills(ide, cwd) {
|
|
447
|
-
const { dir, ext, dirPerSkill } = getSkillDir(ide, cwd);
|
|
448
|
-
if (!existsSync(dir))
|
|
449
|
-
return [];
|
|
450
|
-
try {
|
|
451
|
-
if (dirPerSkill) {
|
|
452
|
-
// Claude Code: .claude/skills/unerr-{name}/SKILL.md
|
|
453
|
-
return readdirSync(dir)
|
|
454
|
-
.filter((f) => f.startsWith("unerr-") && existsSync(join(dir, f, "SKILL.md")))
|
|
455
|
-
.map((f) => ({
|
|
456
|
-
name: f.replace("unerr-", ""),
|
|
457
|
-
path: join(dir, f, "SKILL.md"),
|
|
458
|
-
}));
|
|
459
|
-
}
|
|
460
|
-
return readdirSync(dir)
|
|
461
|
-
.filter((f) => f.startsWith("unerr-") && f.endsWith(ext))
|
|
462
|
-
.map((f) => ({
|
|
463
|
-
name: f.replace("unerr-", "").replace(ext, ""),
|
|
464
|
-
path: join(dir, f),
|
|
465
|
-
}));
|
|
466
|
-
}
|
|
467
|
-
catch {
|
|
468
|
-
return [];
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Remove all unerr-installed skills for an IDE.
|
|
473
|
-
* Returns the number of skills removed.
|
|
474
|
-
*/
|
|
475
|
-
export function removeInstalledSkills(ide, cwd) {
|
|
476
|
-
const skills = listInstalledSkills(ide, cwd);
|
|
477
|
-
const { dirPerSkill } = getSkillDir(ide, cwd);
|
|
478
|
-
let removed = 0;
|
|
479
|
-
for (const skill of skills) {
|
|
480
|
-
try {
|
|
481
|
-
if (dirPerSkill) {
|
|
482
|
-
// Claude Code: remove the entire unerr-{name}/ directory
|
|
483
|
-
rmSync(dirname(skill.path), { recursive: true, force: true });
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
unlinkSync(skill.path);
|
|
487
|
-
}
|
|
488
|
-
removed++;
|
|
489
|
-
}
|
|
490
|
-
catch {
|
|
491
|
-
// Non-blocking
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return removed;
|
|
495
|
-
}
|
package/dist/state-detector.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Smart Default State Detector — pure function that inspects the local
|
|
3
|
-
* environment and determines what action the CLI should take.
|
|
4
|
-
*
|
|
5
|
-
* State priority (checked top-to-bottom):
|
|
6
|
-
* 1. Not inside a git repo → not_git_repo
|
|
7
|
-
* 2. No .unerr/config.json → needs_setup
|
|
8
|
-
* 3. PID file present + alive → already_running
|
|
9
|
-
* 4. PID file present + dead → stale_pid (cleaned, becomes ready)
|
|
10
|
-
* 5. Everything good → ready
|
|
11
|
-
*/
|
|
12
|
-
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
import { isGitRepo as isGitRepoAsync } from "./utils/git.js";
|
|
15
|
-
/**
|
|
16
|
-
* Inspect environment → return state.
|
|
17
|
-
* All I/O is read-only (except stale PID cleanup).
|
|
18
|
-
*/
|
|
19
|
-
export async function detectState(deps = {}) {
|
|
20
|
-
const cwd = deps.cwd ?? process.cwd();
|
|
21
|
-
// 1. Git repo
|
|
22
|
-
const checkGitRepo = deps.isGitRepo ?? (() => isGitRepoAsync(cwd));
|
|
23
|
-
if (!(await checkGitRepo())) {
|
|
24
|
-
return { state: "not_git_repo" };
|
|
25
|
-
}
|
|
26
|
-
// 2. Config
|
|
27
|
-
const configPath = join(cwd, ".unerr", "config.json");
|
|
28
|
-
if (!existsSync(configPath)) {
|
|
29
|
-
return { state: "needs_setup" };
|
|
30
|
-
}
|
|
31
|
-
let repoId;
|
|
32
|
-
try {
|
|
33
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
34
|
-
repoId = config.repoId;
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return { state: "needs_setup" };
|
|
38
|
-
}
|
|
39
|
-
// 3/4. PID check — supports both JSON { pid, startedAt, healthPort } and legacy plain number
|
|
40
|
-
const pidPath = join(cwd, ".unerr", "state", "proxy.pid");
|
|
41
|
-
if (existsSync(pidPath)) {
|
|
42
|
-
try {
|
|
43
|
-
const raw = readFileSync(pidPath, "utf-8").trim();
|
|
44
|
-
let pid;
|
|
45
|
-
if (raw.startsWith("{")) {
|
|
46
|
-
const data = JSON.parse(raw);
|
|
47
|
-
pid = data.pid;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
pid = Number.parseInt(raw, 10);
|
|
51
|
-
}
|
|
52
|
-
if (pid && !Number.isNaN(pid) && isProcessAlive(pid)) {
|
|
53
|
-
return { state: "already_running", pid, repoId };
|
|
54
|
-
}
|
|
55
|
-
// Stale PID — clean up
|
|
56
|
-
unlinkSync(pidPath);
|
|
57
|
-
return { state: "stale_pid", pid, repoId };
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
// PID file unreadable — treat as stale
|
|
61
|
-
try {
|
|
62
|
-
unlinkSync(pidPath);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
/* ignore */
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// 5. All good
|
|
70
|
-
return { state: "ready", repoId };
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Check if a process is alive via kill(pid, 0).
|
|
74
|
-
*/
|
|
75
|
-
function isProcessAlive(pid) {
|
|
76
|
-
try {
|
|
77
|
-
process.kill(pid, 0);
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|