@cassiomc1/forgeloop 1.2.1 → 1.2.2

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 (65) hide show
  1. package/.cursor/rules/project-loop.mdc +1 -1
  2. package/.github/copilot-instructions.md +1 -1
  3. package/AGENTS.md +1 -1
  4. package/CLAUDE.md +1 -1
  5. package/DOCS_INDEX.md +3 -0
  6. package/ENG/design-code-eng.md +59 -0
  7. package/ENG/premium-sites-studio-eng.md +28 -0
  8. package/LOOP_ENGINEERING.md +23 -0
  9. package/LOOP_SYSTEM_DESIGN.md +9 -5
  10. package/ORCHESTRATOR_INTEGRATION.md +37 -4
  11. package/PROTOCOL_INTEGRATION.md +13 -0
  12. package/README.md +34 -2
  13. package/TERMINOLOGY.md +10 -0
  14. package/THIRD_PARTY_NOTICES.md +34 -0
  15. package/THREAT_MODEL.md +12 -1
  16. package/docs/ARTIFACT_REFERENCE.md +150 -0
  17. package/docs/CLI_REFERENCE.md +263 -30
  18. package/docs/CROSS_HARNESS_CONTINUITY.md +1 -0
  19. package/docs/DOCUMENTATION_GUIDE.md +41 -4
  20. package/docs/GETTING_STARTED.md +9 -4
  21. package/docs/RECIPES.md +31 -1
  22. package/docs/TROUBLESHOOTING.md +191 -0
  23. package/package.json +1 -1
  24. package/schemas/policy-baseline.schema.json +26 -0
  25. package/schemas/policy-discovery.schema.json +45 -0
  26. package/schemas/policy-lock.schema.json +16 -0
  27. package/schemas/policy-rules.schema.json +48 -0
  28. package/schemas/policy-snapshot.schema.json +16 -0
  29. package/src/cli.js +69 -1
  30. package/src/commands/baseline.js +120 -0
  31. package/src/commands/init.js +304 -6
  32. package/src/commands/policy-diff.js +51 -0
  33. package/src/commands/policy-discover.js +42 -0
  34. package/src/commands/policy-status.js +33 -0
  35. package/src/commands/profile-interview.js +50 -0
  36. package/src/commands/reconcile-closure.js +49 -0
  37. package/src/commands/rule-verify.js +36 -0
  38. package/src/commands/validate-receipt.js +38 -3
  39. package/src/core/artifact-registry.js +60 -0
  40. package/src/core/audit.js +24 -0
  41. package/src/core/cli-command-definitions.js +114 -7
  42. package/src/core/cli-metadata.js +1 -1
  43. package/src/core/completion-artifacts.js +29 -3
  44. package/src/core/completion.js +101 -10
  45. package/src/core/error-codes.js +227 -0
  46. package/src/core/events.js +22 -0
  47. package/src/core/execution-prerequisites.js +38 -20
  48. package/src/core/execution.js +20 -3
  49. package/src/core/native-adapters.js +14 -4
  50. package/src/core/next-action-model.js +9 -0
  51. package/src/core/next-action.js +128 -82
  52. package/src/core/policy-adapters.js +276 -0
  53. package/src/core/policy-baseline.js +144 -0
  54. package/src/core/policy-diff.js +133 -0
  55. package/src/core/policy-discovery.js +225 -0
  56. package/src/core/policy-engine.js +533 -0
  57. package/src/core/policy-mutation.js +139 -0
  58. package/src/core/preflight-consistency.js +23 -15
  59. package/src/core/preflight.js +65 -1
  60. package/src/core/reconcile-closure.js +173 -0
  61. package/src/core/schema-validation.js +6 -0
  62. package/src/core/task-context.js +11 -0
  63. package/src/core/task-discovery.js +67 -1
  64. package/src/core/task-paths.js +9 -0
  65. package/src/core/templates.js +5 -0
@@ -0,0 +1,225 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ export const CONFIDENCE_LEVELS = Object.freeze({
5
+ HIGH: "HIGH",
6
+ MEDIUM: "MEDIUM",
7
+ LOW: "LOW",
8
+ UNKNOWN: "UNKNOWN",
9
+ });
10
+
11
+ export const ENFORCEMENT_MODES = Object.freeze({
12
+ BLOCKING: "BLOCKING",
13
+ ADVISORY: "ADVISORY",
14
+ NONE: "NONE",
15
+ });
16
+
17
+ export const BUILTIN_POLICY_RULES = Object.freeze([
18
+ {
19
+ id: "SECURITY.NO_HARDCODED_SECRET",
20
+ severity: "HIGH",
21
+ source: "builtin",
22
+ blocking: true,
23
+ why: "Credentials committed to source control can expose systems and users.",
24
+ fix: "Move the credential to an approved secret source and remove it from tracked files.",
25
+ confidence: CONFIDENCE_LEVELS.HIGH,
26
+ check: {
27
+ type: "adapter",
28
+ adapter: "secret-detection",
29
+ },
30
+ },
31
+ ]);
32
+
33
+ export async function discoverPolicy({ target = process.cwd() } = {}) {
34
+ const languages = [];
35
+ const entries = await (async () => {
36
+ try {
37
+ return await readdir(target);
38
+ } catch {
39
+ return [];
40
+ }
41
+ })();
42
+
43
+ const entrySet = new Set(entries);
44
+
45
+ // 1. Detect Languages & Manifests
46
+ let packageJson = null;
47
+ if (entrySet.has("package.json")) {
48
+ try {
49
+ const raw = await readFile(path.join(target, "package.json"), "utf8");
50
+ packageJson = JSON.parse(raw);
51
+ if (entrySet.has("tsconfig.json")) {
52
+ languages.push("typescript");
53
+ } else {
54
+ languages.push("javascript");
55
+ }
56
+ } catch {
57
+ // ignore parse error
58
+ }
59
+ }
60
+
61
+ if (entrySet.has("pyproject.toml") || entrySet.has("requirements.txt") || entrySet.has("setup.py") || entrySet.has("Pipfile")) {
62
+ languages.push("python");
63
+ }
64
+ if (entrySet.has("Cargo.toml")) {
65
+ languages.push("rust");
66
+ }
67
+ if (entrySet.has("go.mod")) {
68
+ languages.push("go");
69
+ }
70
+ if (entrySet.has("pom.xml") || entrySet.has("build.gradle") || entrySet.has("build.gradle.kts")) {
71
+ languages.push("java");
72
+ }
73
+
74
+ // 2. Detect Testing
75
+ let testing = {
76
+ detected: false,
77
+ confidence: CONFIDENCE_LEVELS.UNKNOWN,
78
+ };
79
+
80
+ if (packageJson?.scripts?.test) {
81
+ testing = {
82
+ detected: true,
83
+ command: ["npm", "test"],
84
+ framework: packageJson.devDependencies?.jest ? "jest"
85
+ : packageJson.devDependencies?.vitest ? "vitest"
86
+ : packageJson.devDependencies?.mocha ? "mocha"
87
+ : "npm-test",
88
+ confidence: CONFIDENCE_LEVELS.HIGH,
89
+ };
90
+ } else if (entrySet.has("Cargo.toml")) {
91
+ testing = {
92
+ detected: true,
93
+ command: ["cargo", "test"],
94
+ framework: "cargo",
95
+ confidence: CONFIDENCE_LEVELS.HIGH,
96
+ };
97
+ } else if (entrySet.has("go.mod")) {
98
+ testing = {
99
+ detected: true,
100
+ command: ["go", "test", "./..."],
101
+ framework: "go",
102
+ confidence: CONFIDENCE_LEVELS.HIGH,
103
+ };
104
+ } else if (entrySet.has("pytest.ini") || entrySet.has("tests") || entrySet.has("test")) {
105
+ testing = {
106
+ detected: true,
107
+ command: ["pytest"],
108
+ framework: "pytest",
109
+ confidence: CONFIDENCE_LEVELS.MEDIUM,
110
+ };
111
+ }
112
+
113
+ // 3. Detect Linting
114
+ let linting = {
115
+ detected: false,
116
+ confidence: CONFIDENCE_LEVELS.UNKNOWN,
117
+ };
118
+
119
+ if (packageJson?.scripts?.lint) {
120
+ linting = {
121
+ detected: true,
122
+ command: ["npm", "run", "lint"],
123
+ tool: "eslint",
124
+ confidence: CONFIDENCE_LEVELS.HIGH,
125
+ };
126
+ } else if (entrySet.has(".eslintrc") || entrySet.has(".eslintrc.json") || entrySet.has(".eslintrc.js") || entrySet.has("eslint.config.js") || entrySet.has("eslint.config.mjs")) {
127
+ linting = {
128
+ detected: true,
129
+ command: ["npx", "eslint", "."],
130
+ tool: "eslint",
131
+ confidence: CONFIDENCE_LEVELS.MEDIUM,
132
+ };
133
+ }
134
+
135
+ // 4. Detect Architecture
136
+ let architecture = {
137
+ value: null,
138
+ confidence: CONFIDENCE_LEVELS.UNKNOWN,
139
+ enforcement: ENFORCEMENT_MODES.NONE,
140
+ };
141
+
142
+ let hasDomain = false;
143
+ let hasInfra = false;
144
+ let hasApp = false;
145
+
146
+ const checkDirs = async (parent) => {
147
+ try {
148
+ const subEntries = await readdir(parent, { withFileTypes: true });
149
+ for (const ent of subEntries) {
150
+ if (ent.isDirectory()) {
151
+ const name = ent.name.toLowerCase();
152
+ if (name === "domain") hasDomain = true;
153
+ if (name === "infrastructure" || name === "infra") hasInfra = true;
154
+ if (name === "application" || name === "app") hasApp = true;
155
+ }
156
+ }
157
+ } catch {
158
+ // ignore
159
+ }
160
+ };
161
+
162
+ await checkDirs(target);
163
+ if (entrySet.has("src")) {
164
+ await checkDirs(path.join(target, "src"));
165
+ }
166
+
167
+ if (hasDomain && (hasInfra || hasApp)) {
168
+ architecture = {
169
+ value: "layered",
170
+ confidence: CONFIDENCE_LEVELS.HIGH,
171
+ enforcement: ENFORCEMENT_MODES.ADVISORY,
172
+ };
173
+ } else if (entrySet.has("src") || entrySet.has("lib") || entrySet.has("utils") || entrySet.has("services")) {
174
+ architecture = {
175
+ value: null,
176
+ confidence: CONFIDENCE_LEVELS.LOW,
177
+ enforcement: ENFORCEMENT_MODES.NONE,
178
+ };
179
+ }
180
+
181
+ // 5. Generate Discovered Rules
182
+ const discoveredRules = [];
183
+
184
+ if (testing.detected && testing.confidence === CONFIDENCE_LEVELS.HIGH) {
185
+ discoveredRules.push({
186
+ id: "TEST.REQUIRED",
187
+ severity: "MEDIUM",
188
+ source: "discovered",
189
+ blocking: false,
190
+ why: "Automated test suite was detected in repository manifests.",
191
+ fix: "Run test suite to verify changes.",
192
+ confidence: testing.confidence,
193
+ check: {
194
+ type: "adapter",
195
+ adapter: "test-runner",
196
+ command: testing.command,
197
+ },
198
+ });
199
+ }
200
+
201
+ if (architecture.value === "layered" && architecture.confidence === CONFIDENCE_LEVELS.HIGH) {
202
+ discoveredRules.push({
203
+ id: "ARCH.NO_DIRECT_DATABASE_ACCESS",
204
+ severity: "MEDIUM",
205
+ source: "discovered",
206
+ blocking: false,
207
+ why: "Clean architecture requires domain layer isolation from infrastructure.",
208
+ fix: "Depend on domain abstractions or repository interfaces.",
209
+ confidence: architecture.confidence,
210
+ check: {
211
+ type: "adapter",
212
+ adapter: "architecture-layers",
213
+ },
214
+ });
215
+ }
216
+
217
+ return {
218
+ schemaVersion: 1,
219
+ languages: [...new Set(languages)].sort(),
220
+ testing,
221
+ linting,
222
+ architecture,
223
+ discoveredRules,
224
+ };
225
+ }