@oh-my-pi/pi-coding-agent 3.24.0 → 3.25.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.
- package/CHANGELOG.md +15 -0
- package/package.json +4 -4
- package/src/core/custom-commands/bundled/wt/index.ts +3 -0
- package/src/core/sdk.ts +7 -0
- package/src/core/tools/complete.ts +131 -0
- package/src/core/tools/index.test.ts +9 -1
- package/src/core/tools/index.ts +18 -5
- package/src/core/tools/jtd-to-json-schema.ts +274 -0
- package/src/core/tools/output.ts +125 -14
- package/src/core/tools/task/artifacts.ts +6 -9
- package/src/core/tools/task/executor.ts +44 -5
- package/src/core/tools/task/index.ts +23 -18
- package/src/core/tools/task/name-generator.ts +247 -0
- package/src/core/tools/task/render.ts +137 -8
- package/src/core/tools/task/types.ts +7 -0
- package/src/core/tools/task/worker-protocol.ts +1 -0
- package/src/core/tools/task/worker.ts +33 -1
- package/src/prompts/task.md +14 -50
- package/src/prompts/tools/output.md +2 -1
- package/src/prompts/tools/task.md +3 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate memorable two-word task identifiers.
|
|
3
|
+
* Format: AdjectiveNoun (e.g., "SwiftFalcon", "CalmPanda")
|
|
4
|
+
*
|
|
5
|
+
* Dictionaries sourced from unique-names-generator (MIT license).
|
|
6
|
+
* 1202 adjectives × 355 animals = 426,710 combinations.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const ADJECTIVES = [
|
|
10
|
+
"able", "above", "absent", "absolute", "abstract", "abundant", "academic", "acceptable",
|
|
11
|
+
"accepted", "accessible", "accurate", "accused", "active", "actual", "acute", "added",
|
|
12
|
+
"additional", "adequate", "adjacent", "administrative", "adorable", "advanced", "adverse",
|
|
13
|
+
"advisory", "aesthetic", "afraid", "aggregate", "aggressive", "agreeable", "agreed",
|
|
14
|
+
"agricultural", "alert", "alive", "alleged", "allied", "alone", "alright", "alternative",
|
|
15
|
+
"amateur", "amazing", "ambitious", "amused", "ancient", "angry", "annoyed", "annual",
|
|
16
|
+
"anonymous", "anxious", "appalling", "apparent", "applicable", "appropriate", "arbitrary",
|
|
17
|
+
"architectural", "armed", "arrogant", "artificial", "artistic", "ashamed", "asleep",
|
|
18
|
+
"assistant", "associated", "atomic", "attractive", "automatic", "autonomous", "available",
|
|
19
|
+
"average", "awake", "aware", "awful", "awkward", "back", "bad", "balanced", "bare", "basic",
|
|
20
|
+
"beautiful", "beneficial", "better", "bewildered", "big", "binding", "biological", "bitter",
|
|
21
|
+
"bizarre", "blank", "blind", "blonde", "bloody", "blushing", "boiling", "bold", "bored",
|
|
22
|
+
"boring", "bottom", "brainy", "brave", "breakable", "breezy", "brief", "bright", "brilliant",
|
|
23
|
+
"broad", "broken", "bumpy", "burning", "busy", "calm", "capable", "capitalist", "careful",
|
|
24
|
+
"casual", "causal", "cautious", "central", "certain", "changing", "characteristic", "charming",
|
|
25
|
+
"cheap", "cheerful", "chemical", "chief", "chilly", "chosen", "chronic", "chubby", "circular",
|
|
26
|
+
"civic", "civil", "civilian", "classic", "classical", "clean", "clear", "clever", "clinical",
|
|
27
|
+
"close", "closed", "cloudy", "clumsy", "coastal", "cognitive", "coherent", "cold", "collective",
|
|
28
|
+
"colonial", "colorful", "colossal", "coloured", "colourful", "combative", "combined",
|
|
29
|
+
"comfortable", "coming", "commercial", "common", "communist", "compact", "comparable",
|
|
30
|
+
"comparative", "compatible", "competent", "competitive", "complete", "complex", "complicated",
|
|
31
|
+
"comprehensive", "compulsory", "conceptual", "concerned", "concrete", "condemned", "confident",
|
|
32
|
+
"confidential", "confused", "conscious", "conservation", "conservative", "considerable",
|
|
33
|
+
"consistent", "constant", "constitutional", "contemporary", "content", "continental",
|
|
34
|
+
"continued", "continuing", "continuous", "controlled", "controversial", "convenient",
|
|
35
|
+
"conventional", "convinced", "convincing", "cooing", "cool", "cooperative", "corporate",
|
|
36
|
+
"correct", "corresponding", "costly", "courageous", "crazy", "creative", "creepy", "criminal",
|
|
37
|
+
"critical", "crooked", "crowded", "crucial", "crude", "cruel", "cuddly", "cultural", "curious",
|
|
38
|
+
"curly", "current", "curved", "cute", "daily", "damaged", "damp", "dangerous", "dark", "dead",
|
|
39
|
+
"deaf", "deafening", "dear", "decent", "decisive", "deep", "defeated", "defensive", "defiant",
|
|
40
|
+
"definite", "deliberate", "delicate", "delicious", "delighted", "delightful", "democratic",
|
|
41
|
+
"dependent", "depressed", "desirable", "desperate", "detailed", "determined", "developed",
|
|
42
|
+
"developing", "devoted", "different", "difficult", "digital", "diplomatic", "direct", "dirty",
|
|
43
|
+
"disabled", "disappointed", "disastrous", "disciplinary", "disgusted", "distant", "distinct",
|
|
44
|
+
"distinctive", "distinguished", "disturbed", "disturbing", "diverse", "divine", "dizzy",
|
|
45
|
+
"domestic", "dominant", "double", "doubtful", "drab", "dramatic", "dreadful", "driving",
|
|
46
|
+
"drunk", "dry", "dual", "due", "dull", "dusty", "dying", "dynamic", "eager", "early", "eastern",
|
|
47
|
+
"easy", "economic", "educational", "eerie", "effective", "efficient", "elaborate", "elated",
|
|
48
|
+
"elderly", "eldest", "electoral", "electric", "electrical", "electronic", "elegant", "eligible",
|
|
49
|
+
"embarrassed", "embarrassing", "emotional", "empirical", "empty", "enchanting", "encouraging",
|
|
50
|
+
"endless", "energetic", "enormous", "enthusiastic", "entire", "entitled", "envious",
|
|
51
|
+
"environmental", "equal", "equivalent", "essential", "established", "estimated", "ethical",
|
|
52
|
+
"ethnic", "eventual", "everyday", "evident", "evil", "evolutionary", "exact", "excellent",
|
|
53
|
+
"exceptional", "excess", "excessive", "excited", "exciting", "exclusive", "existing", "exotic",
|
|
54
|
+
"expected", "expensive", "experienced", "experimental", "explicit", "extended", "extensive",
|
|
55
|
+
"external", "extra", "extraordinary", "extreme", "exuberant", "faint", "fair", "faithful",
|
|
56
|
+
"familiar", "famous", "fancy", "fantastic", "far", "fascinating", "fashionable", "fast", "fat",
|
|
57
|
+
"fatal", "favourable", "favourite", "federal", "fellow", "female", "feminist", "few", "fierce",
|
|
58
|
+
"filthy", "final", "financial", "fine", "firm", "fiscal", "fit", "fixed", "flaky", "flat",
|
|
59
|
+
"flexible", "fluffy", "fluttering", "flying", "following", "fond", "foolish", "foreign",
|
|
60
|
+
"formal", "formidable", "forthcoming", "fortunate", "forward", "fragile", "frail", "frantic",
|
|
61
|
+
"free", "frequent", "fresh", "friendly", "frightened", "front", "frozen", "full", "fun",
|
|
62
|
+
"functional", "fundamental", "funny", "furious", "future", "fuzzy", "gastric", "gay", "general",
|
|
63
|
+
"generous", "genetic", "gentle", "genuine", "geographical", "giant", "gigantic", "given",
|
|
64
|
+
"glad", "glamorous", "gleaming", "global", "glorious", "golden", "good", "gorgeous", "gothic",
|
|
65
|
+
"governing", "graceful", "gradual", "grand", "grateful", "greasy", "great", "grieving", "grim",
|
|
66
|
+
"gross", "grotesque", "growing", "grubby", "grumpy", "guilty", "handicapped", "handsome",
|
|
67
|
+
"happy", "hard", "harsh", "head", "healthy", "heavy", "helpful", "helpless", "hidden", "high",
|
|
68
|
+
"hilarious", "hissing", "historic", "historical", "hollow", "holy", "homeless", "homely",
|
|
69
|
+
"honest", "horizontal", "horrible", "hostile", "hot", "huge", "human", "hungry", "hurt",
|
|
70
|
+
"hushed", "husky", "icy", "ideal", "identical", "ideological", "ill", "illegal", "imaginative",
|
|
71
|
+
"immediate", "immense", "imperial", "implicit", "important", "impossible", "impressed",
|
|
72
|
+
"impressive", "improved", "inadequate", "inappropriate", "inclined", "increased", "increasing",
|
|
73
|
+
"incredible", "independent", "indirect", "individual", "industrial", "inevitable", "influential",
|
|
74
|
+
"informal", "inherent", "initial", "injured", "inland", "inner", "innocent", "innovative",
|
|
75
|
+
"inquisitive", "instant", "institutional", "insufficient", "intact", "integral", "integrated",
|
|
76
|
+
"intellectual", "intelligent", "intense", "intensive", "interested", "interesting", "interim",
|
|
77
|
+
"interior", "intermediate", "internal", "international", "intimate", "invisible", "involved",
|
|
78
|
+
"irrelevant", "isolated", "itchy", "jealous", "jittery", "joint", "jolly", "joyous", "judicial",
|
|
79
|
+
"juicy", "junior", "just", "keen", "key", "kind", "known", "labour", "large", "late", "latin",
|
|
80
|
+
"lazy", "leading", "left", "legal", "legislative", "legitimate", "lengthy", "lesser", "level",
|
|
81
|
+
"lexical", "liable", "liberal", "light", "like", "likely", "limited", "linear", "linguistic",
|
|
82
|
+
"liquid", "literary", "little", "live", "lively", "living", "local", "logical", "lonely",
|
|
83
|
+
"long", "loose", "lost", "loud", "lovely", "low", "loyal", "lucky", "mad", "magic", "magnetic",
|
|
84
|
+
"magnificent", "main", "major", "male", "mammoth", "managerial", "managing", "manual", "many",
|
|
85
|
+
"marginal", "marine", "marked", "married", "marvellous", "marxist", "mass", "massive",
|
|
86
|
+
"mathematical", "mature", "maximum", "mean", "meaningful", "mechanical", "medical", "medieval",
|
|
87
|
+
"melodic", "melted", "mental", "mere", "metropolitan", "mid", "middle", "mighty", "mild",
|
|
88
|
+
"military", "miniature", "minimal", "minimum", "ministerial", "minor", "miserable", "misleading",
|
|
89
|
+
"missing", "misty", "mixed", "moaning", "mobile", "moderate", "modern", "modest", "molecular",
|
|
90
|
+
"monetary", "monthly", "moral", "motionless", "muddy", "multiple", "mushy", "musical", "mute",
|
|
91
|
+
"mutual", "mysterious", "naked", "narrow", "nasty", "national", "native", "natural", "naughty",
|
|
92
|
+
"naval", "near", "nearby", "neat", "necessary", "negative", "neighbouring", "nervous", "net",
|
|
93
|
+
"neutral", "new", "nice", "noble", "noisy", "normal", "northern", "nosy", "notable", "novel",
|
|
94
|
+
"nuclear", "numerous", "nursing", "nutritious", "nutty", "obedient", "objective", "obliged",
|
|
95
|
+
"obnoxious", "obvious", "occasional", "occupational", "odd", "official", "old", "olympic",
|
|
96
|
+
"only", "open", "operational", "opposite", "optimistic", "oral", "ordinary", "organic",
|
|
97
|
+
"organisational", "original", "orthodox", "other", "outdoor", "outer", "outrageous", "outside",
|
|
98
|
+
"outstanding", "overall", "overseas", "overwhelming", "painful", "pale", "panicky", "parallel",
|
|
99
|
+
"parental", "parliamentary", "partial", "particular", "passing", "passive", "past", "patient",
|
|
100
|
+
"payable", "peaceful", "peculiar", "perfect", "permanent", "persistent", "personal", "petite",
|
|
101
|
+
"philosophical", "physical", "plain", "planned", "plastic", "pleasant", "pleased", "poised",
|
|
102
|
+
"polite", "political", "poor", "popular", "positive", "possible", "potential", "powerful",
|
|
103
|
+
"practical", "precious", "precise", "preferred", "pregnant", "preliminary", "premier",
|
|
104
|
+
"prepared", "present", "presidential", "pretty", "previous", "prickly", "primary", "prime",
|
|
105
|
+
"primitive", "principal", "printed", "prior", "private", "probable", "productive", "professional",
|
|
106
|
+
"profitable", "profound", "progressive", "prominent", "promising", "proper", "proposed",
|
|
107
|
+
"prospective", "protective", "proud", "provincial", "psychiatric", "psychological", "public",
|
|
108
|
+
"puny", "pure", "purring", "puzzled", "quaint", "qualified", "quarrelsome", "querulous",
|
|
109
|
+
"quick", "quickest", "quiet", "quintessential", "quixotic", "racial", "radical", "rainy",
|
|
110
|
+
"random", "rapid", "rare", "raspy", "rational", "ratty", "raw", "ready", "real", "realistic",
|
|
111
|
+
"rear", "reasonable", "recent", "reduced", "redundant", "regional", "registered", "regular",
|
|
112
|
+
"regulatory", "related", "relative", "relaxed", "relevant", "reliable", "relieved", "religious",
|
|
113
|
+
"reluctant", "remaining", "remarkable", "remote", "renewed", "representative", "repulsive",
|
|
114
|
+
"required", "resident", "residential", "resonant", "respectable", "respective", "responsible",
|
|
115
|
+
"resulting", "retail", "retired", "revolutionary", "rich", "ridiculous", "right", "rigid",
|
|
116
|
+
"ripe", "rising", "rival", "roasted", "robust", "rolling", "romantic", "rotten", "rough",
|
|
117
|
+
"round", "royal", "rubber", "rude", "ruling", "running", "rural", "sacred", "sad", "safe",
|
|
118
|
+
"salty", "satisfactory", "satisfied", "scared", "scary", "scattered", "scientific", "scornful",
|
|
119
|
+
"scrawny", "screeching", "secondary", "secret", "secure", "select", "selected", "selective",
|
|
120
|
+
"selfish", "semantic", "senior", "sensible", "sensitive", "separate", "serious", "severe",
|
|
121
|
+
"sexual", "shaggy", "shaky", "shallow", "shared", "sharp", "sheer", "shiny", "shivering",
|
|
122
|
+
"shocked", "short", "shrill", "shy", "sick", "significant", "silent", "silky", "silly",
|
|
123
|
+
"similar", "simple", "single", "skilled", "skinny", "sleepy", "slight", "slim", "slimy",
|
|
124
|
+
"slippery", "slow", "small", "smart", "smiling", "smoggy", "smooth", "social", "socialist",
|
|
125
|
+
"soft", "solar", "sole", "solid", "sophisticated", "sore", "sorry", "sound", "sour", "southern",
|
|
126
|
+
"soviet", "spare", "sparkling", "spatial", "special", "specific", "specified", "spectacular",
|
|
127
|
+
"spicy", "spiritual", "splendid", "spontaneous", "sporting", "spotless", "spotty", "square",
|
|
128
|
+
"squealing", "stable", "stale", "standard", "static", "statistical", "statutory", "steady",
|
|
129
|
+
"steep", "sticky", "stiff", "still", "stingy", "stormy", "straight", "straightforward",
|
|
130
|
+
"strange", "strategic", "strict", "striking", "striped", "strong", "structural", "stuck",
|
|
131
|
+
"stupid", "subjective", "subsequent", "substantial", "subtle", "successful", "successive",
|
|
132
|
+
"sudden", "sufficient", "suitable", "sunny", "super", "superb", "superior", "supporting",
|
|
133
|
+
"supposed", "supreme", "sure", "surprised", "surprising", "surrounding", "surviving",
|
|
134
|
+
"suspicious", "sweet", "swift", "symbolic", "sympathetic", "systematic", "tall", "tame",
|
|
135
|
+
"tart", "tasteless", "tasty", "technical", "technological", "teenage", "temporary", "tender",
|
|
136
|
+
"tense", "terrible", "territorial", "testy", "theoretical", "thick", "thin", "thirsty",
|
|
137
|
+
"thorough", "thoughtful", "thoughtless", "thundering", "tight", "tiny", "tired", "top", "total",
|
|
138
|
+
"tough", "toxic", "traditional", "tragic", "tremendous", "tricky", "tropical", "troubled",
|
|
139
|
+
"typical", "ugliest", "ugly", "ultimate", "unable", "unacceptable", "unaware", "uncertain",
|
|
140
|
+
"unchanged", "uncomfortable", "unconscious", "underground", "underlying", "unemployed",
|
|
141
|
+
"uneven", "unexpected", "unfair", "unfortunate", "unhappy", "uniform", "uninterested", "unique",
|
|
142
|
+
"united", "universal", "unknown", "unlikely", "unnecessary", "unpleasant", "unsightly",
|
|
143
|
+
"unusual", "unwilling", "upper", "upset", "uptight", "urban", "urgent", "used", "useful",
|
|
144
|
+
"useless", "usual", "vague", "valid", "valuable", "variable", "varied", "various", "varying",
|
|
145
|
+
"vast", "verbal", "vertical", "vicarious", "vicious", "victorious", "violent", "visible",
|
|
146
|
+
"visiting", "visual", "vital", "vivacious", "vivid", "vocal", "vocational", "voiceless",
|
|
147
|
+
"voluminous", "voluntary", "vulnerable", "wandering", "warm", "wasteful", "watery", "weak",
|
|
148
|
+
"wealthy", "weary", "weekly", "weird", "welcome", "well", "western", "wet", "whispering",
|
|
149
|
+
"whole", "wicked", "wide", "widespread", "wild", "willing", "willowy", "wily", "wise", "wispy",
|
|
150
|
+
"witty", "wonderful", "wooden", "working", "worldwide", "worried", "worrying", "worthwhile",
|
|
151
|
+
"worthy", "written", "wrong", "xenial", "xeric", "yawning", "yearning", "young", "youngest",
|
|
152
|
+
"youthful", "zany", "zealous", "zesty", "zippy",
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
const NOUNS = [
|
|
156
|
+
"aardvark", "aardwolf", "albatross", "alligator", "alpaca", "amphibian", "anaconda",
|
|
157
|
+
"angelfish", "anglerfish", "ant", "anteater", "antelope", "antlion", "ape", "aphid",
|
|
158
|
+
"armadillo", "asp", "baboon", "badger", "bandicoot", "barnacle", "barracuda", "basilisk",
|
|
159
|
+
"bass", "bat", "bear", "beaver", "bedbug", "bee", "beetle", "bird", "bison", "blackbird",
|
|
160
|
+
"boa", "boar", "bobcat", "bobolink", "bonobo", "booby", "bovid", "bug", "butterfly", "buzzard",
|
|
161
|
+
"camel", "canid", "capybara", "cardinal", "caribou", "carp", "cat", "caterpillar", "catfish",
|
|
162
|
+
"catshark", "cattle", "centipede", "cephalopod", "chameleon", "cheetah", "chickadee", "chicken",
|
|
163
|
+
"chimpanzee", "chinchilla", "chipmunk", "cicada", "clam", "clownfish", "cobra", "cockroach",
|
|
164
|
+
"cod", "condor", "constrictor", "coral", "cougar", "cow", "coyote", "crab", "crane", "crawdad",
|
|
165
|
+
"crayfish", "cricket", "crocodile", "crow", "cuckoo", "damselfly", "deer", "dingo", "dinosaur",
|
|
166
|
+
"dog", "dolphin", "donkey", "dormouse", "dove", "dragon", "dragonfly", "duck", "eagle",
|
|
167
|
+
"earthworm", "earwig", "echidna", "eel", "egret", "elephant", "elk", "emu", "ermine", "falcon",
|
|
168
|
+
"ferret", "finch", "firefly", "fish", "flamingo", "flea", "fly", "flyingfish", "fowl", "fox",
|
|
169
|
+
"frog", "gamefowl", "gazelle", "gecko", "gerbil", "gibbon", "giraffe", "goat", "goldfish",
|
|
170
|
+
"goose", "gopher", "gorilla", "grasshopper", "grouse", "guan", "guanaco", "guineafowl", "gull",
|
|
171
|
+
"guppy", "haddock", "halibut", "hamster", "hare", "harrier", "hawk", "hedgehog", "heron",
|
|
172
|
+
"herring", "hippopotamus", "hookworm", "hornet", "horse", "hoverfly", "hummingbird", "hyena",
|
|
173
|
+
"iguana", "impala", "jackal", "jaguar", "jay", "jellyfish", "kangaroo", "kingfisher", "kite",
|
|
174
|
+
"kiwi", "koala", "koi", "krill", "ladybug", "lamprey", "lark", "leech", "lemming", "lemur",
|
|
175
|
+
"leopard", "leopon", "limpet", "lion", "lizard", "llama", "lobster", "locust", "loon", "louse",
|
|
176
|
+
"lungfish", "lynx", "macaw", "mackerel", "magpie", "mammal", "manatee", "mandrill", "marlin",
|
|
177
|
+
"marmoset", "marmot", "marsupial", "marten", "mastodon", "meadowlark", "meerkat", "mink",
|
|
178
|
+
"minnow", "mite", "mockingbird", "mole", "mollusk", "mongoose", "monkey", "moose", "mosquito",
|
|
179
|
+
"moth", "mouse", "mule", "muskox", "narwhal", "newt", "nightingale", "ocelot", "octopus",
|
|
180
|
+
"opossum", "orangutan", "orca", "ostrich", "otter", "owl", "ox", "panda", "panther", "parakeet",
|
|
181
|
+
"parrot", "parrotfish", "partridge", "peacock", "peafowl", "pelican", "penguin", "perch",
|
|
182
|
+
"pheasant", "pig", "pigeon", "pike", "pinniped", "piranha", "planarian", "platypus", "pony",
|
|
183
|
+
"porcupine", "porpoise", "possum", "prawn", "primate", "ptarmigan", "puffin", "puma", "python",
|
|
184
|
+
"quail", "quelea", "quokka", "rabbit", "raccoon", "rat", "rattlesnake", "raven", "reindeer",
|
|
185
|
+
"reptile", "rhinoceros", "roadrunner", "rodent", "rook", "rooster", "roundworm", "sailfish",
|
|
186
|
+
"salamander", "salmon", "sawfish", "scallop", "scorpion", "seahorse", "shark", "sheep", "shrew",
|
|
187
|
+
"shrimp", "silkworm", "silverfish", "skink", "skunk", "sloth", "slug", "smelt", "snail",
|
|
188
|
+
"snake", "snipe", "sole", "sparrow", "spider", "spoonbill", "squid", "squirrel", "starfish",
|
|
189
|
+
"stingray", "stoat", "stork", "sturgeon", "swallow", "swan", "swift", "swordfish", "swordtail",
|
|
190
|
+
"tahr", "takin", "tapir", "tarantula", "tarsier", "termite", "tern", "thrush", "tick", "tiger",
|
|
191
|
+
"tiglon", "toad", "tortoise", "toucan", "trout", "tuna", "turkey", "turtle", "tyrannosaurus",
|
|
192
|
+
"unicorn", "urial", "vicuna", "viper", "vole", "vulture", "wallaby", "walrus", "warbler",
|
|
193
|
+
"wasp", "weasel", "whale", "whippet", "whitefish", "wildcat", "wildebeest", "wildfowl", "wolf",
|
|
194
|
+
"wolverine", "wombat", "woodpecker", "worm", "wren", "yak", "zebra",
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
function capitalize(s: string): string {
|
|
198
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let usedNames = new Set<string>();
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generate a unique two-word identifier (e.g., "SwiftFalcon").
|
|
205
|
+
* Falls back to numeric suffix if all combinations exhausted.
|
|
206
|
+
*/
|
|
207
|
+
export function generateTaskName(): string {
|
|
208
|
+
// Try random combinations first (50 attempts)
|
|
209
|
+
for (let attempt = 0; attempt < 50; attempt++) {
|
|
210
|
+
const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
211
|
+
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
212
|
+
const name = `${capitalize(adj)}${capitalize(noun)}`;
|
|
213
|
+
if (!usedNames.has(name)) {
|
|
214
|
+
usedNames.add(name);
|
|
215
|
+
return name;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Exhaustive search if random fails
|
|
220
|
+
for (const adj of ADJECTIVES) {
|
|
221
|
+
for (const noun of NOUNS) {
|
|
222
|
+
const name = `${capitalize(adj)}${capitalize(noun)}`;
|
|
223
|
+
if (!usedNames.has(name)) {
|
|
224
|
+
usedNames.add(name);
|
|
225
|
+
return name;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// All 426k combinations used, add numeric suffix
|
|
231
|
+
let counter = 0;
|
|
232
|
+
while (true) {
|
|
233
|
+
const name = `Task${counter}`;
|
|
234
|
+
if (!usedNames.has(name)) {
|
|
235
|
+
usedNames.add(name);
|
|
236
|
+
return name;
|
|
237
|
+
}
|
|
238
|
+
counter++;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Reset name generator state (for testing).
|
|
244
|
+
*/
|
|
245
|
+
export function resetTaskNames(): void {
|
|
246
|
+
usedNames = new Set<string>();
|
|
247
|
+
}
|
|
@@ -69,6 +69,118 @@ function formatFindingSummary(findings: ReportFindingDetails[], theme: Theme): s
|
|
|
69
69
|
return `${theme.fg("dim", "Findings:")} ${parts.join(theme.sep.dot)}`;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function formatJsonScalar(value: unknown): string {
|
|
73
|
+
if (value === null) return "null";
|
|
74
|
+
if (typeof value === "string") return `"${value}"`;
|
|
75
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildTreePrefix(ancestors: boolean[], theme: Theme): string {
|
|
80
|
+
return ancestors.map((hasNext) => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function renderJsonTreeLines(
|
|
84
|
+
value: unknown,
|
|
85
|
+
theme: Theme,
|
|
86
|
+
maxDepth: number,
|
|
87
|
+
maxLines: number,
|
|
88
|
+
): { lines: string[]; truncated: boolean } {
|
|
89
|
+
const lines: string[] = [];
|
|
90
|
+
let truncated = false;
|
|
91
|
+
|
|
92
|
+
const iconObject = theme.styledSymbol("icon.folder", "muted");
|
|
93
|
+
const iconArray = theme.styledSymbol("icon.package", "muted");
|
|
94
|
+
const iconScalar = theme.styledSymbol("icon.file", "muted");
|
|
95
|
+
|
|
96
|
+
const pushLine = (line: string) => {
|
|
97
|
+
if (lines.length >= maxLines) {
|
|
98
|
+
truncated = true;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
lines.push(line);
|
|
102
|
+
return true;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const renderNode = (val: unknown, key: string | undefined, ancestors: boolean[], isLast: boolean, depth: number) => {
|
|
106
|
+
if (lines.length >= maxLines) {
|
|
107
|
+
truncated = true;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const connector = isLast ? theme.tree.last : theme.tree.branch;
|
|
112
|
+
const prefix = `${buildTreePrefix(ancestors, theme)}${theme.fg("dim", connector)} `;
|
|
113
|
+
const scalar = formatJsonScalar(val);
|
|
114
|
+
|
|
115
|
+
if (scalar) {
|
|
116
|
+
const label = key ? theme.fg("muted", key) : theme.fg("muted", "value");
|
|
117
|
+
pushLine(`${prefix}${iconScalar} ${label}: ${theme.fg("dim", scalar)}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (Array.isArray(val)) {
|
|
122
|
+
const header = key ? theme.fg("muted", key) : theme.fg("muted", "array");
|
|
123
|
+
pushLine(`${prefix}${iconArray} ${header}`);
|
|
124
|
+
if (val.length === 0) {
|
|
125
|
+
pushLine(
|
|
126
|
+
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.hook)} ${theme.fg("dim", "[]")}`,
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (depth >= maxDepth) {
|
|
131
|
+
pushLine(
|
|
132
|
+
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.hook)} ${theme.fg("dim", theme.format.ellipsis)}`,
|
|
133
|
+
);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const nextAncestors = [...ancestors, !isLast];
|
|
137
|
+
for (let i = 0; i < val.length; i++) {
|
|
138
|
+
renderNode(val[i], `[${i}]`, nextAncestors, i === val.length - 1, depth + 1);
|
|
139
|
+
if (lines.length >= maxLines) {
|
|
140
|
+
truncated = true;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (val && typeof val === "object") {
|
|
148
|
+
const header = key ? theme.fg("muted", key) : theme.fg("muted", "object");
|
|
149
|
+
pushLine(`${prefix}${iconObject} ${header}`);
|
|
150
|
+
const entries = Object.entries(val as Record<string, unknown>);
|
|
151
|
+
if (entries.length === 0) {
|
|
152
|
+
pushLine(
|
|
153
|
+
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.hook)} ${theme.fg("dim", "{}")}`,
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (depth >= maxDepth) {
|
|
158
|
+
pushLine(
|
|
159
|
+
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.hook)} ${theme.fg("dim", theme.format.ellipsis)}`,
|
|
160
|
+
);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const nextAncestors = [...ancestors, !isLast];
|
|
164
|
+
for (let i = 0; i < entries.length; i++) {
|
|
165
|
+
const [childKey, child] = entries[i];
|
|
166
|
+
renderNode(child, childKey, nextAncestors, i === entries.length - 1, depth + 1);
|
|
167
|
+
if (lines.length >= maxLines) {
|
|
168
|
+
truncated = true;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const label = key ? theme.fg("muted", key) : theme.fg("muted", "value");
|
|
176
|
+
pushLine(`${prefix}${iconScalar} ${label}: ${theme.fg("dim", String(val))}`);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
renderNode(value, undefined, [], true, 0);
|
|
180
|
+
|
|
181
|
+
return { lines, truncated };
|
|
182
|
+
}
|
|
183
|
+
|
|
72
184
|
function renderOutputSection(
|
|
73
185
|
output: string,
|
|
74
186
|
continuePrefix: string,
|
|
@@ -78,11 +190,30 @@ function renderOutputSection(
|
|
|
78
190
|
maxExpanded = 10,
|
|
79
191
|
): string[] {
|
|
80
192
|
const lines: string[] = [];
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
193
|
+
const trimmedOutput = output.trim();
|
|
194
|
+
if (!trimmedOutput) return lines;
|
|
83
195
|
|
|
84
196
|
lines.push(`${continuePrefix}${theme.fg("dim", "Output")}`);
|
|
85
197
|
|
|
198
|
+
if (trimmedOutput.startsWith("{") || trimmedOutput.startsWith("[")) {
|
|
199
|
+
try {
|
|
200
|
+
const parsed = JSON.parse(trimmedOutput);
|
|
201
|
+
const tree = renderJsonTreeLines(parsed, theme, expanded ? 6 : 2, expanded ? 24 : 6);
|
|
202
|
+
if (tree.lines.length > 0) {
|
|
203
|
+
for (const line of tree.lines) {
|
|
204
|
+
lines.push(`${continuePrefix} ${line}`);
|
|
205
|
+
}
|
|
206
|
+
if (tree.truncated) {
|
|
207
|
+
lines.push(`${continuePrefix} ${theme.fg("dim", theme.format.ellipsis)}`);
|
|
208
|
+
}
|
|
209
|
+
return lines;
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// Fall back to raw output
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const outputLines = output.split("\n").filter((line) => line.trim());
|
|
86
217
|
const previewCount = expanded ? maxExpanded : maxCollapsed;
|
|
87
218
|
for (const line of outputLines.slice(0, previewCount)) {
|
|
88
219
|
lines.push(`${continuePrefix} ${theme.fg("dim", truncate(line, 70, theme.format.ellipsis))}`);
|
|
@@ -144,9 +275,8 @@ function renderAgentProgress(
|
|
|
144
275
|
? "error"
|
|
145
276
|
: "accent";
|
|
146
277
|
|
|
147
|
-
// Main status line -
|
|
148
|
-
|
|
149
|
-
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", agentId)}`;
|
|
278
|
+
// Main status line - use taskId for Output tool
|
|
279
|
+
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", progress.taskId)}`;
|
|
150
280
|
const description = progress.description?.trim();
|
|
151
281
|
if (description) {
|
|
152
282
|
statusLine += ` ${theme.fg("muted", truncate(description, 40, theme.format.ellipsis))}`;
|
|
@@ -342,9 +472,8 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
|
|
|
342
472
|
const iconColor = success ? "success" : "error";
|
|
343
473
|
const statusText = aborted ? "aborted" : success ? "done" : "failed";
|
|
344
474
|
|
|
345
|
-
// Main status line -
|
|
346
|
-
|
|
347
|
-
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", agentId)} ${formatBadge(statusText, iconColor, theme)}`;
|
|
475
|
+
// Main status line - use taskId for Output tool
|
|
476
|
+
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", result.taskId)} ${formatBadge(statusText, iconColor, theme)}`;
|
|
348
477
|
const description = result.description?.trim();
|
|
349
478
|
if (description) {
|
|
350
479
|
statusLine += ` ${theme.fg("muted", truncate(description, 40, theme.format.ellipsis))}`;
|
|
@@ -38,6 +38,11 @@ export type TaskItem = Static<typeof taskItemSchema>;
|
|
|
38
38
|
/** Task tool parameters */
|
|
39
39
|
export const taskSchema = Type.Object({
|
|
40
40
|
context: Type.Optional(Type.String({ description: "Shared context prepended to all task prompts" })),
|
|
41
|
+
output_schema: Type.Optional(
|
|
42
|
+
Type.Any({
|
|
43
|
+
description: "JSON schema for structured subagent output (used by the complete tool)",
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
41
46
|
tasks: Type.Array(taskItemSchema, {
|
|
42
47
|
description: "Tasks to run in parallel",
|
|
43
48
|
maxItems: MAX_PARALLEL_TASKS,
|
|
@@ -85,6 +90,7 @@ export interface AgentDefinition {
|
|
|
85
90
|
/** Progress tracking for a single agent */
|
|
86
91
|
export interface AgentProgress {
|
|
87
92
|
index: number;
|
|
93
|
+
taskId: string;
|
|
88
94
|
agent: string;
|
|
89
95
|
agentSource: AgentSource;
|
|
90
96
|
status: "pending" | "running" | "completed" | "failed" | "aborted";
|
|
@@ -106,6 +112,7 @@ export interface AgentProgress {
|
|
|
106
112
|
/** Result from a single agent execution */
|
|
107
113
|
export interface SingleResult {
|
|
108
114
|
index: number;
|
|
115
|
+
taskId: string;
|
|
109
116
|
agent: string;
|
|
110
117
|
agentSource: AgentSource;
|
|
111
118
|
task: string;
|
|
@@ -123,6 +123,9 @@ async function runTask(payload: SubagentWorkerStartPayload): Promise<void> {
|
|
|
123
123
|
|
|
124
124
|
// Create agent session (equivalent to CLI's createAgentSession)
|
|
125
125
|
// Note: hasUI: false disables interactive features
|
|
126
|
+
const completionInstruction =
|
|
127
|
+
"When finished, call the complete tool exactly once. Do not end with a plain-text final answer.";
|
|
128
|
+
|
|
126
129
|
const { session } = await createAgentSession({
|
|
127
130
|
cwd: payload.cwd,
|
|
128
131
|
authStorage,
|
|
@@ -130,8 +133,10 @@ async function runTask(payload: SubagentWorkerStartPayload): Promise<void> {
|
|
|
130
133
|
model,
|
|
131
134
|
thinkingLevel,
|
|
132
135
|
toolNames: payload.toolNames,
|
|
136
|
+
outputSchema: payload.outputSchema,
|
|
137
|
+
requireCompleteTool: true,
|
|
133
138
|
// Append system prompt (equivalent to CLI's --append-system-prompt)
|
|
134
|
-
systemPrompt: (defaultPrompt) => `${defaultPrompt}\n\n${payload.systemPrompt}`,
|
|
139
|
+
systemPrompt: (defaultPrompt) => `${defaultPrompt}\n\n${payload.systemPrompt}\n\n${completionInstruction}`,
|
|
135
140
|
sessionManager,
|
|
136
141
|
hasUI: false,
|
|
137
142
|
// Pass spawn restrictions to nested tasks
|
|
@@ -164,16 +169,43 @@ async function runTask(payload: SubagentWorkerStartPayload): Promise<void> {
|
|
|
164
169
|
await extensionRunner.emit({ type: "session_start" });
|
|
165
170
|
}
|
|
166
171
|
|
|
172
|
+
// Track complete tool calls
|
|
173
|
+
const MAX_COMPLETE_RETRIES = 3;
|
|
174
|
+
let completeCalled = false;
|
|
175
|
+
|
|
167
176
|
// Subscribe to events and forward to parent (equivalent to --mode json output)
|
|
168
177
|
session.subscribe((event: AgentSessionEvent) => {
|
|
169
178
|
if (isAgentEvent(event)) {
|
|
170
179
|
postMessageSafe({ type: "event", event });
|
|
180
|
+
// Track when complete tool is called
|
|
181
|
+
if (event.type === "tool_execution_end" && event.toolName === "complete") {
|
|
182
|
+
completeCalled = true;
|
|
183
|
+
}
|
|
171
184
|
}
|
|
172
185
|
});
|
|
173
186
|
|
|
174
187
|
// Run the prompt (equivalent to --prompt flag)
|
|
175
188
|
await session.prompt(payload.task);
|
|
176
189
|
|
|
190
|
+
// Retry loop if complete was not called
|
|
191
|
+
let retryCount = 0;
|
|
192
|
+
while (!completeCalled && retryCount < MAX_COMPLETE_RETRIES && !abortRequested) {
|
|
193
|
+
retryCount++;
|
|
194
|
+
const reminder = `<system-reminder>
|
|
195
|
+
CRITICAL: You stopped without calling the complete tool. This is reminder ${retryCount} of ${MAX_COMPLETE_RETRIES}.
|
|
196
|
+
|
|
197
|
+
You MUST call the complete tool to finish your task. Options:
|
|
198
|
+
1. Call complete with your result data if you have completed the task
|
|
199
|
+
2. Call complete with status="aborted" and an error message if you cannot complete the task
|
|
200
|
+
|
|
201
|
+
Failure to call complete after ${MAX_COMPLETE_RETRIES} reminders will result in task failure.
|
|
202
|
+
</system-reminder>
|
|
203
|
+
|
|
204
|
+
Call complete now.`;
|
|
205
|
+
|
|
206
|
+
await session.prompt(reminder);
|
|
207
|
+
}
|
|
208
|
+
|
|
177
209
|
// Check if aborted during execution
|
|
178
210
|
const lastMessage = session.state.messages[session.state.messages.length - 1];
|
|
179
211
|
if (lastMessage?.role === "assistant" && lastMessage.stopReason === "aborted") {
|
package/src/prompts/task.md
CHANGED
|
@@ -1,50 +1,14 @@
|
|
|
1
|
-
You are a worker agent for delegated tasks
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
- Verify with tools; ask for clarification when required.
|
|
16
|
-
- For file searches: Use grep/glob when you need to search broadly. Use read when you know the specific file path.
|
|
17
|
-
- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.
|
|
18
|
-
- Be thorough: Check multiple locations, consider different naming conventions, look for related files.
|
|
19
|
-
- When spawning subagents with the Task tool, include a short, user-facing `description` for each task (5-8 words) that summarizes the approach.
|
|
20
|
-
- NEVER create files unless absolutely necessary. ALWAYS prefer editing existing files.
|
|
21
|
-
- NEVER proactively create documentation files (\*.md) or README files unless explicitly requested.
|
|
22
|
-
- Any file paths in your response MUST be absolute. Do NOT use relative paths.
|
|
23
|
-
- Include relevant code snippets in your final response.
|
|
24
|
-
|
|
25
|
-
Output format when finished:
|
|
26
|
-
|
|
27
|
-
## Completed
|
|
28
|
-
|
|
29
|
-
What was done.
|
|
30
|
-
|
|
31
|
-
## Files Changed
|
|
32
|
-
|
|
33
|
-
- `/absolute/path/to/file.ts` - what changed
|
|
34
|
-
|
|
35
|
-
## Key Code
|
|
36
|
-
|
|
37
|
-
Relevant snippets or signatures touched:
|
|
38
|
-
|
|
39
|
-
```language
|
|
40
|
-
// actual code
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Notes (if any)
|
|
44
|
-
|
|
45
|
-
Anything the main agent should know.
|
|
46
|
-
|
|
47
|
-
If handing off to another agent (e.g. reviewer), include:
|
|
48
|
-
|
|
49
|
-
- Exact file paths changed
|
|
50
|
-
- Key functions/types touched (short list)
|
|
1
|
+
You are a worker agent for delegated tasks in an isolated context. Finish only the assigned work and return the minimum useful result.
|
|
2
|
+
|
|
3
|
+
Principles:
|
|
4
|
+
|
|
5
|
+
- Be concise. No filler, repetition, or tool transcripts.
|
|
6
|
+
- If blocked, ask a single focused question; otherwise proceed autonomously.
|
|
7
|
+
- Prefer narrow search (grep/glob) then read only needed ranges.
|
|
8
|
+
- Avoid full-file reads unless necessary.
|
|
9
|
+
- NEVER create files unless absolutely required. Prefer edits to existing files.
|
|
10
|
+
- NEVER create documentation files (\*.md) unless explicitly requested.
|
|
11
|
+
- Any file paths in your response MUST be absolute.
|
|
12
|
+
- When spawning subagents with the Task tool, include a 5-8 word user-facing description.
|
|
13
|
+
- Include the smallest relevant code snippet when discussing code or config.
|
|
14
|
+
- Follow the main agent's instructions.
|
|
@@ -21,7 +21,8 @@ Do NOT use when:
|
|
|
21
21
|
- `"raw"` (default): Full output with ANSI codes preserved
|
|
22
22
|
- `"json"`: Structured object with metadata
|
|
23
23
|
- `"stripped"`: Plain text with ANSI codes removed for parsing
|
|
24
|
+
- `query` (optional): jq-like query for JSON outputs (e.g., `.result.items[0].name`)
|
|
24
25
|
- `offset` (optional): Line number to start reading from (1-indexed)
|
|
25
26
|
- `limit` (optional): Maximum number of lines to read
|
|
26
27
|
|
|
27
|
-
Use offset/limit for line ranges to reduce context usage on large outputs.
|
|
28
|
+
Use offset/limit for line ranges to reduce context usage on large outputs. Use `query` for JSON outputs (for example, subagent `complete` results).
|