specweave 0.23.10 → 0.23.12
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/.claude-plugin/marketplace.json +7 -7
- package/CLAUDE.md +384 -1449
- package/dist/src/cli/commands/cleanup-cache.d.ts +14 -0
- package/dist/src/cli/commands/cleanup-cache.d.ts.map +1 -0
- package/dist/src/cli/commands/cleanup-cache.js +63 -0
- package/dist/src/cli/commands/cleanup-cache.js.map +1 -0
- package/dist/src/cli/commands/init.js +40 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/async-project-loader.d.ts +148 -0
- package/dist/src/cli/helpers/async-project-loader.d.ts.map +1 -0
- package/dist/src/cli/helpers/async-project-loader.js +351 -0
- package/dist/src/cli/helpers/async-project-loader.js.map +1 -0
- package/dist/src/cli/helpers/cancelation-handler.d.ts +123 -0
- package/dist/src/cli/helpers/cancelation-handler.d.ts.map +1 -0
- package/dist/src/cli/helpers/cancelation-handler.js +187 -0
- package/dist/src/cli/helpers/cancelation-handler.js.map +1 -0
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts +43 -0
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -0
- package/dist/src/cli/helpers/import-strategy-prompter.js +136 -0
- package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +5 -2
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +90 -40
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts +2 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +120 -35
- package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
- package/dist/src/cli/helpers/progress-tracker.d.ts +121 -0
- package/dist/src/cli/helpers/progress-tracker.d.ts.map +1 -0
- package/dist/src/cli/helpers/progress-tracker.js +202 -0
- package/dist/src/cli/helpers/progress-tracker.js.map +1 -0
- package/dist/src/cli/helpers/project-count-fetcher.d.ts +69 -0
- package/dist/src/cli/helpers/project-count-fetcher.d.ts.map +1 -0
- package/dist/src/cli/helpers/project-count-fetcher.js +173 -0
- package/dist/src/cli/helpers/project-count-fetcher.js.map +1 -0
- package/dist/src/config/types.d.ts +14 -14
- package/dist/src/core/cache/cache-manager.d.ts +119 -0
- package/dist/src/core/cache/cache-manager.d.ts.map +1 -0
- package/dist/src/core/cache/cache-manager.js +304 -0
- package/dist/src/core/cache/cache-manager.js.map +1 -0
- package/dist/src/core/cache/rate-limit-checker.d.ts +92 -0
- package/dist/src/core/cache/rate-limit-checker.d.ts.map +1 -0
- package/dist/src/core/cache/rate-limit-checker.js +160 -0
- package/dist/src/core/cache/rate-limit-checker.js.map +1 -0
- package/dist/src/core/progress/cancelation-handler.d.ts +79 -0
- package/dist/src/core/progress/cancelation-handler.d.ts.map +1 -0
- package/dist/src/core/progress/cancelation-handler.js +111 -0
- package/dist/src/core/progress/cancelation-handler.js.map +1 -0
- package/dist/src/core/progress/import-state.d.ts +71 -0
- package/dist/src/core/progress/import-state.d.ts.map +1 -0
- package/dist/src/core/progress/import-state.js +96 -0
- package/dist/src/core/progress/import-state.js.map +1 -0
- package/dist/src/core/progress/progress-tracker.d.ts +139 -0
- package/dist/src/core/progress/progress-tracker.d.ts.map +1 -0
- package/dist/src/core/progress/progress-tracker.js +223 -0
- package/dist/src/core/progress/progress-tracker.js.map +1 -0
- package/dist/src/init/architecture/types.d.ts +6 -6
- package/dist/src/integrations/ado/ado-client.d.ts +25 -0
- package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-client.js +67 -0
- package/dist/src/integrations/ado/ado-client.js.map +1 -1
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts +99 -0
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -0
- package/dist/src/integrations/ado/ado-dependency-loader.js +207 -0
- package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +32 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +81 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-dependency-loader.d.ts +101 -0
- package/dist/src/integrations/jira/jira-dependency-loader.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira-dependency-loader.js +200 -0
- package/dist/src/integrations/jira/jira-dependency-loader.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +20 -0
- package/plugins/specweave/agents/architect/AGENT.md +100 -602
- package/plugins/specweave/agents/pm/AGENT.md +96 -597
- package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
- package/plugins/specweave/agents/pm/AGENT.md.bak2 +1754 -0
- package/plugins/specweave/commands/check-hooks.md +257 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
- package/plugins/specweave/hooks/post-task-completion.sh +225 -228
- package/plugins/specweave/hooks/post-write-spec.sh +207 -31
- package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +5 -7
- package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
- package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -0
- package/plugins/specweave/skills/compliance-architecture/SKILL.md +374 -0
- package/plugins/specweave/skills/external-sync-wizard/SKILL.md +610 -0
- package/plugins/specweave/skills/pm-closure-validation/SKILL.md +541 -0
- package/plugins/specweave/skills/roadmap-planner/SKILL.md +473 -0
- package/plugins/specweave-ado/commands/refresh-cache.js +25 -0
- package/plugins/specweave-ado/commands/refresh-cache.ts +40 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh +1 -1
- package/plugins/specweave-github/hooks/post-task-completion.sh +1 -1
- package/plugins/specweave-jira/commands/refresh-cache.js +25 -0
- package/plugins/specweave-jira/commands/refresh-cache.ts +40 -0
- package/plugins/specweave-jira/hooks/post-task-completion.sh +1 -1
- package/plugins/specweave-kafka-streams/commands/topology.md +437 -0
- package/plugins/specweave-n8n/commands/workflow-template.md +262 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +228 -6465
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CancelationHandler - Graceful Ctrl+C (SIGINT) handling with state persistence
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Single Ctrl+C: Save state and exit gracefully
|
|
6
|
+
* - Double Ctrl+C (within 2s): Force exit immediately
|
|
7
|
+
* - Polling mechanism for cooperative cancelation
|
|
8
|
+
* - State persistence to resume later
|
|
9
|
+
*
|
|
10
|
+
* @module core/progress/cancelation-handler
|
|
11
|
+
*/
|
|
12
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
13
|
+
/**
|
|
14
|
+
* CancelationHandler - Handles SIGINT gracefully with double Ctrl+C force exit
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const handler = new CancelationHandler({
|
|
19
|
+
* onSaveState: async () => {
|
|
20
|
+
* await saveImportState();
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* handler.register();
|
|
25
|
+
*
|
|
26
|
+
* // In import loop
|
|
27
|
+
* for (const project of projects) {
|
|
28
|
+
* if (handler.shouldCancel()) {
|
|
29
|
+
* break;
|
|
30
|
+
* }
|
|
31
|
+
* await importProject(project);
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* handler.unregister();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class CancelationHandler {
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
this.cancelRequested = false;
|
|
40
|
+
this.firstCtrlCTime = null;
|
|
41
|
+
this.sigintHandler = null;
|
|
42
|
+
this.logger = options.logger ?? consoleLogger;
|
|
43
|
+
this.onSaveState = options.onSaveState;
|
|
44
|
+
this.forceExitTimeout = options.forceExitTimeout ?? 2000;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Register SIGINT listener
|
|
48
|
+
*/
|
|
49
|
+
register() {
|
|
50
|
+
this.sigintHandler = () => {
|
|
51
|
+
void this.handleSigint();
|
|
52
|
+
};
|
|
53
|
+
process.on('SIGINT', this.sigintHandler);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Unregister SIGINT listener
|
|
57
|
+
*/
|
|
58
|
+
unregister() {
|
|
59
|
+
if (this.sigintHandler) {
|
|
60
|
+
process.off('SIGINT', this.sigintHandler);
|
|
61
|
+
this.sigintHandler = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if cancelation was requested (polling mechanism)
|
|
66
|
+
*
|
|
67
|
+
* @returns true if Ctrl+C was pressed
|
|
68
|
+
*/
|
|
69
|
+
shouldCancel() {
|
|
70
|
+
return this.cancelRequested;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Handle SIGINT signal (Ctrl+C)
|
|
74
|
+
*/
|
|
75
|
+
async handleSigint() {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
// Double Ctrl+C detection
|
|
78
|
+
if (this.firstCtrlCTime !== null) {
|
|
79
|
+
const elapsed = now - this.firstCtrlCTime;
|
|
80
|
+
if (elapsed < this.forceExitTimeout) {
|
|
81
|
+
// Force exit on double Ctrl+C
|
|
82
|
+
this.logger.warn('\n⚠️ Forced exit (progress may be lost)');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// First Ctrl+C
|
|
87
|
+
this.firstCtrlCTime = now;
|
|
88
|
+
this.cancelRequested = true;
|
|
89
|
+
this.logger.log('\n⏸️ Cancelation requested. Saving progress...');
|
|
90
|
+
try {
|
|
91
|
+
// Save state if callback provided
|
|
92
|
+
if (this.onSaveState) {
|
|
93
|
+
await this.onSaveState();
|
|
94
|
+
}
|
|
95
|
+
this.logger.log('✅ Progress saved. Run with --resume to continue.');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.logger.error('❌ Failed to save progress:', error);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Reset cancelation state (useful for testing)
|
|
105
|
+
*/
|
|
106
|
+
reset() {
|
|
107
|
+
this.cancelRequested = false;
|
|
108
|
+
this.firstCtrlCTime = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cancelation-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancelation-handler.js","sourceRoot":"","sources":["../../../../src/core/progress/cancelation-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AActD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,kBAAkB;IAS7B,YAAY,UAA8B,EAAE;QAJpC,oBAAe,GAAY,KAAK,CAAC;QACjC,mBAAc,GAAkB,IAAI,CAAC;QACrC,kBAAa,GAAwB,IAAI,CAAC;QAGhD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC;IAC3D,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,IAAI,CAAC,aAAa,GAAG,GAAG,EAAE;YACxB,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3B,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,0BAA0B;QAC1B,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC;YAC1C,IAAI,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACpC,8BAA8B;gBAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;gBAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,eAAe;QACf,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC;QAC1B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAE5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAEnE,IAAI,CAAC;YACH,kCAAkC;YAClC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3B,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImportState - State persistence for resumable imports
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Save/load import progress
|
|
6
|
+
* - 24-hour TTL for stale state cleanup
|
|
7
|
+
* - Error tracking for retry logic
|
|
8
|
+
*
|
|
9
|
+
* @module core/progress/import-state
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Import error record
|
|
13
|
+
*/
|
|
14
|
+
export interface ImportError {
|
|
15
|
+
/** Item identifier (e.g., project key) */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Error message */
|
|
18
|
+
error: string;
|
|
19
|
+
/** Timestamp when error occurred */
|
|
20
|
+
timestamp: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Import state structure
|
|
24
|
+
*/
|
|
25
|
+
export interface ImportState {
|
|
26
|
+
/** Total number of items to import */
|
|
27
|
+
total: number;
|
|
28
|
+
/** Number of items completed (success + failed) */
|
|
29
|
+
completed: number;
|
|
30
|
+
/** Number of successful imports */
|
|
31
|
+
succeeded: number;
|
|
32
|
+
/** Number of failed imports */
|
|
33
|
+
failed: number;
|
|
34
|
+
/** Array of import errors */
|
|
35
|
+
errors: ImportError[];
|
|
36
|
+
/** Timestamp when state was saved */
|
|
37
|
+
timestamp: number;
|
|
38
|
+
/** Whether import was canceled by user */
|
|
39
|
+
canceled: boolean;
|
|
40
|
+
/** Optional: Last successfully processed item ID */
|
|
41
|
+
lastProcessedId?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Save import state to disk
|
|
45
|
+
*
|
|
46
|
+
* @param state Import state to save
|
|
47
|
+
* @param projectRoot Project root directory
|
|
48
|
+
*/
|
|
49
|
+
export declare function saveImportState(state: ImportState, projectRoot?: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Load import state from disk with TTL validation
|
|
52
|
+
*
|
|
53
|
+
* @param projectRoot Project root directory
|
|
54
|
+
* @param ttlMs Time-to-live in milliseconds (default: 24 hours)
|
|
55
|
+
* @returns Import state if valid, null if expired or missing
|
|
56
|
+
*/
|
|
57
|
+
export declare function loadImportState(projectRoot?: string, ttlMs?: number): Promise<ImportState | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Delete import state file
|
|
60
|
+
*
|
|
61
|
+
* @param projectRoot Project root directory
|
|
62
|
+
*/
|
|
63
|
+
export declare function deleteImportState(projectRoot?: string): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Check if import state exists (without loading)
|
|
66
|
+
*
|
|
67
|
+
* @param projectRoot Project root directory
|
|
68
|
+
* @returns true if state file exists
|
|
69
|
+
*/
|
|
70
|
+
export declare function hasImportState(projectRoot?: string): boolean;
|
|
71
|
+
//# sourceMappingURL=import-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-state.d.ts","sourceRoot":"","sources":["../../../../src/core/progress/import-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAYD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,WAAW,EAClB,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,WAAW,GAAE,MAAsB,EACnC,KAAK,GAAE,MAA4B,GAClC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA0B7B;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAG3E"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImportState - State persistence for resumable imports
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Save/load import progress
|
|
6
|
+
* - 24-hour TTL for stale state cleanup
|
|
7
|
+
* - Error tracking for retry logic
|
|
8
|
+
*
|
|
9
|
+
* @module core/progress/import-state
|
|
10
|
+
*/
|
|
11
|
+
import { promises as fs } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { existsSync } from 'fs';
|
|
14
|
+
import { mkdirpSync } from '../../utils/fs-native.js';
|
|
15
|
+
/**
|
|
16
|
+
* Get import state file path
|
|
17
|
+
*
|
|
18
|
+
* @param projectRoot Project root directory
|
|
19
|
+
* @returns Path to import-state.json
|
|
20
|
+
*/
|
|
21
|
+
function getStateFilePath(projectRoot = process.cwd()) {
|
|
22
|
+
return join(projectRoot, '.specweave', 'cache', 'import-state.json');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Save import state to disk
|
|
26
|
+
*
|
|
27
|
+
* @param state Import state to save
|
|
28
|
+
* @param projectRoot Project root directory
|
|
29
|
+
*/
|
|
30
|
+
export async function saveImportState(state, projectRoot = process.cwd()) {
|
|
31
|
+
const stateFilePath = getStateFilePath(projectRoot);
|
|
32
|
+
const cacheDir = join(projectRoot, '.specweave', 'cache');
|
|
33
|
+
// Ensure cache directory exists
|
|
34
|
+
mkdirpSync(cacheDir);
|
|
35
|
+
// Add timestamp
|
|
36
|
+
const stateWithTimestamp = {
|
|
37
|
+
...state,
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
// Write to file
|
|
41
|
+
await fs.writeFile(stateFilePath, JSON.stringify(stateWithTimestamp, null, 2), 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load import state from disk with TTL validation
|
|
45
|
+
*
|
|
46
|
+
* @param projectRoot Project root directory
|
|
47
|
+
* @param ttlMs Time-to-live in milliseconds (default: 24 hours)
|
|
48
|
+
* @returns Import state if valid, null if expired or missing
|
|
49
|
+
*/
|
|
50
|
+
export async function loadImportState(projectRoot = process.cwd(), ttlMs = 24 * 60 * 60 * 1000 // 24 hours
|
|
51
|
+
) {
|
|
52
|
+
const stateFilePath = getStateFilePath(projectRoot);
|
|
53
|
+
// Check if file exists
|
|
54
|
+
if (!existsSync(stateFilePath)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
// Read and parse file
|
|
59
|
+
const content = await fs.readFile(stateFilePath, 'utf-8');
|
|
60
|
+
const state = JSON.parse(content);
|
|
61
|
+
// Validate TTL
|
|
62
|
+
const age = Date.now() - state.timestamp;
|
|
63
|
+
if (age > ttlMs) {
|
|
64
|
+
// State is expired, delete it
|
|
65
|
+
await deleteImportState(projectRoot);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return state;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// Invalid JSON or read error
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Delete import state file
|
|
77
|
+
*
|
|
78
|
+
* @param projectRoot Project root directory
|
|
79
|
+
*/
|
|
80
|
+
export async function deleteImportState(projectRoot = process.cwd()) {
|
|
81
|
+
const stateFilePath = getStateFilePath(projectRoot);
|
|
82
|
+
if (existsSync(stateFilePath)) {
|
|
83
|
+
await fs.unlink(stateFilePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if import state exists (without loading)
|
|
88
|
+
*
|
|
89
|
+
* @param projectRoot Project root directory
|
|
90
|
+
* @returns true if state file exists
|
|
91
|
+
*/
|
|
92
|
+
export function hasImportState(projectRoot = process.cwd()) {
|
|
93
|
+
const stateFilePath = getStateFilePath(projectRoot);
|
|
94
|
+
return existsSync(stateFilePath);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=import-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-state.js","sourceRoot":"","sources":["../../../../src/core/progress/import-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAoCtD;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IAC3D,OAAO,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAkB,EAClB,cAAsB,OAAO,CAAC,GAAG,EAAE;IAEnC,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAE1D,gCAAgC;IAChC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAErB,gBAAgB;IAChB,MAAM,kBAAkB,GAAgB;QACtC,GAAG,KAAK;QACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IAEF,gBAAgB;IAChB,MAAM,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,cAAsB,OAAO,CAAC,GAAG,EAAE,EACnC,QAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW;;IAE/C,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAEpD,uBAAuB;IACvB,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,sBAAsB;QACtB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE/C,eAAe;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC;QACzC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;YAChB,8BAA8B;YAC9B,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,6BAA6B;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IACzE,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAEpD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IAChE,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACpD,OAAO,UAAU,CAAC,aAAa,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressTracker - Real-time progress tracking with ETA calculation
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - ASCII progress bar (30 characters)
|
|
6
|
+
* - Linear ETA extrapolation
|
|
7
|
+
* - Elapsed time tracking
|
|
8
|
+
* - Project-level status display (✅ ❌ ⏳)
|
|
9
|
+
* - Configurable update frequency
|
|
10
|
+
*
|
|
11
|
+
* @module core/progress/progress-tracker
|
|
12
|
+
*/
|
|
13
|
+
import { Logger } from '../../utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* Item status types
|
|
16
|
+
*/
|
|
17
|
+
export type ItemStatus = 'pending' | 'success' | 'error';
|
|
18
|
+
/**
|
|
19
|
+
* Progress tracker options
|
|
20
|
+
*/
|
|
21
|
+
export interface ProgressTrackerOptions {
|
|
22
|
+
/** Total number of items to process */
|
|
23
|
+
total: number;
|
|
24
|
+
/** Label for the operation (e.g., "Importing projects") */
|
|
25
|
+
label: string;
|
|
26
|
+
/** Show ETA calculation */
|
|
27
|
+
showEta?: boolean;
|
|
28
|
+
/** Update frequency (update every N items, default: 5) */
|
|
29
|
+
updateFrequency?: number;
|
|
30
|
+
/** Logger instance */
|
|
31
|
+
logger?: Logger;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Item with status
|
|
35
|
+
*/
|
|
36
|
+
interface TrackedItem {
|
|
37
|
+
name: string;
|
|
38
|
+
status: ItemStatus;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* ProgressTracker - Real-time progress tracking with ASCII progress bar and ETA
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const tracker = new ProgressTracker({
|
|
47
|
+
* total: 127,
|
|
48
|
+
* label: 'Importing projects',
|
|
49
|
+
* showEta: true,
|
|
50
|
+
* updateFrequency: 5
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* for (const project of projects) {
|
|
54
|
+
* await importProject(project);
|
|
55
|
+
* tracker.update(project.key, 'success');
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* tracker.finish(120, 5, 2);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare class ProgressTracker {
|
|
62
|
+
private total;
|
|
63
|
+
private label;
|
|
64
|
+
private showEta;
|
|
65
|
+
private updateFrequency;
|
|
66
|
+
private logger;
|
|
67
|
+
private items;
|
|
68
|
+
private startTime;
|
|
69
|
+
private lastUpdateCount;
|
|
70
|
+
constructor(options: ProgressTrackerOptions);
|
|
71
|
+
/**
|
|
72
|
+
* Update progress for a specific item
|
|
73
|
+
*
|
|
74
|
+
* @param itemName Item identifier (e.g., project key)
|
|
75
|
+
* @param status Item status (pending, success, error)
|
|
76
|
+
*/
|
|
77
|
+
update(itemName: string, status: ItemStatus): void;
|
|
78
|
+
/**
|
|
79
|
+
* Finish progress tracking and show final summary
|
|
80
|
+
*
|
|
81
|
+
* @param succeeded Count of successful items
|
|
82
|
+
* @param failed Count of failed items
|
|
83
|
+
* @param skipped Count of skipped items
|
|
84
|
+
*/
|
|
85
|
+
finish(succeeded: number, failed: number, skipped: number): void;
|
|
86
|
+
/**
|
|
87
|
+
* Render progress bar to console
|
|
88
|
+
*/
|
|
89
|
+
private render;
|
|
90
|
+
/**
|
|
91
|
+
* Render ASCII progress bar
|
|
92
|
+
*
|
|
93
|
+
* @param percentage Completion percentage (0-100)
|
|
94
|
+
* @returns ASCII progress bar (30 characters)
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* renderProgressBar(0) => "[> ]"
|
|
98
|
+
* renderProgressBar(37) => "[==========> ]"
|
|
99
|
+
* renderProgressBar(100) => "[==============================]"
|
|
100
|
+
*/
|
|
101
|
+
renderProgressBar(percentage: number): string;
|
|
102
|
+
/**
|
|
103
|
+
* Get elapsed time in human-readable format
|
|
104
|
+
*
|
|
105
|
+
* @returns Elapsed time string (e.g., "2m 34s", "45s")
|
|
106
|
+
*/
|
|
107
|
+
getElapsedTime(): string;
|
|
108
|
+
/**
|
|
109
|
+
* Get estimated time remaining (ETA) using linear extrapolation
|
|
110
|
+
*
|
|
111
|
+
* Formula: ETA = (total - completed) * (elapsed / completed)
|
|
112
|
+
*
|
|
113
|
+
* @returns ETA string (e.g., ", ~2m remaining", ", ~45s remaining")
|
|
114
|
+
*/
|
|
115
|
+
getEta(): string;
|
|
116
|
+
/**
|
|
117
|
+
* Get count of completed items (success + error)
|
|
118
|
+
*/
|
|
119
|
+
private getCompletedCount;
|
|
120
|
+
/**
|
|
121
|
+
* Get items by status
|
|
122
|
+
*/
|
|
123
|
+
getItemsByStatus(status: ItemStatus): TrackedItem[];
|
|
124
|
+
/**
|
|
125
|
+
* Get current progress statistics
|
|
126
|
+
*/
|
|
127
|
+
getStats(): {
|
|
128
|
+
total: number;
|
|
129
|
+
completed: number;
|
|
130
|
+
succeeded: number;
|
|
131
|
+
failed: number;
|
|
132
|
+
pending: number;
|
|
133
|
+
percentage: number;
|
|
134
|
+
elapsedMs: number;
|
|
135
|
+
etaMs: number | null;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export {};
|
|
139
|
+
//# sourceMappingURL=progress-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-tracker.d.ts","sourceRoot":"","sources":["../../../../src/core/progress/progress-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAa;gBAExB,OAAO,EAAE,sBAAsB;IAS3C;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAoBlD;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAgBhE;;OAEG;IACH,OAAO,CAAC,MAAM;IAcd;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAuB7C;;;;OAIG;IACH,cAAc,IAAI,MAAM;IAaxB;;;;;;OAMG;IACH,MAAM,IAAI,MAAM;IAyBhB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE;IAUnD;;OAEG;IACH,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB;CA0BF"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressTracker - Real-time progress tracking with ETA calculation
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - ASCII progress bar (30 characters)
|
|
6
|
+
* - Linear ETA extrapolation
|
|
7
|
+
* - Elapsed time tracking
|
|
8
|
+
* - Project-level status display (✅ ❌ ⏳)
|
|
9
|
+
* - Configurable update frequency
|
|
10
|
+
*
|
|
11
|
+
* @module core/progress/progress-tracker
|
|
12
|
+
*/
|
|
13
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* ProgressTracker - Real-time progress tracking with ASCII progress bar and ETA
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const tracker = new ProgressTracker({
|
|
20
|
+
* total: 127,
|
|
21
|
+
* label: 'Importing projects',
|
|
22
|
+
* showEta: true,
|
|
23
|
+
* updateFrequency: 5
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* for (const project of projects) {
|
|
27
|
+
* await importProject(project);
|
|
28
|
+
* tracker.update(project.key, 'success');
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* tracker.finish(120, 5, 2);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ProgressTracker {
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.items = new Map();
|
|
37
|
+
this.lastUpdateCount = 0;
|
|
38
|
+
this.total = options.total;
|
|
39
|
+
this.label = options.label;
|
|
40
|
+
this.showEta = options.showEta ?? true;
|
|
41
|
+
this.updateFrequency = options.updateFrequency ?? 5;
|
|
42
|
+
this.logger = options.logger ?? consoleLogger;
|
|
43
|
+
this.startTime = Date.now();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Update progress for a specific item
|
|
47
|
+
*
|
|
48
|
+
* @param itemName Item identifier (e.g., project key)
|
|
49
|
+
* @param status Item status (pending, success, error)
|
|
50
|
+
*/
|
|
51
|
+
update(itemName, status) {
|
|
52
|
+
// Track item
|
|
53
|
+
this.items.set(itemName, {
|
|
54
|
+
name: itemName,
|
|
55
|
+
status,
|
|
56
|
+
timestamp: Date.now(),
|
|
57
|
+
});
|
|
58
|
+
const completed = this.getCompletedCount();
|
|
59
|
+
// Only render if we've processed enough items since last update
|
|
60
|
+
if (completed - this.lastUpdateCount >= this.updateFrequency ||
|
|
61
|
+
completed === this.total) {
|
|
62
|
+
this.render();
|
|
63
|
+
this.lastUpdateCount = completed;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Finish progress tracking and show final summary
|
|
68
|
+
*
|
|
69
|
+
* @param succeeded Count of successful items
|
|
70
|
+
* @param failed Count of failed items
|
|
71
|
+
* @param skipped Count of skipped items
|
|
72
|
+
*/
|
|
73
|
+
finish(succeeded, failed, skipped) {
|
|
74
|
+
// Final render
|
|
75
|
+
this.render();
|
|
76
|
+
// Show summary
|
|
77
|
+
console.log(''); // Blank line
|
|
78
|
+
console.log(`✅ Succeeded: ${succeeded}`);
|
|
79
|
+
if (failed > 0) {
|
|
80
|
+
console.log(`❌ Failed: ${failed}`);
|
|
81
|
+
}
|
|
82
|
+
if (skipped > 0) {
|
|
83
|
+
console.log(`⏭️ Skipped: ${skipped}`);
|
|
84
|
+
}
|
|
85
|
+
console.log(''); // Blank line
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Render progress bar to console
|
|
89
|
+
*/
|
|
90
|
+
render() {
|
|
91
|
+
const completed = this.getCompletedCount();
|
|
92
|
+
const percentage = Math.round((completed / this.total) * 100);
|
|
93
|
+
const progressBar = this.renderProgressBar(percentage);
|
|
94
|
+
const elapsed = this.getElapsedTime();
|
|
95
|
+
const eta = this.showEta ? this.getEta() : '';
|
|
96
|
+
// Clear current line and print progress
|
|
97
|
+
process.stdout.write('\r'); // Carriage return to beginning of line
|
|
98
|
+
process.stdout.write(`${this.label}... ${completed}/${this.total} (${percentage}%) ${progressBar} [${elapsed}${eta}]`);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render ASCII progress bar
|
|
102
|
+
*
|
|
103
|
+
* @param percentage Completion percentage (0-100)
|
|
104
|
+
* @returns ASCII progress bar (30 characters)
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* renderProgressBar(0) => "[> ]"
|
|
108
|
+
* renderProgressBar(37) => "[==========> ]"
|
|
109
|
+
* renderProgressBar(100) => "[==============================]"
|
|
110
|
+
*/
|
|
111
|
+
renderProgressBar(percentage) {
|
|
112
|
+
const barLength = 30;
|
|
113
|
+
const filledLength = Math.round((percentage / 100) * barLength);
|
|
114
|
+
const emptyLength = barLength - filledLength;
|
|
115
|
+
let bar = '[';
|
|
116
|
+
// Filled portion
|
|
117
|
+
if (filledLength > 0) {
|
|
118
|
+
bar += '='.repeat(Math.max(0, filledLength - 1));
|
|
119
|
+
bar += '>';
|
|
120
|
+
bar += ' '.repeat(Math.max(0, emptyLength));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// At 0%, show cursor at start
|
|
124
|
+
bar += '>';
|
|
125
|
+
bar += ' '.repeat(Math.max(0, emptyLength - 1));
|
|
126
|
+
}
|
|
127
|
+
bar += ']';
|
|
128
|
+
return bar;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get elapsed time in human-readable format
|
|
132
|
+
*
|
|
133
|
+
* @returns Elapsed time string (e.g., "2m 34s", "45s")
|
|
134
|
+
*/
|
|
135
|
+
getElapsedTime() {
|
|
136
|
+
const elapsedMs = Date.now() - this.startTime;
|
|
137
|
+
const elapsedSeconds = Math.floor(elapsedMs / 1000);
|
|
138
|
+
if (elapsedSeconds < 60) {
|
|
139
|
+
return `${elapsedSeconds}s elapsed`;
|
|
140
|
+
}
|
|
141
|
+
const minutes = Math.floor(elapsedSeconds / 60);
|
|
142
|
+
const seconds = elapsedSeconds % 60;
|
|
143
|
+
return `${minutes}m ${seconds}s elapsed`;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get estimated time remaining (ETA) using linear extrapolation
|
|
147
|
+
*
|
|
148
|
+
* Formula: ETA = (total - completed) * (elapsed / completed)
|
|
149
|
+
*
|
|
150
|
+
* @returns ETA string (e.g., ", ~2m remaining", ", ~45s remaining")
|
|
151
|
+
*/
|
|
152
|
+
getEta() {
|
|
153
|
+
const completed = this.getCompletedCount();
|
|
154
|
+
if (completed === 0) {
|
|
155
|
+
return ', ~? remaining';
|
|
156
|
+
}
|
|
157
|
+
if (completed === this.total) {
|
|
158
|
+
return ', ~0s remaining';
|
|
159
|
+
}
|
|
160
|
+
const elapsedMs = Date.now() - this.startTime;
|
|
161
|
+
const avgTimePerItem = elapsedMs / completed;
|
|
162
|
+
const remaining = this.total - completed;
|
|
163
|
+
const etaMs = remaining * avgTimePerItem;
|
|
164
|
+
const etaSeconds = Math.floor(etaMs / 1000);
|
|
165
|
+
if (etaSeconds < 60) {
|
|
166
|
+
return `, ~${etaSeconds}s remaining`;
|
|
167
|
+
}
|
|
168
|
+
const minutes = Math.floor(etaSeconds / 60);
|
|
169
|
+
return `, ~${minutes}m remaining`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get count of completed items (success + error)
|
|
173
|
+
*/
|
|
174
|
+
getCompletedCount() {
|
|
175
|
+
let count = 0;
|
|
176
|
+
for (const item of this.items.values()) {
|
|
177
|
+
if (item.status === 'success' || item.status === 'error') {
|
|
178
|
+
count++;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return count;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get items by status
|
|
185
|
+
*/
|
|
186
|
+
getItemsByStatus(status) {
|
|
187
|
+
const result = [];
|
|
188
|
+
for (const item of this.items.values()) {
|
|
189
|
+
if (item.status === status) {
|
|
190
|
+
result.push(item);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get current progress statistics
|
|
197
|
+
*/
|
|
198
|
+
getStats() {
|
|
199
|
+
const completed = this.getCompletedCount();
|
|
200
|
+
const succeeded = this.getItemsByStatus('success').length;
|
|
201
|
+
const failed = this.getItemsByStatus('error').length;
|
|
202
|
+
const pending = this.getItemsByStatus('pending').length;
|
|
203
|
+
const percentage = Math.round((completed / this.total) * 100);
|
|
204
|
+
const elapsedMs = Date.now() - this.startTime;
|
|
205
|
+
let etaMs = null;
|
|
206
|
+
if (completed > 0 && completed < this.total) {
|
|
207
|
+
const avgTimePerItem = elapsedMs / completed;
|
|
208
|
+
const remaining = this.total - completed;
|
|
209
|
+
etaMs = remaining * avgTimePerItem;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
total: this.total,
|
|
213
|
+
completed,
|
|
214
|
+
succeeded,
|
|
215
|
+
failed,
|
|
216
|
+
pending,
|
|
217
|
+
percentage,
|
|
218
|
+
elapsedMs,
|
|
219
|
+
etaMs,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=progress-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-tracker.js","sourceRoot":"","sources":["../../../../src/core/progress/progress-tracker.ts"],"names":[],"mappings":"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"}
|