@basou/cli 0.16.0 → 0.17.0

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/dist/index.js CHANGED
@@ -1804,13 +1804,15 @@ async function assertWorkspaceInitialized4(basouRoot) {
1804
1804
  import { createReadStream } from "fs";
1805
1805
  import { readdir, readFile as readFile2, rm, stat as stat2 } from "fs/promises";
1806
1806
  import { homedir as homedir4 } from "os";
1807
- import { basename as basename2, join as join4, resolve as resolve4 } from "path";
1807
+ import { basename as basename2, dirname, join as join4, resolve as resolve4 } from "path";
1808
1808
  import { createInterface } from "readline";
1809
1809
  import {
1810
+ AGENT_INFRA_DIRS,
1810
1811
  assertBasouRootSafe as assertBasouRootSafe6,
1811
1812
  basouPaths as basouPaths7,
1812
1813
  CLAUDE_IMPORT_SOURCE,
1813
1814
  CODEX_IMPORT_SOURCE,
1815
+ classifyFilesBySourceRoot,
1814
1816
  claudeTranscriptToImportPayload,
1815
1817
  codexRolloutToImportPayload,
1816
1818
  enumerateSessionDirs,
@@ -1908,7 +1910,15 @@ async function doRunImportClaudeCode(options, ctx) {
1908
1910
  }
1909
1911
  };
1910
1912
  });
1911
- await importDerivedSessions(paths, manifest, options, CLAUDE_IMPORT_SOURCE, candidates);
1913
+ await importDerivedSessions(
1914
+ paths,
1915
+ manifest,
1916
+ options,
1917
+ CLAUDE_IMPORT_SOURCE,
1918
+ candidates,
1919
+ projectPaths,
1920
+ hasDeclaredBoundary(options, manifest)
1921
+ );
1912
1922
  }
1913
1923
  async function doRunImportCodex(options, ctx) {
1914
1924
  assertSelector(options);
@@ -1933,7 +1943,18 @@ async function doRunImportCodex(options, ctx) {
1933
1943
  });
1934
1944
  }
1935
1945
  }));
1936
- await importDerivedSessions(paths, manifest, options, CODEX_IMPORT_SOURCE, candidates);
1946
+ await importDerivedSessions(
1947
+ paths,
1948
+ manifest,
1949
+ options,
1950
+ CODEX_IMPORT_SOURCE,
1951
+ candidates,
1952
+ projectPaths,
1953
+ hasDeclaredBoundary(options, manifest)
1954
+ );
1955
+ }
1956
+ function hasDeclaredBoundary(options, manifest) {
1957
+ return (options.project?.length ?? 0) > 0 || (manifest.import?.source_roots?.length ?? 0) > 0;
1937
1958
  }
1938
1959
  function assertSelector(options) {
1939
1960
  if (options.session !== void 0 && options.all === true) {
@@ -1951,9 +1972,25 @@ async function resolveImportTarget(ctx) {
1951
1972
  const manifest = await readManifest4(paths);
1952
1973
  return { repositoryRoot, paths, manifest };
1953
1974
  }
1954
- async function importDerivedSessions(paths, manifest, options, sourceKind, candidates) {
1975
+ async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
1955
1976
  const existingByExternalId = await loadExistingByExternalId(paths, sourceKind);
1956
1977
  const seenThisRun = /* @__PURE__ */ new Set();
1978
+ const crossProjectCheck = boundaryDeclared;
1979
+ const crossProject = [];
1980
+ const noteCrossProject = async (externalId, payload) => {
1981
+ if (!crossProjectCheck) return;
1982
+ try {
1983
+ const scope = await classifyFilesBySourceRoot({
1984
+ files: payload.session.related_files ?? [],
1985
+ workingDirectory: payload.session.working_directory,
1986
+ sourceRoots: projectPaths,
1987
+ masterRoot: dirname(paths.root),
1988
+ extraInRoot: AGENT_INFRA_DIRS
1989
+ });
1990
+ if (scope.outOfRoot.length > 0) crossProject.push({ externalId, outOfRoot: scope.outOfRoot });
1991
+ } catch {
1992
+ }
1993
+ };
1957
1994
  const results = [];
1958
1995
  const counts = {
1959
1996
  skippedNoAction: 0,
@@ -2010,6 +2047,7 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
2010
2047
  }
2011
2048
  counts.reimported++;
2012
2049
  seenThisRun.add(externalId);
2050
+ await noteCrossProject(externalId, payload2);
2013
2051
  continue;
2014
2052
  }
2015
2053
  const payload = validate(await toPayload());
@@ -2031,10 +2069,21 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
2031
2069
  results.push(result);
2032
2070
  seenThisRun.add(externalId);
2033
2071
  sanitizedPaths += result.pathSanitizeReport.relatedFiles + (result.pathSanitizeReport.workingDirectoryRewritten ? 1 : 0);
2072
+ await noteCrossProject(externalId, payload);
2034
2073
  }
2035
2074
  if (sanitizedPaths > 0) {
2036
2075
  console.error(`Imported sessions: ${sanitizedPaths} path(s) sanitized`);
2037
2076
  }
2077
+ if (crossProject.length > 0) {
2078
+ const PATH_SAMPLE = 5;
2079
+ for (const { externalId, outOfRoot } of crossProject) {
2080
+ const sample = outOfRoot.slice(0, PATH_SAMPLE).join(", ");
2081
+ const more = outOfRoot.length > PATH_SAMPLE ? ` (... +${outOfRoot.length - PATH_SAMPLE} more)` : "";
2082
+ console.error(
2083
+ `basou: session ${externalId} edited ${outOfRoot.length} file(s) outside this project's source_roots: ${sample}${more} \u2014 they may belong to another project.`
2084
+ );
2085
+ }
2086
+ }
2038
2087
  printImportResult(options, results, counts);
2039
2088
  }
2040
2089
  async function classifyReimport(priors, sourcePath, externalId, counts) {
@@ -2941,7 +2990,7 @@ import {
2941
2990
  unlinkSync,
2942
2991
  writeFileSync
2943
2992
  } from "fs";
2944
- import { basename as basename4, dirname, isAbsolute as isAbsolute3, join as join6, relative as relative2, resolve as resolve7 } from "path";
2993
+ import { basename as basename4, dirname as dirname2, isAbsolute as isAbsolute3, join as join6, relative as relative2, resolve as resolve7 } from "path";
2945
2994
  import {
2946
2995
  basouPaths as basouPaths10,
2947
2996
  GENERATED_END,
@@ -3580,7 +3629,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
3580
3629
  for (const { name, target } of plan.toCreate) {
3581
3630
  const filePath = join6(real, name);
3582
3631
  try {
3583
- mkdirSync(dirname(filePath), { recursive: true });
3632
+ mkdirSync(dirname2(filePath), { recursive: true });
3584
3633
  symlinkSync(target, filePath);
3585
3634
  created.push(name);
3586
3635
  } catch (error) {
@@ -3720,7 +3769,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
3720
3769
  return realpathSync(abs);
3721
3770
  } catch {
3722
3771
  try {
3723
- return join6(realpathSync(dirname(abs)), basename4(abs));
3772
+ return join6(realpathSync(dirname2(abs)), basename4(abs));
3724
3773
  } catch {
3725
3774
  return abs;
3726
3775
  }
@@ -3754,7 +3803,7 @@ function applyViewPlan(viewDir, toCreate) {
3754
3803
  for (const { name, target } of toCreate) {
3755
3804
  const filePath = join6(viewDir, name);
3756
3805
  try {
3757
- mkdirSync(dirname(filePath), { recursive: true });
3806
+ mkdirSync(dirname2(filePath), { recursive: true });
3758
3807
  symlinkSync(target, filePath);
3759
3808
  created.push(name);
3760
3809
  } catch (error) {
@@ -4101,7 +4150,7 @@ async function applyPresetPlan(anchorReal, plan) {
4101
4150
  isLink = false;
4102
4151
  }
4103
4152
  if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
4104
- if (plan.action === "create") mkdirSync(dirname(file), { recursive: true });
4153
+ if (plan.action === "create") mkdirSync(dirname2(file), { recursive: true });
4105
4154
  const existing = await readMarkdownFile4(file);
4106
4155
  await writeMarkdownFile5(file, renderWithMarkers4(existing, plan.desiredBlock, label));
4107
4156
  }
@@ -4617,7 +4666,7 @@ import {
4617
4666
  // src/lib/durable-write.ts
4618
4667
  import { randomUUID } from "crypto";
4619
4668
  import { lstat, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
4620
- import { basename as basename5, dirname as dirname2, join as join7 } from "path";
4669
+ import { basename as basename5, dirname as dirname3, join as join7 } from "path";
4621
4670
  async function assertNotSymlink(targetPath) {
4622
4671
  try {
4623
4672
  const st = await lstat(targetPath);
@@ -4632,7 +4681,7 @@ async function assertNotSymlink(targetPath) {
4632
4681
  }
4633
4682
  }
4634
4683
  async function writeFileDurable(targetPath, content) {
4635
- const dir = dirname2(targetPath);
4684
+ const dir = dirname3(targetPath);
4636
4685
  const tmpPath = join7(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
4637
4686
  let mode = 420;
4638
4687
  try {