Erste Fassung: Claude-Werkzeuge aus dem Vault
.vault-tools (ohne trading-plattform-pruefung und watermarks-remover), .claude/agents, .claude/skills, .claude/workflows, .claude/settings.json. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
fa1c26b407
137 files changed
+43926
No files matched your search
@@ -0,0 +1,150 @@
|
||||
// Attrappe für die Workflow-Skripte unter .claude/workflows/: führt ein Skript mit nachgebauten
|
||||
// Globals aus und protokolliert jeden agent()-Aufruf mit Modell, Phase, Start und Ende.
|
||||
//
|
||||
// Grenzen wie im Ziel (Workflow-Referenz der Claude-Code-Sitzung vom 2026-09-24, dazu die
|
||||
// Plankritik vom selben Tag am Laufzeitsystem 2.1.280):
|
||||
// - workflow() in einem Kind wirft; Verschachtelung geht nur eine Ebene tief. Das Ziel meldet
|
||||
// "workflow() cannot be called from within a child workflow", dann einen Gedankenstrich und
|
||||
// "nesting is limited to one level"; die Attrappe setzt einen Doppelpunkt statt des Strichs.
|
||||
// - Im Kind ist phase() wirkungslos, und jeder agent() bekommt die Phase "▸ <meta.name>", ab dem
|
||||
// zweiten Aufruf desselben Kindes "▸ <meta.name> #<n>" (Bundle 2.1.280 und meta.json eines
|
||||
// echten Laufs mit drei Ablesungen: "▸ verbrauch", "▸ verbrauch #2", "▸ verbrauch #3").
|
||||
// - workflow() mit unbekanntem Namen oder unlesbarem scriptPath wirft.
|
||||
// - Date.now(), new Date() ohne Argument und Math.random() werfen.
|
||||
// - agent() mit schema braucht type object und properties, required muss in properties stehen.
|
||||
// Ein Ergebnis ohne die required-Felder wird wie ein gescheiterter Agent zu null.
|
||||
// - parallel() ist eine Barriere; ein werfender Thunk wird zu null.
|
||||
// Zusätzlich als Verstoß protokolliert (Stamm, "Agenten & Modelle"): agent() ohne model.
|
||||
// Was die Attrappe nicht nachbildet: die Obergrenze gleichzeitiger Agenten je Lauf, Budget,
|
||||
// Resume und das Verhalten echter Modelle. Sie ist ein Fangnetz für die Steuerlogik der
|
||||
// Skripte, kein Nachweis für einen echten Lauf.
|
||||
import { readFileSync, existsSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
// Aufrufe über den echten Pfad werden in den Ordner unter Test umgeleitet.
|
||||
const ECHTER_ORDNER = 'D:/Claude/.claude/workflows/'
|
||||
|
||||
class DatumAttrappe extends Date {
|
||||
constructor(...a) {
|
||||
if (a.length === 0) throw new Error('new Date() ohne Argument ist im Workflow nicht erlaubt')
|
||||
super(...a)
|
||||
}
|
||||
static now() { throw new Error('Date.now() ist im Workflow nicht erlaubt') }
|
||||
}
|
||||
const MatheAttrappe = Object.create(Math, {
|
||||
random: { value: () => { throw new Error('Math.random() ist im Workflow nicht erlaubt') } },
|
||||
})
|
||||
|
||||
function lade(datei) {
|
||||
if (!existsSync(datei)) throw new Error('Workflow-Datei nicht lesbar: ' + datei)
|
||||
const text = readFileSync(datei, 'utf8')
|
||||
if (!/^export const meta = /m.test(text)) throw new Error(datei + ': export const meta fehlt')
|
||||
const name = /^export const meta = \{[\s\S]*?\bname:\s*'([^']+)'/m.exec(text)
|
||||
const quelle = text.replace(/^export const meta = /m, 'const meta = ')
|
||||
return { name: name ? name[1] : path.basename(datei, '.js'), fn: new AsyncFunction('agent', 'parallel', 'pipeline', 'phase', 'log', 'workflow', 'args', 'budget', 'Date', 'Math', quelle) }
|
||||
}
|
||||
|
||||
const warte = ms => new Promise(r => setTimeout(r, ms))
|
||||
|
||||
// antworte(aufruf) liefert { wert, dauer } oder undefined (Agent ohne Ergebnis) oder wirft.
|
||||
// aufruf trägt label, phase, modell, effort, agentType, prompt, tiefe und schema.
|
||||
export async function fuehreAus(datei, args, antworte, optionen = {}) {
|
||||
const ordner = optionen.ordner || path.dirname(datei)
|
||||
const protokoll = { agents: [], logs: [], phasen: [], verstoesse: [], spitze: {}, spitzeJePhase: {}, workflows: [] }
|
||||
const laufend = {}
|
||||
const laufendJePhase = {}
|
||||
const kindAufrufe = {}
|
||||
let uhr = 0
|
||||
|
||||
function aufloesen(ref) {
|
||||
if (typeof ref === 'string') return path.join(ordner, ref + '.js')
|
||||
if (ref && typeof ref.scriptPath === 'string') {
|
||||
const s = ref.scriptPath.replace(/\\/g, '/')
|
||||
return s.startsWith(ECHTER_ORDNER) ? path.join(ordner, s.slice(ECHTER_ORDNER.length)) : s
|
||||
}
|
||||
throw new Error('workflow(): Name oder { scriptPath } erwartet')
|
||||
}
|
||||
|
||||
async function lauf(datei, a, tiefe) {
|
||||
const { name, fn } = lade(datei)
|
||||
let kindPhase = null
|
||||
if (tiefe >= 1) {
|
||||
kindAufrufe[name] = (kindAufrufe[name] || 0) + 1
|
||||
kindPhase = '▸ ' + name + (kindAufrufe[name] > 1 ? ' #' + kindAufrufe[name] : '')
|
||||
}
|
||||
let aktuellePhase = null
|
||||
|
||||
async function agent(prompt, opts = {}) {
|
||||
if (typeof prompt !== 'string' || !prompt.trim()) protokoll.verstoesse.push('agent() ohne Auftragstext: ' + (opts.label || '?'))
|
||||
if (!opts.model) protokoll.verstoesse.push('agent() ohne model: ' + (opts.label || '?'))
|
||||
if (opts.schema !== undefined) {
|
||||
if (!opts.schema || opts.schema.type !== 'object' || !opts.schema.properties) throw new Error('Schema braucht type object und properties: ' + opts.label)
|
||||
if ((opts.schema.required || []).some(k => !(k in opts.schema.properties))) throw new Error('required nicht in properties: ' + opts.label)
|
||||
}
|
||||
const modell = opts.model || '(erbt)'
|
||||
const phase = kindPhase || opts.phase || aktuellePhase || '(ohne)'
|
||||
const eintrag = { label: opts.label || null, phase, modell, effort: opts.effort, agentType: opts.agentType, prompt, tiefe, start: ++uhr, ende: null, ergebnis: undefined }
|
||||
protokoll.agents.push(eintrag)
|
||||
const k = phase + '|' + modell
|
||||
laufend[modell] = (laufend[modell] || 0) + 1
|
||||
laufendJePhase[k] = (laufendJePhase[k] || 0) + 1
|
||||
protokoll.spitze[modell] = Math.max(protokoll.spitze[modell] || 0, laufend[modell])
|
||||
protokoll.spitzeJePhase[k] = Math.max(protokoll.spitzeJePhase[k] || 0, laufendJePhase[k])
|
||||
let wert = null
|
||||
try {
|
||||
const antwort = await antworte({ ...eintrag, schema: opts.schema })
|
||||
await warte(antwort && typeof antwort.dauer === 'number' ? antwort.dauer : 5)
|
||||
wert = antwort && typeof antwort === 'object' && 'wert' in antwort ? antwort.wert : null
|
||||
if (wert !== null && opts.schema) {
|
||||
const fehlt = (opts.schema.required || []).filter(f => !wert || typeof wert !== 'object' || !(f in wert))
|
||||
if (fehlt.length) {
|
||||
protokoll.verstoesse.push(`${opts.label}: Ergebnis ohne ${fehlt.join(', ')}`)
|
||||
wert = null
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
protokoll.logs.push(`agent ${opts.label} geworfen: ${e && e.message}`)
|
||||
wert = null
|
||||
} finally {
|
||||
laufend[modell]--
|
||||
laufendJePhase[k]--
|
||||
eintrag.ende = ++uhr
|
||||
}
|
||||
eintrag.ergebnis = wert
|
||||
return wert
|
||||
}
|
||||
|
||||
const parallel = thunks => Promise.all(thunks.map(t => Promise.resolve().then(t).catch(e => {
|
||||
protokoll.logs.push('parallel: ' + (e && e.message))
|
||||
return null
|
||||
})))
|
||||
const pipeline = (items, ...stufen) => Promise.all(items.map(async (item, i) => {
|
||||
try {
|
||||
let r = item
|
||||
for (const stufe of stufen) r = await stufe(r, item, i)
|
||||
return r
|
||||
} catch (e) {
|
||||
protokoll.logs.push('pipeline: ' + (e && e.message))
|
||||
return null
|
||||
}
|
||||
}))
|
||||
const phase = titel => { if (!kindPhase) { aktuellePhase = titel; protokoll.phasen.push(titel) } }
|
||||
const log = text => { protokoll.logs.push(String(text)) }
|
||||
const workflow = async (ref, kindArgs) => {
|
||||
if (tiefe >= 1) throw new Error('workflow() cannot be called from within a child workflow: nesting is limited to one level')
|
||||
const ziel = aufloesen(ref)
|
||||
protokoll.workflows.push({ ref: typeof ref === 'string' ? ref : ref.scriptPath, args: kindArgs, tiefe: tiefe + 1 })
|
||||
return lauf(ziel, kindArgs, tiefe + 1)
|
||||
}
|
||||
const budget = { total: null, spent: () => 0, remaining: () => Infinity }
|
||||
return fn(agent, parallel, pipeline, phase, log, workflow, a, budget, DatumAttrappe, MatheAttrappe)
|
||||
}
|
||||
|
||||
try {
|
||||
const ergebnis = await lauf(datei, args, optionen.tiefe || 0)
|
||||
return { ergebnis, fehler: null, protokoll }
|
||||
} catch (e) {
|
||||
return { ergebnis: null, fehler: (e && e.message) || String(e), protokoll }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user