altis-claude-harness 1.4.0 → 1.4.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.
- package/bin/cli.js +5 -3
- package/lib/settings.js +25 -9
- package/package.json +1 -1
- package/payload/commands/altis-comandos-update.md +35 -5
- package/payload/hooks/legacy_cleanup.cjs +100 -0
- package/payload/hooks/statusline-command.ps1 +115 -0
- package/payload/mcp-fragment.json +1 -1
- package/payload/settings-fragment.json +14 -0
package/bin/cli.js
CHANGED
|
@@ -24,8 +24,10 @@ function parseArgs(argv) {
|
|
|
24
24
|
return { cmd, target };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function loadFragment() {
|
|
28
|
-
|
|
27
|
+
function loadFragment(target) {
|
|
28
|
+
const raw = fs.readFileSync(path.join(PAYLOAD_DIR, 'settings-fragment.json'), 'utf8');
|
|
29
|
+
const home = target.split(path.sep).join('/');
|
|
30
|
+
return JSON.parse(raw.split('__ALTIS_HOME__').join(home));
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
function loadMcpFragment() {
|
|
@@ -54,7 +56,7 @@ function msgNaoLiberada() {
|
|
|
54
56
|
function runSync(target) {
|
|
55
57
|
const old = readManifest(target);
|
|
56
58
|
const result = syncPayload(PAYLOAD_DIR, target, (old && old.files) || {});
|
|
57
|
-
mergeSettings(path.join(target, 'settings.json'), loadFragment());
|
|
59
|
+
mergeSettings(path.join(target, 'settings.json'), loadFragment(target));
|
|
58
60
|
mergeMcpConfig(mcpConfigPath(target), loadMcpFragment());
|
|
59
61
|
const now = new Date().toISOString();
|
|
60
62
|
const manifest = {
|
package/lib/settings.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
5
|
const ALTIS_MARKER = 'hooks/altis/';
|
|
6
|
+
const STATUSLINE_MARKER = 'statusline-command.ps1';
|
|
6
7
|
|
|
7
8
|
function readSettings(settingsPath) {
|
|
8
9
|
if (!fs.existsSync(settingsPath)) return {};
|
|
@@ -34,6 +35,15 @@ function writeSettings(settingsPath, settings) {
|
|
|
34
35
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
function aplicarStatusLine(settings, statusLine) {
|
|
39
|
+
if (!statusLine) return;
|
|
40
|
+
const atual = settings.statusLine;
|
|
41
|
+
const comandoAtual = atual && typeof atual.command === 'string' ? atual.command : '';
|
|
42
|
+
if (!atual || comandoAtual.includes(STATUSLINE_MARKER)) {
|
|
43
|
+
settings.statusLine = statusLine;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
function mergeSettings(settingsPath, fragment) {
|
|
38
48
|
const settings = readSettings(settingsPath);
|
|
39
49
|
const backedUp = backupOnce(settingsPath);
|
|
@@ -48,6 +58,7 @@ function mergeSettings(settingsPath, fragment) {
|
|
|
48
58
|
const existing = Array.isArray(settings.hooks[event]) ? settings.hooks[event] : [];
|
|
49
59
|
settings.hooks[event] = existing.concat(groups);
|
|
50
60
|
}
|
|
61
|
+
aplicarStatusLine(settings, fragment.statusLine);
|
|
51
62
|
writeSettings(settingsPath, settings);
|
|
52
63
|
return { changed: true, backedUp };
|
|
53
64
|
}
|
|
@@ -55,18 +66,23 @@ function mergeSettings(settingsPath, fragment) {
|
|
|
55
66
|
function removeAltisEntries(settingsPath) {
|
|
56
67
|
if (!fs.existsSync(settingsPath)) return { changed: false };
|
|
57
68
|
const settings = readSettings(settingsPath);
|
|
58
|
-
if (!settings.hooks) return { changed: false };
|
|
59
69
|
let changed = false;
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
if (settings.hooks) {
|
|
71
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
72
|
+
const groups = settings.hooks[event];
|
|
73
|
+
if (!Array.isArray(groups)) continue;
|
|
74
|
+
const kept = groups.filter(g => !isAltisGroup(g));
|
|
75
|
+
if (kept.length !== groups.length) changed = true;
|
|
76
|
+
if (kept.length === 0) delete settings.hooks[event]; else settings.hooks[event] = kept;
|
|
77
|
+
}
|
|
78
|
+
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
79
|
+
}
|
|
80
|
+
if (settings.statusLine && typeof settings.statusLine.command === 'string' && settings.statusLine.command.includes(STATUSLINE_MARKER)) {
|
|
81
|
+
delete settings.statusLine;
|
|
82
|
+
changed = true;
|
|
66
83
|
}
|
|
67
|
-
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
68
84
|
if (changed) writeSettings(settingsPath, settings);
|
|
69
85
|
return { changed };
|
|
70
86
|
}
|
|
71
87
|
|
|
72
|
-
module.exports = { ALTIS_MARKER, mergeSettings, removeAltisEntries, readSettings };
|
|
88
|
+
module.exports = { ALTIS_MARKER, STATUSLINE_MARKER, mergeSettings, removeAltisEntries, readSettings };
|
package/package.json
CHANGED
|
@@ -96,12 +96,39 @@ Mapeie o que foi adicionado (use os tipos/definições **exatamente** como estã
|
|
|
96
96
|
|
|
97
97
|
| Mudança detectada nas linhas `+` | Comando gerado |
|
|
98
98
|
|---|---|
|
|
99
|
-
| **Coluna nova** | `alter table T
|
|
99
|
+
| **Coluna(s) nova(s)** | **UMA só** `alter table T` agrupando **todas** as colunas novas dessa tabela — ver "Agrupamento de colunas novas" abaixo. |
|
|
100
100
|
| **Constraint ALTERADA** (mesma constraint mudou de definição) | `alter table T`<br>`drop constraint C;`<br><br>`alter table T`<br>`add constraint C`<br>`<definição nova exata>;` |
|
|
101
101
|
| **Constraint NOVA** (não existia antes) | `alter table T`<br>`add constraint C`<br>`<definição exata>;` (sem drop) |
|
|
102
102
|
|
|
103
103
|
Para saber se a constraint é **nova** ou **alterada**: é **alterada** se o nome já existia na versão base (`git show "$BASE:<arquivo>"`) com definição diferente; é **nova** se o nome não existia na base.
|
|
104
104
|
|
|
105
|
+
#### Agrupamento de colunas novas — UMA só `alter table` por tabela
|
|
106
|
+
|
|
107
|
+
Quando o diff adiciona **várias colunas à mesma tabela**, gere **um único** `alter table` com todas elas juntas — **nunca** um `alter table ... add` por coluna. Mantenha o tipo e as cláusulas de cada coluna **exatamente** como no script e preserve a ordem em que aparecem no diff.
|
|
108
|
+
|
|
109
|
+
- **Oracle** — sintaxe `add ( ... )`, colunas separadas por vírgula, uma por linha (indentação de 2 espaços), fechando com `);`:
|
|
110
|
+
```sql
|
|
111
|
+
alter table FUNCIONARIOS
|
|
112
|
+
add (
|
|
113
|
+
VALOR_COMISSAO_HORA number(7,2) default 0 not null,
|
|
114
|
+
HORA_INICIO_EXPEDIENTE varchar2(5),
|
|
115
|
+
HORA_INICIO_ALMOCO varchar2(5)
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
- **PostgreSQL** — **não** aceita `add ( ... )`; agrupe com várias cláusulas `add column` separadas por vírgula dentro do **mesmo** `alter table`:
|
|
119
|
+
```sql
|
|
120
|
+
alter table funcionarios
|
|
121
|
+
add column valor_comissao_hora numeric(7,2) not null default 0,
|
|
122
|
+
add column hora_inicio_expediente varchar(5),
|
|
123
|
+
add column hora_inicio_almoco varchar(5);
|
|
124
|
+
```
|
|
125
|
+
- **Uma única coluna nova** → forma simples, sem parênteses (Oracle) / sem repetir `add` (PostgreSQL):
|
|
126
|
+
```sql
|
|
127
|
+
alter table T
|
|
128
|
+
add COLUNA <tipo e cláusulas exatas>;
|
|
129
|
+
```
|
|
130
|
+
- O agrupamento vale **só para colunas**. Constraints continuam **cada uma em seu próprio** `alter table` (pois podem exigir `drop`+`add`) — não as junte no bloco de colunas.
|
|
131
|
+
|
|
105
132
|
### Caso C — Arquivos compartilhados (sempre existentes → trate pelo diff)
|
|
106
133
|
|
|
107
134
|
- **`Sequenciais.sql`** → diff e extraia o **comando `create sequence` COMPLETO** de cada `SEQ_*` adicionada (linhas `+`), exatamente como está no arquivo (incluindo `nocycle nocache noorder` e demais cláusulas). Ex.: `create sequence SEQ_ALERTA_ID nocycle nocache noorder;`
|
|
@@ -169,7 +196,7 @@ Um bloco SQL por banco, em **ordem de aplicação**:
|
|
|
169
196
|
|
|
170
197
|
1. `create sequence` das sequences novas
|
|
171
198
|
2. **Tabelas novas, na ordem topológica calculada no Passo 3** (`NOME_TABELA;`) — referenciada antes de quem referencia
|
|
172
|
-
3. `alter table`
|
|
199
|
+
3. `alter table` de colunas novas em tabelas existentes — **uma única `alter table` por tabela**, agrupando todas as colunas novas dela (Oracle: `add ( ... )`; PostgreSQL: várias `add column` na mesma `alter`)
|
|
173
200
|
4. Constraints alteradas/novas em tabelas existentes (`drop` + `add`, ou só `add`) — incluindo FKs de tabela existente que apontam para tabela nova (já garantidamente criada no passo 2)
|
|
174
201
|
5. Índices novos (`IDX_*;`)
|
|
175
202
|
6. Triggers / Views / Procedures / Functions novos (`NOME;`)
|
|
@@ -189,7 +216,10 @@ ALERTAS_CARGOS;
|
|
|
189
216
|
ALERTAS_IU_BR;
|
|
190
217
|
|
|
191
218
|
alter table CONEXAO_WHATSAPP
|
|
192
|
-
add
|
|
219
|
+
add (
|
|
220
|
+
LIMITE_DIARIO number(10),
|
|
221
|
+
MENSAGEM_PADRAO varchar2(200)
|
|
222
|
+
);
|
|
193
223
|
|
|
194
224
|
alter table CONEXAO_WHATSAPP
|
|
195
225
|
drop constraint CK_CONEXAO_WHATSAPP_ATIVO;
|
|
@@ -204,7 +234,7 @@ check (ATIVO in ('S','N'));
|
|
|
204
234
|
logs_exemplo;
|
|
205
235
|
```
|
|
206
236
|
|
|
207
|
-
> `ALERTAS` aparece **antes** de `ALERTAS_CARGOS` porque esta tem FK para aquela — ordem topológica, não de descoberta. Tabelas novas aparecem **só pelo nome** — sem `alter table`. Os `alter table` são **só** de tabelas que já existiam (ex.: `CONEXAO_WHATSAPP`). Itens "avulsos" e "revisar manualmente", se houver, vão em texto após os blocos, **nunca** dentro do SQL. Se um banco não tiver itens, omita a seção dele.
|
|
237
|
+
> `ALERTAS` aparece **antes** de `ALERTAS_CARGOS` porque esta tem FK para aquela — ordem topológica, não de descoberta. Tabelas novas aparecem **só pelo nome** — sem `alter table`. Os `alter table` são **só** de tabelas que já existiam (ex.: `CONEXAO_WHATSAPP`), e **todas as colunas novas de uma mesma tabela entram num único `alter table ... add ( ... )`** (Oracle) — nunca um `alter` por coluna. Itens "avulsos" e "revisar manualmente", se houver, vão em texto após os blocos, **nunca** dentro do SQL. Se um banco não tiver itens, omita a seção dele.
|
|
208
238
|
|
|
209
239
|
---
|
|
210
240
|
|
|
@@ -225,7 +255,7 @@ logs_exemplo;
|
|
|
225
255
|
- **Sequence nova** → comando `create sequence ...;` completo (não só o nome).
|
|
226
256
|
- **Objeto novo** (tabela, trigger, view, procedure, function, índice) → **só o nome `;`**.
|
|
227
257
|
- **Tabelas novas são ordenadas topologicamente por dependência de FK** (referenciada antes de quem referencia), via helper do Passo 3 — nunca pela ordem de descoberta. FK circular entre tabelas novas é reportada para tratamento manual.
|
|
228
|
-
- **Tabela já existente** com **coluna nova** → `alter table ... add
|
|
258
|
+
- **Tabela já existente** com **coluna(s) nova(s)** → **uma única** `alter table` por tabela agrupando todas as colunas (Oracle: `add ( col1, col2, ... )`; PostgreSQL: várias `add column` na mesma `alter`) — nunca um `alter` por coluna.
|
|
229
259
|
- **Constraint alterada** → `drop constraint` + recriação; **constraint nova** → só `add constraint`.
|
|
230
260
|
- Mudanças destrutivas/ambíguas e scripts avulsos → reportados em texto no chat, fora do bloco SQL.
|
|
231
261
|
- **Bloco SQL sem comentários.** Exibe no chat e, com confirmação, anexa ao arquivo de update.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const ALTIS_MARKER = 'hooks/altis/';
|
|
8
|
+
const LEGACY_SCRIPTS = ['check_delphi_encoding.py', 'delphi_guard.py', 'license_guard.cjs', 'rag_telemetria.cjs', 'auto_update.cjs'];
|
|
9
|
+
const SETTINGS_FILES = ['settings.json', 'settings.local.json'];
|
|
10
|
+
|
|
11
|
+
function removerBom(texto) {
|
|
12
|
+
return typeof texto === 'string' && texto.charCodeAt(0) === 0xfeff ? texto.slice(1) : texto;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ehHookAltisLegado(comando) {
|
|
16
|
+
if (typeof comando !== 'string') return false;
|
|
17
|
+
if (comando.includes(ALTIS_MARKER)) return false;
|
|
18
|
+
return LEGACY_SCRIPTS.some(script => comando.includes(script));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function limparSettings(settings) {
|
|
22
|
+
let alterado = false;
|
|
23
|
+
if (!settings || typeof settings !== 'object' || !settings.hooks || typeof settings.hooks !== 'object') {
|
|
24
|
+
return { settings, alterado };
|
|
25
|
+
}
|
|
26
|
+
for (const evento of Object.keys(settings.hooks)) {
|
|
27
|
+
const grupos = settings.hooks[evento];
|
|
28
|
+
if (!Array.isArray(grupos)) continue;
|
|
29
|
+
const gruposMantidos = [];
|
|
30
|
+
for (const grupo of grupos) {
|
|
31
|
+
if (!grupo || !Array.isArray(grupo.hooks)) {
|
|
32
|
+
gruposMantidos.push(grupo);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const hooksMantidos = grupo.hooks.filter(hook => !ehHookAltisLegado(hook && hook.command));
|
|
36
|
+
if (hooksMantidos.length !== grupo.hooks.length) alterado = true;
|
|
37
|
+
if (hooksMantidos.length > 0) gruposMantidos.push({ ...grupo, hooks: hooksMantidos });
|
|
38
|
+
}
|
|
39
|
+
if (gruposMantidos.length === 0) delete settings.hooks[evento];
|
|
40
|
+
else settings.hooks[evento] = gruposMantidos;
|
|
41
|
+
}
|
|
42
|
+
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
43
|
+
return { settings, alterado };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function alvoClaude() {
|
|
47
|
+
return process.env.ALTIS_CLAUDE_TARGET || path.join(os.homedir(), '.claude');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function logar(mensagem) {
|
|
51
|
+
try {
|
|
52
|
+
fs.appendFileSync(path.join(alvoClaude(), 'hooks', 'altis', 'legacy_cleanup.log'), `${new Date().toISOString()} ${mensagem}\n`);
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function curarArquivo(caminho) {
|
|
57
|
+
let bruto;
|
|
58
|
+
try {
|
|
59
|
+
bruto = fs.readFileSync(caminho, 'utf8');
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
let settings;
|
|
64
|
+
try {
|
|
65
|
+
settings = JSON.parse(removerBom(bruto));
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const { alterado } = limparSettings(settings);
|
|
70
|
+
if (!alterado) return false;
|
|
71
|
+
try {
|
|
72
|
+
const bak = caminho + '.altis-legacy-bak';
|
|
73
|
+
if (!fs.existsSync(bak)) fs.writeFileSync(bak, bruto, 'utf8');
|
|
74
|
+
fs.writeFileSync(caminho, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
75
|
+
logar(`sobra pre-npx removida de ${caminho}`);
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function dirProjeto() {
|
|
83
|
+
try {
|
|
84
|
+
const dados = JSON.parse(removerBom(fs.readFileSync(0, 'utf8')));
|
|
85
|
+
if (dados && typeof dados.cwd === 'string' && dados.cwd) return dados.cwd;
|
|
86
|
+
} catch {}
|
|
87
|
+
return process.cwd();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function principal() {
|
|
91
|
+
try {
|
|
92
|
+
const dirClaude = path.join(dirProjeto(), '.claude');
|
|
93
|
+
for (const nome of SETTINGS_FILES) curarArquivo(path.join(dirClaude, nome));
|
|
94
|
+
} catch {}
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { ehHookAltisLegado, limparSettings, LEGACY_SCRIPTS, ALTIS_MARKER };
|
|
99
|
+
|
|
100
|
+
if (require.main === module) principal();
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Altis Claude Code status line
|
|
2
|
+
# Segments: subprojeto | branch git | modelo | contexto restante | tokens da sessao | uso da assinatura
|
|
3
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
$raw = [Console]::In.ReadToEnd()
|
|
7
|
+
$data = $raw | ConvertFrom-Json
|
|
8
|
+
} catch {
|
|
9
|
+
$data = $null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
$cwd = $null
|
|
13
|
+
if ($data) {
|
|
14
|
+
if ($data.workspace -and $data.workspace.current_dir) { $cwd = $data.workspace.current_dir }
|
|
15
|
+
elseif ($data.cwd) { $cwd = $data.cwd }
|
|
16
|
+
}
|
|
17
|
+
if (-not $cwd) { $cwd = (Get-Location).Path }
|
|
18
|
+
|
|
19
|
+
$esc = [char]27
|
|
20
|
+
$reset = "$esc[0m"
|
|
21
|
+
$cyan = "$esc[36m"
|
|
22
|
+
$green = "$esc[32m"
|
|
23
|
+
$yellow = "$esc[33m"
|
|
24
|
+
$blue = "$esc[34m"
|
|
25
|
+
$magenta = "$esc[35m"
|
|
26
|
+
$red = "$esc[31m"
|
|
27
|
+
$dim = "$esc[2m"
|
|
28
|
+
|
|
29
|
+
function Get-AltisProjectLabel([string]$path) {
|
|
30
|
+
if (-not $path) { return "?" }
|
|
31
|
+
$parts = $path -split '[\\/]' | Where-Object { $_ -ne "" }
|
|
32
|
+
$idx = -1
|
|
33
|
+
for ($i = 0; $i -lt $parts.Length; $i++) {
|
|
34
|
+
if ($parts[$i] -ieq "Orientado a Objetos") { $idx = $i; break }
|
|
35
|
+
}
|
|
36
|
+
$containers = @("SpringBoot", "Angular", "ReactNative", "Android", "Python")
|
|
37
|
+
if ($idx -ge 0 -and ($idx + 1) -lt $parts.Length) {
|
|
38
|
+
$seg = $parts[$idx + 1]
|
|
39
|
+
if ($containers -contains $seg -and ($idx + 2) -lt $parts.Length) {
|
|
40
|
+
return $parts[$idx + 2]
|
|
41
|
+
}
|
|
42
|
+
return $seg
|
|
43
|
+
}
|
|
44
|
+
if ($parts.Length -gt 0) { return $parts[$parts.Length - 1] }
|
|
45
|
+
return "?"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Format-Tokens([double]$n) {
|
|
49
|
+
if ($n -ge 1000000) { return ("{0:0.0}M" -f ($n / 1000000)) }
|
|
50
|
+
if ($n -ge 1000) { return ("{0}k" -f [math]::Round($n / 1000)) }
|
|
51
|
+
return [string][int]$n
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$projectLabel = Get-AltisProjectLabel $cwd
|
|
55
|
+
|
|
56
|
+
$branch = $null
|
|
57
|
+
try {
|
|
58
|
+
$branchRaw = git --no-optional-locks -C "$cwd" branch --show-current 2>$null
|
|
59
|
+
if ($LASTEXITCODE -eq 0 -and $branchRaw) { $branch = $branchRaw.Trim() }
|
|
60
|
+
} catch {
|
|
61
|
+
$branch = $null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
$modelName = "?"
|
|
65
|
+
if ($data -and $data.model -and $data.model.display_name) { $modelName = $data.model.display_name }
|
|
66
|
+
|
|
67
|
+
$ctxPct = $null
|
|
68
|
+
if ($data -and $data.context_window -and $null -ne $data.context_window.remaining_percentage) {
|
|
69
|
+
$ctxPct = [math]::Round([double]$data.context_window.remaining_percentage)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$sessTokens = $null
|
|
73
|
+
if ($data -and $data.context_window) {
|
|
74
|
+
$ti = $data.context_window.total_input_tokens
|
|
75
|
+
$to = $data.context_window.total_output_tokens
|
|
76
|
+
if ($null -ne $ti -or $null -ne $to) {
|
|
77
|
+
$sessTokens = [double]0
|
|
78
|
+
if ($null -ne $ti) { $sessTokens += [double]$ti }
|
|
79
|
+
if ($null -ne $to) { $sessTokens += [double]$to }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
$sub5h = $null
|
|
84
|
+
$sub7d = $null
|
|
85
|
+
$subMax = $null
|
|
86
|
+
if ($data -and $data.rate_limits) {
|
|
87
|
+
if ($data.rate_limits.five_hour -and $null -ne $data.rate_limits.five_hour.used_percentage) {
|
|
88
|
+
$sub5h = [math]::Round([double]$data.rate_limits.five_hour.used_percentage)
|
|
89
|
+
}
|
|
90
|
+
if ($data.rate_limits.seven_day -and $null -ne $data.rate_limits.seven_day.used_percentage) {
|
|
91
|
+
$sub7d = [math]::Round([double]$data.rate_limits.seven_day.used_percentage)
|
|
92
|
+
}
|
|
93
|
+
$vals = @()
|
|
94
|
+
if ($null -ne $sub5h) { $vals += $sub5h }
|
|
95
|
+
if ($null -ne $sub7d) { $vals += $sub7d }
|
|
96
|
+
if ($vals.Count -gt 0) { $subMax = ($vals | Measure-Object -Maximum).Maximum }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$segments = New-Object System.Collections.Generic.List[string]
|
|
100
|
+
$segments.Add("$cyan$projectLabel$reset")
|
|
101
|
+
if ($branch) { $segments.Add("$green$branch$reset") }
|
|
102
|
+
$segments.Add("$yellow$modelName$reset")
|
|
103
|
+
if ($null -ne $ctxPct) { $segments.Add("$blue$ctxPct% ctx$reset") }
|
|
104
|
+
if ($null -ne $sessTokens) { $segments.Add("$magenta$(Format-Tokens $sessTokens) tok$reset") }
|
|
105
|
+
if ($null -ne $subMax) {
|
|
106
|
+
$subColor = $green
|
|
107
|
+
if ($subMax -ge 85) { $subColor = $red } elseif ($subMax -ge 60) { $subColor = $yellow }
|
|
108
|
+
$subParts = @()
|
|
109
|
+
if ($null -ne $sub5h) { $subParts += "5h $sub5h%" }
|
|
110
|
+
if ($null -ne $sub7d) { $subParts += "7d $sub7d%" }
|
|
111
|
+
$segments.Add("$subColor$([string]::Join(' ', $subParts))$reset")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
$sep = "$dim |$reset "
|
|
115
|
+
Write-Output ([string]::Join($sep, $segments))
|
|
@@ -63,6 +63,16 @@
|
|
|
63
63
|
"timeout": 10
|
|
64
64
|
}
|
|
65
65
|
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"hooks": [
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "node \"$HOME/.claude/hooks/altis/legacy_cleanup.cjs\"",
|
|
72
|
+
"shell": "bash",
|
|
73
|
+
"timeout": 10
|
|
74
|
+
}
|
|
75
|
+
]
|
|
66
76
|
}
|
|
67
77
|
],
|
|
68
78
|
"Stop": [
|
|
@@ -89,5 +99,9 @@
|
|
|
89
99
|
]
|
|
90
100
|
}
|
|
91
101
|
]
|
|
102
|
+
},
|
|
103
|
+
"statusLine": {
|
|
104
|
+
"type": "command",
|
|
105
|
+
"command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"__ALTIS_HOME__/hooks/altis/statusline-command.ps1\""
|
|
92
106
|
}
|
|
93
107
|
}
|