@hassangameryt/freeflow 0.1.0 → 0.2.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.
Files changed (89) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +8 -4
  3. package/package.json +15 -2
  4. package/plugins/freeflow/.claude-plugin/plugin.json +1 -1
  5. package/plugins/freeflow/.codex-plugin/plugin.json +1 -1
  6. package/plugins/freeflow/command-surface.json +2 -10
  7. package/plugins/freeflow/docs/README.md +3 -2
  8. package/plugins/freeflow/docs/adr/0002-evals-before-hooks.md +1 -1
  9. package/plugins/freeflow/docs/adr/0003-release-boundary.md +1 -1
  10. package/plugins/freeflow/docs/adr/0004-research-replaces-shallow-discovery-skills.md +26 -0
  11. package/plugins/freeflow/docs/adr/README.md +2 -1
  12. package/plugins/freeflow/docs/architecture.md +3 -3
  13. package/plugins/freeflow/docs/output-router.md +212 -0
  14. package/plugins/freeflow/docs/release-evidence.md +25 -5
  15. package/plugins/freeflow/docs/skills.md +6 -7
  16. package/plugins/freeflow/docs/workflow.md +1 -1
  17. package/plugins/freeflow/hooks/freeflow-runtime-context.mjs +23 -6
  18. package/plugins/freeflow/pi-extension/index.js +941 -15
  19. package/plugins/freeflow/router/dist/benchmark-harness.d.ts +32 -0
  20. package/plugins/freeflow/router/dist/benchmark-harness.js +92 -0
  21. package/plugins/freeflow/router/dist/benchmarks.d.ts +102 -0
  22. package/plugins/freeflow/router/dist/benchmarks.js +880 -0
  23. package/plugins/freeflow/router/dist/bounded-evidence.d.ts +40 -0
  24. package/plugins/freeflow/router/dist/bounded-evidence.js +310 -0
  25. package/plugins/freeflow/router/dist/codex-qa-benchmarks.d.ts +91 -0
  26. package/plugins/freeflow/router/dist/codex-qa-benchmarks.js +461 -0
  27. package/plugins/freeflow/router/dist/command-benchmarks.d.ts +128 -0
  28. package/plugins/freeflow/router/dist/command-benchmarks.js +652 -0
  29. package/plugins/freeflow/router/dist/config.d.ts +22 -0
  30. package/plugins/freeflow/router/dist/config.js +114 -0
  31. package/plugins/freeflow/router/dist/evidence-range-selector.d.ts +22 -0
  32. package/plugins/freeflow/router/dist/evidence-range-selector.js +180 -0
  33. package/plugins/freeflow/router/dist/evidence-search.d.ts +25 -0
  34. package/plugins/freeflow/router/dist/evidence-search.js +450 -0
  35. package/plugins/freeflow/router/dist/evidence.d.ts +38 -0
  36. package/plugins/freeflow/router/dist/evidence.js +196 -0
  37. package/plugins/freeflow/router/dist/experimental-local-index.d.ts +1 -0
  38. package/plugins/freeflow/router/dist/experimental-local-index.js +1 -0
  39. package/plugins/freeflow/router/dist/experiments/local-index.d.ts +58 -0
  40. package/plugins/freeflow/router/dist/experiments/local-index.js +562 -0
  41. package/plugins/freeflow/router/dist/index-benchmarks.d.ts +84 -0
  42. package/plugins/freeflow/router/dist/index-benchmarks.js +458 -0
  43. package/plugins/freeflow/router/dist/index.d.ts +12 -0
  44. package/plugins/freeflow/router/dist/index.js +12 -0
  45. package/plugins/freeflow/router/dist/line-ranges.d.ts +22 -0
  46. package/plugins/freeflow/router/dist/line-ranges.js +22 -0
  47. package/plugins/freeflow/router/dist/parsers.d.ts +16 -0
  48. package/plugins/freeflow/router/dist/parsers.js +473 -0
  49. package/plugins/freeflow/router/dist/repo-traversal.d.ts +18 -0
  50. package/plugins/freeflow/router/dist/repo-traversal.js +242 -0
  51. package/plugins/freeflow/router/dist/retrieve.d.ts +33 -0
  52. package/plugins/freeflow/router/dist/retrieve.js +855 -0
  53. package/plugins/freeflow/router/dist/router-contract.d.ts +10 -0
  54. package/plugins/freeflow/router/dist/router-contract.js +54 -0
  55. package/plugins/freeflow/router/dist/run.d.ts +26 -0
  56. package/plugins/freeflow/router/dist/run.js +356 -0
  57. package/plugins/freeflow/router/dist/schema.d.ts +18 -0
  58. package/plugins/freeflow/router/dist/schema.js +354 -0
  59. package/plugins/freeflow/router/dist/types.d.ts +234 -0
  60. package/plugins/freeflow/router/dist/types.js +9 -0
  61. package/plugins/freeflow/router/dist/vault.d.ts +80 -0
  62. package/plugins/freeflow/router/dist/vault.js +484 -0
  63. package/plugins/freeflow/skills/commit-work/SKILL.md +17 -16
  64. package/plugins/freeflow/skills/commit-work/references/staging-decisions.md +3 -3
  65. package/plugins/freeflow/skills/execute-plan/SKILL.md +70 -5
  66. package/plugins/freeflow/skills/execute-plan/references/execution-map.md +122 -0
  67. package/plugins/freeflow/skills/mode-contract/SKILL.md +1 -1
  68. package/plugins/freeflow/skills/output-router/SKILL.md +69 -0
  69. package/plugins/freeflow/skills/output-router/references/safety-policy.md +33 -0
  70. package/plugins/freeflow/skills/research/ARTIFACT-DESTINATIONS.md +58 -0
  71. package/plugins/freeflow/skills/research/CHECKPOINTS.md +74 -0
  72. package/plugins/freeflow/skills/research/LANGUAGE.md +61 -0
  73. package/plugins/freeflow/skills/research/SKILL.md +128 -0
  74. package/plugins/freeflow/skills/review-artifact/SKILL.md +34 -1
  75. package/plugins/freeflow/skills/review-artifact/references/reviewer-prompt.md +12 -1
  76. package/plugins/freeflow/skills/review-work/SKILL.md +25 -2
  77. package/plugins/freeflow/skills/review-work/references/reviewer-prompt.md +8 -0
  78. package/plugins/freeflow/skills/setup-freeflow/SKILL.md +14 -4
  79. package/plugins/freeflow/skills/setup-freeflow/references/activation-contract.md +8 -3
  80. package/plugins/freeflow/skills/setup-freeflow/references/host-setup.md +3 -1
  81. package/plugins/freeflow/skills/setup-freeflow/references/output-router-setup.md +52 -0
  82. package/plugins/freeflow/skills/workflow/SKILL.md +11 -3
  83. package/plugins/freeflow/skills/workflow/references/workflow-map.md +10 -11
  84. package/plugins/freeflow/skills/write-skill/SKILL.md +1 -1
  85. package/plugins/freeflow/skills/capture-decisions/SKILL.md +0 -95
  86. package/plugins/freeflow/skills/capture-decisions/references/destination-guide.md +0 -65
  87. package/plugins/freeflow/skills/grill-context/SKILL.md +0 -79
  88. package/plugins/freeflow/skills/grill-context/references/approach-framing.md +0 -52
  89. package/plugins/freeflow/skills/research-brief/SKILL.md +0 -69
@@ -0,0 +1,242 @@
1
+ import { readdir, realpath, stat } from "node:fs/promises";
2
+ import { relative, resolve } from "node:path";
3
+ const BROAD_SCAN_MAX_FILE_BYTES = 1024 * 1024;
4
+ const SKIP_DIRS = new Set([
5
+ ".git",
6
+ "node_modules",
7
+ "dist",
8
+ "build",
9
+ "out",
10
+ ".next",
11
+ ".nuxt",
12
+ "coverage",
13
+ "target",
14
+ "graphify-out",
15
+ ".cache",
16
+ ".tmp",
17
+ "tmp",
18
+ "temp",
19
+ "logs",
20
+ "generated",
21
+ ]);
22
+ const BROAD_SKIP_FILE_EXTENSIONS = new Set([
23
+ ".7z",
24
+ ".avi",
25
+ ".bin",
26
+ ".bmp",
27
+ ".br",
28
+ ".class",
29
+ ".db",
30
+ ".dll",
31
+ ".dylib",
32
+ ".eot",
33
+ ".exe",
34
+ ".gif",
35
+ ".gz",
36
+ ".heic",
37
+ ".icns",
38
+ ".ico",
39
+ ".jar",
40
+ ".jpeg",
41
+ ".jpg",
42
+ ".mov",
43
+ ".mp3",
44
+ ".mp4",
45
+ ".otf",
46
+ ".parquet",
47
+ ".pdf",
48
+ ".png",
49
+ ".pyc",
50
+ ".rar",
51
+ ".so",
52
+ ".sqlite",
53
+ ".sqlite3",
54
+ ".tar",
55
+ ".tgz",
56
+ ".ttf",
57
+ ".wasm",
58
+ ".webm",
59
+ ".webp",
60
+ ".woff",
61
+ ".woff2",
62
+ ".zip",
63
+ ".zst",
64
+ ]);
65
+ export async function collectRepoTextFileRefs(options) {
66
+ const start = await resolveRepoPath(options.root, options.requestedPath);
67
+ const fileRefs = [];
68
+ const visitedDirectories = new Set();
69
+ const broadTraversal = !isExplicitRequestedPath(start.relativePath);
70
+ await collectTextFileRefs({
71
+ root: start.root,
72
+ currentPath: start.absolutePath,
73
+ fileRefs,
74
+ broadTraversal,
75
+ visitedDirectories,
76
+ generatedPathGlobs: options.generatedPathGlobs ?? [],
77
+ });
78
+ return fileRefs;
79
+ }
80
+ export async function resolveRepoPath(root, requestedPath) {
81
+ const rootRealPath = await realpath(resolve(root));
82
+ const requestedAbsolutePath = requestedPath ? resolve(rootRealPath, requestedPath) : rootRealPath;
83
+ const requestedRealPath = await realpath(requestedAbsolutePath);
84
+ if (!isPathInsideRoot(rootRealPath, requestedRealPath)) {
85
+ throw new Error(`Repo path escapes root: ${requestedPath ?? "."}`);
86
+ }
87
+ return {
88
+ root: rootRealPath,
89
+ absolutePath: requestedRealPath,
90
+ relativePath: normalizeRelativePath(relative(rootRealPath, requestedRealPath)),
91
+ };
92
+ }
93
+ async function collectTextFileRefs(options) {
94
+ let currentRealPath;
95
+ try {
96
+ currentRealPath = await realpath(options.currentPath);
97
+ }
98
+ catch {
99
+ return;
100
+ }
101
+ if (!isPathInsideRoot(options.root, currentRealPath)) {
102
+ return;
103
+ }
104
+ let currentStat;
105
+ try {
106
+ currentStat = await stat(currentRealPath);
107
+ }
108
+ catch {
109
+ return;
110
+ }
111
+ const path = normalizeRelativePath(relative(options.root, currentRealPath));
112
+ if (currentStat.isDirectory()) {
113
+ if (options.visitedDirectories.has(currentRealPath)) {
114
+ return;
115
+ }
116
+ options.visitedDirectories.add(currentRealPath);
117
+ if (options.broadTraversal && path !== "" && shouldSkipBroadDirectory(path, options.generatedPathGlobs)) {
118
+ return;
119
+ }
120
+ const entries = await readdir(currentRealPath, { withFileTypes: true });
121
+ for (const entry of entries) {
122
+ await collectTextFileRefs({
123
+ ...options,
124
+ currentPath: resolve(currentRealPath, entry.name),
125
+ });
126
+ }
127
+ return;
128
+ }
129
+ if (!currentStat.isFile()) {
130
+ return;
131
+ }
132
+ if (options.broadTraversal && shouldSkipBroadFile(path, currentStat.size, options.generatedPathGlobs)) {
133
+ return;
134
+ }
135
+ options.fileRefs.push({ path, absolutePath: currentRealPath, sizeBytes: currentStat.size });
136
+ }
137
+ function isExplicitRequestedPath(requestedRelativePath) {
138
+ return requestedRelativePath !== "" && requestedRelativePath !== ".";
139
+ }
140
+ function shouldSkipBroadDirectory(path, generatedPathGlobs) {
141
+ const name = path.split("/").at(-1) ?? path;
142
+ return SKIP_DIRS.has(name) || matchesGeneratedPathHint(path, generatedPathGlobs);
143
+ }
144
+ function shouldSkipBroadFile(path, size, generatedPathGlobs) {
145
+ if (matchesGeneratedPathHint(path, generatedPathGlobs)) {
146
+ return true;
147
+ }
148
+ const name = path.split("/").at(-1)?.toLowerCase() ?? path.toLowerCase();
149
+ if (isLockfile(name)) {
150
+ return false;
151
+ }
152
+ if (hasBroadSkippedFileExtension(name)) {
153
+ return true;
154
+ }
155
+ if (size > BROAD_SCAN_MAX_FILE_BYTES) {
156
+ return true;
157
+ }
158
+ if (name.endsWith(".min.js") || name.endsWith(".min.css") || name.endsWith(".map")) {
159
+ return true;
160
+ }
161
+ if (name.includes(".bundle.") || name.endsWith(".log")) {
162
+ return true;
163
+ }
164
+ if ((name.endsWith(".html") || name.endsWith(".json")) && size > 64_000) {
165
+ return true;
166
+ }
167
+ return false;
168
+ }
169
+ function matchesGeneratedPathHint(path, hints) {
170
+ if (hints.length === 0) {
171
+ return false;
172
+ }
173
+ const normalizedPath = normalizeRelativePath(path);
174
+ return hints.some((hint) => matchesGeneratedPathGlob(normalizedPath, hint));
175
+ }
176
+ export function matchesGeneratedPathGlob(path, pattern) {
177
+ const normalizedPath = normalizeRelativePath(path).replace(/\/+$/, "");
178
+ const normalizedPattern = normalizeRelativePath(pattern).replace(/\/+$/, "");
179
+ if (!normalizedPath || !normalizedPattern || hasUnsupportedGlobSyntax(normalizedPattern)) {
180
+ return false;
181
+ }
182
+ const patternSegments = normalizedPattern.split("/");
183
+ const pathSegments = normalizedPath.split("/");
184
+ if (patternSegments.some((segment) => segment.includes("**") && segment !== "**")) {
185
+ return false;
186
+ }
187
+ return matchGlobSegments(patternSegments, pathSegments, 0, 0);
188
+ }
189
+ function matchGlobSegments(patternSegments, pathSegments, patternIndex, pathIndex) {
190
+ if (patternIndex === patternSegments.length) {
191
+ return pathIndex === pathSegments.length;
192
+ }
193
+ const patternSegment = patternSegments[patternIndex];
194
+ if (patternSegment === "**") {
195
+ if (patternIndex === patternSegments.length - 1) {
196
+ return true;
197
+ }
198
+ for (let nextPathIndex = pathIndex; nextPathIndex <= pathSegments.length; nextPathIndex += 1) {
199
+ if (matchGlobSegments(patternSegments, pathSegments, patternIndex + 1, nextPathIndex)) {
200
+ return true;
201
+ }
202
+ }
203
+ return false;
204
+ }
205
+ const pathSegment = pathSegments[pathIndex];
206
+ if (pathSegment === undefined || patternSegment === undefined || !matchesGlobSegment(pathSegment, patternSegment)) {
207
+ return false;
208
+ }
209
+ return matchGlobSegments(patternSegments, pathSegments, patternIndex + 1, pathIndex + 1);
210
+ }
211
+ function matchesGlobSegment(pathSegment, patternSegment) {
212
+ const escaped = patternSegment
213
+ .split("*")
214
+ .map((part) => part.replace(/[.+^${}()|[\]\\]/g, "\\$&"))
215
+ .join("[^/]*");
216
+ return new RegExp(`^${escaped}$`).test(pathSegment);
217
+ }
218
+ function hasUnsupportedGlobSyntax(pattern) {
219
+ return /[{}!?[\]\\]/.test(pattern);
220
+ }
221
+ function hasBroadSkippedFileExtension(name) {
222
+ for (const extension of BROAD_SKIP_FILE_EXTENSIONS) {
223
+ if (name.endsWith(extension)) {
224
+ return true;
225
+ }
226
+ }
227
+ return false;
228
+ }
229
+ function isLockfile(name) {
230
+ return (name === "package-lock.json" ||
231
+ name === "npm-shrinkwrap.json" ||
232
+ name === "pnpm-lock.yaml" ||
233
+ name === "yarn.lock" ||
234
+ name === "bun.lockb");
235
+ }
236
+ function isPathInsideRoot(root, absolutePath) {
237
+ const relativePath = relative(root, absolutePath);
238
+ return relativePath === "" || (!relativePath.startsWith("..") && !relativePath.startsWith("/") && !/^[A-Za-z]:/.test(relativePath));
239
+ }
240
+ function normalizeRelativePath(path) {
241
+ return path.split(/[\\/]+/).join("/");
242
+ }
@@ -0,0 +1,33 @@
1
+ import type { EvidencePacket, OutputStream, PreserveMode, RetrievalAction, RetrievalRoutedResult } from "./types.js";
2
+ export interface RepoRetrieveSourceInput {
3
+ kind: "repo";
4
+ root: string;
5
+ path?: string;
6
+ }
7
+ export interface VaultRetrieveSourceInput {
8
+ kind: "vault";
9
+ root: string;
10
+ sessionId: string;
11
+ outputId: string;
12
+ stream?: OutputStream;
13
+ }
14
+ export type RetrieveSourceInput = RepoRetrieveSourceInput | VaultRetrieveSourceInput;
15
+ export type RepoExpansion = "lines_30" | "lines_80" | "full";
16
+ export interface RetrieveLineRangeInput {
17
+ start: number;
18
+ end: number;
19
+ }
20
+ export interface FreeflowRetrieveOptions {
21
+ action: RetrievalAction;
22
+ source: RetrieveSourceInput;
23
+ query?: string;
24
+ preserve?: PreserveMode;
25
+ evidence?: EvidencePacket;
26
+ expansion?: RepoExpansion;
27
+ maxFullBytes?: number;
28
+ lineRange?: RetrieveLineRangeInput;
29
+ topK?: number;
30
+ decision?: RetrievalRoutedResult;
31
+ generatedPathGlobs?: readonly string[];
32
+ }
33
+ export declare function freeflowRetrieve(options: FreeflowRetrieveOptions): Promise<RetrievalRoutedResult>;