@noclaf/cli 0.7.0 → 0.9.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/README.md +26 -84
- package/dist/app.js +13 -0
- package/dist/app.js.map +1 -0
- package/dist/brand.js +9 -49
- package/dist/brand.js.map +1 -1
- package/dist/cli.js +3 -37
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server.js +3 -187
- package/dist/mcp-server.js.map +1 -1
- package/package.json +8 -17
- package/dist/adapters/supabase/allocation-gateway.js +0 -291
- package/dist/adapters/supabase/allocation-gateway.js.map +0 -1
- package/dist/adapters/supabase/analytics-gateway.js +0 -18
- package/dist/adapters/supabase/analytics-gateway.js.map +0 -1
- package/dist/adapters/supabase/calendar-gateway.js +0 -48
- package/dist/adapters/supabase/calendar-gateway.js.map +0 -1
- package/dist/adapters/supabase/calendar-write-gateway.js +0 -35
- package/dist/adapters/supabase/calendar-write-gateway.js.map +0 -1
- package/dist/adapters/supabase/client.js +0 -136
- package/dist/adapters/supabase/client.js.map +0 -1
- package/dist/adapters/supabase/context-gateway.js +0 -23
- package/dist/adapters/supabase/context-gateway.js.map +0 -1
- package/dist/adapters/supabase/gateway.js +0 -26
- package/dist/adapters/supabase/gateway.js.map +0 -1
- package/dist/adapters/supabase/release-gateway.js +0 -61
- package/dist/adapters/supabase/release-gateway.js.map +0 -1
- package/dist/adapters/supabase/sprint-gateway.js +0 -59
- package/dist/adapters/supabase/sprint-gateway.js.map +0 -1
- package/dist/adapters/supabase/task-gateway.js +0 -533
- package/dist/adapters/supabase/task-gateway.js.map +0 -1
- package/dist/auth.js +0 -141
- package/dist/auth.js.map +0 -1
- package/dist/cli/commands/auth.js +0 -127
- package/dist/cli/commands/auth.js.map +0 -1
- package/dist/cli/commands/clean.js +0 -120
- package/dist/cli/commands/clean.js.map +0 -1
- package/dist/cli/commands/completion.js +0 -114
- package/dist/cli/commands/completion.js.map +0 -1
- package/dist/cli/commands/exec.js +0 -69
- package/dist/cli/commands/exec.js.map +0 -1
- package/dist/cli/commands/init/analyze-step.js +0 -21
- package/dist/cli/commands/init/analyze-step.js.map +0 -1
- package/dist/cli/commands/init/apply-answers.js +0 -122
- package/dist/cli/commands/init/apply-answers.js.map +0 -1
- package/dist/cli/commands/init/apply-step.js +0 -165
- package/dist/cli/commands/init/apply-step.js.map +0 -1
- package/dist/cli/commands/init/auth-step.js +0 -48
- package/dist/cli/commands/init/auth-step.js.map +0 -1
- package/dist/cli/commands/init/boilerplate-step.js +0 -20
- package/dist/cli/commands/init/boilerplate-step.js.map +0 -1
- package/dist/cli/commands/init/clients-step.js +0 -78
- package/dist/cli/commands/init/clients-step.js.map +0 -1
- package/dist/cli/commands/init/context-step.js +0 -81
- package/dist/cli/commands/init/context-step.js.map +0 -1
- package/dist/cli/commands/init/describe-step.js +0 -68
- package/dist/cli/commands/init/describe-step.js.map +0 -1
- package/dist/cli/commands/init/index.js +0 -98
- package/dist/cli/commands/init/index.js.map +0 -1
- package/dist/cli/commands/init/mode.js +0 -18
- package/dist/cli/commands/init/mode.js.map +0 -1
- package/dist/cli/commands/init/progress.js +0 -60
- package/dist/cli/commands/init/progress.js.map +0 -1
- package/dist/cli/commands/init/project-step.js +0 -133
- package/dist/cli/commands/init/project-step.js.map +0 -1
- package/dist/cli/commands/init/provision-step.js +0 -103
- package/dist/cli/commands/init/provision-step.js.map +0 -1
- package/dist/cli/commands/map.js +0 -18
- package/dist/cli/commands/map.js.map +0 -1
- package/dist/cli/commands/plan.js +0 -45
- package/dist/cli/commands/plan.js.map +0 -1
- package/dist/cli/commands/skills.js +0 -23
- package/dist/cli/commands/skills.js.map +0 -1
- package/dist/cli/commands/sync.js +0 -319
- package/dist/cli/commands/sync.js.map +0 -1
- package/dist/cli/commands/validate-plan.js +0 -42
- package/dist/cli/commands/validate-plan.js.map +0 -1
- package/dist/cli/commands/wave.js +0 -57
- package/dist/cli/commands/wave.js.map +0 -1
- package/dist/cli/copy/auth.json +0 -6
- package/dist/cli/copy/flows.json +0 -6
- package/dist/cli/copy/init.json +0 -20
- package/dist/cli/copy/sync.json +0 -3
- package/dist/cli/copy.js +0 -23
- package/dist/cli/copy.js.map +0 -1
- package/dist/cli/flows/clients.js +0 -130
- package/dist/cli/flows/clients.js.map +0 -1
- package/dist/cli/flows/completion.js +0 -63
- package/dist/cli/flows/completion.js.map +0 -1
- package/dist/cli/flows/dependencies.js +0 -114
- package/dist/cli/flows/dependencies.js.map +0 -1
- package/dist/cli/flows/sections.js +0 -85
- package/dist/cli/flows/sections.js.map +0 -1
- package/dist/cli/flows/user-config.js +0 -37
- package/dist/cli/flows/user-config.js.map +0 -1
- package/dist/cli/ui/render.js +0 -239
- package/dist/cli/ui/render.js.map +0 -1
- package/dist/cli/ui/report.js +0 -130
- package/dist/cli/ui/report.js.map +0 -1
- package/dist/config.js +0 -289
- package/dist/config.js.map +0 -1
- package/dist/constants.js +0 -9
- package/dist/constants.js.map +0 -1
- package/dist/core/ports.js +0 -2
- package/dist/core/ports.js.map +0 -1
- package/dist/core/types.js +0 -5
- package/dist/core/types.js.map +0 -1
- package/dist/database.types.js +0 -11
- package/dist/database.types.js.map +0 -1
- package/dist/lib/active-project.js +0 -28
- package/dist/lib/active-project.js.map +0 -1
- package/dist/lib/autopull.js +0 -13
- package/dist/lib/autopull.js.map +0 -1
- package/dist/lib/boilerplate-scaffold.js +0 -65
- package/dist/lib/boilerplate-scaffold.js.map +0 -1
- package/dist/lib/boilerplates.js +0 -61
- package/dist/lib/boilerplates.js.map +0 -1
- package/dist/lib/calendar-logic.js +0 -59
- package/dist/lib/calendar-logic.js.map +0 -1
- package/dist/lib/calendar-write-logic.js +0 -6
- package/dist/lib/calendar-write-logic.js.map +0 -1
- package/dist/lib/client-configs.js +0 -191
- package/dist/lib/client-configs.js.map +0 -1
- package/dist/lib/client-install.js +0 -40
- package/dist/lib/client-install.js.map +0 -1
- package/dist/lib/colors.js +0 -92
- package/dist/lib/colors.js.map +0 -1
- package/dist/lib/cowork-extension.js +0 -143
- package/dist/lib/cowork-extension.js.map +0 -1
- package/dist/lib/dates.js +0 -21
- package/dist/lib/dates.js.map +0 -1
- package/dist/lib/dependencies.js +0 -145
- package/dist/lib/dependencies.js.map +0 -1
- package/dist/lib/dependency-install.js +0 -163
- package/dist/lib/dependency-install.js.map +0 -1
- package/dist/lib/duration.js +0 -5
- package/dist/lib/duration.js.map +0 -1
- package/dist/lib/exec-delegate.js +0 -218
- package/dist/lib/exec-delegate.js.map +0 -1
- package/dist/lib/exec-engines.js +0 -90
- package/dist/lib/exec-engines.js.map +0 -1
- package/dist/lib/exec-events.js +0 -87
- package/dist/lib/exec-events.js.map +0 -1
- package/dist/lib/exec-translators.js +0 -198
- package/dist/lib/exec-translators.js.map +0 -1
- package/dist/lib/file-merge.js +0 -33
- package/dist/lib/file-merge.js.map +0 -1
- package/dist/lib/git-exposure.js +0 -35
- package/dist/lib/git-exposure.js.map +0 -1
- package/dist/lib/harness.js +0 -127
- package/dist/lib/harness.js.map +0 -1
- package/dist/lib/hooks.js +0 -216
- package/dist/lib/hooks.js.map +0 -1
- package/dist/lib/patterns.js +0 -25
- package/dist/lib/patterns.js.map +0 -1
- package/dist/lib/pg-error.js +0 -53
- package/dist/lib/pg-error.js.map +0 -1
- package/dist/lib/plan-delegate.js +0 -117
- package/dist/lib/plan-delegate.js.map +0 -1
- package/dist/lib/project-context.js +0 -151
- package/dist/lib/project-context.js.map +0 -1
- package/dist/lib/prompts.js +0 -69
- package/dist/lib/prompts.js.map +0 -1
- package/dist/lib/provision-apply.js +0 -220
- package/dist/lib/provision-apply.js.map +0 -1
- package/dist/lib/provision-collect.js +0 -47
- package/dist/lib/provision-collect.js.map +0 -1
- package/dist/lib/provision.js +0 -45
- package/dist/lib/provision.js.map +0 -1
- package/dist/lib/release-logic.js +0 -10
- package/dist/lib/release-logic.js.map +0 -1
- package/dist/lib/repo-map.js +0 -176
- package/dist/lib/repo-map.js.map +0 -1
- package/dist/lib/require-config.js +0 -32
- package/dist/lib/require-config.js.map +0 -1
- package/dist/lib/rules.js +0 -146
- package/dist/lib/rules.js.map +0 -1
- package/dist/lib/sections.js +0 -89
- package/dist/lib/sections.js.map +0 -1
- package/dist/lib/skill-agent.js +0 -44
- package/dist/lib/skill-agent.js.map +0 -1
- package/dist/lib/skill-serve.js +0 -128
- package/dist/lib/skill-serve.js.map +0 -1
- package/dist/lib/skills-cache.js +0 -99
- package/dist/lib/skills-cache.js.map +0 -1
- package/dist/lib/skills.js +0 -237
- package/dist/lib/skills.js.map +0 -1
- package/dist/lib/sprint-deadline.js +0 -28
- package/dist/lib/sprint-deadline.js.map +0 -1
- package/dist/lib/sprint-logic.js +0 -31
- package/dist/lib/sprint-logic.js.map +0 -1
- package/dist/lib/targets.js +0 -150
- package/dist/lib/targets.js.map +0 -1
- package/dist/lib/task-history.js +0 -15
- package/dist/lib/task-history.js.map +0 -1
- package/dist/lib/telemetry.js +0 -51
- package/dist/lib/telemetry.js.map +0 -1
- package/dist/lib/tool-result.js +0 -15
- package/dist/lib/tool-result.js.map +0 -1
- package/dist/lib/validate-plan-delegate.js +0 -106
- package/dist/lib/validate-plan-delegate.js.map +0 -1
- package/dist/lib/version-check.js +0 -89
- package/dist/lib/version-check.js.map +0 -1
- package/dist/lib/wave-scheduler.js +0 -95
- package/dist/lib/wave-scheduler.js.map +0 -1
- package/dist/session-factory.js +0 -19
- package/dist/session-factory.js.map +0 -1
- package/dist/spinner.js +0 -12
- package/dist/spinner.js.map +0 -1
- package/dist/tools/activate-sprint.js +0 -44
- package/dist/tools/activate-sprint.js.map +0 -1
- package/dist/tools/add-checklist.js +0 -63
- package/dist/tools/add-checklist.js.map +0 -1
- package/dist/tools/add-holiday.js +0 -41
- package/dist/tools/add-holiday.js.map +0 -1
- package/dist/tools/add-home-office.js +0 -31
- package/dist/tools/add-home-office.js.map +0 -1
- package/dist/tools/add-vacation.js +0 -39
- package/dist/tools/add-vacation.js.map +0 -1
- package/dist/tools/attach-pr.js +0 -97
- package/dist/tools/attach-pr.js.map +0 -1
- package/dist/tools/comment-task.js +0 -72
- package/dist/tools/comment-task.js.map +0 -1
- package/dist/tools/create-release.js +0 -46
- package/dist/tools/create-release.js.map +0 -1
- package/dist/tools/create-sprint.js +0 -66
- package/dist/tools/create-sprint.js.map +0 -1
- package/dist/tools/create-task.js +0 -174
- package/dist/tools/create-task.js.map +0 -1
- package/dist/tools/delegate-exec.js +0 -63
- package/dist/tools/delegate-exec.js.map +0 -1
- package/dist/tools/end-allocation.js +0 -38
- package/dist/tools/end-allocation.js.map +0 -1
- package/dist/tools/end-sprint.js +0 -37
- package/dist/tools/end-sprint.js.map +0 -1
- package/dist/tools/end-task-allocation.js +0 -60
- package/dist/tools/end-task-allocation.js.map +0 -1
- package/dist/tools/exec-status.js +0 -45
- package/dist/tools/exec-status.js.map +0 -1
- package/dist/tools/finalize-release.js +0 -45
- package/dist/tools/finalize-release.js.map +0 -1
- package/dist/tools/get-active-allocation.js +0 -69
- package/dist/tools/get-active-allocation.js.map +0 -1
- package/dist/tools/get-calendar.js +0 -40
- package/dist/tools/get-calendar.js.map +0 -1
- package/dist/tools/get-context.js +0 -48
- package/dist/tools/get-context.js.map +0 -1
- package/dist/tools/get-sprint.js +0 -38
- package/dist/tools/get-sprint.js.map +0 -1
- package/dist/tools/get-task.js +0 -146
- package/dist/tools/get-task.js.map +0 -1
- package/dist/tools/index.js +0 -84
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/list-my-allocations.js +0 -71
- package/dist/tools/list-my-allocations.js.map +0 -1
- package/dist/tools/list-projects.js +0 -38
- package/dist/tools/list-projects.js.map +0 -1
- package/dist/tools/list-releases.js +0 -32
- package/dist/tools/list-releases.js.map +0 -1
- package/dist/tools/list-sprints.js +0 -30
- package/dist/tools/list-sprints.js.map +0 -1
- package/dist/tools/list-tasks.js +0 -95
- package/dist/tools/list-tasks.js.map +0 -1
- package/dist/tools/move-task.js +0 -82
- package/dist/tools/move-task.js.map +0 -1
- package/dist/tools/plan.js +0 -56
- package/dist/tools/plan.js.map +0 -1
- package/dist/tools/record-delivery.js +0 -100
- package/dist/tools/record-delivery.js.map +0 -1
- package/dist/tools/remove-holiday.js +0 -31
- package/dist/tools/remove-holiday.js.map +0 -1
- package/dist/tools/remove-home-office.js +0 -32
- package/dist/tools/remove-home-office.js.map +0 -1
- package/dist/tools/remove-vacation.js +0 -31
- package/dist/tools/remove-vacation.js.map +0 -1
- package/dist/tools/set-project.js +0 -52
- package/dist/tools/set-project.js.map +0 -1
- package/dist/tools/set-review.js +0 -132
- package/dist/tools/set-review.js.map +0 -1
- package/dist/tools/sprint-common.js +0 -13
- package/dist/tools/sprint-common.js.map +0 -1
- package/dist/tools/start-allocation.js +0 -32
- package/dist/tools/start-allocation.js.map +0 -1
- package/dist/tools/start-task-allocation.js +0 -83
- package/dist/tools/start-task-allocation.js.map +0 -1
- package/dist/tools/update-release.js +0 -58
- package/dist/tools/update-release.js.map +0 -1
- package/dist/tools/update-sprint.js +0 -56
- package/dist/tools/update-sprint.js.map +0 -1
- package/dist/tools/update-task.js +0 -183
- package/dist/tools/update-task.js.map +0 -1
- package/dist/tools/validate-plan.js +0 -58
- package/dist/tools/validate-plan.js.map +0 -1
- package/dist/version.js +0 -16
- package/dist/version.js.map +0 -1
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { FINISHED_FILTER } from '../../core/types.js';
|
|
2
|
-
const COLS = 'id, project_id, name, status, target_date, delivered_at';
|
|
3
|
-
/** Adaptador Supabase do `ReleaseGateway`. */
|
|
4
|
-
export function releaseGateway(db) {
|
|
5
|
-
return {
|
|
6
|
-
async listReleases(projectId, status) {
|
|
7
|
-
let q = db.from('releases').select(COLS).eq('project_id', projectId);
|
|
8
|
-
if (status)
|
|
9
|
-
q = q.eq('status', status);
|
|
10
|
-
const { data, error } = await q.order('target_date', { ascending: true });
|
|
11
|
-
if (error)
|
|
12
|
-
throw new Error(`Erro ao listar releases: ${error.message}`);
|
|
13
|
-
return data ?? [];
|
|
14
|
-
},
|
|
15
|
-
async findRelease(projectId, releaseId) {
|
|
16
|
-
const { data, error } = await db
|
|
17
|
-
.from('releases')
|
|
18
|
-
.select(COLS)
|
|
19
|
-
.eq('project_id', projectId)
|
|
20
|
-
.eq('id', releaseId)
|
|
21
|
-
.maybeSingle();
|
|
22
|
-
if (error)
|
|
23
|
-
throw new Error(`Erro ao buscar release: ${error.message}`);
|
|
24
|
-
return data ?? null;
|
|
25
|
-
},
|
|
26
|
-
async createRelease(r) {
|
|
27
|
-
const { data, error } = await db.from('releases').insert(r).select(COLS).single();
|
|
28
|
-
if (error)
|
|
29
|
-
throw new Error(`Erro ao criar release: ${error.message}`);
|
|
30
|
-
return data;
|
|
31
|
-
},
|
|
32
|
-
async updateRelease(releaseId, patch) {
|
|
33
|
-
const { data, error } = await db.from('releases').update(patch).eq('id', releaseId).select(COLS).single();
|
|
34
|
-
if (error)
|
|
35
|
-
throw new Error(`Erro ao atualizar release: ${error.message}`);
|
|
36
|
-
return data;
|
|
37
|
-
},
|
|
38
|
-
async finalizeRelease(releaseId, unlinkPending) {
|
|
39
|
-
// Desvincular é idempotente: roda antes, então retry após falha não deixa release delivered com pendentes.
|
|
40
|
-
if (unlinkPending) {
|
|
41
|
-
const { error } = await db
|
|
42
|
-
.from('tasks')
|
|
43
|
-
.update({ release_id: null, updated_at: new Date().toISOString() })
|
|
44
|
-
.eq('release_id', releaseId)
|
|
45
|
-
.not('status', 'in', FINISHED_FILTER);
|
|
46
|
-
if (error)
|
|
47
|
-
throw new Error(`Erro ao desvincular tasks pendentes: ${error.message}`);
|
|
48
|
-
}
|
|
49
|
-
const { data, error } = await db
|
|
50
|
-
.from('releases')
|
|
51
|
-
.update({ status: 'delivered', delivered_at: new Date().toISOString() })
|
|
52
|
-
.eq('id', releaseId)
|
|
53
|
-
.select(COLS)
|
|
54
|
-
.single();
|
|
55
|
-
if (error)
|
|
56
|
-
throw new Error(`Erro ao finalizar release: ${error.message}`);
|
|
57
|
-
return data;
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
//# sourceMappingURL=release-gateway.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"release-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/release-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const COLS = 'id, project_id, name, status, start_date, end_date';
|
|
2
|
-
/** Adaptador Supabase do `SprintGateway` (ciclo de vida de sprints). */
|
|
3
|
-
export function sprintGateway(db) {
|
|
4
|
-
return {
|
|
5
|
-
async listSprints(projectId) {
|
|
6
|
-
const { data, error } = await db
|
|
7
|
-
.from('sprints')
|
|
8
|
-
.select(COLS)
|
|
9
|
-
.eq('project_id', projectId)
|
|
10
|
-
.order('created_at', { ascending: false });
|
|
11
|
-
if (error)
|
|
12
|
-
throw new Error(`Erro ao listar sprints: ${error.message}`);
|
|
13
|
-
return data ?? [];
|
|
14
|
-
},
|
|
15
|
-
async findSprint(projectId, idOrActive) {
|
|
16
|
-
let q = db.from('sprints').select(COLS).eq('project_id', projectId);
|
|
17
|
-
q = idOrActive === 'active'
|
|
18
|
-
? q.eq('status', 'ativa').order('created_at', { ascending: false }).limit(1)
|
|
19
|
-
: q.eq('id', idOrActive);
|
|
20
|
-
const { data, error } = await q.maybeSingle();
|
|
21
|
-
if (error)
|
|
22
|
-
throw new Error(`Erro ao buscar sprint: ${error.message}`);
|
|
23
|
-
return data ?? null;
|
|
24
|
-
},
|
|
25
|
-
async listSprintTasks(sprintId) {
|
|
26
|
-
const { data, error } = await db
|
|
27
|
-
.from('tasks')
|
|
28
|
-
.select('id, key, title, status, end_date')
|
|
29
|
-
.eq('sprint_id', sprintId);
|
|
30
|
-
if (error)
|
|
31
|
-
throw new Error(`Erro ao listar tasks da sprint: ${error.message}`);
|
|
32
|
-
return data ?? [];
|
|
33
|
-
},
|
|
34
|
-
async createSprint(projectId, name, startDate, endDate) {
|
|
35
|
-
const { data, error } = await db
|
|
36
|
-
.from('sprints')
|
|
37
|
-
.insert({ project_id: projectId, name, status: 'ativa', start_date: startDate, end_date: endDate })
|
|
38
|
-
.select(COLS)
|
|
39
|
-
.single();
|
|
40
|
-
if (error)
|
|
41
|
-
throw new Error(`Erro ao criar sprint: ${error.message}`);
|
|
42
|
-
return data;
|
|
43
|
-
},
|
|
44
|
-
async updateSprint(sprintId, patch) {
|
|
45
|
-
const { data, error } = await db.from('sprints').update(patch).eq('id', sprintId).select(COLS).single();
|
|
46
|
-
if (error)
|
|
47
|
-
throw new Error(`Erro ao atualizar sprint: ${error.message}`);
|
|
48
|
-
return data;
|
|
49
|
-
},
|
|
50
|
-
async returnTasksToBacklog(taskIds) {
|
|
51
|
-
if (taskIds.length === 0)
|
|
52
|
-
return;
|
|
53
|
-
const { error } = await db.from('tasks').update({ sprint_id: null, status: 'backlog' }).in('id', taskIds);
|
|
54
|
-
if (error)
|
|
55
|
-
throw new Error(`Erro ao devolver tasks ao backlog: ${error.message}`);
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=sprint-gateway.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sprint-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/sprint-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { insertTaskHistory } from '../../lib/task-history.js';
|
|
2
|
-
const SPRINT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3
|
-
/** Mapeia o conjunto de vínculos → tabela/coluna/campo de histórico + mensagens de erro. */
|
|
4
|
-
const ID_SET_SPEC = {
|
|
5
|
-
assignees: {
|
|
6
|
-
table: 'task_assignees', idColumn: 'user_id', historyField: 'assignees',
|
|
7
|
-
fetchErr: 'Erro ao buscar assignees atuais', clearErr: 'Erro ao limpar assignees', insertErr: 'Erro ao gravar novos assignees',
|
|
8
|
-
},
|
|
9
|
-
labels: {
|
|
10
|
-
table: 'task_label_assignments', idColumn: 'label_id', historyField: 'labels',
|
|
11
|
-
fetchErr: 'Erro ao buscar labels atuais', clearErr: 'Erro ao limpar labels', insertErr: 'Erro ao gravar novas labels',
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
/** Agrupa assignees (já resolvidos p/ perfil) por task_id. Pura. */
|
|
15
|
-
export function groupAssigneesByTask(assigneeRows, profilesById) {
|
|
16
|
-
const assigneesByTask = new Map();
|
|
17
|
-
for (const row of assigneeRows) {
|
|
18
|
-
const profile = profilesById.get(row.user_id);
|
|
19
|
-
const list = assigneesByTask.get(row.task_id) ?? [];
|
|
20
|
-
list.push({
|
|
21
|
-
user_id: row.user_id,
|
|
22
|
-
full_name: profile?.full_name ?? null,
|
|
23
|
-
username: profile?.username ?? null,
|
|
24
|
-
});
|
|
25
|
-
assigneesByTask.set(row.task_id, list);
|
|
26
|
-
}
|
|
27
|
-
return assigneesByTask;
|
|
28
|
-
}
|
|
29
|
-
/** Monta a lista de tasks com assignees embutidos, a partir das linhas + assignees agrupados. Pura. */
|
|
30
|
-
export function formatTaskList(tasks, assigneesByTask) {
|
|
31
|
-
return tasks.map((t) => ({
|
|
32
|
-
id: t.id,
|
|
33
|
-
key: t.key,
|
|
34
|
-
title: t.title,
|
|
35
|
-
status: t.status,
|
|
36
|
-
priority: t.priority,
|
|
37
|
-
type: t.type,
|
|
38
|
-
sprint_id: t.sprint_id,
|
|
39
|
-
start_date: t.start_date,
|
|
40
|
-
end_date: t.end_date,
|
|
41
|
-
release_id: t.release_id,
|
|
42
|
-
created_at: t.created_at,
|
|
43
|
-
assignees: assigneesByTask.get(t.id) ?? [],
|
|
44
|
-
}));
|
|
45
|
-
}
|
|
46
|
-
/** Adaptador Supabase do `TaskGateway` (listagem, detalhe, CRUD, comentário). */
|
|
47
|
-
/** Achata o embed do PostgREST em checklists com itens ordenados. Pura. */
|
|
48
|
-
export function normalizeChecklists(rows) {
|
|
49
|
-
return (rows ?? []).map((c) => ({
|
|
50
|
-
id: c.id,
|
|
51
|
-
title: c.title,
|
|
52
|
-
icon: c.icon ?? null,
|
|
53
|
-
order: c.order ?? 0,
|
|
54
|
-
items: (c.task_checklist_items ?? [])
|
|
55
|
-
.map((i) => ({ id: i.id, text: i.text, checked: i.checked, order: i.order ?? 0 }))
|
|
56
|
-
.sort((a, b) => a.order - b.order),
|
|
57
|
-
}));
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Payload de insert de uma checklist. `icon` é NOT NULL DEFAULT 'ListChecks' no banco —
|
|
61
|
-
* mandar `null` explícito estoura 23502, então sem ícone a chave é omitida. Pura.
|
|
62
|
-
*/
|
|
63
|
-
export function buildChecklistInsert(taskId, title, icon, order) {
|
|
64
|
-
return { task_id: taskId, title, order, ...(icon ? { icon } : {}) };
|
|
65
|
-
}
|
|
66
|
-
export function taskGateway(db) {
|
|
67
|
-
return {
|
|
68
|
-
async listTasks(filter) {
|
|
69
|
-
const sprint = await resolveSprintFilter(db, filter.projectId, filter.sprint);
|
|
70
|
-
if (sprint.noActive)
|
|
71
|
-
return { tasks: [], note: 'Não há sprint ativa neste projeto.' };
|
|
72
|
-
const rows = await queryFilteredTasks(db, filter, sprint.filter);
|
|
73
|
-
if (rows.length === 0)
|
|
74
|
-
return { tasks: [] };
|
|
75
|
-
const { assigneeRows, profilesById } = await loadAssigneeProfiles(db, rows.map((t) => t.id));
|
|
76
|
-
return { tasks: formatTaskList(rows, groupAssigneesByTask(assigneeRows, profilesById)) };
|
|
77
|
-
},
|
|
78
|
-
async getTaskWithRelations(taskId, projectId) {
|
|
79
|
-
const taskRes = await db
|
|
80
|
-
.from('tasks')
|
|
81
|
-
.select('*')
|
|
82
|
-
.eq('id', taskId)
|
|
83
|
-
.eq('project_id', projectId)
|
|
84
|
-
.maybeSingle();
|
|
85
|
-
if (taskRes.error)
|
|
86
|
-
throw new Error(`Erro ao buscar tarefa: ${taskRes.error.message}`);
|
|
87
|
-
if (!taskRes.data)
|
|
88
|
-
return null;
|
|
89
|
-
const [assigneesRes, checklistRes, commentsRes, historyRes, reposRes, labelsRes] = await Promise.all([
|
|
90
|
-
db.from('task_assignees').select('user_id').eq('task_id', taskId),
|
|
91
|
-
// Destipado: `task_checklists` não está nos types gerados.
|
|
92
|
-
db
|
|
93
|
-
.from('task_checklists')
|
|
94
|
-
.select('id, title, icon, order, task_checklist_items(id, text, checked, order)')
|
|
95
|
-
.eq('task_id', taskId)
|
|
96
|
-
.order('order', { ascending: true }),
|
|
97
|
-
db
|
|
98
|
-
.from('task_comments')
|
|
99
|
-
.select('id, content, created_at, updated_at, author_id')
|
|
100
|
-
.eq('task_id', taskId)
|
|
101
|
-
.order('created_at', { ascending: true })
|
|
102
|
-
.limit(50),
|
|
103
|
-
db
|
|
104
|
-
.from('task_history')
|
|
105
|
-
.select('id, action, field, old_value, new_value, created_at, user_id')
|
|
106
|
-
.eq('task_id', taskId)
|
|
107
|
-
.order('created_at', { ascending: false })
|
|
108
|
-
.limit(50),
|
|
109
|
-
db.from('task_repositories').select('repository_id').eq('task_id', taskId),
|
|
110
|
-
db.from('task_label_assignments').select('label_id').eq('task_id', taskId),
|
|
111
|
-
]);
|
|
112
|
-
if (assigneesRes.error)
|
|
113
|
-
throw new Error(`Erro ao buscar assignees: ${assigneesRes.error.message}`);
|
|
114
|
-
if (checklistRes.error)
|
|
115
|
-
throw new Error(`Erro ao buscar checklist: ${checklistRes.error.message}`);
|
|
116
|
-
if (commentsRes.error)
|
|
117
|
-
throw new Error(`Erro ao buscar comentários: ${commentsRes.error.message}`);
|
|
118
|
-
if (historyRes.error)
|
|
119
|
-
throw new Error(`Erro ao buscar histórico: ${historyRes.error.message}`);
|
|
120
|
-
if (reposRes.error)
|
|
121
|
-
throw new Error(`Erro ao buscar repositórios: ${reposRes.error.message}`);
|
|
122
|
-
if (labelsRes.error)
|
|
123
|
-
throw new Error(`Erro ao buscar labels: ${labelsRes.error.message}`);
|
|
124
|
-
return {
|
|
125
|
-
task: taskRes.data,
|
|
126
|
-
rows: {
|
|
127
|
-
assigneeRows: assigneesRes.data ?? [],
|
|
128
|
-
checklistRows: normalizeChecklists(checklistRes.data),
|
|
129
|
-
commentRows: commentsRes.data ?? [],
|
|
130
|
-
historyRows: historyRes.data ?? [],
|
|
131
|
-
repoRows: reposRes.data ?? [],
|
|
132
|
-
labelRows: labelsRes.data ?? [],
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
},
|
|
136
|
-
async loadTaskRefs(userIds, repoIds, labelIds) {
|
|
137
|
-
const profiles = await fetchProfiles(db, userIds);
|
|
138
|
-
const reposById = new Map();
|
|
139
|
-
if (repoIds.length > 0) {
|
|
140
|
-
const repoFetch = await db
|
|
141
|
-
.from('repositories')
|
|
142
|
-
.select('id, name, url, default_branch')
|
|
143
|
-
.in('id', repoIds);
|
|
144
|
-
if (repoFetch.error)
|
|
145
|
-
throw new Error(`Erro ao buscar repositórios: ${repoFetch.error.message}`);
|
|
146
|
-
for (const r of repoFetch.data ?? []) {
|
|
147
|
-
reposById.set(r.id, { name: r.name, url: r.url, default_branch: r.default_branch });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const labelsById = new Map();
|
|
151
|
-
if (labelIds.length > 0) {
|
|
152
|
-
const labelFetch = await db.from('task_labels').select('id, name, color').in('id', labelIds);
|
|
153
|
-
if (labelFetch.error)
|
|
154
|
-
throw new Error(`Erro ao buscar labels: ${labelFetch.error.message}`);
|
|
155
|
-
for (const l of labelFetch.data ?? [])
|
|
156
|
-
labelsById.set(l.id, { name: l.name, color: l.color });
|
|
157
|
-
}
|
|
158
|
-
return { profiles, reposById, labelsById };
|
|
159
|
-
},
|
|
160
|
-
async resolveSprintId(projectId, sprintInput) {
|
|
161
|
-
if (sprintInput === 'active') {
|
|
162
|
-
const sprintRes = await db
|
|
163
|
-
.from('sprints')
|
|
164
|
-
.select('id')
|
|
165
|
-
.eq('project_id', projectId)
|
|
166
|
-
.eq('status', 'ativa')
|
|
167
|
-
.maybeSingle();
|
|
168
|
-
if (sprintRes.error)
|
|
169
|
-
throw new Error(`Erro ao buscar sprint ativa: ${sprintRes.error.message}`);
|
|
170
|
-
// Criação prefere a sprint ativa (quem pede tarefa costuma ir fazer em
|
|
171
|
-
// seguida), mas projeto sem sprint ativa não é motivo pra tarefa não
|
|
172
|
-
// nascer: cai no backlog e a tool avisa. Diferente do update, onde
|
|
173
|
-
// pedir "active" sem sprint ativa é erro de verdade.
|
|
174
|
-
return sprintRes.data?.id ?? null;
|
|
175
|
-
}
|
|
176
|
-
if (sprintInput === 'none')
|
|
177
|
-
return null;
|
|
178
|
-
if (!SPRINT_UUID_RE.test(sprintInput)) {
|
|
179
|
-
throw new Error('Argumento "sprint" deve ser "active", "none" ou um UUID válido.');
|
|
180
|
-
}
|
|
181
|
-
const sprintRes = await db.from('sprints').select('id, project_id').eq('id', sprintInput).maybeSingle();
|
|
182
|
-
if (sprintRes.error)
|
|
183
|
-
throw new Error(`Erro ao validar sprint: ${sprintRes.error.message}`);
|
|
184
|
-
if (!sprintRes.data || sprintRes.data.project_id !== projectId) {
|
|
185
|
-
throw new Error('Sprint informada não pertence ao projeto atual.');
|
|
186
|
-
}
|
|
187
|
-
return sprintRes.data.id;
|
|
188
|
-
},
|
|
189
|
-
async createTask(payload, links, userId) {
|
|
190
|
-
const insertRes = await db
|
|
191
|
-
.from('tasks')
|
|
192
|
-
.insert(payload)
|
|
193
|
-
.select('id, key, title, status, priority, type, sprint_id, start_date, end_date, created_at')
|
|
194
|
-
.single();
|
|
195
|
-
if (insertRes.error)
|
|
196
|
-
throw new Error(`Erro ao criar tarefa: ${insertRes.error.message}`);
|
|
197
|
-
const task = insertRes.data;
|
|
198
|
-
const warnings = await linkSecondaryRecords(db, task.id, links.assigneeIds, links.labelIds, links.repoIds);
|
|
199
|
-
await insertTaskHistory(db, {
|
|
200
|
-
task_id: task.id,
|
|
201
|
-
user_id: userId,
|
|
202
|
-
action: 'created',
|
|
203
|
-
field: 'mcp',
|
|
204
|
-
new_value: task.title,
|
|
205
|
-
});
|
|
206
|
-
return { task, warnings };
|
|
207
|
-
},
|
|
208
|
-
async getCurrentTask(taskId, projectId) {
|
|
209
|
-
const res = await db
|
|
210
|
-
.from('tasks')
|
|
211
|
-
.select('id, title, description, status, priority, type, sprint_id, release_id, start_date, end_date, project_id')
|
|
212
|
-
.eq('id', taskId)
|
|
213
|
-
.eq('project_id', projectId)
|
|
214
|
-
.maybeSingle();
|
|
215
|
-
if (res.error)
|
|
216
|
-
throw new Error(`Erro ao buscar tarefa: ${res.error.message}`);
|
|
217
|
-
return res.data ?? null;
|
|
218
|
-
},
|
|
219
|
-
async resolveNextSprintId(projectId, sprintInput) {
|
|
220
|
-
if (sprintInput === undefined)
|
|
221
|
-
return undefined;
|
|
222
|
-
if (sprintInput === 'none')
|
|
223
|
-
return null;
|
|
224
|
-
if (sprintInput === 'active') {
|
|
225
|
-
const sprintRes = await db
|
|
226
|
-
.from('sprints')
|
|
227
|
-
.select('id')
|
|
228
|
-
.eq('project_id', projectId)
|
|
229
|
-
.eq('status', 'ativa')
|
|
230
|
-
.maybeSingle();
|
|
231
|
-
if (sprintRes.error)
|
|
232
|
-
throw new Error(`Erro ao buscar sprint ativa: ${sprintRes.error.message}`);
|
|
233
|
-
if (!sprintRes.data)
|
|
234
|
-
throw new Error('Não há sprint ativa no projeto.');
|
|
235
|
-
return sprintRes.data.id;
|
|
236
|
-
}
|
|
237
|
-
if (!SPRINT_UUID_RE.test(sprintInput)) {
|
|
238
|
-
throw new Error('Argumento "sprint" deve ser "active", "none" ou um UUID válido.');
|
|
239
|
-
}
|
|
240
|
-
const sprintRes = await db.from('sprints').select('id, project_id').eq('id', sprintInput).maybeSingle();
|
|
241
|
-
if (sprintRes.error)
|
|
242
|
-
throw new Error(`Erro ao validar sprint: ${sprintRes.error.message}`);
|
|
243
|
-
if (!sprintRes.data || sprintRes.data.project_id !== projectId) {
|
|
244
|
-
throw new Error('Sprint informada não pertence ao projeto atual.');
|
|
245
|
-
}
|
|
246
|
-
return sprintRes.data.id;
|
|
247
|
-
},
|
|
248
|
-
async applyTaskPatch(taskId, projectId, userId, patch, changes) {
|
|
249
|
-
if (Object.keys(patch).length > 0) {
|
|
250
|
-
const updateRes = await db.from('tasks').update(patch).eq('id', taskId).eq('project_id', projectId);
|
|
251
|
-
if (updateRes.error)
|
|
252
|
-
throw new Error(`Erro ao atualizar tarefa: ${updateRes.error.message}`);
|
|
253
|
-
}
|
|
254
|
-
for (const ch of changes) {
|
|
255
|
-
await insertTaskHistory(db, {
|
|
256
|
-
task_id: taskId,
|
|
257
|
-
user_id: userId,
|
|
258
|
-
action: 'updated',
|
|
259
|
-
field: ch.field,
|
|
260
|
-
old_value: ch.old_value,
|
|
261
|
-
new_value: ch.new_value,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
async getTaskIdSet(kind, taskId) {
|
|
266
|
-
const spec = ID_SET_SPEC[kind];
|
|
267
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- tabela dinâmica
|
|
268
|
-
const res = await db.from(spec.table).select(spec.idColumn).eq('task_id', taskId);
|
|
269
|
-
if (res.error)
|
|
270
|
-
throw new Error(`${spec.fetchErr}: ${res.error.message}`);
|
|
271
|
-
return (res.data ?? []).map((r) => r[spec.idColumn]);
|
|
272
|
-
},
|
|
273
|
-
async replaceTaskIdSet(kind, taskId, userId, nextIds, before, after) {
|
|
274
|
-
const spec = ID_SET_SPEC[kind];
|
|
275
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- tabela dinâmica
|
|
276
|
-
const del = await db.from(spec.table).delete().eq('task_id', taskId);
|
|
277
|
-
if (del.error)
|
|
278
|
-
throw new Error(`${spec.clearErr}: ${del.error.message}`);
|
|
279
|
-
if (nextIds.length > 0) {
|
|
280
|
-
const rows = nextIds.map((id) => ({ task_id: taskId, [spec.idColumn]: id }));
|
|
281
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- tabela dinâmica
|
|
282
|
-
const ins = await db.from(spec.table).insert(rows);
|
|
283
|
-
if (ins.error)
|
|
284
|
-
throw new Error(`${spec.insertErr}: ${ins.error.message}`);
|
|
285
|
-
}
|
|
286
|
-
await insertTaskHistory(db, {
|
|
287
|
-
task_id: taskId,
|
|
288
|
-
user_id: userId,
|
|
289
|
-
action: 'updated',
|
|
290
|
-
field: spec.historyField,
|
|
291
|
-
old_value: JSON.stringify(before),
|
|
292
|
-
new_value: JSON.stringify(after),
|
|
293
|
-
});
|
|
294
|
-
},
|
|
295
|
-
async reloadTask(taskId) {
|
|
296
|
-
const finalRes = await db
|
|
297
|
-
.from('tasks')
|
|
298
|
-
.select('id, key, title, status, priority, type, sprint_id, start_date, end_date, updated_at')
|
|
299
|
-
.eq('id', taskId)
|
|
300
|
-
.single();
|
|
301
|
-
if (finalRes.error)
|
|
302
|
-
throw new Error(`Tarefa atualizada, mas falhou ao recarregar: ${finalRes.error.message}`);
|
|
303
|
-
return finalRes.data;
|
|
304
|
-
},
|
|
305
|
-
async getTaskForMove(taskId, projectId) {
|
|
306
|
-
const res = await db
|
|
307
|
-
.from('tasks')
|
|
308
|
-
.select('id, key, title, status')
|
|
309
|
-
.eq('id', taskId)
|
|
310
|
-
.eq('project_id', projectId)
|
|
311
|
-
.maybeSingle();
|
|
312
|
-
if (res.error)
|
|
313
|
-
throw new Error(`Erro ao buscar tarefa: ${res.error.message}`);
|
|
314
|
-
return res.data ?? null;
|
|
315
|
-
},
|
|
316
|
-
async getTaskForAttachPr(taskId, projectId) {
|
|
317
|
-
const res = await db
|
|
318
|
-
.from('tasks')
|
|
319
|
-
.select('key, title, status, pr_url, review_state')
|
|
320
|
-
.eq('id', taskId)
|
|
321
|
-
.eq('project_id', projectId)
|
|
322
|
-
.maybeSingle();
|
|
323
|
-
if (res.error)
|
|
324
|
-
throw new Error(`Erro ao buscar tarefa: ${res.error.message}`);
|
|
325
|
-
return res.data ?? null;
|
|
326
|
-
},
|
|
327
|
-
async getTaskForSetReview(taskId, projectId) {
|
|
328
|
-
const res = await db
|
|
329
|
-
.from('tasks')
|
|
330
|
-
.select('key, title, status, review_state, pr_url')
|
|
331
|
-
.eq('id', taskId)
|
|
332
|
-
.eq('project_id', projectId)
|
|
333
|
-
.maybeSingle();
|
|
334
|
-
if (res.error)
|
|
335
|
-
throw new Error(`Erro ao buscar tarefa: ${res.error.message}`);
|
|
336
|
-
return res.data ?? null;
|
|
337
|
-
},
|
|
338
|
-
async moveTaskStatus(taskId, projectId, status, userId, oldStatus) {
|
|
339
|
-
// Cast: status já validado pela tool (zod) contra o enum gerado.
|
|
340
|
-
const updateRes = await db
|
|
341
|
-
.from('tasks')
|
|
342
|
-
.update({ status: status })
|
|
343
|
-
.eq('id', taskId)
|
|
344
|
-
.eq('project_id', projectId);
|
|
345
|
-
if (updateRes.error)
|
|
346
|
-
throw new Error(`Erro ao mover tarefa: ${updateRes.error.message}`);
|
|
347
|
-
await insertTaskHistory(db, {
|
|
348
|
-
task_id: taskId,
|
|
349
|
-
user_id: userId,
|
|
350
|
-
action: 'moved',
|
|
351
|
-
field: 'status',
|
|
352
|
-
old_value: oldStatus,
|
|
353
|
-
new_value: status,
|
|
354
|
-
});
|
|
355
|
-
},
|
|
356
|
-
async commentTask(taskId, projectId, userId, content, historyEntry) {
|
|
357
|
-
const taskRes = await db
|
|
358
|
-
.from('tasks')
|
|
359
|
-
.select('id')
|
|
360
|
-
.eq('id', taskId)
|
|
361
|
-
.eq('project_id', projectId)
|
|
362
|
-
.maybeSingle();
|
|
363
|
-
if (taskRes.error)
|
|
364
|
-
throw new Error(`Erro ao validar tarefa: ${taskRes.error.message}`);
|
|
365
|
-
if (!taskRes.data)
|
|
366
|
-
return null;
|
|
367
|
-
const insertRes = await db
|
|
368
|
-
.from('task_comments')
|
|
369
|
-
.insert({ task_id: taskId, author_id: userId, content })
|
|
370
|
-
.select('id, task_id, created_at')
|
|
371
|
-
.single();
|
|
372
|
-
if (insertRes.error)
|
|
373
|
-
throw new Error(`Erro ao gravar comentário: ${insertRes.error.message}`);
|
|
374
|
-
await insertTaskHistory(db, historyEntry);
|
|
375
|
-
return insertRes.data;
|
|
376
|
-
},
|
|
377
|
-
async addChecklist(taskId, projectId, title, icon, items) {
|
|
378
|
-
const taskRes = await db
|
|
379
|
-
.from('tasks')
|
|
380
|
-
.select('id')
|
|
381
|
-
.eq('id', taskId)
|
|
382
|
-
.eq('project_id', projectId)
|
|
383
|
-
.maybeSingle();
|
|
384
|
-
if (taskRes.error)
|
|
385
|
-
throw new Error(`Erro ao validar tarefa: ${taskRes.error.message}`);
|
|
386
|
-
if (!taskRes.data)
|
|
387
|
-
return null;
|
|
388
|
-
// `task_checklists`/`task_checklist_items` não estão nos types gerados — client destipado.
|
|
389
|
-
const raw = db;
|
|
390
|
-
const existing = await raw.from('task_checklists').select('id').eq('task_id', taskId);
|
|
391
|
-
if (existing.error)
|
|
392
|
-
throw new Error(`Erro ao ler checklists: ${existing.error.message}`);
|
|
393
|
-
const clRes = await raw
|
|
394
|
-
.from('task_checklists')
|
|
395
|
-
.insert(buildChecklistInsert(taskId, title, icon, existing.data?.length ?? 0))
|
|
396
|
-
.select('id')
|
|
397
|
-
.single();
|
|
398
|
-
if (clRes.error)
|
|
399
|
-
throw new Error(`Erro ao criar checklist: ${clRes.error.message}`);
|
|
400
|
-
const checklistId = clRes.data.id;
|
|
401
|
-
const rows = items.map((text, i) => ({
|
|
402
|
-
task_id: taskId,
|
|
403
|
-
checklist_id: checklistId,
|
|
404
|
-
text,
|
|
405
|
-
checked: false,
|
|
406
|
-
order: i,
|
|
407
|
-
}));
|
|
408
|
-
const itRes = await raw.from('task_checklist_items').insert(rows);
|
|
409
|
-
if (itRes.error)
|
|
410
|
-
throw new Error(`Erro ao gravar itens da checklist: ${itRes.error.message}`);
|
|
411
|
-
return { checklist_id: checklistId, item_count: rows.length };
|
|
412
|
-
},
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
async function resolveSprintFilter(db, projectId, sprint) {
|
|
416
|
-
if (!sprint)
|
|
417
|
-
return { noActive: false, filter: null };
|
|
418
|
-
if (sprint === 'active') {
|
|
419
|
-
const res = await db
|
|
420
|
-
.from('sprints')
|
|
421
|
-
.select('id')
|
|
422
|
-
.eq('project_id', projectId)
|
|
423
|
-
.eq('status', 'ativa')
|
|
424
|
-
.maybeSingle();
|
|
425
|
-
if (res.error)
|
|
426
|
-
throw new Error(`Erro ao buscar sprint ativa: ${res.error.message}`);
|
|
427
|
-
if (!res.data)
|
|
428
|
-
return { noActive: true };
|
|
429
|
-
return { noActive: false, filter: { mode: 'eq', value: res.data.id } };
|
|
430
|
-
}
|
|
431
|
-
if (sprint === 'none')
|
|
432
|
-
return { noActive: false, filter: { mode: 'is_null' } };
|
|
433
|
-
return { noActive: false, filter: { mode: 'eq', value: sprint } };
|
|
434
|
-
}
|
|
435
|
-
function applyAssigneeFilter(query, filter) {
|
|
436
|
-
const { assignee } = filter;
|
|
437
|
-
if (assignee === 'unassigned')
|
|
438
|
-
return query.is('task_assignees', null);
|
|
439
|
-
if (assignee)
|
|
440
|
-
return query.eq('task_assignees.user_id', assignee === 'me' ? filter.userId : assignee);
|
|
441
|
-
return query;
|
|
442
|
-
}
|
|
443
|
-
/** Sobreposição: end_date >= from E coalesce(start_date, end_date) <= to (sem end_date não entra). */
|
|
444
|
-
function applyOverlapFilter(query, filter) {
|
|
445
|
-
let q = query;
|
|
446
|
-
if (filter.from)
|
|
447
|
-
q = q.gte('end_date', filter.from);
|
|
448
|
-
if (filter.to) {
|
|
449
|
-
q = q.not('end_date', 'is', null);
|
|
450
|
-
q = q.or(`start_date.lte.${filter.to},and(start_date.is.null,end_date.lte.${filter.to})`);
|
|
451
|
-
}
|
|
452
|
-
return q;
|
|
453
|
-
}
|
|
454
|
-
async function queryFilteredTasks(db, filter, sprintFilter) {
|
|
455
|
-
const cols = 'id, key, title, status, priority, type, sprint_id, start_date, end_date, release_id, created_at';
|
|
456
|
-
const { assignee } = filter;
|
|
457
|
-
// Join filtrado no PostgREST: evita mandar centenas de ids na URL (.in/.not.in).
|
|
458
|
-
const embed = !assignee ? '' : assignee === 'unassigned' ? ', task_assignees!left(user_id)' : ', task_assignees!inner(user_id)';
|
|
459
|
-
let query = db.from('tasks').select(`${cols}${embed}`).eq('project_id', filter.projectId);
|
|
460
|
-
query = applyAssigneeFilter(query, filter);
|
|
461
|
-
query = applyOverlapFilter(query, filter);
|
|
462
|
-
if (sprintFilter?.mode === 'eq')
|
|
463
|
-
query = query.eq('sprint_id', sprintFilter.value);
|
|
464
|
-
else if (sprintFilter?.mode === 'is_null')
|
|
465
|
-
query = query.is('sprint_id', null);
|
|
466
|
-
// Cast: a porta é neutra (`string[]`), mas estes valores já vêm validados
|
|
467
|
-
// pela tool (zod) contra os mesmos enums — o cast só reconcilia os tipos gerados.
|
|
468
|
-
if (filter.status?.length)
|
|
469
|
-
query = query.in('status', filter.status);
|
|
470
|
-
if (filter.priority?.length)
|
|
471
|
-
query = query.in('priority', filter.priority);
|
|
472
|
-
if (filter.type?.length)
|
|
473
|
-
query = query.in('type', filter.type);
|
|
474
|
-
if (filter.search) {
|
|
475
|
-
const pattern = `%${filter.search.replace(/[%_]/g, (m) => `\\${m}`)}%`;
|
|
476
|
-
query = query.or(`title.ilike.${pattern},description.ilike.${pattern},key.ilike.${pattern}`);
|
|
477
|
-
}
|
|
478
|
-
query = query
|
|
479
|
-
.order('created_at', { ascending: false })
|
|
480
|
-
.range(filter.offset, filter.offset + filter.limit - 1);
|
|
481
|
-
const { data, error } = await query;
|
|
482
|
-
if (error)
|
|
483
|
-
throw new Error(`Erro ao listar tarefas: ${error.message}`);
|
|
484
|
-
// O embed serve só de filtro: não vaza pro resultado.
|
|
485
|
-
const rows = (data ?? []);
|
|
486
|
-
return rows.map(({ task_assignees: _ta, ...row }) => row);
|
|
487
|
-
}
|
|
488
|
-
async function loadAssigneeProfiles(db, taskIds) {
|
|
489
|
-
const assigneesRes = await db.from('task_assignees').select('task_id, user_id').in('task_id', taskIds);
|
|
490
|
-
if (assigneesRes.error)
|
|
491
|
-
throw new Error(`Erro ao buscar assignees: ${assigneesRes.error.message}`);
|
|
492
|
-
const assigneeRows = assigneesRes.data ?? [];
|
|
493
|
-
const userIds = Array.from(new Set(assigneeRows.map((a) => a.user_id)));
|
|
494
|
-
let profilesById = new Map();
|
|
495
|
-
if (userIds.length > 0) {
|
|
496
|
-
const profilesRes = await db.from('profiles').select('id, full_name, username').in('id', userIds);
|
|
497
|
-
if (profilesRes.error)
|
|
498
|
-
throw new Error(`Erro ao buscar perfis: ${profilesRes.error.message}`);
|
|
499
|
-
profilesById = new Map((profilesRes.data ?? []).map((p) => [p.id, { full_name: p.full_name, username: p.username }]));
|
|
500
|
-
}
|
|
501
|
-
return { assigneeRows, profilesById };
|
|
502
|
-
}
|
|
503
|
-
async function linkSecondaryRecords(db, taskId, assigneeIds, labelIds, repoIds) {
|
|
504
|
-
const results = await Promise.all([
|
|
505
|
-
insertTaskLinks(db, 'task_assignees', 'user_id', 'assignees', taskId, assigneeIds),
|
|
506
|
-
insertTaskLinks(db, 'task_label_assignments', 'label_id', 'labels', taskId, labelIds),
|
|
507
|
-
insertTaskLinks(db, 'task_repositories', 'repository_id', 'repositories', taskId, repoIds),
|
|
508
|
-
]);
|
|
509
|
-
return results.filter((w) => w !== null);
|
|
510
|
-
}
|
|
511
|
-
async function insertTaskLinks(db, table, idField, label, taskId, ids) {
|
|
512
|
-
if (ids.length === 0)
|
|
513
|
-
return null;
|
|
514
|
-
const rows = ids.map((id) => ({ task_id: taskId, [idField]: id }));
|
|
515
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- tabela dinâmica
|
|
516
|
-
const res = await db.from(table).insert(rows);
|
|
517
|
-
return res.error ? `Falha ao vincular ${label}: ${res.error.message}` : null;
|
|
518
|
-
}
|
|
519
|
-
async function fetchProfiles(db, userIds) {
|
|
520
|
-
if (userIds.length === 0)
|
|
521
|
-
return new Map();
|
|
522
|
-
const res = await db
|
|
523
|
-
.from('profiles')
|
|
524
|
-
.select('id, full_name, username, avatar_url')
|
|
525
|
-
.in('id', userIds);
|
|
526
|
-
if (res.error)
|
|
527
|
-
throw new Error(`Erro ao buscar perfis: ${res.error.message}`);
|
|
528
|
-
return new Map((res.data ?? []).map((p) => [
|
|
529
|
-
p.id,
|
|
530
|
-
{ full_name: p.full_name, username: p.username, avatar_url: p.avatar_url },
|
|
531
|
-
]));
|
|
532
|
-
}
|
|
533
|
-
//# sourceMappingURL=task-gateway.js.map
|