srclawyer 0.4.1 → 0.5.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.
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { createInterface } from "node:readline/promises";
4
- import { CONFIG_FILENAME, DEFAULT_CONFIG, configPath, writeConfig } from "../config/config.js";
4
+ import { CONFIG_FILENAME, DEFAULT_CONFIG, configPath, hasUnmigratedLegacyConfig, legacyConfigMessage, writeConfig, } from "../config/config.js";
5
5
  function guessIndustry(root) {
6
6
  const pkgPath = join(root, "package.json");
7
7
  if (!existsSync(pkgPath))
@@ -29,6 +29,10 @@ export async function runInit(root) {
29
29
  console.log(`${CONFIG_FILENAME} already exists. Delete it first if you want to redo setup.`);
30
30
  return;
31
31
  }
32
+ if (hasUnmigratedLegacyConfig(root)) {
33
+ console.log(legacyConfigMessage());
34
+ return;
35
+ }
32
36
  const guessedIndustry = guessIndustry(root);
33
37
  const rl = createInterface({ input: process.stdin, output: process.stdout });
34
38
  const lines = rl[Symbol.asyncIterator]();
@@ -1,6 +1,6 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import { loadConfig } from "../config/config.js";
3
+ import { CONFIG_FILENAME, hasUnmigratedLegacyConfig, legacyConfigMessage, loadConfig } from "../config/config.js";
4
4
  import { scan } from "../engine/scanEngine.js";
5
5
  import { formatText } from "../report/formatText.js";
6
6
  import { generatePolicyMarkdown } from "../policy/generatePolicy.js";
@@ -10,8 +10,15 @@ import { resolveProtectedLogic } from "../cloud/resolveProtectedLogic.js";
10
10
  import { resolveProtectedLogicEndpoint } from "../cloud/config.js";
11
11
  export async function runScan(root, options) {
12
12
  const config = loadConfig(root);
13
+ let legacyConfigWarning = null;
13
14
  if (!config) {
14
- console.error('No .privacypolicy.yml found. Run "srclawyer init" first.\n');
15
+ if (hasUnmigratedLegacyConfig(root)) {
16
+ legacyConfigWarning = legacyConfigMessage();
17
+ console.error(legacyConfigWarning);
18
+ }
19
+ else {
20
+ console.error(`No ${CONFIG_FILENAME} found. Run "srclawyer init" first.\n`);
21
+ }
15
22
  }
16
23
  const result = await scan(root);
17
24
  let findings = result.findings;
@@ -52,11 +59,11 @@ export async function runScan(root, options) {
52
59
  const finalResult = { ...result, findings, ambiguousCount };
53
60
  const policyPath = resolve(root, options.out);
54
61
  writeFileSync(policyPath, generatePolicyMarkdown(finalResult, config, llmError));
55
- if (result.unsupportedStackWarning || llmError || protectedLogicWarning) {
62
+ if (result.unsupportedStackWarning || llmError || protectedLogicWarning || legacyConfigWarning) {
56
63
  process.exitCode = 1;
57
64
  }
58
65
  if (options.json) {
59
- console.log(JSON.stringify({ ...finalResult, policyPath, llmError, protectedLogicWarning }, null, 2));
66
+ console.log(JSON.stringify({ ...finalResult, policyPath, llmError, protectedLogicWarning, legacyConfigWarning }, null, 2));
60
67
  return;
61
68
  }
62
69
  console.log(formatText(finalResult));
@@ -1,4 +1,6 @@
1
- export declare const CONFIG_FILENAME = ".privacypolicy.yml";
1
+ export declare const CONFIG_FILENAME = ".srclawyer.config.yml";
2
+ /** The pre-0.5.0 name. Never auto-migrated or silently read as a fallback -- see legacyConfigMessage. */
3
+ export declare const LEGACY_CONFIG_FILENAME = ".privacypolicy.yml";
2
4
  export type SrcLawyerTier = "free" | "byok" | "managed";
3
5
  export interface SrcLawyerLlmConfig {
4
6
  provider: "anthropic";
@@ -14,5 +16,15 @@ export interface SrcLawyerConfig {
14
16
  }
15
17
  export declare const DEFAULT_CONFIG: SrcLawyerConfig;
16
18
  export declare function configPath(root: string): string;
19
+ export declare function legacyConfigPath(root: string): string;
20
+ /** True only when the old file is present and the new one isn't -- never fires once migrated. */
21
+ export declare function hasUnmigratedLegacyConfig(root: string): boolean;
22
+ /**
23
+ * Single source of truth for this message so init and scan can't drift into saying it two different
24
+ * ways. Deliberately never auto-reads the legacy file as a fallback, even though the YAML shape is
25
+ * identical -- the rename needs to be an explicit, visible action, not a silent compatibility shim
26
+ * that lets the old filename linger indefinitely.
27
+ */
28
+ export declare function legacyConfigMessage(): string;
17
29
  export declare function loadConfig(root: string): SrcLawyerConfig | null;
18
30
  export declare function writeConfig(root: string, config: SrcLawyerConfig): void;
@@ -1,7 +1,9 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import yaml from "js-yaml";
4
- export const CONFIG_FILENAME = ".privacypolicy.yml";
4
+ export const CONFIG_FILENAME = ".srclawyer.config.yml";
5
+ /** The pre-0.5.0 name. Never auto-migrated or silently read as a fallback -- see legacyConfigMessage. */
6
+ export const LEGACY_CONFIG_FILENAME = ".privacypolicy.yml";
5
7
  export const DEFAULT_CONFIG = {
6
8
  entityLocation: null,
7
9
  targetMarkets: [],
@@ -12,6 +14,22 @@ export const DEFAULT_CONFIG = {
12
14
  export function configPath(root) {
13
15
  return join(root, CONFIG_FILENAME);
14
16
  }
17
+ export function legacyConfigPath(root) {
18
+ return join(root, LEGACY_CONFIG_FILENAME);
19
+ }
20
+ /** True only when the old file is present and the new one isn't -- never fires once migrated. */
21
+ export function hasUnmigratedLegacyConfig(root) {
22
+ return existsSync(legacyConfigPath(root)) && !existsSync(configPath(root));
23
+ }
24
+ /**
25
+ * Single source of truth for this message so init and scan can't drift into saying it two different
26
+ * ways. Deliberately never auto-reads the legacy file as a fallback, even though the YAML shape is
27
+ * identical -- the rename needs to be an explicit, visible action, not a silent compatibility shim
28
+ * that lets the old filename linger indefinitely.
29
+ */
30
+ export function legacyConfigMessage() {
31
+ return `Found a legacy ${LEGACY_CONFIG_FILENAME} from an older version of SrcLawyer. Rename it to keep your existing settings:\n\n mv ${LEGACY_CONFIG_FILENAME} ${CONFIG_FILENAME}\n\nThen re-run this command. (If you'd rather start fresh instead, delete ${LEGACY_CONFIG_FILENAME} first.)`;
32
+ }
15
33
  export function loadConfig(root) {
16
34
  const path = configPath(root);
17
35
  if (!existsSync(path))
@@ -1,3 +1,3 @@
1
- import type { SrcLawyerConfig } from "../config/config.js";
1
+ import { type SrcLawyerConfig } from "../config/config.js";
2
2
  import type { ProviderResolution } from "./types.js";
3
3
  export declare function resolveProviderForTier(config: SrcLawyerConfig): ProviderResolution;
@@ -1,3 +1,4 @@
1
+ import { CONFIG_FILENAME } from "../config/config.js";
1
2
  import { AnthropicProvider } from "./anthropicProvider.js";
2
3
  export function resolveProviderForTier(config) {
3
4
  const tier = config.tier ?? "free";
@@ -9,7 +10,7 @@ export function resolveProviderForTier(config) {
9
10
  if (!envVar) {
10
11
  return {
11
12
  provider: null,
12
- error: 'Tier is "byok" but no API key environment variable is configured in .privacypolicy.yml. Run "srclawyer init" to set one.',
13
+ error: `Tier is "byok" but no API key environment variable is configured in ${CONFIG_FILENAME}. Run "srclawyer init" to set one.`,
13
14
  };
14
15
  }
15
16
  const apiKey = process.env[envVar];
@@ -27,5 +28,5 @@ export function resolveProviderForTier(config) {
27
28
  error: 'Tier is "managed" but SrcLawyer\'s managed backend is not available yet. Use "byok" with your own Anthropic API key, or "free".',
28
29
  };
29
30
  }
30
- return { provider: null, error: `Unknown tier "${tier}" in .privacypolicy.yml.` };
31
+ return { provider: null, error: `Unknown tier "${tier}" in ${CONFIG_FILENAME}.` };
31
32
  }
@@ -1,3 +1,3 @@
1
1
  import type { ScanResult } from "../engine/types.js";
2
- import type { SrcLawyerConfig } from "../config/config.js";
2
+ import { type SrcLawyerConfig } from "../config/config.js";
3
3
  export declare function generatePolicyMarkdown(result: ScanResult, config: SrcLawyerConfig | null, llmError?: string | null): string;
@@ -1,3 +1,4 @@
1
+ import { CONFIG_FILENAME } from "../config/config.js";
1
2
  import { DATA_CATEGORY_LABELS } from "./dataCategoryLabels.js";
2
3
  import { MANDATORY_DISCLAIMER } from "./disclaimer.js";
3
4
  function locationRef(finding) {
@@ -88,7 +89,7 @@ function renderContextSection(config) {
88
89
  return [
89
90
  "## Scope",
90
91
  "",
91
- "No `.privacypolicy.yml` configuration was found, so this policy was generated without jurisdiction, target-market, or industry context. Run `srclawyer init` and re-run `srclawyer scan` to include that context.",
92
+ `No \`${CONFIG_FILENAME}\` configuration was found, so this policy was generated without jurisdiction, target-market, or industry context. Run \`srclawyer init\` and re-run \`srclawyer scan\` to include that context.`,
92
93
  ].join("\n");
93
94
  }
94
95
  const lines = ["## Scope", ""];
@@ -129,6 +130,9 @@ export function generatePolicyMarkdown(result, config, llmError) {
129
130
  if (categorySections.length > 0) {
130
131
  parts.push(categorySections.join("\n\n"));
131
132
  }
133
+ else if (result.unsupportedStackWarning) {
134
+ parts.push("**Not analyzed:** this codebase's primary language/framework isn't supported yet (see the warning above). This is not a clean-scan result — nothing below should be read as \"no data collection found.\"");
135
+ }
132
136
  else {
133
137
  parts.push("No data-collection patterns were confidently identified in this codebase.");
134
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "srclawyer",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Static analysis engine that derives privacy policy documentation directly from source code",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",