@mrpatronz/nexusflow 0.2.19 → 1.3.1
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 +37 -7
- package/dist/analyzers/index.d.ts +23 -0
- package/dist/analyzers/index.d.ts.map +1 -1
- package/dist/analyzers/index.js +62 -0
- package/dist/analyzers/index.js.map +1 -1
- package/dist/commands/add-repo.d.ts +9 -0
- package/dist/commands/add-repo.d.ts.map +1 -1
- package/dist/commands/add-repo.js +12 -1
- package/dist/commands/add-repo.js.map +1 -1
- package/dist/commands/add-repo.test.d.ts +2 -0
- package/dist/commands/add-repo.test.d.ts.map +1 -0
- package/dist/commands/add-repo.test.js +30 -0
- package/dist/commands/add-repo.test.js.map +1 -0
- package/dist/commands/commands.test.js +26 -6
- package/dist/commands/commands.test.js.map +1 -1
- package/dist/commands/commit.d.ts +7 -1
- package/dist/commands/commit.d.ts.map +1 -1
- package/dist/commands/commit.js +31 -8
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/commit.test.d.ts +2 -0
- package/dist/commands/commit.test.d.ts.map +1 -0
- package/dist/commands/commit.test.js +54 -0
- package/dist/commands/commit.test.js.map +1 -0
- package/dist/commands/diff.d.ts +9 -2
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +36 -12
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +16 -20
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +11 -15
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/refresh.d.ts +4 -2
- package/dist/commands/refresh.d.ts.map +1 -1
- package/dist/commands/refresh.js +20 -36
- package/dist/commands/refresh.js.map +1 -1
- package/dist/commands/schedule.d.ts +35 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +151 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +28 -7
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/tui.js +1 -1
- package/dist/commands/tui.js.map +1 -1
- package/dist/commands/ui.d.ts +1 -0
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +5 -2
- package/dist/commands/ui.js.map +1 -1
- package/dist/core/adapters/local-storage.d.ts +7 -0
- package/dist/core/adapters/local-storage.d.ts.map +1 -1
- package/dist/core/adapters/local-storage.js +13 -4
- package/dist/core/adapters/local-storage.js.map +1 -1
- package/dist/core/adapters/storage.test.js +18 -0
- package/dist/core/adapters/storage.test.js.map +1 -1
- package/dist/core/analysis-cache.d.ts +58 -0
- package/dist/core/analysis-cache.d.ts.map +1 -0
- package/dist/core/analysis-cache.js +116 -0
- package/dist/core/analysis-cache.js.map +1 -0
- package/dist/core/analysis-cache.test.d.ts +2 -0
- package/dist/core/analysis-cache.test.d.ts.map +1 -0
- package/dist/core/analysis-cache.test.js +108 -0
- package/dist/core/analysis-cache.test.js.map +1 -0
- package/dist/core/refresh.d.ts +41 -0
- package/dist/core/refresh.d.ts.map +1 -0
- package/dist/core/refresh.js +56 -0
- package/dist/core/refresh.js.map +1 -0
- package/dist/core/scheduler.d.ts +117 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +361 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/scheduler.test.d.ts +2 -0
- package/dist/core/scheduler.test.d.ts.map +1 -0
- package/dist/core/scheduler.test.js +163 -0
- package/dist/core/scheduler.test.js.map +1 -0
- package/dist/core/sync.d.ts +6 -0
- package/dist/core/sync.d.ts.map +1 -1
- package/dist/core/sync.js +17 -12
- package/dist/core/sync.js.map +1 -1
- package/dist/core/workspace.d.ts +6 -0
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +32 -15
- package/dist/core/workspace.js.map +1 -1
- package/dist/core/workspace.test.d.ts +2 -0
- package/dist/core/workspace.test.d.ts.map +1 -0
- package/dist/core/workspace.test.js +42 -0
- package/dist/core/workspace.test.js.map +1 -0
- package/dist/generators/index.d.ts +7 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +14 -1
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/map-generator.d.ts.map +1 -1
- package/dist/generators/map-generator.js +4 -3
- package/dist/generators/map-generator.js.map +1 -1
- package/dist/generators/map-generator.test.js +4 -2
- package/dist/generators/map-generator.test.js.map +1 -1
- package/dist/gui/assets/index-ElroPpSP.js +26 -0
- package/dist/gui/index.html +1 -1
- package/dist/index.js +108 -4
- package/dist/index.js.map +1 -1
- package/dist/orchestration/index.d.ts +1 -1
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/runner.d.ts +16 -1
- package/dist/orchestration/runner.d.ts.map +1 -1
- package/dist/orchestration/runner.js +41 -5
- package/dist/orchestration/runner.js.map +1 -1
- package/dist/orchestration/runner.test.d.ts +2 -0
- package/dist/orchestration/runner.test.d.ts.map +1 -0
- package/dist/orchestration/runner.test.js +71 -0
- package/dist/orchestration/runner.test.js.map +1 -0
- package/dist/server.d.ts +17 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +275 -114
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +158 -1
- package/dist/server.test.js.map +1 -1
- package/dist/utils/multi-git.d.ts +21 -0
- package/dist/utils/multi-git.d.ts.map +1 -1
- package/dist/utils/multi-git.js +40 -4
- package/dist/utils/multi-git.js.map +1 -1
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +8 -2
- package/dist/utils/prompts.js.map +1 -1
- package/package.json +13 -1
- package/.agents/skills/ccc/SKILL.md +0 -71
- package/.agents/skills/ccc/references/management.md +0 -110
- package/.agents/skills/ccc/references/settings.md +0 -126
- package/.github/dependabot.yml +0 -52
- package/.github/workflows/ci.yml +0 -44
- package/.github/workflows/dependabot-merge.yml +0 -53
- package/.github/workflows/release-desktop.yml +0 -67
- package/.github/workflows/release-npm.yml +0 -65
- package/.github/workflows/release-vscode.yml +0 -61
- package/.vscode/launch.json +0 -17
- package/.vscode/tasks.json +0 -17
- package/GETTING_STARTED.md +0 -114
- package/desktop/build-installer.js +0 -228
- package/desktop/build.js +0 -118
- package/desktop/installer/nexusflow.iss +0 -35
- package/desktop/neutralino.config.json +0 -39
- package/desktop/package-lock.json +0 -1060
- package/desktop/package.json +0 -14
- package/dist/gui/assets/index-D8iJPvnk.js +0 -26
- package/extension/media/icon.svg +0 -1
- package/extension/package-lock.json +0 -6044
- package/extension/package.json +0 -117
- package/extension/src/extension.ts +0 -653
- package/extension/tsconfig.json +0 -21
- package/gui/README.md +0 -73
- package/gui/e2e/dashboard.spec.ts +0 -61
- package/gui/e2e/wizard.spec.ts +0 -346
- package/gui/eslint.config.js +0 -26
- package/gui/index.html +0 -17
- package/gui/package-lock.json +0 -3137
- package/gui/package.json +0 -37
- package/gui/playwright.config.ts +0 -41
- package/gui/public/app_logo.png +0 -0
- package/gui/public/favicon.svg +0 -1
- package/gui/public/icons.svg +0 -24
- package/gui/src/App.css +0 -1
- package/gui/src/App.tsx +0 -3573
- package/gui/src/app/AppSidebar.tsx +0 -69
- package/gui/src/assets/hero.png +0 -0
- package/gui/src/assets/react.svg +0 -1
- package/gui/src/assets/vite.svg +0 -1
- package/gui/src/components/AddRepoPicker.tsx +0 -81
- package/gui/src/components/ui/Button.tsx +0 -40
- package/gui/src/components/ui/Card.tsx +0 -6
- package/gui/src/components/ui/EmptyState.tsx +0 -22
- package/gui/src/components/ui/Input.tsx +0 -21
- package/gui/src/components/ui/Menu.tsx +0 -71
- package/gui/src/components/ui/Modal.tsx +0 -39
- package/gui/src/components/ui/PageHeader.tsx +0 -21
- package/gui/src/components/ui/RepoStatusStrip.tsx +0 -33
- package/gui/src/components/ui/Skeleton.tsx +0 -5
- package/gui/src/components/ui/StatusPill.tsx +0 -36
- package/gui/src/components/ui/Tabs.tsx +0 -40
- package/gui/src/components/ui/cn.ts +0 -4
- package/gui/src/components/ui/index.ts +0 -16
- package/gui/src/features/changes/ChangesViewer.tsx +0 -388
- package/gui/src/features/knowledge/KnowledgeBase.tsx +0 -84
- package/gui/src/features/onboarding/OnboardingWizard.tsx +0 -230
- package/gui/src/features/plan/ImplementationPlan.tsx +0 -32
- package/gui/src/features/services/ServiceConsole.tsx +0 -215
- package/gui/src/features/sessions/SessionHistory.tsx +0 -195
- package/gui/src/index.css +0 -167
- package/gui/src/lib/status.ts +0 -24
- package/gui/src/main.tsx +0 -94
- package/gui/src/pages/DashboardPage.tsx +0 -129
- package/gui/src/pages/WorkspacesPage.tsx +0 -352
- package/gui/src/types.ts +0 -81
- package/gui/tsconfig.app.json +0 -25
- package/gui/tsconfig.json +0 -7
- package/gui/tsconfig.node.json +0 -24
- package/gui/vite.config.ts +0 -12
- package/resources/workflows/plan-implement-review.md +0 -8
- package/resources/workflows/research-verify.md +0 -6
- package/resources/workflows/solo-developer.md +0 -3
- package/scripts/simulate-workspaces.ts +0 -55
- package/skills-lock.json +0 -11
- package/src/analyzers/detect-apis.ts +0 -290
- package/src/analyzers/detect-deps.ts +0 -315
- package/src/analyzers/detect-existing.ts +0 -74
- package/src/analyzers/detect-ports.ts +0 -110
- package/src/analyzers/index.ts +0 -97
- package/src/analyzers/messaging-analyzer.ts +0 -254
- package/src/analyzers/readme-summarizer.ts +0 -102
- package/src/analyzers/run-analyzer.ts +0 -269
- package/src/analyzers/tech-stack.ts +0 -283
- package/src/commands/adapter.ts +0 -348
- package/src/commands/add-repo.ts +0 -156
- package/src/commands/commands.test.ts +0 -156
- package/src/commands/commit.ts +0 -131
- package/src/commands/config.ts +0 -52
- package/src/commands/create.ts +0 -204
- package/src/commands/desktop.ts +0 -128
- package/src/commands/diff.ts +0 -125
- package/src/commands/doctor.ts +0 -370
- package/src/commands/handoff.ts +0 -266
- package/src/commands/init.ts +0 -134
- package/src/commands/list.ts +0 -46
- package/src/commands/logs.ts +0 -63
- package/src/commands/mcp.ts +0 -94
- package/src/commands/open.ts +0 -129
- package/src/commands/refresh.ts +0 -137
- package/src/commands/remove.ts +0 -98
- package/src/commands/start.ts +0 -117
- package/src/commands/status.ts +0 -54
- package/src/commands/stop.ts +0 -55
- package/src/commands/sync.ts +0 -125
- package/src/commands/tui.ts +0 -484
- package/src/commands/ui.ts +0 -111
- package/src/core/adapters/local-storage.ts +0 -66
- package/src/core/adapters/obsidian-storage.ts +0 -151
- package/src/core/adapters/registry.ts +0 -44
- package/src/core/adapters/storage.test.ts +0 -174
- package/src/core/adapters/vault-storage.ts +0 -86
- package/src/core/config.test.ts +0 -96
- package/src/core/config.ts +0 -140
- package/src/core/graph.ts +0 -344
- package/src/core/plugins/index.ts +0 -17
- package/src/core/plugins/loader.ts +0 -36
- package/src/core/ports/storage.ts +0 -71
- package/src/core/scanner.test.ts +0 -61
- package/src/core/scanner.ts +0 -91
- package/src/core/storage.ts +0 -37
- package/src/core/sync.ts +0 -121
- package/src/core/workspace-state.test.ts +0 -108
- package/src/core/workspace-state.ts +0 -130
- package/src/core/workspace.ts +0 -487
- package/src/core/worktree.ts +0 -120
- package/src/generators/antigravity.ts +0 -32
- package/src/generators/base.ts +0 -243
- package/src/generators/claude.ts +0 -34
- package/src/generators/codex.ts +0 -48
- package/src/generators/copilot.ts +0 -56
- package/src/generators/cursor.ts +0 -44
- package/src/generators/diff-context.ts +0 -75
- package/src/generators/index.ts +0 -322
- package/src/generators/map-generator.test.ts +0 -159
- package/src/generators/map-generator.ts +0 -539
- package/src/generators/plan-generator.ts +0 -483
- package/src/generators/skills-generator.ts +0 -259
- package/src/index.ts +0 -539
- package/src/mcp/server.ts +0 -338
- package/src/orchestration/detect.ts +0 -313
- package/src/orchestration/index.ts +0 -7
- package/src/orchestration/runner.ts +0 -283
- package/src/server.test.ts +0 -618
- package/src/server.ts +0 -1402
- package/src/types.ts +0 -451
- package/src/utils/detect-ai.test.ts +0 -44
- package/src/utils/detect-ai.ts +0 -84
- package/src/utils/detect-editors.test.ts +0 -48
- package/src/utils/detect-editors.ts +0 -57
- package/src/utils/git.test.ts +0 -74
- package/src/utils/git.ts +0 -117
- package/src/utils/local-ai.test.ts +0 -130
- package/src/utils/local-ai.ts +0 -116
- package/src/utils/multi-git.test.ts +0 -158
- package/src/utils/multi-git.ts +0 -405
- package/src/utils/prompts.ts +0 -209
- package/src/utils/session-finder.ts +0 -483
- package/src/utils/system-scanner.test.ts +0 -89
- package/src/utils/system-scanner.ts +0 -96
- package/src/utils/update-check.ts +0 -286
- package/src/utils/workflow-advisor.ts +0 -118
- package/src/utils/workflows.test.ts +0 -114
- package/src/utils/workflows.ts +0 -178
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -14
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/analysis-cache
|
|
3
|
+
* Persists per-repo analysis results in `.nexusflow-analysis-cache.json` at
|
|
4
|
+
* the workspace root, keyed by a git content fingerprint (HEAD SHA plus a
|
|
5
|
+
* signature of dirty files). Lets refresh/sync re-analyze only repos whose
|
|
6
|
+
* content actually changed, keeping unchanged maps byte-identical — which
|
|
7
|
+
* avoids file churn and preserves AI assistants' prompt caches (token saving).
|
|
8
|
+
*/
|
|
9
|
+
import type { ProjectAnalysis } from '../types.js';
|
|
10
|
+
/** A cached analysis result for a single repo. */
|
|
11
|
+
export interface AnalysisCacheEntry {
|
|
12
|
+
/** Directory name of the repo. */
|
|
13
|
+
repoName: string;
|
|
14
|
+
/** Content fingerprint of the repo when the analysis ran. */
|
|
15
|
+
fingerprint: string;
|
|
16
|
+
/** ISO timestamp of when the analysis ran. */
|
|
17
|
+
analyzedAt: string;
|
|
18
|
+
/** The cached analysis result. */
|
|
19
|
+
analysis: ProjectAnalysis;
|
|
20
|
+
}
|
|
21
|
+
/** On-disk shape of the analysis cache. */
|
|
22
|
+
export interface AnalysisCache {
|
|
23
|
+
version: 1;
|
|
24
|
+
/** Entries keyed by repo name. */
|
|
25
|
+
repos: Record<string, AnalysisCacheEntry>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns the path to the analysis cache file for a workspace.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getAnalysisCachePath(workspacePath: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Loads the analysis cache, returning an empty skeleton when the file does
|
|
33
|
+
* not exist or cannot be parsed.
|
|
34
|
+
*
|
|
35
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadAnalysisCache(workspacePath: string): Promise<AnalysisCache>;
|
|
38
|
+
/**
|
|
39
|
+
* Saves the analysis cache, pruning entries for repos that are no longer in
|
|
40
|
+
* the given list of current repo names.
|
|
41
|
+
*
|
|
42
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
43
|
+
* @param cache - The cache to persist.
|
|
44
|
+
* @param currentRepoNames - Names of repos currently in the workspace; when
|
|
45
|
+
* provided, entries for other repos are dropped.
|
|
46
|
+
*/
|
|
47
|
+
export declare function saveAnalysisCache(workspacePath: string, cache: AnalysisCache, currentRepoNames?: string[]): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Computes a content fingerprint for a repo: the HEAD commit SHA, extended
|
|
50
|
+
* with a hash of the dirty working-tree files (status line + size + mtime per
|
|
51
|
+
* file) when the tree is not clean. Editing, adding, or deleting an
|
|
52
|
+
* uncommitted file therefore changes the fingerprint too.
|
|
53
|
+
*
|
|
54
|
+
* @param repoPath - Absolute path to the repo root.
|
|
55
|
+
* @returns The fingerprint, or null when git fails (caller should re-analyze).
|
|
56
|
+
*/
|
|
57
|
+
export declare function getRepoFingerprint(repoPath: string): Promise<string | null>;
|
|
58
|
+
//# sourceMappingURL=analysis-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-cache.d.ts","sourceRoot":"","sources":["../../src/core/analysis-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC3C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CAWxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,EACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAUf;AAaD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqCxB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/analysis-cache
|
|
3
|
+
* Persists per-repo analysis results in `.nexusflow-analysis-cache.json` at
|
|
4
|
+
* the workspace root, keyed by a git content fingerprint (HEAD SHA plus a
|
|
5
|
+
* signature of dirty files). Lets refresh/sync re-analyze only repos whose
|
|
6
|
+
* content actually changed, keeping unchanged maps byte-identical — which
|
|
7
|
+
* avoids file churn and preserves AI assistants' prompt caches (token saving).
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import * as fs from 'node:fs/promises';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { execa } from 'execa';
|
|
13
|
+
/** Name of the analysis cache file, written at the workspace root. */
|
|
14
|
+
const CACHE_FILE = '.nexusflow-analysis-cache.json';
|
|
15
|
+
/**
|
|
16
|
+
* Returns the path to the analysis cache file for a workspace.
|
|
17
|
+
*/
|
|
18
|
+
export function getAnalysisCachePath(workspacePath) {
|
|
19
|
+
return path.join(workspacePath, CACHE_FILE);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Loads the analysis cache, returning an empty skeleton when the file does
|
|
23
|
+
* not exist or cannot be parsed.
|
|
24
|
+
*
|
|
25
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
26
|
+
*/
|
|
27
|
+
export async function loadAnalysisCache(workspacePath) {
|
|
28
|
+
try {
|
|
29
|
+
const raw = await fs.readFile(getAnalysisCachePath(workspacePath), 'utf-8');
|
|
30
|
+
const cache = JSON.parse(raw);
|
|
31
|
+
if (!cache.repos || typeof cache.repos !== 'object') {
|
|
32
|
+
cache.repos = {};
|
|
33
|
+
}
|
|
34
|
+
return cache;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { version: 1, repos: {} };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Saves the analysis cache, pruning entries for repos that are no longer in
|
|
42
|
+
* the given list of current repo names.
|
|
43
|
+
*
|
|
44
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
45
|
+
* @param cache - The cache to persist.
|
|
46
|
+
* @param currentRepoNames - Names of repos currently in the workspace; when
|
|
47
|
+
* provided, entries for other repos are dropped.
|
|
48
|
+
*/
|
|
49
|
+
export async function saveAnalysisCache(workspacePath, cache, currentRepoNames) {
|
|
50
|
+
if (currentRepoNames) {
|
|
51
|
+
for (const name of Object.keys(cache.repos)) {
|
|
52
|
+
if (!currentRepoNames.includes(name)) {
|
|
53
|
+
delete cache.repos[name];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const data = JSON.stringify(cache, null, 2) + '\n';
|
|
58
|
+
await fs.writeFile(getAnalysisCachePath(workspacePath), data, 'utf-8');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Strips the surrounding quotes git adds to porcelain paths containing
|
|
62
|
+
* special characters.
|
|
63
|
+
*/
|
|
64
|
+
function unquotePorcelainPath(p) {
|
|
65
|
+
if (p.startsWith('"') && p.endsWith('"')) {
|
|
66
|
+
return p.slice(1, -1);
|
|
67
|
+
}
|
|
68
|
+
return p;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Computes a content fingerprint for a repo: the HEAD commit SHA, extended
|
|
72
|
+
* with a hash of the dirty working-tree files (status line + size + mtime per
|
|
73
|
+
* file) when the tree is not clean. Editing, adding, or deleting an
|
|
74
|
+
* uncommitted file therefore changes the fingerprint too.
|
|
75
|
+
*
|
|
76
|
+
* @param repoPath - Absolute path to the repo root.
|
|
77
|
+
* @returns The fingerprint, or null when git fails (caller should re-analyze).
|
|
78
|
+
*/
|
|
79
|
+
export async function getRepoFingerprint(repoPath) {
|
|
80
|
+
try {
|
|
81
|
+
const { stdout: shaOut } = await execa('git', ['rev-parse', 'HEAD'], {
|
|
82
|
+
cwd: repoPath,
|
|
83
|
+
});
|
|
84
|
+
const sha = shaOut.trim();
|
|
85
|
+
const { stdout: statusOut } = await execa('git', ['status', '--porcelain'], {
|
|
86
|
+
cwd: repoPath,
|
|
87
|
+
});
|
|
88
|
+
const lines = statusOut.split('\n').filter(Boolean);
|
|
89
|
+
if (lines.length === 0) {
|
|
90
|
+
return sha;
|
|
91
|
+
}
|
|
92
|
+
let dirtySignature = '';
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
const rel = line.slice(3).trim();
|
|
95
|
+
// Rename lines look like "R old -> new"; stat the new path.
|
|
96
|
+
const target = rel.includes(' -> ') ? rel.split(' -> ').pop() : rel;
|
|
97
|
+
const filePath = path.join(repoPath, unquotePorcelainPath(target));
|
|
98
|
+
try {
|
|
99
|
+
const st = await fs.stat(filePath);
|
|
100
|
+
dirtySignature += `${line}|${st.size}|${Math.floor(st.mtimeMs)}\n`;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
dirtySignature += `${line}|missing\n`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const dirtyHash = createHash('sha1')
|
|
107
|
+
.update(dirtySignature)
|
|
108
|
+
.digest('hex')
|
|
109
|
+
.slice(0, 12);
|
|
110
|
+
return `${sha}+${dirtyHash}`;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=analysis-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-cache.js","sourceRoot":"","sources":["../../src/core/analysis-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-cache.test.d.ts","sourceRoot":"","sources":["../../src/core/analysis-cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { loadAnalysisCache, saveAnalysisCache, getRepoFingerprint, } from './analysis-cache.js';
|
|
5
|
+
vi.mock('node:fs/promises');
|
|
6
|
+
vi.mock('execa');
|
|
7
|
+
const mockAnalysis = {
|
|
8
|
+
name: 'repo-1',
|
|
9
|
+
path: '/ws/repo-1',
|
|
10
|
+
techStack: { languages: ['typescript'], frameworks: [], buildTools: [], projectType: 'backend' },
|
|
11
|
+
endpoints: [],
|
|
12
|
+
dependencies: [],
|
|
13
|
+
ports: [],
|
|
14
|
+
readmeSummary: null,
|
|
15
|
+
existingAIConfigs: [],
|
|
16
|
+
};
|
|
17
|
+
/** Parses the JSON written by the most recent writeFile call. */
|
|
18
|
+
function lastWritten() {
|
|
19
|
+
const calls = vi.mocked(fs.writeFile).mock.calls;
|
|
20
|
+
const data = calls[calls.length - 1][1];
|
|
21
|
+
return JSON.parse(data);
|
|
22
|
+
}
|
|
23
|
+
describe('analysis-cache', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
27
|
+
});
|
|
28
|
+
describe('loadAnalysisCache', () => {
|
|
29
|
+
it('returns an empty skeleton when the file is absent', async () => {
|
|
30
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('ENOENT'));
|
|
31
|
+
const cache = await loadAnalysisCache('/ws');
|
|
32
|
+
expect(cache.version).toBe(1);
|
|
33
|
+
expect(cache.repos).toEqual({});
|
|
34
|
+
});
|
|
35
|
+
it('round-trips an existing cache file', async () => {
|
|
36
|
+
const existing = {
|
|
37
|
+
version: 1,
|
|
38
|
+
repos: {
|
|
39
|
+
'repo-1': {
|
|
40
|
+
repoName: 'repo-1',
|
|
41
|
+
fingerprint: 'abc123',
|
|
42
|
+
analyzedAt: '2026-01-01T00:00:00.000Z',
|
|
43
|
+
analysis: mockAnalysis,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(existing));
|
|
48
|
+
const cache = await loadAnalysisCache('/ws');
|
|
49
|
+
expect(cache.repos['repo-1'].fingerprint).toBe('abc123');
|
|
50
|
+
expect(cache.repos['repo-1'].analysis.name).toBe('repo-1');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('saveAnalysisCache', () => {
|
|
54
|
+
it('prunes entries for repos no longer in the workspace', async () => {
|
|
55
|
+
const cache = {
|
|
56
|
+
version: 1,
|
|
57
|
+
repos: {
|
|
58
|
+
'repo-1': { repoName: 'repo-1', fingerprint: 'a', analyzedAt: '', analysis: mockAnalysis },
|
|
59
|
+
'removed-repo': { repoName: 'removed-repo', fingerprint: 'b', analyzedAt: '', analysis: mockAnalysis },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
await saveAnalysisCache('/ws', cache, ['repo-1']);
|
|
63
|
+
const written = lastWritten();
|
|
64
|
+
expect(written.repos['repo-1']).toBeDefined();
|
|
65
|
+
expect(written.repos['removed-repo']).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('getRepoFingerprint', () => {
|
|
69
|
+
it('returns the bare HEAD SHA for a clean tree', async () => {
|
|
70
|
+
vi.mocked(execa).mockImplementation((async (_cmd, args) => {
|
|
71
|
+
if (args[0] === 'rev-parse')
|
|
72
|
+
return { stdout: 'abc123\n' };
|
|
73
|
+
return { stdout: '' }; // clean status
|
|
74
|
+
}));
|
|
75
|
+
const fp = await getRepoFingerprint('/ws/repo-1');
|
|
76
|
+
expect(fp).toBe('abc123');
|
|
77
|
+
});
|
|
78
|
+
it('extends the SHA with a dirty-files hash when the tree is dirty', async () => {
|
|
79
|
+
vi.mocked(execa).mockImplementation((async (_cmd, args) => {
|
|
80
|
+
if (args[0] === 'rev-parse')
|
|
81
|
+
return { stdout: 'abc123\n' };
|
|
82
|
+
return { stdout: ' M src/file1.ts\n' };
|
|
83
|
+
}));
|
|
84
|
+
vi.mocked(fs.stat).mockResolvedValue({ size: 100, mtimeMs: 1000 });
|
|
85
|
+
const fp = await getRepoFingerprint('/ws/repo-1');
|
|
86
|
+
expect(fp).toMatch(/^abc123\+[0-9a-f]{12}$/);
|
|
87
|
+
});
|
|
88
|
+
it('changes the fingerprint when a dirty file is edited', async () => {
|
|
89
|
+
vi.mocked(execa).mockImplementation((async (_cmd, args) => {
|
|
90
|
+
if (args[0] === 'rev-parse')
|
|
91
|
+
return { stdout: 'abc123\n' };
|
|
92
|
+
return { stdout: ' M src/file1.ts\n' };
|
|
93
|
+
}));
|
|
94
|
+
vi.mocked(fs.stat).mockResolvedValue({ size: 100, mtimeMs: 1000 });
|
|
95
|
+
const before = await getRepoFingerprint('/ws/repo-1');
|
|
96
|
+
vi.mocked(fs.stat).mockResolvedValue({ size: 150, mtimeMs: 2000 });
|
|
97
|
+
const after = await getRepoFingerprint('/ws/repo-1');
|
|
98
|
+
expect(before).not.toBe(after);
|
|
99
|
+
expect(before).not.toBe('abc123');
|
|
100
|
+
});
|
|
101
|
+
it('returns null when git fails', async () => {
|
|
102
|
+
vi.mocked(execa).mockRejectedValue(new Error('not a git repository'));
|
|
103
|
+
const fp = await getRepoFingerprint('/not-a-repo');
|
|
104
|
+
expect(fp).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=analysis-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-cache.test.js","sourceRoot":"","sources":["../../src/core/analysis-cache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/refresh
|
|
3
|
+
* Headless workspace refresh — regenerates context files, maps, and plans for
|
|
4
|
+
* a workspace using the analysis cache, so only repos whose content changed
|
|
5
|
+
* are re-analyzed and only their maps are rewritten. Backs the CLI command,
|
|
6
|
+
* the HTTP API, and the scheduler alike.
|
|
7
|
+
*/
|
|
8
|
+
/** Options for a headless refresh run. */
|
|
9
|
+
export interface RefreshOptions {
|
|
10
|
+
/** Restrict regeneration to a single repo (by directory name). */
|
|
11
|
+
onlyRepo?: string;
|
|
12
|
+
/** Only refresh base-layer maps and codebase knowledge. */
|
|
13
|
+
baseOnly?: boolean;
|
|
14
|
+
/** Ignore the analysis cache and re-analyze every repo. */
|
|
15
|
+
force?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Outcome of a headless refresh run. */
|
|
18
|
+
export interface RefreshReport {
|
|
19
|
+
/** Absolute path to the workspace. */
|
|
20
|
+
workspacePath: string;
|
|
21
|
+
/** Names of repos that were re-analyzed because their content changed. */
|
|
22
|
+
analyzedRepos: string[];
|
|
23
|
+
/** Names of repos whose cached analysis was reused. */
|
|
24
|
+
reusedRepos: string[];
|
|
25
|
+
/** Whether the handoff bundle was refreshed too. */
|
|
26
|
+
refreshedHandoff: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Refreshes a workspace's context files, maps, and plans. Throws if the
|
|
30
|
+
* workspace configuration cannot be loaded.
|
|
31
|
+
*
|
|
32
|
+
* Token-saving behavior: repos with an unchanged git fingerprint reuse their
|
|
33
|
+
* cached analysis and keep their existing map files byte-identical, so AI
|
|
34
|
+
* assistants' prompt caches stay valid across refreshes.
|
|
35
|
+
*
|
|
36
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
37
|
+
* @param options - Refresh options.
|
|
38
|
+
* @returns A report of what was analyzed vs reused.
|
|
39
|
+
*/
|
|
40
|
+
export declare function refreshWorkspace(workspacePath: string, options?: RefreshOptions): Promise<RefreshReport>;
|
|
41
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/core/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,oDAAoD;IACpD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/refresh
|
|
3
|
+
* Headless workspace refresh — regenerates context files, maps, and plans for
|
|
4
|
+
* a workspace using the analysis cache, so only repos whose content changed
|
|
5
|
+
* are re-analyzed and only their maps are rewritten. Backs the CLI command,
|
|
6
|
+
* the HTTP API, and the scheduler alike.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { loadFeatureConfig, resolveRepoInfos } from './workspace.js';
|
|
11
|
+
import { analyzeAllReposCached } from '../analyzers/index.js';
|
|
12
|
+
import { generateContextFiles } from '../generators/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* Refreshes a workspace's context files, maps, and plans. Throws if the
|
|
15
|
+
* workspace configuration cannot be loaded.
|
|
16
|
+
*
|
|
17
|
+
* Token-saving behavior: repos with an unchanged git fingerprint reuse their
|
|
18
|
+
* cached analysis and keep their existing map files byte-identical, so AI
|
|
19
|
+
* assistants' prompt caches stay valid across refreshes.
|
|
20
|
+
*
|
|
21
|
+
* @param workspacePath - Absolute path to the workspace root.
|
|
22
|
+
* @param options - Refresh options.
|
|
23
|
+
* @returns A report of what was analyzed vs reused.
|
|
24
|
+
*/
|
|
25
|
+
export async function refreshWorkspace(workspacePath, options = {}) {
|
|
26
|
+
const feature = await loadFeatureConfig(workspacePath);
|
|
27
|
+
if (!feature) {
|
|
28
|
+
throw new Error(`Failed to load workspace configuration. Ensure nexusflow.json exists at ${workspacePath}.`);
|
|
29
|
+
}
|
|
30
|
+
const allRepos = await resolveRepoInfos(feature.repos);
|
|
31
|
+
const { analysis, analyzed, reused } = await analyzeAllReposCached(allRepos, workspacePath, { force: options.force });
|
|
32
|
+
const ctx = { feature, repos: allRepos, analysis };
|
|
33
|
+
await generateContextFiles(ctx, feature.assistants, workspacePath, options.onlyRepo, options.baseOnly, options.force ? undefined : analyzed);
|
|
34
|
+
// If a handoff bundle exists, refresh it too — it reuses the cached
|
|
35
|
+
// analysis, so this no longer triggers a second full analysis pass.
|
|
36
|
+
let refreshedHandoff = false;
|
|
37
|
+
const handoffPath = path.join(workspacePath, 'nexusflow-handoff.md');
|
|
38
|
+
if (!options.baseOnly) {
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(handoffPath);
|
|
41
|
+
const { handoffCommand } = await import('../commands/handoff.js');
|
|
42
|
+
await handoffCommand(workspacePath);
|
|
43
|
+
refreshedHandoff = true;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// No handoff file, or refresh failed — non-fatal either way.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
workspacePath,
|
|
51
|
+
analyzedRepos: analyzed,
|
|
52
|
+
reusedRepos: reused,
|
|
53
|
+
refreshedHandoff,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/core/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAyB9D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,aAAqB,EACrB,UAA0B,EAAE;IAE5B,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2EAA2E,aAAa,GAAG,CAC5F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEvD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAChE,QAAQ,EACR,aAAa,EACb,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CACzB,CAAC;IAEF,MAAM,GAAG,GAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAErE,MAAM,oBAAoB,CACxB,GAAG,EACH,OAAO,CAAC,UAAU,EAClB,aAAa,EACb,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CACrC,CAAC;IAEF,oEAAoE;IACpE,oEAAoE;IACpE,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC7B,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;YAClE,MAAM,cAAc,CAAC,aAAa,CAAC,CAAC;YACpC,gBAAgB,GAAG,IAAI,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;IACH,CAAC;IAED,OAAO;QACL,aAAa;QACb,aAAa,EAAE,QAAQ;QACvB,WAAW,EAAE,MAAM;QACnB,gBAAgB;KACjB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/scheduler
|
|
3
|
+
* Recurring workspace jobs — periodic `sync` and `refresh` runs per workspace.
|
|
4
|
+
*
|
|
5
|
+
* Job definitions are persisted at `~/.nexusflow/schedules.json` and executed
|
|
6
|
+
* by any long-running NexusFlow process (the dashboard server started with
|
|
7
|
+
* `nexusflow ui` runs the scheduler automatically). Jobs use the headless
|
|
8
|
+
* cores (`syncWorkspace`, `refreshWorkspace`), which are cache-aware: a
|
|
9
|
+
* scheduled run only re-analyzes repos whose content changed, so unattended
|
|
10
|
+
* schedules stay token-efficient and never churn unchanged context files.
|
|
11
|
+
*/
|
|
12
|
+
/** Kinds of work a schedule can run. */
|
|
13
|
+
export type ScheduleTask = 'sync' | 'refresh';
|
|
14
|
+
/** A persisted recurring job definition. */
|
|
15
|
+
export interface ScheduledJob {
|
|
16
|
+
/** Unique job id, e.g. 'sync-my-feature-k3x9q2'. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Absolute path to the workspace the job operates on. */
|
|
19
|
+
workspacePath: string;
|
|
20
|
+
/** What the job runs. */
|
|
21
|
+
task: ScheduleTask;
|
|
22
|
+
/** How often the job runs, in minutes. */
|
|
23
|
+
intervalMinutes: number;
|
|
24
|
+
/** Disabled jobs are kept but never run. */
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
/** ISO timestamp of when the job was created. */
|
|
27
|
+
createdAt: string;
|
|
28
|
+
/** ISO timestamp of the last run, if any. */
|
|
29
|
+
lastRunAt?: string;
|
|
30
|
+
/** Outcome of the last run. */
|
|
31
|
+
lastStatus?: 'success' | 'error';
|
|
32
|
+
/** Human-readable summary of the last run. */
|
|
33
|
+
lastMessage?: string;
|
|
34
|
+
}
|
|
35
|
+
/** On-disk shape of the schedules file. */
|
|
36
|
+
export interface ScheduleStore {
|
|
37
|
+
version: 1;
|
|
38
|
+
jobs: ScheduledJob[];
|
|
39
|
+
}
|
|
40
|
+
/** Result of running a single job. */
|
|
41
|
+
export interface JobRunResult {
|
|
42
|
+
status: 'success' | 'error';
|
|
43
|
+
message: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns the path to the schedules file (~/.nexusflow/schedules.json).
|
|
47
|
+
*/
|
|
48
|
+
export declare function getSchedulesPath(): string;
|
|
49
|
+
/**
|
|
50
|
+
* Loads all schedules, returning an empty store when the file does not exist
|
|
51
|
+
* or cannot be parsed.
|
|
52
|
+
*/
|
|
53
|
+
export declare function loadSchedules(): Promise<ScheduleStore>;
|
|
54
|
+
/**
|
|
55
|
+
* Persists the schedule store to ~/.nexusflow/schedules.json.
|
|
56
|
+
*/
|
|
57
|
+
export declare function saveSchedules(store: ScheduleStore): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Parses a human interval like '30m', '2h', '1d', or a bare minute count
|
|
60
|
+
* ('45') into minutes. Returns null for unparseable or non-positive input.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseInterval(text: string): number | null;
|
|
63
|
+
/**
|
|
64
|
+
* Formats an interval in minutes back to a compact human string.
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatInterval(minutes: number): string;
|
|
67
|
+
/**
|
|
68
|
+
* Whether a job is due to run: it is enabled and either never ran or its
|
|
69
|
+
* interval has elapsed since the last run. A job whose interval elapsed while
|
|
70
|
+
* no scheduler was running simply runs on the next tick.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isDue(job: ScheduledJob, now?: Date): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Computes when a job will next be due, for display purposes.
|
|
75
|
+
*/
|
|
76
|
+
export declare function nextDueAt(job: ScheduledJob): Date | null;
|
|
77
|
+
/**
|
|
78
|
+
* Adds a new schedule and persists it.
|
|
79
|
+
*
|
|
80
|
+
* @param input - Workspace, task, and interval for the new job.
|
|
81
|
+
* @returns The created job.
|
|
82
|
+
*/
|
|
83
|
+
export declare function addSchedule(input: {
|
|
84
|
+
workspacePath: string;
|
|
85
|
+
task: ScheduleTask;
|
|
86
|
+
intervalMinutes: number;
|
|
87
|
+
}): Promise<ScheduledJob>;
|
|
88
|
+
/**
|
|
89
|
+
* Removes a schedule by id. Returns false when no job matched.
|
|
90
|
+
*/
|
|
91
|
+
export declare function removeSchedule(id: string): Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* Enables or disables a schedule by id. Returns the updated job, or null
|
|
94
|
+
* when no job matched.
|
|
95
|
+
*/
|
|
96
|
+
export declare function setScheduleEnabled(id: string, enabled: boolean): Promise<ScheduledJob | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Runs a single job immediately and records the outcome in the store.
|
|
99
|
+
* Never throws — failures are captured in the returned result.
|
|
100
|
+
*
|
|
101
|
+
* @param job - The job to run.
|
|
102
|
+
* @returns The classified outcome with a summary message.
|
|
103
|
+
*/
|
|
104
|
+
export declare function runJob(job: ScheduledJob): Promise<JobRunResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Starts the scheduler loop inside the current process. Every tick it
|
|
107
|
+
* re-loads the schedules file (so jobs added/removed via CLI are picked up
|
|
108
|
+
* without a restart) and runs all due jobs sequentially. Ticks never overlap.
|
|
109
|
+
*
|
|
110
|
+
* @param options - Tick interval (default 60s) and an optional log sink.
|
|
111
|
+
* @returns A stop function that clears the interval.
|
|
112
|
+
*/
|
|
113
|
+
export declare function startScheduler(options?: {
|
|
114
|
+
tickMs?: number;
|
|
115
|
+
log?: (message: string) => void;
|
|
116
|
+
}): () => void;
|
|
117
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkBH,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9C,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,0CAA0C;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IACjC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAuGD;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAW5D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAezD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAItD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAQxE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAMxD;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,YAAY,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,YAAY,CAAC,CAexB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOjE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAO9B;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA2DrE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,GAAG,MAAM,IAAI,CA8BlB"}
|