@zalom/plastic 2.0.0-alpha.1 → 2.0.0-alpha.3
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.
- package/bin/lib/context_budget.rb +453 -0
- package/bin/plastic-bench +78 -0
- package/hooks/savepoint +5 -5
- package/package.json +1 -1
- package/scripts/append-ledger +16 -3
- package/scripts/day-summary +53 -0
- package/scripts/hook-capture +2 -1
- package/scripts/hook-close +3 -1
- package/scripts/hook-savepoint +45 -0
- package/scripts/hook-session-start +11 -0
- package/scripts/lib/compact_instructions.rb +56 -0
- package/scripts/lib/day_summary.rb +206 -0
- package/scripts/lib/doctor_core.rb +48 -0
- package/scripts/lib/handoff.rb +184 -0
- package/scripts/lib/installer_core.rb +85 -10
- package/scripts/lib/session_close.rb +22 -2
- package/scripts/read-config +3 -0
- package/scripts/rollback.rb +6 -0
- package/scripts/write-handoff +60 -0
- package/skills/intent-continuing/SKILL.md +3 -2
- package/templates/config.yml +5 -0
|
@@ -9,6 +9,7 @@ require "time"
|
|
|
9
9
|
require_relative "hook_registry"
|
|
10
10
|
require_relative "agent_models"
|
|
11
11
|
require_relative "harness_text"
|
|
12
|
+
require_relative "compact_instructions"
|
|
12
13
|
|
|
13
14
|
# Shared installer machinery, instantiable with injected package root / store / agent
|
|
14
15
|
# map so the verb scripts (install/update/uninstall/rollback) and their tests can run
|
|
@@ -33,6 +34,15 @@ class InstallerCore
|
|
|
33
34
|
# Regex matching exactly one managed section (BEGIN line .. END line), non-greedy.
|
|
34
35
|
CODEX_SECTION_RE = /^<!-- BEGIN PLASTIC INTEGRATION.*?-->\n.*?\n<!-- END PLASTIC INTEGRATION -->\n?/m
|
|
35
36
|
|
|
37
|
+
# Claude CLAUDE.md marked-section markers (intent 312). A pair of its own, not the
|
|
38
|
+
# Codex literals: the two managed files can be one file (a user who symlinks
|
|
39
|
+
# ~/.claude/CLAUDE.md at ~/.codex/AGENTS.md, or the reverse), and a shared literal
|
|
40
|
+
# would let one body silently replace the other and an uninstall of one strip both.
|
|
41
|
+
# doctor_core.rb matches these literals structurally, so keep the two in sync by hand.
|
|
42
|
+
CLAUDE_SECTION_BEGIN_PREFIX = "<!-- BEGIN PLASTIC COMPACT"
|
|
43
|
+
CLAUDE_SECTION_END = "<!-- END PLASTIC COMPACT -->"
|
|
44
|
+
CLAUDE_SECTION_RE = /^<!-- BEGIN PLASTIC COMPACT.*?-->\n.*?\n<!-- END PLASTIC COMPACT -->\n?/m
|
|
45
|
+
|
|
36
46
|
# Curated essentials plus a pointer to ~/.plastic/PLASTIC.md and the plastic-conventions
|
|
37
47
|
# skill, injected into ~/.codex/AGENTS.md. Not a slice of PLASTIC.md itself: AGENTS.md is
|
|
38
48
|
# a shared file Codex merges from multiple sources, so this block stays a small,
|
|
@@ -336,6 +346,7 @@ class InstallerCore
|
|
|
336
346
|
"scripts/lib/lock.rb" => "scripts/lib/lock.rb",
|
|
337
347
|
"scripts/plastic-lock" => "scripts/plastic-lock",
|
|
338
348
|
"scripts/lib/hook_registry.rb" => "scripts/lib/hook_registry.rb",
|
|
349
|
+
"scripts/lib/compact_instructions.rb" => "scripts/lib/compact_instructions.rb",
|
|
339
350
|
"scripts/agent-report" => "scripts/agent-report",
|
|
340
351
|
"scripts/lib/insights.rb" => "scripts/lib/insights.rb",
|
|
341
352
|
"scripts/insight-append" => "scripts/insight-append",
|
|
@@ -408,6 +419,11 @@ class InstallerCore
|
|
|
408
419
|
"scripts/lib/session_backfill.rb" => "scripts/lib/session_backfill.rb",
|
|
409
420
|
"scripts/lib/session_close.rb" => "scripts/lib/session_close.rb",
|
|
410
421
|
"scripts/lib/session_git.rb" => "scripts/lib/session_git.rb",
|
|
422
|
+
"scripts/lib/handoff.rb" => "scripts/lib/handoff.rb",
|
|
423
|
+
"scripts/lib/day_summary.rb" => "scripts/lib/day_summary.rb",
|
|
424
|
+
"scripts/write-handoff" => "scripts/write-handoff",
|
|
425
|
+
"scripts/day-summary" => "scripts/day-summary",
|
|
426
|
+
"scripts/hook-savepoint" => "scripts/hook-savepoint",
|
|
411
427
|
}
|
|
412
428
|
end
|
|
413
429
|
|
|
@@ -421,6 +437,8 @@ class InstallerCore
|
|
|
421
437
|
version: 3
|
|
422
438
|
execution_mode: subagent-driven
|
|
423
439
|
stale_threshold_days: 3
|
|
440
|
+
context_offer_tokens: 350000
|
|
441
|
+
context_insist_tokens: 500000
|
|
424
442
|
hash_length: 6
|
|
425
443
|
hash_algorithm: sha256-base36
|
|
426
444
|
max_slug_words: 5
|
|
@@ -779,6 +797,11 @@ class InstallerCore
|
|
|
779
797
|
choice = statusline_choice(settings_path, argv: argv, input: input, reinstall: reinstall)
|
|
780
798
|
merge_claude_hooks(settings_path, choice: choice)
|
|
781
799
|
|
|
800
|
+
# Instruction injection (intent 312): the compact-instructions block into
|
|
801
|
+
# ~/.claude/CLAUDE.md. A partial-ownership user file, so it is NOT manifest-tracked
|
|
802
|
+
# (stripped surgically on uninstall), the same treatment ~/.codex/AGENTS.md gets.
|
|
803
|
+
inject_claude_compact_md(File.join(config[:dir], "CLAUDE.md"))
|
|
804
|
+
|
|
782
805
|
# Write manifest
|
|
783
806
|
manifest_path = File.join(plastic_dir, "manifest.json")
|
|
784
807
|
write_manifest(installed, manifest_path)
|
|
@@ -1344,14 +1367,28 @@ class InstallerCore
|
|
|
1344
1367
|
raise e
|
|
1345
1368
|
end
|
|
1346
1369
|
|
|
1347
|
-
|
|
1370
|
+
# A managed instruction file may be a symlink into a dotfiles repo. write_text_atomic
|
|
1371
|
+
# renames a temp file over its argument, which would replace the link with a regular
|
|
1372
|
+
# file and silently detach it, so every read and write resolves the link first and the
|
|
1373
|
+
# change lands on its target (intent 312).
|
|
1374
|
+
def resolve_managed_path(path)
|
|
1375
|
+
File.symlink?(path) ? File.realpath(path) : path
|
|
1376
|
+
rescue Errno::ENOENT
|
|
1377
|
+
path
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1380
|
+
def marked_section(body: CODEX_AGENTS_MD_BODY, begin_prefix: CODEX_SECTION_BEGIN_PREFIX,
|
|
1381
|
+
end_marker: CODEX_SECTION_END)
|
|
1348
1382
|
hash = Digest::SHA256.hexdigest(body)[0, 12]
|
|
1349
|
-
"#{
|
|
1383
|
+
"#{begin_prefix} hash:#{hash} -->\n#{body.strip}\n#{end_marker}\n"
|
|
1350
1384
|
end
|
|
1351
1385
|
|
|
1352
1386
|
# Returns :created / :appended / :replaced / :refused. Never raises on a normal user file.
|
|
1353
|
-
def
|
|
1354
|
-
|
|
1387
|
+
def inject_marked_section(path, body: CODEX_AGENTS_MD_BODY,
|
|
1388
|
+
begin_prefix: CODEX_SECTION_BEGIN_PREFIX,
|
|
1389
|
+
end_marker: CODEX_SECTION_END, section_re: CODEX_SECTION_RE)
|
|
1390
|
+
section = marked_section(body: body, begin_prefix: begin_prefix, end_marker: end_marker)
|
|
1391
|
+
path = resolve_managed_path(path)
|
|
1355
1392
|
|
|
1356
1393
|
unless File.exist?(path)
|
|
1357
1394
|
FileUtils.mkdir_p(File.dirname(path))
|
|
@@ -1360,14 +1397,14 @@ class InstallerCore
|
|
|
1360
1397
|
end
|
|
1361
1398
|
|
|
1362
1399
|
content = File.read(path)
|
|
1363
|
-
has_begin = content.include?(
|
|
1364
|
-
has_end = content.include?(
|
|
1400
|
+
has_begin = content.include?(begin_prefix)
|
|
1401
|
+
has_end = content.include?(end_marker)
|
|
1365
1402
|
|
|
1366
1403
|
# 22a safety rule: never write if the existing section cannot be parsed.
|
|
1367
1404
|
return :refused if has_begin && !has_end
|
|
1368
1405
|
|
|
1369
1406
|
if has_begin
|
|
1370
|
-
write_text_atomic(path, content.sub(
|
|
1407
|
+
write_text_atomic(path, content.sub(section_re, section))
|
|
1371
1408
|
:replaced
|
|
1372
1409
|
else
|
|
1373
1410
|
base = content.end_with?("\n") ? content : content + "\n"
|
|
@@ -1376,18 +1413,41 @@ class InstallerCore
|
|
|
1376
1413
|
end
|
|
1377
1414
|
end
|
|
1378
1415
|
|
|
1416
|
+
# --- The two blocks Plastic ships, each with its own marker pair ---
|
|
1417
|
+
|
|
1418
|
+
def codex_section(body: CODEX_AGENTS_MD_BODY)
|
|
1419
|
+
marked_section(body: body)
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
def inject_codex_agents_md(path, body: CODEX_AGENTS_MD_BODY)
|
|
1423
|
+
inject_marked_section(path, body: body)
|
|
1424
|
+
end
|
|
1425
|
+
|
|
1426
|
+
# The compact-instructions block for ~/.claude/CLAUDE.md (intent 312).
|
|
1427
|
+
def claude_compact_section(body: CompactInstructions::BODY)
|
|
1428
|
+
marked_section(body: body, begin_prefix: CLAUDE_SECTION_BEGIN_PREFIX,
|
|
1429
|
+
end_marker: CLAUDE_SECTION_END)
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1432
|
+
def inject_claude_compact_md(path, body: CompactInstructions::BODY)
|
|
1433
|
+
inject_marked_section(path, body: body, begin_prefix: CLAUDE_SECTION_BEGIN_PREFIX,
|
|
1434
|
+
end_marker: CLAUDE_SECTION_END, section_re: CLAUDE_SECTION_RE)
|
|
1435
|
+
end
|
|
1436
|
+
|
|
1379
1437
|
# Remove exactly Plastic's managed section from a user-owned AGENTS.md. Preserve all other
|
|
1380
1438
|
# content. Delete the file only if Plastic created it and nothing else remains. Returns the
|
|
1381
1439
|
# path when it acted, nil on no-op. Mirrors remove_claude_hooks: dedicated surgical strip,
|
|
1382
1440
|
# never the manifest whole-file-delete path.
|
|
1383
|
-
def
|
|
1441
|
+
def strip_marked_section(path, begin_prefix: CODEX_SECTION_BEGIN_PREFIX,
|
|
1442
|
+
section_re: CODEX_SECTION_RE)
|
|
1443
|
+
path = resolve_managed_path(path)
|
|
1384
1444
|
return nil unless File.exist?(path)
|
|
1385
1445
|
content = File.read(path)
|
|
1386
|
-
return nil unless content.include?(
|
|
1446
|
+
return nil unless content.include?(begin_prefix)
|
|
1387
1447
|
|
|
1388
1448
|
# Remove the section plus the single separator newline the append introduced, so a
|
|
1389
1449
|
# standard user file round-trips byte-identical.
|
|
1390
|
-
stripped = content.sub(/\n?#{
|
|
1450
|
+
stripped = content.sub(/\n?#{section_re}/, "")
|
|
1391
1451
|
|
|
1392
1452
|
if stripped.strip.empty?
|
|
1393
1453
|
File.delete(path) # Plastic-created file: nothing else left
|
|
@@ -1398,6 +1458,15 @@ class InstallerCore
|
|
|
1398
1458
|
path
|
|
1399
1459
|
end
|
|
1400
1460
|
|
|
1461
|
+
def strip_codex_section(path)
|
|
1462
|
+
strip_marked_section(path)
|
|
1463
|
+
end
|
|
1464
|
+
|
|
1465
|
+
def strip_claude_compact_section(path)
|
|
1466
|
+
strip_marked_section(path, begin_prefix: CLAUDE_SECTION_BEGIN_PREFIX,
|
|
1467
|
+
section_re: CLAUDE_SECTION_RE)
|
|
1468
|
+
end
|
|
1469
|
+
|
|
1401
1470
|
# --- Uninstall ---
|
|
1402
1471
|
|
|
1403
1472
|
def handle_uninstall(uninstall_agents)
|
|
@@ -1424,6 +1493,7 @@ class InstallerCore
|
|
|
1424
1493
|
puts " ls ~/.claude/skills | grep '^plastic-' # → no output"
|
|
1425
1494
|
puts " ls ~/.claude/hooks | grep '^plastic-' # → no output"
|
|
1426
1495
|
puts " grep -c plastic ~/.claude/settings.json # → only hook refs gone"
|
|
1496
|
+
puts " grep 'PLASTIC COMPACT' ~/.claude/CLAUDE.md # → no output"
|
|
1427
1497
|
puts "\n To also delete your intent store: rm -rf #{tilde(plastic_home)}\n\n"
|
|
1428
1498
|
end
|
|
1429
1499
|
|
|
@@ -1470,6 +1540,11 @@ class InstallerCore
|
|
|
1470
1540
|
settings_path = File.join(config[:dir], "settings.json")
|
|
1471
1541
|
remove_claude_hooks(settings_path) if File.exist?(settings_path)
|
|
1472
1542
|
removed.concat(migrate_legacy_plugin(config[:dir]))
|
|
1543
|
+
|
|
1544
|
+
# The compact-instructions block in the user-owned CLAUDE.md (intent 312): a
|
|
1545
|
+
# surgical strip, never the manifest whole-file-delete path above.
|
|
1546
|
+
stripped = strip_claude_compact_section(File.join(config[:dir], "CLAUDE.md"))
|
|
1547
|
+
removed << stripped if stripped
|
|
1473
1548
|
end
|
|
1474
1549
|
|
|
1475
1550
|
# Codex: surgically strip Plastic's marked section from the user-owned AGENTS.md
|
|
@@ -9,12 +9,22 @@
|
|
|
9
9
|
|
|
10
10
|
require "fileutils"
|
|
11
11
|
require_relative "session_ledger"
|
|
12
|
+
require_relative "handoff"
|
|
12
13
|
|
|
13
14
|
module SessionClose
|
|
14
15
|
module_function
|
|
15
16
|
|
|
16
17
|
NOOP_REASONS = %w[clear resume].freeze
|
|
17
18
|
|
|
19
|
+
# The default hand-off writer (intent 311, spec D6): renders this session's
|
|
20
|
+
# share of the pointer day into handoff--<session>.md before the tmp dir
|
|
21
|
+
# goes. The hook script builds it with the shipped templates dir.
|
|
22
|
+
def default_handoff(templates)
|
|
23
|
+
lambda do |store, day, session|
|
|
24
|
+
Handoff.write(store: store, day: day, session: session, trigger: "close", templates: templates)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
18
28
|
# The default spawner starts the day filer detached so a slow filing never
|
|
19
29
|
# blocks the harness shutdown (Codex kills a SessionEnd hook after 3 s).
|
|
20
30
|
def default_spawner(script)
|
|
@@ -26,8 +36,8 @@ module SessionClose
|
|
|
26
36
|
end
|
|
27
37
|
|
|
28
38
|
# Returns a small report hash; never raises.
|
|
29
|
-
def run(payload:, store:, today:, spawner:, now: Time.now)
|
|
30
|
-
report = { reason: nil, dropped: 0, removed_tmp: false, spawned: nil }
|
|
39
|
+
def run(payload:, store:, today:, spawner:, handoff: nil, now: Time.now)
|
|
40
|
+
report = { reason: nil, dropped: 0, removed_tmp: false, spawned: nil, handoff: false }
|
|
31
41
|
reason = payload.is_a?(Hash) ? payload["reason"].to_s : ""
|
|
32
42
|
report[:reason] = reason
|
|
33
43
|
return report if NOOP_REASONS.include?(reason)
|
|
@@ -49,6 +59,16 @@ module SessionClose
|
|
|
49
59
|
count
|
|
50
60
|
end || 0
|
|
51
61
|
|
|
62
|
+
# The hand-off (intent 311, spec D6) is written after the drop, so it
|
|
63
|
+
# reflects it, and before the tmp dir goes, since the pointer lives there.
|
|
64
|
+
# An intent pointer falls back to today, the same as the drop above.
|
|
65
|
+
if handoff
|
|
66
|
+
report[:handoff] = safely do
|
|
67
|
+
handoff.call(store, pointer_day, session)
|
|
68
|
+
true
|
|
69
|
+
end || false
|
|
70
|
+
end
|
|
71
|
+
|
|
52
72
|
report[:removed_tmp] = safely do
|
|
53
73
|
dir = SessionLedger.session_tmp_dir(store, session)
|
|
54
74
|
FileUtils.rm_rf(dir) if Dir.exist?(dir)
|
package/scripts/read-config
CHANGED
|
@@ -14,6 +14,9 @@ require_relative "lib/agent_models"
|
|
|
14
14
|
DEFAULTS = {
|
|
15
15
|
"version" => 3,
|
|
16
16
|
"stale_threshold_days" => 3,
|
|
17
|
+
# Absolute token counts for a 1M window, 35 and 50 percent (intent 312, 296 D38).
|
|
18
|
+
"context_offer_tokens" => 350_000,
|
|
19
|
+
"context_insist_tokens" => 500_000,
|
|
17
20
|
"execution_mode" => "subagent-driven",
|
|
18
21
|
"hash_length" => 6,
|
|
19
22
|
"hash_algorithm" => "sha256-base36",
|
package/scripts/rollback.rb
CHANGED
|
@@ -118,6 +118,12 @@ class Rollback < InstallerCore
|
|
|
118
118
|
remove_claude_hooks(path)
|
|
119
119
|
stripped << path
|
|
120
120
|
end
|
|
121
|
+
# The compact-instructions block (intent 312), for the same reason: no older
|
|
122
|
+
# package knows the section exists, so nothing there would ever replace or
|
|
123
|
+
# remove it. The Codex AGENTS.md section needs no equivalent: every older
|
|
124
|
+
# package knows that one and rewrites it on the downgrade install.
|
|
125
|
+
claude_md = strip_claude_compact_section(File.join(agent[:dir], "CLAUDE.md"))
|
|
126
|
+
stripped << claude_md if claude_md
|
|
121
127
|
when "codex"
|
|
122
128
|
path = agent[:home_dir] && File.join(agent[:home_dir], "hooks.json")
|
|
123
129
|
if path && File.exist?(path)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# write-handoff (intent 311): render and write one session's hand-off into
|
|
6
|
+
# the day directory (.sessions/<day>/handoff--<session>.md). Derived from the
|
|
7
|
+
# day ledger, so it can be regenerated at any time. Prints the path.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# write-handoff --trigger tick|precompact|close [--store <dir>] [--day <YYYYMMDD>]
|
|
11
|
+
# [--session <id>] [--templates <dir>]
|
|
12
|
+
#
|
|
13
|
+
# Defaults match append-ledger: the store is $PLASTIC_HOME/store, the day is
|
|
14
|
+
# today (local wall clock), the session is $CLAUDE_CODE_SESSION_ID, and the
|
|
15
|
+
# templates dir is the one shipped next to this script.
|
|
16
|
+
#
|
|
17
|
+
# Exit codes: 0 done; 2 usage error.
|
|
18
|
+
|
|
19
|
+
require_relative "lib/session_ledger"
|
|
20
|
+
require_relative "lib/handoff"
|
|
21
|
+
|
|
22
|
+
def usage_abort(message)
|
|
23
|
+
warn "write-handoff: #{message}"
|
|
24
|
+
exit 2
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def expand(path)
|
|
28
|
+
File.expand_path(path.to_s.sub(/\A~/, Dir.home))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_args(argv)
|
|
32
|
+
opts = {}
|
|
33
|
+
i = 0
|
|
34
|
+
while i < argv.length
|
|
35
|
+
flag = argv[i]
|
|
36
|
+
usage_abort("unknown argument #{flag.inspect}") unless flag.start_with?("--")
|
|
37
|
+
usage_abort("#{flag} requires a value") if i + 1 >= argv.length
|
|
38
|
+
key = flag.delete_prefix("--").to_sym
|
|
39
|
+
usage_abort("unknown flag #{flag.inspect}") unless %i[store day session trigger templates].include?(key)
|
|
40
|
+
opts[key] = argv[i + 1]
|
|
41
|
+
i += 2
|
|
42
|
+
end
|
|
43
|
+
opts
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
opts = parse_args(ARGV)
|
|
47
|
+
usage_abort("--trigger is required (#{Handoff::TRIGGERS.join(', ')})") unless opts[:trigger]
|
|
48
|
+
usage_abort("--trigger must be one of #{Handoff::TRIGGERS.join(', ')}") unless Handoff::TRIGGERS.include?(opts[:trigger])
|
|
49
|
+
|
|
50
|
+
plastic_home = expand(ENV.fetch("PLASTIC_HOME", "~/.plastic"))
|
|
51
|
+
store = opts[:store] ? expand(opts[:store]) : File.join(plastic_home, "store")
|
|
52
|
+
templates = opts[:templates] ? expand(opts[:templates]) : File.expand_path("../templates", __dir__)
|
|
53
|
+
usage_abort("templates dir not found: #{templates}") unless Dir.exist?(templates)
|
|
54
|
+
|
|
55
|
+
day = opts[:day] || SessionLedger.day_id
|
|
56
|
+
usage_abort("--day must be eight digits parsing as a real date, got #{day.inspect}") unless SessionLedger.valid_day_id?(day)
|
|
57
|
+
session = SessionLedger.short_session_id(opts[:session], ENV["CLAUDE_CODE_SESSION_ID"])
|
|
58
|
+
|
|
59
|
+
puts Handoff.write(store: store, day: day, session: session, trigger: opts[:trigger], templates: templates)
|
|
60
|
+
exit 0
|
|
@@ -95,8 +95,9 @@ For a live intent's directory:
|
|
|
95
95
|
```bash
|
|
96
96
|
ruby -r ~/.plastic/scripts/lib/savepoint -e 'Savepoint.rebuild_savepoint("<intent_dir>")'
|
|
97
97
|
```
|
|
98
|
-
3. **Read the hand-off.**
|
|
99
|
-
session's own account of where things stand; read
|
|
98
|
+
3. **Read the hand-off.** The newest `~/.plastic/store/.sessions/<day>/handoff--*.md` (today,
|
|
99
|
+
else the newest prior day) is the prior session's own account of where things stand; read
|
|
100
|
+
it after the ledger, never instead of it.
|
|
100
101
|
4. **Derive the next step:** the first unchecked item in `checklist.md` when it exists, else
|
|
101
102
|
the next thing the station needs (see the matrix). The newest `## Insights` entry supplies
|
|
102
103
|
the human-readable context; an entry marked `(autonomous)` means an auto team was
|
package/templates/config.yml
CHANGED
|
@@ -2,6 +2,11 @@ version: 3
|
|
|
2
2
|
project_roots:
|
|
3
3
|
- ~/.plastic/projects
|
|
4
4
|
stale_threshold_days: 3
|
|
5
|
+
# When to compact. Absolute token counts for a 1M window (35 and 50 percent), not
|
|
6
|
+
# percentages: models are reliable only to roughly 50 to 65 percent of advertised
|
|
7
|
+
# context, so a percentage that is right at 200k floats to an untested size at 1M.
|
|
8
|
+
context_offer_tokens: 350000
|
|
9
|
+
context_insist_tokens: 500000
|
|
5
10
|
execution_mode: subagent-driven
|
|
6
11
|
hash_length: 6
|
|
7
12
|
hash_algorithm: sha256-base36
|