boxsafe 1.0.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 (118) hide show
  1. package/.directory +2 -0
  2. package/.env.example +3 -0
  3. package/AUDIT_LANG.md +45 -0
  4. package/BOXSAFE_VERSION_NOTES.md +14 -0
  5. package/README.md +4 -0
  6. package/TODO.md +130 -0
  7. package/adapters/index.ts +27 -0
  8. package/adapters/primary/cli-adapter.ts +56 -0
  9. package/adapters/secondary/filesystem/node-filesystem.ts +307 -0
  10. package/adapters/secondary/system/configuration.ts +147 -0
  11. package/ai/caller.ts +42 -0
  12. package/ai/label.ts +33 -0
  13. package/ai/modelConfig.ts +236 -0
  14. package/ai/provider.ts +111 -0
  15. package/boxsafe.config.json +68 -0
  16. package/core/auth/dasktop/cred/CRED.md +112 -0
  17. package/core/auth/dasktop/cred/credLinux.ts +82 -0
  18. package/core/auth/dasktop/cred/credWin.ts +2 -0
  19. package/core/config/defaults/boxsafeDefaults.ts +67 -0
  20. package/core/config/defaults/index.ts +1 -0
  21. package/core/config/loadConfig.ts +133 -0
  22. package/core/loop/about.md +13 -0
  23. package/core/loop/boxConfig.ts +20 -0
  24. package/core/loop/buildExecCommand.ts +76 -0
  25. package/core/loop/cmd/execode.ts +121 -0
  26. package/core/loop/cmd/test.js +3 -0
  27. package/core/loop/execLoop.ts +341 -0
  28. package/core/loop/git/VERSIONING.md +17 -0
  29. package/core/loop/git/commands.ts +11 -0
  30. package/core/loop/git/gitClient.ts +78 -0
  31. package/core/loop/git/index.ts +99 -0
  32. package/core/loop/git/runVersionControlRunner.ts +33 -0
  33. package/core/loop/initNavigator.ts +44 -0
  34. package/core/loop/initTasksManager.ts +35 -0
  35. package/core/loop/runValidation.ts +25 -0
  36. package/core/loop/tasks/AGENT-TASKS.md +36 -0
  37. package/core/loop/tasks/index.ts +96 -0
  38. package/core/loop/toolCalls.ts +168 -0
  39. package/core/loop/toolDispatcher.ts +146 -0
  40. package/core/loop/traceLogger.ts +106 -0
  41. package/core/loop/types.ts +26 -0
  42. package/core/loop/versionControlAdapter.ts +36 -0
  43. package/core/loop/waterfall.ts +404 -0
  44. package/core/loop/writeArtifactAtomically.ts +13 -0
  45. package/core/navigate/NAVIGATE.md +186 -0
  46. package/core/navigate/about.md +128 -0
  47. package/core/navigate/examples.ts +367 -0
  48. package/core/navigate/handler.ts +148 -0
  49. package/core/navigate/index.ts +32 -0
  50. package/core/navigate/navigate.test.ts +372 -0
  51. package/core/navigate/navigator.ts +437 -0
  52. package/core/navigate/types.ts +132 -0
  53. package/core/navigate/utils.ts +146 -0
  54. package/core/paths/paths.ts +33 -0
  55. package/core/ports/index.ts +271 -0
  56. package/core/segments/CONVENTIONS.md +30 -0
  57. package/core/segments/loop/index.ts +18 -0
  58. package/core/segments/map.ts +56 -0
  59. package/core/segments/navigate/index.ts +20 -0
  60. package/core/segments/versionControl/index.ts +18 -0
  61. package/core/util/logger.ts +128 -0
  62. package/docs/AGENT-TASKS.md +36 -0
  63. package/docs/ARQUITETURA_CORRECAO.md +121 -0
  64. package/docs/CONVENTIONS.md +30 -0
  65. package/docs/CRED.md +112 -0
  66. package/docs/L_RAG.md +567 -0
  67. package/docs/NAVIGATE.md +186 -0
  68. package/docs/PRIMARY_ACTORS.md +78 -0
  69. package/docs/SECONDARY_ACTORS.md +174 -0
  70. package/docs/VERSIONING.md +17 -0
  71. package/docs/boxsafe.config.md +472 -0
  72. package/eslint.config.mts +15 -0
  73. package/main.ts +53 -0
  74. package/memo/generated/codelog.md +13 -0
  75. package/memo/state/tasks/state.json +6 -0
  76. package/memo/state/tasks/tasks/task_001.md +2 -0
  77. package/memo/states-logs/logs.txt +7 -0
  78. package/memo/states-logs/trace-mljvrxvi-9g0k4q.jsonl +11 -0
  79. package/memo/states-logs/trace-mljvvc9j-pe9ekj.jsonl +11 -0
  80. package/memo/states-logs/trace-mljvvm1c-wbnqzp.jsonl +11 -0
  81. package/memo/states-logs/trace-mljxecwn-9xh3nw.jsonl +11 -0
  82. package/memo/states-logs/trace-mljxqkfm-ipijik.jsonl +11 -0
  83. package/memo/states-logs/trace-mljxwtrw-3fanky.jsonl +11 -0
  84. package/memo/states-logs/trace-mljxzen3-m8iinh.jsonl +11 -0
  85. package/memo/states-logs/trace-mljyucef-td6odn.jsonl +11 -0
  86. package/memo/states-logs/trace-mljyuprw-b1a6f4.jsonl +11 -0
  87. package/memo/states-logs/trace-mljyvefl-b6yoce.jsonl +11 -0
  88. package/memo/states-logs/trace-mljyxjo4-n7ibj2.jsonl +13 -0
  89. package/memo/states-logs/trace-mljziez5-8drqtn.jsonl +13 -0
  90. package/memo/states-logs/trace-mljziulp-dtd03z.jsonl +13 -0
  91. package/memo/states-logs/trace-mljzjwrq-1p2krb.jsonl +13 -0
  92. package/memo/states-logs/trace-mljzl0i7-b1cqa6.jsonl +13 -0
  93. package/memo/states-logs/trace-mljzmlk6-7kdyls.jsonl +13 -0
  94. package/memo/states-logs/trace-mlk0oj25-xa3dcu.jsonl +13 -0
  95. package/memo/states-logs/trace-mlk1x59q-713huj.jsonl +14 -0
  96. package/memo/states-logs/trace-mlk22dz8-7fd6hq.jsonl +14 -0
  97. package/memo/states-logs/trace-mlk241uy-wmx907.jsonl +14 -0
  98. package/memo/states-logs/trace-mlk2bf5r-yoh1vg.jsonl +15 -0
  99. package/package.json +44 -0
  100. package/pnpm-workspace.yaml +4 -0
  101. package/prompt_improvement_example.md +55 -0
  102. package/remove.txt +1 -0
  103. package/tests/adapters.test.ts +128 -0
  104. package/tests/extractCode.test.ts +26 -0
  105. package/tests/integration.test.ts +83 -0
  106. package/tests/loadConfig.test.ts +25 -0
  107. package/tests/navigatorBoundary.test.ts +17 -0
  108. package/tests/ports.test.ts +84 -0
  109. package/tests/runAllTests.ts +49 -0
  110. package/tests/toolCalls.test.ts +149 -0
  111. package/tests/waterfall.test.ts +52 -0
  112. package/tsconfig.json +32 -0
  113. package/tsup.config.ts +17 -0
  114. package/types.d.ts +96 -0
  115. package/util/ANSI.ts +29 -0
  116. package/util/extractCode.ts +217 -0
  117. package/util/extractToolCalls.ts +80 -0
  118. package/util/logger.ts +125 -0
@@ -0,0 +1,217 @@
1
+ import { remark } from 'remark';
2
+ import { visit } from 'unist-util-visit';
3
+ import { Logger } from '@util/logger';
4
+
5
+ const logger = Logger.createModuleLogger('ExtractCode');
6
+
7
+ /* -------------------------------------------------------------------------- */
8
+ /* Types */
9
+ /* -------------------------------------------------------------------------- */
10
+
11
+ interface ExtractCodeOptions {
12
+ /**
13
+ * If true, throws error when code is not found.
14
+ * If false, returns guidance prompt.
15
+ * @default false
16
+ */
17
+ throwOnNotFound?: boolean;
18
+
19
+ /**
20
+ * Custom message when code is not found
21
+ */
22
+ customNotFoundMessage?: string;
23
+ }
24
+
25
+ /* -------------------------------------------------------------------------- */
26
+ /* Configuration */
27
+ /* -------------------------------------------------------------------------- */
28
+
29
+ const LANG_ALIASES: Record<string, string[]> = {
30
+ javascript: ['javascript', 'js'],
31
+ typescript: ['typescript', 'ts'],
32
+ python: ['python', 'py'],
33
+ bash: ['bash', 'sh', 'shell'],
34
+ rust: ['rust', 'rs'],
35
+ };
36
+
37
+ /* -------------------------------------------------------------------------- */
38
+ /* Helpers */
39
+ /* -------------------------------------------------------------------------- */
40
+
41
+ function normalizeLang(lang?: string): string | undefined {
42
+ return lang?.toLowerCase().trim();
43
+ }
44
+
45
+ function matchesLanguage(target: string, nodeLang?: string): boolean {
46
+ if (!nodeLang) return false;
47
+
48
+ const normalizedNodeLang = normalizeLang(nodeLang);
49
+
50
+ if (normalizedNodeLang === target) return true;
51
+
52
+ return Object.values(LANG_ALIASES).some(
53
+ (aliases) =>
54
+ aliases.includes(target) && aliases.includes(normalizedNodeLang!)
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Heuristic detection of language from a code snippet when no fence language is provided.
60
+ * Returns true when the snippet contains tokens commonly associated with `target`.
61
+ */
62
+ function detectByContent(target: string, code: string): boolean {
63
+ const c = code || '';
64
+ switch (target) {
65
+ case 'typescript':
66
+ case 'ts':
67
+ case 'javascript':
68
+ case 'js':
69
+ return /\b(import|export|const|let|interface|type|from|require\(|console\.log)\b/.test(c);
70
+ case 'python':
71
+ case 'py':
72
+ return /\b(def |import |from |print\()/.test(c);
73
+ case 'bash':
74
+ case 'sh':
75
+ case 'shell':
76
+ return /(^#!\/bin\/bash)|(^#!\/usr\/bin\/env bash)|\becho\b|\bcd\b|\bmkdir\b/.test(c);
77
+ case 'rust':
78
+ case 'rs':
79
+ return /\b(fn |let |use |extern crate)\b/.test(c);
80
+ default:
81
+ return false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Generates guidance prompt when code is not found
87
+ */
88
+ function generateNotFoundPrompt(lang: string): string {
89
+ return `ERROR: ${lang} code was not found in the response.
90
+
91
+ MANDATORY INSTRUCTIONS:
92
+ 1. Generate ONLY the requested ${lang} code
93
+ 2. Use the correct code block format: \`\`\`${lang}
94
+ 3. DO NOT include explanations, descriptions, or additional text
95
+ 4. DO NOT generate multiple code blocks
96
+ 5. The code will be executed directly - it will not be read by humans
97
+ 6. Do not use markdown for anything other than the code block
98
+ 7. Return ONLY a single functional code block
99
+
100
+ Correct format example:
101
+ \`\`\`${lang}
102
+ // your code here
103
+ \`\`\`
104
+
105
+ Generate the ${lang} code now following these instructions.`;
106
+ }
107
+
108
+ /* -------------------------------------------------------------------------- */
109
+ /* Public */
110
+ /* -------------------------------------------------------------------------- */
111
+
112
+ /**
113
+ * Extracts code blocks of a specific language from Markdown.
114
+ *
115
+ * @param md - Markdown string (usually LLM response)
116
+ * @param lang - Code language (javascript, python, etc)
117
+ * @param options - Configuration options
118
+ * @returns Array of found code blocks or error prompt
119
+ */
120
+ export const extractCode = async (
121
+ md: string,
122
+ lang: string,
123
+ options: ExtractCodeOptions = {}
124
+ ): Promise<string[]> => {
125
+ const { throwOnNotFound = false, customNotFoundMessage } = options;
126
+
127
+ if (!md || typeof md !== 'string') {
128
+ const errorMsg = 'Invalid markdown input: expected non-empty string';
129
+ logger.error(errorMsg);
130
+ throw new Error(errorMsg);
131
+ }
132
+
133
+ if (!lang || typeof lang !== 'string') {
134
+ const errorMsg = 'Invalid language: expected non-empty string';
135
+ logger.error(errorMsg);
136
+ throw new Error(errorMsg);
137
+ }
138
+
139
+ const codeBlocks: string[] = [];
140
+ const normalizedLang = normalizeLang(lang)!;
141
+
142
+ const onVisitCode = (node: any) => {
143
+ if (!node?.value) return;
144
+
145
+ // Exact match by explicit fence language
146
+ if (matchesLanguage(normalizedLang, node.lang)) {
147
+ codeBlocks.push(node.value.trim());
148
+ return;
149
+ }
150
+
151
+ // Fallback: if the fence had no language, try to heuristically detect
152
+ // the requested language from the code content itself.
153
+ if (!node.lang && detectByContent(normalizedLang, node.value)) {
154
+ codeBlocks.push(node.value.trim());
155
+ return;
156
+ }
157
+ };
158
+
159
+ try {
160
+ await remark()
161
+ .use(() => (tree) => {
162
+ visit(tree, 'code', onVisitCode);
163
+ })
164
+ .process(md);
165
+
166
+ if (codeBlocks.length === 0) {
167
+ if (throwOnNotFound) {
168
+ const errorMsg = `No ${lang} code block found in markdown`;
169
+ logger.error(errorMsg);
170
+ throw new Error(errorMsg);
171
+ }
172
+
173
+ // Fallback: try to extract unlabeled fenced blocks or raw code from the
174
+ // entire markdown string when no explicit code nodes were found.
175
+ // 1) unlabeled/any fenced blocks via regex
176
+ const fenceRegex = /```(?:\w+)?\n([\s\S]*?)\n```/g;
177
+ let m: RegExpExecArray | null;
178
+ while ((m = fenceRegex.exec(md)) !== null) {
179
+ if (!m || !m[1]) continue;
180
+ const candidate = m[1].trim();
181
+ if (candidate && (detectByContent(normalizedLang, candidate) || normalizedLang === 'ts' || normalizedLang === 'js')) {
182
+ codeBlocks.push(candidate);
183
+ }
184
+ }
185
+
186
+ // 2) If still nothing, heuristically treat the whole markdown as code
187
+ // when it clearly contains tokens for the requested language.
188
+ if (codeBlocks.length === 0 && detectByContent(normalizedLang, md)) {
189
+ // require at least two lines to avoid grabbing short messages
190
+ if ((md.match(/\n/g) || []).length >= 1) {
191
+ codeBlocks.push(md.trim());
192
+ }
193
+ }
194
+
195
+ if (codeBlocks.length === 0) {
196
+ if (throwOnNotFound) {
197
+ const errorMsg = `No ${lang} code block found in markdown`;
198
+ logger.error(errorMsg);
199
+ throw new Error(errorMsg);
200
+ }
201
+
202
+ return [customNotFoundMessage ?? generateNotFoundPrompt(lang)];
203
+ }
204
+ }
205
+
206
+ return codeBlocks;
207
+ } catch (error) {
208
+ const errorMsg = `Failed to parse markdown: ${error}`;
209
+ logger.error(errorMsg);
210
+
211
+ throw new Error(
212
+ `Failed to extract code: ${
213
+ error instanceof Error ? error.message : 'unknown error'
214
+ }`
215
+ );
216
+ }
217
+ };
@@ -0,0 +1,80 @@
1
+ import { remark } from 'remark';
2
+ import { visit } from 'unist-util-visit';
3
+ import { Logger } from '@core/util/logger';
4
+ import type { ToolCallParseResult, ToolCallParseError } from '@core/loop/toolCalls';
5
+ import { parseOneToolCall, isParseError } from '@core/loop/toolCalls';
6
+
7
+ const logger = Logger.createModuleLogger('ExtractToolCalls');
8
+
9
+ /**
10
+ * Extracts json-tool blocks from Markdown using remark (same approach as extractCode)
11
+ *
12
+ * @param md - Markdown string (usually LLM response)
13
+ * @returns ToolCallParseResult with parsed calls and errors
14
+ */
15
+ export const extractToolCalls = async (md: string): Promise<ToolCallParseResult> => {
16
+ const calls: any[] = [];
17
+ const errors: ToolCallParseError[] = [];
18
+
19
+ if (!md || typeof md !== 'string') {
20
+ const errorMsg = 'Invalid markdown input: expected non-empty string';
21
+ logger.error(errorMsg);
22
+ throw new Error(errorMsg);
23
+ }
24
+
25
+ logger.debug(`Processing markdown of length ${md.length}`);
26
+
27
+ const onVisitCode = (node: any) => {
28
+ if (!node?.value) return;
29
+
30
+ // Only process json-tool blocks
31
+ if (node.lang === 'json-tool') {
32
+ logger.debug(`Found json-tool block: ${node.value.substring(0, 100)}...`);
33
+
34
+ try {
35
+ const jsonContent = node.value.trim();
36
+ const parsed = JSON.parse(jsonContent);
37
+ const toolCall = parseOneToolCall(parsed);
38
+
39
+ if (isParseError(toolCall)) {
40
+ logger.warn(`Invalid tool call: ${toolCall.error}`);
41
+ errors.push({ ...toolCall, fence: jsonContent });
42
+ } else {
43
+ logger.info(`Valid tool call detected: ${toolCall.tool}`);
44
+ calls.push(toolCall);
45
+ }
46
+ } catch (e: any) {
47
+ logger.error(`JSON parse error: ${e?.message ?? String(e)}`);
48
+ errors.push({
49
+ ok: false,
50
+ error: `invalid JSON: ${e?.message ?? String(e)}`,
51
+ fence: node.value
52
+ });
53
+ }
54
+ }
55
+ };
56
+
57
+ try {
58
+ await remark()
59
+ .use(() => (tree) => {
60
+ visit(tree, 'code', onVisitCode);
61
+ })
62
+ .process(md);
63
+
64
+ logger.info(`Extracted ${calls.length} tool calls, ${errors.length} errors`);
65
+
66
+ return {
67
+ ok: true,
68
+ calls,
69
+ errors
70
+ };
71
+ } catch (error) {
72
+ const errorMsg = `Failed to parse markdown for tool calls: ${error}`;
73
+ logger.error(errorMsg);
74
+ throw new Error(
75
+ `Failed to extract tool calls: ${
76
+ error instanceof Error ? error.message : 'unknown error'
77
+ }`
78
+ );
79
+ }
80
+ };
package/util/logger.ts ADDED
@@ -0,0 +1,125 @@
1
+ export enum LogLevel {
2
+ DEBUG = 0,
3
+ INFO = 1,
4
+ WARN = 2,
5
+ ERROR = 3,
6
+ }
7
+
8
+ export interface LogEntry {
9
+ level: LogLevel;
10
+ message: string;
11
+ module: string;
12
+ timestamp: Date;
13
+ }
14
+
15
+ export class Logger {
16
+ private static instance: Logger;
17
+ private currentLevel: LogLevel;
18
+ private moduleName: string;
19
+
20
+ // ANSI color codes - simplified for better readability
21
+ private static readonly COLORS = {
22
+ RESET: '\x1b[0m',
23
+ DEBUG: '\x1b[36m', // Cyan
24
+ INFO: '\x1b[32m', // Green
25
+ WARN: '\x1b[33m', // Yellow
26
+ ERROR: '\x1b[31m', // Red
27
+ };
28
+
29
+ private static readonly LEVEL_NAMES = {
30
+ [LogLevel.DEBUG]: 'DEBUG',
31
+ [LogLevel.INFO]: 'INFO',
32
+ [LogLevel.WARN]: 'WARN',
33
+ [LogLevel.ERROR]: 'ERROR',
34
+ };
35
+
36
+ constructor(moduleName: string = 'root', level: LogLevel = LogLevel.INFO) {
37
+ this.moduleName = moduleName;
38
+ this.currentLevel = level;
39
+ }
40
+
41
+ static getInstance(moduleName?: string, level?: LogLevel): Logger {
42
+ if (!Logger.instance) {
43
+ Logger.instance = new Logger(moduleName || 'root', level);
44
+ }
45
+ return Logger.instance;
46
+ }
47
+
48
+ setLevel(level: LogLevel): void {
49
+ this.currentLevel = level;
50
+ }
51
+
52
+ getLevel(): LogLevel {
53
+ return this.currentLevel;
54
+ }
55
+
56
+ private formatMessage(level: LogLevel, message: string): string {
57
+ const levelName = Logger.LEVEL_NAMES[level];
58
+ const color = Logger.COLORS[levelName as keyof typeof Logger.COLORS];
59
+ const reset = Logger.COLORS.RESET;
60
+
61
+ return `${color}[${levelName}(${this.moduleName})]${reset} ${message}\n\n`;
62
+ }
63
+
64
+ private log(level: LogLevel, message: string): void {
65
+ if (level < this.currentLevel) {
66
+ return;
67
+ }
68
+
69
+ const formattedMessage = this.formatMessage(level, message);
70
+ process.stdout.write(formattedMessage);
71
+ }
72
+
73
+ debug(message: string): void {
74
+ this.log(LogLevel.DEBUG, message);
75
+ }
76
+
77
+ info(message: string): void {
78
+ this.log(LogLevel.INFO, message);
79
+ }
80
+
81
+ warn(message: string): void {
82
+ this.log(LogLevel.WARN, message);
83
+ }
84
+
85
+ error(message: string): void {
86
+ this.log(LogLevel.ERROR, message);
87
+ }
88
+
89
+ // Static methods for quick access
90
+ static debug(message: string, moduleName?: string): void {
91
+ const logger = new Logger(moduleName || 'root');
92
+ logger.debug(message);
93
+ }
94
+
95
+ static info(message: string, moduleName?: string): void {
96
+ const logger = new Logger(moduleName || 'root');
97
+ logger.info(message);
98
+ }
99
+
100
+ static warn(message: string, moduleName?: string): void {
101
+ const logger = new Logger(moduleName || 'root');
102
+ logger.warn(message);
103
+ }
104
+
105
+ static error(message: string, moduleName?: string): void {
106
+ const logger = new Logger(moduleName || 'root');
107
+ logger.error(message);
108
+ }
109
+
110
+ // Method to create a logger instance for a specific module
111
+ static createModuleLogger(moduleName: string, level?: LogLevel): Logger {
112
+ return new Logger(moduleName, level);
113
+ }
114
+ }
115
+
116
+ // Export a default logger instance
117
+ export const logger = Logger.getInstance();
118
+
119
+ // Export convenience functions
120
+ export const log = {
121
+ debug: (message: string, moduleName?: string) => Logger.debug(message, moduleName),
122
+ info: (message: string, moduleName?: string) => Logger.info(message, moduleName),
123
+ warn: (message: string, moduleName?: string) => Logger.warn(message, moduleName),
124
+ error: (message: string, moduleName?: string) => Logger.error(message, moduleName),
125
+ };