specweave 0.23.2 → 0.23.5
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.md +367 -0
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts +133 -0
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js +224 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +52 -20
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js +2 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
- package/dist/src/core/ac-test-validator-cli.d.ts +16 -0
- package/dist/src/core/ac-test-validator-cli.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator-cli.js +118 -0
- package/dist/src/core/ac-test-validator-cli.js.map +1 -0
- package/dist/src/core/ac-test-validator.d.ts +111 -0
- package/dist/src/core/ac-test-validator.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator.js +292 -0
- package/dist/src/core/ac-test-validator.js.map +1 -0
- package/dist/src/core/increment/desync-detector.d.ts +142 -0
- package/dist/src/core/increment/desync-detector.d.ts.map +1 -0
- package/dist/src/core/increment/desync-detector.js +270 -0
- package/dist/src/core/increment/desync-detector.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +8 -4
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +45 -21
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/qa/qa-runner.js +9 -2
- package/dist/src/core/qa/qa-runner.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts +1 -1
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +40 -2
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/fs-native.d.ts +133 -0
- package/dist/src/utils/fs-native.d.ts.map +1 -0
- package/dist/src/utils/fs-native.js +224 -0
- package/dist/src/utils/fs-native.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +12 -0
- package/plugins/specweave/agents/AGENTS-INDEX.md +216 -0
- package/plugins/specweave/agents/architect/AGENT.md +17 -0
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +16 -0
- package/plugins/specweave/agents/docs-writer/AGENT.md +16 -0
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +704 -0
- package/plugins/specweave/agents/infrastructure/AGENT.md +16 -0
- package/plugins/specweave/agents/performance/AGENT.md +16 -0
- package/plugins/specweave/agents/pm/AGENT.md +17 -0
- package/plugins/specweave/agents/qa-lead/AGENT.md +15 -0
- package/plugins/specweave/agents/reflective-reviewer/AGENT.md +16 -0
- package/plugins/specweave/agents/security/AGENT.md +16 -0
- package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +16 -0
- package/plugins/specweave/agents/tech-lead/AGENT.md +16 -0
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +16 -0
- package/plugins/specweave/agents/translator/AGENT.md +13 -0
- package/plugins/specweave/commands/specweave-done.md +14 -0
- package/plugins/specweave/commands/specweave-qa.md +11 -1
- package/plugins/specweave/commands/specweave-sync-status.md +356 -0
- package/plugins/specweave/commands/specweave-validate.md +10 -1
- package/plugins/specweave/hooks/pre-task-completion.sh +196 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.js +3 -3
- package/plugins/specweave/lib/hooks/git-diff-analyzer.ts +3 -3
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js +3 -2
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +3 -2
- package/plugins/specweave/lib/hooks/prepare-reflection-context.js +3 -3
- package/plugins/specweave/lib/hooks/prepare-reflection-context.ts +3 -3
- package/plugins/specweave/lib/hooks/reflection-config-loader.js +4 -4
- package/plugins/specweave/lib/hooks/reflection-config-loader.ts +4 -4
- package/plugins/specweave/lib/hooks/reflection-storage.js +9 -9
- package/plugins/specweave/lib/hooks/reflection-storage.ts +9 -9
- package/plugins/specweave/lib/hooks/sync-cache.js +9 -8
- package/plugins/specweave/lib/hooks/sync-living-docs.js +57 -6
- package/plugins/specweave/lib/hooks/sync-us-tasks.js +6 -6
- package/plugins/specweave/lib/hooks/translate-file.js +3 -2
- package/plugins/specweave/lib/hooks/translate-file.ts +3 -2
- package/plugins/specweave/lib/hooks/translate-living-docs.js +4 -3
- package/plugins/specweave/lib/hooks/translate-living-docs.ts +4 -3
- package/plugins/specweave/lib/hooks/update-tasks-md.js +3 -3
- package/plugins/specweave/lib/hooks/update-tasks-md.ts +3 -3
- package/plugins/specweave/lib/utils/fs-native.js +182 -0
- package/plugins/specweave/lib/utils/fs-native.ts +283 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +8 -4
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +45 -21
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/SKILLS-INDEX.md +26 -2
- package/plugins/specweave/skills/increment-planner/SKILL.md +2 -2
- package/plugins/specweave-ado/commands/specweave-ado-close-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-create-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-status.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-sync.md +1 -1
- package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +1 -1
- package/plugins/specweave-diagrams/skills/diagrams-generator/SKILL.md +4 -4
- package/plugins/specweave-github/lib/github-client-v2.js +2 -1
- package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
- package/plugins/specweave-github/lib/github-feature-sync.js +30 -17
- package/plugins/specweave-github/lib/github-feature-sync.ts +54 -24
- package/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.ts +33 -0
- package/plugins/specweave-mobile/README.md +1 -1
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +72 -0
- package/src/templates/CLAUDE.md.template +13 -0
- package/plugins/specweave/skills/task-builder/README.md +0 -84
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from "fs
|
|
2
|
+
import { promises as fs, existsSync } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
|
+
import { mkdirpSync } from "../utils/fs-native.js";
|
|
5
6
|
async function syncLivingDocs(incrementId) {
|
|
6
7
|
try {
|
|
7
8
|
console.log(`
|
|
8
9
|
\u{1F4DA} Checking living docs sync for increment: ${incrementId}`);
|
|
10
|
+
// Load and validate config with error handling
|
|
9
11
|
const configPath = path.join(process.cwd(), ".specweave", "config.json");
|
|
10
12
|
let config = {};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (existsSync(configPath)) {
|
|
16
|
+
const rawContent = await fs.readFile(configPath, "utf-8");
|
|
17
|
+
config = JSON.parse(rawContent);
|
|
18
|
+
} else {
|
|
19
|
+
console.log("\u26A0\uFE0F No config.json found, using safe defaults (all permissions disabled)");
|
|
20
|
+
console.log(" To configure: Run 'specweave init' or create .specweave/config.json");
|
|
21
|
+
config = { sync: { settings: {} } };
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("\u274C Failed to load config.json:", error.message);
|
|
25
|
+
console.error(" File may be corrupted or contain invalid JSON");
|
|
26
|
+
console.error(" Using safe defaults (all permissions denied)");
|
|
27
|
+
console.error(" To fix: Check .specweave/config.json for syntax errors");
|
|
28
|
+
config = { sync: { settings: {} } };
|
|
13
29
|
}
|
|
14
30
|
const syncEnabled = config.hooks?.post_task_completion?.sync_living_docs ?? false;
|
|
15
31
|
if (!syncEnabled) {
|
|
@@ -18,6 +34,22 @@ async function syncLivingDocs(incrementId) {
|
|
|
18
34
|
return;
|
|
19
35
|
}
|
|
20
36
|
console.log("\u2705 Living docs sync enabled");
|
|
37
|
+
|
|
38
|
+
// ========================================================================
|
|
39
|
+
// GATE 1: canUpsertInternalItems (v0.24.0+ - Internal Docs Permission)
|
|
40
|
+
// ========================================================================
|
|
41
|
+
// This permission controls whether SpecWeave can CREATE/UPDATE internal docs.
|
|
42
|
+
// If false, ALL living docs sync is blocked (both local and external).
|
|
43
|
+
const canUpsertInternal = config.sync?.settings?.canUpsertInternalItems ?? false;
|
|
44
|
+
|
|
45
|
+
if (!canUpsertInternal) {
|
|
46
|
+
console.log("\u26D4 Living docs sync BLOCKED (canUpsertInternalItems = false)");
|
|
47
|
+
console.log(" To enable: Set sync.settings.canUpsertInternalItems = true in config.json");
|
|
48
|
+
console.log(" No internal docs or external tools will be updated");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log("\u2705 Internal docs sync permitted (canUpsertInternalItems = true)");
|
|
21
53
|
const intelligentEnabled = config.livingDocs?.intelligent?.enabled ?? false;
|
|
22
54
|
let specCopied = false;
|
|
23
55
|
let changedDocs = [];
|
|
@@ -54,6 +86,25 @@ async function syncLivingDocs(incrementId) {
|
|
|
54
86
|
return;
|
|
55
87
|
}
|
|
56
88
|
|
|
89
|
+
// ========================================================================
|
|
90
|
+
// GATE 3: autoSyncOnCompletion (v0.24.0+ - Automatic vs Manual Sync)
|
|
91
|
+
// ========================================================================
|
|
92
|
+
// This setting controls whether sync to external tools happens automatically
|
|
93
|
+
// on increment completion or requires manual /specweave:sync-* commands.
|
|
94
|
+
// DEFAULT: true (automatic sync enabled for better UX)
|
|
95
|
+
const autoSync = config.sync?.settings?.autoSyncOnCompletion ?? true;
|
|
96
|
+
|
|
97
|
+
if (!autoSync) {
|
|
98
|
+
console.log("\u26A0\uFE0F Automatic external sync DISABLED (autoSyncOnCompletion = false)");
|
|
99
|
+
console.log(" Living docs updated locally, but external tools NOT synced");
|
|
100
|
+
console.log(" To sync manually: Run /specweave-github:sync or /specweave-jira:sync");
|
|
101
|
+
console.log(" To enable auto-sync: Set sync.settings.autoSyncOnCompletion = true");
|
|
102
|
+
console.log("\u2705 Living docs sync complete (manual external sync required)\n");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log("\u2705 Automatic external sync permitted (autoSyncOnCompletion = true)");
|
|
107
|
+
|
|
57
108
|
// T-034E: Use FormatPreservationSyncService for origin-aware sync
|
|
58
109
|
await syncWithFormatPreservation(incrementId);
|
|
59
110
|
|
|
@@ -184,7 +235,7 @@ async function extractAndMergeLivingDocs(incrementId) {
|
|
|
184
235
|
} = await import("../../../../dist/src/utils/spec-parser.js");
|
|
185
236
|
const projectRoot = process.cwd();
|
|
186
237
|
const incrementSpecPath = path.join(projectRoot, ".specweave", "increments", incrementId, "spec.md");
|
|
187
|
-
if (!
|
|
238
|
+
if (!existsSync(incrementSpecPath)) {
|
|
188
239
|
console.log(`\u26A0\uFE0F Increment spec not found: ${incrementSpecPath}`);
|
|
189
240
|
return false;
|
|
190
241
|
}
|
|
@@ -198,7 +249,7 @@ async function extractAndMergeLivingDocs(incrementId) {
|
|
|
198
249
|
const specId = incrementSpec.implementsSpec || extractSpecId(incrementId);
|
|
199
250
|
const livingDocsDir = path.join(projectRoot, ".specweave", "docs", "internal", "specs", "default");
|
|
200
251
|
const livingDocsPath = path.join(livingDocsDir, `${specId}-${incrementId.replace(/^\d+-/, "")}.md`);
|
|
201
|
-
const livingDocsExists =
|
|
252
|
+
const livingDocsExists = existsSync(livingDocsPath);
|
|
202
253
|
if (livingDocsExists) {
|
|
203
254
|
console.log(` \u{1F4DA} Living docs spec exists, merging user stories...`);
|
|
204
255
|
const livingSpec = await parseLivingDocsSpec(livingDocsPath);
|
|
@@ -253,7 +304,7 @@ async function extractAndMergeLivingDocs(incrementId) {
|
|
|
253
304
|
created: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
254
305
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
255
306
|
};
|
|
256
|
-
|
|
307
|
+
mkdirpSync(livingDocsDir);
|
|
257
308
|
await writeLivingDocsSpec(livingDocsPath, livingSpec);
|
|
258
309
|
console.log(` \u2705 Created new living docs spec: ${specId}`);
|
|
259
310
|
console.log(` \u2705 Added ${incrementSpec.userStories.length} user stories`);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Part of increment 0047-us-task-linkage implementation.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import fs from 'fs
|
|
13
|
+
import { readFileSync, existsSync, promises as fs } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { parseTasksWithUSLinks, getAllTasks } from '../vendor/generators/spec/task-parser.js';
|
|
16
16
|
import { glob } from 'glob';
|
|
@@ -32,7 +32,7 @@ export async function syncUSTasksToLivingDocs(incrementId, projectRoot, featureI
|
|
|
32
32
|
const tasksPath = path.join(projectRoot, '.specweave', 'increments', incrementId, 'tasks.md');
|
|
33
33
|
|
|
34
34
|
// Check if tasks.md exists
|
|
35
|
-
if (!
|
|
35
|
+
if (!existsSync(tasksPath)) {
|
|
36
36
|
console.log(` ⚠️ tasks.md not found, skipping task sync`);
|
|
37
37
|
return { success: true, updatedFiles: [], errors: [] };
|
|
38
38
|
}
|
|
@@ -451,8 +451,8 @@ function getProjectId(projectRoot, incrementId) {
|
|
|
451
451
|
try {
|
|
452
452
|
// Try to get from metadata.json
|
|
453
453
|
const metadataPath = path.join(projectRoot, '.specweave', 'increments', incrementId, 'metadata.json');
|
|
454
|
-
if (
|
|
455
|
-
const metadata = JSON.parse(
|
|
454
|
+
if (existsSync(metadataPath)) {
|
|
455
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
|
|
456
456
|
if (metadata.project) {
|
|
457
457
|
return metadata.project;
|
|
458
458
|
}
|
|
@@ -460,8 +460,8 @@ function getProjectId(projectRoot, incrementId) {
|
|
|
460
460
|
|
|
461
461
|
// Try to get from config.json
|
|
462
462
|
const configPath = path.join(projectRoot, '.specweave', 'config.json');
|
|
463
|
-
if (
|
|
464
|
-
const config = JSON.parse(
|
|
463
|
+
if (existsSync(configPath)) {
|
|
464
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
465
465
|
if (config.defaultProject) {
|
|
466
466
|
return config.defaultProject;
|
|
467
467
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { promises as fs } from "fs";
|
|
2
3
|
import {
|
|
3
4
|
detectLanguage,
|
|
4
5
|
prepareTranslation,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
} from "../vendor/utils/translation.js";
|
|
10
11
|
async function translateFile(options) {
|
|
11
12
|
const { filePath, targetLang, preview, verbose } = options;
|
|
12
|
-
if (!
|
|
13
|
+
if (!existsSync(filePath)) {
|
|
13
14
|
throw new Error(`File not found: ${filePath}`);
|
|
14
15
|
}
|
|
15
16
|
if (verbose) {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* @see .specweave/increments/0006-llm-native-i18n/reports/DESIGN-POST-GENERATION-TRANSLATION.md
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import
|
|
19
|
+
import { existsSync } from 'fs';
|
|
20
|
+
import { promises as fs } from 'fs';
|
|
20
21
|
import path from 'path';
|
|
21
22
|
import {
|
|
22
23
|
detectLanguage,
|
|
@@ -62,7 +63,7 @@ export async function translateFile(options: CLIOptions): Promise<FileTranslatio
|
|
|
62
63
|
const { filePath, targetLang, preview, verbose } = options;
|
|
63
64
|
|
|
64
65
|
// 1. Validate file exists
|
|
65
|
-
if (!
|
|
66
|
+
if (!existsSync(filePath)) {
|
|
66
67
|
throw new Error(`File not found: ${filePath}`);
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
|
-
import
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { promises as fs } from "fs";
|
|
3
4
|
async function translateLivingDocs(incrementId) {
|
|
4
5
|
try {
|
|
5
6
|
const configPath = ".specweave/config.json";
|
|
6
|
-
if (!
|
|
7
|
+
if (!existsSync(configPath)) {
|
|
7
8
|
console.log("[translate-living-docs] No config found, skipping translation");
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
|
-
const config = await fs.
|
|
11
|
+
const config = JSON.parse(await fs.readFile(configPath, "utf-8"));
|
|
11
12
|
if (!config.language || config.language === "en") {
|
|
12
13
|
console.log("[translate-living-docs] Project language is English, skipping translation");
|
|
13
14
|
return;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { execSync } from 'child_process';
|
|
11
|
-
import
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { promises as fs } from 'fs';
|
|
12
13
|
import path from 'path';
|
|
13
14
|
|
|
14
15
|
interface Config {
|
|
@@ -27,12 +28,12 @@ export async function translateLivingDocs(incrementId: string): Promise<void> {
|
|
|
27
28
|
try {
|
|
28
29
|
// 1. Load config
|
|
29
30
|
const configPath = '.specweave/config.json';
|
|
30
|
-
if (!
|
|
31
|
+
if (!existsSync(configPath)) {
|
|
31
32
|
console.log('[translate-living-docs] No config found, skipping translation');
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const config: Config = await fs.
|
|
36
|
+
const config: Config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
|
|
36
37
|
|
|
37
38
|
// 2. Check if translation is enabled
|
|
38
39
|
if (!config.language || config.language === 'en') {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from "fs
|
|
2
|
+
import { existsSync, promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
async function updateTasksMd(incrementId) {
|
|
5
5
|
try {
|
|
6
6
|
console.log(`
|
|
7
7
|
\u{1F504} Updating tasks.md for increment: ${incrementId}`);
|
|
8
8
|
const incrementDir = path.join(process.cwd(), ".specweave", "increments", incrementId);
|
|
9
|
-
if (!
|
|
9
|
+
if (!existsSync(incrementDir)) {
|
|
10
10
|
console.error(`\u274C Increment directory not found: ${incrementDir}`);
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
const tasksPath = path.join(incrementDir, "tasks.md");
|
|
14
|
-
if (!
|
|
14
|
+
if (!existsSync(tasksPath)) {
|
|
15
15
|
console.error(`\u274C tasks.md not found: ${tasksPath}`);
|
|
16
16
|
process.exit(1);
|
|
17
17
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* @version 1.0.0
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import fs from 'fs
|
|
26
|
+
import { existsSync, promises as fs } from 'fs';
|
|
27
27
|
import path from 'path';
|
|
28
28
|
|
|
29
29
|
interface TaskMatch {
|
|
@@ -48,13 +48,13 @@ async function updateTasksMd(incrementId: string): Promise<void> {
|
|
|
48
48
|
|
|
49
49
|
// 1. Validate increment exists
|
|
50
50
|
const incrementDir = path.join(process.cwd(), '.specweave', 'increments', incrementId);
|
|
51
|
-
if (!
|
|
51
|
+
if (!existsSync(incrementDir)) {
|
|
52
52
|
console.error(`❌ Increment directory not found: ${incrementDir}`);
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const tasksPath = path.join(incrementDir, 'tasks.md');
|
|
57
|
-
if (!
|
|
57
|
+
if (!existsSync(tasksPath)) {
|
|
58
58
|
console.error(`❌ tasks.md not found: ${tasksPath}`);
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { promises as fsPromises, existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync, rmSync, unlinkSync, copyFileSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
async function ensureDir(dirPath) {
|
|
4
|
+
if (!existsSync(dirPath)) {
|
|
5
|
+
await fsPromises.mkdir(dirPath, { recursive: true });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function ensureDirSync(dirPath) {
|
|
9
|
+
if (!existsSync(dirPath)) {
|
|
10
|
+
mkdirSync(dirPath, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function mkdirpSync(dirPath) {
|
|
14
|
+
ensureDirSync(dirPath);
|
|
15
|
+
}
|
|
16
|
+
async function pathExists(filePath) {
|
|
17
|
+
return existsSync(filePath);
|
|
18
|
+
}
|
|
19
|
+
async function readJson(filePath) {
|
|
20
|
+
const content = await fsPromises.readFile(filePath, "utf-8");
|
|
21
|
+
return JSON.parse(content);
|
|
22
|
+
}
|
|
23
|
+
function readJsonSync(filePath) {
|
|
24
|
+
const content = readFileSync(filePath, "utf-8");
|
|
25
|
+
return JSON.parse(content);
|
|
26
|
+
}
|
|
27
|
+
async function writeJson(filePath, data, options) {
|
|
28
|
+
const spaces = options?.spaces ?? 2;
|
|
29
|
+
const content = JSON.stringify(data, null, spaces);
|
|
30
|
+
await fsPromises.writeFile(filePath, content, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
function writeJsonSync(filePath, data, options) {
|
|
33
|
+
const spaces = options?.spaces ?? 2;
|
|
34
|
+
const content = JSON.stringify(data, null, spaces);
|
|
35
|
+
writeFileSync(filePath, content, "utf-8");
|
|
36
|
+
}
|
|
37
|
+
async function remove(targetPath) {
|
|
38
|
+
if (existsSync(targetPath)) {
|
|
39
|
+
await fsPromises.rm(targetPath, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function removeSync(targetPath) {
|
|
43
|
+
if (existsSync(targetPath)) {
|
|
44
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function copy(src, dest, options) {
|
|
48
|
+
const srcStat = await fsPromises.stat(src);
|
|
49
|
+
if (srcStat.isFile()) {
|
|
50
|
+
await fsPromises.mkdir(path.dirname(dest), { recursive: true });
|
|
51
|
+
await fsPromises.copyFile(src, dest);
|
|
52
|
+
} else if (srcStat.isDirectory()) {
|
|
53
|
+
await fsPromises.mkdir(dest, { recursive: true });
|
|
54
|
+
const entries = await fsPromises.readdir(src, { withFileTypes: true });
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const srcPath = path.join(src, entry.name);
|
|
57
|
+
const destPath = path.join(dest, entry.name);
|
|
58
|
+
if (options?.filter && !options.filter(srcPath)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
await copy(srcPath, destPath, options);
|
|
63
|
+
} else {
|
|
64
|
+
await fsPromises.copyFile(srcPath, destPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function copySync(src, dest, options) {
|
|
70
|
+
const srcStat = statSync(src);
|
|
71
|
+
if (srcStat.isFile()) {
|
|
72
|
+
mkdirSync(path.dirname(dest), { recursive: true });
|
|
73
|
+
copyFileSync(src, dest);
|
|
74
|
+
} else if (srcStat.isDirectory()) {
|
|
75
|
+
mkdirSync(dest, { recursive: true });
|
|
76
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const srcPath = path.join(src, entry.name);
|
|
79
|
+
const destPath = path.join(dest, entry.name);
|
|
80
|
+
if (options?.filter && !options.filter(srcPath)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (entry.isDirectory()) {
|
|
84
|
+
copySync(srcPath, destPath, options);
|
|
85
|
+
} else {
|
|
86
|
+
copyFileSync(srcPath, destPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function ensureFile(filePath) {
|
|
92
|
+
if (!existsSync(filePath)) {
|
|
93
|
+
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
94
|
+
await fsPromises.writeFile(filePath, "", "utf-8");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function ensureFileSync(filePath) {
|
|
98
|
+
if (!existsSync(filePath)) {
|
|
99
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
100
|
+
writeFileSync(filePath, "", "utf-8");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const {
|
|
104
|
+
readFile,
|
|
105
|
+
writeFile,
|
|
106
|
+
appendFile,
|
|
107
|
+
stat,
|
|
108
|
+
readdir,
|
|
109
|
+
access,
|
|
110
|
+
unlink,
|
|
111
|
+
rmdir,
|
|
112
|
+
rename,
|
|
113
|
+
chmod
|
|
114
|
+
} = fsPromises;
|
|
115
|
+
var fs_native_default = {
|
|
116
|
+
// Async methods
|
|
117
|
+
ensureDir,
|
|
118
|
+
pathExists,
|
|
119
|
+
readJson,
|
|
120
|
+
writeJson,
|
|
121
|
+
remove,
|
|
122
|
+
copy,
|
|
123
|
+
ensureFile,
|
|
124
|
+
readFile,
|
|
125
|
+
writeFile,
|
|
126
|
+
appendFile,
|
|
127
|
+
stat,
|
|
128
|
+
readdir,
|
|
129
|
+
access,
|
|
130
|
+
unlink,
|
|
131
|
+
// Sync methods
|
|
132
|
+
ensureDirSync,
|
|
133
|
+
mkdirpSync,
|
|
134
|
+
existsSync,
|
|
135
|
+
readJsonSync,
|
|
136
|
+
writeJsonSync,
|
|
137
|
+
removeSync,
|
|
138
|
+
copySync,
|
|
139
|
+
ensureFileSync,
|
|
140
|
+
readFileSync,
|
|
141
|
+
writeFileSync,
|
|
142
|
+
statSync,
|
|
143
|
+
readdirSync,
|
|
144
|
+
unlinkSync,
|
|
145
|
+
mkdirSync,
|
|
146
|
+
rmSync
|
|
147
|
+
};
|
|
148
|
+
export {
|
|
149
|
+
access,
|
|
150
|
+
appendFile,
|
|
151
|
+
chmod,
|
|
152
|
+
copy,
|
|
153
|
+
copySync,
|
|
154
|
+
fs_native_default as default,
|
|
155
|
+
ensureDir,
|
|
156
|
+
ensureDirSync,
|
|
157
|
+
ensureFile,
|
|
158
|
+
ensureFileSync,
|
|
159
|
+
existsSync,
|
|
160
|
+
mkdirSync,
|
|
161
|
+
mkdirpSync,
|
|
162
|
+
pathExists,
|
|
163
|
+
readFile,
|
|
164
|
+
readFileSync,
|
|
165
|
+
readJson,
|
|
166
|
+
readJsonSync,
|
|
167
|
+
readdir,
|
|
168
|
+
readdirSync,
|
|
169
|
+
remove,
|
|
170
|
+
removeSync,
|
|
171
|
+
rename,
|
|
172
|
+
rmSync,
|
|
173
|
+
rmdir,
|
|
174
|
+
stat,
|
|
175
|
+
statSync,
|
|
176
|
+
unlink,
|
|
177
|
+
unlinkSync,
|
|
178
|
+
writeFile,
|
|
179
|
+
writeFileSync,
|
|
180
|
+
writeJson,
|
|
181
|
+
writeJsonSync
|
|
182
|
+
};
|