portable-forge 0.1.0__py3-none-any.whl

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 (84) hide show
  1. forge/FIRST_BOOT.txt +147 -0
  2. forge/__init__.py +45 -0
  3. forge/__main__.py +9 -0
  4. forge/standard.py +340 -0
  5. forge_core/__init__.py +1 -0
  6. forge_core/aliases.py +228 -0
  7. forge_core/ast_search.py +269 -0
  8. forge_core/ast_tools.py +298 -0
  9. forge_core/codegraph.py +602 -0
  10. forge_core/config.py +439 -0
  11. forge_core/core_guard.py +107 -0
  12. forge_core/docs.py +239 -0
  13. forge_core/engine.py +206 -0
  14. forge_core/environment.py +317 -0
  15. forge_core/file_safety.py +92 -0
  16. forge_core/hinting.py +611 -0
  17. forge_core/language.py +87 -0
  18. forge_core/models.py +59 -0
  19. forge_core/parser.py +273 -0
  20. forge_core/preparse.py +206 -0
  21. forge_core/presentation/__init__.py +14 -0
  22. forge_core/presentation/plain.py +9 -0
  23. forge_core/presentation/standard.py +139 -0
  24. forge_core/protocol/__init__.py +6 -0
  25. forge_core/protocol/packet.py +234 -0
  26. forge_core/registry.py +151 -0
  27. forge_core/run_storage.py +991 -0
  28. forge_core/runner.py +169 -0
  29. forge_core/source_edit.py +93 -0
  30. forge_packages/__init__.py +1 -0
  31. forge_packages/core_ops/__init__.py +1 -0
  32. forge_packages/core_ops/branch/README.txt +7 -0
  33. forge_packages/core_ops/branch/manifest.py +9 -0
  34. forge_packages/core_ops/branch/op.py +1017 -0
  35. forge_packages/core_ops/copy/README.txt +25 -0
  36. forge_packages/core_ops/copy/manifest.py +9 -0
  37. forge_packages/core_ops/copy/op.py +177 -0
  38. forge_packages/core_ops/delete/README.txt +65 -0
  39. forge_packages/core_ops/delete/manifest.py +9 -0
  40. forge_packages/core_ops/delete/op.py +331 -0
  41. forge_packages/core_ops/diff/README.txt +80 -0
  42. forge_packages/core_ops/diff/manifest.py +11 -0
  43. forge_packages/core_ops/diff/op.py +453 -0
  44. forge_packages/core_ops/forge/README.txt +40 -0
  45. forge_packages/core_ops/forge/manifest.py +11 -0
  46. forge_packages/core_ops/forge/op.py +1246 -0
  47. forge_packages/core_ops/insert/README.txt +216 -0
  48. forge_packages/core_ops/insert/manifest.py +7 -0
  49. forge_packages/core_ops/insert/op.py +539 -0
  50. forge_packages/core_ops/map/README.txt +237 -0
  51. forge_packages/core_ops/map/manifest.py +11 -0
  52. forge_packages/core_ops/map/op.py +1492 -0
  53. forge_packages/core_ops/read/README.txt +143 -0
  54. forge_packages/core_ops/read/manifest.py +9 -0
  55. forge_packages/core_ops/read/op.py +338 -0
  56. forge_packages/core_ops/replace/README.txt +231 -0
  57. forge_packages/core_ops/replace/manifest.py +11 -0
  58. forge_packages/core_ops/replace/op.py +560 -0
  59. forge_packages/core_ops/revert/README.txt +19 -0
  60. forge_packages/core_ops/revert/manifest.py +11 -0
  61. forge_packages/core_ops/revert/op.py +126 -0
  62. forge_packages/core_ops/run/README.txt +23 -0
  63. forge_packages/core_ops/run/manifest.py +11 -0
  64. forge_packages/core_ops/run/op.py +351 -0
  65. forge_packages/core_ops/search/README.txt +238 -0
  66. forge_packages/core_ops/search/manifest.py +11 -0
  67. forge_packages/core_ops/search/op.py +678 -0
  68. forge_packages/core_ops/url/README.txt +37 -0
  69. forge_packages/core_ops/url/manifest.py +9 -0
  70. forge_packages/core_ops/url/op.py +398 -0
  71. forge_packages/core_ops/write/README.txt +35 -0
  72. forge_packages/core_ops/write/manifest.py +11 -0
  73. forge_packages/core_ops/write/op.py +302 -0
  74. forge_packages/custom_ops/__init__.py +1 -0
  75. forge_packages/custom_ops/alias/README.txt +51 -0
  76. forge_packages/custom_ops/alias/expander.py +17 -0
  77. forge_packages/custom_ops/alias/manifest.py +11 -0
  78. forge_packages/custom_ops/alias/op.py +729 -0
  79. portable_forge-0.1.0.dist-info/METADATA +734 -0
  80. portable_forge-0.1.0.dist-info/RECORD +84 -0
  81. portable_forge-0.1.0.dist-info/WHEEL +5 -0
  82. portable_forge-0.1.0.dist-info/entry_points.txt +2 -0
  83. portable_forge-0.1.0.dist-info/licenses/LICENSE +21 -0
  84. portable_forge-0.1.0.dist-info/top_level.txt +3 -0
forge/FIRST_BOOT.txt ADDED
@@ -0,0 +1,147 @@
1
+ FORGE FIRST BOOT
2
+ ================
3
+
4
+ Forge is a local Python runtime for human + AI pair-programming.
5
+
6
+ The assistant proposes.
7
+ The user runs.
8
+ Forge reports.
9
+ The packet confirms.
10
+
11
+ The run packet is ground truth.
12
+
13
+
14
+ FIRST RESPONSE
15
+ --------------
16
+
17
+ At the start of a cold Forge session, output this read-only bundle first:
18
+
19
+ MAP .
20
+ DEPTH: 2
21
+
22
+ FORGE ops
23
+
24
+ Return the bundle as one clean, copyable code block with no explanation
25
+ inside the block.
26
+
27
+ Do not explain it first.
28
+
29
+ Wait for the returned packet. Inspect it, then ask what the user wants to
30
+ work on. Do not assume the task is Forge itself.
31
+
32
+
33
+ THE LOOP
34
+ --------
35
+
36
+ 1. Write a plain-text Forge bundle.
37
+ 2. The user runs it locally.
38
+ 3. Forge parses the whole bundle before executing anything.
39
+ 4. Forge executes against project_root.
40
+ 5. Forge returns a packet.
41
+ 6. Read that packet before continuing.
42
+
43
+ Nothing happened unless the packet says it happened.
44
+
45
+ Never claim a local change without a returned packet.
46
+
47
+ New syntax created by one bundle is available only to a later run because
48
+ the complete bundle is parsed before execution starts.
49
+
50
+
51
+ WORKING RULES
52
+ -------------
53
+
54
+ - Return executable Forge bundles as one clean, copyable code block.
55
+ - Keep explanation outside the bundle.
56
+ - Prefer small bundles.
57
+ - Inspect before editing.
58
+ - Use MAP for structure.
59
+ - Use SEARCH to locate candidate areas.
60
+ - Use READ for exact content before editing.
61
+ - Use the smallest write operation that fits.
62
+ - Use RUN for relevant tests.
63
+ - Use DIFF to review changes.
64
+ - Use BRANCH before risky work when useful.
65
+ - Read errors and hints before retrying failures.
66
+ - Never invent Forge syntax or directives.
67
+ - Use FORGE help <OP> when syntax is unclear.
68
+ - Use FORGE runs for stored run history.
69
+
70
+
71
+ FAILURE RECOVERY
72
+ ----------------
73
+
74
+ When a run fails:
75
+
76
+ 1. Read ERRORS, HINTS, and PREVIEW before retrying.
77
+ 2. Identify the failed operation and failure type.
78
+ 3. Follow the Forge hint when one is present.
79
+ 4. Use FORGE help <OP> if syntax or the operation contract is unclear.
80
+ 5. Inspect the relevant file or state before changing anything.
81
+ 6. Make the smallest correction that addresses the reported failure.
82
+ 7. Run again and read the new packet.
83
+
84
+ Do not guess Forge syntax after a failure.
85
+
86
+
87
+ PUBLIC LANGUAGE
88
+ ---------------
89
+
90
+ FORGE
91
+ MAP
92
+ READ
93
+ SEARCH
94
+
95
+ WRITE
96
+ REPLACE
97
+ INSERT
98
+ DELETE
99
+ COPY
100
+
101
+ RUN
102
+ DIFF
103
+ REVERT
104
+ BRANCH
105
+
106
+ URL
107
+ ALIAS
108
+
109
+ Use:
110
+
111
+ FORGE ops
112
+
113
+ for the public language.
114
+
115
+ Use:
116
+
117
+ FORGE ops all
118
+
119
+ to inspect installed extensions.
120
+
121
+
122
+ NORMAL EDIT LOOP
123
+ ----------------
124
+
125
+ MAP path
126
+
127
+ READ path
128
+
129
+ REPLACE path
130
+ ...
131
+
132
+ RUN relevant_test.py
133
+
134
+ DIFF current
135
+
136
+
137
+ OUTPUT
138
+ ------
139
+
140
+ The canonical packet is the AI-facing audit trail.
141
+
142
+ Standard Forge prints that packet unchanged, followed by a small human
143
+ summary.
144
+
145
+ A host may change how bundle text enters Forge or where rendered output goes.
146
+ It must not change Forge execution semantics or the meaning of the canonical
147
+ packet.
forge/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Portable Forge public Python API.
4
+
5
+ Normal use:
6
+
7
+ import forge
8
+
9
+ run = forge.run_text(
10
+ bundle,
11
+ project_root='/path/to/project',
12
+ )
13
+
14
+ print(
15
+ forge.render_standard(run)
16
+ )
17
+
18
+ Advanced callers may build and supply an explicit environment.
19
+ """
20
+
21
+ from forge_core.environment import make_environment
22
+ from forge_core.presentation.standard import (
23
+ format_summary,
24
+ render_standard,
25
+ )
26
+
27
+ from .standard import (
28
+ default_forge_home,
29
+ first_boot_text,
30
+ main,
31
+ run_text,
32
+ standard_environment,
33
+ )
34
+
35
+
36
+ __all__ = [
37
+ 'default_forge_home',
38
+ 'first_boot_text',
39
+ 'format_summary',
40
+ 'main',
41
+ 'make_environment',
42
+ 'render_standard',
43
+ 'run_text',
44
+ 'standard_environment',
45
+ ]
forge/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .standard import main
4
+
5
+
6
+ if __name__ == '__main__':
7
+ raise SystemExit(
8
+ main()
9
+ )
forge/standard.py ADDED
@@ -0,0 +1,340 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Standard host for portable Forge.
4
+
5
+ This is the ordinary Python edge around the portable core.
6
+
7
+ Host responsibilities:
8
+ - choose project_root when the caller does not
9
+ - choose writable Forge home
10
+ - load user configuration
11
+ - construct resolved environment
12
+ - read bundle input for CLI use
13
+ - print standard presentation
14
+
15
+ Portable core remains responsible for parsing, execution, storage semantics,
16
+ recovery data, and canonical packet generation.
17
+ """
18
+
19
+ import argparse
20
+ import os
21
+ import sys
22
+
23
+ from forge_core.config import (
24
+ load_config,
25
+ resolve_config,
26
+ )
27
+ from forge_core.environment import (
28
+ make_environment,
29
+ )
30
+ from forge_core.presentation.standard import (
31
+ render_standard,
32
+ )
33
+ from forge_core.runner import (
34
+ run_text as core_run_text,
35
+ )
36
+
37
+
38
+ HERE = os.path.dirname(
39
+ os.path.abspath(__file__)
40
+ )
41
+
42
+ FIRST_BOOT_PATH = os.path.join(
43
+ HERE,
44
+ 'FIRST_BOOT.txt',
45
+ )
46
+
47
+
48
+ def _absolute(path):
49
+ return os.path.abspath(
50
+ os.path.expanduser(
51
+ str(path)
52
+ )
53
+ )
54
+
55
+
56
+ def default_forge_home():
57
+ """
58
+ Return the standard host's writable Forge home.
59
+
60
+ Installed source code is not used as writable state.
61
+ """
62
+ configured = str(
63
+ os.environ.get(
64
+ 'FORGE_HOME'
65
+ )
66
+ or ''
67
+ ).strip()
68
+
69
+ if configured:
70
+ return _absolute(
71
+ configured
72
+ )
73
+
74
+ return _absolute(
75
+ '~/.forge'
76
+ )
77
+
78
+
79
+ def standard_environment(
80
+ project_root=None,
81
+ forge_home=None,
82
+ capabilities=None,
83
+ config_path=None,
84
+ ):
85
+ """Resolve ordinary Python host facts into one Forge environment."""
86
+ root = _absolute(
87
+ project_root
88
+ or os.getcwd()
89
+ )
90
+
91
+ home = _absolute(
92
+ forge_home
93
+ or default_forge_home()
94
+ )
95
+
96
+ os.makedirs(
97
+ home,
98
+ exist_ok=True,
99
+ )
100
+
101
+ config = load_config(
102
+ home,
103
+ path=config_path,
104
+ )
105
+
106
+ resolved = resolve_config(
107
+ config,
108
+ home,
109
+ path=config_path,
110
+ )
111
+
112
+ return make_environment(
113
+ project_root=root,
114
+ forge_home=home,
115
+ storage_root=resolved[
116
+ 'storage_root'
117
+ ],
118
+ aliases_path=resolved[
119
+ 'aliases_path'
120
+ ],
121
+ capabilities=(
122
+ capabilities
123
+ or {}
124
+ ),
125
+ host='standard',
126
+ storage=resolved[
127
+ 'storage'
128
+ ],
129
+ features=resolved[
130
+ 'features'
131
+ ],
132
+ config_path=resolved[
133
+ 'config_path'
134
+ ],
135
+ )
136
+
137
+
138
+ def run_text(
139
+ bundle_text,
140
+ project_root=None,
141
+ mode='dev',
142
+ store=True,
143
+ environment=None,
144
+ forge_home=None,
145
+ capabilities=None,
146
+ config_path=None,
147
+ ):
148
+ """
149
+ Execute Forge from normal Python code.
150
+
151
+ Supplying environment gives the caller complete control.
152
+
153
+ Otherwise the standard host resolves project_root and writable Forge home.
154
+ """
155
+ if environment is None:
156
+ environment = standard_environment(
157
+ project_root=project_root,
158
+ forge_home=forge_home,
159
+ capabilities=capabilities,
160
+ config_path=config_path,
161
+ )
162
+
163
+ return core_run_text(
164
+ bundle_text,
165
+ project_root=environment.get(
166
+ 'project_root'
167
+ ),
168
+ mode=mode,
169
+ store=store,
170
+ environment=environment,
171
+ )
172
+
173
+
174
+ def first_boot_text():
175
+ """Return the portable Forge first-boot prompt."""
176
+ with open(
177
+ FIRST_BOOT_PATH,
178
+ 'r',
179
+ encoding='utf-8',
180
+ ) as handle:
181
+ return handle.read()
182
+
183
+
184
+ def _parser():
185
+ parser = argparse.ArgumentParser(
186
+ prog='python -m forge',
187
+ description=(
188
+ 'Run a Forge bundle using the standard pure-Python host.'
189
+ ),
190
+ )
191
+
192
+ parser.add_argument(
193
+ 'bundle_file',
194
+ nargs='?',
195
+ help='Bundle text file. If omitted, read stdin.',
196
+ )
197
+
198
+ parser.add_argument(
199
+ '--project',
200
+ help='Project root. Defaults to current working directory.',
201
+ )
202
+
203
+ parser.add_argument(
204
+ '--home',
205
+ help='Writable Forge home. Defaults to FORGE_HOME or ~/.forge.',
206
+ )
207
+
208
+ parser.add_argument(
209
+ '--config',
210
+ help='Optional forge.json path.',
211
+ )
212
+
213
+ parser.add_argument(
214
+ '--mode',
215
+ default='dev',
216
+ help='Run storage lane. Default: dev.',
217
+ )
218
+
219
+ parser.add_argument(
220
+ '--no-store',
221
+ action='store_true',
222
+ help='Do not persist this run.',
223
+ )
224
+
225
+ parser.add_argument(
226
+ '--first-boot',
227
+ action='store_true',
228
+ help='Print the portable AI first-boot prompt.',
229
+ )
230
+
231
+ return parser
232
+
233
+
234
+ def _read_bundle(args, stdin):
235
+ if args.bundle_file:
236
+ with open(
237
+ args.bundle_file,
238
+ 'r',
239
+ encoding='utf-8',
240
+ ) as handle:
241
+ return handle.read()
242
+
243
+ if (
244
+ hasattr(
245
+ stdin,
246
+ 'isatty',
247
+ )
248
+ and stdin.isatty()
249
+ ):
250
+ return None
251
+
252
+ return stdin.read()
253
+
254
+
255
+ def main(
256
+ argv=None,
257
+ stdin=None,
258
+ stdout=None,
259
+ ):
260
+ """Standard CLI entrypoint."""
261
+ argv = list(
262
+ sys.argv[1:]
263
+ if argv is None
264
+ else argv
265
+ )
266
+
267
+ stdin = (
268
+ sys.stdin
269
+ if stdin is None
270
+ else stdin
271
+ )
272
+
273
+ stdout = (
274
+ sys.stdout
275
+ if stdout is None
276
+ else stdout
277
+ )
278
+
279
+ parser = _parser()
280
+ args = parser.parse_args(
281
+ argv
282
+ )
283
+
284
+ if args.first_boot:
285
+ text = first_boot_text()
286
+
287
+ stdout.write(
288
+ text
289
+ )
290
+
291
+ if (
292
+ text
293
+ and not text.endswith('\n')
294
+ ):
295
+ stdout.write('\n')
296
+
297
+ return 0
298
+
299
+ bundle = _read_bundle(
300
+ args,
301
+ stdin,
302
+ )
303
+
304
+ if bundle is None:
305
+ parser.print_help(
306
+ file=stdout
307
+ )
308
+ return 2
309
+
310
+ if not str(bundle).strip():
311
+ stdout.write(
312
+ 'Forge: no bundle text supplied.\n'
313
+ )
314
+ return 2
315
+
316
+ run = run_text(
317
+ bundle,
318
+ project_root=args.project,
319
+ forge_home=args.home,
320
+ mode=args.mode,
321
+ store=not args.no_store,
322
+ config_path=args.config,
323
+ )
324
+
325
+ stdout.write(
326
+ render_standard(
327
+ run
328
+ )
329
+ )
330
+
331
+ if (
332
+ str(
333
+ run.get('status')
334
+ or ''
335
+ ).upper()
336
+ == 'APPLIED'
337
+ ):
338
+ return 0
339
+
340
+ return 1
forge_core/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # forge_reboot core package