speccrew 0.5.7 → 0.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speccrew",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Spec-Driven Development toolkit for AI-powered IDEs",
5
5
  "author": "charlesmu99",
6
6
  "repository": {
@@ -225,7 +225,10 @@ function parseArgs() {
225
225
  description: null,
226
226
  startedAt: null,
227
227
  completedAt: null,
228
- confirmedAt: null
228
+ confirmedAt: null,
229
+ featuresDir: null,
230
+ platforms: null,
231
+ force: false
229
232
  };
230
233
 
231
234
  // 第一个参数是命令
@@ -314,6 +317,18 @@ function parseArgs() {
314
317
  case '-Overview':
315
318
  result.overview = true;
316
319
  break;
320
+ case '--features-dir':
321
+ case '-Features-Dir':
322
+ result.featuresDir = args[++i];
323
+ break;
324
+ case '--platforms':
325
+ case '-Platforms':
326
+ result.platforms = args[++i];
327
+ break;
328
+ case '--force':
329
+ case '-Force':
330
+ result.force = true;
331
+ break;
317
332
  }
318
333
  }
319
334
 
@@ -785,6 +800,154 @@ function cmdUpdateWorkflow(args) {
785
800
  }
786
801
  }
787
802
 
803
+ /**
804
+ * 命令:init-tasks - 扫描 feature-design 目录生成任务列表
805
+ */
806
+ function cmdInitTasks(args) {
807
+ // 参数验证
808
+ if (!args.file || !args.stage || !args.featuresDir || !args.platforms) {
809
+ outputError('Usage: init-tasks --file <path> --stage <stage_name> --features-dir <dir> --platforms <comma-separated> [--force]');
810
+ }
811
+
812
+ const filePath = path.resolve(args.file);
813
+ const featuresDir = path.resolve(args.featuresDir);
814
+ const platforms = args.platforms.split(',').map(p => p.trim()).filter(p => p);
815
+
816
+ // 验证 platforms 非空
817
+ if (platforms.length === 0) {
818
+ outputError('Platforms list cannot be empty');
819
+ }
820
+
821
+ // 验证 features-dir 存在
822
+ if (!fs.existsSync(featuresDir)) {
823
+ outputError(`Features directory not found: ${featuresDir}`);
824
+ }
825
+
826
+ // 扫描 .feature-spec.md 文件
827
+ const featureFiles = [];
828
+ const files = fs.readdirSync(featuresDir);
829
+ for (const file of files) {
830
+ if (file.endsWith('.feature-spec.md')) {
831
+ featureFiles.push(file);
832
+ }
833
+ }
834
+
835
+ if (featureFiles.length === 0) {
836
+ outputError(`No .feature-spec.md files found in: ${featuresDir}`);
837
+ }
838
+
839
+ // 从文件名提取 feature 信息
840
+ // 格式: F-{MODULE}-{NNN}-{feature-name}.feature-spec.md
841
+ const featurePattern = /^(F-([A-Z]+)-\d+)-(.+)\.feature-spec\.md$/;
842
+ const features = [];
843
+
844
+ for (const file of featureFiles) {
845
+ const match = file.match(featurePattern);
846
+ if (match) {
847
+ features.push({
848
+ feature_id: match[1], // F-APPT-001
849
+ module: match[2], // APPT
850
+ name: match[3], // 预约信息CRUD
851
+ file: file
852
+ });
853
+ }
854
+ }
855
+
856
+ if (features.length === 0) {
857
+ outputError('No valid feature files found. Expected format: F-{MODULE}-{NNN}-{feature-name}.feature-spec.md');
858
+ }
859
+
860
+ // 按 feature ID 排序
861
+ features.sort((a, b) => a.feature_id.localeCompare(b.feature_id));
862
+
863
+ // 检查目标文件是否已有 tasks
864
+ if (fs.existsSync(filePath)) {
865
+ const existingData = readJsonFile(filePath);
866
+ if (existingData.tasks && existingData.tasks.length > 0 && !args.force) {
867
+ outputError(`Progress file already has ${existingData.tasks.length} tasks. Use --force to overwrite.`);
868
+ }
869
+ }
870
+
871
+ // 生成任务列表
872
+ const tasks = [];
873
+ const now = getTimestamp();
874
+
875
+ // Module 排序顺序
876
+ const moduleOrder = ['APPT', 'BASE', 'CUST', 'EMP', 'ITEM', 'KNW', 'REPORT', 'REV', 'SERV'];
877
+ const getModuleIndex = (module) => {
878
+ const idx = moduleOrder.indexOf(module);
879
+ return idx === -1 ? 999 : idx;
880
+ };
881
+
882
+ // 按 module 分组
883
+ const featuresByModule = {};
884
+ for (const feature of features) {
885
+ if (!featuresByModule[feature.module]) {
886
+ featuresByModule[feature.module] = [];
887
+ }
888
+ featuresByModule[feature.module].push(feature);
889
+ }
890
+
891
+ // 每个 module 内按 feature ID 排序
892
+ for (const module of Object.keys(featuresByModule)) {
893
+ featuresByModule[module].sort((a, b) => a.feature_id.localeCompare(b.feature_id));
894
+ }
895
+
896
+ // 按 module 顺序生成任务
897
+ const sortedModules = Object.keys(featuresByModule).sort((a, b) => getModuleIndex(a) - getModuleIndex(b));
898
+
899
+ for (const module of sortedModules) {
900
+ for (const feature of featuresByModule[module]) {
901
+ for (const platform of platforms) {
902
+ tasks.push({
903
+ id: `sd-${platform}-${feature.feature_id}`,
904
+ name: `System Design - ${platform} - ${feature.feature_id} ${feature.name}`,
905
+ status: 'pending',
906
+ platform: platform,
907
+ feature_id: feature.feature_id,
908
+ module: feature.module,
909
+ created_at: now
910
+ });
911
+ }
912
+ }
913
+ }
914
+
915
+ // 创建进度文件结构
916
+ const progressData = {
917
+ stage: args.stage,
918
+ created_at: now,
919
+ updated_at: now,
920
+ counts: calculateCounts(tasks),
921
+ tasks: tasks,
922
+ checkpoints: {}
923
+ };
924
+
925
+ // 确保目录存在
926
+ const dir = path.dirname(filePath);
927
+ if (!fs.existsSync(dir)) {
928
+ fs.mkdirSync(dir, { recursive: true });
929
+ }
930
+
931
+ // 获取锁并写入
932
+ let lockPath = null;
933
+ try {
934
+ lockPath = acquireLock(filePath);
935
+ atomicWriteJson(filePath, progressData);
936
+ outputSuccess(
937
+ `Generated ${tasks.length} tasks from ${features.length} features × ${platforms.length} platforms`,
938
+ {
939
+ file: filePath,
940
+ stage: args.stage,
941
+ features_count: features.length,
942
+ platforms: platforms,
943
+ counts: progressData.counts
944
+ }
945
+ );
946
+ } finally {
947
+ if (lockPath) releaseLock(lockPath);
948
+ }
949
+ }
950
+
788
951
  // ============================================================================
789
952
  // 主入口
790
953
  // ============================================================================
@@ -803,6 +966,7 @@ function main() {
803
966
  console.error(' update-counts Recalculate task counts');
804
967
  console.error(' write-checkpoint Write or update a checkpoint');
805
968
  console.error(' update-workflow Update a workflow stage status');
969
+ console.error(' init-tasks Generate tasks from feature-spec files');
806
970
  console.error('');
807
971
  console.error('Run "node update-progress.js <command> --help" for more information.');
808
972
  process.exit(1);
@@ -829,6 +993,9 @@ function main() {
829
993
  case 'update-workflow':
830
994
  cmdUpdateWorkflow(args);
831
995
  break;
996
+ case 'init-tasks':
997
+ cmdInitTasks(args);
998
+ break;
832
999
  default:
833
1000
  outputError(`Unknown command: ${args.command}`);
834
1001
  }