@hasna/project 0.1.13 → 0.1.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+EzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8rB9D"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoIzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6rB9D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=projects.json-output.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.json-output.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/projects.json-output.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=projects.limit-validation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.limit-validation.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/projects.limit-validation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=projects.not-found-hints.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.not-found-hints.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/projects.not-found-hints.test.ts"],"names":[],"mappings":""}
package/dist/cli/index.js CHANGED
@@ -46409,6 +46409,55 @@ function suppressSslWarnings() {
46409
46409
  return orig(msg, ...args);
46410
46410
  };
46411
46411
  }
46412
+ function wantsJsonOutput(opts) {
46413
+ return Boolean(opts?.json || process.env["PROJECTS_JSON"]);
46414
+ }
46415
+ function parsePositiveIntOrExit(raw, flagName, fallback) {
46416
+ if (raw === undefined)
46417
+ return fallback;
46418
+ const value = Number.parseInt(raw, 10);
46419
+ if (!Number.isInteger(value) || value < 1) {
46420
+ console.error(source_default.red(`Invalid value for ${flagName}: ${raw}. Expected a positive integer.`));
46421
+ process.exit(1);
46422
+ }
46423
+ return value;
46424
+ }
46425
+ function exitProjectNotFound(idOrSlug) {
46426
+ console.error(source_default.red(`Project not found: ${idOrSlug}`));
46427
+ const query = idOrSlug.toLowerCase();
46428
+ const candidates = [
46429
+ ...listProjects({ status: "active", limit: 500 }),
46430
+ ...listProjects({ status: "archived", limit: 500 })
46431
+ ];
46432
+ const seen = new Set;
46433
+ const matches = [];
46434
+ for (const project of candidates) {
46435
+ if (seen.has(project.id))
46436
+ continue;
46437
+ seen.add(project.id);
46438
+ const haystack = `${project.slug} ${project.name}`.toLowerCase();
46439
+ if (haystack.includes(query) || project.id.startsWith(idOrSlug)) {
46440
+ matches.push(project);
46441
+ }
46442
+ if (matches.length >= 5)
46443
+ break;
46444
+ }
46445
+ if (matches.length) {
46446
+ console.error(source_default.dim("Did you mean:"));
46447
+ for (const project of matches) {
46448
+ console.error(source_default.dim(` - ${project.slug} (${project.id})`));
46449
+ }
46450
+ } else {
46451
+ console.error(source_default.dim("Hint: run `project list --limit 20` to see available project IDs/slugs."));
46452
+ }
46453
+ process.exit(1);
46454
+ }
46455
+ function resolveProjectOrExit(idOrSlug) {
46456
+ const project = resolveProject(idOrSlug);
46457
+ if (!project)
46458
+ exitProjectNotFound(idOrSlug);
46459
+ return project;
46460
+ }
46412
46461
  function requireProject(idOrSlug) {
46413
46462
  if (idOrSlug)
46414
46463
  return resolveProject(idOrSlug);
@@ -46437,7 +46486,7 @@ function printProject(p2) {
46437
46486
  }
46438
46487
  function registerProjectCommands(program2) {
46439
46488
  const cmd = program2;
46440
- cmd.command("create").description("Register a new project").requiredOption("--name <name>", "Project name").option("--path <path>", "Project path (default: cwd)").option("--slug <slug>", "Custom slug (auto-generated from name if omitted)").option("--description <desc>", "Description").option("--tags <tags>", "Comma-separated tags").option("--s3-bucket <bucket>", "S3 bucket for sync").option("--s3-prefix <prefix>", "S3 prefix").option("--git-remote <remote>", "Git remote URL").option("--no-git-init", "Skip auto git init").action((opts) => {
46489
+ cmd.command("create").description("Register a new project").requiredOption("--name <name>", "Project name").option("--path <path>", "Project path (default: cwd)").option("--slug <slug>", "Custom slug (auto-generated from name if omitted)").option("--description <desc>", "Description").option("--tags <tags>", "Comma-separated tags").option("--s3-bucket <bucket>", "S3 bucket for sync").option("--s3-prefix <prefix>", "S3 prefix").option("--git-remote <remote>", "Git remote URL").option("--no-git-init", "Skip auto git init").option("-j, --json", "Output raw JSON").action((opts) => {
46441
46490
  try {
46442
46491
  const path = opts.path ? resolve4(opts.path) : process.cwd();
46443
46492
  const project = createProject({
@@ -46451,6 +46500,10 @@ function registerProjectCommands(program2) {
46451
46500
  git_remote: opts.gitRemote,
46452
46501
  git_init: opts.gitInit !== false
46453
46502
  });
46503
+ if (wantsJsonOutput(opts)) {
46504
+ console.log(JSON.stringify(project, null, 2));
46505
+ return;
46506
+ }
46454
46507
  console.log(source_default.green("\u2713 Project created"));
46455
46508
  printProject(project);
46456
46509
  if (!existsSync13(project.path)) {
@@ -46465,7 +46518,7 @@ function registerProjectCommands(program2) {
46465
46518
  cmd.command("list").description("List projects").option("--status <status>", "Filter by status (active|archived)").option("--tags <tags>", "Filter by tags (comma-separated)").option("--limit <n>", "Max results", "50").option("--json", "Output raw JSON").action((opts) => {
46466
46519
  const filter = {
46467
46520
  status: opts.status,
46468
- limit: parseInt(opts.limit, 10),
46521
+ limit: parsePositiveIntOrExit(opts.limit, "--limit", 50),
46469
46522
  tags: opts.tags ? opts.tags.split(",").map((t8) => t8.trim()) : undefined
46470
46523
  };
46471
46524
  const projects = listProjects(filter);
@@ -46489,7 +46542,9 @@ ${projects.length} project(s)`));
46489
46542
  cmd.command("get [id-or-slug]").description("Get project details (auto-detects from cwd if no arg given)").option("--json", "Output raw JSON").action((idOrSlug, opts) => {
46490
46543
  const project = requireProject(idOrSlug);
46491
46544
  if (!project) {
46492
- console.error(source_default.red(idOrSlug ? `Project not found: ${idOrSlug}` : "No project detected in current directory. Pass a project ID or slug."));
46545
+ if (idOrSlug)
46546
+ exitProjectNotFound(idOrSlug);
46547
+ console.error(source_default.red("No project detected in current directory. Pass a project ID or slug."));
46493
46548
  process.exit(1);
46494
46549
  }
46495
46550
  if (opts?.json || process.env["PROJECTS_JSON"]) {
@@ -46498,12 +46553,8 @@ ${projects.length} project(s)`));
46498
46553
  }
46499
46554
  printProject(project);
46500
46555
  });
46501
- cmd.command("rename <id-or-slug> <new-name>").description("Rename a project and update slug + .project.json in all workdirs").action((idOrSlug, newName) => {
46502
- const project = resolveProject(idOrSlug);
46503
- if (!project) {
46504
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46505
- process.exit(1);
46506
- }
46556
+ cmd.command("rename <id-or-slug> <new-name>").description("Rename a project and update slug + .project.json in all workdirs").option("-j, --json", "Output raw JSON").action((idOrSlug, newName, opts) => {
46557
+ const project = resolveProjectOrExit(idOrSlug);
46507
46558
  const updated = updateProject(project.id, { name: newName });
46508
46559
  const workdirs = listWorkdirs(project.id);
46509
46560
  for (const w2 of workdirs) {
@@ -46516,34 +46567,34 @@ ${projects.length} project(s)`));
46516
46567
  }
46517
46568
  } catch {}
46518
46569
  }
46570
+ if (wantsJsonOutput(opts)) {
46571
+ console.log(JSON.stringify(updated, null, 2));
46572
+ return;
46573
+ }
46519
46574
  console.log(source_default.green(`\u2713 Renamed: ${project.name} \u2192 ${updated.name} (slug: ${updated.slug})`));
46520
46575
  });
46521
- cmd.command("tag <id-or-slug> [tags...]").description("Add tags to a project").action((idOrSlug, tags) => {
46522
- const project = resolveProject(idOrSlug);
46523
- if (!project) {
46524
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46525
- process.exit(1);
46526
- }
46576
+ cmd.command("tag <id-or-slug> [tags...]").description("Add tags to a project").option("-j, --json", "Output raw JSON").action((idOrSlug, tags, opts) => {
46577
+ const project = resolveProjectOrExit(idOrSlug);
46527
46578
  const merged = [...new Set([...project.tags, ...tags])];
46528
- updateProject(project.id, { tags: merged });
46579
+ const updated = updateProject(project.id, { tags: merged });
46580
+ if (wantsJsonOutput(opts)) {
46581
+ console.log(JSON.stringify(updated, null, 2));
46582
+ return;
46583
+ }
46529
46584
  console.log(source_default.green(`\u2713 Tags: ${merged.join(", ")}`));
46530
46585
  });
46531
- cmd.command("untag <id-or-slug> [tags...]").description("Remove tags from a project").action((idOrSlug, tags) => {
46532
- const project = resolveProject(idOrSlug);
46533
- if (!project) {
46534
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46535
- process.exit(1);
46536
- }
46586
+ cmd.command("untag <id-or-slug> [tags...]").description("Remove tags from a project").option("-j, --json", "Output raw JSON").action((idOrSlug, tags, opts) => {
46587
+ const project = resolveProjectOrExit(idOrSlug);
46537
46588
  const remaining = project.tags.filter((t8) => !tags.includes(t8));
46538
- updateProject(project.id, { tags: remaining });
46589
+ const updated = updateProject(project.id, { tags: remaining });
46590
+ if (wantsJsonOutput(opts)) {
46591
+ console.log(JSON.stringify(updated, null, 2));
46592
+ return;
46593
+ }
46539
46594
  console.log(source_default.green(`\u2713 Tags: ${remaining.length ? remaining.join(", ") : "(none)"}`));
46540
46595
  });
46541
- cmd.command("update <id-or-slug>").description("Update a project").option("--name <name>", "New name").option("--description <desc>", "New description").option("--path <path>", "New path").option("--tags <tags>", "New tags (comma-separated, replaces existing)").option("--s3-bucket <bucket>", "S3 bucket").option("--s3-prefix <prefix>", "S3 prefix").option("--git-remote <remote>", "Git remote URL").action((idOrSlug, opts) => {
46542
- const project = resolveProject(idOrSlug);
46543
- if (!project) {
46544
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46545
- process.exit(1);
46546
- }
46596
+ cmd.command("update <id-or-slug>").description("Update a project").option("--name <name>", "New name").option("--description <desc>", "New description").option("--path <path>", "New path").option("--tags <tags>", "New tags (comma-separated, replaces existing)").option("--s3-bucket <bucket>", "S3 bucket").option("--s3-prefix <prefix>", "S3 prefix").option("--git-remote <remote>", "Git remote URL").option("-j, --json", "Output raw JSON").action((idOrSlug, opts) => {
46597
+ const project = resolveProjectOrExit(idOrSlug);
46547
46598
  const updated = updateProject(project.id, {
46548
46599
  name: opts.name,
46549
46600
  description: opts.description,
@@ -46553,25 +46604,29 @@ ${projects.length} project(s)`));
46553
46604
  s3_prefix: opts.s3Prefix,
46554
46605
  git_remote: opts.gitRemote
46555
46606
  });
46607
+ if (wantsJsonOutput(opts)) {
46608
+ console.log(JSON.stringify(updated, null, 2));
46609
+ return;
46610
+ }
46556
46611
  console.log(source_default.green("\u2713 Project updated"));
46557
46612
  printProject(updated);
46558
46613
  });
46559
- cmd.command("archive <id-or-slug>").description("Archive a project").action((idOrSlug) => {
46560
- const project = resolveProject(idOrSlug);
46561
- if (!project) {
46562
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46563
- process.exit(1);
46614
+ cmd.command("archive <id-or-slug>").description("Archive a project").option("-j, --json", "Output raw JSON").action((idOrSlug, opts) => {
46615
+ const project = resolveProjectOrExit(idOrSlug);
46616
+ const archived = archiveProject(project.id);
46617
+ if (wantsJsonOutput(opts)) {
46618
+ console.log(JSON.stringify(archived, null, 2));
46619
+ return;
46564
46620
  }
46565
- archiveProject(project.id);
46566
46621
  console.log(source_default.yellow(`\u2713 Archived: ${project.name}`));
46567
46622
  });
46568
- cmd.command("unarchive <id-or-slug>").description("Unarchive a project").action((idOrSlug) => {
46569
- const project = resolveProject(idOrSlug);
46570
- if (!project) {
46571
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46572
- process.exit(1);
46623
+ cmd.command("unarchive <id-or-slug>").description("Unarchive a project").option("-j, --json", "Output raw JSON").action((idOrSlug, opts) => {
46624
+ const project = resolveProjectOrExit(idOrSlug);
46625
+ const unarchived = unarchiveProject(project.id);
46626
+ if (wantsJsonOutput(opts)) {
46627
+ console.log(JSON.stringify(unarchived, null, 2));
46628
+ return;
46573
46629
  }
46574
- unarchiveProject(project.id);
46575
46630
  console.log(source_default.green(`\u2713 Unarchived: ${project.name}`));
46576
46631
  });
46577
46632
  cmd.command("open [id-or-slug]").description("Print the path of a project (for use with cd, auto-detects from cwd)").action((idOrSlug) => {
@@ -46588,7 +46643,7 @@ ${projects.length} project(s)`));
46588
46643
  console.log(project.path);
46589
46644
  });
46590
46645
  cmd.command("recent").description("List recently opened projects").option("--limit <n>", "Max results", "10").option("--json", "Output raw JSON").action((opts) => {
46591
- const projects = getRecentProjects(parseInt(opts.limit, 10));
46646
+ const projects = getRecentProjects(parsePositiveIntOrExit(opts.limit, "--limit", 10));
46592
46647
  if (opts.json || process.env["PROJECTS_JSON"]) {
46593
46648
  console.log(JSON.stringify(projects, null, 2));
46594
46649
  return;
@@ -46605,11 +46660,7 @@ ${projects.length} project(s)`));
46605
46660
  });
46606
46661
  cmd.command("status [id-or-slug]").description("Show project health at a glance").option("--json", "Output raw JSON").action(async (idOrSlug, opts) => {
46607
46662
  if (idOrSlug) {
46608
- const project = resolveProject(idOrSlug);
46609
- if (!project) {
46610
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46611
- process.exit(1);
46612
- }
46663
+ const project = resolveProjectOrExit(idOrSlug);
46613
46664
  const s2 = getProjectStatus(project);
46614
46665
  if (opts?.json || process.env["PROJECTS_JSON"]) {
46615
46666
  console.log(JSON.stringify(s2, null, 2));
@@ -46642,10 +46693,8 @@ ${projects.length} project(s)`));
46642
46693
  });
46643
46694
  cmd.command("doctor [id-or-slug]").description("Health-check all registered projects").option("--fix", "Auto-repair what can be fixed").option("--json", "Output raw JSON").action(async (idOrSlug, opts) => {
46644
46695
  const target = idOrSlug ? resolveProject(idOrSlug) : null;
46645
- if (idOrSlug && !target) {
46646
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46647
- process.exit(1);
46648
- }
46696
+ if (idOrSlug && !target)
46697
+ exitProjectNotFound(idOrSlug);
46649
46698
  const results = target ? [await doctorProject(target)] : await doctorAll();
46650
46699
  if (opts?.json || process.env["PROJECTS_JSON"]) {
46651
46700
  console.log(JSON.stringify(results, null, 2));
@@ -46671,11 +46720,7 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46671
46720
  });
46672
46721
  cmd.command("stats [id-or-slug]").description("Show storage and sync statistics").option("--json", "Output raw JSON").action((idOrSlug, opts) => {
46673
46722
  if (idOrSlug) {
46674
- const project = resolveProject(idOrSlug);
46675
- if (!project) {
46676
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46677
- process.exit(1);
46678
- }
46723
+ const project = resolveProjectOrExit(idOrSlug);
46679
46724
  const s2 = getProjectStats(project.id);
46680
46725
  if (!s2) {
46681
46726
  console.error(source_default.red("Stats not available"));
@@ -46719,11 +46764,7 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46719
46764
  }
46720
46765
  });
46721
46766
  cmd.command("clone <id-or-slug> [target-path]").description("Pull a project from S3 to a new local path and register as workdir").option("--region <region>", "AWS region").option("--label <label>", "Workdir label", "clone").action(async (idOrSlug, targetPath, opts) => {
46722
- const project = resolveProject(idOrSlug);
46723
- if (!project) {
46724
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46725
- process.exit(1);
46726
- }
46767
+ const project = resolveProjectOrExit(idOrSlug);
46727
46768
  const dest = resolve4(targetPath ?? join14(process.cwd(), project.slug));
46728
46769
  console.log(source_default.dim(`Cloning ${project.name} \u2192 ${dest}...`));
46729
46770
  try {
@@ -46753,7 +46794,9 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46753
46794
  cmd.command("sync [id-or-slug]").description("Sync project files to/from S3 (auto-detects from cwd)").option("--direction <dir>", "push, pull, or both (default: both)", "both").option("--dry-run", "Show what would be synced without doing it").option("--region <region>", "AWS region").action(async (idOrSlug, opts) => {
46754
46795
  const project = requireProject(idOrSlug);
46755
46796
  if (!project) {
46756
- console.error(source_default.red(idOrSlug ? `Project not found: ${idOrSlug}` : "No project detected in current directory."));
46797
+ if (idOrSlug)
46798
+ exitProjectNotFound(idOrSlug);
46799
+ console.error(source_default.red("No project detected in current directory."));
46757
46800
  process.exit(1);
46758
46801
  }
46759
46802
  try {
@@ -46812,11 +46855,7 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46812
46855
  });
46813
46856
  const workdirCmd = cmd.command("workdir").description("Manage working directories for a project");
46814
46857
  workdirCmd.command("add <id-or-slug> <path>").description("Add a working directory to a project").option("--label <label>", "Label for this directory (e.g. frontend, backend)", "main").option("--primary", "Set as the primary working directory").option("--generate", "Generate CLAUDE.md + AGENTS.md immediately").action((idOrSlug, path, opts) => {
46815
- const project = resolveProject(idOrSlug);
46816
- if (!project) {
46817
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46818
- process.exit(1);
46819
- }
46858
+ const project = resolveProjectOrExit(idOrSlug);
46820
46859
  const absPath = resolve4(path);
46821
46860
  const workdir = addWorkdir({ project_id: project.id, path: absPath, label: opts.label, is_primary: opts.primary });
46822
46861
  console.log(source_default.green(`\u2713 Added workdir: ${workdir.label} \u2192 ${workdir.path}`));
@@ -46827,13 +46866,13 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46827
46866
  console.log(source_default.dim(` AGENTS.md \u2192 ${result.path}/AGENTS.md`));
46828
46867
  }
46829
46868
  });
46830
- workdirCmd.command("list <id-or-slug>").description("List all working directories for a project").action((idOrSlug) => {
46831
- const project = resolveProject(idOrSlug);
46832
- if (!project) {
46833
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46834
- process.exit(1);
46835
- }
46869
+ workdirCmd.command("list <id-or-slug>").description("List all working directories for a project").option("-j, --json", "Output raw JSON").action((idOrSlug, opts) => {
46870
+ const project = resolveProjectOrExit(idOrSlug);
46836
46871
  const workdirs = listWorkdirs(project.id);
46872
+ if (wantsJsonOutput(opts)) {
46873
+ console.log(JSON.stringify(workdirs, null, 2));
46874
+ return;
46875
+ }
46837
46876
  if (!workdirs.length) {
46838
46877
  console.log(source_default.dim("No workdirs registered."));
46839
46878
  return;
@@ -46846,20 +46885,12 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46846
46885
  }
46847
46886
  });
46848
46887
  workdirCmd.command("remove <id-or-slug> <path>").description("Remove a working directory from a project").action((idOrSlug, path) => {
46849
- const project = resolveProject(idOrSlug);
46850
- if (!project) {
46851
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46852
- process.exit(1);
46853
- }
46888
+ const project = resolveProjectOrExit(idOrSlug);
46854
46889
  removeWorkdir(project.id, resolve4(path));
46855
46890
  console.log(source_default.yellow(`\u2713 Removed workdir: ${path}`));
46856
46891
  });
46857
46892
  workdirCmd.command("generate <id-or-slug>").description("Generate CLAUDE.md + AGENTS.md in all working directories").option("--path <path>", "Only generate for a specific workdir path").option("--dry-run", "Show what would be generated without writing").option("--force", "Overwrite existing CLAUDE.md even if not generated by open-projects").action((idOrSlug, opts) => {
46858
- const project = resolveProject(idOrSlug);
46859
- if (!project) {
46860
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46861
- process.exit(1);
46862
- }
46893
+ const project = resolveProjectOrExit(idOrSlug);
46863
46894
  const allDirs = listWorkdirs(project.id);
46864
46895
  if (opts.path) {
46865
46896
  const absPath = resolve4(opts.path);
@@ -46880,11 +46911,7 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46880
46911
  }
46881
46912
  });
46882
46913
  cmd.command("publish <id-or-slug>").description("Publish project to GitHub").option("--org <org>", "GitHub org (default: hasnaxyz)").option("--public", "Make repo public (default: private)").action((idOrSlug, opts) => {
46883
- const project = resolveProject(idOrSlug);
46884
- if (!project) {
46885
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46886
- process.exit(1);
46887
- }
46914
+ const project = resolveProjectOrExit(idOrSlug);
46888
46915
  try {
46889
46916
  const result = publishProject(project.name, project.path, {
46890
46917
  org: opts.org,
@@ -46900,20 +46927,22 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46900
46927
  }
46901
46928
  });
46902
46929
  cmd.command("unpublish <id-or-slug>").description("Remove GitHub remote from project (does not delete the repo)").action((idOrSlug) => {
46903
- const project = resolveProject(idOrSlug);
46904
- if (!project) {
46905
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46906
- process.exit(1);
46907
- }
46930
+ const project = resolveProjectOrExit(idOrSlug);
46908
46931
  unpublishProject(project.path);
46909
46932
  console.log(source_default.yellow(`\u2713 Removed origin remote from ${project.name}`));
46910
46933
  });
46911
- cmd.command("import <path>").description("Import an existing directory as a project").option("--tags <tags>", "Tags (comma-separated)").option("--dry-run", "Show what would be imported without doing it").action(async (path, opts) => {
46934
+ cmd.command("import <path>").description("Import an existing directory as a project").option("--tags <tags>", "Tags (comma-separated)").option("--dry-run", "Show what would be imported without doing it").option("-j, --json", "Output raw JSON").action(async (path, opts) => {
46912
46935
  const { project, skipped, error } = await importProject(path, {
46913
46936
  dryRun: opts.dryRun,
46914
46937
  defaultTags: opts.tags ? opts.tags.split(",").map((t8) => t8.trim()) : [],
46915
46938
  onProgress: (msg) => console.log(source_default.dim(` ${msg}`))
46916
46939
  });
46940
+ if (wantsJsonOutput(opts)) {
46941
+ console.log(JSON.stringify({ project: project ?? null, skipped: skipped ?? null, error: error ?? null }, null, 2));
46942
+ if (error)
46943
+ process.exit(1);
46944
+ return;
46945
+ }
46917
46946
  if (error) {
46918
46947
  console.error(source_default.red(`Error: ${error}`));
46919
46948
  process.exit(1);
@@ -46927,12 +46956,16 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46927
46956
  printProject(project);
46928
46957
  }
46929
46958
  });
46930
- cmd.command("import-bulk <dir>").description("Import all subdirectories of a directory as projects").option("--tags <tags>", "Tags to apply to all imported projects (comma-separated)").option("--dry-run", "Show what would be imported without doing it").action(async (dir, opts) => {
46959
+ cmd.command("import-bulk <dir>").description("Import all subdirectories of a directory as projects").option("--tags <tags>", "Tags to apply to all imported projects (comma-separated)").option("--dry-run", "Show what would be imported without doing it").option("-j, --json", "Output raw JSON").action(async (dir, opts) => {
46931
46960
  const result = await importBulk(dir, {
46932
46961
  dryRun: opts.dryRun,
46933
46962
  defaultTags: opts.tags ? opts.tags.split(",").map((t8) => t8.trim()) : [],
46934
46963
  onProgress: (msg) => console.log(source_default.dim(` ${msg}`))
46935
46964
  });
46965
+ if (wantsJsonOutput(opts)) {
46966
+ console.log(JSON.stringify(result, null, 2));
46967
+ return;
46968
+ }
46936
46969
  console.log(source_default.green(`\u2713 imported: ${result.imported.length}`));
46937
46970
  if (result.skipped.length)
46938
46971
  console.log(source_default.dim(` skipped: ${result.skipped.length}`));
@@ -46942,11 +46975,7 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
46942
46975
  }
46943
46976
  });
46944
46977
  cmd.command("git <id-or-slug> [git-args...]").description("Run a git command inside the project directory").allowUnknownOption().action((idOrSlug, gitArgs) => {
46945
- const project = resolveProject(idOrSlug);
46946
- if (!project) {
46947
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
46948
- process.exit(1);
46949
- }
46978
+ const project = resolveProjectOrExit(idOrSlug);
46950
46979
  try {
46951
46980
  gitPassthrough(project.path, gitArgs);
46952
46981
  } catch (err) {
@@ -47025,13 +47054,13 @@ ${source_default.bold(r2.project.name)} ${source_default.dim(r2.project.slug)}`)
47025
47054
  process.exit(1);
47026
47055
  }
47027
47056
  });
47028
- cmd.command("sync-log <id-or-slug>").description("Show sync history for a project").option("--limit <n>", "Max entries", "10").action((idOrSlug, opts) => {
47029
- const project = resolveProject(idOrSlug);
47030
- if (!project) {
47031
- console.error(source_default.red(`Project not found: ${idOrSlug}`));
47032
- process.exit(1);
47057
+ cmd.command("sync-log <id-or-slug>").description("Show sync history for a project").option("--limit <n>", "Max entries", "10").option("-j, --json", "Output raw JSON").action((idOrSlug, opts) => {
47058
+ const project = resolveProjectOrExit(idOrSlug);
47059
+ const logs = listSyncLogs(project.id, parsePositiveIntOrExit(opts?.limit, "--limit", 10));
47060
+ if (wantsJsonOutput(opts)) {
47061
+ console.log(JSON.stringify(logs, null, 2));
47062
+ return;
47033
47063
  }
47034
- const logs = listSyncLogs(project.id, parseInt(opts.limit, 10));
47035
47064
  if (!logs.length) {
47036
47065
  console.log(source_default.dim("No sync history."));
47037
47066
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/project",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Project management CLI + MCP server for AI agents — register, sync, and open projects across machines",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",