create-filegrc 0.2.0 → 0.3.1

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.
Files changed (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +31 -4
  3. package/package.json +3 -3
  4. package/src/cli.js +106 -16
  5. package/src/defaults.js +17 -13
  6. package/src/index.js +265 -32
  7. package/template/AGENTS.md +21 -35
  8. package/template/README.md +24 -20
  9. package/template/WORKSPACE.md +5 -14
  10. package/template/data/AGENTS.md +8 -8
  11. package/template/data/action-items/AGENTS.md +2 -2
  12. package/template/data/audits/AGENTS.md +3 -3
  13. package/template/data/documents/document-business-continuity-disaster-recovery.json +0 -1
  14. package/template/data/documents/document-contractor-policy-acknowledgement.json +0 -1
  15. package/template/data/documents/document-contractor-training-acknowledgement.json +0 -1
  16. package/template/data/documents/document-data-retention-schedule.json +0 -1
  17. package/template/data/documents/document-employee-handbook-acknowledgement.json +0 -1
  18. package/template/data/documents/document-employee-policy-acknowledgement.json +0 -1
  19. package/template/data/documents/document-employee-training-acknowledgement.json +0 -1
  20. package/template/data/documents/document-incident-response-plan.json +0 -1
  21. package/template/data/documents/document-soc2-management-assertion.json +0 -3
  22. package/template/data/documents/document-soc2-management-representation.json +0 -3
  23. package/template/data/documents/document-soc2-period-completeness.json +0 -3
  24. package/template/data/documents/document-soc2-period-completeness.md +2 -2
  25. package/template/data/documents/document-soc2-system-description.json +0 -3
  26. package/template/data/documents/document-soc2-system-description.md +3 -3
  27. package/template/data/evidence/AGENTS.md +3 -3
  28. package/template/data/obligation-events/AGENTS.md +1 -1
  29. package/template/data/people/person-policy-owner.json +1 -1
  30. package/template/data/policies/policy-anti-bribery-corruption.json +0 -1
  31. package/template/data/policies/policy-clear-desk-screen.json +0 -1
  32. package/template/data/policies/policy-data-protection-handling.json +0 -1
  33. package/template/data/policies/policy-employee-handbook.json +0 -1
  34. package/template/data/policies/policy-information-security.json +0 -1
  35. package/template/data/policies/policy-mobile-computing-communications.json +0 -1
  36. package/template/data/systems/system-filegrc-program-repository.md +3 -3
  37. package/template/data/workspace.json +3 -3
  38. package/template/docs/filegrc-audit.png +0 -0
  39. package/template/docs/filegrc-home.png +0 -0
  40. package/template/docs/filegrc-social-preview.png +0 -0
  41. package/template/package.json +2 -2
  42. package/template-parameters.json +46 -2
  43. package/template/data/people/person-independent-approver.json +0 -9
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 FileGRC contributors
3
+ Copyright (c) 2026 filegrc contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # create-filegrc
2
2
 
3
- Create a Git-native FileGRC workspace for a SOC 2 program:
3
+ Create a Git-native filegrc workspace for a SOC 2 program:
4
4
 
5
5
  ```sh
6
6
  npx create-filegrc@latest company-grc
@@ -9,12 +9,39 @@ npm run validate
9
9
  npm run serve
10
10
  ```
11
11
 
12
- Setup asks for the company name, the initial policy owner, and a security contact email. The generated private project includes a starter Security program, Program Readiness, a policy-driven Work Queue, Policy Events that add linked tasks, later audit preparation, evidence packets, and one dependency: `filegrc`.
12
+ Setup asks for the legal organization name, the initial policy owner and their email, a security reporting address, and the program timezone. The generated private project includes a starter Security program, Program Readiness, a policy-driven Work Queue, Policy Events that add linked tasks, later audit preparation, evidence packets, and one dependency: `filegrc`.
13
+
14
+ Creation has two layers. The five-record foundation contains workspace settings, the initial owner, the oversight team, renderer settings, and the filegrc system of record. The default `security` starter adds the framework references, proposed policies, controls, obligations, documents, and training records. Pass `--starter foundation` when you want to stop before selecting a framework.
15
+
16
+ For one noninteractive run, pass company and service fields together or use `--config setup.json`. The optional `setup` object defines the service boundary and management goal after installation. Use `--filegrc-package <directory>` to exercise unpublished local engine changes instead of installing the registry release. This writes a machine-local `file:` dependency, so replace it with a released version before sharing the generated workspace.
17
+
18
+ ```json
19
+ {
20
+ "companyName": "Example Company",
21
+ "policyOwnerName": "Security Owner",
22
+ "policyOwnerEmail": "owner@example.com",
23
+ "securityContactEmail": "security@example.com",
24
+ "timezone": "America/Chicago",
25
+ "starter": "security",
26
+ "setup": {
27
+ "serviceName": "Example Service",
28
+ "boundary": "The production service and supporting infrastructure.",
29
+ "criticality": "high",
30
+ "dataClassification": "Confidential",
31
+ "internetExposed": true,
32
+ "programGoal": "type-2"
33
+ }
34
+ }
35
+ ```
36
+
37
+ ```sh
38
+ npx create-filegrc@latest company-grc --config setup.json
39
+ ```
13
40
 
14
41
  Generated workspaces also include layered `AGENTS.md` instructions and model-driven headless commands. `filegrc program-path` gives agents the same six steps, exact page guidance, current status, and next actions shown in the renderer. Agents can define program scope, approve policies, implement controls, test External Evidence, complete policy work, trigger event tasks, and prepare later audit packets through the same domain functions used by the renderer.
15
42
 
16
43
  Git is initialized when needed. The browser can create local commits before a remote is configured.
17
44
 
18
- Creation output reports the resolved FileGRC version, whether installation ran, and whether the target joined an existing Git worktree or received a new repository.
45
+ Creation output reports the resolved filegrc version, program timezone, starter record counts, whether installation ran, and whether the target joined an existing Git worktree or received a new repository.
19
46
 
20
- Use `npx create-filegrc@latest --help` for non-interactive options.
47
+ Use `npx create-filegrc@latest --help` for noninteractive options.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "create-filegrc",
3
- "version": "0.2.0",
4
- "description": "Create a FileGRC workspace for a SOC 2 program",
3
+ "version": "0.3.1",
4
+ "description": "Create a filegrc workspace for a SOC 2 program",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/Sunpeak-AI/FileGRC.git"
8
+ "url": "git+https://github.com/Sunpeak-AI/filegrc.git"
9
9
  },
10
10
  "type": "module",
11
11
  "bin": {
package/src/cli.js CHANGED
@@ -1,26 +1,67 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { createFileGRC } from "./index.js";
2
+ import { resolve } from "node:path";
3
+ import { createFilegrc } from "./index.js";
3
4
 
4
5
  export async function runCli(argv = process.argv.slice(2)) {
5
- const { target, options } = parseArgs(argv);
6
+ const parsed = parseArgs(argv);
7
+ const config = parsed.options.config ? await readConfig(parsed.options.config) : {};
8
+ const organization = config.organization && typeof config.organization === "object"
9
+ ? config.organization
10
+ : {};
11
+ const options = {
12
+ ...organization,
13
+ ...config,
14
+ ...parsed.options,
15
+ setup: {
16
+ ...(config.setup || {}),
17
+ ...parsed.setup
18
+ }
19
+ };
20
+ delete options.organization;
21
+ delete options.config;
22
+ delete options.target;
23
+ if (!Object.keys(options.setup).length) delete options.setup;
24
+ const target = parsed.target ?? config.target ?? "filegrc-program";
6
25
  if (options.help) return printHelp();
7
26
  if (options.version) return printVersion();
8
- const result = await createFileGRC({ target, ...options });
27
+ const result = await createFilegrc({ target, ...options });
9
28
  console.log(`Created ${result.target}`);
10
- console.log(`FileGRC ${result.engineVersion}: ${result.install === "installed" ? "installed" : "installation skipped"}`);
29
+ console.log(
30
+ `filegrc ${result.engineVersion}${result.enginePackage ? ` from ${result.enginePackage}` : ""}: ` +
31
+ `${result.install === "installed" ? "installed" : "installation skipped"}`
32
+ );
11
33
  console.log(`Git: ${result.gitMode === "existing-worktree" ? "joined existing worktree" : "initialized new repository"}`);
34
+ console.log(`Timezone: ${result.values.timezone}`);
35
+ for (const stage of result.stages) {
36
+ console.log(`Stage ${stage.id}: ${stage.status}${stage.status === "created" ? ` (${stage.records} records)` : ""}`);
37
+ }
38
+ console.log(
39
+ `${result.setup ? "Workspace after setup" : "Program baseline"}: ${result.resourceCounts.total} records, including ` +
40
+ `${result.resourceCounts.byType.requirement || 0} requirements, ` +
41
+ `${result.resourceCounts.byType.control || 0} controls, and ` +
42
+ `${result.resourceCounts.byType.obligation || 0} obligations.`
43
+ );
12
44
  console.log("");
13
- console.log(` cd ${result.target}`);
45
+ console.log(` cd ${shellQuote(result.target)}`);
14
46
  if (options.install === false) console.log(" npm install");
47
+ if (!result.setup) console.log(" npx filegrc setup");
15
48
  console.log(" npm run validate");
16
49
  console.log(" npm run serve");
17
50
  console.log("");
18
- console.log("Review the starter drafts, then commit the approved baseline.");
51
+ if (result.setup) {
52
+ console.log(`Service setup: ${result.setup.system.id} (${result.setup.system.status}), target ${result.setup.target.assuranceGoal}.`);
53
+ }
54
+ console.log("Review the generated records, then commit the approved baseline.");
55
+ }
56
+
57
+ function shellQuote(value) {
58
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
19
59
  }
20
60
 
21
61
  function parseArgs(argv) {
22
62
  let target;
23
63
  const options = {};
64
+ const setup = {};
24
65
  for (let index = 0; index < argv.length; index += 1) {
25
66
  const value = argv[index];
26
67
  if (!value.startsWith("-") && !target) {
@@ -34,33 +75,82 @@ function parseArgs(argv) {
34
75
  else if (name === "yes" || name === "y") options.yes = true;
35
76
  else if (name === "force") options.force = true;
36
77
  else if (name === "no-install") options.install = false;
78
+ else if (name === "config") options.config = next();
37
79
  else if (name === "company-name") options.companyName = next();
38
80
  else if (name === "policy-owner-name") options.policyOwnerName = next();
81
+ else if (name === "policy-owner-email") options.policyOwnerEmail = next();
39
82
  else if (name === "security-contact-email") options.securityContactEmail = next();
83
+ else if (name === "timezone") options.timezone = next();
84
+ else if (name === "starter") options.starter = next();
40
85
  else if (name === "filegrc-version") options.filegrcVersion = next();
86
+ else if (name === "filegrc-package") options.filegrcPackage = next();
87
+ else if (name === "service-name") setup.serviceName = next();
88
+ else if (name === "boundary") setup.boundary = next();
89
+ else if (name === "service-owner") setup.ownerId = next();
90
+ else if (name === "criticality") setup.criticality = next();
91
+ else if (name === "classification") setup.dataClassification = next();
92
+ else if (name === "internet-exposed") setup.internetExposed = booleanOption(next(), "internet-exposed");
93
+ else if (name === "program-goal") setup.programGoal = next();
94
+ else if (name === "setup-draft") setup.draft = true;
41
95
  else throw new Error(`Unknown option "${value}".`);
42
96
  }
43
- return { target: target ?? "filegrc-program", options };
97
+ return { target, options, setup };
44
98
  }
45
99
 
46
100
  function printHelp() {
47
101
  console.log(`create-filegrc [directory] [options]
48
102
 
49
- Create a FileGRC workspace for a SOC 2 program.
103
+ Create a filegrc workspace for a SOC 2 program.
50
104
 
51
105
  Options:
52
- --company-name <name>
53
- --policy-owner-name <name>
54
- --security-contact-email <email>
106
+ --config <json-file|-> Read creation and optional setup values
107
+ --company-name <legal-name> Legal organization name
108
+ --policy-owner-name <name> Initial policy owner
109
+ --policy-owner-email <email> Policy owner's email address
110
+ --security-contact-email <email> Security reporting address
111
+ --timezone <iana-timezone> Program timezone, such as America/Chicago
112
+ --starter <profile> security (default) or foundation
55
113
  --filegrc-version <version> Override resolved engine version
56
- --no-install Write files and a preliminary lockfile only
57
- --force Allow a non-empty target without overwriting files
58
- --yes Use generic prompt defaults
59
- --version Show the package version
60
- --help Show this help`);
114
+ --filegrc-package <directory> Install an unpublished local filegrc package
115
+ --service-name <name> Complete service setup after creation
116
+ --boundary <description> Initial service boundary
117
+ --service-owner <person-id> Defaults to person-policy-owner
118
+ --criticality <level> low, medium, high, or critical
119
+ --classification <name> Initial data classification
120
+ --internet-exposed <bool> true or false
121
+ --program-goal <goal> none, readiness, type-1, or type-2
122
+ --setup-draft Save combined service setup as a draft
123
+ --no-install Write files and a preliminary lockfile only
124
+ --force Allow a non-empty target without overwriting files
125
+ --yes Use generic prompt defaults
126
+ --version Show the package version
127
+ --help Show this help`);
61
128
  }
62
129
 
63
130
  async function printVersion() {
64
131
  const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
65
132
  console.log(packageJson.version);
66
133
  }
134
+
135
+ async function readConfig(path) {
136
+ const source = path === "-"
137
+ ? await readStdin()
138
+ : await readFile(resolve(String(path)), "utf8");
139
+ const parsed = JSON.parse(source);
140
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
141
+ throw new Error("Creation config must be a JSON object.");
142
+ }
143
+ return parsed;
144
+ }
145
+
146
+ async function readStdin() {
147
+ const chunks = [];
148
+ for await (const chunk of process.stdin) chunks.push(chunk);
149
+ return Buffer.concat(chunks).toString("utf8");
150
+ }
151
+
152
+ function booleanOption(value, name) {
153
+ if (value === "true") return true;
154
+ if (value === "false") return false;
155
+ throw new Error(`--${name} must be true or false.`);
156
+ }
package/src/defaults.js CHANGED
@@ -4,7 +4,6 @@ import { mkdir, writeFile } from "node:fs/promises";
4
4
  const FRAMEWORK_ID = "framework-aicpa-trust-services-criteria";
5
5
  const DESCRIPTION_FRAMEWORK_ID = "framework-aicpa-soc2-description-criteria";
6
6
  const OWNER_ID = "person-policy-owner";
7
- const INDEPENDENT_APPROVER_ID = "person-independent-approver";
8
7
  const OVERSIGHT_TEAM_ID = "team-security-risk-oversight";
9
8
  const INFORMATION_SECURITY_POLICY_ID = "policy-information-security";
10
9
  const DATA_POLICY_ID = "policy-data-protection-handling";
@@ -885,11 +884,11 @@ const obligations = [
885
884
  }
886
885
  ];
887
886
 
888
- export function baselineRecordPaths() {
889
- return baselineRecordFiles("2000-01-01").map(({ path }) => path);
887
+ export function baselineRecordPaths(starter = "security") {
888
+ return baselineRecordFiles("2000-01-01", starter).map(({ path }) => path);
890
889
  }
891
890
 
892
- export function baselineRecordFiles(effectiveDate) {
891
+ export function baselineRecordFiles(effectiveDate, starter = "security") {
893
892
  const framework = {
894
893
  schemaVersion: 1,
895
894
  id: FRAMEWORK_ID,
@@ -963,21 +962,21 @@ export function baselineRecordFiles(effectiveDate) {
963
962
  id: OVERSIGHT_TEAM_ID,
964
963
  type: "team",
965
964
  title: "Security and Risk Oversight",
966
- status: "active",
965
+ status: "inactive",
967
966
  purpose: "Provide independent oversight of the security program, risk register, incidents, findings, vendor and access reviews, policy changes, exercises, and overdue work. The chair must be separate from the policy owner and people who operate the controls under review.",
968
- memberIds: [OWNER_ID, INDEPENDENT_APPROVER_ID],
969
- chairIds: [INDEPENDENT_APPROVER_ID],
967
+ memberIds: [OWNER_ID],
968
+ chairIds: [],
970
969
  meetingCadence: calendar("month", 3, effectiveDate)
971
970
  };
972
971
  const programRepository = {
973
972
  schemaVersion: 1,
974
973
  id: "system-filegrc-program-repository",
975
974
  type: "system",
976
- title: "FileGRC Program Repository",
975
+ title: "filegrc Program Repository",
977
976
  status: "active",
978
977
  criticality: "high",
979
978
  ownerIds: [OWNER_ID],
980
- description: "The Git repository that is authoritative for FileGRC governance records, approvals, exceptions, findings, acknowledgements, evidence indexes, and their revision history.",
979
+ description: "The Git repository that is authoritative for filegrc governance records, approvals, exceptions, findings, acknowledgements, evidence indexes, and their revision history.",
981
980
  systemKind: "governance-system-of-record",
982
981
  environment: "Git repository",
983
982
  dataClassification: "Confidential",
@@ -1007,20 +1006,25 @@ export function baselineRecordFiles(effectiveDate) {
1007
1006
  policyIds: obligation.policyIds
1008
1007
  }));
1009
1008
 
1009
+ const foundation = [
1010
+ recordFile("systems", programRepository),
1011
+ recordFile("teams", team)
1012
+ ];
1013
+ if (starter === "foundation") return foundation;
1014
+ if (starter !== "security") throw new Error(`Unknown starter profile "${starter}".`);
1010
1015
  return [
1011
1016
  recordFile("frameworks", framework),
1012
1017
  recordFile("frameworks", descriptionFramework),
1013
1018
  ...commonRequirements.map((record) => recordFile("requirements", record)),
1014
1019
  ...descriptionRequirements.map((record) => recordFile("requirements", record)),
1015
1020
  ...controlRecords.map((record) => recordFile("controls", record)),
1016
- recordFile("systems", programRepository),
1017
- recordFile("teams", team),
1021
+ ...foundation,
1018
1022
  ...obligationRecords.map((record) => recordFile("obligations", record))
1019
1023
  ];
1020
1024
  }
1021
1025
 
1022
- export async function writeBaselineRecords(target, effectiveDate) {
1023
- for (const { path: relativePath, record } of baselineRecordFiles(effectiveDate)) {
1026
+ export async function writeBaselineRecords(target, effectiveDate, starter = "security") {
1027
+ for (const { path: relativePath, record } of baselineRecordFiles(effectiveDate, starter)) {
1024
1028
  const path = join(target, relativePath);
1025
1029
  await mkdir(dirname(path), { recursive: true });
1026
1030
  await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, { encoding: "utf8", flag: "wx" });