win-get-updates 0.0.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.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: 'GitHub Actions Expert'
3
+ description: 'GitHub Actions specialist focused on secure CI/CD workflows, action pinning, OIDC authentication, permissions least privilege, and supply-chain security'
4
+ tools: ['codebase', 'edit/editFiles', 'terminalCommand', 'search', 'githubRepo']
5
+ ---
6
+
7
+ # GitHub Actions Expert
8
+
9
+ You are a GitHub Actions specialist helping teams build secure, efficient, and reliable CI/CD workflows with emphasis on security hardening, supply-chain safety, and operational best practices.
10
+
11
+ ## Your Mission
12
+
13
+ Design and optimize GitHub Actions workflows that prioritize security-first practices, efficient resource usage, and reliable automation. Every workflow should follow least privilege principles, use immutable action references, and implement comprehensive security scanning.
14
+
15
+ ## Clarifying Questions Checklist
16
+
17
+ Before creating or modifying workflows:
18
+
19
+ ### Workflow Purpose & Scope
20
+
21
+ - Workflow type (CI, CD, security scanning, release management)
22
+ - Triggers (push, PR, schedule, manual) and target branches
23
+ - Target environments and cloud providers
24
+ - Approval requirements
25
+
26
+ ### Security & Compliance
27
+
28
+ - Security scanning needs (SAST, dependency review, container scanning)
29
+ - Compliance constraints (SOC2, HIPAA, PCI-DSS)
30
+ - Secret management and OIDC availability
31
+ - Supply chain security requirements (SBOM, signing)
32
+
33
+ ### Performance
34
+
35
+ - Expected duration and caching needs
36
+ - Self-hosted vs GitHub-hosted runners
37
+ - Concurrency requirements
38
+
39
+ ## Security-First Principles
40
+
41
+ **Permissions**:
42
+
43
+ - Default to `contents: read` at workflow level
44
+ - Override only at job level when needed
45
+ - Grant minimal necessary permissions
46
+
47
+ **Action Pinning**:
48
+
49
+ - Pin to specific versions for stability
50
+ - Use major version tags (`@v4`) for balance of security and maintenance
51
+ - Consider full commit SHA for maximum security (requires more maintenance)
52
+ - Never use `@main` or `@latest`
53
+
54
+ **Secrets**:
55
+
56
+ - Access via environment variables only
57
+ - Never log or expose in outputs
58
+ - Use environment-specific secrets for production
59
+ - Prefer OIDC over long-lived credentials
60
+
61
+ ## OIDC Authentication
62
+
63
+ Eliminate long-lived credentials:
64
+
65
+ - **AWS**: Configure IAM role with trust policy for GitHub OIDC provider
66
+ - **Azure**: Use workload identity federation
67
+ - **GCP**: Use workload identity provider
68
+ - Requires `id-token: write` permission
69
+
70
+ ## Concurrency Control
71
+
72
+ - Prevent concurrent deployments: `cancel-in-progress: false`
73
+ - Cancel outdated PR builds: `cancel-in-progress: true`
74
+ - Use `concurrency.group` to control parallel execution
75
+
76
+ ## Security Hardening
77
+
78
+ **Dependency Review**: Scan for vulnerable dependencies on PRs
79
+ **CodeQL Analysis**: SAST scanning on push, PR, and schedule
80
+ **Container Scanning**: Scan images with Trivy or similar
81
+ **SBOM Generation**: Create software bill of materials
82
+ **Secret Scanning**: Enable with push protection
83
+
84
+ ## Caching & Optimization
85
+
86
+ - Use built-in caching when available (setup-node, setup-python)
87
+ - Cache dependencies with `actions/cache`
88
+ - Use effective cache keys (hash of lock files)
89
+ - Implement restore-keys for fallback
90
+
91
+ ## Workflow Validation
92
+
93
+ - Use actionlint for workflow linting
94
+ - Validate YAML syntax
95
+ - Test in forks before enabling on main repo
96
+
97
+ ## Workflow Security Checklist
98
+
99
+ - [ ] Actions pinned to specific versions
100
+ - [ ] Permissions: least privilege (default `contents: read`)
101
+ - [ ] Secrets via environment variables only
102
+ - [ ] OIDC for cloud authentication
103
+ - [ ] Concurrency control configured
104
+ - [ ] Caching implemented
105
+ - [ ] Artifact retention set appropriately
106
+ - [ ] Dependency review on PRs
107
+ - [ ] Security scanning (CodeQL, container, dependencies)
108
+ - [ ] Workflow validated with actionlint
109
+ - [ ] Environment protection for production
110
+ - [ ] Branch protection rules enabled
111
+ - [ ] Secret scanning with push protection
112
+ - [ ] No hardcoded credentials
113
+ - [ ] Third-party actions from trusted sources
114
+
115
+ ## Best Practices Summary
116
+
117
+ 1. Pin actions to specific versions
118
+ 2. Use least privilege permissions
119
+ 3. Never log secrets
120
+ 4. Prefer OIDC for cloud access
121
+ 5. Implement concurrency control
122
+ 6. Cache dependencies
123
+ 7. Set artifact retention policies
124
+ 8. Scan for vulnerabilities
125
+ 9. Validate workflows before merging
126
+ 10. Use environment protection for production
127
+ 11. Enable secret scanning
128
+ 12. Generate SBOMs for transparency
129
+ 13. Audit third-party actions
130
+ 14. Keep actions updated with Dependabot
131
+ 15. Test in forks first
132
+
133
+ ## Important Reminders
134
+
135
+ - Default permissions should be read-only
136
+ - OIDC is preferred over static credentials
137
+ - Validate workflows with actionlint
138
+ - Never skip security scanning
139
+ - Monitor workflows for failures and anomalies
@@ -0,0 +1,29 @@
1
+ <!--- Inspired by Kent Beck - Augmented Coding: Beyond the Vibes Appendix 1 (https://tidyfirst.substack.com/p/augmented-coding-beyond-the-vibes) --->
2
+
3
+ # ROLE AND EXPERTISE
4
+
5
+ You are a senior software engineer who follows Martin Fowler's Clean Code and Clean Architecture principles. Your purpose is to guide development following these methodologies precisely.
6
+
7
+ # CORE DEVELOPMENT PRINCIPLES
8
+
9
+ - Write the simplest solution first.
10
+
11
+ - Implement the minimum code needed to fulfill the requirements.
12
+
13
+ - Maintain high code quality throughout development.
14
+
15
+ - Use meaningful names that describe what "it" is rather than what "it" does.
16
+
17
+ # CODE QUALITY STANDARDS
18
+
19
+ - Eliminate duplication ruthlessly
20
+
21
+ - Express intent clearly through naming and structure
22
+
23
+ - Make dependencies explicit
24
+
25
+ - Keep methods small and focused on a single responsibility
26
+
27
+ - Minimize state and side effects
28
+
29
+ - Use the simplest solution that could possibly work
@@ -0,0 +1,88 @@
1
+ # Copilot instructions for this repository (JavaScript CLI)
2
+
3
+ ## Ground rules
4
+
5
+ - Conversation will be in English (US) only.
6
+ - You are native in both English (US) and German (DE).
7
+ - Prefer brief answers and short acknowledgements.
8
+ - Just answer the question. Do not provide contextual information or overview lists or tables.
9
+ - Do not perform agentic actions until I explictly say "go".
10
+ - **ALWAYS** start replies with STARTER_CHARACTER + space (default: 🍀).
11
+ - Stack emojis when requested, don't replace.
12
+ - Be very honest. Tell me something I need to know even if I don't want to hear it.
13
+ - Don't flatter me. Give me honest feedback even if I don't want to hear it.
14
+ - Push back when something seems wrong - don't just agree with mistakes.
15
+ - Be proactive and flag issues before they become problems.
16
+ - Flag issues early, ask questions if unsure of direction instead of choosing randomly.
17
+ - No shortcuts or direction changes without permission. Ask with❓emoji when changing course.
18
+ - If you need to ask me a list of questions, ask one question at a time.
19
+ - When you show me a potential error or miss, start your response with❗️emoji.
20
+ - When refering to lines of previously mentioned code or examples then always show the line number for clarity.
21
+ - When refering to lines of code from context then always show the line number for clarity.
22
+
23
+ ## Target Environment
24
+
25
+ - Build a **command-line application** intended to run on **Windows**.
26
+ - Use **Node.js LTS** on Windows.
27
+ - Use **ESM** (`"type": "module"`). Do not introduce CommonJS (`require`, `module.exports`).
28
+ - Prefer Node built-ins over dependencies (e.g., `node:fs`, `node:path`, `node:child_process`, `node:readline`, `node:os`).
29
+
30
+ ### Windows + CMD conventions (preferred shell)
31
+
32
+ - Assume **CMD.EXE** as the primary shell environment for documentation and examples.
33
+ - When providing usage examples, use **CMD syntax** (e.g., `%VAR%` for env vars), not PowerShell syntax (e.g., `$env:VAR`).
34
+ - When spawning subprocesses:
35
+ - Prefer `spawn(command, args, { shell: false })`.
36
+ - Do **not** rely on PowerShell features or cmdlets.
37
+ - If a shell is explicitly required, use `cmd.exe /d /s /c` and document why; avoid `powershell.exe`.
38
+ - Avoid instructions that require PowerShell (execution policy changes, PS-only piping idioms, etc.).
39
+ - CLI interface expectations:
40
+ - Provide `--help` and a non-zero exit code on invalid usage.
41
+ - Use `stdout` for normal output and `stderr` for errors/warnings.
42
+ - Return meaningful exit codes (`0` success, `1` general failure, `2` usage/config errors).
43
+ - prefer flags and stdin piping over interactive prompts
44
+ - Performance/safety:
45
+ - Stream large files instead of loading them fully into memory.
46
+ - Treat all input (args, env vars, stdin, files) as untrusted; validate and sanitize.
47
+
48
+ ## Coding Standards
49
+
50
+ - Use modern JavaScript (ES2022+ features available in Node LTS).
51
+ - Prefer `const` / `let`; never use `var`.
52
+ - Prefer small, pure functions; avoid hidden side effects.
53
+ - Handle errors explicitly:
54
+ - Use `try/catch` around I/O boundaries.
55
+ - Provide actionable error messages (what failed + how to fix).
56
+ - Do not swallow exceptions; either handle or rethrow with context.
57
+ - Keep dependencies minimal; justify new deps in the PR/commit message.
58
+ - Security:
59
+ - Do not use `eval` or construct shell commands from untrusted input.
60
+ - When using `child_process`, prefer `spawn` with args array over `exec`.
61
+ - Formatting:
62
+ - Keep code consistently formatted (assume Prettier if present).
63
+ - Use early returns to reduce nesting.
64
+ - Prefer strict equality `===` / `!==`.
65
+
66
+ ## Structural Preferences
67
+
68
+ - Organize code so the CLI entrypoint is thin and delegates to library functions.
69
+ - Suggested layout (adjust to the repo’s existing structure):
70
+ - `src/cli.js` — argument parsing, help text, exit codes
71
+ - `src/index.js` — main orchestration (calls into modules)
72
+ - `src/lib/*.js` — core logic (testable, no direct process I/O)
73
+ - `src/utils/*.js` — small shared helpers (logging, validation, fs helpers)
74
+ - Keep all side effects (file system, process exit) near the edges (CLI layer).
75
+ - Provide a single `main(args, { stdin, stdout, stderr })`-style entry for testability when feasible.
76
+ - Avoid deep inheritance; prefer composition and plain objects.
77
+
78
+ ## Documentation & Commenting
79
+
80
+ - Prefer self-explanatory code and clear naming over heavy commenting.
81
+ - Add comments only when they explain **why** (tradeoffs, constraints), not **what**.
82
+ - Use JSDoc for public functions/modules and for non-obvious parameter/return shapes:
83
+ - Document parameters, return types, thrown errors, and side effects.
84
+ - CLI help text must be accurate and updated with every flag/behavior change.
85
+ - All examples in docs/help must use **CMD** conventions.
86
+ - Keep logs and errors user-focused:
87
+ - include actionable next steps
88
+ - avoid dumping stack traces unless in a `--debug` mode (if present)
@@ -0,0 +1,26 @@
1
+ # See https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: "npm"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "monthly"
8
+ open-pull-requests-limit: 5
9
+ commit-message:
10
+ prefix: "deps"
11
+ labels:
12
+ - "dependencies"
13
+ ignore:
14
+ - dependency-name: "eslint"
15
+ update-types: ["version-update:semver-major"]
16
+ - dependency-name: "prettier"
17
+ update-types: ["version-update:semver-major"]
18
+ - package-ecosystem: "github-actions"
19
+ directory: "/"
20
+ schedule:
21
+ interval: "monthly"
22
+ open-pull-requests-limit: 5
23
+ commit-message:
24
+ prefix: "ci"
25
+ labels:
26
+ - "ci"
@@ -0,0 +1,396 @@
1
+ ---
2
+ description: 'WGU code review instructions'
3
+ applyTo: '**'
4
+ excludeAgent: ['coding-agent']
5
+ ---
6
+
7
+ # WGU Code Review Instructions
8
+
9
+ Comprehensive code review guidelines for GitHub Copilot. These instructions follow best practices from prompt engineering and provide a structured approach to code quality, security, testing, and architecture review.
10
+
11
+ ## Review Language
12
+
13
+ When performing a code review, respond in **English**.
14
+
15
+ ## Review Priorities
16
+
17
+ When performing a code review, prioritize issues in the following order:
18
+
19
+ ### 🔴 CRITICAL (Block merge)
20
+
21
+ - **Security**: Vulnerabilities, exposed secrets, authentication/authorization issues
22
+ - **Correctness**: Logic errors, data corruption risks, race conditions
23
+ - **Breaking Changes**: API contract changes without versioning
24
+ - **Data Loss**: Risk of data loss or corruption
25
+
26
+ ### 🟡 IMPORTANT (Requires discussion)
27
+
28
+ - **Code Quality**: Severe violations of SOLID principles, excessive duplication
29
+ - **Test Coverage**: Missing tests for critical paths or new functionality
30
+ - **Performance**: Obvious performance bottlenecks (N+1 queries, memory leaks)
31
+ - **Architecture**: Significant deviations from established patterns
32
+
33
+ ### 🟢 SUGGESTION (Non-blocking improvements)
34
+
35
+ - **Readability**: Poor naming, complex logic that could be simplified
36
+ - **Optimization**: Performance improvements without functional impact
37
+ - **Best Practices**: Minor deviations from conventions
38
+ - **Documentation**: Missing or incomplete comments/documentation
39
+
40
+ ## General Review Principles
41
+
42
+ When performing a code review, follow these principles:
43
+
44
+ 0. **Hold your horses**: Just show. Do not actually change the code without having explicitly been told to.
45
+ 1. **Be specific**: Reference exact lines, files, and provide concrete examples
46
+ 2. **Provide context**: Explain WHY something is an issue and the potential impact
47
+ 3. **Suggest solutions**: Show corrected code when applicable, not just what's wrong
48
+ 4. **Be constructive**: Focus on improving the code, not criticizing the author
49
+ 5. **Recognize good practices**: Acknowledge well-written code and smart solutions
50
+ 6. **Be pragmatic**: Not every suggestion needs immediate implementation
51
+ 7. **Group related comments**: Avoid multiple comments about the same topic
52
+
53
+ ## Code Quality Standards
54
+
55
+ When performing a code review, check for:
56
+
57
+ ### Clean Code
58
+
59
+ - Descriptive and meaningful names for variables, functions, and classes
60
+ - Single Responsibility Principle: each function/class does one thing well
61
+ - DRY (Don't Repeat Yourself): no code duplication
62
+ - Functions should be small and focused (ideally < 20-30 lines)
63
+ - Avoid deeply nested code (max 3-4 levels)
64
+ - Avoid magic numbers and strings (use constants)
65
+ - Code should be self-documenting; comments only when necessary
66
+
67
+ #### Examples
68
+
69
+ ```javascript
70
+ // ❌ BAD: Poor naming and magic numbers
71
+ function calc(x, y) {
72
+ if (x > 100) return y * 0.15;
73
+ return y * 0.1;
74
+ }
75
+
76
+ // ✅ GOOD: Clear naming and constants
77
+ const PREMIUM_THRESHOLD = 100;
78
+ const PREMIUM_DISCOUNT_RATE = 0.15;
79
+ const STANDARD_DISCOUNT_RATE = 0.1;
80
+
81
+ function calculateDiscount(orderTotal, itemPrice) {
82
+ const isPremiumOrder = orderTotal > PREMIUM_THRESHOLD;
83
+ const discountRate = isPremiumOrder ? PREMIUM_DISCOUNT_RATE : STANDARD_DISCOUNT_RATE;
84
+ return itemPrice * discountRate;
85
+ }
86
+ ```
87
+
88
+ ### Error Handling
89
+
90
+ - Proper error handling at appropriate levels
91
+ - Meaningful error messages
92
+ - No silent failures or ignored exceptions
93
+ - Fail fast: validate inputs early
94
+ - Use appropriate error types/exceptions
95
+
96
+ #### Examples
97
+
98
+ ```python
99
+ # ❌ BAD: Silent failure and generic error
100
+ def process_user(user_id):
101
+ try:
102
+ user = db.get(user_id)
103
+ user.process()
104
+ except:
105
+ pass
106
+
107
+ # ✅ GOOD: Explicit error handling
108
+ def process_user(user_id):
109
+ if not user_id or user_id <= 0:
110
+ raise ValueError(f"Invalid user_id: {user_id}")
111
+
112
+ try:
113
+ user = db.get(user_id)
114
+ except UserNotFoundError:
115
+ raise UserNotFoundError(f"User {user_id} not found in database")
116
+ except DatabaseError as e:
117
+ raise ProcessingError(f"Failed to retrieve user {user_id}: {e}")
118
+
119
+ return user.process()
120
+ ```
121
+
122
+ ## Security Review
123
+
124
+ When performing a code review, check for security issues:
125
+
126
+ - **Sensitive Data**: No passwords, API keys, tokens, or PII in code or logs
127
+ - **Input Validation**: All user inputs are validated and sanitized
128
+ - **SQL Injection**: Use parameterized queries, never string concatenation
129
+ - **Authentication**: Proper authentication checks before accessing resources
130
+ - **Authorization**: Verify user has permission to perform action
131
+ - **Cryptography**: Use established libraries, never roll your own crypto
132
+ - **Dependency Security**: Check for known vulnerabilities in dependencies
133
+
134
+ ### Examples
135
+
136
+ ```java
137
+ // ❌ BAD: SQL injection vulnerability
138
+ String query = "SELECT * FROM users WHERE email = '" + email + "'";
139
+
140
+ // ✅ GOOD: Parameterized query
141
+ PreparedStatement stmt = conn.prepareStatement(
142
+ "SELECT * FROM users WHERE email = ?"
143
+ );
144
+ stmt.setString(1, email);
145
+ ```
146
+
147
+ ```javascript
148
+ // ❌ BAD: Exposed secret in code
149
+ const API_KEY = 'sk_live_abc123xyz789';
150
+
151
+ // ✅ GOOD: Use environment variables
152
+ const API_KEY = process.env.API_KEY;
153
+ ```
154
+
155
+ ## Testing Standards
156
+
157
+ When performing a code review, verify test quality:
158
+
159
+ - **Coverage**: Critical paths and new functionality must have tests
160
+ - **Test Names**: Descriptive names that explain what is being tested
161
+ - **Test Structure**: Clear Arrange-Act-Assert or Given-When-Then pattern
162
+ - **Independence**: Tests should not depend on each other or external state
163
+ - **Assertions**: Use specific assertions, avoid generic assertTrue/assertFalse
164
+ - **Edge Cases**: Test boundary conditions, null values, empty collections
165
+ - **Mock Appropriately**: Mock external dependencies, not domain logic
166
+
167
+ ### Examples
168
+
169
+ ```typescript
170
+ // ❌ BAD: Vague name and assertion
171
+ test('test1', () => {
172
+ const result = calc(5, 10);
173
+ expect(result).toBeTruthy();
174
+ });
175
+
176
+ // ✅ GOOD: Descriptive name and specific assertion
177
+ test('should calculate 10% discount for orders under $100', () => {
178
+ const orderTotal = 50;
179
+ const itemPrice = 20;
180
+
181
+ const discount = calculateDiscount(orderTotal, itemPrice);
182
+
183
+ expect(discount).toBe(2.0);
184
+ });
185
+ ```
186
+
187
+ ## Performance Considerations
188
+
189
+ When performing a code review, check for performance issues:
190
+
191
+ - **Database Queries**: Avoid N+1 queries, use proper indexing
192
+ - **Algorithms**: Appropriate time/space complexity for the use case
193
+ - **Caching**: Utilize caching for expensive or repeated operations
194
+ - **Resource Management**: Proper cleanup of connections, files, streams
195
+ - **Pagination**: Large result sets should be paginated
196
+ - **Lazy Loading**: Load data only when needed
197
+
198
+ ### Examples
199
+
200
+ ```python
201
+ # ❌ BAD: N+1 query problem
202
+ users = User.query.all()
203
+ for user in users:
204
+ orders = Order.query.filter_by(user_id=user.id).all() # N+1!
205
+
206
+ # ✅ GOOD: Use JOIN or eager loading
207
+ users = User.query.options(joinedload(User.orders)).all()
208
+ for user in users:
209
+ orders = user.orders
210
+ ```
211
+
212
+ ## Architecture and Design
213
+
214
+ When performing a code review, verify architectural principles:
215
+
216
+ - **Separation of Concerns**: Clear boundaries between layers/modules
217
+ - **Dependency Direction**: High-level modules don't depend on low-level details
218
+ - **Interface Segregation**: Prefer small, focused interfaces
219
+ - **Loose Coupling**: Components should be independently testable
220
+ - **High Cohesion**: Related functionality grouped together
221
+ - **Consistent Patterns**: Follow established patterns in the codebase
222
+
223
+ ## Documentation Standards
224
+
225
+ When performing a code review, check documentation:
226
+
227
+ - **API Documentation**: Public APIs must be documented (purpose, parameters, returns)
228
+ - **Complex Logic**: Non-obvious logic should have explanatory comments
229
+ - **README Updates**: Update README when adding features or changing setup
230
+ - **Breaking Changes**: Document any breaking changes clearly
231
+ - **Examples**: Provide usage examples for complex features
232
+
233
+ ## Comment Format Template
234
+
235
+ When performing a code review, use this format for comments:
236
+
237
+ ```markdown
238
+ **[PRIORITY] Category: Brief title**
239
+
240
+ Detailed description of the issue or suggestion.
241
+
242
+ **Why this matters:**
243
+ Explanation of the impact or reason for the suggestion.
244
+
245
+ **Suggested fix:**
246
+ [code example if applicable]
247
+
248
+ **Reference:** [link to relevant documentation or standard]
249
+ ```
250
+
251
+ ### Example Comments
252
+
253
+ #### Critical Issue
254
+
255
+ ````markdown
256
+ **🔴 CRITICAL - Security: SQL Injection Vulnerability**
257
+
258
+ The query on line 45 concatenates user input directly into the SQL string,
259
+ creating a SQL injection vulnerability.
260
+
261
+ **Why this matters:**
262
+ An attacker could manipulate the email parameter to execute arbitrary SQL commands,
263
+ potentially exposing or deleting all database data.
264
+
265
+ **Suggested fix:**
266
+
267
+ ```sql
268
+ -- Instead of:
269
+ query = "SELECT * FROM users WHERE email = '" + email + "'"
270
+
271
+ -- Use:
272
+ PreparedStatement stmt = conn.prepareStatement(
273
+ "SELECT * FROM users WHERE email = ?"
274
+ );
275
+ stmt.setString(1, email);
276
+ ```
277
+ ````
278
+
279
+ **Reference:** OWASP SQL Injection Prevention Cheat Sheet
280
+
281
+ #### Important Issue
282
+
283
+ ````markdown
284
+ **🟡 IMPORTANT - Testing: Missing test coverage for critical path**
285
+
286
+ The `processPayment()` function handles financial transactions but has no tests
287
+ for the refund scenario.
288
+
289
+ **Why this matters:**
290
+ Refunds involve money movement and should be thoroughly tested to prevent
291
+ financial errors or data inconsistencies.
292
+
293
+ **Suggested fix:**
294
+ Add test case:
295
+
296
+ ```javascript
297
+ test('should process full refund when order is cancelled', () => {
298
+ const order = createOrder({ total: 100, status: 'cancelled' });
299
+
300
+ const result = processPayment(order, { type: 'refund' });
301
+
302
+ expect(result.refundAmount).toBe(100);
303
+ expect(result.status).toBe('refunded');
304
+ });
305
+ ```
306
+ ````
307
+
308
+ #### Suggestion
309
+
310
+ ````markdown
311
+ **🟢 SUGGESTION - Readability: Simplify nested conditionals**
312
+
313
+ The nested if statements on lines 30-40 make the logic hard to follow.
314
+
315
+ **Why this matters:**
316
+ Simpler code is easier to maintain, debug, and test.
317
+
318
+ **Suggested fix:**
319
+
320
+ ```javascript
321
+ // Instead of nested ifs:
322
+ if (user) {
323
+ if (user.isActive) {
324
+ if (user.hasPermission('write')) {
325
+ // do something
326
+ }
327
+ }
328
+ }
329
+
330
+ // Consider guard clauses:
331
+ if (!user || !user.isActive || !user.hasPermission('write')) {
332
+ return;
333
+ }
334
+ // do something
335
+ ```
336
+ ````
337
+
338
+ ## Review Checklist
339
+
340
+ When performing a code review, systematically verify:
341
+
342
+ ### Code Quality
343
+
344
+ - [ ] Code follows consistent style and conventions
345
+ - [ ] Names are descriptive and follow naming conventions
346
+ - [ ] Functions/methods are small and focused
347
+ - [ ] No code duplication
348
+ - [ ] Complex logic is broken into simpler parts
349
+ - [ ] Error handling is appropriate
350
+ - [ ] No commented-out code or TODO without tickets
351
+
352
+ ### Security
353
+
354
+ - [ ] No sensitive data in code or logs
355
+ - [ ] Input validation on all user inputs
356
+ - [ ] No SQL injection vulnerabilities
357
+ - [ ] Authentication and authorization properly implemented
358
+ - [ ] Dependencies are up-to-date and secure
359
+
360
+ ### Testing
361
+
362
+ - [ ] New code has appropriate test coverage
363
+ - [ ] Tests are well-named and focused
364
+ - [ ] Tests cover edge cases and error scenarios
365
+ - [ ] Tests are independent and deterministic
366
+ - [ ] No tests that always pass or are commented out
367
+
368
+ ### Performance
369
+
370
+ - [ ] No obvious performance issues (N+1, memory leaks)
371
+ - [ ] Appropriate use of caching
372
+ - [ ] Efficient algorithms and data structures
373
+ - [ ] Proper resource cleanup
374
+
375
+ ### Architecture
376
+
377
+ - [ ] Follows established patterns and conventions
378
+ - [ ] Proper separation of concerns
379
+ - [ ] No architectural violations
380
+ - [ ] Dependencies flow in correct direction
381
+
382
+ ### Documentation
383
+
384
+ - [ ] Public APIs are documented
385
+ - [ ] Complex logic has explanatory comments
386
+ - [ ] README is updated if needed
387
+ - [ ] Breaking changes are documented
388
+
389
+ ## Project Context
390
+
391
+ This is a generic template. Customize this section with your project-specific information:
392
+
393
+ - **Tech Stack**: JavaScript, Node.js
394
+ - **Architecture**: ESM
395
+ - **Build Tool**: npm
396
+ - **Testing**: none yet
@@ -0,0 +1,25 @@
1
+ name: Lint and Format
2
+ on:
3
+ push:
4
+ branches: ['**']
5
+ pull_request:
6
+ branches: ['**']
7
+ permissions:
8
+ contents: read
9
+ jobs:
10
+ lint-and-format:
11
+ name: Lint and Format
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v4
16
+ - name: Set up Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 'lts/*'
20
+ - name: Install dependencies
21
+ run: npm ci
22
+ - name: Run ESLint
23
+ run: npm run lint
24
+ - name: Run Prettier check
25
+ run: npm run format
package/.prettierrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "tabWidth": 2,
6
+ "printWidth": 160,
7
+ "arrowParens": "always"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "editor.formatOnSave": false,
3
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
4
+ "editor.codeActionsOnSave": {
5
+ "source.formatDocument": "explicit",
6
+ "source.fixAll.eslint": "explicit"
7
+ }
8
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jan Mosig
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # win-get-updates
2
+
3
+ Third party CLI frontend for [winget](https://en.wikipedia.org/wiki/Windows_Package_Manager) update runs. It lets you interactively choose which package to install.
4
+
5
+ ## Requirements
6
+
7
+ Windows with [winget](https://en.wikipedia.org/wiki/Windows_Package_Manager) installed. Supposed to run on cmd.exe.
8
+
9
+ ## Run
10
+
11
+ ```
12
+ node src\cli.js
13
+ ```
14
+
15
+ ## Update dependencies
16
+
17
+ - Run once:
18
+
19
+ ```
20
+ npm install -g npm-check-updates
21
+ ```
22
+
23
+ - Run every time:
24
+
25
+ ```
26
+ ncu -c 3 --peer -ui
27
+ ```
@@ -0,0 +1,69 @@
1
+ import js from '@eslint/js';
2
+ import prettierConfig from 'eslint-config-prettier';
3
+
4
+ export default [
5
+ js.configs.recommended,
6
+ prettierConfig,
7
+ {
8
+ languageOptions: {
9
+ ecmaVersion: 2022,
10
+ sourceType: 'module',
11
+ globals: {
12
+ console: 'readonly',
13
+ process: 'readonly',
14
+ Buffer: 'readonly',
15
+ URL: 'readonly',
16
+ URLSearchParams: 'readonly',
17
+ },
18
+ },
19
+ rules: {
20
+ // Error prevention
21
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
22
+ 'no-console': 'off', // CLI apps need console
23
+ 'no-await-in-loop': 'warn',
24
+ 'no-promise-executor-return': 'error',
25
+ 'no-unreachable-loop': 'error',
26
+ 'require-atomic-updates': 'error',
27
+
28
+ // Best practices
29
+ eqeqeq: ['error', 'always'],
30
+ 'no-eval': 'error',
31
+ 'no-implied-eval': 'error',
32
+ 'no-var': 'error',
33
+ 'prefer-const': 'error',
34
+ 'prefer-arrow-callback': 'warn',
35
+ 'no-param-reassign': 'warn',
36
+ 'no-return-await': 'error',
37
+ 'prefer-promise-reject-errors': 'error',
38
+
39
+ // Node.js specific
40
+ 'no-process-exit': 'off', // CLI apps need process.exit
41
+ 'no-path-concat': 'error', // Use path.join instead of __dirname + '/'
42
+
43
+ // Code quality
44
+ curly: ['error', 'all'],
45
+ 'default-case-last': 'error',
46
+ 'dot-notation': 'error',
47
+ 'no-else-return': 'warn',
48
+ 'no-lonely-if': 'warn',
49
+ 'no-useless-return': 'warn',
50
+ 'prefer-template': 'warn',
51
+ yoda: 'error',
52
+
53
+ // Stylistic choices
54
+ indent: ['error', 2],
55
+ 'linebreak-style': ['error', 'unix'],
56
+ 'array-element-newline': [
57
+ 'error',
58
+ {
59
+ ArrayExpression: { minItems: 3 },
60
+ ArrayPattern: { minItems: 3 },
61
+ },
62
+ ],
63
+ 'array-bracket-newline': ['error', { minItems: 3 }],
64
+ },
65
+ },
66
+ {
67
+ ignores: ['node_modules/**', 'dist/**', 'coverage/**', '*.config.js'],
68
+ },
69
+ ];
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "win-get-updates",
3
+ "version": "0.0.1",
4
+ "description": "Third party CLI frontend for winget update runs.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/JanMosigItemis/wgu.git"
8
+ },
9
+ "homepage": "https://github.com/JanMosigItemis/wgu",
10
+ "bugs": {
11
+ "url": "https://github.com/JanMosigItemis/wgu/issues"
12
+ },
13
+ "type": "module",
14
+ "main": "src/index.js",
15
+ "bin": {
16
+ "wgu": "src/cli.js"
17
+ },
18
+ "scripts": {
19
+ "start": "node src/cli.js",
20
+ "version:gen": "node -p \"'const WGU_VERSION = \"' + require('./package.json').version + '\";\\nexport default WGU_VERSION;'\" > src/lib/version.js",
21
+ "lint": "eslint .",
22
+ "lint:fix": "eslint . --fix",
23
+ "format": "prettier --check .",
24
+ "format:fix": "prettier --write ."
25
+ },
26
+ "keywords": [
27
+ "winget",
28
+ "update",
29
+ "windows",
30
+ "cli"
31
+ ],
32
+ "author": "Jan Mosig",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18.0.0",
36
+ "npm": ">=11.5.1"
37
+ },
38
+ "devDependencies": {
39
+ "@eslint/js": "9.39.2",
40
+ "eslint": "9.39.2",
41
+ "eslint-config-prettier": "10.1.8",
42
+ "prettier": "3.8.0"
43
+ }
44
+ }
package/src/cli.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from './index.js';
4
+ import WGU_VERSION from './lib/version.js';
5
+
6
+ const HELP_TEXT = `
7
+ wgu - Winget update on steroids
8
+
9
+ USAGE:
10
+ wgu [options]
11
+
12
+ OPTIONS:
13
+ --help, -h Show this help message
14
+ --version, -v Show version
15
+
16
+ DESCRIPTION:
17
+ Interactive CLI for managing Windows package updates via winget.
18
+ Provides a menu-driven interface to select and update packages.
19
+
20
+ EXAMPLES:
21
+ wgu Run the interactive updater
22
+ wgu --help Display help
23
+ wgu --version Show version
24
+ `;
25
+
26
+ /**
27
+ * Parse command line arguments
28
+ * @param {string[]} args - Command line arguments
29
+ * @returns {{ help: boolean, version: boolean, error: string | null }}
30
+ */
31
+ function parseArgs(args) {
32
+ const result = { help: false, version: false, error: null };
33
+
34
+ for (const arg of args) {
35
+ if (arg === '--help' || arg === '-h') {
36
+ result.help = true;
37
+ } else if (arg === '--version' || arg === '-v') {
38
+ result.version = true;
39
+ } else {
40
+ result.error = `Unknown option: ${arg}`;
41
+ return result;
42
+ }
43
+ }
44
+
45
+ return result;
46
+ }
47
+
48
+ /**
49
+ * CLI entry point
50
+ */
51
+ async function cli() {
52
+ const args = process.argv.slice(2);
53
+ const parsed = parseArgs(args);
54
+
55
+ if (parsed.error) {
56
+ console.error(`Error: ${parsed.error}`);
57
+ console.error('Use --help for usage information');
58
+ process.exit(2);
59
+ }
60
+
61
+ if (parsed.help) {
62
+ console.log(HELP_TEXT);
63
+ process.exit(0);
64
+ }
65
+
66
+ if (parsed.version) {
67
+ console.log(`wgu v${WGU_VERSION}`);
68
+ process.exit(0);
69
+ }
70
+
71
+ const exitCode = await main();
72
+ process.exit(exitCode);
73
+ }
74
+
75
+ cli().catch((err) => {
76
+ console.error(`Unexpected error: ${err.message}`);
77
+ process.exit(1);
78
+ });
package/src/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { getUpdateCandidateIds, runUpdates } from './lib/winget.js';
2
+ import { interactiveSelect } from './lib/menu.js';
3
+ import WGU_VERSION from './lib/version.js';
4
+ import { askPermissionToContinue } from './lib/console_commons.js';
5
+ import { assertWindows, assertWingetAvailable } from './lib/os.js';
6
+
7
+ /**
8
+ * Main application logic
9
+ * @param {Object} options - Configuration options
10
+ * @param {NodeJS.WriteStream} options.stdout - Output stream
11
+ * @param {NodeJS.WriteStream} options.stderr - Error stream
12
+ * @returns {Promise<number>} Exit code
13
+ */
14
+ export async function main({ stdout = process.stdout, stderr = process.stderr } = {}) {
15
+ assertWindows();
16
+ assertWingetAvailable();
17
+
18
+ try {
19
+ // Set cursor to underline
20
+ stdout.write('\x1b[4 q');
21
+
22
+ // Restore default cursor on exit
23
+ const restoreCursor = () => {
24
+ stdout.write('\x1b[6 q');
25
+ };
26
+ process.on('exit', restoreCursor);
27
+ process.on('SIGINT', () => {
28
+ restoreCursor();
29
+ process.exit(0);
30
+ });
31
+
32
+ console.log(`This is WGU v${WGU_VERSION}`);
33
+ console.log('');
34
+
35
+ console.log('Retrieving list of updatable packages..');
36
+ const candidateIds = getUpdateCandidateIds();
37
+
38
+ if (candidateIds.length === 0) {
39
+ console.log('No packages available to update.');
40
+ return 0;
41
+ }
42
+
43
+ const selectedIds = await interactiveSelect(candidateIds);
44
+
45
+ if (selectedIds.length === 0) {
46
+ console.log('Exiting without performing updates.');
47
+ return 0;
48
+ }
49
+
50
+ console.log('Ok, running updates..');
51
+ await runUpdates(selectedIds, async (id, err) => {
52
+ console.error(`Failed to update package ${id}: ${err.message}`);
53
+ return askPermissionToContinue();
54
+ });
55
+
56
+ return 0;
57
+ } catch (err) {
58
+ stderr.write(`Error: ${err.message}\n`);
59
+ return 1;
60
+ }
61
+ }
@@ -0,0 +1,40 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { stdin as input, stdout as output } from 'node:process';
3
+
4
+ export const CARRIAGE_RETURN = '\r';
5
+ export const MOVE_RIGHT = '\x1b[1C';
6
+ export const MOVE_UP = (lines) => `\x1b[${lines}A`;
7
+ export const MOVE_DOWN = (lines) => `\x1b[${lines}B`;
8
+
9
+ /**
10
+ * Move cursor to start of line (after checkbox position)
11
+ */
12
+ export function moveCursorToStartOfLine() {
13
+ process.stdout.write(CARRIAGE_RETURN);
14
+ process.stdout.write(MOVE_RIGHT);
15
+ }
16
+
17
+ /**
18
+ * Move the cursor to the specified index (0-based)
19
+ * @param {number} current - Current index
20
+ * @param {number} target - Target index
21
+ */
22
+ export function moveCursor(current, target) {
23
+ if (target < current) {
24
+ process.stdout.write(MOVE_UP(current - target));
25
+ } else if (target > current) {
26
+ process.stdout.write(MOVE_DOWN(target - current));
27
+ }
28
+ moveCursorToStartOfLine();
29
+ }
30
+
31
+ export async function askPermissionToContinue() {
32
+ const rl = createInterface({ input, output });
33
+
34
+ const answer = await rl.question('Do you want to continue? (y/n): ');
35
+ const userChoice = answer.toLowerCase() === 'y';
36
+
37
+ rl.close();
38
+
39
+ return userChoice;
40
+ }
@@ -0,0 +1,104 @@
1
+ import * as readline from 'node:readline';
2
+ import { moveCursorToStartOfLine, moveCursor, MOVE_UP, CARRIAGE_RETURN } from './console_commons.js';
3
+
4
+ const EXPLANATORY_LINE_COUNT = 2;
5
+
6
+ /**
7
+ * Displays an interactive menu for selecting items from a list
8
+ * @param {string[]} items - List of items to display
9
+ * @returns {Promise<string[]>} Selected items, or empty array if user quits
10
+ */
11
+ export async function interactiveSelect(items) {
12
+ if (!items || items.length === 0) {
13
+ return [];
14
+ }
15
+
16
+ return new Promise((resolve, _reject) => {
17
+ const selectedLines = new Map();
18
+ let activeLine = 0;
19
+
20
+ // Display initial menu
21
+ for (const item of items) {
22
+ console.log(`[ ] ${item}`);
23
+ }
24
+ console.log('');
25
+ console.log("Use Up/Down arrows to navigate, Space to toggle selection, 'y' to confirm, 'q' to quit.");
26
+
27
+ // Move cursor up to the first item
28
+ process.stdout.write(MOVE_UP(items.length + EXPLANATORY_LINE_COUNT));
29
+ moveCursorToStartOfLine();
30
+
31
+ // Set up raw mode for keypress detection
32
+ if (process.stdin.isTTY) {
33
+ process.stdin.setRawMode(true);
34
+ }
35
+ readline.emitKeypressEvents(process.stdin);
36
+
37
+ const onKeypress = (str, key) => {
38
+ // Handle Ctrl+C and Ctrl+D
39
+ if (key && key.ctrl && key.name === 'c') {
40
+ cleanup();
41
+ process.exit(1);
42
+ }
43
+
44
+ if (key && key.ctrl && key.name === 'd') {
45
+ cleanup();
46
+ resolve([]);
47
+ return;
48
+ }
49
+
50
+ // Handle arrow keys
51
+ if (key && key.name === 'up') {
52
+ if (activeLine > 0) {
53
+ moveCursor(activeLine, activeLine - 1);
54
+ activeLine--;
55
+ }
56
+ } else if (key && key.name === 'down') {
57
+ if (activeLine < items.length - 1) {
58
+ moveCursor(activeLine, activeLine + 1);
59
+ activeLine++;
60
+ }
61
+ } else if (str === ' ') {
62
+ // Toggle selection
63
+ if (selectedLines.has(activeLine)) {
64
+ selectedLines.delete(activeLine);
65
+ process.stdout.write(`${CARRIAGE_RETURN}[ ]`);
66
+ } else {
67
+ selectedLines.set(activeLine, true);
68
+ process.stdout.write(`${CARRIAGE_RETURN}[x]`);
69
+ }
70
+ moveCursorToStartOfLine();
71
+ } else if (str === 'y' || str === 'Y') {
72
+ // Confirm selection
73
+ cleanup();
74
+
75
+ // Move to one line below the end of the list
76
+ moveCursor(activeLine, items.length + EXPLANATORY_LINE_COUNT);
77
+ process.stdout.write(CARRIAGE_RETURN);
78
+
79
+ // Sort selected line numbers and get corresponding items
80
+ const selectedIndices = Array.from(selectedLines.keys()).sort((a, b) => a - b);
81
+ const selectedItems = selectedIndices.map((idx) => items[idx]);
82
+ resolve(selectedItems);
83
+ } else if (str === 'q' || str === 'Q' || (key && key.name === 'escape')) {
84
+ // Quit without selection
85
+ cleanup();
86
+
87
+ // Move to one line below the end of the list
88
+ moveCursor(activeLine, items.length + EXPLANATORY_LINE_COUNT);
89
+ process.stdout.write(CARRIAGE_RETURN);
90
+
91
+ resolve([]);
92
+ }
93
+ };
94
+
95
+ const cleanup = () => {
96
+ process.stdin.removeListener('keypress', onKeypress);
97
+ if (process.stdin.isTTY) {
98
+ process.stdin.setRawMode(false);
99
+ }
100
+ };
101
+
102
+ process.stdin.on('keypress', onKeypress);
103
+ });
104
+ }
package/src/lib/os.js ADDED
@@ -0,0 +1,18 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ export function assertWindows() {
4
+ if (process.platform !== 'win32') {
5
+ throw new Error('This application can only be run on Windows.');
6
+ }
7
+ }
8
+
9
+ /**
10
+ * Throws an error if winget is not available in PATH.
11
+ * @throws {Error} If winget is not found or not executable.
12
+ */
13
+ export function assertWingetAvailable() {
14
+ const result = spawnSync('winget', ['--version'], { encoding: 'utf8' });
15
+ if (result.error || result.status !== 0) {
16
+ throw new Error('winget is not available. Please install winget and ensure it is in your PATH.');
17
+ }
18
+ }
@@ -0,0 +1,3 @@
1
+ const WGU_VERSION = '0.0.1';
2
+
3
+ export default WGU_VERSION;
@@ -0,0 +1,116 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ /**
4
+ * Retrieves a list of package IDs that can be updated via winget
5
+ * @returns {string[]} Array of package IDs
6
+ * @throws {Error} If winget command fails or returns empty output
7
+ */
8
+ export function getUpdateCandidateIds() {
9
+ const NAME_COL = 'Name';
10
+ const ID_COL = 'ID';
11
+ const VERSION_COL = 'Version';
12
+ const tableHeaderRegex = new RegExp(`.*${NAME_COL}\\s+${ID_COL}\\s+${VERSION_COL}.*`);
13
+
14
+ // Get winget upgrade list
15
+ const wgOutput = execWinget(['upgrade', '--include-unknown']);
16
+
17
+ if (!wgOutput || wgOutput.trim().length === 0) {
18
+ throw new Error('winget update command failed or returned empty output');
19
+ }
20
+
21
+ // Remove last 2 lines (summary info + newline)
22
+ const lines = wgOutput
23
+ .split('\n')
24
+ .slice(0, -2)
25
+ .map((line) => line.trim());
26
+
27
+ const outputLineCount = lines.length;
28
+ const tableHeaderIndex = lines.findIndex((line) => line.match(tableHeaderRegex) !== null);
29
+ if (tableHeaderIndex === -1) {
30
+ throw new Error('Could not find table header in winget output');
31
+ }
32
+ // Find column positions
33
+ const namePos = lines[tableHeaderIndex].indexOf(NAME_COL);
34
+ const idPos = lines[tableHeaderIndex].indexOf(ID_COL);
35
+ const versionPos = lines[tableHeaderIndex].indexOf(VERSION_COL);
36
+ if (namePos === -1 || idPos === -1 || versionPos === -1) {
37
+ throw new Error('Could not find expected column headers in winget output');
38
+ }
39
+
40
+ // Calculate column positions relative to start of each line
41
+ const packageIdStartPos = idPos - namePos;
42
+ const packageIdColLength = versionPos - idPos;
43
+
44
+ const packageListStartLineIndex = tableHeaderIndex + 2; // Skip header and separator line
45
+ const packageIds = [];
46
+
47
+ // Extract package IDs from each line
48
+ for (let i = packageListStartLineIndex; i < outputLineCount; i++) {
49
+ const line = lines[i];
50
+ if (!line || line.trim().length === 0) {
51
+ continue;
52
+ }
53
+
54
+ const packageId = line.substring(packageIdStartPos, packageIdStartPos + packageIdColLength).trim();
55
+
56
+ if (packageId) {
57
+ packageIds.push(packageId);
58
+ }
59
+ }
60
+
61
+ return packageIds;
62
+ }
63
+
64
+ /**
65
+ * Runs winget update commands for the provided package IDs
66
+ * @param {string[]} ids - Package IDs to update
67
+ * @returns {Promise<void>}
68
+ */
69
+ export async function runUpdates(ids, errorHandler = async (_id, _err) => false) {
70
+ for (const id of ids) {
71
+ console.log(`Updating package: ${id}`);
72
+ // prettier-ignore
73
+ try {
74
+ execWinget([
75
+ 'upgrade',
76
+ '-i',
77
+ '--id',
78
+ id,
79
+ '--accept-source-agreements',
80
+ '--accept-package-agreements'
81
+ ], { inheritStdio: true });
82
+ console.log(`Package ${id} updated successfully.`);
83
+ } catch (err) {
84
+ // eslint-disable-next-line no-await-in-loop
85
+ if (!(await errorHandler(id, err))) {
86
+ throw err;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Synchronously executes winget command and returns stdout
94
+ * @param {string[]} args - Command arguments
95
+ * @param {Object} options - Execution options
96
+ * @param {boolean} options.inheritStdio - Whether to inherit stdio
97
+ * @returns {string} Command stdout
98
+ * @throws {Error} If command fails
99
+ */
100
+ function execWinget(args, { inheritStdio = false } = {}) {
101
+ const stdio = inheritStdio ? 'inherit' : 'pipe';
102
+ const child = spawnSync('winget', args, { shell: false, stdio, encoding: 'utf8' });
103
+
104
+ const stdout = child.stdout || '';
105
+ const stderr = child.stderr || '';
106
+
107
+ if (child.error) {
108
+ throw new Error(`Failed to execute winget: ${child.error.message}`);
109
+ }
110
+
111
+ if (child.status !== 0) {
112
+ throw new Error(`winget exited with code ${child.status}${stderr ? `: ${stderr}` : ''}`);
113
+ }
114
+
115
+ return stdout;
116
+ }